module_test.dart 8.04 KB
Newer Older
1 2 3 4 5 6
// Copyright (c) 2018 The Chromium 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:io';
7

8
import 'package:flutter_devicelab/framework/apk_utils.dart';
9 10 11 12
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;

13 14 15
final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
final String gradlewExecutable = Platform.isWindows ? gradlew : './$gradlew';

16
/// Tests that the Flutter module project template works and supports
17
/// adding Flutter to an existing Android app.
18
Future<void> main() async {
19 20
  await task(() async {

21 22 23 24
    section('Find Java');

    final String javaHome = await findJavaHome();
    if (javaHome == null)
25
      return TaskResult.failure('Could not find Java');
26 27
    print('\nUsing JAVA_HOME=$javaHome');

28
    section('Create Flutter module project');
29

30
    final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
31
    final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
32
    try {
33
      await inDirectory(tempDir, () async {
34 35
        await flutter(
          'create',
36
          options: <String>['--org', 'io.flutter.devicelab', '--template=module', 'hello'],
37 38 39
        );
      });

40 41
      section('Add plugins');

42
      final File pubspec = File(path.join(projectDir.path, 'pubspec.yaml'));
43 44 45
      String content = await pubspec.readAsString();
      content = content.replaceFirst(
        '\ndependencies:\n',
46
        '\ndependencies:\n  device_info:\n  package_info:\n',
47 48
      );
      await pubspec.writeAsString(content, flush: true);
49
      await inDirectory(projectDir, () async {
50 51 52 53 54
        await flutter(
          'packages',
          options: <String>['get'],
        );
      });
55

56
      section('Build Flutter module library archive');
57

58
      await inDirectory(Directory(path.join(projectDir.path, '.android')), () async {
59
        await exec(
60
          gradlewExecutable,
61 62 63
          <String>['flutter:assembleDebug'],
          environment: <String, String>{ 'JAVA_HOME': javaHome },
        );
64 65
      });

66
      final bool aarBuilt = exists(File(path.join(
67
        projectDir.path,
68 69
        '.android',
        'Flutter',
70 71 72 73 74 75 76
        'build',
        'outputs',
        'aar',
        'flutter-debug.aar',
      )));

      if (!aarBuilt) {
77
        return TaskResult.failure('Failed to build .aar');
78 79
      }

80 81
      section('Build ephemeral host app');

82
      await inDirectory(projectDir, () async {
83 84 85 86 87 88
        await flutter(
          'build',
          options: <String>['apk'],
        );
      });

89
      final bool ephemeralHostApkBuilt = exists(File(path.join(
90
        projectDir.path,
91 92 93 94 95 96 97 98
        'build',
        'host',
        'outputs',
        'apk',
        'release',
        'app-release.apk',
      )));

99
      if (!ephemeralHostApkBuilt) {
100
        return TaskResult.failure('Failed to build ephemeral host .apk');
101 102
      }

103 104
      section('Clean build');

105
      await inDirectory(projectDir, () async {
106 107 108
        await flutter('clean');
      });

109
      section('Make Android host app editable');
110

111
      await inDirectory(projectDir, () async {
112
        await flutter(
113
          'make-host-app-editable',
114 115 116 117
          options: <String>['android'],
        );
      });

118
      section('Build editable host app');
119

120
      await inDirectory(projectDir, () async {
121 122 123 124 125 126
        await flutter(
          'build',
          options: <String>['apk'],
        );
      });

127 128
      final bool editableHostApkBuilt = exists(File(path.join(
        projectDir.path,
129 130 131 132 133 134 135 136
        'build',
        'host',
        'outputs',
        'apk',
        'release',
        'app-release.apk',
      )));

137 138
      if (!editableHostApkBuilt) {
        return TaskResult.failure('Failed to build editable host .apk');
139 140
      }

141
      section('Add to existing Android app');
142

143
      final Directory hostApp = Directory(path.join(tempDir.path, 'hello_host_app'));
144 145
      mkdir(hostApp);
      recursiveCopy(
146
        Directory(path.join(flutterDirectory.path, 'dev', 'integration_tests', 'android_host_app')),
147 148 149
        hostApp,
      );
      copy(
150
        File(path.join(projectDir.path, '.android', gradlew)),
151 152 153
        hostApp,
      );
      copy(
154
        File(path.join(projectDir.path, '.android', 'gradle', 'wrapper', 'gradle-wrapper.jar')),
155
        Directory(path.join(hostApp.path, 'gradle', 'wrapper')),
156 157
      );

158 159
      final File analyticsOutputFile = File(path.join(tempDir.path, 'analytics.log'));

160 161
      section('Build debug host APK');

162
      await inDirectory(hostApp, () async {
163 164 165 166
        if (!Platform.isWindows) {
          await exec('chmod', <String>['+x', 'gradlew']);
        }
        await exec(gradlewExecutable,
167
          <String>['app:assembleDebug'],
168 169 170 171
          environment: <String, String>{
            'JAVA_HOME': javaHome,
            'FLUTTER_ANALYTICS_LOG_FILE': analyticsOutputFile.path,
          },
172
        );
173 174
      });

175 176 177
      section('Check debug APK exists');

      final String debugHostApk = path.join(
178 179 180 181 182 183 184
        hostApp.path,
        'app',
        'build',
        'outputs',
        'apk',
        'debug',
        'app-debug.apk',
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
      );
      if (!exists(File(debugHostApk))) {
        return TaskResult.failure('Failed to build debug host APK');
      }

      section('Check files in debug APK');

      checkItContains<String>(<String>[
        'AndroidManifest.xml',
        'assets/flutter_assets/isolate_snapshot_data',
        'assets/flutter_assets/kernel_blob.bin',
        'assets/flutter_assets/vm_snapshot_data',
      ], await getFilesInApk(debugHostApk));

      section('Check debug AndroidManifest.xml');

      final String androidManifestDebug = await getAndroidManifest(debugHostApk);
      if (!androidManifestDebug.contains('''
        <meta-data
            android:name="flutterProjectType"
            android:value="module" />''')
      ) {
        return TaskResult.failure('Debug host APK doesn\'t contain metadata: flutterProjectType = module ');
208
      }
209 210 211 212 213 214 215 216 217 218 219 220

      final String analyticsOutput = analyticsOutputFile.readAsStringSync();
      if (!analyticsOutput.contains('cd24: android-arm64')
          || !analyticsOutput.contains('cd25: true')
          || !analyticsOutput.contains('viewName: build/bundle')) {
        return TaskResult.failure(
          'Building outer app produced the following analytics: "$analyticsOutput"'
          'but not the expected strings: "cd24: android-arm64", "cd25: true" and '
          '"viewName: build/bundle"'
        );
      }

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
      section('Build release host APK');

      await inDirectory(hostApp, () async {
        await exec(gradlewExecutable,
          <String>['app:assembleRelease'],
          environment: <String, String>{
            'JAVA_HOME': javaHome,
            'FLUTTER_ANALYTICS_LOG_FILE': analyticsOutputFile.path,
          },
        );
      });

      final String releaseHostApk = path.join(
        hostApp.path,
        'app',
        'build',
        'outputs',
        'apk',
        'release',
        'app-release-unsigned.apk',
      );
      if (!exists(File(releaseHostApk))) {
        return TaskResult.failure('Failed to build release host APK');
      }

      section('Check files in release APK');

      checkItContains<String>(<String>[
        'AndroidManifest.xml',
        'lib/arm64-v8a/libapp.so',
        'lib/arm64-v8a/libflutter.so',
        'lib/armeabi-v7a/libapp.so',
        'lib/armeabi-v7a/libflutter.so',
      ], await getFilesInApk(releaseHostApk));

      section('Check release AndroidManifest.xml');

      final String androidManifestRelease = await getAndroidManifest(debugHostApk);
      if (!androidManifestRelease.contains('''
        <meta-data
            android:name="flutterProjectType"
            android:value="module" />''')
      ) {
        return TaskResult.failure('Release host APK doesn\'t contain metadata: flutterProjectType = module ');
      }
266
      return TaskResult.success(null);
267 268
    } on TaskResult catch (taskResult) {
      return taskResult;
269
    } catch (e) {
270
      return TaskResult.failure(e.toString());
271
    } finally {
272
      rmTree(tempDir);
273 274 275
    }
  });
}