30 #include "application.h"
32 #include "asset_manager.h"
34 #define assert_equal(expected, actual) _assert_equal((expected), (actual), __FILE__, __LINE__)
35 #define assert_not_equal(expected, actual) _assert_not_equal((expected), (actual), __FILE__, __LINE__)
36 #define assert_false(actual) _assert_false((actual), __FILE__, __LINE__)
37 #define assert_true(actual) _assert_true((actual), __FILE__, __LINE__)
38 #define assert_close(expected, actual, difference) _assert_close((expected), (actual), (difference), __FILE__, __LINE__)
39 #define assert_is_null(actual) _assert_is_null((actual), __FILE__, __LINE__)
40 #define assert_is_not_null(actual) _assert_is_not_null((actual), __FILE__, __LINE__)
41 #define assert_raises(exception, func) _assert_raises<exception>((func), __FILE__, __LINE__)
42 #define assert_items_equal(expected, actual) _assert_items_equal((actual), (expected), __FILE__, __LINE__)
43 #define not_implemented() _not_implemented(__FILE__, __LINE__)
61 std::string format(T value) {
64 return _do_format(0, ss.str());
68 std::string format(Counter count, T value) {
71 return _do_format(count.c, ss.str());
74 template<
typename T,
typename... Args>
75 std::string format(T value,
const Args&... args) {
78 return StringFormatter(_do_format(0, ss.str())).format(Counter(1), args...);
81 template<
typename T,
typename... Args>
82 std::string format(Counter count, T value,
const Args&... args) {
85 return StringFormatter(_do_format(count.c, ss.str())).format(Counter(count.c + 1), args...);
88 std::string _do_format(uint32_t counter,
const std::string& value) {
92 const std::string to_replace =
"{" + ss.str() +
"}";
93 std::string output = templ_;
95 auto replace = [](std::string& str,
const std::string& from,
const std::string& to) ->
bool {
96 size_t start_pos = str.find(from);
97 if(start_pos == std::string::npos)
99 str.replace(start_pos, from.length(), to);
103 replace(output, to_replace, value);
118 std::vector<std::string> split() {
119 std::vector<std::string> result;
124 if(!buffer.empty()) {
125 result.push_back(buffer);
133 if(!buffer.empty()) {
134 result.push_back(buffer);
149 std::logic_error(what),
154 AssertionError(
const std::pair<std::string, int> file_and_line,
const std::string& what):
155 std::logic_error(what),
156 file(file_and_line.first),
157 line(file_and_line.second) {
173 std::logic_error(
_Format(
"Not implemented at {0}:{1}").format(file, line)) {}
180 std::logic_error(reason) {
189 virtual void set_up() {}
190 virtual void tear_down() {}
192 void skip_if(
const bool& flag,
const std::string& reason) {
196 template<
typename T,
typename U>
197 void _assert_equal(T expected, U actual, std::string file,
int line) {
198 if(expected != actual) {
199 auto file_and_line = std::make_pair(file, line);
204 template<
typename T,
typename U>
205 void _assert_not_equal(T lhs, U rhs, std::string file,
int line) {
207 auto file_and_line = std::make_pair(file, line);
213 void _assert_true(T actual, std::string file,
int line) {
215 auto file_and_line = std::make_pair(file, line);
221 void _assert_false(T actual, std::string file,
int line) {
223 auto file_and_line = std::make_pair(file, line);
228 template<
typename T,
typename U,
typename V>
229 void _assert_close(T expected, U actual, V difference, std::string file,
int line) {
230 if(actual < expected - difference ||
231 actual > expected + difference) {
232 auto file_and_line = std::make_pair(file, line);
238 void _assert_is_null(T* thing, std::string file,
int line) {
239 if(thing !=
nullptr) {
240 auto file_and_line = std::make_pair(file, line);
246 void _assert_is_not_null(T* thing, std::string file,
int line) {
247 if(thing ==
nullptr) {
248 auto file_and_line = std::make_pair(file, line);
253 template<
typename T,
typename Func>
254 void _assert_raises(Func func, std::string file,
int line) {
257 auto file_and_line = std::make_pair(file, line);
262 template<
typename T,
typename U>
263 void _assert_items_equal(
const T& lhs,
const U& rhs, std::string file,
int line) {
264 auto file_and_line = std::make_pair(file, line);
266 if(lhs.size() != rhs.size()) {
270 for(
auto item: lhs) {
271 if(std::find(rhs.begin(), rhs.end(), item) == rhs.end()) {
277 void _not_implemented(std::string file,
int line) {
284 template<
typename T,
typename U>
285 void register_case(std::vector<U> methods, std::vector<std::string> names) {
286 std::shared_ptr<TestCase> instance = std::make_shared<T>();
288 instances_.push_back(instance);
290 for(std::string name: names) {
291 names_.push_back(name);
294 for(U& method: methods) {
295 std::function<void()> func = std::bind(method,
dynamic_cast<T*
>(instance.get()));
296 tests_.push_back([=]() {
302 instance->tear_down();
306 instance->tear_down();
311 int32_t run(
const std::string& test_case,
const std::string& junit_output=
"") {
317 auto new_tests = tests_;
318 auto new_names = names_;
320 if(!test_case.empty()) {
324 for(uint32_t i = 0; i < names_.size(); ++i) {
325 if(names_[i].find(test_case) == 0) {
326 new_tests.push_back(tests_[i]);
327 new_names.push_back(names_[i]);
332 std::cout << std::endl <<
"Running " << new_tests.size() <<
" tests" << std::endl << std::endl;
334 std::vector<std::string> junit_lines;
335 junit_lines.push_back(
"<testsuites>\n");
337 std::string klass =
"";
339 for(std::function<
void ()> test: new_tests) {
340 std::string name = new_names[ran];
341 std::string this_klass(name.begin(), name.begin() + name.find_first_of(
":"));
342 bool close_klass = ran == (int) new_tests.size() - 1;
344 if(this_klass != klass) {
346 junit_lines.push_back(
" </testsuite>\n");
349 junit_lines.push_back(
" <testsuite name=\"" + this_klass +
"\">\n");
353 junit_lines.push_back(
" <testcase name=\"" + new_names[ran] +
"\">\n");
354 std::string output =
" " + new_names[ran];
356 for(
int i = output.length(); i < 76; ++i) {
362 std::cout <<
"\033[32m" <<
" OK " <<
"\033[0m" << std::endl;
363 junit_lines.push_back(
" </testcase>\n");
365 std::cout <<
"\033[34m" <<
" SKIPPED" <<
"\033[0m" << std::endl;
367 junit_lines.push_back(
" </testcase>\n");
369 std::cout <<
"\033[34m" <<
" SKIPPED" <<
"\033[0m" << std::endl;
371 junit_lines.push_back(
" </testcase>\n");
373 std::cout <<
"\033[33m" <<
" FAILED " <<
"\033[0m" << std::endl;
374 std::cout <<
" " << e.what() << std::endl;
375 if(!e.file.empty()) {
376 std::cout <<
" " << e.file <<
":" << e.line << std::endl;
378 std::ifstream ifs(e.file);
381 std::vector<std::string> lines;
382 while(std::getline(ifs, buffer)) {
383 lines.push_back(buffer);
386 int line_count = lines.size();
387 if(line_count && e.line <= line_count) {
388 std::cout << lines.at(e.line - 1) << std::endl << std::endl;
394 junit_lines.push_back(
" <failure message=\"" + std::string(e.what()) +
"\"/>\n");
395 junit_lines.push_back(
" </testcase>\n");
396 }
catch(std::exception& e) {
397 std::cout <<
"\033[31m" <<
" EXCEPT " << std::endl;
398 std::cout <<
" " << e.what() <<
"\033[0m" << std::endl;
401 junit_lines.push_back(
" <failure message=\"" + std::string(e.what()) +
"\"/>\n");
402 junit_lines.push_back(
" </testcase>\n");
404 std::cout <<
"\033[0m";
408 junit_lines.push_back(
" </testsuite>\n");
412 junit_lines.push_back(
"</testsuites>\n");
414 if(!junit_output.empty()) {
415 FILE* f = fopen(junit_output.c_str(),
"wt");
417 for(
auto& line: junit_lines) {
418 fwrite(line.c_str(),
sizeof(
char), line.length(), f);
425 std::cout <<
"-----------------------" << std::endl;
426 if(!failed && !crashed && !skipped) {
427 std::cout <<
"All tests passed" << std::endl << std::endl;
430 std::cout << skipped <<
" tests skipped";
437 std::cout << failed <<
" tests failed";
444 std::cout << crashed <<
" tests crashed";
446 std::cout << std::endl << std::endl;
449 return failed + crashed;
453 std::vector<std::shared_ptr<TestCase>> instances_;
454 std::vector<std::function<void()> > tests_;
455 std::vector<std::string> names_;
464 void load()
override {}
475 scenes->register_scene<
TestScene>(
"main");
482 void set_app_and_window(std::shared_ptr<Application>* app,
Window** window,
SceneBase** scene) {
483 static std::shared_ptr<Application> application;
489 config.fullscreen =
false;
490 config.log_level = LOG_LEVEL_WARN;
494 config.search_paths.push_back(
"assets");
495 config.search_paths.push_back(
"sample_data");
497 #if defined(__DREAMCAST__)
498 config.search_paths.push_back(
"/cd");
499 config.search_paths.push_back(
"/cd/assets");
500 config.search_paths.push_back(
"/cd/sample_data");
501 config.search_paths.push_back(
"/pc");
502 config.search_paths.push_back(
"/pc/assets");
503 config.search_paths.push_back(
"/pc/sample_data");
506 application.reset(
new TestApp(config));
507 application->run_frame();
509 application->scenes->unload(
"main");
510 application->stop_all_coroutines();
511 application->update_coroutines();
512 application->window->reset();
514 application->scenes->activate(
"main");
518 application->run_frame();
522 *window = (*app)->window;
523 *scene = (*app)->scenes->active_scene().get();
529 std::shared_ptr<Application> application;
532 virtual void set_up() {
534 set_app_and_window(&application, &window, &scene);