From 4e6555c8e19e04a68bab11f2745e4403b03fecb5 Mon Sep 17 00:00:00 2001 From: Olivier Cots Date: Mon, 8 Jun 2026 18:56:36 +0200 Subject: [PATCH] Standardize private/public method naming conventions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add naming conventions to modules.md: symbol (public), _symbol (private helper), __symbol (default value) - Rename private functions in Descriptions: compute_similarity → _compute_similarity, find_similar_descriptions → _find_similar_descriptions, format_description_candidates → _format_description_candidates - Rename private functions in Exceptions: extract_user_frames → _extract_user_frames, format_user_friendly_error → _format_user_friendly_error, ansi_color → _ansi_color, ansi_reset → _ansi_reset, print_ansi_styled → _print_ansi_styled - Update planning.md with improved checkpoint templates (MCP preferred, fallback commands) - Fix extension tests to use fake types per testing-creation.md §6 (prevents flaky tests when extensions are loaded elsewhere) --- dev/philosophy/modules.md | 24 ++++- dev/planning.md | 94 ++++++++++++++++--- src/Descriptions/complete.jl | 6 +- src/Descriptions/similarity.jl | 8 +- src/Exceptions/display.jl | 20 ++-- src/Extensions/Extensions.jl | 5 +- test/suite/descriptions/test_similarity.jl | 88 ++++++++--------- test/suite/exceptions/test_display.jl | 4 +- .../extensions/test_extensions_enriched.jl | 42 +++------ 9 files changed, 185 insertions(+), 106 deletions(-) diff --git a/dev/philosophy/modules.md b/dev/philosophy/modules.md index 357513b6..6c3822d4 100644 --- a/dev/philosophy/modules.md +++ b/dev/philosophy/modules.md @@ -92,9 +92,31 @@ end Why: visible origin, safe refactors (only the `using ..X` line changes), no accidental shadowing, same rule for external and sibling symbols. +## Naming conventions + +Three visibility levels, signalled by the name itself: + +- `symbol` — **public**: exported via `export`; part of the documented API. +- `_symbol` — **private helper**: unexported; internal implementation detail. + Accessible via qualified path (`Module._symbol`) but not advertised. +- `__symbol` — **default value**: unexported; provides a replaceable semantic + default (e.g. `__display() = true`). The double underscore distinguishes it + from a plain helper: it is a *default* that a higher-level layer may override, + not merely a utility function. + +```julia +export build_thing # public + +function _validate(x) # private helper + ... +end + +__verbose()::Bool = false # default value (replaceable by extension or subtype) +``` + ## Two-level exports -- **Submodule**: `export` for its public API; internals (`_helper`) unexported. +- **Submodule**: `export` for its public API; internals (`_helper`, `__default`) unexported. - **Package (top-level)**: **no** `export`. Load submodules with `using .Submodule`; users reach them via `Package.Submodule.sym`. diff --git a/dev/planning.md b/dev/planning.md index 21a0c080..8b9bf2d1 100644 --- a/dev/planning.md +++ b/dev/planning.md @@ -6,6 +6,7 @@ to align with the human before touching code, not to produce a 50-step document. ## When to write a plan Before any task that: + - touches more than one file, - changes a public interface, or - involves a non-obvious design decision. @@ -16,7 +17,7 @@ Write the plan, share it, wait for confirmation before starting. ## Template -``` +````markdown # Plan: ## What and why @@ -34,32 +35,74 @@ Write the plan, share it, wait for confirmation before starting. Steps: 1. … 2. … -Checkpoint: run `test/suite/` via MCP. All green before moving on. +Checkpoint: + +```bash +# MCP (preferred) +get_test_command test_args=["suite/"] +then generate_report + +# Fallback +julia --project=@. -e 'using Pkg; Pkg.test(; test_args=["suite/"])' 2>&1 | tee /tmp/_phaseA.log +grep -E "Error|Fail|Test Summary" /tmp/_phaseA.log +``` + +All green before moving on. ### Phase B — + Steps: 1. … -Checkpoint: run `test/suite/` + full suite. +Checkpoint: + +```bash +# MCP (preferred) +get_test_command test_args=["suite/", "suite/"] +then generate_report + +# Final phase only: full suite +julia --project=@. -e 'using Pkg; Pkg.test()' 2>&1 | tee /tmp/_final.log +grep -E "Error|Fail|Test Summary" /tmp/_final.log +``` … ## Human checkpoints + - ⛔ Ask before first commit. - ⛔ Ask before pushing to a shared branch. - ⛔ Ask before deleting files that were not created in this task. - ⛔ Ask if a design decision arises that was not in the plan. ## Out of scope + -``` +```` --- ## Rules for filling in the template **Phases** — one phase = one coherent unit that can be tested independently. A phase -ends with a test checkpoint (run the affected test suite via the MCP `get_test_command` -tool, then `generate_report`). Never skip the checkpoint. +ends with a test checkpoint. Never skip the checkpoint. + +**Test checkpoints** — always use the MCP `get_test_command` tool with `test_args` to +run only the affected test groups, then `generate_report` to parse the result. If MCP +is unavailable, fall back to: + +```bash +# Phase-specific (preferred during intermediate phases) +julia --project=@. -e 'using Pkg; Pkg.test(; test_args=["suite/"])' \ + 2>&1 | tee /tmp/_.log +grep -E "Error|Fail|Test Summary" /tmp/_.log + +# Full suite (mandatory for the last phase only) +julia --project=@. -e 'using Pkg; Pkg.test()' \ + 2>&1 | tee /tmp/_final.log +grep -E "Error|Fail|Test Summary" /tmp/_final.log +``` + +Never run the full suite for intermediate phases — it is too slow and hides errors. **Steps** — file-level granularity. "Edit `src/X/y.jl` to add method `f`" not "implement the feature". Concrete enough that a step is either done or not. @@ -70,6 +113,15 @@ missed. **Docstrings** — always the last step of the last phase, once the API is stable. +**Code snippets** — include short code snippets directly in the plan to guide the +implementer. This applies to: + +- *Source changes*: show the before/after signature or struct definition. +- *Call-site updates*: show the exact old and new lines when renaming symbols. +- *Test patterns*: show the test structure (fake type + testset skeleton) when the + test pattern is non-obvious. +Snippets should be *concise shapes*, not full implementations. + **Out of scope** — explicit. Prevents scope creep and clarifies what the human should not expect after the plan is executed. @@ -77,7 +129,7 @@ not expect after the plan is executed. ## Concrete example structure (abbreviated) -``` +````markdown # Plan: rename content → dynamics trait ## What and why @@ -97,23 +149,43 @@ names the axis. Purely mechanical, no behavior change. Steps: 1. Rename src/Traits/content.jl → dynamics.jl; update all names inside. 2. Update include path in src/Traits/Traits.jl; update exports. -Checkpoint: test/suite/traits — all green. +Checkpoint: + +```bash +get_test_command test_args=["suite/traits"] # MCP +# fallback: julia --project=@. -e 'using Pkg; Pkg.test(; test_args=["suite/traits"])' 2>&1 | tee /tmp/rename_traits.log +``` ### Phase B — Adapt consumers (Configs, Solutions) + Steps: + 1. Update src/Configs/* (content_trait → dynamics_trait, value names). 2. Update src/Solutions/building.jl. -Checkpoint: test/suite/configs + test/suite/solutions — all green. +Checkpoint: + +```bash +get_test_command test_args=["suite/configs", "suite/solutions"] # MCP +``` ### Phase C — Tests + docs + Steps: + 1. Rename and update test/suite/traits/test_content.jl → test_dynamics.jl. 2. Update docs/api_reference.jl file list. -Checkpoint: full suite green. +Checkpoint (full suite — last phase): + +```bash +get_test_command # MCP, no test_args = full suite +# fallback: julia --project=@. -e 'using Pkg; Pkg.test()' 2>&1 | tee /tmp/rename_final.log +``` ## Human checkpoints + - ⛔ Ask before commit after Phase C. ## Out of scope + Parametrizing AbstractSystem/AbstractFlow by the dynamics trait (see action_plan.md Phase C/D). -``` +```` diff --git a/src/Descriptions/complete.jl b/src/Descriptions/complete.jl index ec0719e6..5ff582d8 100644 --- a/src/Descriptions/complete.jl +++ b/src/Descriptions/complete.jl @@ -45,7 +45,7 @@ ERROR: AmbiguousDescription: the description (:f,) is ambiguous / incorrect Context: description completion ``` -See also: [`CTBase.Descriptions.compute_similarity`](@ref), [`CTBase.Descriptions.find_similar_descriptions`](@ref), [`CTBase.Descriptions.format_description_candidates`](@ref), [`CTBase.Exceptions.AmbiguousDescription`](@ref) +See also: [`CTBase.Descriptions._compute_similarity`](@ref), [`CTBase.Descriptions._find_similar_descriptions`](@ref), [`CTBase.Descriptions._format_description_candidates`](@ref), [`CTBase.Exceptions.AmbiguousDescription`](@ref) """ function complete(list::Symbol...; descriptions::Tuple{Vararg{Description}})::Description n = length(descriptions) @@ -70,8 +70,8 @@ function complete(list::Symbol...; descriptions::Tuple{Vararg{Description}})::De if maximum(table[:, 2]) == 0 # Find similar descriptions for helpful suggestions - similar_descs = find_similar_descriptions(list, descriptions; max_results=5) - all_candidates = format_description_candidates(descriptions; max_show=10) + similar_descs = _find_similar_descriptions(list, descriptions; max_results=5) + all_candidates = _format_description_candidates(descriptions; max_show=10) # Build contextual suggestion suggestion = if !isempty(similar_descs) diff --git a/src/Descriptions/similarity.jl b/src/Descriptions/similarity.jl index c39e58a3..f711d910 100644 --- a/src/Descriptions/similarity.jl +++ b/src/Descriptions/similarity.jl @@ -23,7 +23,7 @@ julia> CTBase.Descriptions.compute_similarity((:x, :y), (:a, :b)) 0.0 ``` """ -function compute_similarity(desc1::Description, desc2::Description)::Float64 +function _compute_similarity(desc1::Description, desc2::Description)::Float64 if isempty(desc1) || isempty(desc2) return 0.0 end @@ -62,7 +62,7 @@ julia> CTBase.Descriptions.find_similar_descriptions((:a,), descriptions) "(:a, :c)" ``` """ -function find_similar_descriptions( +function _find_similar_descriptions( target::Tuple{Vararg{Symbol}}, descriptions::Tuple{Vararg{Description}}; max_results::Int=5, @@ -73,7 +73,7 @@ function find_similar_descriptions( # Compute similarities similarities = [ - (compute_similarity(target, desc), string(desc)) for desc in descriptions + (_compute_similarity(target, desc), string(desc)) for desc in descriptions ] # Sort by similarity (descending) and take top results @@ -118,7 +118,7 @@ julia> CTBase.Descriptions.format_description_candidates(descriptions; max_show= "(:x, :y)" ``` """ -function format_description_candidates( +function _format_description_candidates( descriptions::Tuple{Vararg{Description}}; max_show::Int=5 )::Vector{String} if isempty(descriptions) diff --git a/src/Exceptions/display.jl b/src/Exceptions/display.jl index 6ee1fb80..dc7d21ae 100644 --- a/src/Exceptions/display.jl +++ b/src/Exceptions/display.jl @@ -44,7 +44,7 @@ Filters out Julia stdlib. # Returns - `Vector`: Filtered stacktrace frames """ -function extract_user_frames(st::Vector) +function _extract_user_frames(st::Vector) user_frames = filter(st) do frame file_str = string(frame.file) # Keep frames that are NOT from Julia stdlib or exception display internals @@ -68,7 +68,7 @@ Display an error in a user-friendly format with clear sections and user code loc - `io::IO`: Output stream - `e::CTException`: The exception to display """ -function format_user_friendly_error(io::IO, e::CTException) +function _format_user_friendly_error(io::IO, e::CTException) #println(io, "\n" * "━"^70) _print_ansi_styled(io, "Control Toolbox Error\n", :red, true) #println(io, "─"^28) @@ -232,7 +232,7 @@ function format_user_friendly_error(io::IO, e::CTException) end # Add user code location - user_frames = extract_user_frames(stacktrace(catch_backtrace())) + user_frames = _extract_user_frames(stacktrace(catch_backtrace())) if !isempty(user_frames) println(io, "📍 In your code:") # Show up to 3 most relevant user frames @@ -262,7 +262,7 @@ Custom error display for IncorrectArgument. Shows user-friendly format with enriched information. """ function Base.showerror(io::IO, e::IncorrectArgument) - return format_user_friendly_error(io, e) + return _format_user_friendly_error(io, e) end """ @@ -271,7 +271,7 @@ end Custom error display for PreconditionError. """ function Base.showerror(io::IO, e::PreconditionError) - return format_user_friendly_error(io, e) + return _format_user_friendly_error(io, e) end """ @@ -280,7 +280,7 @@ end Custom error display for NotImplemented. """ function Base.showerror(io::IO, e::NotImplemented) - return format_user_friendly_error(io, e) + return _format_user_friendly_error(io, e) end """ @@ -289,7 +289,7 @@ end Custom error display for ParsingError. """ function Base.showerror(io::IO, e::ParsingError) - return format_user_friendly_error(io, e) + return _format_user_friendly_error(io, e) end """ @@ -298,7 +298,7 @@ end Custom error display for AmbiguousDescription. """ function Base.showerror(io::IO, e::AmbiguousDescription) - return format_user_friendly_error(io, e) + return _format_user_friendly_error(io, e) end """ @@ -307,7 +307,7 @@ end Custom error display for ExtensionError. """ function Base.showerror(io::IO, e::ExtensionError) - return format_user_friendly_error(io, e) + return _format_user_friendly_error(io, e) end """ @@ -316,5 +316,5 @@ end Custom error display for SolverFailure. """ function Base.showerror(io::IO, e::SolverFailure) - return format_user_friendly_error(io, e) + return _format_user_friendly_error(io, e) end diff --git a/src/Extensions/Extensions.jl b/src/Extensions/Extensions.jl index 35b7e6a7..78d9d86a 100644 --- a/src/Extensions/Extensions.jl +++ b/src/Extensions/Extensions.jl @@ -332,7 +332,10 @@ function run_tests(; kwargs...) return run_tests(TestRunnerTag(); kwargs...) end -# Export public API (only user-facing functions, tags are internal) +# Export public API export automatic_reference_documentation, postprocess_coverage, run_tests +export AbstractDocumenterReferenceTag, DocumenterReferenceTag +export AbstractCoveragePostprocessingTag, CoveragePostprocessingTag +export AbstractTestRunnerTag, TestRunnerTag end # module diff --git a/test/suite/descriptions/test_similarity.jl b/test/suite/descriptions/test_similarity.jl index cb61b2d1..4c27fb17 100644 --- a/test/suite/descriptions/test_similarity.jl +++ b/test/suite/descriptions/test_similarity.jl @@ -13,67 +13,67 @@ function test_similarity() @testset "compute_similarity - basic cases" begin # Identical descriptions - @test CTBase.Descriptions.compute_similarity((:a, :b), (:a, :b)) == 1.0 + @test CTBase.Descriptions._compute_similarity((:a, :b), (:a, :b)) == 1.0 # Partial overlap - @test CTBase.Descriptions.compute_similarity((:a, :b), (:a, :c)) == 1 / 3 - @test CTBase.Descriptions.compute_similarity((:a, :c), (:a, :b, :c)) == 2 / 3 + @test CTBase.Descriptions._compute_similarity((:a, :b), (:a, :c)) == 1 / 3 + @test CTBase.Descriptions._compute_similarity((:a, :c), (:a, :b, :c)) == 2 / 3 # No overlap - @test CTBase.Descriptions.compute_similarity((:x, :y), (:a, :b)) == 0.0 + @test CTBase.Descriptions._compute_similarity((:x, :y), (:a, :b)) == 0.0 end @testset "compute_similarity - edge cases" begin # Empty tuples - @test CTBase.Descriptions.compute_similarity((), ()) == 0.0 - @test CTBase.Descriptions.compute_similarity((), (:a,)) == 0.0 - @test CTBase.Descriptions.compute_similarity((:a,), ()) == 0.0 + @test CTBase.Descriptions._compute_similarity((), ()) == 0.0 + @test CTBase.Descriptions._compute_similarity((), (:a,)) == 0.0 + @test CTBase.Descriptions._compute_similarity((:a,), ()) == 0.0 # Single-element tuples - @test CTBase.Descriptions.compute_similarity((:a,), (:a,)) == 1.0 - @test CTBase.Descriptions.compute_similarity((:a,), (:b,)) == 0.0 - @test CTBase.Descriptions.compute_similarity((:a,), (:a, :b)) == 0.5 + @test CTBase.Descriptions._compute_similarity((:a,), (:a,)) == 1.0 + @test CTBase.Descriptions._compute_similarity((:a,), (:b,)) == 0.0 + @test CTBase.Descriptions._compute_similarity((:a,), (:a, :b)) == 0.5 # Large descriptions desc1 = (:a, :b, :c, :d, :e) desc2 = (:a, :b, :c, :d, :e) - @test CTBase.Descriptions.compute_similarity(desc1, desc2) == 1.0 + @test CTBase.Descriptions._compute_similarity(desc1, desc2) == 1.0 desc3 = (:a, :b, :c) desc4 = (:d, :e, :f) - @test CTBase.Descriptions.compute_similarity(desc3, desc4) == 0.0 + @test CTBase.Descriptions._compute_similarity(desc3, desc4) == 0.0 end @testset "compute_similarity - mathematical properties" begin # Symmetry: sim(A, B) == sim(B, A) desc1 = (:a, :b, :c) desc2 = (:b, :c, :d) - @test CTBase.Descriptions.compute_similarity(desc1, desc2) == - CTBase.Descriptions.compute_similarity(desc2, desc1) + @test CTBase.Descriptions._compute_similarity(desc1, desc2) == + CTBase.Descriptions._compute_similarity(desc2, desc1) # Reflexivity: sim(A, A) == 1.0 - @test CTBase.Descriptions.compute_similarity(desc1, desc1) == 1.0 - @test CTBase.Descriptions.compute_similarity(desc2, desc2) == 1.0 + @test CTBase.Descriptions._compute_similarity(desc1, desc1) == 1.0 + @test CTBase.Descriptions._compute_similarity(desc2, desc2) == 1.0 # Range: 0.0 <= sim(A, B) <= 1.0 desc3 = (:x, :y) desc4 = (:a, :b, :c, :d) - sim = CTBase.Descriptions.compute_similarity(desc3, desc4) + sim = CTBase.Descriptions._compute_similarity(desc3, desc4) @test 0.0 <= sim <= 1.0 end @testset "Type stability - compute_similarity" begin # Basic case - @test (@inferred CTBase.Descriptions.compute_similarity((:a, :b), (:a, :c))) isa + @test (@inferred CTBase.Descriptions._compute_similarity((:a, :b), (:a, :c))) isa Float64 # Edge cases - @test (@inferred CTBase.Descriptions.compute_similarity((), ())) isa Float64 - @test (@inferred CTBase.Descriptions.compute_similarity((:a,), (:b,))) isa + @test (@inferred CTBase.Descriptions._compute_similarity((), ())) isa Float64 + @test (@inferred CTBase.Descriptions._compute_similarity((:a,), (:b,))) isa Float64 # Verify always returns Float64 - result = CTBase.Descriptions.compute_similarity((:a, :b, :c), (:b, :c, :d)) + result = CTBase.Descriptions._compute_similarity((:a, :b, :c), (:b, :c, :d)) @test result isa Float64 end @@ -84,7 +84,7 @@ function test_similarity() @testset "find_similar_descriptions - basic" begin descriptions = ((:a, :b), (:a, :c), (:x, :y)) target = (:a,) - similar = CTBase.Descriptions.find_similar_descriptions(target, descriptions) + similar = CTBase.Descriptions._find_similar_descriptions(target, descriptions) @test length(similar) == 2 @test "(:a, :b)" in similar @test "(:a, :c)" in similar @@ -92,7 +92,7 @@ function test_similarity() # No similar descriptions @test isempty( - CTBase.Descriptions.find_similar_descriptions((:z,), descriptions) + CTBase.Descriptions._find_similar_descriptions((:z,), descriptions) ) end @@ -100,42 +100,42 @@ function test_similarity() # Test max_results boundary - exactly max_results descriptions = ((:a, :b), (:a, :c), (:a, :d), (:a, :e), (:a, :f)) target = (:a,) - similar = CTBase.Descriptions.find_similar_descriptions( + similar = CTBase.Descriptions._find_similar_descriptions( target, descriptions; max_results=5 ) @test length(similar) == 5 # More than max_results available descriptions2 = ((:a, :b), (:a, :c), (:a, :d), (:a, :e), (:a, :f), (:a, :g)) - similar2 = CTBase.Descriptions.find_similar_descriptions( + similar2 = CTBase.Descriptions._find_similar_descriptions( target, descriptions2; max_results=3 ) @test length(similar2) == 3 # Less than max_results available descriptions3 = ((:a, :b), (:a, :c)) - similar3 = CTBase.Descriptions.find_similar_descriptions( + similar3 = CTBase.Descriptions._find_similar_descriptions( target, descriptions3; max_results=5 ) @test length(similar3) == 2 # All zero similarity (should return empty) descriptions4 = ((:x, :y), (:z, :w)) - similar4 = CTBase.Descriptions.find_similar_descriptions((:a,), descriptions4) + similar4 = CTBase.Descriptions._find_similar_descriptions((:a,), descriptions4) @test isempty(similar4) end @testset "find_similar_descriptions - edge cases" begin # Empty descriptions catalog - @test isempty(CTBase.Descriptions.find_similar_descriptions((:a,), ())) + @test isempty(CTBase.Descriptions._find_similar_descriptions((:a,), ())) # Empty target descriptions = ((:a, :b), (:c, :d)) - @test isempty(CTBase.Descriptions.find_similar_descriptions((), descriptions)) + @test isempty(CTBase.Descriptions._find_similar_descriptions((), descriptions)) # Single description in catalog descriptions2 = ((:a, :b),) - similar = CTBase.Descriptions.find_similar_descriptions((:a,), descriptions2) + similar = CTBase.Descriptions._find_similar_descriptions((:a,), descriptions2) @test length(similar) == 1 @test "(:a, :b)" in similar end @@ -144,15 +144,15 @@ function test_similarity() descriptions = ((:a, :b), (:a, :c), (:x, :y)) target = (:a,) - @test (@inferred CTBase.Descriptions.find_similar_descriptions( + @test (@inferred CTBase.Descriptions._find_similar_descriptions( target, descriptions )) isa Vector{String} - @test (@inferred CTBase.Descriptions.find_similar_descriptions( + @test (@inferred CTBase.Descriptions._find_similar_descriptions( target, descriptions; max_results=3 )) isa Vector{String} # Edge cases - @test (@inferred CTBase.Descriptions.find_similar_descriptions((), ())) isa + @test (@inferred CTBase.Descriptions._find_similar_descriptions((), ())) isa Vector{String} end @@ -162,13 +162,13 @@ function test_similarity() @testset "format_description_candidates - basic" begin descriptions = ((:a, :b), (:a, :c), (:x, :y), (:p, :q), (:r, :s), (:t, :u)) - formatted = CTBase.Descriptions.format_description_candidates(descriptions) + formatted = CTBase.Descriptions._format_description_candidates(descriptions) @test length(formatted) == 5 # default max_show=5 @test formatted[1] == "(:a, :b)" @test formatted[5] == "(:r, :s)" # Custom max_show - formatted3 = CTBase.Descriptions.format_description_candidates( + formatted3 = CTBase.Descriptions._format_description_candidates( descriptions; max_show=3 ) @test length(formatted3) == 3 @@ -179,27 +179,27 @@ function test_similarity() @testset "format_description_candidates - boundaries" begin # Exactly max_show descriptions descriptions = ((:a, :b), (:c, :d), (:e, :f), (:g, :h), (:i, :j)) - formatted = CTBase.Descriptions.format_description_candidates( + formatted = CTBase.Descriptions._format_description_candidates( descriptions; max_show=5 ) @test length(formatted) == 5 # Less than max_show descriptions2 = ((:a, :b), (:c, :d)) - formatted2 = CTBase.Descriptions.format_description_candidates( + formatted2 = CTBase.Descriptions._format_description_candidates( descriptions2; max_show=5 ) @test length(formatted2) == 2 # More than max_show descriptions3 = ((:a, :b), (:c, :d), (:e, :f), (:g, :h), (:i, :j), (:k, :l)) - formatted3 = CTBase.Descriptions.format_description_candidates( + formatted3 = CTBase.Descriptions._format_description_candidates( descriptions3; max_show=3 ) @test length(formatted3) == 3 # max_show=1 - formatted4 = CTBase.Descriptions.format_description_candidates( + formatted4 = CTBase.Descriptions._format_description_candidates( descriptions3; max_show=1 ) @test length(formatted4) == 1 @@ -208,11 +208,11 @@ function test_similarity() @testset "format_description_candidates - edge cases" begin # Empty descriptions - @test isempty(CTBase.Descriptions.format_description_candidates(())) + @test isempty(CTBase.Descriptions._format_description_candidates(())) # Single description descriptions = ((:a, :b),) - formatted = CTBase.Descriptions.format_description_candidates(descriptions) + formatted = CTBase.Descriptions._format_description_candidates(descriptions) @test length(formatted) == 1 @test formatted[1] == "(:a, :b)" end @@ -220,15 +220,15 @@ function test_similarity() @testset "Type stability - format_description_candidates" begin descriptions = ((:a, :b), (:c, :d), (:e, :f)) - @test (@inferred CTBase.Descriptions.format_description_candidates( + @test (@inferred CTBase.Descriptions._format_description_candidates( descriptions )) isa Vector{String} - @test (@inferred CTBase.Descriptions.format_description_candidates( + @test (@inferred CTBase.Descriptions._format_description_candidates( descriptions; max_show=2 )) isa Vector{String} # Edge case - @test (@inferred CTBase.Descriptions.format_description_candidates(())) isa + @test (@inferred CTBase.Descriptions._format_description_candidates(())) isa Vector{String} end end diff --git a/test/suite/exceptions/test_display.jl b/test/suite/exceptions/test_display.jl index c26aeb01..12867a9a 100644 --- a/test/suite/exceptions/test_display.jl +++ b/test/suite/exceptions/test_display.jl @@ -202,12 +202,12 @@ function test_exception_display() error("test error") catch e st = stacktrace(catch_backtrace()) - filtered = CTBase.Exceptions.extract_user_frames(st) + filtered = CTBase.Exceptions._extract_user_frames(st) # Should return some frames (non-empty in normal test environment) @test filtered isa Vector # The filtering should work without errors - @test_nowarn CTBase.Exceptions.extract_user_frames(st) + @test_nowarn CTBase.Exceptions._extract_user_frames(st) end end diff --git a/test/suite/extensions/test_extensions_enriched.jl b/test/suite/extensions/test_extensions_enriched.jl index 6bfbf3b2..11dc7907 100644 --- a/test/suite/extensions/test_extensions_enriched.jl +++ b/test/suite/extensions/test_extensions_enriched.jl @@ -4,6 +4,13 @@ using Test using CTBase using Main.TestOptions: VERBOSE, SHOWTIMING +# Fake tag types for extension stub testing (testing-creation.md §6). +# These subtype the abstract tags but are unknown to any extension, +# so the stubs always throw ExtensionError regardless of which extensions are loaded. +struct FakeDocumenterReferenceTag <: CTBase.AbstractDocumenterReferenceTag end +struct FakeCoveragePostprocessingTag <: CTBase.AbstractCoveragePostprocessingTag end +struct FakeTestRunnerTag <: CTBase.AbstractTestRunnerTag end + function test_extensions_enriched() @testset verbose = VERBOSE showtiming = SHOWTIMING "Enriched Extension Errors" begin @@ -38,46 +45,21 @@ function test_extensions_enriched() @testset "Extension Function Error Handling" begin # Test automatic_reference_documentation error @testset "automatic_reference_documentation" begin - @test_throws Exception CTBase.automatic_reference_documentation( - CTBase.DocumenterReferenceTag() + @test_throws CTBase.ExtensionError CTBase.automatic_reference_documentation( + FakeDocumenterReferenceTag() ) - - # Test that it throws some kind of exception (ExtensionError or UndefVarError) - try - CTBase.automatic_reference_documentation( - CTBase.DocumenterReferenceTag() - ) - @test false # Should not reach here - catch e - # Accept either ExtensionError (if function is available) or UndefVarError (if not) - @test e isa CTBase.ExtensionError || e isa UndefVarError - end end # Test postprocess_coverage error @testset "postprocess_coverage" begin - @test_throws Exception CTBase.postprocess_coverage( - CTBase.CoveragePostprocessingTag() + @test_throws CTBase.ExtensionError CTBase.postprocess_coverage( + FakeCoveragePostprocessingTag() ) - - try - CTBase.postprocess_coverage(CTBase.CoveragePostprocessingTag()) - @test false # Should not reach here - catch e - @test e isa CTBase.ExtensionError || e isa UndefVarError - end end # Test run_tests error @testset "run_tests" begin - @test_throws Exception CTBase.run_tests(CTBase.TestRunnerTag()) - - try - CTBase.run_tests(CTBase.TestRunnerTag()) - @test false # Should not reach here - catch e - @test e isa CTBase.ExtensionError || e isa UndefVarError - end + @test_throws CTBase.ExtensionError CTBase.run_tests(FakeTestRunnerTag()) end end