| Index: lib/src/frontend/timeout.dart
|
| diff --git a/lib/src/frontend/timeout.dart b/lib/src/frontend/timeout.dart
|
| index 54ce540415dc2c9af0eda2eccc679c9950a1103e..9aca1c021102a00fe0c8bb2d08c7b38807f6463d 100644
|
| --- a/lib/src/frontend/timeout.dart
|
| +++ b/lib/src/frontend/timeout.dart
|
| @@ -2,6 +2,21 @@
|
| // for details. All rights reserved. Use of this source code is governed by a
|
| // BSD-style license that can be found in the LICENSE file.
|
|
|
| +import 'package:string_scanner/string_scanner.dart';
|
| +
|
| +/// A regular expression that matches text until a letter or whitespace.
|
| +///
|
| +/// This is intended to scan through a number without actually encoding the full
|
| +/// Dart number grammar. It doesn't stop on "e" because that can be a component
|
| +/// of numbers.
|
| +final _untilUnit = new RegExp(r"[^a-df-z\s]+", caseSensitive: false);
|
| +
|
| +/// A regular expression that matches a time unit.
|
| +final _unit = new RegExp(r"([um]s|[dhms])", caseSensitive: false);
|
| +
|
| +/// A regular expression that matches a section of whitespace.
|
| +final _whitespace = new RegExp(r"\s+");
|
| +
|
| /// A class representing a modification to the default timeout for a test.
|
| ///
|
| /// By default, a test will time out after 30 seconds. With [new Timeout], that
|
| @@ -39,10 +54,76 @@ class Timeout {
|
| : scaleFactor = null,
|
| duration = null;
|
|
|
| + /// Parse the timeout from a user-provided string.
|
| + ///
|
| + /// This supports the following formats:
|
| + ///
|
| + /// * `Number "x"`, which produces a relative timeout with the given scale
|
| + /// factor.
|
| + ///
|
| + /// * `(Number ("d" | "h" | "m" | "s" | "ms" | "us") (" ")?)+`, which produces
|
| + /// an absolute timeout with the duration given by the sum of the given
|
| + /// units.
|
| + ///
|
| + /// * `"none"`, which produces [Timeout.none].
|
| + ///
|
| + /// Throws a [FormatException] if [timeout] is not in a valid format
|
| + factory Timeout.parse(String timeout) {
|
| + var scanner = new StringScanner(timeout);
|
| +
|
| + // First check for the string "none".
|
| + if (scanner.scan("none")) {
|
| + scanner.expectDone();
|
| + return Timeout.none;
|
| + }
|
| +
|
| + // Scan a number. This will be either a time unit or a scale factor.
|
| + scanner.expect(_untilUnit, name: "number");
|
| + var number = double.parse(scanner.lastMatch[0]);
|
| +
|
| + // A number followed by "x" is a scale factor.
|
| + if (scanner.scan("x") || scanner.scan("X")) {
|
| + scanner.expectDone();
|
| + return new Timeout.factor(number);
|
| + }
|
| +
|
| + // Parse time units until none are left. The condition is in the middle of
|
| + // the loop because we've already parsed the first number.
|
| + var microseconds = 0;
|
| + while (true) {
|
| + scanner.expect(_unit, name: "unit");
|
| + microseconds += _microsecondsFor(number, scanner.lastMatch[0]);
|
| +
|
| + scanner.scan(_whitespace);
|
| +
|
| + // Scan the next number, if it's avaialble.
|
| + if (!scanner.scan(_untilUnit)) break;
|
| + number = double.parse(scanner.lastMatch[0]);
|
| + }
|
| +
|
| + scanner.expectDone();
|
| + return new Timeout(new Duration(microseconds: microseconds.round()));
|
| + }
|
| +
|
| + /// Returns the number of microseconds in [number] [unit]s.
|
| + static double _microsecondsFor(double number, String unit) {
|
| + switch (unit) {
|
| + case "d": return number * 24 * 60 * 60 * 1000000;
|
| + case "h": return number * 60 * 60 * 1000000;
|
| + case "m": return number * 60 * 1000000;
|
| + case "s": return number * 1000000;
|
| + case "ms": return number * 1000;
|
| + case "us": return number;
|
| + default: throw new ArgumentError("Unknown unit $unit.");
|
| + }
|
| + }
|
| +
|
| /// Returns a new [Timeout] that merges [this] with [other].
|
| ///
|
| - /// If [other] declares a [duration], that takes precedence. Otherwise, this
|
| - /// timeout's [duration] or [factor] are multiplied by [other]'s [factor].
|
| + /// [Timeout.none] takes precedence over everything. If timeout is
|
| + /// [Timeout.none] and [other] declares a [duration], that takes precedence.
|
| + /// Otherwise, this timeout's [duration] or [factor] are multiplied by
|
| + /// [other]'s [factor].
|
| Timeout merge(Timeout other) {
|
| if (this == none || other == none) return none;
|
| if (other.duration != null) return new Timeout(other.duration);
|
| @@ -58,6 +139,11 @@ class Timeout {
|
| return duration == null ? base * scaleFactor : duration;
|
| }
|
|
|
| + int get hashCode => duration.hashCode ^ 5 * scaleFactor.hashCode;
|
| +
|
| + bool operator==(other) => other is Timeout && other.duration == duration &&
|
| + other.scaleFactor == scaleFactor;
|
| +
|
| String toString() {
|
| if (duration != null) return duration.toString();
|
| if (scaleFactor != null) return "${scaleFactor}x";
|
|
|