[libcamera-devel,2/2] test: Add tests for the Flags class

Message ID 20200724230841.27838-3-laurent.pinchart@ideasonboard.com
State Changes Requested
Delegated to: Laurent Pinchart
Headers show
Series
  • libcamera: Add type-safe enum-based flags
Related show

Commit Message

Laurent Pinchart July 24, 2020, 11:08 p.m. UTC
Add tests that exercise the whole API of the Flags class.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 test/flags.cpp   | 204 +++++++++++++++++++++++++++++++++++++++++++++++
 test/meson.build |   1 +
 2 files changed, 205 insertions(+)
 create mode 100644 test/flags.cpp

Comments

Kieran Bingham April 26, 2021, 8:22 a.m. UTC | #1
On 25/07/2020 00:08, Laurent Pinchart wrote:
> Add tests that exercise the whole API of the Flags class.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  test/flags.cpp   | 204 +++++++++++++++++++++++++++++++++++++++++++++++
>  test/meson.build |   1 +
>  2 files changed, 205 insertions(+)
>  create mode 100644 test/flags.cpp
> 
> diff --git a/test/flags.cpp b/test/flags.cpp
> new file mode 100644
> index 000000000000..82eca556f35e
> --- /dev/null
> +++ b/test/flags.cpp
> @@ -0,0 +1,204 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2020, Google Inc.
> + *
> + * flags.cpp - Flags tests
> + */
> +
> +#include <iostream>
> +
> +#include <libcamera/flags.h>
> +
> +#include "test.h"
> +
> +using namespace libcamera;
> +using namespace std;
> +
> +class FlagsTest : public Test
> +{
> +protected:
> +	enum class Option {
> +		First = (1 << 0),
> +		Second = (1 << 1),
> +		Third = (1 << 2),
> +	};
> +
> +	using Options = Flags<Option>;
> +
> +	enum class Mode {
> +		Alpha = (1 << 0),
> +		Beta = (1 << 2),
> +		Gamma = (1 << 2),

I have to wonder if this is an intentional 'fault' ?

Or is there be a better way to ensure that the bit field enums are not
incorrectly duplicated?
An enum automatically assigns numbers, so it's a shame to take the
automatic assignment, and replace it with a manual error prone detail.

Unfortunately I don't expect there's a way to make an enum automatically
<<= 1 instead of += 1



> +	};
> +
> +	using Modes = Flags<Mode>;
> +
> +	int run() override;
> +};
> +
> +namespace libcamera {
> +
> +LIBCAMERA_FLAGS_ENABLE_OPERATORS(FlagsTest::Option)
> +
> +} /* namespace libcamera */
> +
> +int FlagsTest::run()
> +{
> +	/* Commented-out constructs are expected not compile. */
> +
> +	/* Flags<int> f; */
> +
> +	/*
> +	 * Unary operators with enum argument.
> +	 */
> +
> +	Options options;
> +
> +	if (options) {
> +		cerr << "Default-constructed Flags<> is not zero" << endl;
> +		return TestFail;
> +	}
> +
> +	options |= Option::First;
> +
> +	if (!options || options != Option::First) {
> +		cerr << "Unary bitwise OR with enum failed" << endl;
> +		return TestFail;
> +	}
> +
> +	/* options &= Mode::Alpha; */
> +	/* options |= Mode::Beta;  */
> +	/* options ^= Mode::Gamma; */
> +
> +	options &= ~Option::First;
> +
> +	if (options) {
> +		cerr << "Unary bitwise AND with enum failed" << endl;
> +		return TestFail;
> +	}
> +
> +	options ^= Option::Second;
> +
> +	if (options != Option::Second) {
> +		cerr << "Unary bitwise XOR with enum failed" << endl;
> +		return TestFail;
> +	}
> +
> +	options = Options();
> +
> +	/*
> +	 * Unary operators with Flags argument.
> +	 */
> +
> +	options |= Options(Option::First);
> +
> +	if (options != Option::First) {
> +		cerr << "Unary bitwise OR with Flags<> failed" << endl;
> +		return TestFail;
> +	}
> +
> +	/* options &= Options(Mode::Alpha); */
> +	/* options |= Options(Mode::Beta);  */
> +	/* options ^= Options(Mode::Gamma); */
> +
> +	options &= ~Options(Option::First);
> +
> +	if (options) {
> +		cerr << "Unary bitwise AND with Flags<> failed" << endl;
> +		return TestFail;
> +	}
> +
> +	options ^= Options(Option::Second);
> +
> +	if (options != Option::Second) {
> +		cerr << "Unary bitwise XOR with Flags<> failed" << endl;
> +		return TestFail;
> +	}
> +
> +	options = Options();
> +
> +	/*
> +	 * Binary operators with enum argument.
> +	 */
> +
> +	options = options | Option::First;
> +
> +	if (!(options & Option::First)) {
> +		cerr << "Binary bitwise OR with enum failed" << endl;
> +		return TestFail;
> +	}
> +
> +	/* options = options & Mode::Alpha; */
> +	/* options = options | Mode::Beta;  */
> +	/* options = options ^ Mode::Gamma; */
> +
> +	options = options & ~Option::First;
> +
> +	if (options != (Option::First & Option::Second)) {
> +		cerr << "Binary bitwise AND with enum failed" << endl;
> +		return TestFail;
> +	}
> +
> +	options = options ^ (Option::First ^ Option::Second);
> +
> +	if (options != (Option::First | Option::Second)) {
> +		cerr << "Binary bitwise XOR with enum failed" << endl;
> +		return TestFail;
> +	}
> +
> +	options = Options();
> +
> +	/*
> +	 * Binary operators with Flags argument.
> +	 */
> +
> +	options |= Options(Option::First);
> +
> +	if (!(options & Option::First)) {
> +		cerr << "Binary bitwise OR with Flags<> failed" << endl;
> +		return TestFail;
> +	}
> +
> +	/* options = options & Options(Mode::Alpha); */
> +	/* options = options | Options(Mode::Beta);  */
> +	/* options = options ^ Options(Mode::Gamma); */
> +
> +	options = options & ~Options(Option::First);
> +
> +	if (options) {
> +		cerr << "Binary bitwise AND with Flags<> failed" << endl;
> +		return TestFail;
> +	}
> +
> +	options = options ^ Options(Option::Second);
> +
> +	if (options != Option::Second) {
> +		cerr << "Binary bitwise XOR with Flags<> failed" << endl;
> +		return TestFail;
> +	}
> +
> +	options = Options();
> +
> +	/*
> +	 * Conversion operators.
> +	 */
> +	options |= Option::First | Option::Second | Option::Third;
> +	if (static_cast<Options::Type>(options) != 7) {
> +		cerr << "Cast to underlying type failed" << endl;
> +		return TestFail;
> +	}
> +
> +	/*
> +	 * Conversion of the result of ninary operators on the underlying enum.
> +	 */
> +
> +	/* unsigned int val1 = Option::First; */
> +	/* unsigned int val2 = ~Option::First; */
> +	/* unsigned int val3 = Option::First | Option::Second; */
> +	/* Option val4 = ~Option::First; */
> +	/* Option val5 = Option::First | Option::Second; */
> +
> +	return TestPass;
> +}
> +
> +TEST_REGISTER(FlagsTest)
> diff --git a/test/meson.build b/test/meson.build
> index f41d6e740e6a..81b20521e8cb 100644
> --- a/test/meson.build
> +++ b/test/meson.build
> @@ -31,6 +31,7 @@ internal_tests = [
>      ['event-thread',                    'event-thread.cpp'],
>      ['file',                            'file.cpp'],
>      ['file-descriptor',                 'file-descriptor.cpp'],
> +    ['flags',                           'flags.cpp'],
>      ['hotplug-cameras',                 'hotplug-cameras.cpp'],
>      ['message',                         'message.cpp'],
>      ['object',                          'object.cpp'],
>
Laurent Pinchart April 26, 2021, 8:34 a.m. UTC | #2
Hi Kieran,

On Mon, Apr 26, 2021 at 09:22:05AM +0100, Kieran Bingham wrote:
> On 25/07/2020 00:08, Laurent Pinchart wrote:
> > Add tests that exercise the whole API of the Flags class.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  test/flags.cpp   | 204 +++++++++++++++++++++++++++++++++++++++++++++++
> >  test/meson.build |   1 +
> >  2 files changed, 205 insertions(+)
> >  create mode 100644 test/flags.cpp
> > 
> > diff --git a/test/flags.cpp b/test/flags.cpp
> > new file mode 100644
> > index 000000000000..82eca556f35e
> > --- /dev/null
> > +++ b/test/flags.cpp
> > @@ -0,0 +1,204 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * Copyright (C) 2020, Google Inc.
> > + *
> > + * flags.cpp - Flags tests
> > + */
> > +
> > +#include <iostream>
> > +
> > +#include <libcamera/flags.h>
> > +
> > +#include "test.h"
> > +
> > +using namespace libcamera;
> > +using namespace std;
> > +
> > +class FlagsTest : public Test
> > +{
> > +protected:
> > +	enum class Option {
> > +		First = (1 << 0),
> > +		Second = (1 << 1),
> > +		Third = (1 << 2),
> > +	};
> > +
> > +	using Options = Flags<Option>;
> > +
> > +	enum class Mode {
> > +		Alpha = (1 << 0),
> > +		Beta = (1 << 2),
> > +		Gamma = (1 << 2),
> 
> I have to wonder if this is an intentional 'fault' ?

Oops. Absolutely non-intentional.

> Or is there be a better way to ensure that the bit field enums are not
> incorrectly duplicated?
> An enum automatically assigns numbers, so it's a shame to take the
> automatic assignment, and replace it with a manual error prone detail.
> 
> Unfortunately I don't expect there's a way to make an enum automatically
> <<= 1 instead of += 1

It may be possible to tweak the Flags class to do this. Anyone wants to
have fun with templates ? :-)

> > +	};
> > +
> > +	using Modes = Flags<Mode>;
> > +
> > +	int run() override;
> > +};
> > +
> > +namespace libcamera {
> > +
> > +LIBCAMERA_FLAGS_ENABLE_OPERATORS(FlagsTest::Option)
> > +
> > +} /* namespace libcamera */
> > +
> > +int FlagsTest::run()
> > +{
> > +	/* Commented-out constructs are expected not compile. */
> > +
> > +	/* Flags<int> f; */
> > +
> > +	/*
> > +	 * Unary operators with enum argument.
> > +	 */
> > +
> > +	Options options;
> > +
> > +	if (options) {
> > +		cerr << "Default-constructed Flags<> is not zero" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	options |= Option::First;
> > +
> > +	if (!options || options != Option::First) {
> > +		cerr << "Unary bitwise OR with enum failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	/* options &= Mode::Alpha; */
> > +	/* options |= Mode::Beta;  */
> > +	/* options ^= Mode::Gamma; */
> > +
> > +	options &= ~Option::First;
> > +
> > +	if (options) {
> > +		cerr << "Unary bitwise AND with enum failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	options ^= Option::Second;
> > +
> > +	if (options != Option::Second) {
> > +		cerr << "Unary bitwise XOR with enum failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	options = Options();
> > +
> > +	/*
> > +	 * Unary operators with Flags argument.
> > +	 */
> > +
> > +	options |= Options(Option::First);
> > +
> > +	if (options != Option::First) {
> > +		cerr << "Unary bitwise OR with Flags<> failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	/* options &= Options(Mode::Alpha); */
> > +	/* options |= Options(Mode::Beta);  */
> > +	/* options ^= Options(Mode::Gamma); */
> > +
> > +	options &= ~Options(Option::First);
> > +
> > +	if (options) {
> > +		cerr << "Unary bitwise AND with Flags<> failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	options ^= Options(Option::Second);
> > +
> > +	if (options != Option::Second) {
> > +		cerr << "Unary bitwise XOR with Flags<> failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	options = Options();
> > +
> > +	/*
> > +	 * Binary operators with enum argument.
> > +	 */
> > +
> > +	options = options | Option::First;
> > +
> > +	if (!(options & Option::First)) {
> > +		cerr << "Binary bitwise OR with enum failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	/* options = options & Mode::Alpha; */
> > +	/* options = options | Mode::Beta;  */
> > +	/* options = options ^ Mode::Gamma; */
> > +
> > +	options = options & ~Option::First;
> > +
> > +	if (options != (Option::First & Option::Second)) {
> > +		cerr << "Binary bitwise AND with enum failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	options = options ^ (Option::First ^ Option::Second);
> > +
> > +	if (options != (Option::First | Option::Second)) {
> > +		cerr << "Binary bitwise XOR with enum failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	options = Options();
> > +
> > +	/*
> > +	 * Binary operators with Flags argument.
> > +	 */
> > +
> > +	options |= Options(Option::First);
> > +
> > +	if (!(options & Option::First)) {
> > +		cerr << "Binary bitwise OR with Flags<> failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	/* options = options & Options(Mode::Alpha); */
> > +	/* options = options | Options(Mode::Beta);  */
> > +	/* options = options ^ Options(Mode::Gamma); */
> > +
> > +	options = options & ~Options(Option::First);
> > +
> > +	if (options) {
> > +		cerr << "Binary bitwise AND with Flags<> failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	options = options ^ Options(Option::Second);
> > +
> > +	if (options != Option::Second) {
> > +		cerr << "Binary bitwise XOR with Flags<> failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	options = Options();
> > +
> > +	/*
> > +	 * Conversion operators.
> > +	 */
> > +	options |= Option::First | Option::Second | Option::Third;
> > +	if (static_cast<Options::Type>(options) != 7) {
> > +		cerr << "Cast to underlying type failed" << endl;
> > +		return TestFail;
> > +	}
> > +
> > +	/*
> > +	 * Conversion of the result of ninary operators on the underlying enum.
> > +	 */
> > +
> > +	/* unsigned int val1 = Option::First; */
> > +	/* unsigned int val2 = ~Option::First; */
> > +	/* unsigned int val3 = Option::First | Option::Second; */
> > +	/* Option val4 = ~Option::First; */
> > +	/* Option val5 = Option::First | Option::Second; */
> > +
> > +	return TestPass;
> > +}
> > +
> > +TEST_REGISTER(FlagsTest)
> > diff --git a/test/meson.build b/test/meson.build
> > index f41d6e740e6a..81b20521e8cb 100644
> > --- a/test/meson.build
> > +++ b/test/meson.build
> > @@ -31,6 +31,7 @@ internal_tests = [
> >      ['event-thread',                    'event-thread.cpp'],
> >      ['file',                            'file.cpp'],
> >      ['file-descriptor',                 'file-descriptor.cpp'],
> > +    ['flags',                           'flags.cpp'],
> >      ['hotplug-cameras',                 'hotplug-cameras.cpp'],
> >      ['message',                         'message.cpp'],
> >      ['object',                          'object.cpp'],
Kieran Bingham April 26, 2021, 11:51 a.m. UTC | #3
On 26/04/2021 09:34, Laurent Pinchart wrote:
> Hi Kieran,
> 
> On Mon, Apr 26, 2021 at 09:22:05AM +0100, Kieran Bingham wrote:
>> On 25/07/2020 00:08, Laurent Pinchart wrote:
>>> Add tests that exercise the whole API of the Flags class.
>>>
>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>> ---
>>>  test/flags.cpp   | 204 +++++++++++++++++++++++++++++++++++++++++++++++
>>>  test/meson.build |   1 +
>>>  2 files changed, 205 insertions(+)
>>>  create mode 100644 test/flags.cpp
>>>
>>> diff --git a/test/flags.cpp b/test/flags.cpp
>>> new file mode 100644
>>> index 000000000000..82eca556f35e
>>> --- /dev/null
>>> +++ b/test/flags.cpp
>>> @@ -0,0 +1,204 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + * Copyright (C) 2020, Google Inc.
>>> + *
>>> + * flags.cpp - Flags tests
>>> + */
>>> +
>>> +#include <iostream>
>>> +
>>> +#include <libcamera/flags.h>
>>> +
>>> +#include "test.h"
>>> +
>>> +using namespace libcamera;
>>> +using namespace std;
>>> +
>>> +class FlagsTest : public Test
>>> +{
>>> +protected:
>>> +	enum class Option {
>>> +		First = (1 << 0),
>>> +		Second = (1 << 1),
>>> +		Third = (1 << 2),
>>> +	};
>>> +
>>> +	using Options = Flags<Option>;
>>> +
>>> +	enum class Mode {
>>> +		Alpha = (1 << 0),
>>> +		Beta = (1 << 2),
>>> +		Gamma = (1 << 2),
>>
>> I have to wonder if this is an intentional 'fault' ?
> 
> Oops. Absolutely non-intentional.
> 
>> Or is there be a better way to ensure that the bit field enums are not
>> incorrectly duplicated?
>> An enum automatically assigns numbers, so it's a shame to take the
>> automatic assignment, and replace it with a manual error prone detail.
>>
>> Unfortunately I don't expect there's a way to make an enum automatically
>> <<= 1 instead of += 1
> 
> It may be possible to tweak the Flags class to do this. Anyone wants to
> have fun with templates ? :-)


Not entirely fond of this but I asked on a CPP discord channel and
someone suggested it can be done with X-Macro, which seems to be some
existing macro pattern.

   https://godbolt.org/z/EnEWoeGYv

I suspect perhaps that could be wrapped into a template too, which might
potentially be more friendly than the continuous line X() X() X() macro..



>>> +	};
>>> +
>>> +	using Modes = Flags<Mode>;
>>> +
>>> +	int run() override;
>>> +};
>>> +
>>> +namespace libcamera {
>>> +
>>> +LIBCAMERA_FLAGS_ENABLE_OPERATORS(FlagsTest::Option)
>>> +
>>> +} /* namespace libcamera */
>>> +
>>> +int FlagsTest::run()
>>> +{
>>> +	/* Commented-out constructs are expected not compile. */
>>> +
>>> +	/* Flags<int> f; */
>>> +
>>> +	/*
>>> +	 * Unary operators with enum argument.
>>> +	 */
>>> +
>>> +	Options options;
>>> +
>>> +	if (options) {
>>> +		cerr << "Default-constructed Flags<> is not zero" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	options |= Option::First;
>>> +
>>> +	if (!options || options != Option::First) {
>>> +		cerr << "Unary bitwise OR with enum failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	/* options &= Mode::Alpha; */
>>> +	/* options |= Mode::Beta;  */
>>> +	/* options ^= Mode::Gamma; */
>>> +
>>> +	options &= ~Option::First;
>>> +
>>> +	if (options) {
>>> +		cerr << "Unary bitwise AND with enum failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	options ^= Option::Second;
>>> +
>>> +	if (options != Option::Second) {
>>> +		cerr << "Unary bitwise XOR with enum failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	options = Options();
>>> +
>>> +	/*
>>> +	 * Unary operators with Flags argument.
>>> +	 */
>>> +
>>> +	options |= Options(Option::First);
>>> +
>>> +	if (options != Option::First) {
>>> +		cerr << "Unary bitwise OR with Flags<> failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	/* options &= Options(Mode::Alpha); */
>>> +	/* options |= Options(Mode::Beta);  */
>>> +	/* options ^= Options(Mode::Gamma); */
>>> +
>>> +	options &= ~Options(Option::First);
>>> +
>>> +	if (options) {
>>> +		cerr << "Unary bitwise AND with Flags<> failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	options ^= Options(Option::Second);
>>> +
>>> +	if (options != Option::Second) {
>>> +		cerr << "Unary bitwise XOR with Flags<> failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	options = Options();
>>> +
>>> +	/*
>>> +	 * Binary operators with enum argument.
>>> +	 */
>>> +
>>> +	options = options | Option::First;
>>> +
>>> +	if (!(options & Option::First)) {
>>> +		cerr << "Binary bitwise OR with enum failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	/* options = options & Mode::Alpha; */
>>> +	/* options = options | Mode::Beta;  */
>>> +	/* options = options ^ Mode::Gamma; */
>>> +
>>> +	options = options & ~Option::First;
>>> +
>>> +	if (options != (Option::First & Option::Second)) {
>>> +		cerr << "Binary bitwise AND with enum failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	options = options ^ (Option::First ^ Option::Second);
>>> +
>>> +	if (options != (Option::First | Option::Second)) {
>>> +		cerr << "Binary bitwise XOR with enum failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	options = Options();
>>> +
>>> +	/*
>>> +	 * Binary operators with Flags argument.
>>> +	 */
>>> +
>>> +	options |= Options(Option::First);
>>> +
>>> +	if (!(options & Option::First)) {
>>> +		cerr << "Binary bitwise OR with Flags<> failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	/* options = options & Options(Mode::Alpha); */
>>> +	/* options = options | Options(Mode::Beta);  */
>>> +	/* options = options ^ Options(Mode::Gamma); */
>>> +
>>> +	options = options & ~Options(Option::First);
>>> +
>>> +	if (options) {
>>> +		cerr << "Binary bitwise AND with Flags<> failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	options = options ^ Options(Option::Second);
>>> +
>>> +	if (options != Option::Second) {
>>> +		cerr << "Binary bitwise XOR with Flags<> failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	options = Options();
>>> +
>>> +	/*
>>> +	 * Conversion operators.
>>> +	 */
>>> +	options |= Option::First | Option::Second | Option::Third;
>>> +	if (static_cast<Options::Type>(options) != 7) {
>>> +		cerr << "Cast to underlying type failed" << endl;
>>> +		return TestFail;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Conversion of the result of ninary operators on the underlying enum.
>>> +	 */
>>> +
>>> +	/* unsigned int val1 = Option::First; */
>>> +	/* unsigned int val2 = ~Option::First; */
>>> +	/* unsigned int val3 = Option::First | Option::Second; */
>>> +	/* Option val4 = ~Option::First; */
>>> +	/* Option val5 = Option::First | Option::Second; */
>>> +
>>> +	return TestPass;
>>> +}
>>> +
>>> +TEST_REGISTER(FlagsTest)
>>> diff --git a/test/meson.build b/test/meson.build
>>> index f41d6e740e6a..81b20521e8cb 100644
>>> --- a/test/meson.build
>>> +++ b/test/meson.build
>>> @@ -31,6 +31,7 @@ internal_tests = [
>>>      ['event-thread',                    'event-thread.cpp'],
>>>      ['file',                            'file.cpp'],
>>>      ['file-descriptor',                 'file-descriptor.cpp'],
>>> +    ['flags',                           'flags.cpp'],
>>>      ['hotplug-cameras',                 'hotplug-cameras.cpp'],
>>>      ['message',                         'message.cpp'],
>>>      ['object',                          'object.cpp'],
>

Patch

diff --git a/test/flags.cpp b/test/flags.cpp
new file mode 100644
index 000000000000..82eca556f35e
--- /dev/null
+++ b/test/flags.cpp
@@ -0,0 +1,204 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * flags.cpp - Flags tests
+ */
+
+#include <iostream>
+
+#include <libcamera/flags.h>
+
+#include "test.h"
+
+using namespace libcamera;
+using namespace std;
+
+class FlagsTest : public Test
+{
+protected:
+	enum class Option {
+		First = (1 << 0),
+		Second = (1 << 1),
+		Third = (1 << 2),
+	};
+
+	using Options = Flags<Option>;
+
+	enum class Mode {
+		Alpha = (1 << 0),
+		Beta = (1 << 2),
+		Gamma = (1 << 2),
+	};
+
+	using Modes = Flags<Mode>;
+
+	int run() override;
+};
+
+namespace libcamera {
+
+LIBCAMERA_FLAGS_ENABLE_OPERATORS(FlagsTest::Option)
+
+} /* namespace libcamera */
+
+int FlagsTest::run()
+{
+	/* Commented-out constructs are expected not compile. */
+
+	/* Flags<int> f; */
+
+	/*
+	 * Unary operators with enum argument.
+	 */
+
+	Options options;
+
+	if (options) {
+		cerr << "Default-constructed Flags<> is not zero" << endl;
+		return TestFail;
+	}
+
+	options |= Option::First;
+
+	if (!options || options != Option::First) {
+		cerr << "Unary bitwise OR with enum failed" << endl;
+		return TestFail;
+	}
+
+	/* options &= Mode::Alpha; */
+	/* options |= Mode::Beta;  */
+	/* options ^= Mode::Gamma; */
+
+	options &= ~Option::First;
+
+	if (options) {
+		cerr << "Unary bitwise AND with enum failed" << endl;
+		return TestFail;
+	}
+
+	options ^= Option::Second;
+
+	if (options != Option::Second) {
+		cerr << "Unary bitwise XOR with enum failed" << endl;
+		return TestFail;
+	}
+
+	options = Options();
+
+	/*
+	 * Unary operators with Flags argument.
+	 */
+
+	options |= Options(Option::First);
+
+	if (options != Option::First) {
+		cerr << "Unary bitwise OR with Flags<> failed" << endl;
+		return TestFail;
+	}
+
+	/* options &= Options(Mode::Alpha); */
+	/* options |= Options(Mode::Beta);  */
+	/* options ^= Options(Mode::Gamma); */
+
+	options &= ~Options(Option::First);
+
+	if (options) {
+		cerr << "Unary bitwise AND with Flags<> failed" << endl;
+		return TestFail;
+	}
+
+	options ^= Options(Option::Second);
+
+	if (options != Option::Second) {
+		cerr << "Unary bitwise XOR with Flags<> failed" << endl;
+		return TestFail;
+	}
+
+	options = Options();
+
+	/*
+	 * Binary operators with enum argument.
+	 */
+
+	options = options | Option::First;
+
+	if (!(options & Option::First)) {
+		cerr << "Binary bitwise OR with enum failed" << endl;
+		return TestFail;
+	}
+
+	/* options = options & Mode::Alpha; */
+	/* options = options | Mode::Beta;  */
+	/* options = options ^ Mode::Gamma; */
+
+	options = options & ~Option::First;
+
+	if (options != (Option::First & Option::Second)) {
+		cerr << "Binary bitwise AND with enum failed" << endl;
+		return TestFail;
+	}
+
+	options = options ^ (Option::First ^ Option::Second);
+
+	if (options != (Option::First | Option::Second)) {
+		cerr << "Binary bitwise XOR with enum failed" << endl;
+		return TestFail;
+	}
+
+	options = Options();
+
+	/*
+	 * Binary operators with Flags argument.
+	 */
+
+	options |= Options(Option::First);
+
+	if (!(options & Option::First)) {
+		cerr << "Binary bitwise OR with Flags<> failed" << endl;
+		return TestFail;
+	}
+
+	/* options = options & Options(Mode::Alpha); */
+	/* options = options | Options(Mode::Beta);  */
+	/* options = options ^ Options(Mode::Gamma); */
+
+	options = options & ~Options(Option::First);
+
+	if (options) {
+		cerr << "Binary bitwise AND with Flags<> failed" << endl;
+		return TestFail;
+	}
+
+	options = options ^ Options(Option::Second);
+
+	if (options != Option::Second) {
+		cerr << "Binary bitwise XOR with Flags<> failed" << endl;
+		return TestFail;
+	}
+
+	options = Options();
+
+	/*
+	 * Conversion operators.
+	 */
+	options |= Option::First | Option::Second | Option::Third;
+	if (static_cast<Options::Type>(options) != 7) {
+		cerr << "Cast to underlying type failed" << endl;
+		return TestFail;
+	}
+
+	/*
+	 * Conversion of the result of ninary operators on the underlying enum.
+	 */
+
+	/* unsigned int val1 = Option::First; */
+	/* unsigned int val2 = ~Option::First; */
+	/* unsigned int val3 = Option::First | Option::Second; */
+	/* Option val4 = ~Option::First; */
+	/* Option val5 = Option::First | Option::Second; */
+
+	return TestPass;
+}
+
+TEST_REGISTER(FlagsTest)
diff --git a/test/meson.build b/test/meson.build
index f41d6e740e6a..81b20521e8cb 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -31,6 +31,7 @@  internal_tests = [
     ['event-thread',                    'event-thread.cpp'],
     ['file',                            'file.cpp'],
     ['file-descriptor',                 'file-descriptor.cpp'],
+    ['flags',                           'flags.cpp'],
     ['hotplug-cameras',                 'hotplug-cameras.cpp'],
     ['message',                         'message.cpp'],
     ['object',                          'object.cpp'],