Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native iOS screenshots to integration_test #84611

Merged

Conversation

jmagman
Copy link
Member

@jmagman jmagman commented Jun 15, 2021

Implement the native iOS screenshot side of #84472. Added an optional "name" argument for the "captureScreenshot" method.

When run as a native Xcode test, the screenshots are saved to the Xcode test results bundle in addition to the bytes being passed back over the channel.
Screen Shot 2021-07-22 at 4 32 46 PM

Screenshot is the app window, not including the status bar, etc.
public png_1_7EACEC54-4CF2-42E1-A6B3-D203D265DD1E

iOS screenshot prototype of #83856

@jmagman jmagman added platform-ios iOS applications specifically f: integration_test The flutter/packages/integration_test plugin labels Jun 15, 2021
@flutter-dashboard flutter-dashboard bot added the team Infra upgrades, team productivity, code health, technical debt. See also team: labels. label Jun 15, 2021
@google-cla google-cla bot added the cla: yes label Jun 15, 2021
@jmagman jmagman force-pushed the integration-test-screenshot branch 2 times, most recently from a80d1ed to bc4d3b8 Compare July 22, 2021 23:35
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have been checked in before.

@@ -475,21 +475,11 @@
baseConfigurationReference = 09505407E99803EF7AA92DE7 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove unnecessary build setting overrides. I can do this in a separate PR, if you prefer.

@jmagman jmagman marked this pull request as ready for review July 22, 2021 23:37
@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat.

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Copy link
Contributor

@stuartmorgan stuartmorgan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with nits. Shouldn't we have a test of this functionality that screenshots a dummy plugin and compares it to a golden image though?

- (BOOL)testIntegrationTest:(NSString **)testResult;
- (instancetype)initWithScreenshotDelegate:(nullable id<FLTIntegrationTestScreenshotDelegate>)delegate NS_DESIGNATED_INITIALIZER;

- (BOOL)testIntegrationTest:(NSString *_Nullable *_Nullable)testResult;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get a comment on this weird API describing its behavior while you are touching it? (Or even better, could we make this an idiomatic ObjC method that returns either an NSString or nil?)

Copy link
Member Author

@jmagman jmagman Aug 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This API is very weird (I just added the nullability), and the INTEGRATION_TEST_IOS_RUNNER macro is downright Swift-hostile...

} else {
result(FlutterMethodNotImplemented);
}
}

- (UIImage *)capturePngScreenshot {
UIWindow *window = [UIApplication.sharedApplication.windows filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"keyWindow = YES"]].firstObject;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we're not running clang-format in this directory on CI? This is way over the 100 char style guide length limit.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there are any formatters (even dartfmt) running on flutter/flutter (plus I hate how clang formatter mangles Obj-C blocks). Let me shorten these up.

@@ -60,8 +61,8 @@ class IOCallbackManager implements CallbackManager {
// comes up in the future. For example: `WebCallbackManager.cleanup`.
}

// Whether the Flutter surface uses an Image.
bool _usesFlutterImage = false;
// Whether the Flutter surface uses an Image (Android only).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds to me like using an image is Android-only, but the logic below is the reverse.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blasten What does Whether the Flutter surface uses an Image mean in this context? I think that the surface is frozen, the screenshot taken, and then it's unfrozen on Android?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -90,7 +91,7 @@ class IOCallbackManager implements CallbackManager {
integrationTestChannel.setMethodCallHandler(_onMethodChannelCall);
final List<int>? rawBytes = await integrationTestChannel.invokeMethod<List<int>>(
'captureScreenshot',
null,
<String, dynamic>{'name': screenshot},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the above !_usesFlutterImage if statement could also check if Platform.isAndroid

// Whether the Flutter surface uses an Image.
bool _usesFlutterImage = false;
// Whether the Flutter surface uses an Image (Android only).
bool _usesFlutterImage = false || !Platform.isAndroid;

@override
Future<void> convertFlutterSurfaceToImage() async {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would need to noop if !Platform.isAndroid

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant to make it so it was never set to false on !Platform.isAndroid, let me clean this up.

Copy link

@blasten blasten left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM % nits

Copy link
Member Author

@jmagman jmagman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with nits. Shouldn't we have a test of this functionality that screenshots a dummy plugin and compares it to a golden image though?

I think the next step for @blasten after I commit this was to set up the screenshot integration tests. But I should also write some iOS native tests.

- (BOOL)testIntegrationTest:(NSString **)testResult;
- (instancetype)initWithScreenshotDelegate:(nullable id<FLTIntegrationTestScreenshotDelegate>)delegate NS_DESIGNATED_INITIALIZER;

- (BOOL)testIntegrationTest:(NSString *_Nullable *_Nullable)testResult;
Copy link
Member Author

@jmagman jmagman Aug 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This API is very weird (I just added the nullability), and the INTEGRATION_TEST_IOS_RUNNER macro is downright Swift-hostile...

} else {
result(FlutterMethodNotImplemented);
}
}

- (UIImage *)capturePngScreenshot {
UIWindow *window = [UIApplication.sharedApplication.windows filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"keyWindow = YES"]].firstObject;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there are any formatters (even dartfmt) running on flutter/flutter (plus I hate how clang formatter mangles Obj-C blocks). Let me shorten these up.

// Whether the Flutter surface uses an Image.
bool _usesFlutterImage = false;
// Whether the Flutter surface uses an Image (Android only).
bool _usesFlutterImage = false || !Platform.isAndroid;

@override
Future<void> convertFlutterSurfaceToImage() async {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant to make it so it was never set to false on !Platform.isAndroid, let me clean this up.

@jmagman
Copy link
Member Author

jmagman commented Aug 6, 2021

LGTM with nits. Shouldn't we have a test of this functionality that screenshots a dummy plugin and compares it to a golden image though?

There will be some test harness work needed for the native side. Filed #87770.

We'll get some unit tests for this going soon.

@blasten
Copy link

blasten commented Aug 6, 2021

🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
f: integration_test The flutter/packages/integration_test plugin platform-ios iOS applications specifically team Infra upgrades, team productivity, code health, technical debt. See also team: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants