From 56dca3b592496961bf925e2a945ebadff0ed41da Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Tue, 10 Oct 2023 23:55:57 +0200 Subject: [PATCH] implement PatrolAppService.addSetUpAll and remembering setUpAlls Problem: not all tests are "falling through" during the initial run --- .../pl/leancode/patrol/PatrolJUnitRunner.java | 2 +- packages/patrol/lib/src/common.dart | 33 +++++++++--- packages/patrol/lib/src/global_state.dart | 4 +- .../lib/src/native/patrol_app_service.dart | 54 ++++++++++--------- packages/patrol_cli/lib/src/test_bundler.dart | 5 +- 5 files changed, 62 insertions(+), 36 deletions(-) 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 6485ff84c..313435362 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 @@ -132,7 +132,7 @@ public Object[] listDartTests() { * Throws AssertionError if the test fails. */ public RunDartTestResponse runDartTest(String name) { - final String TAG = "PatrolJUnitRunner.runDartTest(" + name + "): "; + final String TAG = "PatrolJUnitRunner.runDartTest(\"" + name + "\"): "; try { Logger.INSTANCE.i(TAG + "Requested execution"); diff --git a/packages/patrol/lib/src/common.dart b/packages/patrol/lib/src/common.dart index 21d100ce0..65c62e0f8 100644 --- a/packages/patrol/lib/src/common.dart +++ b/packages/patrol/lib/src/common.dart @@ -49,19 +49,35 @@ void patrolTearDown(Future Function() body) { } /// A modification of [setUpAll] that works with Patrol's native automation. +/// +/// It keeps track of calls made to setUpAll. void patrolSetUpAll(Future Function() body) { setUpAll(() async { final patrolAppService = PatrolBinding.instance.patrolAppService; - final parentGroupsName = global_state.currentGroupFullName; - patrolAppService.addSetUpAll(parentGroupsName); + final setUpAllName = patrolAppService.addSetUpAll(parentGroupsName); + + if (await global_state.isInitialRun) { + // Skip calling body if we're in test discovery phase + print( + "PATROL_DEBUG: Skipping setUpAll '$setUpAllName' because it's test discovery phase", + ); + return; + } + + // TODO: Skip calling body if it this setUpAll was already executed + + final requestedTest = await patrolAppService.testExecutionRequested; + + // Skip calling if parentGroupName is not a substring of requestedTestName + if (!requestedTest.startsWith(parentGroupsName)) { + // This is not exhaustive. + return; + } - // final requestedToExecute = await PatrolBinding.instance.patrolAppService - // .waitForExecutionRequest(currentSetUpAllIndex); + await body(); - // if (requestedToExecute) { - // await body(); - // } + // TODO: Mark this setUpAll as executed }); } @@ -142,6 +158,9 @@ void patrolTest( (widgetTester) async { if (!constants.hotRestartEnabled) { if (await global_state.isInitialRun) { + print( + 'PATROL_DEBUG: falling through test "${global_state.currentTestFullName}"', + ); // Fall through tests during the initial run that discovers tests. // // This is required to be able to find all setUpAll callbacks. diff --git a/packages/patrol/lib/src/global_state.dart b/packages/patrol/lib/src/global_state.dart index 81b922c53..ca4e33987 100644 --- a/packages/patrol/lib/src/global_state.dart +++ b/packages/patrol/lib/src/global_state.dart @@ -1,5 +1,5 @@ -// ignore: implementation_imports import 'package:flutter/services.dart'; +// ignore: implementation_imports import 'package:test_api/src/backend/invoker.dart'; /// Maximum test case length for ATO, after transformations. @@ -46,6 +46,8 @@ bool get isCurrentTestPassing { const _channel = MethodChannel('pl.leancode.patrol/main'); +/// Returns whether this is the first run of the app under test during which +/// test discovery happens. Future get isInitialRun async { return await _channel.invokeMethod('isInitialRun') as bool; } diff --git a/packages/patrol/lib/src/native/patrol_app_service.dart b/packages/patrol/lib/src/native/patrol_app_service.dart index 68a62f2f4..4fbaac2ff 100644 --- a/packages/patrol/lib/src/native/patrol_app_service.dart +++ b/packages/patrol/lib/src/native/patrol_app_service.dart @@ -62,6 +62,9 @@ class PatrolAppService extends PatrolAppServiceServer { /// setUpAlls, unlike setUps, aren't executed in the [LiveTest] context. /// Because of this, we can't depend on the [LiveTest]'s name, so we identify /// them by the parent group ID instead. + /// + /// This is a list of groups containing setUpAllCallbacks with an index + /// appended. final List setUpAlls = []; /// A completer that completes with the name of the Dart test file that was @@ -83,28 +86,26 @@ class PatrolAppService extends PatrolAppServiceServer { } /// Adds a setUpAll callback to the list of all setUpAll callbacks. - void addSetUpAll(String group) { - // Not very optimal, but good enough for now. - - setUpAlls.add('$group + ${group.hashCode}'); - - // for (final setUpAll in setUpAlls) { - // final parts = setUpAll.split(' '); - // final groupName = parts.sublist(0, parts.length - 1).join(' '); - - // if (groupName == group) { - - // } + /// + /// Returns the name under which this setUpAll callback was added. + String addSetUpAll(String group) { + // Not optimal, but good enough for now. + + // Go over all groups, checking if the group is already in the list. + var groupIndex = 0; + for (final setUpAll in setUpAlls) { + final parts = setUpAll.split(' '); + final groupName = parts.sublist(0, parts.length - 1).join(' '); + + if (groupName == group) { + groupIndex++; + } + } - // final index = parts.last; - // if (setUpAll == group) { - // return; - // } - // } + final name = '$group $groupIndex'; - // add an index at the end of setUpAlls - // parent testA 1 - // parent testA 2 + setUpAlls.add(name); + return name; } /// Marks [dartFileName] as completed with the given [passed] status. @@ -167,7 +168,6 @@ class PatrolAppService extends PatrolAppServiceServer { @override Future listDartTests() async { print('PatrolAppService.listDartTests() called'); - print('PATROL_DEBUG: setUpAlls: $setUpAlls'); return ListDartTestsResponse(group: topLevelDartTestGroup); } @@ -175,7 +175,6 @@ class PatrolAppService extends PatrolAppServiceServer { @override Future runDartTest(RunDartTestRequest request) async { assert(_testExecutionCompleted.isCompleted == false); - // patrolTest() always calls this method. print('PatrolAppService.runDartTest(${request.name}) called'); _testExecutionRequested.complete(request.name); @@ -190,8 +189,13 @@ class PatrolAppService extends PatrolAppServiceServer { } @override - Future listDartLifecycleCallbacks() { - // TODO: implement listDartLifecycleCallbacks - throw UnimplementedError(); + Future + listDartLifecycleCallbacks() async { + print('PatrolAppService.listDartLifecycleCallbacks() called'); + + return ListDartLifecycleCallbacksResponse( + setUpAlls: setUpAlls, + tearDownAlls: [], + ); } } diff --git a/packages/patrol_cli/lib/src/test_bundler.dart b/packages/patrol_cli/lib/src/test_bundler.dart index 36906cb0d..d14716c45 100644 --- a/packages/patrol_cli/lib/src/test_bundler.dart +++ b/packages/patrol_cli/lib/src/test_bundler.dart @@ -72,6 +72,7 @@ Future main() async { await nativeAutomator.initialize(); final binding = PatrolBinding.ensureInitialized(); final testExplorationCompleter = Completer(); + late final PatrolAppService appService; // A special test to expore the hierarchy of groups and tests. This is a hack // around https://github.com/dart-lang/test/issues/1998. @@ -94,11 +95,11 @@ ${generateGroupsCode(testFilePaths).split('\n').map((e) => ' $e').join('\n')} // An additional callback to discover setUpAlls. tearDownAll(() { - print('PATROL_DEBUG: calling tearDownAll to print setUpAlls'); + print('PATROL_DEBUG: tearDownAll(): setUpAlls: \${appService.setUpAlls}'); }); final dartTestGroup = await testExplorationCompleter.future; - final appService = PatrolAppService(topLevelDartTestGroup: dartTestGroup); + appService = PatrolAppService(topLevelDartTestGroup: dartTestGroup); binding.patrolAppService = appService; await runAppService(appService);