blob: aa4fe0c5b3977ebc8ac6d2e0533aa0e72a1c78a9 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuchsia/camera/test/cpp/fidl.h>
#include <fuchsia/camera2/hal/cpp/fidl.h>
#include <fuchsia/camera3/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/sys/cpp/testing/fake_launcher.h>
#include "src/camera/bin/device_watcher/device_instance.h"
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
class DeviceWatcherTest : public gtest::TestLoopFixture {
protected:
DeviceWatcherTest() : context_(sys::ComponentContext::CreateAndServeOutgoingDirectory()) {}
void SetUp() override {
ASSERT_EQ(context_->svc()->Connect(watcher_.NewRequest()), ZX_OK);
watcher_.set_error_handler([](zx_status_t status) {
ADD_FAILURE() << "DeviceWatcher server disconnected: " << status;
});
ASSERT_EQ(context_->svc()->Connect(tester_.NewRequest()), ZX_OK);
tester_.set_error_handler([](zx_status_t status) {
ADD_FAILURE() << "DeviceWatcherTester server disconnected: " << status;
});
RunLoopUntilIdle();
}
void TearDown() override {
tester_ = nullptr;
watcher_ = nullptr;
RunLoopUntilIdle();
}
std::unique_ptr<sys::ComponentContext> context_;
fuchsia::camera3::DeviceWatcherPtr watcher_;
fuchsia::camera::test::DeviceWatcherTesterPtr tester_;
};
constexpr uint16_t kFakeVendorId = 0xFFFF;
constexpr uint16_t kFakeProductId = 0xABCD;
class FakeCamera : public fuchsia::hardware::camera::Device,
public fuchsia::camera2::hal::Controller {
public:
explicit FakeCamera(fidl::InterfaceRequest<fuchsia::hardware::camera::Device> request)
: camera_binding_(this, std::move(request)), controller_binding_(this) {}
void GetChannel(zx::channel channel) override {}
void GetChannel2(fidl::InterfaceRequest<fuchsia::camera2::hal::Controller> server_end) override {
ZX_ASSERT(controller_binding_.Bind(std::move(server_end)) == ZX_OK);
}
void GetNextConfig(fuchsia::camera2::hal::Controller::GetNextConfigCallback callback) override {}
void CreateStream(uint32_t config_index, uint32_t stream_index, uint32_t image_format_index,
fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection,
fidl::InterfaceRequest<fuchsia::camera2::Stream> stream) override {}
void EnableStreaming() override {}
void DisableStreaming() override {}
void GetDeviceInfo(fuchsia::camera2::hal::Controller::GetDeviceInfoCallback callback) override {
fuchsia::camera2::DeviceInfo info{};
info.set_vendor_id(kFakeVendorId);
info.set_product_id(kFakeProductId);
callback(std::move(info));
}
private:
fidl::Binding<fuchsia::hardware::camera::Device> camera_binding_;
fidl::Binding<fuchsia::camera2::hal::Controller> controller_binding_;
};
// TODO(fxbug.dev/53130): fix device_watcher_test flake
TEST_F(DeviceWatcherTest, DISABLED_WatchDevicesFindsCameras) {
fidl::InterfaceHandle<fuchsia::hardware::camera::Device> camera;
FakeCamera fake(camera.NewRequest());
tester_->InjectDevice(std::move(camera));
std::set<uint64_t> cameras;
// Wait until the watcher has discovered the real camera and the injected fake camera.
constexpr uint32_t kExpectedCameras = 2;
while (!HasFailure() && cameras.size() < kExpectedCameras) {
bool watch_devices_returned = false;
watcher_->WatchDevices([&](std::vector<fuchsia::camera3::WatchDevicesEvent> events) {
for (auto& event : events) {
if (event.is_added()) {
EXPECT_EQ(cameras.find(event.added()), cameras.end());
cameras.insert(event.added());
}
EXPECT_FALSE(event.is_removed());
}
watch_devices_returned = true;
});
while (!HasFailure() && !watch_devices_returned) {
RunLoopUntilIdle();
}
}
ASSERT_EQ(cameras.size(), kExpectedCameras);
// Ensure that a second watcher client is given the same cameras.
fuchsia::camera3::DeviceWatcherPtr watcher2;
ASSERT_EQ(context_->svc()->Connect(watcher2.NewRequest()), ZX_OK);
watcher2.set_error_handler(
[](zx_status_t status) { ADD_FAILURE() << "DeviceWatcher server disconnected: " << status; });
while (!HasFailure() && !cameras.empty()) {
bool watch_devices_returned = false;
watcher2->WatchDevices([&](std::vector<fuchsia::camera3::WatchDevicesEvent> events) {
for (auto& event : events) {
ASSERT_TRUE(event.is_added());
auto it = cameras.find(event.added());
ASSERT_NE(it, cameras.end());
cameras.erase(it);
}
watch_devices_returned = true;
});
while (!HasFailure() && !watch_devices_returned) {
RunLoopUntilIdle();
}
}
}
TEST_F(DeviceWatcherTest, InstanceLaunches) {
sys::testing::FakeLauncher fake_launcher;
constexpr auto kCameraDeviceUrl =
"fuchsia-pkg://fuchsia.com/camera_device#meta/camera_device.cmx";
bool camera_launched = false;
fake_launcher.RegisterComponent(
kCameraDeviceUrl, [&](fuchsia::sys::LaunchInfo launch_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> request) {
EXPECT_EQ(launch_info.url, kCameraDeviceUrl);
ASSERT_TRUE(launch_info.arguments.has_value());
EXPECT_EQ(launch_info.arguments.value().size(), 1u);
request.Close(ZX_ERR_PEER_CLOSED);
camera_launched = true;
});
fuchsia::sys::LauncherPtr launcher;
fake_launcher.GetHandler()(launcher.NewRequest());
bool component_unavailable_received = false;
auto result =
DeviceInstance::Create(launcher, nullptr, [&]() { component_unavailable_received = true; });
ASSERT_TRUE(result.is_ok());
auto instance = result.take_value();
// The instance should attempt to launch the component. Then, upon seeing the controller request
// close, it should call the component_unavailable callback.
while (!HasFailure() && (!camera_launched || !component_unavailable_received)) {
RunLoopUntilIdle();
}
}