mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

This auto-formats all *.dart files in the repository outside of the `engine` subdirectory and enforces that these files stay formatted with a presubmit check. **Reviewers:** Please carefully review all the commits except for the one titled "formatted". The "formatted" commit was auto-generated by running `dev/tools/format.sh -a -f`. The other commits were hand-crafted to prepare the repo for the formatting change. I recommend reviewing the commits one-by-one via the "Commits" tab and avoiding Github's "Files changed" tab as it will likely slow down your browser because of the size of this PR. --------- Co-authored-by: Kate Lovett <katelovett@google.com> Co-authored-by: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com>
170 lines
5.7 KiB
Dart
170 lines
5.7 KiB
Dart
// 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/devices.dart';
|
|
import '../framework/framework.dart';
|
|
import '../framework/task_result.dart';
|
|
import '../framework/utils.dart';
|
|
|
|
const String _kOrgName = 'com.example.activitydestroy';
|
|
|
|
final RegExp _lifecycleSentinelRegExp = RegExp(r'==== lifecycle\: (.+) ====');
|
|
|
|
/// Tests the following Android lifecycles: Activity#onStop(), Activity#onResume(), Activity#onPause(),
|
|
/// and Activity#onDestroy() from Dart perspective in debug, profile, and release modes.
|
|
TaskFunction androidLifecyclesTest({Map<String, String>? environment}) {
|
|
final Directory tempDir = Directory.systemTemp.createTempSync(
|
|
'flutter_devicelab_activity_destroy.',
|
|
);
|
|
return () async {
|
|
try {
|
|
section('Create app');
|
|
await inDirectory(tempDir, () async {
|
|
await flutter(
|
|
'create',
|
|
options: <String>['--platforms', 'android', '--org', _kOrgName, '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(r'''
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
class LifecycleObserver extends WidgetsBindingObserver {
|
|
@override
|
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
print('==== lifecycle: $state ====');
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
WidgetsBinding.instance.addObserver(LifecycleObserver());
|
|
runApp(Container());
|
|
}
|
|
''', flush: true);
|
|
|
|
Future<TaskResult> runTestFor(String mode) async {
|
|
final AndroidDevice device = await devices.workingDevice as AndroidDevice;
|
|
await device.unlock();
|
|
|
|
section('Flutter run on device running API level ${device.apiLevel} (mode: $mode)');
|
|
|
|
late Process run;
|
|
await inDirectory(path.join(tempDir.path, 'app'), () async {
|
|
run = await startFlutter('run', options: <String>['--$mode']);
|
|
});
|
|
|
|
final StreamController<String> lifecycles = StreamController<String>();
|
|
final StreamIterator<String> lifecycleItr = StreamIterator<String>(lifecycles.stream);
|
|
|
|
final StreamSubscription<void> stdout = run.stdout
|
|
.transform<String>(utf8.decoder)
|
|
.transform<String>(const LineSplitter())
|
|
.listen((String log) {
|
|
final RegExpMatch? match = _lifecycleSentinelRegExp.firstMatch(log);
|
|
print('stdout: $log');
|
|
if (match == null) {
|
|
return;
|
|
}
|
|
final String lifecycle = match[1]!;
|
|
print('stdout: Found app lifecycle: $lifecycle');
|
|
lifecycles.add(lifecycle);
|
|
});
|
|
|
|
final StreamSubscription<void> stderr = run.stderr
|
|
.transform<String>(utf8.decoder)
|
|
.transform<String>(const LineSplitter())
|
|
.listen((String log) {
|
|
print('stderr: $log');
|
|
});
|
|
|
|
Future<void> expectedLifecycle(String expected) async {
|
|
section('Wait for lifecycle: $expected (mode: $mode)');
|
|
await lifecycleItr.moveNext();
|
|
final String got = lifecycleItr.current;
|
|
if (expected != got) {
|
|
throw TaskResult.failure('expected lifecycles: `$expected`, but got` $got`');
|
|
}
|
|
}
|
|
|
|
await expectedLifecycle('AppLifecycleState.resumed');
|
|
|
|
section('Toggling app switch (mode: $mode)');
|
|
await device.shellExec('input', <String>['keyevent', 'KEYCODE_APP_SWITCH']);
|
|
|
|
await expectedLifecycle('AppLifecycleState.inactive');
|
|
if (device.apiLevel == 28) {
|
|
// Device lab currently runs 28.
|
|
await expectedLifecycle('AppLifecycleState.paused');
|
|
await expectedLifecycle('AppLifecycleState.detached');
|
|
}
|
|
|
|
section('Bring activity to foreground (mode: $mode)');
|
|
await device.shellExec('am', <String>['start', '-n', '$_kOrgName.app/.MainActivity']);
|
|
|
|
await expectedLifecycle('AppLifecycleState.resumed');
|
|
|
|
section('Launch Settings app (mode: $mode)');
|
|
await device.shellExec('am', <String>['start', '-a', 'android.settings.SETTINGS']);
|
|
|
|
await expectedLifecycle('AppLifecycleState.inactive');
|
|
if (device.apiLevel == 28) {
|
|
// Device lab currently runs 28.
|
|
await expectedLifecycle('AppLifecycleState.paused');
|
|
await expectedLifecycle('AppLifecycleState.detached');
|
|
}
|
|
|
|
section('Bring activity to foreground (mode: $mode)');
|
|
await device.shellExec('am', <String>['start', '-n', '$_kOrgName.app/.MainActivity']);
|
|
|
|
await expectedLifecycle('AppLifecycleState.resumed');
|
|
|
|
run.kill();
|
|
|
|
section('Stop subscriptions (mode: $mode)');
|
|
|
|
await lifecycleItr.cancel();
|
|
await lifecycles.close();
|
|
await stdout.cancel();
|
|
await stderr.cancel();
|
|
return TaskResult.success(null);
|
|
}
|
|
|
|
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);
|
|
} on TaskResult catch (error) {
|
|
return error;
|
|
} finally {
|
|
rmTree(tempDir);
|
|
}
|
|
};
|
|
}
|