Gui: Add tests for StyleParameters tuple support

Test cases cover unnamed, named, and mixed tuples, single named
elements, nested tuples, expressions and mixed types in elements,
toString roundtrip, Value::holds/get accessors, Tuple::at/find/size,
error handling, and backward compatibility of grouped expressions.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Kacper Donat
2026-02-09 00:33:00 +01:00
co-authored by Claude Opus 4.6
parent 884baf2cb8
commit 7c813a6f5a
@@ -604,6 +604,268 @@ TEST_F(ParserTest, ParseOperatorPrecedence)
}
}
// Test unnamed tuple
TEST_F(ParserTest, ParseUnnamedTuple)
{
Parser parser("(10, 20, 30)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(result.holds<Tuple>());
const auto& tuple = result.get<Tuple>();
EXPECT_EQ(tuple.size(), 3);
EXPECT_DOUBLE_EQ(std::get<Numeric>(tuple.at(0)).value, 10.0);
EXPECT_DOUBLE_EQ(std::get<Numeric>(tuple.at(1)).value, 20.0);
EXPECT_DOUBLE_EQ(std::get<Numeric>(tuple.at(2)).value, 30.0);
}
// Test named tuple
TEST_F(ParserTest, ParseNamedTuple)
{
Parser parser("(x: 10, y: 20)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(result.holds<Tuple>());
const auto& tuple = result.get<Tuple>();
EXPECT_EQ(tuple.size(), 2);
auto* x = tuple.find("x");
ASSERT_NE(x, nullptr);
EXPECT_DOUBLE_EQ(std::get<Numeric>(*x).value, 10.0);
auto* y = tuple.find("y");
ASSERT_NE(y, nullptr);
EXPECT_DOUBLE_EQ(std::get<Numeric>(*y).value, 20.0);
}
// Test mixed named/unnamed tuple
TEST_F(ParserTest, ParseMixedTuple)
{
Parser parser("(x: 10, 20)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(result.holds<Tuple>());
const auto& tuple = result.get<Tuple>();
EXPECT_EQ(tuple.size(), 2);
auto* x = tuple.find("x");
ASSERT_NE(x, nullptr);
EXPECT_DOUBLE_EQ(std::get<Numeric>(*x).value, 10.0);
EXPECT_DOUBLE_EQ(std::get<Numeric>(tuple.at(1)).value, 20.0);
}
// Test single named element is a tuple, not a grouped expression
TEST_F(ParserTest, ParseSingleNamedElementIsTuple)
{
Parser parser("(x: 10)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(result.holds<Tuple>());
const auto& tuple = result.get<Tuple>();
EXPECT_EQ(tuple.size(), 1);
auto* x = tuple.find("x");
ASSERT_NE(x, nullptr);
EXPECT_DOUBLE_EQ(std::get<Numeric>(*x).value, 10.0);
}
// Test expressions inside tuple elements
TEST_F(ParserTest, ParseTupleWithExpressions)
{
Parser parser("(x: 10 + 5, y: @TestParam)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(result.holds<Tuple>());
const auto& tuple = result.get<Tuple>();
EXPECT_EQ(tuple.size(), 2);
auto* x = tuple.find("x");
ASSERT_NE(x, nullptr);
EXPECT_DOUBLE_EQ(std::get<Numeric>(*x).value, 15.0);
auto* y = tuple.find("y");
ASSERT_NE(y, nullptr);
EXPECT_DOUBLE_EQ(std::get<Numeric>(*y).value, 10.0);
EXPECT_EQ(std::get<Numeric>(*y).unit, "px");
}
// Test mixed types in tuple
TEST_F(ParserTest, ParseTupleWithMixedTypes)
{
Parser parser("(color: #ff0000, opacity: 0.5)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(result.holds<Tuple>());
const auto& tuple = result.get<Tuple>();
EXPECT_EQ(tuple.size(), 2);
auto* color = tuple.find("color");
ASSERT_NE(color, nullptr);
EXPECT_TRUE(std::holds_alternative<Base::Color>(*color));
auto c = std::get<Base::Color>(*color);
EXPECT_EQ(c.r, 1);
EXPECT_EQ(c.g, 0);
EXPECT_EQ(c.b, 0);
auto* opacity = tuple.find("opacity");
ASSERT_NE(opacity, nullptr);
EXPECT_DOUBLE_EQ(std::get<Numeric>(*opacity).value, 0.5);
}
// Test nested tuples
TEST_F(ParserTest, ParseNestedTuples)
{
Parser parser("((1, 2), (3, 4))");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(result.holds<Tuple>());
const auto& outer = result.get<Tuple>();
EXPECT_EQ(outer.size(), 2);
EXPECT_TRUE(outer.at(0).holds<Tuple>());
const auto& inner1 = outer.at(0).get<Tuple>();
EXPECT_EQ(inner1.size(), 2);
EXPECT_DOUBLE_EQ(std::get<Numeric>(inner1.at(0)).value, 1.0);
EXPECT_DOUBLE_EQ(std::get<Numeric>(inner1.at(1)).value, 2.0);
EXPECT_TRUE(outer.at(1).holds<Tuple>());
const auto& inner2 = outer.at(1).get<Tuple>();
EXPECT_EQ(inner2.size(), 2);
EXPECT_DOUBLE_EQ(std::get<Numeric>(inner2.at(0)).value, 3.0);
EXPECT_DOUBLE_EQ(std::get<Numeric>(inner2.at(1)).value, 4.0);
}
// Test complex expressions in tuple elements
TEST_F(ParserTest, ParseTupleWithComplexExpressions)
{
Parser parser("(x: lighten(#ff0000, 20), y: 10px * 2)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(result.holds<Tuple>());
const auto& tuple = result.get<Tuple>();
EXPECT_EQ(tuple.size(), 2);
auto* x = tuple.find("x");
ASSERT_NE(x, nullptr);
EXPECT_TRUE(std::holds_alternative<Base::Color>(*x));
auto* y = tuple.find("y");
ASSERT_NE(y, nullptr);
EXPECT_DOUBLE_EQ(std::get<Numeric>(*y).value, 20.0);
EXPECT_EQ(std::get<Numeric>(*y).unit, "px");
}
// Test tuple toString roundtrip
TEST_F(ParserTest, TupleToString)
{
{
Parser parser("(x: 10, y: 20)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
auto str = result.toString();
EXPECT_EQ(str, "(x: 10, y: 20)");
}
{
Parser parser("(10, 20, 30)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
auto str = result.toString();
EXPECT_EQ(str, "(10, 20, 30)");
}
{
Parser parser("(x: 10, 20)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
auto str = result.toString();
EXPECT_EQ(str, "(x: 10, 20)");
}
}
// Test Value::holds and Value::get
TEST_F(ParserTest, ValueHoldsAndGet)
{
Value numericValue = Numeric {42, "px"};
EXPECT_TRUE(numericValue.holds<Numeric>());
EXPECT_FALSE(numericValue.holds<Tuple>());
EXPECT_DOUBLE_EQ(numericValue.get<Numeric>().value, 42.0);
Value colorValue = Base::Color(1.0, 0.0, 0.0);
EXPECT_TRUE(colorValue.holds<Base::Color>());
EXPECT_FALSE(colorValue.holds<Tuple>());
Value stringValue = std::string("hello");
EXPECT_TRUE(stringValue.holds<std::string>());
EXPECT_FALSE(stringValue.holds<Tuple>());
Parser parser("(1, 2)");
auto expr = parser.parse();
auto tupleValue = expr->evaluate({&manager, {}});
EXPECT_TRUE(tupleValue.holds<Tuple>());
EXPECT_FALSE(tupleValue.holds<Numeric>());
EXPECT_EQ(tupleValue.get<Tuple>().size(), 2);
}
// Test Tuple::at out of bounds
TEST_F(ParserTest, TupleAtOutOfBounds)
{
Parser parser("(10, 20)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
const auto& tuple = result.get<Tuple>();
EXPECT_NO_THROW(tuple.at(0));
EXPECT_NO_THROW(tuple.at(1));
EXPECT_THROW(tuple.at(2), Base::RuntimeError);
}
// Test Tuple::find nonexistent name
TEST_F(ParserTest, TupleFindNonexistent)
{
Parser parser("(x: 10, y: 20)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
const auto& tuple = result.get<Tuple>();
EXPECT_NE(tuple.find("x"), nullptr);
EXPECT_NE(tuple.find("y"), nullptr);
EXPECT_EQ(tuple.find("z"), nullptr);
}
// Test error: missing ')' in tuple
TEST_F(ParserTest, TupleMissingClosingParen)
{
EXPECT_THROW(
{
Parser parser("(x: 10, y: 20");
parser.parse();
},
Base::ParserError
);
}
// Test that grouped expressions still work (backward compatibility)
TEST_F(ParserTest, GroupedExpressionStillWorks)
{
{
Parser parser("(10 + 5) * 2");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<Numeric>(result));
EXPECT_DOUBLE_EQ(std::get<Numeric>(result).value, 30.0);
}
{
Parser parser("(42)");
auto expr = parser.parse();
auto result = expr->evaluate({&manager, {}});
EXPECT_TRUE(std::holds_alternative<Numeric>(result));
EXPECT_DOUBLE_EQ(std::get<Numeric>(result).value, 42.0);
}
}
// Test nested parentheses
TEST_F(ParserTest, ParseNestedParentheses)
{