ios_content_validation_test.dart 4.79 KB
Newer Older
1 2 3 4 5 6 7 8 9
// 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:io';

import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
10
import 'package:flutter_devicelab/framework/ios.dart';
11 12 13 14 15 16 17
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;

Future<void> main() async {
  await task(() async {
    try {
      bool foundProjectName = false;
18
      bool bitcode = false;
19
      await runProjectTest((FlutterProject flutterProject) async {
20
        section('Build app with with --obfuscate');
21 22 23 24 25 26
        await inDirectory(flutterProject.rootPath, () async {
          await flutter('build', options: <String>[
            'ios',
            '--release',
            '--obfuscate',
            '--split-debug-info=foo/',
27
            '--no-codesign',
28 29
          ]);
        });
30
        final String buildPath = path.join(
31
          flutterProject.rootPath,
32 33 34
          'build',
          'ios',
          'iphoneos',
35
        );
36 37 38
        final String outputAppPath = path.join(
          buildPath,
          'Runner.app',
39
        );
40 41 42 43 44 45 46 47 48 49 50 51 52
        final Directory outputAppFramework = Directory(path.join(
          outputAppPath,
          'Frameworks',
          'App.framework',
        ));

        final File outputAppFrameworkBinary = File(path.join(
          outputAppFramework.path,
          'App',
        ));

        if (!outputAppFrameworkBinary.existsSync()) {
          fail('Failed to produce expected output at ${outputAppFrameworkBinary.path}');
53 54
        }

55 56
        section('Validate obfuscation');

57 58 59 60 61
        // Verify that an identifier from the Dart project code is not present
        // in the compiled binary.
        await inDirectory(flutterProject.rootPath, () async {
          final String response = await eval(
            'grep',
62
            <String>[flutterProject.name, outputAppFrameworkBinary.path],
63 64 65 66 67 68
            canFail: true,
          );
          if (response.trim().contains('matches')) {
            foundProjectName = true;
          }
        });
69 70 71

        section('Validate bitcode');

72
        final Directory outputFlutterFramework = Directory(path.join(
73 74
          flutterProject.rootPath,
          outputAppPath,
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
          'Frameworks',
          'Flutter.framework',
        ));
        final File outputFlutterFrameworkBinary = File(path.join(
          outputFlutterFramework.path,
          'Flutter',
        ));

        if (!outputFlutterFrameworkBinary.existsSync()) {
          fail('Failed to produce expected output at ${outputFlutterFrameworkBinary.path}');
        }
        bitcode = await containsBitcode(outputFlutterFrameworkBinary.path);

        section('Xcode backend script');

        outputFlutterFramework.deleteSync(recursive: true);
        outputAppFramework.deleteSync(recursive: true);
        if (outputFlutterFramework.existsSync() || outputAppFramework.existsSync()) {
          fail('Failed to delete embedded frameworks');
        }

        final String xcodeBackendPath = path.join(
          flutterDirectory.path,
          'packages',
          'flutter_tools',
          'bin',
          'xcode_backend.sh'
        );

        // Simulate a commonly Xcode build setting misconfiguration
        // where FLUTTER_APPLICATION_PATH is missing
        final int result = await exec(
          xcodeBackendPath,
          <String>['embed_and_thin'],
          environment: <String, String>{
            'SOURCE_ROOT': flutterProject.iosPath,
            'TARGET_BUILD_DIR': buildPath,
            'FRAMEWORKS_FOLDER_PATH': 'Runner.app/Frameworks',
            'VERBOSE_SCRIPT_LOGGING': '1',
            'ACTION': 'install', // Skip bitcode stripping since we just checked that above.
          },
116 117
        );

118 119 120 121 122 123 124 125 126 127
        if (result != 0) {
          fail('xcode_backend embed_and_thin failed');
        }

        if (!outputFlutterFrameworkBinary.existsSync()) {
          fail('Failed to re-embed ${outputFlutterFrameworkBinary.path}');
        }

        if (!outputAppFrameworkBinary.existsSync()) {
          fail('Failed to re-embed ${outputAppFrameworkBinary.path}');
128
        }
129
      });
130

131 132 133
      if (foundProjectName) {
        return TaskResult.failure('Found project name in obfuscated dart library');
      }
134 135 136 137 138 139 140
      // Archiving should contain a bitcode blob, but not building in release.
      // This mimics Xcode behavior and present a developer from having to install a
      // 300+MB app to test devices.
      if (bitcode) {
        return TaskResult.failure('Bitcode present in Flutter.framework');
      }

141 142 143 144 145 146 147 148
      return TaskResult.success(null);
    } on TaskResult catch (taskResult) {
      return taskResult;
    } catch (e) {
      return TaskResult.failure(e.toString());
    }
  });
}