test: timer-thread: Move timer start from wrong thread to separate test

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.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
This commit is contained in:
Laurent Pinchart 2024-01-19 03:21:15 +02:00
parent dfe81fd702
commit 4f76beae86
3 changed files with 109 additions and 25 deletions

View file

@ -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

103
test/timer-fail.cpp Normal file
View file

@ -0,0 +1,103 @@
/* 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));
/*
* 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.
*/
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)

View file

@ -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;
}