Update tools and pkg/smith directories on analyzer-0.33 branch to match origin/master.
This should hopefully be sufficient to get bots passing on the
analyzer-0.33 branch.
Updated to origin/master as of c987c9e5a7e6f0c1158749e8f6cf694605ab48fb.
Change-Id: Icb24d30dcbb0aa525c0598973a1090385a2f3d73
Reviewed-on: https://dart-review.googlesource.com/c/86682
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/smith/lib/configuration.dart b/pkg/smith/lib/configuration.dart
index 9bd0e37..ea0b8d1 100644
--- a/pkg/smith/lib/configuration.dart
+++ b/pkg/smith/lib/configuration.dart
@@ -259,7 +259,6 @@
useAnalyzerCfe: boolOption("use-cfe"),
useAnalyzerFastaParser: boolOption("analyzer-use-fasta-parser"),
useBlobs: boolOption("use-blobs"),
- keepGeneratedFiles: boolOption("keep-generated-files"),
useDart2JSWithKernel: boolOption("dart2js-with-kernel"),
useDart2JSOldFrontEnd: boolOption("dart2js-old-frontend"),
useFastStartup: boolOption("fast-startup"),
@@ -315,9 +314,6 @@
// TODO(rnystrom): What is this?
final bool useBlobs;
- // Keep generated files (instead of deleting them).
- final bool keepGeneratedFiles;
-
// TODO(rnystrom): Remove these when Dart 1.0 is no longer supported.
final bool useDart2JSWithKernel;
final bool useDart2JSOldFrontEnd;
@@ -343,7 +339,6 @@
bool useAnalyzerCfe,
bool useAnalyzerFastaParser,
bool useBlobs,
- bool keepGeneratedFiles,
bool useDart2JSWithKernel,
bool useDart2JSOldFrontEnd,
bool useFastStartup,
@@ -362,7 +357,6 @@
useAnalyzerCfe = useAnalyzerCfe ?? false,
useAnalyzerFastaParser = useAnalyzerFastaParser ?? false,
useBlobs = useBlobs ?? false,
- keepGeneratedFiles = keepGeneratedFiles ?? false,
useDart2JSWithKernel = useDart2JSWithKernel ?? false,
useDart2JSOldFrontEnd = useDart2JSOldFrontEnd ?? false,
useFastStartup = useFastStartup ?? false,
@@ -390,7 +384,6 @@
useAnalyzerCfe == other.useAnalyzerCfe &&
useAnalyzerFastaParser == other.useAnalyzerFastaParser &&
useBlobs == other.useBlobs &&
- keepGeneratedFiles == other.keepGeneratedFiles &&
useDart2JSWithKernel == other.useDart2JSWithKernel &&
useDart2JSOldFrontEnd == other.useDart2JSOldFrontEnd &&
useFastStartup == other.useFastStartup &&
@@ -425,8 +418,7 @@
(useFastStartup ? 2048 : 0) ^
(useHotReload ? 4096 : 0) ^
(useHotReloadRollback ? 8192 : 0) ^
- (useSdk ? 16384 : 0) ^
- (keepGeneratedFiles ? 32768 : 0);
+ (useSdk ? 16384 : 0);
String toString() {
var buffer = new StringBuffer();
@@ -452,7 +444,6 @@
if (useAnalyzerCfe) fields.add("use-cfe");
if (useAnalyzerFastaParser) fields.add("analyzer-use-fasta-parser");
if (useBlobs) fields.add("use-blobs");
- if (keepGeneratedFiles) fields.add("keep-generated-files");
if (useDart2JSWithKernel) fields.add("dart2js-with-kernel");
if (useDart2JSOldFrontEnd) fields.add("dart2js-old-frontend");
if (useFastStartup) fields.add("fast-startup");
@@ -516,10 +507,6 @@
if (useBlobs || other.useBlobs) {
fields.add("useBlobs $useBlobs ${other.useBlobs}");
}
- if (keepGeneratedFiles || other.keepGeneratedFiles) {
- fields.add(
- "keepGeneratedFiles $keepGeneratedFiles ${other.keepGeneratedFiles}");
- }
if (useDart2JSWithKernel || other.useDart2JSWithKernel) {
fields.add("useDart2JSWithKernel "
"$useDart2JSWithKernel ${other.useDart2JSWithKernel}");
diff --git a/tools/FAKE_COMMITS b/tools/FAKE_COMMITS
index fbe07a6..14748a9 100644
--- a/tools/FAKE_COMMITS
+++ b/tools/FAKE_COMMITS
@@ -24,3 +24,6 @@
Trigger mirroring of github repository
Force build after DEPS format revert
Force build while trybots are broken, to check builders for brokenness.
+
+Analyzer branch commits:
+Force build on new analyzer-branch linux build with new workflow
\ No newline at end of file
diff --git a/tools/android/VERSION_LINUX_NDK b/tools/android/VERSION_LINUX_NDK
index 9fff8ff..4fac77b 100644
--- a/tools/android/VERSION_LINUX_NDK
+++ b/tools/android/VERSION_LINUX_NDK
@@ -1 +1 @@
-50bb526ee77fee88b9382d2bf48e7399751b98ae
+e626c47cb82a7439b0eda03ac6e0e9e1e41c6093
diff --git a/tools/android/VERSION_MACOSX_NDK b/tools/android/VERSION_MACOSX_NDK
index aac8005..bd19b05 100644
--- a/tools/android/VERSION_MACOSX_NDK
+++ b/tools/android/VERSION_MACOSX_NDK
@@ -1 +1 @@
-ed883dfaad6f27350eb3de426ccbf9abdc2943e8
+e8b6ecb5d15c4c4018a62b52aabc13e41b17df8f
diff --git a/tools/approve_results.dart b/tools/approve_results.dart
index d8adccd..616a27f 100755
--- a/tools/approve_results.dart
+++ b/tools/approve_results.dart
@@ -73,8 +73,30 @@
? loadResultsMap(path)
: <String, Map<String, dynamic>>{};
+/// Loads a log from logdog.
+Future<String> loadLog(String id, String step) async {
+ final logUrl = Uri.parse("https://logs.chromium.org/"
+ "logs/dart/buildbucket/cr-buildbucket.appspot.com/"
+ "$id/+/steps/$step?format=raw");
+ final client = new HttpClient();
+ final request =
+ await client.getUrl(logUrl).timeout(const Duration(seconds: 60));
+ final response = await request.close().timeout(const Duration(seconds: 60));
+ if (response.statusCode != HttpStatus.ok) {
+ throw new Exception("The log at $logUrl doesn't exist");
+ }
+ final contents = (await response
+ .transform(new Utf8Decoder())
+ .timeout(const Duration(seconds: 60))
+ .toList())
+ .join("");
+ client.close();
+ return contents;
+}
+
/// Loads the results from the bot.
-Future<List<Test>> loadResultsFromBot(String bot, ArgResults options) async {
+Future<List<Test>> loadResultsFromBot(String bot, ArgResults options,
+ Map<String, dynamic> changelistBuild) async {
if (options["verbose"]) {
print("Loading $bot...");
}
@@ -82,16 +104,28 @@
final tmpdir = await Directory.systemTemp.createTemp("approve_results.");
try {
// The 'latest' file contains the name of the latest build that we
- // should download.
- final build = await readFile(bot, "latest");
+ // should download. When preapproving a changelist, we instead find out
+ // which build the commit queue was rebased on.
+ final build = (changelistBuild != null
+ ? await loadLog(changelistBuild["id"],
+ "gsutil_find_latest_build/0/logs/raw_io.output_text_latest_/0")
+ : await readFile(bot, "latest"))
+ .trim();
// Asynchronously download the latest build and the current approved
- // results.
+ // results. Download try results from trybot try runs if preapproving.
+ final tryResults = <String, Map<String, dynamic>>{};
await Future.wait([
cpRecursiveGsutil(buildCloudPath(bot, build), tmpdir.path),
cpRecursiveGsutil(
"$approvedResultsStoragePath/$bot/approved_results.json",
"${tmpdir.path}/approved_results.json"),
+ new Future(() async {
+ if (changelistBuild != null) {
+ tryResults.addAll(parseResultsMap(await loadLog(
+ changelistBuild["id"], "test_results/0/logs/results.json/0")));
+ }
+ }),
]);
// Check the build was properly downloaded.
@@ -123,10 +157,58 @@
final result = results[key];
final approvedResult = approvedResults[key];
final flakiness = flaky[key];
+ // If preapproving results, allow new non-matching results that are
+ // different from the baseline. The approved results will be the current
+ // approved results, plus the difference between the tryrun's baseline and
+ // the tryrun's results.
+ if (tryResults.containsKey(key)) {
+ final tryResult = tryResults[key];
+ final wasFlake = flakiness != null &&
+ (flakiness["outcomes"] as List<dynamic>)
+ .contains(tryResult["result"]);
+ // Pick the try run result if the try result was not a flake and it's a
+ // non-matching result that's different than the approved result. If
+ // there is no approved result yet, use the latest result from the
+ // builder instead.
+ final baseResult = approvedResult ?? result;
+ if ((!wasFlake &&
+ !tryResult["matches"] &&
+ tryResult["result"] != result["result"])) {
+ // The approved_results.json format currently does not natively
+ // support preapproval, so preapproving turning one failure into
+ // another will turn the builder in question red until the CL lands.
+ if (!baseResult["matches"]) {
+ print("Warning: Preapproving changed failure modes will turn the "
+ "CI red until the CL is submitted: $bot: $key: "
+ "${baseResult["result"]} -> ${tryResult["result"]}");
+ }
+ result.clear();
+ result.addAll(tryResult);
+ } else {
+ if (approvedResult != null) {
+ result.clear();
+ result.addAll(approvedResult);
+ }
+ }
+ } else if (tryResults.isNotEmpty && approvedResult != null) {
+ result.clear();
+ result.addAll(approvedResult);
+ }
final name = result["name"];
final test = new Test(bot, name, result, approvedResult, flakiness);
tests.add(test);
}
+ // If preapproving and the CL has introduced new tests, add the new tests
+ // as well to the approved data.
+ final newTestKeys = new Set<String>.from(tryResults.keys)
+ .difference(new Set<String>.from(results.keys));
+ for (final key in newTestKeys) {
+ final result = tryResults[key];
+ final flakiness = flaky[key];
+ final name = result["name"];
+ final test = new Test(bot, name, result, null, flakiness);
+ tests.add(test);
+ }
if (options["verbose"]) {
print("Loaded $bot (${tests.length} tests).");
}
@@ -150,13 +232,18 @@
abbr: "n",
help: "Show changed results but don't approve.",
negatable: false);
+ parser.addOption("preapprove",
+ abbr: "p", help: "Preapprove the new failures in a gerrit CL.");
parser.addFlag("verbose",
abbr: "v", help: "Describe asynchronous operations.", negatable: false);
parser.addFlag("yes",
abbr: "y", help: "Approve the results.", negatable: false);
final options = parser.parse(args);
- if ((options["bot"].isEmpty && !options["list"]) || options["help"]) {
+ if ((options["preapprove"] == null &&
+ options["bot"].isEmpty &&
+ !options["list"]) ||
+ options["help"]) {
print("""
Usage: approve_results.dart [OPTION]...
List tests whose results are different from the previously approved results, and
@@ -218,12 +305,106 @@
return;
}
+ // Determine which builders have run for the changelist.
+ final changelistBuilds = <String, Map<String, dynamic>>{};
+ if (options["preapprove"] != null) {
+ if (options["verbose"]) {
+ print("Loading list of try runs...");
+ }
+ final gerritHost = "dart-review.googlesource.com";
+ final gerritProject = "sdk";
+ final prefix = "https://$gerritHost/c/$gerritProject/+/";
+ final gerrit = options["preapprove"];
+ if (!gerrit.startsWith(prefix)) {
+ stderr.writeln("error: $gerrit doesn't start with $prefix");
+ exitCode = 1;
+ return;
+ }
+ final components = gerrit.substring(prefix.length).split("/");
+ if (components.length != 2 ||
+ int.tryParse(components[0]) == null ||
+ int.tryParse(components[1]) == null) {
+ stderr.writeln("error: $gerrit must be in the form of "
+ "$prefix<changelist>/<patchset>");
+ exitCode = 1;
+ return;
+ }
+ final changelist = int.parse(components[0]);
+ final patchset = int.parse(components[1]);
+ final buildset = "buildset:patch/gerrit/$gerritHost/$changelist/$patchset";
+ final url = Uri.parse(
+ "https://cr-buildbucket.appspot.com/_ah/api/buildbucket/v1/search"
+ "?bucket=luci.dart.try"
+ "&tag=${Uri.encodeComponent(buildset)}"
+ "&fields=builds(id%2Ctags%2Cstatus%2Cstarted_ts)");
+ final client = new HttpClient();
+ final request =
+ await client.getUrl(url).timeout(const Duration(seconds: 30));
+ final response = await request.close().timeout(const Duration(seconds: 30));
+ if (response.statusCode != HttpStatus.ok) {
+ throw new Exception("Failed to request try runs for $gerrit");
+ }
+ final Map<String, dynamic> object = await response
+ .transform(new Utf8Decoder())
+ .transform(new JsonDecoder())
+ .first
+ .timeout(const Duration(seconds: 30));
+ client.close();
+ final builds = object["builds"];
+ if (builds == null) {
+ stderr.writeln(
+ "error: $prefix$changelist has no try runs for patchset $patchset");
+ exitCode = 1;
+ return;
+ }
+
+ // Prefer the newest completed build.
+ Map<String, dynamic> preferredBuild(
+ Map<String, dynamic> a, Map<String, dynamic> b) {
+ if (a != null && b == null) return a;
+ if (a == null && b != null) return b;
+ if (a != null && b != null) {
+ if (a["status"] == "COMPLETED" && b["status"] != "COMPLETED") return a;
+ if (a["status"] != "COMPLETED" && b["status"] == "COMPLETED") return b;
+ if (a["started_ts"] == null && b["started_ts"] != null) return a;
+ if (a["started_ts"] != null && b["started_ts"] == null) return b;
+ if (a["started_ts"] != null && b["started_ts"] != null) {
+ if (int.parse(a["started_ts"]) > int.parse(b["started_ts"])) return a;
+ if (int.parse(a["started_ts"]) < int.parse(b["started_ts"])) return b;
+ }
+ }
+ return b;
+ }
+
+ for (final build in builds) {
+ final tags = (build["tags"] as List<dynamic>).cast<String>();
+ final builder = tags
+ .firstWhere((tag) => tag.startsWith("builder:"))
+ .substring("builder:".length);
+ final ciBuilder = builder.replaceFirst(new RegExp("-try\$"), "");
+ if (!allBots.contains(ciBuilder)) {
+ continue;
+ }
+ changelistBuilds[ciBuilder] =
+ preferredBuild(changelistBuilds[ciBuilder], build);
+ }
+ if (options["verbose"]) {
+ print("Loaded list of try runs.");
+ }
+ }
+ final changelistBuilders = new Set<String>.from(changelistBuilds.keys);
+
// Select all the bots matching the glob patterns,
+ final finalBotList =
+ options["preapprove"] != null ? changelistBuilders : allBots;
+ final botPatterns = options["preapprove"] != null && options["bot"].isEmpty
+ ? ["*"]
+ : options["bot"];
final bots = new Set<String>();
- for (final botPattern in options["bot"]) {
+ for (final botPattern in botPatterns) {
final glob = new Glob(botPattern);
bool any = false;
- for (final bot in allBots) {
+ for (final bot in finalBotList) {
if (glob.matches(bot)) {
bots.add(bot);
any = true;
@@ -240,12 +421,28 @@
print("Selected bot: $bot");
}
+ // Error out if any of the requested try runs are incomplete.
+ bool anyIncomplete = false;
+ for (final bot in bots) {
+ if (options["preapprove"] != null &&
+ changelistBuilds[bot]["status"] != "COMPLETED") {
+ stderr.writeln("error: The try run for $bot isn't complete yet" +
+ changelistBuilds[bot]["status"]);
+ anyIncomplete = true;
+ }
+ }
+ if (anyIncomplete) {
+ exitCode = 1;
+ return;
+ }
+
// Load all the latest results for the selected bots, as well as flakiness
// data, and the set of currently approved results. Each bot's latest build
// is downloaded in parallel to make this phase faster.
final testListFutures = <Future>[];
for (final String bot in bots) {
- testListFutures.add(loadResultsFromBot(bot, options));
+ testListFutures
+ .add(loadResultsFromBot(bot, options, changelistBuilds[bot]));
}
// Collect all the tests from the synchronous downloads.
@@ -377,6 +574,10 @@
print("Note: Approving the failures will turn the "
"$botPlural green on the next commit.");
}
+ if (options["preapprove"] != null) {
+ print("Warning: Preapproval is currently not sticky and somebody else "
+ "approving before your CL has landed will undo your preapproval.");
+ }
while (true) {
stdout.write("Do you want to approve? (yes/no) [yes] ");
final line = stdin.readLineSync();
@@ -399,6 +600,18 @@
}
print("");
+ // Log who approved these results.
+ final username = Platform.environment["LOGNAME"] ??
+ Platform.environment["USER"] ??
+ Platform.environment["USERNAME"];
+ if (username == null || username == "") {
+ stderr.writeln("error: Your identity could not be established. "
+ "Please set one of the LOGNAME, USER, USERNAME environment variables.");
+ exitCode = 1;
+ return;
+ }
+ final now = new DateTime.now().toUtc().toIso8601String();
+
// Update approved_results.json for each bot with unapproved changes.
final outDirectory =
await Directory.systemTemp.createTemp("approved_results.");
@@ -414,10 +627,19 @@
print("Uploading approved results...");
final futures = <Future>[];
for (final String bot in unapprovedBots) {
- final testsList = testsForBots[bot];
+ Map<String, dynamic> approveData(Test test) {
+ final data = new Map<String, dynamic>.from(test.resultData);
+ if (!test.isApproved) {
+ data["approver"] = username;
+ data["approved_at"] = now;
+ }
+ return data;
+ }
+
+ final dataList = testsForBots[bot].map(approveData).toList();
final localPath = "${outDirectory.path}/$bot.json";
await new File(localPath).writeAsString(
- testsList.map((test) => jsonEncode(test.resultData) + "\n").join(""));
+ dataList.map((data) => jsonEncode(data) + "\n").join(""));
final remotePath =
"$approvedResultsStoragePath/$bot/approved_results.json";
futures.add(cpGsutil(localPath, remotePath)
diff --git a/tools/bots/compare_results.dart b/tools/bots/compare_results.dart
index e475e4c..f7c9efb 100755
--- a/tools/bots/compare_results.dart
+++ b/tools/bots/compare_results.dart
@@ -34,20 +34,23 @@
flaked = flakinessData != null &&
flakinessData["outcomes"].contains(map["result"]);
- String get key => "$name:$configuration";
+ String get key => "$configuration:$name";
}
class Event {
final Result before;
final Result after;
+ final Result approved;
- Event(this.before, this.after);
+ Event(this.before, this.after, this.approved);
bool get isNew => before == null;
bool get isNewPassing => before == null && after.matches;
bool get isNewFailing => before == null && !after.matches;
bool get changed => !unchanged;
bool get unchanged => before != null && before.outcome == after.outcome;
+ bool get isApproved => approved != null && approved.outcome == after.outcome;
+ bool get isUnapproved => !isApproved;
bool get remainedPassing => before.matches && after.matches;
bool get remainedFailing => !before.matches && !after.matches;
bool get flaked => after.flaked;
@@ -75,28 +78,41 @@
bool firstSection = true;
-bool search(String description, String searchFor, List<Event> events,
- ArgResults options) {
+bool search(
+ String description,
+ String searchForStatus,
+ String searchForApproval,
+ List<Event> events,
+ ArgResults options,
+ Map<String, Map<String, dynamic>> logs,
+ List<String> logSection) {
bool judgement = false;
bool beganSection = false;
int count = options["count"] != null ? int.parse(options["count"]) : null;
final configurations =
events.map((event) => event.after.configuration).toSet();
for (final event in events) {
- if (searchFor == "passing" &&
+ if (searchForStatus == "passing" &&
(event.after.flaked || !event.after.matches)) {
continue;
}
- if (searchFor == "flaky" && !event.after.flaked) {
+ if (searchForStatus == "flaky" && !event.after.flaked) {
continue;
}
- if (searchFor == "failing" && (event.after.flaked || event.after.matches)) {
+ if (searchForStatus == "failing" &&
+ (event.after.flaked || event.after.matches)) {
+ continue;
+ }
+ if (searchForApproval == "approved" && !event.isApproved) {
+ continue;
+ }
+ if (searchForApproval == "unapproved" && !event.isUnapproved) {
continue;
}
if (options["unchanged"] && !event.unchanged) continue;
if (options["changed"] && !event.changed) continue;
if (!beganSection) {
- if (options["human"]) {
+ if (options["human"] && !options["logs-only"]) {
if (!firstSection) {
print("");
}
@@ -124,30 +140,32 @@
break;
}
}
- if (options["human"]) {
- if (options["verbose"]) {
- String expected =
- after.matches ? "" : ", expected ${after.expectation}";
+ String output;
+ if (options["verbose"]) {
+ if (options["human"]) {
+ String expect = after.matches ? "" : ", expected ${after.expectation}";
if (before == null || before.outcome == after.outcome) {
- print("${name} ${event.description} "
- "(${event.after.outcome}${expected})");
+ output = "$name ${event.description} "
+ "(${event.after.outcome}${expect})";
} else {
- print("${name} ${event.description} "
- "(${event.before?.outcome} -> ${event.after.outcome}${expected})");
+ output = "$name ${event.description} "
+ "(${event.before?.outcome} -> ${event.after.outcome}${expect})";
}
} else {
- print(name);
- }
- } else {
- if (options["verbose"]) {
- print("$name "
- "${before?.outcome} ${after.outcome} "
+ output = "$name ${before?.outcome} ${after.outcome} "
"${before?.expectation} ${after.expectation} "
"${before?.matches} ${after.matches} "
- "${before?.flaked} ${after.flaked}");
- } else {
- print(name);
+ "${before?.flaked} ${after.flaked}";
}
+ } else {
+ output = name;
+ }
+ if (logs != null) {
+ final log = logs[event.after.key];
+ if (log != null) logSection?.add("\n\nLog for $output\n${log["log"]}");
+ }
+ if (!options["logs-only"]) {
+ print(output);
}
}
@@ -156,6 +174,8 @@
main(List<String> args) async {
final parser = new ArgParser();
+ parser.addFlag("approved",
+ abbr: 'A', negatable: false, help: "Show approved tests.");
parser.addFlag("changed",
abbr: 'c',
negatable: false,
@@ -180,6 +200,8 @@
negatable: false);
parser.addFlag("passing",
abbr: 'p', negatable: false, help: "Show passing tests.");
+ parser.addFlag("unapproved",
+ abbr: 'U', negatable: false, help: "Show unapproved tests.");
parser.addFlag("unchanged",
abbr: 'u',
negatable: false,
@@ -188,12 +210,18 @@
abbr: "v",
help: "Show the old and new result for each test",
negatable: false);
+ parser.addOption("logs",
+ abbr: "l", help: "Path to file holding logs of failing tests.");
+ parser.addFlag("logs-only",
+ help: "Only print logs of failing tests, no other output",
+ negatable: false);
final options = parser.parse(args);
if (options["help"]) {
print("""
-Usage: compare_results.dart [OPTION]... [BEFORE] [AFTER]
+Usage: compare_results.dart [OPTION]... BEFORE AFTER [APPROVED]
Compare the old and new test results and list tests that pass the filters.
+Three-way compare with the approved results if provided.
All tests are listed if no filters are given.
The options are as follows:
@@ -210,8 +238,9 @@
}
final parameters = options.rest;
- if (parameters.length != 2) {
- print("error: Expected two parameters (results before and results after)");
+ if (parameters.length != 2 && parameters.length != 3) {
+ print("error: Expected two or three parameters "
+ "(results before, results after, and (optionally) approved results)");
exitCode = 2;
return;
}
@@ -219,6 +248,12 @@
// Load the input and the flakiness data if specified.
final before = await loadResultsMap(parameters[0]);
final after = await loadResultsMap(parameters[1]);
+ final approved = 3 <= parameters.length
+ ? await loadResultsMap(parameters[2])
+ : <String, Map<String, dynamic>>{};
+ final logs = options['logs'] == null
+ ? <String, Map<String, dynamic>>{}
+ : await loadResultsMap(options['logs']);
final flakinessData = options["flakiness-data"] != null
? await loadResultsMap(options["flakiness-data"])
: <String, Map<String, dynamic>>{};
@@ -230,76 +265,112 @@
for (final name in names) {
final mapBefore = before[name];
final mapAfter = after[name];
+ final mapApproved = approved[name];
final resultBefore = mapBefore != null
? new Result.fromMap(mapBefore, flakinessData[name])
: null;
final resultAfter = new Result.fromMap(mapAfter, flakinessData[name]);
- final event = new Event(resultBefore, resultAfter);
+ final resultApproved = mapApproved != null
+ ? new Result.fromMap(mapApproved, flakinessData[name])
+ : null;
+ final event = new Event(resultBefore, resultAfter, resultApproved);
events.add(event);
}
+ final filterDescriptions = {
+ "passing": {
+ "unchanged": "continued to pass",
+ "changed": "began passing",
+ null: "passed",
+ },
+ "flaky": {
+ "unchanged": "are known to flake but didn't",
+ "changed": "flaked",
+ null: "are known to flake",
+ },
+ "failing": {
+ "unchanged": "continued to fail",
+ "changed": "began failing",
+ null: "failed",
+ },
+ null: {
+ "unchanged": "had the same result",
+ "changed": "changed result",
+ null: "ran",
+ },
+ };
+
+ final searchForStatuses =
+ ["passing", "flaky", "failing"].where((option) => options[option]);
+
+ final approvalDescriptions = {
+ "passing": {
+ "approved": " (approved)",
+ "unapproved": " (should be approved)",
+ null: "",
+ },
+ "flaky": {
+ "approved": " (approved result)",
+ "unapproved": " (unapproved result)",
+ null: "",
+ },
+ "failing": {
+ "approved": " (approved)",
+ "unapproved": " (needs approval)",
+ null: "",
+ },
+ null: {
+ "approved": " (approved)",
+ "unapproved": " (needs approval)",
+ null: "",
+ },
+ };
+
+ final searchForApprovals =
+ ["approved", "unapproved"].where((option) => options[option]);
+
// Report tests matching the filters.
+ final logSection = <String>[];
bool judgement = false;
- if (options["passing"] || options["flaky"] || options["failing"]) {
- if (options["passing"]) {
- String sectionHeader;
- if (options["unchanged"]) {
- sectionHeader = "The following tests continued to pass:";
- } else if (options["changed"]) {
- sectionHeader = "The following tests began passing:";
- } else {
- sectionHeader = "The following tests passed:";
+ for (final searchForStatus
+ in searchForStatuses.isNotEmpty ? searchForStatuses : <String>[null]) {
+ for (final searchForApproval in searchForApprovals.isNotEmpty
+ ? searchForApprovals
+ : <String>[null]) {
+ final searchForChanged = options["unchanged"]
+ ? "unchanged"
+ : options["changed"] ? "changed" : null;
+ final aboutStatus = filterDescriptions[searchForStatus][searchForChanged];
+ final aboutApproval =
+ approvalDescriptions[searchForStatus][searchForApproval];
+ final sectionHeader = "The following tests $aboutStatus$aboutApproval:";
+ final logSectionArg = searchForStatus == "failing" ? logSection : null;
+ bool possibleJudgement = search(sectionHeader, searchForStatus,
+ searchForApproval, events, options, logs, logSectionArg);
+ if ((searchForStatus == null || searchForStatus == "failing") &&
+ (searchForApproval == null || searchForApproval == "unapproved")) {
+ judgement = possibleJudgement;
}
- search(sectionHeader, "passing", events, options);
}
- if (options["flaky"]) {
- String sectionHeader;
- if (options["unchanged"]) {
- sectionHeader = "The following tests are known to flake but didn't:";
- } else if (options["changed"]) {
- sectionHeader = "The following tests flaked:";
- } else {
- sectionHeader = "The following tests are known to flake:";
- }
- search(sectionHeader, "flaky", events, options);
- }
- if (options["failing"]) {
- String sectionHeader;
- if (options["unchanged"]) {
- sectionHeader = "The following tests continued to fail:";
- } else if (options["changed"]) {
- sectionHeader = "The following tests began failing:";
- } else {
- sectionHeader = "The following tests failed:";
- }
- judgement = search(sectionHeader, "failing", events, options);
- }
- } else {
- String sectionHeader;
- if (options["unchanged"]) {
- sectionHeader = "The following tests had the same result:";
- } else if (options["changed"]) {
- sectionHeader = "The following tests changed result:";
- } else {
- sectionHeader = "The following tests ran:";
- }
- judgement = search(sectionHeader, null, events, options);
}
+ if (logSection.isNotEmpty) {
+ print(logSection.join());
+ }
// Exit 1 only if --judgement and any test failed.
if (options["judgement"]) {
- if (options["human"] && !firstSection) {
+ if (options["human"] && !options["logs-only"] && !firstSection) {
print("");
}
String oldNew =
options["unchanged"] ? "old " : options["changed"] ? "new " : "";
if (judgement) {
- if (options["human"]) {
+ if (options["human"] && !options["logs-only"]) {
print("There were ${oldNew}test failures.");
}
exitCode = 1;
} else {
- if (options["human"]) {
+ if (options["human"] && !options["logs-only"]) {
print("No ${oldNew}test failures were found.");
}
}
diff --git a/tools/bots/results.dart b/tools/bots/results.dart
index 36b27f2..8405a6d 100644
--- a/tools/bots/results.dart
+++ b/tools/bots/results.dart
@@ -115,12 +115,7 @@
Map<String, Map<String, dynamic>> createResultsMap(
List<Map<String, dynamic>> results) =>
- new Map<String, Map<String, dynamic>>.fromIterable(
- results
- // TODO: Temporarily discard results in the old flaky.json format
- // This can be removed once every bot has run once after this commit
- // has landed, purging all old flakiness information.
- .where((result) => result["configuration"] != null),
+ new Map<String, Map<String, dynamic>>.fromIterable(results,
key: (dynamic result) =>
"${result["configuration"]}:${result["name"]}");
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 70fdc8d..38bf229 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -98,6 +98,7 @@
"third_party/observatory_pub_packages/packages/",
"tools/sdks/dart-sdk/",
"pkg/async_helper/",
+ "pkg/build_integration/",
"pkg/dart_internal/",
"pkg/expect/",
"pkg/front_end/",
@@ -150,6 +151,7 @@
"tests/standalone/",
"tests/standalone_2/",
"pkg/async_helper/",
+ "pkg/build_integration/",
"pkg/dart_internal/",
"pkg/expect/",
"pkg/front_end/",
@@ -347,12 +349,6 @@
"compiler": "dart2analyzer",
"enable-asserts": true,
"use-sdk": true
- }},
- "analyzer-fasta_parser-linux": {
- "options": {
- "compiler": "dart2analyzer",
- "analyzer-use-fasta-parser": true,
- "use-sdk": true
}}
},
"builder_configurations": [
@@ -425,7 +421,6 @@
"name": "vm mixed mode tests",
"arguments": [
"-ndartkb-mixed-linux-${mode}-x64",
- "--compiler=dartkb",
"language_2",
"corelib_2",
"lib_2",
@@ -438,7 +433,6 @@
"name": "vm bytecode compiler tests",
"arguments": [
"-ndartkb-compile-linux-${mode}-x64",
- "--compiler=dartkb",
"language_2",
"corelib_2",
"lib_2",
@@ -451,7 +445,6 @@
"name": "vm interpreter tests",
"arguments": [
"-ndartkb-interpret-linux-${mode}-x64",
- "--compiler=dartkb",
"language_2",
"corelib_2",
"lib_2",
@@ -1325,13 +1318,17 @@
"steps": [
{
"name": "build dart",
- "script": "tools/build.py",
- "arguments": ["--arch=ia32,x64", "create_sdk" ]
+ "script": "tools/bots/dart_sdk.py",
+ "arguments": [],
+ "environment": {"BUILDBOT_BUILDERNAME": "dart-sdk-linux"}
+
},
{
- "name": "generate API docs",
+ "name": "build api docs",
"script": "tools/bots/dart_sdk.py",
- "arguments": [ "api_docs" ]
+ "arguments": [ "api_docs" ],
+ "environment": {"BUILDBOT_BUILDERNAME": "dart-sdk-linux"}
+
}
]
},
@@ -1343,8 +1340,10 @@
"steps": [
{
"name": "build dart",
- "script": "tools/build.py",
- "arguments": ["--arch=ia32,x64", "create_sdk" ]
+ "script": "tools/bots/dart_sdk.py",
+ "arguments": [],
+ "environment": {"BUILDBOT_BUILDERNAME": "dart-sdk-mac"}
+
}
]
},
@@ -1356,8 +1355,10 @@
"steps": [
{
"name": "build dart",
- "script": "tools/build.py",
- "arguments": ["--arch=ia32,x64", "create_sdk" ]
+ "script": "tools/bots/dart_sdk.py",
+ "arguments": [],
+ "environment": {"BUILDBOT_BUILDERNAME": "dart-sdk-win"}
+
}
]
},
@@ -1365,7 +1366,11 @@
"builders": [
"analyzer-linux-release",
"analyzer-mac-release",
- "analyzer-win-release"
+ "analyzer-win-release",
+ "analyzer-linux-release-analyzer",
+ "analyzer-linux-release-analyzer-new",
+ "analyzer-mac-release-analyzer",
+ "analyzer-win-release-analyzer"
],
"meta": {
"description": "This configuration is used by the analyzer builders."
@@ -1430,7 +1435,10 @@
]
},
{
- "builders": ["analyzer-analysis-server-linux"],
+ "builders": [
+ "analyzer-analysis-server-linux",
+ "analyzer-analysis-server-linux-analyzer"
+ ],
"meta": {
"description": "Analyze analyzer related packages."
},
@@ -1596,74 +1604,6 @@
]
},
{
- "builders": ["analyzer-use-fasta-parser-linux"],
- "meta": {
- "description": "Run the analyzer using the Fasta parser."
- },
- "steps": [
- {
- "name": "build dart",
- "script": "tools/build.py",
- "arguments": ["create_sdk"]
- },
- {
- "name": "analyze pkg/analyzer --use-fasta-parser",
- "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer",
- "arguments": [
- "--use-fasta-parser",
- "pkg/analyzer"
- ]
- },
- {
- "name": "analyze pkg/analyzer --no-use-fasta-parser",
- "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer",
- "arguments": [
- "--no-use-fasta-parser",
- "pkg/analyzer"
- ]
- },
- {
- "name": "analyze pkg/analyzer_cli --use-fasta-parser",
- "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer",
- "arguments": [
- "--use-fasta-parser",
- "pkg/analyzer_cli"
- ]
- },
- {
- "name": "run language2 tests",
- "arguments": [
- "-nanalyzer-fasta_parser-linux",
- "language_2"
- ]
- },
- {
- "name": "analyze pkg/analyzer_cli --no-use-fasta-parser",
- "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer",
- "arguments": [
- "--no-use-fasta-parser",
- "pkg/analyzer_cli"
- ]
- },
- {
- "name": "analyze pkg/analysis_server --use-fasta-parser",
- "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer",
- "arguments": [
- "--use-fasta-parser",
- "pkg/analysis_server"
- ]
- },
- {
- "name": "analyze pkg/analysis_server --no-use-fasta-parser",
- "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer",
- "arguments": [
- "--no-use-fasta-parser",
- "pkg/analysis_server"
- ]
- }
- ]
- },
- {
"builders": ["pkg-linux-release",
"pkg-win-release",
"pkg-mac-release"],
@@ -1795,7 +1735,7 @@
"--time",
"2700"
],
- "shards": 10,
+ "shards": 100,
"fileset": "fuzzer"
}
]
diff --git a/tools/bots/update_flakiness.dart b/tools/bots/update_flakiness.dart
index 87c584b..fb7130c 100755
--- a/tools/bots/update_flakiness.dart
+++ b/tools/bots/update_flakiness.dart
@@ -51,6 +51,16 @@
outcomes.add(result["result"]);
outcomes..sort();
}
+ if (testData["current"] == result["result"]) {
+ testData["current_counter"]++;
+ } else {
+ testData["current"] = result["result"];
+ testData["current_counter"] = 1;
+ }
+ final occurences =
+ testData.putIfAbsent("occurences", () => <String, dynamic>{});
+ occurences.putIfAbsent(result["result"], () => 0);
+ occurences[result["result"]]++;
}
}
@@ -63,6 +73,15 @@
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));
}
}
diff --git a/tools/buildtools/README.md b/tools/buildtools/README.md
index 8cd1cfb..7d79285 100644
--- a/tools/buildtools/README.md
+++ b/tools/buildtools/README.md
@@ -1,7 +1,7 @@
To build Dart for Mac and Linux, we pull Fuchsia's buildtools, which is also
-used by Flutter. Fuchsia's buildtools includes gn, ninja, and the clang
-toolchain. Fuchsia buildtools vends clang-format as part of the clang toolchain.
-Since Fuchsia's buildtools doesn't vend a clang toolchain for Windows, we can't
+used by Flutter. Fuchsia's buildtools includes gn, and the clang toolchain.
+Fuchsia buildtools vends clang-format as part of the clang toolchain. Since
+Fuchsia's buildtools doesn't vend a clang toolchain for Windows, we can't
get a Windows clang-format binary from it. Therefore, from Chromium's buildtools
here:
diff --git a/tools/buildtools/update.py b/tools/buildtools/update.py
index 5cae6a8..936e729 100755
--- a/tools/buildtools/update.py
+++ b/tools/buildtools/update.py
@@ -5,10 +5,10 @@
"""Pulls down tools required to build Dart."""
+import errno
import os
import platform
import subprocess
-import shutil
import sys
THIS_DIR = os.path.abspath(os.path.dirname(__file__))
@@ -22,34 +22,6 @@
DEPOT_PATH = find_depot_tools.add_depot_tools_to_path()
-def Update():
- path = os.path.join(BUILDTOOLS, 'update.sh')
- command = ['/bin/bash', path, '--clang', '--gn']
- return subprocess.call(command, cwd=DART_ROOT)
-
-
-def UpdateGNOnWindows():
- sha1_file = os.path.join(TOOLS_BUILDTOOLS, 'win', 'gn.exe.sha1')
- output_dir = os.path.join(BUILDTOOLS, 'win', 'gn.exe')
- downloader_script = os.path.join(
- DEPOT_PATH, 'download_from_google_storage.py')
- download_cmd = [
- 'python',
- downloader_script,
- '--no_auth',
- '--no_resume',
- '--quiet',
- '--platform=win*',
- '--bucket',
- 'chromium-gn',
- '-s',
- sha1_file,
- '-o',
- output_dir
- ]
- return subprocess.call(download_cmd)
-
-
def UpdateClangFormatOnWindows():
sha1_file = os.path.join(TOOLS_BUILDTOOLS, 'win', 'clang-format.exe.sha1')
output_dir = os.path.join(BUILDTOOLS, 'win', 'clang-format.exe')
@@ -72,9 +44,20 @@
return subprocess.call(download_cmd)
-# On Mac and Linux we copy clang-format and gn to the place where git cl format
-# expects them to be.
-def CopyClangFormat():
+def CreateSymlink(symlink, link_name):
+ try:
+ os.symlink(symlink, link_name)
+ except OSError, e:
+ if e.errno == errno.EEXIST:
+ os.remove(link_name)
+ os.symlink(symlink, link_name)
+ else:
+ raise e
+
+
+# On Mac and Linux we symlink clang-format and gn to the place where
+# 'git cl format' expects them to be.
+def LinksForGitCLFormat():
if sys.platform == 'darwin':
platform = 'darwin'
tools = 'mac'
@@ -89,14 +72,14 @@
clang_format = os.path.join(
BUILDTOOLS, toolchain, 'clang', 'bin', 'clang-format')
- gn = os.path.join(BUILDTOOLS, toolchain, 'gn')
+ gn = os.path.join(BUILDTOOLS, 'gn')
dest_dir = os.path.join(BUILDTOOLS, tools)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
clang_format_dest = os.path.join(dest_dir, 'clang-format')
gn_dest = os.path.join(dest_dir, 'gn')
- shutil.copy2(clang_format, clang_format_dest)
- shutil.copy2(gn, gn_dest)
+ CreateSymlink(clang_format, clang_format_dest)
+ CreateSymlink(gn, gn_dest)
return 0
@@ -107,13 +90,8 @@
print('Not downloading buildtools binaries for ' + arch_id)
return 0
if sys.platform.startswith('win'):
- result = UpdateGNOnWindows()
- if result != 0:
- return result
return UpdateClangFormatOnWindows()
- if Update() != 0:
- return 1
- return CopyClangFormat()
+ return LinksForGitCLFormat()
if __name__ == '__main__':
diff --git a/tools/generate_compile_commands.py b/tools/generate_compile_commands.py
new file mode 100755
index 0000000..b94885d
--- /dev/null
+++ b/tools/generate_compile_commands.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+#
+# 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.
+
+import subprocess
+import json
+import utils
+
+### Python script to generate compile_commands.json and make it more useful.
+
+HOST_OS = utils.GuessOS()
+
+if not HOST_OS in ['linux', 'macos']:
+ print ("Generate compile_commands has not been ported to %s yet." % (HOST_OS))
+ exit
+
+if HOST_OS == 'linux':
+ ninja = 'ninja-linux64'
+ out_folder = ' out'
+if HOST_OS == 'macos':
+ ninja = 'ninja-mac'
+ out_folder = ' xcodebuild'
+
+subprocess.call("python tools/generate_buildfiles.py", shell=True)
+
+command_set = json.loads(
+ subprocess.check_output(
+ ninja + " -C " + out_folder + "/DebugX64 -t compdb cxx cc h", shell=True
+ )
+)
+
+commands = []
+for obj in command_set:
+ C = obj["command"]
+
+ # Skip precompiled mode, a lot of code is commented out in precompiled mode
+ if ("-DDART_PRECOMPILED_RUNTIME" in C):
+ continue
+
+ # Remove warnings
+ C = C.replace("-Werror", "")
+
+ obj["command"] = C
+ commands += [obj]
+
+json.dump(commands, open("compile_commands.json", "w"), indent=4)
diff --git a/tools/gn.py b/tools/gn.py
index 9b34862..bc05a61 100755
--- a/tools/gn.py
+++ b/tools/gn.py
@@ -496,16 +496,11 @@
starttime = time.time()
args = parse_args(argv)
- if sys.platform.startswith(('cygwin', 'win')):
- subdir = 'win'
- elif sys.platform == 'darwin':
- subdir = 'mac-x64'
- elif sys.platform.startswith('linux'):
- subdir = 'linux-x64'
- else:
- print 'Unknown platform: ' + sys.platform
+ gn = os.path.join(DART_ROOT, 'buildtools',
+ 'gn.exe' if utils.IsWindows() else 'gn')
+ if not os.path.isfile(gn):
+ print "Couldn't find the gn binary at path: " + gn
return 1
- gn = os.path.join(DART_ROOT, 'buildtools', subdir, 'gn')
commands = []
for target_os in args.os:
diff --git a/tools/patches/flutter-engine/f9ebf2129732fd2b606286fdf58e500384b8a0bc.flutter.patch b/tools/patches/flutter-engine/f9ebf2129732fd2b606286fdf58e500384b8a0bc.flutter.patch
index e4dcd66..7e5352b 100644
--- a/tools/patches/flutter-engine/f9ebf2129732fd2b606286fdf58e500384b8a0bc.flutter.patch
+++ b/tools/patches/flutter-engine/f9ebf2129732fd2b606286fdf58e500384b8a0bc.flutter.patch
@@ -1,5 +1,17 @@
+diff --git a/analysis_options.yaml b/analysis_options.yaml
+index 8957dfe03..68eea153e 100644
+--- a/analysis_options.yaml
++++ b/analysis_options.yaml
+@@ -114,7 +114,6 @@ linter:
+ # - parameter_assignments # we do this commonly
+ - prefer_adjacent_string_concatenation
+ - prefer_asserts_in_initializer_lists
+- - prefer_bool_in_asserts
+ - prefer_collection_literals
+ - prefer_conditional_assignment
+ - prefer_const_constructors
diff --git a/dev/devicelab/pubspec.yaml b/dev/devicelab/pubspec.yaml
-index 9dee6de4a12..8063988556d 100644
+index 9dee6de4a..806398855 100644
--- a/dev/devicelab/pubspec.yaml
+++ b/dev/devicelab/pubspec.yaml
@@ -5,7 +5,7 @@ homepage: https://github.com/flutter/flutter
@@ -12,7 +24,7 @@
dependencies:
args: 1.5.1
diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml
-index 81564e6a2b4..9f85ed7ab4d 100644
+index 81564e6a2..9f85ed7ab 100644
--- a/packages/flutter/pubspec.yaml
+++ b/packages/flutter/pubspec.yaml
@@ -5,7 +5,7 @@ homepage: http://flutter.io
@@ -25,7 +37,7 @@
dependencies:
# To update these, use "flutter update-packages --force-upgrade".
diff --git a/packages/flutter_test/pubspec.yaml b/packages/flutter_test/pubspec.yaml
-index aa930cbdf49..531d24b7187 100644
+index aa930cbdf..531d24b71 100644
--- a/packages/flutter_test/pubspec.yaml
+++ b/packages/flutter_test/pubspec.yaml
@@ -2,7 +2,7 @@ name: flutter_test
@@ -38,7 +50,7 @@
dependencies:
# To update these, use "flutter update-packages --force-upgrade".
diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml
-index 5f89d0f18ea..a869162b0e8 100644
+index 5f89d0f18..a869162b0 100644
--- a/packages/flutter_tools/pubspec.yaml
+++ b/packages/flutter_tools/pubspec.yaml
@@ -5,7 +5,7 @@ author: Flutter Authors <flutter-dev@googlegroups.com>
diff --git a/tools/patches/flutter-engine/f9ebf2129732fd2b606286fdf58e500384b8a0bc.patch b/tools/patches/flutter-engine/f9ebf2129732fd2b606286fdf58e500384b8a0bc.patch
index 0d76138..4a016f9 100644
--- a/tools/patches/flutter-engine/f9ebf2129732fd2b606286fdf58e500384b8a0bc.patch
+++ b/tools/patches/flutter-engine/f9ebf2129732fd2b606286fdf58e500384b8a0bc.patch
@@ -1,5 +1,5 @@
diff --git a/DEPS b/DEPS
-index bb8bca813..701871f79 100644
+index cd4acaf5e..28f8d1b72 100644
--- a/DEPS
+++ b/DEPS
@@ -47,7 +47,7 @@ vars = {
@@ -11,6 +11,15 @@
'dart_dart_style_tag': '1.2.0',
'dart_dartdoc_tag': 'v0.24.1',
'dart_fixnum_tag': '0.10.8',
+@@ -60,7 +60,7 @@ vars = {
+ 'dart_http_throttle_tag': '1.0.2',
+ 'dart_intl_tag': '0.15.7',
+ 'dart_json_rpc_2_tag': '2.0.9',
+- 'dart_linter_tag': '0.1.71',
++ 'dart_linter_tag': '0.1.75',
+ 'dart_logging_tag': '0.11.3+2',
+ 'dart_markdown_tag': '2.0.2',
+ 'dart_matcher_tag': '0.12.3',
diff --git a/lib/snapshot/BUILD.gn b/lib/snapshot/BUILD.gn
index ef06063a2..fa18ebfbf 100644
--- a/lib/snapshot/BUILD.gn
diff --git a/tools/test.dart b/tools/test.dart
index 5914e5f..abb3b35 100755
--- a/tools/test.dart
+++ b/tools/test.dart
@@ -15,6 +15,67 @@
const int deflakingCount = 5;
+/// Quotes a string in shell single quote mode. This function produces a single
+/// shell argument that evaluates to the exact string provided, handling any
+/// special characters in the input string. Shell single quote mode works uses
+/// the single quote character as the delimiter and uses the characters
+/// in-between verbatim without any special processing. To insert the single
+/// quote character itself, escape single quote mode, insert an escaped single
+/// quote, and then return to single quote mode.
+///
+/// Examples:
+/// foo becomes 'foo'
+/// foo bar becomes 'foo bar'
+/// foo\ bar becomes 'foo\ bar'
+/// foo's bar becomes 'foo '\''s bar'
+/// foo "b"ar becomes 'foo "b"'
+/// foo
+/// bar becomes 'foo
+/// bar'
+String shellSingleQuote(String string) {
+ return "'${string.replaceAll("'", "'\\''")}'";
+}
+
+/// Like [shellSingleQuote], but if the string only contains safe ASCII
+/// characters, don't quote it. Note that it's not always safe to omit the
+/// quotes even if the string only has safe characters, as doing so might match
+/// a shell keyword or a shell builtin in the first argument in a command. It
+/// should be safe to use this for the second argument onwards in a command.
+String simpleShellSingleQuote(String string) {
+ return new RegExp(r"^[a-zA-Z0-9%+,./:_-]*$").hasMatch(string)
+ ? string
+ : shellSingleQuote(string);
+}
+
+/// Runs a process and exits likewise if the process exits non-zero.
+Future<ProcessResult> runProcess(
+ String executable, List<String> arguments) async {
+ final processResult = await Process.run(executable, arguments);
+ if (processResult.exitCode != 0) {
+ final command =
+ ([executable]..addAll(arguments)).map(simpleShellSingleQuote).join(" ");
+ throw new Exception("Command exited ${processResult.exitCode}: $command\n"
+ "${processResult.stdout}\n${processResult.stderr}");
+ }
+ return processResult;
+}
+
+/// Runs a process and exits likewise if the process exits non-zero, but let the
+/// child process inherit out stdio handles.
+Future<ProcessResult> runProcessInheritStdio(
+ String executable, List<String> arguments) async {
+ final process = await Process.start(executable, arguments,
+ mode: ProcessStartMode.inheritStdio);
+ final exitCode = await process.exitCode;
+ final processResult = new ProcessResult(process.pid, exitCode, "", "");
+ if (processResult.exitCode != 0) {
+ final command =
+ ([executable]..addAll(arguments)).map(simpleShellSingleQuote).join(" ");
+ throw new Exception("Command exited ${processResult.exitCode}: $command");
+ }
+ return processResult;
+}
+
/// Returns the operating system of a builder.
String systemOfBuilder(String builder) {
return builder.split("-").firstWhere(
@@ -188,6 +249,7 @@
// Run each step like the builder would, deflaking tests that need it.
final stepResultsPaths = <String>[];
+ final stepLogsPaths = <String>[];
for (int stepIndex = 0; stepIndex < testSteps.length; stepIndex++) {
// Run the test step.
final testStep = testSteps[stepIndex];
@@ -200,22 +262,29 @@
.cast<String>();
final fullArguments = <String>[]
..addAll(stepArguments)
- ..addAll(
- ["--output-directory=${stepDirectory.path}", "--write-results"])
+ ..addAll([
+ "--output-directory=${stepDirectory.path}",
+ "--clean-exit",
+ "--silent-failures",
+ "--write-results",
+ "--write-logs",
+ ])
..addAll(options.rest);
+ print("".padLeft(80, "="));
print("$stepName: Running tests");
- final testProcess = await Process.start("tools/test.py", fullArguments);
- await testProcess.exitCode;
+ print("".padLeft(80, "="));
+ await runProcessInheritStdio("tools/test.py", fullArguments);
stepResultsPaths.add("${stepDirectory.path}/results.json");
+ stepLogsPaths.add("${stepDirectory.path}/logs.json");
// Find the list of tests to deflake.
- final deflakeListOutput = await Process.run(Platform.resolvedExecutable, [
+ final deflakeListOutput = await runProcess(Platform.resolvedExecutable, [
"tools/bots/compare_results.dart",
"--changed",
"--failing",
"--passing",
"--flakiness-data=${outDirectory.path}/flaky.json",
"${outDirectory.path}/previous.json",
- "${stepDirectory.path}/results.json"
+ "${stepDirectory.path}/results.json",
]);
final deflakeListPath = "${stepDirectory.path}/deflake.list";
final deflakeListFile = new File(deflakeListPath);
@@ -225,27 +294,30 @@
for (int i = 1;
deflakeListOutput.stdout != "" && i <= deflakingCount;
i++) {
+ print("".padLeft(80, "="));
print("$stepName: Running deflaking iteration $i");
+ print("".padLeft(80, "="));
final deflakeDirectory = new Directory("${stepDirectory.path}/$i");
await deflakeDirectory.create();
final deflakeArguments = <String>[]
..addAll(stepArguments)
..addAll([
"--output-directory=${deflakeDirectory.path}",
+ "--clean-exit",
+ "--silent-failures",
"--write-results",
- "--test-list=$deflakeListPath"
+ "--test-list=$deflakeListPath",
])
..addAll(options.rest);
- final deflakeProcess =
- await Process.start("tools/test.py", deflakeArguments);
- await deflakeProcess.exitCode;
+ await runProcessInheritStdio("tools/test.py", deflakeArguments);
deflakingResultsPaths.add("${deflakeDirectory.path}/results.json");
}
// Update the flakiness information based on what we've learned.
print("$stepName: Updating flakiness information");
- await Process.run(
- "tools/bots/update_flakiness.dart",
+ await runProcess(
+ Platform.resolvedExecutable,
[
+ "tools/bots/update_flakiness.dart",
"--input=${outDirectory.path}/flaky.json",
"--output=${outDirectory.path}/flaky.json",
"${stepDirectory.path}/results.json",
@@ -256,19 +328,31 @@
stepResultsPaths
.map((path) => new File(path).readAsStringSync())
.join(""));
+ // Collect all the logs from all the steps.
+ await new File("${outDirectory.path}/logs.json").writeAsString(stepLogsPaths
+ .map((path) => new File(path).readAsStringSync())
+ .join(""));
// Write out the final comparison.
- print("");
- final compareOutput = await Process.run("tools/bots/compare_results.dart", [
+ print("".padLeft(80, "="));
+ print("Test Results");
+ print("".padLeft(80, "="));
+ final compareOutput = await runProcess(Platform.resolvedExecutable, [
+ "tools/bots/compare_results.dart",
"--human",
"--verbose",
"--changed",
"--failing",
"--passing",
"--flakiness-data=${outDirectory.path}/flaky.json",
+ "--logs=${outDirectory.path}/logs.json",
"${outDirectory.path}/previous.json",
- "${outDirectory.path}/results.json"
+ "${outDirectory.path}/results.json",
]);
- stdout.write(compareOutput.stdout);
+ if (compareOutput.stdout == "") {
+ print("There were no test failures.");
+ } else {
+ stdout.write(compareOutput.stdout);
+ }
} finally {
await outDirectory.delete(recursive: true);
}
diff --git a/tools/testing/dart/configuration.dart b/tools/testing/dart/configuration.dart
index 53f95df..aa0b4f1 100644
--- a/tools/testing/dart/configuration.dart
+++ b/tools/testing/dart/configuration.dart
@@ -33,7 +33,8 @@
this.isVerbose,
this.listTests,
this.listStatusFiles,
- this.noStatus,
+ this.cleanExit,
+ this.silentFailures,
this.printTiming,
this.printReport,
this.reportInJson,
@@ -58,6 +59,7 @@
this.testServerCrossOriginPort,
this.testDriverErrorPort,
this.localIP,
+ this.keepGeneratedFiles,
this.dart2jsOptions,
String packages,
this.packageRoot,
@@ -83,7 +85,8 @@
final bool isVerbose;
final bool listTests;
final bool listStatusFiles;
- final bool noStatus;
+ final bool cleanExit;
+ final bool silentFailures;
final bool printTiming;
final bool printReport;
final bool reportInJson;
@@ -112,7 +115,6 @@
bool get useAnalyzerCfe => configuration.useAnalyzerCfe;
bool get useAnalyzerFastaParser => configuration.useAnalyzerFastaParser;
bool get useBlobs => configuration.useBlobs;
- bool get keepGeneratedFiles => configuration.keepGeneratedFiles;
bool get useSdk => configuration.useSdk;
bool get useFastStartup => configuration.useFastStartup;
bool get useEnableAsserts => configuration.enableAsserts;
@@ -140,6 +142,7 @@
final int testServerCrossOriginPort;
final int testDriverErrorPort;
final String localIP;
+ final bool keepGeneratedFiles;
/// Extra dart2js options passed to the testing script.
final List<String> dart2jsOptions;
diff --git a/tools/testing/dart/options.dart b/tools/testing/dart/options.dart
index 3bea2d7..72ef8d4 100644
--- a/tools/testing/dart/options.dart
+++ b/tools/testing/dart/options.dart
@@ -226,10 +226,12 @@
'List status files for test-suites. Do not run any test suites.',
hide: true),
new _Option.bool(
- 'no_status',
- 'Do not use status files for test expectations. Use Skip '
- 'and Slow from status files. Return exit code 0 if tests '
- 'run and return results.',
+ 'clean_exit', 'Exit 0 if tests ran and results were output.',
+ hide: true),
+ new _Option.bool(
+ 'silent_failures',
+ "Don't complain about failing tests. This is useful when in "
+ "combination with --write-results.",
hide: true),
new _Option.bool('report_in_json',
'When listing with --list, output result summary in JSON.',
@@ -333,14 +335,15 @@
/// options.
static final _blacklistedOptions = [
'build_directory',
- 'debug_output_directory',
'chrome',
+ 'clean_exit',
'copy_coredumps',
'dart',
- 'flutter',
+ 'debug_output_directory',
'drt',
'exclude_suite',
'firefox',
+ 'flutter',
'local_ip',
'output_directory',
'progress',
@@ -348,15 +351,25 @@
'safari',
'shard',
'shards',
+ 'silent_failures',
'step_name',
'tasks',
'time',
'verbose',
- 'write_logs',
'write_debug_log',
+ 'write_logs',
'write_results',
].toSet();
+ /// The set of objects which the named configuration should imply.
+ static final _namedConfigurationOptions = [
+ 'system',
+ 'arch',
+ 'mode',
+ 'compiler',
+ 'runtime',
+ ].toSet();
+
/// Parses a list of strings as test options.
///
/// Returns a list of configurations in which to run the tests.
@@ -473,6 +486,18 @@
}
}
+ // If a named configuration was specified ensure no other options, which are
+ // implied by the named configuration, were specified.
+ if (configuration['named_configuration'] is String) {
+ for (final optionName in _namedConfigurationOptions) {
+ if (configuration.containsKey(optionName)) {
+ final namedConfig = configuration['named_configuration'];
+ _fail("The named configuration '$namedConfig' implies "
+ "'$optionName'. Try removing '$optionName'.");
+ }
+ }
+ }
+
// Apply default values for unspecified options.
for (var option in _options) {
if (!configuration.containsKey(option.name)) {
@@ -497,12 +522,16 @@
/// Given a set of parsed option values, returns the list of command line
/// arguments that would reproduce that configuration.
- List<String> _reproducingCommand(Map<String, dynamic> data) {
+ List<String> _reproducingCommand(
+ Map<String, dynamic> data, bool usingNamedConfiguration) {
var arguments = <String>[];
for (var option in _options) {
var name = option.name;
- if (!data.containsKey(name) || _blacklistedOptions.contains(name)) {
+ if (!data.containsKey(name) ||
+ _blacklistedOptions.contains(name) ||
+ (usingNamedConfiguration &&
+ _namedConfigurationOptions.contains(name))) {
continue;
}
@@ -634,7 +663,7 @@
// Expand compilers.
for (var compiler in compilers) {
// Expand modes.
- String modes = data["mode"] ?? compiler.defaultMode.name;
+ String modes = (data["mode"] as String) ?? compiler.defaultMode.name;
if (modes == "all") modes = "debug,release,product";
for (var modeName in modes.split(",")) {
var mode = Mode.find(modeName);
@@ -650,7 +679,6 @@
useAnalyzerFastaParser:
data["analyzer_use_fasta_parser"] as bool,
useBlobs: data["use_blobs"] as bool,
- keepGeneratedFiles: data["keep_generated_files"] as bool,
useSdk: data["use_sdk"] as bool,
useFastStartup: data["fast_startup"] as bool,
useDart2JSWithKernel: data["dart2js_with_kernel"] as bool,
@@ -676,13 +704,15 @@
isVerbose: data["verbose"] as bool,
listTests: data["list"] as bool,
listStatusFiles: data["list_status_files"] as bool,
- noStatus: data["no_status"] as bool,
+ cleanExit: data["clean_exit"] as bool,
+ silentFailures: data["silent_failures"] as bool,
printTiming: data["time"] as bool,
printReport: data["report"] as bool,
reportInJson: data["report_in_json"] as bool,
resetBrowser: data["reset_browser_configuration"] as bool,
skipCompilation: data["skip_compilation"] as bool,
- useKernelBytecode: compiler == Compiler.dartkb,
+ useKernelBytecode:
+ innerConfiguration.compiler == Compiler.dartkb,
writeDebugLog: data["write_debug_log"] as bool,
writeResults: data["write_results"] as bool,
writeLogs: data["write_logs"] as bool,
@@ -693,6 +723,7 @@
dartPath: data["dart"] as String,
dartPrecompiledPath: data["dart_precompiled"] as String,
flutterPath: data["flutter"] as String,
+ keepGeneratedFiles: data["keep_generated_files"] as bool,
taskCount: data["tasks"] as int,
shardCount: data["shards"] as int,
shard: data["shard"] as int,
@@ -707,7 +738,8 @@
packageRoot: data["package_root"] as String,
suiteDirectory: data["suite_dir"] as String,
outputDirectory: data["output_directory"] as String,
- reproducingArguments: _reproducingCommand(data),
+ reproducingArguments:
+ _reproducingCommand(data, namedConfiguration != null),
fastTestsOnly: data["fast_tests"] as bool,
printPassingStdout: data["print_passing_stdout"] as bool);
diff --git a/tools/testing/dart/runtime_configuration.dart b/tools/testing/dart/runtime_configuration.dart
index ff8efbb..8330b51 100644
--- a/tools/testing/dart/runtime_configuration.dart
+++ b/tools/testing/dart/runtime_configuration.dart
@@ -81,7 +81,8 @@
TestSuite suite,
CommandArtifact artifact,
List<String> arguments,
- Map<String, String> environmentOverrides) {
+ Map<String, String> environmentOverrides,
+ bool isCrashExpected) {
// TODO(ahe): Make this method abstract.
throw "Unimplemented runtime '$runtimeType'";
}
@@ -99,7 +100,8 @@
TestSuite suite,
CommandArtifact artifact,
List<String> arguments,
- Map<String, String> environmentOverrides) {
+ Map<String, String> environmentOverrides,
+ bool isCrashExpected) {
return <Command>[];
}
}
@@ -125,7 +127,8 @@
TestSuite suite,
CommandArtifact artifact,
List<String> arguments,
- Map<String, String> environmentOverrides) {
+ Map<String, String> environmentOverrides,
+ bool isCrashExpected) {
// TODO(ahe): Avoid duplication of this method between d8 and jsshell.
checkArtifact(artifact);
return [
@@ -147,7 +150,8 @@
TestSuite suite,
CommandArtifact artifact,
List<String> arguments,
- Map<String, String> environmentOverrides) {
+ Map<String, String> environmentOverrides,
+ bool isCrashExpected) {
checkArtifact(artifact);
return [
Command.jsCommandLine(
@@ -205,7 +209,8 @@
TestSuite suite,
CommandArtifact artifact,
List<String> arguments,
- Map<String, String> environmentOverrides) {
+ Map<String, String> environmentOverrides,
+ bool isCrashExpected) {
String script = artifact.filename;
String type = artifact.mimeType;
if (script != null &&
@@ -215,7 +220,9 @@
type != 'application/kernel-ir-fully-linked') {
throw "Dart VM cannot run files of type '$type'.";
}
-
+ if (isCrashExpected) {
+ arguments.insert(0, '--suppress-core-dump');
+ }
String executable = suite.dartVmBinaryFileName;
if (type == 'application/kernel-ir-fully-linked') {
executable = suite.dartVmExecutableFileName;
@@ -230,7 +237,8 @@
TestSuite suite,
CommandArtifact artifact,
List<String> arguments,
- Map<String, String> environmentOverrides) {
+ Map<String, String> environmentOverrides,
+ bool isCrashExpected) {
String script = artifact.filename;
String type = artifact.mimeType;
if (script != null &&
@@ -252,7 +260,8 @@
TestSuite suite,
CommandArtifact artifact,
List<String> arguments,
- Map<String, String> environmentOverrides) {
+ Map<String, String> environmentOverrides,
+ bool isCrashExpected) {
String script = artifact.filename;
String type = artifact.mimeType;
if (script != null && type != 'application/dart-precompiled') {
@@ -279,7 +288,8 @@
TestSuite suite,
CommandArtifact artifact,
List<String> arguments,
- Map<String, String> environmentOverrides) {
+ Map<String, String> environmentOverrides,
+ bool isCrashExpected) {
String script = artifact.filename;
String type = artifact.mimeType;
if (script != null && type != 'application/dart-precompiled') {
@@ -315,7 +325,8 @@
TestSuite suite,
CommandArtifact artifact,
List<String> arguments,
- Map<String, String> environmentOverrides) {
+ Map<String, String> environmentOverrides,
+ bool isCrashExpected) {
String executable = suite.dartVmBinaryFileName;
return selfCheckers
.map((String tester) => Command.vmBatch(
@@ -336,7 +347,8 @@
TestSuite suite,
CommandArtifact artifact,
List<String> arguments,
- Map<String, String> environmentOverrides) {
+ Map<String, String> environmentOverrides,
+ bool isCrashExpected) {
throw "Unimplemented runtime '$runtimeType'";
}
}
diff --git a/tools/testing/dart/test_configurations.dart b/tools/testing/dart/test_configurations.dart
index 00e5ebf..d20f969 100644
--- a/tools/testing/dart/test_configurations.dart
+++ b/tools/testing/dart/test_configurations.dart
@@ -188,6 +188,9 @@
printFailures = false;
eventListener.add(new StatusFileUpdatePrinter());
}
+ if (firstConf.silentFailures) {
+ printFailures = false;
+ }
eventListener.add(new SummaryPrinter());
if (printFailures) {
// The buildbot has it's own failure summary since it needs to wrap it
@@ -222,7 +225,7 @@
if (listTests) {
eventListener.add(new SummaryPrinter(jsonOnly: reportInJson));
} else {
- if (!firstConf.noStatus) {
+ if (!firstConf.cleanExit) {
eventListener.add(new ExitCodeSetter());
}
eventListener.add(new IgnoredTestMonitor());
diff --git a/tools/testing/dart/test_suite.dart b/tools/testing/dart/test_suite.dart
index 4a76a66..cb2ca1e 100644
--- a/tools/testing/dart/test_suite.dart
+++ b/tools/testing/dart/test_suite.dart
@@ -483,6 +483,9 @@
}
void _addTest(ExpectationSet testExpectations, String testName) {
+ var fullName = 'cc/$testName';
+ var expectations = testExpectations.expectations(fullName);
+
var args = configuration.standardOptions.toList();
if (configuration.compilerConfiguration.previewDart2) {
final dfePath = new Path("$buildDir/gen/kernel-service.dart.snapshot")
@@ -491,13 +494,14 @@
// '--dfe' has to be the first argument for run_vm_test to pick it up.
args.insert(0, '--dfe=$dfePath');
}
+ if (expectations.contains(Expectation.crash)) {
+ args.insert(0, '--suppress-core-dump');
+ }
args.add(testName);
var command = Command.process(
'run_vm_unittest', targetRunnerPath, args, environmentOverrides);
- var fullName = 'cc/$testName';
- var expectations = testExpectations.expectations(fullName);
enqueueNewTestCase(fullName, [command], expectations);
}
@@ -828,15 +832,16 @@
allVmOptions = vmOptions.toList()..addAll(extraVmOptions);
}
- var commands =
- makeCommands(info, vmOptionsVariant, allVmOptions, commonArguments);
var expectations = testExpectations.expectations(testName);
+ var isCrashExpected = expectations.contains(Expectation.crash);
+ var commands = makeCommands(info, vmOptionsVariant, allVmOptions,
+ commonArguments, isCrashExpected);
enqueueNewTestCase(testName, commands, expectations, info);
}
}
List<Command> makeCommands(TestInformation info, int vmOptionsVariant,
- List<String> vmOptions, List<String> args) {
+ List<String> vmOptions, List<String> args, bool isCrashExpected) {
var commands = <Command>[];
var compilerConfiguration = configuration.compilerConfiguration;
var sharedOptions = info.optionsFromFile['sharedOptions'] as List<String>;
@@ -891,14 +896,14 @@
compilationArtifact);
Map<String, String> environment = environmentOverrides;
- Map<String, String> extraEnv = info.optionsFromFile['environment'];
+ var extraEnv = info.optionsFromFile['environment'] as Map<String, String>;
if (extraEnv != null) {
environment = new Map.from(environment)..addAll(extraEnv);
}
return commands
- ..addAll(configuration.runtimeConfiguration.computeRuntimeCommands(
- this, compilationArtifact, runtimeArguments, environment));
+ ..addAll(configuration.runtimeConfiguration.computeRuntimeCommands(this,
+ compilationArtifact, runtimeArguments, environment, isCrashExpected));
}
CreateTest makeTestCaseCreator(Map<String, dynamic> optionsFromFile) {