| 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 import 'dart:async'; | 5 import 'dart:async'; |
| 6 | 6 |
| 7 import 'package:stack_trace/stack_trace.dart'; |
| 8 |
| 7 import '../frontend/timeout.dart'; | 9 import '../frontend/timeout.dart'; |
| 8 import '../utils.dart'; | 10 import '../utils.dart'; |
| 9 import 'group.dart'; | 11 import 'group.dart'; |
| 10 import 'group_entry.dart'; | 12 import 'group_entry.dart'; |
| 11 import 'invoker.dart'; | 13 import 'invoker.dart'; |
| 12 import 'metadata.dart'; | 14 import 'metadata.dart'; |
| 13 import 'test.dart'; | 15 import 'test.dart'; |
| 14 | 16 |
| 15 /// A class that manages the state of tests as they're declared. | 17 /// A class that manages the state of tests as they're declared. |
| 16 /// | 18 /// |
| 17 /// A nested tree of Declarers tracks the current group, set-up, and tear-down | 19 /// A nested tree of Declarers tracks the current group, set-up, and tear-down |
| 18 /// functions. Each Declarer in the tree corresponds to a group. This tree is | 20 /// functions. Each Declarer in the tree corresponds to a group. This tree is |
| 19 /// tracked by a zone-scoped "current" Declarer; the current declarer can be set | 21 /// tracked by a zone-scoped "current" Declarer; the current declarer can be set |
| 20 /// for a block using [Declarer.declare], and it can be accessed using | 22 /// for a block using [Declarer.declare], and it can be accessed using |
| 21 /// [Declarer.current]. | 23 /// [Declarer.current]. |
| 22 class Declarer { | 24 class Declarer { |
| 23 /// The parent declarer, or `null` if this corresponds to the root group. | 25 /// The parent declarer, or `null` if this corresponds to the root group. |
| 24 final Declarer _parent; | 26 final Declarer _parent; |
| 25 | 27 |
| 26 /// The name of the current test group, including the name of any parent | 28 /// The name of the current test group, including the name of any parent |
| 27 /// groups. | 29 /// groups. |
| 28 /// | 30 /// |
| 29 /// This is `null` if this is the root group. | 31 /// This is `null` if this is the root group. |
| 30 final String _name; | 32 final String _name; |
| 31 | 33 |
| 32 /// The metadata for this group, including the metadata of any parent groups | 34 /// The metadata for this group, including the metadata of any parent groups |
| 33 /// and of the test suite. | 35 /// and of the test suite. |
| 34 final Metadata _metadata; | 36 final Metadata _metadata; |
| 35 | 37 |
| 38 /// The stack trace for this group. |
| 39 final Trace _trace; |
| 40 |
| 36 /// The set-up functions to run for each test in this group. | 41 /// The set-up functions to run for each test in this group. |
| 37 final _setUps = new List<AsyncFunction>(); | 42 final _setUps = new List<AsyncFunction>(); |
| 38 | 43 |
| 39 /// The tear-down functions to run for each test in this group. | 44 /// The tear-down functions to run for each test in this group. |
| 40 final _tearDowns = new List<AsyncFunction>(); | 45 final _tearDowns = new List<AsyncFunction>(); |
| 41 | 46 |
| 42 /// The set-up functions to run once for this group. | 47 /// The set-up functions to run once for this group. |
| 43 final _setUpAlls = new List<AsyncFunction>(); | 48 final _setUpAlls = new List<AsyncFunction>(); |
| 44 | 49 |
| 50 /// The trace for the first call to [setUpAll]. |
| 51 /// |
| 52 /// All [setUpAll]s are run in a single logical test, so they can only have |
| 53 /// one trace. The first trace is most often correct, since the first |
| 54 /// [setUpAll] is always run and the rest are only run if that one succeeds. |
| 55 Trace _setUpAllTrace; |
| 56 |
| 45 /// The tear-down functions to run once for this group. | 57 /// The tear-down functions to run once for this group. |
| 46 final _tearDownAlls = new List<AsyncFunction>(); | 58 final _tearDownAlls = new List<AsyncFunction>(); |
| 47 | 59 |
| 60 /// The trace for the first call to [tearDownAll]. |
| 61 /// |
| 62 /// All [tearDownAll]s are run in a single logical test, so they can only have |
| 63 /// one trace. The first trace matches [_setUpAllTrace]. |
| 64 Trace _tearDownAllTrace; |
| 65 |
| 48 /// The children of this group, either tests or sub-groups. | 66 /// The children of this group, either tests or sub-groups. |
| 49 final _entries = new List<GroupEntry>(); | 67 final _entries = new List<GroupEntry>(); |
| 50 | 68 |
| 51 /// Whether [build] has been called for this declarer. | 69 /// Whether [build] has been called for this declarer. |
| 52 bool _built = false; | 70 bool _built = false; |
| 53 | 71 |
| 54 /// The current zone-scoped declarer. | 72 /// The current zone-scoped declarer. |
| 55 static Declarer get current => Zone.current[#test.declarer]; | 73 static Declarer get current => Zone.current[#test.declarer]; |
| 56 | 74 |
| 57 /// Creates a new declarer for the root group. | 75 /// Creates a new declarer for the root group. |
| 58 /// | 76 /// |
| 59 /// This is the implicit group that exists outside of any calls to `group()`. | 77 /// This is the implicit group that exists outside of any calls to `group()`. |
| 60 /// If [metadata] is passed, it's used as the metadata for the implicit root | 78 /// If [metadata] is passed, it's used as the metadata for the implicit root |
| 61 /// group. | 79 /// group. |
| 62 Declarer([Metadata metadata]) | 80 Declarer([Metadata metadata]) |
| 63 : this._(null, null, metadata == null ? new Metadata() : metadata); | 81 : this._(null, null, metadata == null ? new Metadata() : metadata, null); |
| 64 | 82 |
| 65 Declarer._(this._parent, this._name, this._metadata); | 83 Declarer._(this._parent, this._name, this._metadata, this._trace); |
| 66 | 84 |
| 67 /// Runs [body] with this declarer as [Declarer.current]. | 85 /// Runs [body] with this declarer as [Declarer.current]. |
| 68 /// | 86 /// |
| 69 /// Returns the return value of [body]. | 87 /// Returns the return value of [body]. |
| 70 declare(body()) => runZoned(body, zoneValues: {#test.declarer: this}); | 88 declare(body()) => runZoned(body, zoneValues: {#test.declarer: this}); |
| 71 | 89 |
| 72 /// Defines a test case with the given name and body. | 90 /// Defines a test case with the given name and body. |
| 73 void test(String name, body(), {String testOn, Timeout timeout, skip, | 91 void test(String name, body(), {String testOn, Timeout timeout, skip, |
| 74 Map<String, dynamic> onPlatform, tags}) { | 92 Map<String, dynamic> onPlatform, tags}) { |
| 75 _checkNotBuilt("test"); | 93 _checkNotBuilt("test"); |
| 76 | 94 |
| 77 var metadata = _metadata.merge(new Metadata.parse( | 95 var metadata = _metadata.merge(new Metadata.parse( |
| 78 testOn: testOn, timeout: timeout, skip: skip, onPlatform: onPlatform, | 96 testOn: testOn, timeout: timeout, skip: skip, onPlatform: onPlatform, |
| 79 tags: tags)); | 97 tags: tags)); |
| 80 | 98 |
| 81 _entries.add(new LocalTest(_prefix(name), metadata, () async { | 99 _entries.add(new LocalTest(_prefix(name), metadata, () async { |
| 82 // TODO(nweiz): It might be useful to throw an error here if a test starts | 100 // TODO(nweiz): It might be useful to throw an error here if a test starts |
| 83 // running while other tests from the same declarer are also running, | 101 // running while other tests from the same declarer are also running, |
| 84 // since they might share closurized state. | 102 // since they might share closurized state. |
| 85 | 103 |
| 86 await Invoker.current.waitForOutstandingCallbacks(() async { | 104 await Invoker.current.waitForOutstandingCallbacks(() async { |
| 87 await _runSetUps(); | 105 await _runSetUps(); |
| 88 await body(); | 106 await body(); |
| 89 }); | 107 }); |
| 90 await _runTearDowns(); | 108 await _runTearDowns(); |
| 91 })); | 109 }, trace: new Trace.current(2))); |
| 92 } | 110 } |
| 93 | 111 |
| 94 /// Creates a group of tests. | 112 /// Creates a group of tests. |
| 95 void group(String name, void body(), {String testOn, Timeout timeout, skip, | 113 void group(String name, void body(), {String testOn, Timeout timeout, skip, |
| 96 Map<String, dynamic> onPlatform, tags}) { | 114 Map<String, dynamic> onPlatform, tags}) { |
| 97 _checkNotBuilt("group"); | 115 _checkNotBuilt("group"); |
| 98 | 116 |
| 99 var metadata = _metadata.merge(new Metadata.parse( | 117 var metadata = _metadata.merge(new Metadata.parse( |
| 100 testOn: testOn, timeout: timeout, skip: skip, onPlatform: onPlatform, | 118 testOn: testOn, timeout: timeout, skip: skip, onPlatform: onPlatform, |
| 101 tags: tags)); | 119 tags: tags)); |
| 120 var trace = new Trace.current(2); |
| 102 | 121 |
| 103 // Don't load the tests for a skipped group. | 122 // Don't load the tests for a skipped group. |
| 104 if (metadata.skip) { | 123 if (metadata.skip) { |
| 105 _entries.add(new Group(name, [], metadata: metadata)); | 124 _entries.add(new Group(name, [], metadata: metadata, trace: trace)); |
| 106 return; | 125 return; |
| 107 } | 126 } |
| 108 | 127 |
| 109 var declarer = new Declarer._(this, _prefix(name), metadata); | 128 var declarer = new Declarer._(this, _prefix(name), metadata, trace); |
| 110 declarer.declare(body); | 129 declarer.declare(body); |
| 111 _entries.add(declarer.build()); | 130 _entries.add(declarer.build()); |
| 112 } | 131 } |
| 113 | 132 |
| 114 /// Returns [name] prefixed with this declarer's group name. | 133 /// Returns [name] prefixed with this declarer's group name. |
| 115 String _prefix(String name) => _name == null ? name : "$_name $name"; | 134 String _prefix(String name) => _name == null ? name : "$_name $name"; |
| 116 | 135 |
| 117 /// Registers a function to be run before each test in this group. | 136 /// Registers a function to be run before each test in this group. |
| 118 void setUp(callback()) { | 137 void setUp(callback()) { |
| 119 _checkNotBuilt("setUp"); | 138 _checkNotBuilt("setUp"); |
| 120 _setUps.add(callback); | 139 _setUps.add(callback); |
| 121 } | 140 } |
| 122 | 141 |
| 123 /// Registers a function to be run after each test in this group. | 142 /// Registers a function to be run after each test in this group. |
| 124 void tearDown(callback()) { | 143 void tearDown(callback()) { |
| 125 _checkNotBuilt("tearDown"); | 144 _checkNotBuilt("tearDown"); |
| 126 _tearDowns.add(callback); | 145 _tearDowns.add(callback); |
| 127 } | 146 } |
| 128 | 147 |
| 129 /// Registers a function to be run once before all tests. | 148 /// Registers a function to be run once before all tests. |
| 130 void setUpAll(callback()) { | 149 void setUpAll(callback()) { |
| 131 _checkNotBuilt("setUpAll"); | 150 _checkNotBuilt("setUpAll"); |
| 151 _setUpAllTrace ??= new Trace.current(2); |
| 132 _setUpAlls.add(callback); | 152 _setUpAlls.add(callback); |
| 133 } | 153 } |
| 134 | 154 |
| 135 /// Registers a function to be run once after all tests. | 155 /// Registers a function to be run once after all tests. |
| 136 void tearDownAll(callback()) { | 156 void tearDownAll(callback()) { |
| 137 _checkNotBuilt("tearDownAll"); | 157 _checkNotBuilt("tearDownAll"); |
| 158 _tearDownAllTrace ??= new Trace.current(2); |
| 138 _tearDownAlls.add(callback); | 159 _tearDownAlls.add(callback); |
| 139 } | 160 } |
| 140 | 161 |
| 141 /// Finalizes and returns the group being declared. | 162 /// Finalizes and returns the group being declared. |
| 142 Group build() { | 163 Group build() { |
| 143 _checkNotBuilt("build"); | 164 _checkNotBuilt("build"); |
| 144 | 165 |
| 145 _built = true; | 166 _built = true; |
| 146 return new Group(_name, _entries.toList(), | 167 return new Group(_name, _entries.toList(), |
| 147 metadata: _metadata, | 168 metadata: _metadata, |
| 169 trace: _trace, |
| 148 setUpAll: _setUpAll, | 170 setUpAll: _setUpAll, |
| 149 tearDownAll: _tearDownAll); | 171 tearDownAll: _tearDownAll); |
| 150 } | 172 } |
| 151 | 173 |
| 152 /// Throws a [StateError] if [build] has been called. | 174 /// Throws a [StateError] if [build] has been called. |
| 153 /// | 175 /// |
| 154 /// [name] should be the name of the method being called. | 176 /// [name] should be the name of the method being called. |
| 155 void _checkNotBuilt(String name) { | 177 void _checkNotBuilt(String name) { |
| 156 if (!_built) return; | 178 if (!_built) return; |
| 157 throw new StateError("Can't call $name() once tests have begun running."); | 179 throw new StateError("Can't call $name() once tests have begun running."); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 182 return Future.forEach(tearDowns, _errorsDontStopTest); | 204 return Future.forEach(tearDowns, _errorsDontStopTest); |
| 183 }); | 205 }); |
| 184 } | 206 } |
| 185 | 207 |
| 186 /// Returns a [Test] that runs the callbacks in [_setUpAll]. | 208 /// Returns a [Test] that runs the callbacks in [_setUpAll]. |
| 187 Test get _setUpAll { | 209 Test get _setUpAll { |
| 188 if (_setUpAlls.isEmpty) return null; | 210 if (_setUpAlls.isEmpty) return null; |
| 189 | 211 |
| 190 return new LocalTest(_prefix("(setUpAll)"), _metadata, () { | 212 return new LocalTest(_prefix("(setUpAll)"), _metadata, () { |
| 191 return Future.forEach(_setUpAlls, (setUp) => setUp()); | 213 return Future.forEach(_setUpAlls, (setUp) => setUp()); |
| 192 }); | 214 }, trace: _setUpAllTrace); |
| 193 } | 215 } |
| 194 | 216 |
| 195 /// Returns a [Test] that runs the callbacks in [_tearDownAll]. | 217 /// Returns a [Test] that runs the callbacks in [_tearDownAll]. |
| 196 Test get _tearDownAll { | 218 Test get _tearDownAll { |
| 197 if (_tearDownAlls.isEmpty) return null; | 219 if (_tearDownAlls.isEmpty) return null; |
| 198 | 220 |
| 199 return new LocalTest(_prefix("(tearDownAll)"), _metadata, () { | 221 return new LocalTest(_prefix("(tearDownAll)"), _metadata, () { |
| 200 return Invoker.current.unclosable(() { | 222 return Invoker.current.unclosable(() { |
| 201 return Future.forEach(_tearDownAlls.reversed, _errorsDontStopTest); | 223 return Future.forEach(_tearDownAlls.reversed, _errorsDontStopTest); |
| 202 }); | 224 }); |
| 203 }); | 225 }, trace: _tearDownAllTrace); |
| 204 } | 226 } |
| 205 | 227 |
| 206 /// Runs [body] with special error-handling behavior. | 228 /// Runs [body] with special error-handling behavior. |
| 207 /// | 229 /// |
| 208 /// Errors emitted [body] will still cause the current test to fail, but they | 230 /// Errors emitted [body] will still cause the current test to fail, but they |
| 209 /// won't cause it to *stop*. In particular, they won't remove any outstanding | 231 /// won't cause it to *stop*. In particular, they won't remove any outstanding |
| 210 /// callbacks registered outside of [body]. | 232 /// callbacks registered outside of [body]. |
| 211 Future _errorsDontStopTest(body()) { | 233 Future _errorsDontStopTest(body()) { |
| 212 var completer = new Completer(); | 234 var completer = new Completer(); |
| 213 | 235 |
| 214 Invoker.current.addOutstandingCallback(); | 236 Invoker.current.addOutstandingCallback(); |
| 215 Invoker.current.waitForOutstandingCallbacks(() { | 237 Invoker.current.waitForOutstandingCallbacks(() { |
| 216 new Future.sync(body).whenComplete(completer.complete); | 238 new Future.sync(body).whenComplete(completer.complete); |
| 217 }).then((_) => Invoker.current.removeOutstandingCallback()); | 239 }).then((_) => Invoker.current.removeOutstandingCallback()); |
| 218 | 240 |
| 219 return completer.future; | 241 return completer.future; |
| 220 } | 242 } |
| 221 } | 243 } |
| OLD | NEW |