[libcamera-devel,3/3] test: file_descriptor: Add test

Message ID 20191216121029.1048242-4-niklas.soderlund@ragnatech.se
State Superseded
Headers show
Series
  • libcamera: Add FileDescriptor to help pass numerical fds around
Related show

Commit Message

Niklas Söderlund Dec. 16, 2019, 12:10 p.m. UTC
Add a test which exercises the whole FileDescriptor interface.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
 test/file_descriptor.cpp | 152 +++++++++++++++++++++++++++++++++++++++
 test/meson.build         |   1 +
 2 files changed, 153 insertions(+)
 create mode 100644 test/file_descriptor.cpp

Comments

Laurent Pinchart Dec. 16, 2019, 5:37 p.m. UTC | #1
Hi Niklas,

Thank you for the patch.

On Mon, Dec 16, 2019 at 01:10:29PM +0100, Niklas Söderlund wrote:
> Add a test which exercises the whole FileDescriptor interface.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> ---
>  test/file_descriptor.cpp | 152 +++++++++++++++++++++++++++++++++++++++
>  test/meson.build         |   1 +
>  2 files changed, 153 insertions(+)
>  create mode 100644 test/file_descriptor.cpp
> 
> diff --git a/test/file_descriptor.cpp b/test/file_descriptor.cpp
> new file mode 100644
> index 0000000000000000..b1d0d0e0b2807b20
> --- /dev/null
> +++ b/test/file_descriptor.cpp
> @@ -0,0 +1,152 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * file_descriptor.cpp - FileDescriptor test
> + */
> +
> +#include <fcntl.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +
> +#include <libcamera/file_descriptor.h>
> +
> +#include "test.h"
> +
> +using namespace std;
> +using namespace libcamera;

Alphabetically sorted ?

> +
> +class FileDescriptorTest : public Test
> +{
> +protected:
> +	int init()
> +	{
> +		fd_ = open("/tmp", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
> +		if (fd_ < 0)
> +			return TestFail;
> +
> +		return 0;
> +	}
> +
> +	int run()
> +	{
> +		FileDescriptor *desc1, *desc2;
> +
> +		/* Test creating FileDescriptor from numerical file descriptor. */
> +		desc1 = new FileDescriptor(fd_);
> +		if (desc1->fd() == fd_)

You're leaking desc1. There are plenty of leaks below too. It's
important to avoid leaks in tests, as otherwise valgrind will catch them
and we won't be able to tell whether libcamera or the tests are leaky.
You could make them class members and delete them in the destructor.
Don't forget to set the pointers to nullptr in the constructor, as well
as after every manual delete to avoid double-delete.

> +			return TestFail;
> +
> +		if (!validFD(fd_) || !validFD(desc1->fd()))
> +			return TestFail;
> +
> +		delete desc1;
> +
> +		if (!validFD(fd_))
> +			return TestFail;

I would add

		int fd = desc1->fd();

before the delete, and replace the above check with

		if (!validFD(fd_) || validFD(fd))
			return TestFail;

to ensure that deleting the FileDescriptor closes the managed fd.

> +
> +		/* Test creating FileDescriptor from other FileDescriptor. */
> +		desc1 = new FileDescriptor(fd_);
> +		desc2 = new FileDescriptor(*desc1);
> +
> +		if (desc1->fd() == fd_ || desc2->fd() == fd_ || desc1->fd() == desc2->fd())
> +			return TestFail;
> +
> +		if (!validFD(fd_) || !validFD(desc1->fd()) || !validFD(desc2->fd()))
> +			return TestFail;
> +
> +		delete desc1;
> +
> +		if (!validFD(fd_) || !validFD(desc2->fd()))
> +			return TestFail;
> +
> +		delete desc2;
> +
> +		if (!validFD(fd_))
> +			return TestFail;

I think you can drop the three !validFD(fd_) checks above, as well as
all the same checks below, as this is already tested by the first test
case.

> +
> +		/* Test creating FileDescriptor by taking over other FileDescriptor. */
> +		desc1 = new FileDescriptor(fd_);
> +		desc2 = new FileDescriptor(std::move(*desc1));
> +
> +		if (desc1->fd() != -1 || desc2->fd() == fd_ || desc1->fd() == desc2->fd())
> +			return TestFail;

You want here to make sure that constructing desc2 didn't dup desc1.

		desc1 = new FileDescriptor(fd_);
		fd = desc1->fd();
		desc2 = new FileDescriptor(std::move(*desc1));

		if (desc1->fd() != -1 || desc2->fd() != fd)
			return TestFail;

> +
> +		if (!validFD(fd_) || !validFD(desc2->fd()))
> +			return TestFail;
> +
> +		delete desc1;
> +		delete desc2;
> +
> +		if (!validFD(fd_))
> +			return TestFail;
> +
> +		/* Test creating FileDescriptor by copy assigment */

s/assignment/assignment./

> +		desc1 = new FileDescriptor();
> +		desc2 = new FileDescriptor(fd_);
> +
> +		if (desc1->fd() != -1 || desc2->fd() == fd_ || desc1->fd() == desc2->fd())
> +			return TestFail;
> +
> +		if (!validFD(fd_) || !validFD(desc2->fd()))
> +			return TestFail;

I think you can drop all these checks except for desc1->fd() != -1 as
that hasn't been tested before.

> +
> +		*desc1 = *desc2;
> +
> +		if (desc1->fd() == fd_ || desc2->fd() == fd_ || desc1->fd() == desc2->fd())
> +			return TestFail;

Similarly as before,

		fd = desc2->fd();

before the assignment, and the test should be

		if (desc1->fd() == fd || desc2->fd() != fd)
			return TestFail;

to ensure that desc1 did dup and the desc2 wasn't modified.

> +
> +		if (!validFD(fd_) || !validFD(desc1->fd()) || !validFD(desc2->fd()))
> +			return TestFail;
> +
> +		delete desc1;
> +		delete desc2;
> +
> +		if (!validFD(fd_))
> +			return TestFail;
> +
> +		/* Test creating FileDescriptor by move assigment */

s/assignment/assignment./

> +		desc1 = new FileDescriptor();
> +		desc2 = new FileDescriptor(fd_);
> +
> +		if (desc1->fd() != -1 || desc2->fd() == fd_ || desc1->fd() == desc2->fd())
> +			return TestFail;
> +
> +		if (!validFD(fd_) || !validFD(desc2->fd()))
> +			return TestFail;
> +

Those checks can be dropped too.

> +		*desc1 = std::move(*desc2);
> +
> +		if (desc1->fd() == fd_ || desc2->fd() != -1 || desc1->fd() == desc2->fd())
> +			return TestFail;

And same as before,

		if (desc1->fd() != fd || desc2->fd() != -1)
			return TestFail;

with

		fd = desc2->fd();

before the assignment.

> +
> +		if (!validFD(fd_) || !validFD(desc1->fd()))
> +			return TestFail;
> +
> +		delete desc1;
> +		delete desc2;
> +
> +		if (!validFD(fd_))
> +			return TestFail;
> +
> +		return TestPass;
> +	}
> +
> +	void cleanup()
> +	{
> +		if (fd_ > 0)

>= 0, or != -1

> +			close(fd_);
> +	}
> +
> +private:
> +	bool validFD(int fd)

This method should be static.

s/validFD/isValidFd/ (or validFd) ?

> +	{
> +		struct stat s;
> +		return fstat(fd, &s) == 0;

What are you trying to catch here, that the fd is still open ? Maybe the
method should then be called isFdOpen() ? If you want to go one step
further you could also compare s.st_ino with a saved value retrieved in
init() after creating the temporary file. Then the method shouldn't be
static, and can be called isValidFd(). A one-line comment explaining
what is checked would be useful in any case.

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

> +	}
> +
> +	int fd_;
> +};
> +
> +TEST_REGISTER(FileDescriptorTest)
> diff --git a/test/meson.build b/test/meson.build
> index 1bb2161dc05a7b1d..9dc392df30efd956 100644
> --- a/test/meson.build
> +++ b/test/meson.build
> @@ -25,6 +25,7 @@ internal_tests = [
>      ['event',                           'event.cpp'],
>      ['event-dispatcher',                'event-dispatcher.cpp'],
>      ['event-thread',                    'event-thread.cpp'],
> +    ['file_descriptor',                 'file_descriptor.cpp'],
>      ['message',                         'message.cpp'],
>      ['object',                          'object.cpp'],
>      ['object-invoke',                   'object-invoke.cpp'],

Patch

diff --git a/test/file_descriptor.cpp b/test/file_descriptor.cpp
new file mode 100644
index 0000000000000000..b1d0d0e0b2807b20
--- /dev/null
+++ b/test/file_descriptor.cpp
@@ -0,0 +1,152 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * file_descriptor.cpp - FileDescriptor test
+ */
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libcamera/file_descriptor.h>
+
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+
+class FileDescriptorTest : public Test
+{
+protected:
+	int init()
+	{
+		fd_ = open("/tmp", O_TMPFILE | O_RDWR, S_IRUSR | S_IWUSR);
+		if (fd_ < 0)
+			return TestFail;
+
+		return 0;
+	}
+
+	int run()
+	{
+		FileDescriptor *desc1, *desc2;
+
+		/* Test creating FileDescriptor from numerical file descriptor. */
+		desc1 = new FileDescriptor(fd_);
+		if (desc1->fd() == fd_)
+			return TestFail;
+
+		if (!validFD(fd_) || !validFD(desc1->fd()))
+			return TestFail;
+
+		delete desc1;
+
+		if (!validFD(fd_))
+			return TestFail;
+
+		/* Test creating FileDescriptor from other FileDescriptor. */
+		desc1 = new FileDescriptor(fd_);
+		desc2 = new FileDescriptor(*desc1);
+
+		if (desc1->fd() == fd_ || desc2->fd() == fd_ || desc1->fd() == desc2->fd())
+			return TestFail;
+
+		if (!validFD(fd_) || !validFD(desc1->fd()) || !validFD(desc2->fd()))
+			return TestFail;
+
+		delete desc1;
+
+		if (!validFD(fd_) || !validFD(desc2->fd()))
+			return TestFail;
+
+		delete desc2;
+
+		if (!validFD(fd_))
+			return TestFail;
+
+		/* Test creating FileDescriptor by taking over other FileDescriptor. */
+		desc1 = new FileDescriptor(fd_);
+		desc2 = new FileDescriptor(std::move(*desc1));
+
+		if (desc1->fd() != -1 || desc2->fd() == fd_ || desc1->fd() == desc2->fd())
+			return TestFail;
+
+		if (!validFD(fd_) || !validFD(desc2->fd()))
+			return TestFail;
+
+		delete desc1;
+		delete desc2;
+
+		if (!validFD(fd_))
+			return TestFail;
+
+		/* Test creating FileDescriptor by copy assigment */
+		desc1 = new FileDescriptor();
+		desc2 = new FileDescriptor(fd_);
+
+		if (desc1->fd() != -1 || desc2->fd() == fd_ || desc1->fd() == desc2->fd())
+			return TestFail;
+
+		if (!validFD(fd_) || !validFD(desc2->fd()))
+			return TestFail;
+
+		*desc1 = *desc2;
+
+		if (desc1->fd() == fd_ || desc2->fd() == fd_ || desc1->fd() == desc2->fd())
+			return TestFail;
+
+		if (!validFD(fd_) || !validFD(desc1->fd()) || !validFD(desc2->fd()))
+			return TestFail;
+
+		delete desc1;
+		delete desc2;
+
+		if (!validFD(fd_))
+			return TestFail;
+
+		/* Test creating FileDescriptor by move assigment */
+		desc1 = new FileDescriptor();
+		desc2 = new FileDescriptor(fd_);
+
+		if (desc1->fd() != -1 || desc2->fd() == fd_ || desc1->fd() == desc2->fd())
+			return TestFail;
+
+		if (!validFD(fd_) || !validFD(desc2->fd()))
+			return TestFail;
+
+		*desc1 = std::move(*desc2);
+
+		if (desc1->fd() == fd_ || desc2->fd() != -1 || desc1->fd() == desc2->fd())
+			return TestFail;
+
+		if (!validFD(fd_) || !validFD(desc1->fd()))
+			return TestFail;
+
+		delete desc1;
+		delete desc2;
+
+		if (!validFD(fd_))
+			return TestFail;
+
+		return TestPass;
+	}
+
+	void cleanup()
+	{
+		if (fd_ > 0)
+			close(fd_);
+	}
+
+private:
+	bool validFD(int fd)
+	{
+		struct stat s;
+		return fstat(fd, &s) == 0;
+	}
+
+	int fd_;
+};
+
+TEST_REGISTER(FileDescriptorTest)
diff --git a/test/meson.build b/test/meson.build
index 1bb2161dc05a7b1d..9dc392df30efd956 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -25,6 +25,7 @@  internal_tests = [
     ['event',                           'event.cpp'],
     ['event-dispatcher',                'event-dispatcher.cpp'],
     ['event-thread',                    'event-thread.cpp'],
+    ['file_descriptor',                 'file_descriptor.cpp'],
     ['message',                         'message.cpp'],
     ['object',                          'object.cpp'],
     ['object-invoke',                   'object-invoke.cpp'],