// 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:file/memory.dart'; import 'package:file_testing/file_testing.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/fuchsia/fuchsia_kernel_compiler.dart'; import 'package:flutter_tools/src/fuchsia/fuchsia_pm.dart'; import 'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:meta/meta.dart'; import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/mocks.dart'; import '../../src/testbed.dart'; // Defined globally for mocks to use. FileSystem fileSystem; void main() { Cache.disableLocking(); final Platform linuxPlatform = FakePlatform( operatingSystem: 'linux', environment: const <String, String>{ 'FLUTTER_ROOT': '/', }, ); final Platform windowsPlatform = FakePlatform( operatingSystem: 'windows', environment: const <String, String>{ 'FLUTTER_ROOT': '/' }, ); MockFuchsiaSdk fuchsiaSdk; setUp(() { fuchsiaSdk = MockFuchsiaSdk(); fileSystem = MemoryFileSystem.test(); }); group('Fuchsia build fails gracefully when', () { testUsingContext('The feature is disabled', () async { final BuildCommand command = BuildCommand(); fileSystem.directory('fuchsia').createSync(recursive: true); fileSystem.file('.packages').createSync(); fileSystem.file('pubspec.yaml').createSync(); fileSystem.file('lib/main.dart').createSync(recursive: true); expect( createTestCommandRunner(command).run(const <String>['build', 'fuchsia']), throwsToolExit(message: '"build fuchsia" is currently disabled'), ); }, overrides: <Type, Generator>{ Platform: () => linuxPlatform, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: false), }); testUsingContext('there is no Fuchsia project', () async { final BuildCommand command = BuildCommand(); expect( createTestCommandRunner(command).run(const <String>['build', 'fuchsia']), throwsToolExit(), ); }, overrides: <Type, Generator>{ Platform: () => linuxPlatform, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true), }); testUsingContext('there is no cmx file', () async { final BuildCommand command = BuildCommand(); fileSystem.directory('fuchsia').createSync(recursive: true); fileSystem.file('.packages').createSync(); fileSystem.file('pubspec.yaml').createSync(); expect( createTestCommandRunner(command).run(const <String>['build', 'fuchsia']), throwsToolExit(), ); }, overrides: <Type, Generator>{ Platform: () => linuxPlatform, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true), }); testUsingContext('on Windows platform', () async { final BuildCommand command = BuildCommand(); const String appName = 'app_name'; fileSystem .file(fileSystem.path.join('fuchsia', 'meta', '$appName.cmx')) ..createSync(recursive: true) ..writeAsStringSync('{}'); fileSystem.file('.packages').createSync(); final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync(); pubspecFile.writeAsStringSync('name: $appName'); expect( createTestCommandRunner(command).run(const <String>['build', 'fuchsia']), throwsToolExit(), ); }, overrides: <Type, Generator>{ Platform: () => windowsPlatform, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true), }); testUsingContext('there is no Fuchsia kernel compiler', () async { final BuildCommand command = BuildCommand(); const String appName = 'app_name'; fileSystem .file(fileSystem.path.join('fuchsia', 'meta', '$appName.cmx')) ..createSync(recursive: true) ..writeAsStringSync('{}'); fileSystem.file('.packages').createSync(); fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true); final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync(); pubspecFile.writeAsStringSync('name: $appName'); expect( createTestCommandRunner(command).run(const <String>['build', 'fuchsia']), throwsToolExit(), ); }, overrides: <Type, Generator>{ Platform: () => linuxPlatform, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true), }); }); testUsingContext('Fuchsia build parts fit together right', () async { final BuildCommand command = BuildCommand(); applyMocksToCommand(command); const String appName = 'app_name'; fileSystem .file(fileSystem.path.join('fuchsia', 'meta', '$appName.cmx')) ..createSync(recursive: true) ..writeAsStringSync('{}'); fileSystem.file('.packages').createSync(); fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true); final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync(); pubspecFile.writeAsStringSync('name: $appName'); await createTestCommandRunner(command) .run(const <String>['build', 'fuchsia']); final String farPath = fileSystem.path.join( getFuchsiaBuildDirectory(), 'pkg', 'app_name-0.far', ); expect(fileSystem.file(farPath), exists); }, overrides: <Type, Generator>{ Platform: () => linuxPlatform, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), FuchsiaSdk: () => fuchsiaSdk, FeatureFlags: () => TestFeatureFlags(isFuchsiaEnabled: true), }); } class MockFuchsiaPM extends Mock implements FuchsiaPM { String _appName; @override Future<bool> init(String buildPath, String appName) async { if (!fileSystem.directory(buildPath).existsSync()) { return false; } fileSystem .file(fileSystem.path.join(buildPath, 'meta', 'package')) .createSync(recursive: true); _appName = appName; return true; } @override Future<bool> genkey(String buildPath, String outKeyPath) async { if (!fileSystem.file(fileSystem.path.join(buildPath, 'meta', 'package')).existsSync()) { return false; } fileSystem.file(outKeyPath).createSync(recursive: true); return true; } @override Future<bool> build(String buildPath, String keyPath, String manifestPath) async { if (!fileSystem.file(fileSystem.path.join(buildPath, 'meta', 'package')).existsSync() || !fileSystem.file(keyPath).existsSync() || !fileSystem.file(manifestPath).existsSync()) { return false; } fileSystem.file(fileSystem.path.join(buildPath, 'meta.far')).createSync(recursive: true); return true; } @override Future<bool> archive(String buildPath, String keyPath, String manifestPath) async { if (!fileSystem.file(fileSystem.path.join(buildPath, 'meta', 'package')).existsSync() || !fileSystem.file(keyPath).existsSync() || !fileSystem.file(manifestPath).existsSync()) { return false; } if (_appName == null) { return false; } fileSystem .file(fileSystem.path.join(buildPath, '$_appName-0.far')) .createSync(recursive: true); return true; } } class MockFuchsiaKernelCompiler extends Mock implements FuchsiaKernelCompiler { @override Future<void> build({ @required FuchsiaProject fuchsiaProject, @required String target, // E.g., lib/main.dart BuildInfo buildInfo = BuildInfo.debug, }) async { final String outDir = getFuchsiaBuildDirectory(); final String appName = fuchsiaProject.project.manifest.appName; final String manifestPath = fileSystem.path.join(outDir, '$appName.dilpmanifest'); fileSystem.file(manifestPath).createSync(recursive: true); } } class MockFuchsiaSdk extends Mock implements FuchsiaSdk { @override final FuchsiaPM fuchsiaPM = MockFuchsiaPM(); @override final FuchsiaKernelCompiler fuchsiaKernelCompiler = MockFuchsiaKernelCompiler(); }