update profile checks

This commit is contained in:
SoftFever
2026-09-28 19:08:35 +08:00
parent 71bb400e46
commit 00a2c5a087
3 changed files with 1131 additions and 10 deletions
+331 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""Tests for the tree-maintenance half of scripts/orca_profile_tool.py: the
normalize, trim, update-index and check commands, and the subcommand dispatch that
reaches them (stdlib unittest, no external deps).
normalize, trim, update-index, fix-variant and check commands, and the subcommand
dispatch that reaches them (stdlib unittest, no external deps).
The id halves are covered by test_filament_id.py and test_setting_id.py.
@@ -140,6 +140,28 @@ class TestObsoleteKeys(unittest.TestCase):
self.assertEqual(apt.OBSOLETE_KEYS, set(re.findall(r'"([^"\n]+)"', keys)))
class TestVariantScheme(unittest.TestCase):
def test_the_key_sets_are_read_from_the_engine(self):
scheme = apt._variant_scheme()
self.assertEqual(scheme["machine"][1]["retraction_length"], 1)
self.assertEqual(scheme["machine"][1]["machine_max_speed_x"], 2)
self.assertIn("outer_wall_speed", scheme["process"][1])
self.assertIn("nozzle_temperature", scheme["filament"][1])
# Commented out of its initializer, so not a member.
self.assertNotIn("filament_extruder_id", scheme["filament"][1])
for list_key, strides in scheme.values():
self.assertEqual(strides[list_key], 1)
def test_the_variant_names_are_read_from_the_engine(self):
extruder_types, volume_types, legacy, extruder_legacy = apt._variant_names()
self.assertEqual(extruder_types, {"Direct Drive", "Bowden"})
self.assertEqual(volume_types, {"Standard", "High Flow", "TPU High Flow", "E3D High Flow"})
# Hybrid is an enum value no variant string may name.
self.assertNotIn("Hybrid", volume_types)
self.assertEqual(legacy, {"Normal": "Standard", "Big Traffic": "High Flow"})
self.assertEqual(extruder_legacy, {"DirectDrive": "Direct Drive"})
class TestNormalize(TreeCase):
def test_a_missing_type_is_filled_in_from_the_directory(self):
self.t.write("V", "filament/A.json", {"name": "A"})
@@ -917,10 +939,315 @@ class TestNormalized(TreeCase):
# CLI dispatch
# ---------------------------------------------------------------------------
class TestFixVariant(TreeCase):
def preset(self, rel, **data):
name = os.path.splitext(os.path.basename(rel))[0]
self.t.write("V", rel, {"type": rel.split("/")[0], "name": name, **data})
self.t.index("V", rel.split("/")[0], name, rel)
def processes(self, common):
"""outer_wall_speed written once in a list-less base, reached by list-less
presets through one base and by two-variant presets through another."""
dual = ["Direct Drive Standard", "Direct Drive High Flow"]
self.preset("process/common.json", instantiation="false", outer_wall_speed=common)
self.preset("process/single.json", inherits="common", instantiation="false")
self.preset("process/dual.json", inherits="common", instantiation="false",
print_extruder_variant=dual, print_extruder_id=["1", "1"])
for name, base in (("S1", "single"), ("S2", "single"), ("D1", "dual")):
self.preset(f"process/{name}.json", inherits=base, instantiation="true")
def width_errors(self, *strict):
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
errors, _warnings = apt.check_variant_arrays(self.t.profiles, "V", bool(strict))
return errors, buf.getvalue()
def test_a_machine_without_a_variant_list_has_one_variant_per_extruder(self):
# extruder_variant_list defaults to one "Direct Drive Standard" per extruder.
self.preset("machine/M.json", instantiation="true", nozzle_diameter=["0.4", "0.4"],
retraction_length=["0.8", "1.2"], z_hop=["0.4"],
machine_max_speed_x=["500", "200"])
errors, out = self.width_errors()
self.assertEqual(errors, 2, out)
self.assertIn('M.json: "z_hop" has 1 values for variant length 2 (no '
'printer_extruder_variant, so one default variant per extruder) at stride 1, '
'which takes 2', out)
self.assertIn('M.json: "machine_max_speed_x" has 2 values for variant length 2', out)
self.assertNotIn("retraction_length", out)
def test_one_value_is_an_error_where_the_list_has_more_variants(self):
self.preset("process/P.json", instantiation="true", outer_wall_speed=["30"],
print_extruder_id=["1", "1"],
print_extruder_variant=["Direct Drive Standard", "Direct Drive High Flow"])
errors, out = self.width_errors()
self.assertEqual(errors, 1, out)
self.assertIn('P.json: "outer_wall_speed" has 1 values for variant length 2 at stride 1, '
'which takes 2', out)
def test_one_value_is_padded_to_every_variant(self):
self.preset("machine/M.json", instantiation="true", nozzle_diameter=["0.4"] * 3,
retraction_length=["0.8"], machine_max_speed_x=["500", "200"])
rc, out = self.run_command("fix-variant")
self.assertEqual(rc, 0, out)
machine = self.t.read("V", "machine/M.json")
self.assertEqual(machine["retraction_length"], ["0.8"] * 3)
self.assertEqual(machine["machine_max_speed_x"], ["500", "200"] * 3)
self.assertEqual(self.width_errors()[0], 0)
def test_extruder_ids_are_left_to_check(self):
self.preset("process/P.json", instantiation="true", print_extruder_id=["1"],
print_extruder_variant=["Direct Drive Standard", "Direct Drive High Flow"])
self.run_command("fix-variant")
self.assertEqual(self.t.read("V", "process/P.json")["print_extruder_id"], ["1"])
errors, out = self.width_errors()
self.assertEqual(errors, 1, out)
self.assertIn("print_extruder_id has 1 entries for the 2 entries", out)
def test_a_base_is_judged_only_where_its_array_reaches_a_preset(self):
# A leaf overriding the base's one-value limit passes in both modes.
self.preset("machine/common.json", instantiation="false", machine_max_speed_x=["500"])
self.preset("machine/M.json", inherits="common", instantiation="true",
machine_max_speed_x=["500", "200"])
self.assertEqual(self.width_errors()[0], 0)
self.assertEqual(self.width_errors("strict")[0], 0)
def test_a_shared_array_is_fixed_in_the_base_whose_presets_agree(self):
self.processes(["30", "40"])
self.assertEqual(self.width_errors()[0], 0)
errors, out = self.width_errors("strict")
self.assertEqual(errors, 2, out)
self.assertIn('process/S1.json: "outer_wall_speed" has 2 values for variant length 1', out)
rc, out = self.run_command("fix-variant", "--strict")
self.assertEqual(rc, 0, out)
self.assertEqual(self.t.read("V", "process/single.json")["outer_wall_speed"], ["30"])
self.assertEqual(self.t.read("V", "process/common.json")["outer_wall_speed"],
["30", "40"])
self.assertNotIn("outer_wall_speed", self.t.read("V", "process/S1.json"))
self.assertEqual(self.width_errors("strict")[0], 0)
def test_an_inherited_array_is_checked_and_restated_only_when_strict(self):
self.processes(["30"])
self.assertEqual(self.width_errors()[0], 0)
errors, out = self.width_errors("strict")
self.assertEqual(errors, 1, out)
self.assertIn('process/D1.json: "outer_wall_speed" has 1 values for variant length 2 at '
'stride 1, which takes 2 (it comes from V/process/common.json)', out)
before = self.t.bytes_map()
self.run_command("fix-variant")
self.assertEqual(self.t.bytes_map(), before)
rc, out = self.run_command("fix-variant", "--strict")
self.assertEqual(rc, 0, out)
self.assertEqual(self.t.read("V", "process/dual.json")["outer_wall_speed"], ["30", "30"])
self.assertEqual(self.t.read("V", "process/common.json")["outer_wall_speed"], ["30"])
self.assertEqual(self.width_errors("strict")[0], 0)
def test_a_strict_array_never_lands_in_a_file_of_another_width(self):
# common is list-less (width 1) and D2 inherits it directly, so D2 takes it.
self.processes(["30"])
self.preset("process/D2.json", inherits="common", instantiation="true",
print_extruder_variant=["Direct Drive Standard", "Direct Drive High Flow"],
print_extruder_id=["1", "1"])
self.run_command("fix-variant", "--strict")
self.assertEqual(self.t.read("V", "process/D2.json")["outer_wall_speed"], ["30", "30"])
self.assertEqual(self.t.read("V", "process/common.json")["outer_wall_speed"], ["30"])
self.assertEqual(self.width_errors("strict")[0], 0)
def test_extra_values_are_dropped_where_they_are_written(self):
self.preset("process/base.json", instantiation="false", outer_wall_speed=["30", "0"])
self.preset("process/P1.json", inherits="base", instantiation="true",
inner_wall_speed=["40", "0"])
self.preset("process/P2.json", inherits="base", instantiation="true")
self.run_command("fix-variant")
self.assertEqual(self.t.read("V", "process/P1.json")["inner_wall_speed"], ["40"])
self.assertEqual(self.t.read("V", "process/base.json")["outer_wall_speed"], ["30", "0"])
self.run_command("fix-variant", "--strict")
self.assertEqual(self.t.read("V", "process/base.json")["outer_wall_speed"], ["30"])
self.assertNotIn("outer_wall_speed", self.t.read("V", "process/P1.json"))
def test_the_rest_of_the_file_keeps_its_bytes(self):
self.t.write_raw("V", "process/P.json", b'{\n "type": "process", "name": "P",\n'
b' "instantiation": "true",\n "outer_wall_speed": ["30", "0"],\n'
b' "inner_wall_speed": [\n "40",\n "0"\n ]\n}\n')
self.t.index("V", "process", "P", "process/P.json")
self.run_command("fix-variant")
self.assertEqual(self.t.raw("V", "process/P.json"),
b'{\n "type": "process", "name": "P",\n'
b' "instantiation": "true",\n "outer_wall_speed": ["30"],\n'
b' "inner_wall_speed": [\n "40"\n ]\n}\n')
def test_a_dry_run_writes_nothing(self):
self.processes(["30"])
before = self.t.bytes_map()
rc, out = self.run_command("fix-variant", "--strict", "--dry-run")
self.assertEqual(rc, 0, out)
self.assertIn("would resize V/process/dual.json: outer_wall_speed added -> 2", out)
self.assertEqual(self.t.bytes_map(), before)
def test_padding_repeats_the_last_value_or_pair(self):
self.assertEqual(apt._fit_width(["a", "b"], 4, 1), ["a", "b", "b", "b"])
self.assertEqual(apt._fit_width(["a", "b", "c"], 6, 2), ["a", "b"] * 3)
self.assertEqual(apt._fit_width(["a", "b", "c"], 2, 2), ["a", "b"])
self.assertEqual(apt._fit_width(["a"], 4, 2), ["a"] * 4)
class TestVariantNames(TreeCase):
def preset(self, rel, vendor="V", **data):
section = rel.split("/")[0]
name = os.path.splitext(os.path.basename(rel))[0]
self.t.write(vendor, rel, {"type": section, "name": name, **data})
self.t.index(vendor, section, name, rel)
def names(self, vendor="V"):
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
errors = apt.check_variant_names(self.t.profiles, vendor)
return errors, buf.getvalue()
def test_a_legal_variant_layout_passes(self):
self.preset("machine/M.json", instantiation="true", nozzle_diameter=["0.4"],
extruder_type=["Direct Drive"],
extruder_variant_list=["Direct Drive Standard,Direct Drive High Flow"],
printer_extruder_id=["1", "1"],
printer_extruder_variant=["Direct Drive Standard",
"Direct Drive High Flow"],
nozzle_volume_type=["High Flow"],
default_nozzle_volume_type=["Standard"])
self.preset("process/P.json", instantiation="true", print_extruder_id=["1", "1"],
print_extruder_variant=["Direct Drive Standard",
"Direct Drive High Flow"])
self.preset("filament/F.json", instantiation="true",
filament_extruder_variant=["Direct Drive Standard", "Bowden High Flow"])
self.assertEqual(self.names()[0], 0, self.names()[1])
def test_a_variant_the_enum_cannot_build_is_an_error(self):
# An unknown volume type, in a list and in a menu, an extruder type that is not
# one, and the legacy extruder spelling that is rewritten in extruder_type alone.
cases = (("process/P1.json", "print_extruder_variant", "Direct Drive Fast",
'entry 1 holds "Direct Drive Fast"',
"not a nozzle volume type the enum has"),
("machine/M.json", "extruder_variant_list",
"Direct Drive Standard,Direct Drive Fast",
'extruder 1 holds "Direct Drive Fast"',
"not a nozzle volume type the enum has"),
("process/P2.json", "print_extruder_variant", "DD Standard",
'entry 1 holds "DD Standard"',
"does not start with an extruder type the enum has"),
("filament/F.json", "filament_extruder_variant", "DirectDrive Standard",
'entry 1 holds "DirectDrive Standard"',
"does not start with an extruder type the enum has"))
for rel, key, value, _held, _why in cases:
self.preset(rel, instantiation="false", **{key: [value]})
errors, out = self.names()
self.assertEqual(errors, len(cases), out)
for rel, key, _value, held, why in cases:
self.assertIn(f"V/{rel}: {key} {held}", out)
self.assertIn(why, out)
def test_the_legacy_names_the_loader_rewrites_are_errors(self):
self.preset("machine/M.json", extruder_type=["DirectDrive"],
nozzle_volume_type=["Big Traffic"],
default_nozzle_volume_type=["Normal"],
printer_extruder_variant=["Direct Drive Big Traffic"])
errors, out = self.names()
self.assertEqual(errors, 4, out)
self.assertIn('extruder_type spells the legacy name "DirectDrive"; the loader '
'still rewrites it to "Direct Drive"', out)
self.assertIn('V/machine/M.json: nozzle_volume_type spells the legacy name '
'"Big Traffic"', out)
self.assertIn('default_nozzle_volume_type spells the legacy name "Normal"', out)
self.assertIn('holds the legacy variant "Direct Drive Big Traffic"', out)
self.assertIn('a profile writes the enum name: "Direct Drive High Flow"', out)
def test_an_empty_entry_is_an_error(self):
self.preset("filament/F.json",
filament_extruder_variant=["Direct Drive Standard", ""])
errors, out = self.names()
self.assertEqual(errors, 1, out)
self.assertIn("filament_extruder_variant entry 2 is empty", out)
def test_hybrid_is_runtime_only(self):
self.preset("machine/M.json", nozzle_volume_type=["Hybrid"],
default_nozzle_volume_type=["Hybrid"],
printer_extruder_variant=["Direct Drive Hybrid"])
errors, out = self.names()
self.assertEqual(errors, 3, out)
self.assertIn("Hybrid names the sub-nozzles of one hybrid extruder at runtime", out)
self.assertIn('V/machine/M.json: nozzle_volume_type names "Hybrid"', out)
self.assertIn('default_nozzle_volume_type names "Hybrid"', out)
def test_bbl_is_held_to_the_enums_like_every_bundle(self):
# E3D High Flow passes because the engine's enum has it; TPU285 is a name no enum has.
self.preset("machine/M.json", vendor="BBL", extruder_type=["Direct Drive"],
extruder_variant_list=["Direct Drive Standard,Direct Drive E3D High Flow"],
printer_extruder_id=["1", "1"],
printer_extruder_variant=["Direct Drive Standard",
"Direct Drive E3D High Flow"])
self.preset("process/P.json", vendor="BBL", instantiation="true",
print_extruder_variant=["Direct Drive TPU285"])
errors, out = self.names("BBL")
self.assertEqual(errors, 1, out)
self.assertNotIn('holds "Direct Drive E3D High Flow"', out)
self.assertIn('"TPU285" is not a nozzle volume type the enum has', out)
def test_a_list_may_not_name_one_variant_twice(self):
self.preset("filament/F.json",
filament_extruder_variant=["Direct Drive Standard",
"Direct Drive High Flow",
"Direct Drive Standard"])
self.preset("machine/M.json",
extruder_variant_list=["Direct Drive Standard,Direct Drive Standard,"
"Direct Drive High Flow"])
self.preset("process/P.json", print_extruder_id=["1", "1", "1"],
print_extruder_variant=["Direct Drive Standard", "Direct Drive High Flow",
"Direct Drive Standard"])
errors, out = self.names()
self.assertEqual(errors, 3, out)
self.assertIn('filament_extruder_variant lists "Direct Drive Standard" 2 times', out)
self.assertIn('extruder_variant_list extruder 1 lists "Direct Drive Standard" 2 '
"times", out)
self.assertIn('print_extruder_variant lists the pair (extruder 1, "Direct Drive '
'Standard") 2 times', out)
def test_one_variant_on_two_extruders_is_two_pairs_not_a_repeat(self):
self.preset("process/P.json", print_extruder_id=["1", "2"],
print_extruder_variant=["Direct Drive Standard",
"Direct Drive Standard"])
self.assertEqual(self.names()[0], 0, self.names()[1])
def test_an_unknown_enum_value_fails_the_bundle(self):
self.preset("machine/M.json", extruder_type=["Direct Drive", "Magnetic Drive"],
nozzle_volume_type=["Turbo"],
default_nozzle_volume_type=["Fast Flow"])
errors, out = self.names()
self.assertEqual(errors, 3, out)
self.assertIn('extruder_type "Magnetic Drive" is not one of (Bowden, Direct Drive)',
out)
self.assertIn('V/machine/M.json: nozzle_volume_type "Turbo" is not one of (E3D High '
"Flow, High Flow, Standard, TPU High Flow)", out)
self.assertIn('default_nozzle_volume_type "Fast Flow" is not one of (E3D High Flow, '
"High Flow, Standard, TPU High Flow)", out)
def test_a_file_no_list_references_is_not_judged(self):
# It never loads, so its variant names cannot reach anything;
# check_index_coverage reports the file itself.
self.t.write("V", "machine/M.json", {"type": "machine", "name": "M",
"printer_extruder_variant": ["Direct Drive Fast"]})
self.assertEqual(self.names()[0], 0, self.names()[1])
def test_check_runs_it(self):
self.preset("filament/F.json", instantiation="false",
filament_extruder_variant=["Direct Drive Normal"])
self.run_command("update-index") # so nothing but the name check can fail
rc, out = self.run_command("check")
self.assertEqual(rc, 1, out)
self.assertIn('holds the legacy variant "Direct Drive Normal"', out)
class TestDispatch(TreeCase):
def test_each_command_reaches_its_own_writer(self):
self.t.write("V", "filament/A.json", {"type": "filament", "name": "A"})
for command, expected in (("normalize", "normalized"),
("fix-variant", "resized"),
("trim", "unreferenced"),
("update-index", "vendor index")):
with self.subTest(command=command):
@@ -931,7 +1258,8 @@ class TestDispatch(TreeCase):
def test_an_option_belongs_to_one_command_only(self):
for argv in (["trim", "--force"],
["update-index", "--filament-id"],
["check", "--profile-type", "filament"]):
["check", "--profile-type", "filament"],
["normalize", "--strict"]):
with self.subTest(argv=argv):
with self.assertRaises(SystemExit) as cm, \
contextlib.redirect_stdout(io.StringIO()), \