-
Notifications
You must be signed in to change notification settings - Fork 9
/
runtests.jl
345 lines (284 loc) · 8.51 KB
/
runtests.jl
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
using Test, SumTypes
@sum_type Foo begin
Bar(::Int)
Baz(x)
end
@sum_type Either{A, B} begin
Left{A}(::A)
Right{B}(::B)
end
@sum_type Result{T <: Union{Number, Uninit}} begin
Failure
Success{T}(::T)
end
function log_nothrow(x::T)::Result{T} where{T<:AbstractFloat}
if x < zero(x)
return Failure
end
Success(log(x))
end
Base.getproperty(f::Foo, s::Symbol) = error("Don't do that!")
Base.getproperty(f::Either, s::Symbol) = error("Don't do that!")
Base.getproperty(f::Result, s::Symbol) = error("Don't do that!")
#-------------------
@testset "Basics " begin
@test SumTypes.is_sumtype(Int) == false
@test Bar(1) isa Foo
@test_throws MethodError Foo(1)
function either_test(x::Either)
let x::Either{Int, Int} = x
@cases x begin
Left(l) => l + 1
Right(r) => r - 1
end
end
end
@test either_test(Left(1)) == 2
@test either_test(Right(1)) == 0
function either_test_incomplete(x::Either)
let x::Either{Int, Int} = x
@cases x begin
Left(l) => l + 1
end
end
end
@test_throws ErrorException either_test_incomplete(Left(1))
function either_test_overcomplete(x::Either)
let x::Either{Int, Int} = x
@cases x begin
Left(l) => l + 1
Right(r) => r - 1
Some_Bullshit => Inf
end
end
end
@test_throws ErrorException either_test_overcomplete(Left(1))
@test log_nothrow(1.0) == Success(0.0)
@test log_nothrow(-1.0) == Failure
@test_throws Exception macroexpand(@__MODULE__(),
:(@cases x begin
Left{Int}(x) => x
Right(x) => x
end))
@test_throws Exception macroexpand(@__MODULE__(),
:(@cases x begin
[Left, (Right + 1)](x) => x
end))
@test_throws Exception SumTypes._sum_type(
:Blah, :(begin
duplicate_field
duplicate_field
end))
@test_throws Exception SumTypes._sum_type(
:Blah, :some_option, :(begin
duplicate_field
end))
@test_throws Exception SumTypes._sum_type(
:Blah, :(begin
x * field^2 -1
end))
@test_throws Exception SumTypes._sum_type(
:(Blah{T}), :(begin
foo{U}(::U)
end ))
let x = Left([1]), y = Left([1.0]), z = Right([1])
@test x == y
@test x != z
end
@test SumTypes.get_tag(Left([1])) == :Left
@test convert(Either{Int, Int}, Left(1)) == Left(1)
@test convert(Either{Int, Int}, Left(1)) !== Left(1)
@test convert(Either{Int, Int}, Left(1)) === Either{Int, Int}'.Left(1)
@test Either{Int, Int}(Left(1)) isa Either{Int, Int}
@test_throws MethodError Left{Int}("hi")
@test_throws MethodError Right{String}(1)
@test Left{Int}(0x01) === Left{Int}(1)
let x = Left(1.0)
@test SumTypes.isvariant(x, :Left) == true
@test SumTypes.isvariant(x, :Right) == false
@test SumTypes.unwrap(x)[1] == 1.0
end
end
#--------------------------------------------------------
@sum_type List{A} begin
Nil
Cons{A}(::A, ::List)
end
Cons(x::A, y::List{Uninit}) where {A} = Cons(x, List{A}(y))
Base.getproperty(f::List, s::Symbol) = error("Don't do that!")
List(first, rest...) = Cons(first, List(rest...))
List() = Nil
function Base.Tuple(l::List)
@cases l begin
Nil => ()
Cons(a, b) => (a, Tuple(b)...)
end
end
Base.length(l::List) = @cases l begin
Nil => 0
Cons(_, l) => 1 + length(l)
end
function Base.collect(l::List{T}) where {T}
v = Vector{T}(undef, length(l))
for i ∈ eachindex(v)
l::List{T} = @cases l begin
Nil => error()
Cons(a, rest) => begin
v[i] = a
rest
end
end
end
end
function collect_to!(v, l)
@cases l begin
Nil => v
Cons(a, b) => (push!(v, a); collect_to!(v, b))
end
end
function Base.show(io::IO, l::List)
print(io, "List", Tuple(l))
end
@testset "Recursive Sum Types" begin
@test Nil isa List{Uninit}
@test Cons(1, Cons(1, Nil)) isa List{Int}
@test Tuple(List(1, 2, 3, 4, 5)) == (1, 2, 3, 4, 5)
end
#--------------------------------------------------------
@sum_type AT begin
A(common_field::Int, a::Bool, b::Int)
B(common_field::Int, a::Int, b::Float64, d::Complex{Float64})
C(common_field::Int, b::Float64, d::Bool, e::Float64, k::Complex{Float64})
D(common_field::Int, b::Char)
end
Base.getproperty(f::AT, s::Symbol) = error("Don't do that!")
A(;common=1, a=true, b=10) = A(common, a, b)
B(;common=1, a=1, b=1.0, d=1 + 1.0im) = B(common, a, b, d)
C(;common=1, b=2.0, d=false, e=3.0, k=1 + 2im) = C(common, b, d, e, k)
D(;common=1, b='h') = D(common, b)
foo!(xs) = for i in eachindex(xs)
xs[i] = @cases xs[i] begin
A => B()
B => C()
C => D()
D => A()
end
end
#CI Doesn't like this test so just disable it in CI
if !haskey(ENV, "CI") || ENV["CI"] != "true"
@testset "Allocation-free @cases" begin
xs = map(x->rand((A(), B(), C(), D())), 1:10000);
foo!(xs)
@test @allocated(foo!(xs)) == 0
end
end
#--------------------------------------------------------
@sum_type Hider{T} :hidden begin
A
B{T}(::T)
end
@sum_type Hider2 :hidden begin
A
B
end
@testset "hidden variants" begin
@test Hider{Int}'.A isa Hider{Int}
@test Hider'.A isa Hider{SumTypes.Uninit}
@test Hider'.A != A
@test Hider'.B != B
@test 1 == @cases Hider'.A begin
A() => 1
B(a) => a
end
@test 2 == @cases Hider'.B(2) begin
A() => 1
B(a) => a
end
@test Hider2'.A isa Hider2
@test Hider2'.B isa Hider2
@test Hider2'.A != A
@test Hider2'.B != B
@test 1 == @cases Hider2'.A begin
A => 1
B(a) => a
end
@test 2 == @cases Hider2'.B begin
A => 1
B => 2
end
end
@sum_type Either2{A, B} :hidden begin
Left{A}(::A)
Right{B}(::B)
end
SumTypes.show_sumtype(io::IO, x::Either2) = @cases x begin
Left(a) => print(io, "L($a)")
Right(a) => print(io, "R($a)")
end
SumTypes.show_sumtype(io::IO, ::MIME"text/plain", x::Either2) = @cases x begin
Left(a) => print(io, "The Leftestmost Value: $a")
Right(a) => print(io, "The Rightestmost Value: $a")
end
@sum_type Fruit begin
apple
banana
orange
end
@testset "printing " begin
@test repr(Left(1)) ∈ ("Left(1)::Either{Int64, Uninit}", "Left(1)::Either{Int64,Uninit}")
@test repr("text/plain", Right(3)) ∈ ("Right(3)::Either{Uninit, Int64}", "Right(3)::Either{Uninit,Int64}")
let Left = Either2'.Left, Right = Either2'.Right
@test repr("text/plain", Left(1)) == "The Leftestmost Value: 1"
@test repr(Right(3)) == "R(3)"
end
@test repr(apple) == "apple::Fruit"
@test repr(Either{Int, Int}'.Left) ∈ ("Either{Int64, Int64}'.Left{Int64}", "Either{Int64,Int64}'.Left{Int64}")
end
#---------------
# https://github.com/MasonProtter/SumTypes.jl/issues/38
struct Singleton end
@testset "Constrained type parameters" begin
@sum_type FooWrapper{T<:Singleton} begin
FooWrapper1{T}(::T)
FooWrapper2{T}(::T)
FooWrapper3{T}(::T)
end
@test FooWrapper2(Singleton()) isa FooWrapper{Singleton}
end
#---------------
module CollectionOfVaraints
using SumTypes, Test
@sum_type Foo begin
A(::Int, ::Int)
B(::Float64, ::Float64)
C(::String)
D(::Pair{Symbol, Int})
end
foo(x::Foo) = @cases x begin
[A, B](x, y) => x + y
C(s) => parse(Int, s)
D((_, x)) => x
end
@sum_type Re begin
Empty
Class(::UInt8)
Rep(::Re)
Alt(::Re, ::Re)
Cat(::Re, ::Re)
Diff(::Re, ::Re)
And(::Re, ::Re)
end;
count_classes(r::Re, c=0) = @cases r begin
Empty => c
Class => c + 1
Rep(x) => c + count_classes(x)
[Alt, Cat, Diff, And](x, y) => c + count_classes(x) + count_classes(y)
end;
@testset "Collection of variants" begin
@test foo(A(1, 1)) == 2
@test foo(B(1, 1.5)) == 2.5
@test foo(C("3")) == 3
@test foo(D(:a => 4)) == 4
@test count_classes(And(Alt(Rep(Class(0x1)), And(Class(0x1), Empty)), Class(0x0))) == 3
end
end