blob: e67d3c97fd82eb4254c620a2583e6cc45097b72d [file] [log] [blame]
/*
* Copyright 2020 The casbin Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <cvaluate/EvaluableExpression.h>
namespace {
/*
Represents a test of parsing all tokens correctly from a string
*/
struct TokenParsingTest {
std::string Name;
std::string Input;
std::vector<Cvaluate::ExpressionToken> Expected;
Cvaluate::ExpressionFunctionMap Functions;
TokenParsingTest(std::string name, std::string input,
std::vector<Cvaluate::ExpressionToken> expected, Cvaluate::ExpressionFunctionMap functions = {}) :
Name(name), Input(input), Expected(expected), Functions(functions) {};
};
/*
Because std::function don't support operator==
So, we can't directly compare to std::function variable in std::variant
*/
template<typename T>
void Assert_Token(Cvaluate::ExpressionToken& expected_token, Cvaluate::ExpressionToken& actual_token, TokenParsingTest& test_case) {
if (auto expected_value = std::get_if<T>(&(expected_token.Value))) {
if (auto actual_value = std::get_if<T>(&(actual_token.Value))) {
ASSERT_EQ(*expected_value, *actual_value) << "Vaule don't match " << test_case.Name;
} else {
FAIL() << test_case.Name << "\n Value type don't match, expected: ";
}
}
}
void RunTokenParsingTest(std::vector<TokenParsingTest>& token_parsing_tests) {
for (auto& test_case: token_parsing_tests) {
auto expression = Cvaluate::EvaluableExpression(test_case.Input, test_case.Functions);
auto actual_tokens = expression.Tokens();
auto expected_tokens = test_case.Expected;
ASSERT_EQ(actual_tokens.size(), expected_tokens.size());
for (size_t i = 0; i < actual_tokens.size(); i++) {
auto actual_token = actual_tokens[i];
auto expected_token = expected_tokens[i];
// Token kind
ASSERT_EQ(actual_token.Kind, expected_token.Kind)
<< test_case.Name << "\nType don't match!";
// Token value
// TokenAvaiableData
Assert_Token<Cvaluate::TokenAvaiableData>(actual_token, expected_token, test_case);
}
}
}
TEST(TestParse, TestConstantParsing) {
std::vector<TokenParsingTest> token_parsing_tests = {
{
"Single numeric",
"1",
{
{
Cvaluate::TokenKind::NUMERIC,
1,
},
}
},
{
"Single two-digit numeric",
"50",
{
{
Cvaluate::TokenKind::NUMERIC,
50,
},
}
},
{
"Zero",
"0",
{
{
Cvaluate::TokenKind::NUMERIC,
0,
},
}
},
{
"One digit hex",
"0x1",
{
{
Cvaluate::TokenKind::NUMERIC,
1,
},
}
},
{
"Two digit hex",
"0x10",
{
{
Cvaluate::TokenKind::NUMERIC,
16,
},
}
},
{
"Hex with lowercase",
"0xabcdef",
{
{
Cvaluate::TokenKind::NUMERIC,
11259375,
},
}
},
{
"Hex with uppercase",
"0xABCDEF",
{
{
Cvaluate::TokenKind::NUMERIC,
11259375,
},
}
},
{
"Single string",
"'foo'",
{
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
}
},
{
"Single boolen",
"true",
{
{
Cvaluate::TokenKind::BOOLEAN,
true,
},
}
},
{
"Single false boolen",
"false",
{
{
Cvaluate::TokenKind::BOOLEAN,
false,
},
}
},
{
"Single large numeric",
"1234567890",
{
{
Cvaluate::TokenKind::NUMERIC,
float(1234567890),
},
}
},
{
"Single floating-point",
"0.5",
{
{
Cvaluate::TokenKind::NUMERIC,
(float)0.5,
},
}
},
{
"Single large floating point",
"3.14567471",
{
{
Cvaluate::TokenKind::NUMERIC,
(float)3.14567471,
},
}
},
{
"Parameterless function",
"foo()",
{
{
Cvaluate::TokenKind::FUNCTION,
Cvaluate::ExpressionFunction(nullptr),
},
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
},
{{"foo", nullptr}},
},
{
"Single parameter function",
"foo('bar')",
{
{
Cvaluate::TokenKind::FUNCTION,
Cvaluate::ExpressionFunction(nullptr),
},
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::STRING,
std::string("bar"),
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
},
{{"foo", nullptr}},
},
{
"Multiple parameter function",
"foo('bar', 1.0)",
{
{
Cvaluate::TokenKind::FUNCTION,
Cvaluate::ExpressionFunction(nullptr),
},
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::STRING,
std::string("bar"),
},
{
Cvaluate::TokenKind::SEPARATOR,
std::string(","),
},
{
Cvaluate::TokenKind::NUMERIC,
(float)1.0,
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
},
{{"foo", nullptr}},
},
{
"Nested function",
"foo(foo('bar'), 1.0, foo(2.0))",
{
{
Cvaluate::TokenKind::FUNCTION,
Cvaluate::ExpressionFunction(nullptr),
},
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::FUNCTION,
Cvaluate::ExpressionFunction(nullptr),
},
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::STRING,
std::string("bar"),
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
{
Cvaluate::TokenKind::SEPARATOR,
std::string(","),
},
{
Cvaluate::TokenKind::NUMERIC,
float(1.0),
},
{
Cvaluate::TokenKind::SEPARATOR,
std::string(","),
},
{
Cvaluate::TokenKind::FUNCTION,
Cvaluate::ExpressionFunction(nullptr),
},
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::NUMERIC,
(float)2.0,
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
},
{{"foo", nullptr}},
},
{
"Function with modifier afterwards (#28)",
"foo() + 1",
{
{
Cvaluate::TokenKind::FUNCTION,
Cvaluate::ExpressionFunction(nullptr),
},
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
{
Cvaluate::TokenKind::MODIFIER,
std::string("+"),
},
{
Cvaluate::TokenKind::NUMERIC,
(int)1,
},
},
{{"foo", nullptr}},
},
{
"Function with modifier afterwards and comparator",
"(foo()-1) > 3",
{
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::FUNCTION,
Cvaluate::ExpressionFunction(nullptr),
},
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
{
Cvaluate::TokenKind::MODIFIER,
std::string("-"),
},
{
Cvaluate::TokenKind::NUMERIC,
(int)1,
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string(">"),
},
{
Cvaluate::TokenKind::NUMERIC,
(int)3,
},
},
{{"foo", nullptr}},
},
{
"Double-quoted string added to square-brackted param (#59)",
"\"a\" + [foo]",
{
{
Cvaluate::TokenKind::STRING,
std::string("a"),
},
{
Cvaluate::TokenKind::MODIFIER,
std::string("+"),
},
{
Cvaluate::TokenKind::VARIABLE,
std::string("foo"),
},
},
},
{
"Accessor variable",
"foo.Var",
{
{
Cvaluate::TokenKind::ACCESSOR,
std::vector<std::string>{"foo", "Var"},
},
},
},
// Don't support
// TokenParsingTest{
// "Accessor function",
// "foo.Operation()",
// {
// {
// Cvaluate::TokenKind::ACCESSOR,
// std::vector<std::string>{"foo", "Operation"},
// },
// {
// Cvaluate::TokenKind::CLAUSE,
// std::string("("),
// },
// {
// Cvaluate::TokenKind::CLAUSE_CLOSE,
// std::string(")"),
// },
// },
// },
};
RunTokenParsingTest(token_parsing_tests);
}
TEST(TestParse, TestLogicalOperatorParsing) {
std::vector<TokenParsingTest> token_parsing_tests = {
{
"Boolean AND",
"true && false",
{
{
Cvaluate::TokenKind::BOOLEAN,
(bool)true,
},
{
Cvaluate::TokenKind::LOGICALOP,
std::string("&&"),
},
{
Cvaluate::TokenKind::BOOLEAN,
(bool)false,
},
},
},
{
"Boolean OR",
"true || false",
{
{
Cvaluate::TokenKind::BOOLEAN,
(bool)true,
},
{
Cvaluate::TokenKind::LOGICALOP,
std::string("||"),
},
{
Cvaluate::TokenKind::BOOLEAN,
(bool)false,
},
},
},
{
"Multiple logical operators",
"true || false && true",
{
{
Cvaluate::TokenKind::BOOLEAN,
(bool)true,
},
{
Cvaluate::TokenKind::LOGICALOP,
std::string("||"),
},
{
Cvaluate::TokenKind::BOOLEAN,
(bool)false,
},
{
Cvaluate::TokenKind::LOGICALOP,
std::string("&&"),
},
{
Cvaluate::TokenKind::BOOLEAN,
(bool)true,
},
},
},
};
RunTokenParsingTest(token_parsing_tests);
}
TEST(TestParse, TestComparatorParsing) {
std::vector<TokenParsingTest> token_parsing_tests = {
{
"Numeric EQ",
"1 == 2",
{
{
Cvaluate::TokenKind::NUMERIC,
(int)1,
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("=="),
},
{
Cvaluate::TokenKind::NUMERIC,
(int)2,
},
},
},
{
"Numeric NEQ",
"1 != 2",
{
{
Cvaluate::TokenKind::NUMERIC,
(int)1,
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("!="),
},
{
Cvaluate::TokenKind::NUMERIC,
(int)2,
},
},
},
{
"Numeric GT",
"1 > 0",
{
{
Cvaluate::TokenKind::NUMERIC,
(int)1,
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string(">"),
},
{
Cvaluate::TokenKind::NUMERIC,
(int)0,
},
},
},
{
"Numeric LT",
"1 < 2",
{
{
Cvaluate::TokenKind::NUMERIC,
int(1),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("<"),
},
{
Cvaluate::TokenKind::NUMERIC,
int(2),
},
},
},
{
"Numeric GTE",
"1 >= 2",
{
{
Cvaluate::TokenKind::NUMERIC,
int(1),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string(">="),
},
{
Cvaluate::TokenKind::NUMERIC,
int(2),
},
},
},
{
"Numeric LTE",
"1 <= 2",
{
{
Cvaluate::TokenKind::NUMERIC,
int(1),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("<="),
},
{
Cvaluate::TokenKind::NUMERIC,
int(2),
},
},
},
TokenParsingTest{
"String LT",
"'ab.cd' < 'abc.def'",
{
{
Cvaluate::TokenKind::STRING,
std::string("ab.cd"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("<"),
},
{
Cvaluate::TokenKind::STRING,
std::string("abc.def"),
},
},
},
{
"String LTE",
"'ab.cd' <= 'abc.def'",
{
{
Cvaluate::TokenKind::STRING,
std::string("ab.cd"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("<="),
},
{
Cvaluate::TokenKind::STRING,
std::string("abc.def"),
},
},
},
{
"String GT",
"'ab.cd' > 'abc.def'",
{
{
Cvaluate::TokenKind::STRING,
std::string("ab.cd"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string(">"),
},
{
Cvaluate::TokenKind::STRING,
std::string("abc.def"),
},
},
},
{
"String GTE",
"'ab.cd' >= 'abc.def'",
{
{
Cvaluate::TokenKind::STRING,
std::string("ab.cd"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string(">="),
},
{
Cvaluate::TokenKind::STRING,
std::string("abc.def"),
},
},
},
// {
// "String REQ",
// "'foobar' =~ 'bar'",
// {
// {
// Cvaluate::TokenKind::STRING,
// std::string("foobar"),
// },
// {
// Cvaluate::TokenKind::COMPARATOR,
// std::string("=~"),
// },
// // it's not particularly clean to test for the contents of a pattern, (since it means modifying the harness below)
// // so pattern contents are left untested.
// ExpressionToken{
// Cvaluate::TokenKind::PATTERN,
// std::string("");
// },
// },
// },
// TokenParsingTest{
// Name: "String NREQ",
// Input: "'foobar' !~ 'bar'",
// Expected: []ExpressionToken{
// ExpressionToken{
// Cvaluate::TokenKind::STRING,
// Value: "foobar",
// },
// ExpressionToken{
// Cvaluate::TokenKind::COMPARATOR,
// Value: "!~",
// },
// ExpressionToken{
// Kind: PATTERN,
// },
// },
// },
{
"Comparator against modifier string additive (#22)",
"'foo' == '+'",
{
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("=="),
},
{
Cvaluate::TokenKind::STRING,
std::string("+"),
},
},
},
{
"Comparator against modifier string multiplicative (#22)",
"'foo' == '/'",
{
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("=="),
},
{
Cvaluate::TokenKind::STRING,
std::string("/"),
},
},
},
{
"Comparator against modifier string exponential (#22)",
"'foo' == '**'",
{
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("=="),
},
{
Cvaluate::TokenKind::STRING,
std::string("**"),
},
},
},
{
"Comparator against modifier string bitwise (#22)",
"'foo' == '^'",
{
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("=="),
},
{
Cvaluate::TokenKind::STRING,
std::string("^"),
},
},
},
{
"Comparator against modifier string shift (#22)",
"'foo' == '>>'",
{
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("=="),
},
{
Cvaluate::TokenKind::STRING,
std::string(">>"),
},
},
},
TokenParsingTest{
"Comparator against modifier string ternary (#22)",
"'foo' == '?'",
{
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("=="),
},
{
Cvaluate::TokenKind::STRING,
std::string("?"),
},
},
},
{
"Array membership lowercase",
"'foo' in ('foo', 'bar')",
{
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("in"),
},
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
{
Cvaluate::TokenKind::SEPARATOR,
std::string(","),
},
{
Cvaluate::TokenKind::STRING,
std::string("bar"),
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
},
},
{
"Array membership uppercase",
"'foo' IN ('foo', 'bar')",
{
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
{
Cvaluate::TokenKind::COMPARATOR,
std::string("in"),
},
{
Cvaluate::TokenKind::CLAUSE,
std::string("("),
},
{
Cvaluate::TokenKind::STRING,
std::string("foo"),
},
{
Cvaluate::TokenKind::SEPARATOR,
std::string(","),
},
{
Cvaluate::TokenKind::STRING,
std::string("bar"),
},
{
Cvaluate::TokenKind::CLAUSE_CLOSE,
std::string(")"),
},
},
},
};
RunTokenParsingTest(token_parsing_tests);
}
TEST(TestParse, TestPrefixParsing) {
std::vector<TokenParsingTest> token_parsing_tests = {
{
"Sign prefix",
"-1",
{
{
Cvaluate::TokenKind::PREFIX,
std::string("-"),
},
{
Cvaluate::TokenKind::NUMERIC,
int(1),
},
},
},
{
"Sign prefix on variable",
"-foo",
{
{
Cvaluate::TokenKind::PREFIX,
std::string("-"),
},
{
Cvaluate::TokenKind::VARIABLE,
std::string("foo"),
},
},
},
{
"Boolean prefix",
"!true",
{
{
Cvaluate::TokenKind::PREFIX,
std::string("!"),
},
{
Cvaluate::TokenKind::BOOLEAN,
bool(true),
},
},
},
{
"Boolean prefix on variable",
"!foo",
{
{
Cvaluate::TokenKind::PREFIX,
std::string("!"),
},
{
Cvaluate::TokenKind::VARIABLE,
std::string("foo"),
},
},
},
{
"Bitwise not prefix",
"~1",
{
{
Cvaluate::TokenKind::PREFIX,
std::string("~"),
},
{
Cvaluate::TokenKind::NUMERIC,
int(1),
},
},
},
{
"Bitwise not prefix on variable",
"~foo",
{
{
Cvaluate::TokenKind::PREFIX,
std::string("~"),
},
{
Cvaluate::TokenKind::VARIABLE,
std::string("foo"),
},
},
},
};
RunTokenParsingTest(token_parsing_tests);
}
} // namespace