Skip to content

Commit 0ee8069

Browse files
committed
Add inital tests based on OurRadonReco example
1 parent 14c4574 commit 0ee8069

File tree

10 files changed

+292
-4
lines changed

10 files changed

+292
-4
lines changed

Project.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "AbstractImageReconstruction"
22
uuid = "a4b4fdbf-6459-4ec9-990d-77e1fa24a91b"
33
authors = ["nHackel <[email protected]> and contributors"]
4-
version = "0.3.6"
4+
version = "0.4.0"
55

66
[deps]
77
LRUCache = "8ac3fa9e-de4c-5943-b1dc-09c6b5f20637"
@@ -20,6 +20,13 @@ julia = "1.9"
2020

2121
[extras]
2222
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
23+
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
24+
ImageGeoms = "9ee76f2b-840d-4475-b6d6-e485c9297852"
25+
ImagePhantoms = "71a99df6-f52c-4da1-bd2a-69d6f37f3252"
26+
RadonKA = "86de8297-835b-47df-b249-c04e8db91db5"
27+
RegularizedLeastSquares = "1e9c538a-f78c-5de5-8ffb-0b6dbe892d23"
28+
LinearOperatorCollection = "a4a2c56f-fead-462a-a3ab-85921a5f2575"
29+
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
2330

2431
[targets]
25-
test = ["Test"]
32+
test = ["Test", "RadonKA", "ImagePhantoms", "ImageGeoms", "CairoMakie", "LinearOperatorCollection", "RegularizedLeastSquares", "Statistics"]

src/RecoPlans/Listeners.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ Observables.on(f, plan::RecoPlan, property::Symbol; kwargs...) = on(f, plan[prop
2020
2121
Remove `f` from the listeners of `property` of `plan`.
2222
"""
23-
Observables.off(plan::RecoPlan, property::Symbol, f) = off(f, plan[property])
23+
Observables.off(plan::RecoPlan, property::Symbol, f) = off(plan[property], f)
2424

2525
include("LinkedPropertyListener.jl")

src/RecoPlans/RecoPlans.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,21 @@ function setAll!(plan::RecoPlan{T}, name::Symbol, x) where {T<:AbstractImageReco
139139
end
140140
setAll!(plans::AbstractArray{<:AbstractRecoPlan}, name::Symbol, x) = foreach(p -> setAll!(p, name, x), plans)
141141
setAll!(plan::RecoPlan{<:AbstractImageReconstructionAlgorithm}, name::Symbol, x) = setAll!(plan.parameter, name, x)
142+
"""
143+
setAll!(plan::AbstractRecoPlan; kwargs...)
144+
145+
Call `setAll!` with each given keyword argument.
146+
"""
142147
function setAll!(plan::AbstractRecoPlan; kwargs...)
143148
for key in keys(kwargs)
144149
setAll!(plan, key, kwargs[key])
145150
end
146151
end
152+
"""
153+
setAll!(plan::AbstractRecoPlan, dict::Union{Dict{Symbol, Any}, Dict{String, Any}})
154+
155+
Call `setAll!` with each entries of the dict.
156+
"""
147157
setAll!(plan::AbstractRecoPlan, dict::Dict{Symbol, Any}) = setAll!(plan; dict...)
148158
setAll!(plan::AbstractRecoPlan, dict::Dict{String, Any}) = setAll!(plan, Dict{Symbol, Any}(Symbol(k) => v for (k,v) in dict))
149159

test/algorithm_api.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
@testset "API" begin
2+
# Partially testing if radon example is constructed correctly
3+
# These things are mostly covered by Literate.jl examples
4+
pre = RadonPreprocessingParameters(frames = collect(1:3))
5+
back_reco = RadonBackprojectionParameters(;angles)
6+
algo = DirectRadonAlgorithm(DirectRadonParameters(pre, back_reco))
7+
8+
@test parameter(algo) isa DirectRadonParameters
9+
10+
# High-level reco
11+
@test isready(algo) == false
12+
reco_1 = reconstruct(algo, sinograms)
13+
@test isready(algo) == false
14+
15+
# Put!/take!
16+
put!(algo, sinograms)
17+
@test isready(algo)
18+
reco_2 = take!(algo)
19+
@test isapprox(reco_1, reco_2)
20+
end

test/caching.jl

Whitespace-only changes.

test/linkedproperty.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
export foo
2+
foo(val) = length(val) % 2 == 0 ? 15 : 10
3+
4+
@testset "LinkedProperty" begin
5+
6+
@testset "Observable" begin
7+
plan = RecoPlan(IterativeRadonAlgorithm; parameter = RecoPlan(IterativeRadonParameters;
8+
pre = RecoPlan(RadonPreprocessingParameters),
9+
reco = RecoPlan(IterativeRadonReconstructionParameters)))
10+
11+
12+
avg_obs = plan.parameter.pre[:numAverages]
13+
frame_obs = plan.parameter.pre[:frames]
14+
@test isempty(avg_obs.listeners)
15+
@test isempty(frame_obs.listeners)
16+
17+
# Connect frames -> averages
18+
list = LinkedPropertyListener((val) -> length(val), plan.parameter.pre, :numAverages, plan.parameter.pre, :frames)
19+
@test !isempty(avg_obs.listeners)
20+
@test !isempty(frame_obs.listeners)
21+
22+
# Call function
23+
@test ismissing(plan.parameter.pre.numAverages)
24+
plan.parameter.pre.frames = collect(1:5)
25+
@test plan.parameter.pre.numAverages == length(plan.parameter.pre.frames)
26+
27+
# Deactivate when user supplies parameter
28+
plan.parameter.pre.numAverages = 50
29+
plan.parameter.pre.frames = collect(1:1)
30+
@test plan.parameter.pre.numAverages == 50
31+
end
32+
33+
@testset "Serialization" begin
34+
plan = RecoPlan(IterativeRadonAlgorithm; parameter = RecoPlan(IterativeRadonParameters;
35+
pre = RecoPlan(RadonPreprocessingParameters),
36+
reco = RecoPlan(IterativeRadonReconstructionParameters)))
37+
38+
# Connect across parameters
39+
list = LinkedPropertyListener(foo, plan.parameter.reco, :iterations, plan.parameter.pre, :frames)
40+
io = IOBuffer()
41+
toTOML(io, plan)
42+
43+
seekstart(io)
44+
plan_copy = loadPlan(io, [Main, AbstractImageReconstruction, OurRadonReco])
45+
46+
# Call function
47+
@test ismissing(plan.parameter.pre.numAverages)
48+
49+
plan_copy.parameter.pre.frames = collect(1:5)
50+
@test plan_copy.parameter.reco.iterations == foo(plan_copy.parameter.pre.frames)
51+
52+
# Deactivate when user supplies parameter
53+
plan_copy.parameter.reco.iterations = -1
54+
plan_copy.parameter.pre.frames = collect(1:2)
55+
@test plan_copy.parameter.reco.iterations == -1
56+
end
57+
end

test/reco_plan.jl

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
@testset "RecoPlan" begin
2+
pre = RadonPreprocessingParameters(frames = collect(1:3))
3+
reco = IterativeRadonReconstructionParameters(; shape = size(images)[1:3], angles = angles, iterations = 1, reg = [L2Regularization(0.001), PositiveRegularization()], solver = CGNR);
4+
algo = IterativeRadonAlgorithm(IterativeRadonParameters(pre, reco))
5+
6+
7+
@testset "Construction" begin
8+
# From algorithm
9+
plan_fromAlgo = toPlan(algo)
10+
11+
# With kwarg constructor
12+
plan_fromKwargs = RecoPlan(IterativeRadonAlgorithm; parameter = RecoPlan(IterativeRadonParameters; pre = RecoPlan(RadonPreprocessingParameters; frames = collect(1:3)),
13+
reco = RecoPlan(IterativeRadonReconstructionParameters; shape = size(images)[1:3], angles = angles, iterations = 1, reg = [L2Regularization(0.001), PositiveRegularization()], solver = CGNR)))
14+
15+
# Individually with setproperty!
16+
plan_pre = RecoPlan(RadonPreprocessingParameters)
17+
plan_pre.frames = collect(1:3)
18+
@test build(plan_pre).frames == collect(1:3)
19+
@test build(plan_pre) isa RadonPreprocessingParameters
20+
21+
plan_reco = RecoPlan(IterativeRadonReconstructionParameters)
22+
plan_reco.shape = size(images)[1:3]
23+
plan_reco.angles = angles
24+
plan_reco.iterations = 1
25+
plan_reco.reg = [L2Regularization(0.001), PositiveRegularization()]
26+
plan_reco.solver = CGNR
27+
@test build(plan_reco).solver == plan_reco.solver
28+
@test build(plan_reco) isa IterativeRadonReconstructionParameters
29+
30+
plan_params = RecoPlan(IterativeRadonParameters)
31+
plan_params.pre = plan_pre
32+
plan_params.reco = plan_reco
33+
34+
plan_set = RecoPlan(IterativeRadonAlgorithm)
35+
plan_set.parameter = plan_params
36+
37+
algo_1 = build(plan_fromAlgo)
38+
algo_2 = build(plan_fromKwargs)
39+
algo_3 = build(plan_set)
40+
# Not the best, but the types dont define proper equals, so we use our default hash method
41+
@test hash(algo_1.parameter.pre) == hash(algo_2.parameter.pre)
42+
@test hash(algo_2.parameter.pre) == hash(algo_3.parameter.pre)
43+
@test hash(algo_1.parameter.reco) == hash(algo_2.parameter.reco)
44+
@test hash(algo_2.parameter.reco) == hash(algo_3.parameter.reco)
45+
end
46+
47+
@testset "Properties" begin
48+
# Test parameter with union property type
49+
plan = RecoPlan(RadonFilteredBackprojectionParameters)
50+
instance = nothing
51+
52+
@testset "Setter/Getter" begin
53+
# Init missing
54+
@test ismissing(plan.angles)
55+
@test ismissing(plan.filter)
56+
57+
# Set/get
58+
plan.angles = angles
59+
@test plan.angles == angles
60+
@test_throws Exception plan.doesntExist = 42
61+
62+
# Type checking
63+
plan.filter = nothing
64+
@test isnothing(plan.filter)
65+
@test_throws Exception plan.filter = "Test"
66+
@test isnothing(plan.filter)
67+
plan.filter = missing
68+
@test ismissing(plan.filter)
69+
plan.filter = [0.2]
70+
@test plan.filter == [0.2]
71+
72+
# Clearing
73+
clear!(plan)
74+
@test ismissing(plan.angles)
75+
@test ismissing(plan.filter)
76+
77+
# Used during construction
78+
plan.angles = angles
79+
instance = build(plan)
80+
@test instance.angles == angles
81+
@test isnothing(instance.filter) # Default kwarg
82+
end
83+
84+
outer = RecoPlan(DirectRadonParameters)
85+
incorrect = RecoPlan(RadonPreprocessingParameters)
86+
@testset "Nested Plans" begin
87+
# Type checking
88+
@test_throws Exception outer.reco = incorrect
89+
outer.reco = instance
90+
@test outer.reco == instance
91+
outer.reco = plan
92+
@test outer.reco == plan
93+
# Clearing
94+
plan.angles = angles
95+
@test !ismissing(outer.reco.angles)
96+
clear!(outer)
97+
@test ismissing(outer.reco.angles)
98+
clear!(outer, false)
99+
@test ismissing(outer.reco)
100+
end
101+
102+
@testset "SetAll!" begin
103+
# setAll! variants
104+
clear!(plan)
105+
# Kwargs
106+
setAll!(plan; angles = angles, filter = nothing, doesntExist = 42)
107+
@test plan.angles == angles
108+
@test isnothing(plan.filter)
109+
clear!(plan)
110+
# Dict{Symbol}
111+
setAll!(plan, Dict{Symbol, Any}(:angles => angles, :filter => nothing, :doesntExist => 42))
112+
@test plan.angles == angles
113+
@test isnothing(plan.filter)
114+
clear!(plan)
115+
# Dict{String}
116+
setAll!(plan, Dict{String, Any}("angles" => angles, "filter" => nothing, "doesntExist" => 42))
117+
@test plan.angles == angles
118+
@test isnothing(plan.filter)
119+
clear!(plan)
120+
# Nested plan
121+
outer.reco = plan
122+
setAll!(plan; angles = angles, filter = nothing)
123+
@test plan.angles == angles
124+
@test isnothing(plan.filter)
125+
clear!(plan)
126+
end
127+
128+
@testset "Property names" begin
129+
# Property names and filtering
130+
struct TestParameters <: AbstractImageReconstructionParameters
131+
a::Int64
132+
b::Float64
133+
_c::String
134+
end
135+
test = RecoPlan(TestParameters)
136+
@test in(:a, collect(propertynames(test)))
137+
@test in(:b, collect(propertynames(test)))
138+
@test !in(:c, collect(propertynames(test)))
139+
end
140+
end
141+
142+
@testset "Observables" begin
143+
plan = RecoPlan(RadonFilteredBackprojectionParameters)
144+
observed = Ref{Bool}()
145+
fun = (val) -> observed[] = true
146+
on(fun, plan, :angles)
147+
plan.angles = angles
148+
@test observed[]
149+
observed[] = false
150+
try
151+
plan.angles = "Test"
152+
catch e
153+
end
154+
@test !(observed[])
155+
156+
off(plan, :angles, fun)
157+
plan.angles = angles
158+
@test !(observed[])
159+
160+
obsv = plan[:angles]
161+
@test obsv isa Observable
162+
163+
on(fun, plan, :angles)
164+
clear!(plan)
165+
plan.angles = angles
166+
@test !(observed[])
167+
end
168+
169+
@testset "Traversal" begin
170+
end
171+
172+
end

test/runtests.jl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
using AbstractImageReconstruction
2+
using AbstractImageReconstruction.Observables
23
using Test
4+
using RegularizedLeastSquares
5+
6+
include(joinpath(@__DIR__(), "..", "docs", "src", "literate", "example", "example_include_all.jl"))
37

48
@testset "AbstractImageReconstruction.jl" begin
5-
# Write your tests here.
9+
include("algorithm_api.jl")
10+
include("reco_plan.jl")
11+
include("struct_transforms.jl")
12+
include("serialization.jl")
13+
include("linkedproperty.jl")
14+
include("caching.jl")
615
end

test/serialization.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@testset "Serialization" begin
2+
pre = RadonPreprocessingParameters(frames = collect(1:3))
3+
filter_reco = RadonFilteredBackprojectionParameters(;angles)
4+
algo = DirectRadonAlgorithm(DirectRadonParameters(pre, filter_reco))
5+
6+
plan = toPlan(algo)
7+
8+
io = IOBuffer()
9+
savePlan(io, plan)
10+
seekstart(io)
11+
plan_copy = loadPlan(io, [Main, AbstractImageReconstruction, RegularizedLeastSquares, OurRadonReco])
12+
@test hash(parameter(build(plan))) == hash(parameter(build(plan_copy)))
13+
end

test/struct_transforms.jl

Whitespace-only changes.

0 commit comments

Comments
 (0)