blob: 5d4689ca85e984c87c2c385535153b574f1d7779 [file] [log] [blame]
// Copyright 2022 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
// ignore_for_file: avoid_print
import 'dart:async';
import 'dart:convert';
import 'package:args/args.dart';
import 'package:devtools_shared/devtools_test_utils.dart';
import '_in_file_args.dart';
import '_test_app_driver.dart';
import '_utils.dart';
/// Runs one test.
///
/// Do not use this method directly, but instead use the run_tests.dart
/// which performs essential set up steps.
Future<void> runFlutterIntegrationTest(
DevToolsAppTestRunnerArgs testRunnerArgs,
TestFileArgs testFileArgs, {
required bool offline,
}) async {
IntegrationTestApp? testApp;
late String testAppUri;
if (!offline) {
if (testRunnerArgs.testAppUri == null) {
// Create the test app and start it.
try {
if (testRunnerArgs.testAppDevice == TestAppDevice.cli) {
debugLog(
'creating a TestDartCliApp with path ${testFileArgs.appPath}',
);
testApp = TestDartCliApp(appPath: testFileArgs.appPath);
} else {
debugLog(
'creating a TestFlutterApp with path ${testFileArgs.appPath} and '
'device ${testRunnerArgs.testAppDevice}',
);
testApp = TestFlutterApp(
appPath: testFileArgs.appPath,
appDevice: testRunnerArgs.testAppDevice,
);
}
debugLog('starting the test app');
await testApp.start();
} catch (e) {
// ignore: avoid-throw-in-catch-block, by design
throw Exception('Error starting test app: $e');
}
testAppUri = testApp.vmServiceUri.toString();
} else {
testAppUri = testRunnerArgs.testAppUri!;
}
}
// Run the flutter integration test.
final testRunner = IntegrationTestRunner();
try {
final testArgs = <String, Object>{if (!offline) 'service_uri': testAppUri};
final testTarget = testRunnerArgs.testTarget!;
debugLog('starting test run for $testTarget');
await testRunner.run(
testTarget,
testDriver: 'test_driver/integration_test.dart',
headless: testRunnerArgs.headless,
dartDefineArgs: [
'test_args=${jsonEncode(testArgs)}',
if (testFileArgs.experimentsOn) 'enable_experiments=true',
if (testRunnerArgs.updateGoldens) 'update_goldens=true',
],
debugLogging: debugTestScript,
);
} finally {
if (testApp != null) {
debugLog('killing the test app');
await testApp.stop();
}
debugLog('cancelling stream subscriptions');
await testRunner.cancelAllStreamSubscriptions();
}
}
class DevToolsAppTestRunnerArgs extends IntegrationTestRunnerArgs {
DevToolsAppTestRunnerArgs(super.args, {super.verifyValidTarget = true})
: super(addExtraArgs: _addExtraArgs) {
testAppDevice =
TestAppDevice.fromArgName(
argResults[_testAppDeviceArg] ?? TestAppDevice.flutterTester.argName,
)!;
}
/// The type of device for the test app to run on.
late final TestAppDevice testAppDevice;
/// The Vm Service URI for the test app to connect devtools to.
///
/// This value will only be used for tests with live connection.
String? get testAppUri => argResults[_testAppUriArg];
/// Whether golden images should be updated with the result of this test run.
bool get updateGoldens => argResults[_updateGoldensArg];
static const _testAppUriArg = 'test-app-uri';
static const _testAppDeviceArg = 'test-app-device';
static const _updateGoldensArg = 'update-goldens';
/// Adds additional argument handlers to [argParser] that are specific to
/// integration tests in package:devtools_app.
static void _addExtraArgs(ArgParser argParser) {
argParser
..addOption(
_testAppUriArg,
help:
'The vm service connection to use for the app that DevTools will '
'connect to during the integration test. If left empty, a sample app '
'will be spun up as part of the integration test process.',
)
..addOption(
_testAppDeviceArg,
help:
'The device to use for the test app that DevTools will connect to.',
)
..addFlag(
_updateGoldensArg,
negatable: false,
help: 'Updates the golden images with the results of this test run.',
);
}
}