Fawkes API Fawkes Development Version
test_domain.cpp
1/***************************************************************************
2 * test_domain.cpp - Tests for the domain representation
3 *
4 * Created: Tue 26 Sep 2017 11:05:41 CEST 11:05
5 * Copyright 2017 Till Hofmann <hofmann@kbsg.rwth-aachen.de>
6 ****************************************************************************/
7
8/* This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * Read the full text in the LICENSE.GPL file in the doc directory.
19 */
20
21#include "clips_test.h"
22
23#include <cstdio>
24#include <string>
25#include <vector>
26
27using namespace std;
28
29/** Test setup for domain tests. */
30class DomainTest : public CLIPSTest
31{
32protected:
33 /** Set up the test environment. */
34 virtual void
36 {
37 string logfile = tmpnam(nullptr);
38 env.evaluate("(open \"" + logfile + "\" error \"w\")");
40 // Helps a lot to diagnose failures
41 //env.evaluate("(watch facts)");
42 //env.evaluate("(watch rules)");
43 }
44 /** These files are loaded during setup by default. */
45 vector<string> clips_files = {"../../../clips/clips/utils.clp", "../plan.clp", "../domain.clp"};
46};
47
48/** Test with the blocksworld domain. */
50{
51protected:
52 /** Set up the test environment. */
53 virtual void
55 {
56 clips_files.push_back("blocksworld.clp");
58 }
59};
60
61/** Check that a precondition of an action that should be executable is really
62 * satisfied.
63 */
64TEST_F(BlocksworldDomainTest, PreconditionsAreSatisfiedTest)
65{
66 env.reset();
67 env.run();
68 EXPECT_TRUE(has_fact("((?p domain-precondition))",
69 "(and (eq ?p:name neg-on-table) (eq ?p:is-satisfied TRUE))"));
70}
71
72/** Check that a negative precondition that should not be satisfied is actually
73 * not satisfied.
74 */
75TEST_F(BlocksworldDomainTest, NegativePreconditionIsNotSatisfied)
76{
77 env.reset();
78 env.assert_fact("(domain-fact (name ontable) (param-values b1))");
79 env.run();
80 EXPECT_TRUE(has_fact("((?p domain-precondition))",
81 "(and (eq ?p:name neg-on-table) (eq ?p:is-satisfied FALSE))"));
82 EXPECT_FALSE(has_fact("((?p domain-precondition))",
83 "(and (eq ?p:name neg-on-table) (eq ?p:is-satisfied TRUE))"));
84 EXPECT_TRUE(has_fact("((?p domain-precondition))",
85 "(and (eq ?p:name pick-up-precond) (eq ?p:is-satisfied FALSE))"));
86 EXPECT_FALSE(has_fact("((?p domain-precondition))",
87 "(and (eq ?p:name pick-up-precond) (eq ?p:is-satisfied TRUE))"));
88}
89
90/** Test disjunctive preconditions. */
91TEST_F(DomainTest, DisjunctivePreconditions)
92{
93 env.assert_fact("(domain-operator (name disjunctive-op))");
94 env.assert_fact("(domain-precondition (name pre) (type disjunction)"
95 " (part-of disjunctive-op))");
96 env.assert_fact("(plan-action (id 1) (goal-id g0) (plan-id p0) "
97 "(action-name disjunctive-op))");
98 env.run();
99 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:executable FALSE))"));
100 env.assert_fact("(domain-predicate (name p))");
101 env.assert_fact("(domain-atomic-precondition (part-of pre) (predicate p))");
102 env.run();
103 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:executable FALSE))"));
104 env.assert_fact("(domain-fact (name p))");
105 env.run();
106 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:executable TRUE))"));
107 env.assert_fact("(domain-predicate (name q))");
108 env.assert_fact("(domain-atomic-precondition (part-of pre) (predicate q))");
109 env.run();
110 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:executable TRUE))"));
111}
112
113/** Check whether the function domain-is-precond-negative computes correct
114 * values
115 */
116TEST_F(DomainTest, IsPrecondNegativeFunction)
117{
118 env.assert_fact("(domain-operator (name op))");
119 env.assert_fact("(domain-precondition (name p1) (part-of op)"
120 "(type conjunction)"
121 ")");
122 env.assert_fact("(domain-precondition (name p11) (part-of p1)"
123 "(type negation)"
124 ")");
125 EXPECT_EQ("TRUE", env.evaluate("(domain-is-precond-negative p11)")[0].as_string());
126 env.assert_fact("(domain-precondition (name p111) (part-of p11)"
127 "(type negation)"
128 ")");
129 EXPECT_EQ("FALSE", env.evaluate("(domain-is-precond-negative p111)")[0].as_string());
130 env.assert_fact("(domain-precondition (name p1111) (part-of p111)"
131 "(type disjunction)"
132 ")");
133 EXPECT_EQ("FALSE", env.evaluate("(domain-is-precond-negative p1111)")[0].as_string());
134 env.assert_fact("(domain-precondition (name p11111) (part-of p1111)"
135 "(type negation)"
136 ")");
137 EXPECT_EQ("TRUE", env.evaluate("(domain-is-precond-negative p11111)")[0].as_string());
138}
139
140/** Ground an action with multiple parameters and check that the grounding is
141 * correct. Also make sure its precondition is satisfied (which it should be).
142 */
143TEST_F(BlocksworldDomainTest, GroundingWithMultipleParameters)
144{
145 env.reset();
146 env.run();
147 EXPECT_TRUE(has_fact("((?p domain-atomic-precondition))",
148 "(and (eq ?p:predicate on) "
149 "(eq ?p:grounded TRUE) "
150 "(eq ?p:param-values (create$ b1 b2)))"));
151 EXPECT_TRUE(has_fact("((?p domain-precondition))",
152 "(and (eq ?p:name unstack-precond) (eq ?p:is-satisfied TRUE))"));
153}
154
155/** Check whether some basic facts about types exist after adding some domain
156 * objects.
157 */
158TEST_F(DomainTest, Typing)
159{
160 env.reset();
161 env.assert_fact("(domain-object (name thing))");
162 env.assert_fact("(domain-object-type (name moveable-obj))");
163 env.assert_fact("(domain-object-type (name cup) (super-type moveable-obj))");
164 env.assert_fact("(domain-object (name c1) (type cup))");
165 env.run();
166 EXPECT_TRUE(has_ordered_fact("domain-obj-is-of-type", {"thing", "object"}));
167 EXPECT_TRUE(has_ordered_fact("domain-obj-is-of-type", {"c1", "cup"}));
168 EXPECT_TRUE(has_ordered_fact("domain-obj-is-of-type", {"c1", "moveable-obj"}));
169 EXPECT_TRUE(has_ordered_fact("domain-obj-is-of-type", {"c1", "object"}));
170}
171
172/** There should be no domain error if the specified domain is valid. */
173TEST_F(BlocksworldDomainTest, NoErrorWithValidDomain)
174{
175 env.reset();
176 env.run();
177 EXPECT_FALSE(has_fact("((?error domain-error))"));
178}
179
180/** Every precondition must have an operator... */
181TEST_F(DomainTest, ErrorIfPreconditionHasNoOperator)
182{
183 env.reset();
184 env.assert_fact("(domain-precondition (name foo))");
185 env.run();
186 EXPECT_TRUE(has_fact("((?e domain-error))", "(eq ?e:error-type precondition-without-parent)"));
187}
188
189/** ... and the operator must actually be defined. */
190TEST_F(DomainTest, ErrorIfOperatorOfPreconditionDoesNotExist)
191{
192 env.reset();
193 env.assert_fact("(domain-precondition (name foo) (part-of op))");
194 env.run();
195 EXPECT_TRUE(has_fact("((?e domain-error))", "(eq ?e:error-type precondition-without-parent)"));
196}
197
198/** The type of an object must exist in the domain. */
199TEST_F(DomainTest, ErrorIfObjTypeDoesNotExist)
200{
201 env.reset();
202 env.assert_fact("(domain-object (name o1) (type t1))");
203 env.run();
204 EXPECT_TRUE(has_fact("((?e domain-error))", "(eq ?e:error-type type-of-object-does-not-exist)"));
205}
206
207/** The super type of a domain type must exist. */
208TEST_F(DomainTest, ErrorIfSuperTypeDoesNotExist)
209{
210 env.reset();
211 env.assert_fact("(domain-object-type (name t2) (super-type t1))");
212 env.run();
213 EXPECT_TRUE(has_fact("((?e domain-error))", "(eq ?e:error-type super-type-does-not-exist)"));
214}
215
216/** If the precondition of an action is satisfied, then the respective action
217 * should also be marked as executable.
218 */
219TEST_F(BlocksworldDomainTest, ActionIsExecutableIfPreconditionIsSatisfied)
220{
221 env.reset();
222 env.run();
223 EXPECT_TRUE(
224 has_fact("((?a plan-action))", "(and (eq ?a:action-name pick-up) (eq ?a:executable TRUE))"));
225 EXPECT_TRUE(
226 has_fact("((?a plan-action))", "(and (eq ?a:action-name unstack) (eq ?a:executable TRUE))"));
227}
228
229/** Applying the effects of an action changes the domain facts. */
230TEST_F(BlocksworldDomainTest, ApplyEffects)
231{
232 env.reset();
233 env.run();
234 EXPECT_FALSE(
235 has_fact("((?p domain-fact))", "(and (eq ?p:name holding) (eq ?p:param-values (create$ b1)))"));
236 EXPECT_TRUE(has_fact("((?p domain-fact))", "(eq ?p:name handempty)"));
237 EXPECT_TRUE(
238 has_fact("((?p domain-fact))", "(and (eq ?p:name clear) (eq ?p:param-values (create$ b1)))"));
239 env.assert_fact("(apply-action g0 p0 1)");
240 env.run();
241 EXPECT_TRUE(
242 has_fact("((?p domain-fact))", "(and (eq ?p:name holding) (eq ?p:param-values (create$ b1)))"));
243 EXPECT_FALSE(has_fact("((?p domain-fact))", "(eq ?p:name handempty)"));
244 EXPECT_FALSE(
245 has_fact("((?p domain-fact))", "(and (eq ?p:name clear) (eq ?p:param-values (create$ b1)))"));
246}
247
248/** When we apply one action after the other, then the effects of the latter
249 * action must hold at the end. Also check that effects on the same predicate
250 * but with different parameters are applied correctly.
251 */
252TEST_F(BlocksworldDomainTest, ApplyContradictingEffectsWithDifferentParams)
253{
254 env.reset();
255 env.run();
256 EXPECT_TRUE(
257 has_fact("((?p domain-fact))", "(and (eq ?p:name clear) (eq ?p:param-values (create$ b1)))"));
258 EXPECT_FALSE(
259 has_fact("((?p domain-fact))", "(and (eq ?p:name clear) (eq ?p:param-values (create$ b2)))"));
260 env.assert_fact("(apply-action g0 p0 2)");
261 env.run();
262 EXPECT_FALSE(
263 has_fact("((?p domain-fact))", "(and (eq ?p:name clear) (eq ?p:param-values (create$ b1)))"));
264 EXPECT_TRUE(
265 has_fact("((?p domain-fact))", "(and (eq ?p:name clear) (eq ?p:param-values (create$ b2)))"));
266}
267
268/** We wait for all sensed effects to occur before we apply any non-sensed
269 * effects.
270 */
271TEST_F(DomainTest, WaitForSensedEffects)
272{
273 env.reset();
274 env.assert_fact("(domain-operator (name drop))");
275 env.assert_fact("(domain-operator-parameter"
276 " (operator drop)"
277 " (type object)"
278 " (name o)"
279 ")");
280 env.assert_fact("(domain-predicate"
281 " (name holding)"
282 " (param-names o)"
283 " (sensed TRUE)"
284 ")");
285 env.assert_fact("(domain-predicate"
286 " (name on-ground)"
287 " (param-names o)"
288 ")");
289 env.assert_fact("(domain-effect"
290 " (type NEGATIVE)"
291 " (part-of drop) (predicate holding) (param-names o)"
292 ")");
293 env.assert_fact("(domain-effect"
294 " (part-of drop) (predicate on-ground) (param-names o)"
295 ")");
296 env.assert_fact("(domain-object (name obj1))");
297 env.assert_fact("(domain-fact (name holding) (param-values obj1))");
298 env.assert_fact("(plan-action"
299 " (goal-id g0) (plan-id p0)"
300 " (id 1)"
301 " (state EXECUTION-SUCCEEDED)"
302 " (action-name drop)"
303 " (param-names o)"
304 " (param-values obj1))");
305 env.run();
306 EXPECT_FALSE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:state FINAL))"));
307 EXPECT_FALSE(
308 has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:state SENSED-EFFECTS-HOLD))"));
309 EXPECT_TRUE(
310 has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:state SENSED-EFFECTS-WAIT))"));
311 EXPECT_FALSE(has_fact("((?f domain-fact))",
312 "(and (eq ?f:name on-ground) (eq ?f:param-values (create$ obj1)))"));
313 env.evaluate("(delayed-do-for-all-facts ((?df domain-fact)) "
314 " (and (eq ?df:name holding) (eq ?df:param-values (create$ obj1))) "
315 " (retract ?df)"
316 ")");
317 env.run();
318 EXPECT_TRUE(has_fact("((?f domain-fact))",
319 "(and (eq ?f:name on-ground) (eq ?f:param-values (create$ obj1)))"));
320 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:state FINAL))"));
321}
322
323/** Do not wait if the operator has wait-sensed set to FALSE. */
324TEST_F(DomainTest, OnlyWaitForEffectsIfWaitSensedIsTRUE)
325{
326 env.reset();
327 env.assert_fact("(domain-operator (name drop) (wait-sensed FALSE))");
328 env.assert_fact("(domain-operator-parameter"
329 " (operator drop)"
330 " (type object)"
331 " (name o)"
332 ")");
333 env.assert_fact("(domain-predicate"
334 " (name holding)"
335 " (param-names o)"
336 " (sensed TRUE)"
337 ")");
338 env.assert_fact("(domain-predicate"
339 " (name on-ground)"
340 " (param-names o)"
341 ")");
342 env.assert_fact("(domain-effect"
343 " (type NEGATIVE)"
344 " (part-of drop) (predicate holding) (param-names o)"
345 ")");
346 env.assert_fact("(domain-effect"
347 " (part-of drop) (predicate on-ground) (param-names o)"
348 ")");
349 env.assert_fact("(domain-object (name obj1))");
350 env.assert_fact("(domain-fact (name holding) (param-values obj1))");
351 env.assert_fact("(plan-action"
352 " (id 1)"
353 " (goal-id g0) (plan-id p0)"
354 " (state EXECUTION-SUCCEEDED)"
355 " (action-name drop)"
356 " (param-names o)"
357 " (param-values obj1))");
358 env.run();
359 EXPECT_TRUE(has_fact("((?f domain-fact))",
360 "(and (eq ?f:name on-ground) (eq ?f:param-values (create$ obj1)))"));
361 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:state FINAL))"));
362}
363
364/** Sensed effects of an exogenous action are dropped from the precondition. */
365TEST_F(DomainTest, ExogenousActions)
366{
367 env.reset();
368 env.assert_fact("(domain-predicate"
369 " (name holding)"
370 " (param-names o)"
371 " (sensed TRUE)"
372 ")");
373 env.assert_fact("(domain-operator (name drop) (exogenous TRUE))");
374 env.assert_fact("(domain-operator-parameter"
375 " (operator drop)"
376 " (type object)"
377 " (name o)"
378 ")");
379 env.assert_fact("(domain-precondition"
380 " (name drop-cond)"
381 " (part-of drop)"
382 ")");
383 env.assert_fact("(domain-atomic-precondition (part-of drop-cond) "
384 " (predicate holding)"
385 " (param-names o)"
386 ")");
387 env.assert_fact("(domain-effect"
388 " (type NEGATIVE)"
389 " (part-of drop) (predicate holding) (param-names o)"
390 ")");
391 env.assert_fact("(domain-object (name obj1))");
392 env.assert_fact("(plan-action"
393 " (id 1)"
394 " (goal-id g0) (plan-id p0)"
395 " (action-name drop)"
396 " (param-names o)"
397 " (param-values obj1))");
398 env.run();
399 // The precondition (holding obj1) is false, but since it is a sensed effect,
400 // it should be removed from the operator's precondition.
401 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:executable TRUE))"));
402}
403
404/** If an action has the same effect both as positive and as negative effect,
405 * then the effect should hold after the effects are applied.
406 */
407TEST_F(DomainTest, ApplyCondtradictingEffectsWithSameParams)
408{
409 env.reset();
410 env.assert_fact("(domain-operator (name op1))");
411 env.assert_fact("(domain-operator-parameter (name x) (operator op1))");
412 env.assert_fact("(domain-predicate (name p) (param-names x))");
413 env.assert_fact("(plan-action"
414 " (id 1)"
415 " (state EXECUTION-SUCCEEDED)"
416 " (action-name op1)"
417 " (param-names x)"
418 " (param-values a))");
419 env.assert_fact("(domain-effect"
420 " (part-of op1)"
421 " (predicate p)"
422 " (type NEGATIVE)"
423 " (param-names x)"
424 ")");
425 env.assert_fact("(domain-effect"
426 " (part-of op1)"
427 " (predicate p)"
428 " (type POSITIVE)"
429 " (param-names x)"
430 ")");
431 env.assert_fact("(domain-effect"
432 " (part-of op1)"
433 " (predicate p)"
434 " (type NEGATIVE)"
435 " (param-names x)"
436 ")");
437 env.run();
438 EXPECT_TRUE(
439 has_fact("((?f domain-fact))", "(and (eq ?f:name p) (eq ?f:param-values (create$ a)))"));
440}
441
442/** Test whether constants in preconditions work as expected. */
443TEST_F(DomainTest, PreconditionWithConstant)
444{
445 env.reset();
446 env.assert_fact("(plan-action"
447 " (id 1)"
448 " (goal-id g0) (plan-id p0)"
449 " (action-name op1)"
450 " (param-names y)"
451 " (param-values b))");
452 env.assert_fact("(domain-precondition (name p1) (part-of op1))");
453 env.assert_fact("(domain-atomic-precondition"
454 " (name ap1)"
455 " (part-of p1)"
456 " (predicate pred1)"
457 " (param-names c y)"
458 " (param-constants a))");
459 env.run();
460 EXPECT_FALSE(has_fact("((?p domain-atomic-precondition))",
461 "(and (eq ?p:name ap1) (eq ?p:is-satisfied TRUE))"));
462 EXPECT_FALSE(
463 has_fact("((?p domain-precondition))", "(and (eq ?p:name p1) (eq ?p:is-satisfied TRUE))"));
464 EXPECT_FALSE(has_fact("((?a plan-action))", "(and (eq a:?id 1) (eq ?a:executable TRUE))"));
465 env.assert_fact("(domain-fact (name pred1) (param-values a b))");
466 env.run();
467 EXPECT_TRUE(has_fact("((?p domain-atomic-precondition))",
468 "(and (eq ?p:name ap1) (eq ?p:is-satisfied TRUE))"));
469 EXPECT_TRUE(
470 has_fact("((?p domain-precondition))", "(and (eq ?p:name p1) (eq ?p:is-satisfied TRUE))"));
471 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:executable TRUE))"));
472}
473
474/** Test whether the order of parameters and constants is correct if the
475 * constant is the second parameter of the precondition.
476 */
477TEST_F(DomainTest, PreconditionWithConstantInSecondSlot)
478{
479 env.reset();
480 env.assert_fact("(plan-action"
481 " (id 1)"
482 " (goal-id g0) (plan-id p0)"
483 " (action-name op1)"
484 " (param-names x)"
485 " (param-values b))");
486 env.assert_fact("(domain-precondition (name p1) (part-of op1))");
487 env.assert_fact("(domain-atomic-precondition"
488 " (name ap1)"
489 " (part-of p1)"
490 " (predicate pred1)"
491 " (param-names x c)"
492 " (param-constants nil a))");
493 env.run();
494 EXPECT_FALSE(has_fact("((?p domain-atomic-precondition))",
495 "(and (eq ?p:name ap1) (eq ?p:is-satisfied TRUE))"));
496 EXPECT_FALSE(
497 has_fact("((?p domain-precondition))", "(and (eq ?p:name p1) (eq ?p:is-satisfied TRUE))"));
498 EXPECT_FALSE(has_fact("((?a plan-action))", "(and (eq a:?id 1) (eq ?a:executable TRUE))"));
499 env.assert_fact("(domain-fact (name pred1) (param-values b a))");
500 env.run();
501 EXPECT_TRUE(has_fact("((?p domain-atomic-precondition))",
502 "(and (eq ?p:name ap1) (eq ?p:is-satisfied TRUE))"));
503 EXPECT_TRUE(
504 has_fact("((?p domain-precondition))", "(and (eq ?p:name p1) (eq ?p:is-satisfied TRUE))"));
505 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:executable TRUE))"));
506}
507
508/** Test equality predicates. */
509TEST_F(DomainTest, Equality)
510{
511 env.reset();
512 env.assert_fact("(plan-action"
513 " (id 1)"
514 " (state PENDING)"
515 " (goal-id g0) (plan-id p0)"
516 " (action-name op1)"
517 " (param-names x y)"
518 " (param-values b b))");
519 env.assert_fact("(domain-precondition (name p1) (part-of op1))");
520 env.assert_fact("(domain-atomic-precondition"
521 " (name ap1)"
522 " (part-of p1)"
523 " (equality TRUE)"
524 " (param-names x y)"
525 ")");
526 env.run();
527 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 1) (eq ?a:executable TRUE))"));
528 env.assert_fact("(plan-action"
529 " (id 2)"
530 " (state PENDING)"
531 " (goal-id g0) (plan-id p0)"
532 " (action-name op1)"
533 " (param-names x y)"
534 " (param-values b c))");
535 env.run();
536 EXPECT_TRUE(has_fact("((?a plan-action))", "(and (eq ?a:id 2) (eq ?a:executable FALSE))"));
537}
538
539/** Test that errors of equality conditions are properly detected. */
540TEST_F(DomainTest, EqualityErrors)
541{
542 env.reset();
543 env.assert_fact("(domain-atomic-precondition"
544 " (name ap1)"
545 " (part-of p1)"
546 " (equality TRUE)"
547 " (param-names x)"
548 ")");
549 env.run();
550 EXPECT_TRUE(has_fact("((?e domain-error))",
551 "(eq ?e:error-type equality-must-have-exactly-two-parameters)"));
552 env.reset();
553 env.assert_fact("(domain-atomic-precondition"
554 " (name ap1)"
555 " (part-of p1)"
556 " (equality TRUE)"
557 " (param-names x y z)"
558 ")");
559 env.run();
560 EXPECT_TRUE(has_fact("((?e domain-error))",
561 "(eq ?e:error-type equality-must-have-exactly-two-parameters)"));
562 env.reset();
563 env.assert_fact("(domain-atomic-precondition"
564 " (name ap1)"
565 " (part-of p1)"
566 " (equality TRUE)"
567 " (predicate p)"
568 " (param-names x y)"
569 ")");
570 env.run();
571 EXPECT_TRUE(
572 has_fact("((?e domain-error))", "(eq ?e:error-type precondition-with-equality-and-predicate)"));
573 env.reset();
574 env.assert_fact("(domain-atomic-precondition"
575 " (name ap1)"
576 " (part-of p1)"
577 " (param-names x y)"
578 ")");
579 env.run();
580 EXPECT_TRUE(has_fact("((?e domain-error))",
581 "(eq ?e:error-type precondition-must-have-predicate-or-be-equality)"));
582}
583
584/** If there is an unknown parameter in a precondition, then the domain contains
585 * an error.
586 */
587TEST_F(DomainTest, PreconditionWithUnknownParameter)
588{
589 env.reset();
590 env.assert_fact("(plan-action"
591 " (id 1)"
592 " (goal-id g0) (plan-id p0)"
593 " (action-name op1)"
594 " (param-names x)"
595 " (param-values b))");
596 env.assert_fact("(domain-precondition (name p1) (part-of op1))");
597 env.assert_fact("(domain-atomic-precondition"
598 " (name ap1)"
599 " (part-of p1)"
600 " (predicate pred1)"
601 " (param-names y c)"
602 " (param-constants nil a))");
603 env.run();
604 EXPECT_TRUE(has_fact("((?e domain-error))", "(eq ?e:error-type unknown-parameter)"));
605}
606
607/** Each action has an operator that is defined in the domain. */
608TEST_F(DomainTest, ActionHasADomainOperator)
609{
610 env.reset();
611 env.assert_fact("(plan-action (action-name doesn-not-exist))");
612 env.run();
613 EXPECT_TRUE(
614 has_fact("((?e domain-error))", "(eq ?e:error-type operator-of-action-does-not-exist)"));
615}
616
617/** Value predicates must take at most one value. */
618TEST_F(DomainTest, ValuePredicatesHaveUniqueValues)
619{
620 env.reset();
621 env.assert_fact("(domain-predicate (name p) (value-predicate TRUE))");
622 env.assert_fact("(domain-fact (name p) (param-values a b c 1))");
623 env.assert_fact("(domain-fact (name p) (param-values a b d 1))");
624 env.run();
625 EXPECT_FALSE(
626 has_fact("((?e domain-error))", "(eq ?e:error-type value-predicate-with-multiple-values)"));
627 env.assert_fact("(domain-fact (name p) (param-values a b c 2))");
628 env.run();
629 EXPECT_TRUE(
630 has_fact("((?e domain-error))", "(eq ?e:error-type value-predicate-with-multiple-values)"));
631 env.evaluate("(do-for-fact ((?f domain-fact)) "
632 "(and (eq ?f:name p) (eq ?f:param-values (create$ a b c 2)))"
633 "(retract ?f)"
634 ")");
635 env.run();
636 EXPECT_FALSE(
637 has_fact("((?e domain-error))", "(eq ?e:error-type value-predicate-with-multiple-values)"));
638}
639
640TEST_F(BlocksworldDomainTest, EffectsOnValuePredicatesMustOccurInPairs)
641{
642 env.reset();
643 env.run();
644 EXPECT_FALSE(
645 has_fact("((?e domain-error))", "(eq ?e:error-type value-predicate-without-paired-effect)"));
646 env.evaluate("(do-for-fact ((?p domain-predicate)) "
647 "(eq ?p:name holding) "
648 "(duplicate ?p (value-predicate TRUE))"
649 "(retract ?p)"
650 ")");
651 env.run();
652 EXPECT_TRUE(
653 has_fact("((?e domain-error))", "(eq ?e:error-type value-predicate-without-paired-effect)"));
654}
655
656/** Action IDs are only unique within the same plan and goal.
657 * Create multiple actions with the same ID but different plans, and test
658 * if they are treated correctly.
659 */
660TEST_F(BlocksworldDomainTest, NonUniqueActionIDs)
661{
662 env.reset();
663 env.assert_fact("(plan-action (id 1) (goal-id g0) (plan-id p1)"
664 " (action-name stack)"
665 " (param-names x y) (param-values b1 b2))");
666 env.assert_fact("(plan-action (id 1) (goal-id g1) (plan-id p0)"
667 " (action-name stack)"
668 " (param-names x y) (param-values b1 b2))");
669 env.run();
670 // The action pick-up with ID 1 in plan p0 of goal g0. This should be
671 // executable, and the executable state should not be copied to the other
672 // actions.
673 EXPECT_TRUE(has_fact("((?a plan-action))",
674 "(and (eq ?a:id 1) (eq ?a:goal-id g0) (eq ?a:plan-id p0)"
675 " (eq ?a:executable TRUE))"));
676 EXPECT_TRUE(has_fact("((?a plan-action))",
677 "(and (eq ?a:id 1) (eq ?a:goal-id g0) (eq ?a:plan-id p1)"
678 " (eq ?a:executable FALSE))"));
679 EXPECT_TRUE(has_fact("((?a plan-action))",
680 "(and (eq ?a:id 1) (eq ?a:goal-id g0) (eq ?a:plan-id p0)"
681 " (eq ?a:executable TRUE))"));
682 EXPECT_TRUE(has_fact("((?a plan-action))",
683 "(and (eq ?a:id 1) (eq ?a:goal-id g1) (eq ?a:plan-id p0)"
684 " (eq ?a:executable FALSE))"));
685}
686
687/** Test with the conditional-say domain. */
689{
690protected:
691 /** Set up the test environment. */
692 virtual void
694 {
695 clips_files.push_back("conditional_say.clp");
697 }
698};
699
700/** A conditional effect is not applied if the condition does not hold. */
701TEST_F(ConditionalSayDomainTest, DISABLED_DoNotApplyCondEffectIfCondDoesNotHold)
702{
703 env.reset();
704 env.assert_fact("(plan-action"
705 " (id 1)"
706 " (goal-id g0) (plan-id p0)"
707 " (action-name say)"
708 " (param-names s t)"
709 " (param-values front_speaker hello)"
710 ")");
711 env.assert_fact("(apply-action g0 p0 1)");
712 env.run();
713 EXPECT_FALSE(has_fact("((?fact domain-fact))",
714 "(and (eq ?fact:name said) (eq ?fact:param-values (create$ hello)))"));
715}
716
717/** A conditional effect is applied if the condition holds. */
718TEST_F(ConditionalSayDomainTest, ApplyCondEffectIfCondHolds)
719{
720 env.reset();
721 env.assert_fact("(plan-action"
722 " (id 1)"
723 " (goal-id g0) (plan-id p0)"
724 " (action-name say)"
725 " (param-names s t)"
726 " (param-values front_speaker hello)"
727 ")");
728 env.assert_fact("(apply-action g0 p0 1)");
729 env.assert_fact("(domain-fact (name speaker-ready) (param-values front_speaker))");
730 env.run();
731 EXPECT_TRUE(has_fact("((?a plan-action))", "(eq ?a:state FINAL)"));
732 EXPECT_TRUE(has_fact("((?fact domain-fact))",
733 "(and (eq ?fact:name said) (eq ?fact:param-values (create$ hello)))"));
734}
735
736/** A precondition of an exogenous action on a non-value predicate should be
737 * removed if the action has the same predicate with the same parameters as
738 * effect.
739 */
740TEST_F(DomainTest, ExogenousActionWithNonValuePredicatePrecondition)
741{
742 env.reset();
743 env.assert_fact("(domain-operator (name put) (exogenous TRUE)"
744 " (param-names mps wp)"
745 ")");
746 env.assert_fact("(domain-operator-parameter (name mps) (operator put)"
747 "(type object)"
748 ")");
749 env.assert_fact("(domain-operator-parameter (name wp) (operator put)"
750 "(type object)"
751 ")");
752 env.assert_fact("(domain-predicate (name wp-at) (sensed TRUE)"
753 " (param-names mps wp) (param-types object object)"
754 ")");
755 env.assert_fact("(domain-precondition (operator put) (part-of put)"
756 " (name put-precond) (type conjunction)"
757 ")");
758 env.assert_fact("(domain-precondition (operator put) (part-of put-precond)"
759 " (name put-precond1) (type negation)"
760 ")");
761 env.assert_fact("(domain-atomic-precondition (operator put)"
762 " (name put-precond11)"
763 " (part-of put-precond1) (predicate wp-at) (param-names mps wp)"
764 ")");
765 env.assert_fact("(domain-effect (part-of put) (predicate wp-at)"
766 " (param-names mps wp)"
767 ")");
768 env.assert_fact("(plan-action (id 1) (goal-id g0) (plan-id p0)"
769 " (state FORMULATED)"
770 " (action-name put) (param-names mps wp) (param-values C-CS wp1)"
771 ")");
772 env.run();
773 EXPECT_TRUE(has_fact("((?a plan-action))", "(eq ?a:executable TRUE)"));
774}
775
776/** A precondition of an exogenous action on a value predicate should be
777 * replaced by a disjunction of the original precondition and the effect of the
778 * action if the action changes the value of the value predicate.
779 */
780TEST_F(DomainTest, ExogenousActionWithValuePredicatePrecondition)
781{
782 env.reset();
783 env.assert_fact("(domain-operator (name dispense) (exogenous TRUE)"
784 " (param-names mps)"
785 ")");
786 env.assert_fact("(domain-operator-parameter (name mps) (operator dispense)"
787 "(type object)"
788 ")");
789 env.assert_fact("(domain-predicate (name mps-state) (sensed TRUE)"
790 " (param-names mps state) (param-types object object)"
791 " (value-predicate TRUE)"
792 ")");
793 env.assert_fact("(domain-precondition (operator dispense) (part-of dispense)"
794 " (name dispense-precond) (type conjunction)"
795 ")");
796 env.assert_fact("(domain-atomic-precondition (operator dispense)"
797 " (part-of dispense-precond) (predicate mps-state) (param-names mps c)"
798 " (param-constants nil PROCESSING)"
799 ")");
800 env.assert_fact("(domain-effect (part-of dispense) (predicate mps-state)"
801 " (param-names mps c) (param-constants nil READY-AT-OUTPUT)"
802 ")");
803 env.assert_fact("(domain-effect (part-of dispense) (predicate mps-state)"
804 " (type NEGATIVE)"
805 " (param-names mps c) (param-constants nil PROCESSING)"
806 ")");
807 env.assert_fact("(plan-action (id 1) (goal-id g0) (plan-id p0)"
808 " (state FORMULATED)"
809 " (action-name dispense) (param-names mps) (param-values C-CS)"
810 ")");
811 env.run();
812 EXPECT_TRUE(has_fact("((?a plan-action))", "(eq ?a:executable FALSE)"));
813 env.assert_fact("(domain-fact (name mps-state)"
814 " (param-values C-CS READY-AT-OUTPUT)"
815 ")");
816 env.run();
817 EXPECT_TRUE(has_fact("((?a plan-action))", "(eq ?a:executable TRUE)"));
818}
Test with the blocksworld domain.
Definition: test_domain.cpp:50
virtual void SetUp()
Set up the test environment.
Definition: test_domain.cpp:54
Base class for unit testing with CLIPS.
Definition: clips_test.h:36
CLIPS::Environment env
The default CLIPS environment used to run tests.
Definition: clips_test.h:39
virtual void LoadCLIPSFiles(std::vector< std::string > files)
Load the vector of CLIPS files into the environment.
Definition: clips_test.h:43
Test with the conditional-say domain.
virtual void SetUp()
Set up the test environment.
Test setup for domain tests.
Definition: test_domain.cpp:31
vector< string > clips_files
These files are loaded during setup by default.
Definition: test_domain.cpp:45
virtual void SetUp()
Set up the test environment.
Definition: test_domain.cpp:35