Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[camera] Handle "create" method call in main thread on iOS #4007

Closed
wants to merge 5 commits into from

Conversation

BeMacized
Copy link
Contributor

@BeMacized BeMacized commented Jun 3, 2021

This PR moves the handling of the "create" method call to the main thread on iOS, due to issues mentioned in flutter/flutter#52578.

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides. (Note that unlike the flutter/flutter repo, the flutter/plugins repo does use dart format. See plugin_tool format)
  • I signed the CLA.
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I listed at least one issue that this PR fixes in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy.
  • I updated CHANGELOG.md to add a description of the change.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@BeMacized BeMacized requested a review from bparrishMines as a code owner June 3, 2021 08:46
@google-cla google-cla bot added the cla: yes label Jun 3, 2021
@BeMacized BeMacized marked this pull request as draft June 3, 2021 08:52
@BeMacized BeMacized marked this pull request as ready for review June 4, 2021 12:29
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

Copy link
Contributor

Choose a reason for hiding this comment

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

This file hasn't been added to the project, so isn't being run.

self.mockRegistrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar));
self.mockMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
OCMStub([self.mockRegistrar messenger]).andReturn(self.mockMessenger);
self.plugin = [[CameraPlugin alloc] initWithRegistry:self.mockRegistrar
Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't make the object under test stateful. Test fixtures should have as little state as possible so that it's obvious what's happening within each test, and that's especially true of seeing how the object under test is being set up.


- (void)testHandleMethodCallSync_ShouldHandleSyncMethods {
id methodCallMock = OCMClassMock([FlutterMethodCall class]);
OCMStub([methodCallMock method]).andReturn(@"create");
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this a mock? Mocking is a tool of last resort; FlutterMethodCall is trivial to instantiate.

- (instancetype)initWithRegistry:(NSObject<FlutterTextureRegistry> *)registry
messenger:(NSObject<FlutterBinaryMessenger> *)messenger;
- (BOOL)handleMethodCallSync:(FlutterMethodCall *)call result:(FlutterResult)result;
- (BOOL)handleMethodCallAsync:(FlutterMethodCall *)call result:(FlutterResult)result;
Copy link
Contributor

Choose a reason for hiding this comment

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

Testing with private methods should only be done when a test can't be done with public methods. I don't see why accessing these should be necessary.

[[self plugin] handleMethodCall:methodCallMock result:result];

OCMVerify([mockedPlugin handleMethodCallSync:methodCallMock result:result]);
OCMVerify(never(), [mockedPlugin handleMethodCallAsync:methodCallMock result:result]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Most of these tests are testing for an implementation detail rather than testing that the thing we want to be true—that interactions with the engine are on the main thread. We should test for what we want to assert, not for an implementation that we believe has the properties we want.

As one of many reasons this is true: would these tests fail if someone accidentally swapped which internal method call was done on a queue and which one wasn't?

See #3778 for an example where I fixed a similar issue in another plugin, and handled it with assertions in the code and tests designed to exercise the assertions without assuming things about the implementation details of the plugin itself.

return;
}

// Otherwise invoke the plugin on another dispatch queue to avoid blocking the UI.
dispatch_async(_dispatchQueue, ^{
[self handleMethodCallAsync:call result:result];
Copy link
Contributor

Choose a reason for hiding this comment

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

I've commented on the issue to get clarity, but I believe all uses of result on the background queue are also incorrect, so this needs a larger restructure.

Delegating to a queue should very likely be limited to just expensive operations, rather than a default behavior.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants