[libcamera-devel,v4,6/6] tests: Introduce hotplug hot-unplug unit test

Message ID 20200611171528.9381-7-email@uajain.com
State Superseded
Headers show
Series
  • Introduce UVC hotplugging support
Related show

Commit Message

Umang Jain June 11, 2020, 5:16 p.m. UTC
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>
---
 test/hotplug-cameras.cpp | 124 +++++++++++++++++++++++++++++++++++++++
 test/meson.build         |   1 +
 2 files changed, 125 insertions(+)
 create mode 100644 test/hotplug-cameras.cpp

Comments

Laurent Pinchart June 12, 2020, 11:42 a.m. UTC | #1
Hi Umang,

Thank you for the patch.

On Thu, Jun 11, 2020 at 05:16:08PM +0000, Umang Jain wrote:
> 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>
> ---
>  test/hotplug-cameras.cpp | 124 +++++++++++++++++++++++++++++++++++++++
>  test/meson.build         |   1 +
>  2 files changed, 125 insertions(+)
>  create mode 100644 test/hotplug-cameras.cpp
> 
> diff --git a/test/hotplug-cameras.cpp b/test/hotplug-cameras.cpp
> new file mode 100644
> index 0000000..07500fc
> --- /dev/null
> +++ b/test/hotplug-cameras.cpp
> @@ -0,0 +1,124 @@
> +/* 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;

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> +			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)
> diff --git a/test/meson.build b/test/meson.build
> index bd7da14..a868813 100644
> --- a/test/meson.build
> +++ b/test/meson.build
> @@ -30,6 +30,7 @@ internal_tests = [
>      ['event-thread',                    'event-thread.cpp'],
>      ['file',                            'file.cpp'],
>      ['file-descriptor',                 'file-descriptor.cpp'],
> +    ['hotplug-cameras',                 'hotplug-cameras.cpp'],
>      ['message',                         'message.cpp'],
>      ['object',                          'object.cpp'],
>      ['object-invoke',                   'object-invoke.cpp'],

Patch

diff --git a/test/hotplug-cameras.cpp b/test/hotplug-cameras.cpp
new file mode 100644
index 0000000..07500fc
--- /dev/null
+++ b/test/hotplug-cameras.cpp
@@ -0,0 +1,124 @@ 
+/* 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_)
+			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_)
+			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)
diff --git a/test/meson.build b/test/meson.build
index bd7da14..a868813 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -30,6 +30,7 @@  internal_tests = [
     ['event-thread',                    'event-thread.cpp'],
     ['file',                            'file.cpp'],
     ['file-descriptor',                 'file-descriptor.cpp'],
+    ['hotplug-cameras',                 'hotplug-cameras.cpp'],
     ['message',                         'message.cpp'],
     ['object',                          'object.cpp'],
     ['object-invoke',                   'object-invoke.cpp'],