Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split value parsing tests #48770

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

namespace facebook::react {

class CSSSyntaxParser;

/**
* Describes context for a CSS function component value.
*/
Expand All @@ -41,9 +43,10 @@ struct CSSSimpleBlock {
* of the function block.
*/
template <typename T, typename ReturnT>
concept CSSFunctionVisitor = requires(T visitor, CSSFunctionBlock func) {
{ visitor(func) } -> std::convertible_to<ReturnT>;
};
concept CSSFunctionVisitor =
requires(T visitor, CSSFunctionBlock func, CSSSyntaxParser& blockParser) {
{ visitor(func, blockParser) } -> std::convertible_to<ReturnT>;
};

/**
* A CSSPreservedTokenVisitor is called after parsing a preserved token
Expand All @@ -61,9 +64,10 @@ concept CSSPreservedTokenVisitor =
* of the block.
*/
template <typename T, typename ReturnT>
concept CSSSimpleBlockVisitor = requires(T visitor, CSSSimpleBlock block) {
{ visitor(block) } -> std::convertible_to<ReturnT>;
};
concept CSSSimpleBlockVisitor =
requires(T visitor, CSSSimpleBlock block, CSSSyntaxParser& blockParser) {
{ visitor(block, blockParser) } -> std::convertible_to<ReturnT>;
};

/**
* Any visitor for a component value.
Expand All @@ -89,6 +93,7 @@ concept CSSUniqueComponentValueVisitors =
enum class CSSComponentValueDelimiter {
Comma,
Whitespace,
CommaOrWhitespace,
None,
};

Expand Down Expand Up @@ -164,6 +169,11 @@ class CSSSyntaxParser {
}

private:
CSSSyntaxParser(CSSSyntaxParser& parser, CSSTokenType terminator)
: tokenizer_{parser.tokenizer_},
currentToken_{parser.currentToken_},
terminator_{terminator} {}

constexpr const CSSToken& peek() const {
return currentToken_;
}
Expand All @@ -174,8 +184,14 @@ class CSSSyntaxParser {
return prevToken;
}

constexpr void advanceToBlockParser(CSSSyntaxParser& blockParser) {
currentToken_ = blockParser.currentToken_;
tokenizer_ = blockParser.tokenizer_;
}

CSSTokenizer tokenizer_;
CSSToken currentToken_;
CSSTokenType terminator_{CSSTokenType::EndOfFile};
};

template <typename ReturnT, CSSComponentValueVisitor<ReturnT>... VisitorsT>
Expand All @@ -197,10 +213,21 @@ struct CSSComponentValueVisitorDispatcher {
case CSSComponentValueDelimiter::Whitespace:
parser.consumeWhitespace();
break;
case CSSComponentValueDelimiter::CommaOrWhitespace:
parser.consumeWhitespace();
if (parser.peek().type() == CSSTokenType::Comma) {
parser.consumeToken();
}
parser.consumeWhitespace();
break;
case CSSComponentValueDelimiter::None:
break;
}

if (parser.peek().type() == parser.terminator_) {
return {};
}

switch (parser.peek().type()) {
case CSSTokenType::Function:
if (auto ret = visitFunction(visitors...)) {
Expand Down Expand Up @@ -235,28 +262,22 @@ struct CSSComponentValueVisitorDispatcher {
return ReturnT{};
}

constexpr ReturnT consumeNextCommaDelimitedValue(
const VisitorsT&... visitors) {
parser.consumeWhitespace();
if (parser.consumeToken().type() != CSSTokenType::Comma) {
return {};
}
parser.consumeWhitespace();
return consumeComponentValue(std::forward<VisitorsT>(visitors)...);
}

constexpr ReturnT consumeNextWhitespaceDelimitedValue(
const VisitorsT&... visitors) {
parser.consumeWhitespace();
return consumeComponentValue(std::forward<VisitorsT>(visitors)...);
}

constexpr std::optional<ReturnT> visitFunction(
const CSSComponentValueVisitor<ReturnT> auto& visitor,
const CSSComponentValueVisitor<ReturnT> auto&... rest) {
if constexpr (CSSFunctionVisitor<decltype(visitor), ReturnT>) {
auto functionValue =
visitor({.name = parser.consumeToken().stringValue()});
auto name = parser.consumeToken().stringValue();

// CSS syntax spec says whitespace is a preserved token, but CSS values
// spec allows whitespace around parens for all function notation, so we
// allow this to let the visitors not need to deal with leading/trailing
// whitespace. https://www.w3.org/TR/css-values-3/#functional-notations
parser.consumeWhitespace();

auto blockParser =
CSSSyntaxParser{parser, CSSTokenType::CloseParen /*terminator*/};
auto functionValue = visitor({name}, blockParser);
parser.advanceToBlockParser(blockParser);
parser.consumeWhitespace();
if (parser.peek().type() == CSSTokenType::CloseParen) {
parser.consumeToken();
Expand All @@ -273,16 +294,16 @@ struct CSSComponentValueVisitorDispatcher {
return {};
}

// Can be one of std::monostate (variant null-type), CSSWideKeyword,
// CSSLength, or CSSPercentage

constexpr std::optional<ReturnT> visitSimpleBlock(
CSSTokenType endToken,
const CSSComponentValueVisitor<ReturnT> auto& visitor,
const CSSComponentValueVisitor<ReturnT> auto&... rest) {
if constexpr (CSSSimpleBlockVisitor<decltype(visitor), ReturnT>) {
auto blockValue =
visitor({.openBracketType = parser.consumeToken().type()});
auto openBracketType = parser.consumeToken().type();
parser.consumeWhitespace();
auto blockParser = CSSSyntaxParser{parser, endToken};
auto blockValue = visitor({openBracketType}, blockParser);
parser.advanceToBlockParser(blockParser);
parser.consumeWhitespace();
if (parser.peek().type() == endToken) {
parser.consumeToken();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ class CSSValueParser {
ReturnT,
CSSDataTypeParser<AllowedTypesT>...>(token);
},
[&](const CSSSimpleBlock& block) {
[&](const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) {
return tryConsumeSimpleBlock<
ReturnT,
CSSDataTypeParser<AllowedTypesT>...>(block);
CSSDataTypeParser<AllowedTypesT>...>(block, blockParser);
},
[&](const CSSFunctionBlock& func) {
[&](const CSSFunctionBlock& func, CSSSyntaxParser& blockParser) {
return tryConsumeFunctionBlock<
ReturnT,
CSSDataTypeParser<AllowedTypesT>...>(func);
CSSDataTypeParser<AllowedTypesT>...>(func, blockParser);
});
}

Expand Down Expand Up @@ -90,41 +90,49 @@ class CSSValueParser {
}

template <typename ReturnT>
constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock& /*token*/) {
constexpr ReturnT tryConsumeSimpleBlock(
const CSSSimpleBlock& /*token*/,
CSSSyntaxParser& /*blockParser*/) {
return {};
}

template <
typename ReturnT,
CSSValidDataTypeParser ParserT,
CSSValidDataTypeParser... RestParserT>
constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock& block) {
constexpr ReturnT tryConsumeSimpleBlock(
const CSSSimpleBlock& block,
CSSSyntaxParser& blockParser) {
if constexpr (CSSSimpleBlockSink<ParserT>) {
if (auto ret = ParserT::consumeSimpleBlock(block, parser_)) {
if (auto ret = ParserT::consumeSimpleBlock(block, blockParser)) {
return *ret;
}
}

return tryConsumeSimpleBlock<ReturnT, RestParserT...>(block);
return tryConsumeSimpleBlock<ReturnT, RestParserT...>(block, blockParser);
}

template <typename ReturnT>
constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock& /*func*/) {
constexpr ReturnT tryConsumeFunctionBlock(
const CSSFunctionBlock& /*func*/,
CSSSyntaxParser& /*blockParser*/) {
return {};
}

template <
typename ReturnT,
CSSValidDataTypeParser ParserT,
CSSValidDataTypeParser... RestParserT>
constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock& func) {
constexpr ReturnT tryConsumeFunctionBlock(
const CSSFunctionBlock& func,
CSSSyntaxParser& blockParser) {
if constexpr (CSSFunctionBlockSink<ParserT>) {
if (auto ret = ParserT::consumeFunctionBlock(func, parser_)) {
if (auto ret = ParserT::consumeFunctionBlock(func, blockParser)) {
return *ret;
}
}

return tryConsumeFunctionBlock<ReturnT, RestParserT...>(func);
return tryConsumeFunctionBlock<ReturnT, RestParserT...>(func, blockParser);
}

CSSSyntaxParser& parser_;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <gtest/gtest.h>
#include <react/renderer/css/CSSAngle.h>
#include <react/renderer/css/CSSValueParser.h>

namespace facebook::react {

TEST(CSSAngle, angle_values) {
auto emptyValue = parseCSSProperty<CSSAngle>("");
EXPECT_TRUE(std::holds_alternative<std::monostate>(emptyValue));

auto degreeValue = parseCSSProperty<CSSAngle>("10deg");
EXPECT_TRUE(std::holds_alternative<CSSAngle>(degreeValue));
EXPECT_EQ(std::get<CSSAngle>(degreeValue).degrees, 10.0f);

auto spongebobCaseValue = parseCSSProperty<CSSAngle>("20dEg");
EXPECT_TRUE(std::holds_alternative<CSSAngle>(spongebobCaseValue));
EXPECT_EQ(std::get<CSSAngle>(spongebobCaseValue).degrees, 20.0f);

auto radianValue = parseCSSProperty<CSSAngle>("10rad");
EXPECT_TRUE(std::holds_alternative<CSSAngle>(radianValue));
ASSERT_NEAR(std::get<CSSAngle>(radianValue).degrees, 572.958f, 0.001f);

auto negativeRadianValue = parseCSSProperty<CSSAngle>("-10rad");
EXPECT_TRUE(std::holds_alternative<CSSAngle>(negativeRadianValue));
ASSERT_NEAR(
std::get<CSSAngle>(negativeRadianValue).degrees, -572.958f, 0.001f);

auto gradianValue = parseCSSProperty<CSSAngle>("10grad");
EXPECT_TRUE(std::holds_alternative<CSSAngle>(gradianValue));
ASSERT_NEAR(std::get<CSSAngle>(gradianValue).degrees, 9.0f, 0.001f);

auto turnValue = parseCSSProperty<CSSAngle>(".25turn");
EXPECT_TRUE(std::holds_alternative<CSSAngle>(turnValue));
EXPECT_EQ(std::get<CSSAngle>(turnValue).degrees, 90.0f);
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <gtest/gtest.h>
#include <react/renderer/css/CSSColor.h>
#include <react/renderer/css/CSSValueParser.h>

namespace facebook::react {

TEST(CSSColor, hex_color_values) {
auto emptyValue = parseCSSProperty<CSSColor>("");
EXPECT_TRUE(std::holds_alternative<std::monostate>(emptyValue));

auto hex3DigitColorValue = parseCSSProperty<CSSColor>("#fff");
EXPECT_TRUE(std::holds_alternative<CSSColor>(hex3DigitColorValue));
EXPECT_EQ(std::get<CSSColor>(hex3DigitColorValue).r, 255);
EXPECT_EQ(std::get<CSSColor>(hex3DigitColorValue).g, 255);
EXPECT_EQ(std::get<CSSColor>(hex3DigitColorValue).b, 255);
EXPECT_EQ(std::get<CSSColor>(hex3DigitColorValue).a, 255);

auto hex4DigitColorValue = parseCSSProperty<CSSColor>("#ffff");
EXPECT_TRUE(std::holds_alternative<CSSColor>(hex4DigitColorValue));
EXPECT_EQ(std::get<CSSColor>(hex4DigitColorValue).r, 255);
EXPECT_EQ(std::get<CSSColor>(hex4DigitColorValue).g, 255);
EXPECT_EQ(std::get<CSSColor>(hex4DigitColorValue).b, 255);
EXPECT_EQ(std::get<CSSColor>(hex4DigitColorValue).a, 255);

auto hex6DigitColorValue = parseCSSProperty<CSSColor>("#ffffff");
EXPECT_TRUE(std::holds_alternative<CSSColor>(hex6DigitColorValue));
EXPECT_EQ(std::get<CSSColor>(hex6DigitColorValue).r, 255);
EXPECT_EQ(std::get<CSSColor>(hex6DigitColorValue).g, 255);
EXPECT_EQ(std::get<CSSColor>(hex6DigitColorValue).b, 255);
EXPECT_EQ(std::get<CSSColor>(hex6DigitColorValue).a, 255);

auto hex8DigitColorValue = parseCSSProperty<CSSColor>("#ffffffff");
EXPECT_TRUE(std::holds_alternative<CSSColor>(hex8DigitColorValue));
EXPECT_EQ(std::get<CSSColor>(hex8DigitColorValue).r, 255);
EXPECT_EQ(std::get<CSSColor>(hex8DigitColorValue).g, 255);
EXPECT_EQ(std::get<CSSColor>(hex8DigitColorValue).b, 255);
EXPECT_EQ(std::get<CSSColor>(hex8DigitColorValue).a, 255);

auto hexMixedCaseColorValue = parseCSSProperty<CSSColor>("#FFCc99");
EXPECT_TRUE(std::holds_alternative<CSSColor>(hexMixedCaseColorValue));
EXPECT_EQ(std::get<CSSColor>(hexMixedCaseColorValue).r, 255);
EXPECT_EQ(std::get<CSSColor>(hexMixedCaseColorValue).g, 204);
EXPECT_EQ(std::get<CSSColor>(hexMixedCaseColorValue).b, 153);
EXPECT_EQ(std::get<CSSColor>(hexMixedCaseColorValue).a, 255);

auto hexDigitOnlyColorValue = parseCSSProperty<CSSColor>("#369");
EXPECT_TRUE(std::holds_alternative<CSSColor>(hexDigitOnlyColorValue));
EXPECT_EQ(std::get<CSSColor>(hexDigitOnlyColorValue).r, 51);
EXPECT_EQ(std::get<CSSColor>(hexDigitOnlyColorValue).g, 102);
EXPECT_EQ(std::get<CSSColor>(hexDigitOnlyColorValue).b, 153);
EXPECT_EQ(std::get<CSSColor>(hexDigitOnlyColorValue).a, 255);

auto hexAlphaTestValue = parseCSSProperty<CSSColor>("#FFFFFFCC");
EXPECT_TRUE(std::holds_alternative<CSSColor>(hexAlphaTestValue));
EXPECT_EQ(std::get<CSSColor>(hexAlphaTestValue).r, 255);
EXPECT_EQ(std::get<CSSColor>(hexAlphaTestValue).g, 255);
EXPECT_EQ(std::get<CSSColor>(hexAlphaTestValue).b, 255);
EXPECT_EQ(std::get<CSSColor>(hexAlphaTestValue).a, 204);
}

TEST(CSSColor, named_colors) {
auto invalidNamedColorTestValue = parseCSSProperty<CSSColor>("redd");
EXPECT_TRUE(
std::holds_alternative<std::monostate>(invalidNamedColorTestValue));

auto namedColorTestValue1 = parseCSSProperty<CSSColor>("red");
EXPECT_TRUE(std::holds_alternative<CSSColor>(namedColorTestValue1));
EXPECT_EQ(std::get<CSSColor>(namedColorTestValue1).r, 255);
EXPECT_EQ(std::get<CSSColor>(namedColorTestValue1).g, 0);
EXPECT_EQ(std::get<CSSColor>(namedColorTestValue1).b, 0);
EXPECT_EQ(std::get<CSSColor>(namedColorTestValue1).a, 255);

auto namedColorTestValue2 = parseCSSProperty<CSSColor>("cornsilk");
EXPECT_TRUE(std::holds_alternative<CSSColor>(namedColorTestValue2));
EXPECT_EQ(std::get<CSSColor>(namedColorTestValue2).r, 255);
EXPECT_EQ(std::get<CSSColor>(namedColorTestValue2).g, 248);
EXPECT_EQ(std::get<CSSColor>(namedColorTestValue2).b, 220);
EXPECT_EQ(std::get<CSSColor>(namedColorTestValue2).a, 255);

auto namedColorMixedCaseTestValue = parseCSSProperty<CSSColor>("sPrINgGrEEn");
EXPECT_TRUE(std::holds_alternative<CSSColor>(namedColorMixedCaseTestValue));
EXPECT_EQ(std::get<CSSColor>(namedColorMixedCaseTestValue).r, 0);
EXPECT_EQ(std::get<CSSColor>(namedColorMixedCaseTestValue).g, 255);
EXPECT_EQ(std::get<CSSColor>(namedColorMixedCaseTestValue).b, 127);
EXPECT_EQ(std::get<CSSColor>(namedColorMixedCaseTestValue).a, 255);

auto transparentColor = parseCSSProperty<CSSColor>("transparent");
EXPECT_TRUE(std::holds_alternative<CSSColor>(transparentColor));
EXPECT_EQ(std::get<CSSColor>(transparentColor).r, 0);
EXPECT_EQ(std::get<CSSColor>(transparentColor).g, 0);
EXPECT_EQ(std::get<CSSColor>(transparentColor).b, 0);
EXPECT_EQ(std::get<CSSColor>(transparentColor).a, 0);
}

} // namespace facebook::react
Loading
Loading