Files
bambuddy/backend/tests/unit/test_process_overrides.py
maziggy f0500578bd Edit the full print-parameter set from the slice dialog
Slicing from Bambuddy meant taking a process preset as-is; any change
meant a round trip through Bambu Studio. The slice dialog now carries
OrcaSlicer's full process tree -- pages, groups, labels, tooltips,
ranges and defaults extracted from the slicer's own sources.

Enable/disable rules are evaluated from the slicer's own enable_if
expressions via a recursive-descent interpreter (no eval, CSP), with
enum comparisons validated against each option's declared values.
Anything undecidable leaves the field editable rather than greyed.

Overrides apply after the source's support config (#1881) and the
designer's carried tweaks (#2622), so an explicit choice always wins;
an untouched panel sends the same request as before.

Adds slice_engine as a separate setting from preferred_slicer -- where
slicing runs is a different axis from which binary the sidecar drives.
Only the sidecar engine is registered, so no picker renders yet.
2026-08-08 16:33:41 +02:00

80 lines
3.7 KiB
Python

"""Unit tests for the slice modal's process-setting overrides."""
import json
from backend.app.services.process_overrides import (
apply_process_overrides,
normalise_process_overrides,
)
def _process(**values: str) -> str:
return json.dumps({"inherits": "0.20mm Standard @BBL X1C", **values})
class TestNormaliseProcessOverrides:
def test_bools_become_the_one_zero_strings_a_preset_stores(self):
assert normalise_process_overrides({"enable_support": True}) == {"enable_support": "1"}
assert normalise_process_overrides({"enable_support": False}) == {"enable_support": "0"}
def test_numbers_become_strings(self):
assert normalise_process_overrides({"wall_loops": 4, "layer_height": 0.16}) == {
"wall_loops": "4",
"layer_height": "0.16",
}
def test_strings_pass_through_including_the_percent_sign(self):
# The frontend serialises percents with the sign; stripping it here
# would silently change the value the slicer sees.
assert normalise_process_overrides({"sparse_infill_density": "35%"}) == {"sparse_infill_density": "35%"}
def test_vector_options_keep_their_list_shape(self):
assert normalise_process_overrides({"default_acceleration": [500, 300]}) == {
"default_acceleration": ["500", "300"]
}
def test_keys_that_are_not_config_identifiers_are_dropped(self):
result = normalise_process_overrides({"wall_loops": 2, "Wall Loops": 3, "__proto__": 1, "a-b": 1, "": 1})
assert result == {"wall_loops": "2"}
def test_values_a_preset_cannot_hold_are_dropped_not_serialised(self):
result = normalise_process_overrides({"wall_loops": 2, "nested": {"a": 1}, "none": None})
assert result == {"wall_loops": "2"}
def test_a_list_containing_a_non_scalar_drops_the_whole_key(self):
# Half-applying a per-extruder vector would send a shorter list than the
# printer has extruders, which is worse than not setting it at all.
assert normalise_process_overrides({"default_acceleration": [500, {"a": 1}]}) == {}
class TestApplyProcessOverrides:
def test_writes_the_users_values_into_the_process_json(self):
result = apply_process_overrides(_process(), {"wall_loops": 4, "enable_support": True})
assert json.loads(result)["wall_loops"] == "4"
assert json.loads(result)["enable_support"] == "1"
def test_keeps_the_inherits_stub_so_the_preset_still_resolves(self):
# A "standard" preset pick is a {inherits: ...} stub; dropping that key
# would leave the slicer with a handful of orphaned values.
result = apply_process_overrides(_process(), {"wall_loops": 4})
assert json.loads(result)["inherits"] == "0.20mm Standard @BBL X1C"
def test_user_value_wins_over_one_already_in_the_preset(self):
result = apply_process_overrides(_process(wall_loops="2"), {"wall_loops": 6})
assert json.loads(result)["wall_loops"] == "6"
def test_empty_overrides_leave_the_json_untouched(self):
original = _process(wall_loops="2")
assert apply_process_overrides(original, {}) == original
def test_overrides_that_all_get_dropped_leave_the_json_untouched(self):
original = _process(wall_loops="2")
assert apply_process_overrides(original, {"Bad Key": 1}) == original
def test_unparseable_process_json_degrades_to_a_plain_slice(self):
# Better a slice with the picked preset than a failed one.
assert apply_process_overrides("not json", {"wall_loops": 4}) == "not json"
def test_non_object_process_json_degrades_to_a_plain_slice(self):
assert apply_process_overrides("[1, 2]", {"wall_loops": 4}) == "[1, 2]"