blob: ec760c55d658c7c1e42050a934e2c5c77710381e [file] [log] [blame]
// Copyright (c) 2018, 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.
// results.json and flaky.json parses.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
/// gsutil.py binary to use.
final gsutil = Platform.isWindows ? "gsutil.py.bat" : "gsutil.py";
/// Cloud storage location containing results.
const testResultsStoragePath = "gs://dart-test-results/builders";
/// Cloud storage location containing approved results.
const approvedResultsStoragePath =
"gs://dart-test-results-approved-results/builders";
/// Runs gsutil with the provided [arguments] and returns the standard output.
/// Returns null if the requested URL didn't exist.
Future<String> runGsutil(List<String> arguments) async {
final processResult = await Process.run(gsutil, arguments,
environment: {"DEPOT_TOOLS_UPDATE": "0"});
if (processResult.exitCode != 0) {
if (processResult.exitCode == 1 &&
processResult.stderr.contains("No URLs matched") ||
processResult.stderr.contains("One or more URLs matched no objects")) {
return null;
}
throw new Exception("Failed to run: $gsutil $arguments\n"
"exitCode: ${processResult.exitCode}\n"
"stdout:\n${processResult.stdout}\n"
"stderr:\n${processResult.stderr}");
}
return processResult.stdout;
}
/// Returns the contents of the provided cloud storage [path], or null if it
/// didn't exist.
Future<String> catGsutil(String path) => runGsutil(["cat", path]);
/// Returns the files and directories in the provided cloud storage [directory],
/// or null if it didn't exist.
Future<Iterable<String>> lsGsutil(String directory) async {
final contents = await runGsutil(["ls", directory]);
if (contents == null) {
return null;
}
return LineSplitter.split(contents).map((String path) {
final elements = path.split("/");
if (elements[elements.length - 1].isEmpty) {
return elements[elements.length - 2];
} else {
return elements[elements.length - 1];
}
});
}
/// Copies a file to or from cloud storage.
Future cpGsutil(String source, String destination) =>
runGsutil(["cp", source, destination]);
/// Copies a directory recursively to or from cloud strorage.
Future cpRecursiveGsutil(String source, String destination) =>
runGsutil(["-m", "cp", "-r", "-Z", source, destination]);
/// Lists the bots in cloud storage.
Future<Iterable<String>> listBots() => lsGsutil("$testResultsStoragePath");
/// Returns the cloud storage path for the [bot].
String botCloudPath(String bot) => "$testResultsStoragePath/$bot";
/// Returns the cloud storage path to the [build] on the [bot].
String buildCloudPath(String bot, String build) =>
"${botCloudPath(bot)}/$build";
/// Returns the cloud storage path to the [file] inside the [bot]'s directory.
String fileCloudPath(String bot, String file) => "${botCloudPath(bot)}/$file";
/// Reads the contents of the [file] inside the [bot]'s cloud storage.
Future<String> readFile(String bot, String file) =>
catGsutil(fileCloudPath(bot, file));
/// Returns the cloud storage path to the [file] inside the [build] on the
/// [bot].
String buildFileCloudPath(String bot, String build, String file) =>
"${buildCloudPath(bot, build)}/$file";
/// Reads the contents of the [file] inside the [build] in the [bot]'s cloud
/// storage.
Future<String> readBuildFile(String bot, String build, String file) =>
catGsutil(buildFileCloudPath(bot, build, file));
List<Map<String, dynamic>> parseResults(String contents) {
return LineSplitter.split(contents)
.map(jsonDecode)
.toList()
.cast<Map<String, dynamic>>();
}
Future<List<Map<String, dynamic>>> loadResults(String path) async {
final results = <Map<String, dynamic>>[];
final lines = new File(path)
.openRead()
.transform(utf8.decoder)
.transform(new LineSplitter());
await for (final line in lines) {
final Map<String, dynamic> map = jsonDecode(line);
results.add(map);
}
return results;
}
Map<String, Map<String, dynamic>> createResultsMap(
List<Map<String, dynamic>> results) =>
new Map<String, Map<String, dynamic>>.fromIterable(results,
key: (dynamic result) =>
"${result["configuration"]}:${result["name"]}");
Map<String, Map<String, dynamic>> parseResultsMap(String contents) =>
createResultsMap(parseResults(contents));
Future<Map<String, Map<String, dynamic>>> loadResultsMap(String path) async =>
createResultsMap(await loadResults(path));