build_appbundle_test.dart 7.91 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 6
// @dart = 2.8

7 8
import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/android/android_builder.dart';
9
import 'package:flutter_tools/src/android/android_sdk.dart';
10 11 12
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build_appbundle.dart';
13
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
14
import 'package:flutter_tools/src/project.dart';
15
import 'package:flutter_tools/src/reporting/reporting.dart';
16
import 'package:test/fake.dart';
17

18
import '../../src/android_common.dart';
19 20
import '../../src/common.dart';
import '../../src/context.dart';
21
import '../../src/test_flutter_command_runner.dart';
22 23 24 25

void main() {
  Cache.disableLocking();

26
  group('Usage', () {
27
    Directory tempDir;
28
    TestUsage testUsage;
29 30

    setUp(() {
31
      tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
32
      testUsage = TestUsage();
33 34 35 36 37 38 39 40 41
    });

    tearDown(() {
      tryToDelete(tempDir);
    });

    testUsingContext('indicate the default target platforms', () async {
      final String projectPath = await createProject(tempDir,
          arguments: <String>['--no-pub', '--template=app']);
42
      final BuildAppBundleCommand command = await runBuildAppBundleCommand(projectPath);
43 44

      expect(await command.usageValues,
45
          containsPair(CustomDimensions.commandBuildAppBundleTargetPlatform, 'android-arm,android-arm64,android-x64'));
46 47 48

    }, overrides: <Type, Generator>{
      AndroidBuilder: () => FakeAndroidBuilder(),
49
    });
50 51 52 53 54

    testUsingContext('build type', () async {
      final String projectPath = await createProject(tempDir,
          arguments: <String>['--no-pub', '--template=app']);

55
      final BuildAppBundleCommand commandDefault = await runBuildAppBundleCommand(projectPath);
56 57 58
      expect(await commandDefault.usageValues,
          containsPair(CustomDimensions.commandBuildAppBundleBuildMode, 'release'));

59
      final BuildAppBundleCommand commandInRelease = await runBuildAppBundleCommand(projectPath,
60 61 62 63
          arguments: <String>['--release']);
      expect(await commandInRelease.usageValues,
          containsPair(CustomDimensions.commandBuildAppBundleBuildMode, 'release'));

64
      final BuildAppBundleCommand commandInDebug = await runBuildAppBundleCommand(projectPath,
65 66 67 68
          arguments: <String>['--debug']);
      expect(await commandInDebug.usageValues,
          containsPair(CustomDimensions.commandBuildAppBundleBuildMode, 'debug'));

69
      final BuildAppBundleCommand commandInProfile = await runBuildAppBundleCommand(projectPath,
70 71 72 73 74 75
          arguments: <String>['--profile']);
      expect(await commandInProfile.usageValues,
          containsPair(CustomDimensions.commandBuildAppBundleBuildMode, 'profile'));

    }, overrides: <Type, Generator>{
      AndroidBuilder: () => FakeAndroidBuilder(),
76
    });
77 78 79 80 81 82 83

    testUsingContext('logs success', () async {
      final String projectPath = await createProject(tempDir,
          arguments: <String>['--no-pub', '--template=app']);

      await runBuildAppBundleCommand(projectPath);

84 85 86
      expect(testUsage.events, contains(
        const TestUsageEvent('tool-command-result', 'appbundle', label: 'success'),
      ));
87 88 89
    },
    overrides: <Type, Generator>{
      AndroidBuilder: () => FakeAndroidBuilder(),
90
      Usage: () => testUsage,
91
    });
92
  });
93

Emmanuel Garcia's avatar
Emmanuel Garcia committed
94
  group('Gradle', () {
95
    Directory tempDir;
96
    FakeProcessManager processManager;
97
    FakeAndroidSdk fakeAndroidSdk;
98
    TestUsage testUsage;
99 100

    setUp(() {
101
      testUsage = TestUsage();
102
      tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
103
      processManager = FakeProcessManager.any();
104
      fakeAndroidSdk = FakeAndroidSdk(globals.fs.directory('irrelevant'));
105 106 107 108 109 110
    });

    tearDown(() {
      tryToDelete(tempDir);
    });

111
    group('AndroidSdk', () {
112 113 114 115 116 117 118 119 120 121
      testUsingContext('throws throwsToolExit if AndroidSdk is null', () async {
        final String projectPath = await createProject(tempDir,
            arguments: <String>['--no-pub', '--template=app']);

        await expectLater(() async {
          await runBuildAppBundleCommand(
            projectPath,
            arguments: <String>['--no-pub'],
          );
        }, throwsToolExit(
122
          message: 'No Android SDK found. Try setting the ANDROID_SDK_ROOT environment variable',
123 124 125 126 127
        ));
      },
      overrides: <Type, Generator>{
        AndroidSdk: () => null,
        FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
128
        ProcessManager: () => processManager,
129 130 131
      });
    });

132
    testUsingContext("reports when the app isn't using AndroidX", () async {
133
      final String projectPath = await createProject(tempDir,
134 135 136 137 138 139 140
          arguments: <String>['--no-pub', '--template=app']);
      // Simulate a non-androidx project.
      tempDir
        .childDirectory('flutter_project')
        .childDirectory('android')
        .childFile('gradle.properties')
        .writeAsStringSync('android.useAndroidX=false');
141 142 143 144 145 146 147 148

      // The command throws a [ToolExit] because it expects an AAB in the file system.
      await expectLater(() async {
        await runBuildAppBundleCommand(
          projectPath,
        );
      }, throwsToolExit());

149 150 151 152 153 154 155
      expect(
        testLogger.statusText,
        containsIgnoringWhitespace("Your app isn't using AndroidX"),
      );
      expect(
        testLogger.statusText,
        containsIgnoringWhitespace(
156 157
        'To avoid potential build failures, you can quickly migrate your app by '
        'following the steps on https://goo.gl/CP92wY'
158
        ),
159
      );
160 161 162 163

      expect(testUsage.events, contains(
        const TestUsageEvent(
          'build',
164
          'gradle',
165 166 167 168
          label: 'app-not-using-android-x',
          parameters: <String, String>{},
        ),
      ));
169 170
    },
    overrides: <Type, Generator>{
171
      AndroidSdk: () => fakeAndroidSdk,
172
      FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
173
      ProcessManager: () => processManager,
174
      Usage: () => testUsage,
175
    });
176 177 178 179 180 181 182 183 184 185 186 187 188

    testUsingContext('reports when the app is using AndroidX', () async {
      final String projectPath = await createProject(tempDir,
          arguments: <String>['--no-pub', '--template=app']);

      // The command throws a [ToolExit] because it expects an AAB in the file system.
      await expectLater(() async {
        await runBuildAppBundleCommand(
          projectPath,
        );
      }, throwsToolExit());

      expect(
189
        testLogger.statusText,
190
        isNot(containsIgnoringWhitespace("Your app isn't using AndroidX")),
191 192 193
      );
      expect(
        testLogger.statusText,
194
        isNot(
195 196 197 198
          containsIgnoringWhitespace(
            'To avoid potential build failures, you can quickly migrate your app by '
            'following the steps on https://goo.gl/CP92wY'),
        )
199
      );
200 201 202 203

      expect(testUsage.events, contains(
        const TestUsageEvent(
          'build',
204
          'gradle',
205 206 207 208
          label: 'app-using-android-x',
          parameters: <String, String>{},
        ),
      ));
209 210
    },
    overrides: <Type, Generator>{
211
      AndroidSdk: () => fakeAndroidSdk,
212
      FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
213
      ProcessManager: () => processManager,
214
      Usage: () => testUsage,
215
    });
216
  });
217
}
218 219

Future<BuildAppBundleCommand> runBuildAppBundleCommand(
220 221 222
  String target, {
  List<String> arguments,
}) async {
223 224 225 226 227
  final BuildAppBundleCommand command = BuildAppBundleCommand();
  final CommandRunner<void> runner = createTestCommandRunner(command);
  await runner.run(<String>[
    'appbundle',
    ...?arguments,
228
    '--no-pub',
229
    globals.fs.path.join(target, 'lib', 'main.dart'),
230 231 232 233
  ]);
  return command;
}

234 235
class FakeAndroidSdk extends Fake implements AndroidSdk {
  FakeAndroidSdk(this.directory);
kwkr's avatar
kwkr committed
236

237 238 239
  @override
  final Directory directory;
}