[libcamera-devel,RFC] test: Move resource locking to test cases

Message ID 20190704013004.14600-1-niklas.soderlund@ragnatech.se
State Superseded
Delegated to: Niklas Söderlund
Headers show
Series
  • [libcamera-devel,RFC] test: Move resource locking to test cases
Related show

Commit Message

Niklas Söderlund July 4, 2019, 1:30 a.m. UTC
Instead of blocking any test which require access to a kernel resource
(vimc or vivid) from running in parallel move the resource locking into
test library and allow meson to execute them in parallel.

Resource locking is achieve using a lockfile for each resource in /tmp
and lockf(). The call to lockf() blocks until it can acquire the
resource and it keeps to lock until the test executable terminates
closing the file descriptor to the lock file  and freeing the lock. This
design ensures the process never deadlocks. The reason a separate lock
file is needed instead of locking the media device associated with the
resource is that such a locking scheme is already used by libcamera
itself.

With this change I cut down the runtime of tests by 50%. The largest
increase is from allowing self-contained tests in parallel with tests
which require a kernel resource. This is because most of the tests who
needs a kernel resource needs vimc so there is heavy contention for that
single resource and little gain from meson to execute them in parallel.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
 test/camera/camera_test.h                     |  2 +-
 test/camera/meson.build                       |  2 +-
 test/controls/control_list.cpp                |  4 ++
 test/controls/meson.build                     |  2 +-
 test/libtest/test.cpp                         | 37 ++++++++++++++++++-
 test/libtest/test.h                           | 15 +++++++-
 test/media_device/media_device_test.h         |  2 +-
 test/media_device/meson.build                 |  2 +-
 test/v4l2_subdevice/meson.build               |  2 +-
 test/v4l2_subdevice/v4l2_subdevice_test.h     |  2 +-
 test/v4l2_videodevice/buffer_sharing.cpp      |  2 +-
 test/v4l2_videodevice/capture_async.cpp       |  2 +-
 test/v4l2_videodevice/double_open.cpp         |  2 +-
 test/v4l2_videodevice/formats.cpp             |  2 +-
 test/v4l2_videodevice/meson.build             |  2 +-
 test/v4l2_videodevice/request_buffers.cpp     |  2 +-
 test/v4l2_videodevice/stream_on_off.cpp       |  2 +-
 .../v4l2_videodevice_test.cpp                 | 16 +++++++-
 test/v4l2_videodevice/v4l2_videodevice_test.h |  4 +-
 19 files changed, 85 insertions(+), 19 deletions(-)

Comments

Kieran Bingham July 4, 2019, 7:45 a.m. UTC | #1
Hi Niklas,

On 04/07/2019 02:30, Niklas Söderlund wrote:
> Instead of blocking any test which require access to a kernel resource
> (vimc or vivid) from running in parallel move the resource locking into
> test library and allow meson to execute them in parallel.
> 
> Resource locking is achieve using a lockfile for each resource in /tmp
> and lockf(). The call to lockf() blocks until it can acquire the
> resource and it keeps to lock until the test executable terminates
> closing the file descriptor to the lock file  and freeing the lock. This
> design ensures the process never deadlocks. The reason a separate lock
> file is needed instead of locking the media device associated with the
> resource is that such a locking scheme is already used by libcamera
> itself.

Can't we instead request the lock from the libcamera locking then ?


> With this change I cut down the runtime of tests by 50%. The largest
> increase is from allowing self-contained tests in parallel with tests
> which require a kernel resource. This is because most of the tests who
> needs a kernel resource needs vimc so there is heavy contention for that
> single resource and little gain from meson to execute them in parallel.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
>  test/camera/camera_test.h                     |  2 +-
>  test/camera/meson.build                       |  2 +-
>  test/controls/control_list.cpp                |  4 ++
>  test/controls/meson.build                     |  2 +-
>  test/libtest/test.cpp                         | 37 ++++++++++++++++++-
>  test/libtest/test.h                           | 15 +++++++-
>  test/media_device/media_device_test.h         |  2 +-
>  test/media_device/meson.build                 |  2 +-
>  test/v4l2_subdevice/meson.build               |  2 +-
>  test/v4l2_subdevice/v4l2_subdevice_test.h     |  2 +-
>  test/v4l2_videodevice/buffer_sharing.cpp      |  2 +-
>  test/v4l2_videodevice/capture_async.cpp       |  2 +-
>  test/v4l2_videodevice/double_open.cpp         |  2 +-
>  test/v4l2_videodevice/formats.cpp             |  2 +-
>  test/v4l2_videodevice/meson.build             |  2 +-
>  test/v4l2_videodevice/request_buffers.cpp     |  2 +-
>  test/v4l2_videodevice/stream_on_off.cpp       |  2 +-
>  .../v4l2_videodevice_test.cpp                 | 16 +++++++-
>  test/v4l2_videodevice/v4l2_videodevice_test.h |  4 +-
>  19 files changed, 85 insertions(+), 19 deletions(-)
> 
> diff --git a/test/camera/camera_test.h b/test/camera/camera_test.h
> index ffc8a485bfaff836..0f3c15cc3dd34f3c 100644
> --- a/test/camera/camera_test.h
> +++ b/test/camera/camera_test.h
> @@ -17,7 +17,7 @@ class CameraTest : public Test
>  {
>  public:
>  	CameraTest()
> -		: cm_(nullptr) {}
> +		: Test(VIMC), cm_(nullptr) {}
>  
>  protected:
>  	int init();
> diff --git a/test/camera/meson.build b/test/camera/meson.build
> index 35e97ce5de1a72ca..b10b1e76aaec0233 100644
> --- a/test/camera/meson.build
> +++ b/test/camera/meson.build
> @@ -12,5 +12,5 @@ foreach t : camera_tests
>                       dependencies : libcamera_dep,
>                       link_with : test_libraries,
>                       include_directories : test_includes_internal)
> -    test(t[0], exe, suite : 'camera', is_parallel : false)
> +    test(t[0], exe, suite : 'camera')
>  endforeach
> diff --git a/test/controls/control_list.cpp b/test/controls/control_list.cpp
> index c834edc352f5d7d4..0fc3ded2545df2e1 100644
> --- a/test/controls/control_list.cpp
> +++ b/test/controls/control_list.cpp
> @@ -18,6 +18,10 @@ using namespace libcamera;
>  
>  class ControlListTest : public Test
>  {
> +public:
> +	ControlListTest()
> +		: Test(VIMC) {}
> +
>  protected:
>  	int init()
>  	{
> diff --git a/test/controls/meson.build b/test/controls/meson.build
> index f4fc7b947dd9e2a6..724871e3136f369b 100644
> --- a/test/controls/meson.build
> +++ b/test/controls/meson.build
> @@ -9,5 +9,5 @@ foreach t : control_tests
>                       dependencies : libcamera_dep,
>                       link_with : test_libraries,
>                       include_directories : test_includes_internal)
> -    test(t[0], exe, suite : 'controls', is_parallel : false)
> +    test(t[0], exe, suite : 'controls')
>  endforeach
> diff --git a/test/libtest/test.cpp b/test/libtest/test.cpp
> index b119cf19273900d2..cf13fa42dd9fdc5a 100644
> --- a/test/libtest/test.cpp
> +++ b/test/libtest/test.cpp
> @@ -5,11 +5,15 @@
>   * test.cpp - libcamera test base class
>   */
>  
> +#include <fcntl.h>
> +#include <iostream>
>  #include <stdlib.h>
> +#include <sys/stat.h>
>  
>  #include "test.h"
>  
> -Test::Test()
> +Test::Test(TestResource resource)
> +	: resource_(resource)
>  {
>  }
>  
> @@ -25,6 +29,9 @@ int Test::execute()
>  	if (ret)
>  		return errno;
>  
> +	if (!resourceLock())
> +		return TestFail;
> +
>  	ret = init();
>  	if (ret)
>  		return ret;
> @@ -35,3 +42,31 @@ int Test::execute()
>  
>  	return ret;
>  }
> +
> +bool Test::resourceLock()
> +{
> +	const char *path;
> +
> +	switch (resource_) {
> +	case None:
> +		return true;
> +	case VIMC:
> +		path = "/tmp/libcamera-test-vimc";
> +		break;
> +	case VIVID:
> +		path = "/tmp/libcamera-test-vivid";
> +		break;
> +	}
> +
> +	int fd = ::open(path, O_CREAT | O_RDWR, 0666);
> +	if (fd < 0) {
> +		std::cerr << "Failed to open resource lockfile" << std::endl;
> +		perror("FOO");
> +		return false;
> +	}
> +
> +	if (lockf(fd, F_LOCK, 0))
> +		return false;


Is there a way that we can do this at the library level, perhaps within
our media controller layer?



> +
> +	return true;
> +}
> diff --git a/test/libtest/test.h b/test/libtest/test.h
> index ec08bf97c03d563e..259fb589eb621489 100644
> --- a/test/libtest/test.h
> +++ b/test/libtest/test.h
> @@ -15,10 +15,16 @@ enum TestStatus {
>  	TestSkip = 77,
>  };
>  
> +enum TestResource {
> +	None,
> +	VIMC,
> +	VIVID,
> +};
> +
>  class Test
>  {
>  public:
> -	Test();
> +	Test(TestResource resource = None);
>  	virtual ~Test();
>  
>  	int execute();
> @@ -27,6 +33,13 @@ protected:
>  	virtual int init() { return 0; }
>  	virtual int run() = 0;
>  	virtual void cleanup() { }
> +
> +	TestResource resource() { return resource_; }
> +
> +private:
> +	bool resourceLock();
> +
> +	TestResource resource_;
>  };
>  
>  #define TEST_REGISTER(klass)						\
> diff --git a/test/media_device/media_device_test.h b/test/media_device/media_device_test.h
> index cdbd14841d5ccca2..9e34ba86c397a99c 100644
> --- a/test/media_device/media_device_test.h
> +++ b/test/media_device/media_device_test.h
> @@ -20,7 +20,7 @@ class MediaDeviceTest : public Test
>  {
>  public:
>  	MediaDeviceTest()
> -		: media_(nullptr), enumerator_(nullptr) {}
> +		: Test(VIMC), media_(nullptr), enumerator_(nullptr) {}
>  
>  protected:
>  	int init();
> diff --git a/test/media_device/meson.build b/test/media_device/meson.build
> index 6a0e468434b58efe..6a5b24d27aa0f618 100644
> --- a/test/media_device/meson.build
> +++ b/test/media_device/meson.build
> @@ -18,5 +18,5 @@ foreach t : media_device_tests
>                       link_with : [test_libraries, lib_mdev_test],
>                       include_directories : test_includes_internal)
>  
> -    test(t[0], exe, suite : 'media_device', is_parallel : false)
> +    test(t[0], exe, suite : 'media_device')
>  endforeach
> diff --git a/test/v4l2_subdevice/meson.build b/test/v4l2_subdevice/meson.build
> index 0521984b2a787ab9..47676adbe5a33f7e 100644
> --- a/test/v4l2_subdevice/meson.build
> +++ b/test/v4l2_subdevice/meson.build
> @@ -8,5 +8,5 @@ foreach t : v4l2_subdevice_tests
>          dependencies : libcamera_dep,
>          link_with : test_libraries,
>          include_directories : test_includes_internal)
> -    test(t[0], exe, suite : 'v4l2_subdevice', is_parallel : false)
> +    test(t[0], exe, suite : 'v4l2_subdevice')
>  endforeach
> diff --git a/test/v4l2_subdevice/v4l2_subdevice_test.h b/test/v4l2_subdevice/v4l2_subdevice_test.h
> index 96646a155536aadf..a9002834bf8959b2 100644
> --- a/test/v4l2_subdevice/v4l2_subdevice_test.h
> +++ b/test/v4l2_subdevice/v4l2_subdevice_test.h
> @@ -21,7 +21,7 @@ class V4L2SubdeviceTest : public Test
>  {
>  public:
>  	V4L2SubdeviceTest()
> -		: scaler_(nullptr){};
> +		: Test(VIMC), scaler_(nullptr){};
>  
>  protected:
>  	int init() override;
> diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp
> index 1bc478fe8f8e1163..d619713257055fd5 100644
> --- a/test/v4l2_videodevice/buffer_sharing.cpp
> +++ b/test/v4l2_videodevice/buffer_sharing.cpp
> @@ -23,7 +23,7 @@ class BufferSharingTest : public V4L2VideoDeviceTest
>  {
>  public:
>  	BufferSharingTest()
> -		: V4L2VideoDeviceTest("vivid", "vivid-000-vid-cap"),
> +		: V4L2VideoDeviceTest(VIVID, "vivid-000-vid-cap"),
>  		  output_(nullptr), framesCaptured_(0), framesOutput_(0) {}
>  
>  protected:
> diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp
> index cea4fffbf7a5603d..9ae8043b31a68f0e 100644
> --- a/test/v4l2_videodevice/capture_async.cpp
> +++ b/test/v4l2_videodevice/capture_async.cpp
> @@ -18,7 +18,7 @@ class CaptureAsyncTest : public V4L2VideoDeviceTest
>  {
>  public:
>  	CaptureAsyncTest()
> -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0"), frames(0) {}
> +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0"), frames(0) {}
>  
>  	void receiveBuffer(Buffer *buffer)
>  	{
> diff --git a/test/v4l2_videodevice/double_open.cpp b/test/v4l2_videodevice/double_open.cpp
> index 5768d4043d0ba03f..ff1c7b136e803929 100644
> --- a/test/v4l2_videodevice/double_open.cpp
> +++ b/test/v4l2_videodevice/double_open.cpp
> @@ -15,7 +15,7 @@ class DoubleOpen : public V4L2VideoDeviceTest
>  {
>  public:
>  	DoubleOpen()
> -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
>  protected:
>  	int run()
>  	{
> diff --git a/test/v4l2_videodevice/formats.cpp b/test/v4l2_videodevice/formats.cpp
> index ee7d357de0752cae..25cd147f804ba0d5 100644
> --- a/test/v4l2_videodevice/formats.cpp
> +++ b/test/v4l2_videodevice/formats.cpp
> @@ -19,7 +19,7 @@ class Format : public V4L2VideoDeviceTest
>  {
>  public:
>  	Format()
> -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
>  protected:
>  	int run()
>  	{
> diff --git a/test/v4l2_videodevice/meson.build b/test/v4l2_videodevice/meson.build
> index 76be5e142bb63aa6..dc8204872efdc038 100644
> --- a/test/v4l2_videodevice/meson.build
> +++ b/test/v4l2_videodevice/meson.build
> @@ -14,5 +14,5 @@ foreach t : v4l2_videodevice_tests
>                       dependencies : libcamera_dep,
>                       link_with : test_libraries,
>                       include_directories : test_includes_internal)
> -    test(t[0], exe, suite : 'v4l2_videodevice', is_parallel : false)
> +    test(t[0], exe, suite : 'v4l2_videodevice')
>  endforeach
> diff --git a/test/v4l2_videodevice/request_buffers.cpp b/test/v4l2_videodevice/request_buffers.cpp
> index c4aedf7b3cd61e80..bbc4073c363c128a 100644
> --- a/test/v4l2_videodevice/request_buffers.cpp
> +++ b/test/v4l2_videodevice/request_buffers.cpp
> @@ -11,7 +11,7 @@ class RequestBuffersTest : public V4L2VideoDeviceTest
>  {
>  public:
>  	RequestBuffersTest()
> -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
>  
>  protected:
>  	int run()
> diff --git a/test/v4l2_videodevice/stream_on_off.cpp b/test/v4l2_videodevice/stream_on_off.cpp
> index 7664adc4c1f07046..fe021667c44559fa 100644
> --- a/test/v4l2_videodevice/stream_on_off.cpp
> +++ b/test/v4l2_videodevice/stream_on_off.cpp
> @@ -11,7 +11,7 @@ class StreamOnStreamOffTest : public V4L2VideoDeviceTest
>  {
>  public:
>  	StreamOnStreamOffTest()
> -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
>  protected:
>  	int run()
>  	{
> diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.cpp b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> index b26d06ad45197c8f..aa39e455360c868c 100644
> --- a/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> +++ b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> @@ -28,6 +28,20 @@ bool exists(const std::string &path)
>  
>  int V4L2VideoDeviceTest::init()
>  {
> +	const char *driver;
> +
> +	switch (resource()) {
> +	case VIMC:
> +		driver = "vimc";
> +		break;
> +	case VIVID:
> +		driver = "vivid";
> +		break;
> +	default:
> +		std::cerr << "Unkown driver for resource" << std::endl;

s/Unkown/Unknown/


> +		return TestFail;
> +	}
> +
>  	enumerator_ = DeviceEnumerator::create();
>  	if (!enumerator_) {
>  		cerr << "Failed to create device enumerator" << endl;
> @@ -39,7 +53,7 @@ int V4L2VideoDeviceTest::init()
>  		return TestFail;
>  	}
>  
> -	DeviceMatch dm(driver_);
> +	DeviceMatch dm(driver);
>  	dm.add(entity_);
>  
>  	media_ = enumerator_->search(dm);
> diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.h b/test/v4l2_videodevice/v4l2_videodevice_test.h
> index 3321b5a4f98fdb1d..b90a5fca6ce48fdd 100644
> --- a/test/v4l2_videodevice/v4l2_videodevice_test.h
> +++ b/test/v4l2_videodevice/v4l2_videodevice_test.h
> @@ -22,8 +22,8 @@ using namespace libcamera;
>  class V4L2VideoDeviceTest : public Test
>  {
>  public:
> -	V4L2VideoDeviceTest(const char *driver, const char *entity)
> -		: driver_(driver), entity_(entity), capture_(nullptr)
> +	V4L2VideoDeviceTest(TestResource resource, const char *entity)
> +		: Test(resource), entity_(entity), capture_(nullptr)
>  	{
>  	}
>  
>
Laurent Pinchart July 4, 2019, 12:49 p.m. UTC | #2
Hello,

> Hi Niklas,
> 
> On 04/07/2019 02:30, Niklas Söderlund wrote:
> > Instead of blocking any test which require access to a kernel resource
> > (vimc or vivid) from running in parallel move the resource locking into
> > test library and allow meson to execute them in parallel.
> > 
> > Resource locking is achieve using a lockfile for each resource in /tmp

s/achieve/achieved/

> > and lockf(). The call to lockf() blocks until it can acquire the
> > resource and it keeps to lock until the test executable terminates

s/to lock/the lock/
s/terminates/terminates,/

> > closing the file descriptor to the lock file  and freeing the lock. This

s/  / /

> > design ensures the process never deadlocks. The reason a separate lock

s/process never deadlocks/test processes never deadlock/

This is however not entirely true, tests that need to lock multiple
devices (for instance to test buffer sharing) may deadlock if they don't
take the locks in the same order.

Edit: I've now read the rest of the series and I see you're not locking
individual devices, but you lock at the vimc or vivid level. There
should thus be no risk of deadlock indeed.

> > file is needed instead of locking the media device associated with the
> > resource is that such a locking scheme is already used by libcamera
> > itself.
> 
> Can't we instead request the lock from the libcamera locking then ?
> 
> > With this change I cut down the runtime of tests by 50%. The largest
> > increase is from allowing self-contained tests in parallel with tests
> > which require a kernel resource. This is because most of the tests who
> > needs a kernel resource needs vimc so there is heavy contention for that
> > single resource and little gain from meson to execute them in parallel.
> > 
> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > ---
> >  test/camera/camera_test.h                     |  2 +-
> >  test/camera/meson.build                       |  2 +-
> >  test/controls/control_list.cpp                |  4 ++
> >  test/controls/meson.build                     |  2 +-
> >  test/libtest/test.cpp                         | 37 ++++++++++++++++++-
> >  test/libtest/test.h                           | 15 +++++++-
> >  test/media_device/media_device_test.h         |  2 +-
> >  test/media_device/meson.build                 |  2 +-
> >  test/v4l2_subdevice/meson.build               |  2 +-
> >  test/v4l2_subdevice/v4l2_subdevice_test.h     |  2 +-
> >  test/v4l2_videodevice/buffer_sharing.cpp      |  2 +-
> >  test/v4l2_videodevice/capture_async.cpp       |  2 +-
> >  test/v4l2_videodevice/double_open.cpp         |  2 +-
> >  test/v4l2_videodevice/formats.cpp             |  2 +-
> >  test/v4l2_videodevice/meson.build             |  2 +-
> >  test/v4l2_videodevice/request_buffers.cpp     |  2 +-
> >  test/v4l2_videodevice/stream_on_off.cpp       |  2 +-
> >  .../v4l2_videodevice_test.cpp                 | 16 +++++++-
> >  test/v4l2_videodevice/v4l2_videodevice_test.h |  4 +-
> >  19 files changed, 85 insertions(+), 19 deletions(-)
> > 
> > diff --git a/test/camera/camera_test.h b/test/camera/camera_test.h
> > index ffc8a485bfaff836..0f3c15cc3dd34f3c 100644
> > --- a/test/camera/camera_test.h
> > +++ b/test/camera/camera_test.h
> > @@ -17,7 +17,7 @@ class CameraTest : public Test
> >  {
> >  public:
> >  	CameraTest()
> > -		: cm_(nullptr) {}
> > +		: Test(VIMC), cm_(nullptr) {}
> >  
> >  protected:
> >  	int init();
> > diff --git a/test/camera/meson.build b/test/camera/meson.build
> > index 35e97ce5de1a72ca..b10b1e76aaec0233 100644
> > --- a/test/camera/meson.build
> > +++ b/test/camera/meson.build
> > @@ -12,5 +12,5 @@ foreach t : camera_tests
> >                       dependencies : libcamera_dep,
> >                       link_with : test_libraries,
> >                       include_directories : test_includes_internal)
> > -    test(t[0], exe, suite : 'camera', is_parallel : false)
> > +    test(t[0], exe, suite : 'camera')
> >  endforeach
> > diff --git a/test/controls/control_list.cpp b/test/controls/control_list.cpp
> > index c834edc352f5d7d4..0fc3ded2545df2e1 100644
> > --- a/test/controls/control_list.cpp
> > +++ b/test/controls/control_list.cpp
> > @@ -18,6 +18,10 @@ using namespace libcamera;
> >  
> >  class ControlListTest : public Test
> >  {
> > +public:
> > +	ControlListTest()
> > +		: Test(VIMC) {}
> > +
> >  protected:
> >  	int init()
> >  	{
> > diff --git a/test/controls/meson.build b/test/controls/meson.build
> > index f4fc7b947dd9e2a6..724871e3136f369b 100644
> > --- a/test/controls/meson.build
> > +++ b/test/controls/meson.build
> > @@ -9,5 +9,5 @@ foreach t : control_tests
> >                       dependencies : libcamera_dep,
> >                       link_with : test_libraries,
> >                       include_directories : test_includes_internal)
> > -    test(t[0], exe, suite : 'controls', is_parallel : false)
> > +    test(t[0], exe, suite : 'controls')
> >  endforeach
> > diff --git a/test/libtest/test.cpp b/test/libtest/test.cpp
> > index b119cf19273900d2..cf13fa42dd9fdc5a 100644
> > --- a/test/libtest/test.cpp
> > +++ b/test/libtest/test.cpp
> > @@ -5,11 +5,15 @@
> >   * test.cpp - libcamera test base class
> >   */
> >  
> > +#include <fcntl.h>
> > +#include <iostream>
> >  #include <stdlib.h>
> > +#include <sys/stat.h>
> >  
> >  #include "test.h"
> >  
> > -Test::Test()
> > +Test::Test(TestResource resource)
> > +	: resource_(resource)
> >  {
> >  }
> >  
> > @@ -25,6 +29,9 @@ int Test::execute()
> >  	if (ret)
> >  		return errno;
> >  
> > +	if (!resourceLock())
> > +		return TestFail;
> > +
> >  	ret = init();
> >  	if (ret)
> >  		return ret;
> > @@ -35,3 +42,31 @@ int Test::execute()
> >  
> >  	return ret;
> >  }
> > +
> > +bool Test::resourceLock()
> > +{
> > +	const char *path;
> > +
> > +	switch (resource_) {
> > +	case None:
> > +		return true;
> > +	case VIMC:
> > +		path = "/tmp/libcamera-test-vimc";
> > +		break;
> > +	case VIVID:
> > +		path = "/tmp/libcamera-test-vivid";
> > +		break;
> > +	}
> > +
> > +	int fd = ::open(path, O_CREAT | O_RDWR, 0666);
> > +	if (fd < 0) {
> > +		std::cerr << "Failed to open resource lockfile" << std::endl;
> > +		perror("FOO");
> > +		return false;
> > +	}
> > +
> > +	if (lockf(fd, F_LOCK, 0))
> > +		return false;
> 
> Is there a way that we can do this at the library level, perhaps within
> our media controller layer?

I wonder if we should rework this by making resources more explicitly
managed. I especially foresee the need to lock multiple resources, so
handling this at the media device level could indeed be a good idea.
Would it make sense to add a lock function that takes a list of media
devices and tries to lock them all ? We could avoid AB-BA deadlocks by
sorting the resources in the lock function. I think I would prefer for
the lock function to be called explicitly in the init() or run()
handler, instead of modifying the base Test constructor. We could add a
MediaResourceLocker (the name can be discussed) class that could serve
as a base of all tests that require media devices and add the required
features there.

> > +
> > +	return true;
> > +}
> > diff --git a/test/libtest/test.h b/test/libtest/test.h
> > index ec08bf97c03d563e..259fb589eb621489 100644
> > --- a/test/libtest/test.h
> > +++ b/test/libtest/test.h
> > @@ -15,10 +15,16 @@ enum TestStatus {
> >  	TestSkip = 77,
> >  };
> >  
> > +enum TestResource {
> > +	None,
> > +	VIMC,
> > +	VIVID,
> > +};
> > +
> >  class Test
> >  {
> >  public:
> > -	Test();
> > +	Test(TestResource resource = None);
> >  	virtual ~Test();
> >  
> >  	int execute();
> > @@ -27,6 +33,13 @@ protected:
> >  	virtual int init() { return 0; }
> >  	virtual int run() = 0;
> >  	virtual void cleanup() { }
> > +
> > +	TestResource resource() { return resource_; }
> > +
> > +private:
> > +	bool resourceLock();
> > +
> > +	TestResource resource_;
> >  };
> >  
> >  #define TEST_REGISTER(klass)						\
> > diff --git a/test/media_device/media_device_test.h b/test/media_device/media_device_test.h
> > index cdbd14841d5ccca2..9e34ba86c397a99c 100644
> > --- a/test/media_device/media_device_test.h
> > +++ b/test/media_device/media_device_test.h
> > @@ -20,7 +20,7 @@ class MediaDeviceTest : public Test
> >  {
> >  public:
> >  	MediaDeviceTest()
> > -		: media_(nullptr), enumerator_(nullptr) {}
> > +		: Test(VIMC), media_(nullptr), enumerator_(nullptr) {}
> >  
> >  protected:
> >  	int init();
> > diff --git a/test/media_device/meson.build b/test/media_device/meson.build
> > index 6a0e468434b58efe..6a5b24d27aa0f618 100644
> > --- a/test/media_device/meson.build
> > +++ b/test/media_device/meson.build
> > @@ -18,5 +18,5 @@ foreach t : media_device_tests
> >                       link_with : [test_libraries, lib_mdev_test],
> >                       include_directories : test_includes_internal)
> >  
> > -    test(t[0], exe, suite : 'media_device', is_parallel : false)
> > +    test(t[0], exe, suite : 'media_device')
> >  endforeach
> > diff --git a/test/v4l2_subdevice/meson.build b/test/v4l2_subdevice/meson.build
> > index 0521984b2a787ab9..47676adbe5a33f7e 100644
> > --- a/test/v4l2_subdevice/meson.build
> > +++ b/test/v4l2_subdevice/meson.build
> > @@ -8,5 +8,5 @@ foreach t : v4l2_subdevice_tests
> >          dependencies : libcamera_dep,
> >          link_with : test_libraries,
> >          include_directories : test_includes_internal)
> > -    test(t[0], exe, suite : 'v4l2_subdevice', is_parallel : false)
> > +    test(t[0], exe, suite : 'v4l2_subdevice')
> >  endforeach
> > diff --git a/test/v4l2_subdevice/v4l2_subdevice_test.h b/test/v4l2_subdevice/v4l2_subdevice_test.h
> > index 96646a155536aadf..a9002834bf8959b2 100644
> > --- a/test/v4l2_subdevice/v4l2_subdevice_test.h
> > +++ b/test/v4l2_subdevice/v4l2_subdevice_test.h
> > @@ -21,7 +21,7 @@ class V4L2SubdeviceTest : public Test
> >  {
> >  public:
> >  	V4L2SubdeviceTest()
> > -		: scaler_(nullptr){};
> > +		: Test(VIMC), scaler_(nullptr){};
> >  
> >  protected:
> >  	int init() override;
> > diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp
> > index 1bc478fe8f8e1163..d619713257055fd5 100644
> > --- a/test/v4l2_videodevice/buffer_sharing.cpp
> > +++ b/test/v4l2_videodevice/buffer_sharing.cpp
> > @@ -23,7 +23,7 @@ class BufferSharingTest : public V4L2VideoDeviceTest
> >  {
> >  public:
> >  	BufferSharingTest()
> > -		: V4L2VideoDeviceTest("vivid", "vivid-000-vid-cap"),
> > +		: V4L2VideoDeviceTest(VIVID, "vivid-000-vid-cap"),
> >  		  output_(nullptr), framesCaptured_(0), framesOutput_(0) {}
> >  
> >  protected:
> > diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp
> > index cea4fffbf7a5603d..9ae8043b31a68f0e 100644
> > --- a/test/v4l2_videodevice/capture_async.cpp
> > +++ b/test/v4l2_videodevice/capture_async.cpp
> > @@ -18,7 +18,7 @@ class CaptureAsyncTest : public V4L2VideoDeviceTest
> >  {
> >  public:
> >  	CaptureAsyncTest()
> > -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0"), frames(0) {}
> > +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0"), frames(0) {}
> >  
> >  	void receiveBuffer(Buffer *buffer)
> >  	{
> > diff --git a/test/v4l2_videodevice/double_open.cpp b/test/v4l2_videodevice/double_open.cpp
> > index 5768d4043d0ba03f..ff1c7b136e803929 100644
> > --- a/test/v4l2_videodevice/double_open.cpp
> > +++ b/test/v4l2_videodevice/double_open.cpp
> > @@ -15,7 +15,7 @@ class DoubleOpen : public V4L2VideoDeviceTest
> >  {
> >  public:
> >  	DoubleOpen()
> > -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> > +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
> >  protected:
> >  	int run()
> >  	{
> > diff --git a/test/v4l2_videodevice/formats.cpp b/test/v4l2_videodevice/formats.cpp
> > index ee7d357de0752cae..25cd147f804ba0d5 100644
> > --- a/test/v4l2_videodevice/formats.cpp
> > +++ b/test/v4l2_videodevice/formats.cpp
> > @@ -19,7 +19,7 @@ class Format : public V4L2VideoDeviceTest
> >  {
> >  public:
> >  	Format()
> > -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> > +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
> >  protected:
> >  	int run()
> >  	{
> > diff --git a/test/v4l2_videodevice/meson.build b/test/v4l2_videodevice/meson.build
> > index 76be5e142bb63aa6..dc8204872efdc038 100644
> > --- a/test/v4l2_videodevice/meson.build
> > +++ b/test/v4l2_videodevice/meson.build
> > @@ -14,5 +14,5 @@ foreach t : v4l2_videodevice_tests
> >                       dependencies : libcamera_dep,
> >                       link_with : test_libraries,
> >                       include_directories : test_includes_internal)
> > -    test(t[0], exe, suite : 'v4l2_videodevice', is_parallel : false)
> > +    test(t[0], exe, suite : 'v4l2_videodevice')
> >  endforeach
> > diff --git a/test/v4l2_videodevice/request_buffers.cpp b/test/v4l2_videodevice/request_buffers.cpp
> > index c4aedf7b3cd61e80..bbc4073c363c128a 100644
> > --- a/test/v4l2_videodevice/request_buffers.cpp
> > +++ b/test/v4l2_videodevice/request_buffers.cpp
> > @@ -11,7 +11,7 @@ class RequestBuffersTest : public V4L2VideoDeviceTest
> >  {
> >  public:
> >  	RequestBuffersTest()
> > -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> > +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
> >  
> >  protected:
> >  	int run()
> > diff --git a/test/v4l2_videodevice/stream_on_off.cpp b/test/v4l2_videodevice/stream_on_off.cpp
> > index 7664adc4c1f07046..fe021667c44559fa 100644
> > --- a/test/v4l2_videodevice/stream_on_off.cpp
> > +++ b/test/v4l2_videodevice/stream_on_off.cpp
> > @@ -11,7 +11,7 @@ class StreamOnStreamOffTest : public V4L2VideoDeviceTest
> >  {
> >  public:
> >  	StreamOnStreamOffTest()
> > -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> > +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
> >  protected:
> >  	int run()
> >  	{
> > diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.cpp b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> > index b26d06ad45197c8f..aa39e455360c868c 100644
> > --- a/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> > +++ b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> > @@ -28,6 +28,20 @@ bool exists(const std::string &path)
> >  
> >  int V4L2VideoDeviceTest::init()
> >  {
> > +	const char *driver;
> > +
> > +	switch (resource()) {
> > +	case VIMC:
> > +		driver = "vimc";
> > +		break;
> > +	case VIVID:
> > +		driver = "vivid";
> > +		break;
> > +	default:
> > +		std::cerr << "Unkown driver for resource" << std::endl;
> 
> s/Unkown/Unknown/
> 
> > +		return TestFail;
> > +	}
> > +
> >  	enumerator_ = DeviceEnumerator::create();
> >  	if (!enumerator_) {
> >  		cerr << "Failed to create device enumerator" << endl;
> > @@ -39,7 +53,7 @@ int V4L2VideoDeviceTest::init()
> >  		return TestFail;
> >  	}
> >  
> > -	DeviceMatch dm(driver_);
> > +	DeviceMatch dm(driver);
> >  	dm.add(entity_);
> >  
> >  	media_ = enumerator_->search(dm);
> > diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.h b/test/v4l2_videodevice/v4l2_videodevice_test.h
> > index 3321b5a4f98fdb1d..b90a5fca6ce48fdd 100644
> > --- a/test/v4l2_videodevice/v4l2_videodevice_test.h
> > +++ b/test/v4l2_videodevice/v4l2_videodevice_test.h
> > @@ -22,8 +22,8 @@ using namespace libcamera;
> >  class V4L2VideoDeviceTest : public Test
> >  {
> >  public:
> > -	V4L2VideoDeviceTest(const char *driver, const char *entity)
> > -		: driver_(driver), entity_(entity), capture_(nullptr)
> > +	V4L2VideoDeviceTest(TestResource resource, const char *entity)
> > +		: Test(resource), entity_(entity), capture_(nullptr)
> >  	{
> >  	}
> >
Niklas Söderlund July 4, 2019, 2:40 p.m. UTC | #3
Hi Laurent, Kieran,

Thanks for your feedback.

On 2019-07-04 15:49:53 +0300, Laurent Pinchart wrote:
> Hello,
> 
> > Hi Niklas,
> > 
> > On 04/07/2019 02:30, Niklas Söderlund wrote:
> > > Instead of blocking any test which require access to a kernel resource
> > > (vimc or vivid) from running in parallel move the resource locking into
> > > test library and allow meson to execute them in parallel.
> > > 
> > > Resource locking is achieve using a lockfile for each resource in /tmp
> 
> s/achieve/achieved/
> 
> > > and lockf(). The call to lockf() blocks until it can acquire the
> > > resource and it keeps to lock until the test executable terminates
> 
> s/to lock/the lock/
> s/terminates/terminates,/
> 
> > > closing the file descriptor to the lock file  and freeing the lock. This
> 
> s/  / /
> 
> > > design ensures the process never deadlocks. The reason a separate lock
> 
> s/process never deadlocks/test processes never deadlock/
> 
> This is however not entirely true, tests that need to lock multiple
> devices (for instance to test buffer sharing) may deadlock if they don't
> take the locks in the same order.
> 
> Edit: I've now read the rest of the series and I see you're not locking
> individual devices, but you lock at the vimc or vivid level. There
> should thus be no risk of deadlock indeed.
> 
> > > file is needed instead of locking the media device associated with the
> > > resource is that such a locking scheme is already used by libcamera
> > > itself.
> > 
> > Can't we instead request the lock from the libcamera locking then ?
> > 
> > > With this change I cut down the runtime of tests by 50%. The largest
> > > increase is from allowing self-contained tests in parallel with tests
> > > which require a kernel resource. This is because most of the tests who
> > > needs a kernel resource needs vimc so there is heavy contention for that
> > > single resource and little gain from meson to execute them in parallel.
> > > 
> > > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > > ---
> > >  test/camera/camera_test.h                     |  2 +-
> > >  test/camera/meson.build                       |  2 +-
> > >  test/controls/control_list.cpp                |  4 ++
> > >  test/controls/meson.build                     |  2 +-
> > >  test/libtest/test.cpp                         | 37 ++++++++++++++++++-
> > >  test/libtest/test.h                           | 15 +++++++-
> > >  test/media_device/media_device_test.h         |  2 +-
> > >  test/media_device/meson.build                 |  2 +-
> > >  test/v4l2_subdevice/meson.build               |  2 +-
> > >  test/v4l2_subdevice/v4l2_subdevice_test.h     |  2 +-
> > >  test/v4l2_videodevice/buffer_sharing.cpp      |  2 +-
> > >  test/v4l2_videodevice/capture_async.cpp       |  2 +-
> > >  test/v4l2_videodevice/double_open.cpp         |  2 +-
> > >  test/v4l2_videodevice/formats.cpp             |  2 +-
> > >  test/v4l2_videodevice/meson.build             |  2 +-
> > >  test/v4l2_videodevice/request_buffers.cpp     |  2 +-
> > >  test/v4l2_videodevice/stream_on_off.cpp       |  2 +-
> > >  .../v4l2_videodevice_test.cpp                 | 16 +++++++-
> > >  test/v4l2_videodevice/v4l2_videodevice_test.h |  4 +-
> > >  19 files changed, 85 insertions(+), 19 deletions(-)
> > > 
> > > diff --git a/test/camera/camera_test.h b/test/camera/camera_test.h
> > > index ffc8a485bfaff836..0f3c15cc3dd34f3c 100644
> > > --- a/test/camera/camera_test.h
> > > +++ b/test/camera/camera_test.h
> > > @@ -17,7 +17,7 @@ class CameraTest : public Test
> > >  {
> > >  public:
> > >  	CameraTest()
> > > -		: cm_(nullptr) {}
> > > +		: Test(VIMC), cm_(nullptr) {}
> > >  
> > >  protected:
> > >  	int init();
> > > diff --git a/test/camera/meson.build b/test/camera/meson.build
> > > index 35e97ce5de1a72ca..b10b1e76aaec0233 100644
> > > --- a/test/camera/meson.build
> > > +++ b/test/camera/meson.build
> > > @@ -12,5 +12,5 @@ foreach t : camera_tests
> > >                       dependencies : libcamera_dep,
> > >                       link_with : test_libraries,
> > >                       include_directories : test_includes_internal)
> > > -    test(t[0], exe, suite : 'camera', is_parallel : false)
> > > +    test(t[0], exe, suite : 'camera')
> > >  endforeach
> > > diff --git a/test/controls/control_list.cpp b/test/controls/control_list.cpp
> > > index c834edc352f5d7d4..0fc3ded2545df2e1 100644
> > > --- a/test/controls/control_list.cpp
> > > +++ b/test/controls/control_list.cpp
> > > @@ -18,6 +18,10 @@ using namespace libcamera;
> > >  
> > >  class ControlListTest : public Test
> > >  {
> > > +public:
> > > +	ControlListTest()
> > > +		: Test(VIMC) {}
> > > +
> > >  protected:
> > >  	int init()
> > >  	{
> > > diff --git a/test/controls/meson.build b/test/controls/meson.build
> > > index f4fc7b947dd9e2a6..724871e3136f369b 100644
> > > --- a/test/controls/meson.build
> > > +++ b/test/controls/meson.build
> > > @@ -9,5 +9,5 @@ foreach t : control_tests
> > >                       dependencies : libcamera_dep,
> > >                       link_with : test_libraries,
> > >                       include_directories : test_includes_internal)
> > > -    test(t[0], exe, suite : 'controls', is_parallel : false)
> > > +    test(t[0], exe, suite : 'controls')
> > >  endforeach
> > > diff --git a/test/libtest/test.cpp b/test/libtest/test.cpp
> > > index b119cf19273900d2..cf13fa42dd9fdc5a 100644
> > > --- a/test/libtest/test.cpp
> > > +++ b/test/libtest/test.cpp
> > > @@ -5,11 +5,15 @@
> > >   * test.cpp - libcamera test base class
> > >   */
> > >  
> > > +#include <fcntl.h>
> > > +#include <iostream>
> > >  #include <stdlib.h>
> > > +#include <sys/stat.h>
> > >  
> > >  #include "test.h"
> > >  
> > > -Test::Test()
> > > +Test::Test(TestResource resource)
> > > +	: resource_(resource)
> > >  {
> > >  }
> > >  
> > > @@ -25,6 +29,9 @@ int Test::execute()
> > >  	if (ret)
> > >  		return errno;
> > >  
> > > +	if (!resourceLock())
> > > +		return TestFail;
> > > +
> > >  	ret = init();
> > >  	if (ret)
> > >  		return ret;
> > > @@ -35,3 +42,31 @@ int Test::execute()
> > >  
> > >  	return ret;
> > >  }
> > > +
> > > +bool Test::resourceLock()
> > > +{
> > > +	const char *path;
> > > +
> > > +	switch (resource_) {
> > > +	case None:
> > > +		return true;
> > > +	case VIMC:
> > > +		path = "/tmp/libcamera-test-vimc";
> > > +		break;
> > > +	case VIVID:
> > > +		path = "/tmp/libcamera-test-vivid";
> > > +		break;
> > > +	}
> > > +
> > > +	int fd = ::open(path, O_CREAT | O_RDWR, 0666);
> > > +	if (fd < 0) {
> > > +		std::cerr << "Failed to open resource lockfile" << std::endl;
> > > +		perror("FOO");
> > > +		return false;
> > > +	}
> > > +
> > > +	if (lockf(fd, F_LOCK, 0))
> > > +		return false;
> > 
> > Is there a way that we can do this at the library level, perhaps within
> > our media controller layer?

I don't feel it's a good idea to add this functionality in libcameras 
media controller layer as it's only use is for our testcases. We also 
have tests which do not start the camera manager and access the v4l2 
devices directly to unit test individual libcamera objects.

> 
> I wonder if we should rework this by making resources more explicitly
> managed. I especially foresee the need to lock multiple resources, so
> handling this at the media device level could indeed be a good idea.

We can not place a lockf() call on the media devices as this is already 
used inside libcamera to manage concurrent libcamera instances.

> Would it make sense to add a lock function that takes a list of media
> devices and tries to lock them all ? We could avoid AB-BA deadlocks by
> sorting the resources in the lock function. I think I would prefer for
> the lock function to be called explicitly in the init() or run()
> handler, instead of modifying the base Test constructor. We could add a
> MediaResourceLocker (the name can be discussed) class that could serve
> as a base of all tests that require media devices and add the required
> features there.

This is indeed an idea which could be explored further. I will see what 
I can dream up for a v1.

> 
> > > +
> > > +	return true;
> > > +}
> > > diff --git a/test/libtest/test.h b/test/libtest/test.h
> > > index ec08bf97c03d563e..259fb589eb621489 100644
> > > --- a/test/libtest/test.h
> > > +++ b/test/libtest/test.h
> > > @@ -15,10 +15,16 @@ enum TestStatus {
> > >  	TestSkip = 77,
> > >  };
> > >  
> > > +enum TestResource {
> > > +	None,
> > > +	VIMC,
> > > +	VIVID,
> > > +};
> > > +
> > >  class Test
> > >  {
> > >  public:
> > > -	Test();
> > > +	Test(TestResource resource = None);
> > >  	virtual ~Test();
> > >  
> > >  	int execute();
> > > @@ -27,6 +33,13 @@ protected:
> > >  	virtual int init() { return 0; }
> > >  	virtual int run() = 0;
> > >  	virtual void cleanup() { }
> > > +
> > > +	TestResource resource() { return resource_; }
> > > +
> > > +private:
> > > +	bool resourceLock();
> > > +
> > > +	TestResource resource_;
> > >  };
> > >  
> > >  #define TEST_REGISTER(klass)						\
> > > diff --git a/test/media_device/media_device_test.h b/test/media_device/media_device_test.h
> > > index cdbd14841d5ccca2..9e34ba86c397a99c 100644
> > > --- a/test/media_device/media_device_test.h
> > > +++ b/test/media_device/media_device_test.h
> > > @@ -20,7 +20,7 @@ class MediaDeviceTest : public Test
> > >  {
> > >  public:
> > >  	MediaDeviceTest()
> > > -		: media_(nullptr), enumerator_(nullptr) {}
> > > +		: Test(VIMC), media_(nullptr), enumerator_(nullptr) {}
> > >  
> > >  protected:
> > >  	int init();
> > > diff --git a/test/media_device/meson.build b/test/media_device/meson.build
> > > index 6a0e468434b58efe..6a5b24d27aa0f618 100644
> > > --- a/test/media_device/meson.build
> > > +++ b/test/media_device/meson.build
> > > @@ -18,5 +18,5 @@ foreach t : media_device_tests
> > >                       link_with : [test_libraries, lib_mdev_test],
> > >                       include_directories : test_includes_internal)
> > >  
> > > -    test(t[0], exe, suite : 'media_device', is_parallel : false)
> > > +    test(t[0], exe, suite : 'media_device')
> > >  endforeach
> > > diff --git a/test/v4l2_subdevice/meson.build b/test/v4l2_subdevice/meson.build
> > > index 0521984b2a787ab9..47676adbe5a33f7e 100644
> > > --- a/test/v4l2_subdevice/meson.build
> > > +++ b/test/v4l2_subdevice/meson.build
> > > @@ -8,5 +8,5 @@ foreach t : v4l2_subdevice_tests
> > >          dependencies : libcamera_dep,
> > >          link_with : test_libraries,
> > >          include_directories : test_includes_internal)
> > > -    test(t[0], exe, suite : 'v4l2_subdevice', is_parallel : false)
> > > +    test(t[0], exe, suite : 'v4l2_subdevice')
> > >  endforeach
> > > diff --git a/test/v4l2_subdevice/v4l2_subdevice_test.h b/test/v4l2_subdevice/v4l2_subdevice_test.h
> > > index 96646a155536aadf..a9002834bf8959b2 100644
> > > --- a/test/v4l2_subdevice/v4l2_subdevice_test.h
> > > +++ b/test/v4l2_subdevice/v4l2_subdevice_test.h
> > > @@ -21,7 +21,7 @@ class V4L2SubdeviceTest : public Test
> > >  {
> > >  public:
> > >  	V4L2SubdeviceTest()
> > > -		: scaler_(nullptr){};
> > > +		: Test(VIMC), scaler_(nullptr){};
> > >  
> > >  protected:
> > >  	int init() override;
> > > diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp
> > > index 1bc478fe8f8e1163..d619713257055fd5 100644
> > > --- a/test/v4l2_videodevice/buffer_sharing.cpp
> > > +++ b/test/v4l2_videodevice/buffer_sharing.cpp
> > > @@ -23,7 +23,7 @@ class BufferSharingTest : public V4L2VideoDeviceTest
> > >  {
> > >  public:
> > >  	BufferSharingTest()
> > > -		: V4L2VideoDeviceTest("vivid", "vivid-000-vid-cap"),
> > > +		: V4L2VideoDeviceTest(VIVID, "vivid-000-vid-cap"),
> > >  		  output_(nullptr), framesCaptured_(0), framesOutput_(0) {}
> > >  
> > >  protected:
> > > diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp
> > > index cea4fffbf7a5603d..9ae8043b31a68f0e 100644
> > > --- a/test/v4l2_videodevice/capture_async.cpp
> > > +++ b/test/v4l2_videodevice/capture_async.cpp
> > > @@ -18,7 +18,7 @@ class CaptureAsyncTest : public V4L2VideoDeviceTest
> > >  {
> > >  public:
> > >  	CaptureAsyncTest()
> > > -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0"), frames(0) {}
> > > +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0"), frames(0) {}
> > >  
> > >  	void receiveBuffer(Buffer *buffer)
> > >  	{
> > > diff --git a/test/v4l2_videodevice/double_open.cpp b/test/v4l2_videodevice/double_open.cpp
> > > index 5768d4043d0ba03f..ff1c7b136e803929 100644
> > > --- a/test/v4l2_videodevice/double_open.cpp
> > > +++ b/test/v4l2_videodevice/double_open.cpp
> > > @@ -15,7 +15,7 @@ class DoubleOpen : public V4L2VideoDeviceTest
> > >  {
> > >  public:
> > >  	DoubleOpen()
> > > -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> > > +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
> > >  protected:
> > >  	int run()
> > >  	{
> > > diff --git a/test/v4l2_videodevice/formats.cpp b/test/v4l2_videodevice/formats.cpp
> > > index ee7d357de0752cae..25cd147f804ba0d5 100644
> > > --- a/test/v4l2_videodevice/formats.cpp
> > > +++ b/test/v4l2_videodevice/formats.cpp
> > > @@ -19,7 +19,7 @@ class Format : public V4L2VideoDeviceTest
> > >  {
> > >  public:
> > >  	Format()
> > > -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> > > +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
> > >  protected:
> > >  	int run()
> > >  	{
> > > diff --git a/test/v4l2_videodevice/meson.build b/test/v4l2_videodevice/meson.build
> > > index 76be5e142bb63aa6..dc8204872efdc038 100644
> > > --- a/test/v4l2_videodevice/meson.build
> > > +++ b/test/v4l2_videodevice/meson.build
> > > @@ -14,5 +14,5 @@ foreach t : v4l2_videodevice_tests
> > >                       dependencies : libcamera_dep,
> > >                       link_with : test_libraries,
> > >                       include_directories : test_includes_internal)
> > > -    test(t[0], exe, suite : 'v4l2_videodevice', is_parallel : false)
> > > +    test(t[0], exe, suite : 'v4l2_videodevice')
> > >  endforeach
> > > diff --git a/test/v4l2_videodevice/request_buffers.cpp b/test/v4l2_videodevice/request_buffers.cpp
> > > index c4aedf7b3cd61e80..bbc4073c363c128a 100644
> > > --- a/test/v4l2_videodevice/request_buffers.cpp
> > > +++ b/test/v4l2_videodevice/request_buffers.cpp
> > > @@ -11,7 +11,7 @@ class RequestBuffersTest : public V4L2VideoDeviceTest
> > >  {
> > >  public:
> > >  	RequestBuffersTest()
> > > -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> > > +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
> > >  
> > >  protected:
> > >  	int run()
> > > diff --git a/test/v4l2_videodevice/stream_on_off.cpp b/test/v4l2_videodevice/stream_on_off.cpp
> > > index 7664adc4c1f07046..fe021667c44559fa 100644
> > > --- a/test/v4l2_videodevice/stream_on_off.cpp
> > > +++ b/test/v4l2_videodevice/stream_on_off.cpp
> > > @@ -11,7 +11,7 @@ class StreamOnStreamOffTest : public V4L2VideoDeviceTest
> > >  {
> > >  public:
> > >  	StreamOnStreamOffTest()
> > > -		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
> > > +		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
> > >  protected:
> > >  	int run()
> > >  	{
> > > diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.cpp b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> > > index b26d06ad45197c8f..aa39e455360c868c 100644
> > > --- a/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> > > +++ b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
> > > @@ -28,6 +28,20 @@ bool exists(const std::string &path)
> > >  
> > >  int V4L2VideoDeviceTest::init()
> > >  {
> > > +	const char *driver;
> > > +
> > > +	switch (resource()) {
> > > +	case VIMC:
> > > +		driver = "vimc";
> > > +		break;
> > > +	case VIVID:
> > > +		driver = "vivid";
> > > +		break;
> > > +	default:
> > > +		std::cerr << "Unkown driver for resource" << std::endl;
> > 
> > s/Unkown/Unknown/
> > 
> > > +		return TestFail;
> > > +	}
> > > +
> > >  	enumerator_ = DeviceEnumerator::create();
> > >  	if (!enumerator_) {
> > >  		cerr << "Failed to create device enumerator" << endl;
> > > @@ -39,7 +53,7 @@ int V4L2VideoDeviceTest::init()
> > >  		return TestFail;
> > >  	}
> > >  
> > > -	DeviceMatch dm(driver_);
> > > +	DeviceMatch dm(driver);
> > >  	dm.add(entity_);
> > >  
> > >  	media_ = enumerator_->search(dm);
> > > diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.h b/test/v4l2_videodevice/v4l2_videodevice_test.h
> > > index 3321b5a4f98fdb1d..b90a5fca6ce48fdd 100644
> > > --- a/test/v4l2_videodevice/v4l2_videodevice_test.h
> > > +++ b/test/v4l2_videodevice/v4l2_videodevice_test.h
> > > @@ -22,8 +22,8 @@ using namespace libcamera;
> > >  class V4L2VideoDeviceTest : public Test
> > >  {
> > >  public:
> > > -	V4L2VideoDeviceTest(const char *driver, const char *entity)
> > > -		: driver_(driver), entity_(entity), capture_(nullptr)
> > > +	V4L2VideoDeviceTest(TestResource resource, const char *entity)
> > > +		: Test(resource), entity_(entity), capture_(nullptr)
> > >  	{
> > >  	}
> > >  
> 
> -- 
> Regards,
> 
> Laurent Pinchart

Patch

diff --git a/test/camera/camera_test.h b/test/camera/camera_test.h
index ffc8a485bfaff836..0f3c15cc3dd34f3c 100644
--- a/test/camera/camera_test.h
+++ b/test/camera/camera_test.h
@@ -17,7 +17,7 @@  class CameraTest : public Test
 {
 public:
 	CameraTest()
-		: cm_(nullptr) {}
+		: Test(VIMC), cm_(nullptr) {}
 
 protected:
 	int init();
diff --git a/test/camera/meson.build b/test/camera/meson.build
index 35e97ce5de1a72ca..b10b1e76aaec0233 100644
--- a/test/camera/meson.build
+++ b/test/camera/meson.build
@@ -12,5 +12,5 @@  foreach t : camera_tests
                      dependencies : libcamera_dep,
                      link_with : test_libraries,
                      include_directories : test_includes_internal)
-    test(t[0], exe, suite : 'camera', is_parallel : false)
+    test(t[0], exe, suite : 'camera')
 endforeach
diff --git a/test/controls/control_list.cpp b/test/controls/control_list.cpp
index c834edc352f5d7d4..0fc3ded2545df2e1 100644
--- a/test/controls/control_list.cpp
+++ b/test/controls/control_list.cpp
@@ -18,6 +18,10 @@  using namespace libcamera;
 
 class ControlListTest : public Test
 {
+public:
+	ControlListTest()
+		: Test(VIMC) {}
+
 protected:
 	int init()
 	{
diff --git a/test/controls/meson.build b/test/controls/meson.build
index f4fc7b947dd9e2a6..724871e3136f369b 100644
--- a/test/controls/meson.build
+++ b/test/controls/meson.build
@@ -9,5 +9,5 @@  foreach t : control_tests
                      dependencies : libcamera_dep,
                      link_with : test_libraries,
                      include_directories : test_includes_internal)
-    test(t[0], exe, suite : 'controls', is_parallel : false)
+    test(t[0], exe, suite : 'controls')
 endforeach
diff --git a/test/libtest/test.cpp b/test/libtest/test.cpp
index b119cf19273900d2..cf13fa42dd9fdc5a 100644
--- a/test/libtest/test.cpp
+++ b/test/libtest/test.cpp
@@ -5,11 +5,15 @@ 
  * test.cpp - libcamera test base class
  */
 
+#include <fcntl.h>
+#include <iostream>
 #include <stdlib.h>
+#include <sys/stat.h>
 
 #include "test.h"
 
-Test::Test()
+Test::Test(TestResource resource)
+	: resource_(resource)
 {
 }
 
@@ -25,6 +29,9 @@  int Test::execute()
 	if (ret)
 		return errno;
 
+	if (!resourceLock())
+		return TestFail;
+
 	ret = init();
 	if (ret)
 		return ret;
@@ -35,3 +42,31 @@  int Test::execute()
 
 	return ret;
 }
+
+bool Test::resourceLock()
+{
+	const char *path;
+
+	switch (resource_) {
+	case None:
+		return true;
+	case VIMC:
+		path = "/tmp/libcamera-test-vimc";
+		break;
+	case VIVID:
+		path = "/tmp/libcamera-test-vivid";
+		break;
+	}
+
+	int fd = ::open(path, O_CREAT | O_RDWR, 0666);
+	if (fd < 0) {
+		std::cerr << "Failed to open resource lockfile" << std::endl;
+		perror("FOO");
+		return false;
+	}
+
+	if (lockf(fd, F_LOCK, 0))
+		return false;
+
+	return true;
+}
diff --git a/test/libtest/test.h b/test/libtest/test.h
index ec08bf97c03d563e..259fb589eb621489 100644
--- a/test/libtest/test.h
+++ b/test/libtest/test.h
@@ -15,10 +15,16 @@  enum TestStatus {
 	TestSkip = 77,
 };
 
+enum TestResource {
+	None,
+	VIMC,
+	VIVID,
+};
+
 class Test
 {
 public:
-	Test();
+	Test(TestResource resource = None);
 	virtual ~Test();
 
 	int execute();
@@ -27,6 +33,13 @@  protected:
 	virtual int init() { return 0; }
 	virtual int run() = 0;
 	virtual void cleanup() { }
+
+	TestResource resource() { return resource_; }
+
+private:
+	bool resourceLock();
+
+	TestResource resource_;
 };
 
 #define TEST_REGISTER(klass)						\
diff --git a/test/media_device/media_device_test.h b/test/media_device/media_device_test.h
index cdbd14841d5ccca2..9e34ba86c397a99c 100644
--- a/test/media_device/media_device_test.h
+++ b/test/media_device/media_device_test.h
@@ -20,7 +20,7 @@  class MediaDeviceTest : public Test
 {
 public:
 	MediaDeviceTest()
-		: media_(nullptr), enumerator_(nullptr) {}
+		: Test(VIMC), media_(nullptr), enumerator_(nullptr) {}
 
 protected:
 	int init();
diff --git a/test/media_device/meson.build b/test/media_device/meson.build
index 6a0e468434b58efe..6a5b24d27aa0f618 100644
--- a/test/media_device/meson.build
+++ b/test/media_device/meson.build
@@ -18,5 +18,5 @@  foreach t : media_device_tests
                      link_with : [test_libraries, lib_mdev_test],
                      include_directories : test_includes_internal)
 
-    test(t[0], exe, suite : 'media_device', is_parallel : false)
+    test(t[0], exe, suite : 'media_device')
 endforeach
diff --git a/test/v4l2_subdevice/meson.build b/test/v4l2_subdevice/meson.build
index 0521984b2a787ab9..47676adbe5a33f7e 100644
--- a/test/v4l2_subdevice/meson.build
+++ b/test/v4l2_subdevice/meson.build
@@ -8,5 +8,5 @@  foreach t : v4l2_subdevice_tests
         dependencies : libcamera_dep,
         link_with : test_libraries,
         include_directories : test_includes_internal)
-    test(t[0], exe, suite : 'v4l2_subdevice', is_parallel : false)
+    test(t[0], exe, suite : 'v4l2_subdevice')
 endforeach
diff --git a/test/v4l2_subdevice/v4l2_subdevice_test.h b/test/v4l2_subdevice/v4l2_subdevice_test.h
index 96646a155536aadf..a9002834bf8959b2 100644
--- a/test/v4l2_subdevice/v4l2_subdevice_test.h
+++ b/test/v4l2_subdevice/v4l2_subdevice_test.h
@@ -21,7 +21,7 @@  class V4L2SubdeviceTest : public Test
 {
 public:
 	V4L2SubdeviceTest()
-		: scaler_(nullptr){};
+		: Test(VIMC), scaler_(nullptr){};
 
 protected:
 	int init() override;
diff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp
index 1bc478fe8f8e1163..d619713257055fd5 100644
--- a/test/v4l2_videodevice/buffer_sharing.cpp
+++ b/test/v4l2_videodevice/buffer_sharing.cpp
@@ -23,7 +23,7 @@  class BufferSharingTest : public V4L2VideoDeviceTest
 {
 public:
 	BufferSharingTest()
-		: V4L2VideoDeviceTest("vivid", "vivid-000-vid-cap"),
+		: V4L2VideoDeviceTest(VIVID, "vivid-000-vid-cap"),
 		  output_(nullptr), framesCaptured_(0), framesOutput_(0) {}
 
 protected:
diff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp
index cea4fffbf7a5603d..9ae8043b31a68f0e 100644
--- a/test/v4l2_videodevice/capture_async.cpp
+++ b/test/v4l2_videodevice/capture_async.cpp
@@ -18,7 +18,7 @@  class CaptureAsyncTest : public V4L2VideoDeviceTest
 {
 public:
 	CaptureAsyncTest()
-		: V4L2VideoDeviceTest("vimc", "Raw Capture 0"), frames(0) {}
+		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0"), frames(0) {}
 
 	void receiveBuffer(Buffer *buffer)
 	{
diff --git a/test/v4l2_videodevice/double_open.cpp b/test/v4l2_videodevice/double_open.cpp
index 5768d4043d0ba03f..ff1c7b136e803929 100644
--- a/test/v4l2_videodevice/double_open.cpp
+++ b/test/v4l2_videodevice/double_open.cpp
@@ -15,7 +15,7 @@  class DoubleOpen : public V4L2VideoDeviceTest
 {
 public:
 	DoubleOpen()
-		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
+		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
 protected:
 	int run()
 	{
diff --git a/test/v4l2_videodevice/formats.cpp b/test/v4l2_videodevice/formats.cpp
index ee7d357de0752cae..25cd147f804ba0d5 100644
--- a/test/v4l2_videodevice/formats.cpp
+++ b/test/v4l2_videodevice/formats.cpp
@@ -19,7 +19,7 @@  class Format : public V4L2VideoDeviceTest
 {
 public:
 	Format()
-		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
+		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
 protected:
 	int run()
 	{
diff --git a/test/v4l2_videodevice/meson.build b/test/v4l2_videodevice/meson.build
index 76be5e142bb63aa6..dc8204872efdc038 100644
--- a/test/v4l2_videodevice/meson.build
+++ b/test/v4l2_videodevice/meson.build
@@ -14,5 +14,5 @@  foreach t : v4l2_videodevice_tests
                      dependencies : libcamera_dep,
                      link_with : test_libraries,
                      include_directories : test_includes_internal)
-    test(t[0], exe, suite : 'v4l2_videodevice', is_parallel : false)
+    test(t[0], exe, suite : 'v4l2_videodevice')
 endforeach
diff --git a/test/v4l2_videodevice/request_buffers.cpp b/test/v4l2_videodevice/request_buffers.cpp
index c4aedf7b3cd61e80..bbc4073c363c128a 100644
--- a/test/v4l2_videodevice/request_buffers.cpp
+++ b/test/v4l2_videodevice/request_buffers.cpp
@@ -11,7 +11,7 @@  class RequestBuffersTest : public V4L2VideoDeviceTest
 {
 public:
 	RequestBuffersTest()
-		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
+		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
 
 protected:
 	int run()
diff --git a/test/v4l2_videodevice/stream_on_off.cpp b/test/v4l2_videodevice/stream_on_off.cpp
index 7664adc4c1f07046..fe021667c44559fa 100644
--- a/test/v4l2_videodevice/stream_on_off.cpp
+++ b/test/v4l2_videodevice/stream_on_off.cpp
@@ -11,7 +11,7 @@  class StreamOnStreamOffTest : public V4L2VideoDeviceTest
 {
 public:
 	StreamOnStreamOffTest()
-		: V4L2VideoDeviceTest("vimc", "Raw Capture 0") {}
+		: V4L2VideoDeviceTest(VIMC, "Raw Capture 0") {}
 protected:
 	int run()
 	{
diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.cpp b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
index b26d06ad45197c8f..aa39e455360c868c 100644
--- a/test/v4l2_videodevice/v4l2_videodevice_test.cpp
+++ b/test/v4l2_videodevice/v4l2_videodevice_test.cpp
@@ -28,6 +28,20 @@  bool exists(const std::string &path)
 
 int V4L2VideoDeviceTest::init()
 {
+	const char *driver;
+
+	switch (resource()) {
+	case VIMC:
+		driver = "vimc";
+		break;
+	case VIVID:
+		driver = "vivid";
+		break;
+	default:
+		std::cerr << "Unkown driver for resource" << std::endl;
+		return TestFail;
+	}
+
 	enumerator_ = DeviceEnumerator::create();
 	if (!enumerator_) {
 		cerr << "Failed to create device enumerator" << endl;
@@ -39,7 +53,7 @@  int V4L2VideoDeviceTest::init()
 		return TestFail;
 	}
 
-	DeviceMatch dm(driver_);
+	DeviceMatch dm(driver);
 	dm.add(entity_);
 
 	media_ = enumerator_->search(dm);
diff --git a/test/v4l2_videodevice/v4l2_videodevice_test.h b/test/v4l2_videodevice/v4l2_videodevice_test.h
index 3321b5a4f98fdb1d..b90a5fca6ce48fdd 100644
--- a/test/v4l2_videodevice/v4l2_videodevice_test.h
+++ b/test/v4l2_videodevice/v4l2_videodevice_test.h
@@ -22,8 +22,8 @@  using namespace libcamera;
 class V4L2VideoDeviceTest : public Test
 {
 public:
-	V4L2VideoDeviceTest(const char *driver, const char *entity)
-		: driver_(driver), entity_(entity), capture_(nullptr)
+	V4L2VideoDeviceTest(TestResource resource, const char *entity)
+		: Test(resource), entity_(entity), capture_(nullptr)
 	{
 	}