devices_test.dart 7.31 KB
Newer Older
1 2 3 4
// Copyright 2017 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.

5 6
import 'dart:async';

7
import 'package:file/file.dart';
8
import 'package:flutter_tools/src/application_package.dart';
9
import 'package:flutter_tools/src/base/file_system.dart';
10 11
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/device.dart';
12
import 'package:flutter_tools/src/ios/devices.dart';
13
import 'package:flutter_tools/src/ios/mac.dart';
14
import 'package:mockito/mockito.dart';
15
import 'package:platform/platform.dart';
16 17 18
import 'package:process/process.dart';
import 'package:test/test.dart';

19
import '../src/context.dart';
20

21
class MockIMobileDevice extends Mock implements IMobileDevice {}
22
class MockProcessManager extends Mock implements ProcessManager {}
23
class MockXcode extends Mock implements Xcode {}
24
class MockFile extends Mock implements File {}
25
class MockProcess extends Mock implements Process {}
26 27

void main() {
28 29 30
  final FakePlatform osx = new FakePlatform.fromPlatform(const LocalPlatform());
  osx.operatingSystem = 'macos';

31
  group('getAttachedDevices', () {
32
    MockIMobileDevice mockIMobileDevice;
33 34

    setUp(() {
35
      mockIMobileDevice = new MockIMobileDevice();
36 37
    });

38
    testUsingContext('return no devices if Xcode is not installed', () async {
39
      when(mockIMobileDevice.isInstalled).thenReturn(false);
40
      expect(await IOSDevice.getAttachedDevices(), isEmpty);
41
    }, overrides: <Type, Generator>{
42
      IMobileDevice: () => mockIMobileDevice,
43 44 45
    });

    testUsingContext('returns no devices if none are attached', () async {
46
      when(iMobileDevice.isInstalled).thenReturn(true);
47 48
      when(iMobileDevice.getAvailableDeviceIDs())
          .thenAnswer((Invocation invocation) => new Future<String>.value(''));
49
      final List<IOSDevice> devices = await IOSDevice.getAttachedDevices();
50 51
      expect(devices, isEmpty);
    }, overrides: <Type, Generator>{
52
      IMobileDevice: () => mockIMobileDevice,
53 54 55
    });

    testUsingContext('returns attached devices', () async {
56
      when(iMobileDevice.isInstalled).thenReturn(true);
57 58
      when(iMobileDevice.getAvailableDeviceIDs())
          .thenAnswer((Invocation invocation) => new Future<String>.value('''
59 60
98206e7a4afd4aedaff06e687594e089dede3c44
f577a7903cc54959be2e34bc4f7f80b7009efcf4
61
'''));
62 63 64 65 66 67 68 69
      when(iMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'DeviceName'))
          .thenAnswer((_) => new Future<String>.value('La tele me regarde'));
      when(iMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'ProductVersion'))
          .thenAnswer((_) => new Future<String>.value('10.3.2'));
      when(iMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'DeviceName'))
          .thenAnswer((_) => new Future<String>.value('Puits sans fond'));
      when(iMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'ProductVersion'))
          .thenAnswer((_) => new Future<String>.value('11.0'));
70
      final List<IOSDevice> devices = await IOSDevice.getAttachedDevices();
71 72 73 74 75 76
      expect(devices, hasLength(2));
      expect(devices[0].id, '98206e7a4afd4aedaff06e687594e089dede3c44');
      expect(devices[0].name, 'La tele me regarde');
      expect(devices[1].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4');
      expect(devices[1].name, 'Puits sans fond');
    }, overrides: <Type, Generator>{
77
      IMobileDevice: () => mockIMobileDevice,
78 79 80
    });
  });

81 82 83 84 85 86 87 88 89 90 91
  group('decodeSyslog', () {
    test('decodes a syslog-encoded line', () {
      final String decoded = decodeSyslog(r'I \M-b\M^]\M-$\M-o\M-8\M^O syslog \M-B\M-/\134_(\M-c\M^C\M^D)_/\M-B\M-/ \M-l\M^F\240!');
      expect(decoded, r'I ❤️ syslog ¯\_(ツ)_/¯ 솠!');
    });

    test('passes through un-decodeable lines as-is', () {
      final String decoded = decodeSyslog(r'I \M-b\M^O syslog!');
      expect(decoded, r'I \M-b\M^O syslog!');
    });
  });
92 93 94 95 96 97 98
  group('logging', () {
    MockIMobileDevice mockIMobileDevice;

    setUp(() {
      mockIMobileDevice = new MockIMobileDevice();
    });

99
    testUsingContext('suppresses non-Flutter lines from output', () async {
100
      when(mockIMobileDevice.startLogger()).thenAnswer((Invocation invocation) {
101
        final Process mockProcess = new MockProcess();
102 103
        when(mockProcess.stdout).thenAnswer((Invocation invocation) =>
            new Stream<List<int>>.fromIterable(<List<int>>['''
104
  Runner(Flutter)[297] <Notice>: A is for ari
105 106
  Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt MobileGestaltSupport.m:153: pid 123 (Runner) does not have sandbox access for frZQaeyWLUvLjeuEK43hmg and IS NOT appropriately entitled
  Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt MobileGestalt.c:550: no access to InverseDeviceID (see <rdar://problem/11744455>)
107 108
  Runner(Flutter)[297] <Notice>: I is for ichigo
  Runner(UIKit)[297] <Notice>: E is for enpitsu"
109
  '''.codeUnits]));
110 111
        when(mockProcess.stderr)
            .thenAnswer((Invocation invocation) => const Stream<List<int>>.empty());
112
        // Delay return of exitCode until after stdout stream data, since it terminates the logger.
113
        when(mockProcess.exitCode)
114
            .thenAnswer((Invocation invocation) => new Future<int>.delayed(Duration.zero, () => 0));
115 116 117 118 119 120 121 122 123 124 125 126 127
        return new Future<Process>.value(mockProcess);
      });

      final IOSDevice device = new IOSDevice('123456');
      final DeviceLogReader logReader = device.getLogReader(
        app: new BuildableIOSApp(projectBundleId: 'bundleId'),
      );

      final List<String> lines = await logReader.logLines.toList();
      expect(lines, <String>['A is for ari', 'I is for ichigo']);
    }, overrides: <Type, Generator>{
      IMobileDevice: () => mockIMobileDevice,
    });
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162

    testUsingContext('includes multi-line Flutter logs in the output', () async {
      when(mockIMobileDevice.startLogger()).thenAnswer((Invocation invocation) {
        final Process mockProcess = new MockProcess();
        when(mockProcess.stdout).thenAnswer((Invocation invocation) =>
            new Stream<List<int>>.fromIterable(<List<int>>['''
  Runner(Flutter)[297] <Notice>: This is a multi-line message,
  with another Flutter message following it.
  Runner(Flutter)[297] <Notice>: This is a multi-line message,
  with a non-Flutter log message following it.
  Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
  '''.codeUnits]));
        when(mockProcess.stderr)
            .thenAnswer((Invocation invocation) => const Stream<List<int>>.empty());
        // Delay return of exitCode until after stdout stream data, since it terminates the logger.
        when(mockProcess.exitCode)
            .thenAnswer((Invocation invocation) => new Future<int>.delayed(Duration.zero, () => 0));
        return new Future<Process>.value(mockProcess);
      });

      final IOSDevice device = new IOSDevice('123456');
      final DeviceLogReader logReader = device.getLogReader(
        app: new BuildableIOSApp(projectBundleId: 'bundleId'),
      );

      final List<String> lines = await logReader.logLines.toList();
      expect(lines, <String>[
        'This is a multi-line message,',
        '  with another Flutter message following it.',
        'This is a multi-line message,',
        '  with a non-Flutter log message following it.',
      ]);
    }, overrides: <Type, Generator>{
      IMobileDevice: () => mockIMobileDevice,
    });
163
  });
164
}