devices_test.dart 8.35 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
import 'package:process/process.dart';

18
import '../src/common.dart';
19
import '../src/context.dart';
20
import '../src/mocks.dart';
21

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

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

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

    setUp(() {
36
      mockIMobileDevice = MockIMobileDevice();
37 38
    });

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

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

    testUsingContext('returns attached devices', () async {
57
      when(iMobileDevice.isInstalled).thenReturn(true);
58
      when(iMobileDevice.getAvailableDeviceIDs())
59
          .thenAnswer((Invocation invocation) => Future<String>.value('''
60 61
98206e7a4afd4aedaff06e687594e089dede3c44
f577a7903cc54959be2e34bc4f7f80b7009efcf4
62
'''));
63
      when(iMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'DeviceName'))
64
          .thenAnswer((_) => Future<String>.value('La tele me regarde'));
65
      when(iMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'ProductVersion'))
66
          .thenAnswer((_) => Future<String>.value('10.3.2'));
67
      when(iMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'DeviceName'))
68
          .thenAnswer((_) => Future<String>.value('Puits sans fond'));
69
      when(iMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'ProductVersion'))
70
          .thenAnswer((_) => Future<String>.value('11.0'));
71
      final List<IOSDevice> devices = await IOSDevice.getAttachedDevices();
72 73 74 75 76 77
      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>{
78
      IMobileDevice: () => mockIMobileDevice,
79
    });
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

    testUsingContext('returns attached devices and ignores devices that cannot be found by ideviceinfo', () async {
      when(iMobileDevice.isInstalled).thenReturn(true);
      when(iMobileDevice.getAvailableDeviceIDs())
          .thenAnswer((Invocation invocation) => Future<String>.value('''
98206e7a4afd4aedaff06e687594e089dede3c44
f577a7903cc54959be2e34bc4f7f80b7009efcf4
'''));
      when(iMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'DeviceName'))
          .thenAnswer((_) => Future<String>.value('La tele me regarde'));
      when(iMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'DeviceName'))
          .thenThrow(IOSDeviceNotFoundError('Device not found'));
      final List<IOSDevice> devices = await IOSDevice.getAttachedDevices();
      expect(devices, hasLength(1));
      expect(devices[0].id, '98206e7a4afd4aedaff06e687594e089dede3c44');
      expect(devices[0].name, 'La tele me regarde');
    }, overrides: <Type, Generator>{
      IMobileDevice: () => mockIMobileDevice,
    });
99 100
  });

101 102 103 104 105 106 107 108 109 110 111
  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!');
    });
  });
112 113
  group('logging', () {
    MockIMobileDevice mockIMobileDevice;
114
    MockIosProject mockIosProject;
115 116

    setUp(() {
117 118
      mockIMobileDevice = MockIMobileDevice();
      mockIosProject = MockIosProject();
119 120
    });

121
    testUsingContext('suppresses non-Flutter lines from output', () async {
122
      when(mockIMobileDevice.startLogger('123456')).thenAnswer((Invocation invocation) {
123
        final Process mockProcess = MockProcess();
124
        when(mockProcess.stdout).thenAnswer((Invocation invocation) =>
125
            Stream<List<int>>.fromIterable(<List<int>>['''
126
  Runner(Flutter)[297] <Notice>: A is for ari
127 128
  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>)
129 130
  Runner(Flutter)[297] <Notice>: I is for ichigo
  Runner(UIKit)[297] <Notice>: E is for enpitsu"
131
  '''.codeUnits]));
132 133
        when(mockProcess.stderr)
            .thenAnswer((Invocation invocation) => const Stream<List<int>>.empty());
134
        // Delay return of exitCode until after stdout stream data, since it terminates the logger.
135
        when(mockProcess.exitCode)
136 137
            .thenAnswer((Invocation invocation) => Future<int>.delayed(Duration.zero, () => 0));
        return Future<Process>.value(mockProcess);
138 139
      });

140
      final IOSDevice device = IOSDevice('123456');
141
      final DeviceLogReader logReader = device.getLogReader(
142
        app: BuildableIOSApp(mockIosProject),
143 144 145 146 147 148 149
      );

      final List<String> lines = await logReader.logLines.toList();
      expect(lines, <String>['A is for ari', 'I is for ichigo']);
    }, overrides: <Type, Generator>{
      IMobileDevice: () => mockIMobileDevice,
    });
150 151

    testUsingContext('includes multi-line Flutter logs in the output', () async {
152
      when(mockIMobileDevice.startLogger('123456')).thenAnswer((Invocation invocation) {
153
        final Process mockProcess = MockProcess();
154
        when(mockProcess.stdout).thenAnswer((Invocation invocation) =>
155
            Stream<List<int>>.fromIterable(<List<int>>['''
156 157 158 159 160 161 162 163 164 165
  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)
166 167
            .thenAnswer((Invocation invocation) => Future<int>.delayed(Duration.zero, () => 0));
        return Future<Process>.value(mockProcess);
168 169
      });

170
      final IOSDevice device = IOSDevice('123456');
171
      final DeviceLogReader logReader = device.getLogReader(
172
        app: BuildableIOSApp(mockIosProject),
173 174 175 176 177 178 179 180 181 182 183 184
      );

      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,
    });
185
  });
186
}