knowrob  2.1.0
A Knowledge Base System for Cognition-enabled Robots
PrologTests.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of KnowRob, please consult
3  * https://github.com/knowrob/knowrob for license details.
4  */
5 
6 #include <boost/algorithm/string/replace.hpp>
7 #include "knowrob/Logger.h"
8 #include "knowrob/terms/ListTerm.h"
9 #include "knowrob/terms/OptionList.h"
10 #include "knowrob/reasoner/prolog/PrologTests.h"
11 #include "knowrob/integration/prolog/PrologBackend.h"
12 #include "knowrob/queries/QueryParser.h"
13 
14 using namespace knowrob;
15 
16 static std::string unescapeString(const std::string &input) {
17  std::string result = input;
18  boost::replace_all(result, "\\n", "\n");
19  boost::replace_all(result, "\\t", "\t");
20  return result;
21 }
22 
24  const std::shared_ptr<knowrob::PrologReasoner> &reasoner,
25  const std::string &target) {
26  bool hasResult = false;
27  int numTests = 0;
28  int numFailedTests = 0;
29 
30  for (const auto &t: reasoner->runTests(PrologEngine::getPrologPath(target))) {
31  hasResult = true;
32  numTests += 1;
33 
34  // each result is a term element/3
35  ASSERT_EQ(t->termType(), TermType::FUNCTION);
36  auto *pElem = (Function *) t.get();
37  ASSERT_EQ(*pElem->functor(), *Atom::Tabled("element"));
38  ASSERT_EQ(pElem->arity(), 3);
39  ASSERT_EQ(*(pElem->arguments()[0]), *Atom::Tabled("testcase"));
40 
41  // the second argument has the form:
42  // [name=::string, file=::string, line=::int, time=::double]
43  OptionList trace(pElem->arguments()[1]);
44  ASSERT_TRUE(trace.contains("file"));
45  ASSERT_TRUE(trace.contains("line"));
46  ASSERT_TRUE(trace.contains("name"));
47  const auto &name = trace.getString("name", "");
48  const char *file = trace.getString("file", "").data();
49  const long line = trace.getLong("line", 0);
50 
51  // the third argument is a list of failures.
52  auto *failureList = (ListTerm *) pElem->arguments()[2].get();
53  if (!failureList->isNIL()) {
54  numFailedTests += 1;
55  }
56  for (const auto &failureTerm: (*failureList)) {
57  ASSERT_EQ(failureTerm->termType(), TermType::FUNCTION);
58  // each failure is a term element/3
59  auto *errElem = (Function *) failureTerm.get();
60  ASSERT_EQ(*errElem->functor(), *Atom::Tabled("element"));
61  ASSERT_EQ(errElem->arity(), 3);
62  ASSERT_EQ(*(errElem->arguments()[0]), *Atom::Tabled("failure"));
63 
64  OptionList errOpts(errElem->arguments()[1]);
65  ASSERT_TRUE(errOpts.contains("type"));
66  ASSERT_TRUE(errOpts.contains("message"));
67 
68  std::ostringstream message_os;
69  message_os << errOpts.getString("type", "");
70  message_os << ": ";
71  message_os << errOpts.getString("message", "");
72  auto message = message_os.str();
73 
74  std::ostringstream summary_os;
75  summary_os << "test: ";
76  summary_os << name;
77  auto summary = summary_os.str();
78 
79  // the second argument has the form: [ type=error|failure, message='...' ]
80  // the third argument is the exact same message, which is a bit strange.
81  //ADD_FAILURE_AT(file,line) << message;
82  GTEST_MESSAGE_AT_(file, line, summary.c_str(), \
83  testing::TestPartResult::kNonFatalFailure) << unescapeString(message);
84  }
85  }
86  EXPECT_TRUE(hasResult);
87  KB_INFO1(target.c_str(), 1, "[plunit] {}/{} tests succeeded for target '{}'.", (numTests - numFailedTests),
88  numTests, target);
89 }
90 
91 namespace knowrob::testing {
92  class PrologReasonerTests : public PrologTests<knowrob::PrologReasoner, knowrob::PrologBackend> {
93  protected:
94  static std::string getPath(const std::string &filename) {
95  return std::filesystem::path("reasoner") / "prolog" / filename;
96  }
97 
98  static std::vector<BindingsPtr> lookup(const SimpleConjunctionPtr &formula) {
99  auto ctx = std::make_shared<QueryContext>(QUERY_FLAG_ALL_SOLUTIONS);
100  auto query = std::make_shared<Goal>(formula, ctx);
101  reasoner()->evaluate(query);
102 
103  auto answerQueue = query->answerBuffer()->createQueue();
104  std::vector<BindingsPtr> out;
105  while (!answerQueue->empty()) {
106  auto solution = answerQueue->pop_front();
107  if (solution->indicatesEndOfEvaluation()) break;
108  if (solution->tokenType() == TokenType::ANSWER_TOKEN) {
109  auto answer = std::static_pointer_cast<const Answer>(solution);
110  if (answer->isPositive()) {
111  auto answerYes = std::static_pointer_cast<const AnswerYes>(answer);
112  out.push_back(answerYes->substitution());
113  }
114  }
115  }
116  return out;
117  }
118  };
119 }
120 using namespace knowrob::testing;
121 
122 #define EXPECT_ONLY_SOLUTION(phi, sol) { \
123  auto sols = lookup(phi); \
124  EXPECT_EQ(sols.size(),1); \
125  if(sols.size()==1) { EXPECT_EQ(*sols[0], sol); } }
126 
127 #define EXPECT_NO_SOLUTION(phi) EXPECT_EQ(lookupAll(phi).size(),0)
128 
129 TEST_F(PrologReasonerTests, simple_conjunction) {
130  auto p1 = QueryParser::parsePredicate("atom_concat(a,b,AB)");
131  auto p2 = QueryParser::parsePredicate("atom_concat(AB,c,ABC)");
132  auto queryFormula = std::make_shared<SimpleConjunction>(std::vector<FirstOrderLiteralPtr>{
133  std::make_shared<FirstOrderLiteral>(p1, false),
134  std::make_shared<FirstOrderLiteral>(p2, false)
135  });
137  // the query formula:
138  queryFormula,
139  // the expected solution as substitution mapping:
140  Bindings({
141  {std::make_shared<Variable>("AB"), Atom::Tabled("ab")},
142  {std::make_shared<Variable>("ABC"), Atom::Tabled("abc")}
143  }))
144 }
145 
146 // register test cases of prolog files in this directory (pl or plt file extension)
147 TEST_F(PrologReasonerTests, semweb) { runTests(getPath("semweb.pl")); }
TEST_F(PrologReasonerTests, simple_conjunction)
#define EXPECT_ONLY_SOLUTION(phi, sol)
#define KB_INFO1(file, line,...)
Definition: Logger.h:47
static std::shared_ptr< knowrob::Atom > Tabled(std::string_view stringForm)
Definition: Atom.cpp:40
std::string_view getString(const std::string &key, const std::string &defaultValue) const
Definition: OptionList.cpp:62
bool contains(const std::string &key) const
Definition: OptionList.cpp:49
long getLong(const std::string &key, long defaultValue) const
Definition: OptionList.cpp:79
static std::filesystem::path getPrologPath(const std::filesystem::path &filename)
static void runPrologTests(const std::shared_ptr< knowrob::PrologReasoner > &reasoner, const std::string &target)
Definition: PrologTests.cpp:23
static PredicatePtr parsePredicate(const std::string &queryString)
Definition: QueryParser.cpp:38
FormulaRule & formula()
Definition: formula.cpp:283
TermRule & string()
Definition: terms.cpp:63
std::shared_ptr< SimpleConjunction > SimpleConjunctionPtr
@ QUERY_FLAG_ALL_SOLUTIONS
Definition: QueryFlag.h:15