Skip to content

Commit

Permalink
test: add tests for openstacks det domain
Browse files Browse the repository at this point in the history
  • Loading branch information
marcofavorito committed Jul 9, 2023
1 parent 696a305 commit d4c76e7
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 54 deletions.
1 change: 1 addition & 0 deletions tests/helpers/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

BENCHMARKS_DIR = TEST_DIR / "benchmarks"
BLOCKSWORLD_DIR = BENCHMARKS_DIR / "deterministic" / "BF" / "blocksworld_ppltl"
OPENSTACKS_DIR = BENCHMARKS_DIR / "deterministic" / "BF" / "openstacks_ppltl"


DOCKER_DIR = TEST_DIR / "docker"
Expand Down
91 changes: 91 additions & 0 deletions tests/test_compiler/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
#
# Copyright 2021 -- 2023 WhiteMech
#
# ------------------------------
#
# This file is part of Plan4Past.
#
# Plan4Past is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Plan4Past is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Plan4Past. If not, see <https://www.gnu.org/licenses/>.
#

"""Base test module for testing the compiler module."""
from abc import abstractmethod
from pathlib import Path
from typing import Tuple

import pytest
from pddl.parser.domain import DomainParser
from pddl.parser.problem import ProblemParser
from pylogics.parsers import parse_pltl

from plan4past.compiler import Compiler
from tests.helpers.misc import check_compilation
from tests.helpers.planutils.base import BasePlannerWrapper
from tests.helpers.planutils.val import VALWrapper


class BaseCompilerTest:
"""Base test class for compiler tests."""

PATH_TO_DOMAINS_DIR: Path
PATH_TO_INSTANCES_DIR: Path

@abstractmethod
def make_formula(self, instance_id: int, domain: Path, problem: Path) -> str:
"""Make the formula for the given instance."""
raise NotImplementedError

@abstractmethod
def get_expected_plan(self, instance_id: int) -> Tuple[str, ...]:
"""Get the expected plan for the given instance."""
raise NotImplementedError

def get_instance_file(self, instance_id: int) -> Path:
"""Get the instance file for the given instance."""
return self.PATH_TO_INSTANCES_DIR / f"p{instance_id}.pddl"

def get_domain_file(self, _instance_id: int) -> Path:
"""Get the domain file for the given instance."""
return self.PATH_TO_DOMAINS_DIR / "domain.pddl"

def _test_instance(
self, instance_id: int, val: VALWrapper, planner: BasePlannerWrapper
) -> None:
"""Test the instance with the given id."""
domain_parser = DomainParser()
problem_parser = ProblemParser()

domain_path = self.get_domain_file(instance_id)
problem_path = self.get_instance_file(instance_id)
if not domain_path.exists():
pytest.fail(f"Domain {domain_path} does not exist.")
if not problem_path.exists():
pytest.fail(f"Instance {problem_path} does not exist.")

domain = domain_parser(domain_path.read_text(encoding="utf-8"))
problem = problem_parser(problem_path.read_text(encoding="utf-8"))
goal = parse_pltl(self.make_formula(instance_id, domain_path, problem_path))

compiler = Compiler(domain, problem, goal)
compiler.compile()
compiled_domain, compiled_problem = compiler.result

check_compilation(
compiled_domain,
compiled_problem,
val,
planner,
self.get_expected_plan(instance_id),
)
63 changes: 9 additions & 54 deletions tests/test_compiler/test_blocksworld_det.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,69 +21,22 @@
#

"""This module contain tests for the compiler module, using the blocksworld domain."""
from abc import abstractmethod
from pathlib import Path
from typing import Tuple

import pytest
from pddl.parser.domain import DomainParser
from pddl.parser.problem import ProblemParser
from pylogics.parsers import parse_pltl

from plan4past.compiler import Compiler
from tests.helpers.constants import BLOCKSWORLD_DIR
from tests.helpers.misc import check_compilation
from tests.helpers.planutils.base import BasePlannerWrapper
from tests.helpers.planutils.val import VALWrapper
from tests.test_compiler.base import BaseCompilerTest


class BaseTestBlocksworldDet:
"""Base test class for deterministic Blocksworld experiments."""
class TestBlocksworldDetSimpleSequence(BaseCompilerTest):
"""Test class for deterministic Blocksworld experiments, simple sequence."""

PATH_TO_DOMAIN = BLOCKSWORLD_DIR / "domain.pddl"
PATH_TO_DOMAINS_DIR = BLOCKSWORLD_DIR
PATH_TO_INSTANCES_DIR = BLOCKSWORLD_DIR

@abstractmethod
def make_formula(self, instance_id: int, domain: Path, problem: Path) -> str:
"""Make the formula for the given instance."""
raise NotImplementedError

@abstractmethod
def get_expected_plan(self, instance_id: int) -> Tuple[str, ...]:
"""Get the expected plan for the given instance."""
raise NotImplementedError

def _test_instance(
self, instance_id: int, val: VALWrapper, planner: BasePlannerWrapper
) -> None:
"""Test the instance with the given id."""
domain_parser = DomainParser()
problem_parser = ProblemParser()

domain_path = self.PATH_TO_DOMAIN
problem_path = self.PATH_TO_INSTANCES_DIR / f"p{instance_id}.pddl"
if not problem_path.exists():
pytest.fail(f"Instance {instance_id} does not exist.")

domain = domain_parser(domain_path.read_text(encoding="utf-8"))
problem = problem_parser(problem_path.read_text(encoding="utf-8"))
goal = parse_pltl(self.make_formula(instance_id, domain_path, problem_path))

compiler = Compiler(domain, problem, goal)
compiler.compile()
compiled_domain, compiled_problem = compiler.result

check_compilation(
compiled_domain,
compiled_problem,
val,
planner,
self.get_expected_plan(instance_id),
)


class TestBlocksworldDetSimpleSequence(BaseTestBlocksworldDet):
"""Test class for deterministic Blocksworld experiments, simple sequence."""
MIN_INSTANCE_ID = 2
MAX_INSTANCE_ID = 10

def make_formula(self, instance_id: int, domain: Path, problem: Path) -> str:
"""
Expand All @@ -109,7 +62,9 @@ def get_expected_plan(self, instance_id: int) -> Tuple[str, ...]:
result.append(f"(stack b{i} b{i + 1})")
return tuple(result)

@pytest.mark.parametrize("instance_id", list(range(2, 11)))
@pytest.mark.parametrize(
"instance_id", list(range(MIN_INSTANCE_ID, MAX_INSTANCE_ID + 1))
)
def test_run(self, instance_id, val, default_planner):
"""Test the instance with the given id."""
self._test_instance(instance_id, val=val, planner=default_planner)
197 changes: 197 additions & 0 deletions tests/test_compiler/test_openstacks_det.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# -*- coding: utf-8 -*-
#
# Copyright 2021 -- 2023 WhiteMech
#
# ------------------------------
#
# This file is part of Plan4Past.
#
# Plan4Past is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Plan4Past is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Plan4Past. If not, see <https://www.gnu.org/licenses/>.
#

"""This module contain tests for the compiler module, using the openstacks domain."""
import json
from pathlib import Path
from typing import Tuple

import pytest

from tests.helpers.constants import OPENSTACKS_DIR
from tests.test_compiler.base import BaseCompilerTest


class TestOpenStacksDet(BaseCompilerTest):
"""Test class for deterministic OpenStacks experiments."""

PATH_TO_DOMAINS_DIR = OPENSTACKS_DIR
PATH_TO_INSTANCES_DIR = OPENSTACKS_DIR
TEGS = OPENSTACKS_DIR / "openstacks_teg.json"
MIN_INSTANCE_ID = 1
MAX_INSTANCE_ID = 5
EXPECTED_PLANS = {
"p01": (
"(setup-machine-p1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o1-n1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o3-n1-n0 )",
"(make-product-p1-n0 )",
"(setup-machine-p2-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o5-n1-n0 )",
"(make-product-p2-n0 )",
"(ship-order-o1-n0-n1 )",
"(setup-machine-p3-n1 )",
"(start-order-o4-n1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o2-n1-n0 )",
"(make-product-p3-n0 )",
"(setup-machine-p4-n0 )",
"(make-product-p4-n0 )",
"(ship-order-o5-n0-n1 )",
"(setup-machine-p5-n1 )",
"(make-product-p5-n1 )",
"(ship-order-o3-n1-n2 )",
"(ship-order-o4-n2-n3 )",
"(ship-order-o2-n3-n4 )",
),
"p02": (
"(setup-machine-p1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o1-n1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o3-n1-n0 )",
"(make-product-p1-n0 )",
"(setup-machine-p2-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o4-n1-n0 )",
"(make-product-p2-n0 )",
"(ship-order-o1-n0-n1 )",
"(setup-machine-p3-n1 )",
"(start-order-o2-n1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o5-n1-n0 )",
"(make-product-p3-n0 )",
"(setup-machine-p4-n0 )",
"(make-product-p4-n0 )",
"(setup-machine-p5-n0 )",
"(make-product-p5-n0 )",
"(ship-order-o3-n0-n1 )",
"(ship-order-o4-n1-n2 )",
"(ship-order-o2-n2-n3 )",
"(ship-order-o5-n3-n4 )",
),
"p03": (
"(setup-machine-p1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o1-n1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o4-n1-n0 )",
"(make-product-p1-n0 )",
"(setup-machine-p2-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o5-n1-n0 )",
"(make-product-p2-n0 )",
"(ship-order-o1-n0-n1 )",
"(setup-machine-p3-n1 )",
"(start-order-o2-n1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o3-n1-n0 )",
"(make-product-p3-n0 )",
"(setup-machine-p4-n0 )",
"(make-product-p4-n0 )",
"(ship-order-o4-n0-n1 )",
"(ship-order-o2-n1-n2 )",
"(setup-machine-p5-n2 )",
"(make-product-p5-n2 )",
"(ship-order-o5-n2-n3 )",
"(ship-order-o3-n3-n4 )",
),
"p04": (
"(setup-machine-p1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o1-n1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o4-n1-n0 )",
"(make-product-p1-n0 )",
"(setup-machine-p2-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o5-n1-n0 )",
"(make-product-p2-n0 )",
"(ship-order-o1-n0-n1 )",
"(setup-machine-p3-n1 )",
"(start-order-o3-n1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o2-n1-n0 )",
"(make-product-p3-n0 )",
"(setup-machine-p4-n0 )",
"(make-product-p4-n0 )",
"(ship-order-o5-n0-n1 )",
"(setup-machine-p5-n1 )",
"(make-product-p5-n1 )",
"(ship-order-o4-n1-n2 )",
"(ship-order-o3-n2-n3 )",
"(ship-order-o2-n3-n4 )",
),
"p05": (
"(setup-machine-p1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o1-n1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o5-n1-n0 )",
"(make-product-p1-n0 )",
"(setup-machine-p2-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o4-n1-n0 )",
"(make-product-p2-n0 )",
"(ship-order-o1-n0-n1 )",
"(setup-machine-p3-n1 )",
"(start-order-o2-n1-n0 )",
"(open-new-stack-n0-n1 )",
"(start-order-o3-n1-n0 )",
"(make-product-p3-n0 )",
"(setup-machine-p4-n0 )",
"(make-product-p4-n0 )",
"(ship-order-o5-n0-n1 )",
"(ship-order-o2-n1-n2 )",
"(setup-machine-p5-n2 )",
"(make-product-p5-n2 )",
"(ship-order-o4-n2-n3 )",
"(ship-order-o3-n3-n4 )",
),
}

def make_formula(self, instance_id: int, domain: Path, problem: Path) -> str:
"""Make the formula for the given instance."""
tegs = json.loads(self.TEGS.read_text())
return tegs[f"p{instance_id:02d}"]

def get_domain_file(self, instance_id: int) -> Path:
"""Get the domain file for the given instance."""
return self.PATH_TO_DOMAINS_DIR / f"domain_p{instance_id:02d}.pddl"

def get_instance_file(self, instance_id: int) -> Path:
"""Get the instance file for the given instance."""
return self.PATH_TO_INSTANCES_DIR / f"p{instance_id:02d}.pddl"

def get_expected_plan(self, instance_id: int) -> Tuple[str, ...]:
"""Get the expected plan for the given instance."""
return self.EXPECTED_PLANS[f"p{instance_id:02d}"]

@pytest.mark.parametrize(
"instance_id", list(range(MIN_INSTANCE_ID, MAX_INSTANCE_ID + 1))
)
def test_run(self, instance_id, val, default_planner):
"""Test the instance with the given id."""
self._test_instance(instance_id, val=val, planner=default_planner)

0 comments on commit d4c76e7

Please sign in to comment.