blob: 1f0955083052ce4a64a4429b8409e7b165f2a697 [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
26#include "Exceptions.h"
27#include "support/ToolchainSupport.h"
28
29#include <chrono>
30#include <iostream>
31#include <sstream>
32#include <type_traits>
33
34namespace arm_compute
35{
36namespace test
37{
38namespace framework
39{
Moritz Pflanzera4f711b2017-07-05 11:02:23 +010040Framework::Framework()
41{
42 _available_instruments.emplace(InstrumentType::WALL_CLOCK_TIMER, Instrument::make_instrument<WallClockTimer>);
43#ifdef PMU_ENABLED
44 _available_instruments.emplace(InstrumentType::PMU_CYCLE_COUNTER, Instrument::make_instrument<CycleCounter>);
45 _available_instruments.emplace(InstrumentType::PMU_INSTRUCTION_COUNTER, Instrument::make_instrument<InstructionCounter>);
46#endif /* PMU_ENABLED */
47}
48
49std::set<InstrumentType> Framework::available_instruments() const
50{
51 std::set<InstrumentType> types;
52
53 for(const auto &instrument : _available_instruments)
54 {
55 types.emplace(instrument.first);
56 }
57
58 return types;
59}
60
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010061std::tuple<int, int, int> Framework::count_test_results() const
62{
63 int passed = 0;
64 int failed = 0;
65 int crashed = 0;
66
67 for(const auto &test : _test_results)
68 {
69 switch(test.second.status)
70 {
71 case TestResult::Status::SUCCESS:
72 ++passed;
73 break;
74 case TestResult::Status::FAILED:
75 ++failed;
76 break;
77 case TestResult::Status::CRASHED:
78 ++crashed;
79 break;
80 default:
81 // Do nothing
82 break;
83 }
84 }
85
86 return std::make_tuple(passed, failed, crashed);
87}
88
89Framework &Framework::get()
90{
91 static Framework instance;
92 return instance;
93}
94
Moritz Pflanzerd03b00a2017-07-17 13:50:12 +010095void Framework::init(const std::vector<InstrumentType> &instruments, int num_iterations, DatasetMode mode, const std::string &name_filter, const std::string &id_filter)
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +010096{
97 _test_name_filter = std::regex{ name_filter };
98 _test_id_filter = std::regex{ id_filter };
99 _num_iterations = num_iterations;
Moritz Pflanzerd03b00a2017-07-17 13:50:12 +0100100 _dataset_mode = mode;
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100101
102 _instruments = InstrumentType::NONE;
103
104 for(const auto &instrument : instruments)
105 {
106 _instruments |= instrument;
107 }
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100108}
109
110std::string Framework::current_suite_name() const
111{
112 return join(_test_suite_name.cbegin(), _test_suite_name.cend(), "/");
113}
114
115void Framework::push_suite(std::string name)
116{
117 _test_suite_name.emplace_back(std::move(name));
118}
119
120void Framework::pop_suite()
121{
122 _test_suite_name.pop_back();
123}
124
125void Framework::log_test_start(const std::string &test_name)
126{
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100127 if(_printer != nullptr)
128 {
129 _printer->print_test_header(test_name);
130 }
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100131}
132
133void Framework::log_test_skipped(const std::string &test_name)
134{
135 static_cast<void>(test_name);
136}
137
138void Framework::log_test_end(const std::string &test_name)
139{
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100140 if(_printer != nullptr)
141 {
142 _printer->print_measurements(_test_results.at(test_name).measurements);
143 _printer->print_test_footer();
144 }
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100145}
146
147void Framework::log_failed_expectation(const std::string &msg)
148{
149 std::cerr << "ERROR: " << msg << "\n";
Moritz Pflanzere1103a82017-07-18 12:20:45 +0100150
151 if(_current_test_result != nullptr)
152 {
153 _current_test_result->status = TestResult::Status::FAILED;
154 }
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100155}
156
157int Framework::num_iterations() const
158{
159 return _num_iterations;
160}
161
162void Framework::set_num_iterations(int num_iterations)
163{
164 _num_iterations = num_iterations;
165}
166
167void Framework::set_throw_errors(bool throw_errors)
168{
169 _throw_errors = throw_errors;
170}
171
172bool Framework::throw_errors() const
173{
174 return _throw_errors;
175}
176
177bool Framework::is_enabled(const TestId &id) const
178{
Moritz Pflanzerd03b00a2017-07-17 13:50:12 +0100179 int test_id = 0;
180 std::string name;
181 DatasetMode mode = DatasetMode::ALL;
182 std::tie(test_id, name, mode) = id;
183
184 if((mode & _dataset_mode) == DatasetMode::DISABLED)
185 {
186 return false;
187 }
188
189 if(!std::regex_search(support::cpp11::to_string(test_id), _test_id_filter))
190 {
191 return false;
192 }
193
194 if(!std::regex_search(name, _test_name_filter))
195 {
196 return false;
197 }
198
199 return true;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100200}
201
202void Framework::run_test(TestCaseFactory &test_factory)
203{
204 const std::string test_case_name = test_factory.name();
205
206 log_test_start(test_case_name);
207
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100208 Profiler profiler = get_profiler();
Moritz Pflanzere1103a82017-07-18 12:20:45 +0100209 TestResult result(TestResult::Status::SUCCESS);
210
211 _current_test_result = &result;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100212
213 try
214 {
215 std::unique_ptr<TestCase> test_case = test_factory.make();
216
217 try
218 {
219 test_case->do_setup();
220
221 for(int i = 0; i < _num_iterations; ++i)
222 {
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100223 profiler.start();
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100224 test_case->do_run();
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100225 profiler.stop();
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100226 }
227
228 test_case->do_teardown();
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100229 }
230 catch(const TestError &error)
231 {
232 std::cerr << "FATAL ERROR: " << error.what() << "\n";
233 result.status = TestResult::Status::FAILED;
234
235 if(_throw_errors)
236 {
237 throw;
238 }
239 }
240 catch(const std::exception &error)
241 {
242 std::cerr << "FATAL ERROR: Received unhandled error: '" << error.what() << "'\n";
243 result.status = TestResult::Status::CRASHED;
244
245 if(_throw_errors)
246 {
247 throw;
248 }
249 }
250 catch(...)
251 {
252 std::cerr << "FATAL ERROR: Received unhandled exception\n";
253 result.status = TestResult::Status::CRASHED;
254
255 if(_throw_errors)
256 {
257 throw;
258 }
259 }
260 }
261 catch(const std::exception &error)
262 {
263 std::cerr << "FATAL ERROR: Received unhandled error during fixture creation: '" << error.what() << "'\n";
264
265 if(_throw_errors)
266 {
267 throw;
268 }
269 }
270 catch(...)
271 {
272 std::cerr << "FATAL ERROR: Received unhandled exception during fixture creation\n";
Moritz Pflanzere1103a82017-07-18 12:20:45 +0100273 result.status = TestResult::Status::CRASHED;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100274
275 if(_throw_errors)
276 {
277 throw;
278 }
279 }
280
Moritz Pflanzere1103a82017-07-18 12:20:45 +0100281 _current_test_result = nullptr;
282
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100283 result.measurements = profiler.measurements();
284
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100285 set_test_result(test_case_name, result);
286 log_test_end(test_case_name);
287}
288
289bool Framework::run()
290{
291 // Clear old test results
292 _test_results.clear();
293 _runtime = std::chrono::seconds{ 0 };
294
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100295 if(_printer != nullptr)
296 {
297 _printer->print_run_header();
298 }
299
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100300 const auto start = std::chrono::high_resolution_clock::now();
301
302 int id = 0;
303
304 for(auto &test_factory : _test_factories)
305 {
306 const std::string test_case_name = test_factory->name();
307
Moritz Pflanzerd03b00a2017-07-17 13:50:12 +0100308 if(!is_enabled(TestId(id, test_case_name, test_factory->mode())))
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100309 {
310 log_test_skipped(test_case_name);
311 }
312 else
313 {
314 run_test(*test_factory);
315 }
316
317 ++id;
318 }
319
320 const auto end = std::chrono::high_resolution_clock::now();
321
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100322 if(_printer != nullptr)
323 {
324 _printer->print_run_footer();
325 }
326
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100327 _runtime = std::chrono::duration_cast<std::chrono::seconds>(end - start);
328
329 int passed = 0;
330 int failed = 0;
331 int crashed = 0;
332
333 std::tie(passed, failed, crashed) = count_test_results();
334
335 std::cout << "Executed " << _test_results.size() << " test(s) (" << passed << " passed, " << failed << " failed, " << crashed << " crashed) in " << _runtime.count() << " second(s)\n";
336
337 return (static_cast<unsigned int>(passed) == _test_results.size());
338}
339
340void Framework::set_test_result(std::string test_case_name, TestResult result)
341{
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100342 _test_results.emplace(std::move(test_case_name), std::move(result));
343}
344
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100345void Framework::print_test_results(Printer &printer) const
346{
347 printer.print_run_header();
348
349 for(const auto &test : _test_results)
350 {
351 printer.print_test_header(test.first);
352 printer.print_measurements(test.second.measurements);
353 printer.print_test_footer();
354 }
355
356 printer.print_run_footer();
357}
358
Moritz Pflanzera4f711b2017-07-05 11:02:23 +0100359Profiler Framework::get_profiler() const
360{
361 Profiler profiler;
362
363 for(const auto &instrument : _available_instruments)
364 {
365 if((instrument.first & _instruments) != InstrumentType::NONE)
366 {
367 profiler.add(instrument.second());
368 }
369 }
370
371 return profiler;
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100372}
373
Moritz Pflanzer80fffae2017-07-05 11:02:37 +0100374void Framework::set_printer(Printer *printer)
375{
376 _printer = printer;
377}
378
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100379std::vector<Framework::TestId> Framework::test_ids() const
380{
381 std::vector<TestId> ids;
382
383 int id = 0;
384
385 for(const auto &factory : _test_factories)
386 {
Moritz Pflanzerd03b00a2017-07-17 13:50:12 +0100387 if(is_enabled(TestId(id, factory->name(), factory->mode())))
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100388 {
Moritz Pflanzerd03b00a2017-07-17 13:50:12 +0100389 ids.emplace_back(id, factory->name(), factory->mode());
Moritz Pflanzerfc95ed22017-07-05 11:07:07 +0100390 }
391
392 ++id;
393 }
394
395 return ids;
396}
397} // namespace framework
398} // namespace test
399} // namespace arm_compute