From cc820e5f7636cd95575c474e1435c9b45c637b73 Mon Sep 17 00:00:00 2001 From: Sylvester Joosten <sjoosten@anl.gov> Date: Tue, 22 Dec 2020 03:45:35 +0000 Subject: [PATCH] Added new testing API --- util/benchmark.hh | 115 ++++++++++++++++++++++++++++++++++++++++++++++ util/exception.hh | 23 ++++++++++ 2 files changed, 138 insertions(+) create mode 100644 util/benchmark.hh create mode 100644 util/exception.hh diff --git a/util/benchmark.hh b/util/benchmark.hh new file mode 100644 index 00000000..050e6c64 --- /dev/null +++ b/util/benchmark.hh @@ -0,0 +1,115 @@ +#ifndef BENCHMARK_LOADED +#define BENCHMARK_LOADED + +#include "exception.hh" +#include <fmt/core.h> +#include <fstream> +#include <iostream> +#include <nlohmann/json.hpp> +#include <string> + +// Bookkeeping of test data to store data of one or more tests in a json file to +// facilitate future accounting. +// +// Usage Example 1 (single test): +// ============================== +// 1. define our test +// juggler_util::test_data test1{ +// {{"name", "example_test"}, +// {"title", "Example Test"}, +// {"description", "This is an example of a test definition"}, +// {"quantity", "efficiency"}, +// {"target", "1"}}}; +// 2. set pass/fail/error status and return value (in this case .99) +// test1.pass(0.99) +// 3. write our test data to a json file +// juggler_util::write_test_data(test1, "test1.json"); +// +// Usage Example 2 (multiple tests): +// ================================= +// 1. define our tests +// juggler_util::test_data test1{ +// {{"name", "example_test"}, +// {"title", "Example Test"}, +// {"description", "This is an example of a test definition"}, +// {"quantity", "efficiency"}, +// {"target", "1"}}}; +// juggler_util::test_data test2{ +// {{"name", "another_test"}, +// {"title", "Another example Test"}, +// {"description", "This is a second example of a test definition"}, +// {"quantity", "resolution"}, +// {"target", "3."}}}; +// 2. set pass/fail/error status and return value (in this case .99) +// test1.fail(10) +// 3. write our test data to a json file +// juggler_util::write_test_data({test1, test2}, "test.json"); + +// Namespace for utility scripts, FIXME this should be part of an independent +// library +namespace juggler_util { + +struct test_definition_error : exception { + test_definition_error(std::string_view msg) + : exception(msg, "test_definition_error") {} +}; + +// Wrapper for our test data json, with three methods to set the status +// after test completion (to pass, fail or error). The default value +// is error. +// The following fields should be defined in the definitions json +// for the test to make sense: +// - name: unique identifier for this test +// - title: Slightly more verbose identifier for this test +// - description: Concise description of what is tested +// - quantity: What quantity is tested? Unites of value/target +// - target: Target value of <quantity> that we want to reach +// - value: Actual value of <quantity> +// - weight: Weight for this test (this is defaulted to 1.0 if not specified) +// - result: pass/fail/error +struct test_data { + test_data(nlohmann::json definition) : json{std::move(definition)} { + json["weight"] = 1.0; + // initialize with error (as we don't have a value yet) + error(); + // Check that all required fields are present + for (const auto& field : {"name", "title", "description", "quantity", + "target", "value", "weight", "result"}) { + if (json.find(field) == json.end()) { + throw test_definition_error{ + fmt::format("Error in test definition: field '{}' missing", field)}; + } + } + } + // Set this test to pass/fail/error + void pass(double value) { update_result("pass", value); } + void fail(double value) { update_result("fail", value); } + void error(double value = 0) { update_result("error", value); } + + nlohmann::json json; + +private: + void update_result(std::string_view status, double value) { + json["result"] = status; + json["value"] = value; + } +}; + +void write_test_data(const std::vector<test_data>& data, + const std::string& fname) { + nlohmann::json test_data; + for (auto& entry : data) { + test_data.push_back(entry.json); + } + std::cout << fmt::format("Writing test data to {}\n", fname); + std::ofstream output_file(fname); + output_file << std::setw(4) << test_data << "\n"; +} +void write_test_data(const test_data& data, const std::string& fname) { + std::vector<test_data> vtd{data}; + write_test_data(vtd, fname); +} + +} // namespace juggler_util + +#endif diff --git a/util/exception.hh b/util/exception.hh new file mode 100644 index 00000000..a443c795 --- /dev/null +++ b/util/exception.hh @@ -0,0 +1,23 @@ +#ifndef UTIL_EXCEPTION +#define UTIL_EXCEPTION + +#include <exception> +#include <string> + +namespace juggler_util { +class exception : public std::exception { +public: + exception(std::string_view msg, std::string_view type = "exception") + : msg_{msg}, type_{type} {} + + virtual const char* what() const throw() { return msg_.c_str(); } + virtual const char* type() const throw() { return type_.c_str(); } + virtual ~exception() throw() {} + +private: + std::string msg_; + std::string type_; +}; +} // namespace juggler_util + +#endif -- GitLab