-
Notifications
You must be signed in to change notification settings - Fork 6k
Add some AppLifecycleTests #11890
Add some AppLifecycleTests #11890
Conversation
|
I was under the impression we didn't want to add swift to the language surface of anything we maintain. @cbracken @chinmaygarde thoughts? |
|
We don't want to add swift to anything people depend on since there's users before 12.2. This is only in leaf targets that are only in tests that no one else uses. |
|
I thought part of the idea was not adding more languages the team/contributors need to know |
|
If there isn't a pressing reason to use Swift, I'd rather stick to Objective-C. While Dan's point of needing to be language polyglot as being a potential hinderance to future contributions is certainly valid, I am also concerned that the usage of Swift might make it harder to integrate this target into the GN so that we can build all engine targets in one go. Currently, the way the Scenario's app is put together is a bit of a hack and needs a separate invocation after building the engine itself. Dan's comment here suggests that this is temporary till proper build system integration is in place. Needing to support Swift in GN will now become a prerequisite to this work. As these are the first Swift TUs in the engine, I'd hold off on setting this precedent till wider consensus on Swift in the engine is reached. |
|
Doh. I got excited too early. In terms of our users, they're already there (https://dashboards.corp.google.com/_8211ecd6_e372_4f1c_b4f8_a1f4f0411822#oyjxd7sm0, 26% of our users use swift currently) but the build system is a sensible reason. I'll swap them back to obj-c. |
8971c8f to
e51d95c
Compare
e51d95c to
5c8ca08
Compare
|
Alright, re-translated. PTAL while I go puke some square brackets. |
|
I don't know what CQ+1 does but it sounds fancy so I'm trying it. |
I edited the label description. It verified this PR in a try-job on LUCI. And yes, it is very fancy. |
|
|
||
| #import <Flutter/Flutter.h> | ||
|
|
||
| @interface Util : NSObject |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Util isn't a great name. Maybe make this a FlutterEngine category? Also _Nullable shouldn't be used on NSObjects.
NS_ASSUME_NONNULL_BEGIN
@interface FlutterEngine (ScenarioTesting)
- (void)runWithScenario:(NSString*)scenario
withCompletion:(void (^)(void))engineRunCompletion;
@end
NS_ASSUME_NONNULL_ENDThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh ya, good idea. Done.
Also _Nullable shouldn't be used on NSObjects.
Not sure I understood. But I think I did what you meant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I meant to use nullable instead of _Nullable. Rule of thumb if the compiler lets you use nullable, then use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohhhh withCompletion:(void (^_Nullable)(void))engineRunCompletion; should be withCompletion:(nullable void (^)(void))engineRunCompletion;. Sigh block syntax...
| [engineStartedExpectation fulfill]; | ||
| }]; | ||
|
|
||
| [self waitForExpectations:[NSArray arrayWithObject:engineStartedExpectation] timeout:5]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to specify the expectation, you can use -[self waitForExpectationsWithTimeout:5.0:handler:nil]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
| [self waitForExpectations:lifecycleExpectations timeout:5]; | ||
|
|
||
| // Expected sequence from showing the FlutterViewController is inactive and resumed. | ||
| BOOL lifecyclesAsExpected = [lifecycleEvents |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of doing this array checking, you can enforce expectation order. Something like:
XCTestExpectation *inactiveExpectation = [self expectationWithDescription:@"inactive"];
XCTestExpectation *resumedExpectation = [self expectationWithDescription:@"resume"];
[engine.lifecycleChannel setMessageHandler:^(id message, FlutterReply callback) {
if ([message isEqual:@"AppLifecycleState.inactive"]) {
[inactiveExpectation fulfill];
else if ([message isEqual:@"AppLifecycleState.resumed"]) {
[resumedExpectation fulfill];
} else {
XCTFail(@"Unexpected lifecycle transition: %@", message);
}
}];
[self waitForExpectations:@[inactiveExpectation, resumedExpectation] timeout:5.0 enforceOrder:YES];There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would be confusing since I'm going to be checking that something isn't done once I fix the actual production problem in the next PR so I wouldn't be waiting for expectations in places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will the message be in the next PR that you're going to forbid that isn't covered by the XCTFail fall-through? Can you invert the expectation? I can write some example code with a concrete example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that's really useful. I think I'll go with your approach when I fix flutter/flutter#37226 and flip the check around.
How would I synchronize at each step to make sure nothing happens then? Would it still be timeout based?
Otherwise, I would expect the error message to be in the shape of:
Expecting transitions inactive, resume. But you got inactive, paused, inactive, resume, inactive, resume.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-inverted is timeout based liked all the expectations, unfortunately, and enforcing order doesn't make sense. So it will fail immediately if fulfilled, but then will need to wait for the timeout to succeed
[self waitForExpectations:@[inactiveExpectation, resumedExpectation] timeout:5.0 enforceOrder:YES];
[self waitForExpectations:@[invertedExpectation] timeout:1.0]; // inactiveExpectation, resumedExpectation already fulfilled so wait less time?
However, what I wrote above should fail with "inactive, paused, inactive, resume, inactive, resume"
as
[inactive silently fulfilled]
FAIL: Unexpected lifecycle transition: paused
If you want to also fail on inactive, resume, inactive, resume, you should also set the expectations' assertForOverFulfill to YES. And if you expect the fulfillment to be more than one, you can also set expectedFulfillmentCount.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was halfway changing it to the way you said, but then I noticed that they both currently do the same thing. They both assert order, check on the same granularity and since NSNotificationCenter posts are synchronous, they also fail on unexpected events at the time of unexpected events.
I think I still prefer the present way which at places lists out the total map of expected behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are the new tests after the fix https://github.com/xster/engine/compare/lifecycle-test...xster:fix-ios-lifecycle?expand=1
| initWithDescription:@"Current implementation sends another AppLifecycleState event"]]; | ||
| [[NSNotificationCenter defaultCenter] | ||
| postNotificationName:UIApplicationWillResignActiveNotification | ||
| object:nil]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is a bit confusing. Can you split it into a test per notification with the expectation order enforced instead of the array checks of the messages?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to be testing that there isn't an expectation per notification soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would still make sense to assert the expectations between notifications? Can you break it down into separate tests at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think this is hard to read and it doesn't actually verify that the expectation was hit because of the notification, because it doesn't wait for the expectations between notifications. If this test ever fails because something is out of order it's going to be tricky to know which notification went awry and difficult to debug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NSNotificationCenter posts are synchronous, and I'm removing these expectations right after in the next PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well you would set up the expectation, post, then wait. We can talk about it in the next PR.
|
|
||
| #import <Flutter/Flutter.h> | ||
| #import <XCTest/XCTest.h> | ||
| #import "../Scenarios/ScreenBeforeFlutter.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just use a project import here.
#import "ScreenBeforeFlutter.h"There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ummm weird. Not sure why I was running into errors trying it before. Done.
Once Swift in GN is figured out, a good unit test with minimum language-polyglot overhead would be one that imports the Flutter module to make sure it can be imported in Swift. import Flutter
class SwiftModuleTests: XCTestCase {
func testModuleImport() {
// This test will fail to compile if the Swift module cannot be imported.
}
} |
|
Thanks for reviewing |
| // Expected sequence from showing the FlutterViewController is inactive and resumed. | ||
| BOOL lifecyclesAsExpected = [lifecycleEvents | ||
| isEqualToArray:@[ @"AppLifecycleState.inactive", @"AppLifecycleState.resumed" ]]; | ||
| XCTAssert(lifecyclesAsExpected, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
XCTAssertEqualObjects
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
dnfield
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few nits on the Dart code. I haven't looked at the ObjC too closely since it seems like @jmagman has that covered and probably is finding far more than I would have :)
| ) { | ||
| window.sendPlatformMessage(name, data, null); | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: new line at EOF.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
| canvas.drawRect( | ||
| Rect.fromLTWH(0, 0, window.physicalSize.width - 1, window.physicalSize.height - 1), | ||
| Paint()..color = const Color.fromARGB(255, 255, 255, 255), | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just canvas.drawPaint?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh cool, never noticed this API. Neat
|
|
||
| @override | ||
| void onDrawFrame() { | ||
| window.scheduleFrame(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you might want this to go to the end of onMetricsChanged and _pop. Right now this is drawing the same frames over and over again even if nothing has changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ya, makes sense
dd62a78 to
8a404a5
Compare
| initWithDescription:@"Current implementation sends another AppLifecycleState event"]]; | ||
| [[NSNotificationCenter defaultCenter] | ||
| postNotificationName:UIApplicationWillResignActiveNotification | ||
| object:nil]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well you would set up the expectation, post, then wait. We can talk about it in the next PR.
This reverts commit 46ff053 as it breaks mac tests https://ci.chromium.org/p/flutter/builders/prod/Mac%20Host%20Engine/2156.
This reverts commit 46ff053 as it breaks mac tests https://ci.chromium.org/p/flutter/builders/prod/Mac%20Host%20Engine/2156.
I was pre-emptively adding some test before fixing flutter/flutter#37226.
Added some stuff to make the Scenario target runnable and interactive without wrapping with a test.