[PATCH 09/12] test: timer-thread: Move timer start from wrong thread to separate test
Milan Zamazal
mzamazal at redhat.com
Tue Jan 23 12:09:18 CET 2024
Laurent Pinchart <laurent.pinchart at ideasonboard.com> writes:
> On Mon, Jan 22, 2024 at 09:17:56PM +0100, Milan Zamazal wrote:
>> Laurent Pinchart <laurent.pinchart at ideasonboard.com> writes:
>>
>
>> > Starting a timer from the wrong thread is expected to fail, and we test
>> > this in the timer-thread unit test. This is however not something that a
>> > caller is allowed to do, and libcamera will get assertion failures to
>> > catch this invalid usage. The unit test will then fail.
>> >
>> > To prepare for this, split the unit test in two, with a test that is
>> > expected by meson to succeed, and one that is expected to fail. The
>> > assertion will then cause an expected failure, making the test suite
>> > succeed.
>>
>> What if the tests crashes for an unrelated reason? Is it possible to
>> distinguish between the "right" error and an unexpected error?
>
> Not easily, meson won't distinguish between different assertion errors.
> I'm pretty sure we could create some kind of wrapper that would parse
> the logs, but I don't think that's in scope for this series.
OK.
>> > Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
>> > ---
>> > test/meson.build | 9 ++--
>> > test/timer-fail.cpp | 101 ++++++++++++++++++++++++++++++++++++++++++
>> > test/timer-thread.cpp | 22 ---------
>> > 3 files changed, 107 insertions(+), 25 deletions(-)
>> > create mode 100644 test/timer-fail.cpp
>> >
>> > diff --git a/test/meson.build b/test/meson.build
>> > index 189e1428485a..8b6057d4e800 100644
>> > --- a/test/meson.build
>> > +++ b/test/meson.build
>> > @@ -69,6 +69,7 @@ internal_tests = [
>> > {'name': 'signal-threads', 'sources': ['signal-threads.cpp']},
>> > {'name': 'threads', 'sources': 'threads.cpp', 'dependencies': [libthreads]},
>> > {'name': 'timer', 'sources': ['timer.cpp']},
>> > + {'name': 'timer-fail', 'sources': ['timer-fail.cpp'], 'should_fail': true},
>> > {'name': 'timer-thread', 'sources': ['timer-thread.cpp']},
>> > {'name': 'unique-fd', 'sources': ['unique-fd.cpp']},
>> > {'name': 'utils', 'sources': ['utils.cpp']},
>> > @@ -91,7 +92,7 @@ foreach test : public_tests
>> > link_with : test_libraries,
>> > include_directories : test_includes_public)
>> >
>> > - test(test['name'], exe)
>> > + test(test['name'], exe, should_fail : test.get('should_fail', false))
>> > endforeach
>> >
>> > foreach test : internal_tests
>> > @@ -105,7 +106,7 @@ foreach test : internal_tests
>> > link_with : test_libraries,
>> > include_directories : test_includes_internal)
>> >
>> > - test(test['name'], exe)
>> > + test(test['name'], exe, should_fail : test.get('should_fail', false))
>> > endforeach
>> >
>> > foreach test : internal_non_parallel_tests
>> > @@ -119,5 +120,7 @@ foreach test : internal_non_parallel_tests
>> > link_with : test_libraries,
>> > include_directories : test_includes_internal)
>> >
>> > - test(test['name'], exe, is_parallel : false)
>> > + test(test['name'], exe,
>> > + is_parallel : false,
>> > + should_fail : test.get('should_fail', false))
>> > endforeach
>> > diff --git a/test/timer-fail.cpp b/test/timer-fail.cpp
>> > new file mode 100644
>> > index 000000000000..e9a3b1b61bcb
>> > --- /dev/null
>> > +++ b/test/timer-fail.cpp
>> > @@ -0,0 +1,101 @@
>> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> > +/*
>> > + * Copyright (C) 2024, Ideas on Board Oy
>> > + *
>> > + * timer-fail.cpp - Threaded timer failure test
>> > + */
>> > +
>> > +#include <chrono>
>> > +#include <iostream>
>> > +
>> > +#include <libcamera/base/event_dispatcher.h>
>> > +#include <libcamera/base/object.h>
>> > +#include <libcamera/base/thread.h>
>> > +#include <libcamera/base/timer.h>
>> > +
>> > +#include "test.h"
>> > +
>> > +using namespace libcamera;
>> > +using namespace std;
>> > +using namespace std::chrono_literals;
>> > +
>> > +class TimeoutHandler : public Object
>> > +{
>> > +public:
>> > + TimeoutHandler()
>> > + : timer_(this), timeout_(false)
>> > + {
>> > + timer_.timeout.connect(this, &TimeoutHandler::timeoutHandler);
>> > + }
>> > +
>> > + void start()
>> > + {
>> > + timer_.start(100ms);
>> > + }
>> > +
>> > + bool timeout() const
>> > + {
>> > + return timeout_;
>> > + }
>> > +
>> > +private:
>> > + void timeoutHandler()
>> > + {
>> > + timeout_ = true;
>> > + }
>> > +
>> > + Timer timer_;
>> > + bool timeout_;
>> > +};
>> > +
>> > +class TimerFailTest : public Test
>> > +{
>> > +protected:
>> > + int init()
>> > + {
>> > + thread_.start();
>> > + timeout_.moveToThread(&thread_);
>> > +
>> > + return TestPass;
>> > + }
>> > +
>> > + int run()
>> > + {
>> > + /*
>> > + * Test that the forbidden operation of starting the timer from
>> > + * another thread results in a failure. We need to interrupt the
>> > + * event dispatcher to make sure we don't succeed simply because
>> > + * the event dispatcher hasn't noticed the timer restart.
>> > + */
>> > + timeout_.start();
>> > + thread_.eventDispatcher()->interrupt();
>> > +
>> > + this_thread::sleep_for(chrono::milliseconds(200));
>> > +
>> > + /*
>> > + * Meson expects this test to fail, so we need to return
>>
>> It'd be good to mention here that it's expected to fail on an assertion.
>
> I'll expand the comment to
>
> * The wrong start() call should result in an assertion in debug
> * builds, and a timeout in release builds. The test is
> * therefore marked in meson.build as expected to fail. We need
> * to return TestPass in the unexpected (usually known as
> * "fail") case, and TestFail otherwise.
Sounds good, thanks.
>> > + * TestPass in the unexpected (usually known as "fail"), case,
>> > + * and TestFail otherwise.
>> > + */
>> > + if (timeout_.timeout()) {
>> > + cout << "Timer start from wrong thread succeeded unexpectedly"
>> > + << endl;
>> > + return TestPass;
>> > + }
>> > +
>> > + return TestFail;
>> > + }
>> > +
>> > + void cleanup()
>> > + {
>> > + /* Must stop thread before destroying timeout. */
>> > + thread_.exit(0);
>> > + thread_.wait();
>> > + }
>> > +
>> > +private:
>> > + TimeoutHandler timeout_;
>> > + Thread thread_;
>> > +};
>> > +
>> > +TEST_REGISTER(TimerFailTest)
>> > diff --git a/test/timer-thread.cpp b/test/timer-thread.cpp
>> > index 0bcd0d8ce194..4caf4e33b2ba 100644
>> > --- a/test/timer-thread.cpp
>> > +++ b/test/timer-thread.cpp
>> > @@ -29,12 +29,6 @@ public:
>> > timer_.start(100ms);
>> > }
>> >
>> > - void restart()
>> > - {
>> > - timeout_ = false;
>> > - timer_.start(100ms);
>> > - }
>> > -
>> > bool timeout() const
>> > {
>> > return timeout_;
>> > @@ -74,22 +68,6 @@ protected:
>> > return TestFail;
>> > }
>> >
>> > - /*
>> > - * Test that starting the timer from another thread fails. We
>> > - * need to interrupt the event dispatcher to make sure we don't
>> > - * succeed simply because the event dispatcher hasn't noticed
>> > - * the timer restart.
>> > - */
>> > - timeout_.restart();
>> > - thread_.eventDispatcher()->interrupt();
>> > -
>> > - this_thread::sleep_for(chrono::milliseconds(200));
>> > -
>> > - if (timeout_.timeout()) {
>> > - cout << "Timer restart test failed" << endl;
>> > - return TestFail;
>> > - }
>> > -
>> > return TestPass;
>> > }
More information about the libcamera-devel
mailing list