Repair shipped default materials and obsolete settings, and validate them (#15741)

* add orca profile skill

* add default material check

Improve validation for default materials and filament profiles

* Fix default materials and obsolete keys

* clarifying orca-profiles skill
This commit is contained in:
SoftFever
2026-09-18 00:39:46 +08:00
committed by GitHub
parent 60b4a61854
commit f520e9221f
2135 changed files with 85633 additions and 87858 deletions
+3 -1
View File
@@ -1696,7 +1696,9 @@ class TestCli(unittest.TestCase):
["--update-snapshot"], # the pre-subcommand flag
["nonsense"], # not a command
["generate-id", "--filament-id", "--setting-id"],
["generate-id", "--materials"], # check's option
["generate-id", "--snapshot", "x"], # check's option
["check", "--materials"], # removed flag
["check", "--obsolete-keys"], # removed flag
["check", "--filament-id"], # generate-id's option
["normalize", "--snapshot", "x"], # not a snapshot command
["normalize", "--profile-type", "nozzle"]): # not a profile type
+114 -10
View File
@@ -12,6 +12,7 @@ import contextlib
import io
import json
import os
import re
import shutil
import sys
import tempfile
@@ -125,6 +126,20 @@ class TreeCase(unittest.TestCase):
# normalize
# ---------------------------------------------------------------------------
class TestObsoleteKeys(unittest.TestCase):
def test_obsolete_keys_match_the_loader_ignore_set(self):
path = os.path.join(REPO_ROOT, "src", "libslic3r", "PrintConfig.cpp")
with open(path, encoding="utf-8") as f:
source = f.read()
match = re.search(
r"void PrintConfigDef::handle_legacy\(.*?"
r"static\s+std::set<std::string>\s+ignore\s*=\s*\{(.*?)\};",
source, re.DOTALL)
self.assertIsNotNone(match, "Could not locate the loader's obsolete-key set")
keys = re.sub(r"//[^\n]*|/\*.*?\*/", "", match.group(1), flags=re.DOTALL)
self.assertEqual(apt.OBSOLETE_KEYS, set(re.findall(r'"([^"\n]+)"', keys)))
class TestNormalize(TreeCase):
def test_a_missing_type_is_filled_in_from_the_directory(self):
self.t.write("V", "filament/A.json", {"name": "A"})
@@ -148,16 +163,36 @@ class TestNormalize(TreeCase):
self.t.write("V", "filament/A.json", {
"type": "filament", "name": "A", "version": "1.2.3",
"is_custom_defined": "1", "filament_type": "PLA",
"filament_vendor": "AV", "travel_speed": 200})
"filament_vendor": "AV", "travel_speed": 200,
"filament_load_time": ["15"], "filament_unload_time": "0"})
rc, out = self.run_command("normalize")
self.assertEqual(rc, 0, out)
data = self.t.read("V", "filament/A.json")
self.assertNotIn("version", data)
self.assertNotIn("is_custom_defined", data)
self.assertNotIn("travel_speed", data) # a process setting, not a filament one
self.assertNotIn("filament_load_time", data)
self.assertNotIn("filament_unload_time", data)
self.assertEqual(data["filament_type"], ["PLA"])
self.assertEqual(data["filament_vendor"], ["AV"])
def test_obsolete_keys_are_removed_from_every_profile_type(self):
for sub in ("filament", "process", "machine"):
with self.subTest(profile_type=sub):
expected = {"type": sub, "name": "A"}
self.t.write("V", f"{sub}/A.json", {
**expected, "silent_mode": "", "adaptive_layer_height": "0",
"anisotropic_surfaces": "1", "filament_load_time": ["0"],
"filament_unload_time": "0"})
rc, out = self.run_command("normalize")
self.assertEqual(rc, 0, out)
self.assertEqual(self.t.read("V", f"{sub}/A.json"), expected)
before = self.t.bytes_map()
rc, out = self.run_command("normalize")
self.assertEqual(rc, 0, out)
self.assertIn("0 profile(s) normalized", out)
self.assertEqual(self.t.bytes_map(), before)
def test_the_larger_extruder_clearance_wins(self):
# Keeping the smaller one would licence a toolhead collision.
self.t.write("V", "machine/M.json", {
@@ -186,6 +221,15 @@ class TestNormalize(TreeCase):
def test_a_conforming_tree_is_left_byte_identical(self):
self.t.write("V", "filament/A.json", {"type": "filament", "name": "A"})
# These used to be misclassified as obsolete: one is active, the other
# is a legacy alias that still supplies the toolhead clearance on load.
self.t.write("V", "machine/M.json", {
"type": "machine", "name": "M", "extruder_type": ["Direct Drive"],
"extruder_clearance_max_radius": "68", "machine_load_filament_time": "15",
"machine_unload_filament_time": "10"})
self.t.write("V", "process/P.json", {
"type": "process", "name": "P", "travel_speed": "200",
"top_surface_fill_order": "outward"})
before = self.t.bytes_map()
rc, out = self.run_command("normalize")
self.assertEqual(rc, 0, out)
@@ -200,7 +244,7 @@ class TestNormalize(TreeCase):
b'{\n\t"type": "filament",\n\t"name": "A"\n}\n')
def test_dry_run_writes_nothing(self):
self.t.write("V", "filament/A.json", {"name": "A"})
self.t.write("V", "filament/A.json", {"name": "A", "bed_temperature": ["60"]})
before = self.t.bytes_map()
rc, out = self.run_command("normalize", "--dry-run")
self.assertEqual(rc, 0, out)
@@ -453,6 +497,8 @@ class TestCheck(TreeCase):
errors += apt.check_filament_id_length(self.t.profiles, "V")
conflict, _warn = apt.check_conflict_keys(self.t.profiles, "V")
errors += conflict
materials, _warn = apt.check_machine_default_materials(self.t.profiles, "V")
errors += materials
return errors, buf.getvalue()
def test_a_clean_bundle_reports_nothing(self):
@@ -467,6 +513,27 @@ class TestCheck(TreeCase):
self.assertGreater(errors, 0)
self.assertIn("'compatible_printers' missing", out)
def test_a_library_filament_may_leave_compatible_printers_empty(self):
# The shared library is exempt from that rule and nothing else.
self.t.write(apt.OFL, "filament/A.json",
{"type": "filament", "name": "A", "instantiation": "true"})
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
errors = apt.check_filament_compatible_printers(self.t.profiles, apt.OFL)
self.assertEqual(errors, 0, buf.getvalue())
def test_the_library_is_checked_like_any_other_bundle(self):
# A file the library's own index does not reference must fail plain
# `check`, now that the per-vendor pass no longer skips it.
self.t.write(apt.OFL, "filament/Stray.json",
{"type": "filament", "name": "Stray"})
snapshot = os.path.join(self.t.dir, "snapshot.json")
self.run_command("update-snapshot", "--snapshot", snapshot)
rc, out = self.run_command("check", "--snapshot", snapshot)
self.assertEqual(rc, 1, out)
self.assertIn(f"{apt.OFL}/filament/Stray.json: no {apt.OFL}.json list "
f"references it", out)
def test_a_duplicate_key_is_an_error(self):
self.bundle().write_raw("V", "filament/B.json",
b'{"type":"filament","name":"B","name":"B2"}')
@@ -516,15 +583,28 @@ class TestCheck(TreeCase):
self.assertGreater(errors, 0)
self.assertIn("Filament id too long", out)
def test_obsolete_keys_are_opt_in_warnings(self):
def test_obsolete_key_warnings_exclude_active_and_renamed_options(self):
self.bundle().write("V", "filament/B.json", {
"type": "filament", "name": "B", "silent_mode": True})
"type": "filament", "name": "B", "silent_mode": "0",
"anisotropic_surfaces": "0", "extruder_type": ["Direct Drive"],
"extruder_clearance_max_radius": "68"})
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
warnings = apt.check_obsolete_keys(self.t.profiles, "V")
self.assertEqual(warnings, 1)
self.assertEqual(warnings, 2)
self.assertIn("Obsolete key", buf.getvalue())
def test_obsolete_key_warnings_run_without_a_flag(self):
self.t.write("V", "filament/A.json", {
"type": "filament", "name": "A", "silent_mode": "0"})
self.run_command("update-index")
snapshot = os.path.join(self.t.dir, "snapshot.json")
self.run_command("update-snapshot", "--snapshot", snapshot)
rc, out = self.run_command("check", "--snapshot", snapshot)
self.assertEqual(rc, 1, out) # normalization also rejects the obsolete key
self.assertIn("Obsolete key: 'silent_mode' found in V/filament/A.json", out)
self.assertIn("Files with warnings : 1", out)
def test_a_default_material_must_exist_somewhere(self):
self.bundle().write("V", "machine/M.json", {
"type": "machine", "name": "M 0.4 nozzle",
@@ -535,6 +615,32 @@ class TestCheck(TreeCase):
self.assertEqual(errors, 1)
self.assertIn("'Nope'", buf.getvalue())
def test_a_default_material_fails_check_without_a_flag(self):
# The reference check is part of the default run, not an opt-in: a
# dangling name has to fail plain `check`.
self.bundle()
self.t.write("V", "machine/M.json", {
"type": "machine", "name": "M 0.4 nozzle",
"default_filament_profile": ["A", "Nope"]})
self.t.index("V", "machine", "M 0.4 nozzle", "machine/M.json")
snapshot = os.path.join(self.t.dir, "snapshot.json")
self.run_command("update-snapshot", "--snapshot", snapshot)
rc, out = self.run_command("check", "--snapshot", snapshot)
self.assertEqual(rc, 1, out)
self.assertIn("Missing filament profile: 'Nope'", out)
def test_the_stray_user_directory_is_not_a_vendor(self):
# A local validator run leaves resources/profiles/user/ behind; an
# unscoped check must not count it as a bundle and warn about it.
self.bundle()
for sub in apt.PROFILE_SUBDIRS:
os.makedirs(os.path.join(self.t.profiles, apt.USER_DIR, "default", sub))
snapshot = os.path.join(self.t.dir, "snapshot.json")
self.run_command("update-snapshot", "--snapshot", snapshot)
_rc, out = self.run_command("check", "--snapshot", snapshot)
self.assertIn("Checked vendors : 1", out)
self.assertNotIn("user", out)
def names(self, vendor="V"):
"""The preset name check for one bundle, which is what --vendor narrows."""
buf = io.StringIO()
@@ -752,9 +858,8 @@ class TestNormalized(TreeCase):
self.assertEqual(gaps["stale_index"], 0, out)
def test_the_shared_base_bundle_is_covered_too(self):
# The per-vendor pass leaves OrcaFilamentLibrary out because its filaments are
# generic by design. That says nothing about the shape of its files, and
# normalize and update-index rewrite that bundle like any other.
# normalize and update-index own the shape of every bundle, the shared
# library included.
self.t.write(apt.OFL, "filament/A.json",
{"type": "filament", "name": "A", "version": "01.00.00.00"})
rc, out = self.run_command("check", "--snapshot", self.snapshot())
@@ -791,8 +896,7 @@ class TestDispatch(TreeCase):
self.assertIn(expected, out)
def test_an_option_belongs_to_one_command_only(self):
for argv in (["normalize", "--materials"],
["trim", "--force"],
for argv in (["trim", "--force"],
["update-index", "--filament-id"],
["check", "--profile-type", "filament"],
["update-snapshot", "--vendor", "V"]):