diff options
| author | s-ol <s+removethis@s-ol.nu> | 2022-02-03 11:21:01 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-02 14:24:49 +0000 |
| commit | 5851bcdf714457057ca9716984b136f37918bf80 (patch) | |
| tree | 7041826b1a792bd23e4b16c8fc202372bb55d382 | |
| parent | fix do, if, when (diff) | |
| download | alive-5851bcdf714457057ca9716984b136f37918bf80.tar.gz alive-5851bcdf714457057ca9716984b136f37918bf80.zip | |
lib: add testing module
| -rw-r--r-- | alv-lib/testing.moon | 97 | ||||
| -rw-r--r-- | spec/lang/testing_spec.moon | 80 |
2 files changed, 177 insertions, 0 deletions
diff --git a/alv-lib/testing.moon b/alv-lib/testing.moon new file mode 100644 index 0000000..a321e43 --- /dev/null +++ b/alv-lib/testing.moon @@ -0,0 +1,97 @@ +import Constant, Op, Builtin, Input, Error, T, any, const from require "alv.base" +import Tag, Cell from require "alv" + +assert_ = Constant.meta + meta: + name: 'assert' + summary: "Throw an error if a condition is false." + examples: { "(assert check [msg])" } + description: " +Check is any bool result. When it is `false`, this op throws an error. +`msg` is a str= value that is used as the error message. +By default, the message contains the failing check expression." + + value: class extends Builtin + assertOp = class extends Op + pattern = any.bool + const.str + setup: (inputs) => + { check, msg } = pattern\match inputs + + super + check: Input.hot check + msg: msg and Input.cold msg + + tick: => + { :check, :msg } = @unwrap_all! + + if not check + error Error 'assertion', msg or "assertion failed" + + eval: (scope, tail) => + L\trace "evaling #{@}" + assert #tail == 1 or #tail == 2, "'assert' takes one or two arguments" + + tag = @tag\clone Tag.parse '-1' + inner = Cell tag, { + Constant.literal T.opdef, assertOp, 'assert' + tail[1] + tail[2] or Constant.str "assertion failed: #{tail[1]\stringify 2}" + } + super inner\eval scope + +expect_eq = Constant.meta + meta: + name: 'expect=' + summary: "Throw an error if arguments aren't equal." + examples: { "(expect= expected actual [actual…])" } + description: " +Check if `expected` and `actual` are equal. +If not, throws an error." + + value: class extends Builtin + expectOp = class extends Op + pattern = any! + (any! + const.str)\named('val', 'src')*0 + setup: (inputs) => + { expected, values } = pattern\match inputs + + super + expected: Input.hot expected + values: [Input.hot got.val for got in *values] + sources: [Input.cold got.src for got in *values] + + type = expected\type! + for i, val in ipairs @inputs.values + same = type == val\type! + assert same, Error 'assertion', "Expected #{@inputs.sources[i]!} to equal #{expected.result} (got #{val.result})" + + tick: => + type = @inputs.expected\type! + expected = @inputs.expected! + + for i, val in ipairs @inputs.values + assert (type\eq expected, val!), Error 'assertion', "Expected #{@inputs.sources[i]!} to equal #{@inputs.expected.result} (got #{val.result})" + + eval: (scope, tail) => + L\trace "evaling #{@}" + assert #tail > 1, "'expect=' takes at least two arguments" + + children = { + Constant.literal T.opdef, expectOp, 'assert' + tail[1] + } + for arg in *tail[2,] + table.insert children, arg + table.insert children, Constant.str arg\stringify 2 + + tag = @tag\clone Tag.parse '-1' + inner = Cell tag, children + super inner\eval scope + +Constant.meta + meta: + name: 'testing' + summary: "Operators for testing." + + value: + 'assert': assert_ + 'expect=': expect_eq diff --git a/spec/lang/testing_spec.moon b/spec/lang/testing_spec.moon new file mode 100644 index 0000000..5be7848 --- /dev/null +++ b/spec/lang/testing_spec.moon @@ -0,0 +1,80 @@ +import TestPilot from require 'spec.test_setup' +import T, Array, Constant from require 'alv' + +describe "testing", -> + test = TestPilot '', '(import* testing logic)\n' + + describe "assert", -> + it "ignores true", -> + with COPILOT\eval_once '(assert true)' + assert.is.true \is_const! + assert.is.nil .result + + with COPILOT\eval_once '(assert true "with message")' + assert.is.true \is_const! + assert.is.nil .result + + it "throws on false", -> + assert.has.error -> COPILOT\eval_once '(assert false)' + + it "shows failing expression", -> + err = assert.has.error -> COPILOT\eval_once '(assert false)' + assert.matches "assertion failed: false", err + + err = assert.has.error -> COPILOT\eval_once '(assert (== 1 2))' + assert.matches "assertion failed: %(== 1 2%)", err + + it "supports custom error messages", -> + err = assert.has.error -> COPILOT\eval_once '(assert (== "green" "red") "duck isnt green")' + assert.matches "duck isnt green", err + + describe "expect=", -> + it "passes equal params", -> + with COPILOT\eval_once '(expect= 1 1 1)' + assert.is.true \is_const! + assert.is.nil .result + + with COPILOT\eval_once '(expect= 2 2 2)' + assert.is.true \is_const! + assert.is.nil .result + + with COPILOT\eval_once '(expect= true true)' + assert.is.true \is_const! + assert.is.nil .result + + with COPILOT\eval_once '(expect= "hello" "hello")' + assert.is.true \is_const! + assert.is.nil .result + + with COPILOT\eval_once '(expect= (array 1 2) (array 1 2))' + assert.is.true \is_const! + assert.is.nil .result + + it "fails different values", -> + assert.has.error -> COPILOT\eval_once '(expect= 1 2)' + assert.has.error -> COPILOT\eval_once '(expect= 1 1 2)' + assert.has.error -> COPILOT\eval_once '(expect= 2 1 1)' + + assert.has.error -> COPILOT\eval_once '(expect= true false)' + + assert.has.error -> COPILOT\eval_once '(expect= "asdf" "bsdf")' + assert.has.error -> COPILOT\eval_once '(expect= (array 1 2) (array 1 3))' + + it "fails different types", -> + assert.has.error -> COPILOT\eval_once '(expect= true 2)' + assert.has.error -> COPILOT\eval_once '(expect= true true 1)' + assert.has.error -> COPILOT\eval_once '(expect= true false "str")' + + it "reports first failing arguments", -> + err = assert.has.error -> COPILOT\eval_once '(expect= 1 2)' + assert.matches "assertion error: Expected 2 to equal <num= 1> %(got <num= 2>%)", err + + err = assert.has.error -> COPILOT\eval_once '(expect= 1 1 2 4)' + assert.matches "assertion error: Expected 2 to equal <num= 1> %(got <num= 2>%)", err + + err = assert.has.error -> COPILOT\eval_once '(expect= true 2 3)' + assert.matches "assertion error: Expected 2 to equal <bool= true> %(got <num= 2>%)", err + + it "reports full expressions", -> + err = assert.has.error -> COPILOT\eval_once '(import* math) (expect= 1 (+ 1 1))' + assert.matches "assertion error: Expected %(%+ 1 1%) to equal <num= 1> %(got <num= 2>%)", err |
