aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
authorGravatar Peter McGoron 2025-08-03 21:08:09 -0400
committerGravatar Peter McGoron 2025-08-03 21:08:09 -0400
commit6c04c5dd1c90df2e485e0bab626dc9f7efa6fd34 (patch)
tree183d168b3998881968e90368d9976db5b19a8787 /README.md
add conspire, with most meta-tests passed
Diffstat (limited to 'README.md')
-rw-r--r--README.md187
1 files changed, 187 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9d00465
--- /dev/null
+++ b/README.md
@@ -0,0 +1,187 @@
+# Conspire
+
+Conspire is an experiment in providing a portable R6RS/R7RS testing
+library. It uses purely functional data structures in a mutable parameter
+object, allowing for procedural programming inside of a dynamic extent
+to not affect the rest of the test system.
+
+## API
+
+### `test-info` procedures
+
+ `test-info`
+
+What SRFI-64 would call the "test runner" is in Conspire the `test-info`,
+which contains a pure SRFI-225 dictionary with an associated DTO. The
+dictionary must map at least symbols to values (including procedures).
+
+Whenever a test group or a test is entered, a new dynamic extent is
+entered with a new test-info object. Destructive updates to the new test
+info are not reflected in the test info of the call.
+
+The `test-info` is a parameter and can be modified with the `parameterize`
+form. The inputs to `test-info` must be one of:
+
+* `copy`: Return an unchanged copy of the `test-info`
+* `replace dict [dto]`: In the new dynamic extent, the dictionary is
+ replaced with `dict`, optionally with new dto `dto`.
+
+ test-set!
+ test-update!
+ test-update/default!
+ test-delete-all!
+ test-contains?
+ test-ref
+ test-ref/default
+ test-update/default
+
+These procedures are the same as their SRFI-225 equivalents, except
+that the DTO and dictionary arguments are not needed, and that the
+mutating procedures return unspecified values. For instance,
+
+ (test-set! key value)
+
+is equivalent to `(dict-set! dto dict key value)`.
+
+ (modify-test-info! proc)
+
+Evaluates `(proc dto dict)`, where `dto` is the current `test-info` DTO
+and `dict` is the current `test-info` dict. The procedure must return
+a dict satisfying the same DTO. This dictionary is set as the current
+`test-info` dictionary within the dynamic extent.
+
+ (inspect-test-info proc)
+
+Evaluates `(proc dto dict)`, where `dto` is the current `test-info` DTO
+and `dict` is the current `test-info` dict, and returns the result.
+
+### `test-info` Standard Procedure Keys
+
+ before-test (default-before-test name)
+
+A procedure with one argument (the name of the test).
+Called in the dynamic extent of the caller of the test. If it returns
+false, then the test is skipped.
+
+ test-setup (test-setup)
+
+A procedure of zero arguments. Called in the dynamic extent of a
+test. Used to set up parts of a test.
+
+ after-test (default-after-test dto dict)
+
+A procedure of two arguments (the DTO and dictionary of the test). Called
+in the dynamic extent of the caller. Used to report information about
+the test, and to merge desired information into the calling test info.
+
+ report-test (default-report-test dto dict)
+
+A procedure of two arguments (the DTO and dictionary of the test). Used by
+`default-after-test` to report the result of the test to the user.
+
+ group-begin (default-group-begin name)
+
+A procedure of one argument (the name of the group). Called in the dynamic
+extent of the group. Used to set up common information for a whole group.
+
+ group-end (default-group-end dto dict)
+
+A procedure of two arguments (the DTO and the dictionary of the
+test). Called in the dynamic extent containing the group. Used to report
+information about the group and merge it with the containing test info.
+
+### `test-info` Standard Keys
+
+* `success?`: Truthy if the test passed.
+* `exception`: The caught exception, if any.
+* `tests`: Number of run tests.
+* `passed`: Number of passed tests.
+* `failed`: Number of failed tests.
+* `skipped`: Number of skipped tests.
+* `verbose?`: Used by the default test setups. If false, only failures are
+ printed. Otherwise all test case information is printed.
+
+### Test Procedures and Forms
+
+If `test-name` is `#f`, then the test is not given a name. Every procedure
+here is implemented using `call-as-test`.
+
+ (call-as-test test-name thunk)
+
+Call `thunk` with the test name `test-name`.
+
+First executes the procedure stored in `before-test` in the `test-info` of
+the caller. If that returns non-false, then it creates a new `test-info`
+inheriting from the `test-info` of the caller, and runs the procedure
+stored in `test-setup` (in the new `test-info`). Then `thunk` will
+be executed in this dynamic extent. If `thunk` throws an exception,
+it will be caught. Afterwards, the procedure stored in `after-test`
+will be run with the `test-info` of the caller.
+
+The test will set
+
+* `success?`: Will be set to `#f` if an exception was caught.
+* `exception`: Only set if an exception was caught. The value is the
+ caught exception.
+
+ (test-application test-name (name expr) ...)
+
+The `name` must be symbols that are mutually not `bound-identifier=?`.
+Runs a test with `test-name` that evaluates `(expr ...)`.
+
+The test will set (in addition to `call-as-test`):
+
+* `name`: To be the passed `expr` quoted.
+* `application`: A list `(expr ...)`, where each is evaluated.
+* `success?`: If `(expr ...)` evaluates to not false.
+
+ (with-test-assert test-name body ...)
+
+Execute `body` in a test with `test-name`.
+
+The test will set (in addition to `call-as-test`):
+
+* `success?`: The returned value of `body` (if an exception is not caught).
+
+ (test-eq name %expected %actual)
+ (test-eqv name %expected %actual)
+ (test-equal name %expected %actual)
+
+Convienence wrappers for
+
+ (test-application test-name ((procedure <procedure>)
+ (expected %expected)
+ (actual %actual)))
+
+ (test-approximate X Y eps)
+
+Tests that
+
+ |X - Y| <= eps
+
+The test will set (in addition to `call-as-test`):
+
+* `procedure`: to be `%test-approximate` (this is an implementation detail).
+* `expected`: to be `X`, quoted.
+* `actual`: to be `Y`, quoted.
+* `error`: to be `eps`, quoted.
+* `application`: The `car` is `%test-approximate`, and the `cdr` is
+ `expected`, `actual`, and `error,` evaluated.
+
+ (with-test-error name error-predicate body ...)
+
+Evaluates `body ...` in a test. This test will set
+
+* `success?`: False if evaluation does not throw an exception. Otherwise
+ the return value of `error-predicate` on the thrown exception.
+
+## Porting Guide
+
+This library requires `make-parameter` and `parameterize` to work like
+in R7RS. Most R6RS implementations should support dynamic parameters out
+of the box.
+
+Multi-threaded implementations must export an SRFI-18 compatible
+interface for mutexes. Single threaded implementations can use the
+`compat.single-threaded.sld` (`compat.single-threaded.sls` for R6RS)
+implementations.