test: add degenerate-triangle limiting-angle and edge-DOF guard tests

Finding-F and Finding-G from doc/reviewer/external-audit-2026-05-30.md
(java-port-audit missing-test items 1 and 2).

Finding-F — Degenerate triangle: limiting angles (item 1)
  test_euclidean_functional.cpp:
    DegenerateTriangle_LimitingAngles_L{12,23,31}TooLong
      — verifies α_opposite = π, other two = 0, valid = false
      for all three edge-over-long cases
    DegenerateTriangle_GradientPicksUpPiCorner
      — end-to-end mesh test: forces effective l12 >> l23+l31 via
        lambda0, evaluates gradient, asserts G_v3 = π (not 2π from
        a skipped degenerate face) and G_v1=G_v2 = 2π

  test_spherical_functional.cpp:
    DegenerateTriangle_LimitingAngles_S{12,23,31}TooLong
      — same coverage for spherical_angles()

Finding-G — euclidean_hessian edge-DOF guard (item 2)
  test_euclidean_hessian.cpp:
    EdgeDOFGuard_Throws
      — assign_euclidean_all_dof_indices + euclidean_hessian → throw
    EdgeDOFGuard_VertexOnlyDoesNotThrow
      — vertex-only layout → no throw (regression guard)

275/275 CGAL tests pass, 0 failed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-05-31 00:31:07 +02:00
parent d600a19ed9
commit 2a5feb7773
4 changed files with 182 additions and 3 deletions

View File

@@ -214,3 +214,42 @@ TEST(EuclideanHessian, FDCheck_MixedPinnedVertices)
EXPECT_TRUE(hessian_check_euclidean(mesh, x, maps))
<< "FD Hessian check failed for mixed pinned/variable vertices";
}
// ════════════════════════════════════════════════════════════════════════════
// Edge-DOF guard (Finding-G, java-port-audit item 2)
//
// euclidean_hessian() (vertex-only cotangent Laplacian) must throw
// std::logic_error when any edge DOF is active. Without this guard the
// function would silently return a Hessian with zero rows/cols for the
// edge DOFs, causing SimplicialLDLT to fail in a hard-to-diagnose way.
// ════════════════════════════════════════════════════════════════════════════
TEST(EuclideanHessian, EdgeDOFGuard_Throws)
{
auto mesh = make_tetrahedron();
auto maps = setup_euclidean_maps(mesh);
compute_euclidean_lambda0_from_mesh(mesh, maps);
assign_euclidean_all_dof_indices(mesh, maps); // assigns vertex + edge DOFs
const int n = euclidean_dimension(mesh, maps);
std::vector<double> x(static_cast<std::size_t>(n), 0.0);
EXPECT_THROW(euclidean_hessian(mesh, x, maps), std::logic_error)
<< "euclidean_hessian must throw when edge DOFs are present";
}
TEST(EuclideanHessian, EdgeDOFGuard_VertexOnlyDoesNotThrow)
{
// Vertex-only layout must NOT trigger the guard.
auto mesh = make_tetrahedron();
auto maps = setup_euclidean_maps(mesh);
compute_euclidean_lambda0_from_mesh(mesh, maps);
auto gauge = *mesh.vertices().begin();
assign_euclidean_vertex_dof_indices(mesh, maps, gauge);
const int n = euclidean_dimension(mesh, maps);
std::vector<double> x(static_cast<std::size_t>(n), 0.0);
EXPECT_NO_THROW(euclidean_hessian(mesh, x, maps))
<< "euclidean_hessian must not throw for vertex-only DOF layout";
}