Skip to content

Commit 2fc1712

Browse files
committed
Increase test coverage
1 parent 57c7b2a commit 2fc1712

File tree

10 files changed

+267
-7
lines changed

10 files changed

+267
-7
lines changed

Project.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
name = "SolarPosition"
22
uuid = "5b9d1343-a731-5a90-8730-7bf8d89bf3eb"
3-
authors = ["Stefan de Lange"]
43
version = "0.1.0"
4+
authors = ["Stefan de Lange"]
5+
6+
[workspace]
7+
projects = ["test", "docs"]
58

69
[deps]
710
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
@@ -29,10 +32,10 @@ Makie = "0.24"
2932
ModelingToolkit = "10.3.0 - 10.26.1"
3033
OhMyThreads = "0.8"
3134
StructArrays = "0.7"
35+
Symbolics = "6,7"
3236
Tables = "1"
3337
Test = "1"
3438
TimeZones = "1.22.0"
35-
Symbolics = "6,7"
3639
julia = "1.10"
3740

3841
[extras]
@@ -41,6 +44,3 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
4144

4245
[targets]
4346
test = ["Aqua", "Test"]
44-
45-
[workspace]
46-
projects = ["test", "docs"]

test/test-deltat.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,25 @@ end
147147

148148
@test deltat_values[1] < deltat_values[end]
149149
end
150+
151+
@testset "ΔT edge cases" begin
152+
# Test years before -1999 (should warn and return extrapolated value)
153+
@test_logs (:warn, r"ΔT is undefined") calculate_deltat(DateTime(-2500, 6, 1))
154+
155+
# Test years after 3000 (should warn and return extrapolated value)
156+
@test_logs (:warn, r"ΔT is undefined") calculate_deltat(DateTime(3500, 6, 1))
157+
158+
# Test year ranges that hit specific polynomial cases
159+
# 1860-1900 range
160+
dt1 = calculate_deltat(DateTime(1870, 6, 1))
161+
@test dt1 isa Float64
162+
163+
# 1900-1920 range
164+
dt2 = calculate_deltat(DateTime(1910, 6, 1))
165+
@test dt2 isa Float64
166+
167+
# 1941-1961 range (line 50)
168+
dt3 = calculate_deltat(DateTime(1950, 6, 1))
169+
@test dt3 isa Float64
170+
@test dt3 > 0.0
171+
end

test/test-interface.jl

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,76 @@ end
290290
@test isfinite(res.equation_of_time)
291291
end
292292
end
293+
294+
@testset "Base.show methods" begin
295+
@testset "Observer show" begin
296+
obs = Observer(45.0, 10.0, 100.0)
297+
str = sprint(show, obs)
298+
@test occursin("Observer", str)
299+
@test occursin("45.0", str)
300+
@test occursin("10.0", str)
301+
@test occursin("100.0", str)
302+
end
303+
304+
@testset "SolPos show" begin
305+
pos = SolPos(180.0, 45.0, 45.0)
306+
str = sprint(show, pos)
307+
@test occursin("SolPos", str)
308+
@test occursin("180.0", str)
309+
@test occursin("45.0", str)
310+
end
311+
312+
@testset "ApparentSolPos show" begin
313+
pos = ApparentSolPos(180.0, 45.0, 45.0, 46.0, 44.0)
314+
str = sprint(show, pos)
315+
@test occursin("ApparentSolPos", str)
316+
@test occursin("180.0", str)
317+
@test occursin("45.0", str)
318+
@test occursin("46.0", str)
319+
@test occursin("44.0", str)
320+
end
321+
322+
@testset "SPASolPos show" begin
323+
pos = SPASolPos(180.0, 45.0, 45.0, 46.0, 44.0, 5.0)
324+
str = sprint(show, pos)
325+
@test occursin("SPASolPos", str)
326+
@test occursin("180.0", str)
327+
@test occursin("45.0", str)
328+
@test occursin("5.0", str)
329+
end
330+
end
331+
332+
@testset "Union{DateTime,ZonedDateTime} vector handling" begin
333+
obs = Observer(45.0, 10.0, 100.0)
334+
dt1 = DateTime(2020, 6, 21, 12, 0, 0)
335+
dt2 = ZonedDateTime(2020, 6, 21, 13, 0, 0, tz"UTC")
336+
337+
# mixed vector of DateTime and ZonedDateTime
338+
dts = Union{DateTime,ZonedDateTime}[dt1, dt2]
339+
positions = solar_position(obs, dts, PSA())
340+
@test length(positions) == 2
341+
@test positions isa StructVector
342+
343+
# in-place version
344+
pos = StructVector{SolPos{Float64}}(undef, 2)
345+
solar_position!(pos, obs, dts, PSA())
346+
@test length(pos) == 2
347+
@test all(p -> p isa SolPos, pos)
348+
end
349+
350+
@testset "Table interface with copy" begin
351+
# non-mutating solar_position function for tables
352+
obs = Observer(45.0, 10.0, 100.0)
353+
df = DataFrame(
354+
datetime = [DateTime(2020, 6, 21, 12, 0, 0), DateTime(2020, 6, 21, 13, 0, 0)],
355+
)
356+
357+
# returns copy
358+
df_result = solar_position(df, obs, PSA())
359+
360+
# should not be modified
361+
@test !hasproperty(df, :azimuth)
362+
@test hasproperty(df_result, :azimuth)
363+
@test hasproperty(df_result, :elevation)
364+
@test hasproperty(df_result, :zenith)
365+
end

test/test-noaa.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,22 @@
8484
)
8585
@test isapprox(res_with_refraction.zenith, res_no_refraction.zenith, atol = 1e-10)
8686
end
87+
88+
@testset "NOAA edge cases" begin
89+
obs = Observer(45.0, 10.0, 100.0)
90+
91+
# Test time that produces negative true_solar_time / 4.0
92+
# This should trigger the hour_angle < 0 branch
93+
dt = DateTime(2020, 1, 1, 0, 0, 0)
94+
pos = solar_position(obs, dt, NOAA(), NoRefraction())
95+
@test pos isa SolPos
96+
@test isfinite(pos.azimuth)
97+
98+
# Test time that produces positive true_solar_time / 4.0 (line 88)
99+
# Solar noon should trigger the else branch
100+
dt_noon = DateTime(2020, 6, 21, 12, 0, 0)
101+
pos_noon = solar_position(obs, dt_noon, NOAA(), NoRefraction())
102+
@test pos_noon isa SolPos
103+
@test isfinite(pos_noon.azimuth)
104+
end
87105
end

test/test-ohmythreads.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ spafields = (
5050
end
5151

5252
@testset "solar_position with ZonedDateTime" begin
53-
tz = tz"UTC"
54-
zoned_times = [ZonedDateTime(dt, tz) for dt in times]
53+
zoned_times = [ZonedDateTime(dt, tz"UTC") for dt in times]
5554
zoned_serial_results = solar_position(obs, zoned_times, PSA(), NoRefraction())
5655
results = solar_position(obs, zoned_times, PSA(), NoRefraction(), scheduler)
5756
@test length(results) == n

test/test-psa.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,10 @@
5353
@test res_alg isa ApparentSolPos
5454
end
5555
end
56+
57+
@testset "PSA coefficient error" begin
58+
obs = Observer(45.0, 10.0, 100.0)
59+
dt = DateTime(2020, 6, 21, 12, 0, 0)
60+
@test_throws ErrorException solar_position(obs, dt, PSA(9999))
61+
end
5662
end

test/test-refraction.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,24 @@ end
107107
@test refraction(SPARefraction(101325.0, 12.0, 0.0), -0.26667) != 0.0
108108
@test refraction(SPARefraction(101325.0, 12.0, 0.0), -0.26668) != 1.0
109109
end
110+
111+
@testset "SPARefraction constructor variants" begin
112+
113+
ref1 = SPARefraction()
114+
@test ref1.pressure == 101325.0
115+
@test ref1.temperature == 12.0
116+
@test ref1.refraction_limit == -0.5667
117+
118+
# with pressure and temperature
119+
ref2 = SPARefraction(100000.0, 15.0)
120+
@test ref2.pressure == 100000.0
121+
@test ref2.temperature == 15.0
122+
@test ref2.refraction_limit == -0.5667
123+
124+
# refraction works
125+
obs = Observer(45.0, 10.0, 100.0)
126+
dt = DateTime(2020, 6, 21, 12, 0, 0)
127+
pos = solar_position(obs, dt, SPA(), ref2)
128+
@test pos isa SPASolPos
129+
@test pos.apparent_elevation != pos.elevation
130+
end

test/test-spa.jl

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,91 @@
8787
@test all(r -> -180.0 <= r.azimuth <= 360.0, results)
8888
@test all(r -> -90.0 <= r.elevation <= 90.0, results)
8989
end
90+
91+
@testset "SPA edge cases" begin
92+
obs = Observer(45.0, 10.0, 5000.0) # High altitude to test parallax terms
93+
94+
# Test with extreme latitude close to poles
95+
obs_pole = Observer(89.99, 10.0, 100.0)
96+
dt = DateTime(2020, 6, 21, 12, 0, 0)
97+
pos = solar_position(obs_pole, dt, SPA())
98+
@test pos isa SPASolPos
99+
100+
# Test equation of time limits
101+
dt_eot = DateTime(2020, 11, 3, 6, 0, 0)
102+
pos_eot = solar_position(obs, dt_eot, SPA())
103+
@test -20.0 <= pos_eot.equation_of_time <= 20.0
104+
end
105+
106+
@testset "SPA x_term and y_term functions" begin
107+
# These functions are used internally by SPA but not directly tested
108+
# They should be covered by creating SPAObserver instances
109+
obs1 = SolarPosition.Positioning.SPAObserver(45.0, 10.0, 0.0)
110+
@test obs1.x != 0.0
111+
@test obs1.y != 0.0
112+
113+
obs2 = SolarPosition.Positioning.SPAObserver(45.0, 10.0, 5000.0)
114+
@test obs2.x != obs1.x # Different altitude should give different x
115+
@test obs2.y != obs1.y # Different altitude should give different y
116+
117+
# Test alternative constructor with keyword argument (line 107)
118+
obs3 = SolarPosition.Positioning.SPAObserver(45.0, 10.0; altitude = 100.0)
119+
@test obs3.altitude == 100.0
120+
@test obs3.latitude == 45.0
121+
@test obs3.longitude == 10.0
122+
123+
# Test internal helper functions directly (lines 281-290)
124+
u = SolarPosition.Positioning.u_term(45.0)
125+
@test u isa Float64
126+
@test isfinite(u)
127+
128+
x = SolarPosition.Positioning.x_term(u, 45.0, 100.0)
129+
@test x isa Float64
130+
@test isfinite(x)
131+
132+
y = SolarPosition.Positioning.y_term(u, 45.0, 100.0)
133+
@test y isa Float64
134+
@test isfinite(y)
135+
end
136+
137+
@testset "SPA pole warnings for SPAObserver" begin
138+
# Test north pole warning
139+
@test_logs (:warn, r"Latitude is 90") SolarPosition.Positioning.SPAObserver(
140+
90.0,
141+
0.0,
142+
0.0,
143+
)
144+
145+
# Test south pole warning
146+
@test_logs (:warn, r"Latitude is -90") SolarPosition.Positioning.SPAObserver(
147+
-90.0,
148+
0.0,
149+
0.0,
150+
)
151+
end
152+
153+
@testset "SPA equation of time limits" begin
154+
obs = Observer(45.0, 10.0, 100.0)
155+
156+
# Test time that produces E > 20.0 (should subtract 1440)
157+
# This is rare but can happen at specific dates/times
158+
dt1 = DateTime(2020, 1, 1, 0, 0, 0)
159+
pos1 = solar_position(obs, dt1, SPA())
160+
@test pos1 isa SPASolPos
161+
@test -20.0 <= pos1.equation_of_time <= 20.0
162+
163+
# Test multiple dates to increase chance of hitting edge cases (line 377)
164+
# Try dates near perihelion and aphelion when equation of time extremes occur
165+
test_dates = [
166+
DateTime(2020, 1, 3, 0, 0, 0), # Near perihelion
167+
DateTime(2020, 7, 4, 0, 0, 0), # Near aphelion
168+
DateTime(2020, 2, 12, 0, 0, 0), # E might be positive extreme
169+
DateTime(2020, 11, 3, 0, 0, 0), # E might be negative extreme
170+
]
171+
172+
for dt in test_dates
173+
pos = solar_position(obs, dt, SPA())
174+
@test -20.0 <= pos.equation_of_time <= 20.0
175+
end
176+
end
90177
end

test/test-usno.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,20 @@
8585
@test res.elevation > 85.0
8686
@test res.zenith < 5.0
8787
end
88+
89+
@testset "USNO with DefaultRefraction" begin
90+
91+
obs = Observer(45.0, 10.0, 100.0)
92+
dt = DateTime(2020, 6, 21, 12, 0, 0)
93+
pos = solar_position(obs, dt, USNO())
94+
@test pos isa SolPos
95+
@test !hasfield(typeof(pos), :apparent_elevation)
96+
97+
# result_type is correctly set
98+
@test SolarPosition.Positioning.result_type(
99+
USNO,
100+
SolarPosition.Refraction.DefaultRefraction,
101+
Float64,
102+
) == SolPos{Float64}
103+
end
88104
end

test/test-walraven.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,22 @@
2121
@test isapprox(res.zenith, exp_zen, atol = 1e-6)
2222
@test isapprox(res.azimuth, exp_az, atol = 1e-6)
2323
end
24+
25+
@testset "Walraven edge cases" begin
26+
obs = Observer(45.0, 10.0, 100.0)
27+
28+
# Test leap year edge case
29+
# February 29 in a leap year
30+
dt_leap = DateTime(2020, 2, 29, 12, 0, 0)
31+
pos = solar_position(obs, dt_leap, Walraven())
32+
@test pos isa SolPos
33+
34+
# Test edge case with negative δ that's not leap*4 (line 33)
35+
# Early in the year when δ < 0
36+
dt_neg = DateTime(2020, 1, 2, 0, 0, 0)
37+
pos = solar_position(obs, dt_neg, Walraven())
38+
@test pos isa SolPos
39+
@test isfinite(pos.azimuth)
40+
@test isfinite(pos.elevation)
41+
end
2442
end

0 commit comments

Comments
 (0)