From 489fa3b19310bd11b039848b69346073e4dda5d7 Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 23 Oct 2024 14:58:08 +0200 Subject: [PATCH 01/47] Add patrol_log package and use it --- .../integration_test/example_test.dart | 2 + .../Flutter/GeneratedPluginRegistrant.swift | 2 + dev/e2e_app/pubspec.lock | 17 +- .../pl/leancode/patrol/PatrolJUnitRunner.java | 1 + packages/patrol/lib/src/common.dart | 6 + .../patrol_integration_tester.dart | 213 +++++++++++++++++- .../lib/src/native/native_automator.dart | 54 ++++- packages/patrol/pubspec.yaml | 2 + .../lib/src/android/android_test_backend.dart | 33 +++ .../lib/src/ios/ios_test_backend.dart | 35 ++- packages/patrol_cli/lib/src/test_bundler.dart | 7 +- packages/patrol_cli/pubspec.yaml | 2 + packages/patrol_log/lib/src/entry.dart | 24 ++ packages/patrol_log/lib/src/log_entry.dart | 34 +++ packages/patrol_log/lib/src/step_entry.dart | 52 +++++ packages/patrol_log/lib/src/test_entry.dart | 64 ++++++ 16 files changed, 534 insertions(+), 14 deletions(-) create mode 100644 packages/patrol_log/lib/src/entry.dart create mode 100644 packages/patrol_log/lib/src/log_entry.dart create mode 100644 packages/patrol_log/lib/src/step_entry.dart create mode 100644 packages/patrol_log/lib/src/test_entry.dart diff --git a/dev/e2e_app/integration_test/example_test.dart b/dev/e2e_app/integration_test/example_test.dart index 096142ad0..c4bfdbe0d 100644 --- a/dev/e2e_app/integration_test/example_test.dart +++ b/dev/e2e_app/integration_test/example_test.dart @@ -19,6 +19,7 @@ void main() { await $(#textField).enterText('Hello, Flutter!'); expect($('Hello, Flutter!'), findsOneWidget); + $.log('Tapped the button'); await $.native.pressHome(); await $.native.openApp(); @@ -32,6 +33,7 @@ void main() { patrol( 'short test with two tags', + skip: true, tags: ['smoke', 'fume'], ($) async { await createApp($); diff --git a/dev/e2e_app/macos/Flutter/GeneratedPluginRegistrant.swift b/dev/e2e_app/macos/Flutter/GeneratedPluginRegistrant.swift index 571b1e023..9b746f0de 100644 --- a/dev/e2e_app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/dev/e2e_app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import flutter_local_notifications import flutter_timezone import geolocator_apple import patrol +import webview_flutter_wkwebview func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) @@ -17,4 +18,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) PatrolPlugin.register(with: registry.registrar(forPlugin: "PatrolPlugin")) + FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "FLTWebViewFlutterPlugin")) } diff --git a/dev/e2e_app/pubspec.lock b/dev/e2e_app/pubspec.lock index f585db467..25e2e1116 100644 --- a/dev/e2e_app/pubspec.lock +++ b/dev/e2e_app/pubspec.lock @@ -198,6 +198,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.10" + dispose_scope: + dependency: transitive + description: + name: dispose_scope + sha256: "48ec38ca2631c53c4f8fa96b294c801e55c335db5e3fb9f82cede150cfe5a2af" + url: "https://pub.dev" + source: hosted + version: "2.1.0" equatable: dependency: transitive description: @@ -491,7 +499,7 @@ packages: path: "../../packages/patrol" relative: true source: path - version: "3.11.1" + version: "3.11.2" patrol_finders: dependency: transitive description: @@ -500,6 +508,13 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + patrol_log: + dependency: transitive + description: + path: "../../packages/patrol_log" + relative: true + source: path + version: "0.0.1" permission_handler: dependency: "direct main" description: diff --git a/packages/patrol/android/src/main/kotlin/pl/leancode/patrol/PatrolJUnitRunner.java b/packages/patrol/android/src/main/kotlin/pl/leancode/patrol/PatrolJUnitRunner.java index 1e5b35648..47e79b3f8 100644 --- a/packages/patrol/android/src/main/kotlin/pl/leancode/patrol/PatrolJUnitRunner.java +++ b/packages/patrol/android/src/main/kotlin/pl/leancode/patrol/PatrolJUnitRunner.java @@ -149,6 +149,7 @@ public RunDartTestResponse runDartTest(String name) { if (response.getResult() == Contracts.RunDartTestResponseResult.failure) { throw new AssertionError("Dart test failed: " + name + "\n" + response.getDetails()); } + Logger.INSTANCE.i(TAG + "Test execution succeeded"); return response; } catch (PatrolAppServiceClientException e) { Logger.INSTANCE.e(TAG + e.getMessage(), e.getCause()); diff --git a/packages/patrol/lib/src/common.dart b/packages/patrol/lib/src/common.dart index a3f54a88f..e152e0a31 100644 --- a/packages/patrol/lib/src/common.dart +++ b/packages/patrol/lib/src/common.dart @@ -10,6 +10,7 @@ import 'package:patrol/src/global_state.dart' as global_state; import 'package:patrol/src/native/contracts/contracts.dart'; import 'package:patrol/src/native/native.dart'; import 'package:patrol_finders/patrol_finders.dart' as finders; +import 'package:patrol_log/patrol_log.dart'; /// We need [Group] to recreate test hierarchy. // ignore: implementation_imports @@ -94,11 +95,15 @@ void patrolTest( LiveTestWidgetsFlutterBindingFramePolicy framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fadePointers, }) { + final patrolLog = PatrolLogWriter(); final automator = NativeAutomator(config: nativeAutomatorConfig); final automator2 = NativeAutomator2(config: nativeAutomatorConfig); final patrolBinding = PatrolBinding.ensureInitialized(nativeAutomatorConfig) ..framePolicy = framePolicy; + if (skip ?? false) { + patrolLog.log(TestEntry(name: description, status: TestEntryStatus.skip)); + } testWidgets( description, skip: skip, @@ -136,6 +141,7 @@ void patrolTest( nativeAutomator: automator, nativeAutomator2: automator2, config: config, + patrolLog: patrolLog, ); await callback(patrolTester); diff --git a/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart b/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart index eacdd4ca7..004fb90fe 100644 --- a/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart +++ b/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart @@ -1,6 +1,9 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; import 'package:patrol/src/native/native_automator.dart'; import 'package:patrol/src/native/native_automator2.dart'; import 'package:patrol_finders/patrol_finders.dart' as finders; +import 'package:patrol_log/patrol_log.dart'; /// PatrolIntegrationTester extends the capabilities of [finders.PatrolTester] /// with the ability to interact with native platform features via [native]. @@ -11,7 +14,11 @@ class PatrolIntegrationTester extends finders.PatrolTester { required super.config, required this.nativeAutomator, required this.nativeAutomator2, - }); + required PatrolLogWriter patrolLog, + }) : _patrolLog = patrolLog; + + /// The log for the patrol. + final PatrolLogWriter _patrolLog; /// Native automator that allows for interaction with OS the app is running /// on. @@ -28,4 +35,208 @@ class PatrolIntegrationTester extends finders.PatrolTester { /// Shorthand for [nativeAutomator2]. NativeAutomator2 get native2 => nativeAutomator2; + + /// Logs a message to the patrol log. + void log(String message) { + _patrolLog.log(LogEntry(message: message)); + } + + /// Wraps a function with a log entry for the start and end of the function. + Future wrapWithPatrolLog({ + required String action, + String? value, + Finder? finder, + required String color, + required Function function, + }) async { + final finderText = finder + ?.toString(describeSelf: true) + .replaceAll('A finder that searches for', '') ?? + ''; + final valueText = value != null ? ' "$value"' : ''; + final text = '\u001b[$color$action\u001b[0m$valueText$finderText'; + _patrolLog.log(StepEntry(action: text, status: StepEntryStatus.start)); + try { + // ignore: avoid_dynamic_calls + final result = await function.call(); + _patrolLog.log(StepEntry(action: text, status: StepEntryStatus.success)); + return result as T; + } catch (err) { + _patrolLog.log( + StepEntry( + action: text, + status: StepEntryStatus.failure, + exception: err.toString(), + ), + ); + rethrow; + } + } + + @override + Future dragUntilExists({ + required Finder finder, + required Finder view, + required Offset moveStep, + int maxIteration = finders.defaultScrollMaxIteration, + Duration? settleBetweenScrollsTimeout, + Duration? dragDuration, + finders.SettlePolicy? settlePolicy, + }) { + return wrapWithPatrolLog( + action: 'dragUntilExists', + finder: view, + color: '34m', + function: () => + super.dragUntilExists(finder: finder, view: view, moveStep: moveStep), + ); + } + + @override + Future dragUntilVisible({ + required Finder finder, + required Finder view, + required Offset moveStep, + int maxIteration = finders.defaultScrollMaxIteration, + Duration? settleBetweenScrollsTimeout, + Duration? dragDuration, + finders.SettlePolicy? settlePolicy, + }) { + return wrapWithPatrolLog( + action: 'dragUntilVisible', + finder: view, + color: '34m', + function: () => super + .dragUntilVisible(finder: finder, view: view, moveStep: moveStep), + ); + } + + @override + Future enterText( + Finder finder, + String text, { + finders.SettlePolicy? settlePolicy, + Duration? visibleTimeout, + Duration? settleTimeout, + }) { + return wrapWithPatrolLog( + action: 'enterText', + value: text, + finder: finder, + color: '35m', + function: () => super.enterText(finder, text), + ); + } + + @override + Future longPress( + Finder finder, { + finders.SettlePolicy? settlePolicy, + Duration? visibleTimeout, + Duration? settleTimeout, + }) { + return wrapWithPatrolLog( + action: 'longPress', + finder: finder, + color: '33m', + function: () => super.longPress(finder), + ); + } + + @override + Future scrollUntilExists({ + required Finder finder, + Finder? view, + double delta = finders.defaultScrollDelta, + AxisDirection? scrollDirection, + int maxScrolls = finders.defaultScrollMaxIteration, + Duration? settleBetweenScrollsTimeout, + Duration? dragDuration, + finders.SettlePolicy? settlePolicy, + }) { + return wrapWithPatrolLog( + action: 'scrollUntilExists', + finder: view, + color: '32m', + function: () => super.scrollUntilExists( + finder: finder, + view: view, + delta: delta, + scrollDirection: scrollDirection, + maxScrolls: maxScrolls, + settleBetweenScrollsTimeout: settleBetweenScrollsTimeout, + dragDuration: dragDuration, + settlePolicy: settlePolicy, + ), + ); + } + + @override + Future scrollUntilVisible({ + required Finder finder, + Finder? view, + double delta = finders.defaultScrollDelta, + AxisDirection? scrollDirection, + int maxScrolls = finders.defaultScrollMaxIteration, + Duration? settleBetweenScrollsTimeout, + Duration? dragDuration, + finders.SettlePolicy? settlePolicy, + }) { + return wrapWithPatrolLog( + action: 'scrollUntilVisible', + finder: view, + color: '32m', + function: () => super.scrollUntilVisible( + finder: finder, + view: view, + delta: delta, + scrollDirection: scrollDirection, + maxScrolls: maxScrolls, + settleBetweenScrollsTimeout: settleBetweenScrollsTimeout, + dragDuration: dragDuration, + settlePolicy: settlePolicy, + ), + ); + } + + @override + Future tap( + Finder finder, { + finders.SettlePolicy? settlePolicy, + Duration? visibleTimeout, + Duration? settleTimeout, + }) { + return wrapWithPatrolLog( + action: 'tap', + finder: finder, + color: '33m', + function: () => super.tap(finder), + ); + } + + @override + Future waitUntilExists( + finders.PatrolFinder finder, { + Duration? timeout, + }) { + return wrapWithPatrolLog( + action: 'waitUntilExists', + finder: finder, + color: '36m', + function: () => super.waitUntilExists(finder), + ); + } + + @override + Future waitUntilVisible( + Finder finder, { + Duration? timeout, + }) { + return wrapWithPatrolLog( + action: 'waitUntilVisible', + finder: finder, + color: '36m', + function: () => super.waitUntilVisible(finder), + ); + } } diff --git a/packages/patrol/lib/src/native/native_automator.dart b/packages/patrol/lib/src/native/native_automator.dart index e390a09aa..183b28432 100644 --- a/packages/patrol/lib/src/native/native_automator.dart +++ b/packages/patrol/lib/src/native/native_automator.dart @@ -6,6 +6,7 @@ import 'package:meta/meta.dart'; import 'package:patrol/src/native/contracts/contracts.dart' as contracts; import 'package:patrol/src/native/contracts/contracts.dart'; import 'package:patrol/src/native/contracts/native_automator_client.dart'; +import 'package:patrol_log/patrol_log.dart'; /// Thrown when a native action fails. class PatrolActionException implements Exception { @@ -173,12 +174,15 @@ class NativeAutomatorConfig { // TODO: Rename to NativeAutomatorClient class NativeAutomator { /// Creates a new [NativeAutomator]. - NativeAutomator({required NativeAutomatorConfig config}) - : assert( + NativeAutomator({ + required NativeAutomatorConfig config, + required PatrolLogWriter patrolLog, + }) : assert( config.connectionTimeout > config.findTimeout, 'find timeout is longer than connection timeout', ), - _config = config { + _config = config, + _patrolLog = patrolLog { if (_config.packageName.isEmpty && io.Platform.isAndroid) { _config.logger("packageName is not set. It's recommended to set it."); } @@ -199,6 +203,7 @@ class NativeAutomator { _config.logger('NativeAutomatorClient created, port: ${_config.port}'); } + final PatrolLogWriter _patrolLog; final NativeAutomatorConfig _config; late final NativeAutomatorClient _client; @@ -214,19 +219,52 @@ class NativeAutomator { throw StateError('unsupported platform'); } - Future _wrapRequest(String name, Future Function() request) async { + Future _wrapRequest( + String name, + Future Function() request, { + bool enablePatrolLog = true, + }) async { _config.logger('$name() started'); + final text = '\u001b[38;5;87m$name\u001b[0m \u001b[30m(native)\u001b[0m'; + + if (enablePatrolLog) { + _patrolLog.log(StepEntry(action: text, status: StepEntryStatus.start)); + } try { final result = await request(); _config.logger('$name() succeeded'); + if (enablePatrolLog) { + _patrolLog + .log(StepEntry(action: text, status: StepEntryStatus.success)); + } return result; } on NativeAutomatorClientException catch (err) { _config.logger('$name() failed'); final log = 'NativeAutomatorClientException: ' '$name() failed with $err'; + + if (enablePatrolLog) { + _patrolLog.log( + StepEntry( + action: text, + status: StepEntryStatus.failure, + exception: log, + ), + ); + } throw PatrolActionException(log); } catch (err) { _config.logger('$name() failed'); + + if (enablePatrolLog) { + _patrolLog.log( + StepEntry( + action: text, + status: StepEntryStatus.failure, + exception: err.toString(), + ), + ); + } rethrow; } } @@ -241,7 +279,11 @@ class NativeAutomator { /// See also: /// * https://github.com/flutter/flutter/issues/129231 Future initialize() async { - await _wrapRequest('initialize', _client.initialize); + await _wrapRequest( + 'initialize', + _client.initialize, + enablePatrolLog: false, + ); } /// Configures the native automator. @@ -260,6 +302,7 @@ class NativeAutomator { findTimeoutMillis: _config.findTimeout.inMilliseconds, ), ), + enablePatrolLog: false, ); exception = null; break; @@ -940,6 +983,7 @@ class NativeAutomator { await _wrapRequest( 'markPatrolAppServiceReady', _client.markPatrolAppServiceReady, + enablePatrolLog: false, ); } } diff --git a/packages/patrol/pubspec.yaml b/packages/patrol/pubspec.yaml index a36c2d895..d3e27c650 100644 --- a/packages/patrol/pubspec.yaml +++ b/packages/patrol/pubspec.yaml @@ -27,6 +27,8 @@ dependencies: json_annotation: ^4.8.1 meta: ^1.10.0 patrol_finders: ^2.1.2 + patrol_log: + path: ../patrol_log shelf: ^1.4.1 test_api: '^0.7.0' diff --git a/packages/patrol_cli/lib/src/android/android_test_backend.dart b/packages/patrol_cli/lib/src/android/android_test_backend.dart index 6f63715d3..917e206ae 100644 --- a/packages/patrol_cli/lib/src/android/android_test_backend.dart +++ b/packages/patrol_cli/lib/src/android/android_test_backend.dart @@ -11,6 +11,7 @@ import 'package:patrol_cli/src/base/process.dart'; import 'package:patrol_cli/src/crossplatform/app_options.dart'; import 'package:patrol_cli/src/devices.dart'; import 'package:patrol_cli/src/runner/flutter_command.dart'; +import 'package:patrol_log/patrol_log.dart'; import 'package:platform/platform.dart'; import 'package:process/process.dart'; @@ -94,6 +95,7 @@ class AndroidTestBackend { ..disposedBy(scope); process.listenStdOut((l) => _logger.detail('\t: $l')).disposedBy(scope); process.listenStdErr((l) => _logger.err('\t$l')).disposedBy(scope); + exitCode = await process.exitCode; if (exitCode == 0) { task.complete('Completed building $subject'); @@ -189,6 +191,32 @@ class AndroidTestBackend { bool interruptible = false, }) async { await _disposeScope.run((scope) async { + // Read patrol logs from logcat + final processLogcat = await _processManager.start( + [ + 'adb', + 'shell', + 'logcat', + '-T', + '1', + 'PatrolServer:I Patrol:I flutter:I *:S', + ], + runInShell: true, + ) + ..disposedBy(scope); + + final reportPath = + 'file://${_fs.currentDirectory.path}/build/app/reports/androidTests/connected/index.html'; + + final patrolLogReader = PatrolLogReader( + listenStdOut: processLogcat.listenStdOut, + scope: scope, + log: _logger.info, + reportPath: reportPath, + ) + ..listen() + ..startTimer(); + final subject = '${options.description} on ${device.description}'; final task = _logger.task('Executing tests of $subject'); @@ -218,6 +246,11 @@ class AndroidTestBackend { }).disposedBy(scope); final exitCode = await process.exitCode; + patrolLogReader.stopTimer(); + processLogcat.kill(); + + _logger.info(patrolLogReader.summary); + if (exitCode == 0) { task.complete('Completed executing $subject'); } else if (exitCode != 0 && interruptible) { diff --git a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart index b95294dea..c35219c04 100644 --- a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart +++ b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart @@ -11,6 +11,7 @@ import 'package:patrol_cli/src/base/logger.dart'; import 'package:patrol_cli/src/base/process.dart'; import 'package:patrol_cli/src/crossplatform/app_options.dart'; import 'package:patrol_cli/src/devices.dart'; +import 'package:patrol_log/patrol_log.dart'; import 'package:platform/platform.dart'; import 'package:process/process.dart'; @@ -103,7 +104,7 @@ class IOSTestBackend { process.kill(); flutterBuildKilled = true; // `flutter build` has exit code 0 on SIGINT }); - process.listenStdOut((l) => _logger.detail('\t$l')).disposedBy(scope); + process.listenStdOut((l) => _logger.info('\t$l')).disposedBy(scope); process.listenStdErr((l) => _logger.err('\t$l')).disposedBy(scope); var exitCode = await process.exitCode; final flutterCommand = options.flutter.command; @@ -154,13 +155,32 @@ class IOSTestBackend { bool interruptible = false, }) async { await _disposeScope.run((scope) async { - final subject = '${options.description} on ${device.description}'; - final task = _logger.task('Running $subject'); + // Read patrol logs from logcat + final processLogs = await _processManager.start( + [ + 'log', + 'stream', + ], + runInShell: true, + ) + ..disposedBy(scope); - final resultsPath = resultBundlePath( + final reportPath = resultBundlePath( timestamp: DateTime.now().millisecondsSinceEpoch, ); + final patrolLogReader = PatrolLogReader( + listenStdOut: processLogs.listenStdOut, + scope: scope, + log: _logger.info, + reportPath: reportPath, + ) + ..listen() + ..startTimer(); + + final subject = '${options.description} on ${device.description}'; + final task = _logger.task('Running $subject'); + final sdkVersion = await getSdkVersion(real: device.real); final process = await _processManager.start( options.testWithoutBuildingInvocation( @@ -170,7 +190,7 @@ class IOSTestBackend { scheme: options.scheme, sdkVersion: sdkVersion, ), - resultBundlePath: resultsPath, + resultBundlePath: reportPath, ), runInShell: true, environment: { @@ -185,10 +205,13 @@ class IOSTestBackend { process.listenStdErr((l) => _logger.err('\t$l')).disposedBy(scope); final exitCode = await process.exitCode; + patrolLogReader.stopTimer(); + processLogs.kill(); + + _logger.info(patrolLogReader.summary); if (exitCode == 0) { task.complete('Completed executing $subject'); - _logger.info('See the native Xcode report at $resultsPath'); } else if (exitCode != 0 && interruptible) { task.complete('App shut down on request'); } else if (exitCode == _xcodebuildInterrupted) { diff --git a/packages/patrol_cli/lib/src/test_bundler.dart b/packages/patrol_cli/lib/src/test_bundler.dart index 7fde70fef..0ce550cca 100644 --- a/packages/patrol_cli/lib/src/test_bundler.dart +++ b/packages/patrol_cli/lib/src/test_bundler.dart @@ -34,6 +34,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:patrol/patrol.dart'; import 'package:patrol/src/native/contracts/contracts.dart'; import 'package:test_api/src/backend/invoker.dart'; +import 'package:patrol_log/patrol_log.dart'; // START: GENERATED TEST IMPORTS ${generateImports(testFilePaths)} @@ -72,7 +73,11 @@ Future main() async { // Dart test (out of which they had been created) and wait for it to complete. // The result of running the Dart test is the result of the native test case. - final nativeAutomator = NativeAutomator(config: NativeAutomatorConfig()); + final patrolLog = PatrolLogWriter(); + final nativeAutomator = NativeAutomator( + config: NativeAutomatorConfig(), + patrolLog: patrolLog, + ); await nativeAutomator.initialize(); final binding = PatrolBinding.ensureInitialized(NativeAutomatorConfig()); final testExplorationCompleter = Completer(); diff --git a/packages/patrol_cli/pubspec.yaml b/packages/patrol_cli/pubspec.yaml index a2341aa77..e467ab3f4 100644 --- a/packages/patrol_cli/pubspec.yaml +++ b/packages/patrol_cli/pubspec.yaml @@ -30,6 +30,8 @@ dependencies: mason_logger: ^0.2.10 meta: ^1.10.0 path: ^1.8.3 + patrol_log: + path: ../patrol_log platform: ^3.1.3 process: ^5.0.1 pub_updater: ^0.4.0 diff --git a/packages/patrol_log/lib/src/entry.dart b/packages/patrol_log/lib/src/entry.dart new file mode 100644 index 000000000..dad4bdcca --- /dev/null +++ b/packages/patrol_log/lib/src/entry.dart @@ -0,0 +1,24 @@ +abstract class Entry { + Entry({required this.timestamp, required this.type}); + + // ignore: avoid_unused_constructor_parameters + factory Entry.fromJson(Map json) { + throw UnimplementedError('fromJson is not implemented'); + } + + final DateTime timestamp; + final EntryType type; + + Map toJson(); + + String pretty(); + + @override + String toString() => throw UnimplementedError('toString is not implemented'); +} + +enum EntryType { + step, + test, + log; +} diff --git a/packages/patrol_log/lib/src/log_entry.dart b/packages/patrol_log/lib/src/log_entry.dart new file mode 100644 index 000000000..b5bc8332b --- /dev/null +++ b/packages/patrol_log/lib/src/log_entry.dart @@ -0,0 +1,34 @@ +import 'package:patrol_log/src/entry.dart'; + +class LogEntry extends Entry { + LogEntry({ + required this.message, + DateTime? timestamp, + }) : super( + timestamp: timestamp ?? DateTime.now(), + type: EntryType.log, + ); + + @override + factory LogEntry.fromJson(Map json) => LogEntry( + timestamp: DateTime.parse(json['timestamp'] as String), + message: json['message'] as String, + ); + + final String message; + + @override + Map toJson() => { + 'message': message, + 'timestamp': timestamp.toIso8601String(), + 'type': type.index, + }; + + @override + String pretty() { + return 'Log: $message'; + } + + @override + String toString() => 'LogEntry(${toJson()})'; +} diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart new file mode 100644 index 000000000..471463f51 --- /dev/null +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -0,0 +1,52 @@ +import 'package:patrol_log/src/entry.dart'; + +class StepEntry extends Entry { + StepEntry({ + required this.action, + required this.status, + this.exception, + this.data, + DateTime? timestamp, + }) : super( + timestamp: timestamp ?? DateTime.now(), + type: EntryType.step, + ); + + @override + factory StepEntry.fromJson(Map json) => StepEntry( + action: json['action'] as String, + timestamp: DateTime.parse(json['timestamp'] as String), + status: StepEntryStatus.values[json['status'] as int], + exception: json['exception'] as String?, + data: json['data'] as Map?, + ); + + final String action; + final StepEntryStatus status; + final String? exception; + final Map? data; + + @override + Map toJson() => { + 'action': action, + 'status': status.index, + 'type': type.index, + 'timestamp': timestamp.toIso8601String(), + 'exception': exception, + 'data': data, + }; + + @override + String pretty() { + return ' Step ${status.name}: $action'; + } + + @override + String toString() => 'StepEntry(${toJson()})'; +} + +enum StepEntryStatus { + start, + success, + failure; +} diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart new file mode 100644 index 000000000..ed2282c47 --- /dev/null +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -0,0 +1,64 @@ +import 'package:patrol_log/src/entry.dart'; + +class TestEntry extends Entry { + TestEntry({ + required this.name, + required this.status, + DateTime? timestamp, + this.error, + }) : super( + timestamp: timestamp ?? DateTime.now(), + type: EntryType.test, + ); + + @override + factory TestEntry.fromJson(Map json) => TestEntry( + timestamp: DateTime.parse(json['timestamp'] as String), + name: json['name'] as String, + status: TestEntryStatus.values[json['status'] as int], + error: json['error'] as String?, + ); + + final String name; + final TestEntryStatus status; + final String? error; + + Duration executionTime(DateTime start) => timestamp.difference(start); + + @override + Map toJson() => { + 'name': name, + 'status': status.index, + 'type': type.index, + 'timestamp': timestamp.toIso8601String(), + 'error': error, + }; + + @override + String pretty() { + return 'Test ${status.name} $name'; + } + + @override + String toString() => 'TestEntry(${toJson()})'; +} + +enum TestEntryStatus { + start, + success, + failure, + skip; + + String get name { + switch (this) { + case TestEntryStatus.start: + return 'started ⏳'; + case TestEntryStatus.success: + return 'succeeded ✅'; + case TestEntryStatus.failure: + return 'failed ❌'; + case TestEntryStatus.skip: + return 'skipped ⏩'; + } + } +} From 4b4daa7058764a1289559c1df7934d9b5424981b Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 23 Oct 2024 17:55:37 +0200 Subject: [PATCH 02/47] Wrap native2 with patrol logs --- .../lib/src/native/native_automator2.dart | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/packages/patrol/lib/src/native/native_automator2.dart b/packages/patrol/lib/src/native/native_automator2.dart index 745c1d82f..bd58a6ec5 100644 --- a/packages/patrol/lib/src/native/native_automator2.dart +++ b/packages/patrol/lib/src/native/native_automator2.dart @@ -8,6 +8,7 @@ import 'package:patrol/src/native/contracts/contracts.dart' as contracts; import 'package:patrol/src/native/contracts/native_automator_client.dart'; import 'package:patrol/src/native/native_automator.dart'; import 'package:patrol/src/native/native_automator.dart' as native_automator; +import 'package:patrol_log/patrol_log.dart'; /// This class represents the result of [NativeAutomator.getNativeViews]. class GetNativeViewsResult { @@ -81,6 +82,7 @@ class NativeAutomator2 { _config.logger('NativeAutomatorClient created, port: ${_config.port}'); } + final PatrolLogWriter _patrolLog = PatrolLogWriter(); final NativeAutomatorConfig _config; late final NativeAutomatorClient _client; @@ -96,19 +98,52 @@ class NativeAutomator2 { throw StateError('unsupported platform'); } - Future _wrapRequest(String name, Future Function() request) async { + Future _wrapRequest( + String name, + Future Function() request, { + bool enablePatrolLog = true, + }) async { _config.logger('$name() started'); + final text = '\u001b[38;5;87m$name\u001b[0m \u001b[30m(native)\u001b[0m'; + + if (enablePatrolLog) { + _patrolLog.log(StepEntry(action: text, status: StepEntryStatus.start)); + } try { final result = await request(); _config.logger('$name() succeeded'); + if (enablePatrolLog) { + _patrolLog + .log(StepEntry(action: text, status: StepEntryStatus.success)); + } return result; } on NativeAutomatorClientException catch (err) { _config.logger('$name() failed'); final log = 'NativeAutomatorClientException: ' '$name() failed with $err'; + + if (enablePatrolLog) { + _patrolLog.log( + StepEntry( + action: text, + status: StepEntryStatus.failure, + exception: log, + ), + ); + } throw PatrolActionException(log); } catch (err) { _config.logger('$name() failed'); + + if (enablePatrolLog) { + _patrolLog.log( + StepEntry( + action: text, + status: StepEntryStatus.failure, + exception: err.toString(), + ), + ); + } rethrow; } } @@ -123,7 +158,11 @@ class NativeAutomator2 { /// See also: /// * https://github.com/flutter/flutter/issues/129231 Future initialize() async { - await _wrapRequest('initialize', _client.initialize); + await _wrapRequest( + 'initialize', + _client.initialize, + enablePatrolLog: false, + ); } /// Configures the native automator. @@ -142,6 +181,7 @@ class NativeAutomator2 { findTimeoutMillis: _config.findTimeout.inMilliseconds, ), ), + enablePatrolLog: false, ); exception = null; break; @@ -819,6 +859,7 @@ class NativeAutomator2 { await _wrapRequest( 'markPatrolAppServiceReady', _client.markPatrolAppServiceReady, + enablePatrolLog: false, ); } } From db52ab7270d986e452193cb2a6f5bbe1e8b1d16a Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 23 Oct 2024 17:56:17 +0200 Subject: [PATCH 03/47] Assign PatrolLogWriter value directly in native --- packages/patrol/lib/src/native/native_automator.dart | 11 ++++------- packages/patrol_cli/lib/src/test_bundler.dart | 7 +------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/packages/patrol/lib/src/native/native_automator.dart b/packages/patrol/lib/src/native/native_automator.dart index 183b28432..d5ddd8b6f 100644 --- a/packages/patrol/lib/src/native/native_automator.dart +++ b/packages/patrol/lib/src/native/native_automator.dart @@ -174,15 +174,12 @@ class NativeAutomatorConfig { // TODO: Rename to NativeAutomatorClient class NativeAutomator { /// Creates a new [NativeAutomator]. - NativeAutomator({ - required NativeAutomatorConfig config, - required PatrolLogWriter patrolLog, - }) : assert( + NativeAutomator({required NativeAutomatorConfig config}) + : assert( config.connectionTimeout > config.findTimeout, 'find timeout is longer than connection timeout', ), - _config = config, - _patrolLog = patrolLog { + _config = config { if (_config.packageName.isEmpty && io.Platform.isAndroid) { _config.logger("packageName is not set. It's recommended to set it."); } @@ -203,7 +200,7 @@ class NativeAutomator { _config.logger('NativeAutomatorClient created, port: ${_config.port}'); } - final PatrolLogWriter _patrolLog; + final PatrolLogWriter _patrolLog = PatrolLogWriter(); final NativeAutomatorConfig _config; late final NativeAutomatorClient _client; diff --git a/packages/patrol_cli/lib/src/test_bundler.dart b/packages/patrol_cli/lib/src/test_bundler.dart index 0ce550cca..7fde70fef 100644 --- a/packages/patrol_cli/lib/src/test_bundler.dart +++ b/packages/patrol_cli/lib/src/test_bundler.dart @@ -34,7 +34,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:patrol/patrol.dart'; import 'package:patrol/src/native/contracts/contracts.dart'; import 'package:test_api/src/backend/invoker.dart'; -import 'package:patrol_log/patrol_log.dart'; // START: GENERATED TEST IMPORTS ${generateImports(testFilePaths)} @@ -73,11 +72,7 @@ Future main() async { // Dart test (out of which they had been created) and wait for it to complete. // The result of running the Dart test is the result of the native test case. - final patrolLog = PatrolLogWriter(); - final nativeAutomator = NativeAutomator( - config: NativeAutomatorConfig(), - patrolLog: patrolLog, - ); + final nativeAutomator = NativeAutomator(config: NativeAutomatorConfig()); await nativeAutomator.initialize(); final binding = PatrolBinding.ensureInitialized(NativeAutomatorConfig()); final testExplorationCompleter = Completer(); From 333dace00f6aa9d6938e514e991a5fdb9fe55a91 Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 23 Oct 2024 17:57:07 +0200 Subject: [PATCH 04/47] Log patrol test status in service --- .../lib/src/native/patrol_app_service.dart | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/patrol/lib/src/native/patrol_app_service.dart b/packages/patrol/lib/src/native/patrol_app_service.dart index a3485d32e..630cc9f8a 100644 --- a/packages/patrol/lib/src/native/patrol_app_service.dart +++ b/packages/patrol/lib/src/native/patrol_app_service.dart @@ -7,6 +7,7 @@ import 'dart:io'; import 'package:patrol/src/common.dart'; import 'package:patrol/src/native/contracts/contracts.dart'; import 'package:patrol/src/native/contracts/patrol_app_service_server.dart'; +import 'package:patrol_log/patrol_log.dart'; import 'package:shelf/shelf.dart' as shelf; import 'package:shelf/shelf_io.dart' as shelf_io; @@ -78,6 +79,8 @@ class PatrolAppService extends PatrolAppServiceServer { return _testExecutionCompleted.future; } + final PatrolLogWriter _patrolLog = PatrolLogWriter(); + /// Marks [dartFileName] as completed with the given [passed] status. /// /// If an exception was thrown during the test, [details] should contain the @@ -146,10 +149,29 @@ class PatrolAppService extends PatrolAppServiceServer { assert(_testExecutionCompleted.isCompleted == false); // patrolTest() always calls this method. + _patrolLog.log( + TestEntry( + name: request.name, + status: TestEntryStatus.start, + ), + ); print('PatrolAppService.runDartTest(${request.name}) called'); _testExecutionRequested.complete(request.name); final testExecutionResult = await testExecutionCompleted; + if (!testExecutionResult.passed) { + _patrolLog.log( + TestEntry(name: request.name, status: TestEntryStatus.failure), + ); + } else { + _patrolLog.log( + TestEntry( + name: request.name, + status: TestEntryStatus.success, + ), + ); + } + return RunDartTestResponse( result: testExecutionResult.passed ? RunDartTestResponseResult.success From a43e1b473ceeea50d267a1048f9114c5671b6273 Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 23 Oct 2024 17:57:28 +0200 Subject: [PATCH 05/47] Display test file path in logs --- packages/patrol_log/lib/src/test_entry.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index ed2282c47..7c04e6787 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -36,9 +36,15 @@ class TestEntry extends Entry { @override String pretty() { - return 'Test ${status.name} $name'; + if (status == TestEntryStatus.skip) { + return 'Test ${status.name} $_testName'; + } + return 'Test ${status.name} $_testName \u001b[30m(integration_test/$_filePath.dart)\u001b[0m'; } + String get _filePath => name.split(' ').first; + String get _testName => name.split(' ').skip(1).join(' '); + @override String toString() => 'TestEntry(${toJson()})'; } From 78f2def596f31bf37ae83ca8ef1455d27b3fecc5 Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 23 Oct 2024 18:08:49 +0200 Subject: [PATCH 06/47] Treat process error as detail logs --- packages/patrol_cli/lib/src/android/android_test_backend.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patrol_cli/lib/src/android/android_test_backend.dart b/packages/patrol_cli/lib/src/android/android_test_backend.dart index 917e206ae..1f7875fe9 100644 --- a/packages/patrol_cli/lib/src/android/android_test_backend.dart +++ b/packages/patrol_cli/lib/src/android/android_test_backend.dart @@ -239,7 +239,7 @@ class AndroidTestBackend { const prefix = 'There were failing tests. '; if (l.contains(prefix)) { final msg = l.substring(prefix.length + 2); - _logger.err('\t$msg'); + _logger.detail('\t$msg'); } else { _logger.detail('\t$l'); } From a17561bcbef605c7ceb5d1f209a0637afc768122 Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 23 Oct 2024 18:09:23 +0200 Subject: [PATCH 07/47] Treat ios process error as detail logs --- packages/patrol_cli/lib/src/ios/ios_test_backend.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart index c35219c04..abd6a836b 100644 --- a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart +++ b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart @@ -202,7 +202,7 @@ class IOSTestBackend { ) ..disposedBy(_disposeScope); process.listenStdOut((l) => _logger.detail('\t$l')).disposedBy(scope); - process.listenStdErr((l) => _logger.err('\t$l')).disposedBy(scope); + process.listenStdErr((l) => _logger.detail('\t$l')).disposedBy(scope); final exitCode = await process.exitCode; patrolLogReader.stopTimer(); From 2fb2c7b086764027654589deba60c2609d511e57 Mon Sep 17 00:00:00 2001 From: pdenert Date: Thu, 24 Oct 2024 11:09:45 +0200 Subject: [PATCH 08/47] Add list of failed tests in summary --- packages/patrol_log/lib/src/test_entry.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index 7c04e6787..6570cca87 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -42,6 +42,9 @@ class TestEntry extends Entry { return 'Test ${status.name} $_testName \u001b[30m(integration_test/$_filePath.dart)\u001b[0m'; } + String get nameWithPath => + '$_testName \u001b[30m(integration_test/$_filePath.dart)\u001b[0m'; + String get _filePath => name.split(' ').first; String get _testName => name.split(' ').skip(1).join(' '); From 927e36165da7aea679ffb5a8b43fc2e143734357 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 25 Oct 2024 12:35:36 +0200 Subject: [PATCH 09/47] Change pretty of StepEntry status --- packages/patrol_log/lib/src/step_entry.dart | 23 ++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index 471463f51..cecaf06ad 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:patrol_log/src/entry.dart'; class StepEntry extends Entry { @@ -36,9 +38,17 @@ class StepEntry extends Entry { 'data': data, }; + void clearPreviousLine() { + // Move the cursor up one line and clear the line + stdout.write('\x1B[A\x1B[K'); + } + @override String pretty() { - return ' Step ${status.name}: $action'; + // if (status != StepEntryStatus.start) { + // clearPreviousLine(); + // } + return ' ${status.name}: $action'; } @override @@ -49,4 +59,15 @@ enum StepEntryStatus { start, success, failure; + + String get name { + switch (this) { + case StepEntryStatus.start: + return '⏳'; + case StepEntryStatus.success: + return '✅'; + case StepEntryStatus.failure: + return '❌'; + } + } } From 3b4db45cc5ac5a7e34bb5c2de2623c437eed581c Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 25 Oct 2024 12:35:54 +0200 Subject: [PATCH 10/47] Change pretty of TestEntry --- packages/patrol_log/lib/src/test_entry.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index 6570cca87..b73d68b82 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -37,15 +37,15 @@ class TestEntry extends Entry { @override String pretty() { if (status == TestEntryStatus.skip) { - return 'Test ${status.name} $_testName'; + return '${status.name} $_testName'; } - return 'Test ${status.name} $_testName \u001b[30m(integration_test/$_filePath.dart)\u001b[0m'; + return '${status.name} $_testName \u001b[30m(integration_test/$_filePath.dart)\u001b[0m'; } String get nameWithPath => '$_testName \u001b[30m(integration_test/$_filePath.dart)\u001b[0m'; - String get _filePath => name.split(' ').first; + String get _filePath => name.split(' ').first.replaceAll('.', '/'); String get _testName => name.split(' ').skip(1).join(' '); @override @@ -61,13 +61,13 @@ enum TestEntryStatus { String get name { switch (this) { case TestEntryStatus.start: - return 'started ⏳'; + return '⏳'; case TestEntryStatus.success: - return 'succeeded ✅'; + return '✅'; case TestEntryStatus.failure: - return 'failed ❌'; + return '❌'; case TestEntryStatus.skip: - return 'skipped ⏩'; + return '⏩'; } } } From a300e7593930a02a09a403f929c2bb934f3346a1 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 25 Oct 2024 17:51:35 +0200 Subject: [PATCH 11/47] Log actions in patrol finders --- dev/e2e_app/pubspec.lock | 7 +- .../patrol_integration_tester.dart | 203 +----------------- packages/patrol/pubspec.yaml | 3 +- packages/patrol_log/lib/src/step_entry.dart | 6 +- 4 files changed, 9 insertions(+), 210 deletions(-) diff --git a/dev/e2e_app/pubspec.lock b/dev/e2e_app/pubspec.lock index 25e2e1116..d298e677d 100644 --- a/dev/e2e_app/pubspec.lock +++ b/dev/e2e_app/pubspec.lock @@ -503,10 +503,9 @@ packages: patrol_finders: dependency: transitive description: - name: patrol_finders - sha256: "6bf2c3093fbccd02f80f73fafc1bd021d76410cbab6e329be220b5e3bc58f072" - url: "https://pub.dev" - source: hosted + path: "../../packages/patrol_finders" + relative: true + source: path version: "2.1.2" patrol_log: dependency: transitive diff --git a/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart b/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart index 004fb90fe..d6bf2d332 100644 --- a/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart +++ b/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart @@ -1,5 +1,3 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; import 'package:patrol/src/native/native_automator.dart'; import 'package:patrol/src/native/native_automator2.dart'; import 'package:patrol_finders/patrol_finders.dart' as finders; @@ -9,7 +7,7 @@ import 'package:patrol_log/patrol_log.dart'; /// with the ability to interact with native platform features via [native]. class PatrolIntegrationTester extends finders.PatrolTester { /// Creates a new [PatrolIntegrationTester] which wraps [tester]. - const PatrolIntegrationTester({ + PatrolIntegrationTester({ required super.tester, required super.config, required this.nativeAutomator, @@ -40,203 +38,4 @@ class PatrolIntegrationTester extends finders.PatrolTester { void log(String message) { _patrolLog.log(LogEntry(message: message)); } - - /// Wraps a function with a log entry for the start and end of the function. - Future wrapWithPatrolLog({ - required String action, - String? value, - Finder? finder, - required String color, - required Function function, - }) async { - final finderText = finder - ?.toString(describeSelf: true) - .replaceAll('A finder that searches for', '') ?? - ''; - final valueText = value != null ? ' "$value"' : ''; - final text = '\u001b[$color$action\u001b[0m$valueText$finderText'; - _patrolLog.log(StepEntry(action: text, status: StepEntryStatus.start)); - try { - // ignore: avoid_dynamic_calls - final result = await function.call(); - _patrolLog.log(StepEntry(action: text, status: StepEntryStatus.success)); - return result as T; - } catch (err) { - _patrolLog.log( - StepEntry( - action: text, - status: StepEntryStatus.failure, - exception: err.toString(), - ), - ); - rethrow; - } - } - - @override - Future dragUntilExists({ - required Finder finder, - required Finder view, - required Offset moveStep, - int maxIteration = finders.defaultScrollMaxIteration, - Duration? settleBetweenScrollsTimeout, - Duration? dragDuration, - finders.SettlePolicy? settlePolicy, - }) { - return wrapWithPatrolLog( - action: 'dragUntilExists', - finder: view, - color: '34m', - function: () => - super.dragUntilExists(finder: finder, view: view, moveStep: moveStep), - ); - } - - @override - Future dragUntilVisible({ - required Finder finder, - required Finder view, - required Offset moveStep, - int maxIteration = finders.defaultScrollMaxIteration, - Duration? settleBetweenScrollsTimeout, - Duration? dragDuration, - finders.SettlePolicy? settlePolicy, - }) { - return wrapWithPatrolLog( - action: 'dragUntilVisible', - finder: view, - color: '34m', - function: () => super - .dragUntilVisible(finder: finder, view: view, moveStep: moveStep), - ); - } - - @override - Future enterText( - Finder finder, - String text, { - finders.SettlePolicy? settlePolicy, - Duration? visibleTimeout, - Duration? settleTimeout, - }) { - return wrapWithPatrolLog( - action: 'enterText', - value: text, - finder: finder, - color: '35m', - function: () => super.enterText(finder, text), - ); - } - - @override - Future longPress( - Finder finder, { - finders.SettlePolicy? settlePolicy, - Duration? visibleTimeout, - Duration? settleTimeout, - }) { - return wrapWithPatrolLog( - action: 'longPress', - finder: finder, - color: '33m', - function: () => super.longPress(finder), - ); - } - - @override - Future scrollUntilExists({ - required Finder finder, - Finder? view, - double delta = finders.defaultScrollDelta, - AxisDirection? scrollDirection, - int maxScrolls = finders.defaultScrollMaxIteration, - Duration? settleBetweenScrollsTimeout, - Duration? dragDuration, - finders.SettlePolicy? settlePolicy, - }) { - return wrapWithPatrolLog( - action: 'scrollUntilExists', - finder: view, - color: '32m', - function: () => super.scrollUntilExists( - finder: finder, - view: view, - delta: delta, - scrollDirection: scrollDirection, - maxScrolls: maxScrolls, - settleBetweenScrollsTimeout: settleBetweenScrollsTimeout, - dragDuration: dragDuration, - settlePolicy: settlePolicy, - ), - ); - } - - @override - Future scrollUntilVisible({ - required Finder finder, - Finder? view, - double delta = finders.defaultScrollDelta, - AxisDirection? scrollDirection, - int maxScrolls = finders.defaultScrollMaxIteration, - Duration? settleBetweenScrollsTimeout, - Duration? dragDuration, - finders.SettlePolicy? settlePolicy, - }) { - return wrapWithPatrolLog( - action: 'scrollUntilVisible', - finder: view, - color: '32m', - function: () => super.scrollUntilVisible( - finder: finder, - view: view, - delta: delta, - scrollDirection: scrollDirection, - maxScrolls: maxScrolls, - settleBetweenScrollsTimeout: settleBetweenScrollsTimeout, - dragDuration: dragDuration, - settlePolicy: settlePolicy, - ), - ); - } - - @override - Future tap( - Finder finder, { - finders.SettlePolicy? settlePolicy, - Duration? visibleTimeout, - Duration? settleTimeout, - }) { - return wrapWithPatrolLog( - action: 'tap', - finder: finder, - color: '33m', - function: () => super.tap(finder), - ); - } - - @override - Future waitUntilExists( - finders.PatrolFinder finder, { - Duration? timeout, - }) { - return wrapWithPatrolLog( - action: 'waitUntilExists', - finder: finder, - color: '36m', - function: () => super.waitUntilExists(finder), - ); - } - - @override - Future waitUntilVisible( - Finder finder, { - Duration? timeout, - }) { - return wrapWithPatrolLog( - action: 'waitUntilVisible', - finder: finder, - color: '36m', - function: () => super.waitUntilVisible(finder), - ); - } } diff --git a/packages/patrol/pubspec.yaml b/packages/patrol/pubspec.yaml index d3e27c650..ff79b309e 100644 --- a/packages/patrol/pubspec.yaml +++ b/packages/patrol/pubspec.yaml @@ -26,7 +26,8 @@ dependencies: http: '^1.1.0' json_annotation: ^4.8.1 meta: ^1.10.0 - patrol_finders: ^2.1.2 + patrol_finders: + path: ../patrol_finders patrol_log: path: ../patrol_log shelf: ^1.4.1 diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index cecaf06ad..887267fd3 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -45,9 +45,9 @@ class StepEntry extends Entry { @override String pretty() { - // if (status != StepEntryStatus.start) { - // clearPreviousLine(); - // } + if (status != StepEntryStatus.start) { + clearPreviousLine(); + } return ' ${status.name}: $action'; } From 47113beda3e27592d896a2b1118cb22e7c03c752 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 25 Oct 2024 18:45:20 +0200 Subject: [PATCH 12/47] Change LogEntry pretty --- packages/patrol_log/lib/src/log_entry.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patrol_log/lib/src/log_entry.dart b/packages/patrol_log/lib/src/log_entry.dart index b5bc8332b..a66ad877e 100644 --- a/packages/patrol_log/lib/src/log_entry.dart +++ b/packages/patrol_log/lib/src/log_entry.dart @@ -26,7 +26,7 @@ class LogEntry extends Entry { @override String pretty() { - return 'Log: $message'; + return ' 📝 $message'; } @override From 23d91f001be8fad2e29345120f64148a86c6e948 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 25 Oct 2024 18:45:31 +0200 Subject: [PATCH 13/47] Remove : from StepEntry pretty --- packages/patrol_log/lib/src/step_entry.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index 887267fd3..bbbd3d020 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -48,7 +48,7 @@ class StepEntry extends Entry { if (status != StepEntryStatus.start) { clearPreviousLine(); } - return ' ${status.name}: $action'; + return ' ${status.name} $action'; } @override From 01216ae29326d1f7b6d13d035f4561ffe744ec13 Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 29 Oct 2024 15:17:59 +0100 Subject: [PATCH 14/47] Prepare logs for patrol develop --- .../patrol_cli/lib/src/android/android_test_backend.dart | 5 ++++- packages/patrol_cli/lib/src/crossplatform/flutter_tool.dart | 6 ++++++ packages/patrol_cli/lib/src/ios/ios_test_backend.dart | 5 ++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/patrol_cli/lib/src/android/android_test_backend.dart b/packages/patrol_cli/lib/src/android/android_test_backend.dart index 1f7875fe9..056ade1a7 100644 --- a/packages/patrol_cli/lib/src/android/android_test_backend.dart +++ b/packages/patrol_cli/lib/src/android/android_test_backend.dart @@ -249,7 +249,10 @@ class AndroidTestBackend { patrolLogReader.stopTimer(); processLogcat.kill(); - _logger.info(patrolLogReader.summary); + // Don't print the summary in develop + if (!interruptible) { + _logger.info(patrolLogReader.summary); + } if (exitCode == 0) { task.complete('Completed executing $subject'); diff --git a/packages/patrol_cli/lib/src/crossplatform/flutter_tool.dart b/packages/patrol_cli/lib/src/crossplatform/flutter_tool.dart index 1999059e9..11beccc4d 100644 --- a/packages/patrol_cli/lib/src/crossplatform/flutter_tool.dart +++ b/packages/patrol_cli/lib/src/crossplatform/flutter_tool.dart @@ -222,6 +222,12 @@ class FlutterTool { completer.complete(); } + // Skip the log line that contains "PATROL_LOG" prefix + const patrolLogPrefix = 'PATROL_LOG'; + if (line.contains(patrolLogPrefix)) { + return; + } + // On iOS, "flutter" is not prefixed final flutterPrefix = RegExp('flutter: '); diff --git a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart index abd6a836b..7d39f8930 100644 --- a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart +++ b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart @@ -208,7 +208,10 @@ class IOSTestBackend { patrolLogReader.stopTimer(); processLogs.kill(); - _logger.info(patrolLogReader.summary); + // Don't print the summary in develop + if (!interruptible) { + _logger.info(patrolLogReader.summary); + } if (exitCode == 0) { task.complete('Completed executing $subject'); From 9bc7321c697740fefea851b27b7cf71b585b42c7 Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 29 Oct 2024 15:18:29 +0100 Subject: [PATCH 15/47] Print steps counter --- packages/patrol_log/lib/src/step_entry.dart | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index bbbd3d020..e1e8c93b1 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -44,11 +44,25 @@ class StepEntry extends Entry { } @override - String pretty() { + String pretty({int? number}) { if (status != StepEntryStatus.start) { clearPreviousLine(); } - return ' ${status.name} $action'; + return ' ${status.name} ${printNumber(number)} $action'; + } + + String printNumber(int? number) { + if (number != null) { + if (number < 10) { + return ' $number.'; + } else if (number < 100) { + return ' $number.'; + } else { + return '$number.'; + } + } + + return ''; } @override From e252b95f19ec2a0e9fae21987f12893bd027619f Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 29 Oct 2024 15:18:49 +0100 Subject: [PATCH 16/47] Change test start emoji --- packages/patrol_log/lib/src/test_entry.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index b73d68b82..6eaa9fb96 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -50,6 +50,10 @@ class TestEntry extends Entry { @override String toString() => 'TestEntry(${toJson()})'; + + /// Returns `true` if the test is finished successfully or with a failure. + bool get isFinished => + status == TestEntryStatus.success || status == TestEntryStatus.failure; } enum TestEntryStatus { @@ -61,7 +65,7 @@ enum TestEntryStatus { String get name { switch (this) { case TestEntryStatus.start: - return '⏳'; + return '🧪'; case TestEntryStatus.success: return '✅'; case TestEntryStatus.failure: From 25097493125362e7f3fca7c15f8da4100ed2ec66 Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 30 Oct 2024 13:44:17 +0100 Subject: [PATCH 17/47] Show flutter logs and test steps with flag --- .../lib/src/android/android_test_backend.dart | 4 ++++ .../patrol_cli/lib/src/commands/develop.dart | 24 +++++++++++++++---- .../patrol_cli/lib/src/commands/test.dart | 20 ++++++++++++++-- .../lib/src/ios/ios_test_backend.dart | 4 ++++ .../lib/src/runner/patrol_command.dart | 14 +++++++++++ .../patrol_log/lib/src/patrol_log_reader.dart | 15 +++++++++++- 6 files changed, 74 insertions(+), 7 deletions(-) diff --git a/packages/patrol_cli/lib/src/android/android_test_backend.dart b/packages/patrol_cli/lib/src/android/android_test_backend.dart index 056ade1a7..93c3fe4ab 100644 --- a/packages/patrol_cli/lib/src/android/android_test_backend.dart +++ b/packages/patrol_cli/lib/src/android/android_test_backend.dart @@ -189,6 +189,8 @@ class AndroidTestBackend { AndroidAppOptions options, Device device, { bool interruptible = false, + required bool showFlutterLogs, + required bool showTestSteps, }) async { await _disposeScope.run((scope) async { // Read patrol logs from logcat @@ -213,6 +215,8 @@ class AndroidTestBackend { scope: scope, log: _logger.info, reportPath: reportPath, + showFlutterLogs: showFlutterLogs, + showTestSteps: showTestSteps, ) ..listen() ..startTimer(); diff --git a/packages/patrol_cli/lib/src/commands/develop.dart b/packages/patrol_cli/lib/src/commands/develop.dart index b20ec5690..47970d4dc 100644 --- a/packages/patrol_cli/lib/src/commands/develop.dart +++ b/packages/patrol_cli/lib/src/commands/develop.dart @@ -54,6 +54,8 @@ class DevelopCommand extends PatrolCommand { usesWaitOption(); usesPortOptions(); usesTagsOption(); + usesShowFlutterLogs(); + usesShowTestSteps(); usesUninstallOption(); @@ -244,6 +246,8 @@ class DevelopCommand extends PatrolCommand { uninstall: uninstall, device: device, openDevtools: boolArg('open-devtools'), + showFlutterLogs: boolArg('show-flutter-logs'), + showTestSteps: boolArg('show-test-steps'), ); return 0; // for now, all exit codes are 0 @@ -319,6 +323,8 @@ class DevelopCommand extends PatrolCommand { required bool uninstall, required Device device, required bool openDevtools, + required bool showFlutterLogs, + required bool showTestSteps, }) async { Future Function() action; Future Function()? finalizer; @@ -327,8 +333,13 @@ class DevelopCommand extends PatrolCommand { switch (device.targetPlatform) { case TargetPlatform.android: appId = android.packageName; - action = () => - _androidTestBackend.execute(android, device, interruptible: true); + action = () => _androidTestBackend.execute( + android, + device, + interruptible: true, + showFlutterLogs: showFlutterLogs, + showTestSteps: showTestSteps, + ); final package = android.packageName; if (package != null && uninstall) { finalizer = () => _androidTestBackend.uninstall(package, device); @@ -339,8 +350,13 @@ class DevelopCommand extends PatrolCommand { _macosTestBackend.execute(macos, device, interruptible: true); case TargetPlatform.iOS: appId = iosOpts.bundleId; - action = () async => - _iosTestBackend.execute(iosOpts, device, interruptible: true); + action = () async => _iosTestBackend.execute( + iosOpts, + device, + interruptible: true, + showFlutterLogs: showFlutterLogs, + showTestSteps: showTestSteps, + ); final bundleId = iosOpts.bundleId; if (bundleId != null && uninstall) { finalizer = () => _iosTestBackend.uninstall( diff --git a/packages/patrol_cli/lib/src/commands/test.dart b/packages/patrol_cli/lib/src/commands/test.dart index acde73363..9f3c51cae 100644 --- a/packages/patrol_cli/lib/src/commands/test.dart +++ b/packages/patrol_cli/lib/src/commands/test.dart @@ -56,6 +56,8 @@ class TestCommand extends PatrolCommand { usesTagsOption(); usesExcludeTagsOption(); useCoverageOptions(); + usesShowFlutterLogs(); + usesShowTestSteps(); usesUninstallOption(); @@ -266,6 +268,8 @@ See https://github.com/leancodepl/patrol/issues/1316 to learn more. macosOpts, uninstall: uninstall, device: device, + showFlutterLogs: boolArg('show-flutter-logs'), + showTestSteps: boolArg('show-test-steps'), ); return allPassed ? 0 : 1; @@ -340,13 +344,20 @@ See https://github.com/leancodepl/patrol/issues/1316 to learn more. MacOSAppOptions macos, { required bool uninstall, required Device device, + required bool showFlutterLogs, + required bool showTestSteps, }) async { Future Function() action; Future Function()? finalizer; switch (device.targetPlatform) { case TargetPlatform.android: - action = () => _androidTestBackend.execute(android, device); + action = () => _androidTestBackend.execute( + android, + device, + showFlutterLogs: showFlutterLogs, + showTestSteps: showTestSteps, + ); final package = android.packageName; if (package != null && uninstall) { finalizer = () => _androidTestBackend.uninstall(package, device); @@ -354,7 +365,12 @@ See https://github.com/leancodepl/patrol/issues/1316 to learn more. case TargetPlatform.macOS: action = () async => _macosTestBackend.execute(macos, device); case TargetPlatform.iOS: - action = () async => _iosTestBackend.execute(ios, device); + action = () async => _iosTestBackend.execute( + ios, + device, + showFlutterLogs: showFlutterLogs, + showTestSteps: showTestSteps, + ); final bundleId = ios.bundleId; if (bundleId != null && uninstall) { finalizer = () => _iosTestBackend.uninstall( diff --git a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart index 7d39f8930..758526bf4 100644 --- a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart +++ b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart @@ -153,6 +153,8 @@ class IOSTestBackend { IOSAppOptions options, Device device, { bool interruptible = false, + required bool showFlutterLogs, + required bool showTestSteps, }) async { await _disposeScope.run((scope) async { // Read patrol logs from logcat @@ -174,6 +176,8 @@ class IOSTestBackend { scope: scope, log: _logger.info, reportPath: reportPath, + showFlutterLogs: showFlutterLogs, + showTestSteps: showTestSteps, ) ..listen() ..startTimer(); diff --git a/packages/patrol_cli/lib/src/runner/patrol_command.dart b/packages/patrol_cli/lib/src/runner/patrol_command.dart index c3fcf28ee..181218a90 100644 --- a/packages/patrol_cli/lib/src/runner/patrol_command.dart +++ b/packages/patrol_cli/lib/src/runner/patrol_command.dart @@ -188,6 +188,20 @@ abstract class PatrolCommand extends Command { ); } + void usesShowFlutterLogs() { + argParser.addFlag( + 'show-flutter-logs', + help: 'Show Flutter logs while running the tests.', + ); + } + + void usesShowTestSteps() { + argParser.addFlag( + 'show-test-steps', + help: 'Show test steps while running the tests.', + ); + } + /// Gets the parsed command-line flag named [name] as a `bool`. /// /// If no flag named [name] was added to the `ArgParser`, an [ArgumentError] diff --git a/packages/patrol_log/lib/src/patrol_log_reader.dart b/packages/patrol_log/lib/src/patrol_log_reader.dart index 450eff2eb..7873e897b 100644 --- a/packages/patrol_log/lib/src/patrol_log_reader.dart +++ b/packages/patrol_log/lib/src/patrol_log_reader.dart @@ -36,7 +36,6 @@ class PatrolLogReader { /// List of tests names that were skipped. final List _skippedTests = []; - final StreamController _controller = StreamController.broadcast(); late final StreamSubscription _streamSubscription; @@ -111,6 +110,20 @@ class PatrolLogReader { } else { _controller.add(entry); } + } else if (showFlutterLogs && line.contains('Runner: (Flutter) flutter:')) { + final regExp = RegExp(r'Runner: \(Flutter\) (.*)'); + final match = regExp.firstMatch(line); + if (match != null) { + final matchedText = match.group(1)!; + log(matchedText); + } + } else if (showFlutterLogs && line.contains('I flutter :')) { + final regExp = RegExp('I (.*)'); + final match = regExp.firstMatch(line); + if (match != null) { + final matchedText = match.group(1)!; + log(matchedText); + } } } From 54da5b0c21fe431402cdc63b477a6764e5168d5e Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 30 Oct 2024 14:56:26 +0100 Subject: [PATCH 18/47] Don't clear lines when flutter logs are enabled --- packages/patrol_log/lib/src/step_entry.dart | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index e1e8c93b1..4cff478ef 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -38,16 +38,8 @@ class StepEntry extends Entry { 'data': data, }; - void clearPreviousLine() { - // Move the cursor up one line and clear the line - stdout.write('\x1B[A\x1B[K'); - } - @override String pretty({int? number}) { - if (status != StepEntryStatus.start) { - clearPreviousLine(); - } return ' ${status.name} ${printNumber(number)} $action'; } From 2fbcda5ff4017349a7194fe6ab7309aca9fe88c0 Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 30 Oct 2024 14:59:28 +0100 Subject: [PATCH 19/47] Remove unused import --- packages/patrol_log/lib/src/step_entry.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index 4cff478ef..b80fd87a0 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:patrol_log/src/entry.dart'; class StepEntry extends Entry { From 3b0c3ed968cfd4cf3e6c657d8129920ff70b47aa Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 30 Oct 2024 15:30:41 +0100 Subject: [PATCH 20/47] Create proper report path on android app with flavor --- .../patrol_cli/lib/src/android/android_test_backend.dart | 7 ++++++- packages/patrol_cli/lib/src/commands/develop.dart | 1 + packages/patrol_cli/lib/src/commands/test.dart | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/patrol_cli/lib/src/android/android_test_backend.dart b/packages/patrol_cli/lib/src/android/android_test_backend.dart index 93c3fe4ab..18e132efe 100644 --- a/packages/patrol_cli/lib/src/android/android_test_backend.dart +++ b/packages/patrol_cli/lib/src/android/android_test_backend.dart @@ -188,6 +188,7 @@ class AndroidTestBackend { Future execute( AndroidAppOptions options, Device device, { + String? flavor, bool interruptible = false, required bool showFlutterLogs, required bool showTestSteps, @@ -207,8 +208,12 @@ class AndroidTestBackend { ) ..disposedBy(scope); + var flavorPath = ''; + if (flavor != null) { + flavorPath = 'flavors/$flavor/'; + } final reportPath = - 'file://${_fs.currentDirectory.path}/build/app/reports/androidTests/connected/index.html'; + 'file://${_fs.currentDirectory.path}/build/app/reports/androidTests/connected/${flavorPath}index.html'; final patrolLogReader = PatrolLogReader( listenStdOut: processLogcat.listenStdOut, diff --git a/packages/patrol_cli/lib/src/commands/develop.dart b/packages/patrol_cli/lib/src/commands/develop.dart index 47970d4dc..b2fea350b 100644 --- a/packages/patrol_cli/lib/src/commands/develop.dart +++ b/packages/patrol_cli/lib/src/commands/develop.dart @@ -339,6 +339,7 @@ class DevelopCommand extends PatrolCommand { interruptible: true, showFlutterLogs: showFlutterLogs, showTestSteps: showTestSteps, + flavor: flutterOpts.flavor, ); final package = android.packageName; if (package != null && uninstall) { diff --git a/packages/patrol_cli/lib/src/commands/test.dart b/packages/patrol_cli/lib/src/commands/test.dart index 9f3c51cae..53e4f6320 100644 --- a/packages/patrol_cli/lib/src/commands/test.dart +++ b/packages/patrol_cli/lib/src/commands/test.dart @@ -357,6 +357,7 @@ See https://github.com/leancodepl/patrol/issues/1316 to learn more. device, showFlutterLogs: showFlutterLogs, showTestSteps: showTestSteps, + flavor: flutterOpts.flavor, ); final package = android.packageName; if (package != null && uninstall) { From b3d9898dd6f02051b798babf4eaba6e3814c7ecd Mon Sep 17 00:00:00 2001 From: pdenert Date: Thu, 31 Oct 2024 12:02:15 +0100 Subject: [PATCH 21/47] Fix taking directory to report in cli --- packages/patrol_cli/lib/src/android/android_test_backend.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patrol_cli/lib/src/android/android_test_backend.dart b/packages/patrol_cli/lib/src/android/android_test_backend.dart index 18e132efe..89822bc87 100644 --- a/packages/patrol_cli/lib/src/android/android_test_backend.dart +++ b/packages/patrol_cli/lib/src/android/android_test_backend.dart @@ -213,7 +213,7 @@ class AndroidTestBackend { flavorPath = 'flavors/$flavor/'; } final reportPath = - 'file://${_fs.currentDirectory.path}/build/app/reports/androidTests/connected/${flavorPath}index.html'; + 'file://${_rootDirectory.childDirectory('android').path}/build/app/reports/androidTests/connected/${flavorPath}index.html'; final patrolLogReader = PatrolLogReader( listenStdOut: processLogcat.listenStdOut, From ca9da64bab8188494ae2429cec3629518f0f2934 Mon Sep 17 00:00:00 2001 From: pdenert Date: Mon, 4 Nov 2024 23:42:07 +0100 Subject: [PATCH 22/47] Use switch expression --- packages/patrol_log/lib/src/step_entry.dart | 15 +++++---------- packages/patrol_log/lib/src/test_entry.dart | 18 ++++++------------ 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index b80fd87a0..25b552ff5 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -64,14 +64,9 @@ enum StepEntryStatus { success, failure; - String get name { - switch (this) { - case StepEntryStatus.start: - return '⏳'; - case StepEntryStatus.success: - return '✅'; - case StepEntryStatus.failure: - return '❌'; - } - } + String get name => switch (this) { + StepEntryStatus.start => '⏳', + StepEntryStatus.success => '✅', + StepEntryStatus.failure => '❌', + }; } diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index 6eaa9fb96..c551ae6f0 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -62,16 +62,10 @@ enum TestEntryStatus { failure, skip; - String get name { - switch (this) { - case TestEntryStatus.start: - return '🧪'; - case TestEntryStatus.success: - return '✅'; - case TestEntryStatus.failure: - return '❌'; - case TestEntryStatus.skip: - return '⏩'; - } - } + String get name => switch (this) { + TestEntryStatus.start => '🧪', + TestEntryStatus.success => '✅', + TestEntryStatus.failure => '❌', + TestEntryStatus.skip => '⏩', + }; } From 57ed1dcedeb61e266c68d6d156167dcfd3113c9d Mon Sep 17 00:00:00 2001 From: pdenert Date: Mon, 4 Nov 2024 23:44:45 +0100 Subject: [PATCH 23/47] Use const indentation --- packages/patrol_log/lib/src/entry.dart | 4 ++++ packages/patrol_log/lib/src/log_entry.dart | 2 +- packages/patrol_log/lib/src/step_entry.dart | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/patrol_log/lib/src/entry.dart b/packages/patrol_log/lib/src/entry.dart index dad4bdcca..5047cbe72 100644 --- a/packages/patrol_log/lib/src/entry.dart +++ b/packages/patrol_log/lib/src/entry.dart @@ -22,3 +22,7 @@ enum EntryType { test, log; } + +/// The number of spaces used for indentation in the pretty print. +/// Used in LogEntry and StepEntry. +const indentation = ' '; diff --git a/packages/patrol_log/lib/src/log_entry.dart b/packages/patrol_log/lib/src/log_entry.dart index a66ad877e..de0ca58ac 100644 --- a/packages/patrol_log/lib/src/log_entry.dart +++ b/packages/patrol_log/lib/src/log_entry.dart @@ -26,7 +26,7 @@ class LogEntry extends Entry { @override String pretty() { - return ' 📝 $message'; + return '$indentation📝 $message'; } @override diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index 25b552ff5..a88d483bf 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -38,7 +38,7 @@ class StepEntry extends Entry { @override String pretty({int? number}) { - return ' ${status.name} ${printNumber(number)} $action'; + return '$indentation${status.name} ${printNumber(number)} $action'; } String printNumber(int? number) { From 7ebc5ec1b91bba605a5f70158997e23748d5cadc Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 5 Nov 2024 10:09:02 +0100 Subject: [PATCH 24/47] Keep emojis in single class --- packages/patrol_log/lib/src/log_entry.dart | 3 ++- packages/patrol_log/lib/src/test_entry.dart | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/patrol_log/lib/src/log_entry.dart b/packages/patrol_log/lib/src/log_entry.dart index de0ca58ac..4975263c4 100644 --- a/packages/patrol_log/lib/src/log_entry.dart +++ b/packages/patrol_log/lib/src/log_entry.dart @@ -1,3 +1,4 @@ +import 'package:patrol_log/src/emojis.dart'; import 'package:patrol_log/src/entry.dart'; class LogEntry extends Entry { @@ -26,7 +27,7 @@ class LogEntry extends Entry { @override String pretty() { - return '$indentation📝 $message'; + return '$indentation${Emojis.log} $message'; } @override diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index c551ae6f0..923f749f6 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -1,3 +1,4 @@ +import 'package:patrol_log/src/emojis.dart'; import 'package:patrol_log/src/entry.dart'; class TestEntry extends Entry { @@ -63,9 +64,9 @@ enum TestEntryStatus { skip; String get name => switch (this) { - TestEntryStatus.start => '🧪', - TestEntryStatus.success => '✅', - TestEntryStatus.failure => '❌', - TestEntryStatus.skip => '⏩', + TestEntryStatus.start => Emojis.testStart, + TestEntryStatus.success => Emojis.success, + TestEntryStatus.failure => Emojis.failure, + TestEntryStatus.skip => Emojis.skip, }; } From ab49ea9eec73fb4970005724555bfb4fb252536f Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 5 Nov 2024 13:11:25 +0100 Subject: [PATCH 25/47] Initialize PatrolLog locally --- packages/patrol/lib/src/common.dart | 1 - .../lib/src/custom_finders/patrol_integration_tester.dart | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/patrol/lib/src/common.dart b/packages/patrol/lib/src/common.dart index e152e0a31..2a927a370 100644 --- a/packages/patrol/lib/src/common.dart +++ b/packages/patrol/lib/src/common.dart @@ -141,7 +141,6 @@ void patrolTest( nativeAutomator: automator, nativeAutomator2: automator2, config: config, - patrolLog: patrolLog, ); await callback(patrolTester); diff --git a/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart b/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart index d6bf2d332..c70fa1acd 100644 --- a/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart +++ b/packages/patrol/lib/src/custom_finders/patrol_integration_tester.dart @@ -12,8 +12,7 @@ class PatrolIntegrationTester extends finders.PatrolTester { required super.config, required this.nativeAutomator, required this.nativeAutomator2, - required PatrolLogWriter patrolLog, - }) : _patrolLog = patrolLog; + }) : _patrolLog = PatrolLogWriter(); /// The log for the patrol. final PatrolLogWriter _patrolLog; From dc0f2264b364800323a9672907c803c9c5e7ccef Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 5 Nov 2024 13:12:18 +0100 Subject: [PATCH 26/47] Change comment describing log source --- packages/patrol_cli/lib/src/ios/ios_test_backend.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart index 758526bf4..cf5f26589 100644 --- a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart +++ b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart @@ -157,7 +157,7 @@ class IOSTestBackend { required bool showTestSteps, }) async { await _disposeScope.run((scope) async { - // Read patrol logs from logcat + // Read patrol logs from log stream final processLogs = await _processManager.start( [ 'log', From ed4ebca040b8b656e90ffd11ed8b1b9fe9b62c73 Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 5 Nov 2024 13:49:42 +0100 Subject: [PATCH 27/47] Change flag to hideTestSteps --- .../lib/src/android/android_test_backend.dart | 4 ++-- packages/patrol_cli/lib/src/commands/develop.dart | 10 +++++----- packages/patrol_cli/lib/src/commands/test.dart | 10 +++++----- packages/patrol_cli/lib/src/ios/ios_test_backend.dart | 4 ++-- packages/patrol_cli/lib/src/runner/patrol_command.dart | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/patrol_cli/lib/src/android/android_test_backend.dart b/packages/patrol_cli/lib/src/android/android_test_backend.dart index 89822bc87..d52586195 100644 --- a/packages/patrol_cli/lib/src/android/android_test_backend.dart +++ b/packages/patrol_cli/lib/src/android/android_test_backend.dart @@ -191,7 +191,7 @@ class AndroidTestBackend { String? flavor, bool interruptible = false, required bool showFlutterLogs, - required bool showTestSteps, + required bool hideTestSteps, }) async { await _disposeScope.run((scope) async { // Read patrol logs from logcat @@ -221,7 +221,7 @@ class AndroidTestBackend { log: _logger.info, reportPath: reportPath, showFlutterLogs: showFlutterLogs, - showTestSteps: showTestSteps, + hideTestSteps: hideTestSteps, ) ..listen() ..startTimer(); diff --git a/packages/patrol_cli/lib/src/commands/develop.dart b/packages/patrol_cli/lib/src/commands/develop.dart index b2fea350b..7b79f2c21 100644 --- a/packages/patrol_cli/lib/src/commands/develop.dart +++ b/packages/patrol_cli/lib/src/commands/develop.dart @@ -55,7 +55,7 @@ class DevelopCommand extends PatrolCommand { usesPortOptions(); usesTagsOption(); usesShowFlutterLogs(); - usesShowTestSteps(); + usesHideTestSteps(); usesUninstallOption(); @@ -247,7 +247,7 @@ class DevelopCommand extends PatrolCommand { device: device, openDevtools: boolArg('open-devtools'), showFlutterLogs: boolArg('show-flutter-logs'), - showTestSteps: boolArg('show-test-steps'), + hideTestSteps: boolArg('hide-test-steps'), ); return 0; // for now, all exit codes are 0 @@ -324,7 +324,7 @@ class DevelopCommand extends PatrolCommand { required Device device, required bool openDevtools, required bool showFlutterLogs, - required bool showTestSteps, + required bool hideTestSteps, }) async { Future Function() action; Future Function()? finalizer; @@ -338,7 +338,7 @@ class DevelopCommand extends PatrolCommand { device, interruptible: true, showFlutterLogs: showFlutterLogs, - showTestSteps: showTestSteps, + hideTestSteps: hideTestSteps, flavor: flutterOpts.flavor, ); final package = android.packageName; @@ -356,7 +356,7 @@ class DevelopCommand extends PatrolCommand { device, interruptible: true, showFlutterLogs: showFlutterLogs, - showTestSteps: showTestSteps, + hideTestSteps: hideTestSteps, ); final bundleId = iosOpts.bundleId; if (bundleId != null && uninstall) { diff --git a/packages/patrol_cli/lib/src/commands/test.dart b/packages/patrol_cli/lib/src/commands/test.dart index 53e4f6320..78ea968fc 100644 --- a/packages/patrol_cli/lib/src/commands/test.dart +++ b/packages/patrol_cli/lib/src/commands/test.dart @@ -57,7 +57,7 @@ class TestCommand extends PatrolCommand { usesExcludeTagsOption(); useCoverageOptions(); usesShowFlutterLogs(); - usesShowTestSteps(); + usesHideTestSteps(); usesUninstallOption(); @@ -269,7 +269,7 @@ See https://github.com/leancodepl/patrol/issues/1316 to learn more. uninstall: uninstall, device: device, showFlutterLogs: boolArg('show-flutter-logs'), - showTestSteps: boolArg('show-test-steps'), + hideTestSteps: boolArg('hide-test-steps'), ); return allPassed ? 0 : 1; @@ -345,7 +345,7 @@ See https://github.com/leancodepl/patrol/issues/1316 to learn more. required bool uninstall, required Device device, required bool showFlutterLogs, - required bool showTestSteps, + required bool hideTestSteps, }) async { Future Function() action; Future Function()? finalizer; @@ -356,7 +356,7 @@ See https://github.com/leancodepl/patrol/issues/1316 to learn more. android, device, showFlutterLogs: showFlutterLogs, - showTestSteps: showTestSteps, + hideTestSteps: hideTestSteps, flavor: flutterOpts.flavor, ); final package = android.packageName; @@ -370,7 +370,7 @@ See https://github.com/leancodepl/patrol/issues/1316 to learn more. ios, device, showFlutterLogs: showFlutterLogs, - showTestSteps: showTestSteps, + hideTestSteps: hideTestSteps, ); final bundleId = ios.bundleId; if (bundleId != null && uninstall) { diff --git a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart index cf5f26589..e6ee8652e 100644 --- a/packages/patrol_cli/lib/src/ios/ios_test_backend.dart +++ b/packages/patrol_cli/lib/src/ios/ios_test_backend.dart @@ -154,7 +154,7 @@ class IOSTestBackend { Device device, { bool interruptible = false, required bool showFlutterLogs, - required bool showTestSteps, + required bool hideTestSteps, }) async { await _disposeScope.run((scope) async { // Read patrol logs from log stream @@ -177,7 +177,7 @@ class IOSTestBackend { log: _logger.info, reportPath: reportPath, showFlutterLogs: showFlutterLogs, - showTestSteps: showTestSteps, + hideTestSteps: hideTestSteps, ) ..listen() ..startTimer(); diff --git a/packages/patrol_cli/lib/src/runner/patrol_command.dart b/packages/patrol_cli/lib/src/runner/patrol_command.dart index 181218a90..23f3e1d40 100644 --- a/packages/patrol_cli/lib/src/runner/patrol_command.dart +++ b/packages/patrol_cli/lib/src/runner/patrol_command.dart @@ -195,10 +195,10 @@ abstract class PatrolCommand extends Command { ); } - void usesShowTestSteps() { + void usesHideTestSteps() { argParser.addFlag( - 'show-test-steps', - help: 'Show test steps while running the tests.', + 'hide-test-steps', + help: 'Hide test steps while running the tests.', ); } From 5e1ec6c54146acad9b1c22de8f5ad31f45d506da Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 5 Nov 2024 14:15:16 +0100 Subject: [PATCH 28/47] Fix android report path --- packages/patrol_cli/lib/src/android/android_test_backend.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patrol_cli/lib/src/android/android_test_backend.dart b/packages/patrol_cli/lib/src/android/android_test_backend.dart index d52586195..d4a50f5c8 100644 --- a/packages/patrol_cli/lib/src/android/android_test_backend.dart +++ b/packages/patrol_cli/lib/src/android/android_test_backend.dart @@ -213,7 +213,7 @@ class AndroidTestBackend { flavorPath = 'flavors/$flavor/'; } final reportPath = - 'file://${_rootDirectory.childDirectory('android').path}/build/app/reports/androidTests/connected/${flavorPath}index.html'; + 'file://${_rootDirectory.path}/build/app/reports/androidTests/connected/${flavorPath}index.html'; final patrolLogReader = PatrolLogReader( listenStdOut: processLogcat.listenStdOut, From d5cf9a1c125523f3c8344483c81f3d1c3fb4627c Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 5 Nov 2024 14:23:03 +0100 Subject: [PATCH 29/47] Change emojis in step_entry to one from Emojis class --- packages/patrol_log/lib/src/step_entry.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index a88d483bf..3505783dc 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -1,3 +1,4 @@ +import 'package:patrol_log/src/emojis.dart'; import 'package:patrol_log/src/entry.dart'; class StepEntry extends Entry { @@ -65,8 +66,8 @@ enum StepEntryStatus { failure; String get name => switch (this) { - StepEntryStatus.start => '⏳', - StepEntryStatus.success => '✅', - StepEntryStatus.failure => '❌', + StepEntryStatus.start => Emojis.waiting, + StepEntryStatus.success => Emojis.success, + StepEntryStatus.failure => Emojis.failure, }; } From 2f60a16f8921588252986e27bd3545ddd1ee3b61 Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 5 Nov 2024 14:24:06 +0100 Subject: [PATCH 30/47] Rename printNumber to printIndex and add comment --- packages/patrol_log/lib/src/step_entry.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index 3505783dc..f04030695 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -39,10 +39,12 @@ class StepEntry extends Entry { @override String pretty({int? number}) { - return '$indentation${status.name} ${printNumber(number)} $action'; + return '$indentation${status.name} ${printIndex(number)} $action'; } - String printNumber(int? number) { + /// Returns the index of the step with the correct number of spaces, + /// to format the output. + String printIndex(int? number) { if (number != null) { if (number < 10) { return ' $number.'; From 829948a4d45d5a3a9980dbd9fd0e1a1b45832f01 Mon Sep 17 00:00:00 2001 From: pdenert Date: Tue, 5 Nov 2024 18:51:37 +0100 Subject: [PATCH 31/47] Use sealed class and json serializable --- packages/patrol_log/lib/src/entry.dart | 18 +++++- packages/patrol_log/lib/src/entry.g.dart | 66 +++++++++++++++++++++ packages/patrol_log/lib/src/log_entry.dart | 16 ++--- packages/patrol_log/lib/src/step_entry.dart | 26 +++----- packages/patrol_log/lib/src/test_entry.dart | 23 +++---- 5 files changed, 104 insertions(+), 45 deletions(-) create mode 100644 packages/patrol_log/lib/src/entry.g.dart diff --git a/packages/patrol_log/lib/src/entry.dart b/packages/patrol_log/lib/src/entry.dart index 5047cbe72..47ead19e5 100644 --- a/packages/patrol_log/lib/src/entry.dart +++ b/packages/patrol_log/lib/src/entry.dart @@ -1,4 +1,14 @@ -abstract class Entry { +import 'package:equatable/equatable.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:patrol_log/src/emojis.dart'; + +part 'log_entry.dart'; +part 'step_entry.dart'; +part 'test_entry.dart'; + +part 'entry.g.dart'; + +sealed class Entry with EquatableMixin { Entry({required this.timestamp, required this.type}); // ignore: avoid_unused_constructor_parameters @@ -15,11 +25,17 @@ abstract class Entry { @override String toString() => throw UnimplementedError('toString is not implemented'); + + @override + List get props => [timestamp, type]; } enum EntryType { + @JsonValue('step') step, + @JsonValue('test') test, + @JsonValue('log') log; } diff --git a/packages/patrol_log/lib/src/entry.g.dart b/packages/patrol_log/lib/src/entry.g.dart new file mode 100644 index 000000000..f412a9984 --- /dev/null +++ b/packages/patrol_log/lib/src/entry.g.dart @@ -0,0 +1,66 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'entry.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LogEntry _$LogEntryFromJson(Map json) => LogEntry( + message: json['message'] as String, + timestamp: json['timestamp'] == null + ? null + : DateTime.parse(json['timestamp'] as String), + ); + +Map _$LogEntryToJson(LogEntry instance) => { + 'timestamp': instance.timestamp.toIso8601String(), + 'message': instance.message, + }; + +StepEntry _$StepEntryFromJson(Map json) => StepEntry( + action: json['action'] as String, + status: $enumDecode(_$StepEntryStatusEnumMap, json['status']), + exception: json['exception'] as String?, + data: json['data'] as Map?, + timestamp: json['timestamp'] == null + ? null + : DateTime.parse(json['timestamp'] as String), + ); + +Map _$StepEntryToJson(StepEntry instance) => { + 'timestamp': instance.timestamp.toIso8601String(), + 'action': instance.action, + 'status': _$StepEntryStatusEnumMap[instance.status]!, + 'exception': instance.exception, + 'data': instance.data, + }; + +const _$StepEntryStatusEnumMap = { + StepEntryStatus.start: 'start', + StepEntryStatus.success: 'success', + StepEntryStatus.failure: 'failure', +}; + +TestEntry _$TestEntryFromJson(Map json) => TestEntry( + name: json['name'] as String, + status: $enumDecode(_$TestEntryStatusEnumMap, json['status']), + timestamp: json['timestamp'] == null + ? null + : DateTime.parse(json['timestamp'] as String), + error: json['error'] as String?, + ); + +Map _$TestEntryToJson(TestEntry instance) => { + 'timestamp': instance.timestamp.toIso8601String(), + 'name': instance.name, + 'status': _$TestEntryStatusEnumMap[instance.status]!, + 'error': instance.error, + }; + +const _$TestEntryStatusEnumMap = { + TestEntryStatus.start: 'start', + TestEntryStatus.success: 'success', + TestEntryStatus.failure: 'failure', + TestEntryStatus.skip: 'skip', +}; diff --git a/packages/patrol_log/lib/src/log_entry.dart b/packages/patrol_log/lib/src/log_entry.dart index 4975263c4..a2e8bcd63 100644 --- a/packages/patrol_log/lib/src/log_entry.dart +++ b/packages/patrol_log/lib/src/log_entry.dart @@ -1,6 +1,6 @@ -import 'package:patrol_log/src/emojis.dart'; -import 'package:patrol_log/src/entry.dart'; +part of 'entry.dart'; +@JsonSerializable(explicitToJson: true) class LogEntry extends Entry { LogEntry({ required this.message, @@ -11,19 +11,13 @@ class LogEntry extends Entry { ); @override - factory LogEntry.fromJson(Map json) => LogEntry( - timestamp: DateTime.parse(json['timestamp'] as String), - message: json['message'] as String, - ); + factory LogEntry.fromJson(Map json) => + _$LogEntryFromJson(json); final String message; @override - Map toJson() => { - 'message': message, - 'timestamp': timestamp.toIso8601String(), - 'type': type.index, - }; + Map toJson() => _$LogEntryToJson(this); @override String pretty() { diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index f04030695..36c16a9da 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -1,6 +1,6 @@ -import 'package:patrol_log/src/emojis.dart'; -import 'package:patrol_log/src/entry.dart'; +part of 'entry.dart'; +@JsonSerializable(explicitToJson: true) class StepEntry extends Entry { StepEntry({ required this.action, @@ -13,14 +13,8 @@ class StepEntry extends Entry { type: EntryType.step, ); - @override - factory StepEntry.fromJson(Map json) => StepEntry( - action: json['action'] as String, - timestamp: DateTime.parse(json['timestamp'] as String), - status: StepEntryStatus.values[json['status'] as int], - exception: json['exception'] as String?, - data: json['data'] as Map?, - ); + factory StepEntry.fromJson(Map json) => + _$StepEntryFromJson(json); final String action; final StepEntryStatus status; @@ -28,14 +22,7 @@ class StepEntry extends Entry { final Map? data; @override - Map toJson() => { - 'action': action, - 'status': status.index, - 'type': type.index, - 'timestamp': timestamp.toIso8601String(), - 'exception': exception, - 'data': data, - }; + Map toJson() => _$StepEntryToJson(this); @override String pretty({int? number}) { @@ -60,6 +47,9 @@ class StepEntry extends Entry { @override String toString() => 'StepEntry(${toJson()})'; + + @override + List get props => [action, status, exception, data, timestamp, type]; } enum StepEntryStatus { diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index 923f749f6..a5c7de68f 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -1,6 +1,6 @@ -import 'package:patrol_log/src/emojis.dart'; -import 'package:patrol_log/src/entry.dart'; +part of 'entry.dart'; +@JsonSerializable(explicitToJson: true) class TestEntry extends Entry { TestEntry({ required this.name, @@ -13,12 +13,8 @@ class TestEntry extends Entry { ); @override - factory TestEntry.fromJson(Map json) => TestEntry( - timestamp: DateTime.parse(json['timestamp'] as String), - name: json['name'] as String, - status: TestEntryStatus.values[json['status'] as int], - error: json['error'] as String?, - ); + factory TestEntry.fromJson(Map json) => + _$TestEntryFromJson(json); final String name; final TestEntryStatus status; @@ -27,13 +23,7 @@ class TestEntry extends Entry { Duration executionTime(DateTime start) => timestamp.difference(start); @override - Map toJson() => { - 'name': name, - 'status': status.index, - 'type': type.index, - 'timestamp': timestamp.toIso8601String(), - 'error': error, - }; + Map toJson() => _$TestEntryToJson(this); @override String pretty() { @@ -52,6 +42,9 @@ class TestEntry extends Entry { @override String toString() => 'TestEntry(${toJson()})'; + @override + List get props => [name, status, error, timestamp, type]; + /// Returns `true` if the test is finished successfully or with a failure. bool get isFinished => status == TestEntryStatus.success || status == TestEntryStatus.failure; From b69c156246113f3feb2dd80352d911ee5d9d0e67 Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 6 Nov 2024 01:26:16 +0100 Subject: [PATCH 32/47] Add class for ascii codes --- packages/patrol/lib/src/native/native_automator.dart | 3 ++- packages/patrol/lib/src/native/native_automator2.dart | 3 ++- packages/patrol_log/lib/src/test_entry.dart | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/patrol/lib/src/native/native_automator.dart b/packages/patrol/lib/src/native/native_automator.dart index d5ddd8b6f..f17b6e1d0 100644 --- a/packages/patrol/lib/src/native/native_automator.dart +++ b/packages/patrol/lib/src/native/native_automator.dart @@ -222,7 +222,8 @@ class NativeAutomator { bool enablePatrolLog = true, }) async { _config.logger('$name() started'); - final text = '\u001b[38;5;87m$name\u001b[0m \u001b[30m(native)\u001b[0m'; + final text = + '${AnsiCodes.lightBlue}$name${AnsiCodes.reset} ${AnsiCodes.gray}(native)${AnsiCodes.reset}'; if (enablePatrolLog) { _patrolLog.log(StepEntry(action: text, status: StepEntryStatus.start)); diff --git a/packages/patrol/lib/src/native/native_automator2.dart b/packages/patrol/lib/src/native/native_automator2.dart index bd58a6ec5..cfe9fd48f 100644 --- a/packages/patrol/lib/src/native/native_automator2.dart +++ b/packages/patrol/lib/src/native/native_automator2.dart @@ -104,7 +104,8 @@ class NativeAutomator2 { bool enablePatrolLog = true, }) async { _config.logger('$name() started'); - final text = '\u001b[38;5;87m$name\u001b[0m \u001b[30m(native)\u001b[0m'; + final text = + '${AnsiCodes.lightBlue}$name${AnsiCodes.reset} ${AnsiCodes.gray}(native)${AnsiCodes.reset}'; if (enablePatrolLog) { _patrolLog.log(StepEntry(action: text, status: StepEntryStatus.start)); diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index a5c7de68f..1b222f408 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -30,11 +30,11 @@ class TestEntry extends Entry { if (status == TestEntryStatus.skip) { return '${status.name} $_testName'; } - return '${status.name} $_testName \u001b[30m(integration_test/$_filePath.dart)\u001b[0m'; + return '${status.name} $_testName ${AnsiCodes.gray}(integration_test/$_filePath.dart)${AnsiCodes.reset}'; } String get nameWithPath => - '$_testName \u001b[30m(integration_test/$_filePath.dart)\u001b[0m'; + '$_testName ${AnsiCodes.gray}(integration_test/$_filePath.dart)${AnsiCodes.reset}'; String get _filePath => name.split(' ').first.replaceAll('.', '/'); String get _testName => name.split(' ').skip(1).join(' '); From af20ae33ec064aecc3f657c28a068735c616343b Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 6 Nov 2024 09:41:48 +0100 Subject: [PATCH 33/47] Change report path on windows --- packages/patrol_cli/lib/src/android/android_test_backend.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/patrol_cli/lib/src/android/android_test_backend.dart b/packages/patrol_cli/lib/src/android/android_test_backend.dart index d4a50f5c8..52d7a7c9c 100644 --- a/packages/patrol_cli/lib/src/android/android_test_backend.dart +++ b/packages/patrol_cli/lib/src/android/android_test_backend.dart @@ -212,8 +212,10 @@ class AndroidTestBackend { if (flavor != null) { flavorPath = 'flavors/$flavor/'; } - final reportPath = + final path = 'file://${_rootDirectory.path}/build/app/reports/androidTests/connected/${flavorPath}index.html'; + final reportPath = + _platform.isWindows ? path.replaceAll(r'\', '/') : path; final patrolLogReader = PatrolLogReader( listenStdOut: processLogcat.listenStdOut, From bae1e7029c181c54674a1cdaa645a43c4c9c0dae Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 6 Nov 2024 13:21:57 +0100 Subject: [PATCH 34/47] Add logcat to adb wrapper --- .../lib/src/android/android_test_backend.dart | 16 ++++++---------- packages/patrol_cli/pubspec.yaml | 3 ++- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/patrol_cli/lib/src/android/android_test_backend.dart b/packages/patrol_cli/lib/src/android/android_test_backend.dart index 52d7a7c9c..f4761cda7 100644 --- a/packages/patrol_cli/lib/src/android/android_test_backend.dart +++ b/packages/patrol_cli/lib/src/android/android_test_backend.dart @@ -195,16 +195,12 @@ class AndroidTestBackend { }) async { await _disposeScope.run((scope) async { // Read patrol logs from logcat - final processLogcat = await _processManager.start( - [ - 'adb', - 'shell', - 'logcat', - '-T', - '1', - 'PatrolServer:I Patrol:I flutter:I *:S', - ], - runInShell: true, + final processLogcat = await _adb.logcat( + device: device.id, + arguments: { + '-T': '1', + }, + filter: 'PatrolServer:I Patrol:I flutter:I *:S', ) ..disposedBy(scope); diff --git a/packages/patrol_cli/pubspec.yaml b/packages/patrol_cli/pubspec.yaml index e467ab3f4..4a989838e 100644 --- a/packages/patrol_cli/pubspec.yaml +++ b/packages/patrol_cli/pubspec.yaml @@ -15,7 +15,8 @@ environment: sdk: '>=3.5.0 <4.0.0' dependencies: - adb: ^0.4.0 + adb: + path: ../adb ansi_styles: ^0.3.2+1 args: ^2.4.2 ci: ^0.1.0 From 787d242cd764feda27bd20ecde61043471554842 Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 6 Nov 2024 13:46:39 +0100 Subject: [PATCH 35/47] Fix - add entry type to generated json --- packages/patrol_log/lib/src/entry.dart | 17 ++++++++++++++--- packages/patrol_log/lib/src/entry.g.dart | 15 +++++++++++++++ packages/patrol_log/lib/src/log_entry.dart | 13 +++++++------ packages/patrol_log/lib/src/step_entry.dart | 8 +++----- packages/patrol_log/lib/src/test_entry.dart | 8 +++----- 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/packages/patrol_log/lib/src/entry.dart b/packages/patrol_log/lib/src/entry.dart index 47ead19e5..256a14005 100644 --- a/packages/patrol_log/lib/src/entry.dart +++ b/packages/patrol_log/lib/src/entry.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:patrol_log/patrol_log.dart'; import 'package:patrol_log/src/emojis.dart'; part 'log_entry.dart'; @@ -31,12 +32,22 @@ sealed class Entry with EquatableMixin { } enum EntryType { - @JsonValue('step') step, - @JsonValue('test') test, - @JsonValue('log') log; + + static EntryType byName(String name) { + switch (name) { + case 'step': + return EntryType.step; + case 'test': + return EntryType.test; + case 'log': + return EntryType.log; + default: + throw ArgumentError('Unknown EntryType: $name'); + } + } } /// The number of spaces used for indentation in the pretty print. diff --git a/packages/patrol_log/lib/src/entry.g.dart b/packages/patrol_log/lib/src/entry.g.dart index f412a9984..537f6e9f1 100644 --- a/packages/patrol_log/lib/src/entry.g.dart +++ b/packages/patrol_log/lib/src/entry.g.dart @@ -11,13 +11,22 @@ LogEntry _$LogEntryFromJson(Map json) => LogEntry( timestamp: json['timestamp'] == null ? null : DateTime.parse(json['timestamp'] as String), + type: $enumDecodeNullable(_$EntryTypeEnumMap, json['type']) ?? + EntryType.log, ); Map _$LogEntryToJson(LogEntry instance) => { 'timestamp': instance.timestamp.toIso8601String(), + 'type': _$EntryTypeEnumMap[instance.type]!, 'message': instance.message, }; +const _$EntryTypeEnumMap = { + EntryType.step: 'step', + EntryType.test: 'test', + EntryType.log: 'log', +}; + StepEntry _$StepEntryFromJson(Map json) => StepEntry( action: json['action'] as String, status: $enumDecode(_$StepEntryStatusEnumMap, json['status']), @@ -26,10 +35,13 @@ StepEntry _$StepEntryFromJson(Map json) => StepEntry( timestamp: json['timestamp'] == null ? null : DateTime.parse(json['timestamp'] as String), + type: $enumDecodeNullable(_$EntryTypeEnumMap, json['type']) ?? + EntryType.step, ); Map _$StepEntryToJson(StepEntry instance) => { 'timestamp': instance.timestamp.toIso8601String(), + 'type': _$EntryTypeEnumMap[instance.type]!, 'action': instance.action, 'status': _$StepEntryStatusEnumMap[instance.status]!, 'exception': instance.exception, @@ -49,10 +61,13 @@ TestEntry _$TestEntryFromJson(Map json) => TestEntry( ? null : DateTime.parse(json['timestamp'] as String), error: json['error'] as String?, + type: $enumDecodeNullable(_$EntryTypeEnumMap, json['type']) ?? + EntryType.test, ); Map _$TestEntryToJson(TestEntry instance) => { 'timestamp': instance.timestamp.toIso8601String(), + 'type': _$EntryTypeEnumMap[instance.type]!, 'name': instance.name, 'status': _$TestEntryStatusEnumMap[instance.status]!, 'error': instance.error, diff --git a/packages/patrol_log/lib/src/log_entry.dart b/packages/patrol_log/lib/src/log_entry.dart index a2e8bcd63..5f4f68c0b 100644 --- a/packages/patrol_log/lib/src/log_entry.dart +++ b/packages/patrol_log/lib/src/log_entry.dart @@ -1,14 +1,12 @@ part of 'entry.dart'; -@JsonSerializable(explicitToJson: true) +@JsonSerializable() class LogEntry extends Entry { LogEntry({ required this.message, DateTime? timestamp, - }) : super( - timestamp: timestamp ?? DateTime.now(), - type: EntryType.log, - ); + super.type = EntryType.log, + }) : super(timestamp: timestamp ?? DateTime.now()); @override factory LogEntry.fromJson(Map json) => @@ -21,9 +19,12 @@ class LogEntry extends Entry { @override String pretty() { - return '$indentation${Emojis.log} $message'; + return '$indentation${Emojis.log} $message'; } @override String toString() => 'LogEntry(${toJson()})'; + + @override + List get props => [message, timestamp, type]; } diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index 36c16a9da..65f3d9d77 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -1,6 +1,6 @@ part of 'entry.dart'; -@JsonSerializable(explicitToJson: true) +@JsonSerializable() class StepEntry extends Entry { StepEntry({ required this.action, @@ -8,10 +8,8 @@ class StepEntry extends Entry { this.exception, this.data, DateTime? timestamp, - }) : super( - timestamp: timestamp ?? DateTime.now(), - type: EntryType.step, - ); + super.type = EntryType.step, + }) : super(timestamp: timestamp ?? DateTime.now()); factory StepEntry.fromJson(Map json) => _$StepEntryFromJson(json); diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index 1b222f408..6dd6eda5e 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -1,16 +1,14 @@ part of 'entry.dart'; -@JsonSerializable(explicitToJson: true) +@JsonSerializable() class TestEntry extends Entry { TestEntry({ required this.name, required this.status, DateTime? timestamp, this.error, - }) : super( - timestamp: timestamp ?? DateTime.now(), - type: EntryType.test, - ); + super.type = EntryType.test, + }) : super(timestamp: timestamp ?? DateTime.now()); @override factory TestEntry.fromJson(Map json) => From 2869e10eb38a42bf0d8808051ab32ec344a9e068 Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 6 Nov 2024 14:48:47 +0100 Subject: [PATCH 36/47] Change way of generating type in entries toJson --- packages/patrol_log/lib/src/entry.dart | 2 ++ packages/patrol_log/lib/src/entry.g.dart | 6 ------ packages/patrol_log/lib/src/log_entry.dart | 6 ++++-- packages/patrol_log/lib/src/step_entry.dart | 6 ++++-- packages/patrol_log/lib/src/test_entry.dart | 6 ++++-- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/patrol_log/lib/src/entry.dart b/packages/patrol_log/lib/src/entry.dart index 256a14005..2bc39ad97 100644 --- a/packages/patrol_log/lib/src/entry.dart +++ b/packages/patrol_log/lib/src/entry.dart @@ -18,6 +18,8 @@ sealed class Entry with EquatableMixin { } final DateTime timestamp; + + @JsonKey(includeToJson: true) final EntryType type; Map toJson(); diff --git a/packages/patrol_log/lib/src/entry.g.dart b/packages/patrol_log/lib/src/entry.g.dart index 537f6e9f1..3657287ec 100644 --- a/packages/patrol_log/lib/src/entry.g.dart +++ b/packages/patrol_log/lib/src/entry.g.dart @@ -11,8 +11,6 @@ LogEntry _$LogEntryFromJson(Map json) => LogEntry( timestamp: json['timestamp'] == null ? null : DateTime.parse(json['timestamp'] as String), - type: $enumDecodeNullable(_$EntryTypeEnumMap, json['type']) ?? - EntryType.log, ); Map _$LogEntryToJson(LogEntry instance) => { @@ -35,8 +33,6 @@ StepEntry _$StepEntryFromJson(Map json) => StepEntry( timestamp: json['timestamp'] == null ? null : DateTime.parse(json['timestamp'] as String), - type: $enumDecodeNullable(_$EntryTypeEnumMap, json['type']) ?? - EntryType.step, ); Map _$StepEntryToJson(StepEntry instance) => { @@ -61,8 +57,6 @@ TestEntry _$TestEntryFromJson(Map json) => TestEntry( ? null : DateTime.parse(json['timestamp'] as String), error: json['error'] as String?, - type: $enumDecodeNullable(_$EntryTypeEnumMap, json['type']) ?? - EntryType.test, ); Map _$TestEntryToJson(TestEntry instance) => { diff --git a/packages/patrol_log/lib/src/log_entry.dart b/packages/patrol_log/lib/src/log_entry.dart index 5f4f68c0b..bddfad150 100644 --- a/packages/patrol_log/lib/src/log_entry.dart +++ b/packages/patrol_log/lib/src/log_entry.dart @@ -5,8 +5,10 @@ class LogEntry extends Entry { LogEntry({ required this.message, DateTime? timestamp, - super.type = EntryType.log, - }) : super(timestamp: timestamp ?? DateTime.now()); + }) : super( + timestamp: timestamp ?? DateTime.now(), + type: EntryType.log, + ); @override factory LogEntry.fromJson(Map json) => diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index 65f3d9d77..0df89f868 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -8,8 +8,10 @@ class StepEntry extends Entry { this.exception, this.data, DateTime? timestamp, - super.type = EntryType.step, - }) : super(timestamp: timestamp ?? DateTime.now()); + }) : super( + timestamp: timestamp ?? DateTime.now(), + type: EntryType.step, + ); factory StepEntry.fromJson(Map json) => _$StepEntryFromJson(json); diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index 6dd6eda5e..51b2d17f2 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -7,8 +7,10 @@ class TestEntry extends Entry { required this.status, DateTime? timestamp, this.error, - super.type = EntryType.test, - }) : super(timestamp: timestamp ?? DateTime.now()); + }) : super( + timestamp: timestamp ?? DateTime.now(), + type: EntryType.test, + ); @override factory TestEntry.fromJson(Map json) => From d1e9d8ba0302daa18401cbd9e05bfa68fedd948f Mon Sep 17 00:00:00 2001 From: pdenert Date: Wed, 6 Nov 2024 17:12:20 +0100 Subject: [PATCH 37/47] Refactor reading Entry --- packages/patrol_log/lib/src/patrol_log_reader.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/patrol_log/lib/src/patrol_log_reader.dart b/packages/patrol_log/lib/src/patrol_log_reader.dart index 7873e897b..d8f39d1e4 100644 --- a/packages/patrol_log/lib/src/patrol_log_reader.dart +++ b/packages/patrol_log/lib/src/patrol_log_reader.dart @@ -36,6 +36,7 @@ class PatrolLogReader { /// List of tests names that were skipped. final List _skippedTests = []; + final StreamController _controller = StreamController.broadcast(); late final StreamSubscription _streamSubscription; From 465434023cd46051488d15e078ce4f5785827760 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 8 Nov 2024 09:41:07 +0100 Subject: [PATCH 38/47] Convert to switch expressions --- packages/patrol_log/lib/src/entry.dart | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/patrol_log/lib/src/entry.dart b/packages/patrol_log/lib/src/entry.dart index 2bc39ad97..7c6e9f756 100644 --- a/packages/patrol_log/lib/src/entry.dart +++ b/packages/patrol_log/lib/src/entry.dart @@ -39,16 +39,12 @@ enum EntryType { log; static EntryType byName(String name) { - switch (name) { - case 'step': - return EntryType.step; - case 'test': - return EntryType.test; - case 'log': - return EntryType.log; - default: - throw ArgumentError('Unknown EntryType: $name'); - } + return switch (name) { + 'step' => EntryType.step, + 'test' => EntryType.test, + 'log' => EntryType.log, + _ => throw ArgumentError('Unknown EntryType: $name') + }; } } From 44e095100da42061a1243bcdf0cfc6ec707649a9 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 8 Nov 2024 09:41:57 +0100 Subject: [PATCH 39/47] Change print index body to switch expression --- packages/patrol_log/lib/src/step_entry.dart | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart index 0df89f868..de86f2f48 100644 --- a/packages/patrol_log/lib/src/step_entry.dart +++ b/packages/patrol_log/lib/src/step_entry.dart @@ -32,17 +32,12 @@ class StepEntry extends Entry { /// Returns the index of the step with the correct number of spaces, /// to format the output. String printIndex(int? number) { - if (number != null) { - if (number < 10) { - return ' $number.'; - } else if (number < 100) { - return ' $number.'; - } else { - return '$number.'; - } - } - - return ''; + return switch (number) { + == null => '', + < 10 => ' $number.', + < 100 => ' $number.', + _ => '$number.', + }; } @override From 0ab20692cd5c1e9ca3757f58e0632d599e1e5f47 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 8 Nov 2024 12:29:19 +0100 Subject: [PATCH 40/47] Bump adb version --- packages/patrol_cli/pubspec.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/patrol_cli/pubspec.yaml b/packages/patrol_cli/pubspec.yaml index 4a989838e..3822d77e5 100644 --- a/packages/patrol_cli/pubspec.yaml +++ b/packages/patrol_cli/pubspec.yaml @@ -15,8 +15,7 @@ environment: sdk: '>=3.5.0 <4.0.0' dependencies: - adb: - path: ../adb + adb: ^0.5.0 ansi_styles: ^0.3.2+1 args: ^2.4.2 ci: ^0.1.0 From 6fa4e0a57222e5a73fde151f0be610017c1018ed Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 8 Nov 2024 12:31:12 +0100 Subject: [PATCH 41/47] Print test error when not null --- packages/patrol_log/lib/src/test_entry.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart index 51b2d17f2..82e08ebd5 100644 --- a/packages/patrol_log/lib/src/test_entry.dart +++ b/packages/patrol_log/lib/src/test_entry.dart @@ -30,7 +30,7 @@ class TestEntry extends Entry { if (status == TestEntryStatus.skip) { return '${status.name} $_testName'; } - return '${status.name} $_testName ${AnsiCodes.gray}(integration_test/$_filePath.dart)${AnsiCodes.reset}'; + return '${status.name} $_testName ${AnsiCodes.gray}(integration_test/$_filePath.dart)${AnsiCodes.reset}${error != null ? '\n$error' : ''}'; } String get nameWithPath => From 4ab8e3b956344fafe202e4453f0ed4be5ac173c2 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 8 Nov 2024 15:20:35 +0100 Subject: [PATCH 42/47] Add ErrorEntry --- .../lib/src/native/patrol_app_service.dart | 8 +- packages/patrol_log/lib/src/entry.dart | 53 ------------- packages/patrol_log/lib/src/entry.g.dart | 75 ------------------- packages/patrol_log/lib/src/log_entry.dart | 32 -------- packages/patrol_log/lib/src/step_entry.dart | 60 --------------- packages/patrol_log/lib/src/test_entry.dart | 65 ---------------- 6 files changed, 7 insertions(+), 286 deletions(-) delete mode 100644 packages/patrol_log/lib/src/entry.dart delete mode 100644 packages/patrol_log/lib/src/entry.g.dart delete mode 100644 packages/patrol_log/lib/src/log_entry.dart delete mode 100644 packages/patrol_log/lib/src/step_entry.dart delete mode 100644 packages/patrol_log/lib/src/test_entry.dart diff --git a/packages/patrol/lib/src/native/patrol_app_service.dart b/packages/patrol/lib/src/native/patrol_app_service.dart index 630cc9f8a..58dc0e582 100644 --- a/packages/patrol/lib/src/native/patrol_app_service.dart +++ b/packages/patrol/lib/src/native/patrol_app_service.dart @@ -161,8 +161,14 @@ class PatrolAppService extends PatrolAppServiceServer { final testExecutionResult = await testExecutionCompleted; if (!testExecutionResult.passed) { _patrolLog.log( - TestEntry(name: request.name, status: TestEntryStatus.failure), + TestEntry( + name: request.name, + status: TestEntryStatus.failure, + ), ); + testExecutionResult.details?.split('\n').forEach( + (e) => _patrolLog.log(ErrorEntry(message: e)), + ); } else { _patrolLog.log( TestEntry( diff --git a/packages/patrol_log/lib/src/entry.dart b/packages/patrol_log/lib/src/entry.dart deleted file mode 100644 index 7c6e9f756..000000000 --- a/packages/patrol_log/lib/src/entry.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:json_annotation/json_annotation.dart'; -import 'package:patrol_log/patrol_log.dart'; -import 'package:patrol_log/src/emojis.dart'; - -part 'log_entry.dart'; -part 'step_entry.dart'; -part 'test_entry.dart'; - -part 'entry.g.dart'; - -sealed class Entry with EquatableMixin { - Entry({required this.timestamp, required this.type}); - - // ignore: avoid_unused_constructor_parameters - factory Entry.fromJson(Map json) { - throw UnimplementedError('fromJson is not implemented'); - } - - final DateTime timestamp; - - @JsonKey(includeToJson: true) - final EntryType type; - - Map toJson(); - - String pretty(); - - @override - String toString() => throw UnimplementedError('toString is not implemented'); - - @override - List get props => [timestamp, type]; -} - -enum EntryType { - step, - test, - log; - - static EntryType byName(String name) { - return switch (name) { - 'step' => EntryType.step, - 'test' => EntryType.test, - 'log' => EntryType.log, - _ => throw ArgumentError('Unknown EntryType: $name') - }; - } -} - -/// The number of spaces used for indentation in the pretty print. -/// Used in LogEntry and StepEntry. -const indentation = ' '; diff --git a/packages/patrol_log/lib/src/entry.g.dart b/packages/patrol_log/lib/src/entry.g.dart deleted file mode 100644 index 3657287ec..000000000 --- a/packages/patrol_log/lib/src/entry.g.dart +++ /dev/null @@ -1,75 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'entry.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -LogEntry _$LogEntryFromJson(Map json) => LogEntry( - message: json['message'] as String, - timestamp: json['timestamp'] == null - ? null - : DateTime.parse(json['timestamp'] as String), - ); - -Map _$LogEntryToJson(LogEntry instance) => { - 'timestamp': instance.timestamp.toIso8601String(), - 'type': _$EntryTypeEnumMap[instance.type]!, - 'message': instance.message, - }; - -const _$EntryTypeEnumMap = { - EntryType.step: 'step', - EntryType.test: 'test', - EntryType.log: 'log', -}; - -StepEntry _$StepEntryFromJson(Map json) => StepEntry( - action: json['action'] as String, - status: $enumDecode(_$StepEntryStatusEnumMap, json['status']), - exception: json['exception'] as String?, - data: json['data'] as Map?, - timestamp: json['timestamp'] == null - ? null - : DateTime.parse(json['timestamp'] as String), - ); - -Map _$StepEntryToJson(StepEntry instance) => { - 'timestamp': instance.timestamp.toIso8601String(), - 'type': _$EntryTypeEnumMap[instance.type]!, - 'action': instance.action, - 'status': _$StepEntryStatusEnumMap[instance.status]!, - 'exception': instance.exception, - 'data': instance.data, - }; - -const _$StepEntryStatusEnumMap = { - StepEntryStatus.start: 'start', - StepEntryStatus.success: 'success', - StepEntryStatus.failure: 'failure', -}; - -TestEntry _$TestEntryFromJson(Map json) => TestEntry( - name: json['name'] as String, - status: $enumDecode(_$TestEntryStatusEnumMap, json['status']), - timestamp: json['timestamp'] == null - ? null - : DateTime.parse(json['timestamp'] as String), - error: json['error'] as String?, - ); - -Map _$TestEntryToJson(TestEntry instance) => { - 'timestamp': instance.timestamp.toIso8601String(), - 'type': _$EntryTypeEnumMap[instance.type]!, - 'name': instance.name, - 'status': _$TestEntryStatusEnumMap[instance.status]!, - 'error': instance.error, - }; - -const _$TestEntryStatusEnumMap = { - TestEntryStatus.start: 'start', - TestEntryStatus.success: 'success', - TestEntryStatus.failure: 'failure', - TestEntryStatus.skip: 'skip', -}; diff --git a/packages/patrol_log/lib/src/log_entry.dart b/packages/patrol_log/lib/src/log_entry.dart deleted file mode 100644 index bddfad150..000000000 --- a/packages/patrol_log/lib/src/log_entry.dart +++ /dev/null @@ -1,32 +0,0 @@ -part of 'entry.dart'; - -@JsonSerializable() -class LogEntry extends Entry { - LogEntry({ - required this.message, - DateTime? timestamp, - }) : super( - timestamp: timestamp ?? DateTime.now(), - type: EntryType.log, - ); - - @override - factory LogEntry.fromJson(Map json) => - _$LogEntryFromJson(json); - - final String message; - - @override - Map toJson() => _$LogEntryToJson(this); - - @override - String pretty() { - return '$indentation${Emojis.log} $message'; - } - - @override - String toString() => 'LogEntry(${toJson()})'; - - @override - List get props => [message, timestamp, type]; -} diff --git a/packages/patrol_log/lib/src/step_entry.dart b/packages/patrol_log/lib/src/step_entry.dart deleted file mode 100644 index de86f2f48..000000000 --- a/packages/patrol_log/lib/src/step_entry.dart +++ /dev/null @@ -1,60 +0,0 @@ -part of 'entry.dart'; - -@JsonSerializable() -class StepEntry extends Entry { - StepEntry({ - required this.action, - required this.status, - this.exception, - this.data, - DateTime? timestamp, - }) : super( - timestamp: timestamp ?? DateTime.now(), - type: EntryType.step, - ); - - factory StepEntry.fromJson(Map json) => - _$StepEntryFromJson(json); - - final String action; - final StepEntryStatus status; - final String? exception; - final Map? data; - - @override - Map toJson() => _$StepEntryToJson(this); - - @override - String pretty({int? number}) { - return '$indentation${status.name} ${printIndex(number)} $action'; - } - - /// Returns the index of the step with the correct number of spaces, - /// to format the output. - String printIndex(int? number) { - return switch (number) { - == null => '', - < 10 => ' $number.', - < 100 => ' $number.', - _ => '$number.', - }; - } - - @override - String toString() => 'StepEntry(${toJson()})'; - - @override - List get props => [action, status, exception, data, timestamp, type]; -} - -enum StepEntryStatus { - start, - success, - failure; - - String get name => switch (this) { - StepEntryStatus.start => Emojis.waiting, - StepEntryStatus.success => Emojis.success, - StepEntryStatus.failure => Emojis.failure, - }; -} diff --git a/packages/patrol_log/lib/src/test_entry.dart b/packages/patrol_log/lib/src/test_entry.dart deleted file mode 100644 index 82e08ebd5..000000000 --- a/packages/patrol_log/lib/src/test_entry.dart +++ /dev/null @@ -1,65 +0,0 @@ -part of 'entry.dart'; - -@JsonSerializable() -class TestEntry extends Entry { - TestEntry({ - required this.name, - required this.status, - DateTime? timestamp, - this.error, - }) : super( - timestamp: timestamp ?? DateTime.now(), - type: EntryType.test, - ); - - @override - factory TestEntry.fromJson(Map json) => - _$TestEntryFromJson(json); - - final String name; - final TestEntryStatus status; - final String? error; - - Duration executionTime(DateTime start) => timestamp.difference(start); - - @override - Map toJson() => _$TestEntryToJson(this); - - @override - String pretty() { - if (status == TestEntryStatus.skip) { - return '${status.name} $_testName'; - } - return '${status.name} $_testName ${AnsiCodes.gray}(integration_test/$_filePath.dart)${AnsiCodes.reset}${error != null ? '\n$error' : ''}'; - } - - String get nameWithPath => - '$_testName ${AnsiCodes.gray}(integration_test/$_filePath.dart)${AnsiCodes.reset}'; - - String get _filePath => name.split(' ').first.replaceAll('.', '/'); - String get _testName => name.split(' ').skip(1).join(' '); - - @override - String toString() => 'TestEntry(${toJson()})'; - - @override - List get props => [name, status, error, timestamp, type]; - - /// Returns `true` if the test is finished successfully or with a failure. - bool get isFinished => - status == TestEntryStatus.success || status == TestEntryStatus.failure; -} - -enum TestEntryStatus { - start, - success, - failure, - skip; - - String get name => switch (this) { - TestEntryStatus.start => Emojis.testStart, - TestEntryStatus.success => Emojis.success, - TestEntryStatus.failure => Emojis.failure, - TestEntryStatus.skip => Emojis.skip, - }; -} From cc0482498b3c5f32b4214eb427f301b30e5dce85 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 8 Nov 2024 17:02:55 +0100 Subject: [PATCH 43/47] Remove show-flutter-logs from develop --- packages/patrol_cli/lib/src/commands/develop.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/patrol_cli/lib/src/commands/develop.dart b/packages/patrol_cli/lib/src/commands/develop.dart index 7b79f2c21..6accebf7c 100644 --- a/packages/patrol_cli/lib/src/commands/develop.dart +++ b/packages/patrol_cli/lib/src/commands/develop.dart @@ -54,7 +54,6 @@ class DevelopCommand extends PatrolCommand { usesWaitOption(); usesPortOptions(); usesTagsOption(); - usesShowFlutterLogs(); usesHideTestSteps(); usesUninstallOption(); @@ -246,7 +245,7 @@ class DevelopCommand extends PatrolCommand { uninstall: uninstall, device: device, openDevtools: boolArg('open-devtools'), - showFlutterLogs: boolArg('show-flutter-logs'), + showFlutterLogs: false, hideTestSteps: boolArg('hide-test-steps'), ); From 5b644d14b4602da4febe2a1183a39c1e02c0497d Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 8 Nov 2024 17:04:20 +0100 Subject: [PATCH 44/47] Log patrol test start in patrolTest --- packages/patrol/lib/src/common.dart | 3 +++ packages/patrol/lib/src/native/patrol_app_service.dart | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/patrol/lib/src/common.dart b/packages/patrol/lib/src/common.dart index 2a927a370..1ba923717 100644 --- a/packages/patrol/lib/src/common.dart +++ b/packages/patrol/lib/src/common.dart @@ -136,6 +136,9 @@ void patrolTest( // We don't have to call this line because automator.configure() does the same. // await automator2.configure(); + patrolLog.log( + TestEntry(name: description, status: TestEntryStatus.start), + ); final patrolTester = PatrolIntegrationTester( tester: widgetTester, nativeAutomator: automator, diff --git a/packages/patrol/lib/src/native/patrol_app_service.dart b/packages/patrol/lib/src/native/patrol_app_service.dart index 58dc0e582..090af209d 100644 --- a/packages/patrol/lib/src/native/patrol_app_service.dart +++ b/packages/patrol/lib/src/native/patrol_app_service.dart @@ -149,12 +149,6 @@ class PatrolAppService extends PatrolAppServiceServer { assert(_testExecutionCompleted.isCompleted == false); // patrolTest() always calls this method. - _patrolLog.log( - TestEntry( - name: request.name, - status: TestEntryStatus.start, - ), - ); print('PatrolAppService.runDartTest(${request.name}) called'); _testExecutionRequested.complete(request.name); From c4308d37532ad3cfe21218436416ab43413ed69d Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 8 Nov 2024 18:19:09 +0100 Subject: [PATCH 45/47] Switch to versions from pub --- packages/patrol/pubspec.yaml | 6 ++---- packages/patrol_cli/pubspec.yaml | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/patrol/pubspec.yaml b/packages/patrol/pubspec.yaml index ff79b309e..8abcc297d 100644 --- a/packages/patrol/pubspec.yaml +++ b/packages/patrol/pubspec.yaml @@ -26,10 +26,8 @@ dependencies: http: '^1.1.0' json_annotation: ^4.8.1 meta: ^1.10.0 - patrol_finders: - path: ../patrol_finders - patrol_log: - path: ../patrol_log + patrol_finders: ^2.2.0 + patrol_log: ^0.0.1+1 shelf: ^1.4.1 test_api: '^0.7.0' diff --git a/packages/patrol_cli/pubspec.yaml b/packages/patrol_cli/pubspec.yaml index 3822d77e5..db40e7730 100644 --- a/packages/patrol_cli/pubspec.yaml +++ b/packages/patrol_cli/pubspec.yaml @@ -30,8 +30,7 @@ dependencies: mason_logger: ^0.2.10 meta: ^1.10.0 path: ^1.8.3 - patrol_log: - path: ../patrol_log + patrol_log: ^0.0.1+1 platform: ^3.1.3 process: ^5.0.1 pub_updater: ^0.4.0 From 60139d208e2ad779a759e61000e6918d6834aac2 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 8 Nov 2024 18:23:14 +0100 Subject: [PATCH 46/47] Remove unneeded conditions --- packages/patrol_log/lib/src/patrol_log_reader.dart | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/patrol_log/lib/src/patrol_log_reader.dart b/packages/patrol_log/lib/src/patrol_log_reader.dart index d8f39d1e4..450eff2eb 100644 --- a/packages/patrol_log/lib/src/patrol_log_reader.dart +++ b/packages/patrol_log/lib/src/patrol_log_reader.dart @@ -111,20 +111,6 @@ class PatrolLogReader { } else { _controller.add(entry); } - } else if (showFlutterLogs && line.contains('Runner: (Flutter) flutter:')) { - final regExp = RegExp(r'Runner: \(Flutter\) (.*)'); - final match = regExp.firstMatch(line); - if (match != null) { - final matchedText = match.group(1)!; - log(matchedText); - } - } else if (showFlutterLogs && line.contains('I flutter :')) { - final regExp = RegExp('I (.*)'); - final match = regExp.firstMatch(line); - if (match != null) { - final matchedText = match.group(1)!; - log(matchedText); - } } } From 944459841e56ba20bbe14f4786dd8cef1dbaf7c1 Mon Sep 17 00:00:00 2001 From: pdenert Date: Fri, 8 Nov 2024 18:38:10 +0100 Subject: [PATCH 47/47] Update changelogs and versions --- packages/patrol/CHANGELOG.md | 3 ++- packages/patrol/pubspec.yaml | 2 +- packages/patrol_cli/CHANGELOG.md | 4 ++++ packages/patrol_cli/lib/src/base/constants.dart | 2 +- packages/patrol_cli/pubspec.yaml | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/patrol/CHANGELOG.md b/packages/patrol/CHANGELOG.md index 904e07a9f..10b1bd394 100644 --- a/packages/patrol/CHANGELOG.md +++ b/packages/patrol/CHANGELOG.md @@ -1,5 +1,6 @@ -## Unreleased +## 3.13.0-dev.1 +- Add support for the patrol_log package. (#2387) - Fix tapping on notification on iOS 18. (#2394) ## 3.12.0 diff --git a/packages/patrol/pubspec.yaml b/packages/patrol/pubspec.yaml index 8abcc297d..086de991f 100644 --- a/packages/patrol/pubspec.yaml +++ b/packages/patrol/pubspec.yaml @@ -2,7 +2,7 @@ name: patrol description: > Powerful Flutter-native UI testing framework overcoming limitations of existing Flutter testing tools. Ready for action! -version: 3.12.0 +version: 3.13.0-dev.1 homepage: https://patrol.leancode.co repository: https://github.com/leancodepl/patrol/tree/master/packages/patrol issue_tracker: https://github.com/leancodepl/patrol/issues diff --git a/packages/patrol_cli/CHANGELOG.md b/packages/patrol_cli/CHANGELOG.md index b94636c57..42fcf70a4 100644 --- a/packages/patrol_cli/CHANGELOG.md +++ b/packages/patrol_cli/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.4.0-dev.1 + +- Add support for the patrol_log package. (#2387) + ## 3.3.0 - Add `clear-permissions` flag on ios commands. (#2367) diff --git a/packages/patrol_cli/lib/src/base/constants.dart b/packages/patrol_cli/lib/src/base/constants.dart index 34fa1e61e..267c66140 100644 --- a/packages/patrol_cli/lib/src/base/constants.dart +++ b/packages/patrol_cli/lib/src/base/constants.dart @@ -1,3 +1,3 @@ /// Version of Patrol CLI. Must be kept in sync with pubspec.yaml. /// If you update this, make sure that compatibility-table.mdx is updated (if needed) -const version = '3.3.0'; +const version = '3.4.0-dev.1'; diff --git a/packages/patrol_cli/pubspec.yaml b/packages/patrol_cli/pubspec.yaml index db40e7730..7e1ec8d33 100644 --- a/packages/patrol_cli/pubspec.yaml +++ b/packages/patrol_cli/pubspec.yaml @@ -1,7 +1,7 @@ name: patrol_cli description: > Command-line tool for Patrol, a powerful Flutter-native UI testing framework. -version: 3.3.0 # Must be kept in sync with constants.dart +version: 3.4.0-dev.1 # Must be kept in sync with constants.dart homepage: https://patrol.leancode.co repository: https://github.com/leancodepl/patrol/tree/master/packages/patrol_cli issue_tracker: https://github.com/leancodepl/patrol/issues?q=is%3Aopen+is%3Aissue+label%3A%22package%3A+patrol_cli%22