mirror of
https://github.com/flutter/flutter
synced 2024-10-01 05:54:08 +00:00
parent
bf1ddbf3cc
commit
e27f714497
11
.ci.yaml
11
.ci.yaml
|
@ -2378,6 +2378,17 @@ targets:
|
|||
benchmark: "true"
|
||||
scheduler: luci
|
||||
|
||||
- name: Linux_android android_choreographer_do_frame_test
|
||||
bringup: true
|
||||
recipe: devicelab/devicelab_drone
|
||||
presubmit: false
|
||||
timeout: 60
|
||||
properties:
|
||||
tags: >
|
||||
["devicelab","android","linux"]
|
||||
task_name: android_choreographer_do_frame_test
|
||||
scheduler: luci
|
||||
|
||||
- name: Mac build_aar_module_test
|
||||
recipe: devicelab/devicelab_drone
|
||||
timeout: 60
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
/dev/devicelab/bin/tasks/opacity_peephole_fade_transition_text_perf__e2e_summary.dart @flar @flutter/engine
|
||||
/dev/devicelab/bin/tasks/opacity_peephole_col_of_alpha_savelayer_rows_perf__e2e_summary.dart @flar @flutter/engine
|
||||
/dev/devicelab/bin/tasks/opacity_peephole_grid_of_alpha_savelayers_perf__e2e_summary.dart @flar @flutter/engine
|
||||
/dev/devicelab/bin/tasks/android_choreographer_do_frame_test.dart @blasten @flutter/engine
|
||||
|
||||
## Windows Android DeviceLab tests
|
||||
/dev/devicelab/bin/tasks/basic_material_app_win__compile.dart @zanderso @flutter/tool
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/tasks/android_choreographer_do_frame_test.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
await task(androidChoreographerDoFrameTest());
|
||||
}
|
193
dev/devicelab/lib/tasks/android_choreographer_do_frame_test.dart
Normal file
193
dev/devicelab/lib/tasks/android_choreographer_do_frame_test.dart
Normal file
|
@ -0,0 +1,193 @@
|
|||
// Copyright 2014 The Flutter Authors. 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 'package:path/path.dart' as path;
|
||||
|
||||
import '../framework/framework.dart';
|
||||
import '../framework/task_result.dart';
|
||||
import '../framework/utils.dart';
|
||||
|
||||
const List<String> kSentinelStr = <String>[
|
||||
'==== sentinel #1 ====',
|
||||
'==== sentinel #2 ====',
|
||||
'==== sentinel #3 ====',
|
||||
];
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/98973
|
||||
// This test ensures that Choreographer#doFrame finishes during application startup.
|
||||
// This test fails if the application hangs during this period.
|
||||
// https://ui.perfetto.dev/#!/?s=da6628c3a92456ae8fa3f345d0186e781da77e90fc8a64d073e9fee11d1e65
|
||||
TaskFunction androidChoreographerDoFrameTest({
|
||||
String? deviceIdOverride,
|
||||
Map<String, String>? environment,
|
||||
}) {
|
||||
final Directory tempDir = Directory.systemTemp
|
||||
.createTempSync('flutter_devicelab_android_surface_recreation.');
|
||||
return () async {
|
||||
try {
|
||||
section('Create app');
|
||||
await inDirectory(tempDir, () async {
|
||||
await flutter(
|
||||
'create',
|
||||
options: <String>[
|
||||
'--platforms',
|
||||
'android',
|
||||
'app',
|
||||
],
|
||||
environment: environment,
|
||||
);
|
||||
});
|
||||
|
||||
final File mainDart = File(path.join(
|
||||
tempDir.absolute.path,
|
||||
'app',
|
||||
'lib',
|
||||
'main.dart',
|
||||
));
|
||||
if (!mainDart.existsSync()) {
|
||||
return TaskResult.failure('${mainDart.path} does not exist');
|
||||
}
|
||||
|
||||
section('Patch lib/main.dart');
|
||||
await mainDart.writeAsString('''
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
print('${kSentinelStr[0]}');
|
||||
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
|
||||
|
||||
print('${kSentinelStr[1]}');
|
||||
// If the Android UI thread is blocked, then this Future won't resolve.
|
||||
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
|
||||
|
||||
print('${kSentinelStr[2]}');
|
||||
runApp(
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xff7c94b6),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
''', flush: true);
|
||||
|
||||
Future<TaskResult> runTestFor(String mode) async {
|
||||
int nextCompleterIdx = 0;
|
||||
final Map<String, Completer<void>> sentinelCompleters = <String, Completer<void>>{};
|
||||
for (final String sentinel in kSentinelStr) {
|
||||
sentinelCompleters[sentinel] = Completer<void>();
|
||||
}
|
||||
|
||||
section('Flutter run (mode: $mode)');
|
||||
await inDirectory(path.join(tempDir.path, 'app'), () async {
|
||||
final Process run = await startProcess(
|
||||
path.join(flutterDirectory.path, 'bin', 'flutter'),
|
||||
flutterCommandArgs('run', <String>['--$mode', '--verbose']),
|
||||
);
|
||||
|
||||
int currSentinelIdx = 0;
|
||||
final StreamSubscription<void> stdout = run.stdout
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
|
||||
if (currSentinelIdx < sentinelCompleters.keys.length &&
|
||||
line.contains(sentinelCompleters.keys.elementAt(currSentinelIdx))) {
|
||||
sentinelCompleters.values.elementAt(currSentinelIdx).complete();
|
||||
currSentinelIdx++;
|
||||
print('stdout(MATCHED): $line');
|
||||
} else {
|
||||
print('stdout: $line');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
final StreamSubscription<void> stderr = run.stderr
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
print('stderr: $line');
|
||||
});
|
||||
|
||||
final Completer<void> exitCompleter = Completer<void>();
|
||||
|
||||
unawaited(run.exitCode.then((int exitCode) {
|
||||
exitCompleter.complete();
|
||||
}));
|
||||
|
||||
section('Wait for sentinels (mode: $mode)');
|
||||
for (final Completer<void> completer in sentinelCompleters.values) {
|
||||
if (nextCompleterIdx == 0) {
|
||||
// Don't time out because we don't know how long it would take to get the first log.
|
||||
await Future.any<dynamic>(
|
||||
<Future<dynamic>>[
|
||||
completer.future,
|
||||
exitCompleter.future,
|
||||
],
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
// Time out since this should not take 1s after the first log was received.
|
||||
await Future.any<dynamic>(
|
||||
<Future<dynamic>>[
|
||||
completer.future.timeout(const Duration(seconds: 1)),
|
||||
exitCompleter.future,
|
||||
],
|
||||
);
|
||||
} on TimeoutException {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exitCompleter.isCompleted) {
|
||||
// The process exited.
|
||||
break;
|
||||
}
|
||||
nextCompleterIdx++;
|
||||
}
|
||||
|
||||
section('Quit app (mode: $mode)');
|
||||
run.stdin.write('q');
|
||||
await exitCompleter.future;
|
||||
|
||||
section('Stop listening to stdout and stderr (mode: $mode)');
|
||||
await stdout.cancel();
|
||||
await stderr.cancel();
|
||||
run.kill();
|
||||
});
|
||||
|
||||
if (nextCompleterIdx == sentinelCompleters.values.length) {
|
||||
return TaskResult.success(null);
|
||||
}
|
||||
final String nextSentinel = sentinelCompleters.keys.elementAt(nextCompleterIdx);
|
||||
return TaskResult.failure('Expected sentinel `$nextSentinel` in mode $mode');
|
||||
}
|
||||
|
||||
final TaskResult debugResult = await runTestFor('debug');
|
||||
if (debugResult.failed) {
|
||||
return debugResult;
|
||||
}
|
||||
|
||||
final TaskResult profileResult = await runTestFor('profile');
|
||||
if (profileResult.failed) {
|
||||
return profileResult;
|
||||
}
|
||||
|
||||
final TaskResult releaseResult = await runTestFor('release');
|
||||
if (releaseResult.failed) {
|
||||
return releaseResult;
|
||||
}
|
||||
|
||||
return TaskResult.success(null);
|
||||
} finally {
|
||||
rmTree(tempDir);
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue