#define DOCTEST_CONFIG_IMPLEMENT #include "main.h" #include #include #include #include #include #include #include #include #include #include #include static const char* datadir = NOTCURSES_SHARE; auto testing_notcurses() -> struct notcurses* { notcurses_options nopts{}; nopts.loglevel = NCLOGLEVEL_VERBOSE; nopts.flags = NCOPTION_SUPPRESS_BANNERS | NCOPTION_INHIBIT_SETLOCALE; auto nc = notcurses_init(&nopts, nullptr); return nc; } auto find_data(const char* datum) -> char* { std::filesystem::path p = datadir; p /= datum; return strdup(p.c_str()); } static void handle_opts(const char** argv){ bool inarg = false; while(*argv){ if(inarg){ datadir = strdup(*argv); inarg = false; }else if(strcmp(*argv, "-p") == 0){ inarg = true; } ++argv; } } // check that the (provided or default) data directory exists, and has at // least one of our necessary files. otherwise, print a warning + return error. static int check_data_dir(){ auto p = find_data("changes.jpg"); if(!p){ std::cerr << "Coudln't find testing data! Supply directory with -p." << std::endl; return -1; } struct stat s; if(stat(p, &s)){ std::cerr << "Couldn't open " << p << ". Supply directory with -p." << std::endl; free(p); return -1; } free(p); return 0; } // reset the terminal in the event of early exit (notcurses_init() presumably // ran, but we don't have the notcurses struct to destroy, so just do it raw). static void reset_terminal(){ int fd = open("/dev/tty", O_RDWR|O_CLOEXEC); if(fd >= 0){ struct termios tios; if(tcgetattr(fd, &tios) == 0){ tios.c_iflag |= INLCR; tios.c_lflag |= ISIG | ICANON | ECHO; tcsetattr(fd, TCSADRAIN, &tios); } char* str = tigetstr("sgr0"); if(str != (char*)-1){ printf("%s", str); } fflush(stdout); str = tigetstr("oc"); if(str != (char*)-1){ printf("%s", str); } fflush(stdout); close(fd); } } // from https://github.com/onqtam/doctest/blob/master/doc/markdown/commandline.md class dt_removed { std::vector vec; public: dt_removed(const char** argv_in) { for(; *argv_in; ++argv_in) if(strncmp(*argv_in, "--dt-", strlen("--dt-")) != 0) vec.push_back(*argv_in); vec.push_back(nullptr); } auto argc() -> int { return static_cast(vec.size()) - 1; } auto argv() -> const char** { return &vec[0]; } }; auto main(int argc, const char **argv) -> int { if(!setlocale(LC_ALL, "")){ std::cerr << "Couldn't set locale based on user preferences!" << std::endl; return EXIT_FAILURE; } const char* lang = getenv("LANG"); if(lang == nullptr){ std::cerr << "Warning: LANG wasn't defined" << std::endl; }else{ std::cout << "Running with LANG=" << lang << std::endl; } const char* term = getenv("TERM"); // ubuntu's buildd sets TERM=unknown, fuck it, handle this atrocity if(term == nullptr || strcmp(term, "unknown") == 0){ std::cerr << "TERM wasn't defined, exiting with success" << std::endl; return EXIT_SUCCESS; } std::cout << "Running with TERM=" << term << std::endl; doctest::Context context; context.setOption("order-by", "name"); // sort the test cases by their name context.applyCommandLine(argc, argv); context.setOption("no-breaks", true); // don't break in the debugger when assertions fail dt_removed args(argv); handle_opts(argv); if(check_data_dir()){ return EXIT_FAILURE; } int res = context.run(); // run if(context.shouldExit()){ // important - query flags (and --exit) rely on the user doing this return res; // propagate the result of the tests } // if we exited via REQUIRE(), we likely left the terminal in an invalid // state. go ahead and reset it manually. if(res){ reset_terminal(); } return res; // the result from doctest is propagated here as well }