Merge pull request #539 from eglaysher/new-stdlib

Unit Testing / Partial New Library Work
This commit is contained in:
cgyarvin 2018-01-29 10:55:47 -08:00 committed by GitHub
commit cadc61929f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 2559 additions and 0 deletions

71
gen/test.hoon Normal file
View File

@ -0,0 +1,71 @@
/+ new-hoon, tester
/= all-tests
/^ (map @ta tests:tester)
/: /===/tests
/_ /test-tree/
::
=, new-hoon
|%
::
++ test-runner
:> run all tests in {a} with a filter.
=| pax=path
|= [filter=path eny=@uvJ a=tests:tester]
^- tang
%- concat:ls
%+ turn a
|= b=instance:tester
^- tang
=^ matches filter (match-filter filter p.b)
?. matches
~
?- -.q.b
%& (run-test [p.b pax] eny p.q.b)
%| ^$(pax [p.b pax], a p.q.b)
==
::
++ run-test
:> executes an individual test.
|= [pax=path eny=@uvJ test=$-(@uvJ (list tape))]
^- tang
=+ name=(spud (flop pax))
=+ run=(mule |.((test eny)))
?- -.run
$| :: the stack is already flopped for output?
;: weld
p:run
`tang`[[%leaf (weld name " CRASHED")] ~]
==
$& ?: =(~ p:run)
[[%leaf (weld name " OK")] ~]
:: Create a welded list of all failures indented.
%- flop
;: weld
`tang`[[%leaf (weld name " FAILED")] ~]
%+ turn p:run
|= {i/tape}
^- tank
[%leaf (weld " " i)]
==
==
::
++ match-filter
:> checks to see if {name} matches the head of {filter}.
|= [filter=path name=term]
^- [? path]
?~ filter
:: when there's no filter, we always match.
[%.y ~]
[=(i.filter name) t.filter]
--
::
:- %say
|= $: [now=@da eny=@uvJ bec=beak]
[filter=$?($~ [pax=path $~])]
$~
==
:- %tang
%^ test-runner
?~ filter ~ pax.filter
eny
(test-map-to-test-list:tester all-tests)

1557
lib/new-hoon.hoon Normal file

File diff suppressed because it is too large Load Diff

165
lib/tester.hoon Normal file
View File

@ -0,0 +1,165 @@
/+ new-hoon
::
:> testing utilities
|%
:> # %models
+|
+= tests
:> a hierarchical structure of tests
:>
:> a recursive association list mapping a part of a path
:> to either a test trap or a sublist of the same type.
(list instance)
::
+= instance
:> a mapping between a term and part of a test tree.
(pair term (each $-(@uvJ (list tape)) tests))
::
:> # %generate
:> utilities for generating ++tests from files and directories.
+|
++ merge-base-and-recur
:> combine the current file and subdirectory.
:>
:> this merges the file {base} with its child files {recur}.
|= [base=vase recur=(map @ta tests:tester)]
^- tests
=+ a=(gen-tests base)
=+ b=(test-map-to-test-list recur)
:: todo: why does ++weld not work here? {a} and {b} are cast and have the
:: correct faces.
(welp a b)
::
++ test-map-to-test-list
:> translates ford output to something we can work with.
:>
:> ford gives us a `(map @ta tests:tester)`, but we actually
:> want something like ++tests.
|= a=(map @ta tests:tester)
:: todo: i'd like to sort this, but ++sort has -find.a problems much like
:: ++weld does above!?
^- tests
%+ turn
(to-list:dct:new-hoon a)
|= {key/@ta value/tests:tester}
[key [%| value]]
::
++ gen-tests
:> creates a {tests} list out of a vase of a test suite
|= v=vase
^- tests
=+ arms=(sort (sloe p.v) aor)
%+ turn arms
|= arm/term
:- arm
:- %&
|= eny=@uvJ
=+ context=(slop !>((init-test eny)) v)
=/ r (slap context [%cnsg [arm ~] [%$ 3] [[%$ 2] ~]])
((hard (list tape)) q:(slap r [%limb %results]))
::
:> # %per-test
:> data initialized on a per-test basis.
::
++ init-test
|= {cookie/@uvJ}
~(. tester `(list tape)`~ cookie 10 0)
::
++ tester-type _(init-test `@uvJ`0)
::
++ tester
|_ $: error-lines=(list tape) :< output messages
eny=@uvJ :< entropy
check-iterations=@u :< # of check trials
current-iteration=@u :< current iteration
==
:> #
:> # %check
:> #
:> gates for quick check style tests.
+|
+- check
|* [generator=$-(@uvJ *) test=$-(* ?)]
|-
^+ +>.$
?: (gth current-iteration check-iterations)
+>.$
:: todo: wrap generator in mule so it can crash.
=+ sample=(generator eny)
:: todo: wrap test in mule so it can crash.
=+ ret=(test sample)
?: ret
%= $
eny (shaf %huh eny) :: xxx: better random?
current-iteration (add current-iteration 1)
==
=+ case=(add 1 current-iteration)
=+ case-plural=?:(=(case 1) "case" "cases")
%= +>.$
error-lines :*
"falsified after {(noah !>(case))} {case-plural} by '{(noah !>(sample))}'"
error-lines
==
==
::
:: todo: a generate function that takes an arbitrary span.
::
++ generate-range
|= [min=@ max=@]
|= c=@uvJ
^- @
=+ gen=(random:new-hoon c)
=^ num gen (range:gen min max)
num
::
++ generate-dict
:> generator which will produce a dict with {count} random pairs.
|= count=@u
:> generate a dict with entropy {c}.
|= c=@uvJ
:>
:> gen: stateful random number generator
:> out: resulting map
:> i: loop counter
:>
=/ gen (random:new-hoon c)
=| out=(dict:new-hoon @ud @ud)
=| i=@u
|-
^- (dict:new-hoon @ud @ud)
?: =(i count)
out
=^ first gen (range:gen 0 100)
=^ second gen (range:gen 0 100)
$(out (put:dct:new-hoon out first second), i +(i))
:> #
:> # %test
:> #
:> test expectation functions
+|
:: todo: unit testing libraries have a lot more to them than just eq.
++ expect-eq
|* [a=* b=* c=tape]
^+ +>
?: =(a b)
+>.$
%= +>.$
error-lines :*
"failure: '{c}'"
" actual: '{(noah !>(a))}'"
" expected: '{(noah !>(b))}'"
error-lines
==
==
::
:> #
:> # %output
:> #
:> called by the test harness
::
++ results
:> returns results.
^- (list tape)
error-lines
--
--

10
ren/test-tree.hoon Normal file
View File

@ -0,0 +1,10 @@
/+ tester
/= base /| /!noun/
/~ ~
==
/= recur /^ (map @ta tests:tester)
/| /_ /test-tree/
/~ ~
==
::
(merge-base-and-recur:tester !>(base) recur)

280
tests/new-hoon/ls.hoon Normal file
View File

@ -0,0 +1,280 @@
/+ new-hoon, tester
=, ls:new-hoon
|_ tester-type:tester
++ test-head
(expect-eq (head [1 ~]) 1 "head")
::
++ test-last
(expect-eq (last:ls [1 2 ~]) 2 "last")
::
++ test-tail
(expect-eq (tail [1 2 3 ~]) [2 3 ~] "tail")
::
++ test-init
(expect-eq (init [1 2 3 ~]) [1 2 ~] "init")
::
++ test-size
(expect-eq (size ['a' 'b' 'c' ~]) 3 "size")
::
++ test-map
(expect-eq (map:ls [1 2 ~] |=(a/@ (add 1 a))) [2 3 ~] "map")
::
++ test-reverse
(expect-eq (reverse [1 2 3 ~]) [3 2 1 ~] "reverse")
::
++ test-intersperse
(expect-eq (intersperse 1 [5 5 5 ~]) [5 1 5 1 5 ~] "intersperse")
::
++ test-intercalate
%^ expect-eq
(intercalate "," ["one" "two" "three" ~])
["one,two,three"]
"intercalate"
::
++ test-transpose
%^ expect-eq
(transpose ~[~[1 2 3] ~[4 5 6]])
~[~[1 4] ~[2 5] ~[3 6]]
"transpose"
::
++ test-foldl
(expect-eq (foldl [1 2 3 ~] 3 |=({a/@ b/@} (add a b))) 9 "foldl")
::
++ test-foldr
(expect-eq (foldr [1 2 3 ~] 1 |=({a/@ b/@} (add a b))) 7 "foldr")
::
++ test-concat
(expect-eq (concat ~[~[1 2] ~[3 4]]) ~[1 2 3 4] "concat")
::
++ test-weld
(expect-eq (weld:ls ~[1 2 3] ~["one" "two"]) ~[1 2 3 "one" "two"] "weld")
::
++ test-any-true
(expect-eq (any [1 2 3 ~] |=(a/@ =(a 2))) %.y "any true")
::
++ test-any-false
(expect-eq (any [1 2 3 ~] |=(a/@ =(a 8))) %.n "any false")
::
++ test-all-true
(expect-eq (all [1 1 1 ~] |=(a/@ =(a 1))) %.y "all true")
::
++ test-all-false
(expect-eq (all [1 3 1 ~] |=(a/@ =(a 1))) %.n "all false")
::
++ test-scanl
%^ expect-eq
(scanl ~[1 2 3] 0 |=({a/@ b/@} (add a b)))
~[0 1 3 6]
"scanl"
::
++ test-scanl1
%^ expect-eq
(scanl1 ~[1 2 3] |=({a/@ b/@} (add a b)))
~[1 3 6]
"scanl1"
::
++ test-scanr
%^ expect-eq
(scanr ~[1 2 3] 0 |=({a/@ b/@} (add a b)))
~[6 5 3 0]
"scanr"
::
++ test-scanr1
%^ expect-eq
(scanr1 ~[1 2 3] |=({a/@ b/@} (add a b)))
~[6 5 3]
"scanr1"
::
++ test-map-foldl
%^ expect-eq
(map-foldl ~[1 2 3] 1 |=({a/@ b/@} [(add a b) (add 1 a)]))
[7 ~[2 3 5]]
"map-foldl"
::
++ test-map-foldr
%^ expect-eq
(map-foldr ~[1 2 3] 1 |=({a/@ b/@} [(add a b) (add 1 a)]))
[7 ~[7 5 2]]
"map-foldr"
::
++ test-unfoldr
%^ expect-eq
(unfoldr 5 |=(a/@ ?:(=(a 0) ~ `[a (dec a)])))
[5 4 3 2 1 ~]
"unfoldr"
::
++ test-take
%^ expect-eq
(take 3 ~[1 2 3 4 5])
[1 2 3 ~]
"take"
::
++ test-drop
%^ expect-eq
(drop:ls 3 ~[1 2 3 4 5])
[4 5 ~]
"drop"
::
++ test-split-at
%^ expect-eq
(split-at 3 ~[1 2 3 4 5])
[[1 2 3 ~] [4 5 ~]]
"split-at"
::
++ test-take-while
%^ expect-eq
(take-while ~[1 2 3 4 5] |=(a/@ (lth a 3)))
[1 2 ~]
"take-while"
::
++ test-drop-while
%^ expect-eq
(drop-while ~[1 2 3 4 5] |=(a/@ (lth a 3)))
[3 4 5 ~]
"drop-while"
::
++ test-drop-while-end
%^ expect-eq
(drop-while-end ~[5 5 1 5 5] |=(a/@ =(a 5)))
[5 5 1 ~]
"drop-while-end"
::
++ test-split-on
%^ expect-eq
(split-on ~[1 2 3 4 1 2 3 4] |=(a/@ (lth a 3)))
[[1 2 ~] [3 4 1 2 3 4 ~]]
"split-on"
::
++ test-break
%^ expect-eq
(break ~[1 2 3 4 1 2 3 4] |=(a/@ (gth a 3)))
[[1 2 3 ~] [4 1 2 3 4 ~]]
"break"
::
++ test-strip-prefix
%^ expect-eq
(strip-prefix "foo" "foobar")
[~ "bar"]
"break"
::
++ test-inits
%^ expect-eq
(inits "abc")
["a" "ab" "abc" ~]
"inits"
::
++ test-tails
%^ expect-eq
(tails "abc")
["abc" "bc" "c" ~]
"tails"
::
++ test-is-prefix-of
%^ expect-eq
(is-prefix-of "foo" "foobar")
%.y
"is-prefix-of"
::
++ test-is-suffix-of
%^ expect-eq
(is-suffix-of "bar" "foobar")
%.y
"is-suffix-of"
::
++ test-is-infix-of
%^ expect-eq
(is-infix-of "ob" "foobar")
%.y
"is-infix-of"
::
++ test-elem
%^ expect-eq
(elem 5 [1 2 3 4 5 ~])
%.y
"elem"
::
++ test-lookup
%^ expect-eq
(lookup "two" [["one" 1] ["two" 2] ["three" 3] ~])
[~ 2]
"lookup"
::
++ test-find
%^ expect-eq
(find:ls [3 2 1 5 1 2 3 ~] |=(a/@ (gth a 3)))
[~ 5]
"find"
::
++ test-filter
%^ expect-eq
(filter [1 2 1 2 1 ~] |=(a/@ =(a 2)))
[1 1 1 ~]
"filter"
::
++ test-partition
%^ expect-eq
(partition [1 2 1 2 1 ~] |=(a/@ =(a 2)))
[[2 2 ~] [1 1 1 ~]]
"partition"
::
++ test-elem-index
%^ expect-eq
(elem-index 2 [1 2 3 4 ~])
`1
"elem-index"
::
++ test-elem-indices
%^ expect-eq
(elem-indices 2 [1 2 1 2 ~])
[1 3 ~]
"elem-indices"
::
++ test-find-index
%^ expect-eq
(find-index [1 2 3 ~] |=(a/@ =(a 2)))
`1
"find-index"
::
++ test-find-indices
%^ expect-eq
(find-indices [1 2 1 2 ~] |=(a/@ =(a 2)))
[1 3 ~]
"find-indices"
::
++ test-zip
%^ expect-eq
(zip [[1 2 3 ~] [4 5 6 ~] [7 8 9 ~] ~])
[[1 4 7 ~] [2 5 8 ~] [3 6 9 ~] ~]
"zip"
::
++ test-unique
%^ expect-eq
(unique [1 2 3 1 2 3 ~])
[1 2 3 ~]
"unique"
::
++ test-delete
%^ expect-eq
(delete 2 [1 2 3 2 ~])
[1 3 2 ~]
"delete"
::
++ test-delete-firsts
%^ expect-eq
(delete-firsts [1 2 2 2 3 4 5 ~] [2 2 5 ~])
[1 2 3 4 ~]
"delete-firsts"
::
++ test-union
%^ expect-eq
(union [1 2 3 ~] [4 2 5 ~])
[1 2 3 4 5 ~]
"union"
::
++ test-intersect
%^ expect-eq
(intersect [5 6 6 7 8 ~] [9 8 8 6 ~])
[6 6 8 ~]
"intersect"
--

360
tests/new-hoon/mp.hoon Normal file
View File

@ -0,0 +1,360 @@
/+ new-hoon, tester
=, dct:new-hoon
=+ four=(from-list [[1 "one"] [2 "two"] [3 "three"] [4 "four"] ~])
=+ three=(from-list [[1 "one"] [2 "two"] [3 "three"] ~])
|_ tester-type:tester
++ test-empty
(expect-eq (empty four) %.n "empty")
::
++ test-size
(expect-eq (size four) 4 "size")
::
++ test-member
(expect-eq (member four 4) %.y "member")
::
++ test-put-with
=+ ints=(from-list [["one" 1] ["two" 2] ["three" 3] ["four" 4] ~])
%^ expect-eq
(put-with ints "three" 2 add)
(from-list [["one" 1] ["two" 2] ["three" 5] ["four" 4] ~])
"put-with"
::
++ test-put-with-key
%^ expect-eq
(put-with-key four 4 "four" |=({a/@ud b/tape c/tape} (weld (scow %ud a) b)))
(from-list [[1 "one"] [2 "two"] [3 "three"] [4 "4four"] ~])
"put-with-key"
::
++ test-put-lookup-with-key
%^ expect-eq
%- put-lookup-with-key :^
four
4
"five"
|=({key/@ud old/tape new/tape} new)
:- `"four"
(from-list [[1 "one"] [2 "two"] [3 "three"] [4 "five"] ~])
"put-lookup-with-key"
::
++ test-delete
%^ expect-eq
(delete four 4)
three
"delete"
::
++ test-adjust
%^ expect-eq
%^ adjust
four
3
|=(a/tape (weld "this" a))
(from-list [[1 "one"] [2 "two"] [3 "thisthree"] [4 "four"] ~])
"adjust"
::
++ test-adjust-with-key
%^ expect-eq
%^ adjust-with-key
four
3
|=({a/@ud b/tape} (weld (scow %ud a) b))
(from-list [[1 "one"] [2 "two"] [3 "3three"] [4 "four"] ~])
"adjust-with-key"
::
++ test-update
%^ expect-eq
%^ update
four
3
|=(a/tape `(maybe tape)`~)
(from-list [[1 "one"] [2 "two"] [4 "four"] ~])
"update"
::
++ test-update-with-key
%^ expect-eq
%^ update-with-key
four
3
|=({a/@u b/tape} `(maybe tape)`[~ (weld (scow %ud a) b)])
(from-list [[1 "one"] [2 "two"] [3 "3three"] [4 "four"] ~])
"update-with-key"
::
++ test-alter-as-add
%^ expect-eq
%^ alter
four
5
|=(a/(maybe tape) `(maybe tape)`[~ "five"])
(from-list [[1 "one"] [2 "two"] [3 "three"] [4 "four"] [5 "five"] ~])
"alter (as add)"
::
++ test-alter-as-delete
%^ expect-eq
%^ alter
four
2
|=(a/(maybe tape) `(maybe tape)`~)
(from-list [[1 "one"] [3 "three"] [4 "four"] ~])
"alter (as delete)"
::
++ test-alter-as-change
%^ expect-eq
%^ alter
four
2
|=(a/(maybe tape) `(maybe tape)`[~ "dos"])
(from-list [[1 "one"] [2 "dos"] [3 "three"] [4 "four"] ~])
"alter (as change)"
::
++ check-alter
:: check random dicts of 50 items with 40 random operations done on them
:: for validity.
%+ check
(generate-dict 50)
|= a/(dict @ud @ud)
:: this is dumb, but use {a} as entropy?
=/ gen (random:new-hoon (jam a))
=| i/@u
|-
?: =(i 40)
%.y
=^ key gen (range:gen 0 100)
=^ value gen (range:gen 0 100)
=. a %^ alter-with-key a key
|= {key/@ud current/(maybe @ud)}
^- (maybe @ud)
=+ action=(mod key 2)
?: =(action 0) :: return nothing
~
?: =(action 1) :: add/set value
`value
~ :: impossible
?. (valid a)
%.n
$(i +(i))
::
++ test-union
%^ expect-eq
%+ union
(from-list [[1 "left"] [2 "left"] ~])
(from-list [[2 "right"] [3 "right"] ~])
(from-list [[1 "left"] [2 "left"] [3 "right"] ~])
"union"
::
++ test-union-with
%^ expect-eq
%^ union-with
(from-list [[1 "left"] [2 "left"] ~])
(from-list [[2 "right"] [3 "right"] ~])
|=({a/tape b/tape} (weld a b))
(from-list [[1 "left"] [2 "leftright"] [3 "right"] ~])
"union-with"
::
++ test-union-with-key
%^ expect-eq
%^ union-with-key
(from-list [[1 "left"] [2 "left"] ~])
(from-list [[2 "right"] [3 "right"] ~])
|=({a/@ud b/tape c/tape} :(weld `tape`(scow %ud a) b c))
(from-list [[1 "left"] [2 "2leftright"] [3 "right"] ~])
"union-with-key"
::
++ test-map
%^ expect-eq
%+ map:dct
three
crip
(from-list [[1 'one'] [2 'two'] [3 'three'] ~])
"map"
::
++ test-map-with-key
%^ expect-eq
%+ map-with-key
three
|=({a/@u b/tape} (weld (scow %ud a) b))
(from-list [[1 "1one"] [2 "2two"] [3 "3three"] ~])
"map-with-key"
::
++ test-map-fold
%^ expect-eq
%^ map-fold
three
"Everything: "
|= {accumulator/tape value/tape}
[(weld accumulator value) (weld value "X")]
:- "Everything: twoonethree"
(from-list [[1 "oneX"] [2 "twoX"] [3 "threeX"] ~])
"map-fold"
::
++ test-map-keys
%^ expect-eq
%+ map-keys
three
|= a/@u
(add a 10)
(from-list [[11 "one"] [12 "two"] [13 "three"] ~])
"map-keys"
::
++ test-map-keys-with
%^ expect-eq
%^ map-keys-with
three
|=(a/@u 42)
weld
(from-list [[42 "twothreeone"] ~])
"map-keys-with"
::
++ test-fold
%^ expect-eq
%^ fold
three
"Everything: "
:: todo: this works but replacing with just ++weld causes an out of loom.
|= {accumulator/tape value/tape}
^- tape
(weld accumulator value)
"Everything: twoonethree"
"map-fold"
::
++ test-fold-with-keys
%^ expect-eq
%^ fold-with-keys
three
"Everything: "
|= {accumulator/tape key/@u value/tape}
^- tape
:(weld accumulator (scow %ud key) value)
"Everything: 2two1one3three"
"map-fold-with-keys"
::
++ test-elems
%^ expect-eq
(elems three)
["two" "three" "one" ~]
"elems"
::
++ test-keys
%^ expect-eq
(keys three)
[2 3 1 ~]
"keys"
::
++ test-keys-set
%^ expect-eq
(keys-set three)
(si:nl [2 3 1 ~])
"keys-set"
::
++ test-from-set
%^ expect-eq
%+ from-set
(si:nl [1 2 3 ~])
|= a/@u
(scow %ud a)
(from-list [[1 "1"] [2 "2"] [3 "3"] ~])
"from-set"
::
++ test-from-list-with
%^ expect-eq
%+ from-list-with
[[1 1] [2 1] [2 1] [3 3] ~]
add
(from-list [[1 1] [2 2] [3 3] ~])
"from-list-with"
::
++ test-filter
%^ expect-eq
%+ filter
(from-list [[1 1] [2 1] [3 2] [4 1] ~])
|=(a/@u !=(a 1))
(from-list [[1 1] [2 1] [4 1] ~])
"filter"
::
++ test-filter-with-key
%^ expect-eq
%+ filter-with-key
(from-list [[1 1] [2 1] [3 2] [4 1] ~])
|=({a/@u b/@u} =(a 2))
(from-list [[1 1] [3 2] [4 1] ~])
"filter-with-key"
::
++ test-restrict-keys
%^ expect-eq
%+ restrict-keys
(from-list [[1 1] [2 2] [3 3] [4 4] [5 5] ~])
(si:nl [1 3 5 ~])
(from-list [[1 1] [3 3] [5 5] ~])
"restrict-keys"
::
++ test-without-keys
%^ expect-eq
%+ without-keys
(from-list [[1 1] [2 2] [3 3] [4 4] [5 5] ~])
(si:nl [1 3 5 ~])
(from-list [[2 2] [4 4] ~])
"restrict-keys"
::
++ test-partition
%^ expect-eq
%+ partition
(from-list [[1 1] [2 2] [3 3] [4 4] [5 5] ~])
|=(a/@u |(=(a 1) =(a 3)))
:- (from-list [[1 1] [3 3] ~])
(from-list [[2 2] [4 4] [5 5] ~])
"partition"
::
++ test-map-maybe
%^ expect-eq
%+ map-maybe
(from-list [[1 1] [2 2] [3 3] [4 4] [5 5] ~])
|=(a/@u ?:(=(a 3) ~ `a))
(from-list [[1 1] [2 2] [4 4] [5 5] ~])
"map-maybe"
::
++ test-map-maybe-with-key
%^ expect-eq
%+ map-maybe-with-key
(from-list [[1 2] [2 3] [3 4] [4 5] [5 6] ~])
|=({k/@u v/@u} ?:(=(k 3) ~ `v))
(from-list [[1 2] [2 3] [4 5] [5 6] ~])
"map-maybe-with-key"
::
++ test-map-either
%^ expect-eq
%+ map-either
(from-list [[1 1] [2 2] [3 3] [4 4] [5 5] ~])
|= value/@u
?: =(0 (mod value 2))
[%& "even"]
[%| 1]
:- (from-list [[2 "even"] [4 "even"] ~])
(from-list [[1 1] [3 1] [5 1] ~])
"map-either"
::
++ test-map-either-with-key
%^ expect-eq
%+ map-either-with-key
(from-list [[1 1] [2 1] [3 1] [4 1] [5 1] ~])
|= {key/@u value/@u}
?: =(0 (mod key 2))
[%& "even"]
[%| 1]
:- (from-list [[2 "even"] [4 "even"] ~])
(from-list [[1 1] [3 1] [5 1] ~])
"map-either"
::
++ test-is-subdict
%^ expect-eq
%^ is-subdict-by
(from-list [[1 1] [4 4] ~])
(from-list [[1 1] [2 2] [3 3] [4 4] [5 5] ~])
|=({a/* b/*} =(a b))
%.y
"is-subdict"
::
++ test-valid
%^ expect-eq
(valid (from-list [[1 1] [2 2] [3 3] [4 4] [5 5] [6 6] [7 7] [8 8] [9 9] ~]))
%.y
"valid"
--

32
tests/new-hoon/myb.hoon Normal file
View File

@ -0,0 +1,32 @@
/+ new-hoon, tester
=, myb:new-hoon
|_ tester-type:tester
++ test-from-list-null
(expect-eq (from-list ~) ~ "from-list")
::
++ test-from-list-real
(expect-eq (from-list [5 ~]) [~ 5] "from-list")
::
++ test-to-list-null
(expect-eq (to-list ~) ~ "to-list")
::
++ test-to-list-real
(expect-eq (to-list [~ 5]) [5 ~] "to-list")
::
++ test-concat-null
(expect-eq (concat ~) ~ "concat")
::
++ test-concat-real
:: wait, if i pull the cast out from below, the concat implementation
:: doesn't compile anymore?
(expect-eq (concat `(list (maybe @ud))`[~ [~ 1] ~ [~ 2] ~]) [1 2 ~] "concat")
::
++ test-map
%^ expect-eq
%+ map:myb
[1 2 3 2 ~]
|=(a/@u ?:(=(2 a) [~ 2] ~))
[2 2 ~]
"map"
--

32
tests/new-hoon/thr.hoon Normal file
View File

@ -0,0 +1,32 @@
:: tests for the either core.
/+ new-hoon, tester
=, thr:new-hoon
=/ data/(list (either @u tape)) [[%& 1] [%| "one"] [%& 2] [%| "two"] ~]
|_ tester-type:tester
++ test-apply
%^ expect-eq
%^ apply
`(either @u tape)`[%| "one"]
|=(a/@u "left")
|=(b/tape "right")
"right"
"apply"
::
++ test-firsts
%^ expect-eq
(firsts data)
[1 2 ~]
"firsts"
::
++ test-seconds
%^ expect-eq
(seconds data)
["one" "two" ~]
"seconds"
::
++ test-partition
%^ expect-eq
(partition data)
[[1 2 ~] ["one" "two" ~]]
"partition"
--

52
web/testing.umd Normal file
View File

@ -0,0 +1,52 @@
:- ~[comments+&]
;>
# Writing Unit Tests
Urbit comes with a built in system for writing tests. Like hoon files with a
certain shape go in `%/app` or `%/gen` or `%/mar`, hoon files with a certain
shape can go in `%/tests` and then are exposed to a system wide test runner.
Say you put a test suite in `%/tests/new-hoon/thr.hoon`:
> +ls %/tests
new-hoon/
> +ls %/tests/new-hoon
ls/hoon mp/hoon myb/hoon thr/hoon
You can then just run that individual test suite (and not the ones that are beside it in the `%/tests/new-hoon` directory) with:
> +tests /new-hoon/thr
/new-hoon/thr/test-seconds OK
/new-hoon/thr/test-partition OK
/new-hoon/thr/test-firsts OK
/new-hoon/thr/test-apply OK
## The test file
So what is the structure of these test files? They contain a door, with arms starting with `++test-` or `++check-`. At minimum:
/+ tester
|_ tester-type:tester
++ test-some-test
(expect-eq 4 4 "trivial")
--
All of the utilities you need to write tests are in the tester library. Also, like other hoon files, you can stack cores for models and utility functions with only the final core being inspected for test arms.
## Some Details
So internally, how does this work?
The `+test` generator depends on each file/directory in `%/tests/` through a renderer. Each node in the filesystem tree is rendered by `%/ren/test-tree.hoon`, which calls itself recursively for subdirectories.
This means all compiling of test cases happens inside ford, which can cache work and not recompile tests whose dependencies haven't changed. At runtime, all the `+test` generator does is filter and execute tests from the tree.
I would like to get to a place where any direct scrying of the filesystem is discouraged, and almost everything flows through the functional reactive build system. This is what it is here for.
### Future distribution of hoon libraries
Implicit in having a standard way to write tests and a standard `+test` runner is the idea that all functionality on the current desk should be tested.
Let's say I'm shipping a program on Urbit and I use multiple third-party libraries. Each of those libraries should have their own test suites placed in `%/tests/`. When I `|merge` their desks into my application desk, having a standard test runner means that all their tests and all my application tests get run. If you're depending on a library, you want to make sure that the tests for your dependencies run when you test your application.