I have defined a simple function to test show methods. It works like this:
julia> struct A
x::Int
y::Float64
p::String
v::Vector{Float64}
end
julia> function Base.show(io::IO, ::MIME"text/plain", a::A)
print(io,"""
My object of type A has x$(a.x) int,
and a path $(a.p) and also a $(a.y) float, an $(a.v) vector.
""")
end
julia> a
My object of type A has x1 int,
and a path /home/user/path.pdb and also a 2.0 float, an [1.0, 2.0, 3.141592653589793] vector.
julia> test_show(a,
"""
My object of type A has x1 int,
and a path /home/user/path.pdb and also a 2.0 float, an [1.0, 2.0, 3.141592653589793] vector.
"""
)
true
The precision to which the floats and ints are compared can be set with keyword arguments (and digits are separated from non-digits characters, except dots, to isolate numbers), the paths are tested only for their last field (to avoid test failures in different machines/CI), and arrays are simplified in such a way that only the first an element are compared, avoiding test failures associated to the number of array elements printed).
I wonder if something like this, but more carefully implemented, would be useful in general?
test_show.jl
function test_show(
x, s::String;
f64 = (x1,x2) -> isapprox(x1,x2,rtol=1e-3),
i64 = (x1,x2) -> x1 == x2,
vector_simplify = true,
repl = Dict(),
)
match(f,x1,x2) = begin
if !f(x1,x2)
println("show method test failed with $x1 ($(typeof(x1))) == $x2 ($(typeof(x2)))")
return false
end
return true
end
buff = IOBuffer()
show(buff, MIME"text/plain"(), x)
ss = String(take!(buff))
# Custom substitutions
s = replace(s, repl...)
ss = replace(ss, repl...)
# add spaces between digits and other characters (except dots), to interpret them as numbers
s = replace(s, r"(?<=\d)(?=[^\d.])|(?<=[^\d.])(?=\d)" => s" ")
ss = replace(ss, r"(?<=\d)(?=[^\d.])|(?<=[^\d.])(?=\d)" => s" ")
if vector_simplify # keep only first and last array elements
s = replace(s, r"\[ (\S+).* (\S+)\ ]" => s"[ \1 \2 ]")
ss = replace(ss, r"\[ (\S+).* (\S+)\ ]" => s"[ \1 \2 ]")
end
sfields = split(s)
ssfields = split(ss)
all_match = true
for (f1, f2) in zip(sfields, ssfields)
!all_match && break
if ispath(f2) # only compares the last entry for paths
all_match = last(split(f1)) == last(split(f2))
continue
end
value = tryparse(Int, f1) # test if f1 can be interpreted as an integer
if !isnothing(value)
all_match = match(i64, value, tryparse(Int, f2))
continue
end
value = tryparse(Float64, f1) # test if f1 can be interpreted as a float
if !isnothing(value)
all_match = match(f64, value, tryparse(Float64,f2))
continue
end
all_match = match(isequal, f1, f2)
end
return all_match
end