diff --git a/SConstruct b/SConstruct index 2a69cd2e38..4538bee69c 100644 --- a/SConstruct +++ b/SConstruct @@ -55,7 +55,7 @@ assert arch in [ "Darwin", # macOS arm64 (x86 not supported) ] -pkg_names = ['acados', 'bzip2', 'capnproto', 'catch2', 'ffmpeg', 'json11', 'ncurses', 'zeromq', 'zstd'] +pkg_names = ['acados', 'bzip2', 'capnproto', 'ffmpeg', 'json11', 'ncurses', 'zeromq', 'zstd'] pkgs = [importlib.import_module(name) for name in pkg_names] acados = pkgs[pkg_names.index('acados')] ffmpeg = pkgs[pkg_names.index('ffmpeg')] diff --git a/msgq_repo b/msgq_repo index a771d031ec..6a8d2e8f06 160000 --- a/msgq_repo +++ b/msgq_repo @@ -1 +1 @@ -Subproject commit a771d031ec58716824f365a89f0607fd786a1161 +Subproject commit 6a8d2e8f06f7864976d148f3cf423e8b3a418e8a diff --git a/openpilot/common/SConscript b/openpilot/common/SConscript index 13935b80d5..600cbec2e5 100644 --- a/openpilot/common/SConscript +++ b/openpilot/common/SConscript @@ -12,9 +12,7 @@ _common = env.Library('common', common_libs, LIBS="json11") Export('_common') if GetOption('extras'): - env.Program('tests/test_common', - ['tests/test_runner.cc', 'tests/test_util.cc', 'tests/test_swaglog.cc'], - LIBS=[_common, 'json11', 'zmq', 'pthread']) + env.Program('tests/test_swaglog', 'tests/test_swaglog.cc', LIBS=[_common, 'json11', 'zmq', 'pthread']) # Cython bindings params_python = envCython.Program('params_pyx.so', 'params_pyx.pyx', LIBS=envCython['LIBS'] + [_common, 'zmq', 'json11']) diff --git a/openpilot/common/tests/native_test.h b/openpilot/common/tests/native_test.h new file mode 100644 index 0000000000..d3dfbb9ce1 --- /dev/null +++ b/openpilot/common/tests/native_test.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +inline void native_test_check(bool condition, const char *expression, const char *file, int line) { + if (!condition) { + throw std::runtime_error(std::string(file) + ":" + std::to_string(line) + ": check failed: " + expression); + } +} + +#define CHECK(condition) native_test_check(static_cast(condition), #condition, __FILE__, __LINE__) +#define REQUIRE(...) CHECK((__VA_ARGS__)) + +template +int run_native_test(Function &&function) { + try { + function(); + return 0; + } catch (const std::exception &error) { + std::cerr << error.what() << '\n'; + return 1; + } +} diff --git a/openpilot/common/tests/test_runner.cc b/openpilot/common/tests/test_runner.cc deleted file mode 100644 index 62bf7476a1..0000000000 --- a/openpilot/common/tests/test_runner.cc +++ /dev/null @@ -1,2 +0,0 @@ -#define CATCH_CONFIG_MAIN -#include "catch2/catch.hpp" diff --git a/openpilot/common/tests/test_swaglog.cc b/openpilot/common/tests/test_swaglog.cc index 0c9cfcc1e6..07e2b16bc1 100644 --- a/openpilot/common/tests/test_swaglog.cc +++ b/openpilot/common/tests/test_swaglog.cc @@ -1,88 +1,47 @@ +#include +#include + #include -#include - -#include "catch2/catch.hpp" -#include "common/swaglog.h" -#include "common/util.h" -#include "common/version.h" #include "common/hardware/hw.h" +#include "common/swaglog.h" +#include "common/tests/native_test.h" #include "json11/json11.hpp" -std::string daemon_name = "testy"; -std::string dongle_id = "test_dongle_id"; -int LINE_NO = 0; +void test_swaglog() { + setenv("MANAGER_DAEMON", "swaglog_test", 1); + setenv("DONGLE_ID", "test_dongle_id", 1); + setenv("CLEAN", "1", 1); -void log_thread(int thread_id, int msg_cnt) { - for (int i = 0; i < msg_cnt; ++i) { - LOGD("%d", thread_id); - LINE_NO = __LINE__ - 1; - usleep(1); - } + void *context = zmq_ctx_new(); + CHECK(context != nullptr); + void *socket = zmq_socket(context, ZMQ_PULL); + CHECK(socket != nullptr); + int timeout = 5000; + CHECK(zmq_setsockopt(socket, ZMQ_RCVTIMEO, &timeout, sizeof(timeout)) == 0); + CHECK(zmq_bind(socket, Path::swaglog_ipc().c_str()) == 0); + + LOGD("native-cpp-log"); + + char buffer[4096] = {}; + const int size = zmq_recv(socket, buffer, sizeof(buffer), 0); + CHECK(size > 1); + CHECK(buffer[0] == CLOUDLOG_DEBUG); + std::string error; + const auto message = json11::Json::parse(std::string(buffer + 1, size - 1), error); + CHECK(error.empty()); + CHECK(message["levelnum"].int_value() == CLOUDLOG_DEBUG); + CHECK(message["msg"].string_value() == "native-cpp-log"); + CHECK(message["funcname"].string_value() == "test_swaglog"); + CHECK(message["filename"].string_value().find("test_swaglog.cc") != std::string::npos); + CHECK(message["ctx"]["daemon"].string_value() == "swaglog_test"); + CHECK(message["ctx"]["dongle_id"].string_value() == "test_dongle_id"); + CHECK(message["ctx"]["dirty"].bool_value() == false); + + CHECK(zmq_close(socket) == 0); + CHECK(zmq_ctx_destroy(context) == 0); } -void recv_log(int thread_cnt, int thread_msg_cnt) { - void *zctx = zmq_ctx_new(); - void *sock = zmq_socket(zctx, ZMQ_PULL); - zmq_bind(sock, Path::swaglog_ipc().c_str()); - std::vector thread_msgs(thread_cnt); - int total_count = 0; - - for (auto start = std::chrono::steady_clock::now(), now = start; - now < start + std::chrono::seconds{1} && total_count < (thread_cnt * thread_msg_cnt); - now = std::chrono::steady_clock::now()) { - char buf[4096] = {}; - if (zmq_recv(sock, buf, sizeof(buf), ZMQ_DONTWAIT) <= 0) { - if (errno == EAGAIN || errno == EINTR || errno == EFSM) continue; - break; - } - - REQUIRE(buf[0] == CLOUDLOG_DEBUG); - std::string err; - auto msg = json11::Json::parse(buf + 1, err); - REQUIRE(!msg.is_null()); - - REQUIRE(msg["levelnum"].int_value() == CLOUDLOG_DEBUG); - REQUIRE_THAT(msg["filename"].string_value(), Catch::Contains("test_swaglog.cc")); - REQUIRE(msg["funcname"].string_value() == "log_thread"); - REQUIRE(msg["lineno"].int_value() == LINE_NO); - - auto ctx = msg["ctx"]; - - REQUIRE(ctx["daemon"].string_value() == daemon_name); - REQUIRE(ctx["dongle_id"].string_value() == dongle_id); - REQUIRE(ctx["dirty"].bool_value() == true); - - REQUIRE(ctx["version"].string_value() == COMMA_VERSION); - - std::string device = Hardware::get_name(); - REQUIRE(ctx["device"].string_value() == device); - - int thread_id = atoi(msg["msg"].string_value().c_str()); - REQUIRE((thread_id >= 0 && thread_id < thread_cnt)); - thread_msgs[thread_id]++; - total_count++; - } - for (int i = 0; i < thread_cnt; ++i) { - INFO("thread :" << i); - REQUIRE(thread_msgs[i] == thread_msg_cnt); - } - zmq_close(sock); - zmq_ctx_destroy(zctx); -} - -TEST_CASE("swaglog") { - setenv("MANAGER_DAEMON", daemon_name.c_str(), 1); - setenv("DONGLE_ID", dongle_id.c_str(), 1); - setenv("dirty", "1", 1); - const int thread_cnt = 5; - const int thread_msg_cnt = 100; - - std::vector log_threads; - for (int i = 0; i < thread_cnt; ++i) { - log_threads.push_back(std::thread(log_thread, i, thread_msg_cnt)); - } - for (auto &t : log_threads) t.join(); - - recv_log(thread_cnt, thread_msg_cnt); +int main() { + return run_native_test(test_swaglog); } diff --git a/openpilot/common/tests/test_util.cc b/openpilot/common/tests/test_util.cc deleted file mode 100644 index d927b98a4d..0000000000 --- a/openpilot/common/tests/test_util.cc +++ /dev/null @@ -1,147 +0,0 @@ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "catch2/catch.hpp" -#include "common/util.h" - -std::string random_bytes(int size) { - std::random_device rd; - std::independent_bits_engine rbe(rd()); - std::string bytes(size + 1, '\0'); - std::generate(bytes.begin(), bytes.end(), std::ref(rbe)); - return bytes; -} - -TEST_CASE("util::read_file") { - SECTION("read /proc/version") { - std::string ret = util::read_file("/proc/version"); - REQUIRE(ret.find("Linux version") != std::string::npos); - } - SECTION("read from sysfs") { - std::string ret = util::read_file("/sys/power/wakeup_count"); - REQUIRE(!ret.empty()); - } - SECTION("read file") { - char filename[] = "/tmp/test_read_XXXXXX"; - int fd = mkstemp(filename); - - REQUIRE(util::read_file(filename).empty()); - - std::string content = random_bytes(64 * 1024); - REQUIRE(write(fd, content.c_str(), content.size()) == (ssize_t)content.size()); - std::string ret = util::read_file(filename); - bool equal = (ret == content); - REQUIRE(equal); - close(fd); - } - SECTION("read directory") { - REQUIRE(util::read_file(".").empty()); - } - SECTION("read non-existent file") { - std::string ret = util::read_file("does_not_exist"); - REQUIRE(ret.empty()); - } - SECTION("read non-permission") { - REQUIRE(util::read_file("/proc/kmsg").empty()); - } -} - -TEST_CASE("util::file_exists") { - char filename[] = "/tmp/test_file_exists_XXXXXX"; - int fd = mkstemp(filename); - REQUIRE(fd != -1); - close(fd); - - SECTION("existent file") { - REQUIRE(util::file_exists(filename)); - REQUIRE(util::file_exists("/tmp")); - } - SECTION("nonexistent file") { - std::string fn = filename; - REQUIRE(!util::file_exists(fn + "/nonexistent")); - } - SECTION("file has no access permissions") { - std::string fn = "/proc/kmsg"; - std::ifstream f(fn); - REQUIRE(f.good() == false); - REQUIRE(util::file_exists(fn)); - } - ::remove(filename); -} - -TEST_CASE("util::read_files_in_dir") { - char tmp_path[] = "/tmp/test_XXXXXX"; - const std::string test_path = mkdtemp(tmp_path); - const std::string files[] = {".test1", "'test2'", "test3"}; - for (auto fn : files) { - std::ofstream{test_path + "/" + fn} << fn; - } - mkdir((test_path + "/dir").c_str(), 0777); - - std::map result = util::read_files_in_dir(test_path); - REQUIRE(result.find("dir") == result.end()); - REQUIRE(result.size() == std::size(files)); - for (auto& [k, v] : result) { - REQUIRE(k == v); - } -} - - -TEST_CASE("util::safe_fwrite") { - char filename[] = "/tmp/XXXXXX"; - int fd = mkstemp(filename); - close(fd); - std::string dat = random_bytes(1024 * 1024); - - FILE *f = util::safe_fopen(filename, "wb"); - REQUIRE(f != nullptr); - size_t size = util::safe_fwrite(dat.data(), 1, dat.size(), f); - REQUIRE(size == dat.size()); - int ret = util::safe_fflush(f); - REQUIRE(ret == 0); - ret = fclose(f); - REQUIRE(ret == 0); - bool equal = (dat == util::read_file(filename)); - REQUIRE(equal); -} - -TEST_CASE("util::create_directories") { - REQUIRE(system("rm /tmp/test_create_directories -rf") == 0); - std::string dir = "/tmp/test_create_directories/a/b/c/d/e/f"; - - auto check_dir_permissions = [](const std::string &path, mode_t mode) -> bool { - struct stat st = {}; - return stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR && (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == mode; - }; - - SECTION("create_directories") { - REQUIRE(util::create_directories(dir, 0755)); - REQUIRE(check_dir_permissions(dir, 0755)); - } - SECTION("dir already exists") { - REQUIRE(util::create_directories(dir, 0755)); - REQUIRE(util::create_directories(dir, 0755)); - } - SECTION("a file exists with the same name") { - REQUIRE(util::create_directories(dir, 0755)); - int f = open((dir + "/file").c_str(), O_RDWR | O_CREAT, 0644); - REQUIRE(f != -1); - close(f); - REQUIRE(util::create_directories(dir + "/file", 0755) == false); - REQUIRE(util::create_directories(dir + "/file/1/2/3", 0755) == false); - } - SECTION("end with slashes") { - REQUIRE(util::create_directories(dir + "/", 0755)); - } - SECTION("empty") { - REQUIRE(util::create_directories("", 0755) == false); - } -} diff --git a/openpilot/selfdrive/pandad/tests/test_pandad_canprotocol.cc b/openpilot/selfdrive/pandad/tests/test_pandad_canprotocol.cc index 83339a4c1c..50bbc51f49 100644 --- a/openpilot/selfdrive/pandad/tests/test_pandad_canprotocol.cc +++ b/openpilot/selfdrive/pandad/tests/test_pandad_canprotocol.cc @@ -1,11 +1,7 @@ -#define CATCH_CONFIG_MAIN -#define CATCH_CONFIG_ENABLE_BENCHMARKING - #include -#include "catch2/catch.hpp" +#include "common/tests/native_test.h" #include "openpilot/cereal/messaging/messaging.h" -#include "common/util.h" #include "selfdrive/pandad/panda.h" struct PandaTest : public Panda { @@ -26,12 +22,9 @@ PandaTest::PandaTest(int can_list_size_, cereal::PandaState::PandaType hw_type_) int data_limit = ((hw_type == cereal::PandaState::PandaType::RED_PANDA) ? std::size(dlc_to_len) : 8); // prepare test data for (int i = 0; i < data_limit; ++i) { - std::random_device rd; - std::independent_bits_engine rbe(rd()); - int data_len = dlc_to_len[i]; std::string bytes(data_len, '\0'); - std::generate(bytes.begin(), bytes.end(), std::ref(rbe)); + for (int j = 0; j < data_len; ++j) bytes[j] = static_cast((i * 31 + j) & 0xff); test_data[data_len] = bytes; } @@ -39,16 +32,15 @@ PandaTest::PandaTest(int can_list_size_, cereal::PandaState::PandaType hw_type_) auto can_list = msg.initEvent().initSendcan(can_list_size); for (uint8_t i = 0; i < can_list_size; ++i) { auto can = can_list[i]; - uint32_t id = util::random_int(0, std::size(dlc_to_len) - 1); + uint32_t id = i % data_limit; const std::string &dat = test_data[dlc_to_len[id]]; can.setAddress(i); - can.setSrc(util::random_int(0, 2)); + can.setSrc(i % 3); can.setDat(kj::ArrayPtr((uint8_t *)dat.data(), dat.size())); total_pakets_size += sizeof(can_header) + dat.size(); } can_data_list = can_list.asReader(); - INFO("test " << can_list_size << " packets, total size " << total_pakets_size); } void PandaTest::test_can_send() { @@ -56,30 +48,29 @@ void PandaTest::test_can_send() { this->pack_can_buffer(can_data_list, [&](uint8_t *chunk, size_t size) { unpacked_data.insert(unpacked_data.end(), chunk, &chunk[size]); }); - REQUIRE(unpacked_data.size() == total_pakets_size); + CHECK(unpacked_data.size() == total_pakets_size); int cnt = 0; - INFO("test can message integrity"); for (int pos = 0, pckt_len = 0; pos < unpacked_data.size(); pos += pckt_len) { can_header header; memcpy(&header, &unpacked_data[pos], sizeof(can_header)); const uint8_t data_len = dlc_to_len[header.data_len_code]; pckt_len = sizeof(can_header) + data_len; - REQUIRE(header.addr == cnt); - REQUIRE(test_data.find(data_len) != test_data.end()); + CHECK(header.addr == cnt); + CHECK(test_data.find(data_len) != test_data.end()); const std::string &dat = test_data[data_len]; - REQUIRE(memcmp(dat.data(), &unpacked_data[pos + sizeof(can_header)], dat.size()) == 0); + CHECK(memcmp(dat.data(), &unpacked_data[pos + sizeof(can_header)], dat.size()) == 0); ++cnt; } - REQUIRE(cnt == can_list_size); + CHECK(cnt == can_list_size); } void PandaTest::test_can_recv(uint32_t rx_chunk_size) { std::vector frames; this->pack_can_buffer(can_data_list, [&](uint8_t *data, uint32_t size) { if (rx_chunk_size == 0) { - REQUIRE(this->unpack_can_buffer(data, size, frames)); + CHECK(this->unpack_can_buffer(data, size, frames)); } else { this->receive_buffer_size = 0; uint32_t pos = 0; @@ -90,46 +81,35 @@ void PandaTest::test_can_recv(uint32_t rx_chunk_size) { this->receive_buffer_size += chunk_size; pos += chunk_size; - REQUIRE(this->unpack_can_buffer(this->receive_buffer, this->receive_buffer_size, frames)); + CHECK(this->unpack_can_buffer(this->receive_buffer, this->receive_buffer_size, frames)); } } }); - REQUIRE(frames.size() == can_list_size); + CHECK(frames.size() == can_list_size); for (int i = 0; i < frames.size(); ++i) { - REQUIRE(frames[i].address == i); - REQUIRE(test_data.find(frames[i].dat.size()) != test_data.end()); + CHECK(frames[i].address == i); + CHECK(test_data.find(frames[i].dat.size()) != test_data.end()); const std::string &dat = test_data[frames[i].dat.size()]; - REQUIRE(memcmp(dat.data(), frames[i].dat.data(), dat.size()) == 0); + CHECK(memcmp(dat.data(), frames[i].dat.data(), dat.size()) == 0); } } -TEST_CASE("send/recv CAN 2.0 packets") { - auto can_list_size = GENERATE(1, 3, 5, 10, 30, 60, 100, 200); - PandaTest test(can_list_size, cereal::PandaState::PandaType::DOS); +void test_can_protocol() { + for (auto hw_type : {cereal::PandaState::PandaType::DOS, cereal::PandaState::PandaType::RED_PANDA}) { + for (int can_list_size : {1, 3, 5, 10, 30, 60, 100, 200}) { + PandaTest send_test(can_list_size, hw_type); + send_test.test_can_send(); - SECTION("can_send") { - test.test_can_send(); - } - SECTION("can_receive") { - test.test_can_recv(); - } - SECTION("chunked_can_receive") { - test.test_can_recv(0x40); + PandaTest receive_test(can_list_size, hw_type); + receive_test.test_can_recv(); + + PandaTest chunked_receive_test(can_list_size, hw_type); + chunked_receive_test.test_can_recv(0x40); + } } } -TEST_CASE("send/recv CAN FD packets") { - auto can_list_size = GENERATE(1, 3, 5, 10, 30, 60, 100, 200); - PandaTest test(can_list_size, cereal::PandaState::PandaType::RED_PANDA); - - SECTION("can_send") { - test.test_can_send(); - } - SECTION("can_receive") { - test.test_can_recv(); - } - SECTION("chunked_can_receive") { - test.test_can_recv(0x40); - } +int main() { + return run_native_test(test_can_protocol); } diff --git a/openpilot/system/camerad/SConscript b/openpilot/system/camerad/SConscript index c28330b32c..e6bc3f2bfb 100644 --- a/openpilot/system/camerad/SConscript +++ b/openpilot/system/camerad/SConscript @@ -6,6 +6,3 @@ if arch != "Darwin": camera_obj = env.Object(['cameras/camera_qcom2.cc', 'cameras/camera_common.cc', 'cameras/spectra.cc', 'cameras/cdm.cc', 'sensors/ox03c10.cc', 'sensors/os04c10.cc']) env.Program('camerad', ['main.cc', camera_obj], LIBS=libs) - -if GetOption("extras") and arch == "x86_64": - env.Program('test/test_ae_gray', ['test/test_ae_gray.cc', camera_obj], LIBS=libs) diff --git a/openpilot/system/camerad/test/test_ae_gray.cc b/openpilot/system/camerad/test/test_ae_gray.cc deleted file mode 100644 index 39c3d9c4e5..0000000000 --- a/openpilot/system/camerad/test/test_ae_gray.cc +++ /dev/null @@ -1,84 +0,0 @@ -#define CATCH_CONFIG_MAIN -#include "catch2/catch.hpp" - -#include - -#include -#include - -#include "common/util.h" -#include "system/camerad/cameras/camera_common.h" - -#define W 240 -#define H 160 - - -#define TONE_SPLITS 3 - -float gts[TONE_SPLITS * TONE_SPLITS * TONE_SPLITS * TONE_SPLITS] = { - 0.917969, 0.917969, 0.375000, 0.917969, 0.375000, 0.375000, 0.187500, 0.187500, 0.187500, 0.917969, - 0.375000, 0.375000, 0.187500, 0.187500, 0.187500, 0.187500, 0.187500, 0.187500, 0.093750, 0.093750, - 0.093750, 0.093750, 0.093750, 0.093750, 0.093750, 0.093750, 0.093750, 0.917969, 0.375000, 0.375000, - 0.187500, 0.187500, 0.187500, 0.187500, 0.187500, 0.187500, 0.093750, 0.093750, 0.093750, 0.093750, - 0.093750, 0.093750, 0.093750, 0.093750, 0.093750, 0.093750, 0.093750, 0.093750, 0.093750, 0.093750, - 0.093750, 0.093750, 0.093750, 0.093750, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, - 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, - 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, - 0.000000}; - - -TEST_CASE("camera.test_calculate_exposure_value") { - // set up fake camerabuf - CameraBuf cb = {}; - VisionBuf vb = {}; - uint8_t * fb_y = new uint8_t[W*H]; - vb.y = fb_y; - cb.cur_yuv_buf = &vb; - cb.out_img_width = W; - cb.out_img_height = H; - Rect rect = {0, 0, W-1, H-1}; - - printf("AE test patterns %dx%d\n", cb.out_img_width, cb.out_img_height); - - // mix of 5 tones - uint8_t l[5] = {0, 24, 48, 96, 235}; // 235 is yuv max - - bool passed = true; - float rtol = 0.05; - // generate pattern and calculate EV - int cnt = 0; - for (int i_0=0; i_0 rtol*evgt) { - passed = false; - } - - // report - printf("%d/%d/%d/%d/%d: ev %f, gt %f, err %f\n", h_0, h_1, h_2, h_3, h_4, ev, evgt, fabs(ev - evgt) / (evgt != 0 ? evgt : 0.00001f)); - cnt++; - } - } - } - } - assert(passed); - - delete[] fb_y; -} diff --git a/openpilot/system/loggerd/SConscript b/openpilot/system/loggerd/SConscript index 7f6d4faf0c..0222803f1d 100644 --- a/openpilot/system/loggerd/SConscript +++ b/openpilot/system/loggerd/SConscript @@ -17,6 +17,3 @@ libs.insert(0, logger_lib) env.Program('loggerd', ['loggerd.cc'], LIBS=libs, FRAMEWORKS=frameworks) env.Program('encoderd', ['encoderd.cc'], LIBS=libs, FRAMEWORKS=frameworks) env.Program('bootlog.cc', LIBS=libs, FRAMEWORKS=frameworks) - -if GetOption('extras'): - env.Program('tests/test_logger', ['tests/test_runner.cc', 'tests/test_logger.cc', 'tests/test_zstd_writer.cc'], LIBS=libs) diff --git a/openpilot/system/loggerd/tests/test_logger.cc b/openpilot/system/loggerd/tests/test_logger.cc deleted file mode 100644 index 61509c256c..0000000000 --- a/openpilot/system/loggerd/tests/test_logger.cc +++ /dev/null @@ -1,75 +0,0 @@ -#include "catch2/catch.hpp" -#include "system/loggerd/logger.h" - -typedef cereal::Sentinel::SentinelType SentinelType; - -void verify_segment(const std::string &route_path, int segment, int max_segment, int required_event_cnt) { - const std::string segment_path = route_path + "--" + std::to_string(segment); - SentinelType begin_sentinel = segment == 0 ? SentinelType::START_OF_ROUTE : SentinelType::START_OF_SEGMENT; - SentinelType end_sentinel = segment == max_segment - 1 ? SentinelType::END_OF_ROUTE : SentinelType::END_OF_SEGMENT; - - REQUIRE(!util::file_exists(segment_path + "/rlog.lock")); - for (const char *fn : {"/rlog.zst", "/qlog.zst"}) { - const std::string log_file = segment_path + fn; - std::string log = util::read_file(log_file); - REQUIRE(!log.empty()); - std::string decompressed_log = zstd_decompress(log); - int event_cnt = 0, i = 0; - kj::ArrayPtr words((capnp::word *)decompressed_log.data(), decompressed_log.size() / sizeof(capnp::word)); - while (words.size() > 0) { - try { - capnp::FlatArrayMessageReader reader(words); - auto event = reader.getRoot(); - words = kj::arrayPtr(reader.getEnd(), words.end()); - if (i == 0) { - REQUIRE(event.which() == cereal::Event::INIT_DATA); - } else if (i == 1) { - REQUIRE(event.which() == cereal::Event::SENTINEL); - REQUIRE(event.getSentinel().getType() == begin_sentinel); - REQUIRE(event.getSentinel().getSignal() == 0); - } else if (words.size() > 0) { - REQUIRE(event.which() == cereal::Event::CLOCKS); - ++event_cnt; - } else { - // the last event must be SENTINEL - REQUIRE(event.which() == cereal::Event::SENTINEL); - REQUIRE(event.getSentinel().getType() == end_sentinel); - REQUIRE(event.getSentinel().getSignal() == (end_sentinel == SentinelType::END_OF_ROUTE ? 1 : 0)); - } - ++i; - } catch (const kj::Exception &ex) { - INFO("failed parse " << i << " exception :" << ex.getDescription()); - REQUIRE(0); - break; - } - } - REQUIRE(event_cnt == required_event_cnt); - } -} - -void write_msg(LoggerState *logger) { - MessageBuilder msg; - msg.initEvent().initClocks(); - logger->write(msg.toBytes(), true); -} - -TEST_CASE("logger") { - const int segment_cnt = 100; - const std::string log_root = "/tmp/test_logger"; - REQUIRE(system(("rm " + log_root + " -rf").c_str()) == 0); - std::string route_name; - { - LoggerState logger(log_root); - route_name = logger.routeName(); - for (int i = 0; i < segment_cnt; ++i) { - REQUIRE(logger.next()); - REQUIRE(util::file_exists(logger.segmentPath() + "/rlog.lock")); - REQUIRE(logger.segment() == i); - write_msg(&logger); - } - logger.setExitSignal(1); - } - for (int i = 0; i < segment_cnt; ++i) { - verify_segment(log_root + "/" + route_name, i, segment_cnt, 1); - } -} diff --git a/openpilot/system/loggerd/tests/test_runner.cc b/openpilot/system/loggerd/tests/test_runner.cc deleted file mode 100644 index 62bf7476a1..0000000000 --- a/openpilot/system/loggerd/tests/test_runner.cc +++ /dev/null @@ -1,2 +0,0 @@ -#define CATCH_CONFIG_MAIN -#include "catch2/catch.hpp" diff --git a/openpilot/system/loggerd/tests/test_zstd_writer.cc b/openpilot/system/loggerd/tests/test_zstd_writer.cc deleted file mode 100644 index 479e866a14..0000000000 --- a/openpilot/system/loggerd/tests/test_zstd_writer.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include - -#include -#include -#include - -#include "common/util.h" -#include "system/loggerd/logger.h" -#include "system/loggerd/zstd_writer.h" - -TEST_CASE("ZstdFileWriter writes and compresses data correctly in loops", "[ZstdFileWriter]") { - const std::string filename = "test_zstd_file.zst"; - const int iterations = 100; - const size_t dataSize = 1024; - - std::string totalTestData; - - // Step 1: Write compressed data to file in a loop - { - ZstdFileWriter writer(filename, LOG_COMPRESSION_LEVEL); - // Write various data sizes including edge cases - std::vector testSizes = {dataSize, 1, 0, dataSize * 2}; // Normal, minimal, empty, large - for (int i = 0; i < iterations; ++i) { - size_t currentSize = testSizes[i % testSizes.size()]; - std::string testData = util::random_string(currentSize); - totalTestData.append(testData); - - writer.write((void *)testData.c_str(), testData.size()); - } - } - - // Step 2: Decompress the file and verify the data - auto compressedContent = util::read_file(filename); - REQUIRE(compressedContent.size() > 0); - REQUIRE(compressedContent.size() < totalTestData.size()); - std::string decompressedData = zstd_decompress(compressedContent); - - // Step 3: Verify that the decompressed data matches the original accumulated data - REQUIRE(decompressedData.size() == totalTestData.size()); - REQUIRE(std::memcmp(decompressedData.data(), totalTestData.c_str(), totalTestData.size()) == 0); - - // Clean up the test file - std::remove(filename.c_str()); -} diff --git a/openpilot/test_native.py b/openpilot/test_native.py index a69771f675..eed549f4e4 100644 --- a/openpilot/test_native.py +++ b/openpilot/test_native.py @@ -7,12 +7,9 @@ from openpilot.common.test import OpenpilotTestCase NATIVE_TESTS = ( - "openpilot/common/tests/test_common", + "openpilot/common/tests/test_swaglog", "openpilot/selfdrive/pandad/tests/test_pandad_canprotocol", - "openpilot/system/loggerd/tests/test_logger", - "openpilot/tools/cabana/tests/test_cabana", "openpilot/tools/cabana/tests/test_dbc_core", - "openpilot/tools/replay/tests/test_replay", ) diff --git a/openpilot/tools/cabana/SConscript b/openpilot/tools/cabana/SConscript index fcaa3b7930..805bc41876 100644 --- a/openpilot/tools/cabana/SConscript +++ b/openpilot/tools/cabana/SConscript @@ -110,14 +110,11 @@ cabana_lib = cabana_env.Library("cabana_lib", cabana_srcs + [bootstrap_icons_src cabana_env.Program('_cabana', ['cabana.cc', cabana_lib, assets], LIBS=cabana_libs, FRAMEWORKS=base_frameworks) if GetOption('extras'): - cabana_env.Program('tests/test_cabana', ['tests/test_runner.cc', 'tests/test_cabana.cc', cabana_lib], LIBS=[cabana_libs]) - # This target deliberately uses the base environment and links no Qt libraries. # It prevents Qt dependencies from creeping back into the DBC core. dbc_core_test_env = env.Clone() dbc_core_test_env['CXXFLAGS'] += [opendbc_path] dbc_core_test_objects = [ - dbc_core_test_env.Object('tests/dbc_core_test_runner', 'tests/test_runner.cc'), dbc_core_test_env.Object('tests/dbc_core_tests', 'tests/test_cabana.cc'), dbc_core_test_env.Object('tests/dbc_core_model', 'dbc/dbc.cc'), dbc_core_test_env.Object('tests/dbc_core_file', 'dbc/dbcfile.cc'), diff --git a/openpilot/tools/cabana/tests/test_cabana.cc b/openpilot/tools/cabana/tests/test_cabana.cc index c66f57d593..53be1b0afa 100644 --- a/openpilot/tools/cabana/tests/test_cabana.cc +++ b/openpilot/tools/cabana/tests/test_cabana.cc @@ -1,20 +1,14 @@ -#undef INFO #include #include -#include "catch2/catch.hpp" +#include "common/tests/native_test.h" #include "tools/cabana/dbc/dbcfile.h" #include "tools/cabana/dbc/dbcmanager.h" -#include "tools/cabana/core/settings.h" - -#ifdef QT_CORE_LIB -#include -#endif const std::string TEST_RLOG_URL = "https://commadataci.blob.core.windows.net/openpilotci/0c94aa1e1296d7c6/2021-05-05--19-48-37/0/rlog.bz2"; -TEST_CASE("DBCFile::generateDBC") { +void test_generate_dbc() { std::string fn = std::string(OPENDBC_FILE_PATH) + "/tesla_can.dbc"; DBCFile dbc_origin(fn); DBCFile dbc_from_generated("", dbc_origin.generateDBC()); @@ -35,7 +29,7 @@ TEST_CASE("DBCFile::generateDBC") { } } -TEST_CASE("DBCFile::generateDBC - comment order") { +void test_comment_order() { // Ensure that message comments are followed by signal comments and in the correct order std::string content = R"(BO_ 160 message_1: 8 EON SG_ signal_1 : 0|12@1+ (1,0) [0|4095] "unit" XXX @@ -52,7 +46,7 @@ CM_ SG_ 162 signal_2 "signal comment"; REQUIRE(dbc.generateDBC() == content); } -TEST_CASE("DBCFile::generateDBC -- preserve original header") { +void test_preserve_original_header() { std::string content = R"(VERSION "1.0" NS_ : @@ -72,7 +66,7 @@ CM_ SG_ 160 signal_1 "signal comment"; REQUIRE(dbc.generateDBC() == content); } -TEST_CASE("DBCFile::generateDBC - escaped quotes") { +void test_escaped_quotes() { std::string content = R"(BO_ 160 message_1: 8 EON SG_ signal_1 : 0|12@1+ (1,0) [0|4095] "unit" XXX @@ -83,7 +77,7 @@ CM_ SG_ 160 signal_1 "signal comment with \"escaped quotes\""; REQUIRE(dbc.generateDBC() == content); } -TEST_CASE("parse_dbc") { +void test_parse_dbc() { std::string content = R"( BO_ 160 message_1: 8 EON SG_ signal_1 : 0|12@1+ (1,0) [0|4095] "unit" XXX @@ -149,7 +143,7 @@ CM_ SG_ 162 signal_1 "signal comment with \"escaped quotes\""; REQUIRE(msg->sigs[0]->comment == "signal comment with \"escaped quotes\""); } -TEST_CASE("parse_opendbc") { +void test_parse_opendbc() { std::vector errors; for (const auto &entry : std::filesystem::directory_iterator(OPENDBC_FILE_PATH)) { if (!entry.is_regular_file() || entry.path().extension() != ".dbc") continue; @@ -161,11 +155,11 @@ TEST_CASE("parse_opendbc") { } std::ostringstream details; for (const auto &error : errors) details << error << '\n'; - INFO(details.str()); + if (!errors.empty()) std::cerr << details.str(); REQUIRE(errors.empty()); } -TEST_CASE("DBCManager core callbacks") { +void test_dbc_manager() { DBCManager manager; int files_changed = 0; int signals_added = 0; @@ -192,34 +186,16 @@ TEST_CASE("DBCManager core callbacks") { REQUIRE(manager.msg({.source = 0, .address = 160})->sig("speed") != nullptr); } -TEST_CASE("Cabana settings core defaults") { - CabanaSettingsState state; - REQUIRE(state.fps == 10); - REQUIRE(state.chart_range == 180); - REQUIRE(state.drag_direction == CabanaSettingsState::MsbFirst); - REQUIRE(state.recent_files.empty()); +void test_cabana_core() { + test_generate_dbc(); + test_comment_order(); + test_preserve_original_header(); + test_escaped_quotes(); + test_parse_dbc(); + test_parse_opendbc(); + test_dbc_manager(); } -#ifdef QT_CORE_LIB -TEST_CASE("CabanaColor preserves QColor transformations") { - const std::vector colors = { - QColor(102, 86, 169, 64), QColor(0, 187, 255, 128), QColor(255, 0, 0, 128), QColor(45, 120, 75, 255), - }; - for (const auto &qt_color : colors) { - CabanaColor color(qt_color.red(), qt_color.green(), qt_color.blue(), qt_color.alpha()); - for (int factor : {75, 100, 135, 150, 200}) { - const auto lighter = color.lighter(factor); - const auto qt_lighter = qt_color.lighter(factor); - CHECK(std::abs(lighter.red() - qt_lighter.red()) <= 1); - CHECK(std::abs(lighter.green() - qt_lighter.green()) <= 1); - CHECK(std::abs(lighter.blue() - qt_lighter.blue()) <= 1); - - const auto darker = color.darker(factor); - const auto qt_darker = qt_color.darker(factor); - CHECK(std::abs(darker.red() - qt_darker.red()) <= 1); - CHECK(std::abs(darker.green() - qt_darker.green()) <= 1); - CHECK(std::abs(darker.blue() - qt_darker.blue()) <= 1); - } - } +int main() { + return run_native_test(test_cabana_core); } -#endif diff --git a/openpilot/tools/cabana/tests/test_runner.cc b/openpilot/tools/cabana/tests/test_runner.cc deleted file mode 100644 index a76c8e16b9..0000000000 --- a/openpilot/tools/cabana/tests/test_runner.cc +++ /dev/null @@ -1,7 +0,0 @@ -#define CATCH_CONFIG_RUNNER -#include "catch2/catch.hpp" - -int main(int argc, char **argv) { - const int res = Catch::Session().run(argc, argv); - return (res < 0xff ? res : 0xff); -} diff --git a/openpilot/tools/replay/SConscript b/openpilot/tools/replay/SConscript index 643de97bb9..c5abae502c 100644 --- a/openpilot/tools/replay/SConscript +++ b/openpilot/tools/replay/SConscript @@ -14,6 +14,3 @@ replay_lib = replay_env.Library("replay", replay_lib_src, LIBS=base_libs, FRAMEW Export('replay_lib') replay_libs = [replay_lib] + ffmpeg_libs + ['bz2', 'zstd', 'ncurses'] + base_libs replay_env.Program("replay", ["main.cc"], LIBS=replay_libs, FRAMEWORKS=base_frameworks) - -if GetOption('extras'): - replay_env.Program('tests/test_replay', ['tests/test_replay.cc'], LIBS=replay_libs) diff --git a/openpilot/tools/replay/tests/test_replay.cc b/openpilot/tools/replay/tests/test_replay.cc deleted file mode 100644 index 45fcc98191..0000000000 --- a/openpilot/tools/replay/tests/test_replay.cc +++ /dev/null @@ -1,18 +0,0 @@ -#define CATCH_CONFIG_MAIN -#include "catch2/catch.hpp" -#include "tools/replay/filereader.h" -#include "tools/replay/replay.h" - -const std::string TEST_RLOG_URL = "https://commadataci.blob.core.windows.net/openpilotci/0c94aa1e1296d7c6/2021-05-05--19-48-37/0/rlog.bz2"; - -TEST_CASE("LogReader") { - SECTION("corrupt log") { - FileReader reader(true); - std::string corrupt_content = reader.read(TEST_RLOG_URL); - corrupt_content.resize(corrupt_content.length() / 2); - corrupt_content = decompressBZ2(corrupt_content); - LogReader log; - REQUIRE(log.load(corrupt_content.data(), corrupt_content.size())); - REQUIRE(log.events.size() > 0); - } -} diff --git a/pyproject.toml b/pyproject.toml index 68c5dd7fe7..678720aa04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,6 @@ dependencies = [ # vendored native dependencies "comma-deps-capnproto", - "comma-deps-catch2", "comma-deps-acados", "comma-deps-ffmpeg", "comma-deps-zstd", diff --git a/uv.lock b/uv.lock index ff155ab3bc..de03b28288 100644 --- a/uv.lock +++ b/uv.lock @@ -142,14 +142,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/a9/f61fe62045c4ea867f51f2d74b4edfabd6c272c62a3281a9f1f33825a725/comma_deps_capnproto-1.0.1.post93-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:f98cdba8f8c7f08a7a0c0a4b7cc0bfcf515e29ad8f1b5cb8eda4e253bef5e6e3", size = 2590769, upload-time = "2026-07-08T19:30:49.728Z" }, ] -[[package]] -name = "comma-deps-catch2" -version = "2.13.10.post93" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ee/b4ef7758d04a024775d49558ca930fec19aa37cd691ad9182b7141f4d4d7/comma_deps_catch2-2.13.10.post93-py3-none-any.whl", hash = "sha256:8f23293251b5db48c08885d8816ca252b3fb11b0c1c26775bae36e8b217e865e", size = 137085, upload-time = "2026-07-08T19:30:53.44Z" }, -] - [[package]] name = "comma-deps-eigen" version = "3.4.0.post93" @@ -621,7 +613,6 @@ source = { editable = "msgq_repo" } [package.metadata] requires-dist = [ - { name = "catch2", marker = "extra == 'dev'", git = "https://github.com/commaai/dependencies.git?subdirectory=catch2&rev=release-catch2" }, { name = "codespell", marker = "extra == 'dev'" }, { name = "cppcheck", marker = "extra == 'dev'" }, { name = "cpplint", marker = "extra == 'dev'" }, @@ -699,10 +690,8 @@ name = "openpilot" version = "0.1.0" source = { editable = "." } dependencies = [ - { name = "cffi" }, { name = "comma-deps-acados" }, { name = "comma-deps-capnproto" }, - { name = "comma-deps-catch2" }, { name = "comma-deps-ffmpeg" }, { name = "comma-deps-gcc-arm-none-eabi" }, { name = "comma-deps-git-lfs" }, @@ -768,13 +757,11 @@ standalone = [ [package.metadata] requires-dist = [ - { name = "cffi" }, { name = "codespell", marker = "extra == 'testing'" }, { name = "comma-deps-acados" }, { name = "comma-deps-bootstrap-icons", marker = "extra == 'tools'" }, { name = "comma-deps-bzip2", marker = "extra == 'tools'" }, { name = "comma-deps-capnproto" }, - { name = "comma-deps-catch2" }, { name = "comma-deps-ffmpeg" }, { name = "comma-deps-gcc-arm-none-eabi" }, { name = "comma-deps-git-lfs" },