{"id":13737245,"url":"https://github.com/disruptek/balls","last_synced_at":"2025-06-14T19:38:27.246Z","repository":{"id":46799310,"uuid":"284849395","full_name":"disruptek/balls","owner":"disruptek","description":"the testing framework with balls 🔴🟡🟢 ","archived":false,"fork":false,"pushed_at":"2024-08-25T13:12:34.000Z","size":775,"stargazers_count":68,"open_issues_count":8,"forks_count":6,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-09T16:18:37.987Z","etag":null,"topics":["nim","testament","testes","tests","testutils","unit","unittests"],"latest_commit_sha":null,"homepage":"","language":"Nim","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/disruptek.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,"publiccode":null,"codemeta":null}},"created_at":"2020-08-04T01:46:18.000Z","updated_at":"2024-09-12T13:20:04.000Z","dependencies_parsed_at":"2023-02-15T23:45:46.497Z","dependency_job_id":"21cc8c08-561c-4c0c-8ed4-5f0ae367cfdd","html_url":"https://github.com/disruptek/balls","commit_stats":null,"previous_names":[],"tags_count":160,"template":false,"template_full_name":null,"purl":"pkg:github/disruptek/balls","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/disruptek%2Fballs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/disruptek%2Fballs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/disruptek%2Fballs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/disruptek%2Fballs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/disruptek","download_url":"https://codeload.github.com/disruptek/balls/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/disruptek%2Fballs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259872733,"owners_count":22924629,"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":["nim","testament","testes","tests","testutils","unit","unittests"],"created_at":"2024-08-03T03:01:38.454Z","updated_at":"2025-06-14T19:38:27.201Z","avatar_url":"https://github.com/disruptek.png","language":"Nim","funding_links":[],"categories":["Development Tools"],"sub_categories":["Testing"],"readme":"# balls\n\n[![CI](https://github.com/disruptek/balls/actions/workflows/ci.yml/badge.svg)](https://github.com/disruptek/balls/actions/workflows/ci.yml)\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/disruptek/balls?style=flat)](https://github.com/disruptek/balls/releases/latest)\n![Supported Nim version](https://img.shields.io/badge/nim-2.0.10%2B-informational?style=flat\u0026logo=nim)\n[![License](https://img.shields.io/github/license/disruptek/balls?style=flat)](#license)\n\n## TL;DR\n\nThis project contains two things:\n- a unit test dsl that helps debug tests that do not compile\n- a test runner that smartly expands the test matrix\n\nThe continuous integration tests include demonstration of all available\nfunctionality and output:\n[![CI](https://github.com/disruptek/balls/actions/workflows/ci.yml/badge.svg)](https://github.com/disruptek/balls/actions/workflows/ci.yml)\n\n## Why?\n\nThere's a want to centralize and reuse the logic that determines what code is\ndeemed successful and how we go about measuring this.\n\n## Goals\n\n- expect everything to work; any statement is a test\n- show the test code as we run it, unless not :wink:\n- better stack traces and test source output\n- less magical syntax and less output omission\n- aim to run many cheap tests in fewer files\n- easily accumulate state between tests when desired\n- when attached to a tty, errors are not fatal\n- individual tests don't even have to compile\n- easier test reordering, built-in timing, mem stats\n- smaller, more self-evident testing macro\n- no options means, \"what would disruptek do?\"\n\n## FAQ\n\nIf you're wondering why you cannot see compiler error messages for a particular\ntest that fails to compile, note that we use `compiles()` to swallow errors and\nallow subsequent tests to get executed, but only outside of `--define:release`.\n\n- With `--define:release`, compiler errors are fatal and output to console.\n- With `--define:ballsDry`, no color or emojis are output.\n- With `--define:ballsNoDump`, no symbol dumps will occur on test failure\n\n## Test Runner Usage\n\nYou can now run `balls` to run a limited local test matrix against the current\ncompiler -- the matrix is expanded automatically on GitHub Actions CI. This\nruntime will try to guess how to test your project regardless of its structure,\nbut you can help narrow what it chooses to compile by...\n\n- placing files matching `t*.nim` under a `tests` subdirectory, or\n- specifying the test directory or filename, or\n- passing arguments to balls which are expanded via globbing:\n\n```\n$ balls                 # run a quick `--define:debug` matrix\n$ balls 'examples/***'  # test all files anywhere beneath examples sub-directory\n$ balls experiments     # test all files below the experiments sub-directory\n$ balls tests/tfoo      # test the tests/tfoo.nim\n$ balls tests/tfoo.nim  # test the tests/tfoo.nim\n$ balls '**/trunner'    # test trunner.nim wherever it may live\n```\n\nYou can `--define:ballsPatterns=regex` to use regular expressions in these\npatterns instead of the simpler glob syntax.\n\nThe test runner is threaded and runs a comprehensive matrix which tries to\nsafely reuse compilation assets.\n\n![runner](docs/runner.svg \"test runner\")\n\nYou can add arguments which will be passed directly to the compiler:\n\n```\n$ balls --styleCheck:error\n# ... all tests are compiled with --styleCheck:error ...\n```\n\nYou can specify memory models with which to restrict the test matrix:\n```\n$ balls --mm:arc --mm:orc\n# ... tests are run only with --mm:arc and --mm:orc ...\n```\n\nYou can similarly constrain backends and optimizations:\n```\n$ balls --backend:c --define:danger\n# ... tests are run only with the c backend, and danger optimizations ...\n```\n\nNote that `--define:debug` is like `--undefine:release --undefine:danger`.\n\n```\n$ balls --backend:cpp --define:debug\n# ... tests are run only with the c++ backend, without optimizations ...\n```\n\nBy default, failing tests that are expected to pass will cause\nearly termination of the test runner, skipping any remaining test\ninvocations. You can disable this behavior by building `balls` with\n`--define:ballsFailFast=off`.\n\nSet `BALLS_CORES` in your process environment to a positive integer to\nconstrain compilation and test execution to a certain amount of concurrency.\n\n## Valgrind and Sanitizers\n\nWhen `--define:danger` test builds are part of the matrix, we will also attempt\nruntime analysis on the tests to check for memory errors, data races, and\nundefined behavior. If `valgrind` is found in the environment, it can be used\nas well.\n\nCurrently, failures of these test runs won't fail the test matrix.\n\n### Compile-time Toggles:\n  - `--define:ballsUseValgrind=off` to never use valgrind\n  - `--define:ballsUseSanitizers=off` to never use sanitizers\n\n### Environmental Variables:\n  - `BALLS_VALGRIND_FLAGS` a string of arguments to add to valgrind\n  - `BALLS_VALGRIND` a boolean to enable or disable valgrind use\n  - `BALLS_SANITIZERS` a boolean to enable or disable sanitizer use\n\n## Test Library Usage\n\nHere's a set of example tests which will pass (and fail) in interesting ways.\n\n```nim\nimport balls\n\nsuite \"suite balls\":\n\n  setup:\n    discard \"setup blocks work like you expect\"\n\n  block goats:\n    ## this is a test of goats\n    discard\n\n  setup:\n    discard \"also, you can redefine them whenever\"\n\n  block pigs:\n    ## a test of pigs\n    discard\n\n  teardown:\n    discard \"teardown works the same way, of course\"\n\n  var r = 3\n\n  block sometimes_the_wolf_is_nice:\n    assert true\n    check true, \"that wolf is very nice\"\n    inc r\n\n  block sheepies:\n    raise newException(ValueError, \"you're terrible\")\n\n  block check_r:\n    ## checking some things\n    ## this block exists only to test inclusion of\n    ## comments in the test code display...\n    check r == 3\n    echo r, \" will not be output\"\n\n  block:\n    ## check a list of statements in a block\n    check \"r should be 4\":\n      r \u003c 5\n      r \u003e 3\n\n  block:\n    ## unnamed block\n    discard\n\n  block:\n    discard \"unnamed test\"\n\n  inc r\n  assert r \u003e 0\n  ## this is a nice comment\n  type TypesAreNotTests = bool\n  ## there's really nothing else to say\n  const VariablesDefinedOutsideBlocksAreNotTests = true\n\n  test \"a test: block is fine\":\n    discard\n\n  block omission:\n    skip()\n\n  block:\n    ## hide this gory when statement\n    when defined(release):\n      suite \"fixed stuff\":\n        const compile = true\n        proc doesnt(c: bool) =\n          if not c:\n            raise\n\n        block:\n          proc fixed() = doesnt(compile)\n    else:\n      suite \"broken stuff\":\n        block:\n          proc broken() = doesnt(compile)\n\n  block assertions:\n    assert 2 == 4 div 2\n    assert 2 != 4 div 2\n\n  block omitted:\n    skip(\"i just don't wanna\")\n\n  assert \"any statement is a test\" != \"\"\n  check r \u003e 0, $r \u0026 \" is a good test of dynamic messages\"\n\n  report \"report for expression expansion:\", r != 5\n  checkpoint \"but checkpoint behaves as it does in unittest: \", r == 5\n\n  block explicit_failure:\n    fail(\"this looks bad\")\n\n  block check_with_message:\n    let x = 0\n    check \"\".len \u003c x, \"empty strings are STILL too long\"\n\n  block great_expectations:\n    expect ValueError:\n      checkpoint \"you love to see it\"\n      raise newException(ValueError, \"awful\")\n\n  block unmet_expectations:\n    expect ValueError:\n      checkpoint \"here comes trouble\"\n\n  block dashed_expectations:\n    expect ValueError:\n      check false, \"the truth hurts, but not as much as the false\"\n```\n\nRelax; your tests won't usually be this chaotic...  Right?  😉\n\n![demonstration](docs/demo.svg \"demonstration\")\n\nHere's a similar demo with `--define:danger` enabled to show the\nperformance metrics; no failing tests are permitted in such a build.\n\n![performance](docs/clean.svg \"performance\")\n\n## Documentation\nSee [the documentation for the balls module](https://disruptek.github.io/balls/balls.html) as generated directly from the source.\n\n## License\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdisruptek%2Fballs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdisruptek%2Fballs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdisruptek%2Fballs/lists"}