Files
OrcaSlicer/scripts/tests/test_filament_id.py
SoftFever 4aa0e1d60b Translate filament ids at the printer boundary
Orca content-addresses every system filament, Bambu's included, but a printer,
its AMS and its vendor's cloud know only that vendor's own catalog ids. The
printer agent now translates between the two: outbound MQTT and FTP traffic, the
AMS mapping sent with a print job, and the ids written into a 3mf bound for the
printer all leave in the printer's own ids, while status messages, loaded
projects and SD-card prints arrive in Orca's. An id with no mapping passes
through unchanged, and an agent whose printers already speak Orca's ids
translates nothing at all.

Bambu's map is generated from BambuStudio's own shipped bundle; a missing or
unreadable file leaves every lookup an identity rather than taking the app down.
The profile check validates the map's shape, and profile CI now runs on the paths
that can change it. docs/HLSD/filament_id.md records the places the map
deliberately does not reach.
2026-09-06 20:53:11 +08:00

1395 lines
66 KiB
Python

#!/usr/bin/env python3
"""Tests for scripts/assign_filament_ids.py (stdlib unittest, no external deps).
Run from the repo root: python -m unittest discover -s scripts/tests -v
"""
import contextlib
import io
import json
import os
import re
import shutil
import sys
import tempfile
import unittest
import uuid
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
import assign_filament_ids as afi # noqa: E402
import update_bambu_filament_ids as ubfi # noqa: E402
REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
REAL_PROFILES = os.path.join(REPO_ROOT, "resources", "profiles")
OFL = "OrcaFilamentLibrary"
def load_json_file(path):
with open(path, encoding="utf-8") as f:
return json.load(f)
# ---------------------------------------------------------------------------
# helpers: synthetic profile trees
# ---------------------------------------------------------------------------
def preset(name, filament_id=None, inherits=None, instantiation=True,
compatible_printers=None, filament_vendor=None, filament_type=None):
data = {"type": "filament", "name": name}
if inherits is not None:
data["inherits"] = inherits
if filament_id is not None:
data["filament_id"] = filament_id
if filament_vendor is not None:
data["filament_vendor"] = (
[filament_vendor] if isinstance(filament_vendor, str) else filament_vendor)
if filament_type is not None:
data["filament_type"] = (
[filament_type] if isinstance(filament_type, str) else filament_type)
data["instantiation"] = "true" if instantiation else "false"
if compatible_printers is not None:
data["compatible_printers"] = compatible_printers
return data
class SyntheticTree:
"""A throwaway resources/profiles-shaped directory plus a snapshot path."""
def __init__(self):
self.dir = tempfile.mkdtemp(prefix="filament_id_test_")
self.profiles = os.path.join(self.dir, "profiles")
os.makedirs(self.profiles)
self.snapshot = os.path.join(self.dir, "filament_id_snapshot.json")
def cleanup(self):
shutil.rmtree(self.dir, ignore_errors=True)
def preset_path(self, vendor, name):
return os.path.join(self.profiles, vendor, "filament", name + ".json")
def add_vendor(self, vendor, presets):
vendor_dir = os.path.join(self.profiles, vendor, "filament")
os.makedirs(vendor_dir, exist_ok=True)
index = {"name": vendor, "version": "01.00.00.00", "filament_list": []}
for data in presets:
fname = data["name"] + ".json"
with open(os.path.join(vendor_dir, fname), "w", encoding="utf-8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
index["filament_list"].append(
{"name": data["name"], "sub_path": f"filament/{fname}"})
with open(os.path.join(self.profiles, vendor + ".json"), "w",
encoding="utf-8") as f:
json.dump(index, f, indent=4, ensure_ascii=False)
def add_to_index(self, vendor, name):
idx_path = os.path.join(self.profiles, vendor + ".json")
with open(idx_path, encoding="utf-8") as f:
index = json.load(f)
index["filament_list"].append(
{"name": name, "sub_path": f"filament/{name}.json"})
with open(idx_path, "w", encoding="utf-8") as f:
json.dump(index, f, indent=4, ensure_ascii=False)
def write_preset(self, vendor, data, register=True):
path = self.preset_path(vendor, data["name"])
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
if register:
self.add_to_index(vendor, data["name"])
def remove_preset(self, vendor, name):
os.remove(self.preset_path(vendor, name))
idx_path = os.path.join(self.profiles, vendor + ".json")
with open(idx_path, encoding="utf-8") as f:
index = json.load(f)
index["filament_list"] = [
e for e in index["filament_list"] if e["name"] != name]
with open(idx_path, "w", encoding="utf-8") as f:
json.dump(index, f, indent=4, ensure_ascii=False)
# -- pipeline wrappers ---------------------------------------------------
def update_snapshot(self, allow_shared_catalog=False):
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
rc = afi.update_snapshot(self.profiles, self.snapshot,
allow_shared_catalog=allow_shared_catalog)
return rc, buf.getvalue()
def check(self, map_path=None):
buf = io.StringIO()
kwargs = {} if map_path is None else {"map_path": map_path}
with contextlib.redirect_stdout(buf):
errors = afi.check_filament_ids(self.profiles, self.snapshot, **kwargs)
return errors, buf.getvalue()
def assign(self):
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
changed, errors = afi.assign_missing_ids(self.profiles, self.snapshot)
return changed, errors, buf.getvalue()
def remint(self, vendors):
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
changed, errors = afi.remint_vendors(vendors, self.profiles)
return changed, errors, buf.getvalue()
def drop_redundant(self, vendor):
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
dropped, errors = afi.drop_redundant_ids(vendor, self.profiles)
return dropped, errors, buf.getvalue()
def make_clean_tree(apla_id="AX01", generic_id="OGFL99"):
"""Baseline tree: OFL base+generic, a vendor family, a clean tuned generic.
apla_id/generic_id default to arbitrary non-OF placeholders (grandfathered
into the snapshot below) since most tests only need "already assigned,
don't touch". TestAssign passes real OF-format ids instead: assign_missing_ids
now treats a non-OF declaration as missing and remints it, so a non-OF
baseline would no longer be a no-op there.
"""
t = SyntheticTree()
t.add_vendor(OFL, [
preset("fdm_pla", filament_id=generic_id, instantiation=False,
filament_vendor="Generic", filament_type="PLA"),
preset("Generic PLA @System", inherits="fdm_pla",
compatible_printers=[]),
])
t.add_vendor("VendorA", [
preset("APLA @base", filament_id=apla_id, instantiation=False,
filament_vendor="AVendor", filament_type="PLA"),
preset("APLA @P1", inherits="APLA @base",
compatible_printers=["P1 0.4 nozzle"]),
# Correctly tuned OFL generic: keeps the OFL base name, claims a printer.
preset("Generic PLA @P1", inherits="Generic PLA @System",
compatible_printers=["P1 0.4 nozzle"]),
])
rc, _out = t.update_snapshot()
assert rc == 0
return t
class SyntheticTreeCase(unittest.TestCase):
def setUp(self):
self.t = make_clean_tree()
self.addCleanup(self.t.cleanup)
class OfCleanTreeCase(unittest.TestCase):
"""Like SyntheticTreeCase, but the baseline family/generic already carry
real OF-format ids (check 1 now rejects "AX01"/"OGFL99" unconditionally,
with no snapshot exemption), so an otherwise-untouched tree still passes
check_filament_ids. Tests that specifically need a non-OF baseline to
remint or drop (TestRemint, TestDropRedundantIds, TestUpdateSnapshot) keep
using SyntheticTreeCase instead.
"""
def setUp(self):
self.t = make_clean_tree(
apla_id=afi.generate_filament_id("AVendor", "PLA", "APLA"),
generic_id=afi.generate_filament_id("Generic", "PLA", "fdm_pla"))
self.addCleanup(self.t.cleanup)
# ---------------------------------------------------------------------------
# mint
# ---------------------------------------------------------------------------
class TestMint(unittest.TestCase):
def test_namespace_literal(self):
# Frozen: derived from the setting_id namespace; baked into the snapshot.
self.assertEqual(afi.FILAMENT_ID_NAMESPACE,
uuid.UUID("c4d3ff49-4c32-5534-a3e3-00894157ab97"))
def test_known_vector(self):
# Hardcoded, independently computed vectors: freeze prefix, input string
# layout ("filament_product/<vendor>/<type>/<family>") and base62 tail.
self.assertEqual(afi.generate_filament_id("Polymaker", "PLA", "PolyLite PLA"),
"OF5CgdDq")
self.assertEqual(afi.generate_filament_id("Polymaker", "PLA", "PolyLite PLA",
salt=1), "OFD9mV8H")
self.assertEqual(afi.generate_filament_id("Generic", "PLA", "Generic PLA"),
"OFDSrzZ8")
def test_determinism_and_format(self):
for triple in [("Polymaker", "PLA", "PolyLite PLA"),
("Creality", "PLA", "CR PLA"),
("Generic", "ABS", "Generic ABS"),
("拓竹", "PLA", "拓竹 PLA")]: # unicode components
a = afi.generate_filament_id(*triple)
b = afi.generate_filament_id(*triple)
self.assertEqual(a, b)
self.assertRegex(a, r"^OF[0-9A-Za-z]{6}$")
self.assertEqual(len(a), 8)
def test_every_triple_component_changes_the_id(self):
base = afi.generate_filament_id("Polymaker", "PLA", "PolyLite PLA")
self.assertNotEqual(base, afi.generate_filament_id("Other", "PLA", "PolyLite PLA"))
self.assertNotEqual(base, afi.generate_filament_id("Polymaker", "PETG", "PolyLite PLA"))
self.assertNotEqual(base, afi.generate_filament_id("Polymaker", "PLA", "PolyLite PLA Pro"))
def test_salt_changes_id(self):
base = afi.generate_filament_id("Polymaker", "PLA", "PolyLite PLA")
salted = afi.generate_filament_id("Polymaker", "PLA", "PolyLite PLA", salt=1)
self.assertNotEqual(base, salted)
self.assertRegex(salted, r"^OF[0-9A-Za-z]{6}$")
def test_mint_salt_iteration(self):
triple = ("Polymaker", "PLA", "PolyLite PLA")
taken = {afi.generate_filament_id(*triple, salt=s) for s in range(2)}
self.assertEqual(afi.mint_filament_id(*triple, taken),
afi.generate_filament_id(*triple, salt=2))
self.assertEqual(afi.mint_filament_id(*triple, set()),
afi.generate_filament_id(*triple))
class TestBaseName(unittest.TestCase):
def test_family_derivation(self):
cases = [
("X @base", "X"),
("Afinia PLA@HS", "Afinia PLA"),
("PolyTerra PLA", "PolyTerra PLA"),
("HATCHBOX PLA @Qidi X-Plus 4 0.6 nozzle", "HATCHBOX PLA"),
("A @B @C", "A"), # first @ wins
("Filár PLA 拓竹 @0.4 nozzle", "Filár PLA 拓竹"),
]
for name, family in cases:
self.assertEqual(afi.base_name(name), family, msg=name)
# ---------------------------------------------------------------------------
# resolver (loader-faithful semantics)
# ---------------------------------------------------------------------------
class TestResolver(unittest.TestCase):
@staticmethod
def rec(name, filament_id=None, inherits=None):
return {"name": name, "filament_id": filament_id, "inherits": inherits}
def resolve(self, name, vendor_recs, ofl_recs, **kw):
fmap = {r["name"]: r for r in vendor_recs}
omap = {r["name"]: r for r in ofl_recs}
return afi.resolve_filament_id(name, fmap, omap, **kw)
def test_own_id(self):
fid, src, entry = self.resolve("A", [self.rec("A", "ID1")], [])
self.assertEqual((fid, src, entry), ("ID1", "own", None))
def test_inherited_within_vendor(self):
fid, src, entry = self.resolve(
"A", [self.rec("A", inherits="B"), self.rec("B", inherits="C"),
self.rec("C", "ID3")], [])
self.assertEqual((fid, src, entry), ("ID3", "inherited", None))
def test_ofl_fallback(self):
# Vendor preset inherits a name that only exists in the OFL map.
fid, src, entry = self.resolve(
"A", [self.rec("A", inherits="Generic PLA @System")],
[self.rec("Generic PLA @System", inherits="fdm_pla"),
self.rec("fdm_pla", "OGFL99")])
self.assertEqual(fid, "OGFL99")
self.assertEqual(entry, "Generic PLA @System")
def test_ofl_stays_in_ofl(self):
# Once a chain enters OFL it stays there: a vendor file sharing an
# OFL-internal hop's name must not shadow it.
fid, _src, entry = self.resolve(
"A",
[self.rec("A", inherits="ofl_entry"), self.rec("fdm_pla", "WRONG")],
[self.rec("ofl_entry", inherits="fdm_pla"), self.rec("fdm_pla", "RIGHT")])
self.assertEqual(fid, "RIGHT")
self.assertEqual(entry, "ofl_entry")
def test_dead_end_retries_parent_in_ofl(self):
# The vendor chain dead-ends id-less on a parent that also exists in
# OFL: the loader re-consults the OFL map for that direct parent.
fid, _src, entry = self.resolve(
"A", [self.rec("A", inherits="shared"), self.rec("shared")],
[self.rec("shared", "OFLID1")])
self.assertEqual(fid, "OFLID1")
self.assertEqual(entry, "shared")
def test_cycle(self):
fid, src, _e = self.resolve(
"A", [self.rec("A", inherits="B"), self.rec("B", inherits="A")], [])
self.assertEqual((fid, src), (None, "cycle"))
def test_dangling_parent(self):
fid, src, _e = self.resolve("A", [self.rec("A", inherits="nope")], [])
self.assertEqual((fid, src), (None, "dangling"))
def test_missing_id(self):
fid, src, _e = self.resolve("A", [self.rec("A")], [])
self.assertEqual((fid, src), (None, "missing"))
def test_skip_own_resolves_inherited(self):
fid, _src, _e = self.resolve(
"A", [self.rec("A", "OWN", inherits="B"), self.rec("B", "PARENT")], [],
skip_own=True)
self.assertEqual(fid, "PARENT")
class TestTripleResolution(unittest.TestCase):
@staticmethod
def rec(name, inherits=None, filament_vendor=None, filament_type=None):
return {"name": name, "inherits": inherits,
"filament_vendor": filament_vendor, "filament_type": filament_type}
def field(self, name, vendor_recs, ofl_recs, field="filament_vendor"):
fmap = {r["name"]: r for r in vendor_recs}
omap = {r["name"]: r for r in ofl_recs}
return afi.resolve_filament_field(name, field, fmap, omap)
def test_own_value_first_element_of_list(self):
got = self.field("A", [self.rec("A", filament_vendor=["Poly", "Ignored"])], [])
self.assertEqual(got, "Poly")
def test_plain_string_value_tolerated(self):
got = self.field("A", [self.rec("A", filament_vendor="Poly")], [])
self.assertEqual(got, "Poly")
def test_inherited_within_vendor(self):
got = self.field(
"A", [self.rec("A", inherits="B"), self.rec("B", inherits="C"),
self.rec("C", filament_vendor=["Poly"])], [])
self.assertEqual(got, "Poly")
def test_empty_list_keeps_walking(self):
got = self.field(
"A", [self.rec("A", inherits="B", filament_vendor=[]),
self.rec("B", filament_vendor=["Poly"])], [])
self.assertEqual(got, "Poly")
def test_ofl_fallback(self):
got = self.field(
"A", [self.rec("A", inherits="Generic PLA @System")],
[self.rec("Generic PLA @System", inherits="fdm_pla"),
self.rec("fdm_pla", filament_vendor=["Generic"])])
self.assertEqual(got, "Generic")
def test_ofl_stays_in_ofl(self):
got = self.field(
"A",
[self.rec("A", inherits="ofl_entry"),
self.rec("fdm_pla", filament_vendor=["WRONG"])],
[self.rec("ofl_entry", inherits="fdm_pla"),
self.rec("fdm_pla", filament_vendor=["RIGHT"])])
self.assertEqual(got, "RIGHT")
def test_dead_end_retries_parent_in_ofl(self):
got = self.field(
"A", [self.rec("A", inherits="shared"), self.rec("shared")],
[self.rec("shared", filament_vendor=["Poly"])])
self.assertEqual(got, "Poly")
def test_missing_is_empty(self):
self.assertEqual(self.field("A", [self.rec("A")], []), "")
self.assertEqual(self.field("A", [self.rec("A", inherits="nope")], []), "")
def test_resolve_triple(self):
recs = [self.rec("MyPLA @base", filament_vendor=["MyVendor"],
filament_type=["PLA"]),
self.rec("MyPLA @P1", inherits="MyPLA @base")]
fmap = {r["name"]: r for r in recs}
self.assertEqual(afi.resolve_triple("MyPLA @P1", fmap, {}),
("MyVendor", "PLA", "MyPLA"))
# ---------------------------------------------------------------------------
# reserved namespaces
# ---------------------------------------------------------------------------
class TestReservedSpaces(unittest.TestCase):
def test_owners(self):
# Bambu AMS/RFID catalog: reserved, but no vendor (not even BBL) may declare it
self.assertEqual(afi.reserved_space_owner("GFL99"), (True, None))
# Qidi device protocol: reserved, but no vendor may declare it
self.assertEqual(afi.reserved_space_owner("QD_X4_PLA"), (True, None))
self.assertEqual(afi.reserved_space_owner("P1234abc"), (True, None))
self.assertEqual(afi.reserved_space_owner("pAbCdEf1"), (True, None)) # case-insensitive
self.assertEqual(afi.reserved_space_owner("null"), (True, None))
self.assertEqual(afi.reserved_space_owner("OF5CgdDq"), (False, None))
self.assertEqual(afi.reserved_space_owner("P1234abcd"), (False, None)) # 8 hex chars: not the user space
def test_gf_is_reserved_and_ownerless(self):
self.assertEqual(afi.reserved_space_owner("GFA00"), (True, None))
# ---------------------------------------------------------------------------
# checks on synthetic trees
# ---------------------------------------------------------------------------
class TestChecks(OfCleanTreeCase):
def test_clean_tree_is_silent(self):
errors, out = self.t.check()
self.assertEqual(errors, 0, out)
self.assertNotIn("[ERROR]", out)
self.assertNotIn("[WARNING]", out)
def test_check1_unknown_non_of_id(self):
self.t.write_preset("VendorA", preset("BPLA @base", filament_id="BOGUS_9",
instantiation=False,
filament_vendor="BV", filament_type="PLA"))
self.t.write_preset("VendorA", preset("BPLA @P1", inherits="BPLA @base",
compatible_printers=["P1"]))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn('is not a minted "OF" id', out)
self.assertIn("BOGUS_9", out)
def test_check2_new_claim_needs_snapshot_update(self):
self.t.write_preset("VendorA", preset("ANEW @P2", inherits="APLA @base",
compatible_printers=["P2"]))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn('claim "VendorA/ANEW" is not sanctioned', out)
self.assertIn("--update-snapshot", out)
def test_check2_vanished_claim_is_stability_error(self):
self.t.remove_preset("VendorA", "APLA @P1")
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn("stability", out)
self.assertIn('"VendorA/APLA"', out)
def test_check2_triple_change_needs_snapshot_update(self):
apla_id = afi.generate_filament_id("AVendor", "PLA", "APLA")
self.t.write_preset("VendorA", preset("APLA @base", filament_id=apla_id,
instantiation=False,
filament_vendor="AVendor",
filament_type="PETG"),
register=False)
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn('triple "AVendor/PETG/APLA" is not sanctioned', out)
self.assertIn('triple stability: snapshot triple "AVendor/PLA/APLA"', out)
rc, _out = self.t.update_snapshot()
self.assertEqual(rc, 0)
errors, out = self.t.check()
self.assertEqual(errors, 0, out)
def test_check3_of_id_must_match_triple_mint(self):
self.t.write_preset("VendorA", preset("BNEW @base", filament_id="OFZZZZZZ",
instantiation=False,
filament_vendor="BV", filament_type="PLA"))
self.t.write_preset("VendorA", preset("BNEW @P1", inherits="BNEW @base",
compatible_printers=["P1"]))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn("does not match the mint of its triple", out)
self.assertIn(afi.generate_filament_id("BV", "PLA", "BNEW"), out)
def test_check3_salted_mint_is_accepted(self):
salted = afi.generate_filament_id("BV", "PLA", "BNEW", salt=3)
self.t.write_preset("VendorA", preset("BNEW @base", filament_id=salted,
instantiation=False,
filament_vendor="BV", filament_type="PLA"))
self.t.write_preset("VendorA", preset("BNEW @P1", inherits="BNEW @base",
compatible_printers=["P1"]))
_errors, out = self.t.check() # check 2 still wants a snapshot update
self.assertNotIn("does not match the mint", out)
def test_check3_grandfathered_id_triple_pair(self):
# A non-conforming (id, triple) pair sanctioned by the snapshot passes.
self.t.write_preset("VendorA", preset("CNEW @base", filament_id="OFZZZZZZ",
instantiation=False,
filament_vendor="CV", filament_type="PLA"))
self.t.write_preset("VendorA", preset("CNEW @P1", inherits="CNEW @base",
compatible_printers=["P1"]))
rc, _out = self.t.update_snapshot()
self.assertEqual(rc, 0)
errors, out = self.t.check()
self.assertEqual(errors, 0, out)
def test_check4_renamed_tuned_generic(self):
self.t.write_preset("VendorA", preset("Tuned PLA @P1",
inherits="Generic PLA @System",
compatible_printers=["P1"]))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn("rename re-exposes the OFL preset", out)
self.assertIn("Tuned PLA @P1", out)
def test_check4_empty_compatible_printers(self):
self.t.write_preset("VendorA", preset("Generic PLA @P2",
inherits="Generic PLA @System",
compatible_printers=[]))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn("cannot shadow the OFL preset", out)
def test_check4_own_id_key_on_ofl_rider(self):
# A vendor root that declares its own id while inheriting an OFL family
# forks the family off the catalog id.
self.t.write_preset("VendorA", preset("GPLA @base", filament_id="AX77",
instantiation=False,
inherits="Generic PLA @System"))
self.t.write_preset("VendorA", preset("GPLA @P1", inherits="GPLA @base",
compatible_printers=["P1"]))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn('declares its own filament_id "AX77"', out)
def test_check4_applies_to_non_generic_ofl_families(self):
fid = afi.generate_filament_id("Polymaker", "PLA", "PolyLite PLA")
self.t.write_preset(OFL, preset("poly_pla_base", filament_id=fid,
instantiation=False,
filament_vendor="Polymaker",
filament_type="PLA"))
self.t.write_preset(OFL, preset("PolyLite PLA @System",
inherits="poly_pla_base",
compatible_printers=[]))
rc, _out = self.t.update_snapshot()
self.assertEqual(rc, 0)
self.t.write_preset("VendorA", preset("PolyX PLA @P1",
inherits="PolyLite PLA @System",
compatible_printers=["P1"]))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn("rename re-exposes the OFL preset", out)
self.assertIn("PolyX PLA @P1", out)
def test_check4_exception_is_grandfathered(self):
self.t.write_preset("VendorA", preset("Tuned PLA @P1",
inherits="Generic PLA @System",
compatible_printers=["P1"]))
rc, _out = self.t.update_snapshot()
self.assertEqual(rc, 0)
errors, out = self.t.check()
self.assertEqual(errors, 0, out)
def test_check5_reserved_namespace_claims(self):
for fid, marker in [("GFX99", "Bambu AMS/RFID catalog"),
("QD_X_PLA", "composed by the device"),
("P1a2b3c4", "user-custom"),
("null", "user-custom")]:
with self.subTest(fid=fid):
name = f"R{fid} @base"
self.t.write_preset("VendorA", preset(name, filament_id=fid,
instantiation=False,
filament_vendor="RV",
filament_type="PLA"))
self.t.write_preset("VendorA", preset(f"R{fid} @P1", inherits=name,
compatible_printers=["P1"]))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn("reserved id space", out)
self.assertIn(marker, out)
def test_check6a_instantiated_preset_with_own_key(self):
# Same value the child would already resolve through inherits: this
# isolates check 6a (own key present) from check 6b (value drift).
apla_id = afi.generate_filament_id("AVendor", "PLA", "APLA")
self.t.write_preset("VendorA", preset("APLA @P1", filament_id=apla_id,
inherits="APLA @base",
compatible_printers=["P1 0.4 nozzle"]),
register=False)
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn("declares its own filament_id key", out)
def test_check6b_declared_vs_inherited_drift(self):
apla_id = afi.generate_filament_id("AVendor", "PLA", "APLA")
drifted = afi.generate_filament_id("AVendor", "PLA", "APLA", salt=1)
self.t.write_preset("VendorA", preset("APLA @P1", filament_id=drifted,
inherits="APLA @base",
compatible_printers=["P1 0.4 nozzle"]),
register=False)
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn(f'declares filament_id "{drifted}" but its inherits chain resolves '
f'"{apla_id}"', out)
def test_check6c_unresolvable_instantiated_filament(self):
self.t.write_preset("VendorA", preset("DNEW @P1", compatible_printers=["P1"]))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn("resolves no filament_id", out)
self.assertIn("hard load error", out)
def test_missing_snapshot_is_an_error(self):
os.remove(self.t.snapshot)
errors, out = self.t.check()
self.assertEqual(errors, 1)
self.assertIn("snapshot not found", out)
class TestCheck7(OfCleanTreeCase):
def test_7a_empty_vendor_is_hard_error(self):
fid = afi.generate_filament_id("", "PLA", "NVPLA")
self.t.write_preset("VendorA", preset("NVPLA @base", filament_id=fid,
instantiation=False,
filament_type="PLA"))
self.t.write_preset("VendorA", preset("NVPLA @P1", inherits="NVPLA @base",
compatible_printers=["P1"]))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn("resolves empty filament_vendor", out)
self.assertIn('filament_vendor "Generic"', out)
# No grandfathering: sanctioning the tree does not silence check 8a.
rc, _out = self.t.update_snapshot()
self.assertEqual(rc, 0)
errors, out = self.t.check()
self.assertEqual(errors, 1, out)
self.assertIn("resolves empty filament_vendor", out)
def test_7b_divergent_family_triples(self):
id1 = afi.generate_filament_id("MV", "PLA", "MPLA")
id2 = afi.generate_filament_id("MV", "PETG", "MPLA")
self.t.write_preset("VendorA", preset("MPLA @base1", filament_id=id1,
instantiation=False,
filament_vendor="MV",
filament_type="PLA"))
self.t.write_preset("VendorA", preset("MPLA @base2", filament_id=id2,
instantiation=False,
filament_vendor="MV",
filament_type="PETG"))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn("divergent triples", out)
self.assertIn("MV/PLA/MPLA", out)
self.assertIn("MV/PETG/MPLA", out)
# ... until --update-snapshot grandfathers the family in triple_exceptions.
rc, _out = self.t.update_snapshot()
self.assertEqual(rc, 0)
self.assertIn("VendorA/MPLA", load_json_file(self.t.snapshot)["triple_exceptions"])
errors, out = self.t.check()
self.assertEqual(errors, 0, out)
def test_7_cross_bundle_divergence_is_warning_only(self):
fid = afi.generate_filament_id("BV", "PETG", "APLA")
self.t.add_vendor("VendorB", [
preset("APLA @base", filament_id=fid, instantiation=False,
filament_vendor="BV", filament_type="PETG"),
preset("APLA @PB", inherits="APLA @base",
compatible_printers=["PB 0.4 nozzle"]),
])
rc, _out = self.t.update_snapshot()
self.assertEqual(rc, 0)
errors, out = self.t.check()
self.assertEqual(errors, 0, out)
self.assertIn("[WARNING]", out)
self.assertIn("across bundles", out)
self.assertIn('"APLA"', out)
class TestCheck8(OfCleanTreeCase):
def _write_map(self, rows):
path = os.path.join(self.t.dir, "bambu_filament_ids.json")
ubfi.write_map(path, rows, "testcommit", "2026-09-04")
return path
def _write_raw_map(self, payload):
"""Write a map write_map() would never produce (hand-edited or mis-generated)."""
path = os.path.join(self.t.dir, "bambu_filament_ids.json")
with open(path, "w", encoding="utf-8", newline="\n") as f:
json.dump(payload, f, indent=2, ensure_ascii=False, sort_keys=True)
return path
def test_row_triple_must_match_tree(self):
fid = afi.generate_filament_id("V", "PLA", "Foo")
self.t.write_preset("VendorA", preset("Foo @base", filament_id=fid,
instantiation=False,
filament_vendor="V", filament_type="PLA"))
self.t.write_preset("VendorA", preset("Foo @P1", inherits="Foo @base",
compatible_printers=["P1"]))
map_path = self._write_map(
{fid: {"bambu_id": "GFZ00", "vendor": "V", "type": "PLA", "name": "Bar"}})
errors, out = self.t.check(map_path)
self.assertGreater(errors, 0)
self.assertIn("regenerate the map", out)
def test_duplicate_bambu_id_is_an_error(self):
map_path = self._write_map({
"OFaaaaaa": {"bambu_id": "GFZ00", "vendor": "V", "type": "PLA", "name": "Foo"},
"OFbbbbbb": {"bambu_id": "GFZ00", "vendor": "V", "type": "PETG", "name": "Bar"},
})
errors, out = self.t.check(map_path)
self.assertGreater(errors, 0)
self.assertIn("GFZ00", out)
def test_row_for_unshipped_product_is_fine(self):
map_path = self._write_map({
"OFcccccc": {"bambu_id": "GFZ99", "vendor": "Nobody", "type": "PLA",
"name": "Ships Nothing"},
})
errors, out = self.t.check(map_path)
self.assertEqual(errors, 0, out)
def test_non_object_top_level_is_a_clean_error(self):
# A hand-edited map that is a list (or any non-object) must report the map,
# not raise AttributeError out of the check.
map_path = self._write_raw_map([{"bambu_id": "GFZ00"}])
errors, out = self.t.check(map_path)
self.assertGreater(errors, 0)
self.assertIn("does not parse", out)
self.assertIn("regenerate the map", out)
def test_empty_filaments_section_is_an_error(self):
# What a regeneration against the wrong --bambustudio-dir writes: a well-formed
# header with zero rows. At runtime every translation silently becomes identity.
map_path = self._write_map({})
errors, out = self.t.check(map_path)
self.assertGreater(errors, 0)
self.assertIn('no "filaments" rows', out)
def test_absent_filaments_section_is_an_error(self):
map_path = self._write_raw_map({
"source": "https://github.com/bambulab/BambuStudio",
"bambustudio_commit": "testcommit",
"generated": "2026-09-04",
})
errors, out = self.t.check(map_path)
self.assertGreater(errors, 0)
self.assertIn('no "filaments" rows', out)
def test_row_without_bambu_id_is_an_error(self):
# Two such rows used to collide on None and be reported as a duplicate id.
map_path = self._write_map({
"OFaaaaaa": {"vendor": "V", "type": "PLA", "name": "Foo"},
"OFbbbbbb": {"vendor": "V", "type": "PETG", "name": "Bar"},
})
errors, out = self.t.check(map_path)
self.assertGreater(errors, 0)
self.assertIn('"OFaaaaaa" declares no "bambu_id"', out)
self.assertIn('"OFbbbbbb" declares no "bambu_id"', out)
self.assertNotIn("mapped by both", out)
def test_empty_bambu_id_is_an_error(self):
# "" would land in the runtime map and translate an empty tray id into a filament.
map_path = self._write_map({
"OFaaaaaa": {"bambu_id": "", "vendor": "V", "type": "PLA", "name": "Foo"},
})
errors, out = self.t.check(map_path)
self.assertGreater(errors, 0)
self.assertIn('declares no "bambu_id"', out)
# ---------------------------------------------------------------------------
# --update-snapshot
# ---------------------------------------------------------------------------
class TestUpdateSnapshot(SyntheticTreeCase):
def test_idempotent_and_deterministic(self):
with open(self.t.snapshot, "rb") as f:
first = f.read()
rc, out = self.t.update_snapshot()
self.assertEqual(rc, 0)
self.assertIn("nothing changed", out)
with open(self.t.snapshot, "rb") as f:
self.assertEqual(f.read(), first)
self.assertTrue(first.endswith(b"\n"))
self.assertNotIn(b"\r", first)
snap = json.loads(first.decode("utf-8"))
self.assertEqual(list(snap["ids"]), sorted(snap["ids"]))
self.assertEqual(snap["ids"]["AX01"], ["VendorA/APLA"])
self.assertEqual(snap["ids"]["OGFL99"],
["OrcaFilamentLibrary/Generic PLA", "VendorA/Generic PLA"])
self.assertEqual(snap["triples"]["AX01"], [["AVendor", "PLA", "APLA"]])
self.assertEqual(snap["triples"]["OGFL99"], [["Generic", "PLA", "fdm_pla"]])
self.assertEqual(snap["triple_exceptions"], [])
def test_refuses_new_reserved_namespace_claims(self):
self.t.write_preset("VendorA", preset("CNEW @base", filament_id="GFX99",
instantiation=False,
filament_vendor="CV",
filament_type="PLA"))
self.t.write_preset("VendorA", preset("CNEW @P1", inherits="CNEW @base",
compatible_printers=["P1"]))
with open(self.t.snapshot, "rb") as f:
before = f.read()
rc, out = self.t.update_snapshot()
self.assertEqual(rc, 1)
self.assertIn("refusing to sanction", out)
with open(self.t.snapshot, "rb") as f:
self.assertEqual(f.read(), before) # nothing written on refusal
rc, _out = self.t.update_snapshot(allow_shared_catalog=True)
self.assertEqual(rc, 0)
snap = load_json_file(self.t.snapshot)
self.assertEqual(snap["ids"]["GFX99"], ["VendorA/CNEW"])
# ---------------------------------------------------------------------------
# default run: mint + insert
# ---------------------------------------------------------------------------
class TestAssign(OfCleanTreeCase):
def test_noop_on_fully_idded_tree(self):
changed, errors, out = self.t.assign()
self.assertEqual((changed, errors), (0, 0))
self.assertIn("nothing to do (0 files changed)", out)
def test_mints_into_family_root_and_rootless_member(self):
self.t.write_preset("VendorA", preset("FNEW @base", instantiation=False,
filament_vendor="FV",
filament_type="PLA"))
self.t.write_preset("VendorA", preset("FNEW @P1", inherits="FNEW @base",
compatible_printers=["P1"]))
self.t.write_preset("VendorA", preset("FNEW @P2", inherits="FNEW @base",
compatible_printers=["P2"]))
self.t.write_preset("VendorA", preset("GNEW @P1", compatible_printers=["P1"],
filament_vendor="GV",
filament_type="PETG"))
changed, errors, _out = self.t.assign()
self.assertEqual(errors, 0)
self.assertEqual(changed, 2) # one root + one root-less member
root = load_json_file(self.t.preset_path("VendorA", "FNEW @base"))
self.assertEqual(root["filament_id"],
afi.generate_filament_id("FV", "PLA", "FNEW"))
member = load_json_file(self.t.preset_path("VendorA", "GNEW @P1"))
self.assertEqual(member["filament_id"],
afi.generate_filament_id("GV", "PETG", "GNEW"))
# variants themselves never get the key
child = load_json_file(self.t.preset_path("VendorA", "FNEW @P1"))
self.assertNotIn("filament_id", child)
# idempotent: second run is a no-op
changed, errors, out = self.t.assign()
self.assertEqual((changed, errors), (0, 0))
self.assertIn("nothing to do", out)
def test_refuses_family_with_incomplete_triple(self):
self.t.write_preset("VendorA", preset("ENEW @base", instantiation=False))
self.t.write_preset("VendorA", preset("ENEW @P1", inherits="ENEW @base",
compatible_printers=["P1"]))
changed, errors, out = self.t.assign()
self.assertEqual(changed, 0)
self.assertGreater(errors, 0)
self.assertIn("resolves empty filament_vendor and filament_type", out)
self.assertIn("mint key needs both", out)
def test_refuses_family_with_divergent_root_fields(self):
self.t.write_preset("VendorA", preset("HNEW @base1", instantiation=False,
filament_vendor="HV",
filament_type="PLA"))
self.t.write_preset("VendorA", preset("HNEW @base2", instantiation=False,
filament_vendor="HV",
filament_type="PETG"))
self.t.write_preset("VendorA", preset("HNEW @P1", inherits="HNEW @base1",
compatible_printers=["P1"]))
self.t.write_preset("VendorA", preset("HNEW @P2", inherits="HNEW @base2",
compatible_printers=["P2"]))
changed, errors, out = self.t.assign()
self.assertEqual(changed, 0)
self.assertGreater(errors, 0)
self.assertIn("divergent (filament_vendor, filament_type)", out)
def test_shared_root_between_families_is_refused(self):
self.t.write_preset("VendorA", preset("shared_base", instantiation=False,
filament_vendor="SV",
filament_type="PLA"))
self.t.write_preset("VendorA", preset("HNEW @P1", inherits="shared_base",
compatible_printers=["P1"]))
self.t.write_preset("VendorA", preset("INEW @P1", inherits="shared_base",
compatible_printers=["P1"]))
changed, errors, out = self.t.assign()
self.assertEqual(changed, 0)
self.assertGreater(errors, 0)
self.assertIn("shared with famil", out)
def test_non_of_declaration_is_treated_as_missing(self):
# A declaration that isn't OF-format (e.g. a vendor bundle synced from
# an upstream catalog, like BBL's GF ids) is reminted like a missing
# family, not left alone.
self.t.write_preset("VendorA", preset("Synced PLA @base", filament_id="GFZZ00",
instantiation=False,
filament_vendor="ZV", filament_type="PLA"))
self.t.write_preset("VendorA", preset("Synced PLA @P1",
inherits="Synced PLA @base",
compatible_printers=["P1"]))
changed, errors, out = self.t.assign()
self.assertEqual(errors, 0, out)
self.assertEqual(changed, 1)
path = self.t.preset_path("VendorA", "Synced PLA @base")
root = load_json_file(path)
want = afi.generate_filament_id("ZV", "PLA", "Synced PLA")
self.assertEqual(root["filament_id"], want)
# the value was replaced in place, not appended as a second key
raw = open(path, encoding="utf-8").read()
self.assertEqual(raw.count('"filament_id"'), 1)
# idempotent: the id is OF-format now, so a second run is a no-op
changed, errors, _out = self.t.assign()
self.assertEqual((changed, errors), (0, 0))
def test_non_of_multi_root_family_converges_on_one_id(self):
# Two per-printer roots of one product (same triple), both carrying
# the SAME non-OF id — the shape a synced vendor bundle ships (e.g.
# BambuStudio's own per-printer @base files). They must converge on
# one freshly minted id, not split into two.
self.t.write_preset("VendorA", preset("Synced ABS @P1base", filament_id="GFSYNC0",
instantiation=False,
filament_vendor="ZV", filament_type="ABS"))
self.t.write_preset("VendorA", preset("Synced ABS @P2base", filament_id="GFSYNC0",
instantiation=False,
filament_vendor="ZV", filament_type="ABS"))
self.t.write_preset("VendorA", preset("Synced ABS @P1",
inherits="Synced ABS @P1base",
compatible_printers=["P1"]))
self.t.write_preset("VendorA", preset("Synced ABS @P2",
inherits="Synced ABS @P2base",
compatible_printers=["P2"]))
changed, errors, out = self.t.assign()
self.assertEqual(errors, 0, out)
self.assertEqual(changed, 2)
want = afi.generate_filament_id("ZV", "ABS", "Synced ABS")
b1 = load_json_file(self.t.preset_path("VendorA", "Synced ABS @P1base"))
b2 = load_json_file(self.t.preset_path("VendorA", "Synced ABS @P2base"))
self.assertEqual(b1["filament_id"], want)
self.assertEqual(b2["filament_id"], want)
# ---------------------------------------------------------------------------
# byte-preserving profile edits
# ---------------------------------------------------------------------------
class TestInsertEditing(unittest.TestCase):
CRLF_TEXT = (
'{\r\n'
'\t"type": "filament",\r\n'
'\t"name": "JNEW @base",\r\n'
'\t"inherits": "fdm_pla",\r\n'
'\t"from": "system",\r\n'
'\t"instantiation": "false",\r\n'
'\t"filament_type": [\r\n'
'\t\t"PLA"\r\n'
'\t]\r\n'
'}\r\n'
)
CRLF_WITH_ID = (
'{\r\n'
'\t"type": "filament",\r\n'
'\t"name": "JNEW @base",\r\n'
'\t"filament_id": "OFold123",\r\n'
'\t"instantiation": "false",\r\n'
'\t"filament_type": [\r\n'
'\t\t"PLA"\r\n'
'\t]\r\n'
'}\r\n'
)
def test_insert_before_instantiation_preserves_bytes(self):
text, n = afi.insert_filament_id(self.CRLF_TEXT, "OFabc123")
self.assertEqual(n, 1)
json.loads(text)
inserted = '\t"filament_id": "OFabc123",\r\n'
self.assertIn(inserted + '\t"instantiation"', text)
# every original byte is preserved: removing the inserted line restores
# the input exactly (CRLF stays CRLF, tabs stay tabs)
self.assertEqual(text.replace(inserted, "", 1), self.CRLF_TEXT)
def test_insert_after_name_when_no_instantiation_line(self):
lf_text = '{\n "type": "filament",\n "name": "K",\n "from": "system"\n}\n'
text, n = afi.insert_filament_id(lf_text, "OFabc123")
self.assertEqual(n, 1)
json.loads(text)
self.assertIn('"name": "K",\n "filament_id": "OFabc123",\n', text)
self.assertNotIn("\r", text)
def test_insert_no_anchor_fails(self):
_text, n = afi.insert_filament_id('{"type": "filament"}', "OFabc123")
self.assertEqual(n, 0)
def test_write_filament_id_keeps_crlf_on_disk(self):
t = SyntheticTree()
self.addCleanup(t.cleanup)
t.add_vendor("VendorA", [])
path = t.preset_path("VendorA", "JNEW @base")
with open(path, "wb") as f:
f.write(self.CRLF_TEXT.encode("utf-8"))
t.add_to_index("VendorA", "JNEW @base")
afi.write_filament_id(path, "OFabc123")
with open(path, "rb") as f:
raw = f.read()
self.assertEqual(raw.count(b"\n"), raw.count(b"\r\n")) # still CRLF-only
self.assertEqual(
raw.replace(b'\t"filament_id": "OFabc123",\r\n', b"", 1),
self.CRLF_TEXT.encode("utf-8"))
def test_replace_value_preserves_every_other_byte(self):
text, n = afi.replace_filament_id_value(self.CRLF_WITH_ID,
"OFold123", "OFnew456")
self.assertEqual(n, 1)
self.assertEqual(json.loads(text)["filament_id"], "OFnew456")
self.assertEqual(text, self.CRLF_WITH_ID.replace('"OFold123"', '"OFnew456"'))
def test_replace_value_requires_exact_old_value(self):
_text, n = afi.replace_filament_id_value(self.CRLF_WITH_ID,
"OFwrong1", "OFnew456")
self.assertEqual(n, 0)
def test_rewrite_filament_id_on_disk(self):
t = SyntheticTree()
self.addCleanup(t.cleanup)
t.add_vendor("VendorA", [])
path = t.preset_path("VendorA", "JNEW @base")
with open(path, "wb") as f:
f.write(self.CRLF_WITH_ID.encode("utf-8"))
afi.rewrite_filament_id(path, "OFold123", "OFnew456")
with open(path, "rb") as f:
raw = f.read()
self.assertEqual(raw,
self.CRLF_WITH_ID.replace('"OFold123"', '"OFnew456"')
.encode("utf-8"))
with self.assertRaises(RuntimeError):
afi.rewrite_filament_id(path, "OFold123", "OFxxx999") # stale old id
def test_delete_line_with_trailing_comma(self):
text, n = afi.delete_filament_id_line(self.CRLF_WITH_ID, "OFold123")
self.assertEqual(n, 1)
self.assertNotIn("filament_id", json.loads(text))
self.assertEqual(text, self.CRLF_WITH_ID.replace(
'\t"filament_id": "OFold123",\r\n', "", 1))
def test_delete_last_property_line(self):
lf_text = ('{\n "name": "K @base",\n "instantiation": "false",\n'
' "filament_id": "OFold123"\n}\n')
text, n = afi.delete_filament_id_line(lf_text, "OFold123")
self.assertEqual(n, 1)
data = json.loads(text)
self.assertNotIn("filament_id", data)
self.assertEqual(data["instantiation"], "false")
# ---------------------------------------------------------------------------
# --remint
# ---------------------------------------------------------------------------
class TestRemint(SyntheticTreeCase):
TRIPLE = ("AVendor", "PLA", "APLA")
def test_rewrites_mismatching_declaration(self):
want = afi.generate_filament_id(*self.TRIPLE)
changed, errors, out = self.t.remint(["VendorA"])
self.assertEqual(errors, 0, out)
self.assertEqual(changed, 1)
self.assertIn('"AX01" -> "%s"' % want, out)
root = load_json_file(self.t.preset_path("VendorA", "APLA @base"))
self.assertEqual(root["filament_id"], want)
# OFL was not part of the run
ofl_root = load_json_file(self.t.preset_path(OFL, "fdm_pla"))
self.assertEqual(ofl_root["filament_id"], "OGFL99")
# idempotent: a second run over the same vendor rewrites nothing
changed, errors, _out = self.t.remint(["VendorA"])
self.assertEqual((changed, errors), (0, 0))
def test_salted_declaration_survives_remint(self):
# A deliberate salt split (a second preset of one product kept on its
# own id for per-printer AMS disambiguation) is mint-conformant and
# must not be converged back onto salt 0.
salt1 = afi.generate_filament_id(*self.TRIPLE, salt=1)
self.t.write_preset("VendorA", preset("APLA @legacy",
filament_id=salt1,
inherits="APLA @base",
compatible_printers=["P2 0.4 nozzle"]))
changed, errors, out = self.t.remint(["VendorA"])
self.assertEqual(errors, 0, out)
self.assertEqual(changed, 1) # only the non-conformant AX01 root
legacy = load_json_file(self.t.preset_path("VendorA", "APLA @legacy"))
self.assertEqual(legacy["filament_id"], salt1)
def test_same_triple_converges_within_run(self):
self.t.add_vendor("VendorB", [
preset("APLA @Bbase", filament_id="BX01", instantiation=False,
filament_vendor="AVendor", filament_type="PLA"),
preset("APLA @PB", inherits="APLA @Bbase",
compatible_printers=["PB 0.4 nozzle"]),
])
want = afi.generate_filament_id(*self.TRIPLE)
changed, errors, _out = self.t.remint(["VendorA", "VendorB"])
self.assertEqual(errors, 0)
self.assertEqual(changed, 2)
a = load_json_file(self.t.preset_path("VendorA", "APLA @base"))
b = load_json_file(self.t.preset_path("VendorB", "APLA @Bbase"))
self.assertEqual(a["filament_id"], want)
self.assertEqual(b["filament_id"], want)
def test_same_triple_converges_with_existing_tree_id(self):
# Another bundle already carries the conforming id for the same triple:
# reuse is required, not blocked.
want = afi.generate_filament_id(*self.TRIPLE)
self.t.add_vendor("VendorB", [
preset("APLA @Bbase", filament_id=want, instantiation=False,
filament_vendor="AVendor", filament_type="PLA"),
])
changed, errors, _out = self.t.remint(["VendorA"])
self.assertEqual((changed, errors), (1, 0))
a = load_json_file(self.t.preset_path("VendorA", "APLA @base"))
self.assertEqual(a["filament_id"], want)
def test_blocked_by_other_triple_occurrence(self):
want0 = afi.generate_filament_id(*self.TRIPLE)
self.t.add_vendor("VendorB", [
preset("BPLA @base", filament_id=want0, instantiation=False,
filament_vendor="BV", filament_type="PETG"),
])
changed, errors, _out = self.t.remint(["VendorA"])
self.assertEqual((changed, errors), (1, 0))
root = load_json_file(self.t.preset_path("VendorA", "APLA @base"))
self.assertEqual(root["filament_id"],
afi.generate_filament_id(*self.TRIPLE, salt=1))
def test_bbl_is_reminted_like_any_vendor(self):
self.t.add_vendor("BBL", [
preset("Bambu ABS @base", filament_id="GFB00", instantiation=False,
filament_vendor="Bambu Lab", filament_type="ABS"),
preset("Bambu ABS @P1", inherits="Bambu ABS @base",
compatible_printers=["P1"]),
])
changed, errors, out = self.t.remint(["BBL"])
self.assertEqual(errors, 0, out)
self.assertEqual(changed, 1)
root = load_json_file(self.t.preset_path("BBL", "Bambu ABS @base"))
self.assertEqual(root["filament_id"],
afi.generate_filament_id("Bambu Lab", "ABS", "Bambu ABS"))
# ---------------------------------------------------------------------------
# --drop-redundant-ids
# ---------------------------------------------------------------------------
class TestDropRedundantIds(SyntheticTreeCase):
def test_drops_only_ofl_riding_same_base_name_declarations(self):
# Redundant: same base name, rides the OFL generic, re-declares its id.
self.t.write_preset("VendorA", preset("Generic PLA @P2",
inherits="Generic PLA @System",
compatible_printers=["P2"],
filament_id="OGFL99"))
# Renamed rider: not dropped (a repoint worksheet decision, not a drop).
self.t.write_preset("VendorA", preset("Tuned PLA @P3",
inherits="Generic PLA @System",
compatible_printers=["P3"],
filament_id="OGFL99"))
dropped, errors, out = self.t.drop_redundant("VendorA")
self.assertEqual(errors, 0, out)
self.assertEqual(dropped, 1)
self.assertIn('dropped filament_id "OGFL99"', out)
self.assertIn('rides OFL "Generic PLA @System"', out)
gone = load_json_file(self.t.preset_path("VendorA", "Generic PLA @P2"))
self.assertNotIn("filament_id", gone)
kept = load_json_file(self.t.preset_path("VendorA", "Tuned PLA @P3"))
self.assertEqual(kept["filament_id"], "OGFL99")
# vendor-rooted families are untouched
root = load_json_file(self.t.preset_path("VendorA", "APLA @base"))
self.assertEqual(root["filament_id"], "AX01")
def test_bbl_is_forbidden(self):
dropped, errors, out = self.t.drop_redundant("BBL")
self.assertEqual(dropped, 0)
self.assertGreater(errors, 0)
self.assertIn("forbidden", out)
# ---------------------------------------------------------------------------
# --mint CLI
# ---------------------------------------------------------------------------
class TestMintCli(unittest.TestCase):
def test_prints_an_of_id(self):
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
rc = afi.main(["--mint", "Polymaker/PLA/PolyLite PLA"])
self.assertEqual(rc, 0)
self.assertRegex(buf.getvalue().strip(), r"^OF[0-9A-Za-z]{6}$")
def test_requires_exactly_three_parts(self):
with self.assertRaises(SystemExit), \
contextlib.redirect_stderr(io.StringIO()):
afi.main(["--mint", "Vendor/Family"])
# ---------------------------------------------------------------------------
# the real tree
# ---------------------------------------------------------------------------
@unittest.skipUnless(os.path.isdir(REAL_PROFILES), "resources/profiles not present")
class TestRealTree(unittest.TestCase):
def test_shipped_snapshot_matches_tree(self):
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
errors = afi.check_filament_ids(REAL_PROFILES)
self.assertEqual(errors, 0, buf.getvalue())
def test_every_instantiated_filament_resolves_an_id(self):
analysis = afi.analyze_tree(REAL_PROFILES)
self.assertEqual(analysis["missing_effective"], [])
self.assertEqual(analysis["read_errors"], [])
# ---------------------------------------------------------------------------
# review-fix regressions
# ---------------------------------------------------------------------------
class TestReviewFixes(OfCleanTreeCase):
def test_check3_skips_of_id_inherited_from_other_vendor(self):
# An OFL family carries its own minted OF id and a vendor tunes it
# correctly (same base name, non-empty printers). The new claim must
# trip only the snapshot gate, never mint conformance.
fid = afi.generate_filament_id("Generic", "PLA", "Generic PLA Matte")
self.t.write_preset(OFL, preset("Generic PLA Matte @base", filament_id=fid,
instantiation=False,
filament_vendor="Generic",
filament_type="PLA"))
self.t.write_preset(OFL, preset("Generic PLA Matte @System",
inherits="Generic PLA Matte @base",
compatible_printers=[]))
rc, _ = self.t.update_snapshot()
self.assertEqual(rc, 0)
self.t.write_preset("VendorA", preset("Generic PLA Matte @P1",
inherits="Generic PLA Matte @System",
compatible_printers=["P1 0.4 nozzle"]))
errors, out = self.t.check()
self.assertNotIn("does not match the mint", out)
self.assertIn("not sanctioned", out)
self.assertEqual(errors, 1, out)
# After sanctioning the claim the tree is fully green again.
rc, _ = self.t.update_snapshot()
self.assertEqual(rc, 0)
errors, out = self.t.check()
self.assertEqual(errors, 0, out)
def test_check6c_prints_expected_mint(self):
self.t.write_preset("VendorA", preset("Orphan PLA @P1",
compatible_printers=["P1 0.4 nozzle"],
filament_vendor="OV",
filament_type="PLA"))
errors, out = self.t.check()
self.assertGreater(errors, 0)
self.assertIn(afi.generate_filament_id("OV", "PLA", "Orphan PLA"), out)
# ---------------------------------------------------------------------------
# scripts/update_bambu_filament_ids.py: the generated Bambu catalog id map
# ---------------------------------------------------------------------------
class TestBambuMap(unittest.TestCase):
def _bs_tree(self, families):
# families: list of (family, bambu_id, vendor, type); builds a minimal BBL bundle
# with an @base per family carrying the id and one instantiated child.
t = SyntheticTree()
self.addCleanup(t.cleanup)
presets = []
for family, bambu_id, vendor, ftype in families:
presets.append(preset(f"{family} @base", filament_id=bambu_id,
instantiation=False, filament_vendor=vendor,
filament_type=ftype))
presets.append(preset(f"{family} @P1", inherits=f"{family} @base",
compatible_printers=["P1 0.4 nozzle"]))
t.add_vendor("BBL", presets)
filaments, errors = afi.load_vendor_filaments(t.profiles, "BBL")
self.assertEqual(errors, [])
return filaments
def test_one_row_per_family(self):
rows = ubfi.derive_rows(self._bs_tree([("Bambu ABS", "GFB00", "Bambu Lab", "ABS")]), {})
self.assertEqual(rows, {afi.generate_filament_id("Bambu Lab", "ABS", "Bambu ABS"):
{"bambu_id": "GFB00", "vendor": "Bambu Lab", "type": "ABS", "name": "Bambu ABS"}})
def test_shared_bambu_id_is_an_error(self):
with self.assertRaises(SystemExit):
ubfi.derive_rows(self._bs_tree([("A", "GFX00", "V", "PLA"), ("B", "GFX00", "V", "PLA")]), {})
def test_reuses_the_id_we_ship_for_that_triple(self):
rows = ubfi.derive_rows(self._bs_tree([("Bambu ABS", "GFB00", "Bambu Lab", "ABS")]),
{("Bambu Lab", "ABS", "Bambu ABS"): "OFsalted1"})
self.assertIn("OFsalted1", rows)
@unittest.skipUnless(os.path.isdir("/Users/lijiang/codes/BambuStudio/resources/profiles"), "no local clone")
def test_local_clone_yields_the_catalog(self):
rows = ubfi.derive_rows(afi.load_vendor_filaments("/Users/lijiang/codes/BambuStudio/resources/profiles", "BBL")[0], {})
self.assertEqual(len(rows), 100)
self.assertEqual(len({r["bambu_id"] for r in rows.values()}), 100)
class TestOrcaTriplesFromAnalysis(unittest.TestCase):
def _tree(self):
t = SyntheticTree()
self.addCleanup(t.cleanup)
t.add_vendor("VendorA", [
# Same triple declared under two different ids on purpose: mirrors
# a real, sanctioned case (Cubicon's xCeler line keeps its own id
# per printer instead of inheriting the family's @base id).
# Ambiguous, but must stay harmless unless something needs it.
preset("Ambig PLA @base", filament_id="OF111111", instantiation=False,
filament_vendor="V", filament_type="PLA"),
preset("Ambig PLA @P1", filament_id="OF222222", inherits="Ambig PLA @base",
compatible_printers=["P1"]),
# An unambiguous family elsewhere in the same tree.
preset("Needed PLA @base", filament_id="OF333333", instantiation=False,
filament_vendor="V2", filament_type="PLA"),
preset("Needed PLA @P1", inherits="Needed PLA @base",
compatible_printers=["P1"]),
])
return afi.analyze_tree(t.profiles)
def test_ambiguity_outside_needed_triples_is_ignored(self):
analysis = self._tree()
result = ubfi.orca_triples_from_analysis(analysis, {("V2", "PLA", "Needed PLA")})
self.assertEqual(result, {("V2", "PLA", "Needed PLA"): "OF333333"})
def test_ambiguity_inside_needed_triples_is_an_error(self):
analysis = self._tree()
with self.assertRaises(SystemExit):
ubfi.orca_triples_from_analysis(analysis, {("V", "PLA", "Ambig PLA")})
class TestWriteMap(unittest.TestCase):
def test_format(self):
d = tempfile.mkdtemp(prefix="bambu_map_test_")
self.addCleanup(shutil.rmtree, d, ignore_errors=True)
# nested, not-yet-existing directory: write_map must create it
path = os.path.join(d, "out", "bambu_filament_ids.json")
rows = {"OFabc123": {"bambu_id": "GFB00", "vendor": "Bambu Lab", "type": "ABS", "name": "Bambu ABS"}}
ubfi.write_map(path, rows, "66e405477", "2026-09-04")
with open(path, "rb") as f:
raw = f.read()
self.assertTrue(raw.endswith(b"\n"))
self.assertNotIn(b"\r", raw)
text = raw.decode("utf-8")
self.assertEqual(json.loads(text), {
"source": "https://github.com/bambulab/BambuStudio",
"bambustudio_commit": "66e405477",
"generated": "2026-09-04",
"filaments": rows,
})
# sorted top-level keys
self.assertLess(text.index('"bambustudio_commit"'), text.index('"filaments"'))
self.assertLess(text.index('"filaments"'), text.index('"generated"'))
self.assertLess(text.index('"generated"'), text.index('"source"'))
class TestDriftReport(unittest.TestCase):
def test_reports_both_directions(self):
t = SyntheticTree()
self.addCleanup(t.cleanup)
t.add_vendor("BBL", [
preset("Match PLA @base", filament_id="GFX01", instantiation=False,
filament_vendor="V", filament_type="PLA"),
preset("Match PLA @P1", inherits="Match PLA @base",
compatible_printers=["P1"]),
preset("Orphan PLA @base", filament_id="GFX03", instantiation=False,
filament_vendor="V", filament_type="PLA"),
preset("Orphan PLA @P1", inherits="Orphan PLA @base",
compatible_printers=["P1"]),
])
rows = {
# matches the BBL bundle's "Match PLA" family: no drift either way
"OFmatch01": {"bambu_id": "GFX01", "vendor": "V", "type": "PLA", "name": "Match PLA"},
# no family of this identity in the BBL bundle above
"OFghost01": {"bambu_id": "GFX02", "vendor": "V", "type": "PLA", "name": "Upstream Only PLA"},
}
orca_analysis = afi.analyze_tree(t.profiles)
lines = ubfi.drift_report(rows, orca_analysis)
report = "\n".join(lines)
self.assertIn("Upstream Only PLA", report)
self.assertIn("Orca BBL families with no row: 1", report)
self.assertNotIn("Match PLA", report) # the matched family is not drift
if __name__ == "__main__":
unittest.main()