blob: c791b9413b1da9eb5f5079973ed892d8cc90b242 [file] [log] [blame]
#!/usr/bin/env dart
// 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.
// Update the flakiness data with a set of fresh results.
import 'dart:convert';
import 'dart:io';
import 'package:args/args.dart';
import 'results.dart';
main(List<String> args) async {
final parser = new ArgParser();
parser.addFlag('help', help: 'Show the program usage.', negatable: false);
parser.addOption('input', abbr: 'i', help: "Input flakiness file.");
parser.addOption('output', abbr: 'o', help: "Output flakiness file.");
final options = parser.parse(args);
if (options["help"]) {
print("""
Usage: update_flakiness.dart [OPTION]... [RESULT-FILE]...
Update the flakiness data with a set of fresh results.
The options are as follows:
${parser.usage}""");
return;
}
final parameters = options.rest;
// Load the existing flakiness data, if any.
final data = options["input"] != null
? await loadResultsMap(options["input"])
: <String, Map<String, dynamic>>{};
// Incrementally update the flakiness data with each observed result.
for (final path in parameters) {
final results = await loadResults(path);
for (final result in results) {
final String configuration = result["configuration"];
final String name = result["name"];
final key = "$configuration:$name";
final Map<String, dynamic> testData =
data.putIfAbsent(key, () => <String, dynamic>{});
testData["configuration"] = configuration;
testData["name"] = name;
final outcomes = testData.putIfAbsent("outcomes", () => []);
if (!outcomes.contains(result["result"])) {
outcomes.add(result["result"]);
outcomes..sort();
}
if (testData["current"] == result["result"]) {
testData["current_counter"]++;
} else {
testData["current"] = result["result"];
testData["current_counter"] = 1;
}
// Remove this code once all files are updated
const occurrencesMisspelled = "occurences";
var occurrences = testData[occurrencesMisspelled];
if (occurrences != null) {
testData["occurrences"] = occurrences;
testData.remove(occurrencesMisspelled);
}
occurrences = testData.putIfAbsent("occurrences", () => <String, dynamic>{});
occurrences.putIfAbsent(result["result"], () => 0);
occurrences[result["result"]]++;
}
}
// Write out the new flakiness data, containing all the tests known to have
// multiple outcomes.
final sink = options["output"] != null
? new File(options["output"]).openWrite()
: stdout;
final keys = new List<String>.from(data.keys)..sort();
for (final key in keys) {
final testData = data[key];
if (testData["outcomes"].length < 2) continue;
// Forgive tests that have become deterministic again. If they flake less
// than once in a 100 (p<1%), then if they flake again, the probability of
// them getting past 5 runs of deflaking is 1%^5 = 0.00000001%.
// TODO(sortie): Transitional compatibility until all flaky.json files have
// this new field.
if (testData["current_counter"] != null &&
100 <= testData["current_counter"]) {
continue;
}
sink.writeln(jsonEncode(testData));
}
}