OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 // Windows doesn't support sending signals. | 5 // Windows doesn't support sending signals. |
6 @TestOn("vm && !windows") | 6 @TestOn("vm && !windows") |
7 | 7 |
8 import 'dart:async'; | 8 import 'dart:async'; |
9 import 'dart:io'; | 9 import 'dart:io'; |
10 | 10 |
11 import 'package:path/path.dart' as p; | 11 import 'package:path/path.dart' as p; |
12 import 'package:test/src/util/io.dart'; | 12 import 'package:scheduled_test/descriptor.dart' as d; |
13 import 'package:test/src/utils.dart'; | 13 import 'package:scheduled_test/scheduled_process.dart'; |
14 import 'package:test/test.dart'; | 14 import 'package:scheduled_test/scheduled_stream.dart'; |
| 15 import 'package:scheduled_test/scheduled_test.dart'; |
15 | 16 |
16 import '../io.dart'; | 17 import '../io.dart'; |
17 | 18 |
18 String _sandbox; | 19 String get _tempDir => p.join(sandbox, "tmp"); |
19 | |
20 String get _tempDir => p.join(_sandbox, "tmp"); | |
21 | 20 |
22 // This test is inherently prone to race conditions. If it fails, it will likely | 21 // This test is inherently prone to race conditions. If it fails, it will likely |
23 // do so flakily, but if it succeeds, it will succeed consistently. The tests | 22 // do so flakily, but if it succeeds, it will succeed consistently. The tests |
24 // represent a best effort to kill the test runner at certain times during its | 23 // represent a best effort to kill the test runner at certain times during its |
25 // execution. | 24 // execution. |
26 void main() { | 25 void main() { |
27 setUp(() { | 26 useSandbox(() => d.dir("tmp").create()); |
28 _sandbox = createTempDir(); | |
29 }); | |
30 | |
31 tearDown(() { | |
32 new Directory(_sandbox).deleteSync(recursive: true); | |
33 }); | |
34 | 27 |
35 group("during loading,", () { | 28 group("during loading,", () { |
36 test("cleans up if killed while loading a VM test", () async { | 29 test("cleans up if killed while loading a VM test", () { |
37 new File(p.join(_sandbox, "test.dart")).writeAsStringSync(""" | 30 d.file("test.dart", """ |
38 void main() { | 31 void main() { |
39 print("in test.dart"); | 32 print("in test.dart"); |
40 // Spin for a long time so the test is probably killed while still loading. | 33 // Spin for a long time so the test is probably killed while still loading. |
41 for (var i = 0; i < 100000000; i++) {} | 34 for (var i = 0; i < 100000000; i++) {} |
42 } | 35 } |
43 """); | 36 """).create(); |
44 | 37 |
45 var process = await _startTest(["-r", "expanded", "test.dart"]); | 38 var test = _runTest(["test.dart"]); |
| 39 test.stdout.expect(consumeThrough("in test.dart")); |
| 40 signalAndQuit(test); |
46 | 41 |
47 // Skip a progress line. | 42 expectTempDirEmpty(); |
48 var line = await lineSplitter.bind(process.stdout).skip(1).first; | |
49 expect(line, equals("in test.dart")); | |
50 process.kill(); | |
51 await process.exitCode; | |
52 expect(new Directory(_tempDir).listSync(), isEmpty); | |
53 }); | 43 }); |
54 | 44 |
55 test("cleans up if killed while loading a browser test", () async { | 45 test("cleans up if killed while loading a browser test", () { |
56 new File(p.join(_sandbox, "test.dart")) | 46 d.file("test.dart", "void main() {}").create(); |
57 .writeAsStringSync("void main() {}"); | |
58 | 47 |
59 var process = await _startTest( | 48 var test = _runTest(["-p", "chrome", "test.dart"]); |
60 ["-r", "expanded", "-p", "chrome", "test.dart"]); | 49 test.stdout.expect(consumeThrough(endsWith("compiling test.dart"))); |
61 var line = await lineSplitter.bind(process.stdout).first; | 50 signalAndQuit(test); |
62 expect(line, endsWith("compiling test.dart")); | 51 |
63 process.kill(); | 52 expectTempDirEmpty(); |
64 await process.exitCode; | |
65 expect(new Directory(_tempDir).listSync(), isEmpty); | |
66 }); | 53 }); |
67 | 54 |
68 test("exits immediately if ^C is sent twice", () async { | 55 test("exits immediately if ^C is sent twice", () { |
69 new File(p.join(_sandbox, "test.dart")).writeAsStringSync(""" | 56 d.file("test.dart", """ |
70 void main() { | 57 void main() { |
71 print("in test.dart"); | 58 print("in test.dart"); |
72 while (true) {} | 59 while (true) {} |
73 } | 60 } |
74 """); | 61 """).create(); |
75 | 62 |
76 var process = await _startTest(["-r", "expanded", "test.dart"]); | 63 var test = _runTest(["test.dart"]); |
77 | 64 test.stdout.expect(consumeThrough("in test.dart")); |
78 // Skip a progress line. | 65 test.signal(ProcessSignal.SIGTERM); |
79 var line = await lineSplitter.bind(process.stdout).skip(1).first; | |
80 expect(line, equals("in test.dart")); | |
81 process.kill(); | |
82 | 66 |
83 // TODO(nweiz): Sending two signals in close succession can cause the | 67 // TODO(nweiz): Sending two signals in close succession can cause the |
84 // second one to be ignored, so we wait a bit before the second | 68 // second one to be ignored, so we wait a bit before the second |
85 // one. Remove this hack when issue 23047 is fixed. | 69 // one. Remove this hack when issue 23047 is fixed. |
86 await new Future.delayed(new Duration(seconds: 1)); | 70 schedule(() => new Future.delayed(new Duration(seconds: 1))); |
87 process.kill(); | 71 |
88 await process.exitCode; | 72 signalAndQuit(test); |
89 }); | 73 }); |
90 }); | 74 }); |
91 | 75 |
92 group("during test running", () { | 76 group("during test running", () { |
93 test("waits for a VM test to finish running", () async { | 77 test("waits for a VM test to finish running", () { |
94 new File(p.join(_sandbox, "test.dart")).writeAsStringSync(""" | 78 d.file("test.dart", """ |
95 import 'dart:async'; | 79 import 'dart:async'; |
96 import 'dart:io'; | 80 import 'dart:io'; |
97 | 81 |
98 import 'package:test/test.dart'; | 82 import 'package:test/test.dart'; |
99 | 83 |
100 void main() { | 84 void main() { |
101 tearDown(() => new File("output").writeAsStringSync("ran teardown")); | 85 tearDown(() => new File("output").writeAsStringSync("ran teardown")); |
102 | 86 |
103 test("test", () { | 87 test("test", () { |
104 print("running test"); | 88 print("running test"); |
105 return new Future.delayed(new Duration(seconds: 1)); | 89 return new Future.delayed(new Duration(seconds: 1)); |
106 }); | 90 }); |
107 } | 91 } |
108 """); | 92 """).create(); |
109 | 93 |
110 var process = await _startTest(["-r", "expanded", "test.dart"]); | 94 var test = _runTest(["test.dart"]); |
| 95 test.stdout.expect(consumeThrough("running test")); |
| 96 signalAndQuit(test); |
111 | 97 |
112 // Skip a progress line. | 98 d.file("output", "ran teardown").validate(); |
113 var line = await lineSplitter.bind(process.stdout).skip(1).first; | 99 expectTempDirEmpty(); |
114 expect(line, equals("running test")); | |
115 process.kill(); | |
116 await process.exitCode; | |
117 expect(new File(p.join(_sandbox, "output")).readAsStringSync(), | |
118 equals("ran teardown")); | |
119 expect(new Directory(_tempDir).listSync(), isEmpty); | |
120 }); | 100 }); |
121 | 101 |
122 test("kills a browser test immediately", () async { | 102 test("kills a browser test immediately", () { |
123 new File(p.join(_sandbox, "test.dart")).writeAsStringSync(""" | 103 d.file("test.dart", """ |
124 import 'dart:async'; | 104 import 'dart:async'; |
125 | 105 |
126 import 'package:test/test.dart'; | 106 import 'package:test/test.dart'; |
127 | 107 |
128 void main() { | 108 void main() { |
129 test("test", () { | 109 test("test", () { |
130 print("running test"); | 110 print("running test"); |
131 | 111 |
132 // Allow an event loop to pass so the preceding print can be handled. | 112 // Allow an event loop to pass so the preceding print can be handled. |
133 return new Future(() { | 113 return new Future(() { |
134 // Loop forever so that if the test isn't stopped while running, it never | 114 // Loop forever so that if the test isn't stopped while running, it never |
135 // stops. | 115 // stops. |
136 while (true) {} | 116 while (true) {} |
137 }); | 117 }); |
138 }); | 118 }); |
139 } | 119 } |
140 """); | 120 """).create(); |
141 | 121 |
142 var process = await _startTest( | 122 var test = _runTest(["-p", "content-shell", "test.dart"]); |
143 ["-r", "expanded", "-p", "content-shell", "test.dart"]); | 123 test.stdout.expect(consumeThrough("running test")); |
| 124 signalAndQuit(test); |
144 | 125 |
145 // Skip a progress line.. | 126 expectTempDirEmpty(); |
146 var line = await lineSplitter.bind(process.stdout).skip(1).first; | |
147 expect(line, equals("running test")); | |
148 process.kill(); | |
149 await process.exitCode; | |
150 expect(new Directory(_tempDir).listSync(), isEmpty); | |
151 }); | 127 }); |
152 | 128 |
153 test("kills a VM test immediately if ^C is sent twice", () async { | 129 test("kills a VM test immediately if ^C is sent twice", () { |
154 new File(p.join(_sandbox, "test.dart")).writeAsStringSync(""" | 130 d.file("test.dart", """ |
155 import 'package:test/test.dart'; | 131 import 'package:test/test.dart'; |
156 | 132 |
157 void main() { | 133 void main() { |
158 test("test", () { | 134 test("test", () { |
159 print("running test"); | 135 print("running test"); |
160 while (true) {} | 136 while (true) {} |
161 }); | 137 }); |
162 } | 138 } |
163 """); | 139 """).create(); |
164 | 140 |
165 var process = await _startTest(["-r", "expanded", "test.dart"]); | 141 var test = _runTest(["test.dart"]); |
166 | 142 test.stdout.expect(consumeThrough("running test")); |
167 // Skip a progress line. | 143 test.signal(ProcessSignal.SIGTERM); |
168 var line = await lineSplitter.bind(process.stdout).skip(1).first; | |
169 expect(line, equals("running test")); | |
170 process.kill(); | |
171 | 144 |
172 // TODO(nweiz): Sending two signals in close succession can cause the | 145 // TODO(nweiz): Sending two signals in close succession can cause the |
173 // second one to be ignored, so we wait a bit before the second | 146 // second one to be ignored, so we wait a bit before the second |
174 // one. Remove this hack when issue 23047 is fixed. | 147 // one. Remove this hack when issue 23047 is fixed. |
175 await new Future.delayed(new Duration(seconds: 1)); | 148 schedule(() => new Future.delayed(new Duration(seconds: 1))); |
176 process.kill(); | 149 signalAndQuit(test); |
177 await process.exitCode; | |
178 }); | 150 }); |
179 | 151 |
180 test("causes expect() to always throw an error immediately", () async { | 152 test("causes expect() to always throw an error immediately", () { |
181 new File(p.join(_sandbox, "test.dart")).writeAsStringSync(""" | 153 d.file("test.dart", """ |
182 import 'dart:async'; | 154 import 'dart:async'; |
183 import 'dart:io'; | 155 import 'dart:io'; |
184 | 156 |
185 import 'package:test/test.dart'; | 157 import 'package:test/test.dart'; |
186 | 158 |
187 void main() { | 159 void main() { |
188 var expectThrewError = false; | 160 var expectThrewError = false; |
189 | 161 |
190 tearDown(() { | 162 tearDown(() { |
191 new File("output").writeAsStringSync(expectThrewError.toString()); | 163 new File("output").writeAsStringSync(expectThrewError.toString()); |
192 }); | 164 }); |
193 | 165 |
194 test("test", () async { | 166 test("test", () async { |
195 print("running test"); | 167 print("running test"); |
196 | 168 |
197 await new Future.delayed(new Duration(seconds: 1)); | 169 await new Future.delayed(new Duration(seconds: 1)); |
198 try { | 170 try { |
199 expect(true, isTrue); | 171 expect(true, isTrue); |
200 } catch (_) { | 172 } catch (_) { |
201 expectThrewError = true; | 173 expectThrewError = true; |
202 } | 174 } |
203 }); | 175 }); |
204 } | 176 } |
205 """); | 177 """).create(); |
206 | 178 |
207 var process = await _startTest(["-r", "expanded", "test.dart"]); | 179 var test = _runTest(["test.dart"]); |
| 180 test.stdout.expect(consumeThrough("running test")); |
| 181 signalAndQuit(test); |
208 | 182 |
209 // Skip a progress line. | 183 d.file("output", "true").validate(); |
210 var line = await lineSplitter.bind(process.stdout).skip(1).first; | 184 expectTempDirEmpty(); |
211 expect(line, equals("running test")); | |
212 process.kill(); | |
213 await process.exitCode; | |
214 expect(new File(p.join(_sandbox, "output")).readAsStringSync(), | |
215 equals("true")); | |
216 expect(new Directory(_tempDir).listSync(), isEmpty); | |
217 }); | 185 }); |
218 | 186 |
219 test("causes expectAsync() to always throw an error immediately", () async { | 187 test("causes expectAsync() to always throw an error immediately", () { |
220 new File(p.join(_sandbox, "test.dart")).writeAsStringSync(""" | 188 d.file("test.dart", """ |
221 import 'dart:async'; | 189 import 'dart:async'; |
222 import 'dart:io'; | 190 import 'dart:io'; |
223 | 191 |
224 import 'package:test/test.dart'; | 192 import 'package:test/test.dart'; |
225 | 193 |
226 void main() { | 194 void main() { |
227 var expectAsyncThrewError = false; | 195 var expectAsyncThrewError = false; |
228 | 196 |
229 tearDown(() { | 197 tearDown(() { |
230 new File("output").writeAsStringSync(expectAsyncThrewError.toString()); | 198 new File("output").writeAsStringSync(expectAsyncThrewError.toString()); |
231 }); | 199 }); |
232 | 200 |
233 test("test", () async { | 201 test("test", () async { |
234 print("running test"); | 202 print("running test"); |
235 | 203 |
236 await new Future.delayed(new Duration(seconds: 1)); | 204 await new Future.delayed(new Duration(seconds: 1)); |
237 try { | 205 try { |
238 expectAsync(() {}); | 206 expectAsync(() {}); |
239 } catch (_) { | 207 } catch (_) { |
240 expectAsyncThrewError = true; | 208 expectAsyncThrewError = true; |
241 } | 209 } |
242 }); | 210 }); |
243 } | 211 } |
244 """); | 212 """).create(); |
245 | 213 |
246 var process = await _startTest(["-r", "expanded", "test.dart"]); | 214 var test = _runTest(["test.dart"]); |
| 215 test.stdout.expect(consumeThrough("running test")); |
| 216 signalAndQuit(test); |
247 | 217 |
248 // Skip a progress line. | 218 d.file("output", "true").validate(); |
249 var line = await lineSplitter.bind(process.stdout).skip(1).first; | 219 expectTempDirEmpty(); |
250 expect(line, equals("running test")); | |
251 process.kill(); | |
252 await process.exitCode; | |
253 expect(new File(p.join(_sandbox, "output")).readAsStringSync(), | |
254 equals("true")); | |
255 expect(new Directory(_tempDir).listSync(), isEmpty); | |
256 }); | 220 }); |
257 }); | 221 }); |
258 } | 222 } |
259 | 223 |
260 Future<Process> _startTest(List<String> args) { | 224 ScheduledProcess _runTest(List<String> args) => |
261 new Directory(_tempDir).create(); | 225 runTest(args, environment: {"_UNITTEST_TEMP_DIR": _tempDir}); |
262 return startTest(args, workingDirectory: _sandbox, | 226 |
263 environment: {"_UNITTEST_TEMP_DIR": _tempDir}); | 227 void signalAndQuit(ScheduledProcess test) { |
| 228 test.signal(ProcessSignal.SIGTERM); |
| 229 test.shouldExit(); |
| 230 test.stderr.expect(isDone); |
264 } | 231 } |
| 232 |
| 233 void expectTempDirEmpty() { |
| 234 schedule(() => expect(new Directory(_tempDir).listSync(), isEmpty)); |
| 235 } |
OLD | NEW |