#include "clang/Analysis/FlowSensitive/DebugSupport.h"
#include "TestingSupport.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace {
using namespace clang;
using namespace dataflow;
using test::ConstraintContext;
using testing::StrEq;
TEST(BoolValueDebugStringTest, AtomicBoolean) {
ConstraintContext Ctx;
auto B = Ctx.atom();
auto Expected = R"(B0)";
debugString(*B);
EXPECT_THAT(debugString(*B), StrEq(Expected));
}
TEST(BoolValueDebugStringTest, Negation) {
ConstraintContext Ctx;
auto B = Ctx.neg(Ctx.atom());
auto Expected = R"((not
B0))";
EXPECT_THAT(debugString(*B), StrEq(Expected));
}
TEST(BoolValueDebugStringTest, Conjunction) {
ConstraintContext Ctx;
auto B = Ctx.conj(Ctx.atom(), Ctx.atom());
auto Expected = R"((and
B0
B1))";
EXPECT_THAT(debugString(*B), StrEq(Expected));
}
TEST(BoolValueDebugStringTest, Disjunction) {
ConstraintContext Ctx;
auto B = Ctx.disj(Ctx.atom(), Ctx.atom());
auto Expected = R"((or
B0
B1))";
EXPECT_THAT(debugString(*B), StrEq(Expected));
}
TEST(BoolValueDebugStringTest, Implication) {
ConstraintContext Ctx;
auto B = Ctx.impl(Ctx.atom(), Ctx.atom());
auto Expected = R"((=>
B0
B1))";
EXPECT_THAT(debugString(*B), StrEq(Expected));
}
TEST(BoolValueDebugStringTest, Iff) {
ConstraintContext Ctx;
auto B = Ctx.iff(Ctx.atom(), Ctx.atom());
auto Expected = R"((=
B0
B1))";
EXPECT_THAT(debugString(*B), StrEq(Expected));
}
TEST(BoolValueDebugStringTest, Xor) {
ConstraintContext Ctx;
auto B0 = Ctx.atom();
auto B1 = Ctx.atom();
auto B = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
auto Expected = R"((or
(and
B0
(not
B1))
(and
(not
B0)
B1)))";
EXPECT_THAT(debugString(*B), StrEq(Expected));
}
TEST(BoolValueDebugStringTest, NestedBoolean) {
ConstraintContext Ctx;
auto B = Ctx.conj(
Ctx.atom(),
Ctx.disj(Ctx.atom(),
Ctx.conj(Ctx.atom(), Ctx.disj(Ctx.atom(), Ctx.atom()))));
auto Expected = R"((and
B0
(or
B1
(and
B2
(or
B3
B4)))))";
EXPECT_THAT(debugString(*B), StrEq(Expected));
}
TEST(BoolValueDebugStringTest, AtomicBooleanWithName) {
ConstraintContext Ctx;
auto True = cast<AtomicBoolValue>(Ctx.atom());
auto B = True;
auto Expected = R"(True)";
EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
}
TEST(BoolValueDebugStringTest, ComplexBooleanWithNames) {
ConstraintContext Ctx;
auto Cond = cast<AtomicBoolValue>(Ctx.atom());
auto Then = cast<AtomicBoolValue>(Ctx.atom());
auto Else = cast<AtomicBoolValue>(Ctx.atom());
auto B = Ctx.disj(Ctx.conj(Cond, Ctx.conj(Then, Ctx.neg(Else))),
Ctx.conj(Ctx.neg(Cond), Ctx.conj(Ctx.neg(Then), Else)));
auto Expected = R"((or
(and
Cond
(and
Then
(not
Else)))
(and
(not
Cond)
(and
(not
Then)
Else))))";
EXPECT_THAT(debugString(*B, {{Cond, "Cond"}, {Then, "Then"}, {Else, "Else"}}),
StrEq(Expected));
}
TEST(BoolValueDebugStringTest, ComplexBooleanWithSomeNames) {
ConstraintContext Ctx;
auto True = cast<AtomicBoolValue>(Ctx.atom());
auto False = cast<AtomicBoolValue>(Ctx.atom());
auto B = Ctx.disj(Ctx.conj(False, Ctx.atom()), Ctx.disj(True, Ctx.atom()));
auto Expected = R"((or
(and
False
B0)
(or
True
B1)))";
EXPECT_THAT(debugString(*B, {{True, "True"}, {False, "False"}}),
StrEq(Expected));
}
TEST(ConstraintSetDebugStringTest, Empty) {
llvm::DenseSet<BoolValue *> Constraints;
EXPECT_THAT(debugString(Constraints), StrEq(""));
}
TEST(ConstraintSetDebugStringTest, Simple) {
ConstraintContext Ctx;
llvm::DenseSet<BoolValue *> Constraints;
auto X = cast<AtomicBoolValue>(Ctx.atom());
auto Y = cast<AtomicBoolValue>(Ctx.atom());
Constraints.insert(Ctx.disj(X, Y));
Constraints.insert(Ctx.disj(X, Ctx.neg(Y)));
auto Expected = R"((or
X
(not
Y))
(or
X
Y)
)";
EXPECT_THAT(debugString(Constraints, {{X, "X"}, {Y, "Y"}}),
StrEq(Expected));
}
Solver::Result CheckSAT(std::vector<BoolValue *> Constraints) {
llvm::DenseSet<BoolValue *> ConstraintsSet(Constraints.begin(),
Constraints.end());
return WatchedLiteralsSolver().solve(std::move(ConstraintsSet));
}
TEST(SATCheckDebugStringTest, AtomicBoolean) {
ConstraintContext Ctx;
std::vector<BoolValue *> Constraints({Ctx.atom()});
auto Result = CheckSAT(Constraints);
auto Expected = R"(
Constraints
------------
B0
------------
Satisfiable.
B0 = True
)";
EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
}
TEST(SATCheckDebugStringTest, AtomicBooleanAndNegation) {
ConstraintContext Ctx;
auto B0 = Ctx.atom();
std::vector<BoolValue *> Constraints({B0, Ctx.neg(B0)});
auto Result = CheckSAT(Constraints);
auto Expected = R"(
Constraints
------------
B0
(not
B0)
------------
Unsatisfiable.
)";
EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
}
TEST(SATCheckDebugStringTest, MultipleAtomicBooleans) {
ConstraintContext Ctx;
std::vector<BoolValue *> Constraints({Ctx.atom(), Ctx.atom()});
auto Result = CheckSAT(Constraints);
auto Expected = R"(
Constraints
------------
B0
B1
------------
Satisfiable.
B0 = True
B1 = True
)";
EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
}
TEST(SATCheckDebugStringTest, Implication) {
ConstraintContext Ctx;
auto B0 = Ctx.atom();
auto B1 = Ctx.atom();
auto Impl = Ctx.disj(Ctx.neg(B0), B1);
std::vector<BoolValue *> Constraints({B0, Impl});
auto Result = CheckSAT(Constraints);
auto Expected = R"(
Constraints
------------
B0
(or
(not
B0)
B1)
------------
Satisfiable.
B0 = True
B1 = True
)";
EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
}
TEST(SATCheckDebugStringTest, Iff) {
ConstraintContext Ctx;
auto B0 = Ctx.atom();
auto B1 = Ctx.atom();
auto Iff = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
std::vector<BoolValue *> Constraints({B0, Iff});
auto Result = CheckSAT(Constraints);
auto Expected = R"(
Constraints
------------
B0
(and
(or
(not
B0)
B1)
(or
B0
(not
B1)))
------------
Satisfiable.
B0 = True
B1 = True
)";
EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
}
TEST(SATCheckDebugStringTest, Xor) {
ConstraintContext Ctx;
auto B0 = Ctx.atom();
auto B1 = Ctx.atom();
auto XOR = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
std::vector<BoolValue *> Constraints({B0, XOR});
auto Result = CheckSAT(Constraints);
auto Expected = R"(
Constraints
------------
B0
(or
(and
B0
(not
B1))
(and
(not
B0)
B1))
------------
Satisfiable.
B0 = True
B1 = False
)";
EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
}
TEST(SATCheckDebugStringTest, ComplexBooleanWithNames) {
ConstraintContext Ctx;
auto Cond = cast<AtomicBoolValue>(Ctx.atom());
auto Then = cast<AtomicBoolValue>(Ctx.atom());
auto Else = cast<AtomicBoolValue>(Ctx.atom());
auto B = Ctx.disj(Ctx.conj(Cond, Ctx.conj(Then, Ctx.neg(Else))),
Ctx.conj(Ctx.neg(Cond), Ctx.conj(Ctx.neg(Then), Else)));
std::vector<BoolValue *> Constraints({Cond, B});
auto Result = CheckSAT(Constraints);
auto Expected = R"(
Constraints
------------
Cond
(or
(and
Cond
(and
Then
(not
Else)))
(and
(not
Cond)
(and
(not
Then)
Else)))
------------
Satisfiable.
Cond = True
Else = False
Then = True
)";
EXPECT_THAT(debugString(Constraints, Result,
{{Cond, "Cond"}, {Then, "Then"}, {Else, "Else"}}),
StrEq(Expected));
}
TEST(SATCheckDebugStringTest, ComplexBooleanWithLongNames) {
ConstraintContext Ctx;
auto IntsAreEq = cast<AtomicBoolValue>(Ctx.atom());
auto ThisIntEqZero = cast<AtomicBoolValue>(Ctx.atom());
auto OtherIntEqZero = cast<AtomicBoolValue>(Ctx.atom());
auto BothZeroImpliesEQ =
Ctx.disj(Ctx.neg(Ctx.conj(ThisIntEqZero, OtherIntEqZero)), IntsAreEq);
std::vector<BoolValue *> Constraints(
{ThisIntEqZero, Ctx.neg(IntsAreEq), BothZeroImpliesEQ});
auto Result = CheckSAT(Constraints);
auto Expected = R"(
Constraints
------------
ThisIntEqZero
(not
IntsAreEq)
(or
(not
(and
ThisIntEqZero
OtherIntEqZero))
IntsAreEq)
------------
Satisfiable.
IntsAreEq = False
OtherIntEqZero = False
ThisIntEqZero = True
)";
EXPECT_THAT(debugString(Constraints, Result,
{{IntsAreEq, "IntsAreEq"},
{ThisIntEqZero, "ThisIntEqZero"},
{OtherIntEqZero, "OtherIntEqZero"}}),
StrEq(Expected));
}
}