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. |
(...skipping 17 matching lines...) Expand all Loading... |
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) { |
| 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 /// Returns the number of microseconds in [number] [unit]s. |
| 109 static double _microsecondsFor(double number, String unit) { |
| 110 switch (unit) { |
| 111 case "d": return number * 24 * 60 * 60 * 1000000; |
| 112 case "h": return number * 60 * 60 * 1000000; |
| 113 case "m": return number * 60 * 1000000; |
| 114 case "s": return number * 1000000; |
| 115 case "ms": return number * 1000; |
| 116 case "us": return number; |
| 117 default: throw new ArgumentError("Unknown unit $unit."); |
| 118 } |
| 119 } |
| 120 |
42 /// Returns a new [Timeout] that merges [this] with [other]. | 121 /// Returns a new [Timeout] that merges [this] with [other]. |
43 /// | 122 /// |
44 /// If [other] declares a [duration], that takes precedence. Otherwise, this | 123 /// [Timeout.none] takes precedence over everything. If timeout is |
45 /// timeout's [duration] or [factor] are multiplied by [other]'s [factor]. | 124 /// [Timeout.none] and [other] declares a [duration], that takes precedence. |
| 125 /// Otherwise, this timeout's [duration] or [factor] are multiplied by |
| 126 /// [other]'s [factor]. |
46 Timeout merge(Timeout other) { | 127 Timeout merge(Timeout other) { |
47 if (this == none || other == none) return none; | 128 if (this == none || other == none) return none; |
48 if (other.duration != null) return new Timeout(other.duration); | 129 if (other.duration != null) return new Timeout(other.duration); |
49 if (duration != null) return new Timeout(duration * other.scaleFactor); | 130 if (duration != null) return new Timeout(duration * other.scaleFactor); |
50 return new Timeout.factor(scaleFactor * other.scaleFactor); | 131 return new Timeout.factor(scaleFactor * other.scaleFactor); |
51 } | 132 } |
52 | 133 |
53 /// Returns a new [Duration] from applying [this] to [base]. | 134 /// Returns a new [Duration] from applying [this] to [base]. |
54 /// | 135 /// |
55 /// If this is [none], returns `null`. | 136 /// If this is [none], returns `null`. |
56 Duration apply(Duration base) { | 137 Duration apply(Duration base) { |
57 if (this == none) return null; | 138 if (this == none) return null; |
58 return duration == null ? base * scaleFactor : duration; | 139 return duration == null ? base * scaleFactor : duration; |
59 } | 140 } |
60 | 141 |
| 142 int get hashCode => duration.hashCode ^ 5 * scaleFactor.hashCode; |
| 143 |
| 144 bool operator==(other) => other is Timeout && other.duration == duration && |
| 145 other.scaleFactor == scaleFactor; |
| 146 |
61 String toString() { | 147 String toString() { |
62 if (duration != null) return duration.toString(); | 148 if (duration != null) return duration.toString(); |
63 if (scaleFactor != null) return "${scaleFactor}x"; | 149 if (scaleFactor != null) return "${scaleFactor}x"; |
64 return "none"; | 150 return "none"; |
65 } | 151 } |
66 } | 152 } |
OLD | NEW |