// SPDX-License-Identifier: LGPL-2.1-or-later /**************************************************************************** * * * Copyright (c) 2025 Kacper Donat * * * * This file is part of FreeCAD. * * * * FreeCAD is free software: you can redistribute it and/or modify it * * under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 2.1 of the * * License, or (at your option) any later version. * * * * FreeCAD is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with FreeCAD. If not, see * * . * * * ***************************************************************************/ #include #include #include #include using namespace Gui::StyleParameters; class ParameterManagerTest: public ::testing::Test { protected: void SetUp() override { // Create test sources auto source1 = std::make_unique( std::list { {"BaseSize", "8px"}, {"PrimaryColor", "#ff0000"}, {"SecondaryColor", "#00ff00"}, }, ParameterSource::Metadata {"Source 1"} ); auto source2 = std::make_unique( std::list { {"BaseSize", "16px"}, // Override from source1 {"Margin", "@BaseSize * 2"}, {"Padding", "@BaseSize / 2"}, }, ParameterSource::Metadata {"Source 2"} ); manager.addSource(source1.get()); manager.addSource(source2.get()); sources.push_back(std::move(source1)); sources.push_back(std::move(source2)); } Gui::StyleParameters::ParameterManager manager; std::vector> sources; }; // Test basic parameter resolution TEST_F(ParameterManagerTest, BasicParameterResolution) { { auto result = manager.resolve("BaseSize"); EXPECT_TRUE(result.has_value()); EXPECT_TRUE(std::holds_alternative(*result)); auto length = std::get(*result); EXPECT_DOUBLE_EQ(length.value, 16.0); // Should get value from source2 (later source) EXPECT_EQ(length.unit, "px"); } { auto result = manager.resolve("PrimaryColor"); EXPECT_TRUE(result.has_value()); EXPECT_TRUE(std::holds_alternative(*result)); auto color = std::get(*result); EXPECT_EQ(color.r, 1); EXPECT_EQ(color.g, 0); EXPECT_EQ(color.b, 0); } { auto result = manager.resolve("SecondaryColor"); EXPECT_TRUE(result.has_value()); EXPECT_TRUE(std::holds_alternative(*result)); auto color = std::get(*result); EXPECT_EQ(color.r, 0); EXPECT_EQ(color.g, 1); EXPECT_EQ(color.b, 0); } } // Test parameter references TEST_F(ParameterManagerTest, ParameterReferences) { { auto result = manager.resolve("Margin"); EXPECT_TRUE(std::holds_alternative(*result)); auto length = std::get(*result); EXPECT_DOUBLE_EQ(length.value, 32.0); // @BaseSize * 2 = 16 * 2 = 32 EXPECT_EQ(length.unit, "px"); } { auto result = manager.resolve("Padding"); EXPECT_TRUE(std::holds_alternative(*result)); auto length = std::get(*result); EXPECT_DOUBLE_EQ(length.value, 8.0); // @BaseSize / 2 = 16 / 2 = 8 EXPECT_EQ(length.unit, "px"); } } // Test caching TEST_F(ParameterManagerTest, Caching) { // First resolution should cache the result auto result1 = manager.resolve("BaseSize"); EXPECT_TRUE(std::holds_alternative(*result1)); // Second resolution should use cached value auto result2 = manager.resolve("BaseSize"); EXPECT_TRUE(std::holds_alternative(*result2)); // Results should be identical auto length1 = std::get(*result1); auto length2 = std::get(*result2); EXPECT_DOUBLE_EQ(length1.value, length2.value); EXPECT_EQ(length1.unit, length2.unit); } // Test cache invalidation TEST_F(ParameterManagerTest, CacheInvalidation) { // Initial resolution auto result1 = manager.resolve("BaseSize"); EXPECT_TRUE(std::holds_alternative(*result1)); auto length1 = std::get(*result1); EXPECT_DOUBLE_EQ(length1.value, 16.0); // Reload should clear cache manager.reload(); // Resolution after reload should work the same auto result2 = manager.resolve("BaseSize"); EXPECT_TRUE(std::holds_alternative(*result2)); auto length2 = std::get(*result2); EXPECT_DOUBLE_EQ(length2.value, 16.0); EXPECT_EQ(length1.unit, length2.unit); } // Test source priority TEST_F(ParameterManagerTest, SourcePriority) { // Create a third source with higher priority auto source3 = std::make_unique( std::list { {"BaseSize", "24px"}, // Should override both previous sources }, ParameterSource::Metadata {"Source 3"} ); manager.addSource(source3.get()); sources.push_back(std::move(source3)); // Should get value from the latest source (highest priority) auto result = manager.resolve("BaseSize"); EXPECT_TRUE(std::holds_alternative(*result)); auto length = std::get(*result); EXPECT_DOUBLE_EQ(length.value, 24.0); EXPECT_EQ(length.unit, "px"); } // Test parameter listing TEST_F(ParameterManagerTest, ParameterListing) { auto params = manager.parameters(); // Should contain all parameters from all sources std::set paramNames; for (const auto& param : params) { paramNames.insert(param.name); } EXPECT_TRUE(paramNames.contains("BaseSize")); EXPECT_TRUE(paramNames.contains("PrimaryColor")); EXPECT_TRUE(paramNames.contains("SecondaryColor")); EXPECT_TRUE(paramNames.contains("Margin")); EXPECT_TRUE(paramNames.contains("Padding")); // Should not contain duplicates (BaseSize should appear only once) EXPECT_EQ(paramNames.count("BaseSize"), 1); } // Test expression retrieval TEST_F(ParameterManagerTest, ExpressionRetrieval) { { auto expr = manager.expression("BaseSize"); EXPECT_TRUE(expr.has_value()); EXPECT_EQ(*expr, "16px"); } { auto expr = manager.expression("Margin"); EXPECT_TRUE(expr.has_value()); EXPECT_EQ(*expr, "@BaseSize * 2"); } { auto expr = manager.expression("NonExistent"); EXPECT_FALSE(expr.has_value()); } } // Test parameter retrieval TEST_F(ParameterManagerTest, ParameterRetrieval) { { auto param = manager.parameter("BaseSize"); EXPECT_TRUE(param.has_value()); EXPECT_EQ(param->name, "BaseSize"); EXPECT_EQ(param->value, "16px"); } { auto param = manager.parameter("NonExistent"); EXPECT_FALSE(param.has_value()); } } // Test source management TEST_F(ParameterManagerTest, SourceManagement) { auto sources = manager.sources(); EXPECT_EQ(sources.size(), 2); // We added 2 sources in SetUp // Test that we can access the sources for (auto source : sources) { EXPECT_NE(source, nullptr); auto params = source->all(); EXPECT_FALSE(params.empty()); } } // Test circular reference detection TEST_F(ParameterManagerTest, CircularReferenceDetection) { // Create a source with circular reference auto circularSource = std::make_unique( std::list { {"A", "@B"}, {"B", "@A"}, }, ParameterSource::Metadata {"Circular Source"} ); manager.addSource(circularSource.get()); sources.push_back(std::move(circularSource)); // Should handle circular reference gracefully auto result = manager.resolve("A"); // Should return the expression string as fallback EXPECT_TRUE(std::holds_alternative(*result)); } // Test complex expressions TEST_F(ParameterManagerTest, ComplexExpressions) { // Create a source with complex expressions auto complexSource = std::make_unique( std::list { {"ComplexMargin", "(@BaseSize + 4px) * 2"}, {"ComplexPadding", "(@BaseSize - 2px) / 2"}, {"ColorWithFunction", "lighten(@PrimaryColor, 20)"}, }, ParameterSource::Metadata {"Complex Source"} ); manager.addSource(complexSource.get()); sources.push_back(std::move(complexSource)); { auto result = manager.resolve("ComplexMargin"); EXPECT_TRUE(std::holds_alternative(*result)); auto length = std::get(*result); EXPECT_DOUBLE_EQ(length.value, 40.0); // (16 + 4) * 2 = 20 * 2 = 40 EXPECT_EQ(length.unit, "px"); } { auto result = manager.resolve("ComplexPadding"); EXPECT_TRUE(std::holds_alternative(*result)); auto length = std::get(*result); EXPECT_DOUBLE_EQ(length.value, 7.0); // (16 - 2) / 2 = 14 / 2 = 7 EXPECT_EQ(length.unit, "px"); } { auto result = manager.resolve("ColorWithFunction"); EXPECT_TRUE(std::holds_alternative(*result)); auto color = std::get(*result).asValue(); // Should be lighter than the original red EXPECT_GT(color.lightness(), QColor(0xff0000).lightness()); } } // Test error handling TEST_F(ParameterManagerTest, ErrorHandling) { // Test non-existent parameter auto result = manager.resolve("NonExistent"); EXPECT_FALSE(result.has_value()); // Test invalid expression auto invalidSource = std::make_unique( std::list { {"Invalid", "invalid expression that will fail"}, }, ParameterSource::Metadata {"Invalid Source"} ); manager.addSource(invalidSource.get()); sources.push_back(std::move(invalidSource)); // Should handle invalid expression gracefully auto invalidResult = manager.resolve("Invalid"); // Should return the expression string as fallback EXPECT_TRUE(invalidResult.has_value()); EXPECT_TRUE(std::holds_alternative(*invalidResult)); } DEFINE_STYLE_PARAMETER(BaseSize, Numeric(8, "px")); TEST_F(ParameterManagerTest, ResolveParameterDefinition) { auto result = manager.resolve(BaseSize); EXPECT_DOUBLE_EQ(result.value, 16); EXPECT_EQ(result.unit, "px"); } DEFINE_STYLE_PARAMETER(MarginSize, Numeric(16, "px")); TEST_F(ParameterManagerTest, ResolveParameterDefinitionDefault) { auto result = manager.resolve(MarginSize); EXPECT_DOUBLE_EQ(result.value, 16); EXPECT_EQ(result.unit, "px"); }