mirror of
https://github.com/flutter/packages.git
synced 2025-08-24 03:18:54 +08:00

Consolidates the code to find all changed file paths into the `PackageLoopingCommand` class that is the base of almost all of the repo tooling commands. This in a preparatory PR for a future change to allow each command to define a list of files or file patterns that definitively *don't* affect that test, so that CI can be smarter about what tests to run (e.g., not running expensive integration tests for README changes). A side effect of this change is that tests of almost all commands now need a mock `GitDir` instance. This would add a lot of copy/pasted boilerplate to the test setup, and there is already too much of that, so instead this refactors common test setup: - Creating a memory file system - Populating it with a packages directory - Creating a RecordingProcessRunner to mock out process calls - Creating a mock GitDir that forwards to a RecordingProcessRunner into a helper method (using records and destructuring to easily return multiple values). While some tests don't need all of these steps, those that don't can easily ignore parts of it, and it will make it much easier to update tests in the future if they need them, and it makes the setup much more consistent which makes it easier to reason about test setup in general. Prep for https://github.com/flutter/flutter/issues/136394
906 lines
33 KiB
Dart
906 lines
33 KiB
Dart
// Copyright 2013 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:args/command_runner.dart';
|
|
import 'package:file/file.dart';
|
|
import 'package:flutter_plugin_tools/src/common/core.dart';
|
|
import 'package:flutter_plugin_tools/src/common/file_utils.dart';
|
|
import 'package:flutter_plugin_tools/src/firebase_test_lab_command.dart';
|
|
import 'package:git/git.dart';
|
|
import 'package:path/path.dart' as p;
|
|
import 'package:test/test.dart';
|
|
|
|
import 'mocks.dart';
|
|
import 'util.dart';
|
|
|
|
void main() {
|
|
group('FirebaseTestLabCommand', () {
|
|
late MockPlatform mockPlatform;
|
|
late Directory packagesDir;
|
|
late CommandRunner<void> runner;
|
|
late RecordingProcessRunner processRunner;
|
|
|
|
setUp(() {
|
|
mockPlatform = MockPlatform();
|
|
final GitDir gitDir;
|
|
(:packagesDir, :processRunner, gitProcessRunner: _, :gitDir) =
|
|
configureBaseCommandMocks(platform: mockPlatform);
|
|
final FirebaseTestLabCommand command = FirebaseTestLabCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: gitDir,
|
|
);
|
|
|
|
runner = CommandRunner<void>(
|
|
'firebase_test_lab_command', 'Test for $FirebaseTestLabCommand');
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
void writeJavaTestFile(RepositoryPackage plugin, String relativeFilePath,
|
|
{String runnerClass = 'FlutterTestRunner'}) {
|
|
childFileWithSubcomponents(
|
|
plugin.directory, p.posix.split(relativeFilePath))
|
|
.writeAsStringSync('''
|
|
@DartIntegrationTest
|
|
@RunWith($runnerClass.class)
|
|
public class MainActivityTest {
|
|
@Rule
|
|
public ActivityTestRule<FlutterActivity> rule = new ActivityTestRule<>(FlutterActivity.class);
|
|
}
|
|
''');
|
|
}
|
|
|
|
test('fails if gcloud auth fails', () async {
|
|
processRunner.mockProcessesForExecutable['gcloud'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(exitCode: 1), <String>['auth'])
|
|
];
|
|
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/foo_test.dart',
|
|
'example/android/gradlew',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--service-key=/path/to/key',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to activate gcloud account.'),
|
|
]));
|
|
});
|
|
|
|
test('retries gcloud set', () async {
|
|
processRunner.mockProcessesForExecutable['gcloud'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(), <String>['auth']),
|
|
FakeProcessInfo(MockProcess(exitCode: 1), <String>['config']),
|
|
];
|
|
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/foo_test.dart',
|
|
'example/android/gradlew',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--service-key=/path/to/key',
|
|
'--project=a-project'
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Warning: gcloud config set returned a non-zero exit code. Continuing anyway.'),
|
|
]));
|
|
});
|
|
|
|
test('only runs gcloud configuration once', () async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir, extraFiles: <String>[
|
|
'test/plugin_test.dart',
|
|
'example/integration_test/foo_test.dart',
|
|
'example/android/gradlew',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin1, javaTestFileRelativePath);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir, extraFiles: <String>[
|
|
'test/plugin_test.dart',
|
|
'example/integration_test/bar_test.dart',
|
|
'example/android/gradlew',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin2, javaTestFileRelativePath);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--project=a-project',
|
|
'--service-key=/path/to/key',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
'--device',
|
|
'model=seoul,version=26',
|
|
'--test-run-id',
|
|
'testRunId',
|
|
'--build-id',
|
|
'buildId',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin1'),
|
|
contains('Firebase project configured.'),
|
|
contains('Testing example/integration_test/foo_test.dart...'),
|
|
contains('Running for plugin2'),
|
|
contains('Testing example/integration_test/bar_test.dart...'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
'flutter',
|
|
const <String>['build', 'apk', '--debug', '--config-only'],
|
|
plugin1
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.path),
|
|
ProcessCall(
|
|
'gcloud',
|
|
'auth activate-service-account --key-file=/path/to/key'
|
|
.split(' '),
|
|
null),
|
|
ProcessCall(
|
|
'gcloud', 'config set project a-project'.split(' '), null),
|
|
ProcessCall(
|
|
'/packages/plugin1/example/android/gradlew',
|
|
'app:assembleAndroidTest -Pverbose=true'.split(' '),
|
|
'/packages/plugin1/example/android'),
|
|
ProcessCall(
|
|
'/packages/plugin1/example/android/gradlew',
|
|
'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin1/example/integration_test/foo_test.dart'
|
|
.split(' '),
|
|
'/packages/plugin1/example/android'),
|
|
ProcessCall(
|
|
'gcloud',
|
|
'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://a_bucket --results-dir=plugins_android_test/plugin1/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26'
|
|
.split(' '),
|
|
'/packages/plugin1/example'),
|
|
ProcessCall(
|
|
'flutter',
|
|
const <String>['build', 'apk', '--debug', '--config-only'],
|
|
plugin2
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.path),
|
|
ProcessCall(
|
|
'/packages/plugin2/example/android/gradlew',
|
|
'app:assembleAndroidTest -Pverbose=true'.split(' '),
|
|
'/packages/plugin2/example/android'),
|
|
ProcessCall(
|
|
'/packages/plugin2/example/android/gradlew',
|
|
'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin2/example/integration_test/bar_test.dart'
|
|
.split(' '),
|
|
'/packages/plugin2/example/android'),
|
|
ProcessCall(
|
|
'gcloud',
|
|
'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://a_bucket --results-dir=plugins_android_test/plugin2/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26'
|
|
.split(' '),
|
|
'/packages/plugin2/example'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('runs integration tests', () async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'test/plugin_test.dart',
|
|
'example/integration_test/bar_test.dart',
|
|
'example/integration_test/foo_test.dart',
|
|
'example/integration_test/should_not_run.dart',
|
|
'example/android/gradlew',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
'--device',
|
|
'model=seoul,version=26',
|
|
'--test-run-id',
|
|
'testRunId',
|
|
'--build-id',
|
|
'buildId',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('Testing example/integration_test/bar_test.dart...'),
|
|
contains('Testing example/integration_test/foo_test.dart...'),
|
|
]),
|
|
);
|
|
expect(output, isNot(contains('test/plugin_test.dart')));
|
|
expect(output,
|
|
isNot(contains('example/integration_test/should_not_run.dart')));
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
'flutter',
|
|
const <String>['build', 'apk', '--debug', '--config-only'],
|
|
plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.path),
|
|
ProcessCall(
|
|
'/packages/plugin/example/android/gradlew',
|
|
'app:assembleAndroidTest -Pverbose=true'.split(' '),
|
|
'/packages/plugin/example/android'),
|
|
ProcessCall(
|
|
'/packages/plugin/example/android/gradlew',
|
|
'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin/example/integration_test/bar_test.dart'
|
|
.split(' '),
|
|
'/packages/plugin/example/android'),
|
|
ProcessCall(
|
|
'gcloud',
|
|
'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://a_bucket --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26'
|
|
.split(' '),
|
|
'/packages/plugin/example'),
|
|
ProcessCall(
|
|
'/packages/plugin/example/android/gradlew',
|
|
'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin/example/integration_test/foo_test.dart'
|
|
.split(' '),
|
|
'/packages/plugin/example/android'),
|
|
ProcessCall(
|
|
'gcloud',
|
|
'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://a_bucket --results-dir=plugins_android_test/plugin/buildId/testRunId/example/1/ --device model=redfin,version=30 --device model=seoul,version=26'
|
|
.split(' '),
|
|
'/packages/plugin/example'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('runs for all examples', () async {
|
|
const List<String> examples = <String>['example1', 'example2'];
|
|
const String javaTestFileExampleRelativePath =
|
|
'android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir,
|
|
examples: examples,
|
|
extraFiles: <String>[
|
|
for (final String example in examples) ...<String>[
|
|
'example/$example/integration_test/a_test.dart',
|
|
'example/$example/android/gradlew',
|
|
'example/$example/$javaTestFileExampleRelativePath',
|
|
],
|
|
]);
|
|
for (final String example in examples) {
|
|
writeJavaTestFile(
|
|
plugin, 'example/$example/$javaTestFileExampleRelativePath');
|
|
}
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
'--device',
|
|
'model=seoul,version=26',
|
|
'--test-run-id',
|
|
'testRunId',
|
|
'--build-id',
|
|
'buildId',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Testing example/example1/integration_test/a_test.dart...'),
|
|
contains('Testing example/example2/integration_test/a_test.dart...'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAll(<ProcessCall>[
|
|
ProcessCall(
|
|
'/packages/plugin/example/example1/android/gradlew',
|
|
'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin/example/example1/integration_test/a_test.dart'
|
|
.split(' '),
|
|
'/packages/plugin/example/example1/android'),
|
|
ProcessCall(
|
|
'gcloud',
|
|
'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://a_bucket --results-dir=plugins_android_test/plugin/buildId/testRunId/example1/0/ --device model=redfin,version=30 --device model=seoul,version=26'
|
|
.split(' '),
|
|
'/packages/plugin/example/example1'),
|
|
ProcessCall(
|
|
'/packages/plugin/example/example2/android/gradlew',
|
|
'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin/example/example2/integration_test/a_test.dart'
|
|
.split(' '),
|
|
'/packages/plugin/example/example2/android'),
|
|
ProcessCall(
|
|
'gcloud',
|
|
'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://a_bucket --results-dir=plugins_android_test/plugin/buildId/testRunId/example2/0/ --device model=redfin,version=30 --device model=seoul,version=26'
|
|
.split(' '),
|
|
'/packages/plugin/example/example2'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if a test fails twice', () async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/bar_test.dart',
|
|
'example/integration_test/foo_test.dart',
|
|
'example/android/gradlew',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
processRunner.mockProcessesForExecutable['gcloud'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(exitCode: 1),
|
|
<String>['firebase', 'test']), // integration test #1
|
|
FakeProcessInfo(MockProcess(exitCode: 1),
|
|
<String>['firebase', 'test']), // integration test #1 retry
|
|
FakeProcessInfo(
|
|
MockProcess(), <String>['firebase', 'test']), // integration test #2
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Testing example/integration_test/bar_test.dart...'),
|
|
contains('Testing example/integration_test/foo_test.dart...'),
|
|
contains('plugin:\n'
|
|
' example/integration_test/bar_test.dart failed tests'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes with warning if a test fails once, then passes on retry',
|
|
() async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/bar_test.dart',
|
|
'example/integration_test/foo_test.dart',
|
|
'example/android/gradlew',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
processRunner.mockProcessesForExecutable['gcloud'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(exitCode: 1),
|
|
<String>['firebase', 'test']), // integration test #1
|
|
FakeProcessInfo(MockProcess(),
|
|
<String>['firebase', 'test']), // integration test #1 retry
|
|
FakeProcessInfo(
|
|
MockProcess(), <String>['firebase', 'test']), // integration test #2
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Testing example/integration_test/bar_test.dart...'),
|
|
contains('bar_test.dart failed on attempt 1. Retrying...'),
|
|
contains('Testing example/integration_test/foo_test.dart...'),
|
|
contains('Ran for 1 package(s) (1 with warnings)'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails for plugins with no androidTest directory', () async {
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/foo_test.dart',
|
|
'example/android/gradlew',
|
|
]);
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('No androidTest directory found.'),
|
|
contains('The following packages had errors:'),
|
|
contains('plugin:\n'
|
|
' No tests ran (use --exclude if this is intentional).'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('skips for non-plugin packages with no androidTest directory',
|
|
() async {
|
|
createFakePackage('a_package', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/foo_test.dart',
|
|
'example/android/gradlew',
|
|
]);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package'),
|
|
contains('No androidTest directory found.'),
|
|
contains('No examples support Android.'),
|
|
contains('Skipped 1 package'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails for packages with no integration test files', () async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/android/gradlew',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('No integration tests were run'),
|
|
contains('The following packages had errors:'),
|
|
contains('plugin:\n'
|
|
' No tests ran (use --exclude if this is intentional).'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails for packages with no integration_test runner', () async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'test/plugin_test.dart',
|
|
'example/integration_test/bar_test.dart',
|
|
'example/integration_test/foo_test.dart',
|
|
'example/integration_test/should_not_run.dart',
|
|
'example/android/gradlew',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
// Use the wrong @RunWith annotation.
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath,
|
|
runnerClass: 'AndroidJUnit4.class');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('No integration_test runner found. '
|
|
'See the integration_test package README for setup instructions.'),
|
|
contains('plugin:\n'
|
|
' No integration_test runner.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('supports kotlin implementation of integration_test runner', () async {
|
|
const String kotlinTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.kt';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'test/plugin_test.dart',
|
|
'example/integration_test/foo_test.dart',
|
|
'example/android/gradlew',
|
|
kotlinTestFileRelativePath,
|
|
]);
|
|
|
|
// Kotlin equivalent of the test runner
|
|
childFileWithSubcomponents(
|
|
plugin.directory, p.posix.split(kotlinTestFileRelativePath))
|
|
.writeAsStringSync('''
|
|
@DartIntegrationTest
|
|
@RunWith(FlutterTestRunner::class)
|
|
class MainActivityTest {
|
|
@JvmField @Rule var rule = ActivityTestRule(MainActivity::class.java)
|
|
}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
],
|
|
);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('Testing example/integration_test/foo_test.dart...'),
|
|
contains('Ran for 1 package')
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('skips packages with no android directory', () async {
|
|
createFakePackage('package', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/foo_test.dart',
|
|
]);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for package'),
|
|
contains('No examples support Android'),
|
|
]),
|
|
);
|
|
expect(output,
|
|
isNot(contains('Testing example/integration_test/foo_test.dart...')));
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[]),
|
|
);
|
|
});
|
|
|
|
test('builds if gradlew is missing', () async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/foo_test.dart',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
'--test-run-id',
|
|
'testRunId',
|
|
'--build-id',
|
|
'buildId',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('Running flutter build apk...'),
|
|
contains('Testing example/integration_test/foo_test.dart...'),
|
|
]),
|
|
);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
'flutter',
|
|
'build apk --debug --config-only'.split(' '),
|
|
plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.path,
|
|
),
|
|
ProcessCall(
|
|
'/packages/plugin/example/android/gradlew',
|
|
'app:assembleAndroidTest -Pverbose=true'.split(' '),
|
|
'/packages/plugin/example/android'),
|
|
ProcessCall(
|
|
'/packages/plugin/example/android/gradlew',
|
|
'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin/example/integration_test/foo_test.dart'
|
|
.split(' '),
|
|
'/packages/plugin/example/android'),
|
|
ProcessCall(
|
|
'gcloud',
|
|
'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://a_bucket --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30'
|
|
.split(' '),
|
|
'/packages/plugin/example'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if building to generate gradlew fails', () async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/foo_test.dart',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
processRunner.mockProcessesForExecutable['flutter'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(exitCode: 1), <String>['build'])
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to build example apk'),
|
|
]));
|
|
});
|
|
|
|
test('fails if assembleAndroidTest fails', () async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/foo_test.dart',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
final String gradlewPath = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.childFile('gradlew')
|
|
.path;
|
|
processRunner.mockProcessesForExecutable[gradlewPath] = <FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(exitCode: 1), <String>['app:assembleAndroidTest']),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to assemble androidTest'),
|
|
]));
|
|
});
|
|
|
|
test('fails if assembleDebug fails', () async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/foo_test.dart',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
final String gradlewPath = plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.childFile('gradlew')
|
|
.path;
|
|
processRunner.mockProcessesForExecutable[gradlewPath] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(), <String>['app:assembleAndroidTest']),
|
|
FakeProcessInfo(MockProcess(exitCode: 1), <String>['app:assembleDebug'])
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Could not build example/integration_test/foo_test.dart'),
|
|
contains('The following packages had errors:'),
|
|
contains(' plugin:\n'
|
|
' example/integration_test/foo_test.dart failed to build'),
|
|
]));
|
|
});
|
|
|
|
test('experimental flag', () async {
|
|
const String javaTestFileRelativePath =
|
|
'example/android/app/src/androidTest/MainActivityTest.java';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
|
|
'example/integration_test/foo_test.dart',
|
|
'example/android/gradlew',
|
|
javaTestFileRelativePath,
|
|
]);
|
|
writeJavaTestFile(plugin, javaTestFileRelativePath);
|
|
|
|
await runCapturingPrint(runner, <String>[
|
|
'firebase-test-lab',
|
|
'--results-bucket=a_bucket',
|
|
'--device',
|
|
'model=redfin,version=30',
|
|
'--test-run-id',
|
|
'testRunId',
|
|
'--build-id',
|
|
'buildId',
|
|
'--enable-experiment=exp1',
|
|
]);
|
|
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
orderedEquals(<ProcessCall>[
|
|
ProcessCall(
|
|
'flutter',
|
|
const <String>[
|
|
'build',
|
|
'apk',
|
|
'--debug',
|
|
'--config-only',
|
|
'--enable-experiment=exp1'
|
|
],
|
|
plugin
|
|
.getExamples()
|
|
.first
|
|
.platformDirectory(FlutterPlatform.android)
|
|
.path),
|
|
ProcessCall(
|
|
'/packages/plugin/example/android/gradlew',
|
|
'app:assembleAndroidTest -Pverbose=true -Pextra-front-end-options=--enable-experiment%3Dexp1 -Pextra-gen-snapshot-options=--enable-experiment%3Dexp1'
|
|
.split(' '),
|
|
'/packages/plugin/example/android'),
|
|
ProcessCall(
|
|
'/packages/plugin/example/android/gradlew',
|
|
'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin/example/integration_test/foo_test.dart -Pextra-front-end-options=--enable-experiment%3Dexp1 -Pextra-gen-snapshot-options=--enable-experiment%3Dexp1'
|
|
.split(' '),
|
|
'/packages/plugin/example/android'),
|
|
ProcessCall(
|
|
'gcloud',
|
|
'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://a_bucket --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30'
|
|
.split(' '),
|
|
'/packages/plugin/example'),
|
|
]),
|
|
);
|
|
});
|
|
});
|
|
}
|