@@ -2,15 +2,19 @@ package build_test
22
33import (
44 "fmt"
5+ "io"
56 "slices"
67 "strings"
78 "testing"
9+ "time"
810
911 "github.com/microsoft/TypeScript/tsc/internal/core"
1012 "github.com/microsoft/TypeScript/tsc/internal/execute/build"
13+ "github.com/microsoft/TypeScript/tsc/internal/execute/tsc"
1114 "github.com/microsoft/TypeScript/tsc/internal/execute/tsctests"
1215 "github.com/microsoft/TypeScript/tsc/internal/tsoptions"
1316 "github.com/microsoft/TypeScript/tsc/internal/tspath"
17+ "github.com/microsoft/TypeScript/tsc/internal/vfs"
1418 "gotest.tools/v3/assert"
1519)
1620
@@ -32,6 +36,123 @@ func TestBuildOrderGenerator(t *testing.T) {
3236 }
3337}
3438
39+ func TestBuildScheduling (t * testing.T ) {
40+ t .Parallel ()
41+ for _ , operation := range []struct {
42+ name string
43+ project string
44+ refsOnly bool
45+ order []string
46+ }{
47+ {"full build" , "" , false , []string {"Leaf" , "Middle" , "Independent" , "Root" , "Other" }},
48+ {"selected project" , "Root" , false , []string {"Leaf" , "Middle" , "Independent" , "Root" }},
49+ {"selected references" , "Root" , true , []string {"Leaf" , "Middle" , "Independent" }},
50+ } {
51+ t .Run (operation .name , func (t * testing.T ) {
52+ t .Parallel ()
53+ sys := newSchedulingTestSystem ()
54+ independentStarted := make (chan struct {})
55+ sys .fs .onRead = func (path tspath.RootedFilePath ) {
56+ if path .BaseName () != "index.ts" {
57+ return
58+ }
59+ switch path .Directory ().BaseName () {
60+ case "Leaf" :
61+ // Independent must start while Leaf is still occupying a builder.
62+ select {
63+ case <- independentStarted :
64+ return
65+ case <- time .After (30 * time .Second ):
66+ t .Error ("Independent was queued behind a builder waiting on Leaf" )
67+ }
68+ case "Independent" :
69+ close (independentStarted )
70+ }
71+ }
72+ command := tsoptions .ParseBuildCommandLine ([]string {"--build" , "--verbose" , "--builders" , "2" , "Root" , "Other" }, sys .FS (), sys .GetCurrentDirectory ())
73+ orchestrator := build .NewOrchestrator (build.Options {Sys : sys , Command : command })
74+ var result * build.OrchestratorResult
75+ if operation .refsOnly {
76+ result = orchestrator .BuildReferences (t .Context (), operation .project )
77+ } else {
78+ result = orchestrator .Build (t .Context (), operation .project )
79+ }
80+ assert .Equal (t , result .Result .Status , tsc .ExitStatusSuccess )
81+ assert .Equal (t , result .Statistics .Projects , len (operation .order ))
82+ assert .Equal (t , result .Statistics .ProjectsBuilt , len (operation .order ))
83+ reported := []string {}
84+ for line := range strings .SplitSeq (sys .output .String (), "\n " ) {
85+ if strings .Contains (line , "Building project" ) {
86+ for _ , project := range operation .order {
87+ if strings .Contains (line , "'" + project + "/tsconfig.json'" ) {
88+ reported = append (reported , project )
89+ }
90+ }
91+ }
92+ }
93+ assert .DeepEqual (t , reported , operation .order )
94+ for _ , project := range []string {"Leaf" , "Middle" , "Independent" , "Root" , "Other" } {
95+ assert .Equal (t , sys .FS ().FileExists (sys .GetCurrentDirectory ().ResolveFile (project + "/dist/index.js" )), slices .Contains (operation .order , project ))
96+ }
97+ })
98+ }
99+ }
100+
101+ type schedulingTestFS struct {
102+ vfs.FS
103+ onRead func (tspath.RootedFilePath )
104+ }
105+
106+ func (f * schedulingTestFS ) ReadFile (path tspath.RootedFilePath ) (string , bool ) {
107+ f .onRead (path )
108+ return f .FS .ReadFile (path )
109+ }
110+
111+ type schedulingTestSystem struct {
112+ * tsctests.TestSys
113+ fs * schedulingTestFS
114+ output strings.Builder
115+ }
116+
117+ func (s * schedulingTestSystem ) FS () vfs.FS { return s .fs }
118+ func (s * schedulingTestSystem ) Writer () io.Writer { return & s .output }
119+
120+ func newSchedulingTestSystem () * schedulingTestSystem {
121+ files := tsctests.FileMap {
122+ "/project/lib.d.ts" : `
123+ interface Array<T> {}
124+ interface Boolean {}
125+ interface CallableFunction {}
126+ interface Function {}
127+ interface IArguments {}
128+ interface NewableFunction {}
129+ interface Number {}
130+ interface Object {}
131+ interface RegExp {}
132+ interface String {}
133+ ` ,
134+ }
135+ deps := map [string ][]string {
136+ "Root" : {"Middle" , "Independent" },
137+ "Middle" : {"Leaf" },
138+ "Leaf" : {},
139+ "Independent" : {},
140+ "Other" : {},
141+ }
142+ for project , references := range deps {
143+ files ["/project/" + project + "/index.ts" ] = "export const value = 1;"
144+ files ["/project/" + project + "/tsconfig.json" ] = fmt .Sprintf (`{
145+ "compilerOptions": { "composite": true, "noLib": true, "outDir": "dist" },
146+ "files": ["../lib.d.ts", "index.ts"],
147+ "references": [%s]
148+ }` , strings .Join (core .Map (references , func (ref string ) string {
149+ return fmt .Sprintf (`{ "path": "../%s" }` , ref )
150+ }), "," ))
151+ }
152+ sys := tsctests .NewTscSystem (files , tspath .CaseSensitive , "/project" )
153+ return & schedulingTestSystem {TestSys : sys , fs : & schedulingTestFS {FS : sys .FS ()}}
154+ }
155+
35156type buildOrderTestCase struct {
36157 name string
37158 projects []string
0 commit comments