From 3dd547c8be444585fdd3e954f097825ccf01f78b Mon Sep 17 00:00:00 2001 From: packerlschupfer <83344883+packerlschupfer@users.noreply.github.com> Date: Tue, 13 Oct 2026 01:23:55 +0200 Subject: [PATCH] CLI: Fix the -50 error reason assuming a 3mf upload (#16339) The reason recorded for CLI_NO_SUITABLE_OBJECTS (-50) assumed a 3mf being uploaded. The CLI also slices STL, OBJ and STEP input, so the reason now states what both -50 sites check: the plate is empty, or no object on it is both printable and fully inside the print volume. The exit code is unchanged. Adds tests/cli/test_cli_no_suitable_objects.sh. --- src/OrcaSlicer.cpp | 2 +- tests/cli/CMakeLists.txt | 8 +++ tests/cli/test_cli_no_suitable_objects.sh | 83 +++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100755 tests/cli/test_cli_no_suitable_objects.sh diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index c42ae676cc..0d0aea0954 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -206,7 +206,7 @@ std::map cli_errors = { {CLI_OBJECT_ORIENT_FAILED, "An error occurred when auto-orienting object(s)."}, {CLI_MODIFIED_PARAMS_TO_PRINTER, "Found modified parameter in printer preset in the 3mf file, which should not be changed."}, {CLI_FILE_VERSION_NOT_SUPPORTED, "Unsupported 3MF version. Please make sure the 3MF file was created with the official version of Bambu Studio, not a beta version."}, - {CLI_NO_SUITABLE_OBJECTS, "One of the plate is empty or has no object fully inside it. Please check that the 3mf contains no empty plate in Orca Slicer before uploading."}, + {CLI_NO_SUITABLE_OBJECTS, "The plate has nothing to slice: it is empty, or no object on it is both printable and fully inside the print volume."}, {CLI_VALIDATE_ERROR, "There are some incorrect slicing parameters in the 3mf. Please verify the slicing of all plates in Orca Slicer before uploading."}, {CLI_OBJECTS_PARTLY_INSIDE, "Some objects are located over the boundary of the heated bed."}, {CLI_EXPORT_CACHE_DIRECTORY_CREATE_FAILED, "Failed creating directory when exporting cache data."}, diff --git a/tests/cli/CMakeLists.txt b/tests/cli/CMakeLists.txt index d48fe479cf..3f2982c453 100644 --- a/tests/cli/CMakeLists.txt +++ b/tests/cli/CMakeLists.txt @@ -39,6 +39,14 @@ set_tests_properties(cli_arrange_rotations PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 900) +add_test(NAME cli_no_suitable_objects + COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/test_cli_no_suitable_objects.sh $ ${ORCA_CLI_TEST_PYTHON} + ${CMAKE_SOURCE_DIR}/resources/profiles/BBL) +set_tests_properties(cli_no_suitable_objects PROPERTIES + LABELS "CLI;RequiresApp" + SKIP_RETURN_CODE 77 + TIMEOUT 900) + add_test(NAME cli_malformed_input COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/test_cli_malformed_input.sh $ ${ORCA_CLI_TEST_PYTHON}) set_tests_properties(cli_malformed_input PROPERTIES diff --git a/tests/cli/test_cli_no_suitable_objects.sh b/tests/cli/test_cli_no_suitable_objects.sh new file mode 100755 index 0000000000..dad9dd4120 --- /dev/null +++ b/tests/cli/test_cli_no_suitable_objects.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# The reason recorded for CLI_NO_SUITABLE_OBJECTS (-50) has to fit the input the CLI was given. +# Any input format is arranged and sliced, so an object larger than the bed empties the plate just +# as a 3mf with an empty plate does, and the recorded reason must not send such a caller off to +# inspect a 3mf or an upload that is not part of their run. +# +# usage: test_cli_no_suitable_objects.sh +set -u + +BIN="${1:-}" +PY="${2:-python3}" +PROFILES="${3:-}" +# 77 is the test's SKIP_RETURN_CODE. +[ -x "$BIN" ] || { echo "SKIP: orca-slicer binary not found: $BIN"; exit 77; } +[ -d "$PROFILES" ] || { echo "FAIL: profiles directory not found: $PROFILES"; exit 1; } + +WORK="$(mktemp -d "${TMPDIR:-/tmp}/orca-cli-no-suitable-objects.XXXXXX")" +trap 'rm -rf "$WORK"' EXIT + +# box : an ASCII STL box, so the test needs no fixture. +box() { + "$PY" - "$@" <<'EOF' +import sys + +out, sx, sy, sz = sys.argv[1], float(sys.argv[2]), float(sys.argv[3]), float(sys.argv[4]) +v = [(x, y, z) for z in (0, sz) for y in (0, sy) for x in (0, sx)] +with open(out, "w") as f: + f.write("solid box\n") + for a, b, c, d in ((0, 2, 3, 1), (4, 5, 7, 6), (0, 1, 5, 4), (2, 6, 7, 3), (0, 4, 6, 2), (1, 3, 7, 5)): + for tri in ((v[a], v[b], v[c]), (v[a], v[c], v[d])): + f.write("facet normal 0 0 0\nouter loop\n") + for p in tri: + f.write("vertex %g %g %g\n" % p) + f.write("endloop\nendfacet\n") + f.write("endsolid box\n") +EOF +} + +# Wider than the 256 mm bed and wider than its 362 mm diagonal, so no placement can fit it. +box "$WORK/slab.stl" 400 400 5 +# Fits, so the run must succeed: proves the harness itself is sound. +box "$WORK/cube.stl" 10 10 10 + +# slice [option...] -> $WORK//{rc,stdout,stderr,result.json} +slice() { + local out="$WORK/$1" input="$2"; shift 2 + mkdir -p "$out" + timeout 300 "$BIN" --datadir "$out/datadir" \ + --load-settings "$PROFILES/machine/Bambu Lab X1 Carbon 0.4 nozzle.json;$PROFILES/process/0.20mm Standard @BBL X1C.json" \ + "$@" --slice 0 --outputdir "$out" "$input" \ + > "$out/stdout" 2> "$out/stderr" + echo "$?" > "$out/rc" +} + +# exit_code : the CLI's negative code as the shell saw it, e.g. -50 arrives as 206. +exit_code() { "$PY" -c "import sys; rc = int(open(sys.argv[1]).read()); print(rc - 256 if rc > 127 else rc)" "$WORK/$1/rc"; } + +fail() { echo "FAIL: $*"; exit 1; } + +# --allow-rotations keeps this on the generic reason: a translate-only arrange has its own, +# more specific one. The slab is wider than the 362 mm bed diagonal, so rotation cannot place it. +slice slab "$WORK/slab.stl" --allow-rotations +[ "$(exit_code slab)" = "-50" ] || fail "slab: expected -50, got $(exit_code slab); $(tail -n 5 "$WORK/slab/stdout")" +"$PY" - "$WORK/slab/result.json" <<'EOF' || exit 1 +import json, sys + +result = json.load(open(sys.argv[1])) +if result["return_code"] != -50: + sys.exit("FAIL: result.json return_code %s, expected -50" % result["return_code"]) +reason = result["error_string"] +if not reason.strip(): + sys.exit("FAIL: result.json carries no error_string") +# The run was handed an STL and asked for g-code: neither a 3mf nor an upload exists to check. +for word in ("3mf", "3MF", "upload"): + if word in reason: + sys.exit("FAIL: error_string mentions %r for an STL run: %s" % (word, reason)) +EOF + +slice cube "$WORK/cube.stl" +[ "$(exit_code cube)" = "0" ] || fail "cube: expected 0, got $(exit_code cube); $(tail -n 5 "$WORK/cube/stdout")" +[ -s "$WORK/cube/plate_1.gcode" ] || fail "cube: no g-code written" + +echo "PASS: the no-suitable-objects reason fits a non-3mf run"