mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-18 08:22:06 +00:00
Complete the plugin settings removal after the feat/plugin-feature merge
The merge kept this branch's PluginConfig design, which deletes PluginDescriptor::settings, get_plugin_settings() and ctx.params, but left references to them behind: the slic3rutils target did not build, and the bindings test still asserted the removed ctx.params attribute. Port the two settings tests onto PluginConfig instead of dropping them. They guard a field bug where a cloud-metadata refresh wiped a plugin's settings and it silently ran on its own defaults, so the equivalent properties are still worth pinning: that a stored config survives the refresh, and that an edited config reaches the plugin through a real dispatch. Also defer PluginsConfigDialog's web commands off the webview script-message callback, as PluginsDialog already does. Its remove_preset_override handler put a modal wxMessageBox on that stack, which is the GTK crash class fixed in b779a7bfed/f2ccbfc8b5 for the sibling dialog.
This commit is contained in:
@@ -1,46 +1,27 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <libslic3r/Utils.hpp>
|
||||
#include <slic3r/plugin/PluginConfig.hpp>
|
||||
#include <slic3r/plugin/PluginDescriptor.hpp>
|
||||
#include <slic3r/plugin/PluginFsUtils.hpp>
|
||||
#include <slic3r/plugin/PluginManager.hpp>
|
||||
#include <slic3r/plugin/PythonInterpreter.hpp>
|
||||
#include <slic3r/plugin/PythonPluginInterface.hpp>
|
||||
|
||||
#include "plugin_test_utils.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
using namespace Slic3r;
|
||||
namespace fs = boost::filesystem;
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace {
|
||||
|
||||
// Point data_dir() at a throwaway directory for the lifetime of a test and restore the previous
|
||||
// value afterwards (same pattern as test_plugin_lifecycle.cpp).
|
||||
struct ScopedDataDir
|
||||
{
|
||||
std::string previous;
|
||||
fs::path dir;
|
||||
|
||||
explicit ScopedDataDir(const std::string& tag)
|
||||
{
|
||||
previous = data_dir();
|
||||
dir = fs::temp_directory_path() / fs::unique_path("orca-" + tag + "-%%%%-%%%%");
|
||||
fs::create_directories(dir);
|
||||
set_data_dir(dir.string());
|
||||
}
|
||||
|
||||
~ScopedDataDir()
|
||||
{
|
||||
set_data_dir(previous);
|
||||
boost::system::error_code ec;
|
||||
fs::remove_all(dir, ec);
|
||||
}
|
||||
|
||||
fs::path plugins_dir() const { return dir / "orca_plugins"; }
|
||||
};
|
||||
|
||||
// Shut both singletons down while boost::log is still alive; left to their static
|
||||
// destructors, shutdown()'s logging runs after boost::log tears down its thread-local
|
||||
// storage and crashes the process on exit (same reason ScopedPluginManager exists in
|
||||
@@ -55,46 +36,46 @@ struct ScopedManagerShutdown
|
||||
}
|
||||
};
|
||||
|
||||
// A plugin whose per-plugin settings live in the PEP-723 header — the only place they exist.
|
||||
const char* const SETTINGS_PLUGIN_SOURCE = R"PY(# /// script
|
||||
const char* const CLOUD_PLUGIN_SOURCE = R"PY(# /// script
|
||||
# requires-python = ">=3.12"
|
||||
#
|
||||
# [tool.orcaslicer.plugin]
|
||||
# name = "Settings Cloud Plugin"
|
||||
# name = "Config Cloud Plugin"
|
||||
# type = "slicing-pipeline"
|
||||
# version = "1.0"
|
||||
#
|
||||
# [tool.orcaslicer.plugin.settings]
|
||||
# twist_deg_per_mm = "1.0"
|
||||
# taper_per_mm = "0.0"
|
||||
# ///
|
||||
print('ok')
|
||||
)PY";
|
||||
|
||||
} // namespace
|
||||
|
||||
// Regression: update_cloud_metadata() replaces a matched entry's descriptor with the cloud
|
||||
// catalog record. Cloud records never carry [tool.orcaslicer.plugin.settings] (it exists only
|
||||
// in the local package's PEP-723 header, parsed at discovery), so the merge must preserve the
|
||||
// locally-parsed settings. When it does not, get_plugin_settings() serves an empty map and
|
||||
// plugins silently fall back to their built-in defaults (found via Twistify running with its
|
||||
// demo defaults instead of the header values, 2026-07-17).
|
||||
TEST_CASE("cloud metadata refresh preserves locally-parsed plugin settings", "[PluginCloudMetadata]")
|
||||
// Regression: update_cloud_metadata() replaces a matched entry's descriptor wholesale with the
|
||||
// cloud catalog record (`entry = cloud_entry`). Configuration used to ride on the descriptor, so
|
||||
// that overwrite silently wiped it and plugins fell back to their built-in defaults (found via
|
||||
// Twistify running with its demo defaults instead of the configured values, 2026-07-17).
|
||||
// Configuration now lives in PluginConfig, keyed by the capability identity and kept off the
|
||||
// descriptor entirely, so the merge cannot reach it. This asserts that end to end: a stored
|
||||
// config survives the same refresh path, while the descriptor fields the refresh owns do update.
|
||||
//
|
||||
// The capability need not exist for this to be meaningful: what is pinned is the architectural
|
||||
// invariant that config never rides on the descriptor again. Anyone reintroducing it there, or
|
||||
// adding a cloud-refresh path that prunes config, fails here.
|
||||
TEST_CASE("cloud metadata refresh preserves a plugin's stored config", "[PluginCloudMetadata]")
|
||||
{
|
||||
ScopedManagerShutdown manager_shutdown_guard; // declared first: destroyed last
|
||||
ScopedDataDir data_dir_guard("cloud-meta-settings");
|
||||
ScopedDataDir data_dir_guard("cloud-meta-config");
|
||||
|
||||
// A locally-installed cloud plugin: package .py with a settings header, plus an
|
||||
// install-state sidecar carrying the cloud identity.
|
||||
// A locally-installed cloud plugin: package .py plus an install-state sidecar carrying the
|
||||
// cloud identity. Discovery derives plugin_key from the cloud UUID.
|
||||
const std::string uuid = "11111111-2222-3333-4444-555555555555";
|
||||
const fs::path plugin_dir = data_dir_guard.plugins_dir() / uuid;
|
||||
const fs::path plugin_dir = fs::path(get_orca_plugins_dir()) / uuid;
|
||||
fs::create_directories(plugin_dir);
|
||||
{
|
||||
std::ofstream out((plugin_dir / "cloud_plugin-test.py").string(), std::ios::binary);
|
||||
out << SETTINGS_PLUGIN_SOURCE;
|
||||
out << CLOUD_PLUGIN_SOURCE;
|
||||
}
|
||||
PluginDescriptor sidecar;
|
||||
sidecar.name = "Settings Cloud Plugin";
|
||||
sidecar.name = "Config Cloud Plugin";
|
||||
sidecar.installed_version = "1.0";
|
||||
sidecar.cloud = CloudPluginState{uuid, true, false, false, false};
|
||||
REQUIRE(write_install_state(plugin_dir, sidecar));
|
||||
@@ -109,25 +90,38 @@ TEST_CASE("cloud metadata refresh preserves locally-parsed plugin settings", "[P
|
||||
return {};
|
||||
};
|
||||
|
||||
// Discovery parsed the header settings (premise).
|
||||
PluginDescriptor discovered = find_by_uuid();
|
||||
REQUIRE(discovered.settings.count("twist_deg_per_mm") == 1);
|
||||
CHECK(discovered.settings.at("twist_deg_per_mm") == "1.0");
|
||||
const PluginDescriptor discovered = find_by_uuid();
|
||||
REQUIRE(discovered.plugin_key == uuid);
|
||||
REQUIRE(discovered.version == "1.0");
|
||||
|
||||
// A cloud catalog refresh for the same plugin: the record knows name/version/uuid but has
|
||||
// no settings, no local paths.
|
||||
// The user has configured the plugin's capability (premise).
|
||||
const PluginCapabilityId id{PluginCapabilityType::SlicingPipeline, "Twist", uuid};
|
||||
const json configured{{"twist_deg_per_mm", 1.0}, {"taper_per_mm", 0.0}};
|
||||
REQUIRE(manager.get_config().store_capability_config(id, configured));
|
||||
|
||||
// A cloud catalog refresh for the same plugin: the record knows name/version/uuid and knows
|
||||
// nothing about local config or local paths.
|
||||
PluginDescriptor cloud_record;
|
||||
cloud_record.name = "Settings Cloud Plugin";
|
||||
cloud_record.name = "Config Cloud Plugin";
|
||||
cloud_record.plugin_key = uuid;
|
||||
cloud_record.version = "1.1";
|
||||
cloud_record.cloud = CloudPluginState{uuid, false, false, false, false};
|
||||
manager.update_cloud_metadata({cloud_record});
|
||||
|
||||
// Cloud metadata landed on the descriptor...
|
||||
const PluginDescriptor refreshed = find_by_uuid();
|
||||
// Cloud metadata landed...
|
||||
CHECK(refreshed.version == "1.1");
|
||||
// ...and the locally-parsed settings survived the merge.
|
||||
REQUIRE(refreshed.settings.count("twist_deg_per_mm") == 1);
|
||||
CHECK(refreshed.settings.at("twist_deg_per_mm") == "1.0");
|
||||
CHECK(refreshed.settings.count("taper_per_mm") == 1);
|
||||
CHECK(refreshed.plugin_key == uuid);
|
||||
CHECK(refreshed.installed_version == "1.0");
|
||||
|
||||
// ...and the stored config is untouched, both in the live store...
|
||||
const auto stored = manager.get_config().get_config(id);
|
||||
REQUIRE(stored);
|
||||
CHECK(stored->config == configured);
|
||||
|
||||
// ...and on disk, which is what the next run reads back.
|
||||
PluginConfig reloaded;
|
||||
reloaded.load();
|
||||
REQUIRE(reloaded.has_config(id));
|
||||
CHECK(reloaded.get_config(id)->config == configured);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user