// 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 'dart:async'; import 'dart:convert'; import 'package:args/command_runner.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/logger.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/analyze.dart'; import 'package:flutter_tools/src/commands/ios_analyze.dart'; import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project_validator.dart'; import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/test_flutter_command_runner.dart'; void main() { group('ios analyze command', () { late BufferLogger logger; late FileSystem fileSystem; late Platform platform; late FakeProcessManager processManager; late Terminal terminal; late AnalyzeCommand command; late CommandRunner<void> runner; setUpAll(() { Cache.disableLocking(); }); setUp(() { logger = BufferLogger.test(); fileSystem = MemoryFileSystem.test(); platform = FakePlatform(); processManager = FakeProcessManager.empty(); terminal = Terminal.test(); command = AnalyzeCommand( artifacts: Artifacts.test(), fileSystem: fileSystem, logger: logger, platform: platform, processManager: processManager, terminal: terminal, allProjectValidators: <ProjectValidator>[], suppressAnalytics: true, ); runner = createTestCommandRunner(command); // Setup repo roots const String homePath = '/home/user/flutter'; Cache.flutterRoot = homePath; for (final String dir in <String>['dev', 'examples', 'packages']) { fileSystem.directory(homePath).childDirectory(dir).createSync(recursive: true); } }); testWithoutContext('can output json file', () async { final MockIosProject ios = MockIosProject(); final MockFlutterProject project = MockFlutterProject(ios); const String expectedConfig = 'someConfig'; const String expectedTarget = 'someTarget'; const String expectedOutputFile = '/someFile'; ios.outputFileLocation = expectedOutputFile; await IOSAnalyze( project: project, option: IOSAnalyzeOption.outputUniversalLinkSettings, configuration: expectedConfig, target: expectedTarget, logger: logger, ).analyze(); expect(logger.statusText, contains(expectedOutputFile)); expect(ios.outputConfiguration, expectedConfig); expect(ios.outputTarget, expectedTarget); }); testWithoutContext('can list build options', () async { final MockIosProject ios = MockIosProject(); final MockFlutterProject project = MockFlutterProject(ios); const List<String> targets = <String>['target1', 'target2']; const List<String> configs = <String>['config1', 'config2']; ios.expectedProjectInfo = XcodeProjectInfo(targets, configs, const <String>[], logger); await IOSAnalyze( project: project, option: IOSAnalyzeOption.listBuildOptions, logger: logger, ).analyze(); final Map<String, Object?> jsonOutput = jsonDecode(logger.statusText) as Map<String, Object?>; expect(jsonOutput['targets'], unorderedEquals(targets)); expect(jsonOutput['configurations'], unorderedEquals(configs)); }); testUsingContext('throws if provide multiple path', () async { final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('someTemp'); final Directory anotherTempDir = fileSystem.systemTempDirectory.createTempSync('another'); await expectLater( runner.run(<String>['analyze', '--ios', '--list-build-options', tempDir.path, anotherTempDir.path]), throwsA( isA<Exception>().having( (Exception e) => e.toString(), 'description', contains('The iOS analyze can process only one directory path'), ), ), ); }); testUsingContext('throws if not enough parameters', () async { final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('someTemp'); await expectLater( runner.run(<String>['analyze', '--ios', '--output-universal-link-settings', tempDir.path]), throwsA( isA<Exception>().having( (Exception e) => e.toString(), 'description', contains('"--configuration" must be provided'), ), ), ); }); }); } class MockFlutterProject extends Fake implements FlutterProject { MockFlutterProject(this.ios); @override final IosProject ios; } class MockIosProject extends Fake implements IosProject { String? outputConfiguration; String? outputTarget; late String outputFileLocation; late XcodeProjectInfo expectedProjectInfo; @override Future<String> outputsUniversalLinkSettings({required String configuration, required String target}) async { outputConfiguration = configuration; outputTarget = target; return outputFileLocation; } @override Future<XcodeProjectInfo> projectInfo() async => expectedProjectInfo; }