1namespace ts { 2 describe("unittests:: tsbuild - graph-ordering", () => { 3 let host: fakes.SolutionBuilderHost | undefined; 4 const deps: [string, string][] = [ 5 ["A", "B"], 6 ["B", "C"], 7 ["A", "C"], 8 ["B", "D"], 9 ["C", "D"], 10 ["C", "E"], 11 ["F", "E"], 12 ["H", "I"], 13 ["I", "J"], 14 ["J", "H"], 15 ["J", "E"] 16 ]; 17 18 before(() => { 19 const fs = new vfs.FileSystem(false); 20 host = fakes.SolutionBuilderHost.create(fs); 21 writeProjects(fs, ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"], deps); 22 }); 23 24 after(() => { 25 host = undefined; 26 }); 27 28 it("orders the graph correctly - specify two roots", () => { 29 checkGraphOrdering(["A", "G"], ["D", "E", "C", "B", "A", "G"]); 30 }); 31 32 it("orders the graph correctly - multiple parts of the same graph in various orders", () => { 33 checkGraphOrdering(["A"], ["D", "E", "C", "B", "A"]); 34 checkGraphOrdering(["A", "C", "D"], ["D", "E", "C", "B", "A"]); 35 checkGraphOrdering(["D", "C", "A"], ["D", "E", "C", "B", "A"]); 36 }); 37 38 it("orders the graph correctly - other orderings", () => { 39 checkGraphOrdering(["F"], ["E", "F"]); 40 checkGraphOrdering(["E"], ["E"]); 41 checkGraphOrdering(["F", "C", "A"], ["E", "F", "D", "C", "B", "A"]); 42 }); 43 44 it("returns circular order", () => { 45 checkGraphOrdering(["H"], ["E", "J", "I", "H"], /*circular*/ true); 46 checkGraphOrdering(["A", "H"], ["D", "E", "C", "B", "A", "J", "I", "H"], /*circular*/ true); 47 }); 48 49 function checkGraphOrdering(rootNames: string[], expectedBuildSet: string[], circular?: true) { 50 const builder = createSolutionBuilder(host!, rootNames.map(getProjectFileName), { dry: true, force: false, verbose: false }); 51 const buildOrder = builder.getBuildOrder(); 52 assert.equal(isCircularBuildOrder(buildOrder), !!circular); 53 const buildQueue = getBuildOrderFromAnyBuildOrder(buildOrder); 54 assert.deepEqual(buildQueue, expectedBuildSet.map(getProjectFileName)); 55 56 if (!circular) { 57 for (const dep of deps) { 58 const child = getProjectFileName(dep[0]); 59 if (buildQueue.indexOf(child) < 0) continue; 60 const parent = getProjectFileName(dep[1]); 61 assert.isAbove(buildQueue.indexOf(child), buildQueue.indexOf(parent), `Expecting child ${child} to be built after parent ${parent}`); 62 } 63 } 64 } 65 66 function getProjectFileName(proj: string) { 67 return `/project/${proj}/tsconfig.json` as ResolvedConfigFileName; 68 } 69 70 function writeProjects(fileSystem: vfs.FileSystem, projectNames: string[], deps: [string, string][]): string[] { 71 const projFileNames: string[] = []; 72 for (const dep of deps) { 73 if (projectNames.indexOf(dep[0]) < 0) throw new Error(`Invalid dependency - project ${dep[0]} does not exist`); 74 if (projectNames.indexOf(dep[1]) < 0) throw new Error(`Invalid dependency - project ${dep[1]} does not exist`); 75 } 76 for (const proj of projectNames) { 77 fileSystem.mkdirpSync(`/project/${proj}`); 78 fileSystem.writeFileSync(`/project/${proj}/${proj}.ts`, "export {}"); 79 const configFileName = getProjectFileName(proj); 80 const configContent = JSON.stringify({ 81 compilerOptions: { composite: true }, 82 files: [`./${proj}.ts`], 83 references: deps.filter(d => d[0] === proj).map(d => ({ path: `../${d[1]}` })) 84 }, undefined, 2); 85 fileSystem.writeFileSync(configFileName, configContent); 86 projFileNames.push(configFileName); 87 } 88 return projFileNames; 89 } 90 }); 91} 92