Unverified Commit bafa03e5 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] Remove mocking and simplify Dart target tests (#50688)

parent 8872d1d8
...@@ -241,7 +241,8 @@ class PackageUriMapper { ...@@ -241,7 +241,8 @@ class PackageUriMapper {
} }
} }
List<String> _buildModeOptions(BuildMode mode) { /// List the preconfigured build options for a given build mode.
List<String> buildModeOptions(BuildMode mode) {
switch (mode) { switch (mode) {
case BuildMode.debug: case BuildMode.debug:
return <String>[ return <String>[
...@@ -316,7 +317,7 @@ class KernelCompiler { ...@@ -316,7 +317,7 @@ class KernelCompiler {
'-Ddart.developer.causal_async_stacks=${buildMode == BuildMode.debug}', '-Ddart.developer.causal_async_stacks=${buildMode == BuildMode.debug}',
for (final Object dartDefine in dartDefines) for (final Object dartDefine in dartDefines)
'-D$dartDefine', '-D$dartDefine',
..._buildModeOptions(buildMode), ...buildModeOptions(buildMode),
if (trackWidgetCreation) '--track-widget-creation', if (trackWidgetCreation) '--track-widget-creation',
if (!linkPlatformKernelIn) '--no-link-platform', if (!linkPlatformKernelIn) '--no-link-platform',
if (aot) ...<String>[ if (aot) ...<String>[
...@@ -357,14 +358,9 @@ class KernelCompiler { ...@@ -357,14 +358,9 @@ class KernelCompiler {
]; ];
globals.printTrace(command.join(' ')); globals.printTrace(command.join(' '));
final Process server = await globals.processManager final Process server = await globals.processManager.start(command);
.start(command)
.catchError((dynamic error, StackTrace stack) {
globals.printError('Failed to start frontend server $error, $stack');
});
final StdoutHandler _stdoutHandler = StdoutHandler(); final StdoutHandler _stdoutHandler = StdoutHandler();
server.stderr server.stderr
.transform<String>(utf8.decoder) .transform<String>(utf8.decoder)
.listen(globals.printError); .listen(globals.printError);
...@@ -678,7 +674,7 @@ class DefaultResidentCompiler implements ResidentCompiler { ...@@ -678,7 +674,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
'--packages', '--packages',
packagesPath, packagesPath,
], ],
..._buildModeOptions(buildMode), ...buildModeOptions(buildMode),
if (trackWidgetCreation) '--track-widget-creation', if (trackWidgetCreation) '--track-widget-creation',
if (fileSystemRoots != null) if (fileSystemRoots != null)
for (final String root in fileSystemRoots) ...<String>[ for (final String root in fileSystemRoots) ...<String>[
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter_tools/src/base/build.dart'; import 'package:file/memory.dart';
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/process.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/exceptions.dart'; import 'package:flutter_tools/src/build_system/exceptions.dart';
...@@ -12,32 +12,30 @@ import 'package:flutter_tools/src/build_system/targets/dart.dart'; ...@@ -12,32 +12,30 @@ import 'package:flutter_tools/src/build_system/targets/dart.dart';
import 'package:flutter_tools/src/build_system/targets/ios.dart'; import 'package:flutter_tools/src/build_system/targets/ios.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/macos/xcode.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:platform/platform.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import '../../../src/common.dart'; import '../../../src/common.dart';
import '../../../src/mocks.dart'; import '../../../src/fake_process_manager.dart';
import '../../../src/testbed.dart'; import '../../../src/testbed.dart';
const String kBoundaryKey = '4d2d9609-c662-4571-afde-31410f96caa6';
const String kElfAot = '--snapshot_kind=app-aot-elf';
const String kAssemblyAot = '--snapshot_kind=app-aot-assembly';
void main() { void main() {
const BuildSystem buildSystem = BuildSystem();
Testbed testbed; Testbed testbed;
FakeProcessManager processManager;
Environment androidEnvironment; Environment androidEnvironment;
Environment iosEnvironment; Environment iosEnvironment;
MockProcessManager mockProcessManager; Artifacts artifacts;
MockXcode mockXcode;
setUpAll(() { setUpAll(() {
Cache.disableLocking(); Cache.disableLocking();
}); });
setUp(() { setUp(() {
mockXcode = MockXcode();
mockProcessManager = MockProcessManager();
testbed = Testbed(setup: () { testbed = Testbed(setup: () {
androidEnvironment = Environment.test( androidEnvironment = Environment.test(
globals.fs.currentDirectory, globals.fs.currentDirectory,
...@@ -46,6 +44,7 @@ void main() { ...@@ -46,6 +44,7 @@ void main() {
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm), kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
}, },
); );
androidEnvironment.buildDir.createSync(recursive: true);
iosEnvironment = Environment.test( iosEnvironment = Environment.test(
globals.fs.currentDirectory, globals.fs.currentDirectory,
defines: <String, String>{ defines: <String, String>{
...@@ -53,427 +52,462 @@ void main() { ...@@ -53,427 +52,462 @@ void main() {
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios), kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios),
}, },
); );
HostPlatform hostPlatform; iosEnvironment.buildDir.createSync(recursive: true);
if (globals.platform.isWindows) { artifacts = CachedArtifacts(
hostPlatform = HostPlatform.windows_x64; cache: globals.cache,
} else if (globals.platform.isLinux) { platform: globals.platform,
hostPlatform = HostPlatform.linux_x64; fileSystem: globals.fs,
} else if (globals.platform.isMacOS) { );
hostPlatform = HostPlatform.darwin_x64;
} else {
assert(false);
}
final String engineArtifacts = globals.fs.path.join('bin', 'cache',
'artifacts', 'engine');
final List<String> paths = <String>[
globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart.exe'),
globals.fs.path.join(engineArtifacts, getNameForHostPlatform(hostPlatform),
'frontend_server.dart.snapshot'),
globals.fs.path.join(engineArtifacts, 'android-arm-profile',
getNameForHostPlatform(hostPlatform), 'gen_snapshot'),
globals.fs.path.join(engineArtifacts, 'ios-profile', 'gen_snapshot'),
globals.fs.path.join(engineArtifacts, 'common', 'flutter_patched_sdk',
'platform_strong.dill'),
globals.fs.path.join('lib', 'foo.dart'),
globals.fs.path.join('lib', 'bar.dart'),
globals.fs.path.join('lib', 'fizz'),
globals.fs.path.join('packages', 'flutter_tools', 'lib', 'src', 'build_system', 'targets', 'dart.dart'),
globals.fs.path.join('packages', 'flutter_tools', 'lib', 'src', 'build_system', 'targets', 'ios.dart'),
];
for (final String path in paths) {
globals.fs.file(path).createSync(recursive: true);
}
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
KernelCompilerFactory: () => FakeKernelCompilerFactory(), Platform: () => FakePlatform(operatingSystem: 'macos', environment: <String, String>{}),
GenSnapshot: () => FakeGenSnapshot(), FileSystem: () => MemoryFileSystem.test(style: FileSystemStyle.posix),
ProcessManager: () => processManager,
}); });
}); });
test('kernel_snapshot Produces correct output directory', () => testbed.run(() async { test('KernelSnapshot throws error if missing build mode', () => testbed.run(() async {
await buildSystem.build(const KernelSnapshot(), androidEnvironment); androidEnvironment.defines.remove(kBuildMode);
expect(
expect(globals.fs.file(globals.fs.path.join(androidEnvironment.buildDir.path,'app.dill')).existsSync(), true); const KernelSnapshot().build(androidEnvironment),
throwsA(isInstanceOf<MissingDefineException>()));
})); }));
test('kernel_snapshot throws error if missing build mode', () => testbed.run(() async { test('KernelSnapshot handles null result from kernel compilation', () => testbed.run(() async {
final BuildResult result = await buildSystem.build(const KernelSnapshot(), globals.fs.file('.packages').writeAsStringSync('\n');
androidEnvironment..defines.remove(kBuildMode)); final String build = androidEnvironment.buildDir.path;
processManager = FakeProcessManager.list(<FakeCommand>[
expect(result.exceptions.values.single.exception, isA<MissingDefineException>()); FakeCommand(command: <String>[
artifacts.getArtifactPath(Artifact.engineDartBinary),
artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
'--sdk-root',
artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath) + '/',
'--target=flutter',
'-Ddart.developer.causal_async_stacks=false',
...buildModeOptions(BuildMode.profile),
'--aot',
'--tfa',
'--packages',
'/.packages',
'--output-dill',
'$build/app.dill',
'--depfile',
'$build/kernel_snapshot.d',
'/lib/main.dart',
], exitCode: 1),
]);
await expectLater(() => const KernelSnapshot().build(androidEnvironment),
throwsA(isA<Exception>()));
expect(processManager.hasRemainingExpectations, false);
})); }));
test('kernel_snapshot handles null result from kernel compilation', () => testbed.run(() async { test('KernelSnapshot does not use track widget creation on profile builds', () => testbed.run(() async {
final FakeKernelCompilerFactory fakeKernelCompilerFactory = kernelCompilerFactory as FakeKernelCompilerFactory; globals.fs.file('.packages').writeAsStringSync('\n');
fakeKernelCompilerFactory.kernelCompiler = MockKernelCompiler(); final String build = androidEnvironment.buildDir.path;
when(fakeKernelCompilerFactory.kernelCompiler.compile( processManager = FakeProcessManager.list(<FakeCommand>[
sdkRoot: anyNamed('sdkRoot'), FakeCommand(command: <String>[
mainPath: anyNamed('mainPath'), artifacts.getArtifactPath(Artifact.engineDartBinary),
outputFilePath: anyNamed('outputFilePath'), artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
depFilePath: anyNamed('depFilePath'), '--sdk-root',
targetModel: anyNamed('targetModel'), artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath) + '/',
linkPlatformKernelIn: anyNamed('linkPlatformKernelIn'), '--target=flutter',
aot: anyNamed('aot'), '-Ddart.developer.causal_async_stacks=false',
buildMode: anyNamed('buildMode'), ...buildModeOptions(BuildMode.profile),
trackWidgetCreation: anyNamed('trackWidgetCreation'), '--aot',
extraFrontEndOptions: anyNamed('extraFrontEndOptions'), '--tfa',
packagesPath: anyNamed('packagesPath'), '--packages',
fileSystemRoots: anyNamed('fileSystemRoots'), '/.packages',
fileSystemScheme: anyNamed('fileSystemScheme'), '--output-dill',
platformDill: anyNamed('platformDill'), '$build/app.dill',
initializeFromDill: anyNamed('initializeFromDill'), '--depfile',
dartDefines: anyNamed('dartDefines'), '$build/kernel_snapshot.d',
)).thenAnswer((Invocation invocation) async { '/lib/main.dart',
return null; ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
}); ]);
final BuildResult result = await buildSystem.build(const KernelSnapshot(), androidEnvironment);
expect(result.exceptions.values.single.exception, isA<Exception>());
}));
test('kernel_snapshot does not use track widget creation on profile builds', () => testbed.run(() async {
final MockKernelCompiler mockKernelCompiler = MockKernelCompiler();
when(kernelCompilerFactory.create(any)).thenAnswer((Invocation _) async {
return mockKernelCompiler;
});
when(mockKernelCompiler.compile(
sdkRoot: anyNamed('sdkRoot'),
aot: anyNamed('aot'),
buildMode: anyNamed('buildMode'),
trackWidgetCreation: false,
targetModel: anyNamed('targetModel'),
outputFilePath: anyNamed('outputFilePath'),
depFilePath: anyNamed('depFilePath'),
packagesPath: anyNamed('packagesPath'),
mainPath: anyNamed('mainPath'),
extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
fileSystemRoots: anyNamed('fileSystemRoots'),
fileSystemScheme: anyNamed('fileSystemScheme'),
linkPlatformKernelIn: anyNamed('linkPlatformKernelIn'),
dartDefines: anyNamed('dartDefines'),
)).thenAnswer((Invocation _) async {
return const CompilerOutput('example', 0, <Uri>[]);
});
await const KernelSnapshot().build(androidEnvironment); await const KernelSnapshot().build(androidEnvironment);
}, overrides: <Type, Generator>{
KernelCompilerFactory: () => MockKernelCompilerFactory(), expect(processManager.hasRemainingExpectations, false);
})); }));
test('kernel_snapshot can disable track-widget-creation on debug builds', () => testbed.run(() async { test('KernelSnapshot can disable track-widget-creation on debug builds', () => testbed.run(() async {
final MockKernelCompiler mockKernelCompiler = MockKernelCompiler(); globals.fs.file('.packages').writeAsStringSync('\n');
when(kernelCompilerFactory.create(any)).thenAnswer((Invocation _) async { final String build = androidEnvironment.buildDir.path;
return mockKernelCompiler; processManager = FakeProcessManager.list(<FakeCommand>[
}); FakeCommand(command: <String>[
when(mockKernelCompiler.compile( artifacts.getArtifactPath(Artifact.engineDartBinary),
sdkRoot: anyNamed('sdkRoot'), artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
aot: anyNamed('aot'), '--sdk-root',
buildMode: anyNamed('buildMode'), artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath) + '/',
trackWidgetCreation: false, '--target=flutter',
targetModel: anyNamed('targetModel'), '-Ddart.developer.causal_async_stacks=true',
outputFilePath: anyNamed('outputFilePath'), ...buildModeOptions(BuildMode.debug),
depFilePath: anyNamed('depFilePath'), '--no-link-platform',
packagesPath: anyNamed('packagesPath'), '--packages',
mainPath: anyNamed('mainPath'), '/.packages',
extraFrontEndOptions: anyNamed('extraFrontEndOptions'), '--output-dill',
fileSystemRoots: anyNamed('fileSystemRoots'), '$build/app.dill',
fileSystemScheme: anyNamed('fileSystemScheme'), '--depfile',
linkPlatformKernelIn: false, '$build/kernel_snapshot.d',
dartDefines: anyNamed('dartDefines'), '/lib/main.dart',
)).thenAnswer((Invocation _) async { ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
return const CompilerOutput('example', 0, <Uri>[]); ]);
});
await const KernelSnapshot().build(androidEnvironment await const KernelSnapshot().build(androidEnvironment
..defines[kBuildMode] = 'debug' ..defines[kBuildMode] = getNameForBuildMode(BuildMode.debug)
..defines[kTrackWidgetCreation] = 'false'); ..defines[kTrackWidgetCreation] = 'false');
}, overrides: <Type, Generator>{
KernelCompilerFactory: () => MockKernelCompilerFactory(), expect(processManager.hasRemainingExpectations, false);
})); }));
test('kernel_snapshot forces platform linking on debug for darwin target platforms', () => testbed.run(() async { test('KernelSnapshot forces platform linking on debug for darwin target platforms', () => testbed.run(() async {
final MockKernelCompiler mockKernelCompiler = MockKernelCompiler(); globals.fs.file('.packages').writeAsStringSync('\n');
when(kernelCompilerFactory.create(any)).thenAnswer((Invocation _) async { final String build = androidEnvironment.buildDir.path;
return mockKernelCompiler; processManager = FakeProcessManager.list(<FakeCommand>[
}); FakeCommand(command: <String>[
when(mockKernelCompiler.compile( artifacts.getArtifactPath(Artifact.engineDartBinary),
sdkRoot: anyNamed('sdkRoot'), artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
aot: anyNamed('aot'), '--sdk-root',
buildMode: anyNamed('buildMode'), artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath) + '/',
trackWidgetCreation: anyNamed('trackWidgetCreation'), '--target=flutter',
targetModel: anyNamed('targetModel'), '-Ddart.developer.causal_async_stacks=true',
outputFilePath: anyNamed('outputFilePath'), ...buildModeOptions(BuildMode.debug),
depFilePath: anyNamed('depFilePath'), '--packages',
packagesPath: anyNamed('packagesPath'), '/.packages',
mainPath: anyNamed('mainPath'), '--output-dill',
extraFrontEndOptions: anyNamed('extraFrontEndOptions'), '$build/app.dill',
fileSystemRoots: anyNamed('fileSystemRoots'), '--depfile',
fileSystemScheme: anyNamed('fileSystemScheme'), '$build/kernel_snapshot.d',
linkPlatformKernelIn: true, '/lib/main.dart',
dartDefines: anyNamed('dartDefines'), ], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
)).thenAnswer((Invocation _) async { ]);
return const CompilerOutput('example', 0, <Uri>[]);
});
await const KernelSnapshot().build(androidEnvironment await const KernelSnapshot().build(androidEnvironment
..defines[kTargetPlatform] = 'darwin-x64' ..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin_x64)
..defines[kBuildMode] = 'debug' ..defines[kBuildMode] = getNameForBuildMode(BuildMode.debug)
..defines[kTrackWidgetCreation] = 'false' ..defines[kTrackWidgetCreation] = 'false'
); );
}, overrides: <Type, Generator>{
KernelCompilerFactory: () => MockKernelCompilerFactory(),
}));
test('kernel_snapshot does use track widget creation on debug builds', () => testbed.run(() async { expect(processManager.hasRemainingExpectations, false);
final MockKernelCompiler mockKernelCompiler = MockKernelCompiler(); }));
when(kernelCompilerFactory.create(any)).thenAnswer((Invocation _) async {
return mockKernelCompiler;
});
when(mockKernelCompiler.compile(
sdkRoot: anyNamed('sdkRoot'),
aot: anyNamed('aot'),
buildMode: anyNamed('buildMode'),
trackWidgetCreation: true,
targetModel: anyNamed('targetModel'),
outputFilePath: anyNamed('outputFilePath'),
depFilePath: anyNamed('depFilePath'),
packagesPath: anyNamed('packagesPath'),
mainPath: anyNamed('mainPath'),
extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
fileSystemRoots: anyNamed('fileSystemRoots'),
fileSystemScheme: anyNamed('fileSystemScheme'),
linkPlatformKernelIn: false,
dartDefines: anyNamed('dartDefines'),
)).thenAnswer((Invocation _) async {
return const CompilerOutput('example', 0, <Uri>[]);
});
await const KernelSnapshot().build(Environment.test( test('KernelSnapshot does use track widget creation on debug builds', () => testbed.run(() async {
globals.fs.file('.packages').writeAsStringSync('\n');
final Environment testEnvironment = Environment.test(
globals.fs.currentDirectory, globals.fs.currentDirectory,
defines: <String, String>{ defines: <String, String>{
kBuildMode: 'debug', kBuildMode: getNameForBuildMode(BuildMode.debug),
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm), kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
})); },
}, overrides: <Type, Generator>{ );
KernelCompilerFactory: () => MockKernelCompilerFactory(), final String build = testEnvironment.buildDir.path;
processManager = FakeProcessManager.list(<FakeCommand>[
FakeCommand(command: <String>[
artifacts.getArtifactPath(Artifact.engineDartBinary),
artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
'--sdk-root',
artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath) + '/',
'--target=flutter',
'-Ddart.developer.causal_async_stacks=true',
...buildModeOptions(BuildMode.debug),
'--track-widget-creation',
'--no-link-platform',
'--packages',
'/.packages',
'--output-dill',
'$build/app.dill',
'--depfile',
'$build/kernel_snapshot.d',
'/lib/main.dart',
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey /build/653e11a8e6908714056a57cd6b4f602a/app.dill 0\n'),
]);
await const KernelSnapshot().build(testEnvironment);
expect(processManager.hasRemainingExpectations, false);
})); }));
test('aot_elf_profile Produces correct output directory', () => testbed.run(() async { test('AotElfProfile Produces correct output directory', () => testbed.run(() async {
await buildSystem.build(const AotElfProfile(), androidEnvironment); final String build = androidEnvironment.buildDir.path;
processManager = FakeProcessManager.list(<FakeCommand>[
expect(globals.fs.file(globals.fs.path.join(androidEnvironment.buildDir.path, 'app.dill')).existsSync(), true); FakeCommand(command: <String>[
expect(globals.fs.file(globals.fs.path.join(androidEnvironment.buildDir.path, 'app.so')).existsSync(), true); artifacts.getArtifactPath(Artifact.genSnapshot, mode: BuildMode.profile),
'--deterministic',
kElfAot,
'--elf=$build/app.so',
'--strip',
'--no-sim-use-hardfp',
'--no-use-integer-division',
'--no-causal-async-stacks',
'--lazy-async-stacks',
'$build/app.dill',
])
]);
androidEnvironment.buildDir.childFile('app.dill').createSync(recursive: true);
await const AotElfProfile().build(androidEnvironment);
expect(processManager.hasRemainingExpectations, false);
})); }));
test('aot_elf_profile throws error if missing build mode', () => testbed.run(() async { test('AotElfProfile throws error if missing build mode', () => testbed.run(() async {
final BuildResult result = await buildSystem.build(const AotElfProfile(), androidEnvironment.defines.remove(kBuildMode);
androidEnvironment..defines.remove(kBuildMode));
expect(result.exceptions.values.single.exception, isA<MissingDefineException>()); expect(const AotElfProfile().build(androidEnvironment),
throwsA(isInstanceOf<MissingDefineException>()));
})); }));
test('aot_elf_profile throws error if missing target platform', () => testbed.run(() async { test('AotElfProfile throws error if missing target platform', () => testbed.run(() async {
final BuildResult result = await buildSystem.build(const AotElfProfile(), androidEnvironment.defines.remove(kTargetPlatform);
androidEnvironment..defines.remove(kTargetPlatform));
expect(result.exceptions.values.single.exception, isA<MissingDefineException>()); expect(const AotElfProfile().build(androidEnvironment),
throwsA(isInstanceOf<MissingDefineException>()));
})); }));
test('aot_assembly_profile throws error if missing build mode', () => testbed.run(() async { test('AotAssemblyProfile throws error if missing build mode', () => testbed.run(() async {
final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment.defines.remove(kBuildMode);
iosEnvironment..defines.remove(kBuildMode));
expect(result.exceptions.values.single.exception, isA<MissingDefineException>()); expect(const AotAssemblyProfile().build(iosEnvironment),
throwsA(isInstanceOf<MissingDefineException>()));
})); }));
test('aot_assembly_profile throws error if missing target platform', () => testbed.run(() async { test('AotAssemblyProfile throws error if missing target platform', () => testbed.run(() async {
final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment.defines.remove(kTargetPlatform);
iosEnvironment..defines.remove(kTargetPlatform));
expect(result.exceptions.values.single.exception, isA<MissingDefineException>()); expect(const AotAssemblyProfile().build(iosEnvironment),
throwsA(isInstanceOf<MissingDefineException>()));
})); }));
test('aot_assembly_profile throws error if built for non-iOS platform', () => testbed.run(() async { test('AotAssemblyProfile throws error if built for non-iOS platform', () => testbed.run(() async {
final BuildResult result = await buildSystem expect(const AotAssemblyProfile().build(androidEnvironment),
.build(const AotAssemblyProfile(), androidEnvironment); throwsA(isInstanceOf<Exception>()));
expect(result.exceptions.values.single.exception, isA<Exception>());
})); }));
test('aot_assembly_profile will lipo binaries together when multiple archs are requested', () => testbed.run(() async { test('AotAssemblyProfile generates multiple arches and lipos together', () => testbed.run(() async {
final String build = iosEnvironment.buildDir.path;
processManager = FakeProcessManager.list(<FakeCommand>[
FakeCommand(command: <String>[
// This path is not known by the cache due to the iOS gen_snapshot split.
'bin/cache/artifacts/engine/ios-profile/gen_snapshot_armv7',
'--deterministic',
kAssemblyAot,
'--assembly=$build/armv7/snapshot_assembly.S',
'--strip',
'--no-sim-use-hardfp',
'--no-use-integer-division',
'--no-causal-async-stacks',
'--lazy-async-stacks',
'$build/app.dill',
]),
FakeCommand(command: <String>[
// This path is not known by the cache due to the iOS gen_snapshot split.
'bin/cache/artifacts/engine/ios-profile/gen_snapshot_arm64',
'--deterministic',
kAssemblyAot,
'--assembly=$build/arm64/snapshot_assembly.S',
'--strip',
'--no-causal-async-stacks',
'--lazy-async-stacks',
'$build/app.dill',
]),
const FakeCommand(command: <String>[
'xcrun',
'--sdk',
'iphoneos',
'--show-sdk-path',
]),
const FakeCommand(command: <String>[
'xcrun',
'--sdk',
'iphoneos',
'--show-sdk-path',
]),
FakeCommand(command: <String>[
'xcrun',
'cc',
'-arch',
'armv7',
'-isysroot',
'',
'-c',
'$build/armv7/snapshot_assembly.S',
'-o',
'$build/armv7/snapshot_assembly.o',
]),
FakeCommand(command: <String>[
'xcrun',
'cc',
'-arch',
'arm64',
'-isysroot',
'',
'-c',
'$build/arm64/snapshot_assembly.S',
'-o',
'$build/arm64/snapshot_assembly.o',
]),
FakeCommand(command: <String>[
'xcrun',
'clang',
'-arch',
'armv7',
'-miphoneos-version-min=8.0',
'-dynamiclib',
'-Xlinker',
'-rpath',
'-Xlinker',
'@executable_path/Frameworks',
'-Xlinker',
'-rpath',
'-Xlinker',
'@loader_path/Frameworks',
'-install_name',
'@rpath/App.framework/App',
'-isysroot',
'',
'-o',
'$build/armv7/App.framework/App',
'$build/armv7/snapshot_assembly.o',
]),
FakeCommand(command: <String>[
'xcrun',
'clang',
'-arch',
'arm64',
'-miphoneos-version-min=8.0',
'-dynamiclib',
'-Xlinker',
'-rpath',
'-Xlinker',
'@executable_path/Frameworks',
'-Xlinker',
'-rpath',
'-Xlinker',
'@loader_path/Frameworks',
'-install_name',
'@rpath/App.framework/App',
'-isysroot',
'',
'-o',
'$build/arm64/App.framework/App',
'$build/arm64/snapshot_assembly.o',
]),
FakeCommand(command: <String>[
'lipo',
'$build/armv7/App.framework/App',
'$build/arm64/App.framework/App',
'-create',
'-output',
'$build/App.framework/App',
]),
]);
iosEnvironment.defines[kIosArchs] ='armv7 arm64'; iosEnvironment.defines[kIosArchs] ='armv7 arm64';
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
.createSync(recursive: true);
return FakeProcessResult(
stdout: '',
stderr: '',
);
});
final BuildResult result = await buildSystem
.build(const AotAssemblyProfile(), iosEnvironment);
expect(result.success, true);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
}));
test('aot_assembly_profile with bitcode sends correct argument to snapshotter (one arch)', () => testbed.run(() async { await const AotAssemblyProfile().build(iosEnvironment);
iosEnvironment.defines[kIosArchs] = 'arm64';
iosEnvironment.defines[kBitcodeFlag] = 'true';
final FakeProcessResult fakeProcessResult = FakeProcessResult(
stdout: '',
stderr: '',
);
final RunResult fakeRunResult = RunResult(fakeProcessResult, const <String>['foo']);
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
.createSync(recursive: true);
return fakeProcessResult;
});
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult));
final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
expect(result.success, true); expect(processManager.hasRemainingExpectations, false);
verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(1);
verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(1);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Xcode: () => mockXcode,
})); }));
test('aot_assembly_profile with bitcode sends correct argument to snapshotter (mutli arch)', () => testbed.run(() async { test('AotAssemblyProfile with bitcode sends correct argument to snapshotter (one arch)', () => testbed.run(() async {
iosEnvironment.defines[kIosArchs] = 'armv7 arm64'; iosEnvironment.defines[kIosArchs] = 'arm64';
iosEnvironment.defines[kBitcodeFlag] = 'true'; iosEnvironment.defines[kBitcodeFlag] = 'true';
final String build = iosEnvironment.buildDir.path;
final FakeProcessResult fakeProcessResult = FakeProcessResult( processManager = FakeProcessManager.list(<FakeCommand>[
stdout: '', FakeCommand(command: <String>[
stderr: '', // This path is not known by the cache due to the iOS gen_snapshot split.
); 'bin/cache/artifacts/engine/ios-profile/gen_snapshot_arm64',
final RunResult fakeRunResult = RunResult(fakeProcessResult, const <String>['foo']); '--deterministic',
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async { kAssemblyAot,
globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App')) '--assembly=$build/arm64/snapshot_assembly.S',
.createSync(recursive: true); '--strip',
return fakeProcessResult; '--no-causal-async-stacks',
}); '--lazy-async-stacks',
'$build/app.dill',
when(mockXcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult)); ]),
when(mockXcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(fakeRunResult)); const FakeCommand(command: <String>[
'xcrun',
'--sdk',
'iphoneos',
'--show-sdk-path',
]),
FakeCommand(command: <String>[
'xcrun',
'cc',
'-arch',
'arm64',
'-isysroot',
'',
// Contains bitcode flag.
'-fembed-bitcode',
'-c',
'$build/arm64/snapshot_assembly.S',
'-o',
'$build/arm64/snapshot_assembly.o',
]),
FakeCommand(command: <String>[
'xcrun',
'clang',
'-arch',
'arm64',
'-miphoneos-version-min=8.0',
'-dynamiclib',
'-Xlinker',
'-rpath',
'-Xlinker',
'@executable_path/Frameworks',
'-Xlinker',
'-rpath',
'-Xlinker',
'@loader_path/Frameworks',
'-install_name',
'@rpath/App.framework/App',
// Contains bitcode flag.
'-fembed-bitcode',
'-isysroot',
'',
'-o',
'$build/arm64/App.framework/App',
'$build/arm64/snapshot_assembly.o',
]),
FakeCommand(command: <String>[
'lipo',
'$build/arm64/App.framework/App',
'-create',
'-output',
'$build/App.framework/App',
]),
]);
await const AotAssemblyProfile().build(iosEnvironment); await const AotAssemblyProfile().build(iosEnvironment);
verify(mockXcode.cc(argThat(contains('-fembed-bitcode')))).called(2); expect(processManager.hasRemainingExpectations, false);
verify(mockXcode.clang(argThat(contains('-fembed-bitcode')))).called(2);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Xcode: () => mockXcode,
}));
test('aot_assembly_profile will lipo binaries together when multiple archs are requested', () => testbed.run(() async {
iosEnvironment.defines[kIosArchs] = 'armv7 arm64';
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
globals.fs.file(globals.fs.path.join(iosEnvironment.buildDir.path, 'App.framework', 'App'))
.createSync(recursive: true);
return FakeProcessResult(
stdout: '',
stderr: '',
);
});
final BuildResult result = await buildSystem.build(const AotAssemblyProfile(), iosEnvironment);
expect(result.success, true);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
})); }));
test('kExtraGenSnapshotOptions passes values to gen_snapshot', () => testbed.run(() async { test('kExtraGenSnapshotOptions passes values to gen_snapshot', () => testbed.run(() async {
androidEnvironment.defines[kExtraGenSnapshotOptions] = 'foo,bar,baz=2'; androidEnvironment.defines[kExtraGenSnapshotOptions] = 'foo,bar,baz=2';
androidEnvironment.defines[kBuildMode] = getNameForBuildMode(BuildMode.profile);
final String build = androidEnvironment.buildDir.path;
when(genSnapshot.run( processManager = FakeProcessManager.list(<FakeCommand>[
snapshotType: anyNamed('snapshotType'), FakeCommand(command: <String>[
darwinArch: anyNamed('darwinArch'), artifacts.getArtifactPath(Artifact.genSnapshot, mode: BuildMode.profile),
additionalArgs: captureAnyNamed('additionalArgs'), '--deterministic',
)).thenAnswer((Invocation invocation) async {
expect(invocation.namedArguments[#additionalArgs], containsAll(<String>[
'foo', 'foo',
'bar', 'bar',
'baz=2', 'baz=2',
])); kElfAot,
return 0; '--elf=$build/app.so',
}); '--strip',
'--no-sim-use-hardfp',
'--no-use-integer-division',
'--no-causal-async-stacks',
'--lazy-async-stacks',
'$build/app.dill',
]),
]);
await const AotElfRelease().build(androidEnvironment); await const AotElfRelease().build(androidEnvironment);
}, overrides: <Type, Generator>{
GenSnapshot: () => MockGenSnapshot(),
}));
}
class MockProcessManager extends Mock implements ProcessManager {} expect(processManager.hasRemainingExpectations, false);
class MockGenSnapshot extends Mock implements GenSnapshot {} }));
class MockXcode extends Mock implements Xcode {}
class FakeGenSnapshot implements GenSnapshot {
List<String> lastCallAdditionalArgs;
@override
Future<int> run({SnapshotType snapshotType, DarwinArch darwinArch, Iterable<String> additionalArgs = const <String>[]}) async {
lastCallAdditionalArgs = additionalArgs.toList();
final Directory out = globals.fs.file(lastCallAdditionalArgs.last).parent;
if (darwinArch == null) {
out.childFile('app.so').createSync();
out.childFile('gen_snapshot.d').createSync();
return 0;
}
out.childDirectory('App.framework').childFile('App').createSync(recursive: true);
final String assembly = lastCallAdditionalArgs
.firstWhere((String arg) => arg.startsWith('--assembly'))
.substring('--assembly='.length);
globals.fs.file(assembly).createSync();
globals.fs.file(assembly.replaceAll('.S', '.o')).createSync();
return 0;
}
}
class FakeKernelCompilerFactory implements KernelCompilerFactory {
KernelCompiler kernelCompiler = FakeKernelCompiler();
@override
Future<KernelCompiler> create(FlutterProject flutterProject) async {
return kernelCompiler;
}
}
class FakeKernelCompiler implements KernelCompiler {
@override
Future<CompilerOutput> compile({
String sdkRoot,
String mainPath,
String outputFilePath,
String depFilePath,
TargetModel targetModel = TargetModel.flutter,
bool linkPlatformKernelIn = false,
bool aot = false,
BuildMode buildMode,
bool causalAsyncStacks = true,
bool trackWidgetCreation,
List<String> extraFrontEndOptions,
String packagesPath,
List<String> fileSystemRoots,
String fileSystemScheme,
String platformDill,
String initializeFromDill,
List<String> dartDefines,
}) async {
globals.fs.file(outputFilePath).createSync(recursive: true);
return CompilerOutput(outputFilePath, 0, null);
}
} }
class MockKernelCompilerFactory extends Mock implements KernelCompilerFactory {}
class MockKernelCompiler extends Mock implements KernelCompiler {}
...@@ -202,6 +202,11 @@ abstract class FakeProcessManager implements ProcessManager { ...@@ -202,6 +202,11 @@ abstract class FakeProcessManager implements ProcessManager {
/// This is a no-op on [FakeProcessManager.any]. /// This is a no-op on [FakeProcessManager.any].
void addCommand(FakeCommand command); void addCommand(FakeCommand command);
/// Whether this fake has more [FakeCommand]s that are expected to run.
///
/// This is always `true` for [FakeProcessManager.any].
bool get hasRemainingExpectations;
@protected @protected
FakeCommand findCommand(List<String> command, String workingDirectory, Map<String, String> environment); FakeCommand findCommand(List<String> command, String workingDirectory, Map<String, String> environment);
...@@ -299,6 +304,9 @@ class _FakeAnyProcessManager extends FakeProcessManager { ...@@ -299,6 +304,9 @@ class _FakeAnyProcessManager extends FakeProcessManager {
@override @override
void addCommand(FakeCommand command) { } void addCommand(FakeCommand command) { }
@override
bool get hasRemainingExpectations => true;
} }
class _SequenceProcessManager extends FakeProcessManager { class _SequenceProcessManager extends FakeProcessManager {
...@@ -325,4 +333,7 @@ class _SequenceProcessManager extends FakeProcessManager { ...@@ -325,4 +333,7 @@ class _SequenceProcessManager extends FakeProcessManager {
void addCommand(FakeCommand command) { void addCommand(FakeCommand command) {
_commands.add(command); _commands.add(command);
} }
@override
bool get hasRemainingExpectations => _commands.isNotEmpty;
} }
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