tests: Introduce hotplug hot-unplug unit test
This test checks the code-paths for camera's hotplugged and unplugged support. It is based on bind/unbind of a UVC device from sysfs. Hence, this test requires root permissions to run and should have at least one already bound UVC device present in the system. Signed-off-by: Umang Jain <email@uajain.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
parent
7055e96ecc
commit
8960ef9715
2 changed files with 129 additions and 0 deletions
128
test/hotplug-cameras.cpp
Normal file
128
test/hotplug-cameras.cpp
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020, Umang Jain <email@uajain.com>
|
||||||
|
*
|
||||||
|
* hotplug-cameras.cpp - Test cameraAdded/cameraRemoved signals in CameraManager
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <libcamera/camera.h>
|
||||||
|
#include <libcamera/camera_manager.h>
|
||||||
|
#include <libcamera/event_dispatcher.h>
|
||||||
|
#include <libcamera/timer.h>
|
||||||
|
|
||||||
|
#include "libcamera/internal/file.h"
|
||||||
|
#include "libcamera/internal/thread.h"
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
using namespace libcamera;
|
||||||
|
|
||||||
|
class HotplugTest : public Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void cameraAddedHandler(std::shared_ptr<Camera> cam)
|
||||||
|
{
|
||||||
|
cameraAdded_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cameraRemovedHandler(std::shared_ptr<Camera> cam)
|
||||||
|
{
|
||||||
|
cameraRemoved_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init()
|
||||||
|
{
|
||||||
|
if (!File::exists("/sys/module/uvcvideo")) {
|
||||||
|
std::cout << "uvcvideo driver is not loaded, skipping" << std::endl;
|
||||||
|
return TestSkip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (geteuid() != 0) {
|
||||||
|
std::cout << "This test requires root permissions, skipping" << std::endl;
|
||||||
|
return TestSkip;
|
||||||
|
}
|
||||||
|
|
||||||
|
cm_ = new CameraManager();
|
||||||
|
if (cm_->start()) {
|
||||||
|
std::cout << "Failed to start camera manager" << std::endl;
|
||||||
|
return TestFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
cameraAdded_ = false;
|
||||||
|
cameraRemoved_ = false;
|
||||||
|
|
||||||
|
cm_->cameraAdded.connect(this, &HotplugTest::cameraAddedHandler);
|
||||||
|
cm_->cameraRemoved.connect(this, &HotplugTest::cameraRemovedHandler);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run()
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *dirent;
|
||||||
|
std::string uvcDeviceDir;
|
||||||
|
|
||||||
|
dir = opendir(uvcDriverDir_.c_str());
|
||||||
|
/* Find a UVC device directory, which we can bind/unbind. */
|
||||||
|
while ((dirent = readdir(dir)) != nullptr) {
|
||||||
|
if (!File::exists(uvcDriverDir_ + dirent->d_name + "/video4linux"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uvcDeviceDir = dirent->d_name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
/* If no UVC device found, skip the test. */
|
||||||
|
if (uvcDeviceDir.empty())
|
||||||
|
return TestSkip;
|
||||||
|
|
||||||
|
/* Unbind a camera and process events. */
|
||||||
|
std::ofstream(uvcDriverDir_ + "unbind", std::ios::binary)
|
||||||
|
<< uvcDeviceDir;
|
||||||
|
Timer timer;
|
||||||
|
timer.start(1000);
|
||||||
|
while (timer.isRunning() && !cameraRemoved_)
|
||||||
|
Thread::current()->eventDispatcher()->processEvents();
|
||||||
|
if (!cameraRemoved_) {
|
||||||
|
std::cout << "Camera unplug not detected" << std::endl;
|
||||||
|
return TestFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bind the camera again and process events. */
|
||||||
|
std::ofstream(uvcDriverDir_ + "bind", std::ios::binary)
|
||||||
|
<< uvcDeviceDir;
|
||||||
|
timer.start(1000);
|
||||||
|
while (timer.isRunning() && !cameraAdded_)
|
||||||
|
Thread::current()->eventDispatcher()->processEvents();
|
||||||
|
if (!cameraAdded_) {
|
||||||
|
std::cout << "Camera plug not detected" << std::endl;
|
||||||
|
return TestFail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TestPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup()
|
||||||
|
{
|
||||||
|
cm_->stop();
|
||||||
|
delete cm_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CameraManager *cm_;
|
||||||
|
static const std::string uvcDriverDir_;
|
||||||
|
bool cameraRemoved_;
|
||||||
|
bool cameraAdded_;
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string HotplugTest::uvcDriverDir_ = "/sys/bus/usb/drivers/uvcvideo/";
|
||||||
|
|
||||||
|
TEST_REGISTER(HotplugTest)
|
|
@ -30,6 +30,7 @@ internal_tests = [
|
||||||
['event-thread', 'event-thread.cpp'],
|
['event-thread', 'event-thread.cpp'],
|
||||||
['file', 'file.cpp'],
|
['file', 'file.cpp'],
|
||||||
['file-descriptor', 'file-descriptor.cpp'],
|
['file-descriptor', 'file-descriptor.cpp'],
|
||||||
|
['hotplug-cameras', 'hotplug-cameras.cpp'],
|
||||||
['message', 'message.cpp'],
|
['message', 'message.cpp'],
|
||||||
['object', 'object.cpp'],
|
['object', 'object.cpp'],
|
||||||
['object-invoke', 'object-invoke.cpp'],
|
['object-invoke', 'object-invoke.cpp'],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue