ios_device_install_test.dart 11.7 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 '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/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
10
import 'package:flutter_tools/src/base/platform.dart';
11 12
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
13
import 'package:flutter_tools/src/ios/application_package.dart';
14 15
import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/ios_deploy.dart';
16
import 'package:flutter_tools/src/ios/iproxy.dart';
17
import 'package:flutter_tools/src/ios/mac.dart';
18 19

import '../../src/common.dart';
20
import '../../src/fake_process_manager.dart';
21
import '../../src/fakes.dart';
22 23

const Map<String, String> kDyLdLibEntry = <String, String>{
24
  'DYLD_LIBRARY_PATH': '/path/to/libraries',
25 26 27
};

void main() {
28 29 30 31
  late Artifacts artifacts;
  late String iosDeployPath;
  late FileSystem fileSystem;
  late Directory bundleDirectory;
32 33 34

  setUp(() {
    artifacts = Artifacts.test();
35 36
    fileSystem = MemoryFileSystem.test();
    bundleDirectory = fileSystem.directory('bundle');
37
    iosDeployPath = artifacts.getHostArtifact(HostArtifact.iosDeploy).path;
38 39
  });

40
  testWithoutContext('IOSDevice.installApp calls ios-deploy correctly with USB', () async {
41 42
    final IOSApp iosApp = PrebuiltIOSApp(
      projectBundleId: 'app',
43 44
      uncompressedBundle: fileSystem.currentDirectory,
      applicationPackage: bundleDirectory,
45 46
    );
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
47 48 49 50 51 52 53 54 55 56 57 58 59 60
      FakeCommand(
        command: <String>[
          iosDeployPath,
          '--id',
          '1234',
          '--bundle',
          '/',
          '--no-wifi',
        ],
        environment: const <String, String>{
          'PATH': '/usr/bin:null',
          ...kDyLdLibEntry,
        },
      ),
61 62 63 64
    ]);
    final IOSDevice device = setUpIOSDevice(
      processManager: processManager,
      fileSystem: fileSystem,
65
      interfaceType: IOSDeviceConnectionInterface.usb,
66
      artifacts: artifacts,
67 68 69 70
    );
    final bool wasInstalled = await device.installApp(iosApp);

    expect(wasInstalled, true);
71
    expect(processManager, hasNoRemainingExpectations);
72 73 74 75 76
  });

  testWithoutContext('IOSDevice.installApp calls ios-deploy correctly with network', () async {
    final IOSApp iosApp = PrebuiltIOSApp(
      projectBundleId: 'app',
77 78
      uncompressedBundle: fileSystem.currentDirectory,
      applicationPackage: bundleDirectory,
79 80
    );
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
81 82 83 84 85 86 87 88 89 90 91 92 93
      FakeCommand(
        command: <String>[
          iosDeployPath,
          '--id',
          '1234',
          '--bundle',
          '/',
        ],
        environment: const <String, String>{
          'PATH': '/usr/bin:null',
          ...kDyLdLibEntry,
        },
      ),
94 95 96 97
    ]);
    final IOSDevice device = setUpIOSDevice(
      processManager: processManager,
      fileSystem: fileSystem,
98
      interfaceType: IOSDeviceConnectionInterface.network,
99
      artifacts: artifacts,
100 101 102 103
    );
    final bool wasInstalled = await device.installApp(iosApp);

    expect(wasInstalled, true);
104
    expect(processManager, hasNoRemainingExpectations);
105 106 107
  });

  testWithoutContext('IOSDevice.uninstallApp calls ios-deploy correctly', () async {
108 109 110 111 112
    final IOSApp iosApp = PrebuiltIOSApp(
      projectBundleId: 'app',
      uncompressedBundle: bundleDirectory,
      applicationPackage: bundleDirectory,
    );
113
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
114 115 116 117 118 119 120 121 122 123 124 125 126 127
      FakeCommand(
        command: <String>[
          iosDeployPath,
          '--id',
          '1234',
          '--uninstall_only',
          '--bundle_id',
          'app',
        ],
        environment: const <String, String>{
          'PATH': '/usr/bin:null',
          ...kDyLdLibEntry,
        },
      ),
128
    ]);
129
    final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts);
130 131 132
    final bool wasUninstalled = await device.uninstallApp(iosApp);

    expect(wasUninstalled, true);
133
    expect(processManager, hasNoRemainingExpectations);
134 135
  });

136 137
  group('isAppInstalled', () {
    testWithoutContext('catches ProcessException from ios-deploy', () async {
138 139 140 141 142
      final IOSApp iosApp = PrebuiltIOSApp(
        projectBundleId: 'app',
        uncompressedBundle: bundleDirectory,
        applicationPackage: bundleDirectory,
      );
143
      final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
144 145
        FakeCommand(command: <String>[
          iosDeployPath,
146 147 148 149 150 151 152 153 154 155
          '--id',
          '1234',
          '--exists',
          '--timeout',
          '10',
          '--bundle_id',
          'app',
        ], environment: const <String, String>{
          'PATH': '/usr/bin:null',
          ...kDyLdLibEntry,
156
        }, exception: const ProcessException('ios-deploy', <String>[])),
157
      ]);
158
      final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts);
159
      final bool isAppInstalled = await device.isAppInstalled(iosApp);
160

161
      expect(isAppInstalled, false);
162
      expect(processManager, hasNoRemainingExpectations);
163 164 165
    });

    testWithoutContext('returns true when app is installed', () async {
166 167 168 169 170
      final IOSApp iosApp = PrebuiltIOSApp(
        projectBundleId: 'app',
        uncompressedBundle: bundleDirectory,
        applicationPackage: bundleDirectory,
      );
171
      final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
        FakeCommand(
          command: <String>[
            iosDeployPath,
            '--id',
            '1234',
            '--exists',
            '--timeout',
            '10',
            '--bundle_id',
            'app',
          ],
          environment: const <String, String>{
            'PATH': '/usr/bin:null',
            ...kDyLdLibEntry,
          },
        ),
188
      ]);
189
      final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts);
190 191 192
      final bool isAppInstalled = await device.isAppInstalled(iosApp);

      expect(isAppInstalled, isTrue);
193
      expect(processManager, hasNoRemainingExpectations);
194 195 196
    });

    testWithoutContext('returns false when app is not installed', () async {
197 198 199 200 201
      final IOSApp iosApp = PrebuiltIOSApp(
        projectBundleId: 'app',
        uncompressedBundle: bundleDirectory,
        applicationPackage: bundleDirectory,
      );
202
      final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
        FakeCommand(
          command: <String>[
            iosDeployPath,
            '--id',
            '1234',
            '--exists',
            '--timeout',
            '10',
            '--bundle_id',
            'app',
          ],
          environment: const <String, String>{
            'PATH': '/usr/bin:null',
            ...kDyLdLibEntry,
          },
          exitCode: 255,
        ),
220 221
      ]);
      final BufferLogger logger = BufferLogger.test();
222
      final IOSDevice device = setUpIOSDevice(processManager: processManager, logger: logger, artifacts: artifacts);
223 224 225
      final bool isAppInstalled = await device.isAppInstalled(iosApp);

      expect(isAppInstalled, isFalse);
226
      expect(processManager, hasNoRemainingExpectations);
227 228 229 230
      expect(logger.traceText, contains('${iosApp.id} not installed on ${device.id}'));
    });

    testWithoutContext('returns false on command timeout or other error', () async {
231 232 233 234 235
      final IOSApp iosApp = PrebuiltIOSApp(
        projectBundleId: 'app',
        uncompressedBundle: bundleDirectory,
        applicationPackage: bundleDirectory,
      );
236 237
      const String stderr = '2020-03-26 17:48:43.484 ios-deploy[21518:5501783] [ !! ] Timed out waiting for device';
      final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
        FakeCommand(
          command: <String>[
            iosDeployPath,
            '--id',
            '1234',
            '--exists',
            '--timeout',
            '10',
            '--bundle_id',
            'app',
          ],
          environment: const <String, String>{
            'PATH': '/usr/bin:null',
            ...kDyLdLibEntry,
          },
          stderr: stderr,
          exitCode: 253,
        ),
256 257
      ]);
      final BufferLogger logger = BufferLogger.test();
258
      final IOSDevice device = setUpIOSDevice(processManager: processManager, logger: logger, artifacts: artifacts);
259 260 261
      final bool isAppInstalled = await device.isAppInstalled(iosApp);

      expect(isAppInstalled, isFalse);
262
      expect(processManager, hasNoRemainingExpectations);
263 264
      expect(logger.traceText, contains(stderr));
    });
265 266 267 268 269
  });

  testWithoutContext('IOSDevice.installApp catches ProcessException from ios-deploy', () async {
    final IOSApp iosApp = PrebuiltIOSApp(
      projectBundleId: 'app',
270 271
      uncompressedBundle: fileSystem.currentDirectory,
      applicationPackage: bundleDirectory,
272 273
    );
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
274 275
      FakeCommand(command: <String>[
        iosDeployPath,
276 277 278 279 280 281 282 283
        '--id',
        '1234',
        '--bundle',
        '/',
        '--no-wifi',
      ], environment: const <String, String>{
        'PATH': '/usr/bin:null',
        ...kDyLdLibEntry,
284
      }, exception: const ProcessException('ios-deploy', <String>[])),
285
    ]);
286
    final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts);
287 288 289 290 291 292
    final bool wasAppInstalled = await device.installApp(iosApp);

    expect(wasAppInstalled, false);
  });

  testWithoutContext('IOSDevice.uninstallApp catches ProcessException from ios-deploy', () async {
293 294 295 296 297
    final IOSApp iosApp = PrebuiltIOSApp(
      projectBundleId: 'app',
      uncompressedBundle: bundleDirectory,
      applicationPackage: bundleDirectory,
    );
298
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
299 300
      FakeCommand(command: <String>[
        iosDeployPath,
301 302 303 304 305 306 307 308
        '--id',
        '1234',
        '--uninstall_only',
        '--bundle_id',
        'app',
      ], environment: const <String, String>{
        'PATH': '/usr/bin:null',
        ...kDyLdLibEntry,
309
      }, exception: const ProcessException('ios-deploy', <String>[])),
310
    ]);
311
    final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts);
312 313 314 315 316 317 318
    final bool wasAppUninstalled = await device.uninstallApp(iosApp);

    expect(wasAppUninstalled, false);
  });
}

IOSDevice setUpIOSDevice({
319 320 321 322 323
  required ProcessManager processManager,
  FileSystem? fileSystem,
  Logger? logger,
  IOSDeviceConnectionInterface? interfaceType,
  Artifacts? artifacts,
324
}) {
325
  logger ??= BufferLogger.test();
326 327 328 329
  final FakePlatform platform = FakePlatform(
    operatingSystem: 'macos',
    environment: <String, String>{},
  );
330 331 332 333 334 335
  artifacts ??= Artifacts.test();
  final Cache cache = Cache.test(
    platform: platform,
    artifacts: <ArtifactSet>[
      FakeDyldEnvironmentArtifact(),
    ],
336
    processManager: FakeProcessManager.any(),
337
  );
338 339 340
  return IOSDevice(
    '1234',
    name: 'iPhone 1',
341
    logger: logger,
342 343 344 345
    fileSystem: fileSystem ?? MemoryFileSystem.test(),
    sdkVersion: '13.3',
    cpuArchitecture: DarwinArch.arm64,
    platform: platform,
346
    iMobileDevice: IMobileDevice(
347
      logger: logger,
348 349 350 351
      processManager: processManager,
      artifacts: artifacts,
      cache: cache,
    ),
352
    iosDeploy: IOSDeploy(
353
      logger: logger,
354 355 356 357 358
      platform: platform,
      processManager: processManager,
      artifacts: artifacts,
      cache: cache,
    ),
359
    iProxy: IProxy.test(logger: logger, processManager: processManager),
360
    interfaceType: interfaceType ?? IOSDeviceConnectionInterface.usb,
361 362
  );
}