build_macos_test.dart 9.3 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:args/command_runner.dart';
6 7
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
8
import 'package:flutter_tools/src/base/platform.dart';
9
import 'package:flutter_tools/src/build_info.dart';
10 11
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
12
import 'package:flutter_tools/src/commands/build_macos.dart';
13
import 'package:flutter_tools/src/features.dart';
14
import 'package:flutter_tools/src/ios/xcodeproj.dart';
15
import 'package:flutter_tools/src/project.dart';
16 17
import 'package:process/process.dart';

18 19
import '../../src/common.dart';
import '../../src/context.dart';
20
import '../../src/testbed.dart';
21

22 23
class FakeXcodeProjectInterpreterWithProfile extends FakeXcodeProjectInterpreter {
  @override
24
  Future<XcodeProjectInfo> getInfo(String projectPath, {String projectFilename}) async {
25 26 27 28 29 30 31 32
    return XcodeProjectInfo(
      <String>['Runner'],
      <String>['Debug', 'Profile', 'Release'],
      <String>['Runner'],
    );
  }
}

33 34 35 36 37 38 39 40 41 42 43 44 45
final Platform macosPlatform = FakePlatform(
  operatingSystem: 'macos',
  environment: <String, String>{
    'FLUTTER_ROOT': '/',
  }
);
final Platform notMacosPlatform = FakePlatform(
  operatingSystem: 'linux',
  environment: <String, String>{
    'FLUTTER_ROOT': '/',
  }
);

46
void main() {
47
  FileSystem fileSystem;
48

49 50
  setUpAll(() {
    Cache.disableLocking();
51
  });
52 53

  setUp(() {
54
    fileSystem = MemoryFileSystem.test();
55
  });
56

57 58
  // Sets up the minimal mock project files necessary to look like a Flutter project.
  void createCoreMockProjectFiles() {
59 60 61
    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('.packages').createSync();
    fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true);
62 63
  }

64 65
  // Sets up the minimal mock project files necessary for macOS builds to succeed.
  void createMinimalMockProjectFiles() {
66
    fileSystem.directory(fileSystem.path.join('macos', 'Runner.xcworkspace')).createSync(recursive: true);
67 68 69
    createCoreMockProjectFiles();
  }

70
  // Creates a FakeCommand for the xcodebuild call to build the app
71
  // in the given configuration.
72
  FakeCommand setUpMockXcodeBuildHandler(String configuration, { bool verbose = false }) {
73 74 75 76 77 78 79 80 81 82 83 84 85
    final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
    final Directory flutterBuildDir = fileSystem.directory(getMacOSBuildDirectory());
    return FakeCommand(
      command: <String>[
        '/usr/bin/env',
        'xcrun',
        'xcodebuild',
        '-workspace', flutterProject.macos.xcodeWorkspace.path,
        '-configuration', configuration,
        '-scheme', 'Runner',
        '-derivedDataPath', flutterBuildDir.absolute.path,
        'OBJROOT=${fileSystem.path.join(flutterBuildDir.absolute.path, 'Build', 'Intermediates.noindex')}',
        'SYMROOT=${fileSystem.path.join(flutterBuildDir.absolute.path, 'Build', 'Products')}',
86 87
        if (verbose)
          'VERBOSE_SCRIPT_LOGGING=YES',
88 89 90 91 92 93 94 95 96
        'COMPILER_INDEX_STORE_ENABLE=NO',
      ],
      stdout: 'STDOUT STUFF',
      onRun: () {
        fileSystem.file(fileSystem.path.join('macos', 'Flutter', 'ephemeral', '.app_filename'))
          ..createSync(recursive: true)
          ..writeAsStringSync('example.app');
      }
    );
97 98
  }

99 100
  testUsingContext('macOS build fails when there is no macos project', () async {
    final BuildCommand command = BuildCommand();
101
    createCoreMockProjectFiles();
102

103 104
    expect(createTestCommandRunner(command).run(
      const <String>['build', 'macos']
105
    ), throwsToolExit(message: 'No macOS desktop project configured'));
106 107
  }, overrides: <Type, Generator>{
    Platform: () => macosPlatform,
108
    FileSystem: () => fileSystem,
109
    ProcessManager: () => FakeProcessManager.any(),
110
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
111 112 113 114
  });

  testUsingContext('macOS build fails on non-macOS platform', () async {
    final BuildCommand command = BuildCommand();
115 116 117 118
    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('.packages').createSync();
    fileSystem.file(fileSystem.path.join('lib', 'main.dart'))
      .createSync(recursive: true);
119 120 121

    expect(createTestCommandRunner(command).run(
      const <String>['build', 'macos']
Dan Field's avatar
Dan Field committed
122
    ), throwsToolExit());
123 124
  }, overrides: <Type, Generator>{
    Platform: () => notMacosPlatform,
125
    FileSystem: () => fileSystem,
126
    ProcessManager: () => FakeProcessManager.any(),
127
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
128 129 130 131 132 133 134 135 136
  });

  testUsingContext('macOS build does not spew stdout to status logger', () async {
    final BuildCommand command = BuildCommand();
    createMinimalMockProjectFiles();

    await createTestCommandRunner(command).run(
      const <String>['build', 'macos', '--debug']
    );
137 138
    expect(testLogger.statusText, isNot(contains('STDOUT STUFF')));
    expect(testLogger.traceText, contains('STDOUT STUFF'));
139
  }, overrides: <Type, Generator>{
140 141 142 143
    FileSystem: () => fileSystem,
    ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
      setUpMockXcodeBuildHandler('Debug')
    ]),
144 145
    Platform: () => macosPlatform,
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
146 147
  });

148
  testUsingContext('macOS build invokes xcode build (debug)', () async {
149
    final BuildCommand command = BuildCommand();
150 151 152 153 154 155
    createMinimalMockProjectFiles();

    await createTestCommandRunner(command).run(
      const <String>['build', 'macos', '--debug']
    );
  }, overrides: <Type, Generator>{
156 157 158 159
    FileSystem: () => fileSystem,
    ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
      setUpMockXcodeBuildHandler('Debug')
    ]),
160 161 162 163
    Platform: () => macosPlatform,
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
  });

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
  testUsingContext('macOS build invokes xcode build (debug) with verbosity', () async {
    final BuildCommand command = BuildCommand();
    createMinimalMockProjectFiles();

    await createTestCommandRunner(command).run(
      const <String>['build', 'macos', '--debug', '-v']
    );
  }, overrides: <Type, Generator>{
    FileSystem: () => fileSystem,
    ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
      setUpMockXcodeBuildHandler('Debug', verbose: true)
    ]),
    Platform: () => macosPlatform,
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
  });


181 182 183 184 185 186 187 188
  testUsingContext('macOS build invokes xcode build (profile)', () async {
    final BuildCommand command = BuildCommand();
    createMinimalMockProjectFiles();

    await createTestCommandRunner(command).run(
      const <String>['build', 'macos', '--profile']
    );
  }, overrides: <Type, Generator>{
189 190 191 192
    FileSystem: () => fileSystem,
    ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
      setUpMockXcodeBuildHandler('Profile')
    ]),
193 194 195 196 197 198 199 200
    Platform: () => macosPlatform,
    XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithProfile(),
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
  });

  testUsingContext('macOS build invokes xcode build (release)', () async {
    final BuildCommand command = BuildCommand();
    createMinimalMockProjectFiles();
201

202
    await createTestCommandRunner(command).run(
203
      const <String>['build', 'macos', '--release']
204
    );
205
  }, overrides: <Type, Generator>{
206 207 208 209
    FileSystem: () => fileSystem,
    ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
      setUpMockXcodeBuildHandler('Release')
    ]),
210
    Platform: () => macosPlatform,
211 212 213
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
  });

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
  testUsingContext('macOS build supports build-name and build-number', () async {
    final BuildCommand command = BuildCommand();
    createMinimalMockProjectFiles();

    await createTestCommandRunner(command).run(
      const <String>[
        'build',
        'macos',
        '--debug',
        '--build-name=1.2.3',
        '--build-number=42',
      ],
    );
    final String contents = fileSystem
      .file('./macos/Flutter/ephemeral/Flutter-Generated.xcconfig')
      .readAsStringSync();

    expect(contents, contains('FLUTTER_BUILD_NAME=1.2.3'));
    expect(contents, contains('FLUTTER_BUILD_NUMBER=42'));
  }, overrides: <Type, Generator>{
    FileSystem: () => fileSystem,
    ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
      setUpMockXcodeBuildHandler('Debug')
    ]),
    Platform: () => macosPlatform,
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
  });

242 243 244 245
  testUsingContext('Refuses to build for macOS when feature is disabled', () {
    final CommandRunner<void> runner = createTestCommandRunner(BuildCommand());

    expect(() => runner.run(<String>['build', 'macos']),
246
      throwsToolExit());
247 248
  }, overrides: <Type, Generator>{
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false),
249
  });
250 251

  testUsingContext('hidden when not enabled on macOS host', () {
252
    expect(BuildMacosCommand(verboseHelp: false).hidden, true);
253 254
  }, overrides: <Type, Generator>{
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false),
255
    Platform: () => macosPlatform,
256 257 258
  });

  testUsingContext('Not hidden when enabled and on macOS host', () {
259
    expect(BuildMacosCommand(verboseHelp: false).hidden, false);
260 261
  }, overrides: <Type, Generator>{
    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
262
    Platform: () => macosPlatform,
263
  });
264
}