-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtest.t
209 lines (166 loc) · 5.59 KB
/
test.t
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
Set up some helpers that we'll use.
$ function runtest {
> # souffle's output is a little noisy; do some light post-processing
> # to make the tests easier to read
> souffle "$TESTDIR"/mixologician.dl
> if [[ -s mixable ]]; then
> echo 'mixable:' $(sort mixable)
> fi
> if [[ -s shopping-list ]]; then
> echo "shopping list:"
> sort shopping-list
> fi
> }
$ function empty_bar { cat /dev/null > bar; }
$ function empty_recipe_book { cat /dev/null > recipes; }
$ function buy {
> for ingredient in "$@"; do
> echo "$ingredient" >> bar;
> done
> }
$ function sell {
> # this function is... very fragile.
> # it will break if an ingredient contains a single quote.
> filter="grep -vF "
> for ingredient in "$@"; do
> filter=$filter"-e '$ingredient'"
> done
> # the eval is necessary to interpret the quotes correctly... look
> # no one is more upset about this than i am
> eval "$filter" bar > new_bar
> mv new_bar bar
> }
$ function set_bar { empty_bar; buy "$@"; }
$ function add_recipe {
> drink=$1
> shift
> for ingredient in "$@"; do
> echo "$drink <- $ingredient" >> recipes
> done
> }
Now let's create some empty files that need to exist but that aren't relevant
to us.
$ cat /dev/null > auto-begets
$ cat /dev/null > auto-unbuyable
And set up some basic rules:
$ echo "lime zest" > unbuyable
$ echo "lime -> lime juice" > begets
$ echo "lime -> lime zest" >> begets
$ echo "reposado tequila -> tequila" >> begets
$ echo "blanco tequila -> tequila" >> begets
$ echo "lime cordial, sugar, lime zest" > combinations
Let's start with the simplest thing we can: nothing in our bar, one trivial
recipe in our book.
$ empty_bar
$ add_recipe "shot of vodka" vodka
$ runtest
shopping list:
vodka -> shot of vodka
Okay! Nothing is mixable, but we learn that if we buy vodka we can mix up a
delicious shot:
$ buy vodka
$ runtest
mixable: shot of vodka
Great. Now let's try a real cocktail:
$ empty_recipe_book
$ add_recipe "vodka martini" vodka "dry vermouth"
$ runtest
shopping list:
dry vermouth -> vodka martini
Okay. Since we already have vodka, all we need is dry vermouth. Let's buy it:
$ buy "dry vermouth"
$ runtest
mixable: vodka martini
Now we can mix a martini, and there are no further things that we should buy.
Now let's make sure that "subtyping" works.
$ empty_bar
$ add_recipe margarita tequila "lime juice" "orange liqueur"
$ buy "lime juice"
$ buy "orange liqueur"
$ runtest
shopping list:
blanco tequila -> margarita
reposado tequila -> margarita
tequila -> margarita
Great. We can't make a margarita yet, but if we buy either generic or specific
tequila, we'll be able to.
$ buy "reposado tequila"
$ runtest
mixable: margarita
Since we bought reposado tequila, there's no longer any reason to buy blanco
tequila (according to our recipe book), so it goes away.
Now let's test multi-ingredient syrups.
$ empty_bar
$ add_recipe gimlet gin "lime juice" "lime cordial"
$ buy gin
$ buy "lime juice"
In order to make lime cordial, I need both limes and sugar. My lime *juice*
won't do, because I need the zest. That's two new ingredients, so neither will
show up on my shopping list.
$ runtest
shopping list:
lime cordial -> gimlet
But it does tell me that I can just buy lime cordial directly (if, you know, I
could find it in a store).
But once I buy sugar...
$ buy sugar
$ runtest
shopping list:
lime -> gimlet
lime cordial -> gimlet
It lets me know that I could either buy lime cordial directly, or I could just
buy limes.
In fact, now that I have sugar, I can get rid of my lime juice, because all I'll
need to make a gimlet is lime.
$ sell "lime juice"
$ runtest
shopping list:
lime -> gimlet
Nice. Notice that I can no longer just buy lime cordial, as that won't be
sufficient: I can't make lime juice out of lime cordial.
Now I suspect that there's a bug in my current implementation, so let's make a
silly-looking test where a recipe uses both an ingredient and a potential output
of that ingredient:
$ empty_bar
$ add_recipe "sour limes" "lime" "lime juice"
$ runtest
shopping list:
lime -> sour limes
And this did detect the bug! But now I've fixed it, and everything is fine.
But I think I might have another bug. Does the Has relation work through
arbitrarily long Begets production chains?
$ empty_bar
$ empty_recipe_book
$ echo 'really really specific scotch -> really specific scotch' >>begets
$ echo 'really specific scotch -> specific scotch' >>begets
$ echo 'specific scotch -> scotch' >>begets
$ add_recipe "shot of scotch" scotch
$ runtest
shopping list:
really really specific scotch -> shot of scotch
really specific scotch -> shot of scotch
scotch -> shot of scotch
specific scotch -> shot of scotch
It didn't, the first time I tried this. But now I've fixed it.
Now let's make sure that the Unbuyable relation is filtering things out
correctly.
$ empty_recipe_book
$ add_recipe gimlet gin "lime juice" "lime cordial"
$ buy "lime" "gin"
$ runtest
shopping list:
lime cordial -> gimlet
sugar -> gimlet
Okay, but I can't find lime cordial in a store, so that line is kind of just
noise to me.
$ echo "lime cordial" >> unbuyable
$ runtest
shopping list:
sugar -> gimlet
There we go. But just because I can't usually buy it doesn't mean I can't use
it to mix the drink, if I happen to have some on hand:
$ buy "lime cordial"
$ runtest
mixable: gimlet
So we can see that the "Unbuyable" relation only affects output of the
"Enables" relation.