blob: 4a79f380402e70133a1808cea75ac3b1b1bb4840 [file] [log] [blame]
// Copyright 2019 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 <fcntl.h>
#include <fuchsia/hardware/camera/cpp/fidl.h>
#include <fuchsia/io/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/namespace.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/gtest/real_loop_fixture.h>
#include <fs/pseudo_dir.h>
#include <fs/service.h>
#include <fs/synchronous_vfs.h>
#include <src/camera/camera_manager2/camera_plug_detector.h>
#include <src/lib/fsl/io/device_watcher.h>
#include "device_creation_fixture.h"
#include "src/lib/syslog/cpp/logger.h"
namespace camera {
namespace {
// A minimal |fuchsia::hardware::camera::Device| that we can use to emulate a fake devfs directory
// for testing.
class FakeCameraDevice : public fuchsia::hardware::camera::Device {
public:
// Checks that the |client_side| channel is the other end of our server_side_
// channel.
bool CheckMatchingChannel(const zx::channel& client_side) const {
zx_info_handle_basic_t info[2];
EXPECT_EQ(ZX_OK, client_side.get_info(ZX_INFO_HANDLE_BASIC, &info[0],
sizeof(zx_info_handle_basic_t), nullptr, nullptr));
EXPECT_EQ(ZX_OK, server_side_.get_info(ZX_INFO_HANDLE_BASIC, &info[1],
sizeof(zx_info_handle_basic_t), nullptr, nullptr));
EXPECT_NE(info[0].koid, 0u);
EXPECT_NE(info[1].koid, 0u);
return (info[0].related_koid == info[1].koid) && (info[1].related_koid == info[0].koid);
}
bool HasConnection() { return server_side_.is_valid(); }
private:
void GetChannel(zx::channel c) override {}
void GetChannel2(zx::channel c) override { server_side_ = std::move(c); }
zx::channel server_side_;
};
// DeviceTracker acts like the CameraManagerApp, keeping track of device connections.
// This allows us to gather all the plug events and make sure they were handled correctly.
class DeviceTracker {
public:
fit::function<void(fidl::InterfaceHandle<fuchsia::camera2::hal::Controller>)> GetHandler() {
return [this](auto handler) { controller_handles_.push_back(std::move(handler)); };
}
size_t size() const { return controller_handles_.size(); }
bool HasMatchingChannel(const FakeCameraDevice& device) {
for (auto& handle : controller_handles_) {
if (device.CheckMatchingChannel(handle.channel())) {
return true;
}
}
return false;
}
private:
std::vector<fidl::InterfaceHandle<fuchsia::camera2::hal::Controller>> controller_handles_;
};
char path[] = "/dev/class/camera";
using CameraDeviceCreationTest = DeviceCreationFixture<path, fuchsia::hardware::camera::Device>;
TEST_F(CameraDeviceCreationTest, DetectExistingDevices) {
// Add some devices that will exist before the plug detector starts.
FakeCameraDevice camera0, camera1;
auto d1 = AddDevice(&camera0);
auto d2 = AddDevice(&camera1);
// Create the plug detector; no events should be sent until |Start|.
DeviceTracker tracker;
CameraPlugDetector plug_detector;
RunLoopUntilIdle();
EXPECT_EQ(0u, tracker.size());
// Start the detector; expect 2 events (1 for each device above);
ASSERT_EQ(ZX_OK, plug_detector.Start(tracker.GetHandler()));
RunLoopUntil([&tracker] { return tracker.size() >= 2; });
EXPECT_EQ(2u, tracker.size());
RunLoopUntil([&camera0] { return camera0.HasConnection(); });
RunLoopUntil([&camera1] { return camera1.HasConnection(); });
EXPECT_TRUE(tracker.HasMatchingChannel(camera0));
EXPECT_TRUE(tracker.HasMatchingChannel(camera1));
plug_detector.Stop();
}
TEST_F(CameraDeviceCreationTest, DetectHotplugDevices) {
DeviceTracker tracker;
CameraPlugDetector plug_detector;
ASSERT_EQ(ZX_OK, plug_detector.Start(tracker.GetHandler()));
RunLoopUntilIdle();
EXPECT_EQ(0u, tracker.size());
// Hotplug a device.
FakeCameraDevice camera0;
auto d1 = AddDevice(&camera0);
RunLoopUntil([&tracker] { return tracker.size() >= 1u; });
ASSERT_EQ(1u, tracker.size());
RunLoopUntil([&camera0] { return camera0.HasConnection(); });
EXPECT_TRUE(tracker.HasMatchingChannel(camera0));
plug_detector.Stop();
}
} // namespace
} // namespace camera