blob: 6a55fdc1b8b63bdc1bd68983fc742de07c79069e [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// 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 'dart:async';
import 'dart:io';
import 'cache_new.dart';
import 'logdog_rpc.dart';
import 'util.dart';
import 'buildbot_structures.dart';
import 'cache.dart';
const String BUILDBOT_BUILDNUMBER = ' BUILDBOT_BUILDNUMBER: ';
const String BUILDBOT_REVISION = ' BUILDBOT_REVISION: ';
/// Read the build result for [buildUri].
///
/// The data is loaded from the cache, if available, otherwise [read] is called
/// to fetch the data and stored in the cache afterwards.
Future<BuildResult> _readBuildResult(
BuildUri buildUri, Future<String> read()) async {
if (buildUri.buildNumber < 0) {
String text = await read();
BuildResult result = parseTestStepResult(buildUri, text);
if (result.buildNumber != null) {
cache.write(result.buildUri.logdogPath, text);
}
return result;
} else {
return parseTestStepResult(
buildUri, await cache.read(buildUri.logdogPath, read));
}
}
/// Fetches test data for [buildUri] through the buildbot stdio.
Future<BuildResult> readBuildResultFromHttp(
HttpClient client, BuildUri buildUri,
[Duration timeout]) {
Future<String> read() async {
Uri uri = buildUri.toUri();
log('Reading buildbot results: $uri');
return readUriAsText(client, uri, timeout);
}
return _readBuildResult(buildUri, read);
}
/// Fetches test data for [buildUri] through logdog.
///
/// The build number of [buildUri] most be non-negative.
Future<BuildResult> readBuildResultFromLogDog(BuildUri buildUri) {
LogdogRpc logdog = new LogdogRpc();
Future<String> read() {
log('Reading logdog results: $buildUri');
return logdog.get(BUILDER_PROJECT, buildUri.logdogPath, noCache()());
}
return _readBuildResult(buildUri, read);
}
/// Parses a test status line of the from
/// `Done <config-name> <arch-name> <test-name>: <status>`.
///
/// If [line] is not of the correct form, `null` is returned.
TestStatus parseTestStatus(String line) {
try {
List<String> parts = split(line, ['Done ', ' ', ' ', ': ']);
String testName = parts[3];
String configName = parts[1];
String archName = parts[2];
String status = parts[4];
return new TestStatus(
new TestConfiguration(configName, archName, testName), status);
} catch (_) {
return null;
}
}
/// Parses the [buildUri] test log and creates a [BuildResult] for it.
BuildResult parseTestStepResult(BuildUri buildUri, String text) {
log('Parsing results: $buildUri (${text.length} bytes)');
int buildNumber;
String buildRevision;
List<String> currentFailure;
bool parsingTimingBlock = false;
List<TestStatus> results = <TestStatus>[];
List<TestFailure> failures = <TestFailure>[];
List<Timing> timings = <Timing>[];
List<String> strings = text.split('\n');
for (String line in strings) {
if (line.startsWith(BUILDBOT_BUILDNUMBER)) {
buildNumber =
int.parse(line.substring(BUILDBOT_BUILDNUMBER.length).trim());
buildUri = buildUri.withBuildNumber(buildNumber);
}
if (line.startsWith(BUILDBOT_REVISION)) {
buildRevision = line.substring(BUILDBOT_REVISION.length).trim();
}
if (currentFailure != null) {
if (line.startsWith('Done ')) {
TestStatus status = parseTestStatus(line);
if (status != null) {
results.add(status);
failures.add(new TestFailure(buildUri, currentFailure));
currentFailure = null;
}
} else {
currentFailure.add(line);
}
} else if (line.startsWith('FAILED:')) {
currentFailure = <String>[];
currentFailure.add(line);
} else if (line.startsWith('Done ')) {
TestStatus status = parseTestStatus(line);
if (status != null) {
results.add(status);
}
}
if (line.startsWith('--- Total time:')) {
parsingTimingBlock = true;
} else if (parsingTimingBlock) {
if (line.startsWith('0:')) {
timings.addAll(parseTimings(buildUri, line));
} else {
parsingTimingBlock = false;
}
}
}
return new BuildResult(buildUri, buildNumber ?? buildUri.absoluteBuildNumber,
buildRevision, results, failures, timings);
}
/// Create the [Timing]s for the [line] as found in the top-20 timings of a
/// build step stdio log.
List<Timing> parseTimings(BuildUri uri, String line) {
List<String> parts = split(line, [' - ', ' - ', ' ']);
String time = parts[0];
String stepName = parts[1];
String configName = parts[2];
String testNames = parts[3];
List<Timing> timings = <Timing>[];
for (String name in testNames.split(',')) {
name = name.trim();
int slashPos = name.indexOf('/');
String archName = name.substring(0, slashPos);
String testName = name.substring(slashPos + 1);
timings.add(new Timing(
uri,
time,
new TestStep(
stepName, new TestConfiguration(configName, archName, testName))));
}
return timings;
}