| // 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. |
| |
| import 'package:flutter_tools/src/base/build.dart'; |
| import 'package:flutter_tools/src/base/file_system.dart'; |
| import 'package:flutter_tools/src/base/io.dart'; |
| import 'package:flutter_tools/src/base/process.dart'; |
| import 'package:flutter_tools/src/build_system/build_system.dart'; |
| import 'package:flutter_tools/src/build_system/targets/dart.dart'; |
| import 'package:flutter_tools/src/build_system/targets/macos.dart'; |
| import 'package:flutter_tools/src/cache.dart'; |
| import 'package:flutter_tools/src/macos/cocoapods.dart'; |
| import 'package:flutter_tools/src/macos/xcode.dart'; |
| import 'package:flutter_tools/src/globals.dart' as globals; |
| import 'package:mockito/mockito.dart'; |
| import 'package:process/process.dart'; |
| import 'package:platform/platform.dart'; |
| |
| import '../../../src/common.dart'; |
| import '../../../src/testbed.dart'; |
| |
| const String _kInputPrefix = 'bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework'; |
| const String _kOutputPrefix = 'FlutterMacOS.framework'; |
| |
| final List<File> inputs = <File>[ |
| globals.fs.file('$_kInputPrefix/FlutterMacOS'), |
| // Headers |
| globals.fs.file('$_kInputPrefix/Headers/FlutterDartProject.h'), |
| globals.fs.file('$_kInputPrefix/Headers/FlutterEngine.h'), |
| globals.fs.file('$_kInputPrefix/Headers/FlutterViewController.h'), |
| globals.fs.file('$_kInputPrefix/Headers/FlutterBinaryMessenger.h'), |
| globals.fs.file('$_kInputPrefix/Headers/FlutterChannels.h'), |
| globals.fs.file('$_kInputPrefix/Headers/FlutterCodecs.h'), |
| globals.fs.file('$_kInputPrefix/Headers/FlutterMacros.h'), |
| globals.fs.file('$_kInputPrefix/Headers/FlutterPluginMacOS.h'), |
| globals.fs.file('$_kInputPrefix/Headers/FlutterPluginRegistrarMacOS.h'), |
| globals.fs.file('$_kInputPrefix/Headers/FlutterMacOS.h'), |
| // Modules |
| globals.fs.file('$_kInputPrefix/Modules/module.modulemap'), |
| // Resources |
| globals.fs.file('$_kInputPrefix/Resources/icudtl.dat'), |
| globals.fs.file('$_kInputPrefix/Resources/Info.plist'), |
| // Ignore Versions folder for now |
| globals.fs.file('packages/flutter_tools/lib/src/build_system/targets/macos.dart'), |
| ]; |
| |
| void main() { |
| Testbed testbed; |
| Environment environment; |
| MockPlatform mockPlatform; |
| MockXcode mockXcode; |
| |
| setUpAll(() { |
| Cache.disableLocking(); |
| Cache.flutterRoot = ''; |
| }); |
| |
| setUp(() { |
| mockXcode = MockXcode(); |
| mockPlatform = MockPlatform(); |
| when(mockPlatform.isWindows).thenReturn(false); |
| when(mockPlatform.isMacOS).thenReturn(true); |
| when(mockPlatform.isLinux).thenReturn(false); |
| when(mockPlatform.environment).thenReturn(const <String, String>{}); |
| testbed = Testbed(setup: () { |
| globals.fs.file(globals.fs.path.join('bin', 'cache', 'pkg', 'sky_engine', 'lib', 'ui', |
| 'ui.dart')).createSync(recursive: true); |
| globals.fs.file(globals.fs.path.join('bin', 'cache', 'pkg', 'sky_engine', 'sdk_ext', |
| 'vmservice_io.dart')).createSync(recursive: true); |
| |
| environment = Environment.test( |
| globals.fs.currentDirectory, |
| defines: <String, String>{ |
| kBuildMode: 'debug', |
| kTargetPlatform: 'darwin-x64', |
| }, |
| ); |
| }, overrides: <Type, Generator>{ |
| ProcessManager: () => MockProcessManager(), |
| Platform: () => mockPlatform, |
| }); |
| }); |
| |
| test('Copies files to correct cache directory', () => testbed.run(() async { |
| for (final File input in inputs) { |
| input.createSync(recursive: true); |
| } |
| // Create output directory so we can test that it is deleted. |
| environment.outputDir.childDirectory(_kOutputPrefix) |
| .createSync(recursive: true); |
| |
| when(globals.processManager.run(any)).thenAnswer((Invocation invocation) async { |
| final List<String> arguments = invocation.positionalArguments.first as List<String>; |
| final String sourcePath = arguments[arguments.length - 2]; |
| final String targetPath = arguments.last; |
| final Directory source = globals.fs.directory(sourcePath); |
| final Directory target = globals.fs.directory(targetPath); |
| |
| // verify directory was deleted by command. |
| expect(target.existsSync(), false); |
| target.createSync(recursive: true); |
| |
| for (final FileSystemEntity entity in source.listSync(recursive: true)) { |
| if (entity is File) { |
| final String relative = globals.fs.path.relative(entity.path, from: source.path); |
| final String destination = globals.fs.path.join(target.path, relative); |
| if (!globals.fs.file(destination).parent.existsSync()) { |
| globals.fs.file(destination).parent.createSync(); |
| } |
| entity.copySync(destination); |
| } |
| } |
| return FakeProcessResult()..exitCode = 0; |
| }); |
| await const DebugUnpackMacOS().build(environment); |
| |
| expect(globals.fs.directory('$_kOutputPrefix').existsSync(), true); |
| for (final File file in inputs) { |
| expect(globals.fs.file(file.path.replaceFirst(_kInputPrefix, _kOutputPrefix)).existsSync(), true); |
| } |
| })); |
| |
| test('debug macOS application fails if App.framework missing', () => testbed.run(() async { |
| final String inputKernel = globals.fs.path.join(environment.buildDir.path, 'app.dill'); |
| globals.fs.file(inputKernel) |
| ..createSync(recursive: true) |
| ..writeAsStringSync('testing'); |
| |
| expect(() async => await const DebugMacOSBundleFlutterAssets().build(environment), |
| throwsException); |
| })); |
| |
| test('debug macOS application creates correctly structured framework', () => testbed.run(() async { |
| globals.fs.file(globals.fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64', |
| 'vm_isolate_snapshot.bin')).createSync(recursive: true); |
| globals.fs.file(globals.fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64', |
| 'isolate_snapshot.bin')).createSync(recursive: true); |
| globals.fs.file(globals.fs.path.join(environment.buildDir.path, 'App.framework', 'App')) |
| ..createSync(recursive: true); |
| |
| final String inputKernel = globals.fs.path.join(environment.buildDir.path, 'app.dill'); |
| final String outputKernel = globals.fs.path.join('App.framework', 'Versions', 'A', 'Resources', |
| 'flutter_assets', 'kernel_blob.bin'); |
| final String outputPlist = globals.fs.path.join('App.framework', 'Versions', 'A', 'Resources', |
| 'Info.plist'); |
| globals.fs.file(inputKernel) |
| ..createSync(recursive: true) |
| ..writeAsStringSync('testing'); |
| |
| await const DebugMacOSBundleFlutterAssets().build(environment); |
| |
| expect(globals.fs.file(outputKernel).readAsStringSync(), 'testing'); |
| expect(globals.fs.file(outputPlist).readAsStringSync(), contains('io.flutter.flutter.app')); |
| })); |
| |
| test('release/profile macOS application has no blob or precompiled runtime', () => testbed.run(() async { |
| globals.fs.file(globals.fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64', |
| 'vm_isolate_snapshot.bin')).createSync(recursive: true); |
| globals.fs.file(globals.fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64', |
| 'isolate_snapshot.bin')).createSync(recursive: true); |
| globals.fs.file(globals.fs.path.join(environment.buildDir.path, 'App.framework', 'App')) |
| ..createSync(recursive: true); |
| final String outputKernel = globals.fs.path.join('App.framework', 'Resources', |
| 'flutter_assets', 'kernel_blob.bin'); |
| final String precompiledVm = globals.fs.path.join('App.framework', 'Resources', |
| 'flutter_assets', 'vm_snapshot_data'); |
| final String precompiledIsolate = globals.fs.path.join('App.framework', 'Resources', |
| 'flutter_assets', 'isolate_snapshot_data'); |
| await const ProfileMacOSBundleFlutterAssets().build(environment..defines[kBuildMode] = 'profile'); |
| |
| expect(globals.fs.file(outputKernel).existsSync(), false); |
| expect(globals.fs.file(precompiledVm).existsSync(), false); |
| expect(globals.fs.file(precompiledIsolate).existsSync(), false); |
| })); |
| |
| test('release/profile macOS application updates when App.framework updates', () => testbed.run(() async { |
| globals.fs.file(globals.fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64', |
| 'vm_isolate_snapshot.bin')).createSync(recursive: true); |
| globals.fs.file(globals.fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64', |
| 'isolate_snapshot.bin')).createSync(recursive: true); |
| final File inputFramework = globals.fs.file(globals.fs.path.join(environment.buildDir.path, 'App.framework', 'App')) |
| ..createSync(recursive: true) |
| ..writeAsStringSync('ABC'); |
| |
| await const ProfileMacOSBundleFlutterAssets().build(environment..defines[kBuildMode] = 'profile'); |
| final File outputFramework = globals.fs.file(globals.fs.path.join(environment.outputDir.path, 'App.framework', 'App')); |
| |
| expect(outputFramework.readAsStringSync(), 'ABC'); |
| |
| inputFramework.writeAsStringSync('DEF'); |
| await const ProfileMacOSBundleFlutterAssets().build(environment..defines[kBuildMode] = 'profile'); |
| |
| expect(outputFramework.readAsStringSync(), 'DEF'); |
| })); |
| |
| test('release/profile macOS compilation uses correct gen_snapshot', () => testbed.run(() async { |
| when(genSnapshot.run( |
| snapshotType: anyNamed('snapshotType'), |
| additionalArgs: anyNamed('additionalArgs'), |
| darwinArch: anyNamed('darwinArch'), |
| )).thenAnswer((Invocation invocation) { |
| environment.buildDir.childFile('snapshot_assembly.o').createSync(); |
| environment.buildDir.childFile('snapshot_assembly.S').createSync(); |
| return Future<int>.value(0); |
| }); |
| when(mockXcode.cc(any)).thenAnswer((Invocation invocation) { |
| return Future<RunResult>.value(RunResult(FakeProcessResult()..exitCode = 0, <String>['test'])); |
| }); |
| when(mockXcode.clang(any)).thenAnswer((Invocation invocation) { |
| return Future<RunResult>.value(RunResult(FakeProcessResult()..exitCode = 0, <String>['test'])); |
| }); |
| environment.buildDir.childFile('app.dill').createSync(recursive: true); |
| globals.fs.file('.packages') |
| ..createSync() |
| ..writeAsStringSync(''' |
| # Generated |
| sky_engine:file:///bin/cache/pkg/sky_engine/lib/ |
| flutter_tools:lib/'''); |
| await const CompileMacOSFramework().build(environment..defines[kBuildMode] = 'release'); |
| }, overrides: <Type, Generator>{ |
| GenSnapshot: () => MockGenSnapshot(), |
| Xcode: () => mockXcode, |
| })); |
| } |
| |
| class MockPlatform extends Mock implements Platform {} |
| class MockCocoaPods extends Mock implements CocoaPods {} |
| class MockProcessManager extends Mock implements ProcessManager {} |
| class MockGenSnapshot extends Mock implements GenSnapshot {} |
| class MockXcode extends Mock implements Xcode {} |
| class FakeProcessResult implements ProcessResult { |
| @override |
| int exitCode; |
| |
| @override |
| int pid = 0; |
| |
| @override |
| String stderr = ''; |
| |
| @override |
| String stdout = ''; |
| } |