From 121c793dc003222ecfe2c6149e30299076d172ec Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Tue, 11 Jul 2023 17:35:57 -0400 Subject: [PATCH] feat(shorebird_cli): add `logcat` to `Adb` (#836) --- packages/shorebird_cli/lib/src/adb.dart | 18 ++++++ packages/shorebird_cli/test/src/adb_test.dart | 64 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/packages/shorebird_cli/lib/src/adb.dart b/packages/shorebird_cli/lib/src/adb.dart index 546686ef..81c7de20 100644 --- a/packages/shorebird_cli/lib/src/adb.dart +++ b/packages/shorebird_cli/lib/src/adb.dart @@ -1,3 +1,6 @@ +import 'dart:async'; +import 'dart:io'; + import 'package:shorebird_cli/src/android_sdk.dart'; import 'package:shorebird_cli/src/process.dart'; @@ -10,6 +13,13 @@ class Adb { return process.run(adbPath, command.split(' ')); } + Future _stream(String command) async { + final adbPath = androidSdk.adbPath; + if (adbPath == null) throw Exception('Unable to locate adb.'); + + return process.start(adbPath, command.split(' ')); + } + /// Starts the app with the given [package] name. Future startApp(String package) async { final result = await _exec('shell monkey -p $package 1'); @@ -17,4 +27,12 @@ class Adb { throw Exception('Unable to start app: ${result.stderr}'); } } + + Future logcat({String? filter}) async { + final logcat = await _stream('logcat'); + if (filter == null) return logcat; + final grep = await process.start('grep', [filter]); + unawaited(logcat.stdout.pipe(grep.stdin)); + return grep; + } } diff --git a/packages/shorebird_cli/test/src/adb_test.dart b/packages/shorebird_cli/test/src/adb_test.dart index e85da656..62cc71e0 100644 --- a/packages/shorebird_cli/test/src/adb_test.dart +++ b/packages/shorebird_cli/test/src/adb_test.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:mocktail/mocktail.dart'; import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/adb.dart'; @@ -9,6 +11,10 @@ class _MockAndroidSdk extends Mock implements AndroidSdk {} class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} +class _MockProcess extends Mock implements Process {} + +class _MockIOSink extends Mock implements IOSink {} + void main() { group(Adb, () { const adbPath = '/path/to/adb'; @@ -27,6 +33,10 @@ void main() { ); } + setUpAll(() { + registerFallbackValue(const Stream>.empty()); + }); + setUp(() { androidSdk = _MockAndroidSdk(); process = _MockShorebirdProcess(); @@ -92,5 +102,59 @@ void main() { ).called(1); }); }); + + group('logcat', () { + test('throws when unable to locate adb', () async { + when(() => androidSdk.adbPath).thenReturn(null); + await expectLater( + () => runWithOverrides(() => adb.logcat()), + throwsA( + isA().having( + (e) => e.toString(), + 'exception()', + contains('Unable to locate adb'), + ), + ), + ); + }); + + test('returns correct process (unfiltered)', () async { + final Process logcatProcess = _MockProcess(); + when(() => process.start(any(), any())).thenAnswer((invocation) async { + final executable = invocation.positionalArguments[0] as String; + if (executable == adbPath) return logcatProcess; + fail('Unexpected executable: $executable'); + }); + final result = await runWithOverrides(() => adb.logcat()); + expect(result, equals(logcatProcess)); + verify(() => process.start(adbPath, ['logcat'])).called(1); + }); + + test('returns correct process (filtered)', () async { + const filter = 'flutter'; + final logcatProcess = _MockProcess(); + final grepProcess = _MockProcess(); + final grepStdin = _MockIOSink(); + const logcatStdout = Stream>.empty(); + + when(grepStdin.close).thenAnswer((_) async {}); + when(() => grepStdin.addStream(any())).thenAnswer((_) async {}); + when(() => grepProcess.stdin).thenReturn(grepStdin); + when( + () => logcatProcess.stdout, + ).thenAnswer((_) => logcatStdout); + when(() => process.start(any(), any())).thenAnswer((invocation) async { + final executable = invocation.positionalArguments[0] as String; + if (executable == adbPath) return logcatProcess; + if (executable == 'grep') return grepProcess; + fail('Unexpected executable: $executable'); + }); + final result = await runWithOverrides(() => adb.logcat(filter: filter)); + expect(result, equals(grepProcess)); + verify(() => process.start(adbPath, ['logcat'])).called(1); + verify(() => process.start('grep', [filter])).called(1); + verify(() => grepStdin.addStream(logcatStdout)).called(1); + }); + }); }); }