| // Copyright 2017 The Effcee Authors. |
| // |
| // 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 <vector> |
| #include "gmock/gmock.h" |
| |
| #include "check.h" |
| |
| namespace { |
| |
| using effcee::Check; |
| using effcee::Options; |
| using effcee::CheckList; |
| using effcee::ParseChecks; |
| using effcee::Result; |
| using effcee::StringPiece; |
| using ::testing::Combine; |
| using ::testing::Eq; |
| using ::testing::HasSubstr; |
| using ::testing::ValuesIn; |
| |
| using Part = effcee::Check::Part; |
| using Status = effcee::Result::Status; |
| using Type = Check::Type; |
| using VarMapping = effcee::VarMapping; |
| |
| // Check class |
| |
| // Returns a vector of all Check types. |
| std::vector<Type> AllTypes() { |
| return {Type::Simple, Type::Next, Type::Same, |
| Type::DAG, Type::Label, Type::Not}; |
| } |
| |
| using CheckTypeTest = ::testing::TestWithParam<Type>; |
| |
| TEST_P(CheckTypeTest, ConstructWithAnyType) { |
| Check check(GetParam(), ""); |
| EXPECT_THAT(check.type(), Eq(GetParam())); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(AllTypes, CheckTypeTest, ValuesIn(AllTypes())); |
| |
| using CheckParamTest = ::testing::TestWithParam<StringPiece>; |
| |
| TEST_P(CheckParamTest, ConstructWithSampleParamValue) { |
| Check check(Type::Simple, GetParam()); |
| // The contents are the same. |
| EXPECT_THAT(check.param(), Eq(GetParam())); |
| // The referenced storage is the same. |
| EXPECT_THAT(check.param().data(), Eq(GetParam().data())); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(SampleParams, CheckParamTest, |
| ValuesIn(std::vector<StringPiece>{ |
| "", "a b c", "The wind {{in}} the willows\n", |
| "Bring me back to the mountains of yore."})); |
| |
| // Equality operator |
| TEST(CheckEqualityTest, TrueWhenAllComponentsSame) { |
| EXPECT_TRUE(Check(Type::Simple, "abc") == Check(Type::Simple, "abc")); |
| } |
| |
| TEST(CheckEqualityTest, FalseWhenTypeDifferent) { |
| EXPECT_FALSE(Check(Type::Simple, "abc") == Check(Type::Next, "abc")); |
| } |
| |
| TEST(CheckEqualityTest, FalseWhenParamDifferent) { |
| EXPECT_FALSE(Check(Type::Simple, "abc") == Check(Type::Simple, "def")); |
| } |
| |
| // Inequality operator |
| TEST(CheckInequalityTest, FalseWhenAllComponentsSame) { |
| EXPECT_FALSE(Check(Type::Simple, "abc") != Check(Type::Simple, "abc")); |
| } |
| |
| TEST(CheckInequalityTest, TrueWhenTypeDifferent) { |
| EXPECT_TRUE(Check(Type::Simple, "abc") != Check(Type::Next, "abc")); |
| } |
| |
| TEST(CheckInequalityTest, TrueWhenParamDifferent) { |
| EXPECT_TRUE(Check(Type::Simple, "abc") != Check(Type::Simple, "def")); |
| } |
| |
| // ParseChecks free function |
| |
| TEST(ParseChecks, FreeFunctionLinks) { |
| std::pair<Result, CheckList> parsed(ParseChecks("", Options())); |
| } |
| |
| TEST(ParseChecks, FailWhenRulePrefixIsEmpty) { |
| const auto parsed(ParseChecks("CHECK: now", Options().SetPrefix(""))); |
| const Result& result = parsed.first; |
| const CheckList& pattern = parsed.second; |
| EXPECT_THAT(result.status(), Eq(Status::BadOption)); |
| EXPECT_THAT(result.message(), Eq("Rule prefix is empty")); |
| EXPECT_THAT(pattern.size(), Eq(0)); |
| } |
| |
| TEST(ParseChecks, FailWhenRulePrefixIsWhitespace) { |
| const auto parsed(ParseChecks("CHECK: now", Options().SetPrefix("\t\n "))); |
| const Result& result = parsed.first; |
| const CheckList& pattern = parsed.second; |
| EXPECT_THAT(result.status(), Eq(Status::BadOption)); |
| EXPECT_THAT(result.message(), |
| Eq("Rule prefix is whitespace. That's silly.")); |
| EXPECT_THAT(pattern.size(), Eq(0)); |
| } |
| |
| TEST(ParseChecks, FailWhenChecksAbsent) { |
| const auto parsed(ParseChecks("no checks", Options())); |
| const Result& result = parsed.first; |
| const CheckList& pattern = parsed.second; |
| EXPECT_THAT(result.status(), Eq(Status::NoRules)); |
| EXPECT_THAT(result.message(), |
| Eq("No check rules specified. Looking for prefix CHECK")); |
| EXPECT_THAT(pattern.size(), Eq(0)); |
| } |
| |
| TEST(ParseChecks, FailWhenChecksAbsentWithCustomPrefix) { |
| const auto parsed(ParseChecks("CHECK: now", Options().SetPrefix("FOO"))); |
| const Result& result = parsed.first; |
| const CheckList& pattern = parsed.second; |
| EXPECT_THAT(result.status(), Eq(Status::NoRules)); |
| EXPECT_THAT(result.message(), |
| Eq("No check rules specified. Looking for prefix FOO")); |
| EXPECT_THAT(pattern.size(), Eq(0)); |
| } |
| |
| TEST(ParseChecks, FindSimpleCheck) { |
| const auto parsed = ParseChecks("CHECK: now", Options()); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::Ok)); |
| EXPECT_THAT(parsed.second, Eq(CheckList({Check(Type::Simple, "now")}))); |
| } |
| |
| TEST(ParseChecks, FindSimpleCheckWithCustomPrefix) { |
| const auto parsed = ParseChecks("FOO: how", Options().SetPrefix("FOO")); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::Ok)); |
| EXPECT_THAT(parsed.second, Eq(CheckList({Check(Type::Simple, "how")}))); |
| } |
| |
| TEST(ParseChecks, FindSimpleCheckWithCustomPrefixHavingRegexpMetachars) { |
| const auto parsed = ParseChecks("[::alpha::]^\\d: how", |
| Options().SetPrefix("[::alpha::]^\\d")); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::Ok)); |
| EXPECT_THAT(parsed.second, Eq(CheckList({Check(Type::Simple, "how")}))); |
| } |
| |
| TEST(ParseChecks, FindSimpleCheckPartwayThroughLine) { |
| const auto parsed = ParseChecks("some other garbageCHECK: now", Options()); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::Ok)); |
| EXPECT_THAT(parsed.second, Eq(CheckList({Check(Type::Simple, "now")}))); |
| } |
| |
| TEST(ParseChecks, FindSimpleCheckCheckListWithoutSurroundingWhitespace) { |
| const auto parsed = ParseChecks("CHECK:now", Options()); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::Ok)); |
| EXPECT_THAT(parsed.second, Eq(CheckList({Check(Type::Simple, "now")}))); |
| } |
| |
| TEST(ParseChecks, FindSimpleCheckCheckListWhileStrippingSurroundingWhitespace) { |
| const auto parsed = ParseChecks("CHECK: \t now\t\t ", Options()); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::Ok)); |
| EXPECT_THAT(parsed.second, Eq(CheckList({Check(Type::Simple, "now")}))); |
| } |
| |
| TEST(ParseChecks, FindSimpleCheckCountsLinesCorrectly) { |
| const auto parsed = ParseChecks("\n\nCHECK: now", Options()); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::Ok)); |
| EXPECT_THAT(parsed.second, Eq(CheckList({Check(Type::Simple, "now")}))); |
| } |
| |
| TEST(ParseChecks, FindSimpleChecksOnSeparateLines) { |
| const auto parsed = |
| ParseChecks("CHECK: now\n\n\nCHECK: and \n CHECK: then", Options()); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::Ok)); |
| EXPECT_THAT(parsed.second, Eq(CheckList({Check(Type::Simple, "now"), |
| Check(Type::Simple, "and"), |
| Check(Type::Simple, "then")}))); |
| } |
| |
| TEST(ParseChecks, FindSimpleChecksOnlyOncePerLine) { |
| const auto parsed = ParseChecks("CHECK: now CHECK: then", Options()); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::Ok)); |
| EXPECT_THAT(parsed.second, |
| Eq(CheckList({Check(Type::Simple, "now CHECK: then")}))); |
| } |
| |
| // Test parsing of the different check rule types. |
| |
| using ParseChecksTypeTest = ::testing::TestWithParam< |
| std::tuple<std::string, std::pair<std::string, Type>>>; |
| |
| TEST_P(ParseChecksTypeTest, Successful) { |
| const auto& prefix = std::get<0>(GetParam()); |
| const auto& type_str = std::get<0>(std::get<1>(GetParam())); |
| const Type& type = std::get<1>(std::get<1>(GetParam())); |
| // A CHECK-SAME rule can't appear first, so insert a CHECK: rule first. |
| const std::string input = prefix + ": here\n" + prefix + type_str + ": now"; |
| const auto parsed = ParseChecks(input, Options().SetPrefix(prefix)); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::Ok)); |
| EXPECT_THAT(parsed.second, |
| Eq(CheckList({Check(Type::Simple, "here"), Check(type, "now")}))); |
| } |
| |
| // Returns a vector of pairs. Each pair has first member being a check type |
| // suffix, and the second member is the corresponding check type. |
| std::vector<std::pair<std::string, Type>> AllCheckTypesAsPairs() { |
| return { |
| {"", Type::Simple}, {"-NEXT", Type::Next}, {"-SAME", Type::Same}, |
| {"-DAG", Type::DAG}, {"-LABEL", Type::Label}, {"-NOT", Type::Not}, |
| }; |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(AllCheckTypes, ParseChecksTypeTest, |
| Combine(ValuesIn(std::vector<std::string>{"CHECK", |
| "FOO"}), |
| ValuesIn(AllCheckTypesAsPairs()))); |
| |
| using ParseChecksTypeFailTest = ::testing::TestWithParam< |
| std::tuple<std::string, std::pair<std::string, Type>>>; |
| |
| // This is just one way to fail. |
| TEST_P(ParseChecksTypeFailTest, FailureWhenNoColon) { |
| const auto& prefix = std::get<0>(GetParam()); |
| const auto& type_str = std::get<0>(std::get<1>(GetParam())); |
| const std::string input = prefix + type_str + "BAD now"; |
| const auto parsed = ParseChecks(input, Options().SetPrefix(prefix)); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::NoRules)); |
| EXPECT_THAT(parsed.second, Eq(CheckList{})); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(AllCheckTypes, ParseChecksTypeFailTest, |
| Combine(ValuesIn(std::vector<std::string>{"CHECK", |
| "FOO"}), |
| ValuesIn(AllCheckTypesAsPairs()))); |
| |
| TEST(ParseChecks, BadRegexpFails) { |
| const auto parsed = ParseChecks("CHECK: {{\\}}", Options()); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::BadRule)); |
| EXPECT_THAT(parsed.first.message(), HasSubstr("invalid regex: \\")); |
| EXPECT_THAT(parsed.second, Eq(CheckList({}))); |
| } |
| |
| TEST(ParseChecks, CheckSameCantBeFirst) { |
| const auto parsed = ParseChecks("CHECK-SAME: now", Options()); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::BadRule)); |
| EXPECT_THAT(parsed.first.message(), |
| HasSubstr("CHECK-SAME can't be the first check rule")); |
| EXPECT_THAT(parsed.second, Eq(CheckList({}))); |
| } |
| |
| TEST(ParseChecks, CheckSameCantBeFirstDifferentPrefix) { |
| const auto parsed = ParseChecks("BOO-SAME: now", Options().SetPrefix("BOO")); |
| EXPECT_THAT(parsed.first.status(), Eq(Status::BadRule)); |
| EXPECT_THAT(parsed.first.message(), |
| HasSubstr("BOO-SAME can't be the first check rule")); |
| EXPECT_THAT(parsed.second, Eq(CheckList({}))); |
| } |
| |
| // Check::Matches |
| struct CheckMatchCase { |
| std::string input; |
| Check check; |
| bool expected; |
| std::string remaining; |
| std::string captured; |
| }; |
| |
| using CheckMatchTest = ::testing::TestWithParam<CheckMatchCase>; |
| |
| TEST_P(CheckMatchTest, Samples) { |
| StringPiece str = GetParam().input; |
| StringPiece captured; |
| VarMapping vars; |
| const bool matched = GetParam().check.Matches(&str, &captured, &vars); |
| EXPECT_THAT(matched, Eq(GetParam().expected)) |
| << "Failed on input " << GetParam().input; |
| EXPECT_THAT(std::string(str.data(), str.size()), Eq(GetParam().remaining)); |
| EXPECT_THAT(std::string(captured.data(), captured.size()), |
| Eq(GetParam().captured)); |
| EXPECT_TRUE(vars.empty()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| Simple, CheckMatchTest, |
| ValuesIn(std::vector<CheckMatchCase>{ |
| {"hello", Check(Type::Simple, "hello"), true, "", "hello"}, |
| {"world", Check(Type::Simple, "hello"), false, "world", ""}, |
| {"in hello now", Check(Type::Simple, "hello"), true, " now", "hello"}, |
| {"hello", Check(Type::Same, "hello"), true, "", "hello"}, |
| {"world", Check(Type::Same, "hello"), false, "world", ""}, |
| {"in hello now", Check(Type::Same, "hello"), true, " now", "hello"}, |
| {"hello", Check(Type::Next, "hello"), true, "", "hello"}, |
| {"world", Check(Type::Next, "hello"), false, "world", ""}, |
| {"in hello now", Check(Type::Next, "hello"), true, " now", "hello"}, |
| {"hello", Check(Type::DAG, "hello"), true, "", "hello"}, |
| {"world", Check(Type::DAG, "hello"), false, "world", ""}, |
| {"in hello now", Check(Type::DAG, "hello"), true, " now", "hello"}, |
| {"hello", Check(Type::Label, "hello"), true, "", "hello"}, |
| {"world", Check(Type::Label, "hello"), false, "world", ""}, |
| {"in hello now", Check(Type::Label, "hello"), true, " now", "hello"}, |
| {"hello", Check(Type::Label, "hello"), true, "", "hello"}, |
| {"world", Check(Type::Label, "hello"), false, "world", ""}, |
| {"in hello now", Check(Type::Label, "hello"), true, " now", "hello"}, |
| {"hello", Check(Type::Not, "hello"), true, "", "hello"}, |
| {"world", Check(Type::Not, "hello"), false, "world", ""}, |
| {"in hello now", Check(Type::Not, "hello"), true, " now", "hello"}, |
| })); |
| |
| // Check::Part::Regex |
| |
| TEST(CheckPart, FixedPartRegex) { |
| VarMapping vm; |
| EXPECT_THAT(Part(Part::Type::Fixed, "abc").Regex(vm), Eq("abc")); |
| EXPECT_THAT(Part(Part::Type::Fixed, "a.bc").Regex(vm), Eq("a\\.bc")); |
| EXPECT_THAT(Part(Part::Type::Fixed, "a?bc").Regex(vm), Eq("a\\?bc")); |
| EXPECT_THAT(Part(Part::Type::Fixed, "a+bc").Regex(vm), Eq("a\\+bc")); |
| EXPECT_THAT(Part(Part::Type::Fixed, "a*bc").Regex(vm), Eq("a\\*bc")); |
| EXPECT_THAT(Part(Part::Type::Fixed, "a[b]").Regex(vm), Eq("a\\[b\\]")); |
| EXPECT_THAT(Part(Part::Type::Fixed, "a[-]").Regex(vm), Eq("a\\[\\-\\]")); |
| EXPECT_THAT(Part(Part::Type::Fixed, "a(-)b").Regex(vm), Eq("a\\(\\-\\)b")); |
| } |
| |
| TEST(CheckPart, RegexPartRegex) { |
| VarMapping vm; |
| EXPECT_THAT(Part(Part::Type::Regex, "abc").Regex(vm), Eq("abc")); |
| EXPECT_THAT(Part(Part::Type::Regex, "a.bc").Regex(vm), Eq("a.bc")); |
| EXPECT_THAT(Part(Part::Type::Regex, "a?bc").Regex(vm), Eq("a?bc")); |
| EXPECT_THAT(Part(Part::Type::Regex, "a+bc").Regex(vm), Eq("a+bc")); |
| EXPECT_THAT(Part(Part::Type::Regex, "a*bc").Regex(vm), Eq("a*bc")); |
| EXPECT_THAT(Part(Part::Type::Regex, "a[b]").Regex(vm), Eq("a[b]")); |
| EXPECT_THAT(Part(Part::Type::Regex, "a[-]").Regex(vm), Eq("a[-]")); |
| EXPECT_THAT(Part(Part::Type::Regex, "a(-)b").Regex(vm), Eq("a(-)b")); |
| } |
| |
| } // namespace |
| |