{"id":13695198,"url":"https://github.com/mortie/snow","last_synced_at":"2025-12-16T08:10:52.523Z","repository":{"id":37285730,"uuid":"118515819","full_name":"mortie/snow","owner":"mortie","description":"A testing library for C.","archived":false,"fork":false,"pushed_at":"2023-12-14T20:35:33.000Z","size":3500,"stargazers_count":350,"open_issues_count":7,"forks_count":21,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-08-03T18:19:23.717Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/mortie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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}},"created_at":"2018-01-22T21:10:53.000Z","updated_at":"2024-06-15T20:42:53.000Z","dependencies_parsed_at":"2024-04-19T02:32:02.416Z","dependency_job_id":null,"html_url":"https://github.com/mortie/snow","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mortie%2Fsnow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mortie%2Fsnow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mortie%2Fsnow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mortie%2Fsnow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mortie","download_url":"https://codeload.github.com/mortie/snow/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224364252,"owners_count":17299038,"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":[],"created_at":"2024-08-02T18:00:19.309Z","updated_at":"2025-12-16T08:10:47.475Z","avatar_url":"https://github.com/mortie.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":["Libraries"],"readme":"[![Build Status](https://travis-ci.org/mortie/snow.svg?branch=master)](https://travis-ci.org/mortie/snow)\n\n# Snow\n\nSnow is a header-only unit testing library for C. Just include the file\n[snow/snow.h](https://github.com/mortie/snow/blob/v2.3.0/snow/snow.h).\n\nIRC channel: [#snow](http://webchat.freenode.net?channels=snow) on Freenode.\nIf you have any questions, or just want to chat, just ping me (@mort) :)\n\n![Screenshot](https://raw.githubusercontent.com/mortie/snow/master/img/screenshot.png)\n\n## Snow 2\n\nSnow 2 is a complete rewrite of Snow. Here are the highlights:\n\n* Blocks have moved from inside of macro arguments (i.e `describe(foo, { ... })`)\n  to outside of macro arguments (i.e `describe(foo) { ... }`). This applies to\n  `describe`, `subdesc`, `it`/`test`, `before_each`, and `after_each`.\n\t* This means that it's possible to show line numbers, that compiler error\n\t  messages are nicer, and syntax highlighters and auto indenters should be\n\t  more happy.\n* `asserteq` and `assertneq` works slightly differently, but most code which\n  worked before should continue to work.\n* All assertion macros have gotten an extra, optional argument, which is an\n  explanation of what the assertion means. For example, you can now write\n  `asserteq(foo, bar, \"Some explanation\")`.\n* You can select what tests to run with glob-style matches, not just filter\n  based on the name of the top-level describe.\n\n## About\n\nSome miscellaneous points:\n\n* Snow uses some GNU extensions, so it might not work with all\n  ISO C compatible compilers. It's confirmed to work with at least GCC and\n  Clang. It should even work on GCC and Clang versions too old to support C11\n  (or even C99), but the convenience `asserteq` and `assertneq` macros require\n  C11.\n* I really recommend running the test executable with\n  [valgrind](http://valgrind.org/). That will help you find memory issues such\n  as memory leaks, out of bounds array reads/writes, etc.\n* Windows is supported through MinGW or cygwin, with the caveat that it assumes\n  your terminal supports UTF-8. CMD.exe and Powershell will print mangled ✓ and ✕\n  characters. (Git Bash and Cygwin's terminal should be fine though)\n\t* Windows also generally doesn't have the `\u003cfnmatch.h\u003e` header.\n\t  Snow defaults to compile without fnmatch under MinGW\n\t  (and instead uses plain strcmp). You can control this with\n\t  `-DSNOW_USE_FNMATCH=1` or `-DSNOW_USE_FNMATCH=0`.\n\t  [Gnulib](https://www.gnu.org/software/gnulib/manual/gnulib.html)\n\t  implements fnmatch, and supports Windows under Cygwin.\n\n## Usage\n\nWhen creating the main function using the `snow_main` macro, your executable\nwill take these arguments. The **--no-** prefixed arguments will disable the\nrelevant function:\n\n* **--version**, **-v**: Show the current version and exit.\n* **--help**, **-h**: Show usage and exit.\n* **--list**, **-l**: List available tests and exit.\n* **--color**, **-c**, or **--no-color**: Enable the use of color.\n  Default: on when output is a TTY, off otherwise.\n* **--quiet**, **-q**, or **--no-quiet**: Suppress most messages, only test faulures\n  and the 'Total: Passed X/Y tests' line will still print.\n  Default: off.\n* **--log \\\u003cfile\\\u003e**: Output to a log file instead of stdout.\n* **--timer**, **-t**, or **--no-timer**: Print the number of miliseconds CPU time\n  spent on each test alongside its success message.\n  Default: on.\n* **--maybes**, **-m**, or **--no-maybes**: Print out messages when beginning a test\n  rather than just when it completed.\n  Default: on when output is a TTY, off otherwise.\n* **--cr**, or **--no-cr**: Print a `\\r` after maybe messages instead of `\\n`. This\n  will override them with successes or failures as they are printed out.\n  Default: on when output is a TTY, off otherwise.\n\n## Example\n\nHere's a simple example which tests a couple of filesystem functions, and has a\nsubdescription for testing fread-related stuff.\n\n* Compile: `gcc -Isnow -DSNOW_ENABLED -g -o test example.c`\n\t* `-Isnow`: add `snow` to our include path, to make `#include \u003csnow/snow.h\u003e`\n\t  work. (That assumes `snow/snow/snow.h` exists, like if you clone this repo.)\n\t* `-DSNOW_ENABLED`: Enable snow (otherwise `describe(...)` would be\n\t  compiled down to nothing).\n\t* `-g`: Add debug symbols for valgrind.\n* Run: `valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --error-exitcode=1 ./test`\n\n``` C\n#include \u003cstdio.h\u003e\n#include \u003csnow/snow.h\u003e\n\ndescribe(files) {\n\tit(\"opens files\") {\n\t\tFILE *f = fopen(\"test\", \"r\");\n\t\tassertneq(f, NULL);\n\t\tdefer(fclose(f));\n\t}\n\n\tit(\"writes to files\") {\n\t\tFILE *f = fopen(\"testfile\", \"w\");\n\t\tassertneq(f, NULL);\n\t\tdefer(remove(\"testfile\"));\n\t\tdefer(fclose(f));\n\n\t\tchar str[] = \"hello there\";\n\t\tasserteq(fwrite(str, 1, sizeof(str), f), sizeof(str));\n\t}\n\n\tsubdesc(fread) {\n\t\tit(\"reads 10 bytes\") {\n\t\t\tFILE *f = fopen(\"/dev/zero\", \"r\");\n\t\t\tassertneq(f, NULL);\n\t\t\tdefer(fclose(f));\n\n\t\t\tchar buf[10];\n\t\t\tasserteq(fread(buf, 1, 10, f), 10);\n\t\t}\n\n\t\tit(\"reads 20 bytes\") {\n\t\t\tFILE *f = fopen(\"/dev/zero\", \"r\");\n\t\t\tassertneq(f, NULL);\n\t\t\tdefer(fclose(f));\n\n\t\t\tchar buf[20];\n\t\t\tasserteq(fread(buf, 1, 20, f), 20);\n\t\t}\n\t}\n}\n\nsnow_main();\n```\n\n## Compile options\n\n* **SNOW\\_ENABLED**: Define to enable Snow.\n* **SNOW\\_USE\\_FNMATCH**: Set to 0 to not use fnmatch for test name\n  matching, and instead just compare literal strings. (Useful for systems\n  without fnmatch)\n* **SNOW\\_COLOR\\_SUCCESS**: The escape sequence before printing success.\n* **SNOW\\_COLOR\\_FAIL**: The escape sequence before printing failure.\n* **SNOW\\_COLOR\\_MAYBE**: The escape sequence before printing maybes.\n* **SNOW\\_COLOR\\_DESC**: The escape sequence before printing the test\n  description.\n* **SNOW\\_COLOR\\_BOLD**: The escape sequence for bold text.\n* **SNOW\\_COLOR\\_RESET**: The escape sequence to reset formatting.\n* **SNOW\\_DEFAULT\\_ARGS**: A comma seperated list of strings to pass as\n  arguments to snow before the command-line arguments.\n\n## Structure Macros\n\n### describe(testname) \\\u003cblock\u003e\n\nA top-level description of a component, which can contain `subdesc`s and `it`s.\nA `describe(testname, block)` will define a function `void test_##testname()`,\nwhich the main function created by `snow_main` will call automatically.\n\n### subdesc(testname) \\\u003cblock\u003e\n\nA description of a sub-component, which can contain nested `subdesc`s and\n`it`s. It's similar to `describe`, but doesn't define a function.\n\n### it(description) \\\u003cblock\u003e\n\nA particular test case. It can contain asserts and `defer`s, as well as just\nregular code. A failing assert (or direct call to `fail(...)`) will mark the\ntest as failed, but if it completes normally, it's marked as successful.\n\n`test(description) \u003cblock\u003e` is an alias, for cases where using `it` would read\nawkwardly.\n\n### defer(expr)\n\n`defer` is used for tearing down, and is inspired by Go's [defer\nstatement](https://gobyexample.com/defer).\n\nOnce the test case completes, each deferred expression will be executed, in the\nreverse order of their definitions (i.e `defer(printf(\"World\"));\ndefer(printf(\"Hello \"));` will print \"Hello World\"). If the test case fails,\nonly deferred expressions defined before the point of failure will be executed.\n\n### before\\_each() \\\u003cblock\u003e\n\nCode to run before each test case.\n\n### after\\_each() \\\u003cblock\u003e\n\nCode to run after each test case.\n\n### snow\\_main()\n\nThis macro expands to a main function which handless stuff like parsing\narguments and freeing memory allocated by Snow. All described functions will\nautomatically be called by the main functions.\n\nIf you want more control over the main function, you can use the\n`snow_main_decls` macro to create the necessary global variables and functions,\nand then call the `snow_main_function(int argc, char **argv)` function.\n\nThis is essentially how `snow_main()` works:\n\n``` C\nsnow_main_decls;\nint main(int argc, char **argv) {\n\treturn snow_main_function(argc, argv);\n}\n```\n\n## Assert Macros\n\n### fail(fmt, ...)\n\nJust directly fail the test case. The arguments are a printf-style format,\noptionally followed by arguments, just like `printf`.\n\n### assert(x [, explanation])\n\nFail if the expression `x` returns 0. `explanation`  is an optional string\nwhich will be printed if the assertion fails, and can be used to provide some\ncontext.\n\n### asserteq(a, b [, explanation])\n\nFail unless `a` equals `b`. If `b` is a string, `strcmp` will be used to check\nfor equality; otherwise, `==` will be used.\n\n`asserteq` requires C11.\nIf you can't use C11, or want to explicitly state what type your arguments are\n(say you want to compare strings by pointer instead of by content), you\ncan use the `asserteq_int`, `asserteq_ptr`, `asserteq_dbl`, and `asserteq_str`\nmacros instead of `asserteq`.\n\n### assertneq(a, b [, explanation])\n\nFail if `a` equals `b`. If `b` is a string, `strcmp` will be used to check\nfor equality; otherwise, `==` will be used.\n\n`assertneq` requires C11.\nIf you can't use C11, or want to explicitly state what type your arguments are\n(say you want to compare strings by pointer instead of by content), you\ncan use the `assertneq_int`, `assertneq_ptr`, `assertneq_dbl`, and `asserteq_str`\nmacros instead of `assertneq`.\n\n### asserteq\\_buf(a, b, n [, explanation])\n\nFail unless the first `n` bytes of `a` and `b` are the same.\n\n### assertneq\\_buf(a, b, n [, explanation])\n\nFail if the first `n` bytes of `a` and `b` are the same.\n\n### snow\\_fail(fmt, ...), snow\\_fail\\_update()\n\n`snow_fail_update` saves the current file/line, while `snow_fail` fails the\ncurrently executing test case and prints the saved file/line from the last\n`snow_fail_update`. This allows for implementing new checks to fail tests.\nAll assertion functions from Snow are implemented using `snow_fail` and\n`snow_fail_update`.\n\n## How to test\n\nExactly how to test your code might not be as obvious with C as it is for other\nlanguages. I haven't yet used Snow for any big projects, but here's how I would\ndo it\n\n### Testing a library's interface\n\nWhen testing a library's interface, you can just create a `test` folder which\nis completely decoupled from the library's source code, and just compile your\ntest code with a flag to link against your library.\n\n### Testing a program or library internals\n\nTesting anything that's not exposed as a library's public API is is possible\nbecause unless `SNOW_ENABLED` is defined, `describe` will just be an empty\nmacro, and all uses of it will be removed by the preprocessor. Therfore, you\ncan include tests directly in your C source files, and regular builds won't\nuse snow, while builds with `-DSNOW_ENABLED` will be your test suite.\n\nSince all test macros are compiled down to nothing, this will have no runtime\nperformance or binary size impact.\n\nNote that since `snow.h` defines macros with pretty general names (`it`,\n`describe`, `assert`), it's probably a good idea to put your tests at the\nbottom of the source and only include `snow.h` right above the test code, to\navoid name conflicts.\n\nThe [exampleproject](https://github.com/mortie/snow/blob/master/exampleproject)\ndirectory is an example of a program tested this way.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmortie%2Fsnow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmortie%2Fsnow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmortie%2Fsnow/lists"}