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
|
||||
Assistant.cpp
|
||||
Camera.cpp
|
||||
OriginManager.cpp
|
||||
StyleParameters/StyleParametersApplicationTest.cpp
|
||||
StyleParameters/ParserTest.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