ios_device_logger_test.dart 6.7 KB
Newer Older
1 2 3 4
// 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.

5 6
import 'dart:async';

7
import 'package:flutter_tools/src/artifacts.dart';
8
import 'package:flutter_tools/src/base/logger.dart';
9
import 'package:flutter_tools/src/build_info.dart';
10
import 'package:flutter_tools/src/convert.dart';
11 12 13 14
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/mac.dart';
import 'package:mockito/mockito.dart';
15
import 'package:vm_service/vm_service.dart';
16 17 18

import '../../src/common.dart';
import '../../src/context.dart';
19
import '../../src/testbed.dart';
20 21 22 23

void main() {
  FakeProcessManager processManager;
  MockArtifacts artifacts;
24 25
  FakeCache fakeCache;
  BufferLogger logger;
26 27 28

  setUp(() {
    processManager = FakeProcessManager.list(<FakeCommand>[]);
29
    fakeCache = FakeCache();
30
    artifacts = MockArtifacts();
31
    logger = BufferLogger.test();
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
    when(artifacts.getArtifactPath(Artifact.idevicesyslog, platform: TargetPlatform.ios))
      .thenReturn('idevice-syslog');
  });

  testWithoutContext('decodeSyslog 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-/\'
      r'134_(\M-c\M^C\M^D)_/\M-B\M-/ \M-l\M^F\240!');

    expect(decoded, r'I ❤️ syslog ¯\_(ツ)_/¯ 솠!');
  });

  testWithoutContext('decodeSyslog 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!');
  });

50
  testWithoutContext('IOSDeviceLogReader suppresses non-Flutter lines from output with syslog', () async {
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
    processManager.addCommand(
      const FakeCommand(
        command: <String>[
          'idevice-syslog', '-u', '1234',
        ],
        stdout: '''
Runner(Flutter)[297] <Notice>: A is for ari
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>)
Runner(Flutter)[297] <Notice>: I is for ichigo
Runner(UIKit)[297] <Notice>: E is for enpitsu"
'''
      ),
    );
    final DeviceLogReader logReader = IOSDeviceLogReader.test(
66 67 68 69 70 71
      iMobileDevice: IMobileDevice(
        artifacts: artifacts,
        processManager: processManager,
        cache: fakeCache,
        logger: logger,
      ),
72 73 74 75 76 77
    );
    final List<String> lines = await logReader.logLines.toList();

    expect(lines, <String>['A is for ari', 'I is for ichigo']);
  });

78
  testWithoutContext('IOSDeviceLogReader includes multi-line Flutter logs in the output with syslog', () async {
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
    processManager.addCommand(
      const FakeCommand(
        command: <String>[
          'idevice-syslog', '-u', '1234',
        ],
        stdout: '''
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
'''
      ),
    );
    final DeviceLogReader logReader = IOSDeviceLogReader.test(
94 95 96 97 98 99
      iMobileDevice: IMobileDevice(
        artifacts: artifacts,
        processManager: processManager,
        cache: fakeCache,
        logger: logger,
      ),
100 101 102 103 104 105 106 107 108 109 110
    );
    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.',
    ]);
  });

111
  testWithoutContext('includes multi-line Flutter logs in the output', () async {
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    processManager.addCommand(
      const FakeCommand(
        command: <String>[
          'idevice-syslog', '-u', '1234',
        ],
        stdout: '''
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
''',
      ),
    );

    final DeviceLogReader logReader = IOSDeviceLogReader.test(
128 129 130 131 132 133
      iMobileDevice: IMobileDevice(
        artifacts: artifacts,
        processManager: processManager,
        cache: fakeCache,
        logger: logger,
      ),
134 135 136 137 138 139 140 141 142 143
    );
    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.',
    ]);
  });
144 145 146 147 148 149 150 151 152 153 154 155

  testWithoutContext('IOSDeviceLogReader can listen to VM Service logs', () async {
    final MockVmService vmService = MockVmService();
    final DeviceLogReader logReader = IOSDeviceLogReader.test(
      useSyslog: false,
      iMobileDevice: IMobileDevice(
        artifacts: artifacts,
        processManager: processManager,
        cache: fakeCache,
        logger: logger,
      ),
    );
156 157
    final StreamController<Event> stdoutController = StreamController<Event>();
    final StreamController<Event> stderController = StreamController<Event>();
158
    final Completer<Success> stdoutCompleter = Completer<Success>();
159
    final Completer<Success> stderrCompleter = Completer<Success>();
160 161 162
    when(vmService.streamListen('Stdout')).thenAnswer((Invocation invocation) {
      return stdoutCompleter.future;
    });
163 164 165
    when(vmService.streamListen('Stderr')).thenAnswer((Invocation invocation) {
      return stderrCompleter.future;
    });
166
    when(vmService.onStdoutEvent).thenAnswer((Invocation invocation) {
167 168 169 170
      return stdoutController.stream;
    });
    when(vmService.onStderrEvent).thenAnswer((Invocation invocation) {
      return stderController.stream;
171 172 173 174
    });
    logReader.connectedVMService = vmService;

    stdoutCompleter.complete(Success());
175 176
    stderrCompleter.complete(Success());
    stdoutController.add(Event(
177 178 179 180
      kind: 'Stdout',
      timestamp: 0,
      bytes: base64.encode(utf8.encode('  This is a message ')),
    ));
181 182 183 184 185
    stderController.add(Event(
      kind: 'Stderr',
      timestamp: 0,
      bytes: base64.encode(utf8.encode('  And this is an error ')),
    ));
186 187

    // Wait for stream listeners to fire.
188 189 190 191
    await expectLater(logReader.logLines, emitsInAnyOrder(<Matcher>[
      equals('  This is a message '),
      equals('  And this is an error '),
    ]));
192
  });
193 194 195
}

class MockArtifacts extends Mock implements Artifacts {}
196
class MockVmService extends Mock implements VmService {}