Files
OrcaSlicer/tests/libslic3r/test_filament_id_succession.cpp
SoftFever 2c0867619c v3.0: content-addressed mint tooling, succession runtime, W1 hardening
Implements phase v3.0 of filament_id_plan_v3.md - tooling and client
prerequisites. No filament_id values change in this commit.

Tooling (scripts/assign_filament_ids.py; 106 unit tests):
- The mint key becomes "filament_product/<filament_vendor>/<filament_type>/
  <family_name>", resolved loader-faithfully from the declarer's flattened
  config - bundle-independent and content-addressed; identity fixes re-id
  by design and are made safe by the succession ledger. --mint now takes
  the triple.
- The snapshot gains "triples" and "triple_exceptions" sections, folded
  into the equality gate: any vendor/type/name change surfaces as a
  reviewable snapshot diff. Check 3 rewritten to triple-mint conformance
  with (id, triple) grandfathering; check 5 generalized from OFL generics
  to every OFL-riding vendor preset; new check 8 (triple integrity) and
  check 9 (succession integrity).
- The retired ledger moves to resources/profiles/retired_filament_ids.json
  so it ships with the app; schema {claims, successor} plus cross-island
  "hints". The 14 v1 entries are migrated with mode-rule successors.
  Vanished ids in a foreign island's space (GF*/QD_*) are RELEASED with a
  hint instead of retired: the island catalog owns them and may
  legitimately (re)ship them, which check 4 must never block.
- New maintenance modes: --remint VENDOR, --drop-redundant-ids VENDOR,
  --add-hint "OLD=NEW", and --update-snapshot --forget-never-shipped FILE
  (never-shipped ids drop from lineage; chains splice through them).

Runtime (C++):
- Succession helpers in libslic3r/Preset (pure chain-follow + lazy ledger
  load from resources), consulted only on resolution miss in
  get_filament_by_filament_id, both AMS sync predicates,
  add_ams_filaments, setting_id_to_type, and the calibration-history
  lookup; behavior is byte-identical while the ledger has no matching
  entry. Catch2 coverage in tests/libslic3r.
- W1 hardening: check_ams_filament_valid no longer remote-wipes trays or
  rewrites temps for P-shaped ids that a system preset carries (ten such
  system ids ship today); the size==8 && [0]=='P' assert is relaxed; the
  unguarded filament_list find deref in the temp-equation check returns
  non-destructively on a miss.
- MoonrakerPrinterAgent: the 23 hardcoded OFL generic ids are replaced by
  a runtime lookup of "Generic <family> @System" (id-equivalent on
  today's shipped profiles, follows future re-mints automatically);
  scripts/test_moonraker_lane_data.py derives its expectations from the
  shipped profiles and gains --check-ofl-map.

Profile data (W3 - type is now a key component, so type bugs are fixed
before any re-mint):
- 17 same-name-different-type groups corrected across 50 files (OFL
  Generic PETG-CF/PE-CF/PP-CF; Flashforge ASA Basic/ASA-CF/ABS-CF/HIPS/
  PAHT-CF/PLA Silk; FusRock PAHT; Creality Generic PA6-CF; InfiMech PETG;
  Anycubic TPU 95A / TPU for ACE; Prusa Generic PA-CF/PLA-CF) - every
  value validated against MaterialType::all(); 3 Snapmaker U1 roots gain
  their missing filament_vendor. Flattened-config equivalence vs the
  previous commit: exactly the 54 intended diffs, zero BBL.
- doc/developer-reference/filament_id.md rewritten for the v3 rule.

Ambiguous type divergences (Prusa Generic TPU/TPU HF FLEX-vs-TPU, FusRock
S-Multi/S-PAHT) and all same-type vendor-tag divergences are deferred to
the v3.2 vendor worksheets, catalogued with analysis.
2026-07-04 16:28:56 +08:00

62 lines
2.6 KiB
C++

#include <catch2/catch_all.hpp>
#include "libslic3r/Preset.hpp"
using namespace Slic3r;
// Pure-function coverage of the succession chain walk (follow_filament_id_succession).
// The ledger-file loader (filament_id_succession_map) is deliberately untested here so
// the test carries no resources-dir dependency.
TEST_CASE("filament_id succession follows forwarding chains", "[Preset][filament_id]") {
SECTION("empty map returns empty") {
const std::map<std::string, std::string> empty;
CHECK(follow_filament_id_succession("OFabc123", empty).empty());
CHECK(follow_filament_id_succession("", empty).empty());
}
const std::map<std::string, std::string> forwards = {
{"OFold001", "OFnew001"},
{"OFchainA", "OFchainB"},
{"OFchainB", "OFchainC"},
{"OFchainC", "OFlive00"},
};
SECTION("absent id returns empty") {
CHECK(follow_filament_id_succession("GFL99", forwards).empty());
}
SECTION("single hop resolves to the successor") {
CHECK(follow_filament_id_succession("OFold001", forwards) == "OFnew001");
}
SECTION("multi-hop chain resolves to the live end") {
CHECK(follow_filament_id_succession("OFchainA", forwards) == "OFlive00");
CHECK(follow_filament_id_succession("OFchainB", forwards) == "OFlive00");
CHECK(follow_filament_id_succession("OFchainC", forwards) == "OFlive00");
}
}
// Cycles are a ledger-validation error (script check 9) and never ship, but the client
// walk must still terminate on corrupt data: it stops at the last id reached before any
// id would repeat.
TEST_CASE("filament_id succession terminates on malformed cycles", "[Preset][filament_id]") {
SECTION("two-node cycle returns the last id before revisiting") {
const std::map<std::string, std::string> cycle = {{"A", "B"}, {"B", "A"}};
CHECK(follow_filament_id_succession("A", cycle) == "B");
CHECK(follow_filament_id_succession("B", cycle) == "A");
}
SECTION("cycle entered through a tail stops before re-entering it") {
// T -> A -> B -> C -> A: stops at C, the last id reached before repeating A.
const std::map<std::string, std::string> cycle = {
{"T", "A"}, {"A", "B"}, {"B", "C"}, {"C", "A"}};
CHECK(follow_filament_id_succession("T", cycle) == "C");
CHECK(follow_filament_id_succession("A", cycle) == "C");
}
SECTION("self-loop returns the id itself") {
const std::map<std::string, std::string> loop = {{"A", "A"}};
CHECK(follow_filament_id_succession("A", loop) == "A");
}
}