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 
27 using namespace std;
28 
29 /** Test setup for domain tests. */
30 class DomainTest : public CLIPSTest
31 {
32 protected:
33  /** Set up the test environment. */
34  virtual void
36  {
37  string logfile = tmpnam(nullptr);
38  env.evaluate("(open \"" + logfile + "\" error \"w\")");
39  LoadCLIPSFiles(clips_files);
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 {
51 protected:
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  */
64 TEST_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  */
75 TEST_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. */
91 TEST_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  */
116 TEST_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  */
143 TEST_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  */
158 TEST_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. */
173 TEST_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... */
181 TEST_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. */
190 TEST_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. */
199 TEST_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. */
208 TEST_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  */
219 TEST_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. */
230 TEST_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  */
252 TEST_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  */
271 TEST_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. */
324 TEST_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. */
365 TEST_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  */
407 TEST_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. */
443 TEST_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  */
477 TEST_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. */
509 TEST_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. */
540 TEST_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  */
587 TEST_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. */
608 TEST_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. */
618 TEST_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 
640 TEST_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  */
660 TEST_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 {
690 protected:
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. */
701 TEST_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. */
718 TEST_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  */
740 TEST_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  */
780 TEST_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 }
ConditionalSayDomainTest
Test with the conditional-say domain.
Definition: test_domain.cpp:688
BlocksworldDomainTest
Test with the blocksworld domain.
Definition: test_domain.cpp:49
DomainTest::SetUp
virtual void SetUp()
Set up the test environment.
Definition: test_domain.cpp:35
CLIPSTest
Base class for unit testing with CLIPS.
Definition: clips_test.h:34
BlocksworldDomainTest::SetUp
virtual void SetUp()
Set up the test environment.
Definition: test_domain.cpp:54
ConditionalSayDomainTest::SetUp
virtual void SetUp()
Set up the test environment.
Definition: test_domain.cpp:693
DomainTest
Test setup for domain tests.
Definition: test_domain.cpp:30