Files
OrcaSlicer/tests/libslic3r/test_sketchimport.cpp
T
Hanif Koh 32d92a55d3 Make Unit Tests That Could Never Fail Check Real Behavior
Rework tests whose assertions held whatever the production code did: inverted or self-referential checks, checks on values the test had just set, branches that never ran, symmetric fixtures that hid the behavior under test, font paths that skipped silently, and Voronoi reproducers that only exercised boost. Each now asserts the behavior its name describes.
2026-10-19 17:02:49 +08:00

100 lines
3.9 KiB
C++

#include <algorithm>
#include <catch2/catch_all.hpp> // mainline OrcaSlicer ships Catch2 v3 (v2 was catch2/catch.hpp)
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include "libslic3r/CAD/SketchImport.hpp"
#include "test_utils.hpp" // ScopedTemporaryFile
#include <cstdlib>
#include <fstream>
#include "libslic3r/Point.hpp"
#include <string>
using namespace Slic3r;
// A 10x10 mm filled square, on disk because nanosvg reads from a file. The path
// must come from the system temp dir: a hardcoded /tmp is not writable on
// Windows, where the stream fails silently and the parse then sees no file.
static void write_square_svg(const std::string& path)
{
std::ofstream f(path);
f << "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10mm\" height=\"10mm\" "
"viewBox=\"0 0 10 10\">"
"<path d=\"M0,0 L10,0 L10,10 L0,10 Z\" fill=\"#000000\"/></svg>";
REQUIRE(f.good());
}
TEST_CASE("svg_to_regions parses a filled path into a region", "[SketchImport]")
{
ScopedTemporaryFile square(".svg");
write_square_svg(square.string());
ImportRegions regs = svg_to_regions(square.string(), 1.0);
REQUIRE(regs.size() >= 1);
// Outer contour present with at least a few vertices.
REQUIRE(regs[0].size() >= 1);
REQUIRE(regs[0][0].size() >= 4);
// Centred on the origin: bbox half-extent ~5 mm on each side.
double hi = 0.0;
for (const auto& region : regs)
for (const auto& contour : region)
for (const Vec2d& p : contour)
hi = std::max(hi, std::max(std::abs(p.x()), std::abs(p.y())));
REQUIRE(hi > 3.0); // not collapsed
REQUIRE(hi < 8.0); // ~5 mm half-size after centring
}
TEST_CASE("svg_to_regions rejects bad input gracefully", "[SketchImport]")
{
ScopedTemporaryFile square(".svg");
write_square_svg(square.string());
ScopedTemporaryFile missing(".svg"); // name reserved, never written
REQUIRE(svg_to_regions("", 1.0).empty());
REQUIRE(svg_to_regions(missing.string(), 1.0).empty());
REQUIRE(svg_to_regions(square.string(), 0.0).empty()); // scale<=0
}
TEST_CASE("transform_regions moves and scales independently", "[SketchImport]")
{
ImportRegions r = {{ {Vec2d(-1,-1), Vec2d(1,-1), Vec2d(1,1), Vec2d(-1,1)} }};
ImportRegions t = transform_regions(r, Vec2d(10, 20), 2.0, 3.0);
REQUIRE(t.size() == 1);
REQUIRE(t[0][0].size() == 4);
// (-1,-1) -> (-1*2+10, -1*3+20) = (8, 17)
REQUIRE_THAT(t[0][0][0].x(), Catch::Matchers::WithinAbs(8.0, 1e-9));
REQUIRE_THAT(t[0][0][0].y(), Catch::Matchers::WithinAbs(17.0, 1e-9));
// (1,1) -> (1*2+10, 1*3+20) = (12, 23)
REQUIRE_THAT(t[0][0][2].x(), Catch::Matchers::WithinAbs(12.0, 1e-9));
REQUIRE_THAT(t[0][0][2].y(), Catch::Matchers::WithinAbs(23.0, 1e-9));
// identity is a no-op
ImportRegions id = transform_regions(r, Vec2d(0,0), 1.0, 1.0);
REQUIRE_THAT(id[0][0][1].x(), Catch::Matchers::WithinAbs(1.0, 1e-9));
}
TEST_CASE("text_to_regions vectorizes glyphs with counters", "[SketchImport]")
{
// The bundled font, from the source tree: PROFILES_DIR is <repo>/resources/profiles.
const std::string font = std::string(PROFILES_DIR) + "/../fonts/HarmonyOS_Sans_SC_Regular.ttf";
REQUIRE(std::ifstream(font).good());
// Bad input is rejected without throwing.
REQUIRE(text_to_regions("", 10.0, font).empty());
REQUIRE(text_to_regions("A", 0.0, font).empty());
// 'A' has one triangular counter -> a region with an outer + 1 hole.
ImportRegions a = text_to_regions("A", 12.0, font);
REQUIRE(a.size() >= 1);
bool has_hole = false;
for (const auto& region : a)
if (region.size() >= 2) has_hole = true;
REQUIRE(has_hole);
// Two letters produce more regions than one.
ImportRegions ab = text_to_regions("AB", 12.0, font);
REQUIRE(ab.size() >= a.size());
}