test(viewer-3d): cover the plate, mesh and grid scene guards - #505
Merged
Conversation
`classify_scene` validates every scene record before any HTML is produced, so a malformed plate, mesh or structural grid fails atomically instead of disappearing inside the generated JS. Most of that refusal logic had only ever been reached on its happy path: one plate frame, one well-formed mesh, one valid set of grid bounds. Eight tests close that. The plate frame must be a nonzero, orthogonal, right-handed basis — the flipped-normal case is the interesting one, because `[0,0,-1]` is a perfectly good normal that merely mirrors the plate, so nothing downstream objects. The nonzero-direction guard has to fire first for a separate reason: with `uDir` zero the orthogonality and handedness tests both divide by zero, and `NaN > 1e-6` is false, so both wave the frame through. Also covered: positive thickness and three-point outline arity; holes that overlap or exactly touch, and the legacy `uv` spelling of a hole centre, which nothing covered and which would have looked free to delete; a `holes` value that is present but not an array, refused rather than read as "no holes"; mesh positions that do not divide into xyz triples, a coordinate that is not finite, index lists that do not divide into triangles, and an index past the last vertex; and grid bounds with equal or inverted extents on either axis, a non-planar axis direction, and a level with no label or a non-numeric elevation. Every added test was proven to fail under a mutation of the guard it covers; the mutations are listed in the pull request. Two neighbouring cases were left out deliberately because `renders_parametric_connection_solids_and_structural_references_with_receipts` already reaches them: an axis with no label, and an empty `levels` list.
Contributor
Author
|
@codex review Generated by Claude Code |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
classify_scenerefuses a malformed scene record before any HTML is produced, but most of that refusal logic had only ever been reached on its happy path — one plate frame, one well-formed mesh, one valid set of grid bounds. Eight tests close that, incli/src/render/viewer_3d.rs.Type of change
Decalog check
What was uncovered, and why it matters
validate_plate,validate_meshandvalidate_grid_boundsare the guards standing between a producer's scene and a viewer that would otherwise draw something wrong in silence. Before this PR the only plate frame ever rendered under test was the axis-aligneduDirx /vDiry /normalz one, the only mesh was well formed, and the only grid bounds were valid.Two of the newly covered cases are worth naming:
[0,0,-1]is a perfectly good unit normal to the same outline, so nothing downstream objects — the plate is simply mirrored, and the extrusion grows away from the face the author drew. Only this guard catches it.uDirzero,dot3(u,v)/(ul*vl)is0/0, andNaN > 1e-6is false, so the orthogonality test waves it through — and so does the handedness test, for the same reason. Delete the nonzero guard and a degenerate frame renders.Mutation evidence
Each row: the guard was broken as shown,
cargo test --bin aware render::viewer_3dwas run, the listed test went red, and the guard was restored. All twenty mutations were killed.viewer_3d.rsif ul <= 1e-9 || vl <= 1e-9 || nl <= 1e-9 {→if false {a_plate_frame_must_be_nonzero_orthogonal_and_right_handed> 1.0e-6→> 1.0e9< 1.0 - 1.0e-6→< -2.0positive_number(…thicknessMm…)→finite_number(…)a_plate_needs_a_positive_thickness_and_three_outline_pointsoutline.len() >= 3→outline.len() >= 2holesarm returns&[]instead ofErra_non_array_holes_field_is_refused_rather_than_treated_as_nonedistance <= (d + other)/2 + 1e-9→distance < 0.0plate_holes_must_stay_clear_of_each_other_and_accept_the_uv_aliashole.get("center").or_else(|| hole.get("uv"))→hole.get("center")len() % 3 == 0a_mesh_needs_complete_triples_and_indices_that_name_a_real_vertexlen() >= 9positionsdeletedlen() % 3 == 0(*value as usize) < vertex_count→truemin_x >= max_x || min_y >= max_y→min_x > max_x || min_y > max_ya_structural_grid_needs_bounds_with_increasing_extents_on_both_axesmin_x >= max_x(Y half deleted)Some("x" | "y") => {}→Some(_) => {}grid_axes_and_levels_must_be_planar_numeric_and_labelledlabelcheck →if false {renders_parametric_…_with_receiptslevelscheck →if false {renders_parametric_…_with_receiptsonlyunknown_records_are_exhaustively_unsupportedonly"plate" => validate_plate(…)arm deletedRows 18 and 19 were killed by pre-existing tests alone. That is the point of running them: both cases turned out to be covered already, so the tests I had drafted for them were dropped rather than shipped as duplicates. Row 17 is covered for an axis label by the existing test and for a level label by the new one.
Row 20 is the guard on the guards: it proves the plate fixture reaches
validate_plateat all, which is what makes the four refusal tests non-vacuous.the_plate_fixture_the_refusal_tests_edit_is_itself_validasserts the unedited fixture renders and is receipted, so no refusal below it can be a broken fixture rather than the guard talking. Each of the mesh and grid tests carries the same positive control inline.Deletions: none, and why
Nothing in this area asserts nothing. The nearest candidate was
renders_a_tessellated_mesh_element, two of whose four assertions (html.contains("BufferGeometry"),html.contains("setIndex")) hold for any scene at all, because they pin strings in the static template rather than anything about the mesh under test. They are kept: the renderer's contract genuinely includes "the shipped template carries mesh support", and stripping a template pin from a renderer unattended is a judgement call, not hygiene. Flagging it here instead.Gates
Run from
cli/on the pinned toolchain (1.95.0), withclang libsecret-1-dev libdbus-1-dev pkg-configinstalled as CI does:cargo fmt --all -- --check— passcargo clippy --all-targets -- -D warnings— passcargo test— 1641 passed, 0 failedNotes for reviewers
The plate, mesh and grid fixtures are each a single valid scene that every refusal case then breaks in exactly one place, so a failure names the guard rather than the fixture.
plate_holes_must_stay_clear_of_each_other_and_accept_the_uv_aliasalso asserts that auvhole is still bounds-checked — the alias is a spelling, not a way past containment.🤖 Generated with Claude Code
https://claude.ai/code/session_0124mWeBTCLtG1xnCf1hGq4m
Generated by Claude Code