1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
|
/*
* Copyright (c) 2017-2020 Arm Limited.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef ARM_COMPUTE_TEST_FRAMEWORK
#define ARM_COMPUTE_TEST_FRAMEWORK
#include "DatasetModes.h"
#include "Exceptions.h"
#include "Profiler.h"
#include "TestCase.h"
#include "TestCaseFactory.h"
#include "TestResult.h"
#include "Utils.h"
#include "instruments/Instruments.h"
#include "printers/Printer.h"
#include <algorithm>
#include <chrono>
#include <map>
#include <memory>
#include <numeric>
#include <ostream>
#include <set>
#include <sstream>
#include <string>
#include <vector>
namespace arm_compute
{
namespace test
{
namespace framework
{
class TestFilter;
/** Framework configuration structure */
struct FrameworkConfig
{
std::vector<framework::InstrumentsDescription> instruments{}; /**< Instrument types that will be used for benchmarking. */
std::string name_filter{}; /**< Regular expression to filter tests by name. Only matching tests will be executed. */
std::string id_filter{}; /**< String to match selected test ids. Only matching tests will be executed. */
DatasetMode mode{ DatasetMode::ALL }; /**< Dataset mode. */
int num_iterations{ 1 }; /**< Number of iterations per test. */
float cooldown_sec{ -1.f }; /**< Delay between tests in seconds. */
LogLevel log_level{ LogLevel::NONE }; /**< Verbosity of the output. */
};
/** Information about a test case.
*
* A test can be identified either via its id or via its name. Additionally
* each test is tagged with the data set mode in which it will be used and
* its status.
*
* @note The mapping between test id and test name is not guaranteed to be
* stable. It is subject to change as new test are added.
*/
struct TestInfo
{
int id; /**< Test ID */
std::string name; /**< Test name */
DatasetMode mode; /**< Test data set mode */
TestCaseFactory::Status status; /**< Test status */
};
inline bool operator<(const TestInfo &lhs, const TestInfo &rhs)
{
return lhs.id < rhs.id;
}
/** Main framework class.
*
* Keeps track of the global state, owns all test cases and collects results.
*/
class Framework final
{
public:
/** Access to the singleton.
*
* @return Unique instance of the framework class.
*/
static Framework &get();
/** Supported instrument types for benchmarking.
*
* @return Set of all available instrument types.
*/
std::set<InstrumentsDescription> available_instruments() const;
/** Init the framework.
*
* @see TestFilter::TestFilter for the format of the string to filter ids.
*
* @param[in] config Framework configuration meta-data.
*/
void init(const FrameworkConfig &config);
/** Add a new test suite.
*
* @warning Cannot be used at execution time. It can only be used for
* registering test cases.
*
* @param[in] name Name of the added test suite.
*
* @return Name of the current test suite.
*/
void push_suite(std::string name);
/** Remove innermost test suite.
*
* @warning Cannot be used at execution time. It can only be used for
* registering test cases.
*/
void pop_suite();
/** Add a test case to the framework.
*
* @param[in] test_name Name of the new test case.
* @param[in] mode Mode in which to include the test.
* @param[in] status Status of the test case.
*/
template <typename T>
void add_test_case(std::string test_name, DatasetMode mode, TestCaseFactory::Status status);
/** Add a data test case to the framework.
*
* @param[in] test_name Name of the new test case.
* @param[in] mode Mode in which to include the test.
* @param[in] status Status of the test case.
* @param[in] description Description of @p data.
* @param[in] data Data that will be used as input to the test.
*/
template <typename T, typename D>
void add_data_test_case(std::string test_name, DatasetMode mode, TestCaseFactory::Status status, std::string description, D &&data);
/** Add info string for the next expectation/assertion.
*
* @param[in] info Info string.
*/
void add_test_info(std::string info);
/** Clear the collected test info. */
void clear_test_info();
/** Check if any info has been registered.
*
* @return True if there is test info.
*/
bool has_test_info() const;
/** Print test info.
*
* @param[out] os Output stream.
*/
void print_test_info(std::ostream &os) const;
/** Tell the framework that execution of a test starts.
*
* @param[in] info Test info.
*/
void log_test_start(const TestInfo &info);
/** Tell the framework that a test case is skipped.
*
* @param[in] info Test info.
*/
void log_test_skipped(const TestInfo &info);
/** Tell the framework that a test case finished.
*
* @param[in] info Test info.
*/
void log_test_end(const TestInfo &info);
/** Tell the framework that the currently running test case failed a non-fatal expectation.
*
* @param[in] error Description of the error.
*/
void log_failed_expectation(const TestError &error);
/** Print the debug information that has already been logged
*
* @param[in] info Description of the log info.
*/
void log_info(const std::string &info);
/** Number of iterations per test case.
*
* @return Number of iterations per test case.
*/
int num_iterations() const;
/** Set number of iterations per test case.
*
* @param[in] num_iterations Number of iterations per test case.
*/
void set_num_iterations(int num_iterations);
/** Should errors be caught or thrown by the framework.
*
* @return True if errors are thrown.
*/
bool throw_errors() const;
/** Set whether errors are caught or thrown by the framework.
*
* @param[in] throw_errors True if errors should be thrown.
*/
void set_throw_errors(bool throw_errors);
/** Indicates if test execution is stopped after the first failed test.
*
* @return True if the execution is going to be aborted after the first failed test.
*/
bool stop_on_error() const;
/** Set whether to abort execution after the first failed test.
*
* @param[in] stop_on_error True if execution is going to be aborted after first failed test.
*/
void set_stop_on_error(bool stop_on_error);
/** Indicates if a test should be marked as failed when its assets are missing.
*
* @return True if a test should be marked as failed when its assets are missing.
*/
bool error_on_missing_assets() const;
/** Set whether a test should be considered as failed if its assets cannot be found.
*
* @param[in] error_on_missing_assets True if a test should be marked as failed when its assets are missing.
*/
void set_error_on_missing_assets(bool error_on_missing_assets);
/** Run all enabled test cases.
*
* @return True if all test cases executed successful.
*/
bool run();
/** Set the result for an executed test case.
*
* @param[in] info Test info.
* @param[in] result Execution result.
*/
void set_test_result(TestInfo info, TestResult result);
/** Use the specified printer to output test results from the last run.
*
* This method can be used if the test results need to be obtained using a
* different printer than the one managed by the framework.
*
* @param[in] printer Printer used to output results.
*/
void print_test_results(Printer &printer) const;
/** Factory method to obtain a configured profiler.
*
* The profiler enables all instruments that have been passed to the @ref
* init method.
*
* @return Configured profiler to collect benchmark results.
*/
Profiler get_profiler() const;
/** Set the printer used for the output of test results.
*
* @param[in] printer Pointer to a printer.
*/
void add_printer(Printer *printer);
/** List of @ref TestInfo's.
*
* @return Vector with all test ids.
*/
std::vector<TestInfo> test_infos() const;
/** Get the current logging level
*
* @return The current logging level.
*/
LogLevel log_level() const;
/** Sets instruments info
*
* @note TODO(COMPMID-2638) : Remove once instruments are transferred outside the framework.
*
* @param[in] instr_info Instruments info to set
*/
void set_instruments_info(InstrumentsInfo instr_info);
private:
Framework();
~Framework() = default;
Framework(const Framework &) = delete;
Framework &operator=(const Framework &) = delete;
void run_test(const TestInfo &info, TestCaseFactory &test_factory);
std::map<TestResult::Status, int> count_test_results() const;
/** Returns the current test suite name.
*
* @warning Cannot be used at execution time to get the test suite of the
* currently executed test case. It can only be used for registering test
* cases.
*
* @return Name of the current test suite.
*/
std::string current_suite_name() const;
/* Perform func on all printers */
template <typename F>
void func_on_all_printers(F &&func);
std::vector<std::string> _test_suite_name{};
std::vector<std::unique_ptr<TestCaseFactory>> _test_factories{};
std::map<TestInfo, TestResult> _test_results{};
int _num_iterations{ 1 };
float _cooldown_sec{ -1.f };
bool _throw_errors{ false };
bool _stop_on_error{ false };
bool _error_on_missing_assets{ false };
std::vector<Printer *> _printers{};
using create_function = std::unique_ptr<Instrument>();
std::map<InstrumentsDescription, create_function *> _available_instruments{};
std::set<framework::InstrumentsDescription> _instruments{ std::pair<InstrumentType, ScaleFactor>(InstrumentType::NONE, ScaleFactor::NONE) };
std::unique_ptr<TestFilter> _test_filter;
LogLevel _log_level{ LogLevel::ALL };
const TestInfo *_current_test_info{ nullptr };
TestResult *_current_test_result{ nullptr };
std::vector<std::string> _test_info{};
};
template <typename T>
inline void Framework::add_test_case(std::string test_name, DatasetMode mode, TestCaseFactory::Status status)
{
_test_factories.emplace_back(support::cpp14::make_unique<SimpleTestCaseFactory<T>>(current_suite_name(), std::move(test_name), mode, status));
}
template <typename T, typename D>
inline void Framework::add_data_test_case(std::string test_name, DatasetMode mode, TestCaseFactory::Status status, std::string description, D &&data)
{
// WORKAROUND for GCC 4.9
// The function should get *it which is tuple but that seems to trigger a
// bug in the compiler.
auto tmp = std::unique_ptr<DataTestCaseFactory<T, decltype(*std::declval<D>())>>(new DataTestCaseFactory<T, decltype(*std::declval<D>())>(current_suite_name(), std::move(test_name), mode, status,
std::move(description), *data));
_test_factories.emplace_back(std::move(tmp));
}
} // namespace framework
} // namespace test
} // namespace arm_compute
#endif /* ARM_COMPUTE_TEST_FRAMEWORK */
|