mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-12 19:47:43 +00:00
fix: stop Qidi slot parse throwing on null
This commit is contained in:
@@ -193,20 +193,10 @@ bool QidiPrinterAgent::fetch_slot_info(const std::string& base_url,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto json = nlohmann::json::parse(response_body, nullptr, false, true);
|
nlohmann::json status;
|
||||||
if (json.is_discarded()) {
|
nlohmann::json variables;
|
||||||
error = "Invalid JSON response";
|
if (!parse_slot_response(response_body, status, variables, error))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!json.contains("result") || !json["result"].contains("status") || !json["result"]["status"].contains("save_variables") ||
|
|
||||||
!json["result"]["status"]["save_variables"].contains("variables")) {
|
|
||||||
error = "Unexpected JSON structure";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& variables = json["result"]["status"]["save_variables"]["variables"];
|
|
||||||
auto& status = json["result"]["status"];
|
|
||||||
|
|
||||||
box_count = variables.value("box_count", 1);
|
box_count = variables.value("box_count", 1);
|
||||||
if (box_count < 0) {
|
if (box_count < 0) {
|
||||||
@@ -281,6 +271,31 @@ bool QidiPrinterAgent::fetch_slot_info(const std::string& base_url,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QidiPrinterAgent::parse_slot_response(const std::string& response_body,
|
||||||
|
nlohmann::json& status,
|
||||||
|
nlohmann::json& variables,
|
||||||
|
std::string& error)
|
||||||
|
{
|
||||||
|
auto json = nlohmann::json::parse(response_body, nullptr, false, true);
|
||||||
|
if (json.is_discarded()) {
|
||||||
|
error = "Invalid JSON response";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json.is_object() || !json.contains("result") || !json["result"].is_object() || !json["result"].contains("status") ||
|
||||||
|
!json["result"]["status"].is_object() || !json["result"]["status"].contains("save_variables") ||
|
||||||
|
!json["result"]["status"]["save_variables"].is_object() || !json["result"]["status"]["save_variables"].contains("variables") ||
|
||||||
|
!json["result"]["status"]["save_variables"]["variables"].is_object()) {
|
||||||
|
// why: Qidi firmware may send null here, but json::value() throws for it.
|
||||||
|
error = "Unexpected JSON structure: save_variables.variables must be an object";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = json["result"]["status"];
|
||||||
|
variables = status["save_variables"]["variables"];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool QidiPrinterAgent::fetch_filament_dict(const std::string& base_url,
|
bool QidiPrinterAgent::fetch_filament_dict(const std::string& base_url,
|
||||||
const std::string& api_key,
|
const std::string& api_key,
|
||||||
QidiFilamentDict& dict,
|
QidiFilamentDict& dict,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define __QIDI_PRINTER_AGENT_HPP__
|
#define __QIDI_PRINTER_AGENT_HPP__
|
||||||
|
|
||||||
#include "MoonrakerPrinterAgent.hpp"
|
#include "MoonrakerPrinterAgent.hpp"
|
||||||
|
#include "nlohmann/json_fwd.hpp"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -21,6 +22,11 @@ public:
|
|||||||
// Override filament sync (Qidi-specific implementation)
|
// Override filament sync (Qidi-specific implementation)
|
||||||
bool fetch_filament_info(std::string dev_id) override;
|
bool fetch_filament_info(std::string dev_id) override;
|
||||||
|
|
||||||
|
static bool parse_slot_response(const std::string& response_body,
|
||||||
|
nlohmann::json& status,
|
||||||
|
nlohmann::json& variables,
|
||||||
|
std::string& error);
|
||||||
|
|
||||||
// Print operations — emit QiDi multi-color box config, then delegate to base.
|
// Print operations — emit QiDi multi-color box config, then delegate to base.
|
||||||
int start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
|
int start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) override;
|
||||||
int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) override;
|
int start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) override;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ add_executable(${_TEST_NAME}_tests
|
|||||||
test_plugin_capabilities_in_use.cpp
|
test_plugin_capabilities_in_use.cpp
|
||||||
test_plugin_status.cpp
|
test_plugin_status.cpp
|
||||||
test_printer_agent.cpp
|
test_printer_agent.cpp
|
||||||
|
test_qidi_printer_agent.cpp
|
||||||
test_plugin_install.cpp
|
test_plugin_install.cpp
|
||||||
test_plugin_lifecycle.cpp
|
test_plugin_lifecycle.cpp
|
||||||
test_slicing_pipeline_bindings.cpp
|
test_slicing_pipeline_bindings.cpp
|
||||||
|
|||||||
131
tests/slic3rutils/test_qidi_printer_agent.cpp
Normal file
131
tests/slic3rutils/test_qidi_printer_agent.cpp
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#include <catch2/catch_all.hpp>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include <slic3r/Utils/QidiPrinterAgent.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
TEST_CASE("Qidi slot response rejects null variables without throwing", "[QidiPrinterAgent]")
|
||||||
|
{
|
||||||
|
const std::string response = R"({
|
||||||
|
"result": {
|
||||||
|
"status": {
|
||||||
|
"save_variables": {
|
||||||
|
"variables": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})";
|
||||||
|
nlohmann::json status;
|
||||||
|
nlohmann::json variables;
|
||||||
|
std::string error;
|
||||||
|
bool parsed = true;
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(parsed = QidiPrinterAgent::parse_slot_response(response, status, variables, error));
|
||||||
|
CHECK_FALSE(parsed);
|
||||||
|
CHECK_THAT(error, Catch::Matchers::ContainsSubstring("variables"));
|
||||||
|
CHECK_THAT(error, Catch::Matchers::ContainsSubstring("object"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Qidi slot response rejects missing and non-object fields without throwing", "[QidiPrinterAgent]")
|
||||||
|
{
|
||||||
|
std::string response;
|
||||||
|
|
||||||
|
SECTION("missing result")
|
||||||
|
{
|
||||||
|
response = R"({})";
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("non-object result")
|
||||||
|
{
|
||||||
|
response = R"({"result":null})";
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("missing status")
|
||||||
|
{
|
||||||
|
response = R"({"result":{}})";
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("non-object status")
|
||||||
|
{
|
||||||
|
response = R"({"result":{"status":null}})";
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("missing save_variables")
|
||||||
|
{
|
||||||
|
response = R"({"result":{"status":{}}})";
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("non-object save_variables")
|
||||||
|
{
|
||||||
|
response = R"({"result":{"status":{"save_variables":null}}})";
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("missing variables")
|
||||||
|
{
|
||||||
|
response = R"({"result":{"status":{"save_variables":{}}}})";
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("scalar")
|
||||||
|
{
|
||||||
|
response = R"({"result":{"status":{"save_variables":{"variables":42}}}})";
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("array")
|
||||||
|
{
|
||||||
|
response = R"({"result":{"status":{"save_variables":{"variables":[]}}}})";
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json status;
|
||||||
|
nlohmann::json variables;
|
||||||
|
std::string error;
|
||||||
|
bool parsed = true;
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(parsed = QidiPrinterAgent::parse_slot_response(response, status, variables, error));
|
||||||
|
CHECK_FALSE(parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Qidi slot response exposes valid status and variables", "[QidiPrinterAgent]")
|
||||||
|
{
|
||||||
|
const std::string response = R"({
|
||||||
|
"result": {
|
||||||
|
"status": {
|
||||||
|
"save_variables": {
|
||||||
|
"variables": {
|
||||||
|
"box_count": 2,
|
||||||
|
"color_slot0": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"box_stepper slot0": {
|
||||||
|
"runout_button": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})";
|
||||||
|
nlohmann::json status;
|
||||||
|
nlohmann::json variables;
|
||||||
|
std::string error;
|
||||||
|
bool parsed = false;
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(parsed = QidiPrinterAgent::parse_slot_response(response, status, variables, error));
|
||||||
|
REQUIRE(parsed);
|
||||||
|
CHECK(status.is_object());
|
||||||
|
CHECK(variables.is_object());
|
||||||
|
CHECK(variables.at("box_count") == 2);
|
||||||
|
CHECK(status.contains("box_stepper slot0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Qidi slot response rejects invalid JSON", "[QidiPrinterAgent]")
|
||||||
|
{
|
||||||
|
nlohmann::json status;
|
||||||
|
nlohmann::json variables;
|
||||||
|
std::string error;
|
||||||
|
bool parsed = true;
|
||||||
|
|
||||||
|
REQUIRE_NOTHROW(parsed = QidiPrinterAgent::parse_slot_response("{not json", status, variables, error));
|
||||||
|
CHECK_FALSE(parsed);
|
||||||
|
CHECK(error == "Invalid JSON response");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user