Add some more filter tests (#4897)

This changes the `make coverage` function to use `gcovr` instead of `lcov`, and to have it generate an html file directly at `coverage/index.html` under the build directory

The only thing this changes, other than adding tests, is making the `Expression` class pure virtual. Every derived class should implement each of the functions
This commit is contained in:
pajlada 2023-10-14 10:41:10 +02:00 committed by GitHub
parent ccaedc3987
commit ab3b2d8515
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 28 deletions

View file

@ -679,15 +679,15 @@ endif()
if (CHATTERINO_GENERATE_COVERAGE) if (CHATTERINO_GENERATE_COVERAGE)
include(CodeCoverage) include(CodeCoverage)
append_coverage_compiler_flags_to_target(${LIBRARY_PROJECT}) append_coverage_compiler_flags_to_target(${LIBRARY_PROJECT})
target_link_libraries(${LIBRARY_PROJECT} PUBLIC gcov)
message(STATUS "project source dir: ${PROJECT_SOURCE_DIR}/src") message(STATUS "project source dir: ${PROJECT_SOURCE_DIR}/src")
setup_target_for_coverage_lcov( setup_target_for_coverage_gcovr_html(
NAME coverage NAME coverage
EXECUTABLE ./bin/chatterino-test EXECUTABLE ctest
BASE_DIRECTORY ${PROJECT_SOURCE_DIR}/src
EXCLUDE "/usr/include/*" EXCLUDE "/usr/include/*"
EXCLUDE "build-*/*" EXCLUDE "build-*/*"
EXCLUDE "lib/*" EXCLUDE "lib/*"
EXCLUDE "*/ui_*.h"
EXCLUDE "*/moc_*.cpp"
) )
endif () endif ()

View file

@ -2,24 +2,4 @@
namespace chatterino::filters { namespace chatterino::filters {
QVariant Expression::execute(const ContextMap & /*context*/) const
{
return false;
}
PossibleType Expression::synthesizeType(const TypingContext & /*context*/) const
{
return IllTyped{this, "Not implemented"};
}
QString Expression::debug(const TypingContext & /*context*/) const
{
return "";
}
QString Expression::filterString() const
{
return "";
}
} // namespace chatterino::filters } // namespace chatterino::filters

View file

@ -16,10 +16,10 @@ class Expression
public: public:
virtual ~Expression() = default; virtual ~Expression() = default;
virtual QVariant execute(const ContextMap &context) const; virtual QVariant execute(const ContextMap &context) const = 0;
virtual PossibleType synthesizeType(const TypingContext &context) const; virtual PossibleType synthesizeType(const TypingContext &context) const = 0;
virtual QString debug(const TypingContext &context) const; virtual QString debug(const TypingContext &context) const = 0;
virtual QString filterString() const; virtual QString filterString() const = 0;
}; };
using ExpressionPtr = std::unique_ptr<Expression>; using ExpressionPtr = std::unique_ptr<Expression>;

View file

@ -1,4 +1,5 @@
#include "controllers/accounts/AccountController.hpp" #include "controllers/accounts/AccountController.hpp"
#include "controllers/filters/lang/expressions/UnaryOperation.hpp"
#include "controllers/filters/lang/Filter.hpp" #include "controllers/filters/lang/Filter.hpp"
#include "controllers/filters/lang/Types.hpp" #include "controllers/filters/lang/Types.hpp"
#include "controllers/highlights/HighlightController.hpp" #include "controllers/highlights/HighlightController.hpp"
@ -285,3 +286,88 @@ TEST_F(FiltersF, TypingContextChecks)
delete privmsg; delete privmsg;
} }
TEST_F(FiltersF, ExpressionDebug)
{
struct TestCase {
QString input;
QString debugString;
QString filterString;
};
// clang-format off
std::vector<TestCase> tests{
{
.input = R".(1 + 1).",
.debugString = "BinaryOp[Plus](Val(1) : Int, Val(1) : Int)",
.filterString = "(1 + 1)",
},
{
.input = R".(author.color == "#ff0000").",
.debugString = "BinaryOp[Eq](Val(author.color) : Color, Val(#ff0000) : String)",
.filterString = R".((author.color == "#ff0000")).",
},
{
.input = R".(1).",
.debugString = "Val(1)",
.filterString = R".(1).",
},
{
.input = R".("asd").",
.debugString = R".(Val(asd)).",
.filterString = R".("asd").",
},
{
.input = R".(("asd")).",
.debugString = R".(Val(asd)).",
.filterString = R".("asd").",
},
{
.input = R".(author.subbed).",
.debugString = R".(Val(author.subbed)).",
.filterString = R".(author.subbed).",
},
{
.input = R".(!author.subbed).",
.debugString = R".(UnaryOp[Not](Val(author.subbed) : Bool)).",
.filterString = R".((!author.subbed)).",
},
{
.input = R".({"foo", "bar"} contains "foo").",
.debugString = R".(BinaryOp[Contains](List(Val(foo) : String, Val(bar) : String) : StringList, Val(foo) : String)).",
.filterString = R".(({"foo", "bar"} contains "foo")).",
},
{
.input = R".(!({"foo", "bar"} contains "foo")).",
.debugString = R".(UnaryOp[Not](BinaryOp[Contains](List(Val(foo) : String, Val(bar) : String) : StringList, Val(foo) : String) : Bool)).",
.filterString = R".((!({"foo", "bar"} contains "foo"))).",
},
{
.input = R".(message.content match r"(\d\d)/(\d\d)/(\d\d\d\d)").",
.debugString = R".(BinaryOp[Match](Val(message.content) : String, RegEx((\d\d)/(\d\d)/(\d\d\d\d)) : RegularExpression)).",
.filterString = R".((message.content match r"(\d\d)/(\d\d)/(\d\d\d\d)")).",
},
};
// clang-format on
for (const auto &[input, debugString, filterString] : tests)
{
const auto filterResult = Filter::fromString(input);
const auto *filter = std::get_if<Filter>(&filterResult);
EXPECT_NE(filter, nullptr)
<< "Filter::fromString(" << qUtf8Printable(input)
<< ") did not build a proper filter";
const auto actualDebugString = filter->debugString(typingContext);
EXPECT_EQ(actualDebugString, debugString)
<< "filter->debugString() on '" << qUtf8Printable(input)
<< "' should be '" << qUtf8Printable(debugString) << "', but got '"
<< qUtf8Printable(actualDebugString) << "'";
const auto actualFilterString = filter->filterString();
EXPECT_EQ(actualFilterString, filterString)
<< "filter->filterString() on '" << qUtf8Printable(input)
<< "' should be '" << qUtf8Printable(filterString) << "', but got '"
<< qUtf8Printable(actualFilterString) << "'";
}
}