[CFE] Add first stab at weekly leak test

Change-Id: Ib0307a4d82414e8387f65a9010a6f0f91827c3bb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/157962
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Alexander Thomas <athom@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Jens Johansen 2020-08-11 09:01:29 +00:00 committed by commit-bot@chromium.org
parent 5ad3540b54
commit 13de7e7560
7 changed files with 561 additions and 50 deletions

View file

@ -0,0 +1,395 @@
// Copyright (c) 2020, 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:convert';
import 'dart:io';
import 'dart:math';
import "vm_service_heap_helper.dart" as helper;
Completer completer;
Set<String> files = {};
// General idea: Do the once-a-week leak testing script
// => In this file:
// * Start the frontend_server
// * Do a compilation
// * Pause the VM and do a "leak iteration"
// * Once the VM has been unpaused, do an invalidation etc and repeat.
//
// This script also makes sure to clone flutter gallery,
// but assumes that flutter has been setup as by the script
// `tools/bots/flutter/compile_flutter.sh`.
main(List<String> args) async {
if (Platform.isWindows) {
throw "This script cannot run on Windows as it uses non-Windows "
"assumptions both for the placement of pub packages and the presence "
"of 'ln' for symbolic links. It has only been tested on Linux but will "
"probably also work on Mac.";
}
bool quicker = false;
bool alternativeInvalidation = false;
String rootPath;
for (String arg in args) {
if (arg == "--quicker") {
quicker = true;
} else if (arg == "--alternativeInvalidation") {
alternativeInvalidation = true;
} else if (arg.startsWith("--path=")) {
rootPath = arg.substring("--path=".length);
} else {
throw "Unknown argument '$arg'";
}
}
if (rootPath == null) {
throw "No path given. Pass with --path=<path>";
}
Directory patchedSdk = new Directory("$rootPath/flutter_patched_sdk/");
if (!patchedSdk.existsSync()) {
throw "Directory $patchedSdk doesn't exist.";
}
Uri frontendServerStarter = Platform.script
.resolve("../../frontend_server/bin/frontend_server_starter.dart");
if (!new File.fromUri(frontendServerStarter).existsSync()) {
throw "File not found: $frontendServerStarter";
}
Directory gallery = new Directory("$rootPath/gallery");
if (!gallery.existsSync()) {
print("Gallery not found... Attempting to clone via git.");
// git clone https://github.com/flutter/gallery.git
Process process = await Process.start("git", [
"clone",
"https://github.com/flutter/gallery.git",
"$rootPath/gallery"
]);
process.stdout
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
print("git stdout> $line");
});
process.stderr
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
print("git stderr> $line");
});
int processExitCode = await process.exitCode;
print("Exit code from git: $processExitCode");
process = await Process.start("../flutter/bin/flutter", ["pub", "get"],
workingDirectory: "$rootPath/gallery/");
process.stdout
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
print("flutter stdout> $line");
});
process.stderr
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
print("flutter stderr> $line");
});
processExitCode = await process.exitCode;
print("Exit code from flutter: $processExitCode");
// Attempt to hack around strings being truncated to 128 bytes in heap dumps
// https://github.com/dart-lang/sdk/blob/c59cdee365b94ce066344840f9e3412d642019b5/runtime/vm/object_graph.cc#L809
// (pub paths can become too long, so two distinct files will look to have
// the same url and thus give a false positive).
Uri pubDirUri = Uri.parse("file://${Platform.environment['HOME']}/"
".pub-cache/hosted/pub.dartlang.org/");
Directory pubDir = new Directory.fromUri(pubDirUri);
if (!pubDir.existsSync()) throw "Expected to find $pubDir";
File galleryDotPackages = new File("$rootPath/gallery/.packages");
if (!galleryDotPackages.existsSync()) {
throw "Didn't find $galleryDotPackages";
}
String data = galleryDotPackages.readAsStringSync();
data = data.replaceAll(pubDirUri.toString(), "pub/");
galleryDotPackages.writeAsStringSync(data);
File galleryPackageConfig =
new File("$rootPath/gallery/.dart_tool/package_config.json");
if (!galleryPackageConfig.existsSync()) {
throw "Didn't find $galleryPackageConfig";
}
data = galleryPackageConfig.readAsStringSync();
data = data.replaceAll(pubDirUri.toString(), "../pub/");
galleryPackageConfig.writeAsStringSync(data);
process = await Process.start("ln", ["-s", pubDir.path, "pub"],
workingDirectory: "$rootPath/gallery/");
process.stdout
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
print("ln stdout> $line");
});
process.stderr
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
print("ln stderr> $line");
});
processExitCode = await process.exitCode;
print("Exit code from ln: $processExitCode");
}
File galleryDotPackages = new File("$rootPath/gallery/.packages");
if (!galleryDotPackages.existsSync()) {
throw "Didn't find $galleryDotPackages";
}
List<helper.Interest> interests = new List<helper.Interest>();
interests.add(new helper.Interest(
Uri.parse("package:kernel/ast.dart"), "Library", ["fileUri"]));
helper.VMServiceHeapHelperSpecificExactLeakFinder heapHelper =
new helper.VMServiceHeapHelperSpecificExactLeakFinder(
interests,
[
new helper.Interest(Uri.parse("package:kernel/ast.dart"), "Library",
["fileUri", "_libraryIdString"]),
],
true,
false);
print("About to run with "
"quicker = $quicker; "
"alternativeInvalidation = $alternativeInvalidation; "
"path = $rootPath; "
"...");
List<String> processArgs = [
"--disable_dart_dev",
"--disable-service-auth-codes",
frontendServerStarter.toString(),
"--sdk-root",
"$rootPath/flutter_patched_sdk/",
"--incremental",
"--target=flutter",
"--debugger-module-names",
"-Ddart.developer.causal_async_stacks=true",
"--output-dill",
"$rootPath/flutter_server_tmp.dill",
"--packages",
"$rootPath/gallery/.packages",
"-Ddart.vm.profile=false",
"-Ddart.vm.product=false",
"--bytecode-options=source-positions,local-var-info,debugger-stops,"
"instance-field-initializers,keep-unreachable-code,"
"avoid-closure-call-instructions",
"--enable-asserts",
"--track-widget-creation",
"--initialize-from-dill",
"$rootPath/cache.dill",
];
if (alternativeInvalidation) {
processArgs.add("--enable-experiment=alternative-invalidation-strategy");
}
await heapHelper.start(processArgs,
stdinReceiver: (s) {
if (s.startsWith("+")) {
files.add(s.substring(1));
} else if (s.startsWith("-")) {
files.remove(s.substring(1));
} else {
List<String> split = s.split(" ");
if (int.tryParse(split.last) != null &&
split[split.length - 2].endsWith(".dill")) {
// e.g. something like "filename.dill 0" for output file and 0
// errors.
completer.complete();
} else {
print("out> $s");
}
}
},
stderrReceiver: (s) => print("err> $s"));
await sendAndWait(heapHelper.process, ['compile package:gallery/main.dart']);
Stopwatch stopwatch = new Stopwatch()..start();
await pauseAndWait(heapHelper);
print("First compile took ${stopwatch.elapsedMilliseconds} ms");
await recompileAndWait(heapHelper.process, "package:gallery/main.dart", []);
await accept(heapHelper);
await sendAndWaitSetSelection(heapHelper.process);
await sendAndWaitToObjectForSourceLocation(heapHelper.process);
await sendAndWaitToObject(heapHelper.process);
await pauseAndWait(heapHelper);
print("Knows about ${files.length} files...");
List<String> listFiles = new List<String>.from(files);
int iteration = 0;
for (String s in listFiles) {
print("On iteration ${iteration++} / ${listFiles.length}");
print(" => Invalidating $s");
stopwatch.reset();
await recompileAndWait(
heapHelper.process, "package:gallery/main.dart", [s]);
await accept(heapHelper);
print("Recompile took ${stopwatch.elapsedMilliseconds} ms");
await sendAndWaitSetSelection(heapHelper.process);
await sendAndWaitToObjectForSourceLocation(heapHelper.process);
await sendAndWaitToObject(heapHelper.process);
if (quicker) {
if (iteration % 10 == 0) {
await pauseAndWait(heapHelper);
}
} else {
await pauseAndWait(heapHelper);
}
}
if (quicker) {
await pauseAndWait(heapHelper);
}
// We should now be done.
print("Done!");
heapHelper.process.kill();
}
Future accept(
helper.VMServiceHeapHelperSpecificExactLeakFinder heapHelper) async {
heapHelper.process.stdin.writeln('accept');
int waits = 0;
while (!await heapHelper.isIdle()) {
if (waits > 100) {
// Waited for at least 10 seconds --- assume there's something wrong.
throw "Timed out waiting to become idle!";
}
await new Future.delayed(new Duration(milliseconds: 100));
waits++;
}
}
class Uuid {
final Random _random = new Random();
/// Generate a version 4 (random) uuid. This is a uuid scheme that only uses
/// random numbers as the source of the generated uuid.
String generateV4() {
// Generate xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx / 8-4-4-4-12.
int special = 8 + _random.nextInt(4);
return '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}-'
'${_bitsDigits(16, 4)}-'
'4${_bitsDigits(12, 3)}-'
'${_printDigits(special, 1)}${_bitsDigits(12, 3)}-'
'${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}';
}
String _bitsDigits(int bitCount, int digitCount) =>
_printDigits(_generateBits(bitCount), digitCount);
int _generateBits(int bitCount) => _random.nextInt(1 << bitCount);
String _printDigits(int value, int count) =>
value.toRadixString(16).padLeft(count, '0');
}
Future pauseAndWait(
helper.VMServiceHeapHelperSpecificExactLeakFinder heapHelper) async {
int prevIterationNumber = heapHelper.iterationNumber;
await heapHelper.pause();
int waits = 0;
while (heapHelper.iterationNumber == prevIterationNumber) {
if (waits > 10000) {
// Waited for at least 1000 seconds --- assume there's something wrong.
throw "Timed out waiting for the helper iteration number to increase!";
}
await new Future.delayed(new Duration(milliseconds: 100));
waits++;
}
}
Future recompileAndWait(
Process _server, String what, List<String> invalidates) async {
String inputKey = Uuid().generateV4();
List<String> data = ['recompile $what $inputKey'];
invalidates.forEach(data.add);
data.add('$inputKey');
await sendAndWait(_server, data);
}
Future sendAndWait(Process _server, List<String> data) async {
completer = new Completer();
data.forEach(_server.stdin.writeln);
await completer.future;
}
Future sendAndWaitDebugDidSendFirstFrameEvent(Process _server) async {
String inputKey = Uuid().generateV4();
await sendAndWait(_server, [
/* 'compile-expression' <boundarykey> */ 'compile-expression $inputKey',
/* expression */ 'WidgetsBinding.instance.debugDidSendFirstFrameEvent',
/* no definitions */
/* <boundarykey> */ inputKey,
/* no type-defintions */
/* <boundarykey> */ inputKey,
/* libraryUri */ 'package:flutter/src/widgets/binding.dart',
/* class */ '',
/* isStatic */ 'true'
]);
}
Future sendAndWaitSetSelection(Process _server) async {
String inputKey = Uuid().generateV4();
await sendAndWait(_server, [
/* 'compile-expression' <boundarykey> */ 'compile-expression $inputKey',
/* expression */ 'WidgetInspectorService.instance.setSelection('
'arg1, "dummy_68")',
/* definition #1 */ 'arg1',
/* <boundarykey> */ inputKey,
/* no type-defintions */
/* <boundarykey> */ inputKey,
/* libraryUri */ 'package:flutter/src/widgets/widget_inspector.dart',
/* class */ '',
/* isStatic */ 'true'
]);
}
Future sendAndWaitToObject(Process _server) async {
String inputKey = Uuid().generateV4();
await sendAndWait(_server, [
/* 'compile-expression' <boundarykey> */ 'compile-expression $inputKey',
/* expression */ 'WidgetInspectorService.instance.toObject('
'"inspector-836", "tree_112")',
/* no definitions */
/* <boundarykey> */ inputKey,
/* no type-defintions */
/* <boundarykey> */ inputKey,
/* libraryUri */ 'package:flutter/src/widgets/widget_inspector.dart',
/* class */ '',
/* isStatic */ 'true'
]);
}
Future sendAndWaitToObjectForSourceLocation(Process _server) async {
String inputKey = Uuid().generateV4();
await sendAndWait(_server, [
/* 'compile-expression' <boundarykey> */ 'compile-expression $inputKey',
/* expression */ 'WidgetInspectorService.instance.'
'toObjectForSourceLocation("inspector-607", "tree_112")',
/* no definitions */
/* <boundarykey> */ inputKey,
/* no type-defintions */
/* <boundarykey> */ inputKey,
/* libraryUri */ 'package:flutter/src/widgets/widget_inspector.dart',
/* class */ '',
/* isStatic */ 'true'
]);
}

View file

@ -65,6 +65,7 @@ blorp
boo
bootstrap
bots
boundarykey
bowtie
boz
bq
@ -75,6 +76,7 @@ builddir
bulk2
bulkcompile
c's
c59cdee365b94ce066344840f9e3412d642019b
ca
cafebabe
callable
@ -131,6 +133,7 @@ contract
conversion
conversions
coo
costly
cov
crashes
cumulative
@ -145,11 +148,13 @@ dash
dashes
day
db
ddart
dds
debugger
decrease
decrements
def
defintions
deleting
denylist
depended
@ -185,6 +190,7 @@ dog
doo
downstream
dumping
dumps
dupe
durations
dw
@ -284,6 +290,7 @@ hi
hints
home
hoo
hosted
hosting
hot
hotreload
@ -291,6 +298,7 @@ hunk
hurray
i'm
ia
idle
ikg
illustrate
image
@ -357,6 +365,7 @@ lints
linux
listening
ll
ln
local1a
local1b
local1c
@ -443,6 +452,7 @@ periodically
person
phrase
pink
placement
places
plug
pointed
@ -468,6 +478,7 @@ pv
px
py
python
quicker
quot
quux
qux
@ -512,6 +523,7 @@ sdkroot
sdks
secondary
segment
selection
sensitive
services
severe
@ -561,6 +573,7 @@ superinterface
supermixin
supplement
suspension
symbolic
t\b\f\u
t\u0008\f\u
tails
@ -571,6 +584,7 @@ test3b
thereof
thing
threw
timed
timeout
timer
timing
@ -599,6 +613,7 @@ unassignment
unawaited
unbreak
unpacked
unpaused
unregistered
untransformed
untrimmed
@ -608,13 +623,17 @@ uppercase
upward
uses8
usual
uuid
val
vars
verbatim
versioning
waited
waiting
waits
walt
warmup
week
wherever
whiskers
wins
@ -626,5 +645,8 @@ x's
xlate
xrequired
xxx
xxxxxxxx
xxxxxxxxxxxx
y's
year
yxxx

View file

@ -30,6 +30,7 @@ main(List<String> args) async {
new helper.Interest(Uri.parse("package:kernel/ast.dart"), "Library",
["fileUri", "_libraryIdString"]),
],
true,
true);
if (args.length > 0 && args[0] == "--dart2js") {

View file

@ -1,3 +1,7 @@
// Copyright (c) 2020, 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:io";
import "vm_service_heap_helper.dart";

View file

@ -1,5 +1,8 @@
// Copyright (c) 2020, 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:convert";
import "dart:developer";
import "dart:io";
import "package:vm_service/vm_service.dart" as vmService;
@ -214,10 +217,13 @@ class VMServiceHeapHelperBase {
abstract class LaunchingVMServiceHeapHelper extends VMServiceHeapHelperBase {
Process _process;
Process get process => _process;
bool _started = false;
void start(List<String> scriptAndArgs) async {
void start(List<String> scriptAndArgs,
{void stdinReceiver(String line),
void stderrReceiver(String line)}) async {
if (_started) throw "Already started";
_started = true;
_process = await Process.start(
@ -232,15 +238,29 @@ abstract class LaunchingVMServiceHeapHelper extends VMServiceHeapHelperBase {
if (line.startsWith(kObservatoryListening)) {
Uri observatoryUri =
Uri.parse(line.substring(kObservatoryListening.length));
_setupAndRun(observatoryUri);
_setupAndRun(observatoryUri).catchError((e, st) {
// Manually kill the process or it will leak,
// see http://dartbug.com/42918
killProcess();
// This seems to rethrow.
throw e;
});
}
if (stdinReceiver != null) {
stdinReceiver(line);
} else {
stdout.writeln("> $line");
}
stdout.writeln("> $line");
});
_process.stderr
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((line) {
stderr.writeln("> $line");
if (stderrReceiver != null) {
stderrReceiver(line);
} else {
stderr.writeln("> $line");
}
});
// ignore: unawaited_futures
_process.exitCode.then((value) {
@ -254,7 +274,7 @@ abstract class LaunchingVMServiceHeapHelper extends VMServiceHeapHelperBase {
_process.kill();
}
void _setupAndRun(Uri observatoryUri) async {
Future _setupAndRun(Uri observatoryUri) async {
await connect(observatoryUri);
await run();
}
@ -269,9 +289,13 @@ class VMServiceHeapHelperSpecificExactLeakFinder
final Map<Uri, Map<String, List<String>>> _prettyPrints =
new Map<Uri, Map<String, List<String>>>();
final bool throwOnPossibleLeak;
final bool tryToFindShortestPathToLeaks;
VMServiceHeapHelperSpecificExactLeakFinder(List<Interest> interests,
List<Interest> prettyPrints, this.throwOnPossibleLeak) {
VMServiceHeapHelperSpecificExactLeakFinder(
List<Interest> interests,
List<Interest> prettyPrints,
this.throwOnPossibleLeak,
this.tryToFindShortestPathToLeaks) {
if (interests.isEmpty) throw "Empty list of interests given";
for (Interest interest in interests) {
Map<String, List<String>> classToFields = _interests[interest.uri];
@ -301,28 +325,50 @@ class VMServiceHeapHelperSpecificExactLeakFinder
}
}
void pause() async {
await _serviceClient.pause(_isolateRef.id);
}
vmService.VM _vm;
vmService.IsolateRef _isolateRef;
int _iterationNumber;
int get iterationNumber => _iterationNumber;
/// Best effort check if the isolate is idle.
Future<bool> isIdle() async {
dynamic tmp = await _serviceClient.getIsolate(_isolateRef.id);
if (tmp is vmService.Isolate) {
vmService.Isolate isolate = tmp;
return isolate.pauseEvent.topFrame == null;
}
return false;
}
@override
Future<void> run() async {
vmService.VM vm = await _serviceClient.getVM();
if (vm.isolates.length != 1) {
throw "Expected 1 isolate, got ${vm.isolates.length}";
_vm = await _serviceClient.getVM();
if (_vm.isolates.length != 1) {
throw "Expected 1 isolate, got ${_vm.isolates.length}";
}
vmService.IsolateRef isolateRef = vm.isolates.single;
await forceGC(isolateRef.id);
_isolateRef = _vm.isolates.single;
await forceGC(_isolateRef.id);
assert(await _isPausedAtStart(isolateRef.id));
await _serviceClient.resume(isolateRef.id);
assert(await _isPausedAtStart(_isolateRef.id));
await _serviceClient.resume(_isolateRef.id);
int iterationNumber = 1;
_iterationNumber = 1;
while (true) {
await waitUntilPaused(isolateRef.id);
print("Iteration: #$iterationNumber");
iterationNumber++;
await forceGC(isolateRef.id);
await waitUntilPaused(_isolateRef.id);
print("Iteration: #$_iterationNumber");
await forceGC(_isolateRef.id);
vmService.HeapSnapshotGraph heapSnapshotGraph =
await vmService.HeapSnapshotGraph.getSnapshot(
_serviceClient, isolateRef);
_serviceClient, _isolateRef);
// TODO: Considering the single source shortest path algorithm
// doesn't seem to give a useful trace anymore (the snapshot seems to
// have changed) and that converting the graph is very costly,
// maybe don't do that...
HeapGraph graph = convertHeapGraph(heapSnapshotGraph);
Set<String> seenPrints = {};
@ -363,48 +409,54 @@ class VMServiceHeapHelperSpecificExactLeakFinder
for (String s in duplicatePrints) {
int count = groupedByToString[s].length;
print("$s ($count)");
for (HeapGraphElement duplicate in groupedByToString[s]) {
print(" => ${duplicate.getPrettyPrint(_prettyPrints)}");
}
print("");
}
print("======================================");
for (String duplicateString in duplicatePrints) {
print("$duplicateString:");
List<HeapGraphElement> Function(HeapGraphElement target)
dijkstraTarget = dijkstra(graph.elements.first, graph);
for (HeapGraphElement duplicate
in groupedByToString[duplicateString]) {
print("${duplicate} pointed to from:");
print(duplicate.getPrettyPrint(_prettyPrints));
List<HeapGraphElement> shortestPath = dijkstraTarget(duplicate);
for (int i = 0; i < shortestPath.length - 1; i++) {
HeapGraphElement thisOne = shortestPath[i];
HeapGraphElement nextOne = shortestPath[i + 1];
String indexFieldName;
if (thisOne is HeapGraphElementActual) {
HeapGraphClass c = thisOne.class_;
if (c is HeapGraphClassActual) {
for (vmService.HeapSnapshotField field in c.origin.fields) {
if (thisOne.references[field.index] == nextOne) {
indexFieldName = field.name;
if (tryToFindShortestPathToLeaks) {
print("======================================");
for (String duplicateString in duplicatePrints) {
print("$duplicateString:");
List<HeapGraphElement> Function(HeapGraphElement target)
dijkstraTarget = dijkstra(graph.elements.first, graph);
for (HeapGraphElement duplicate
in groupedByToString[duplicateString]) {
print("${duplicate} pointed to from:");
print(duplicate.getPrettyPrint(_prettyPrints));
List<HeapGraphElement> shortestPath = dijkstraTarget(duplicate);
for (int i = 0; i < shortestPath.length - 1; i++) {
HeapGraphElement thisOne = shortestPath[i];
HeapGraphElement nextOne = shortestPath[i + 1];
String indexFieldName;
if (thisOne is HeapGraphElementActual) {
HeapGraphClass c = thisOne.class_;
if (c is HeapGraphClassActual) {
for (vmService.HeapSnapshotField field in c.origin.fields) {
if (thisOne.references[field.index] == nextOne) {
indexFieldName = field.name;
}
}
}
}
if (indexFieldName == null) {
indexFieldName = "no field found; index "
"${thisOne.references.indexOf(nextOne)}";
}
print(" $thisOne -> $nextOne ($indexFieldName)");
}
if (indexFieldName == null) {
indexFieldName = "no field found; index "
"${thisOne.references.indexOf(nextOne)}";
}
print(" $thisOne -> $nextOne ($indexFieldName)");
print("---------------------------");
}
print("---------------------------");
}
}
if (throwOnPossibleLeak) {
debugger();
throw "Possible leak detected.";
}
}
await _serviceClient.resume(isolateRef.id);
await _serviceClient.resume(_isolateRef.id);
_iterationNumber++;
}
}
@ -545,7 +597,6 @@ HeapGraph convertHeapGraph(vmService.HeapSnapshotGraph graph) {
}
};
}
return new HeapGraph(classSentinel, classes, elementSentinel, elements);
}

View file

@ -8,6 +8,7 @@
set -e
prepareOnly=false
leakTest=false
REMAINING_ARGS=()
while [[ $# -gt 0 ]]; do
@ -16,6 +17,10 @@ while [[ $# -gt 0 ]]; do
prepareOnly=true
shift
;;
--leakTest|--leak-test|--leak_test)
leakTest=true
shift
;;
*)
REMAINING_ARGS+=("$1")
shift
@ -27,6 +32,8 @@ set -- "${REMAINING_ARGS[@]}"
if $prepareOnly; then
echo "Will prepare only!"
elif $leakTest; then
echo "Will run leak test"
fi
checkout=$(pwd)
@ -106,6 +113,11 @@ if $prepareOnly; then
echo "Preparations complete!"
echo "Flutter is now in $tmpdir/flutter and the patched sdk in $tmpdir/flutter_patched_sdk"
echo "You can run the test with $dart --enable-asserts pkg/frontend_server/test/frontend_server_flutter.dart --flutterDir=$tmpdir/flutter --flutterPlatformDir=$tmpdir/flutter_patched_sdk"
elif $leakTest; then
$dart \
--enable-asserts \
pkg/front_end/test/flutter_gallery_leak_tester.dart \
--path=$tmpdir
else
$dart \
--enable-asserts \

View file

@ -3437,6 +3437,32 @@
}
]
},
{
"builders": [
"frontend-weekly"
],
"meta": {
"description": "This configuration is used for running slow frontend tests for instance weekly."
},
"steps": [
{
"name": "build dart",
"script": "tools/build.py",
"arguments": [
"--mode=release",
"--arch=x64",
"create_sdk"
]
},
{
"name": "run tests",
"script": "tools/bots/flutter/compile_flutter.sh",
"arguments": [
"--leakTest"
]
}
]
},
{
"builders": [
"fuzz-linux"