mirror of
https://github.com/flutter/flutter
synced 2024-10-13 03:32:55 +00:00
254 lines
8.3 KiB
Dart
254 lines
8.3 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.
|
|
|
|
// This test builds an integration test from the list of samples in the
|
|
// examples/api/lib directory, and then runs it. The tests are just smoke tests,
|
|
// designed to start up each example and run it for a couple of frames to make
|
|
// sure it doesn't throw an exception or fail to compile.
|
|
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io' show Process, ProcessException, exitCode, stderr, stdout;
|
|
|
|
import 'package:file/file.dart';
|
|
import 'package:file/local.dart';
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:platform/platform.dart';
|
|
import 'package:process/process.dart';
|
|
|
|
FileSystem filesystem = const LocalFileSystem();
|
|
ProcessManager processManager = const LocalProcessManager();
|
|
Platform platform = const LocalPlatform();
|
|
|
|
FutureOr<dynamic> main() async {
|
|
if (!platform.isLinux && !platform.isWindows && !platform.isMacOS) {
|
|
stderr.writeln('Example smoke tests are only designed to run on desktop platforms');
|
|
exitCode = 4;
|
|
return;
|
|
}
|
|
final Directory flutterDir = filesystem.directory(
|
|
path.absolute(
|
|
path.dirname(
|
|
path.dirname(
|
|
path.dirname(platform.script.toFilePath()),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
final Directory apiDir = flutterDir.childDirectory('examples').childDirectory('api');
|
|
final File integrationTest = await generateTest(apiDir);
|
|
try {
|
|
await runSmokeTests(flutterDir: flutterDir, integrationTest: integrationTest, apiDir: apiDir);
|
|
} finally {
|
|
await cleanUp(integrationTest);
|
|
}
|
|
}
|
|
|
|
Future<void> cleanUp(File integrationTest) async {
|
|
try {
|
|
await integrationTest.delete();
|
|
// Delete the integration_test directory if it is empty.
|
|
await integrationTest.parent.delete();
|
|
} on FileSystemException {
|
|
// Ignore, there might be other files in there preventing it from
|
|
// being removed, or it might not exist.
|
|
}
|
|
}
|
|
|
|
// Executes the generated smoke test.
|
|
Future<void> runSmokeTests({
|
|
required Directory flutterDir,
|
|
required File integrationTest,
|
|
required Directory apiDir,
|
|
}) async {
|
|
final File flutterExe =
|
|
flutterDir.childDirectory('bin').childFile(platform.isWindows ? 'flutter.bat' : 'flutter');
|
|
final List<String> cmd = <String>[
|
|
// If we're in a container with no X display, then use the virtual framebuffer.
|
|
if (platform.isLinux &&
|
|
(platform.environment['DISPLAY'] == null ||
|
|
platform.environment['DISPLAY']!.isEmpty)) '/usr/bin/xvfb-run',
|
|
flutterExe.absolute.path,
|
|
'test',
|
|
'--reporter=expanded',
|
|
'--device-id=${platform.operatingSystem}',
|
|
integrationTest.absolute.path,
|
|
];
|
|
await runCommand(cmd, workingDirectory: apiDir);
|
|
}
|
|
|
|
// A class to hold information related to an example, used to generate names
|
|
// from for the tests.
|
|
class ExampleInfo {
|
|
ExampleInfo(this.file, Directory examplesLibDir)
|
|
: importPath = _getImportPath(file, examplesLibDir),
|
|
importName = '' {
|
|
importName = importPath.replaceAll(RegExp(r'\.dart$'), '').replaceAll(RegExp(r'\W'), '_');
|
|
}
|
|
|
|
final File file;
|
|
final String importPath;
|
|
String importName;
|
|
|
|
static String _getImportPath(File example, Directory examplesLibDir) {
|
|
final String relativePath =
|
|
path.relative(example.absolute.path, from: examplesLibDir.absolute.path);
|
|
// So that Windows paths are proper URIs in the import statements.
|
|
return path.toUri(relativePath).toFilePath(windows: false);
|
|
}
|
|
}
|
|
|
|
// Generates the combined smoke test.
|
|
Future<File> generateTest(Directory apiDir) async {
|
|
final Directory examplesLibDir = apiDir.childDirectory('lib');
|
|
|
|
// Get files from git, to avoid any non-repo files that might be in someone's
|
|
// workspace.
|
|
final List<String> gitFiles = (await runCommand(
|
|
<String>['git', 'ls-files', '**/*.dart'],
|
|
workingDirectory: examplesLibDir,
|
|
quiet: true,
|
|
)).replaceAll(r'\', '/')
|
|
.trim()
|
|
.split('\n');
|
|
final Iterable<File> examples = gitFiles.map<File>((String examplePath) {
|
|
return filesystem.file(path.join(examplesLibDir.absolute.path, examplePath));
|
|
});
|
|
|
|
// Collect the examples, and import them all as separate symbols.
|
|
final List<String> imports = <String>[];
|
|
imports.add('''import 'package:flutter/widgets.dart';''');
|
|
imports.add('''import 'package:flutter/scheduler.dart';''');
|
|
imports.add('''import 'package:flutter_test/flutter_test.dart';''');
|
|
imports.add('''import 'package:integration_test/integration_test.dart';''');
|
|
final List<ExampleInfo> infoList = <ExampleInfo>[];
|
|
for (final File example in examples) {
|
|
final ExampleInfo info = ExampleInfo(example, examplesLibDir);
|
|
infoList.add(info);
|
|
imports.add('''import 'package:flutter_api_samples/${info.importPath}' as ${info.importName};''');
|
|
}
|
|
imports.sort();
|
|
infoList.sort((ExampleInfo a, ExampleInfo b) => a.importPath.compareTo(b.importPath));
|
|
|
|
final StringBuffer buffer = StringBuffer();
|
|
buffer.writeln('// Temporary generated file. Do not commit.');
|
|
buffer.writeln("import 'dart:io';");
|
|
buffer.writeAll(imports, '\n');
|
|
buffer.writeln(r'''
|
|
|
|
|
|
import '../../../dev/manual_tests/test/mock_image_http.dart';
|
|
|
|
void main() {
|
|
IntegrationTestWidgetsFlutterBinding? binding;
|
|
try {
|
|
binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding;
|
|
} catch (e) {
|
|
stderr.writeln('Unable to initialize binding${binding == null ? '' : ' $binding'}: $e');
|
|
exitCode = 128;
|
|
return;
|
|
}
|
|
|
|
''');
|
|
for (final ExampleInfo info in infoList) {
|
|
buffer.writeln('''
|
|
testWidgets(
|
|
'Smoke test ${info.importPath}',
|
|
(WidgetTester tester) async {
|
|
final ErrorWidgetBuilder originalBuilder = ErrorWidget.builder;
|
|
try {
|
|
HttpOverrides.runZoned(() {
|
|
${info.importName}.main();
|
|
}, createHttpClient: (SecurityContext? context) => FakeHttpClient(context));
|
|
await tester.pump();
|
|
await tester.pump();
|
|
expect(find.byType(WidgetsApp), findsOneWidget);
|
|
} finally {
|
|
ErrorWidget.builder = originalBuilder;
|
|
timeDilation = 1.0;
|
|
}
|
|
},
|
|
);
|
|
''');
|
|
}
|
|
buffer.writeln('}');
|
|
|
|
final File integrationTest =
|
|
apiDir.childDirectory('integration_test').childFile('smoke_integration_test.dart');
|
|
integrationTest.createSync(recursive: true);
|
|
integrationTest.writeAsStringSync(buffer.toString());
|
|
return integrationTest;
|
|
}
|
|
|
|
// Run a command, and optionally stream the output as it runs, returning the
|
|
// stdout.
|
|
Future<String> runCommand(
|
|
List<String> cmd, {
|
|
required Directory workingDirectory,
|
|
bool quiet = false,
|
|
List<String>? output,
|
|
Map<String, String>? environment,
|
|
}) async {
|
|
final List<int> stdoutOutput = <int>[];
|
|
final List<int> combinedOutput = <int>[];
|
|
final Completer<void> stdoutComplete = Completer<void>();
|
|
final Completer<void> stderrComplete = Completer<void>();
|
|
|
|
late Process process;
|
|
Future<int> allComplete() async {
|
|
await stderrComplete.future;
|
|
await stdoutComplete.future;
|
|
return process.exitCode;
|
|
}
|
|
|
|
try {
|
|
process = await processManager.start(
|
|
cmd,
|
|
workingDirectory: workingDirectory.absolute.path,
|
|
environment: environment,
|
|
);
|
|
process.stdout.listen(
|
|
(List<int> event) {
|
|
stdoutOutput.addAll(event);
|
|
combinedOutput.addAll(event);
|
|
if (!quiet) {
|
|
stdout.add(event);
|
|
}
|
|
},
|
|
onDone: () async => stdoutComplete.complete(),
|
|
);
|
|
process.stderr.listen(
|
|
(List<int> event) {
|
|
combinedOutput.addAll(event);
|
|
if (!quiet) {
|
|
stderr.add(event);
|
|
}
|
|
},
|
|
onDone: () async => stderrComplete.complete(),
|
|
);
|
|
} on ProcessException catch (e) {
|
|
stderr.writeln('Running "${cmd.join(' ')}" in ${workingDirectory.path} '
|
|
'failed with:\n$e');
|
|
exitCode = 2;
|
|
return utf8.decode(stdoutOutput);
|
|
} on ArgumentError catch (e) {
|
|
stderr.writeln('Running "${cmd.join(' ')}" in ${workingDirectory.path} '
|
|
'failed with:\n$e');
|
|
exitCode = 3;
|
|
return utf8.decode(stdoutOutput);
|
|
}
|
|
|
|
final int processExitCode = await allComplete();
|
|
if (processExitCode != 0) {
|
|
stderr.writeln('Running "${cmd.join(' ')}" in ${workingDirectory.path} exited with code $processExitCode');
|
|
exitCode = processExitCode;
|
|
}
|
|
|
|
if (output != null) {
|
|
output.addAll(utf8.decode(combinedOutput).split('\n'));
|
|
}
|
|
|
|
return utf8.decode(stdoutOutput);
|
|
}
|