mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Move UnitTest to diagnostic
This commit is contained in:
232
modules/beast_core/diagnostic/beast_UnitTest.cpp
Normal file
232
modules/beast_core/diagnostic/beast_UnitTest.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
UnitTest::UnitTest (const String& name_)
|
||||
: name (name_), runner (nullptr)
|
||||
{
|
||||
getAllTests().add (this);
|
||||
}
|
||||
|
||||
UnitTest::~UnitTest()
|
||||
{
|
||||
getAllTests().removeFirstMatchingValue (this);
|
||||
}
|
||||
|
||||
Array<UnitTest*>& UnitTest::getAllTests()
|
||||
{
|
||||
static Array<UnitTest*> tests;
|
||||
return tests;
|
||||
}
|
||||
|
||||
void UnitTest::initialise() {}
|
||||
void UnitTest::shutdown() {}
|
||||
|
||||
void UnitTest::performTest (UnitTestRunner* const runner_)
|
||||
{
|
||||
bassert (runner_ != nullptr);
|
||||
runner = runner_;
|
||||
|
||||
initialise();
|
||||
runTest();
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void UnitTest::logMessage (const String& message)
|
||||
{
|
||||
runner->logMessage (message);
|
||||
}
|
||||
|
||||
void UnitTest::beginTest (const String& testName)
|
||||
{
|
||||
runner->beginNewTest (this, testName);
|
||||
}
|
||||
|
||||
void UnitTest::expect (const bool result, const String& failureMessage)
|
||||
{
|
||||
if (result)
|
||||
runner->addPass();
|
||||
else
|
||||
runner->addFail (failureMessage);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
UnitTestRunner::UnitTestRunner()
|
||||
: currentTest (nullptr),
|
||||
assertOnFailure (true),
|
||||
logPasses (false)
|
||||
{
|
||||
}
|
||||
|
||||
UnitTestRunner::~UnitTestRunner()
|
||||
{
|
||||
}
|
||||
|
||||
void UnitTestRunner::setAssertOnFailure (bool shouldAssert) noexcept
|
||||
{
|
||||
assertOnFailure = shouldAssert;
|
||||
}
|
||||
|
||||
void UnitTestRunner::setPassesAreLogged (bool shouldDisplayPasses) noexcept
|
||||
{
|
||||
logPasses = shouldDisplayPasses;
|
||||
}
|
||||
|
||||
int UnitTestRunner::getNumResults() const noexcept
|
||||
{
|
||||
return results.size();
|
||||
}
|
||||
|
||||
const UnitTestRunner::TestResult* UnitTestRunner::getResult (int index) const noexcept
|
||||
{
|
||||
return results [index];
|
||||
}
|
||||
|
||||
void UnitTestRunner::resultsUpdated()
|
||||
{
|
||||
}
|
||||
|
||||
void UnitTestRunner::runTests (const Array<UnitTest*>& tests)
|
||||
{
|
||||
results.clear();
|
||||
resultsUpdated();
|
||||
|
||||
for (int i = 0; i < tests.size(); ++i)
|
||||
{
|
||||
if (shouldAbortTests())
|
||||
break;
|
||||
|
||||
try
|
||||
{
|
||||
tests.getUnchecked(i)->performTest (this);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
addFail ("An unhandled exception was thrown!");
|
||||
}
|
||||
}
|
||||
|
||||
endTest();
|
||||
}
|
||||
|
||||
void UnitTestRunner::runAllTests()
|
||||
{
|
||||
runTests (UnitTest::getAllTests());
|
||||
}
|
||||
|
||||
void UnitTestRunner::logMessage (const String& message)
|
||||
{
|
||||
Logger::writeToLog (message);
|
||||
}
|
||||
|
||||
bool UnitTestRunner::shouldAbortTests()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void UnitTestRunner::beginNewTest (UnitTest* const test, const String& subCategory)
|
||||
{
|
||||
endTest();
|
||||
currentTest = test;
|
||||
|
||||
TestResult* const r = new TestResult();
|
||||
results.add (r);
|
||||
r->unitTestName = test->getName();
|
||||
r->subcategoryName = subCategory;
|
||||
r->passes = 0;
|
||||
r->failures = 0;
|
||||
|
||||
logMessage ("-----------------------------------------------------------------");
|
||||
logMessage ("Starting test: " + r->unitTestName + " / " + subCategory + "...");
|
||||
|
||||
resultsUpdated();
|
||||
}
|
||||
|
||||
void UnitTestRunner::endTest()
|
||||
{
|
||||
if (results.size() > 0)
|
||||
{
|
||||
TestResult* const r = results.getLast();
|
||||
|
||||
if (r->failures > 0)
|
||||
{
|
||||
String m ("FAILED!! ");
|
||||
m << r->failures << (r->failures == 1 ? " test" : " tests")
|
||||
<< " failed, out of a total of " << (r->passes + r->failures);
|
||||
|
||||
logMessage (String::empty);
|
||||
logMessage (m);
|
||||
logMessage (String::empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
logMessage ("All tests completed successfully");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnitTestRunner::addPass()
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (results.getLock());
|
||||
|
||||
TestResult* const r = results.getLast();
|
||||
bassert (r != nullptr); // You need to call UnitTest::beginTest() before performing any tests!
|
||||
|
||||
r->passes++;
|
||||
|
||||
if (logPasses)
|
||||
{
|
||||
String message ("Test ");
|
||||
message << (r->failures + r->passes) << " passed";
|
||||
logMessage (message);
|
||||
}
|
||||
}
|
||||
|
||||
resultsUpdated();
|
||||
}
|
||||
|
||||
void UnitTestRunner::addFail (const String& failureMessage)
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (results.getLock());
|
||||
|
||||
TestResult* const r = results.getLast();
|
||||
bassert (r != nullptr); // You need to call UnitTest::beginTest() before performing any tests!
|
||||
|
||||
r->failures++;
|
||||
|
||||
String message ("!!! Test ");
|
||||
message << (r->failures + r->passes) << " failed";
|
||||
|
||||
if (failureMessage.isNotEmpty())
|
||||
message << ": " << failureMessage;
|
||||
|
||||
r->messages.add (message);
|
||||
|
||||
logMessage (message);
|
||||
}
|
||||
|
||||
resultsUpdated();
|
||||
|
||||
if (assertOnFailure) { bassertfalse; }
|
||||
}
|
||||
281
modules/beast_core/diagnostic/beast_UnitTest.h
Normal file
281
modules/beast_core/diagnostic/beast_UnitTest.h
Normal file
@@ -0,0 +1,281 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Portions of this file are from JUCE.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
Please visit http://www.juce.com
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_UNITTEST_BEASTHEADER
|
||||
#define BEAST_UNITTEST_BEASTHEADER
|
||||
|
||||
#include "../text/beast_StringArray.h"
|
||||
#include "../containers/beast_OwnedArray.h"
|
||||
class UnitTestRunner;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This is a base class for classes that perform a unit test.
|
||||
|
||||
To write a test using this class, your code should look something like this:
|
||||
|
||||
@code
|
||||
class MyTest : public UnitTest
|
||||
{
|
||||
public:
|
||||
MyTest() : UnitTest ("Foobar testing") {}
|
||||
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("Part 1");
|
||||
|
||||
expect (myFoobar.doesSomething());
|
||||
expect (myFoobar.doesSomethingElse());
|
||||
|
||||
beginTest ("Part 2");
|
||||
|
||||
expect (myOtherFoobar.doesSomething());
|
||||
expect (myOtherFoobar.doesSomethingElse());
|
||||
|
||||
...etc..
|
||||
}
|
||||
};
|
||||
|
||||
// Creating a static instance will automatically add the instance to the array
|
||||
// returned by UnitTest::getAllTests(), so the test will be included when you call
|
||||
// UnitTestRunner::runAllTests()
|
||||
static MyTest test;
|
||||
@endcode
|
||||
|
||||
To run a test, use the UnitTestRunner class.
|
||||
|
||||
@see UnitTestRunner
|
||||
*/
|
||||
class BEAST_API UnitTest : Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a test with the given name. */
|
||||
explicit UnitTest (const String& name);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~UnitTest();
|
||||
|
||||
/** Returns the name of the test. */
|
||||
const String& getName() const noexcept { return name; }
|
||||
|
||||
/** Runs the test, using the specified UnitTestRunner.
|
||||
You shouldn't need to call this method directly - use
|
||||
UnitTestRunner::runTests() instead.
|
||||
*/
|
||||
void performTest (UnitTestRunner* runner);
|
||||
|
||||
/** Returns the set of all UnitTest objects that currently exist. */
|
||||
static Array<UnitTest*>& getAllTests();
|
||||
|
||||
//==============================================================================
|
||||
/** You can optionally implement this method to set up your test.
|
||||
This method will be called before runTest().
|
||||
*/
|
||||
virtual void initialise();
|
||||
|
||||
/** You can optionally implement this method to clear up after your test has been run.
|
||||
This method will be called after runTest() has returned.
|
||||
*/
|
||||
virtual void shutdown();
|
||||
|
||||
/** Implement this method in your subclass to actually run your tests.
|
||||
|
||||
The content of your implementation should call beginTest() and expect()
|
||||
to perform the tests.
|
||||
*/
|
||||
virtual void runTest() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Tells the system that a new subsection of tests is beginning.
|
||||
This should be called from your runTest() method, and may be called
|
||||
as many times as you like, to demarcate different sets of tests.
|
||||
*/
|
||||
void beginTest (const String& testName);
|
||||
|
||||
//==============================================================================
|
||||
/** Checks that the result of a test is true, and logs this result.
|
||||
|
||||
In your runTest() method, you should call this method for each condition that
|
||||
you want to check, e.g.
|
||||
|
||||
@code
|
||||
void runTest()
|
||||
{
|
||||
beginTest ("basic tests");
|
||||
expect (x + y == 2);
|
||||
expect (getThing() == someThing);
|
||||
...etc...
|
||||
}
|
||||
@endcode
|
||||
|
||||
If testResult is true, a pass is logged; if it's false, a failure is logged.
|
||||
If the failure message is specified, it will be written to the log if the test fails.
|
||||
*/
|
||||
void expect (bool testResult, const String& failureMessage = String::empty);
|
||||
|
||||
/** Compares two values, and if they don't match, prints out a message containing the
|
||||
expected and actual result values.
|
||||
*/
|
||||
template <class ValueType>
|
||||
void expectEquals (ValueType actual, ValueType expected, String failureMessage = String::empty)
|
||||
{
|
||||
const bool result = (actual == expected);
|
||||
|
||||
if (! result)
|
||||
{
|
||||
if (failureMessage.isNotEmpty())
|
||||
failureMessage << " -- ";
|
||||
|
||||
failureMessage << "Expected value: " << expected << ", Actual value: " << actual;
|
||||
}
|
||||
|
||||
expect (result, failureMessage);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Writes a message to the test log.
|
||||
This can only be called from within your runTest() method.
|
||||
*/
|
||||
void logMessage (const String& message);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const String name;
|
||||
UnitTestRunner* runner;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Runs a set of unit tests.
|
||||
|
||||
You can instantiate one of these objects and use it to invoke tests on a set of
|
||||
UnitTest objects.
|
||||
|
||||
By using a subclass of UnitTestRunner, you can intercept logging messages and
|
||||
perform custom behaviour when each test completes.
|
||||
|
||||
@see UnitTest
|
||||
*/
|
||||
class BEAST_API UnitTestRunner : Uncopyable
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** */
|
||||
UnitTestRunner();
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~UnitTestRunner();
|
||||
|
||||
/** Runs a set of tests.
|
||||
|
||||
The tests are performed in order, and the results are logged. To run all the
|
||||
registered UnitTest objects that exist, use runAllTests().
|
||||
*/
|
||||
void runTests (const Array<UnitTest*>& tests);
|
||||
|
||||
/** Runs all the UnitTest objects that currently exist.
|
||||
This calls runTests() for all the objects listed in UnitTest::getAllTests().
|
||||
*/
|
||||
void runAllTests();
|
||||
|
||||
/** Sets a flag to indicate whether an assertion should be triggered if a test fails.
|
||||
This is true by default.
|
||||
*/
|
||||
void setAssertOnFailure (bool shouldAssert) noexcept;
|
||||
|
||||
/** Sets a flag to indicate whether successful tests should be logged.
|
||||
By default, this is set to false, so that only failures will be displayed in the log.
|
||||
*/
|
||||
void setPassesAreLogged (bool shouldDisplayPasses) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Contains the results of a test.
|
||||
|
||||
One of these objects is instantiated each time UnitTest::beginTest() is called, and
|
||||
it contains details of the number of subsequent UnitTest::expect() calls that are
|
||||
made.
|
||||
*/
|
||||
struct TestResult
|
||||
{
|
||||
/** The main name of this test (i.e. the name of the UnitTest object being run). */
|
||||
String unitTestName;
|
||||
/** The name of the current subcategory (i.e. the name that was set when UnitTest::beginTest() was called). */
|
||||
String subcategoryName;
|
||||
|
||||
/** The number of UnitTest::expect() calls that succeeded. */
|
||||
int passes;
|
||||
/** The number of UnitTest::expect() calls that failed. */
|
||||
int failures;
|
||||
|
||||
/** A list of messages describing the failed tests. */
|
||||
StringArray messages;
|
||||
};
|
||||
|
||||
/** Returns the number of TestResult objects that have been performed.
|
||||
@see getResult
|
||||
*/
|
||||
int getNumResults() const noexcept;
|
||||
|
||||
/** Returns one of the TestResult objects that describes a test that has been run.
|
||||
@see getNumResults
|
||||
*/
|
||||
const TestResult* getResult (int index) const noexcept;
|
||||
|
||||
protected:
|
||||
/** Called when the list of results changes.
|
||||
You can override this to perform some sort of behaviour when results are added.
|
||||
*/
|
||||
virtual void resultsUpdated();
|
||||
|
||||
/** Logs a message about the current test progress.
|
||||
By default this just writes the message to the Logger class, but you could override
|
||||
this to do something else with the data.
|
||||
*/
|
||||
virtual void logMessage (const String& message);
|
||||
|
||||
/** This can be overridden to let the runner know that it should abort the tests
|
||||
as soon as possible, e.g. because the thread needs to stop.
|
||||
*/
|
||||
virtual bool shouldAbortTests();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
friend class UnitTest;
|
||||
|
||||
UnitTest* currentTest;
|
||||
String currentSubCategory;
|
||||
OwnedArray <TestResult, CriticalSection> results;
|
||||
bool assertOnFailure, logPasses;
|
||||
|
||||
void beginNewTest (UnitTest* test, const String& subCategory);
|
||||
void endTest();
|
||||
|
||||
void addPass();
|
||||
void addFail (const String& failureMessage);
|
||||
};
|
||||
|
||||
|
||||
#endif // BEAST_UNITTEST_BEASTHEADER
|
||||
Reference in New Issue
Block a user