test(gui): add unit tests for OriginManager and LocalFileOrigin
Some checks failed
Build and Test / build (push) Has been cancelled
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:
@@ -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
|
||||||
|
|||||||
382
tests/src/Gui/OriginManager.cpp
Normal file
382
tests/src/Gui/OriginManager.cpp
Normal 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user