LUMIERA.clone/tests/library/thread-wrapper-join-test.cpp
Ichthyostega 1d625a01e0 Library: complete and modernise ThreadWrapperJoin_test
Since the decision was taken to retain support for this special feature,
and even extend it to allow passing values, the additional functionality
should be documented in the test. Doing so also highlighted subtle problems
with argument binding.
2023-09-29 23:42:22 +02:00

151 lines
4.2 KiB
C++

/*
ThreadWrapperJoin(Test) - wait blocking on termination of a thread
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* *****************************************************/
/** @file thread-wrapper-join-test.cpp
** unit test \ref ThreadWrapperJoin_test
*/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/thread.hpp"
#include "lib/error.hpp"
#include <chrono>
using test::Test;
using std::this_thread::sleep_for;
using namespace std::chrono_literals;
namespace lib {
namespace test {
using error::LUMIERA_ERROR_LOGIC;
namespace {
const int DESTRUCTION_CODE = -23;
LUMIERA_ERROR_DEFINE(SPECIAL, "007 exception");
#define Type(_EXPR_) lib::test::showType<decltype(_EXPR_)>()
}
/***********************************************************************//**
* @test verify the special flavour of the thread-wrapper, allowing to block
* waiting on thread end and then pass result or error state.
* @see thread.hpp
* @see Result_test
* @see ThreadWrapper_test
* @see SyncLocking_test
*/
class ThreadWrapperJoin_test : public Test
{
virtual void
run (Arg)
{
simpleUse();
returnValue();
detectFailure();
joinOnceOnly();
}
void
simpleUse()
{
ThreadJoinable theThread{"test join-1"
,[]{ sleep_for (10ms); }};
CHECK (theThread);
theThread.join(); // blocks until thread-function has terminated
CHECK (not theThread);
}
int
theAction (int secretValue) ///< to be run in a new thread...
{
sleep_for (100ms); // pause 100ms prior to modifying
if (DESTRUCTION_CODE == secretValue)
throw error::External{"special agent detected"
, LUMIERA_ERROR_SPECIAL};
else
return secretValue+42;
}
void
returnValue()
{
int mySecret = rand() % 1000;
ThreadJoinable theThread{"test join-2"
,&ThreadWrapperJoin_test::theAction
, this, mySecret};
// Note: join() passes the result value captured in the thread
CHECK (mySecret+42 == theThread.join());
}
void
detectFailure()
{
ThreadJoinable theThread{"test join-3"
,&ThreadWrapperJoin_test::theAction
, this, DESTRUCTION_CODE};
// join() actually returns a proxy...
auto res = theThread.join();
CHECK (Type(res) == "Result<int>"_expect);
// can detect that the thread was aborted with an exception
CHECK (not res.isValid());
VERIFY_ERROR(SPECIAL, res.maybeThrow() );
VERIFY_ERROR(SPECIAL, (int)res );
}
void
joinOnceOnly ()
{
ThreadJoinable theThread{"joining-4"
,[]{ sleep_for (10ms); }};
theThread.join();
VERIFY_ERROR(LOGIC, theThread.join() );
VERIFY_ERROR(LOGIC, theThread.join() );
}
};
/** Register this test class... */
LAUNCHER (ThreadWrapperJoin_test, "function common");
}} // namespace lib::test