on 175c53c help sign in
urbit.org/tutorials/hoon/test-sets.md
193 lines 8.8 kB history raw
1+++
2title = "2.8.1 Unit Testing with Ford"
3weight = 39
4template = "doc.html"
5+++
7# Unit Testing with Ford
9In this walkthrough we will show how to use Ford as a testing suite.
11First we will cover the essential molds, arms, and generators used for testing, followed by walking through the code that implements a series of tests for standard library sets, as described [here](@/docs/tutorials/hoon/trees-sets-and-maps.md).
13## Running our first test
15To start with, lets try running the unit tests on sets by entering the following into the Dojo, and then we will work backwards to see how we obtained this output:
16```
17> +test /sys/hoon/set
18OK /sys/hoon/set/test-set-all
19OK /sys/hoon/set/test-set-any
20OK /sys/hoon/set/test-set-apt
21OK /sys/hoon/set/test-set-bif
22OK /sys/hoon/set/test-set-del
23OK /sys/hoon/set/test-set-dif
24OK /sys/hoon/set/test-set-dig
25OK /sys/hoon/set/test-set-gas
26OK /sys/hoon/set/test-set-has
27OK /sys/hoon/set/test-set-int
28OK /sys/hoon/set/test-set-put
29OK /sys/hoon/set/test-set-rep
30OK /sys/hoon/set/test-set-run
31OK /sys/hoon/set/test-set-tap
32OK /sys/hoon/set/test-set-uni
33OK /sys/hoon/set/test-set-wyt
34```
35Here, we ran a generator called `+test` with a path argument `/sys/hoon/set`. The output tells us that all tests ran successfully. Running this generator automatically appends `/tests/` to the front of the path entered, so lets look at the beginning of `/tests/sys/hoon/set.hoon` to see what the `+test` generator took as an input:
37```hoon
38:: Tests for +in (set logic)
39::
40/+ *test
41::
42:: Testing arms
43::
44|%
45```
46We note that the first line that isn't a comment is `/+ *test`, which from [the previous lesson](@/docs/tutorials/hoon/ford.md) you know means that we are importing the shared library `lib/test.hoon`. This is followed by a core which will contain arms that perform our tests, which we will see an example of shortly.
48In `lib/test.hoon`, we find a core with a few gates: `expect-eq`, `expect-fail`, and `category`. The source for `expect-eq` is:
49```hoon
50++ expect-eq
51 |= [expected=vase actual=vase]
52 ^- tang
53 ::
54 =| result=tang
55 ::
56 =? result !=(q.expected q.actual)
57 %+ weld result
58 ^- tang
59 :~ [%palm [": " ~ ~ ~] [leaf+"expected" (sell expected) ~]]
60 [%palm [": " ~ ~ ~] [leaf+"actual" (sell actual) ~]]
61 ==
62 ::
63 =? result !(~(nest ut p.actual) | p.expected)
64 %+ weld result
65 ^- tang
66 :~ :+ %palm [": " ~ ~ ~]
67 :~ [%leaf "failed to nest"]
68 (~(dunk ut p.actual) %actual)
69 (~(dunk ut p.expected) %expected)
70 == ==
71 result
72```
73This is the most frequently used gate in testing, so we may conclude that any testing suite will likely have the line `/+ *test` at the top so that we may utilize this gate. What does this gate do? Recall that a `vase` consists of `[p=type q=*]`. Then what `expect-eq` does is check to see if two `vase`s are equal, and pretty-prints the result of that test.
75## Testing the functionality of set intersection
77Here's one of the testing arms in the set testing core:
78```hoon
79++ test-set-int ^- tang
80 =/ s-nul=(set @) *(set @)
81 =/ s-asc=(set @) (sy (gulf 1 7))
82 =/ s-des=(set @) (sy (flop (gulf 1 7)))
83 =/ s-dos=(set @) (sy (gulf 8 9))
84 =/ s-dup (sy ~[1 1 4 1 3 5 9 4])
85 ;: weld
86 :: Checks with empty set
87 ::
88 %+ expect-eq
89 !> ~
90 !> (~(int in s-nul) s-asc)
91 %+ expect-eq
92 !> ~
93 !> (~(int in s-asc) s-nul)
94 :: Checks with all elements different
95 ::
96 %+ expect-eq
97 !> ~
98 !> (~(int in s-dos) s-asc)
99 :: Checks success (total intersection)
100 ::
101 %+ expect-eq
102 !> s-asc
103 !> (~(int in s-asc) s-des)
104 :: Checks success (partial intersection)
105 ::
106 %+ expect-eq
107 !> (sy ~[9])
108 !> (~(int in s-dos) s-dup)
109 ==
110```
111This arm is used to test whether the set intersection functionality works as expected. Let's walk through it line by line.
112```hoon
113++ test-set-int ^- tang
114 =/ s-nul=(set @) *(set @)
115 =/ s-asc=(set @) (sy (gulf 1 7))
116 =/ s-des=(set @) (sy (flop (gulf 1 7)))
117 =/ s-dos=(set @) (sy (gulf 8 9))
118 =/ s-dup (sy ~[1 1 4 1 3 5 9 4])
119```
120We see that this arm returns a `tang`, i.e. a pretty-printed message, and adds several sets to the subject which will be utilized in our tests.
121```hoon
122;: weld
123```
124`;: weld` calls `weld`, which is typically a binary function, as an n-ary function. This will allow us to concatenate all of `tang`s that are to be generated into a single `tang`.
125```hoon
126 :: Checks with empty set
127 ::
128 %+ expect-eq
129 !> ~
130 !> (~(int in s-nul) s-asc)
131```
132`%+` is used to call a gate with a cell sample. `expect-eq` is the gate, `!> ~` is the head of the sample, and `!> (~(int in s-nul) s-asc)` is the tail of the sample. `!>` is a rune used to wrap a noun in its type - in other words, it produces a `vase`. We've seen this rune before when we learned about the type spear `-:!>`. Thus, what this block of code is doing is checking whether the expected `vase` that is the product of `!> ~` is equal to the `vase` that is the product of `!> (~(int in s-nul) s-asc)`. `s-nul` is simply the empty set, and here we are taking the intersection of the empty set with `s-asc`, a non-empty set. This ought to produce the empty set of course, and so when this block of code runs, we compare the expected result `!> ~` (which is something we determined by hand) with the actual result `!> (~(int in s-nul) s-asc)`.
133```hoon
134 %+ expect-eq
135 !> ~
136 !> (~(int in s-asc) s-nul)
137```
138This is another test to check if intersection works with the empty set, but we have swapped where `s-asc` and `s-nul` are. We do not know a priori whether this ordering matters (and of course it shouldn't), which is why it is important to perform both tests.
141```hoon
142 :: Checks with all elements different
143 ::
144 %+ expect-eq
145 !> ~
146 !> (~(int in s-dos) s-asc)
147```
148Now we are checking if intersection works correctly when two non-empty sets share no common elements. We expect the intersection to be empty, so our expected `vase` is `!> ~`, and we then compare it with what the set library produces when it takes the intersection of `s-dos` and `s-asc`.
150```hoon
151 :: Checks success (total intersection)
152 ::
153 %+ expect-eq
154 !> s-asc
155 !> (~(int in s-asc) s-des)
156```
157`s-asc` and `s-des` both ought to be the set containing the integers 1 through 7, but `s-asc` was generated with the list `~[1,2,3,4,5,6,7]` while `s-des` was generated with the list `~[7,6,5,4,3,2,1]`. Sets have no ordering, so they ought to be the same. Thus, when we take their intersection, we should get the same set back. So, our expected `vase` is `!> s-asc` and our actual `vase` is `!> (~(int in s-asc) s-des)`.
159```hoon
160 :: Checks success (partial intersection)
161 ::
162 %+ expect-eq
163 !> (sy ~[9])
164 !> (~(int in s-dos) s-dup)
165```
166Our last test in this arm is to check whether partial intersection works. Here, we compute the intersection of `s-dos` and `s-dup`. Our expectation is that this should return the set whose only element is 9, and so our expected vase is `!> (sy ~[9])`. Then our actual vase is given by actually computing this intersection: `!> (~(int in s-dos) s-dup)`.
168We invite you to look further into the source of `/tests/sys/hoon/set.hoon` to see how other tests are written.
170## Running many tests
172When we ran the command `+test /sys/hoon/set` in the Dojo, what ultimately happened is that Ford looked inside of `/tests/sys/hoon/set.hoon` and found each of the tests in there and performed them in sequence. However, it is not necessary to put all tests you would like to run in a single source file, or even in a single folder. What `+test` actually does is take in a path and then performs all tests located in that folder (if it is a folder) and then looks for additional folders inside of the given path to find additional tests.
174In other words, if you want to run all of the tests located in `/tests/sys/hoon/`, you would run `+test /sys/hoon`:
176```
177> +test /sys/hoon
178OK /sys/hoon/auras/test-parse-p
179OK /sys/hoon/auras/test-parse-q
180OK /sys/hoon/auras/test-render-p
181OK /sys/hoon/auras/test-render-q
182OK /sys/hoon/bits/test-bits
183OK /sys/hoon/differ/test-berk
184OK /sys/hoon/differ/test-loss
185OK /sys/hoon/differ/test-lurk
186OK /sys/hoon/differ/test-lusk
187OK /sys/hoon/hashes/test-mug
188OK /sys/hoon/hashes/test-muk
189OK /sys/hoon/map/test-map-all
190OK /sys/hoon/map/test-map-any
191...
192```
193We have truncated the results here, but you can already see that this command is performing tests found in subfolders of `/tests/sys/hoon`. This command runs quicky, but if you ran e.g. `+test /sys` you may be waiting quite awhile.