dartTestCases = ContractsExtensionsKt.listTestsFlat(dartTestGroup, "");
@@ -158,6 +161,15 @@ public Object[] listDartTests() {
}
}
+ private void handleLifecycleCallbacks() {
+ if (isInitialRun()) {
+ Object[] lifecycleCallbacks = listLifecycleCallbacks();
+ saveLifecycleCallbacks(lifecycleCallbacks);
+ } else {
+ setLifecycleCallbacksState();
+ }
+ }
+
public Object[] listLifecycleCallbacks() {
final String TAG = "PatrolJUnitRunner.listLifecycleCallbacks(): ";
@@ -222,7 +234,7 @@ static String convertStreamToString(InputStream inputStream) {
/**
* Sets the state of lifecycle callbacks in the app.
- *
+ *
* This is required because the app is launched in a new process for each test.
*/
public void setLifecycleCallbacksState() {
diff --git a/packages/patrol/example/android/app/src/androidTest/java/pl/leancode/patrol/example/MainActivityTest.java b/packages/patrol/example/android/app/src/androidTest/java/pl/leancode/patrol/example/MainActivityTest.java
index a419edebc..675e46464 100644
--- a/packages/patrol/example/android/app/src/androidTest/java/pl/leancode/patrol/example/MainActivityTest.java
+++ b/packages/patrol/example/android/app/src/androidTest/java/pl/leancode/patrol/example/MainActivityTest.java
@@ -14,16 +14,7 @@ public static Object[] testCases() {
PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation();
instrumentation.setUp(MainActivity.class);
instrumentation.waitForPatrolAppService();
- Object[] dartTests = instrumentation.listDartTests();
-
- if (instrumentation.isInitialRun()) {
- Object[] lifecycleCallbacks = instrumentation.listLifecycleCallbacks();
- instrumentation.saveLifecycleCallbacks(lifecycleCallbacks);
- } else {
- instrumentation.setLifecycleCallbacksState();
- }
-
- return dartTests;
+ return instrumentation.listDartTests();
}
public MainActivityTest(String dartTestName) {
From 68b969b291b2b6be3b57d4afe94ea5471299c866 Mon Sep 17 00:00:00 2001
From: Albert Wolszon
Date: Tue, 17 Oct 2023 17:00:39 +0200
Subject: [PATCH 48/77] Provide redirects for link changes from 2138733
---
docs/patrol/finders/advanced.mdx | 3 +++
docs/patrol/finders/finders-setup.mdx | 3 +++
docs/patrol/finders/overview.mdx | 3 +++
docs/patrol/finders/usage.mdx | 3 +++
docs/patrol/native/advanced.mdx | 3 +++
docs/patrol/native/feature-parity.mdx | 3 +++
docs/patrol/native/overview.mdx | 3 +++
docs/patrol/native/usage.mdx | 3 +++
8 files changed, 24 insertions(+)
create mode 100644 docs/patrol/finders/advanced.mdx
create mode 100644 docs/patrol/finders/finders-setup.mdx
create mode 100644 docs/patrol/finders/overview.mdx
create mode 100644 docs/patrol/finders/usage.mdx
create mode 100644 docs/patrol/native/advanced.mdx
create mode 100644 docs/patrol/native/feature-parity.mdx
create mode 100644 docs/patrol/native/overview.mdx
create mode 100644 docs/patrol/native/usage.mdx
diff --git a/docs/patrol/finders/advanced.mdx b/docs/patrol/finders/advanced.mdx
new file mode 100644
index 000000000..9a8aba064
--- /dev/null
+++ b/docs/patrol/finders/advanced.mdx
@@ -0,0 +1,3 @@
+---
+redirect: /finders/advanced
+---
diff --git a/docs/patrol/finders/finders-setup.mdx b/docs/patrol/finders/finders-setup.mdx
new file mode 100644
index 000000000..ee38c9573
--- /dev/null
+++ b/docs/patrol/finders/finders-setup.mdx
@@ -0,0 +1,3 @@
+---
+redirect: /finders/finders-setup
+---
diff --git a/docs/patrol/finders/overview.mdx b/docs/patrol/finders/overview.mdx
new file mode 100644
index 000000000..ff33c72bc
--- /dev/null
+++ b/docs/patrol/finders/overview.mdx
@@ -0,0 +1,3 @@
+---
+redirect: /finders/overview
+---
diff --git a/docs/patrol/finders/usage.mdx b/docs/patrol/finders/usage.mdx
new file mode 100644
index 000000000..df9cd5211
--- /dev/null
+++ b/docs/patrol/finders/usage.mdx
@@ -0,0 +1,3 @@
+---
+redirect: /finders/usage
+---
diff --git a/docs/patrol/native/advanced.mdx b/docs/patrol/native/advanced.mdx
new file mode 100644
index 000000000..f56697249
--- /dev/null
+++ b/docs/patrol/native/advanced.mdx
@@ -0,0 +1,3 @@
+---
+redirect: /native/advanced
+---
diff --git a/docs/patrol/native/feature-parity.mdx b/docs/patrol/native/feature-parity.mdx
new file mode 100644
index 000000000..ddcb4c0d4
--- /dev/null
+++ b/docs/patrol/native/feature-parity.mdx
@@ -0,0 +1,3 @@
+---
+redirect: /native/feature-parity
+---
diff --git a/docs/patrol/native/overview.mdx b/docs/patrol/native/overview.mdx
new file mode 100644
index 000000000..1d9a5ad43
--- /dev/null
+++ b/docs/patrol/native/overview.mdx
@@ -0,0 +1,3 @@
+---
+redirect: /native/overview
+---
diff --git a/docs/patrol/native/usage.mdx b/docs/patrol/native/usage.mdx
new file mode 100644
index 000000000..ed1b1adaf
--- /dev/null
+++ b/docs/patrol/native/usage.mdx
@@ -0,0 +1,3 @@
+---
+redirect: /native/usage
+---
From 54b2715addbee14358bcc4a5ab3c47ff57fa2921 Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Tue, 17 Oct 2023 20:23:58 +0200
Subject: [PATCH 49/77] Revert "Provide redirects for link changes from
2138733"
---
docs/patrol/finders/advanced.mdx | 3 ---
docs/patrol/finders/finders-setup.mdx | 3 ---
docs/patrol/finders/overview.mdx | 3 ---
docs/patrol/finders/usage.mdx | 3 ---
docs/patrol/native/advanced.mdx | 3 ---
docs/patrol/native/feature-parity.mdx | 3 ---
docs/patrol/native/overview.mdx | 3 ---
docs/patrol/native/usage.mdx | 3 ---
8 files changed, 24 deletions(-)
delete mode 100644 docs/patrol/finders/advanced.mdx
delete mode 100644 docs/patrol/finders/finders-setup.mdx
delete mode 100644 docs/patrol/finders/overview.mdx
delete mode 100644 docs/patrol/finders/usage.mdx
delete mode 100644 docs/patrol/native/advanced.mdx
delete mode 100644 docs/patrol/native/feature-parity.mdx
delete mode 100644 docs/patrol/native/overview.mdx
delete mode 100644 docs/patrol/native/usage.mdx
diff --git a/docs/patrol/finders/advanced.mdx b/docs/patrol/finders/advanced.mdx
deleted file mode 100644
index 9a8aba064..000000000
--- a/docs/patrol/finders/advanced.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
----
-redirect: /finders/advanced
----
diff --git a/docs/patrol/finders/finders-setup.mdx b/docs/patrol/finders/finders-setup.mdx
deleted file mode 100644
index ee38c9573..000000000
--- a/docs/patrol/finders/finders-setup.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
----
-redirect: /finders/finders-setup
----
diff --git a/docs/patrol/finders/overview.mdx b/docs/patrol/finders/overview.mdx
deleted file mode 100644
index ff33c72bc..000000000
--- a/docs/patrol/finders/overview.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
----
-redirect: /finders/overview
----
diff --git a/docs/patrol/finders/usage.mdx b/docs/patrol/finders/usage.mdx
deleted file mode 100644
index df9cd5211..000000000
--- a/docs/patrol/finders/usage.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
----
-redirect: /finders/usage
----
diff --git a/docs/patrol/native/advanced.mdx b/docs/patrol/native/advanced.mdx
deleted file mode 100644
index f56697249..000000000
--- a/docs/patrol/native/advanced.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
----
-redirect: /native/advanced
----
diff --git a/docs/patrol/native/feature-parity.mdx b/docs/patrol/native/feature-parity.mdx
deleted file mode 100644
index ddcb4c0d4..000000000
--- a/docs/patrol/native/feature-parity.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
----
-redirect: /native/feature-parity
----
diff --git a/docs/patrol/native/overview.mdx b/docs/patrol/native/overview.mdx
deleted file mode 100644
index 1d9a5ad43..000000000
--- a/docs/patrol/native/overview.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
----
-redirect: /native/overview
----
diff --git a/docs/patrol/native/usage.mdx b/docs/patrol/native/usage.mdx
deleted file mode 100644
index ed1b1adaf..000000000
--- a/docs/patrol/native/usage.mdx
+++ /dev/null
@@ -1,3 +0,0 @@
----
-redirect: /native/usage
----
From ddcb70fa753e47f45147122d74e2b2219014b45f Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Tue, 17 Oct 2023 22:02:05 +0200
Subject: [PATCH 50/77] update guide
---
dev_docs/GUIDE.md | 40 +++++++++++++++++++++++++++++++++-------
1 file changed, 33 insertions(+), 7 deletions(-)
diff --git a/dev_docs/GUIDE.md b/dev_docs/GUIDE.md
index f021c39e6..1e1cc5d9b 100644
--- a/dev_docs/GUIDE.md
+++ b/dev_docs/GUIDE.md
@@ -1,15 +1,31 @@
# Working on the test bundling feature
+_Test bundling_, also known as _native automation_, is a core feature of Patrol.
+It bridges the native world of tests on Android and iOS with the Flutter/Dart
+world of tests.
+
+It lives in the [patrol package](../packages/patrol).
+
+To learn more about test bundling, [read this article][test_bundling_article].
+
+This document is a collection of tips and tricks to make it easier to work on
+test bundling-related code.
+
+### Tools
+
`adb logcat` is your friend. Spice it up with `-v color`. If you need something
more powerful, check out [`purr`](https://github.com/google/purr).
+### Show Dart-side logs only
+
+Search for `flutter :`.
+
### Find out when a test starts
Search for `TestRunner: started`.
```
09-21 12:24:09.223 23387 23406 I TestRunner: started: runDartTest[callbacks_test testA](pl.leancode.patrol.example.MainActivityTest)
-
```
### Find out when a test ends
@@ -18,10 +34,20 @@ Search for `TestRunner: finished`.
### I made some changes to test bundling code that result in a deadlock
-This is normal when editing this code. It's a mine field of shared global
-mutable state and things happening in parallel.
+This can often happen when editing test bundling code. Because of various
+limitations of the `test` package, which Patrol has to base on, test bundling
+code is full of shared global mutable state and unobvious things happening in
+parallel.
+
+When trying to find the cause of a deadlock:
+
+- search for `await`s in custom functions provided by Patrol (e.g.
+ `patrolTest()` and `patrolSetUpAll()`) and global lifecycle callbacks
+ registered by the generated Dart test bundle or PatrolBinding (e.g.
+ `tearDown()`s)
+- Use `print`s amply to pinpint where the code is stuck.
+
+In the future, we should think about how to refactor this code to be more
+maintainable and simpler.
-Look for `await`s in custom functions provided by Patrol (e.g. `patrolTest()`
-and `patrolSetUpAll()`) and global lifecycle callbacks registered by the
-generated Dart test bundle or PatrolBinding (e.g. `tearDown()`s). Use `print`s
-amply to pinpint where the code is stuck.
+[test_bundling_article]: https://leancode.co/blog/patrol-2-0-improved-flutter-ui-testing
From 36ab2096b97a27fe35788d1c0710942bd07bbade Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Tue, 17 Oct 2023 22:05:48 +0200
Subject: [PATCH 51/77] PatrolJUnitRunner: update comment
---
.../main/kotlin/pl/leancode/patrol/PatrolJUnitRunner.java | 5 +++--
1 file changed, 3 insertions(+), 2 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 b36299654..a085a65cd 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
@@ -141,8 +141,9 @@ public void waitForPatrolAppService() {
public Object[] listDartTests() {
final String TAG = "PatrolJUnitRunner.listDartTests(): ";
- // This call is here for backward compatibility.
- // It should be in MainActivityTest.java.
+ // This call should be in MainActivityTest.java, but that would require
+ // users to change that file in their projects, thus breaking backward
+ // compatibility.
handleLifecycleCallbacks();
try {
From 33caf18a876330020350cc7287aa14567b0a6b08 Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Wed, 18 Oct 2023 17:46:47 +0200
Subject: [PATCH 52/77] fix generated test_bundle referencing Completers with
changede names
---
packages/patrol_cli/lib/src/test_bundler.dart | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/packages/patrol_cli/lib/src/test_bundler.dart b/packages/patrol_cli/lib/src/test_bundler.dart
index 20df3e4c9..a38afdffc 100644
--- a/packages/patrol_cli/lib/src/test_bundler.dart
+++ b/packages/patrol_cli/lib/src/test_bundler.dart
@@ -29,7 +29,6 @@ import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol/patrol.dart';
import 'package:patrol/src/global_state.dart' as global_state;
-import 'package:patrol/src/logs.dart';
import 'package:patrol/src/native/contracts/contracts.dart';
import 'package:test_api/src/backend/invoker.dart';
@@ -118,7 +117,7 @@ ${generateGroupsCode(testFilePaths).split('\n').map((e) => ' $e').join('\n')}
// about Dart tests.
await nativeAutomator.markPatrolAppServiceReady();
- await appService.testExecutionCompleted;
+ await appService.didCompleteTestExecution;
}
''';
From 16f0b8f14029edef83542aa5da1e963b5052f48a Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Wed, 18 Oct 2023 17:55:35 +0200
Subject: [PATCH 53/77] make Hot Restart work again
---
packages/patrol_cli/lib/src/test_bundler.dart | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/packages/patrol_cli/lib/src/test_bundler.dart b/packages/patrol_cli/lib/src/test_bundler.dart
index a38afdffc..656a15e86 100644
--- a/packages/patrol_cli/lib/src/test_bundler.dart
+++ b/packages/patrol_cli/lib/src/test_bundler.dart
@@ -153,7 +153,11 @@ ${generateImports([testFilePath])}
Future main() async {
final nativeAutomator = NativeAutomator(config: NativeAutomatorConfig());
await nativeAutomator.initialize();
- PatrolBinding.ensureInitialized();
+
+ final appService = PatrolAppService();
+ await runAppService(appService);
+
+ PatrolBinding.ensureInitialized(appService, nativeAutomator);
// START: GENERATED TEST GROUPS
${generateGroupsCode([testFilePath]).split('\n').map((e) => ' $e').join('\n')}
From cf42a719ef00d79333e0a01dcc43674875ab5752 Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Wed, 18 Oct 2023 18:27:48 +0200
Subject: [PATCH 54/77] fix setUpAll and tearDownAll - don't block when Hot
Restart is enabled
---
packages/patrol/lib/src/common.dart | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/packages/patrol/lib/src/common.dart b/packages/patrol/lib/src/common.dart
index 775a4fba9..9456acaed 100644
--- a/packages/patrol/lib/src/common.dart
+++ b/packages/patrol/lib/src/common.dart
@@ -23,6 +23,16 @@ typedef PatrolTesterCallback = Future Function(PatrolIntegrationTester $);
/// A modification of [setUp] that works with Patrol's native automation.
void patrolSetUp(Future Function() body) {
setUp(() async {
+ if (global_state.hotRestartEnabled) {
+ await body();
+ return;
+ }
+
+ if (await global_state.isInitialRun) {
+ // Skip calling body if we're in test discovery phase
+ return;
+ }
+
final currentTest = global_state.currentTestFullName;
final requestedToExecute = await PatrolBinding.instance.patrolAppService
@@ -37,6 +47,16 @@ void patrolSetUp(Future Function() body) {
/// A modification of [tearDown] that works with Patrol's native automation.
void patrolTearDown(Future Function() body) {
tearDown(() async {
+ if (global_state.hotRestartEnabled) {
+ await body();
+ return;
+ }
+
+ if (await global_state.isInitialRun) {
+ // Skip calling body if we're in test discovery phase
+ return;
+ }
+
final currentTest = global_state.currentTestFullName;
final requestedToExecute = await PatrolBinding.instance.patrolAppService
From 5e730371db25a62f010f16d4c5c82336b930f13a Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Thu, 19 Oct 2023 10:26:46 +0200
Subject: [PATCH 55/77] regenerate contracts
---
packages/patrol/ios/Classes/AutomatorServer/Contracts.swift | 2 +-
.../ios/Classes/AutomatorServer/PatrolAppServiceClient.swift | 4 ++--
packages/patrol/lib/src/native/contracts/contracts.dart | 2 +-
packages/patrol/lib/src/native/contracts/contracts.g.dart | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/packages/patrol/ios/Classes/AutomatorServer/Contracts.swift b/packages/patrol/ios/Classes/AutomatorServer/Contracts.swift
index a413af830..543adcdc2 100644
--- a/packages/patrol/ios/Classes/AutomatorServer/Contracts.swift
+++ b/packages/patrol/ios/Classes/AutomatorServer/Contracts.swift
@@ -61,7 +61,7 @@ struct RunDartTestResponse: Codable {
}
struct SetLifecycleCallbacksStateRequest: Codable {
- var state: Map
+ var state: [String: Bool]
}
struct ConfigureRequest: Codable {
diff --git a/packages/patrol/ios/Classes/AutomatorServer/PatrolAppServiceClient.swift b/packages/patrol/ios/Classes/AutomatorServer/PatrolAppServiceClient.swift
index f368fc8fa..66ae8b542 100644
--- a/packages/patrol/ios/Classes/AutomatorServer/PatrolAppServiceClient.swift
+++ b/packages/patrol/ios/Classes/AutomatorServer/PatrolAppServiceClient.swift
@@ -21,13 +21,13 @@ class PatrolAppServiceClient {
}
func listDartLifecycleCallbacks(completion: @escaping (Result) -> Void) {
- performRequest(requestName: "listDartLifecycleCallbacks", completion: completion)
+ performRequestWithResult(requestName: "listDartLifecycleCallbacks", completion: completion)
}
func setLifecycleCallbacksState(request: SetLifecycleCallbacksStateRequest, completion: @escaping (Result) -> Void) {
do {
let body = try JSONEncoder().encode(request)
- performRequest(requestName: "setLifecycleCallbacksState", body: body, completion: completion)
+ performRequestWithResult(requestName: "setLifecycleCallbacksState", body: body, completion: completion)
} catch let err {
completion(.failure(err))
}
diff --git a/packages/patrol/lib/src/native/contracts/contracts.dart b/packages/patrol/lib/src/native/contracts/contracts.dart
index 9c4e0101f..3c90d4c5e 100644
--- a/packages/patrol/lib/src/native/contracts/contracts.dart
+++ b/packages/patrol/lib/src/native/contracts/contracts.dart
@@ -179,7 +179,7 @@ class SetLifecycleCallbacksStateRequest with EquatableMixin {
Map json) =>
_$SetLifecycleCallbacksStateRequestFromJson(json);
- final Map state;
+ final Map state;
Map toJson() =>
_$SetLifecycleCallbacksStateRequestToJson(this);
diff --git a/packages/patrol/lib/src/native/contracts/contracts.g.dart b/packages/patrol/lib/src/native/contracts/contracts.g.dart
index 4a5bad73e..7fec943ac 100644
--- a/packages/patrol/lib/src/native/contracts/contracts.g.dart
+++ b/packages/patrol/lib/src/native/contracts/contracts.g.dart
@@ -92,7 +92,7 @@ const _$RunDartTestResponseResultEnumMap = {
SetLifecycleCallbacksStateRequest _$SetLifecycleCallbacksStateRequestFromJson(
Map json) =>
SetLifecycleCallbacksStateRequest(
- state: json['state'] as Map,
+ state: Map.from(json['state'] as Map),
);
Map _$SetLifecycleCallbacksStateRequestToJson(
From 102f95b46820eea604a03604b43df661d29cccc5 Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Thu, 19 Oct 2023 10:50:44 +0200
Subject: [PATCH 56/77] copy PATROL_INTEGRATION_TEST_IOS_RUNNER macro to
RunnerUITests
---
.../example/ios/RunnerUITests/RunnerUITests.m | 102 ++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m b/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m
index bfc5dbda2..b09632d8d 100644
--- a/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m
+++ b/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m
@@ -2,4 +2,106 @@
@import patrol;
@import ObjectiveC.runtime;
+#define PATROL_INTEGRATION_TEST_IOS_RUNNER(__test_class)
+ @interface __test_class : XCTestCase
+ @end
+
+@implementation __test_class
+
++(NSArray *)testInvocations {
+ /* Start native automation server */
+ PatrolServer *server = [[PatrolServer alloc] init];
+
+ NSError *_Nullable __autoreleasing *_Nullable err = NULL;
+ [server startAndReturnError:err];
+ if (err != NULL) {
+ NSLog(@"patrolServer.start(): failed, err: %@", err);
+ }
+
+ /* Create a client for PatrolAppService, which lets us list and run Dart tests */
+ __block ObjCPatrolAppServiceClient *appServiceClient = [[ObjCPatrolAppServiceClient alloc] init];
+
+ /* Allow the Local Network permission required by Dart Observatory */
+ XCUIApplication *springboard = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.springboard"];
+ XCUIElementQuery *systemAlerts = springboard.alerts;
+ if (systemAlerts.buttons[@"Allow"].exists) {
+ [systemAlerts.buttons[@"Allow"] tap];
+ }
+
+ /* Run the app for the first time to gather Dart tests */
+ [[[XCUIApplication alloc] init] launch];
+
+ /* Spin the runloop waiting until the app reports that it is ready to report Dart tests */
+ while (!server.appReady) {
+ [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
+ }
+
+ __block NSArray *dartTests = NULL;
+ [appServiceClient listDartTestsWithCompletion:^(NSArray *_Nullable tests, NSError *_Nullable err) {
+ if (err != NULL) {
+ NSLog(@"listDartTests(): failed, err: %@", err);
+ }
+
+ dartTests = tests;
+ }];
+
+ /* Spin the runloop waiting until the app reports the Dart tests it contains */
+ while (!dartTests) {
+ [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
+ }
+
+ NSLog(@"Got %lu Dart tests: %@", dartTests.count, dartTests);
+
+ NSMutableArray *invocations = [[NSMutableArray alloc] init];
+
+ /**
+ * Once Dart tests are available, we:
+ *
+ * Step 1. Dynamically add test case methods that request execution of an individual Dart test file.
+ *
+ * Step 2. Create invocations to the generated methods and return them
+ */
+
+ for (NSString * dartTest in dartTests) {
+ /* Step 1 - dynamically create test cases */
+
+ IMP implementation = imp_implementationWithBlock(^(id _self) {
+ [[[XCUIApplication alloc] init] launch];
+
+ __block ObjCRunDartTestResponse *response = NULL;
+ [appServiceClient runDartTestWithName:dartTest
+ completion:^(ObjCRunDartTestResponse *_Nullable r, NSError *_Nullable err) {
+ if (err != NULL) {
+ NSLog(@"runDartTestWithName(%@): failed, err: %@", dartTest, err);
+ }
+
+ response = r;
+ }];
+
+ /* Wait until Dart test finishes */
+ while (!response) {
+ [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
+ }
+
+ XCTAssertTrue(response.passed, @"%@", response.details);
+ });
+ NSString *selectorName = [PatrolUtils createMethodNameFromPatrolGeneratedGroup:dartTest];
+ SEL selector = NSSelectorFromString(selectorName);
+ class_addMethod(self, selector, implementation, "v@:");
+
+ /* Step 2 – create invocations to the dynamically created methods */
+ NSMethodSignature *signature = [self instanceMethodSignatureForSelector:selector];
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
+ invocation.selector = selector;
+
+ NSLog(@"RunnerUITests.testInvocations(): selectorName = %@, signature: %@", selectorName, signature);
+
+ [invocations addObject:invocation];
+ }
+
+ return invocations;
+}
+
+@end
+
PATROL_INTEGRATION_TEST_IOS_RUNNER(RunnerUITests)
From bc8a7735424ef5fece6eef864d0ed4f7f7387c47 Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Thu, 19 Oct 2023 11:15:08 +0200
Subject: [PATCH 57/77] ObjCPatrolAppServiceClient: implement
listDartLifecycleCallbacks and setLifecycleCallbacksState
---
.../Classes/ObjCPatrolAppServiceClient.swift | 34 ++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/packages/patrol/ios/Classes/ObjCPatrolAppServiceClient.swift b/packages/patrol/ios/Classes/ObjCPatrolAppServiceClient.swift
index 9b2b3290a..147abecf0 100644
--- a/packages/patrol/ios/Classes/ObjCPatrolAppServiceClient.swift
+++ b/packages/patrol/ios/Classes/ObjCPatrolAppServiceClient.swift
@@ -45,6 +45,39 @@
}
}
+ @objc public func listDartLifecycleCallbacks(
+ completion: @escaping ([String]?, [String]?, Error?) -> Void
+ ) {
+ NSLog("PatrolAppService.listDartLifecycleCallbacks()")
+
+ client.listDartLifecycleCallbacks {
+ result in
+ switch result {
+ case .success(let result):
+ completion(result.setUpAlls, result.tearDownAlls, nil)
+ case .failure(let error):
+ completion(nil, nil, error)
+ }
+ }
+ }
+
+ @objc public func setLifecycleCallbacksState(
+ state: [String: Bool], completion: @escaping (Error?) -> Void
+ ) {
+ NSLog("PatrolAppService.setLifecycleCallbacksState()")
+
+ let request = SetLifecycleCallbacksStateRequest(state: state)
+ client.setLifecycleCallbacksState(request: request) {
+ result in
+ switch result {
+ case .success(_):
+ completion(nil)
+ case .failure(let error):
+ completion(error)
+ }
+ }
+ }
+
@objc public func runDartTest(
name: String, completion: @escaping (ObjCRunDartTestResponse?, Error?) -> Void
) {
@@ -66,7 +99,6 @@
case .failure(let error):
completion(nil, error)
}
-
}
}
}
From dac690750e11e207fef4ccb52621583aa82206dc Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Thu, 19 Oct 2023 11:24:15 +0200
Subject: [PATCH 58/77] AutomatorServer: fix compile errors
---
.../patrol/ios/Classes/AutomatorServer/AutomatorServer.swift | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/packages/patrol/ios/Classes/AutomatorServer/AutomatorServer.swift b/packages/patrol/ios/Classes/AutomatorServer/AutomatorServer.swift
index 0f67dea3a..b66c4b4a8 100644
--- a/packages/patrol/ios/Classes/AutomatorServer/AutomatorServer.swift
+++ b/packages/patrol/ios/Classes/AutomatorServer/AutomatorServer.swift
@@ -296,6 +296,10 @@
func markPatrolAppServiceReady() throws {
onAppReady(true)
}
+
+ func markLifecycleCallbackExecuted(request: MarkLifecycleCallbackExecutedRequest) throws {
+ // TODO: Implement
+ }
}
#endif
From eb2f236fda6ca96337917dc0d97b5689a3713205 Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Thu, 19 Oct 2023 11:59:50 +0200
Subject: [PATCH 59/77] forward PATROL_INITIAL_RUN to app and expose it on
method channel
---
.../example/ios/RunnerUITests/RunnerUITests.m | 7 ++++--
.../ios/Classes/SwiftPatrolPlugin.swift | 22 +++++++++++++++----
2 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m b/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m
index b09632d8d..a727b1a26 100644
--- a/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m
+++ b/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m
@@ -29,9 +29,12 @@ @implementation __test_class
}
/* Run the app for the first time to gather Dart tests */
- [[[XCUIApplication alloc] init] launch];
+ XCUIApplication *app = [[XCUIApplication alloc] init];
+ NSDictionary *args = @{ @"PATROL_INITIAL_RUN" : @"true" };
+ app.launchEnvironment = args;
+ [app launch];
- /* Spin the runloop waiting until the app reports that it is ready to report Dart tests */
+ /* Spin the runloop waiting until the app reports that PatrolAppService is up */
while (!server.appReady) {
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
diff --git a/packages/patrol/ios/Classes/SwiftPatrolPlugin.swift b/packages/patrol/ios/Classes/SwiftPatrolPlugin.swift
index c07ab9da1..48fe6d327 100644
--- a/packages/patrol/ios/Classes/SwiftPatrolPlugin.swift
+++ b/packages/patrol/ios/Classes/SwiftPatrolPlugin.swift
@@ -2,10 +2,9 @@ import Flutter
import UIKit
let kChannelName = "pl.leancode.patrol/main"
-let kMethodAllTestsFinished = "allTestsFinished"
-let kErrorCreateChannelFailed = "create_channel_failed"
-let kErrorCreateChannelFailedMsg = "Failed to create GRPC channel"
+let kErrorInvalidValue = "invalid value"
+let kErrorInvalidValueMsg = "isInitialRun env var is not a bool"
/// A Flutter plugin that was responsible for communicating the test results back
/// to iOS XCUITest.
@@ -23,6 +22,21 @@ public class SwiftPatrolPlugin: NSObject, FlutterPlugin {
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
- result(FlutterMethodNotImplemented)
+ switch (call.method) {
+ case "isInitialRun":
+ let rawInitialRun = ProcessInfo.processInfo.environment["PATROL_INITIAL_RUN"]
+ let initialRun = Bool(rawInitialRun ?? "invalid")
+ if initialRun == nil {
+ result(FlutterError(
+ code: kErrorInvalidValue,
+ message: "PATROL_INITIAL_RUN value is invalid: \(String(describing: rawInitialRun))",
+ details: nil)
+ )
+ } else {
+ result(initialRun)
+ }
+ default:
+ result(FlutterMethodNotImplemented)
+ }
}
}
From 7cb598499feb065e1cbeeb87bf3b30aa24628013 Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Thu, 19 Oct 2023 13:36:00 +0200
Subject: [PATCH 60/77] ObjCPatrolAppServiceClient.swift: add 1 sec timeout to
wait for patrolAppService
---
.../Classes/ObjCPatrolAppServiceClient.swift | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/packages/patrol/ios/Classes/ObjCPatrolAppServiceClient.swift b/packages/patrol/ios/Classes/ObjCPatrolAppServiceClient.swift
index 147abecf0..916e8376c 100644
--- a/packages/patrol/ios/Classes/ObjCPatrolAppServiceClient.swift
+++ b/packages/patrol/ios/Classes/ObjCPatrolAppServiceClient.swift
@@ -66,14 +66,16 @@
) {
NSLog("PatrolAppService.setLifecycleCallbacksState()")
- let request = SetLifecycleCallbacksStateRequest(state: state)
- client.setLifecycleCallbacksState(request: request) {
- result in
- switch result {
- case .success(_):
- completion(nil)
- case .failure(let error):
- completion(error)
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+ let request = SetLifecycleCallbacksStateRequest(state: state)
+ self.client.setLifecycleCallbacksState(request: request) {
+ result in
+ switch result {
+ case .success(_):
+ completion(nil)
+ case .failure(let error):
+ completion(error)
+ }
}
}
}
From 5f61d5c14ad642e7e54ec996a3d1826e6d6228c6 Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Thu, 19 Oct 2023 13:36:16 +0200
Subject: [PATCH 61/77] common.dart: print before setUpAll() - debugging aid
---
packages/patrol/lib/src/common.dart | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/patrol/lib/src/common.dart b/packages/patrol/lib/src/common.dart
index 9456acaed..75bd89284 100644
--- a/packages/patrol/lib/src/common.dart
+++ b/packages/patrol/lib/src/common.dart
@@ -85,6 +85,7 @@ void patrolSetUpAll(Future Function() body) {
return;
}
+ patrolDebug('Waiting for lifecycle callbacks state...');
final callbacksState =
await patrolAppService.didReceiveLifecycleCallbacksState;
From 289aaaa74a7eb9bab078a9ebe2f12b088b0da203 Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Thu, 19 Oct 2023 13:37:03 +0200
Subject: [PATCH 62/77] RunnerUITests: call listDartLifecycleCallbacks and
setDartLifecycleCallbacksState
Unfortunately it's failing for unknown reason (yet)
---
.../example/ios/RunnerUITests/RunnerUITests.m | 37 ++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m b/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m
index a727b1a26..9613ac0f1 100644
--- a/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m
+++ b/packages/patrol/example/ios/RunnerUITests/RunnerUITests.m
@@ -39,6 +39,20 @@ @implementation __test_class
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
+ // MARK: List Dart lifecycle callbacks
+
+ __block NSMutableDictionary *callbacksState = [[NSMutableDictionary alloc] init];
+ [appServiceClient
+ listDartLifecycleCallbacksWithCompletion:^(NSArray * _Nullable setUpAlls,
+ NSArray * _Nullable tearDownAlls,
+ NSError * _Nullable err) {
+ for (NSString* setUpAll in setUpAlls) {
+ [callbacksState setObject:@NO forKey:setUpAll];
+ }
+ }];
+
+ // MARK: List Dart tests
+
__block NSArray *dartTests = NULL;
[appServiceClient listDartTestsWithCompletion:^(NSArray *_Nullable tests, NSError *_Nullable err) {
if (err != NULL) {
@@ -55,6 +69,8 @@ @implementation __test_class
NSLog(@"Got %lu Dart tests: %@", dartTests.count, dartTests);
+ // MARK: Dynamically create test case methods
+
NSMutableArray *invocations = [[NSMutableArray alloc] init];
/**
@@ -69,7 +85,26 @@ @implementation __test_class
/* Step 1 - dynamically create test cases */
IMP implementation = imp_implementationWithBlock(^(id _self) {
- [[[XCUIApplication alloc] init] launch];
+ XCUIApplication *app = [[XCUIApplication alloc] init];
+ NSDictionary *args = @{ @"PATROL_INITIAL_RUN" : @"false" };
+ app.launchEnvironment = args;
+ [app launch];
+
+ // TODO: wait for patrolAppService to be ready
+
+ __block BOOL callbacksSet = NO;
+ [appServiceClient setLifecycleCallbacksStateWithState:callbacksState completion:^(NSError * _Nullable err) {
+ if (err != NULL) {
+ NSLog(@"setLifecycleCallbacksStateWithState(): failed, err: %@", err);
+ }
+
+ callbacksSet = YES;
+ }];
+
+ /*Wait until lifecycle callbacks are set*/
+ while (!callbacksSet) {
+ [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
+ }
__block ObjCRunDartTestResponse *response = NULL;
[appServiceClient runDartTestWithName:dartTest
From f571f2b010b4a27a5047c490aa1405509867fa1f Mon Sep 17 00:00:00 2001
From: Bartek Pacia
Date: Thu, 19 Oct 2023 16:26:40 +0200
Subject: [PATCH 63/77] PatrolJUnitRunner: fix sending lifecycle callback
status as String instead of Bool
---
.../main/kotlin/pl/leancode/patrol/PatrolJUnitRunner.java | 4 ++--
packages/patrol/lib/src/native/patrol_app_service.dart | 7 +------
2 files changed, 3 insertions(+), 8 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 a085a65cd..91ea9bcef 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
@@ -207,7 +207,7 @@ private Map readStateFile() {
InputStream inputStream = TestStorageUtil.getInputStream(stateFileUri, getContentResolver());
String content = convertStreamToString(inputStream);
Gson gson = new Gson();
- Type typeOfHashMap = new TypeToken