| Index: tools/gardening/lib/src/buildbot_structures.dart
|
| diff --git a/tools/gardening/lib/src/buildbot_structures.dart b/tools/gardening/lib/src/buildbot_structures.dart
|
| index 8d86e4aeb00e69c6eeddbd97c52f77ea20534cc5..eee03a5290b06087c9f1aa946f189e684785d62e 100644
|
| --- a/tools/gardening/lib/src/buildbot_structures.dart
|
| +++ b/tools/gardening/lib/src/buildbot_structures.dart
|
| @@ -31,6 +31,10 @@ class BuildUri {
|
| BuildUri.internal(this.scheme, this.host, this.prefix, this.botName,
|
| this.buildNumber, this.stepName, this.suffix);
|
|
|
| + BuildUri withBuildNumber(int buildNumber) {
|
| + return new BuildUri.fromData(botName, buildNumber, stepName);
|
| + }
|
| +
|
| String get shortBuildName => '$botName/$stepName';
|
|
|
| String get buildName =>
|
| @@ -38,6 +42,18 @@ class BuildUri {
|
|
|
| String get path => '$prefix$buildName/logs/$suffix';
|
|
|
| + /// Returns the path used in logdog for this build uri.
|
| + ///
|
| + /// Since logdog only supports absolute build numbers, [buildNumber] must be
|
| + /// non-negative. A [StateError] is thrown, otherwise.
|
| + String get logdogPath {
|
| + if (buildNumber < 0)
|
| + throw new StateError('BuildUri $buildName must have a non-negative build '
|
| + 'number to a valid logdog path.');
|
| + return 'chromium/bb/client.dart/$botName/$buildNumber/+/recipes/steps/'
|
| + '${stepName.replaceAll(' ', '_')}/0/stdout';
|
| + }
|
| +
|
| /// Creates the [Uri] for this build step stdio log.
|
| Uri toUri() {
|
| return new Uri(scheme: scheme, host: host, path: path);
|
| @@ -80,3 +96,133 @@ class TestConfiguration {
|
| testName == other.testName;
|
| }
|
| }
|
| +
|
| +/// The results of a build step.
|
| +class BuildResult {
|
| + final BuildUri _buildUri;
|
| +
|
| + /// The absolute build number, if found.
|
| + ///
|
| + /// The [buildUri] can be created with a relative build number, such as `-2`
|
| + /// which means the second-to-last build. The absolute build number, a
|
| + /// positive number, is read from the build results.
|
| + final int buildNumber;
|
| +
|
| + final List<TestStatus> _results;
|
| + final List<TestFailure> _failures;
|
| + final List<Timing> _timings;
|
| +
|
| + BuildResult(this._buildUri, this.buildNumber, this._results, this._failures,
|
| + this._timings);
|
| +
|
| + BuildUri get buildUri =>
|
| + buildNumber != null ? _buildUri.withBuildNumber(buildNumber) : _buildUri;
|
| +
|
| + /// `true` of the build result has test failures.
|
| + bool get hasFailures => _failures.isNotEmpty;
|
| +
|
| + /// Returns the top-20 timings found in the build log.
|
| + Iterable<Timing> get timings => _timings;
|
| +
|
| + /// Returns the [TestStatus] for all tests.
|
| + Iterable<TestStatus> get results => _results;
|
| +
|
| + /// Returns the [TestFailure]s for tests that timed out.
|
| + Iterable<TestFailure> get timeouts {
|
| + return _failures
|
| + .where((TestFailure failure) => failure.actual == 'Timeout');
|
| + }
|
| +
|
| + /// Returns the [TestFailure]s for failing tests that did not time out.
|
| + Iterable<TestFailure> get errors {
|
| + return _failures
|
| + .where((TestFailure failure) => failure.actual != 'Timeout');
|
| + }
|
| +
|
| + String toString() {
|
| + StringBuffer sb = new StringBuffer();
|
| + sb.write('$buildUri\n');
|
| + sb.write('Failures:\n${_failures.join('\n-----\n')}\n');
|
| + sb.write('\nTimings:\n${_timings.join('\n')}');
|
| + return sb.toString();
|
| + }
|
| +}
|
| +
|
| +/// Test failure data derived from the test failure summary in the build step
|
| +/// stdio log.
|
| +class TestFailure {
|
| + final BuildUri uri;
|
| + final TestConfiguration id;
|
| + final String expected;
|
| + final String actual;
|
| + final String text;
|
| +
|
| + factory TestFailure(BuildUri uri, List<String> lines) {
|
| + List<String> parts = split(lines.first, ['FAILED: ', ' ', ' ']);
|
| + String configName = parts[1];
|
| + String archName = parts[2];
|
| + String testName = parts[3];
|
| + TestConfiguration id =
|
| + new TestConfiguration(configName, archName, testName);
|
| + String expected = split(lines[1], ['Expected: '])[1];
|
| + String actual = split(lines[2], ['Actual: '])[1];
|
| + return new TestFailure.internal(
|
| + uri, id, expected, actual, lines.skip(3).join('\n'));
|
| + }
|
| +
|
| + TestFailure.internal(
|
| + this.uri, this.id, this.expected, this.actual, this.text);
|
| +
|
| + String toString() {
|
| + StringBuffer sb = new StringBuffer();
|
| + sb.write('FAILED: $id\n');
|
| + sb.write('Expected: $expected\n');
|
| + sb.write('Actual: $actual\n');
|
| + sb.write(text);
|
| + return sb.toString();
|
| + }
|
| +}
|
| +
|
| +/// Id for a single test step, for instance the compilation and run steps of
|
| +/// a test.
|
| +class TestStep {
|
| + final String stepName;
|
| + final TestConfiguration id;
|
| +
|
| + TestStep(this.stepName, this.id);
|
| +
|
| + String toString() {
|
| + return '$stepName - $id';
|
| + }
|
| +
|
| + int get hashCode => stepName.hashCode * 13 + id.hashCode * 17;
|
| +
|
| + bool operator ==(other) {
|
| + if (identical(this, other)) return true;
|
| + if (other is! TestStep) return false;
|
| + return stepName == other.stepName && id == other.id;
|
| + }
|
| +}
|
| +
|
| +/// The timing result for a single test step.
|
| +class Timing {
|
| + final BuildUri uri;
|
| + final String time;
|
| + final TestStep step;
|
| +
|
| + Timing(this.uri, this.time, this.step);
|
| +
|
| + String toString() {
|
| + return '$time - $step';
|
| + }
|
| +}
|
| +
|
| +/// The result of a single test for a single test step.
|
| +class TestStatus {
|
| + final TestConfiguration config;
|
| + final String status;
|
| +
|
| + TestStatus(this.config, this.status);
|
| +
|
| + String toString() => '$config: $status';
|
| +}
|
|
|