{"id":32191080,"url":"https://github.com/john-poplett/figleaf","last_synced_at":"2025-10-22T01:32:31.845Z","repository":{"id":2889000,"uuid":"3896187","full_name":"John-Poplett/figleaf","owner":"John-Poplett","description":"figleaf is a minimalist code coverage utility for Clojure based on cl-figleaf.","archived":false,"fork":false,"pushed_at":"2020-03-21T06:21:11.000Z","size":26,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-22T01:32:21.363Z","etag":null,"topics":["clojure","code-coverage","test-driven-development","unit-test"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/John-Poplett.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-04-01T23:32:38.000Z","updated_at":"2020-03-21T06:21:13.000Z","dependencies_parsed_at":"2022-08-06T13:00:29.699Z","dependency_job_id":null,"html_url":"https://github.com/John-Poplett/figleaf","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/John-Poplett/figleaf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/John-Poplett%2Ffigleaf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/John-Poplett%2Ffigleaf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/John-Poplett%2Ffigleaf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/John-Poplett%2Ffigleaf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/John-Poplett","download_url":"https://codeload.github.com/John-Poplett/figleaf/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/John-Poplett%2Ffigleaf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280365539,"owners_count":26318383,"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","status":"online","status_checked_at":"2025-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["clojure","code-coverage","test-driven-development","unit-test"],"created_at":"2025-10-22T01:31:07.172Z","updated_at":"2025-10-22T01:32:31.836Z","avatar_url":"https://github.com/John-Poplett.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Figleaf\n[![Clojars Project](https://img.shields.io/clojars/v/figleaf.svg)](https://clojars.org/figleaf)\n\nFigleaf is a lightweight code coverage utility for Clojure\nthat layers on top of the clojure.test unit test framework.\n\nYou specify a namespace to instrument and Figleaf instruments\nall the *public* functions in that namespace. As your unit tests execute,\nit keeps a real-time tally of how many times your test suite invokes\neach function was invoked. At the end of a test run, you get a report\nof which functions were tested and which were not.\n\nDespite its lightweight, as its name suggests, it's sufficient\nto ahem... cover your private parts. Though it doesn't analyze coverage by\nstatement or condition / decision logic, it's surprisingly useful.\nIt's also easy to use.\n\nFigleaf is based on and structured similarly to [cl-figleaf](https://github.com/John-Poplett/cl-figleaf), an earlier\nimplementation for Common Lisp.\n\n## Figleaf @ work\n\nFor clj-based command line development, add an alias to deps.edn, specifying\nyour two class files: the first specifying the class \"under test\" and the second\na class file containing corresponding unit tests:\n\n```clojure\n:aliases     {:figleaf   {:extra-deps  {org.clojure/clojure {:mvn/version \"1.10.1\"}\n                                        figleaf/figleaf     {:mvn/version \"1.0.1\"}}\n                          :extra-paths [\"test\"]\n                          :main-opts   [\"-m\" \"figleaf.core\" \"sentiment.core\" \"sentiment.core_test\"]}}\n```\nTo run Figleaf from the command line, invoke the figleaf alias:\n\n\u003e clj -A figleaf\n\nHere is an example of figleaf output on a sentiment analyzer project:\n\n```bash\nMacBook Pro:sentiment john$ clj -Afigleaf\n20-03-20 13:00:06 MacBook Pro INFO [sentiment.core:79] - Building AFFIN map for scoring words...\n\nTesting sentiment.core_test\n20-03-20 13:00:06 MacBook Pro DEBUG [sentiment.core:85] - Generating tokens...\n20-03-20 13:00:06 MacBook Pro INFO [sentiment.core:79] - Building AFFIN map for scoring words...\n\nRan 7 tests containing 11 assertions.\n0 failures, 0 errors.\nFUNCTIONS TESTED: #'sentiment.core/confusion-matrix, #'sentiment.core/compute-valid-values, #'sentiment.core/build-AFFIN-map, #'sentiment.core/-\u003eSample, #'sentiment.core/tokenize-text, #'sentiment.core/mean, #'sentiment.core/classification-report, #'sentiment.core/zip, #'sentiment.core/handle-sentiment-data, #'sentiment.core/process-sentiment-data\nFUNCTIONS UNTESTED: #'sentiment.core/make-predictions, #'sentiment.core/map-\u003eSample, #'sentiment.core/-main, #'sentiment.core/AFFIN-predict\nCODE COVERAGE: Functions 14, Tested 10, Ratio 71%\n```\n\n## Figleaf @ play\nFigleaf plays well with the REPL. \n\nFrom the REPL:\n```clojure\n(use 'figleaf.core)\n(require 'namespace-under-test)\n(require 'unit-test-namespace)\n(run-tests namespace-under-test unit-test-namespace)\n```\n\nHere's an example run of invoking Figleaf from the REPL prompt:\n```clojure\nMacBook Pro:sentiment john$ clj\nClojure 1.10.1\nuser=\u003e (use 'figleaf.core)\nnil\nuser=\u003e (require 'sentiment.core)\n20-03-20 22:05:19 MacBook Pro INFO [sentiment.core:79] - Building AFFIN map for scoring words...\nnil\nuser=\u003e (require 'sentiment.core_test)\nnil\nuser=\u003e (run-tests sentiment.core sentiment.core_test)\nTesting sentiment.core_test\n20-03-20 22:06:11 MacBook Pro DEBUG [sentiment.core:85] - Generating tokens...\n20-03-20 22:06:11 MacBook Pro INFO [sentiment.core:79] - Building AFFIN map for scoring words...\n\nRan 7 tests containing 11 assertions.\n0 failures, 0 errors.\nFUNCTIONS TESTED: #'sentiment.core/confusion-matrix, #'sentiment.core/compute-valid-values, #'sentiment.core/build-AFFIN-map, #'sentiment.core/-\u003eSample, #'sentiment.core/tokenize-text, #'sentiment.core/mean, #'sentiment.core/classification-report, #'sentiment.core/zip, #'sentiment.core/handle-sentiment-data, #'sentiment.core/process-sentiment-data\nFUNCTIONS UNTESTED: #'sentiment.core/make-predictions, #'sentiment.core/map-\u003eSample, #'sentiment.core/-main, #'sentiment.core/AFFIN-predict\nCODE COVERAGE: Functions 14, Tested 10, Ratio 71%\nnil\n```\n\nFigleaf keeps statistics after a run in transaction-friendly \"let-over-lambda\" style closure.\nA handful of functions are defined to access its statistics: These include the functions:\n\n* all\n* tested\n* untested\n* namespace-function-count\n* funcall-count\n\n\n\nAfter running tests, we can query the counter from the REPL prompt as this example\ndemonstrates:\n```clojure\nuser=\u003e (all)\n(\"#'sentiment.core/process-sentiment-data\" \"#'sentiment.core/build-AFFIN-map\" \"#'sentiment.core/mean\" \"#'sentiment.core/-main\" \"#'sentiment.core/make-predictions\" \"#'sentiment.core/classification-report\" \"#'sentiment.core/AFFIN-predict\" \"#'sentiment.core/-\u003eSample\" \"#'sentiment.core/handle-sentiment-data\" \"#'sentiment.core/zip\" \"#'sentiment.core/compute-valid-values\" \"#'sentiment.core/confusion-matrix\" \"#'sentiment.core/tokenize-text\" \"#'sentiment.core/map-\u003eSample\")\nuser=\u003e (funcall-count)\n1015\nuser=\u003e (tested)\n(\"#'sentiment.core/confusion-matrix\" \"#'sentiment.core/compute-valid-values\" \"#'sentiment.core/build-AFFIN-map\" \"#'sentiment.core/-\u003eSample\" \"#'sentiment.core/tokenize-text\" \"#'sentiment.core/mean\" \"#'sentiment.core/classification-report\" \"#'sentiment.core/zip\" \"#'sentiment.core/handle-sentiment-data\" \"#'sentiment.core/process-sentiment-data\")\nuser=\u003e (/ (funcall-count) (count (tested)))\n203/2\nuser=\u003e (/ (funcall-count) (count (tested)) 1.0)\n101.5\nuser=\u003e (tested-function-count)\n10\nuser=\u003e (/ (funcall-count) (count (all)) 1.0)\n72.5\n```\nWith version 1.02, you can fetch the raw content of the\nfunction counter with the `get-funcall-counter` function.\n```clojure\nuser=\u003e (get-funcall-counter)\n{\"#'sentiment.core/confusion-matrix\" 8, \"#'sentiment.core/compute-valid-values\" 1, \"#'sentiment.core/build-AFFIN-map\" 1, \"#'sentiment.core/-\u003eSample\" 996, \"#'sentiment.core/tokenize-text\" 1, \"#'sentiment.core/mean\" 3, \"#'sentiment.core/classification-report\" 1, \"#'sentiment.core/zip\" 2, \"#'sentiment.core/handle-sentiment-data\" 1, \"#'sentiment.core/process-sentiment-data\" 1}\n```\n## Benefits\n\u003e Nothing measured, nothing gained.\n  \nAs the adage suggests, writing unit tests alone doesn't promote\nprogress. Uneven test coverage is conceivably as useless as no\ncoverage at all.\n\nAs soon as you start measuring test coverage, it is surprising how\nfast you adjust your coding practice. It provides a new source of \nsatisfaction. Whereas before, you might have taken some pride in the\nsheer number of lines of code you were cranking out, now you have the\ncounter-balancing satisfaction of knowing how well your code is tested.\n\nYour behaviors change. As you drive to have 100% coverage, you will\ndo things that never occurred to you before! You will start to prune\nand preen to eliminate unused functions or replace \"hand-rolled\" functions\nwith equivalents from highly-regarded open source projects.\n\nYou discover that one way to 100% coverage is to reduce the test burden.\n\nCode coverage promotes pace. As long as you are pursuing complete\ncoverage, your code output will not increase to the point that\nyou're writing unqualified code.\n\n## Coding with a Lisp\nA long time ago, I had been intrigued by the way object-relational modeling\nmiddleware companies performed bytecode rewriting just\nto instrument Java methods. Talk about pluck!\n\nI also had first-hand experience with C/C++ code coverage tools. Again those tools go\nto extraordinary lengths—parsing and rewriting source code—to achieve the desired result.\n\nI was motivated to write the original Common Lisp Figleaf to find out how much easier it might be\nto write than any comparable system designed for a static, procedural language. I was not disappointed.\n\nThe ease of the original implementation and the port from Common Lisp to Clojure was straight-forward; the\neffort, not extraordinary.\n\nFigleaf is a shining example of the benefits of Lisp and a language where \ncode is data. I cringe to think of the effort involved to produce like functionality\nin a static procedural language. The final result is much more satisfying. \nConsider these differences:\n \n### Static Instrumentation\n* Instrument at compile time\n* Requires special builds\n* Requires intimate, semantic knowledge of sometimes parser-unfriendly\nlanguages and the skill level of a compiler writer\n\n### Dynamic Instrumentation\n* Instrument at run-time or class-load time\n* Special build not required\n* Instrument live images\n* Remove instrumentation from live images\n* Instrumentation simple for Clojure and other languages where functions are first-class objects\n\n## Copyright and License\nCopyright (C) 2012-2020 John H. Poplett. All rights reserved.\n\nDistributed under the Eclipse Public License, the same as Clojure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohn-poplett%2Ffigleaf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohn-poplett%2Ffigleaf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohn-poplett%2Ffigleaf/lists"}