test(gui): add unit tests for OriginManager and LocalFileOrigin
Some checks failed
Build and Test / build (push) Has been cancelled

Add 20 GTest cases covering the Kindred Origin system:

- LocalFileOriginTest: identity methods and capability flags
- OriginManagerTest: registration lifecycle, current origin
  selection, signal emissions, and fallback behavior
- OriginManagerDocTest: document ownership via SiloItemId
  property detection, origin association, and null safety

Uses MockOrigin stub for testing OriginManager without real
PLM backends.
This commit is contained in:
forbes
2026-02-07 20:56:08 -06:00
parent 99bc7629e7
commit 62906b0269
2 changed files with 383 additions and 0 deletions

View File

@@ -4,6 +4,7 @@
add_executable(Gui_tests_run add_executable(Gui_tests_run
Assistant.cpp Assistant.cpp
Camera.cpp Camera.cpp
OriginManager.cpp
StyleParameters/StyleParametersApplicationTest.cpp StyleParameters/StyleParametersApplicationTest.cpp
StyleParameters/ParserTest.cpp StyleParameters/ParserTest.cpp
StyleParameters/ParameterManagerTest.cpp StyleParameters/ParameterManagerTest.cpp

View File

@@ -0,0 +1,382 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#include <gtest/gtest.h>
#include <string>
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/PropertyStandard.h>
#include <Gui/FileOrigin.h>
#include <Gui/OriginManager.h>
#include <src/App/InitApplication.h>
namespace
{
/**
* Minimal FileOrigin implementation for testing OriginManager.
* All document operations are stubs -- we only need identity,
* capability, and ownership methods to test the manager.
*/
class MockOrigin : public Gui::FileOrigin
{
public:
explicit MockOrigin(std::string originId,
Gui::OriginType originType = Gui::OriginType::PLM,
bool ownsAll = false)
: _id(std::move(originId))
, _type(originType)
, _ownsAll(ownsAll)
{}
// Identity
std::string id() const override { return _id; }
std::string name() const override { return _id + " Name"; }
std::string nickname() const override { return _id; }
QIcon icon() const override { return {}; }
Gui::OriginType type() const override { return _type; }
// Characteristics
bool tracksExternally() const override { return _type == Gui::OriginType::PLM; }
bool requiresAuthentication() const override { return _type == Gui::OriginType::PLM; }
// Capabilities
bool supportsRevisions() const override { return _supportsRevisions; }
bool supportsBOM() const override { return _supportsBOM; }
bool supportsPartNumbers() const override { return _supportsPartNumbers; }
// Document identity
std::string documentIdentity(App::Document* /*doc*/) const override { return {}; }
std::string documentDisplayId(App::Document* /*doc*/) const override { return {}; }
bool ownsDocument(App::Document* doc) const override
{
if (!doc) {
return false;
}
return _ownsAll;
}
// Document operations (stubs)
App::Document* newDocument(const std::string& /*name*/) override { return nullptr; }
App::Document* openDocument(const std::string& /*identity*/) override { return nullptr; }
App::Document* openDocumentInteractive() override { return nullptr; }
bool saveDocument(App::Document* /*doc*/) override { return false; }
bool saveDocumentAs(App::Document* /*doc*/, const std::string& /*id*/) override
{
return false;
}
bool saveDocumentAsInteractive(App::Document* /*doc*/) override { return false; }
// Test controls
bool _supportsRevisions = true;
bool _supportsBOM = true;
bool _supportsPartNumbers = true;
private:
std::string _id;
Gui::OriginType _type;
bool _ownsAll;
};
} // namespace
// =========================================================================
// LocalFileOrigin identity tests
// =========================================================================
class LocalFileOriginTest : public ::testing::Test
{
protected:
static void SetUpTestSuite()
{
tests::initApplication();
}
void SetUp() override
{
_origin = std::make_unique<Gui::LocalFileOrigin>();
}
Gui::LocalFileOrigin* origin() { return _origin.get(); }
private:
std::unique_ptr<Gui::LocalFileOrigin> _origin;
};
TEST_F(LocalFileOriginTest, LocalOriginId)
{
EXPECT_EQ(origin()->id(), "local");
}
TEST_F(LocalFileOriginTest, LocalOriginName)
{
EXPECT_EQ(origin()->name(), "Local Files");
}
TEST_F(LocalFileOriginTest, LocalOriginNickname)
{
EXPECT_EQ(origin()->nickname(), "Local");
}
TEST_F(LocalFileOriginTest, LocalOriginType)
{
EXPECT_EQ(origin()->type(), Gui::OriginType::Local);
}
TEST_F(LocalFileOriginTest, LocalOriginCapabilities)
{
EXPECT_FALSE(origin()->tracksExternally());
EXPECT_FALSE(origin()->requiresAuthentication());
EXPECT_FALSE(origin()->supportsRevisions());
EXPECT_FALSE(origin()->supportsBOM());
EXPECT_FALSE(origin()->supportsPartNumbers());
EXPECT_FALSE(origin()->supportsAssemblies());
}
// =========================================================================
// OriginManager tests
// =========================================================================
class OriginManagerTest : public ::testing::Test
{
protected:
static void SetUpTestSuite()
{
tests::initApplication();
}
void SetUp() override
{
// Ensure clean singleton state for each test
Gui::OriginManager::destruct();
_mgr = Gui::OriginManager::instance();
}
void TearDown() override
{
Gui::OriginManager::destruct();
}
Gui::OriginManager* mgr() { return _mgr; }
private:
Gui::OriginManager* _mgr = nullptr;
};
// --- Registration ---
TEST_F(OriginManagerTest, LocalOriginAlwaysPresent)
{
auto* local = mgr()->getOrigin("local");
ASSERT_NE(local, nullptr);
EXPECT_EQ(local->id(), "local");
}
TEST_F(OriginManagerTest, RegisterCustomOrigin)
{
auto* raw = new MockOrigin("test-plm");
EXPECT_TRUE(mgr()->registerOrigin(raw));
EXPECT_EQ(mgr()->getOrigin("test-plm"), raw);
}
TEST_F(OriginManagerTest, RejectDuplicateId)
{
mgr()->registerOrigin(new MockOrigin("dup"));
// Second registration with same ID should fail
EXPECT_FALSE(mgr()->registerOrigin(new MockOrigin("dup")));
}
TEST_F(OriginManagerTest, RejectNullOrigin)
{
EXPECT_FALSE(mgr()->registerOrigin(nullptr));
}
TEST_F(OriginManagerTest, OriginIdsIncludesAll)
{
mgr()->registerOrigin(new MockOrigin("alpha"));
mgr()->registerOrigin(new MockOrigin("beta"));
auto ids = mgr()->originIds();
EXPECT_EQ(ids.size(), 3u); // local + alpha + beta
auto has = [&](const std::string& id) {
return std::find(ids.begin(), ids.end(), id) != ids.end();
};
EXPECT_TRUE(has("local"));
EXPECT_TRUE(has("alpha"));
EXPECT_TRUE(has("beta"));
}
// --- Unregistration ---
TEST_F(OriginManagerTest, UnregisterCustomOrigin)
{
mgr()->registerOrigin(new MockOrigin("removable"));
EXPECT_TRUE(mgr()->unregisterOrigin("removable"));
EXPECT_EQ(mgr()->getOrigin("removable"), nullptr);
}
TEST_F(OriginManagerTest, CannotUnregisterLocal)
{
EXPECT_FALSE(mgr()->unregisterOrigin("local"));
EXPECT_NE(mgr()->getOrigin("local"), nullptr);
}
TEST_F(OriginManagerTest, UnregisterCurrentSwitchesToLocal)
{
mgr()->registerOrigin(new MockOrigin("ephemeral"));
mgr()->setCurrentOrigin("ephemeral");
EXPECT_EQ(mgr()->currentOriginId(), "ephemeral");
mgr()->unregisterOrigin("ephemeral");
EXPECT_EQ(mgr()->currentOriginId(), "local");
}
// --- Current origin selection ---
TEST_F(OriginManagerTest, DefaultCurrentIsLocal)
{
EXPECT_EQ(mgr()->currentOriginId(), "local");
EXPECT_NE(mgr()->currentOrigin(), nullptr);
}
TEST_F(OriginManagerTest, SetCurrentOrigin)
{
mgr()->registerOrigin(new MockOrigin("plm1"));
std::string notified;
auto conn = mgr()->signalCurrentOriginChanged.connect([&](const std::string& id) {
notified = id;
});
EXPECT_TRUE(mgr()->setCurrentOrigin("plm1"));
EXPECT_EQ(mgr()->currentOriginId(), "plm1");
EXPECT_EQ(notified, "plm1");
}
TEST_F(OriginManagerTest, SetCurrentRejectsUnknown)
{
EXPECT_FALSE(mgr()->setCurrentOrigin("nonexistent"));
EXPECT_EQ(mgr()->currentOriginId(), "local");
}
TEST_F(OriginManagerTest, SetCurrentSameIdNoSignal)
{
int signalCount = 0;
auto conn = mgr()->signalCurrentOriginChanged.connect([&](const std::string&) {
signalCount++;
});
mgr()->setCurrentOrigin("local"); // already current
EXPECT_EQ(signalCount, 0);
}
// --- Document ownership ---
class OriginManagerDocTest : public ::testing::Test
{
protected:
static void SetUpTestSuite()
{
tests::initApplication();
}
void SetUp() override
{
Gui::OriginManager::destruct();
_mgr = Gui::OriginManager::instance();
_docName = App::GetApplication().getUniqueDocumentName("test");
_doc = App::GetApplication().newDocument(_docName.c_str(), "testUser");
}
void TearDown() override
{
App::GetApplication().closeDocument(_docName.c_str());
Gui::OriginManager::destruct();
}
Gui::OriginManager* mgr() { return _mgr; }
App::Document* doc() { return _doc; }
private:
Gui::OriginManager* _mgr = nullptr;
std::string _docName;
App::Document* _doc = nullptr;
};
TEST_F(OriginManagerDocTest, LocalOwnsPlainDocument)
{
// A document with no SiloItemId property should be owned by local
auto* local = mgr()->getOrigin("local");
ASSERT_NE(local, nullptr);
EXPECT_TRUE(local->ownsDocument(doc()));
}
TEST_F(OriginManagerDocTest, LocalDisownsTrackedDocument)
{
// Add an object with a SiloItemId property -- local should reject ownership
auto* obj = doc()->addObject("App::FeaturePython", "TrackedPart");
ASSERT_NE(obj, nullptr);
obj->addDynamicProperty("App::PropertyString", "SiloItemId");
auto* prop = dynamic_cast<App::PropertyString*>(obj->getPropertyByName("SiloItemId"));
ASSERT_NE(prop, nullptr);
prop->setValue("some-uuid");
auto* local = mgr()->getOrigin("local");
EXPECT_FALSE(local->ownsDocument(doc()));
}
TEST_F(OriginManagerDocTest, FindOwningOriginPrefersNonLocal)
{
// Register a PLM mock that claims ownership of everything
auto* plm = new MockOrigin("test-plm", Gui::OriginType::PLM, /*ownsAll=*/true);
mgr()->registerOrigin(plm);
auto* owner = mgr()->findOwningOrigin(doc());
ASSERT_NE(owner, nullptr);
EXPECT_EQ(owner->id(), "test-plm");
}
TEST_F(OriginManagerDocTest, FindOwningOriginFallsBackToLocal)
{
// No PLM origins registered -- should fall back to local
auto* owner = mgr()->findOwningOrigin(doc());
ASSERT_NE(owner, nullptr);
EXPECT_EQ(owner->id(), "local");
}
TEST_F(OriginManagerDocTest, OriginForNewDocumentReturnsCurrent)
{
mgr()->registerOrigin(new MockOrigin("plm2"));
mgr()->setCurrentOrigin("plm2");
auto* origin = mgr()->originForNewDocument();
ASSERT_NE(origin, nullptr);
EXPECT_EQ(origin->id(), "plm2");
}
TEST_F(OriginManagerDocTest, SetAndClearDocumentOrigin)
{
auto* local = mgr()->getOrigin("local");
mgr()->setDocumentOrigin(doc(), local);
EXPECT_EQ(mgr()->originForDocument(doc()), local);
mgr()->clearDocumentOrigin(doc());
// After clearing, originForDocument falls back to ownership detection
auto* resolved = mgr()->originForDocument(doc());
ASSERT_NE(resolved, nullptr);
EXPECT_EQ(resolved->id(), "local");
}
TEST_F(OriginManagerDocTest, NullDocumentHandling)
{
EXPECT_EQ(mgr()->findOwningOrigin(nullptr), nullptr);
EXPECT_EQ(mgr()->originForDocument(nullptr), nullptr);
}