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 'package:string_scanner/string_scanner.dart'; | |
6 | |
7 /// A regular expression that matches text until a letter or whitespace. | |
8 /// | |
9 /// This is intended to scan through a number without actually encoding the full | |
10 /// Dart number grammar. It doesn't stop on "e" because that can be a component | |
11 /// of numbers. | |
12 final _untilUnit = new RegExp(r"[^a-df-z\s]+", caseSensitive: false); | |
13 | |
14 /// A regular expression that matches a time unit. | |
15 final _unit = new RegExp(r"([um]s|[dhms])", caseSensitive: false); | |
16 | |
17 /// A regular expression that matches a section of whitespace. | |
18 final _whitespace = new RegExp(r"\s+"); | |
19 | |
5 /// A class representing a modification to the default timeout for a test. | 20 /// A class representing a modification to the default timeout for a test. |
6 /// | 21 /// |
7 /// By default, a test will time out after 30 seconds. With [new Timeout], that | 22 /// By default, a test will time out after 30 seconds. With [new Timeout], that |
8 /// can be overridden entirely; with [new Timeout.factor], it can be scaled | 23 /// can be overridden entirely; with [new Timeout.factor], it can be scaled |
9 /// relative to the default. | 24 /// relative to the default. |
10 class Timeout { | 25 class Timeout { |
11 /// A constant indicating that a test should never time out. | 26 /// A constant indicating that a test should never time out. |
12 static const none = const Timeout._none(); | 27 static const none = const Timeout._none(); |
13 | 28 |
14 /// The timeout duration. | 29 /// The timeout duration. |
15 /// | 30 /// |
16 /// If set, this overrides the default duration entirely. It's `null` for | 31 /// If set, this overrides the default duration entirely. It's `null` for |
17 /// timeouts with a non-null [scaleFactor] and for [Timeout.none]. | 32 /// timeouts with a non-null [scaleFactor] and for [Timeout.none]. |
18 final Duration duration; | 33 final Duration duration; |
19 | 34 |
20 /// The timeout factor. | 35 /// The timeout factor. |
21 /// | 36 /// |
22 /// The default timeout will be multiplied by this to get the new timeout. | 37 /// The default timeout will be multiplied by this to get the new timeout. |
23 /// Thus a factor of 2 means that the test will take twice as long to time | 38 /// Thus a factor of 2 means that the test will take twice as long to time |
24 /// out, and a factor of 0.5 means that it will time out twice as quickly. | 39 /// out, and a factor of 0.5 means that it will time out twice as quickly. |
25 /// | 40 /// |
26 /// This is `null` for timeouts with a non-null [duration] and for | 41 /// This is `null` for timeouts with a non-null [duration] and for |
27 /// [Timeout.none]. | 42 /// [Timeout.none]. |
28 final num scaleFactor; | 43 final num scaleFactor; |
29 | 44 |
30 /// Declares an absolute timeout that overrides the default. | 45 /// Declares an absolute timeout that overrides the default. |
31 const Timeout(this.duration) | 46 const Timeout(this.duration) |
Lasse Reichstein Nielsen
2016/01/26 09:58:58
Nothing here prevents you from passing "null" as a
nweiz
2016/01/26 23:15:40
It needs to be const because it's used as an annot
| |
32 : scaleFactor = null; | 47 : scaleFactor = null; |
33 | 48 |
34 /// Declares a relative timeout that scales the default. | 49 /// Declares a relative timeout that scales the default. |
35 const Timeout.factor(this.scaleFactor) | 50 const Timeout.factor(this.scaleFactor) |
36 : duration = null; | 51 : duration = null; |
37 | 52 |
38 const Timeout._none() | 53 const Timeout._none() |
39 : scaleFactor = null, | 54 : scaleFactor = null, |
40 duration = null; | 55 duration = null; |
41 | 56 |
57 /// Parse the timeout from a user-provided string. | |
58 /// | |
59 /// This supports the following formats: | |
60 /// | |
61 /// * `Number "x"`, which produces a relative timeout with the given scale | |
62 /// factor. | |
63 /// | |
64 /// * `(Number ("d" | "h" | "m" | "s" | "ms" | "us") (" ")?)+`, which produces | |
65 /// an absolute timeout with the duration given by the sum of the given | |
66 /// units. | |
67 /// | |
68 /// * `"none"`, which produces [Timeout.none]. | |
69 /// | |
70 /// Throws a [FormatException] if [timeout] is not in a valid format | |
71 factory Timeout.parse(String timeout) { | |
Lasse Reichstein Nielsen
2016/01/26 09:58:59
We generally make "parse" a static function, not a
nweiz
2016/01/26 23:15:40
This is true in the SDK, but externally pretty muc
| |
72 var scanner = new StringScanner(timeout); | |
73 | |
74 // First check for the string "none". | |
75 if (scanner.scan("none")) { | |
76 scanner.expectDone(); | |
77 return Timeout.none; | |
78 } | |
79 | |
80 // Scan a number. This will be either a time unit or a scale factor. | |
81 scanner.expect(_untilUnit, name: "number"); | |
82 var number = double.parse(scanner.lastMatch[0]); | |
83 | |
84 // A number followed by "x" is a scale factor. | |
85 if (scanner.scan("x") || scanner.scan("X")) { | |
86 scanner.expectDone(); | |
87 return new Timeout.factor(number); | |
88 } | |
89 | |
90 // Parse time units until none are left. The condition is in the middle of | |
91 // the loop because we've already parsed the first number. | |
92 var microseconds = 0; | |
93 while (true) { | |
94 scanner.expect(_unit, name: "unit"); | |
95 microseconds += _microsecondsFor(number, scanner.lastMatch[0]); | |
96 | |
97 scanner.scan(_whitespace); | |
98 | |
99 // Scan the next number, if it's avaialble. | |
100 if (!scanner.scan(_untilUnit)) break; | |
101 number = double.parse(scanner.lastMatch[0]); | |
102 } | |
103 | |
104 scanner.expectDone(); | |
105 return new Timeout(new Duration(microseconds: microseconds.round())); | |
106 } | |
107 | |
108 static double _microsecondsFor(double number, String unit) { | |
109 switch (unit) { | |
110 case "d": return number * 24 * 60 * 60 * 1000000; | |
Lasse Reichstein Nielsen
2016/01/26 09:58:59
Maybe parenthesize (24 * 60 * 60 * 1000000).
nweiz
2016/01/26 23:15:40
I don't think that adds much.
| |
111 case "h": return number * 60 * 60 * 1000000; | |
112 case "m": return number * 60 * 1000000; | |
113 case "s": return number * 1000000; | |
114 case "ms": return number * 1000; | |
115 case "us": return number; | |
116 default: throw "Unknown unit $unit."; | |
Lasse Reichstein Nielsen
2016/01/26 09:58:58
Consider making this an error instead of just a st
nweiz
2016/01/26 23:15:39
Done.
| |
117 } | |
118 } | |
119 | |
42 /// Returns a new [Timeout] that merges [this] with [other]. | 120 /// Returns a new [Timeout] that merges [this] with [other]. |
43 /// | 121 /// |
44 /// If [other] declares a [duration], that takes precedence. Otherwise, this | 122 /// If [other] declares a [duration], that takes precedence. Otherwise, this |
45 /// timeout's [duration] or [factor] are multiplied by [other]'s [factor]. | 123 /// timeout's [duration] or [factor] are multiplied by [other]'s [factor]. |
46 Timeout merge(Timeout other) { | 124 Timeout merge(Timeout other) { |
Lasse Reichstein Nielsen
2016/01/26 09:58:58
"merge" sounds symmetrical, but this seems like it
nweiz
2016/01/26 23:15:40
I guess "compose" might be a more accurate name, b
| |
47 if (this == none || other == none) return none; | 125 if (this == none || other == none) return none; |
48 if (other.duration != null) return new Timeout(other.duration); | 126 if (other.duration != null) return new Timeout(other.duration); |
49 if (duration != null) return new Timeout(duration * other.scaleFactor); | 127 if (duration != null) return new Timeout(duration * other.scaleFactor); |
50 return new Timeout.factor(scaleFactor * other.scaleFactor); | 128 return new Timeout.factor(scaleFactor * other.scaleFactor); |
51 } | 129 } |
52 | 130 |
53 /// Returns a new [Duration] from applying [this] to [base]. | 131 /// Returns a new [Duration] from applying [this] to [base]. |
54 /// | 132 /// |
55 /// If this is [none], returns `null`. | 133 /// If this is [none], returns `null`. |
56 Duration apply(Duration base) { | 134 Duration apply(Duration base) { |
57 if (this == none) return null; | 135 if (this == none) return null; |
58 return duration == null ? base * scaleFactor : duration; | 136 return duration == null ? base * scaleFactor : duration; |
59 } | 137 } |
60 | 138 |
139 int get hashCode => duration.hashCode ^ 4 * scaleFactor.hashCode; | |
Lasse Reichstein Nielsen
2016/01/26 09:58:58
Any particular reason for the 4*? Consider somethi
nweiz
2016/01/26 23:15:39
Done.
| |
140 | |
141 bool operator==(other) => other is Timeout && other.duration == duration && | |
142 other.scaleFactor == scaleFactor; | |
143 | |
61 String toString() { | 144 String toString() { |
62 if (duration != null) return duration.toString(); | 145 if (duration != null) return duration.toString(); |
63 if (scaleFactor != null) return "${scaleFactor}x"; | 146 if (scaleFactor != null) return "${scaleFactor}x"; |
64 return "none"; | 147 return "none"; |
65 } | 148 } |
66 } | 149 } |
OLD | NEW |