@@ -10,6 +10,7 @@ export function getTestRunnerCode(options: {
1010 includeEsModule : boolean | undefined ;
1111 includeScriptModule : boolean | undefined ;
1212 preloadEntryPoint ?: string ;
13+ testIsolation ?: "process" | "none" ;
1314} ) {
1415 const usesDenoTest = options . denoTestShimPackageName != null ;
1516 const preloadPath = options . preloadEntryPoint == null
@@ -34,7 +35,11 @@ export function getTestRunnerCode(options: {
3435 } ) ;
3536 writer . writeLine ( "];" ) . newLine ( ) ;
3637
38+ const isolateTestFiles = options . testIsolation === "process" ;
3739 writer . write ( "async function main()" ) . block ( ( ) => {
40+ if ( isolateTestFiles ) {
41+ writeSpawnPerFile ( ) ;
42+ }
3843 if ( preloadPath != null ) {
3944 if ( options . includeScriptModule ) {
4045 writer . writeLine ( `process.chdir(__dirname + "/script");` ) ;
@@ -59,60 +64,18 @@ export function getTestRunnerCode(options: {
5964 writer . writeLine ( "pc," ) ;
6065 } ) . write ( ";" ) . newLine ( ) ;
6166 }
62- writer . write ( "for (const [i, filePath] of filePaths.entries())" )
63- . block ( ( ) => {
64- writer . write ( "if (i > 0)" ) . block ( ( ) => {
65- writer . writeLine ( `console.log("");` ) ;
66- } ) . blankLine ( ) ;
67-
68- if ( options . includeScriptModule ) {
69- writer . writeLine ( `const scriptPath = "./script/" + filePath;` ) ;
70- writer . writeLine (
71- `console.log("Running tests in " + pc.underline(scriptPath) + "...\\n");` ,
72- ) ;
73- writer . writeLine ( `process.chdir(__dirname + "/script");` ) ;
74- if ( usesDenoTest ) {
75- writer . write ( `const scriptTestContext = ` ) . inlineBlock ( ( ) => {
76- writer . writeLine ( "origin: pathToFileURL(filePath).toString()," ) ;
77- writer . writeLine ( "...testContext," ) ;
78- } ) . write ( ";" ) . newLine ( ) ;
79- }
80- writer . write ( "try " ) . inlineBlock ( ( ) => {
81- writer . writeLine ( `require(scriptPath);` ) ;
82- } ) . write ( " catch(err)" ) . block ( ( ) => {
83- writer . writeLine ( "console.error(err);" ) ;
84- writer . writeLine ( "process.exit(1);" ) ;
85- } ) ;
86- if ( usesDenoTest ) {
87- writer . writeLine (
88- "await runTestDefinitions(testDefinitions.splice(0, testDefinitions.length), scriptTestContext);" ,
89- ) ;
90- }
91- }
67+ if ( isolateTestFiles ) {
68+ writeTestFileRun ( ) ;
69+ } else {
70+ writer . write ( "for (const [i, filePath] of filePaths.entries())" )
71+ . block ( ( ) => {
72+ writer . write ( "if (i > 0)" ) . block ( ( ) => {
73+ writer . writeLine ( `console.log("");` ) ;
74+ } ) . blankLine ( ) ;
9275
93- if ( options . includeEsModule ) {
94- if ( options . includeScriptModule ) {
95- writer . blankLine ( ) ;
96- }
97- writer . writeLine ( `const esmPath = "./esm/" + filePath;` ) ;
98- writer . writeLine (
99- `console.log("\\nRunning tests in " + pc.underline(esmPath) + "...\\n");` ,
100- ) ;
101- writer . writeLine ( `process.chdir(__dirname + "/esm");` ) ;
102- if ( usesDenoTest ) {
103- writer . write ( `const esmTestContext = ` ) . inlineBlock ( ( ) => {
104- writer . writeLine ( "origin: pathToFileURL(filePath).toString()," ) ;
105- writer . writeLine ( "...testContext," ) ;
106- } ) . write ( ";" ) . newLine ( ) ;
107- }
108- writer . writeLine ( `await import(esmPath);` ) ;
109- if ( usesDenoTest ) {
110- writer . writeLine (
111- "await runTestDefinitions(testDefinitions.splice(0, testDefinitions.length), esmTestContext);" ,
112- ) ;
113- }
114- }
115- } ) ;
76+ writeTestFileRun ( ) ;
77+ } ) ;
78+ }
11679 } ) ;
11780 writer . blankLine ( ) ;
11881
@@ -123,6 +86,96 @@ export function getTestRunnerCode(options: {
12386
12487 writer . writeLine ( "main();" ) ;
12588 return writer . toString ( ) ;
89+
90+ function writeSpawnPerFile ( ) {
91+ // run each test file in its own process so that the module state of a
92+ // test file doesn't leak into the next one, which is what `deno test`
93+ // does by running each file in its own isolate
94+ writer . writeLine ( "const fileIndexArg = process.argv[2];" ) ;
95+ writer . write ( "if (fileIndexArg == null)" ) . block ( ( ) => {
96+ writer . writeLine ( `const { spawnSync } = require("child_process");` ) ;
97+ writer . writeLine ( "let failed = false;" ) ;
98+ writer . write ( "for (const i of filePaths.keys())" ) . block ( ( ) => {
99+ writer . write ( "if (i > 0)" ) . block ( ( ) => {
100+ writer . writeLine ( `console.log("");` ) ;
101+ } ) ;
102+ writer . writeLine (
103+ "const args = [...process.execArgv, __filename, String(i)];" ,
104+ ) ;
105+ writer . writeLine (
106+ `const result = spawnSync(process.execPath, args, { stdio: "inherit" });` ,
107+ ) ;
108+ writer . write ( "if (result.error != null)" ) . block ( ( ) => {
109+ writer . writeLine ( "console.error(result.error);" ) ;
110+ } ) ;
111+ writer . write ( "if (result.status !== 0)" ) . block ( ( ) => {
112+ writer . writeLine ( "failed = true;" ) ;
113+ } ) ;
114+ } ) ;
115+ writer . write ( "if (failed)" ) . block ( ( ) => {
116+ writer . writeLine ( "process.exitCode = 1;" ) ;
117+ } ) ;
118+ writer . writeLine ( "return;" ) ;
119+ } ) . blankLine ( ) ;
120+ writer . writeLine ( "const filePath = filePaths[Number(fileIndexArg)];" ) ;
121+ writer . write ( "if (filePath == null)" ) . block ( ( ) => {
122+ writer . writeLine (
123+ `console.error("Unknown test file index: " + fileIndexArg);` ,
124+ ) ;
125+ writer . writeLine ( "process.exitCode = 1;" ) ;
126+ writer . writeLine ( "return;" ) ;
127+ } ) . blankLine ( ) ;
128+ }
129+
130+ function writeTestFileRun ( ) {
131+ if ( options . includeScriptModule ) {
132+ writer . writeLine ( `const scriptPath = "./script/" + filePath;` ) ;
133+ writer . writeLine (
134+ `console.log("Running tests in " + pc.underline(scriptPath) + "...\\n");` ,
135+ ) ;
136+ writer . writeLine ( `process.chdir(__dirname + "/script");` ) ;
137+ if ( usesDenoTest ) {
138+ writer . write ( `const scriptTestContext = ` ) . inlineBlock ( ( ) => {
139+ writer . writeLine ( "origin: pathToFileURL(filePath).toString()," ) ;
140+ writer . writeLine ( "...testContext," ) ;
141+ } ) . write ( ";" ) . newLine ( ) ;
142+ }
143+ writer . write ( "try " ) . inlineBlock ( ( ) => {
144+ writer . writeLine ( `require(scriptPath);` ) ;
145+ } ) . write ( " catch(err)" ) . block ( ( ) => {
146+ writer . writeLine ( "console.error(err);" ) ;
147+ writer . writeLine ( "process.exit(1);" ) ;
148+ } ) ;
149+ if ( usesDenoTest ) {
150+ writer . writeLine (
151+ "await runTestDefinitions(testDefinitions.splice(0, testDefinitions.length), scriptTestContext);" ,
152+ ) ;
153+ }
154+ }
155+
156+ if ( options . includeEsModule ) {
157+ if ( options . includeScriptModule ) {
158+ writer . blankLine ( ) ;
159+ }
160+ writer . writeLine ( `const esmPath = "./esm/" + filePath;` ) ;
161+ writer . writeLine (
162+ `console.log("\\nRunning tests in " + pc.underline(esmPath) + "...\\n");` ,
163+ ) ;
164+ writer . writeLine ( `process.chdir(__dirname + "/esm");` ) ;
165+ if ( usesDenoTest ) {
166+ writer . write ( `const esmTestContext = ` ) . inlineBlock ( ( ) => {
167+ writer . writeLine ( "origin: pathToFileURL(filePath).toString()," ) ;
168+ writer . writeLine ( "...testContext," ) ;
169+ } ) . write ( ";" ) . newLine ( ) ;
170+ }
171+ writer . writeLine ( `await import(esmPath);` ) ;
172+ if ( usesDenoTest ) {
173+ writer . writeLine (
174+ "await runTestDefinitions(testDefinitions.splice(0, testDefinitions.length), esmTestContext);" ,
175+ ) ;
176+ }
177+ }
178+ }
126179}
127180
128181function getRunTestDefinitionsCode ( ) {
0 commit comments