diff --git a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart index df8ff64646c..545c91b1fba 100644 --- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart @@ -517,13 +517,13 @@ void main() { }); group('log reader', () { - MockProcessManager mockProcessManager; + FakeProcessManager fakeProcessManager; MockIosProject mockIosProject; MockSimControl mockSimControl; MockXcode mockXcode; setUp(() { - mockProcessManager = MockProcessManager(); + fakeProcessManager = FakeProcessManager.list([]); mockIosProject = MockIosProject(); mockSimControl = MockSimControl(); mockXcode = MockXcode(); @@ -535,25 +535,49 @@ void main() { osx.environment['IOS_SIMULATOR_LOG_FILE_PATH'] = syslog.path; }); + testUsingContext('simulator can parse Xcode 8/iOS 10-style logs', () async { + fakeProcessManager + ..addCommand(const FakeCommand( + command: ['tail', '-n', '0', '-F', 'system.log'], + stdout: 'Dec 20 17:04:32 md32-11-vm1 Runner[88374]: flutter: Observatory listening on http://127.0.0.1:64213/1Uoeu523990=/', + )) + ..addCommand(const FakeCommand( + command: ['tail', '-n', '0', '-F', '/private/var/log/system.log'] + )); + + final IOSSimulator device = IOSSimulator( + '123456', + simulatorCategory: 'iOS 10.0', + simControl: mockSimControl, + xcode: mockXcode, + ); + final DeviceLogReader logReader = device.getLogReader( + app: await BuildableIOSApp.fromProject(mockIosProject), + ); + + final List lines = await logReader.logLines.toList(); + expect(lines, [ + 'flutter: Observatory listening on http://127.0.0.1:64213/1Uoeu523990=/', + ]); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); + }, overrides: { + ProcessManager: () => fakeProcessManager, + FileSystem: () => fileSystem, + Platform: () => osx, + }); + testUsingContext('simulator can output `)`', () async { - when(mockProcessManager.start(any, environment: null, workingDirectory: null)) - .thenAnswer((Invocation invocation) { - final Process mockProcess = MockProcess(); - when(mockProcess.stdout) - .thenAnswer((Invocation invocation) { - return Stream>.fromIterable(>[''' + fakeProcessManager + ..addCommand(const FakeCommand( + command: ['tail', '-n', '0', '-F', 'system.log'], + stdout: ''' 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/ 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) )))))))))) 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) #0 Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)''' - .codeUnits]); - }); - when(mockProcess.stderr) - .thenAnswer((Invocation invocation) => const Stream>.empty()); - // Delay return of exitCode until after stdout stream data, since it terminates the logger. - when(mockProcess.exitCode) - .thenAnswer((Invocation invocation) => Future.delayed(Duration.zero, () => 0)); - return Future.value(mockProcess); - }); + )) + ..addCommand(const FakeCommand( + command: ['tail', '-n', '0', '-F', '/private/var/log/system.log'] + )); final IOSSimulator device = IOSSimulator( '123456', @@ -571,20 +595,18 @@ void main() { '))))))))))', '#0 Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)', ]); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); }, overrides: { - ProcessManager: () => mockProcessManager, + ProcessManager: () => fakeProcessManager, FileSystem: () => fileSystem, Platform: () => osx, }); - testUsingContext('log reader handles multiline messages', () async { - when(mockProcessManager.start(any, environment: null, workingDirectory: null)) - .thenAnswer((Invocation invocation) { - final Process mockProcess = MockProcess(); - when(mockProcess.stdout) - .thenAnswer((Invocation invocation) { - // Messages from 2017 should pass through, 2020 message should not - return Stream>.fromIterable(>[''' + testUsingContext('multiline messages', () async { + fakeProcessManager + ..addCommand(const FakeCommand( + command: ['tail', '-n', '0', '-F', 'system.log'], + stdout: ''' 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Single line message 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Multi line message continues... @@ -599,15 +621,10 @@ void main() { and goes... 2017-09-13 15:36:57.228948-0700 localhost Runner[37195]: (Flutter) Single line message, not the part of the above ''' - .codeUnits]); - }); - when(mockProcess.stderr) - .thenAnswer((Invocation invocation) => const Stream>.empty()); - // Delay return of exitCode until after stdout stream data, since it terminates the logger. - when(mockProcess.exitCode) - .thenAnswer((Invocation invocation) => Future.delayed(Duration.zero, () => 0)); - return Future.value(mockProcess); - }); + )) + ..addCommand(const FakeCommand( + command: ['tail', '-n', '0', '-F', '/private/var/log/system.log'] + )); final IOSSimulator device = IOSSimulator( '123456', @@ -630,8 +647,9 @@ void main() { ' and goes...', 'Single line message, not the part of the above' ]); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); }, overrides: { - ProcessManager: () => mockProcessManager, + ProcessManager: () => fakeProcessManager, FileSystem: () => fileSystem, Platform: () => osx, }); @@ -639,12 +657,25 @@ void main() { group('unified logging', () { testUsingContext('log reader handles escaped multiline messages', () async { - when(mockProcessManager.start(any, environment: null, workingDirectory: null)) - .thenAnswer((Invocation invocation) { - final Process mockProcess = MockProcess(); - when(mockProcess.stdout) - .thenAnswer((Invocation invocation) { - return Stream>.fromIterable(>[''' + const String logPredicate = 'eventType = logEvent AND processImagePath ENDSWITH "Runner" ' + 'AND (senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" ' + 'OR processImageUUID == senderImageUUID) AND NOT(eventMessage CONTAINS ": could not find icon ' + 'for representation -> com.apple.") AND NOT(eventMessage BEGINSWITH "assertion failed: ") ' + 'AND NOT(eventMessage CONTAINS " libxpc.dylib ")'; + fakeProcessManager.addCommand(const FakeCommand( + command: [ + '/usr/bin/xcrun', + 'simctl', + 'spawn', + '123456', + 'log', + 'stream', + '--style', + 'json', + '--predicate', + logPredicate, + ], + stdout: ''' },{ "traceID" : 37579774151491588, "eventMessage" : "Single line message", @@ -658,15 +689,7 @@ void main() { "eventType" : "logEvent" },{ ''' - .codeUnits]); - }); - when(mockProcess.stderr) - .thenAnswer((Invocation invocation) => const Stream>.empty()); - // Delay return of exitCode until after stdout stream data, since it terminates the logger. - when(mockProcess.exitCode) - .thenAnswer((Invocation invocation) => Future.delayed(Duration.zero, () => 0)); - return Future.value(mockProcess); - }); + )); final IOSSimulator device = IOSSimulator( '123456', @@ -683,8 +706,9 @@ void main() { 'Single line message', 'Multi line message\n continues...\n continues...', 'Single line message, not the part of the above' ]); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); }, overrides: { - ProcessManager: () => mockProcessManager, + ProcessManager: () => fakeProcessManager, FileSystem: () => fileSystem, }); });