Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 83 additions & 35 deletions Common/Cpp/TestRunners/ParallelUnitTestRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "Common/Cpp/PrettyPrint.h"
#include "Common/Cpp/MemoryUtilization/MemoryUtilization.h"
#include "Common/Cpp/Concurrency/ReverseLockGuard.h"
#include "TestImageDistortionManager.h"
#include "ParallelUnitTestRunner.h"

//#include <iostream>
Expand Down Expand Up @@ -87,6 +88,19 @@ void UnitTestRunner::add_test(std::shared_ptr<const UnitTest> test){
m_cv.notify_all();
}

void UnitTestRunner::enable_test_image_distortions(const std::string& test_repo_path){
initialize_test_image_distortions(test_repo_path);
const TestImageDistortionManager& manager = *test_image_distortion_manager();

std::string labels;
for (size_t c = 0; c < manager.num_test_distortions(); c++){
labels += c == 0 ? "" : ", ";
labels += manager.test_distortion_label(c);
}
m_logger.log("Test image distortions enabled. Tests with labeled test images also run with: " + labels);
m_test_image_distortions = true;
}

void UnitTestRunner::run(){
m_logger.log(
"Starting UnitTestRunner with:"
Expand Down Expand Up @@ -184,49 +198,49 @@ void UnitTestRunner::dispatch_test(const std::string& name){
{
ReverseLockGuard<Mutex> lg(m_lock);
node.task = m_thread_pool.dispatch_now_blocking([this, iter_current]{
const UnitTest& test = *iter_current->second.test;
UnitTestResult result;
try{
m_logger.log("Starting: " + test.name());
result = test.run(m_logger, *this);
}catch (Exception& e){
result = UnitTestResult(UnitTestResult::FAILED, e.to_str());
}catch (std::bad_alloc&){
result = UnitTestResult::OOM;
}catch (std::exception& e){
result = UnitTestResult(UnitTestResult::FAILED, e.what());
}catch (...){
result = UnitTestResult(UnitTestResult::FAILED, "Unknown exception.");
const std::shared_ptr<const UnitTest>& test = iter_current->second.test;

// The distortion labels to run the test with. "" runs it on its images as they are.
// The runs happen one after another on this thread, so changing the test's label
// between them is safe.
std::vector<std::string> labels{""};
if (m_test_image_distortions){
const TestImageDistortionManager& manager = *test_image_distortion_manager();
const size_t distortions = manager.num_applicable_distortions(test->image_paths());
for (size_t c = 0; c < distortions; c++){
labels.emplace_back(manager.test_distortion_label(c));
}
}

switch (result.result){
case UnitTestResult::NOT_RUN:
case UnitTestResult::PASSED:
m_logger.log("Passed: " + test.name(), COLOR_BLUE);
break;
case UnitTestResult::FAILED:
m_logger.log("Failed: " + test.name() + ", Message: " + result.message, COLOR_RED);
break;
case UnitTestResult::SKIPPED:
m_logger.log("Skipped: " + test.name(), COLOR_ORANGE);
break;
case UnitTestResult::OOM:
m_logger.log("Out-of-Memory: " + test.name(), COLOR_ORANGE);
break;
for (const std::string& label : labels){
// Once cancelled, skip the remaining distortion runs. The test itself still
// runs once, as it always did after being dispatched.
if (!label.empty() && cancelled()){
break;
}
if (m_test_image_distortions){
test->set_distortion_label(label);
}
UnitTestResult result = run_test(*test);

// Pass a copy of the test pointer, not a moved one: the test must stay alive for its
// next run and for the resource accounting below.
m_listeners.run_method(
&Listener::on_test_finished,
test,
std::move(result)
);
}
if (m_test_image_distortions){
test->set_distortion_label("");
}

m_listeners.run_method(
&Listener::on_test_finished,
std::move(iter_current->second.test),
std::move(result)
);

{
std::lock_guard<Mutex> lg(m_lock);
m_current_memory -= test->memory();
m_current_threads -= test->threads();
auto node = m_currently_running.extract(iter_current);
m_completed.insert(std::move(node));
m_current_memory -= test.memory();
m_current_threads -= test.threads();
}
m_cv.notify_all();
});
Expand All @@ -238,6 +252,40 @@ void UnitTestRunner::dispatch_test(const std::string& name){
// cout << "dispatch_test() - end" << endl;
}

UnitTestResult UnitTestRunner::run_test(const UnitTest& test){
const std::string name = test.name_with_distortion();
UnitTestResult result;
try{
m_logger.log("Starting: " + name);
result = test.run(m_logger, *this);
}catch (Exception& e){
result = UnitTestResult(UnitTestResult::FAILED, e.to_str());
}catch (std::bad_alloc&){
result = UnitTestResult::OOM;
}catch (std::exception& e){
result = UnitTestResult(UnitTestResult::FAILED, e.what());
}catch (...){
result = UnitTestResult(UnitTestResult::FAILED, "Unknown exception.");
}

switch (result.result){
case UnitTestResult::NOT_RUN:
case UnitTestResult::PASSED:
m_logger.log("Passed: " + name, COLOR_BLUE);
break;
case UnitTestResult::FAILED:
m_logger.log("Failed: " + name + ", Message: " + result.message, COLOR_RED);
break;
case UnitTestResult::SKIPPED:
m_logger.log("Skipped: " + name, COLOR_ORANGE);
break;
case UnitTestResult::OOM:
m_logger.log("Out-of-Memory: " + name, COLOR_ORANGE);
break;
}
return result;
}




Expand Down
22 changes: 22 additions & 0 deletions Common/Cpp/TestRunners/ParallelUnitTestRunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ namespace PokemonAutomation{
class UnitTestRunner final : public CancellableScope{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing with these two files. Test-specific stuff like image distortions should not be leaking into the generic test API.

public:
struct Listener{
// Called after each run of a test.
// If test image distortions is enabled, a test can run several times,
// once per distortion. During this call, `test->distortion_label()` is the
// label of the run that finished, so use `test->name_with_distortion()` to name it.
virtual void on_test_finished(
std::shared_ptr<const UnitTest> test,
UnitTestResult result
Expand Down Expand Up @@ -64,14 +68,26 @@ class UnitTestRunner final : public CancellableScope{
size_t max_threads = 0
);

// Add a unit test instance.
template <typename TestType, class... Args>
void add_test(Args&&... args){
add_test(std::make_shared<const TestType>(std::forward<Args>(args)...));
}
// Add a unit test instance.
void add_test(std::shared_ptr<const UnitTest> test);

// Initialize the image distortion augmentation on each supported image unit test.
//
// After called, it will load distortion resources from the test folder. During `run()`,
// it will check if a test has color distortion augmentation available and run the
// test with different distortions it after the basic unit test is run.
//
// Throws if the calibration data can't be loaded.
void enable_test_image_distortions(const std::string& test_repo_path);


public:
// Main function to run the all tests added by `add_test()`.
void run();

virtual bool cancel(std::exception_ptr reason) noexcept override;
Expand All @@ -80,11 +96,17 @@ class UnitTestRunner final : public CancellableScope{
private:
void dispatch_test(const std::string& name);

// Run `test` once with its current distortion label, log the result and return it.
// Exceptions thrown by the test are turned into a failed (or out-of-memory) result.
UnitTestResult run_test(const UnitTest& test);


private:
Logger& m_logger;
ThreadPool& m_thread_pool;

bool m_test_image_distortions = false;

uint64_t m_max_memory = 0;
size_t m_max_threads = 1;

Expand Down
Loading
Loading