Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
29beaa7
Fixed crash when streaming in iOS
zuvola Nov 18, 2021
aec0f93
Fixed skip judgment
zuvola Nov 18, 2021
0d9f61a
Merge branch 'flutter:master' into fix_ios_streaming
zuvola Nov 22, 2021
7cff775
Add test
zuvola Nov 22, 2021
5d1fb56
Update CHANGELOG.md
zuvola Nov 22, 2021
7c0fa9e
Fix test
zuvola Nov 22, 2021
3056e17
Add frameStack property to startImageStream
zuvola Dec 10, 2021
1a48ffc
Merge branch 'flutter:master' into fix_ios_streaming
zuvola Dec 10, 2021
4cb5d6e
Merge branch 'fix_ios_streaming' of https://github.com/zuvola/plugins…
zuvola Dec 10, 2021
27957a1
Bump version to 0.9.4+6
zuvola Dec 10, 2021
97d16c7
format
zuvola Dec 10, 2021
1382d62
Fixed tests
zuvola Dec 14, 2021
46d9df0
Merge branch 'main' into fix_ios_streaming
zuvola Jan 27, 2022
84dc53d
Restored the API and set the number of pending frames to 4
zuvola Jan 28, 2022
3ed1e94
Fixed tests
zuvola Feb 7, 2022
9a3e627
Merge commit 'f7138f7222856623796c2ee2e561f866fcf08d38' into fix_ios_…
zuvola Feb 7, 2022
6191e3a
Merge
zuvola Feb 7, 2022
e732d7b
Fixed CHANGELOG
zuvola Feb 7, 2022
b69ea0f
Fixed tests
zuvola Feb 7, 2022
e9beb1f
Merge branch 'main' into fix_ios_streaming
zuvola Mar 8, 2022
74d9d85
Apply PR feedback
zuvola Mar 10, 2022
6b81e1a
Update messages
zuvola Mar 11, 2022
96f8edb
Merge branch 'main' into fix_ios_streaming
zuvola Mar 11, 2022
1167112
Update to use CameraTestUtils
zuvola Mar 11, 2022
24871c6
Update _Test header
zuvola Mar 11, 2022
7635469
Bump version to 0.9.4+17
zuvola Mar 11, 2022
376bbfa
Inject FLTImageStreamHandler object
zuvola Mar 16, 2022
f276bd0
Capital S
zuvola Mar 16, 2022
f69aed6
Use XCTNSPredicateExpectation
zuvola Mar 16, 2022
9653345
Wait 1 second
zuvola Mar 16, 2022
661cf49
Merge branch 'main' into fix_ios_streaming
zuvola Mar 16, 2022
8e334bf
Format
zuvola Mar 16, 2022
7b8d74f
Using KVO to wait
zuvola Mar 16, 2022
ab233fa
Add comments
zuvola Mar 23, 2022
830d5ad
Update comments
zuvola Mar 24, 2022
095faf3
Merge branch 'main' into fix_ios_streaming
stuartmorgan-g Mar 25, 2022
e185988
Version bump
stuartmorgan-g Mar 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fixed tests
  • Loading branch information
zuvola committed Feb 7, 2022
commit 3ed1e94d0dde99a8a96f9a0df284e15bbc9c4a16
26 changes: 0 additions & 26 deletions packages/camera/camera/example/integration_test/camera_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -292,30 +292,4 @@ void main() {
},
skip: !Platform.isIOS,
);

testWidgets(
'Image streaming persistence test on iOS',
(WidgetTester tester) async {
final List<CameraDescription> cameras = await availableCameras();
if (cameras.isEmpty) {
return;
}

final CameraController controller = CameraController(
cameras[0],
ResolutionPreset.max,
enableAudio: false,
);

await controller.initialize();

await controller.startImageStream((CameraImage image) {});

await Future.delayed(Duration(seconds: 5));

await controller.stopImageStream();
await controller.dispose();
},
skip: !Platform.isIOS,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */; };
334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
7888073027A77C12000EA0DC /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7888072F27A77C12000EA0DC /* StreamingTest.m */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
Expand Down Expand Up @@ -66,6 +67,7 @@
1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
7888072F27A77C12000EA0DC /* StreamingTest.m */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = StreamingTest.m; sourceTree = "<group>"; tabWidth = 2; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -128,6 +130,7 @@
F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */,
E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */,
E032F2A627A0BF2D009E9028 /* FLTFlashModeTests.m */,
7888072F27A77C12000EA0DC /* StreamingTest.m */,
);
path = RunnerTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -395,6 +398,7 @@
033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */,
03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */,
E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */,
7888073027A77C12000EA0DC /* StreamingTest.m in Sources */,
F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */,
334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */,
E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */,
Expand Down
97 changes: 97 additions & 0 deletions packages/camera/camera/example/ios/RunnerTests/StreamingTest.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// 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.

@import camera;
@import camera.Test;
@import XCTest;
@import AVFoundation;
#import <OCMock/OCMock.h>
#import "MockFLTThreadSafeFlutterResult.h"

@interface FLTImageStreamHandler : NSObject <FlutterStreamHandler>
@property FlutterEventSink eventSink;
@end

@interface FLTCam : NSObject <FlutterTexture,
AVCaptureVideoDataOutputSampleBufferDelegate,
AVCaptureAudioDataOutputSampleBufferDelegate>
@property(assign, nonatomic) int streamingPendingFrames;
@property(assign, nonatomic) int maxStreamingPendingFrames;
@property(assign, nonatomic) BOOL isStreamingImages;
@property(nonatomic) FLTImageStreamHandler *imageStreamHandler;
- (void)captureOutput:(AVCaptureOutput *)output
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection;
Copy link
Contributor

Choose a reason for hiding this comment

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

You can make the delegate conformance part of the test header, rather than declaring this.

@end
Copy link
Contributor

Choose a reason for hiding this comment

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

Anything you need to access from a test should be in the _Test headers, not re-declared here.

But this much use of private API in tests (which is an anti-pattern) is concerning; see comments below.

Copy link
Contributor

@hellohuanlin hellohuanlin Feb 23, 2022

Choose a reason for hiding this comment

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

@zuvola You can find XXX_Test.h examples in the .modulemap file


@interface CameraPlugin (Private)
@property(retain, nonatomic) FLTCam *camera;
@end

@interface StreamingTests : XCTestCase
@end

@implementation StreamingTests

- (void)testStreamingPendingFrames {
CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil];

// Set up mocks for initWithCameraName method
id avCaptureDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]);
OCMStub([avCaptureDeviceInputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg anyObjectRef]])
.andReturn([AVCaptureInput alloc]);
id avCaptureSessionMock = OCMClassMock([AVCaptureSession class]);
OCMStub([avCaptureSessionMock alloc]).andReturn(avCaptureSessionMock);
OCMStub([avCaptureSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES);

// Set up method calls
FlutterMethodCall *createCall = [FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"resolutionPreset" : @"medium", @"enableAudio" : @(1)}];
FlutterMethodCall *startCall = [FlutterMethodCall methodCallWithMethodName:@"startImageStream"
arguments:nil];
FlutterMethodCall *receivedCall =
[FlutterMethodCall methodCallWithMethodName:@"receivedImageStreamData" arguments:nil];

// Set up sampleBuffer
CVPixelBufferRef pixelBuffer;
CVPixelBufferCreate(kCFAllocatorDefault, 100, 100, kCVPixelFormatType_32BGRA, nil, &pixelBuffer);
CMVideoFormatDescriptionRef formatDescription;
CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer,
&formatDescription);
CMSampleBufferRef sampleBuffer;
CMSampleBufferCreateReadyWithImageBuffer(kCFAllocatorDefault, pixelBuffer, formatDescription,
&kCMTimingInfoInvalid, &sampleBuffer);

// Start streaming
[camera handleMethodCallAsync:createCall result:nil];
[camera handleMethodCallAsync:startCall result:nil];
Copy link
Contributor

Choose a reason for hiding this comment

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

You should be able to trigger these via calIs to the platform method channel handler (which is public API), rather than needing to access internal implementation details. Not only would that reduce use of private APIs, but you are violating the threading expectations by calling this directly.

camera.camera.imageStreamHandler.eventSink = ^(id _Nullable event) {
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are you using internals here rather than the public startImageStreamWithMessenger:?


// Waiting for streaming to start
FLTCam *cam = [camera camera];
while (!cam.isStreamingImages) {
[NSThread sleepForTimeInterval:0.001];
Copy link
Contributor

Choose a reason for hiding this comment

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

If isStreamingImages flag is not flipped, then this unit test will run forever, instead of reporting test failure. can you wait for the flag instead? (e.g. XCTNSPredicateExpectation)

}

// Initial value
XCTAssertEqual(cam.streamingPendingFrames, 0);

// Emulate receiving a video frame
[camera.camera captureOutput:nil didOutputSampleBuffer:sampleBuffer fromConnection:nil];
XCTAssertEqual(cam.streamingPendingFrames, 1);

// ReceivedCall reduces streamingPendingFrames
[camera handleMethodCallAsync:receivedCall result:nil];
XCTAssertEqual(cam.streamingPendingFrames, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do you need to do all of these assertions of internal counts? Isn't what matters here the externally observable behavior, which is that you don't get more than N callbacks to your handler if you don't acknowledge receipt? (And that once you do, you start getting more callbacks again.)


// Don't exceed maxStreamingPendingFrames
for (int i = 0; i < cam.maxStreamingPendingFrames + 2; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

You should hard-code a value in tests rather than reading this. If someone changes the constant to 100, this test should fail.

[camera.camera captureOutput:nil didOutputSampleBuffer:sampleBuffer fromConnection:nil];
}
XCTAssertEqual(cam.streamingPendingFrames, cam.maxStreamingPendingFrames);
}

@end