{"id":23427669,"url":"https://github.com/sunxfancy/zeroerr","last_synced_at":"2025-07-13T07:39:48.256Z","repository":{"id":163303762,"uuid":"500598361","full_name":"sunxfancy/zeroerr","owner":"sunxfancy","description":"A powerful C++ unit testing/assert/log utils","archived":false,"fork":false,"pushed_at":"2025-04-04T22:03:16.000Z","size":10942,"stargazers_count":32,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-04T23:19:00.237Z","etag":null,"topics":["assert","assertion","benchmarking","c-plus-plus-11","cpp","cpp11","dbg","fuzzing","header-only","logger","logging","test-framework","unit-test","unit-testing","unit-testing-framework"],"latest_commit_sha":null,"homepage":"https://sunxfancy.github.io/zeroerr/en/html/","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sunxfancy.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":"Contributing.en.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-06T21:26:50.000Z","updated_at":"2025-03-13T21:24:55.000Z","dependencies_parsed_at":"2024-01-19T09:24:17.350Z","dependency_job_id":"446ed505-4c68-4e75-9de9-733e33d4dc31","html_url":"https://github.com/sunxfancy/zeroerr","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunxfancy%2Fzeroerr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunxfancy%2Fzeroerr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunxfancy%2Fzeroerr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sunxfancy%2Fzeroerr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sunxfancy","download_url":"https://codeload.github.com/sunxfancy/zeroerr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248621058,"owners_count":21134727,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assert","assertion","benchmarking","c-plus-plus-11","cpp","cpp11","dbg","fuzzing","header-only","logger","logging","test-framework","unit-test","unit-testing","unit-testing-framework"],"created_at":"2024-12-23T06:35:13.792Z","updated_at":"2025-04-12T19:33:33.682Z","avatar_url":"https://github.com/sunxfancy.png","language":"C++","readme":"# ZeroErr\n\n[![Standard](https://img.shields.io/badge/C%2B%2B%2FCUDA-11%2F14%2F17%2F20-blue)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![download](https://img.shields.io/badge/-Download-brightgreen)](https://raw.githubusercontent.com/sunxfancy/zeroerr/master/zeroerr.hpp) [![Chinese-Readme](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-Readme-blue)](./Readme.zh.md)\n[![TryItOnline](https://img.shields.io/badge/TryItOnline-purple)](https://replit.com/@sunxfancy/ZeroErr-Demo#main.cpp)\n\n\nHope you get 0 errors and 0 warnings everyday!\n\n![](./docs/fig/zeroerr.jpg)\n\n\nZeroErr is a smart assertion library, a lightweight unit testing framework and a structure logging framework. It integrates those features and provided an unite and clear interface for separated usage or combined usage. \n\n[English Documentation](https://sunxfancy.github.io/zeroerr/en/html/) | [项目文档](https://sunxfancy.github.io/zeroerr/zh/html/)\n\nNote: The project is currently in the experimental stage, and the API may change significantly. It is not recommended to use it in a production environment.\n\n\n## Why we need another unit testing framework\n\nThe current popular unit testing frameworks, e.g. Catch2, doctest, Boost.Test and cpputest are mature and well-established which covers common cases during development. The logger libraries like glog and spdlog are also easy to use. However, there are still some issues:\n\n### 1. Generic Printing\n\nMost unit testing frameworks and logger libraries can not provide a generic printing for user customized types. Especially, when using containers, structures and pointers (including smart pointers), user have to manually write code to generate the log message or print those information during unit testing failed cases. \n\nThis library `zeroerr` gives you an ability to print generically for all types:\n\n```c++\nTEST_CASE(\"Try logging\") {\n    std::vector\u003cint\u003e data = {1, 2, 3};\n    LOG_IF(1 == 1, \"data = {data}\", data);\n}\n```\n\nSimilar to other C++ unit testing frameworks, `zeroerr` will convert this piece of code into a function and register it to automatically run once you link the main function and the library. Here, we can log the data in `vector` template directly without writing any code. \n\n![case1](docs/fig/case1.png)\n\nFor the custom struct type with override `std::ostream\u0026 operator\u003c\u003c(std::ostream\u0026, Type)` stream output, you can use it not only for this type but also all contains using this type, including multiple recursive contains:\n\n```c++\nstruct Node {\n    std::string name;\n    int id;\n};\n\nstd::ostream\u0026 operator\u003c\u003c(std::ostream\u0026 out, Node n) {\n    out \u003c\u003c n.id \u003c\u003c '.' \u003c\u003c n.name;\n    return out;\n}\n\nTEST_CASE(\"Try logging with custom type\") {\n    std::map\u003cstd::string, std::vector\u003cNode\u003e\u003e data = {\n        {\"user1\", {{\"a\",1}, {\"b\",2}}}, {\"user2\", {{\"c\",3}, {\"d\",4}}}\n    };\n    LOG(\"data = {data}\", data);\n}\n```\n\n![case2](docs/fig/case2.png)\n\nOf cause, in many cases, some third-party libraries may not use `\u003c\u003c` operators. For those cases, we can write own rules to create a generic way for printing. For example, LLVM `llvm::Function*` type can not be streamed into std::ostream, we can write code to handle it. However, it will be more simple if we can write a rule for all the sub-classes of `llvm::Value` and `llvm::Type` since we can call the `print` method to print the output. Here we use a `dbg` marco defined in `zeroerr` to quickly print any type. This is very similar to the `dbg` marco in rust.\n\n\n```c++\nnamespace zeroerr { // must defined in namespace zeroerr\n\ntemplate \u003ctypename T\u003e\ntypename std::enable_if\u003c\n    std::is_base_of\u003cllvm::Value, T\u003e::value || std::is_base_of\u003cllvm::Type, T\u003e::value, void\u003e::type\nPrinterExt(Printer\u0026 P, T* s, unsigned level, const char* lb, rank\u003c2\u003e) {\n    if (s == nullptr) {\n        P.os \u003c\u003c P.tab(level) \u003c\u003c \"nullptr\" \u003c\u003c lb;\n    } else {\n        llvm::raw_os_ostream os(P.os);\n        s-\u003eprint(os);\n    }\n}\n}\n\nTEST_CASE(\"customize printing of LLVM pointers\") {\n    llvm::LLVMContext        context;\n    std::vector\u003cllvm::Type*\u003e args   = {llvm::Type::getInt32Ty(context)};\n    llvm::Module*            module = new llvm::Module(\"test_module\", context);\n\n    auto* f =\n        llvm::Function::Create(llvm::FunctionType::get(llvm::Type::getVoidTy(context), args, false),\n                               llvm::GlobalValue::ExternalLinkage, \"test\", module);\n    dbg(dbg(f)-\u003egetType());\n}\n```\n\nThis functin `PrintExt` will match all the class who's base class is `Value` and `Type`. Then, it will create a stream ``llvm::raw_os_ostream` for output.\n\n![case3-llvm](./docs/fig/case3.png)\n\n### 2. Combined usage of assert, log and unit testing\n\nIf you use one logging framework, an unit testing framework and an assertion library, it's not a easy work to combine them together. There is a lot of benefits to use assertion, logging and unit testing together. In `zeroerr`, if an assertion is failed, the logger will receive an event and stored the event in your log file. If you are using an assertion in unit testing, the assertion failure, logged fatal events can be recorded and reported.\n\n```c++\nint fib(int n) {\n    REQUIRE(n \u003e= 0, \"n must be non-negative\");\n    REQUIRE(n \u003c 20, \"n must be less than 20\");\n    if (n \u003c= 2) {\n        return 1;\n    }\n    return fib(n - 1) + fib(n - 2);\n}\n\nTEST_CASE(\"fib function test\") {\n    CHECK(fib(0) == 0);\n    CHECK(fib(1) == 1);\n    CHECK(fib(2) == 1);\n    CHECK(fib(3) == 2);\n    CHECK(fib(4) == 3);\n    CHECK(fib(5) == 5);\n    CHECK(fib(20) == 6765);\n}\n```\n\n![joint1](docs/fig/joint1.png)\n\nFor the logging system, the unit testing can access the log data to ensure that the function has executed the expected logic and results.\n\n```c++\n118 static void function() {\n119    int k = system_call();\n120    LOG_IF(k != 0, \"System call failed, error code = {k}\", k);\n121 }\n...\n\nTEST_CASE(\"access log in Test case\") {\n    zeroerr::suspendLog();\n    function();\n    CHECK(LOG_GET(function, 120, k, int) == ERROR_CODE);\n    zeroerr::resumeLog();\n}\n```\n\nIn order to access the log, we need to pause the log system first, to avoid the data being output to the file, then call the function, access the data in the log through the `LOG_GET` macro, and finally resume the log system. (Currently experimental, only the first call of each log point can be accessed)\n\n\nFurther more, the unit testing can check the logged result if it matches the previous running result (a golden file) to avoid writing any code in the test case.\n\n```c++\nTEST_CASE(\"match ostream\") {\n    // match output can be done in the following workflow\n    // 1. user mark the test case which are comparing output use 'ZEROERR_HAVE_SAME_OUTPUT'\n    // 2. If the output is not exist, the result will be store to the disk.\n    // 3. If the output is exist, compare with it and report error if output is not match.\n    std::cerr \u003c\u003c \"a = 100\" \u003c\u003c std::endl;\n\n    ZEROERR_HAVE_SAME_OUTPUT;\n}\n```\n\nOnce you set `ZEROERR_HAVE_SAME_OUTPUT` marco, the system will check the output stream and save the first run result into a file. Then, the next run will compare the result to see if it the same. (Currently experimental)\n\n\n## 3. Fuzzing Support\n\nMost Unit Testing frameworks do not support fuzzing. However, it's a powerful feature to automatically detect faults in the software and can greatly reduce the work to write test cases.\n\nDifferent than other fuzzing framework, `zeroerr` can also support logging and assertion in the code, so the fuzzing result not only contains corpus but also with the logging and assertion information.\n\nHere is an example of using `zeroerr` to do structured fuzzing:\n\n```c++\nFUZZ_TEST_CASE(\"fuzz_test\") {\n    LOG(\"Run fuzz_test\");\n    FUZZ_FUNC([=](int k, std::string num) {\n        int t = atoi(num.c_str());\n        LOG(\"k: {k}, num:{num}, t: {t}\", k, num, t);\n        REQUIRE(k == t);\n    })\n        .WithDomains(InRange\u003cint\u003e(0, 10), Arbitrary\u003cstd::string\u003e())\n        .WithSeeds({{5, \"Foo\"}, {10, \"Bar\"}})\n        .Run(10);\n}\n```\n\nInspired by [fuzztest](https://github.com/google/fuzztest), Domain is a concept to specify the input data range (or patterns) for the target function. Here, we use `InRange` to specify the range of `k` is 0 to 10, and `Arbitrary` to specify the data of `num` can be any random string. Then, we use `WithSeeds` to specify the initial seeds for the fuzzing. \n\nThe macro `FUZZ_TEST_CASE` will generate a test case which can connect with `libFuzzer` to run the fuzzing. Finally, we use `Run(10)` to call `libFuzzer` to run the target for 10 times.\n\nTo build the test case with fuzzing, you need to use `clang++` to compile the code and with `-fsanitize=fuzzer-no-link` and link the `-lclang_rt.fuzzer_no_main-x86_64` which is a version of libFuzzer without main function. You can find this runtime library by calling `clang++ -print-runtime-dir`. Here is the complete command to build the test case with fuzzing support:\n\n```bash\nclang++ -std=c++11 -fsanitize=fuzzer-no-link -L=`clang++ -print-runtime-dir` -lclang_rt.fuzzer_no_main-x86_64  -o test_fuzz test_fuzz.cpp \n```\n\n\n## CppCon 24 Talk\n\nIf you are interested in the design and internal techiques used in `zeroerr`, please checkout [the talk in CppCon 24](https://youtu.be/otSPZyXqY_M?si=LLSHh8gkfCiQk983). \n\n[![](docs/fig/video-screenshot.png)](https://youtu.be/otSPZyXqY_M?si=LLSHh8gkfCiQk983)\n\n## Header-only libraries\n\n* dbg\n* print (without use extern functions)\n* assert\n* color (if always enabled)\n\n\n## Other Good Features\n\nHere are a list of features we provided:\n\n1. Partially include\nYou can only include what you need. If you need only assertion but no unit testing, you can only include the header `zeroerr/assert.h`. \n\n2. Optional thread safety \nYou can choose to build with/without thread safety.\n\n3. Fastest log\nMultiple level of log writing policies. You can choose to only write to disk with the most important events.\n\n4. Customized print / log / assert printing format\nYou can customize your printing format for everything. There is a templated callback function for the printing.\n\n5. Quickly debug something\nYou can use dbg macro to quickly see the output, it can be applied to any expression.\n\n6. Colorful output\nYou can have default colorful output to terminal and no color for file output\n\n7. Print struct/stl/pointers without any extra code\n\n8. Doctest like assertion and unit test feature\nYou can use your unit test as a documentation of function behavior. The output of unittest can be a documented report.\n\n9. Lazy logging for assertion\nAfter assertion failed, the logging result will print automatically even if you didn't redirect to your error stream\n\n10. Logging Category \nLogging information can have customized category and only display one category based on your assertion or configuration\n\n11. Logging for Unit Testing\nYou can use a correct logging result as your unit testing golden file. So you just need to manually verify your log once and save it. The unit testing framework will use the golden file to verify your unit testing result.\n\n12. Structured Logging\nWe can support output structured information directly into plain text or lisp format (json, logfmt, or other custom format should be the next step to support)\n\n13. Automatic Tracing with logging\nWhile logging at the end, we can record the time consuming for this function.\n\n\n\n## The logo generation\n\nThanks to the `tiv` tool:\nhttps://github.com/stefanhaustein/TerminalImageViewer","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunxfancy%2Fzeroerr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsunxfancy%2Fzeroerr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsunxfancy%2Fzeroerr/lists"}