mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-20 23:42:54 +00:00
Updated unit tests
This commit is contained in:
@@ -186,4 +186,29 @@ assert.equal(ctx.revealTarget(100, -5, 50), 50, "negative start is clamped to th
|
||||
assert.equal(ctx.revealTarget(200, 50, 100), 150, "a scroll viewpoint reveals a window past the current rows");
|
||||
assert.equal(ctx.revealTarget(10, 0, 50), 10, "a list shorter than one window stays fully materialized");
|
||||
|
||||
// visibleFavourites: the quick-bar drops pins whose action no longer exists (plugin unloaded,
|
||||
// command removed) and collapses duplicate ids, keeping the persisted pin order.
|
||||
assert.deepEqual(ctx.visibleFavourites(["a", "b", "c"], [{ id: "a" }, { id: "b" }]),
|
||||
["a", "b"], "a pin with no live action is dropped from the quick-bar");
|
||||
assert.deepEqual(ctx.visibleFavourites(["b", "a", "b"], [{ id: "a" }, { id: "b" }]),
|
||||
["b", "a"], "duplicate pins collapse to the first occurrence");
|
||||
assert.deepEqual(ctx.visibleFavourites([], [{ id: "a" }]), [], "no pins renders an empty quick-bar");
|
||||
assert.deepEqual(ctx.visibleFavourites(["a"], []), [], "a stale config with no actions renders nothing");
|
||||
|
||||
// tileCode: monogram ladder - title initial, then title+source initials, then a stable ordinal
|
||||
// by id. The ordinal is keyed by id, not by list order, so frecency reshuffles never renumber tiles.
|
||||
const monoPool = [
|
||||
{ id: "z", title: "Repair", source: "Mesh Tools" },
|
||||
{ id: "a", title: "Repair", source: "Mesh Tools" },
|
||||
{ id: "b", title: "Repair", source: "Filament" }
|
||||
];
|
||||
assert.equal(ctx.tileCode(monoPool[0], monoPool), "MR2",
|
||||
"same title+source resolves to source+title initials with an id-keyed ordinal (id z sorts after id a)");
|
||||
assert.equal(ctx.tileCode(monoPool[1], monoPool), "MR1",
|
||||
"the earlier id is numbered first among same-title+source tiles");
|
||||
assert.equal(ctx.tileCode(monoPool[2], monoPool), "FR",
|
||||
"same title but different source resolves to source+title initials");
|
||||
assert.equal(ctx.tileCode({ id: "x", title: "Slice", source: "OrcaSlicer" }, [{ id: "x", title: "Slice", source: "OrcaSlicer" }]),
|
||||
"S", "a unique title resolves to the bare title initial");
|
||||
|
||||
console.log("ok");
|
||||
|
||||
@@ -31,6 +31,19 @@
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
std::vector<std::string> cap_favourites(const std::vector<std::string>& ids, size_t limit)
|
||||
{
|
||||
std::vector<std::string> out;
|
||||
out.reserve(std::min(ids.size(), limit));
|
||||
for (const auto& id : ids) {
|
||||
if (out.size() >= limit)
|
||||
break;
|
||||
if (std::find(out.begin(), out.end(), id) == out.end())
|
||||
out.push_back(id);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kConfigSection = "speed_dial";
|
||||
@@ -468,18 +481,8 @@ bool ActionRegistry::set_favourite(const std::string& id, bool on)
|
||||
std::vector<std::string> ActionRegistry::favourite_ids() const
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
// Enforce the cap + dedupe on read so the persisted order can never grow past kFavLimit,
|
||||
// even from an older config. The pinned order is intentionally preserved (slice, not sort).
|
||||
std::vector<std::string> favs = read_string_array("favourite_actions");
|
||||
std::vector<std::string> out;
|
||||
out.reserve(std::min(favs.size(), kFavLimit));
|
||||
for (const auto& id : favs) {
|
||||
if (out.size() >= kFavLimit)
|
||||
break;
|
||||
if (std::find(out.begin(), out.end(), id) == out.end())
|
||||
out.push_back(id);
|
||||
}
|
||||
return out;
|
||||
// Enforce the cap + dedupe on read so the persisted order can never grow past kFavLimit, even from an older config.
|
||||
return cap_favourites(read_string_array("favourite_actions"), kFavLimit);
|
||||
}
|
||||
|
||||
void ActionRegistry::reorder_favourites(const std::vector<std::string>& ids)
|
||||
@@ -496,8 +499,7 @@ void ActionRegistry::reorder_favourites(const std::vector<std::string>& ids)
|
||||
if (std::find(next.begin(), next.end(), id) == next.end())
|
||||
next.push_back(id);
|
||||
// never write the bar back larger than the quick-launch slots
|
||||
if (next.size() > kFavLimit)
|
||||
next.resize(kFavLimit);
|
||||
next = cap_favourites(next, kFavLimit);
|
||||
write_section("favourite_actions", nlohmann::json(next));
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,10 @@ private:
|
||||
std::string m_source_name; // display name of the action's source
|
||||
};
|
||||
|
||||
// Cap + dedupe a persisted favourite-id list, preserving first-occurrence order. A stale or
|
||||
// hand-edited config must never grow the quick-launch bar past `limit`, and a duplicated id must collapse to its first pin.
|
||||
std::vector<std::string> cap_favourites(const std::vector<std::string>& ids, size_t limit);
|
||||
|
||||
// Self-contained sink and single owner of runnable actions for the app session.
|
||||
//
|
||||
// Workflow:
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "slic3r/GUI/ActionRegistry.hpp"
|
||||
#include "slic3r/GUI/NativeCommands.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
using Slic3r::GUI::AppAction;
|
||||
using Slic3r::GUI::AppActionRunResult;
|
||||
@@ -81,3 +84,31 @@ TEST_CASE("Command actions are keyed by catalog key, not display title", "[speed
|
||||
CHECK(AppAction::compose_id("orca_command", "save_project", "orca") !=
|
||||
AppAction::compose_id("orca_command", "load_project", "orca"));
|
||||
}
|
||||
|
||||
// The quick-launch cap must stay 10 to match the numbered Alt/Option+1..9,0 keys. The web palette
|
||||
// mirrors it as K_FAV_LIMIT (asserted in speeddial.test.js); the C++ side pins it here.
|
||||
static_assert(Slic3r::GUI::ActionRegistry::kFavLimit == 10, "kFavLimit must stay 10");
|
||||
|
||||
TEST_CASE("Favourite lists are capped and deduped preserving order", "[speeddial][actions]")
|
||||
{
|
||||
using Slic3r::GUI::cap_favourites;
|
||||
|
||||
CHECK(cap_favourites({}, 10) == std::vector<std::string>{});
|
||||
CHECK(cap_favourites({"a", "b", "a"}, 10) == std::vector<std::string>{"a", "b"});
|
||||
CHECK(cap_favourites({"c", "a", "b", "c"}, 3) == std::vector<std::string>{"c", "a", "b"});
|
||||
CHECK(cap_favourites({"a", "b"}, 0) == std::vector<std::string>{});
|
||||
}
|
||||
|
||||
TEST_CASE("Native command catalog has unique keys and present titles", "[speeddial][actions]")
|
||||
{
|
||||
const std::vector<Slic3r::GUI::NativeCommand>& commands = Slic3r::GUI::NativeCommands::catalog();
|
||||
CHECK_FALSE(commands.empty());
|
||||
|
||||
std::set<std::string> seen;
|
||||
for (const auto& c : commands) {
|
||||
CHECK_FALSE(c.key.empty());
|
||||
CHECK_FALSE(c.title.empty());
|
||||
// A duplicated key would silently shadow the earlier command in the palette.
|
||||
CHECK(seen.insert(c.key).second);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user