|
| 1 | +# Additional tests to improve code coverage for uncovered but valid code paths |
| 2 | + |
| 3 | +# ============================================================================ |
| 4 | +# 1. Iteration edge cases (src/iteration.jl) |
| 5 | +# ============================================================================ |
| 6 | +@testset "Iteration Coverage" begin |
| 7 | + @testset "Scalar iteration" begin |
| 8 | + # Iterating over a non-vector GiacExpr yields itself once |
| 9 | + x = giac_eval("42") |
| 10 | + # Use iterate directly since collect may use getindex which throws for scalars |
| 11 | + result = iterate(x) |
| 12 | + @test result !== nothing |
| 13 | + @test string(result[1]) == "42" |
| 14 | + # Second call should return nothing (single element) |
| 15 | + result2 = iterate(x, result[2]) |
| 16 | + @test result2 === nothing |
| 17 | + end |
| 18 | + |
| 19 | + @testset "Scalar length and size" begin |
| 20 | + x = giac_eval("42") |
| 21 | + @test length(x) == 1 |
| 22 | + @test size(x) == (1,) |
| 23 | + @test firstindex(x) == 1 |
| 24 | + @test lastindex(x) == 1 |
| 25 | + @test collect(eachindex(x)) == [1] |
| 26 | + end |
| 27 | + |
| 28 | + @testset "Vector iteration" begin |
| 29 | + v = giac_eval("[1,2,3]") |
| 30 | + collected = collect(v) |
| 31 | + @test length(collected) == 3 |
| 32 | + @test string(collected[1]) == "1" |
| 33 | + @test string(collected[2]) == "2" |
| 34 | + @test string(collected[3]) == "3" |
| 35 | + end |
| 36 | + |
| 37 | + @testset "Scalar in operator" begin |
| 38 | + # in() on a non-vector compares string representations |
| 39 | + x = giac_eval("42") |
| 40 | + @test giac_eval("42") in x |
| 41 | + @test !(giac_eval("43") in x) |
| 42 | + end |
| 43 | + |
| 44 | + @testset "Vector keys/values/pairs" begin |
| 45 | + v = giac_eval("[10,20,30]") |
| 46 | + ks = collect(keys(v)) |
| 47 | + @test ks == [1, 2, 3] |
| 48 | + vs = collect(values(v)) |
| 49 | + @test length(vs) == 3 |
| 50 | + @test string(vs[2]) == "20" |
| 51 | + ps = collect(pairs(v)) |
| 52 | + @test ps[1][1] == 1 |
| 53 | + @test string(ps[1][2]) == "10" |
| 54 | + end |
| 55 | + |
| 56 | + @testset "Vector slicing" begin |
| 57 | + v = giac_eval("[1,2,3,4,5]") |
| 58 | + sliced = v[2:4] |
| 59 | + @test length(sliced) == 3 |
| 60 | + @test string(sliced[1]) == "2" |
| 61 | + |
| 62 | + all_elems = v[:] |
| 63 | + @test length(all_elems) == 5 |
| 64 | + end |
| 65 | + |
| 66 | + @testset "Indexing errors" begin |
| 67 | + x = giac_eval("42") |
| 68 | + @test_throws ErrorException x[1] |
| 69 | + |
| 70 | + v = giac_eval("[1,2,3]") |
| 71 | + @test_throws BoundsError v[0] |
| 72 | + @test_throws BoundsError v[4] |
| 73 | + |
| 74 | + # Range indexing on non-vector |
| 75 | + @test_throws ErrorException x[1:2] |
| 76 | + end |
| 77 | +end |
| 78 | + |
| 79 | +# ============================================================================ |
| 80 | +# 2. Introspection edge cases (src/introspection.jl) |
| 81 | +# ============================================================================ |
| 82 | +@testset "Introspection Edge Cases" begin |
| 83 | + @testset "numer on integer" begin |
| 84 | + n = giac_eval("5") |
| 85 | + result = Giac.numer(n) |
| 86 | + @test string(result) == "5" |
| 87 | + end |
| 88 | + |
| 89 | + @testset "denom on integer" begin |
| 90 | + n = giac_eval("5") |
| 91 | + result = Giac.denom(n) |
| 92 | + @test string(result) == "1" |
| 93 | + end |
| 94 | + |
| 95 | + @testset "numer/denom on fraction" begin |
| 96 | + f = giac_eval("3/7") |
| 97 | + @test string(Giac.numer(f)) == "3" |
| 98 | + @test string(Giac.denom(f)) == "7" |
| 99 | + end |
| 100 | + |
| 101 | + @testset "real_part on real number" begin |
| 102 | + r = giac_eval("3") |
| 103 | + @test string(Giac.real_part(r)) == "3" |
| 104 | + end |
| 105 | + |
| 106 | + @testset "imag_part on real number" begin |
| 107 | + r = giac_eval("3") |
| 108 | + @test string(Giac.imag_part(r)) == "0" |
| 109 | + end |
| 110 | + |
| 111 | + @testset "real_part/imag_part on complex" begin |
| 112 | + c = giac_eval("3+4*i") |
| 113 | + @test string(Giac.real_part(c)) == "3" |
| 114 | + @test string(Giac.imag_part(c)) == "4" |
| 115 | + end |
| 116 | + |
| 117 | + @testset "Type predicates" begin |
| 118 | + @test Giac.is_integer(giac_eval("42")) |
| 119 | + @test !Giac.is_integer(giac_eval("3.14")) |
| 120 | + @test Giac.is_numeric(giac_eval("42")) |
| 121 | + @test Giac.is_numeric(giac_eval("3.14")) |
| 122 | + @test !Giac.is_numeric(giac_eval("x")) |
| 123 | + @test Giac.is_vector(giac_eval("[1,2,3]")) |
| 124 | + @test !Giac.is_vector(giac_eval("42")) |
| 125 | + @test Giac.is_symbolic(giac_eval("sin(x)")) |
| 126 | + @test !Giac.is_symbolic(giac_eval("42")) |
| 127 | + @test Giac.is_identifier(giac_eval("x")) |
| 128 | + @test !Giac.is_identifier(giac_eval("42")) |
| 129 | + @test Giac.is_fraction(giac_eval("3/4")) |
| 130 | + @test !Giac.is_fraction(giac_eval("42")) |
| 131 | + @test Giac.is_boolean(giac_eval("true")) |
| 132 | + @test Giac.is_boolean(giac_eval("false")) |
| 133 | + @test !Giac.is_boolean(giac_eval("1")) |
| 134 | + end |
| 135 | + |
| 136 | + @testset "symb_funcname and symb_argument" begin |
| 137 | + s = giac_eval("sin(x)") |
| 138 | + @test Giac.symb_funcname(s) == "sin" |
| 139 | + arg = Giac.symb_argument(s) |
| 140 | + @test string(arg) == "x" |
| 141 | + end |
| 142 | + |
| 143 | + @testset "Error on non-fraction numer/denom" begin |
| 144 | + x = giac_eval("x") |
| 145 | + @test_throws GiacError Giac.numer(x) |
| 146 | + @test_throws GiacError Giac.denom(x) |
| 147 | + end |
| 148 | + |
| 149 | + @testset "Error on non-symbolic symb_funcname/symb_argument" begin |
| 150 | + n = giac_eval("42") |
| 151 | + @test_throws GiacError Giac.symb_funcname(n) |
| 152 | + @test_throws GiacError Giac.symb_argument(n) |
| 153 | + end |
| 154 | +end |
| 155 | + |
| 156 | +# ============================================================================ |
| 157 | +# 3. HeldCmd LaTeX edge cases (src/held_cmd.jl) |
| 158 | +# ============================================================================ |
| 159 | +@testset "HeldCmd LaTeX Coverage" begin |
| 160 | + @giac_var x t s n k |
| 161 | + |
| 162 | + @testset "Directional limits" begin |
| 163 | + # Left limit (dir = -1) |
| 164 | + lim_left = hold_cmd(:limit, giac_eval("1/x"), x, giac_eval("0"), giac_eval("-1")) |
| 165 | + latex_left = sprint(show, MIME("text/latex"), lim_left) |
| 166 | + @test occursin("\\lim", latex_left) |
| 167 | + @test occursin("^-", latex_left) |
| 168 | + |
| 169 | + # Right limit (dir = 1) |
| 170 | + lim_right = hold_cmd(:limit, giac_eval("1/x"), x, giac_eval("0"), giac_eval("1")) |
| 171 | + latex_right = sprint(show, MIME("text/latex"), lim_right) |
| 172 | + @test occursin("\\lim", latex_right) |
| 173 | + @test occursin("^+", latex_right) |
| 174 | + end |
| 175 | + |
| 176 | + @testset "Limit with fewer args" begin |
| 177 | + # 2 args |
| 178 | + lim2 = hold_cmd(:limit, giac_eval("1/x"), x) |
| 179 | + latex2 = sprint(show, MIME("text/latex"), lim2) |
| 180 | + @test occursin("\\lim", latex2) |
| 181 | + |
| 182 | + # 1 arg |
| 183 | + lim1 = hold_cmd(:limit, giac_eval("1/x")) |
| 184 | + latex1 = sprint(show, MIME("text/latex"), lim1) |
| 185 | + @test occursin("\\lim", latex1) |
| 186 | + end |
| 187 | + |
| 188 | + @testset "Sum and product" begin |
| 189 | + h_sum = hold_cmd(:sum, giac_eval("k^2"), k, giac_eval("1"), giac_eval("10")) |
| 190 | + latex_sum = sprint(show, MIME("text/latex"), h_sum) |
| 191 | + @test occursin("\\sum", latex_sum) |
| 192 | + |
| 193 | + h_prod = hold_cmd(:product, giac_eval("k"), k, giac_eval("1"), giac_eval("5")) |
| 194 | + latex_prod = sprint(show, MIME("text/latex"), h_prod) |
| 195 | + @test occursin("\\prod", latex_prod) |
| 196 | + end |
| 197 | + |
| 198 | + @testset "Sum/product with fewer args" begin |
| 199 | + # 2 args |
| 200 | + h2 = hold_cmd(:sum, giac_eval("k^2"), k) |
| 201 | + latex2 = sprint(show, MIME("text/latex"), h2) |
| 202 | + @test occursin("\\sum", latex2) |
| 203 | + |
| 204 | + # 1 arg |
| 205 | + h1 = hold_cmd(:sum, giac_eval("k^2")) |
| 206 | + latex1 = sprint(show, MIME("text/latex"), h1) |
| 207 | + @test occursin("\\sum", latex1) |
| 208 | + end |
| 209 | + |
| 210 | + @testset "HeldEquation with Number" begin |
| 211 | + eq = HeldEquation(giac_eval("x^2"), 0) |
| 212 | + latex_str = sprint(show, MIME("text/latex"), eq) |
| 213 | + @test occursin("=", latex_str) |
| 214 | + @test occursin("0", latex_str) |
| 215 | + end |
| 216 | + |
| 217 | + @testset "HeldEquation with HeldCmd on left" begin |
| 218 | + held = hold_cmd(:integrate, giac_eval("x^2"), x) |
| 219 | + eq = HeldEquation(held, giac_eval("x^3/3")) |
| 220 | + latex_str = sprint(show, MIME("text/latex"), eq) |
| 221 | + @test occursin("\\int", latex_str) |
| 222 | + @test occursin("=", latex_str) |
| 223 | + end |
| 224 | + |
| 225 | + @testset "HeldEquation with HeldCmd on both sides" begin |
| 226 | + lhs = hold_cmd(:diff, giac_eval("x^3/3"), x) |
| 227 | + rhs = hold_cmd(:integrate, giac_eval("x"), x) |
| 228 | + eq = HeldEquation(lhs, rhs) |
| 229 | + latex_str = sprint(show, MIME("text/latex"), eq) |
| 230 | + @test occursin("\\frac{d}{dx}", latex_str) |
| 231 | + @test occursin("=", latex_str) |
| 232 | + end |
| 233 | + |
| 234 | + @testset "HeldEquation plain text" begin |
| 235 | + held = hold_cmd(:factor, giac_eval("x^2-1")) |
| 236 | + eq = HeldEquation(held, giac_eval("(x-1)*(x+1)")) |
| 237 | + txt = sprint(show, eq) |
| 238 | + @test occursin("=", txt) |
| 239 | + @test occursin("factor", txt) |
| 240 | + |
| 241 | + txt_plain = sprint(show, MIME("text/plain"), eq) |
| 242 | + @test occursin("=", txt_plain) |
| 243 | + end |
| 244 | + |
| 245 | + @testset "Tilde operator creates HeldEquation" begin |
| 246 | + held = hold_cmd(:factor, giac_eval("x^2-1")) |
| 247 | + eq1 = held ~ giac_eval("(x-1)*(x+1)") |
| 248 | + @test eq1 isa HeldEquation |
| 249 | + |
| 250 | + eq2 = giac_eval("x^2") ~ held |
| 251 | + @test eq2 isa HeldEquation |
| 252 | + |
| 253 | + held2 = hold_cmd(:diff, x, x) |
| 254 | + eq3 = held ~ held2 |
| 255 | + @test eq3 isa HeldEquation |
| 256 | + |
| 257 | + eq4 = held ~ 42 |
| 258 | + @test eq4 isa HeldEquation |
| 259 | + |
| 260 | + eq5 = 42 ~ held |
| 261 | + @test eq5 isa HeldEquation |
| 262 | + end |
| 263 | + |
| 264 | + @testset "LaTeX: ilaplace alias" begin |
| 265 | + h = hold_cmd(:ilaplace, giac_eval("1/s"), s, t) |
| 266 | + latex = sprint(show, MIME("text/latex"), h) |
| 267 | + @test occursin("\\mathcal{L}^{-1}", latex) |
| 268 | + end |
| 269 | + |
| 270 | + @testset "LaTeX: ztransform alias" begin |
| 271 | + # Use HeldCmd constructor directly to test LaTeX rendering without command validation |
| 272 | + h = HeldCmd(:ztransform, (giac_eval("n"), n, giac_eval("z"))) |
| 273 | + latex = sprint(show, MIME("text/latex"), h) |
| 274 | + @test occursin("\\mathcal{Z}", latex) |
| 275 | + end |
| 276 | + |
| 277 | + @testset "LaTeX: invztransform alias" begin |
| 278 | + h = HeldCmd(:invztransform, (giac_eval("1/z"), giac_eval("z"), n)) |
| 279 | + latex = sprint(show, MIME("text/latex"), h) |
| 280 | + @test occursin("\\mathcal{Z}^{-1}", latex) |
| 281 | + end |
| 282 | + |
| 283 | + @testset "LaTeX: transform with fewer args" begin |
| 284 | + # 1 arg |
| 285 | + h1 = hold_cmd(:laplace, giac_eval("sin(t)")) |
| 286 | + latex1 = sprint(show, MIME("text/latex"), h1) |
| 287 | + @test occursin("\\mathcal{L}", latex1) |
| 288 | + |
| 289 | + # 0 args (edge case) |
| 290 | + h0 = hold_cmd(:laplace) |
| 291 | + latex0 = sprint(show, MIME("text/latex"), h0) |
| 292 | + @test occursin("\\mathcal{L}", latex0) |
| 293 | + |
| 294 | + # inv with 1 arg |
| 295 | + h1i = hold_cmd(:invlaplace, giac_eval("1/s")) |
| 296 | + latex1i = sprint(show, MIME("text/latex"), h1i) |
| 297 | + @test occursin("\\mathcal{L}^{-1}", latex1i) |
| 298 | + |
| 299 | + # inv with 0 args |
| 300 | + h0i = hold_cmd(:invlaplace) |
| 301 | + latex0i = sprint(show, MIME("text/latex"), h0i) |
| 302 | + @test occursin("\\mathcal{L}^{-1}", latex0i) |
| 303 | + end |
| 304 | + |
| 305 | + @testset "LaTeX: integrate with 1 arg" begin |
| 306 | + h = hold_cmd(:integrate, giac_eval("x^2")) |
| 307 | + latex = sprint(show, MIME("text/latex"), h) |
| 308 | + @test occursin("\\int", latex) |
| 309 | + end |
| 310 | + |
| 311 | + @testset "LaTeX: diff with 1 arg" begin |
| 312 | + h = hold_cmd(:diff, giac_eval("x^2")) |
| 313 | + latex = sprint(show, MIME("text/latex"), h) |
| 314 | + @test occursin("\\frac{d}{d?}", latex) |
| 315 | + end |
| 316 | + |
| 317 | + @testset "HeldCmd text/plain show" begin |
| 318 | + h = hold_cmd(:factor, giac_eval("x^2-1")) |
| 319 | + txt = sprint(show, MIME("text/plain"), h) |
| 320 | + @test occursin("HeldCmd:", txt) |
| 321 | + @test occursin("factor", txt) |
| 322 | + end |
| 323 | + |
| 324 | + @testset "_arg_to_latex edge cases" begin |
| 325 | + # Symbol arg |
| 326 | + h = hold_cmd(:diff, giac_eval("x^2"), :x) |
| 327 | + latex = sprint(show, MIME("text/latex"), h) |
| 328 | + @test occursin("dx", latex) |
| 329 | + |
| 330 | + # String arg in generic command |
| 331 | + h_str = HeldCmd(:foo, ("hello",)) |
| 332 | + latex_str = sprint(show, MIME("text/latex"), h_str) |
| 333 | + @test occursin("\\text{hello}", latex_str) |
| 334 | + |
| 335 | + # Vector arg in generic command |
| 336 | + h_vec = HeldCmd(:bar, ([1, 2, 3],)) |
| 337 | + latex_vec = sprint(show, MIME("text/latex"), h_vec) |
| 338 | + @test occursin("[1, 2, 3]", latex_vec) |
| 339 | + end |
| 340 | + |
| 341 | + @testset "Generic fallback with no args" begin |
| 342 | + h = HeldCmd(:foo, ()) |
| 343 | + latex = sprint(show, MIME("text/latex"), h) |
| 344 | + @test occursin("\\mathrm{foo}", latex) |
| 345 | + @test occursin("()", latex) |
| 346 | + end |
| 347 | +end |
| 348 | + |
| 349 | +# ============================================================================ |
| 350 | +# 4. GiacExpr method syntax (src/types.jl) |
| 351 | +# ============================================================================ |
| 352 | +@testset "GiacExpr Method Syntax" begin |
| 353 | + @testset "Method-style command access" begin |
| 354 | + expr = giac_eval("x^2 - 1") |
| 355 | + result = expr.factor() |
| 356 | + @test string(result) == "(x-1)*(x+1)" |
| 357 | + end |
| 358 | + |
| 359 | + @testset "Method-style with additional args" begin |
| 360 | + @giac_var x |
| 361 | + expr = giac_eval("x^2") |
| 362 | + result = expr.integrate(x) |
| 363 | + @test occursin("x^3", string(result)) |
| 364 | + end |
| 365 | + |
| 366 | + @testset "propertynames" begin |
| 367 | + expr = giac_eval("42") |
| 368 | + @test propertynames(expr) == (:ptr,) |
| 369 | + end |
| 370 | +end |
0 commit comments