| 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 |