Unverified Commit 13bf3415 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] update build rules to depend on subset of package_config contents (#67165)

Split from #66776

Even if pub does not change the packge_config contents, it will still update a timestamp in one of the fields. This causes unnecessary rebuilds. To fix this, generate an additional file when running pub get that only contains the relevant fields and then update the KernelSnapshot rule to depend on it only.
parent ab9373bf
...@@ -192,7 +192,6 @@ class AndroidAot extends AotElfBase { ...@@ -192,7 +192,6 @@ class AndroidAot extends AotElfBase {
List<Source> get inputs => <Source>[ List<Source> get inputs => <Source>[
const Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/android.dart'), const Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/android.dart'),
const Source.pattern('{BUILD_DIR}/app.dill'), const Source.pattern('{BUILD_DIR}/app.dill'),
const Source.pattern('{PROJECT_DIR}/.packages'),
const Source.artifact(Artifact.engineDartBinary), const Source.artifact(Artifact.engineDartBinary),
const Source.artifact(Artifact.skyEnginePath), const Source.artifact(Artifact.skyEnginePath),
Source.artifact(Artifact.genSnapshot, Source.artifact(Artifact.genSnapshot,
......
...@@ -161,8 +161,12 @@ class ReleaseCopyFlutterBundle extends CopyFlutterBundle { ...@@ -161,8 +161,12 @@ class ReleaseCopyFlutterBundle extends CopyFlutterBundle {
List<Target> get dependencies => const <Target>[]; List<Target> get dependencies => const <Target>[];
} }
/// Generate a snapshot of the dart code used in the program. /// Generate a snapshot of the dart code used in the program.
///
/// Note that this target depends on the `.dart_tool/package_config.json` file
/// even though it is not listed as an input. Pub inserts a timestamp into
/// the file which causes unecessary rebuilds, so instead a subset of the contents
/// are used an input instead.
class KernelSnapshot extends Target { class KernelSnapshot extends Target {
const KernelSnapshot(); const KernelSnapshot();
...@@ -171,7 +175,7 @@ class KernelSnapshot extends Target { ...@@ -171,7 +175,7 @@ class KernelSnapshot extends Target {
@override @override
List<Source> get inputs => const <Source>[ List<Source> get inputs => const <Source>[
Source.pattern('{PROJECT_DIR}/.packages'), Source.pattern('{PROJECT_DIR}/.dart_tool/package_config_subset'),
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/common.dart'), Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/common.dart'),
Source.artifact(Artifact.platformKernelDill), Source.artifact(Artifact.platformKernelDill),
Source.artifact(Artifact.engineDartBinary), Source.artifact(Artifact.engineDartBinary),
...@@ -209,7 +213,9 @@ class KernelSnapshot extends Target { ...@@ -209,7 +213,9 @@ class KernelSnapshot extends Target {
} }
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]); final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final String targetFile = environment.defines[kTargetFile] ?? environment.fileSystem.path.join('lib', 'main.dart'); final String targetFile = environment.defines[kTargetFile] ?? environment.fileSystem.path.join('lib', 'main.dart');
final File packagesFile = environment.projectDir.childFile('.packages'); final File packagesFile = environment.projectDir
.childDirectory('.dart_tool')
.childFile('package_config.json');
final String targetFileAbsolute = environment.fileSystem.file(targetFile).absolute.path; final String targetFileAbsolute = environment.fileSystem.file(targetFile).absolute.path;
// everything besides 'false' is considered to be enabled. // everything besides 'false' is considered to be enabled.
final bool trackWidgetCreation = environment.defines[kTrackWidgetCreation] != 'false'; final bool trackWidgetCreation = environment.defines[kTrackWidgetCreation] != 'false';
...@@ -240,7 +246,7 @@ class KernelSnapshot extends Target { ...@@ -240,7 +246,7 @@ class KernelSnapshot extends Target {
} }
final PackageConfig packageConfig = await loadPackageConfigWithLogging( final PackageConfig packageConfig = await loadPackageConfigWithLogging(
environment.projectDir.childFile('.packages'), packagesFile,
logger: environment.logger, logger: environment.logger,
); );
...@@ -341,7 +347,6 @@ class AotElfProfile extends AotElfBase { ...@@ -341,7 +347,6 @@ class AotElfProfile extends AotElfBase {
List<Source> get inputs => <Source>[ List<Source> get inputs => <Source>[
const Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/common.dart'), const Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/common.dart'),
const Source.pattern('{BUILD_DIR}/app.dill'), const Source.pattern('{BUILD_DIR}/app.dill'),
const Source.pattern('{PROJECT_DIR}/.packages'),
const Source.artifact(Artifact.engineDartBinary), const Source.artifact(Artifact.engineDartBinary),
const Source.artifact(Artifact.skyEnginePath), const Source.artifact(Artifact.skyEnginePath),
Source.artifact(Artifact.genSnapshot, Source.artifact(Artifact.genSnapshot,
...@@ -374,7 +379,6 @@ class AotElfRelease extends AotElfBase { ...@@ -374,7 +379,6 @@ class AotElfRelease extends AotElfBase {
List<Source> get inputs => <Source>[ List<Source> get inputs => <Source>[
const Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/common.dart'), const Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/common.dart'),
const Source.pattern('{BUILD_DIR}/app.dill'), const Source.pattern('{BUILD_DIR}/app.dill'),
const Source.pattern('{PROJECT_DIR}/.packages'),
const Source.artifact(Artifact.engineDartBinary), const Source.artifact(Artifact.engineDartBinary),
const Source.artifact(Artifact.skyEnginePath), const Source.artifact(Artifact.skyEnginePath),
Source.artifact(Artifact.genSnapshot, Source.artifact(Artifact.genSnapshot,
......
...@@ -250,10 +250,11 @@ class _DefaultPub implements Pub { ...@@ -250,10 +250,11 @@ class _DefaultPub implements Pub {
'The time now is: $now' 'The time now is: $now'
); );
} }
// Insert references to synthetic flutter package. await _updatePackageConfig(
if (generateSyntheticPackage) { packageConfigFile,
await _updatePackageConfig(packageConfigFile, generatedDirectory); generatedDirectory,
} generateSyntheticPackage,
);
} }
@override @override
...@@ -458,13 +459,35 @@ class _DefaultPub implements Pub { ...@@ -458,13 +459,35 @@ class _DefaultPub implements Pub {
return environment; return environment;
} }
/// Insert the flutter_gen synthetic package into the package configuration file if /// Update the package configuration file.
/// there is an l10n.yaml. ///
Future<void> _updatePackageConfig(File packageConfigFile, Directory generatedDirectory) async { /// Creates a corresponding `package_config_subset` file that is used by the build
if (!packageConfigFile.existsSync()) { /// system to avoid rebuilds caused by an updated pub timestamp.
///
/// if [generateSyntheticPackage] is true then insert flutter_gen synthetic
/// package into the package configuration. This is used by the l10n localization
/// tooling to insert a new reference into the package_config file, allowing the import
/// of a package URI that is not specified in the pubspec.yaml
///
/// For more information, see:
/// * [generateLocalizations], `in lib/src/localizations/gen_l10n.dart`
Future<void> _updatePackageConfig(
File packageConfigFile,
Directory generatedDirectory,
bool generateSyntheticPackage,
) async {
final PackageConfig packageConfig = await loadPackageConfigWithLogging(packageConfigFile, logger: _logger);
packageConfigFile.parent
.childFile('package_config_subset')
.writeAsStringSync(_computePackageConfigSubset(
packageConfig,
_fileSystem,
));
if (!generateSyntheticPackage) {
return; return;
} }
final PackageConfig packageConfig = await loadPackageConfigWithLogging(packageConfigFile, logger: _logger);
final Package flutterGen = Package('flutter_gen', generatedDirectory.uri, languageVersion: LanguageVersion(2, 8)); final Package flutterGen = Package('flutter_gen', generatedDirectory.uri, languageVersion: LanguageVersion(2, 8));
if (packageConfig.packages.any((Package package) => package.name == 'flutter_gen')) { if (packageConfig.packages.any((Package package) => package.name == 'flutter_gen')) {
return; return;
...@@ -480,4 +503,18 @@ class _DefaultPub implements Pub { ...@@ -480,4 +503,18 @@ class _DefaultPub implements Pub {
await savePackageConfig(newPackageConfig, packageConfigFile.parent.parent); await savePackageConfig(newPackageConfig, packageConfigFile.parent.parent);
} }
} }
// Subset the package config file to only the parts that are relevant for
// rerunning the dart compiler.
String _computePackageConfigSubset(PackageConfig packageConfig, FileSystem fileSystem) {
final StringBuffer buffer = StringBuffer();
for (final Package package in packageConfig.packages) {
buffer.writeln(package.name);
buffer.writeln(package.languageVersion);
buffer.writeln(package.root);
buffer.writeln(package.packageUriRoot);
}
buffer.writeln(packageConfig.version);
return buffer.toString();
}
} }
...@@ -117,7 +117,6 @@ void main() { ...@@ -117,7 +117,6 @@ void main() {
platform: const LocalPlatform(), platform: const LocalPlatform(),
usage: globals.flutterUsage, usage: globals.flutterUsage,
botDetector: globals.botDetector, botDetector: globals.botDetector,
toolStampFile: () => globals.fs.file('test'),
); );
await pub.get( await pub.get(
context: PubContext.flutterTests, context: PubContext.flutterTests,
......
...@@ -72,7 +72,9 @@ void main() { ...@@ -72,7 +72,9 @@ void main() {
}); });
testWithoutContext('KernelSnapshot handles null result from kernel compilation', () async { testWithoutContext('KernelSnapshot handles null result from kernel compilation', () async {
fileSystem.file('.packages').writeAsStringSync('\n'); fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
final String build = androidEnvironment.buildDir.path; final String build = androidEnvironment.buildDir.path;
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[ FakeCommand(command: <String>[
...@@ -91,7 +93,7 @@ void main() { ...@@ -91,7 +93,7 @@ void main() {
'--aot', '--aot',
'--tfa', '--tfa',
'--packages', '--packages',
'/.packages', '/.dart_tool/package_config.json',
'--output-dill', '--output-dill',
'$build/app.dill', '$build/app.dill',
'--depfile', '--depfile',
...@@ -106,7 +108,9 @@ void main() { ...@@ -106,7 +108,9 @@ void main() {
}); });
testWithoutContext('KernelSnapshot does not use track widget creation on profile builds', () async { testWithoutContext('KernelSnapshot does not use track widget creation on profile builds', () async {
fileSystem.file('.packages').writeAsStringSync('\n'); fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
final String build = androidEnvironment.buildDir.path; final String build = androidEnvironment.buildDir.path;
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[ FakeCommand(command: <String>[
...@@ -125,7 +129,7 @@ void main() { ...@@ -125,7 +129,7 @@ void main() {
'--aot', '--aot',
'--tfa', '--tfa',
'--packages', '--packages',
'/.packages', '/.dart_tool/package_config.json',
'--output-dill', '--output-dill',
'$build/app.dill', '$build/app.dill',
'--depfile', '--depfile',
...@@ -140,7 +144,9 @@ void main() { ...@@ -140,7 +144,9 @@ void main() {
}); });
testWithoutContext('KernelSnapshot correctly handles an empty string in ExtraFrontEndOptions', () async { testWithoutContext('KernelSnapshot correctly handles an empty string in ExtraFrontEndOptions', () async {
fileSystem.file('.packages').writeAsStringSync('\n'); fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
final String build = androidEnvironment.buildDir.path; final String build = androidEnvironment.buildDir.path;
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[ FakeCommand(command: <String>[
...@@ -159,7 +165,7 @@ void main() { ...@@ -159,7 +165,7 @@ void main() {
'--aot', '--aot',
'--tfa', '--tfa',
'--packages', '--packages',
'/.packages', '/.dart_tool/package_config.json',
'--output-dill', '--output-dill',
'$build/app.dill', '$build/app.dill',
'--depfile', '--depfile',
...@@ -175,7 +181,9 @@ void main() { ...@@ -175,7 +181,9 @@ void main() {
}); });
testWithoutContext('KernelSnapshot correctly forwards ExtraFrontEndOptions', () async { testWithoutContext('KernelSnapshot correctly forwards ExtraFrontEndOptions', () async {
fileSystem.file('.packages').writeAsStringSync('\n'); fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
final String build = androidEnvironment.buildDir.path; final String build = androidEnvironment.buildDir.path;
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[ FakeCommand(command: <String>[
...@@ -194,7 +202,7 @@ void main() { ...@@ -194,7 +202,7 @@ void main() {
'--aot', '--aot',
'--tfa', '--tfa',
'--packages', '--packages',
'/.packages', '/.dart_tool/package_config.json',
'--output-dill', '--output-dill',
'$build/app.dill', '$build/app.dill',
'--depfile', '--depfile',
...@@ -212,7 +220,9 @@ void main() { ...@@ -212,7 +220,9 @@ void main() {
}); });
testWithoutContext('KernelSnapshot can disable track-widget-creation on debug builds', () async { testWithoutContext('KernelSnapshot can disable track-widget-creation on debug builds', () async {
fileSystem.file('.packages').writeAsStringSync('\n'); fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
final String build = androidEnvironment.buildDir.path; final String build = androidEnvironment.buildDir.path;
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[ FakeCommand(command: <String>[
...@@ -230,7 +240,7 @@ void main() { ...@@ -230,7 +240,7 @@ void main() {
...buildModeOptions(BuildMode.debug), ...buildModeOptions(BuildMode.debug),
'--no-link-platform', '--no-link-platform',
'--packages', '--packages',
'/.packages', '/.dart_tool/package_config.json',
'--output-dill', '--output-dill',
'$build/app.dill', '$build/app.dill',
'--depfile', '--depfile',
...@@ -247,7 +257,9 @@ void main() { ...@@ -247,7 +257,9 @@ void main() {
}); });
testWithoutContext('KernelSnapshot forces platform linking on debug for darwin target platforms', () async { testWithoutContext('KernelSnapshot forces platform linking on debug for darwin target platforms', () async {
fileSystem.file('.packages').writeAsStringSync('\n'); fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
final String build = androidEnvironment.buildDir.path; final String build = androidEnvironment.buildDir.path;
processManager.addCommands(<FakeCommand>[ processManager.addCommands(<FakeCommand>[
FakeCommand(command: <String>[ FakeCommand(command: <String>[
...@@ -264,7 +276,7 @@ void main() { ...@@ -264,7 +276,7 @@ void main() {
'-Ddart.developer.causal_async_stacks=true', '-Ddart.developer.causal_async_stacks=true',
...buildModeOptions(BuildMode.debug), ...buildModeOptions(BuildMode.debug),
'--packages', '--packages',
'/.packages', '/.dart_tool/package_config.json',
'--output-dill', '--output-dill',
'$build/app.dill', '$build/app.dill',
'--depfile', '--depfile',
...@@ -283,7 +295,9 @@ void main() { ...@@ -283,7 +295,9 @@ void main() {
}); });
testWithoutContext('KernelSnapshot does use track widget creation on debug builds', () async { testWithoutContext('KernelSnapshot does use track widget creation on debug builds', () async {
fileSystem.file('.packages').writeAsStringSync('\n'); fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
final Environment testEnvironment = Environment.test( final Environment testEnvironment = Environment.test(
fileSystem.currentDirectory, fileSystem.currentDirectory,
defines: <String, String>{ defines: <String, String>{
...@@ -313,7 +327,7 @@ void main() { ...@@ -313,7 +327,7 @@ void main() {
'--track-widget-creation', '--track-widget-creation',
'--no-link-platform', '--no-link-platform',
'--packages', '--packages',
'/.packages', '/.dart_tool/package_config.json',
'--output-dill', '--output-dill',
'$build/app.dill', '$build/app.dill',
'--depfile', '--depfile',
......
...@@ -223,6 +223,52 @@ void main() { ...@@ -223,6 +223,52 @@ void main() {
verify(usage.sendEvent('pub-result', 'flutter-tests', label: 'success')).called(1); verify(usage.sendEvent('pub-result', 'flutter-tests', label: 'success')).called(1);
}); });
testWithoutContext('package_config_subset file is generated from packages and not timestamp', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final MockUsage usage = MockUsage();
final Pub pub = Pub(
fileSystem: fileSystem,
logger: BufferLogger.test(),
processManager: MockProcessManager(0),
botDetector: const BotDetectorAlwaysNo(),
usage: usage,
platform: FakePlatform(
environment: const <String, String>{
'PUB_CACHE': 'custom/pub-cache/path',
}
),
);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('''
{"configVersion": 2,"packages": [
{
"name": "flutter_tools",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "2.7"
}
],"generated":"some-time"}
''');
await pub.get(
context: PubContext.flutterTests,
generateSyntheticPackage: true,
checkLastModified: false,
);
expect(
fileSystem.file('.dart_tool/package_config_subset').readAsStringSync(),
'flutter_tools\n'
'2.7\n'
'file:///\n'
'file:///lib/\n'
'2\n',
);
});
testWithoutContext('analytics sent on failure', () async { testWithoutContext('analytics sent on failure', () async {
MockDirectory.findCache = true; MockDirectory.findCache = true;
final MockUsage usage = MockUsage(); final MockUsage usage = MockUsage();
......
...@@ -5,79 +5,84 @@ ...@@ -5,79 +5,84 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import '../../src/common.dart'; import '../src/common.dart';
import '../../src/context.dart'; import 'test_utils.dart';
// This test depends on some files in ///dev/automated_tests/flutter_test/* // This test depends on some files in ///dev/automated_tests/flutter_test/*
Future<void> _testExclusionLock; final String automatedTestsDirectory = fileSystem.path.join('..', '..', 'dev', 'automated_tests');
final String missingDependencyDirectory = fileSystem.path.join('..', '..', 'dev', 'missing_dependency_tests');
final String flutterTestDirectory = fileSystem.path.join(automatedTestsDirectory, 'flutter_test');
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', platform.isWindows ? 'flutter.bat' : 'flutter');
void main() { void main() {
final String automatedTestsDirectory = globals.fs.path.join('..', '..', 'dev', 'automated_tests'); setUpAll(() async {
final String flutterTestDirectory = globals.fs.path.join(automatedTestsDirectory, 'flutter_test'); await processManager.run(
<String>[
flutterBin,
'pub',
'get'
],
workingDirectory: flutterTestDirectory
);
await processManager.run(
<String>[
flutterBin,
'pub',
'get'
],
workingDirectory: missingDependencyDirectory
);
});
testUsingContext('flutter test should not have extraneous error messages', () async { testWithoutContext('flutter test should not have extraneous error messages', () async {
Cache.flutterRoot = '../..';
return _testFile('trivial_widget', automatedTestsDirectory, flutterTestDirectory, exitCode: isZero); return _testFile('trivial_widget', automatedTestsDirectory, flutterTestDirectory, exitCode: isZero);
}); });
testUsingContext('flutter test should report nice errors for exceptions thrown within testWidgets()', () async { testWithoutContext('flutter test should report nice errors for exceptions thrown within testWidgets()', () async {
Cache.flutterRoot = '../..';
return _testFile('exception_handling', automatedTestsDirectory, flutterTestDirectory); return _testFile('exception_handling', automatedTestsDirectory, flutterTestDirectory);
}); });
testUsingContext('flutter test should report a nice error when a guarded function was called without await', () async { testWithoutContext('flutter test should report a nice error when a guarded function was called without await', () async {
Cache.flutterRoot = '../..';
return _testFile('test_async_utils_guarded', automatedTestsDirectory, flutterTestDirectory); return _testFile('test_async_utils_guarded', automatedTestsDirectory, flutterTestDirectory);
}); });
testUsingContext('flutter test should report a nice error when an async function was called without await', () async { testWithoutContext('flutter test should report a nice error when an async function was called without await', () async {
Cache.flutterRoot = '../..';
return _testFile('test_async_utils_unguarded', automatedTestsDirectory, flutterTestDirectory); return _testFile('test_async_utils_unguarded', automatedTestsDirectory, flutterTestDirectory);
}); });
testUsingContext('flutter test should report a nice error when a Ticker is left running', () async { testWithoutContext('flutter test should report a nice error when a Ticker is left running', () async {
Cache.flutterRoot = '../..';
return _testFile('ticker', automatedTestsDirectory, flutterTestDirectory); return _testFile('ticker', automatedTestsDirectory, flutterTestDirectory);
}); });
testUsingContext('flutter test should report a nice error when a pubspec.yaml is missing a flutter_test dependency', () async { testWithoutContext('flutter test should report a nice error when a pubspec.yaml is missing a flutter_test dependency', () async {
final String missingDependencyTests = globals.fs.path.join('..', '..', 'dev', 'missing_dependency_tests'); final String missingDependencyTests = fileSystem.path.join('..', '..', 'dev', 'missing_dependency_tests');
Cache.flutterRoot = '../..';
return _testFile('trivial', missingDependencyTests, missingDependencyTests); return _testFile('trivial', missingDependencyTests, missingDependencyTests);
}); });
testUsingContext('flutter test should report which user-created widget caused the error', () async { testWithoutContext('flutter test should report which user-created widget caused the error', () async {
Cache.flutterRoot = '../..';
return _testFile('print_user_created_ancestor', automatedTestsDirectory, flutterTestDirectory, return _testFile('print_user_created_ancestor', automatedTestsDirectory, flutterTestDirectory,
extraArguments: const <String>['--track-widget-creation']); extraArguments: const <String>['--track-widget-creation']);
}); });
testUsingContext('flutter test should report which user-created widget caused the error - no flag', () async { testWithoutContext('flutter test should report which user-created widget caused the error - no flag', () async {
Cache.flutterRoot = '../..';
return _testFile('print_user_created_ancestor_no_flag', automatedTestsDirectory, flutterTestDirectory, return _testFile('print_user_created_ancestor_no_flag', automatedTestsDirectory, flutterTestDirectory,
extraArguments: const <String>['--no-track-widget-creation']); extraArguments: const <String>['--no-track-widget-creation']);
}); });
testUsingContext('flutter test should report the correct user-created widget that caused the error', () async { testWithoutContext('flutter test should report the correct user-created widget that caused the error', () async {
Cache.flutterRoot = '../..';
return _testFile('print_correct_local_widget', automatedTestsDirectory, flutterTestDirectory, return _testFile('print_correct_local_widget', automatedTestsDirectory, flutterTestDirectory,
extraArguments: const <String>['--track-widget-creation']); extraArguments: const <String>['--track-widget-creation']);
}); });
testUsingContext('flutter test should can load assets within its own package', () async { testWithoutContext('flutter test should can load assets within its own package', () async {
Cache.flutterRoot = '../..';
return _testFile('package_assets', automatedTestsDirectory, flutterTestDirectory, exitCode: isZero); return _testFile('package_assets', automatedTestsDirectory, flutterTestDirectory, exitCode: isZero);
}); });
testUsingContext('flutter test should run a test when its name matches a regexp', () async { testWithoutContext('flutter test should run a test when its name matches a regexp', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory, final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory,
extraArguments: const <String>['--name', 'inc.*de']); extraArguments: const <String>['--name', 'inc.*de']);
if (!(result.stdout as String).contains('+1: All tests passed')) { if (!(result.stdout as String).contains('+1: All tests passed')) {
...@@ -86,8 +91,7 @@ void main() { ...@@ -86,8 +91,7 @@ void main() {
expect(result.exitCode, 0); expect(result.exitCode, 0);
}); });
testUsingContext('flutter test should run a test when its name contains a string', () async { testWithoutContext('flutter test should run a test when its name contains a string', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory, final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory,
extraArguments: const <String>['--plain-name', 'include']); extraArguments: const <String>['--plain-name', 'include']);
if (!(result.stdout as String).contains('+1: All tests passed')) { if (!(result.stdout as String).contains('+1: All tests passed')) {
...@@ -96,8 +100,7 @@ void main() { ...@@ -96,8 +100,7 @@ void main() {
expect(result.exitCode, 0); expect(result.exitCode, 0);
}); });
testUsingContext('flutter test should run a test with a given tag', () async { testWithoutContext('flutter test should run a test with a given tag', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('filtering_tag', automatedTestsDirectory, flutterTestDirectory, final ProcessResult result = await _runFlutterTest('filtering_tag', automatedTestsDirectory, flutterTestDirectory,
extraArguments: const <String>['--tags', 'include-tag']); extraArguments: const <String>['--tags', 'include-tag']);
if (!(result.stdout as String).contains('+1: All tests passed')) { if (!(result.stdout as String).contains('+1: All tests passed')) {
...@@ -106,8 +109,7 @@ void main() { ...@@ -106,8 +109,7 @@ void main() {
expect(result.exitCode, 0); expect(result.exitCode, 0);
}); });
testUsingContext('flutter test should not run a test with excluded tag', () async { testWithoutContext('flutter test should not run a test with excluded tag', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('filtering_tag', automatedTestsDirectory, flutterTestDirectory, final ProcessResult result = await _runFlutterTest('filtering_tag', automatedTestsDirectory, flutterTestDirectory,
extraArguments: const <String>['--exclude-tags', 'exclude-tag']); extraArguments: const <String>['--exclude-tags', 'exclude-tag']);
if (!(result.stdout as String).contains('+1: All tests passed')) { if (!(result.stdout as String).contains('+1: All tests passed')) {
...@@ -116,8 +118,7 @@ void main() { ...@@ -116,8 +118,7 @@ void main() {
expect(result.exitCode, 0); expect(result.exitCode, 0);
}); });
testUsingContext('flutter test should run all tests when tags are unspecified', () async { testWithoutContext('flutter test should run all tests when tags are unspecified', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('filtering_tag', automatedTestsDirectory, flutterTestDirectory); final ProcessResult result = await _runFlutterTest('filtering_tag', automatedTestsDirectory, flutterTestDirectory);
if (!(result.stdout as String).contains('+1 -1: Some tests failed')) { if (!(result.stdout as String).contains('+1 -1: Some tests failed')) {
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n'); fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
...@@ -125,8 +126,7 @@ void main() { ...@@ -125,8 +126,7 @@ void main() {
expect(result.exitCode, 1); expect(result.exitCode, 1);
}); });
testUsingContext('flutter test should run a widgetTest with a given tag', () async { testWithoutContext('flutter test should run a widgetTest with a given tag', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('filtering_tag_widget', automatedTestsDirectory, flutterTestDirectory, final ProcessResult result = await _runFlutterTest('filtering_tag_widget', automatedTestsDirectory, flutterTestDirectory,
extraArguments: const <String>['--tags', 'include-tag']); extraArguments: const <String>['--tags', 'include-tag']);
if (!(result.stdout as String).contains('+1: All tests passed')) { if (!(result.stdout as String).contains('+1: All tests passed')) {
...@@ -135,8 +135,7 @@ void main() { ...@@ -135,8 +135,7 @@ void main() {
expect(result.exitCode, 0); expect(result.exitCode, 0);
}); });
testUsingContext('flutter test should not run a widgetTest with excluded tag', () async { testWithoutContext('flutter test should not run a widgetTest with excluded tag', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('filtering_tag_widget', automatedTestsDirectory, flutterTestDirectory, final ProcessResult result = await _runFlutterTest('filtering_tag_widget', automatedTestsDirectory, flutterTestDirectory,
extraArguments: const <String>['--exclude-tags', 'exclude-tag']); extraArguments: const <String>['--exclude-tags', 'exclude-tag']);
if (!(result.stdout as String).contains('+1: All tests passed')) { if (!(result.stdout as String).contains('+1: All tests passed')) {
...@@ -145,8 +144,7 @@ void main() { ...@@ -145,8 +144,7 @@ void main() {
expect(result.exitCode, 0); expect(result.exitCode, 0);
}); });
testUsingContext('flutter test should run all widgetTest when tags are unspecified', () async { testWithoutContext('flutter test should run all widgetTest when tags are unspecified', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('filtering_tag_widget', automatedTestsDirectory, flutterTestDirectory); final ProcessResult result = await _runFlutterTest('filtering_tag_widget', automatedTestsDirectory, flutterTestDirectory);
if (!(result.stdout as String).contains('+1 -1: Some tests failed')) { if (!(result.stdout as String).contains('+1 -1: Some tests failed')) {
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n'); fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
...@@ -154,8 +152,7 @@ void main() { ...@@ -154,8 +152,7 @@ void main() {
expect(result.exitCode, 1); expect(result.exitCode, 1);
}); });
testUsingContext('flutter test should test runs to completion', () async { testWithoutContext('flutter test should test runs to completion', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('trivial', automatedTestsDirectory, flutterTestDirectory, final ProcessResult result = await _runFlutterTest('trivial', automatedTestsDirectory, flutterTestDirectory,
extraArguments: const <String>['--verbose']); extraArguments: const <String>['--verbose']);
final String stdout = result.stdout as String; final String stdout = result.stdout as String;
...@@ -172,8 +169,7 @@ void main() { ...@@ -172,8 +169,7 @@ void main() {
expect(result.exitCode, 0); expect(result.exitCode, 0);
}); });
testUsingContext('flutter test should run all tests inside of a directory with no trailing slash', () async { testWithoutContext('flutter test should run all tests inside of a directory with no trailing slash', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest(null, automatedTestsDirectory, flutterTestDirectory + '/child_directory', final ProcessResult result = await _runFlutterTest(null, automatedTestsDirectory, flutterTestDirectory + '/child_directory',
extraArguments: const <String>['--verbose']); extraArguments: const <String>['--verbose']);
final String stdout = result.stdout as String; final String stdout = result.stdout as String;
...@@ -199,16 +195,12 @@ Future<void> _testFile( ...@@ -199,16 +195,12 @@ Future<void> _testFile(
List<String> extraArguments = const <String>[], List<String> extraArguments = const <String>[],
}) async { }) async {
exitCode ??= isNonZero; exitCode ??= isNonZero;
final String fullTestExpectation = globals.fs.path.join(testDirectory, '${testName}_expectation.txt'); final String fullTestExpectation = fileSystem.path.join(testDirectory, '${testName}_expectation.txt');
final File expectationFile = globals.fs.file(fullTestExpectation); final File expectationFile = fileSystem.file(fullTestExpectation);
if (!expectationFile.existsSync()) { if (!expectationFile.existsSync()) {
fail('missing expectation file: $expectationFile'); fail('missing expectation file: $expectationFile');
} }
while (_testExclusionLock != null) {
await _testExclusionLock;
}
final ProcessResult exec = await _runFlutterTest( final ProcessResult exec = await _runFlutterTest(
testName, testName,
workingDirectory, workingDirectory,
...@@ -226,7 +218,7 @@ Future<void> _testFile( ...@@ -226,7 +218,7 @@ Future<void> _testFile(
} }
output.add('<<stderr>>'); output.add('<<stderr>>');
output.addAll((exec.stderr as String).split('\n')); output.addAll((exec.stderr as String).split('\n'));
final List<String> expectations = globals.fs.file(fullTestExpectation).readAsLinesSync(); final List<String> expectations = fileSystem.file(fullTestExpectation).readAsLinesSync();
bool allowSkip = false; bool allowSkip = false;
int expectationLineNumber = 0; int expectationLineNumber = 0;
int outputLineNumber = 0; int outputLineNumber = 0;
...@@ -288,44 +280,33 @@ Future<ProcessResult> _runFlutterTest( ...@@ -288,44 +280,33 @@ Future<ProcessResult> _runFlutterTest(
if (testName == null) { if (testName == null) {
// Test everything in the directory. // Test everything in the directory.
testPath = testDirectory; testPath = testDirectory;
final Directory directoryToTest = globals.fs.directory(testPath); final Directory directoryToTest = fileSystem.directory(testPath);
if (!directoryToTest.existsSync()) { if (!directoryToTest.existsSync()) {
fail('missing test directory: $directoryToTest'); fail('missing test directory: $directoryToTest');
} }
} else { } else {
// Test just a specific test file. // Test just a specific test file.
testPath = globals.fs.path.join(testDirectory, '${testName}_test.dart'); testPath = fileSystem.path.join(testDirectory, '${testName}_test.dart');
final File testFile = globals.fs.file(testPath); final File testFile = fileSystem.file(testPath);
if (!testFile.existsSync()) { if (!testFile.existsSync()) {
fail('missing test file: $testFile'); fail('missing test file: $testFile');
} }
} }
final List<String> args = <String>[ final List<String> args = <String>[
globals.fs.path.absolute(globals.fs.path.join('bin', 'flutter_tools.dart')),
'test', 'test',
'--no-color', '--no-color',
'--no-version-check', '--no-version-check',
'--no-pub',
...extraArguments, ...extraArguments,
testPath, testPath,
]; ];
while (_testExclusionLock != null) { return Process.run(
await _testExclusionLock; flutterBin, // Uses the precompiled flutter tool for faster tests,
}
final Completer<void> testExclusionCompleter = Completer<void>();
_testExclusionLock = testExclusionCompleter.future;
try {
return await Process.run(
globals.artifacts.getArtifactPath(Artifact.engineDartBinary),
args, args,
workingDirectory: workingDirectory, workingDirectory: workingDirectory,
stdoutEncoding: utf8, stdoutEncoding: utf8,
stderrEncoding: utf8, stderrEncoding: utf8,
); );
} finally {
_testExclusionLock = null;
testExclusionCompleter.complete();
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment