#include #include #include #include #include #include #include #include #include #include #include #include using namespace plasp::pddl; //////////////////////////////////////////////////////////////////////////////////////////////////// TEST_CASE("[PDDL parser] The Blocks World domain is parsed correctly", "[PDDL parser]") { plasp::output::Logger logger; Context context(Parser(), logger); const auto description = Description::fromFile("data/blocksworld-domain.pddl", context); REQUIRE_NOTHROW(description.domain()); const auto &domain = description.domain(); // Name CHECK(domain.name() == "blocks"); // Requirements REQUIRE(domain.requirements().size() == 2u); CHECK(domain.requirements()[0].type() == Requirement::Type::STRIPS); CHECK(domain.requirements()[1].type() == Requirement::Type::Typing); // Types REQUIRE(domain.types().size() == 1u); const auto &block = *domain.types()[0]; CHECK(block.name() == "block"); REQUIRE(block.parentTypes().size() == 0u); // Predicates REQUIRE(domain.predicates().size() == 5u); const auto &on = *domain.predicates()[0]; CHECK(on.name() == "on"); REQUIRE(on.parameters().size() == 2u); CHECK(on.parameters()[0]->name() == "x"); const auto &onArgument0Type = on.parameters()[0]->type()->as(); CHECK(&onArgument0Type == &block); CHECK(on.parameters()[1]->name() == "y"); const auto &onArgument1Type = on.parameters()[1]->type()->as(); CHECK(&onArgument1Type == &block); const auto &handempty = *domain.predicates()[3]; CHECK(handempty.name() == "handempty"); CHECK(handempty.parameters().empty()); // Actions REQUIRE(domain.actions().size() == 4u); const auto &pickUp = *domain.actions()[0]; CHECK(pickUp.name() == "pick-up"); REQUIRE(pickUp.parameters().size() == 1u); CHECK(pickUp.parameters()[0]->name() == "x"); CHECK(pickUp.parameters()[0]->type() == &block); const auto &pickUpPre = pickUp.precondition()->as(); REQUIRE(pickUpPre.arguments().size() == 3u); const auto &pickUpPre0 = pickUpPre.arguments()[0]->as(); CHECK(pickUpPre0.name() == "clear"); REQUIRE(pickUpPre0.arguments().size() == 1u); const auto &pickUpPre00 = pickUpPre0.arguments()[0]->as(); CHECK(pickUpPre00.name() == "x"); CHECK(pickUpPre00.type() == &block); CHECK(&pickUpPre00 == pickUp.parameters()[0].get()); const auto &pickUpPre2 = pickUpPre.arguments()[2]->as(); CHECK(pickUpPre2.name() == "handempty"); CHECK(pickUpPre2.arguments().empty()); const auto &pickUpEff = pickUp.effect()->as(); REQUIRE(pickUpEff.arguments().size() == 4u); const auto &pickUpEff0 = pickUpEff.arguments()[0]->as(); const auto &pickUpEff00 = pickUpEff0.argument()->as(); CHECK(pickUpEff00.name() == "ontable"); REQUIRE(pickUpEff00.arguments().size() == 1u); const auto &pickUpEff000 = pickUpEff00.arguments()[0]->as(); CHECK(pickUpEff000.name() == "x"); CHECK(pickUpEff000.type() == &block); } //////////////////////////////////////////////////////////////////////////////////////////////////// TEST_CASE("[PDDL parser] A Blocks World problem is parsed correctly", "[PDDL parser]") { plasp::output::Logger logger; Context context(Parser(), logger); const auto description = Description::fromFiles({"data/blocksworld-domain.pddl", "data/blocksworld-problem.pddl"}, context); REQUIRE_NOTHROW(description.problem()); const auto &problem = description.problem(); // Name CHECK(problem.name() == "blocks-4-0"); CHECK(problem.domain().name() == "blocks"); // Requirements // TODO: compute domain vs. problem requirements correctly and check them // Objects REQUIRE(problem.objects().size() == 4u); CHECK(problem.objects()[0]->name() == "d"); REQUIRE(problem.objects()[0]->type() != nullptr); CHECK(problem.objects()[0]->type()->name() == "block"); CHECK(problem.objects()[3]->name() == "c"); REQUIRE(problem.objects()[3]->type() != nullptr); CHECK(problem.objects()[3]->type()->name() == "block"); // Initial State const auto &facts = problem.initialState().facts(); REQUIRE(facts.size() == 9u); const auto &fact0 = facts[0].get()->as(); CHECK(fact0.name() == "clear"); REQUIRE(fact0.arguments().size() == 1u); const auto &fact00 = fact0.arguments()[0]->as(); CHECK(fact00.name() == "c"); REQUIRE(fact00.type() != nullptr); CHECK(fact00.type()->name() == "block"); const auto &fact8 = facts[8].get()->as(); CHECK(fact8.name() == "handempty"); REQUIRE(fact8.arguments().size() == 0u); // Goal const auto &goal = problem.goal().as(); REQUIRE(goal.arguments().size() == 3u); const auto &goal0 = goal.arguments()[0]->as(); CHECK(goal0.name() == "on"); REQUIRE(goal0.arguments().size() == 2u); const auto &goal00 = goal0.arguments()[0]->as(); CHECK(goal00.name() == "d"); const auto &goal01 = goal0.arguments()[1]->as(); CHECK(goal01.name() == "c"); const auto &goal2 = goal.arguments()[2]->as(); CHECK(goal2.name() == "on"); REQUIRE(goal2.arguments().size() == 2u); const auto &goal20 = goal2.arguments()[0]->as(); CHECK(goal20.name() == "b"); const auto &goal21 = goal2.arguments()[1]->as(); CHECK(goal21.name() == "a"); } //////////////////////////////////////////////////////////////////////////////////////////////////// TEST_CASE("[PDDL parser] The Storage domain is parsed correctly", "[PDDL parser]") { plasp::output::Logger logger; Context context(Parser(), logger); const auto description = plasp::pddl::Description::fromFile("data/storage-domain.pddl", context); REQUIRE_NOTHROW(description.domain()); const auto &domain = description.domain(); // Name CHECK(domain.name() == "storage-propositional"); // Requirements REQUIRE(domain.requirements().size() == 1u); CHECK(domain.requirements()[0].type() == Requirement::Type::Typing); // Types REQUIRE(domain.types().size() == 10u); const auto &hoist = *domain.types()[0]; const auto &surface = *domain.types()[1]; const auto &area = *domain.types()[3]; const auto &object = *domain.types()[4]; const auto &storearea = *domain.types()[7]; const auto &crate = *domain.types()[9]; const auto &hoistParents = hoist.parentTypes(); REQUIRE(hoistParents.size() == 1u); CHECK(std::find(hoistParents.cbegin(), hoistParents.cend(), &object) != hoistParents.cend()); const auto &areaParents = area.parentTypes(); REQUIRE(areaParents.size() == 2u); CHECK(std::find(areaParents.cbegin(), areaParents.cend(), &object) != areaParents.cend()); CHECK(std::find(areaParents.cbegin(), areaParents.cend(), &surface) != areaParents.cend()); // Predicates REQUIRE(domain.predicates().size() == 8u); const auto &on = *domain.predicates()[5]; CHECK(on.name() == "on"); REQUIRE(on.parameters().size() == 2u); CHECK(on.parameters()[0]->name() == "c"); const auto &onArgument0Type = on.parameters()[0]->type()->as(); CHECK(&onArgument0Type == &crate); CHECK(on.parameters()[1]->name() == "s"); const auto &onArgument1Type = on.parameters()[1]->type()->as(); CHECK(&onArgument1Type == &storearea); const auto &in = *domain.predicates()[1]; CHECK(in.name() == "in"); REQUIRE(in.parameters().size() == 2u); CHECK(in.parameters()[0]->name() == "x"); const auto &inArgument0Type = in.parameters()[0]->type()->as(); REQUIRE(inArgument0Type.arguments().size() == 2u); const auto &inArgument0Type0 = inArgument0Type.arguments()[0]->as(); CHECK(&inArgument0Type0 == &storearea); const auto &inArgument0Type1 = inArgument0Type.arguments()[1]->as(); CHECK(&inArgument0Type1 == &crate); // Actions REQUIRE(domain.actions().size() == 5u); const auto &drop = *domain.actions()[1]; CHECK(drop.name() == "drop"); REQUIRE(drop.parameters().size() == 5u); CHECK(drop.parameters()[3]->name() == "a2"); CHECK(drop.parameters()[3]->type() == &area); const auto &dropPre = drop.precondition()->as(); REQUIRE(dropPre.arguments().size() == 5u); const auto &dropPre2 = dropPre.arguments()[2]->as(); CHECK(dropPre2.name() == "lifting"); REQUIRE(dropPre2.arguments().size() == 2u); const auto &dropPre21 = dropPre2.arguments()[1]->as(); CHECK(dropPre21.name() == "c"); CHECK(dropPre21.type() == &crate); const auto &dropEff = drop.effect()->as(); REQUIRE(dropEff.arguments().size() == 5u); const auto &dropEff2 = dropEff.arguments()[2]->as(); const auto &dropEff20 = dropEff2.argument()->as(); CHECK(dropEff20.name() == "clear"); REQUIRE(dropEff20.arguments().size() == 1u); const auto &dropEff200 = dropEff20.arguments()[0]->as(); CHECK(dropEff200.name() == "a1"); CHECK(dropEff200.type() == &storearea); } //////////////////////////////////////////////////////////////////////////////////////////////////// TEST_CASE("[PDDL parser] A Storage problem is parsed correctly", "[PDDL parser]") { plasp::output::Logger logger; Context context(Parser(), logger); const auto description = Description::fromFiles({"data/storage-domain.pddl", "data/storage-problem.pddl"}, context); REQUIRE_NOTHROW(description.problem()); const auto &problem = description.problem(); // Name CHECK(problem.name() == "storage-1"); CHECK(problem.domain().name() == "storage-propositional"); // Requirements // TODO: compute domain vs. problem requirements correctly and check them // Objects REQUIRE(problem.objects().size() == 7u); CHECK(problem.objects()[0]->name() == "depot0-1-1"); REQUIRE(problem.objects()[0]->type() != nullptr); CHECK(problem.objects()[0]->type()->name() == "storearea"); CHECK(problem.objects()[6]->name() == "loadarea"); REQUIRE(problem.objects()[6]->type() != nullptr); CHECK(problem.objects()[6]->type()->name() == "transitarea"); // Initial State const auto &facts = problem.initialState().facts(); REQUIRE(facts.size() == 10u); const auto &fact0 = facts[0].get()->as(); CHECK(fact0.name() == "in"); REQUIRE(fact0.arguments().size() == 2u); const auto &fact01 = fact0.arguments()[1]->as(); CHECK(fact01.name() == "depot0"); REQUIRE(fact01.type() != nullptr); CHECK(fact01.type()->name() == "depot"); const auto &fact9 = facts[9].get()->as(); CHECK(fact9.name() == "available"); REQUIRE(fact9.arguments().size() == 1u); const auto &fact90 = fact9.arguments()[0]->as(); CHECK(fact90.name() == "hoist0"); REQUIRE(fact90.type() != nullptr); CHECK(fact90.type()->name() == "hoist"); // Goal const auto &goal = problem.goal().as(); REQUIRE(goal.arguments().size() == 1u); const auto &goal0 = goal.arguments()[0]->as(); CHECK(goal0.name() == "in"); REQUIRE(goal0.arguments().size() == 2u); const auto &goal00 = goal0.arguments()[0]->as(); CHECK(goal00.name() == "crate0"); const auto &goal01 = goal0.arguments()[1]->as(); CHECK(goal01.name() == "depot0"); } //////////////////////////////////////////////////////////////////////////////////////////////////// TEST_CASE("[PDDL parser] Constants are parsed correctly", "[PDDL parser]") { plasp::output::Logger logger; Context context(Parser(), logger); const auto description = Description::fromFile("data/woodworking-domain.pddl", context); REQUIRE_NOTHROW(description.domain()); const auto &domain = description.domain(); // Name CHECK(domain.name() == "woodworking"); // Types const auto &acolour = *domain.types()[0]; const auto &surface = *domain.types()[4]; const auto &treatmentstatus = *domain.types()[5]; // Constants REQUIRE(domain.constants().size() == 8u); CHECK(domain.constants()[0]->type() == &surface); CHECK(domain.constants()[2]->type() == &surface); CHECK(domain.constants()[3]->type() == &treatmentstatus); CHECK(domain.constants()[6]->type() == &treatmentstatus); CHECK(domain.constants()[7]->type() == &acolour); // TODO: add test with constants in predicates } //////////////////////////////////////////////////////////////////////////////////////////////////// TEST_CASE("[PDDL parser] White spaces are ignored", "[PDDL parser]") { plasp::output::Logger logger; Context context(Parser(), logger); CHECK_NOTHROW(Description::fromFile("data/white-space-test.pddl", context)); } //////////////////////////////////////////////////////////////////////////////////////////////////// TEST_CASE("[PDDL parser] Missing or unmatching domain descriptions are detected", "[PDDL parser]") { plasp::output::Logger logger; Context context(Parser(), logger); SECTION("") { CHECK_THROWS_AS(Description::fromFile("data/blocksworld-problem.pddl", context), ConsistencyException); } SECTION("") { CHECK_THROWS_AS(Description::fromFiles({"data/blocksworld-problem.pddl", "data/storage-domain.pddl"}, context), parsebase::ParserException); } } //////////////////////////////////////////////////////////////////////////////////////////////////// TEST_CASE("[PDDL parser] Common PDDL syntax errors are detected", "[PDDL parser]") { plasp::output::Logger logger; Context context(Parser(), logger); SECTION("") { CHECK_NOTHROW(Description::fromFile("data/pddl-syntax/domain-valid.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-expressions-1.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-expressions-2.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-expressions-3.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-expression-name-1.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-expression-name-2.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-expression-name-3.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-parentheses-1.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-parentheses-2.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-parentheses-3.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-parentheses-4.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-parentheses-5.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-parentheses-6.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-parentheses-7.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-parentheses-8.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-section-name-1.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-section-name-2.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-section-name-3.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-section-name-4.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-section-name-5.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-section-name-6.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-section-name-7.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-types-1.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-types-2.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-types-3.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-types-4.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-variables-1.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-variables-2.pddl", context)); } SECTION("") { CHECK_THROWS(Description::fromFile("data/pddl-syntax/domain-variables-3.pddl", context)); } } //////////////////////////////////////////////////////////////////////////////////////////////////// TEST_CASE("[PDDL parser] Former issues are fixed", "[PDDL parser]") { plasp::output::Logger logger; Context context(Parser(), logger); SECTION("white space issues with constants and parsing unsupported sections") { CHECK_NOTHROW(Description::fromFile("data/issues/issue-1.pddl", context)); } SECTION("white space issues with empty n-ary predicates") { CHECK_NOTHROW(Description::fromFile("data/issues/issue-2.pddl", context)); } SECTION("comments are correctly ignored") { CHECK_NOTHROW(Description::fromFile("data/issues/issue-3.pddl", context)); } }