diff --git a/CMakeLists.txt b/CMakeLists.txt index fa98228..c667240 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,3 +32,8 @@ endif(PROJECT_IS_TOP_LEVEL) add_subdirectory(src) add_subdirectory(src/pogPrinter) + +if(PROJECT_IS_TOP_LEVEL) + enable_testing() + add_subdirectory(tests) +endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..26de877 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,36 @@ +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.14.0 +) +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +include(GoogleTest) + +# Sanitizers for Debug builds (GCC/Clang only) +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + if(CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang$) + add_compile_options(-fsanitize=address,undefined -fno-omit-frame-pointer) + add_link_options(-fsanitize=address,undefined) + endif() +endif() + +function(bast_add_test name) + add_executable(${name} ${name}.cpp) + target_link_libraries(${name} PRIVATE BAST_LIB tinyxml2 GTest::gtest_main) + target_include_directories(${name} PRIVATE ${CMAKE_SOURCE_DIR}/src) + gtest_discover_tests(${name}) +endfunction() + +bast_add_test(test_hash) +bast_add_test(test_btype) +bast_add_test(test_vars) +bast_add_test(test_expr) +bast_add_test(test_pred) +bast_add_test(test_subst) +bast_add_test(test_readExpr) +bast_add_test(test_readPred) +bast_add_test(test_readBType) +bast_add_test(test_readSubst) diff --git a/tests/test_btype.cpp b/tests/test_btype.cpp new file mode 100644 index 0000000..831225e --- /dev/null +++ b/tests/test_btype.cpp @@ -0,0 +1,286 @@ +#include "btype.h" + +#include + +// --- Primitive types --- + +TEST(BType, PrimitiveKinds) { + EXPECT_EQ(BType::INT.getKind(), BType::Kind::INTEGER); + EXPECT_EQ(BType::BOOL.getKind(), BType::Kind::BOOLEAN); + EXPECT_EQ(BType::FLOAT.getKind(), BType::Kind::FLOAT); + EXPECT_EQ(BType::REAL.getKind(), BType::Kind::REAL); + EXPECT_EQ(BType::STRING.getKind(), BType::Kind::STRING); +} + +TEST(BType, PrimitiveEquality) { + EXPECT_EQ(BType::INT, BType::INT); + EXPECT_EQ(BType::BOOL, BType::BOOL); + EXPECT_NE(BType::INT, BType::BOOL); + EXPECT_NE(BType::FLOAT, BType::REAL); + EXPECT_NE(BType::STRING, BType::INT); +} + +TEST(BType, PrimitiveToString) { + EXPECT_EQ(BType::INT.to_string(), "INT"); + EXPECT_EQ(BType::BOOL.to_string(), "BOOL"); + EXPECT_EQ(BType::FLOAT.to_string(), "FLOAT"); + EXPECT_EQ(BType::REAL.to_string(), "REAL"); + EXPECT_EQ(BType::STRING.to_string(), "STRING"); +} + +TEST(BType, DefaultConstructorIsInteger) { + BType t; + EXPECT_EQ(t.getKind(), BType::Kind::INTEGER); + EXPECT_EQ(t, BType::INT); +} + +// --- Power types --- + +TEST(BType, PowerTypeKind) { + auto t = BType::POW(BType::INT); + EXPECT_EQ(t.getKind(), BType::Kind::PowerType); +} + +TEST(BType, PowerTypeContent) { + auto t = BType::POW(BType::INT); + EXPECT_EQ(t.toPowerType().content, BType::INT); +} + +TEST(BType, PowerTypeEquality) { + EXPECT_EQ(BType::POW(BType::INT), BType::POW(BType::INT)); + EXPECT_NE(BType::POW(BType::INT), BType::POW(BType::BOOL)); +} + +TEST(BType, PowerTypeNested) { + auto t = BType::POW(BType::POW(BType::INT)); + EXPECT_EQ(t.getKind(), BType::Kind::PowerType); + auto inner = t.toPowerType().content; + EXPECT_EQ(inner.getKind(), BType::Kind::PowerType); + EXPECT_EQ(inner.toPowerType().content, BType::INT); +} + +TEST(BType, PowerTypeToString) { + EXPECT_EQ(BType::POW(BType::INT).to_string(), "POW(INT)"); + EXPECT_EQ(BType::POW(BType::POW(BType::BOOL)).to_string(), + "POW(POW(BOOL))"); +} + +TEST(BType, PrebuiltPowerTypes) { + EXPECT_EQ(BType::POW_INT, BType::POW(BType::INT)); + EXPECT_EQ(BType::POW_BOOL, BType::POW(BType::BOOL)); + EXPECT_EQ(BType::POW_FLOAT, BType::POW(BType::FLOAT)); + EXPECT_EQ(BType::POW_STRING, BType::POW(BType::STRING)); + EXPECT_EQ(BType::POW_REAL, BType::POW(BType::REAL)); +} + +// --- Product types --- + +TEST(BType, ProductTypeKind) { + auto t = BType::PROD(BType::INT, BType::BOOL); + EXPECT_EQ(t.getKind(), BType::Kind::ProductType); +} + +TEST(BType, ProductTypeComponents) { + auto t = BType::PROD(BType::INT, BType::BOOL); + EXPECT_EQ(t.toProductType().lhs, BType::INT); + EXPECT_EQ(t.toProductType().rhs, BType::BOOL); +} + +TEST(BType, ProductTypeEquality) { + EXPECT_EQ(BType::PROD(BType::INT, BType::BOOL), + BType::PROD(BType::INT, BType::BOOL)); + EXPECT_NE(BType::PROD(BType::INT, BType::BOOL), + BType::PROD(BType::BOOL, BType::INT)); +} + +TEST(BType, ProductTypeToString) { + EXPECT_EQ(BType::PROD(BType::INT, BType::BOOL).to_string(), + "PROD(INT, BOOL)"); +} + +TEST(BType, RelationInteger) { + EXPECT_EQ(BType::RELATION_INTEGER, BType::POW(BType::PROD(BType::INT, BType::INT))); +} + +// --- Abstract sets --- + +TEST(BType, AbstractSetKind) { + auto t = BType::SET("COLORS"); + EXPECT_EQ(t.getKind(), BType::Kind::AbstractSet); +} + +TEST(BType, AbstractSetName) { + auto t = BType::ABSTRACT_SET("COLORS"); + EXPECT_EQ(t.toAbstractSetType().getName(), "COLORS"); +} + +TEST(BType, AbstractSetEquality) { + EXPECT_EQ(BType::SET("S"), BType::SET("S")); + EXPECT_NE(BType::SET("S"), BType::SET("T")); +} + +TEST(BType, AbstractSetToString) { + EXPECT_EQ(BType::SET("COLORS").to_string(), "ASET(COLORS)"); +} + +TEST(BType, SetAndAbstractSetEqual) { + EXPECT_EQ(BType::SET("X"), BType::ABSTRACT_SET("X")); +} + +// --- Enumerated sets --- + +TEST(BType, EnumeratedSetKind) { + auto t = BType::ENUMERATED_SET({"COLORS", {"red", "green", "blue"}}); + EXPECT_EQ(t.getKind(), BType::Kind::EnumeratedSet); +} + +TEST(BType, EnumeratedSetAccessors) { + auto t = BType::ENUMERATED_SET({"COLORS", {"red", "green", "blue"}}); + EXPECT_EQ(t.toEnumeratedSetType().getName(), "COLORS"); + auto &content = t.toEnumeratedSetType().getContent(); + ASSERT_EQ(content.size(), 3u); + EXPECT_EQ(content[0], "red"); + EXPECT_EQ(content[1], "green"); + EXPECT_EQ(content[2], "blue"); +} + +TEST(BType, EnumeratedSetToString) { + auto t = BType::ENUMERATED_SET({"COLORS", {"red", "green"}}); + EXPECT_EQ(t.to_string(), "ESET(COLORS)"); +} + +// --- Struct types --- + +TEST(BType, StructKind) { + auto t = BType::STRUCT({{"x", BType::INT}, {"y", BType::BOOL}}); + EXPECT_EQ(t.getKind(), BType::Kind::Struct); +} + +TEST(BType, StructFieldsSorted) { + auto t = BType::STRUCT({{"z", BType::INT}, {"a", BType::BOOL}}); + auto &fields = t.toRecordType().m_fields; + ASSERT_EQ(fields.size(), 2u); + EXPECT_EQ(fields[0].first, "a"); + EXPECT_EQ(fields[1].first, "z"); +} + +TEST(BType, StructEquality) { + auto t1 = BType::STRUCT({{"x", BType::INT}, {"y", BType::BOOL}}); + auto t2 = BType::STRUCT({{"y", BType::BOOL}, {"x", BType::INT}}); + EXPECT_EQ(t1, t2); +} + +TEST(BType, StructInequality) { + auto t1 = BType::STRUCT({{"x", BType::INT}}); + auto t2 = BType::STRUCT({{"x", BType::BOOL}}); + EXPECT_NE(t1, t2); +} + +TEST(BType, StructToString) { + auto t = BType::STRUCT({{"x", BType::INT}, {"y", BType::BOOL}}); + EXPECT_EQ(t.to_string(), "STRUCT(x : INT, y : BOOL)"); +} + +// --- Comparison --- + +TEST(BType, CompareReflexive) { + EXPECT_EQ(BType::compare(BType::INT, BType::INT), 0); +} + +TEST(BType, CompareTransitive) { + // Different kinds have an ordering + auto cmp1 = BType::compare(BType::INT, BType::BOOL); + auto cmp2 = BType::compare(BType::BOOL, BType::INT); + EXPECT_NE(cmp1, 0); + EXPECT_EQ(cmp1, -cmp2); +} + +TEST(BType, CompareOperators) { + EXPECT_TRUE(BType::INT == BType::INT); + EXPECT_TRUE(BType::INT != BType::BOOL); + // One of these must be true + EXPECT_TRUE(BType::INT < BType::BOOL || BType::INT > BType::BOOL); +} + +// Note: BType::vec_compare is declared but not implemented in the library + +// --- Hashing --- + +TEST(BType, HashConsistency) { + EXPECT_EQ(BType::INT.hash_combine(0), BType::INT.hash_combine(0)); + EXPECT_EQ(BType::POW(BType::INT).hash_combine(0), + BType::POW(BType::INT).hash_combine(0)); +} + +TEST(BType, HashEqualObjectsSameHash) { + auto t1 = BType::PROD(BType::INT, BType::BOOL); + auto t2 = BType::PROD(BType::INT, BType::BOOL); + EXPECT_EQ(t1, t2); + EXPECT_EQ(t1.hash_combine(0), t2.hash_combine(0)); +} + +// --- Visitor --- + +class TestVisitor : public BType::Visitor { + public: + BType::Kind visited = BType::Kind::INTEGER; + void visitINTEGER() override { visited = BType::Kind::INTEGER; } + void visitBOOLEAN() override { visited = BType::Kind::BOOLEAN; } + void visitFLOAT() override { visited = BType::Kind::FLOAT; } + void visitREAL() override { visited = BType::Kind::REAL; } + void visitSTRING() override { visited = BType::Kind::STRING; } + void visitProductType(const BType &, const BType &) override { + visited = BType::Kind::ProductType; + } + void visitPowerType(const BType &) override { + visited = BType::Kind::PowerType; + } + void visitRecordType( + const std::vector> &) override { + visited = BType::Kind::Struct; + } + void visitAbstractSet(const BType::AbstractSet &) override { + visited = BType::Kind::AbstractSet; + } + void visitEnumeratedSet(const BType::EnumeratedSet &) override { + visited = BType::Kind::EnumeratedSet; + } +}; + +TEST(BType, VisitorPrimitive) { + TestVisitor v; + BType::INT.accept(v); + EXPECT_EQ(v.visited, BType::Kind::INTEGER); + BType::BOOL.accept(v); + EXPECT_EQ(v.visited, BType::Kind::BOOLEAN); +} + +TEST(BType, VisitorPowerType) { + TestVisitor v; + BType::POW(BType::INT).accept(v); + EXPECT_EQ(v.visited, BType::Kind::PowerType); +} + +TEST(BType, VisitorProductType) { + TestVisitor v; + BType::PROD(BType::INT, BType::BOOL).accept(v); + EXPECT_EQ(v.visited, BType::Kind::ProductType); +} + +TEST(BType, VisitorStruct) { + TestVisitor v; + BType::STRUCT({{"x", BType::INT}}).accept(v); + EXPECT_EQ(v.visited, BType::Kind::Struct); +} + +TEST(BType, VisitorAbstractSet) { + TestVisitor v; + BType::SET("S").accept(v); + EXPECT_EQ(v.visited, BType::Kind::AbstractSet); +} + +TEST(BType, VisitorEnumeratedSet) { + TestVisitor v; + BType::ENUMERATED_SET({"S", {"a", "b"}}).accept(v); + EXPECT_EQ(v.visited, BType::Kind::EnumeratedSet); +} diff --git a/tests/test_expr.cpp b/tests/test_expr.cpp new file mode 100644 index 0000000..308513c --- /dev/null +++ b/tests/test_expr.cpp @@ -0,0 +1,536 @@ +#include "expr.h" +#include "exprDesc.h" + +#include + +#include "pred.h" + +// --- Constants --- + +TEST(Expr, MaxInt) { + auto e = Expr::makeMaxInt(); + EXPECT_EQ(e.getTag(), Expr::EKind::MaxInt); +} + +TEST(Expr, MinInt) { + auto e = Expr::makeMinInt(); + EXPECT_EQ(e.getTag(), Expr::EKind::MinInt); +} + +TEST(Expr, ConstantINTEGER) { + auto e = Expr::makeINTEGER(); + EXPECT_EQ(e.getTag(), Expr::EKind::INTEGER); + EXPECT_TRUE(e.isTypeExpression()); +} + +TEST(Expr, ConstantNATURAL) { + auto e = Expr::makeNATURAL(); + EXPECT_EQ(e.getTag(), Expr::EKind::NATURAL); +} + +TEST(Expr, ConstantBOOL) { + auto e = Expr::makeBOOL(); + EXPECT_EQ(e.getTag(), Expr::EKind::BOOL); +} + +TEST(Expr, ConstantTRUE) { + auto e = Expr::makeTRUE(); + EXPECT_EQ(e.getTag(), Expr::EKind::TRUE); +} + +TEST(Expr, ConstantFALSE) { + auto e = Expr::makeFALSE(); + EXPECT_EQ(e.getTag(), Expr::EKind::FALSE); +} + +TEST(Expr, Successor) { + auto e = Expr::makeSuccessor(); + EXPECT_EQ(e.getTag(), Expr::EKind::Successor); +} + +TEST(Expr, Predecessor) { + auto e = Expr::makePredecessor(); + EXPECT_EQ(e.getTag(), Expr::EKind::Predecessor); +} + +// --- Literals --- + +TEST(Expr, IntegerLiteral) { + auto e = Expr::makeInteger("42"); + EXPECT_EQ(e.getTag(), Expr::EKind::IntegerLiteral); + EXPECT_EQ(e.getIntegerLiteral(), "42"); +} + +TEST(Expr, StringLiteral) { + auto e = Expr::makeString("hello"); + EXPECT_EQ(e.getTag(), Expr::EKind::StringLiteral); + EXPECT_EQ(e.getStringLiteral(), "hello"); +} + +TEST(Expr, RealLiteral) { + Expr::Decimal d("3", "14"); + auto e = Expr::makeReal(d); + EXPECT_EQ(e.getTag(), Expr::EKind::RealLiteral); + EXPECT_EQ(e.getRealLiteral().integerPart, "3"); + EXPECT_EQ(e.getRealLiteral().fractionalPart, "14"); +} + +// --- Decimal --- + +TEST(Decimal, Compare) { + Expr::Decimal d1("3", "14"); + Expr::Decimal d2("3", "14"); + EXPECT_EQ(d1.compare(d2), 0); +} + +TEST(Decimal, CompareDifferent) { + Expr::Decimal d1("3", "14"); + Expr::Decimal d2("4", "0"); + EXPECT_NE(d1.compare(d2), 0); +} + +TEST(Decimal, IntegerOnly) { + Expr::Decimal d("42"); + EXPECT_EQ(d.integerPart, "42"); + EXPECT_EQ(d.fractionalPart, "0"); +} + +// --- Identifiers --- + +TEST(Expr, Ident) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto e = Expr::makeIdent(x, BType::INT); + EXPECT_EQ(e.getTag(), Expr::EKind::Id); + EXPECT_EQ(e.getId(), x); + EXPECT_EQ(e.getType(), BType::INT); +} + +// --- EmptySet --- + +TEST(Expr, EmptySet) { + auto e = Expr::makeEmptySet(BType::POW_INT); + EXPECT_EQ(e.getTag(), Expr::EKind::EmptySet); + EXPECT_EQ(e.getType(), BType::POW_INT); +} + +// --- Binary expressions --- + +TEST(Expr, BinaryExpr) { + auto lhs = Expr::makeInteger("1"); + auto rhs = Expr::makeInteger("2"); + auto e = Expr::makeBinaryExpr(Expr::BinaryOp::IAddition, std::move(lhs), + std::move(rhs), BType::INT); + EXPECT_EQ(e.getTag(), Expr::EKind::BinaryExpr); + EXPECT_EQ(e.getType(), BType::INT); + auto &bin = e.toBinaryExpr(); + EXPECT_EQ(bin.op, Expr::BinaryOp::IAddition); +} + +// --- Unary expressions --- + +TEST(Expr, UnaryExpr) { + auto inner = Expr::makeIdent(VarName::makeVarWithoutSuffix("s"), BType::POW_INT); + auto e = + Expr::makeUnaryExpr(Expr::UnaryOp::Cardinality, std::move(inner), BType::INT); + EXPECT_EQ(e.getTag(), Expr::EKind::UnaryExpr); + EXPECT_EQ(e.toUnaryExpr().op, Expr::UnaryOp::Cardinality); +} + +// --- Nary expressions --- + +TEST(Expr, NaryExprSet) { + std::vector elems; + elems.push_back(Expr::makeInteger("1")); + elems.push_back(Expr::makeInteger("2")); + auto e = + Expr::makeNaryExpr(Expr::NaryOp::Set, std::move(elems), BType::POW_INT); + EXPECT_EQ(e.getTag(), Expr::EKind::NaryExpr); + EXPECT_EQ(e.toNaryExpr().op, Expr::NaryOp::Set); +} + +TEST(Expr, NaryExprSequence) { + std::vector elems; + elems.push_back(Expr::makeInteger("1")); + auto seqType = BType::POW(BType::PROD(BType::INT, BType::INT)); + auto e = + Expr::makeNaryExpr(Expr::NaryOp::Sequence, std::move(elems), seqType); + EXPECT_EQ(e.getTag(), Expr::EKind::NaryExpr); + EXPECT_EQ(e.toNaryExpr().op, Expr::NaryOp::Sequence); +} + +// --- Ternary expressions --- + +TEST(Expr, TernaryExpr) { + auto fst = Expr::makeInteger("1"); + auto snd = Expr::makeInteger("2"); + auto thd = Expr::makeInteger("3"); + auto e = Expr::makeTernaryExpr(Expr::TernaryOp::Son, std::move(fst), + std::move(snd), std::move(thd), BType::INT); + EXPECT_EQ(e.getTag(), Expr::EKind::TernaryExpr); + EXPECT_EQ(e.toTernaryExpr().op, Expr::TernaryOp::Son); +} + +// --- Quantified expressions --- + +TEST(Expr, QuantifiedExpr) { + auto x = VarName::makeVarWithoutSuffix("x"); + std::vector vars = {TypedVar(x, BType::INT)}; + auto cond = Pred::makeTrue(); + auto body = Expr::makeIdent(x, BType::INT); + auto e = Expr::makeQuantifiedExpr(Expr::QuantifiedOp::Lambda, vars, + std::move(cond), std::move(body), + BType::POW(BType::PROD(BType::INT, BType::INT))); + EXPECT_EQ(e.getTag(), Expr::EKind::QuantifiedExpr); +} + +// --- Quantified set --- + +TEST(Expr, QuantifiedSet) { + auto x = VarName::makeVarWithoutSuffix("x"); + std::vector vars = {TypedVar(x, BType::INT)}; + auto xExpr = Expr::makeIdent(x, BType::INT); + auto cond = Pred::makeExprComparison(Pred::ComparisonOp::Igt, + std::move(xExpr), Expr::makeInteger("0")); + auto e = Expr::makeQuantifiedSet(vars, std::move(cond), BType::POW_INT); + EXPECT_EQ(e.getTag(), Expr::EKind::QuantifiedSet); +} + +// --- BooleanExpr --- + +TEST(Expr, BooleanExpr) { + auto p = Pred::makeTrue(); + auto e = Expr::makeBooleanExpr(std::move(p)); + EXPECT_EQ(e.getTag(), Expr::EKind::BooleanExpr); +} + +// --- Record / Struct --- + +TEST(Expr, RecordExpr) { + std::vector> fields; + fields.push_back({"a", Expr::makeInteger("1")}); + fields.push_back({"b", Expr::makeInteger("2")}); + auto recType = BType::STRUCT({{"a", BType::INT}, {"b", BType::INT}}); + auto e = Expr::makeRecord(std::move(fields), recType); + EXPECT_EQ(e.getTag(), Expr::EKind::Record); +} + +TEST(Expr, StructExpr) { + std::vector> fields; + fields.push_back({"a", Expr::makeINTEGER()}); + auto structType = BType::STRUCT({{"a", BType::POW_INT}}); + auto e = Expr::makeStruct(std::move(fields), structType); + EXPECT_EQ(e.getTag(), Expr::EKind::Struct); +} + +// --- Record field access --- + +TEST(Expr, RecordFieldAccess) { + auto x = Expr::makeIdent( + VarName::makeVarWithoutSuffix("r"), + BType::STRUCT({{"a", BType::INT}, {"b", BType::BOOL}})); + auto e = Expr::makeRecordFieldAccess(std::move(x), "a", BType::INT); + EXPECT_EQ(e.getTag(), Expr::EKind::Record_Field_Access); +} + +// --- Record field update --- + +TEST(Expr, RecordFieldUpdate) { + auto recType = BType::STRUCT({{"a", BType::INT}}); + auto rec = Expr::makeIdent(VarName::makeVarWithoutSuffix("r"), recType); + auto val = Expr::makeInteger("42"); + auto e = Expr::makeRecordFieldUpdate(std::move(rec), "a", std::move(val), + recType); + EXPECT_EQ(e.getTag(), Expr::EKind::Record_Field_Update); +} + +// --- Copy --- + +TEST(Expr, CopyPreservesTag) { + auto e = Expr::makeInteger("42"); + auto c = e.copy(); + EXPECT_EQ(c.getTag(), Expr::EKind::IntegerLiteral); + EXPECT_EQ(c.getIntegerLiteral(), "42"); +} + +TEST(Expr, CopyIndependent) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto e = Expr::makeIdent(x, BType::INT); + auto c = e.copy(); + EXPECT_EQ(Expr::compare(e, c), 0); +} + +// --- Comparison --- + +TEST(Expr, CompareEqual) { + auto e1 = Expr::makeInteger("42"); + auto e2 = Expr::makeInteger("42"); + EXPECT_EQ(Expr::compare(e1, e2), 0); +} + +TEST(Expr, CompareDifferentKind) { + auto e1 = Expr::makeInteger("42"); + auto e2 = Expr::makeString("hello"); + EXPECT_NE(Expr::compare(e1, e2), 0); +} + +TEST(Expr, CompareBinary) { + auto e1 = Expr::makeBinaryExpr(Expr::BinaryOp::IAddition, + Expr::makeInteger("1"), Expr::makeInteger("2"), + BType::INT); + auto e2 = Expr::makeBinaryExpr(Expr::BinaryOp::IAddition, + Expr::makeInteger("1"), Expr::makeInteger("2"), + BType::INT); + EXPECT_EQ(Expr::compare(e1, e2), 0); +} + +TEST(Expr, CompareBinaryDifferentOp) { + auto e1 = Expr::makeBinaryExpr(Expr::BinaryOp::IAddition, + Expr::makeInteger("1"), Expr::makeInteger("2"), + BType::INT); + auto e2 = Expr::makeBinaryExpr(Expr::BinaryOp::ISubtraction, + Expr::makeInteger("1"), Expr::makeInteger("2"), + BType::INT); + EXPECT_NE(Expr::compare(e1, e2), 0); +} + +// --- Alpha equality --- + +TEST(Expr, AlphaEqualsIdentical) { + auto e1 = Expr::makeInteger("42"); + auto e2 = Expr::makeInteger("42"); + EXPECT_TRUE(Expr::alpha_equals(e1, e2)); +} + +TEST(Expr, AlphaEqualsDifferent) { + auto e1 = Expr::makeInteger("1"); + auto e2 = Expr::makeInteger("2"); + EXPECT_FALSE(Expr::alpha_equals(e1, e2)); +} + +TEST(Expr, AlphaEqualsQuantified) { + // lambda x . x vs lambda x$1 . x$1 (same prefix, different suffix) + auto x = VarName::makeVarWithoutSuffix("x"); + auto x1 = VarName::makeVar("x", 1); + std::vector vars1 = {TypedVar(x, BType::INT)}; + std::vector vars2 = {TypedVar(x1, BType::INT)}; + auto lambdaType = BType::POW(BType::PROD(BType::INT, BType::INT)); + + auto e1 = Expr::makeQuantifiedExpr(Expr::QuantifiedOp::Lambda, vars1, + Pred::makeTrue(), + Expr::makeIdent(x, BType::INT), lambdaType); + auto e2 = Expr::makeQuantifiedExpr(Expr::QuantifiedOp::Lambda, vars2, + Pred::makeTrue(), + Expr::makeIdent(x1, BType::INT), lambdaType); + EXPECT_TRUE(Expr::alpha_equals(e1, e2)); +} + +TEST(Expr, AlphaNotEqualsDifferentPrefix) { + // lambda x . x vs lambda y . y (different prefix -> not alpha-equal) + auto x = VarName::makeVarWithoutSuffix("x"); + auto y = VarName::makeVarWithoutSuffix("y"); + std::vector vars1 = {TypedVar(x, BType::INT)}; + std::vector vars2 = {TypedVar(y, BType::INT)}; + auto lambdaType = BType::POW(BType::PROD(BType::INT, BType::INT)); + + auto e1 = Expr::makeQuantifiedExpr(Expr::QuantifiedOp::Lambda, vars1, + Pred::makeTrue(), + Expr::makeIdent(x, BType::INT), lambdaType); + auto e2 = Expr::makeQuantifiedExpr(Expr::QuantifiedOp::Lambda, vars2, + Pred::makeTrue(), + Expr::makeIdent(y, BType::INT), lambdaType); + EXPECT_FALSE(Expr::alpha_equals(e1, e2)); +} + +// --- Hashing --- + +TEST(Expr, HashConsistency) { + auto e1 = Expr::makeInteger("42"); + auto e2 = Expr::makeInteger("42"); + EXPECT_EQ(e1.hash_combine(0), e2.hash_combine(0)); +} + +TEST(Expr, HashEqualImpliesSameHash) { + auto e1 = Expr::makeBinaryExpr(Expr::BinaryOp::IAddition, + Expr::makeInteger("1"), Expr::makeInteger("2"), + BType::INT); + auto e2 = Expr::makeBinaryExpr(Expr::BinaryOp::IAddition, + Expr::makeInteger("1"), Expr::makeInteger("2"), + BType::INT); + EXPECT_EQ(Expr::compare(e1, e2), 0); + EXPECT_EQ(e1.hash_combine(0), e2.hash_combine(0)); +} + +// --- Free variables --- + +TEST(Expr, FreeVarsIdent) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto e = Expr::makeIdent(x, BType::INT); + auto fv = e.getFreeVars(); + EXPECT_EQ(fv.size(), 1u); + EXPECT_TRUE(fv.count(x)); +} + +TEST(Expr, FreeVarsConstant) { + auto e = Expr::makeInteger("42"); + auto fv = e.getFreeVars(); + EXPECT_TRUE(fv.empty()); +} + +TEST(Expr, FreeVarsBinary) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto y = VarName::makeVarWithoutSuffix("y"); + auto e = Expr::makeBinaryExpr(Expr::BinaryOp::IAddition, + Expr::makeIdent(x, BType::INT), + Expr::makeIdent(y, BType::INT), BType::INT); + auto fv = e.getFreeVars(); + EXPECT_EQ(fv.size(), 2u); + EXPECT_TRUE(fv.count(x)); + EXPECT_TRUE(fv.count(y)); +} + +TEST(Expr, FreeVarsQuantifiedBound) { + auto x = VarName::makeVarWithoutSuffix("x"); + std::vector vars = {TypedVar(x, BType::INT)}; + auto lambdaType = BType::POW(BType::PROD(BType::INT, BType::INT)); + auto e = Expr::makeQuantifiedExpr(Expr::QuantifiedOp::Lambda, vars, + Pred::makeTrue(), + Expr::makeIdent(x, BType::INT), lambdaType); + auto fv = e.getFreeVars(); + EXPECT_TRUE(fv.empty()); +} + +TEST(Expr, FreeVarsQuantifiedMixed) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto y = VarName::makeVarWithoutSuffix("y"); + std::vector vars = {TypedVar(x, BType::INT)}; + auto lambdaType = BType::POW(BType::PROD(BType::INT, BType::INT)); + // lambda x . x + y => y is free + auto body = Expr::makeBinaryExpr(Expr::BinaryOp::IAddition, + Expr::makeIdent(x, BType::INT), + Expr::makeIdent(y, BType::INT), BType::INT); + auto e = Expr::makeQuantifiedExpr(Expr::QuantifiedOp::Lambda, vars, + Pred::makeTrue(), std::move(body), + lambdaType); + auto fv = e.getFreeVars(); + EXPECT_EQ(fv.size(), 1u); + EXPECT_TRUE(fv.count(y)); + EXPECT_FALSE(fv.count(x)); +} + +// --- getAllVars --- + +TEST(Expr, GetAllVarsQuantified) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto y = VarName::makeVarWithoutSuffix("y"); + std::vector vars = {TypedVar(x, BType::INT)}; + auto lambdaType = BType::POW(BType::PROD(BType::INT, BType::INT)); + auto body = Expr::makeBinaryExpr(Expr::BinaryOp::IAddition, + Expr::makeIdent(x, BType::INT), + Expr::makeIdent(y, BType::INT), BType::INT); + auto e = Expr::makeQuantifiedExpr(Expr::QuantifiedOp::Lambda, vars, + Pred::makeTrue(), std::move(body), + lambdaType); + auto av = e.getAllVars(); + EXPECT_EQ(av.size(), 2u); + EXPECT_TRUE(av.count(x)); + EXPECT_TRUE(av.count(y)); +} + +// --- Substitution --- + +TEST(Expr, SubstIdent) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto e = Expr::makeIdent(x, BType::INT); + std::map map; + map.insert({x, Expr::makeInteger("42")}); + e.subst(map); + EXPECT_EQ(e.getTag(), Expr::EKind::IntegerLiteral); + EXPECT_EQ(e.getIntegerLiteral(), "42"); +} + +TEST(Expr, SubstNoMatch) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto y = VarName::makeVarWithoutSuffix("y"); + auto e = Expr::makeIdent(x, BType::INT); + std::map map; + map.insert({y, Expr::makeInteger("42")}); + e.subst(map); + EXPECT_EQ(e.getTag(), Expr::EKind::Id); + EXPECT_EQ(e.getId(), x); +} + +TEST(Expr, SubstBinary) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto e = Expr::makeBinaryExpr(Expr::BinaryOp::IAddition, + Expr::makeIdent(x, BType::INT), + Expr::makeInteger("1"), BType::INT); + std::map map; + map.insert({x, Expr::makeInteger("5")}); + e.subst(map); + // After substitution, lhs should be 5 + auto &bin = e.toBinaryExpr(); + EXPECT_EQ(bin.lhs.getTag(), Expr::EKind::IntegerLiteral); + EXPECT_EQ(bin.lhs.getIntegerLiteral(), "5"); +} + +// --- Alpha renaming --- + +TEST(Expr, AlphaRename) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto y = VarName::makeVarWithoutSuffix("y"); + auto e = Expr::makeIdent(x, BType::INT); + std::map map; + map.insert({x, y}); + e.alpha(map); + EXPECT_EQ(e.getId(), y); +} + +// --- BxmlTag --- + +TEST(Expr, BxmlTag) { + auto e = Expr::makeInteger("1", {"tag1", "tag2"}); + auto &tags = e.getBxmlTag(); + ASSERT_EQ(tags.size(), 2u); + EXPECT_EQ(tags[0], "tag1"); + EXPECT_EQ(tags[1], "tag2"); +} + +// --- Default constructor --- + +TEST(Expr, DefaultConstructor) { + Expr e; + EXPECT_EQ(e.getTag(), Expr::EKind::MaxInt); +} + +// --- Vec compare --- + +TEST(Expr, VecCompareEqual) { + std::vector v1; + v1.push_back(Expr::makeInteger("1")); + v1.push_back(Expr::makeInteger("2")); + std::vector v2; + v2.push_back(Expr::makeInteger("1")); + v2.push_back(Expr::makeInteger("2")); + EXPECT_EQ(Expr::vec_compare(v1, v2), 0); +} + +TEST(Expr, VecCompareDifferentSize) { + std::vector v1; + v1.push_back(Expr::makeInteger("1")); + std::vector v2; + v2.push_back(Expr::makeInteger("1")); + v2.push_back(Expr::makeInteger("2")); + EXPECT_LT(Expr::vec_compare(v1, v2), 0); +} + +// --- getFreeTVars --- + +TEST(Expr, GetFreeTVars) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto e = Expr::makeIdent(x, BType::INT); + auto ftv = e.getFreeTVars(); + EXPECT_EQ(ftv.size(), 1u); + auto it = ftv.begin(); + EXPECT_EQ(it->name, x); + EXPECT_EQ(it->type, BType::INT); +} diff --git a/tests/test_hash.cpp b/tests/test_hash.cpp new file mode 100644 index 0000000..4d822d0 --- /dev/null +++ b/tests/test_hash.cpp @@ -0,0 +1,65 @@ +#include "hash.h" + +#include + +TEST(HashUtil, CombineIntDeterministic) { + size_t a = hashUtil::hash_combine_int(42, 0); + size_t b = hashUtil::hash_combine_int(42, 0); + EXPECT_EQ(a, b); +} + +TEST(HashUtil, CombineIntDifferentValues) { + size_t a = hashUtil::hash_combine_int(1, 0); + size_t b = hashUtil::hash_combine_int(2, 0); + EXPECT_NE(a, b); +} + +TEST(HashUtil, CombineIntDifferentSeeds) { + size_t a = hashUtil::hash_combine_int(42, 0); + size_t b = hashUtil::hash_combine_int(42, 1); + EXPECT_NE(a, b); +} + +TEST(HashUtil, CombineStringDeterministic) { + size_t a = hashUtil::hash_combine_string("hello", 0); + size_t b = hashUtil::hash_combine_string("hello", 0); + EXPECT_EQ(a, b); +} + +TEST(HashUtil, CombineStringDifferentValues) { + size_t a = hashUtil::hash_combine_string("hello", 0); + size_t b = hashUtil::hash_combine_string("world", 0); + EXPECT_NE(a, b); +} + +TEST(HashUtil, CombineStringDifferentSeeds) { + size_t a = hashUtil::hash_combine_string("hello", 0); + size_t b = hashUtil::hash_combine_string("hello", 1); + EXPECT_NE(a, b); +} + +TEST(HashUtil, CombineStringEmpty) { + size_t a = hashUtil::hash_combine_string("", 0); + size_t b = hashUtil::hash_combine_string("x", 0); + EXPECT_NE(a, b); +} + +TEST(HashUtil, CombineChaining) { + size_t h1 = hashUtil::hash_combine_int(1, 0); + h1 = hashUtil::hash_combine_int(2, h1); + + size_t h2 = hashUtil::hash_combine_int(1, 0); + h2 = hashUtil::hash_combine_int(2, h2); + + EXPECT_EQ(h1, h2); +} + +TEST(HashUtil, CombineOrderMatters) { + size_t h1 = hashUtil::hash_combine_int(1, 0); + h1 = hashUtil::hash_combine_int(2, h1); + + size_t h2 = hashUtil::hash_combine_int(2, 0); + h2 = hashUtil::hash_combine_int(1, h2); + + EXPECT_NE(h1, h2); +} diff --git a/tests/test_pred.cpp b/tests/test_pred.cpp new file mode 100644 index 0000000..ca70fe3 --- /dev/null +++ b/tests/test_pred.cpp @@ -0,0 +1,462 @@ +#include "pred.h" +#include "predDesc.h" + +#include + +// --- Constants --- + +TEST(Pred, True) { + auto p = Pred::makeTrue(); + EXPECT_EQ(p.getTag(), Pred::PKind::True); +} + +TEST(Pred, False) { + auto p = Pred::makeFalse(); + EXPECT_EQ(p.getTag(), Pred::PKind::False); +} + +// --- Implication --- + +TEST(Pred, Implication) { + auto p = Pred::makeImplication(Pred::makeTrue(), Pred::makeFalse()); + EXPECT_EQ(p.getTag(), Pred::PKind::Implication); + auto &impl = p.toImplication(); + EXPECT_EQ(impl.lhs.getTag(), Pred::PKind::True); + EXPECT_EQ(impl.rhs.getTag(), Pred::PKind::False); +} + +// --- Equivalence --- + +TEST(Pred, Equivalence) { + auto p = Pred::makeEquivalence(Pred::makeTrue(), Pred::makeTrue()); + EXPECT_EQ(p.getTag(), Pred::PKind::Equivalence); +} + +// --- Negation --- + +TEST(Pred, Negation) { + auto p = Pred::makeNegation(Pred::makeTrue()); + EXPECT_EQ(p.getTag(), Pred::PKind::Negation); + auto &neg = p.toNegation(); + EXPECT_EQ(neg.operand.getTag(), Pred::PKind::True); +} + +// --- Conjunction --- + +TEST(Pred, Conjunction) { + std::vector vec; + vec.push_back(Pred::makeTrue()); + vec.push_back(Pred::makeFalse()); + auto p = Pred::makeConjunction(std::move(vec)); + EXPECT_EQ(p.getTag(), Pred::PKind::Conjunction); + auto &conj = p.toConjunction(); + EXPECT_EQ(conj.operands.size(), 2u); +} + +// --- Disjunction --- + +TEST(Pred, Disjunction) { + std::vector vec; + vec.push_back(Pred::makeTrue()); + vec.push_back(Pred::makeFalse()); + auto p = Pred::makeDisjunction(std::move(vec)); + EXPECT_EQ(p.getTag(), Pred::PKind::Disjunction); + auto &disj = p.toDisjunction(); + EXPECT_EQ(disj.operands.size(), 2u); +} + +// --- ExprComparison --- + +TEST(Pred, ExprComparison) { + auto lhs = Expr::makeIdent(VarName::makeVarWithoutSuffix("x"), BType::INT); + auto rhs = Expr::makeInteger("0"); + auto p = Pred::makeExprComparison(Pred::ComparisonOp::Equality, + std::move(lhs), std::move(rhs)); + EXPECT_EQ(p.getTag(), Pred::PKind::ExprComparison); + auto &cmp = p.toExprComparison(); + EXPECT_EQ(cmp.op, Pred::ComparisonOp::Equality); +} + +TEST(Pred, ExprComparisonMembership) { + auto x = Expr::makeIdent(VarName::makeVarWithoutSuffix("x"), BType::INT); + auto s = Expr::makeINTEGER(); + auto p = Pred::makeExprComparison(Pred::ComparisonOp::Membership, + std::move(x), std::move(s)); + EXPECT_EQ(p.getTag(), Pred::PKind::ExprComparison); + EXPECT_EQ(p.toExprComparison().op, Pred::ComparisonOp::Membership); +} + +// --- Forall --- + +TEST(Pred, Forall) { + auto x = VarName::makeVarWithoutSuffix("x"); + std::vector vars = {TypedVar(x, BType::INT)}; + auto body = Pred::makeTrue(); + auto p = Pred::makeForall(vars, std::move(body)); + EXPECT_EQ(p.getTag(), Pred::PKind::Forall); + auto &fa = p.toForall(); + EXPECT_EQ(fa.vars.size(), 1u); +} + +// --- Exists --- + +TEST(Pred, Exists) { + auto x = VarName::makeVarWithoutSuffix("x"); + std::vector vars = {TypedVar(x, BType::INT)}; + auto body = Pred::makeTrue(); + auto p = Pred::makeExists(vars, std::move(body)); + EXPECT_EQ(p.getTag(), Pred::PKind::Exists); + auto &ex = p.toExists(); + EXPECT_EQ(ex.vars.size(), 1u); +} + +// --- GoalTag --- + +TEST(Pred, GoalTag) { + auto p = Pred::makeTrue("myGoal"); + EXPECT_EQ(p.getGoalTag(), "myGoal"); +} + +TEST(Pred, SetGoalTag) { + auto p = Pred::makeTrue(); + p.setGoalTag("updated"); + EXPECT_EQ(p.getGoalTag(), "updated"); +} + +// --- BxmlTag --- + +TEST(Pred, BxmlTag) { + Pred::BXmlTags tags = {"t1", "t2"}; + auto p = Pred::makeTrue("", tags); + EXPECT_EQ(p.getBxmlTag().size(), 2u); + EXPECT_TRUE(p.getBxmlTag().count("t1")); + EXPECT_TRUE(p.getBxmlTag().count("t2")); +} + +// --- Copy --- + +TEST(Pred, CopyTrue) { + auto p = Pred::makeTrue("goal1"); + auto c = p.copy(); + EXPECT_EQ(c.getTag(), Pred::PKind::True); + EXPECT_EQ(c.getGoalTag(), "goal1"); +} + +TEST(Pred, CopyImplication) { + auto p = Pred::makeImplication(Pred::makeTrue(), Pred::makeFalse()); + auto c = p.copy(); + EXPECT_EQ(c.getTag(), Pred::PKind::Implication); + EXPECT_EQ(Pred::compare(p, c), 0); +} + +// --- Comparison --- + +TEST(Pred, CompareEqual) { + auto p1 = Pred::makeTrue(); + auto p2 = Pred::makeTrue(); + EXPECT_EQ(Pred::compare(p1, p2), 0); +} + +TEST(Pred, CompareDifferent) { + auto p1 = Pred::makeTrue(); + auto p2 = Pred::makeFalse(); + EXPECT_NE(Pred::compare(p1, p2), 0); +} + +TEST(Pred, CompareImplication) { + auto p1 = Pred::makeImplication(Pred::makeTrue(), Pred::makeFalse()); + auto p2 = Pred::makeImplication(Pred::makeTrue(), Pred::makeFalse()); + EXPECT_EQ(Pred::compare(p1, p2), 0); +} + +TEST(Pred, VecCompareEqual) { + std::vector v1; + v1.push_back(Pred::makeTrue()); + v1.push_back(Pred::makeFalse()); + std::vector v2; + v2.push_back(Pred::makeTrue()); + v2.push_back(Pred::makeFalse()); + EXPECT_EQ(Pred::vec_compare(v1, v2), 0); +} + +TEST(Pred, VecCompareDifferentSize) { + std::vector v1; + v1.push_back(Pred::makeTrue()); + std::vector v2; + v2.push_back(Pred::makeTrue()); + v2.push_back(Pred::makeFalse()); + EXPECT_LT(Pred::vec_compare(v1, v2), 0); +} + +// --- Hashing --- + +TEST(Pred, HashConsistency) { + auto p1 = Pred::makeTrue(); + auto p2 = Pred::makeTrue(); + EXPECT_EQ(p1.hash_combine(0), p2.hash_combine(0)); +} + +TEST(Pred, HashEqualImpliesSameHash) { + auto p1 = Pred::makeImplication(Pred::makeTrue(), Pred::makeFalse()); + auto p2 = Pred::makeImplication(Pred::makeTrue(), Pred::makeFalse()); + EXPECT_EQ(Pred::compare(p1, p2), 0); + EXPECT_EQ(p1.hash_combine(0), p2.hash_combine(0)); +} + +TEST(Pred, StdHash) { + auto p1 = Pred::makeTrue(); + auto p2 = Pred::makeTrue(); + std::hash h; + EXPECT_EQ(h(p1), h(p2)); +} + +// --- Free variables --- + +TEST(Pred, FreeVarsTrue) { + auto p = Pred::makeTrue(); + EXPECT_TRUE(p.getFreeVars().empty()); +} + +TEST(Pred, FreeVarsComparison) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto p = Pred::makeExprComparison( + Pred::ComparisonOp::Equality, Expr::makeIdent(x, BType::INT), + Expr::makeInteger("0")); + auto fv = p.getFreeVars(); + EXPECT_EQ(fv.size(), 1u); + EXPECT_TRUE(fv.count(x)); +} + +TEST(Pred, FreeVarsForallBound) { + auto x = VarName::makeVarWithoutSuffix("x"); + std::vector vars = {TypedVar(x, BType::INT)}; + auto body = Pred::makeExprComparison( + Pred::ComparisonOp::Igt, Expr::makeIdent(x, BType::INT), + Expr::makeInteger("0")); + auto p = Pred::makeForall(vars, std::move(body)); + auto fv = p.getFreeVars(); + EXPECT_TRUE(fv.empty()); +} + +TEST(Pred, FreeVarsForallMixed) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto y = VarName::makeVarWithoutSuffix("y"); + std::vector vars = {TypedVar(x, BType::INT)}; + auto body = Pred::makeExprComparison( + Pred::ComparisonOp::Equality, Expr::makeIdent(x, BType::INT), + Expr::makeIdent(y, BType::INT)); + auto p = Pred::makeForall(vars, std::move(body)); + auto fv = p.getFreeVars(); + EXPECT_EQ(fv.size(), 1u); + EXPECT_TRUE(fv.count(y)); +} + +// --- getAllVars --- + +TEST(Pred, GetAllVarsForall) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto y = VarName::makeVarWithoutSuffix("y"); + std::vector vars = {TypedVar(x, BType::INT)}; + auto body = Pred::makeExprComparison( + Pred::ComparisonOp::Equality, Expr::makeIdent(x, BType::INT), + Expr::makeIdent(y, BType::INT)); + auto p = Pred::makeForall(vars, std::move(body)); + auto av = p.getAllVars(); + EXPECT_EQ(av.size(), 2u); + EXPECT_TRUE(av.count(x)); + EXPECT_TRUE(av.count(y)); +} + +// --- getFreeTVars --- + +TEST(Pred, GetFreeTVars) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto p = Pred::makeExprComparison( + Pred::ComparisonOp::Equality, Expr::makeIdent(x, BType::INT), + Expr::makeInteger("0")); + auto ftv = p.getFreeTVars(); + EXPECT_EQ(ftv.size(), 1u); + auto it = ftv.begin(); + EXPECT_EQ(it->name, x); + EXPECT_EQ(it->type, BType::INT); +} + +// --- Substitution --- + +TEST(Pred, SubstComparison) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto p = Pred::makeExprComparison( + Pred::ComparisonOp::Equality, Expr::makeIdent(x, BType::INT), + Expr::makeInteger("0")); + std::map map; + map.insert({x, Expr::makeInteger("5")}); + p.subst(map); + auto &cmp = p.toExprComparison(); + EXPECT_EQ(cmp.lhs.getTag(), Expr::EKind::IntegerLiteral); + EXPECT_EQ(cmp.lhs.getIntegerLiteral(), "5"); +} + +// --- Alpha renaming --- + +TEST(Pred, AlphaRename) { + auto x = VarName::makeVarWithoutSuffix("x"); + auto y = VarName::makeVarWithoutSuffix("y"); + auto p = Pred::makeExprComparison( + Pred::ComparisonOp::Equality, Expr::makeIdent(x, BType::INT), + Expr::makeInteger("0")); + std::map map; + map.insert({x, y}); + p.alpha(map); + auto &cmp = p.toExprComparison(); + EXPECT_EQ(cmp.lhs.getId(), y); +} + +// --- Alpha equality --- + +TEST(Pred, AlphaEqualsTrue) { + EXPECT_TRUE(Pred::alpha_equals(Pred::makeTrue(), Pred::makeTrue())); +} + +TEST(Pred, AlphaEqualsDifferent) { + EXPECT_FALSE(Pred::alpha_equals(Pred::makeTrue(), Pred::makeFalse())); +} + +TEST(Pred, AlphaEqualsForall) { + // forall x . x > 0 vs forall x$1 . x$1 > 0 (same prefix, diff suffix) + auto x = VarName::makeVarWithoutSuffix("x"); + auto x1 = VarName::makeVar("x", 1); + std::vector v1 = {TypedVar(x, BType::INT)}; + std::vector v2 = {TypedVar(x1, BType::INT)}; + auto p1 = Pred::makeForall( + v1, Pred::makeExprComparison(Pred::ComparisonOp::Igt, + Expr::makeIdent(x, BType::INT), + Expr::makeInteger("0"))); + auto p2 = Pred::makeForall( + v2, Pred::makeExprComparison(Pred::ComparisonOp::Igt, + Expr::makeIdent(x1, BType::INT), + Expr::makeInteger("0"))); + EXPECT_TRUE(Pred::alpha_equals(p1, p2)); +} + +TEST(Pred, AlphaNotEqualsForallDifferentPrefix) { + // forall x . x > 0 vs forall y . y > 0 (different prefix -> not equal) + auto x = VarName::makeVarWithoutSuffix("x"); + auto y = VarName::makeVarWithoutSuffix("y"); + std::vector v1 = {TypedVar(x, BType::INT)}; + std::vector v2 = {TypedVar(y, BType::INT)}; + auto p1 = Pred::makeForall( + v1, Pred::makeExprComparison(Pred::ComparisonOp::Igt, + Expr::makeIdent(x, BType::INT), + Expr::makeInteger("0"))); + auto p2 = Pred::makeForall( + v2, Pred::makeExprComparison(Pred::ComparisonOp::Igt, + Expr::makeIdent(y, BType::INT), + Expr::makeInteger("0"))); + EXPECT_FALSE(Pred::alpha_equals(p1, p2)); +} + +// --- isPureTypingPredicate --- + +TEST(Pred, IsPureTypingMembership) { + // x : INTEGER is a pure typing predicate + auto x = Expr::makeIdent(VarName::makeVarWithoutSuffix("x"), BType::INT); + auto p = Pred::makeExprComparison(Pred::ComparisonOp::Membership, + std::move(x), Expr::makeINTEGER()); + EXPECT_TRUE(p.isPureTypingPredicate()); +} + +TEST(Pred, IsNotPureTypingEquality) { + // x = 3 is not a pure typing predicate + auto x = Expr::makeIdent(VarName::makeVarWithoutSuffix("x"), BType::INT); + auto p = Pred::makeExprComparison(Pred::ComparisonOp::Equality, + std::move(x), Expr::makeInteger("3")); + EXPECT_FALSE(p.isPureTypingPredicate()); +} + +TEST(Pred, IsNotPureTypingTrue) { + auto p = Pred::makeTrue(); + EXPECT_FALSE(p.isPureTypingPredicate()); +} + +// --- Visitor --- + +class TestPredVisitor : public Pred::Visitor { + public: + Pred::PKind visited = Pred::PKind::True; + void visitImplication(const Pred &, const Pred &) override { + visited = Pred::PKind::Implication; + } + void visitEquivalence(const Pred &, const Pred &) override { + visited = Pred::PKind::Equivalence; + } + void visitExprComparison(Pred::ComparisonOp, const Expr &, + const Expr &) override { + visited = Pred::PKind::ExprComparison; + } + void visitNegation(const Pred &) override { + visited = Pred::PKind::Negation; + } + void visitConjunction(const std::vector &) override { + visited = Pred::PKind::Conjunction; + } + void visitDisjunction(const std::vector &) override { + visited = Pred::PKind::Disjunction; + } + void visitForall(const std::vector &, const Pred &) override { + visited = Pred::PKind::Forall; + } + void visitExists(const std::vector &, const Pred &) override { + visited = Pred::PKind::Exists; + } + void visitTrue() override { visited = Pred::PKind::True; } + void visitFalse() override { visited = Pred::PKind::False; } +}; + +TEST(Pred, VisitorTrue) { + TestPredVisitor v; + Pred::makeTrue().accept(v); + EXPECT_EQ(v.visited, Pred::PKind::True); +} + +TEST(Pred, VisitorImplication) { + TestPredVisitor v; + Pred::makeImplication(Pred::makeTrue(), Pred::makeFalse()).accept(v); + EXPECT_EQ(v.visited, Pred::PKind::Implication); +} + +TEST(Pred, VisitorNegation) { + TestPredVisitor v; + Pred::makeNegation(Pred::makeTrue()).accept(v); + EXPECT_EQ(v.visited, Pred::PKind::Negation); +} + +TEST(Pred, VisitorConjunction) { + TestPredVisitor v; + std::vector vec; + vec.push_back(Pred::makeTrue()); + Pred::makeConjunction(std::move(vec)).accept(v); + EXPECT_EQ(v.visited, Pred::PKind::Conjunction); +} + +TEST(Pred, VisitorExprComparison) { + TestPredVisitor v; + Pred::makeExprComparison(Pred::ComparisonOp::Equality, + Expr::makeInteger("1"), Expr::makeInteger("1")) + .accept(v); + EXPECT_EQ(v.visited, Pred::PKind::ExprComparison); +} + +TEST(Pred, VisitorForall) { + TestPredVisitor v; + std::vector vars = { + TypedVar(VarName::makeVarWithoutSuffix("x"), BType::INT)}; + Pred::makeForall(vars, Pred::makeTrue()).accept(v); + EXPECT_EQ(v.visited, Pred::PKind::Forall); +} + +// --- ComparisonOp to_string --- + +TEST(Pred, ComparisonOpToString) { + EXPECT_FALSE(Pred::to_string(Pred::ComparisonOp::Equality).empty()); + EXPECT_FALSE(Pred::to_string(Pred::ComparisonOp::Membership).empty()); +} diff --git a/tests/test_readBType.cpp b/tests/test_readBType.cpp new file mode 100644 index 0000000..a1822bf --- /dev/null +++ b/tests/test_readBType.cpp @@ -0,0 +1,498 @@ +#include + +#include "btypeReader.h" +#include "tinyxml2.h" + +// ============================================================ +// readTypeInfos tests +// ============================================================ + +TEST(ReadTypeInfos, NullDomReturnsEmpty) { + std::vector ti; + Xml::readTypeInfos(nullptr, ti); + EXPECT_TRUE(ti.empty()); +} + +TEST(ReadTypeInfos, PrimitiveINTEGER) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::INT); +} + +TEST(ReadTypeInfos, PrimitiveFLOAT) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::FLOAT); +} + +TEST(ReadTypeInfos, PrimitiveREAL) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::REAL); +} + +TEST(ReadTypeInfos, PrimitiveSTRING) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::STRING); +} + +TEST(ReadTypeInfos, PrimitiveBOOL) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::BOOL); +} + +TEST(ReadTypeInfos, MultiplePrimitives) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 3u); + EXPECT_EQ(ti[0], BType::INT); + EXPECT_EQ(ti[1], BType::BOOL); + EXPECT_EQ(ti[2], BType::STRING); +} + +TEST(ReadTypeInfos, PowerType) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::POW(BType::INT)); +} + +TEST(ReadTypeInfos, ProductType) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::PROD(BType::INT, BType::BOOL)); +} + +TEST(ReadTypeInfos, StructType) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::STRUCT({{"a", BType::INT}, {"b", BType::BOOL}})); +} + +TEST(ReadTypeInfos, AbstractSetFromSets) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::ABSTRACT_SET("COLOR")); +} + +TEST(ReadTypeInfos, EnumeratedSetFromSets) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], + BType::ENUMERATED_SET({"COLOR", {"red", "green", "blue"}})); +} + +TEST(ReadTypeInfos, SetFromDefine) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::ABSTRACT_SET("STATUS")); +} + +TEST(ReadTypeInfos, NestedPowerProduct) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + Xml::readTypeInfos(tiElem, ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::POW(BType::PROD(BType::INT, BType::INT))); +} + +// --- Error cases --- + +TEST(ReadTypeInfos, MissingIdAttributeThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + EXPECT_THROW(Xml::readTypeInfos(tiElem, ti), Xml::BTypeReaderException); +} + +TEST(ReadTypeInfos, NonSequentialIdThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + EXPECT_THROW(Xml::readTypeInfos(tiElem, ti), Xml::BTypeReaderException); +} + +TEST(ReadTypeInfos, UnknownSetNameThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + EXPECT_THROW(Xml::readTypeInfos(tiElem, ti), Xml::BTypeReaderException); +} + +TEST(ReadTypeInfos, UnexpectedTagThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto *tiElem = doc.RootElement()->FirstChildElement("TypeInfos"); + std::vector ti; + EXPECT_THROW(Xml::readTypeInfos(tiElem, ti), Xml::BTypeReaderException); +} + +// ============================================================ +// readRichTypesInfo tests +// ============================================================ + +TEST(ReadRichTypesInfo, NullDomReturnsEmpty) { + std::vector ti; + Xml::readRichTypesInfo(nullptr, ti); + EXPECT_TRUE(ti.empty()); +} + +TEST(ReadRichTypesInfo, PrimitiveINTEGER) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + Xml::readRichTypesInfo(doc.RootElement(), ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::INT); +} + +TEST(ReadRichTypesInfo, PrimitiveBOOL) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + Xml::readRichTypesInfo(doc.RootElement(), ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::BOOL); +} + +TEST(ReadRichTypesInfo, PrimitiveREAL) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + Xml::readRichTypesInfo(doc.RootElement(), ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::REAL); +} + +TEST(ReadRichTypesInfo, PrimitiveFLOAT) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + Xml::readRichTypesInfo(doc.RootElement(), ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::FLOAT); +} + +TEST(ReadRichTypesInfo, PrimitiveSTRING) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + Xml::readRichTypesInfo(doc.RootElement(), ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::STRING); +} + +TEST(ReadRichTypesInfo, AbstractSet) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + Xml::readRichTypesInfo(doc.RootElement(), ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], BType::ABSTRACT_SET("COLOR")); +} + +TEST(ReadRichTypesInfo, EnumeratedSet) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + )"); + std::vector ti; + Xml::readRichTypesInfo(doc.RootElement(), ti); + ASSERT_EQ(ti.size(), 1u); + EXPECT_EQ(ti[0], + BType::ENUMERATED_SET({"COLOR", {"red", "green", "blue"}})); +} + +TEST(ReadRichTypesInfo, PowerSetRefPrevious) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + std::vector ti; + Xml::readRichTypesInfo(doc.RootElement(), ti); + ASSERT_EQ(ti.size(), 2u); + EXPECT_EQ(ti[0], BType::INT); + EXPECT_EQ(ti[1], BType::POW(BType::INT)); +} + +TEST(ReadRichTypesInfo, MultipleMixed) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + )"); + std::vector ti; + Xml::readRichTypesInfo(doc.RootElement(), ti); + ASSERT_EQ(ti.size(), 4u); + EXPECT_EQ(ti[0], BType::INT); + EXPECT_EQ(ti[1], BType::BOOL); + EXPECT_EQ(ti[2], BType::ABSTRACT_SET("S")); + EXPECT_EQ(ti[3], BType::POW(BType::INT)); +} + +// --- Error cases --- + +TEST(ReadRichTypesInfo, MissingIdAttributeThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + EXPECT_THROW(Xml::readRichTypesInfo(doc.RootElement(), ti), + Xml::BTypeReaderException); +} + +TEST(ReadRichTypesInfo, NonSequentialIdThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + EXPECT_THROW(Xml::readRichTypesInfo(doc.RootElement(), ti), + Xml::BTypeReaderException); +} + +TEST(ReadRichTypesInfo, UnexpectedTagThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + EXPECT_THROW(Xml::readRichTypesInfo(doc.RootElement(), ti), + Xml::BTypeReaderException); +} + +TEST(ReadRichTypesInfo, AbstractSetMissingIdThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + EXPECT_THROW(Xml::readRichTypesInfo(doc.RootElement(), ti), + Xml::BTypeReaderException); +} + +TEST(ReadRichTypesInfo, EnumeratedSetMissingIdThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + EXPECT_THROW(Xml::readRichTypesInfo(doc.RootElement(), ti), + Xml::BTypeReaderException); +} + +TEST(ReadRichTypesInfo, EnumeratedValueMissingIdThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + )"); + std::vector ti; + EXPECT_THROW(Xml::readRichTypesInfo(doc.RootElement(), ti), + Xml::BTypeReaderException); +} + +TEST(ReadRichTypesInfo, PowerSetMissingArgThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + EXPECT_THROW(Xml::readRichTypesInfo(doc.RootElement(), ti), + Xml::BTypeReaderException); +} + +TEST(ReadRichTypesInfo, PowerSetOutOfRangeArgThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + std::vector ti; + EXPECT_THROW(Xml::readRichTypesInfo(doc.RootElement(), ti), + Xml::BTypeReaderException); +} diff --git a/tests/test_readExpr.cpp b/tests/test_readExpr.cpp new file mode 100644 index 0000000..f2896f7 --- /dev/null +++ b/tests/test_readExpr.cpp @@ -0,0 +1,641 @@ +#include + +#include "exprReader.h" +#include "exprDesc.h" +#include "tinyxml2.h" + +// typeInfos: [0]=INT, [1]=POW_INT, [2]=BOOL, [3]=POW_BOOL, [4]=STRING, +// [5]=REAL, [6]=POW(PROD(INT,INT)), [7]=STRUCT({a:INT,b:INT}) +static const auto structAB = + BType::STRUCT({{"a", BType::INT}, {"b", BType::INT}}); +static const std::vector TI = { + BType::INT, // 0 + BType::POW_INT, // 1 + BType::BOOL, // 2 + BType::POW_BOOL, // 3 + BType::STRING, // 4 + BType::REAL, // 5 + BType::POW(BType::PROD(BType::INT, BType::INT)), // 6 + structAB, // 7 +}; + +// --- Literals --- + +TEST(ReadExpr, IntegerLiteral) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::IntegerLiteral); + EXPECT_EQ(e.getIntegerLiteral(), "42"); +} + +TEST(ReadExpr, StringLiteral) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::StringLiteral); + EXPECT_EQ(e.getStringLiteral(), "hello"); +} + +TEST(ReadExpr, STRING_Literal) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::StringLiteral); + EXPECT_EQ(e.getStringLiteral(), "world"); +} + +TEST(ReadExpr, RealLiteralWithDecimal) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::RealLiteral); + EXPECT_EQ(e.getRealLiteral().integerPart, "3"); + EXPECT_EQ(e.getRealLiteral().fractionalPart, "14"); +} + +TEST(ReadExpr, RealLiteralIntegerOnly) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::RealLiteral); + EXPECT_EQ(e.getRealLiteral().integerPart, "5"); +} + +TEST(ReadExpr, BooleanLiteralTrue) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::TRUE); +} + +TEST(ReadExpr, BooleanLiteralFalse) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::FALSE); +} + +TEST(ReadExpr, BooleanLiteralCaseInsensitive) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::TRUE); +} + +// --- Identifiers --- + +TEST(ReadExpr, IdPlain) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Id); + EXPECT_EQ(e.getId(), VarName::makeVarWithoutSuffix("x")); + EXPECT_EQ(e.getType(), BType::INT); +} + +TEST(ReadExpr, IdWithSuffix) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Id); + EXPECT_EQ(e.getId(), VarName::makeVar("x", 1)); +} + +TEST(ReadExpr, IdWithSuffix0) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Id); + EXPECT_EQ(e.getId(), VarName::makeVarWithoutSuffix("x")); +} + +TEST(ReadExpr, FreshId) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Id); + EXPECT_EQ(e.getId(), VarName::makeFreshId("f1")); +} + +TEST(ReadExpr, IdWithRichtypref) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Id); + EXPECT_EQ(e.getType(), BType::INT); +} + +// --- Constants --- + +TEST(ReadExpr, ConstMAXINT) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::MaxInt); +} + +TEST(ReadExpr, ConstMININT) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::MinInt); +} + +TEST(ReadExpr, ConstINTEGER) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::INTEGER); +} + +TEST(ReadExpr, ConstNATURAL) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::NATURAL); +} + +TEST(ReadExpr, ConstNATURAL1) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::NATURAL1); +} + +TEST(ReadExpr, ConstINT) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::INT); +} + +TEST(ReadExpr, ConstNAT) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::NAT); +} + +TEST(ReadExpr, ConstNAT1) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::NAT1); +} + +TEST(ReadExpr, ConstSTRING) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::STRING); +} + +TEST(ReadExpr, ConstBOOL) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::BOOL); +} + +TEST(ReadExpr, ConstREAL) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::REAL); +} + +TEST(ReadExpr, ConstFLOAT) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::FLOAT); +} + +TEST(ReadExpr, ConstTRUE) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::TRUE); +} + +TEST(ReadExpr, ConstFALSE) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::FALSE); +} + +TEST(ReadExpr, ConstSuccessor) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Successor); +} + +TEST(ReadExpr, ConstPredecessor) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Predecessor); +} + +// --- EmptySet / EmptySeq --- + +TEST(ReadExpr, EmptySet) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::EmptySet); + EXPECT_EQ(e.getType(), TI[1]); +} + +TEST(ReadExpr, EmptySeq) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::EmptySet); +} + +// --- Binary expressions --- + +TEST(ReadExpr, BinaryExpAddition) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::BinaryExpr); + EXPECT_EQ(e.getType(), BType::INT); + auto &bin = e.toBinaryExpr(); + EXPECT_EQ(bin.op, Expr::BinaryOp::IAddition); + EXPECT_EQ(bin.lhs.getIntegerLiteral(), "1"); + EXPECT_EQ(bin.rhs.getIntegerLiteral(), "2"); +} + +TEST(ReadExpr, BinaryExpMapplet) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.toBinaryExpr().op, Expr::BinaryOp::Mapplet); +} + +TEST(ReadExpr, BinaryExpInterval) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.toBinaryExpr().op, Expr::BinaryOp::Interval); +} + +// --- Unary expressions --- + +TEST(ReadExpr, UnaryExpCard) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::UnaryExpr); + EXPECT_EQ(e.toUnaryExpr().op, Expr::UnaryOp::Cardinality); +} + +TEST(ReadExpr, UnaryExpDom) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.toUnaryExpr().op, Expr::UnaryOp::Domain); +} + +TEST(ReadExpr, UnaryExpSucc) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + // succ is rewritten as Application(successor, arg) + EXPECT_EQ(e.getTag(), Expr::EKind::BinaryExpr); + EXPECT_EQ(e.toBinaryExpr().op, Expr::BinaryOp::Application); + EXPECT_EQ(e.toBinaryExpr().lhs.getTag(), Expr::EKind::Successor); +} + +TEST(ReadExpr, UnaryExpPred) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::BinaryExpr); + EXPECT_EQ(e.toBinaryExpr().op, Expr::BinaryOp::Application); + EXPECT_EQ(e.toBinaryExpr().lhs.getTag(), Expr::EKind::Predecessor); +} + +// --- Nary expressions --- + +TEST(ReadExpr, NaryExpSet) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::NaryExpr); + EXPECT_EQ(e.toNaryExpr().op, Expr::NaryOp::Set); + EXPECT_EQ(e.toNaryExpr().vec.size(), 3u); +} + +TEST(ReadExpr, NaryExpSequence) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.toNaryExpr().op, Expr::NaryOp::Sequence); +} + +// --- Ternary expressions --- + +TEST(ReadExpr, TernaryExpSon) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::TernaryExpr); + EXPECT_EQ(e.toTernaryExpr().op, Expr::TernaryOp::Son); +} + +TEST(ReadExpr, TernaryExpBin) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.toTernaryExpr().op, Expr::TernaryOp::Bin); +} + +// --- Quantified expression --- + +TEST(ReadExpr, QuantifiedExprLambda) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::QuantifiedExpr); +} + +TEST(ReadExpr, QuantifiedExprISigma) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::QuantifiedExpr); +} + +// --- Quantified set --- + +TEST(ReadExpr, QuantifiedSet) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::QuantifiedSet); +} + +// --- Boolean expression --- + +TEST(ReadExpr, BooleanExp) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::BooleanExpr); +} + +// --- Struct / Record --- + +TEST(ReadExpr, Struct) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Struct); +} + +TEST(ReadExpr, Record) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Record); +} + +// --- Record_Field_Access --- + +TEST(ReadExpr, RecordFieldAccess) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Record_Field_Access); +} + +// --- Record_Update --- + +TEST(ReadExpr, RecordFieldUpdate) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_EQ(e.getTag(), Expr::EKind::Record_Field_Update); +} + +// --- Tag attribute --- + +TEST(ReadExpr, TagAttribute) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + ASSERT_EQ(e.getBxmlTag().size(), 1u); + EXPECT_EQ(e.getBxmlTag()[0], "foo"); +} + +TEST(ReadExpr, EmptyTagIgnored) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto e = Xml::readExpression(doc.RootElement(), TI); + EXPECT_TRUE(e.getBxmlTag().empty()); +} + +// --- Error cases --- + +TEST(ReadExpr, NullDomThrows) { + EXPECT_THROW(Xml::readExpression(nullptr, TI), Xml::ExprReaderException); +} + +TEST(ReadExpr, UnknownTagThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + EXPECT_THROW(Xml::readExpression(doc.RootElement(), TI), + Xml::ExprReaderException); +} + +TEST(ReadExpr, MissingTyprefThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + EXPECT_THROW(Xml::readExpression(doc.RootElement(), TI), + Xml::ExprReaderException); +} + +TEST(ReadExpr, OutOfRangeTyprefThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + EXPECT_THROW(Xml::readExpression(doc.RootElement(), TI), + Xml::ExprReaderException); +} + +TEST(ReadExpr, UnknownBinaryOpThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + EXPECT_THROW(Xml::readExpression(doc.RootElement(), TI), + Xml::ExprReaderException); +} + +TEST(ReadExpr, UnknownUnaryOpThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + EXPECT_THROW(Xml::readExpression(doc.RootElement(), TI), + Xml::ExprReaderException); +} + +TEST(ReadExpr, UnknownNaryOpThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + EXPECT_THROW(Xml::readExpression(doc.RootElement(), TI), + Xml::ExprReaderException); +} + +TEST(ReadExpr, UnknownTernaryOpThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + EXPECT_THROW(Xml::readExpression(doc.RootElement(), TI), + Xml::ExprReaderException); +} + +TEST(ReadExpr, UnknownQuantifiedExprTypeThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + EXPECT_THROW(Xml::readExpression(doc.RootElement(), TI), + Xml::ExprReaderException); +} + +TEST(ReadExpr, UnknownBooleanLiteralThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + EXPECT_THROW(Xml::readExpression(doc.RootElement(), TI), + Xml::ExprReaderException); +} + +// --- VarNameFromId --- + +TEST(ReadExpr, VarNameFromIdPlain) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto tv = Xml::VarNameFromId(doc.RootElement(), TI); + EXPECT_EQ(tv.name, VarName::makeVarWithoutSuffix("x")); + EXPECT_EQ(tv.type, BType::INT); +} + +TEST(ReadExpr, VarNameFromIdFresh) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto tv = Xml::VarNameFromId(doc.RootElement(), TI); + EXPECT_EQ(tv.name, VarName::makeFreshId("f1")); +} + +TEST(ReadExpr, VarNameFromIdBadTagThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + EXPECT_THROW(Xml::VarNameFromId(doc.RootElement(), TI), + Xml::ExprReaderException); +} diff --git a/tests/test_readPred.cpp b/tests/test_readPred.cpp new file mode 100644 index 0000000..42f69f7 --- /dev/null +++ b/tests/test_readPred.cpp @@ -0,0 +1,379 @@ +#include + +#include "predReader.h" +#include "predDesc.h" +#include "tinyxml2.h" + +// typeInfos: [0]=INT, [1]=POW_INT +static const std::vector TI = { + BType::INT, // 0 + BType::POW_INT, // 1 +}; + +// --- Exp_Comparison --- + +TEST(ReadPred, ComparisonEquality) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::ExprComparison); + EXPECT_EQ(p.toExprComparison().op, Pred::ComparisonOp::Equality); +} + +TEST(ReadPred, ComparisonMembership) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.toExprComparison().op, Pred::ComparisonOp::Membership); +} + +TEST(ReadPred, ComparisonSubset) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.toExprComparison().op, Pred::ComparisonOp::Subset); +} + +TEST(ReadPred, ComparisonStrictSubset) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.toExprComparison().op, Pred::ComparisonOp::Strict_Subset); +} + +TEST(ReadPred, ComparisonIgt) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.toExprComparison().op, Pred::ComparisonOp::Igt); +} + +TEST(ReadPred, ComparisonIlt) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.toExprComparison().op, Pred::ComparisonOp::Ilt); +} + +TEST(ReadPred, ComparisonIge) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.toExprComparison().op, Pred::ComparisonOp::Ige); +} + +TEST(ReadPred, ComparisonIle) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.toExprComparison().op, Pred::ComparisonOp::Ile); +} + +// --- Negated comparisons --- + +TEST(ReadPred, NegatedMembership) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Negation); + auto &inner = p.toNegation().operand; + EXPECT_EQ(inner.getTag(), Pred::PKind::ExprComparison); + EXPECT_EQ(inner.toExprComparison().op, Pred::ComparisonOp::Membership); +} + +TEST(ReadPred, NegatedSubset) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Negation); + EXPECT_EQ(p.toNegation().operand.toExprComparison().op, + Pred::ComparisonOp::Subset); +} + +TEST(ReadPred, NegatedStrictSubset) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Negation); + EXPECT_EQ(p.toNegation().operand.toExprComparison().op, + Pred::ComparisonOp::Strict_Subset); +} + +TEST(ReadPred, NegatedEquality) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Negation); + EXPECT_EQ(p.toNegation().operand.toExprComparison().op, + Pred::ComparisonOp::Equality); +} + +// --- Binary predicates --- + +TEST(ReadPred, Implication) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Implication); +} + +TEST(ReadPred, Equivalence) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Equivalence); +} + +// --- Unary predicate --- + +TEST(ReadPred, Negation) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Negation); +} + +// --- Nary predicates --- + +TEST(ReadPred, Conjunction) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Conjunction); + EXPECT_EQ(p.toConjunction().operands.size(), 2u); +} + +TEST(ReadPred, Disjunction) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Disjunction); + EXPECT_EQ(p.toDisjunction().operands.size(), 2u); +} + +// --- Quantified predicates --- + +TEST(ReadPred, Forall) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Forall); + EXPECT_EQ(p.toForall().vars.size(), 1u); +} + +TEST(ReadPred, Exists) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Exists); + EXPECT_EQ(p.toExists().vars.size(), 1u); +} + +TEST(ReadPred, ForallMultipleVars) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::Forall); + EXPECT_EQ(p.toForall().vars.size(), 2u); +} + +// --- Tag wrapper --- + +TEST(ReadPred, TagWrapper) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + )"); + auto p = Xml::readPredicate(doc.RootElement(), TI); + EXPECT_EQ(p.getTag(), Pred::PKind::ExprComparison); + EXPECT_EQ(p.toExprComparison().op, Pred::ComparisonOp::Equality); +} + +// --- Error cases --- + +TEST(ReadPred, NullDomThrows) { + EXPECT_THROW(Xml::readPredicate(nullptr, TI), Xml::PredReaderException); +} + +TEST(ReadPred, UnknownTagThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + EXPECT_THROW(Xml::readPredicate(doc.RootElement(), TI), + Xml::PredReaderException); +} + +TEST(ReadPred, UnknownBinaryPredOpThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + EXPECT_THROW(Xml::readPredicate(doc.RootElement(), TI), + Xml::PredReaderException); +} + +TEST(ReadPred, UnknownComparisonOpThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + EXPECT_THROW(Xml::readPredicate(doc.RootElement(), TI), + Xml::PredReaderException); +} + +TEST(ReadPred, UnknownUnaryPredOpThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + )"); + EXPECT_THROW(Xml::readPredicate(doc.RootElement(), TI), + Xml::PredReaderException); +} + +TEST(ReadPred, UnknownNaryPredOpThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + )"); + EXPECT_THROW(Xml::readPredicate(doc.RootElement(), TI), + Xml::PredReaderException); +} + +TEST(ReadPred, UnknownQuantifiedPredTypeThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + )"); + EXPECT_THROW(Xml::readPredicate(doc.RootElement(), TI), + Xml::PredReaderException); +} diff --git a/tests/test_readSubst.cpp b/tests/test_readSubst.cpp new file mode 100644 index 0000000..137179f --- /dev/null +++ b/tests/test_readSubst.cpp @@ -0,0 +1,600 @@ +#include + +#include "substReader.h" +#include "tinyxml2.h" + +// typeInfos: [0]=INT, [1]=POW_INT +static const std::vector TI = {BType::INT, BType::POW_INT}; + +// ============================================================ +// Skip / Block +// ============================================================ + +TEST(ReadSubst, Skip) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Skip); +} + +TEST(ReadSubst, Block) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Block); + EXPECT_EQ(s.toBlock().getTag(), Subst::SKind::Skip); +} + +// ============================================================ +// Assert / PRE +// ============================================================ + +TEST(ReadSubst, AssertSub) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Assert); + EXPECT_EQ(s.toAssert().condition.getTag(), Pred::PKind::ExprComparison); + EXPECT_EQ(s.toAssert().content.getTag(), Subst::SKind::Skip); +} + +TEST(ReadSubst, PreSub) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Assert); +} + +// ============================================================ +// IfThen / IfThenElse +// ============================================================ + +TEST(ReadSubst, IfThen) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::IfThen); +} + +TEST(ReadSubst, IfThenElse) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::IfThenElse); + EXPECT_EQ(s.toIfThenElse().s_if.getTag(), Subst::SKind::Skip); + EXPECT_EQ(s.toIfThenElse().s_else.getTag(), Subst::SKind::Skip); +} + +// ============================================================ +// SimpleAssignment +// ============================================================ + +TEST(ReadSubst, SimpleAssignment) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::SimpleAssignment); + auto &sa = s.toSimpleAssignment(); + ASSERT_EQ(sa.vars.size(), 1u); + EXPECT_EQ(sa.vars[0].name, VarName::makeVarWithoutSuffix("x")); + ASSERT_EQ(sa.exprs.size(), 1u); + EXPECT_EQ(sa.exprs[0].getTag(), Expr::EKind::IntegerLiteral); +} + +TEST(ReadSubst, SimpleAssignmentMultiple) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.toSimpleAssignment().vars.size(), 2u); + EXPECT_EQ(s.toSimpleAssignment().exprs.size(), 2u); +} + +// ============================================================ +// Select / SelectElse +// ============================================================ + +TEST(ReadSubst, Select) { + tinyxml2::XMLDocument doc; + doc.Parse(R"()"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Select); + EXPECT_EQ(s.toSelect().clauses.size(), 1u); +} + +TEST(ReadSubst, SelectElse) { + tinyxml2::XMLDocument doc; + doc.Parse(R"()"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::SelectElse); +} + +// ============================================================ +// Case / CaseElse +// ============================================================ + +TEST(ReadSubst, Case) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Case); + EXPECT_EQ(s.toCase().m_cases.size(), 2u); +} + +TEST(ReadSubst, CaseElse) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::CaseElse); +} + +// ============================================================ +// Any +// ============================================================ + +TEST(ReadSubst, Any) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Any); + EXPECT_EQ(s.toAny().vars.size(), 1u); + EXPECT_EQ(s.toAny().body.getTag(), Subst::SKind::Skip); +} + +// ============================================================ +// While +// ============================================================ + +TEST(ReadSubst, While) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::While); + auto &w = s.toWhile(); + EXPECT_EQ(w.body.getTag(), Subst::SKind::SimpleAssignment); +} + +// ============================================================ +// Nary_Sub: Sequence, Parallel, Choice +// ============================================================ + +TEST(ReadSubst, Sequence) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Sequence); + EXPECT_EQ(s.toSequence().size(), 2u); +} + +TEST(ReadSubst, Parallel) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Parallel); + EXPECT_EQ(s.toParallel().size(), 3u); +} + +TEST(ReadSubst, Choice) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Choice); + EXPECT_EQ(s.toChoice().size(), 2u); +} + +// ============================================================ +// Witness +// ============================================================ + +TEST(ReadSubst, WitnessSingle) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Witness); + auto &wt = s.toWitness(); + EXPECT_EQ(wt.witnesses.size(), 1u); + EXPECT_TRUE(wt.witnesses.count("w")); + EXPECT_EQ(wt.body.getTag(), Subst::SKind::Skip); +} + +TEST(ReadSubst, WitnessMultiple) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::Witness); + EXPECT_EQ(s.toWitness().witnesses.size(), 2u); + EXPECT_TRUE(s.toWitness().witnesses.count("a")); + EXPECT_TRUE(s.toWitness().witnesses.count("b")); +} + +// ============================================================ +// OperationCall +// ============================================================ + +TEST(ReadSubst, OperationCallNoParams) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::OperationCall); + EXPECT_EQ(s.toOpCall().name, "op1"); +} + +TEST(ReadSubst, OperationCallWithParams) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.getTag(), Subst::SKind::OperationCall); + auto &oc = s.toOpCall(); + EXPECT_EQ(oc.name, "op2"); + EXPECT_EQ(oc.input.size(), 1u); + EXPECT_EQ(oc.output.size(), 1u); + EXPECT_EQ(oc.op_input.size(), 1u); + EXPECT_EQ(oc.op_output.size(), 1u); +} + +TEST(ReadSubst, OperationCallWithPrecondition) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + + + + + )"); + auto s = Xml::readSubstitution(doc.RootElement(), TI); + EXPECT_EQ(s.toOpCall().op_precondition.getTag(), Pred::PKind::ExprComparison); +} + +// ============================================================ +// Error cases +// ============================================================ + +TEST(ReadSubst, NullDomThrows) { + EXPECT_THROW(Xml::readSubstitution(nullptr, TI), + Xml::SubstReaderException); +} + +TEST(ReadSubst, UnknownTagThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(""); + EXPECT_THROW(Xml::readSubstitution(doc.RootElement(), TI), + Xml::SubstReaderException); +} + +TEST(ReadSubst, UnknownNaryOpThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"()"); + EXPECT_THROW(Xml::readSubstitution(doc.RootElement(), TI), + Xml::SubstReaderException); +} + +TEST(ReadSubst, AssertMissingGuardThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"()"); + EXPECT_THROW(Xml::readSubstitution(doc.RootElement(), TI), + Xml::SubstReaderException); +} + +TEST(ReadSubst, AssertMissingBodyThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + )"); + EXPECT_THROW(Xml::readSubstitution(doc.RootElement(), TI), + Xml::SubstReaderException); +} + +TEST(ReadSubst, PreSubMissingPreconditionThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"()"); + EXPECT_THROW(Xml::readSubstitution(doc.RootElement(), TI), + Xml::SubstReaderException); +} + +TEST(ReadSubst, IfSubMissingConditionThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"()"); + EXPECT_THROW(Xml::readSubstitution(doc.RootElement(), TI), + Xml::SubstReaderException); +} + +TEST(ReadSubst, IfSubMissingThenThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + + + + + + )"); + EXPECT_THROW(Xml::readSubstitution(doc.RootElement(), TI), + Xml::SubstReaderException); +} + +TEST(ReadSubst, SimpleAssignmentMissingVariablesThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + EXPECT_THROW(Xml::readSubstitution(doc.RootElement(), TI), + Xml::SubstReaderException); +} + +TEST(ReadSubst, SimpleAssignmentMissingValuesThrows) { + tinyxml2::XMLDocument doc; + doc.Parse(R"( + + )"); + EXPECT_THROW(Xml::readSubstitution(doc.RootElement(), TI), + Xml::SubstReaderException); +} + +TEST(ReadSubst, SelectMissingWhenClausesThrows) { + tinyxml2::XMLDocument doc; + doc.Parse("