blob: 056ee598603edf9695501390ed4ca1de595f6501 [file] [log] [blame]
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +01001/*
2 * Copyright (c) 2017 ARM Limited.
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#include "Framework.h"
25
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010026#include "support/ToolchainSupport.h"
27
Moritz Pflanzer47752c92017-07-18 13:38:47 +010028#ifdef ARM_COMPUTE_CL
29#include "arm_compute/core/CL/OpenCL.h"
Anthony Barbierbf959222017-07-19 17:01:42 +010030#include "arm_compute/runtime/CL/CLScheduler.h"
Moritz Pflanzer47752c92017-07-18 13:38:47 +010031#endif /* ARM_COMPUTE_CL */
32
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010033#include <chrono>
34#include <iostream>
35#include <sstream>
36#include <type_traits>
37
38namespace arm_compute
39{
40namespace test
41{
42namespace framework
43{
Moritz Pflanzera4f711b2017-07-05 11:02:23 +010044Framework::Framework()
45{
46 _available_instruments.emplace(InstrumentType::WALL_CLOCK_TIMER, Instrument::make_instrument<WallClockTimer>);
47#ifdef PMU_ENABLED
48 _available_instruments.emplace(InstrumentType::PMU_CYCLE_COUNTER, Instrument::make_instrument<CycleCounter>);
49 _available_instruments.emplace(InstrumentType::PMU_INSTRUCTION_COUNTER, Instrument::make_instrument<InstructionCounter>);
50#endif /* PMU_ENABLED */
51}
52
53std::set<InstrumentType> Framework::available_instruments() const
54{
55 std::set<InstrumentType> types;
56
57 for(const auto &instrument : _available_instruments)
58 {
59 types.emplace(instrument.first);
60 }
61
62 return types;
63}
64
Moritz Pflanzerbf234e02017-07-24 15:04:14 +010065std::map<TestResult::Status, int> Framework::count_test_results() const
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010066{
Moritz Pflanzerbf234e02017-07-24 15:04:14 +010067 std::map<TestResult::Status, int> counts;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010068
69 for(const auto &test : _test_results)
70 {
Moritz Pflanzerbf234e02017-07-24 15:04:14 +010071 ++counts[test.second.status];
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010072 }
73
Moritz Pflanzerbf234e02017-07-24 15:04:14 +010074 return counts;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010075}
76
77Framework &Framework::get()
78{
79 static Framework instance;
80 return instance;
81}
82
Moritz Pflanzer2ac50402017-07-24 15:52:54 +010083void Framework::init(const std::vector<InstrumentType> &instruments, int num_iterations, DatasetMode mode, const std::string &name_filter, int64_t id_filter, LogLevel log_level)
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010084{
85 _test_name_filter = std::regex{ name_filter };
Moritz Pflanzer81527bf2017-07-20 15:11:33 +010086 _test_id_filter = id_filter;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010087 _num_iterations = num_iterations;
Moritz Pflanzerd03b00a2017-07-17 13:50:12 +010088 _dataset_mode = mode;
Moritz Pflanzer2ac50402017-07-24 15:52:54 +010089 _log_level = log_level;
Moritz Pflanzera4f711b2017-07-05 11:02:23 +010090
91 _instruments = InstrumentType::NONE;
92
93 for(const auto &instrument : instruments)
94 {
95 _instruments |= instrument;
96 }
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010097}
98
99std::string Framework::current_suite_name() const
100{
101 return join(_test_suite_name.cbegin(), _test_suite_name.cend(), "/");
102}
103
104void Framework::push_suite(std::string name)
105{
106 _test_suite_name.emplace_back(std::move(name));
107}
108
109void Framework::pop_suite()
110{
111 _test_suite_name.pop_back();
112}
113
Moritz Pflanzerc7d15032017-07-18 16:21:16 +0100114void Framework::add_test_info(std::string info)
115{
116 _test_info.emplace_back(std::move(info));
117}
118
119void Framework::clear_test_info()
120{
121 _test_info.clear();
122}
123
124bool Framework::has_test_info() const
125{
126 return !_test_info.empty();
127}
128
129void Framework::print_test_info(std::ostream &os) const
130{
131 os << "CONTEXT:\n";
132
133 for(const auto &str : _test_info)
134 {
135 os << " " << str << "\n";
136 }
137}
138
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100139void Framework::log_test_start(const TestInfo &info)
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100140{
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100141 if(_printer != nullptr && _log_level >= LogLevel::TESTS)
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100142 {
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100143 _printer->print_test_header(info);
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100144 }
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100145}
146
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100147void Framework::log_test_skipped(const TestInfo &info)
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100148{
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100149 static_cast<void>(info);
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100150}
151
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100152void Framework::log_test_end(const TestInfo &info)
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100153{
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100154 if(_printer != nullptr)
155 {
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100156 if(_log_level >= LogLevel::MEASUREMENTS)
157 {
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100158 _printer->print_measurements(_test_results.at(info).measurements);
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100159 }
160
161 if(_log_level >= LogLevel::TESTS)
162 {
163 _printer->print_test_footer();
164 }
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100165 }
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100166}
167
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100168void Framework::log_failed_expectation(const std::string &msg, LogLevel level)
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100169{
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100170 if(_log_level >= level)
171 {
172 std::cerr << "ERROR: " << msg << "\n";
173 }
Moritz Pflanzere1103a82017-07-18 12:20:45 +0100174
175 if(_current_test_result != nullptr)
176 {
177 _current_test_result->status = TestResult::Status::FAILED;
178 }
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100179}
180
181int Framework::num_iterations() const
182{
183 return _num_iterations;
184}
185
186void Framework::set_num_iterations(int num_iterations)
187{
188 _num_iterations = num_iterations;
189}
190
191void Framework::set_throw_errors(bool throw_errors)
192{
193 _throw_errors = throw_errors;
194}
195
196bool Framework::throw_errors() const
197{
198 return _throw_errors;
199}
200
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100201bool Framework::is_selected(const TestInfo &info) const
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100202{
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100203 if((info.mode & _dataset_mode) == DatasetMode::DISABLED)
Moritz Pflanzerd03b00a2017-07-17 13:50:12 +0100204 {
205 return false;
206 }
207
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100208 if(_test_id_filter > -1 && _test_id_filter != info.id)
Moritz Pflanzerd03b00a2017-07-17 13:50:12 +0100209 {
210 return false;
211 }
212
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100213 if(!std::regex_search(info.name, _test_name_filter))
Moritz Pflanzerd03b00a2017-07-17 13:50:12 +0100214 {
215 return false;
216 }
217
218 return true;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100219}
220
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100221void Framework::run_test(const TestInfo &info, TestCaseFactory &test_factory)
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100222{
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100223 if(test_factory.status() == TestCaseFactory::Status::DISABLED)
224 {
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100225 log_test_skipped(info);
226 set_test_result(info, TestResult(TestResult::Status::DISABLED));
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100227 return;
228 }
229
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100230 log_test_start(info);
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100231
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100232 Profiler profiler = get_profiler();
Moritz Pflanzere1103a82017-07-18 12:20:45 +0100233 TestResult result(TestResult::Status::SUCCESS);
234
235 _current_test_result = &result;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100236
237 try
238 {
239 std::unique_ptr<TestCase> test_case = test_factory.make();
240
241 try
242 {
243 test_case->do_setup();
244
245 for(int i = 0; i < _num_iterations; ++i)
246 {
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100247 profiler.start();
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100248 test_case->do_run();
Anthony Barbierbf959222017-07-19 17:01:42 +0100249#ifdef ARM_COMPUTE_CL
250 if(opencl_is_available())
251 {
252 CLScheduler::get().sync();
253 }
254#endif /* ARM_COMPUTE_CL */
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100255 profiler.stop();
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100256 }
257
258 test_case->do_teardown();
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100259 }
260 catch(const TestError &error)
261 {
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100262 if(_log_level >= error.level())
263 {
264 std::cerr << "FATAL ERROR: " << error.what() << "\n";
265 }
266
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100267 result.status = TestResult::Status::FAILED;
268
269 if(_throw_errors)
270 {
271 throw;
272 }
273 }
Moritz Pflanzer47752c92017-07-18 13:38:47 +0100274#ifdef ARM_COMPUTE_CL
275 catch(const ::cl::Error &error)
276 {
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100277 if(_log_level >= LogLevel::ERRORS)
278 {
279 std::cerr << "FATAL CL ERROR: " << error.what() << " with code " << error.err() << "\n";
280 }
281
Moritz Pflanzer47752c92017-07-18 13:38:47 +0100282 result.status = TestResult::Status::FAILED;
283
284 if(_throw_errors)
285 {
286 throw;
287 }
288 }
289#endif /* ARM_COMPUTE_CL */
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100290 catch(const std::exception &error)
291 {
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100292 if(_log_level >= LogLevel::ERRORS)
293 {
294 std::cerr << "FATAL ERROR: Received unhandled error: '" << error.what() << "'\n";
295 }
296
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100297 result.status = TestResult::Status::CRASHED;
298
299 if(_throw_errors)
300 {
301 throw;
302 }
303 }
304 catch(...)
305 {
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100306 if(_log_level >= LogLevel::ERRORS)
307 {
308 std::cerr << "FATAL ERROR: Received unhandled exception\n";
309 }
310
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100311 result.status = TestResult::Status::CRASHED;
312
313 if(_throw_errors)
314 {
315 throw;
316 }
317 }
318 }
319 catch(const std::exception &error)
320 {
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100321 if(_log_level >= LogLevel::ERRORS)
322 {
323 std::cerr << "FATAL ERROR: Received unhandled error during fixture creation: '" << error.what() << "'\n";
324 }
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100325
326 if(_throw_errors)
327 {
328 throw;
329 }
330 }
331 catch(...)
332 {
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100333 if(_log_level >= LogLevel::ERRORS)
334 {
335 std::cerr << "FATAL ERROR: Received unhandled exception during fixture creation\n";
336 }
337
Moritz Pflanzere1103a82017-07-18 12:20:45 +0100338 result.status = TestResult::Status::CRASHED;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100339
340 if(_throw_errors)
341 {
342 throw;
343 }
344 }
345
Moritz Pflanzere1103a82017-07-18 12:20:45 +0100346 _current_test_result = nullptr;
347
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100348 if(test_factory.status() == TestCaseFactory::Status::EXPECTED_FAILURE && result.status == TestResult::Status::FAILED)
349 {
350 result.status = TestResult::Status::EXPECTED_FAILURE;
351 }
352
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100353 result.measurements = profiler.measurements();
354
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100355 set_test_result(info, result);
356 log_test_end(info);
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100357}
358
359bool Framework::run()
360{
361 // Clear old test results
362 _test_results.clear();
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100363
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100364 if(_printer != nullptr && _log_level >= LogLevel::TESTS)
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100365 {
366 _printer->print_run_header();
367 }
368
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100369 const std::chrono::time_point<std::chrono::high_resolution_clock> start = std::chrono::high_resolution_clock::now();
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100370
371 int id = 0;
372
373 for(auto &test_factory : _test_factories)
374 {
375 const std::string test_case_name = test_factory->name();
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100376 const TestInfo test_info{ id, test_case_name, test_factory->mode(), test_factory->status() };
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100377
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100378 if(is_selected(test_info))
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100379 {
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100380 run_test(test_info, *test_factory);
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100381 }
382
383 ++id;
384 }
385
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100386 const std::chrono::time_point<std::chrono::high_resolution_clock> end = std::chrono::high_resolution_clock::now();
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100387
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100388 if(_printer != nullptr && _log_level >= LogLevel::TESTS)
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100389 {
390 _printer->print_run_footer();
391 }
392
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100393 auto runtime = std::chrono::duration_cast<std::chrono::seconds>(end - start);
394 std::map<TestResult::Status, int> results = count_test_results();
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100395
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100396 if(_log_level > LogLevel::NONE)
397 {
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100398 std::cout << "Executed " << results.size() << " test(s) ("
399 << results[TestResult::Status::SUCCESS] << " passed, "
400 << results[TestResult::Status::EXPECTED_FAILURE] << " expected failures, "
401 << results[TestResult::Status::FAILED] << " failed, "
402 << results[TestResult::Status::CRASHED] << " crashed, "
403 << results[TestResult::Status::DISABLED] << " disabled) in " << runtime.count() << " second(s)\n";
Moritz Pflanzer2ac50402017-07-24 15:52:54 +0100404 }
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100405
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100406 int num_successful_tests = results[TestResult::Status::SUCCESS] + results[TestResult::Status::EXPECTED_FAILURE];
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100407
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100408 return (static_cast<unsigned int>(num_successful_tests) == _test_results.size());
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100409}
410
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100411void Framework::set_test_result(TestInfo info, TestResult result)
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100412{
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100413 _test_results.emplace(std::move(info), std::move(result));
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100414}
415
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100416void Framework::print_test_results(Printer &printer) const
417{
418 printer.print_run_header();
419
420 for(const auto &test : _test_results)
421 {
422 printer.print_test_header(test.first);
423 printer.print_measurements(test.second.measurements);
424 printer.print_test_footer();
425 }
426
427 printer.print_run_footer();
428}
429
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100430Profiler Framework::get_profiler() const
431{
432 Profiler profiler;
433
434 for(const auto &instrument : _available_instruments)
435 {
436 if((instrument.first & _instruments) != InstrumentType::NONE)
437 {
438 profiler.add(instrument.second());
439 }
440 }
441
442 return profiler;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100443}
444
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100445void Framework::set_printer(Printer *printer)
446{
447 _printer = printer;
448}
449
Moritz Pflanzer542002c2017-07-26 16:03:58 +0100450std::vector<TestInfo> Framework::test_infos() const
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100451{
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100452 std::vector<TestInfo> ids;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100453
454 int id = 0;
455
456 for(const auto &factory : _test_factories)
457 {
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100458 TestInfo test_info{ id, factory->name(), factory->mode(), factory->status() };
459
460 if(is_selected(test_info))
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100461 {
Moritz Pflanzerbf234e02017-07-24 15:04:14 +0100462 ids.emplace_back(std::move(test_info));
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100463 }
464
465 ++id;
466 }
467
468 return ids;
469}
470} // namespace framework
471} // namespace test
472} // namespace arm_compute