Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
adds unit tests and a helper function
  • Loading branch information
jlundOverlay committed Jul 26, 2024
commit 0b59bfda44e88cf47f576c38f82597b405b84bc8
10 changes: 8 additions & 2 deletions packages/camera/camera_windows/windows/camera_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ std::optional<std::string> GetFilePathForVideo() {
}
} // namespace

// a setter for the event sink helpful for testing.
void CameraPlugin::SetEventSink(
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> events) {
event_sink = std::move(events);
}

// static
void CameraPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarWindows* registrar) {
Expand All @@ -134,8 +140,8 @@ void CameraPlugin::RegisterWithRegistrar(

auto event_channel_handler =
std::make_unique<flutter::StreamHandlerFunctions<>>(
[](auto arguments, auto events) {
event_sink = std::move(events);
[plugin = plugin.get()](auto arguments, auto events) {
plugin->SetEventSink(std::move(events));
return nullptr;
},
[](auto arguments) {
Expand Down
2 changes: 2 additions & 0 deletions packages/camera/camera_windows/windows/camera_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class CameraPlugin : public flutter::Plugin,
public CameraApi,
public VideoCaptureDeviceEnumerator {
public:
void SetEventSink(
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> events);
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar);

CameraPlugin(flutter::TextureRegistrar* texture_registrar,
Expand Down
226 changes: 226 additions & 0 deletions packages/camera/camera_windows/windows/test/camera_plugin_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,232 @@ TEST(CameraPlugin, InitializeHandlerCallStartPreview) {
EXPECT_TRUE(result_called);
}

TEST(CameraPlugin, StartImageStreamHandlerCallsStartImageStream) {
int64_t mock_camera_id = 1234;

std::unique_ptr<MockCamera> camera =
std::make_unique<MockCamera>(MOCK_DEVICE_ID);

std::unique_ptr<MockCaptureController> capture_controller =
std::make_unique<MockCaptureController>();

EXPECT_CALL(*camera, HasCameraId(Eq(mock_camera_id)))
.Times(1)
.WillOnce([cam = camera.get()](int64_t camera_id) {
return cam->camera_id_ == camera_id;
});

EXPECT_CALL(*camera,
HasPendingResultByType(Eq(PendingResultType::kStartStream)))
.Times(1)
.WillOnce(Return(false));

EXPECT_CALL(*camera,
AddPendingVoidResult(Eq(PendingResultType::kStartStream), _))
.Times(1)
.WillOnce([cam = camera.get()](
PendingResultType type,
std::function<void(std::optional<FlutterError>)> result) {
cam->pending_void_result_ = std::move(result);
return true;
});

EXPECT_CALL(*camera, GetCaptureController)
.Times(1)
.WillOnce([cam = camera.get()]() {
assert(cam->pending_void_result_);
return cam->capture_controller_.get();
});

EXPECT_CALL(*capture_controller, StartImageStream)
.Times(1)
.WillOnce([cam = camera.get()]() {
assert(cam->pending_void_result_);
return cam->pending_void_result_(std::nullopt);
});

camera->camera_id_ = mock_camera_id;
camera->capture_controller_ = std::move(capture_controller);

MockCameraPlugin plugin(std::make_unique<MockTextureRegistrar>().get(),
std::make_unique<MockBinaryMessenger>().get(),
std::make_unique<MockCameraFactory>());

// Add mocked camera to plugins camera list.
plugin.AddCamera(std::move(camera));

// Set the event sink to a mocked event sink.
auto mock_event_sink = std::make_unique<MockEventSink>();
plugin.SetEventSink(std::move(mock_event_sink));

bool result_called = false;
std::function<void(std::optional<FlutterError>)> start_image_stream_result =
[&result_called](std::optional<FlutterError> reply) {
EXPECT_FALSE(result_called); // Ensure only one reply call.
result_called = true;
EXPECT_FALSE(reply);
};

plugin.StartImageStream(mock_camera_id, std::move(start_image_stream_result));

EXPECT_TRUE(result_called);
}

TEST(CameraPlugin, StartImageStreamHandlerErrorOnInvalidCameraId) {
int64_t mock_camera_id = 1234;
int64_t missing_camera_id = 5678;

std::unique_ptr<MockCamera> camera =
std::make_unique<MockCamera>(MOCK_DEVICE_ID);

std::unique_ptr<MockCaptureController> capture_controller =
std::make_unique<MockCaptureController>();

EXPECT_CALL(*camera, HasCameraId)
.Times(1)
.WillOnce([cam = camera.get()](int64_t camera_id) {
return cam->camera_id_ == camera_id;
});

EXPECT_CALL(*camera, HasPendingResultByType).Times(0);
EXPECT_CALL(*camera, AddPendingVoidResult).Times(0);
EXPECT_CALL(*camera, GetCaptureController).Times(0);
EXPECT_CALL(*capture_controller, StartImageStream).Times(0);

camera->camera_id_ = mock_camera_id;

MockCameraPlugin plugin(std::make_unique<MockTextureRegistrar>().get(),
std::make_unique<MockBinaryMessenger>().get(),
std::make_unique<MockCameraFactory>());

// Add mocked camera to plugins camera list.
plugin.AddCamera(std::move(camera));

bool result_called = false;
std::function<void(std::optional<FlutterError>)> start_image_stream_result =
[&result_called](std::optional<FlutterError> reply) {
EXPECT_FALSE(result_called); // Ensure only one reply call.
result_called = true;
EXPECT_TRUE(reply);
};

plugin.StartImageStream(missing_camera_id,
std::move(start_image_stream_result));

EXPECT_TRUE(result_called);
}

TEST(CameraPlugin, StopImageStreamHandlerCallsStopImageStream) {
int64_t mock_camera_id = 1234;

std::unique_ptr<MockCamera> camera =
std::make_unique<MockCamera>(MOCK_DEVICE_ID);

std::unique_ptr<MockCaptureController> capture_controller =
std::make_unique<MockCaptureController>();

EXPECT_CALL(*camera, HasCameraId(Eq(mock_camera_id)))
.Times(1)
.WillOnce([cam = camera.get()](int64_t camera_id) {
return cam->camera_id_ == camera_id;
});

EXPECT_CALL(*camera,
HasPendingResultByType(Eq(PendingResultType::kStopStream)))
.Times(1)
.WillOnce(Return(false));

EXPECT_CALL(*camera,
AddPendingVoidResult(Eq(PendingResultType::kStopStream), _))
.Times(1)
.WillOnce([cam = camera.get()](
PendingResultType type,
std::function<void(std::optional<FlutterError>)> result) {
cam->pending_void_result_ = std::move(result);
return true;
});

EXPECT_CALL(*camera, GetCaptureController)
.Times(1)
.WillOnce([cam = camera.get()]() {
assert(cam->pending_void_result_);
return cam->capture_controller_.get();
});

EXPECT_CALL(*capture_controller, StopImageStream)
.Times(1)
.WillOnce([cam = camera.get()]() {
assert(cam->pending_void_result_);
return cam->pending_void_result_(std::nullopt);
});

camera->camera_id_ = mock_camera_id;
camera->capture_controller_ = std::move(capture_controller);

MockCameraPlugin plugin(std::make_unique<MockTextureRegistrar>().get(),
std::make_unique<MockBinaryMessenger>().get(),
std::make_unique<MockCameraFactory>());

// Add mocked camera to plugins camera list.
plugin.AddCamera(std::move(camera));

bool result_called = false;
std::function<void(std::optional<FlutterError>)> stop_image_stream_result =
[&result_called](std::optional<FlutterError> reply) {
EXPECT_FALSE(result_called); // Ensure only one reply call.
result_called = true;
EXPECT_FALSE(reply);
};

plugin.StopImageStream(mock_camera_id, std::move(stop_image_stream_result));

EXPECT_TRUE(result_called);
}

TEST(CameraPlugin, StopImageStreamHandlerErrorOnInvalidCameraId) {
int64_t mock_camera_id = 1234;
int64_t missing_camera_id = 5678;

std::unique_ptr<MockCamera> camera =
std::make_unique<MockCamera>(MOCK_DEVICE_ID);

std::unique_ptr<MockCaptureController> capture_controller =
std::make_unique<MockCaptureController>();

EXPECT_CALL(*camera, HasCameraId)
.Times(1)
.WillOnce([cam = camera.get()](int64_t camera_id) {
return cam->camera_id_ == camera_id;
});

EXPECT_CALL(*camera, HasPendingResultByType).Times(0);
EXPECT_CALL(*camera, AddPendingVoidResult).Times(0);
EXPECT_CALL(*camera, GetCaptureController).Times(0);
EXPECT_CALL(*capture_controller, StopImageStream).Times(0);

camera->camera_id_ = mock_camera_id;

MockCameraPlugin plugin(std::make_unique<MockTextureRegistrar>().get(),
std::make_unique<MockBinaryMessenger>().get(),
std::make_unique<MockCameraFactory>());

// Add mocked camera to plugins camera list.
plugin.AddCamera(std::move(camera));

bool result_called = false;
std::function<void(std::optional<FlutterError>)> stop_image_stream_result =
[&result_called](std::optional<FlutterError> reply) {
EXPECT_FALSE(result_called); // Ensure only one reply call.
result_called = true;
EXPECT_TRUE(reply);
};

plugin.StopImageStream(missing_camera_id,
std::move(stop_image_stream_result));

EXPECT_TRUE(result_called);
}

TEST(CameraPlugin, InitializeHandlerErrorOnInvalidCameraId) {
int64_t mock_camera_id = 1234;
int64_t missing_camera_id = 5678;
Expand Down
11 changes: 11 additions & 0 deletions packages/camera/camera_windows/windows/test/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,17 @@ class MockCaptureEngine : public IMFCaptureEngine {
volatile ULONG ref_ = 0;
bool initialized_ = false;
};
// Mock class for flutter::EventSink<flutter::EncodableValue>
class MockEventSink : public flutter::EventSink<flutter::EncodableValue> {
public:
MOCK_METHOD(void, SuccessInternal, (const flutter::EncodableValue* event),
(override));
MOCK_METHOD(void, ErrorInternal,
(const std::string& error_code, const std::string& error_message,
const flutter::EncodableValue* error_details),
(override));
MOCK_METHOD(void, EndOfStreamInternal, (), (override));
};

#define MOCK_DEVICE_ID "mock_device_id"
#define MOCK_CAMERA_NAME "mock_camera_name <" MOCK_DEVICE_ID ">"
Expand Down