packages feed

swarm-0.4: test/unit/TestBoolExpr.hs

{-# LANGUAGE OverloadedStrings #-}

-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Boolean expression unit tests
module TestBoolExpr where

import Data.BoolExpr qualified as BE
import Data.BoolExpr.Simplify qualified as Simplify
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Set qualified as Set
import Swarm.Game.Scenario.Objective.Logic
import Swarm.Game.Scenario.Objective.WinCheck qualified as WC
import Test.Tasty
import Test.Tasty.HUnit

testBoolExpr :: TestTree
testBoolExpr =
  testGroup
    "Boolean evaluation"
    [ testGroup
        "Expression simplification"
        [ testGroup
            "Return true if the expression can be simplified to False"
            [ testGroup
                "Effect of constant literals"
                [ testCase
                    "False input via single literal"
                    $ expectTrue BE.BFalse
                , testCase
                    "True input via composed literals"
                    $ expectFalse BE.BTrue
                , testCase
                    "False input via composed literals"
                    $ expectTrue
                    $ BE.BOr BE.BFalse BE.BFalse
                , testCase
                    "True input via composed literals"
                    $ expectFalse
                    $ BE.BOr BE.BFalse BE.BTrue
                , testCase
                    "Constant OR'd with False"
                    $ expectFalse
                    $ BE.BOr BE.BFalse (BE.BConst (BE.Positive "foo"))
                , testCase
                    "Constant OR'd with True"
                    $ expectFalse
                    $ BE.BOr (BE.BConst (BE.Positive "foo")) BE.BTrue
                , testCase
                    "Constant AND'd with False"
                    $ expectTrue
                    $ BE.BAnd BE.BFalse (BE.BConst (BE.Positive "foo"))
                , testCase
                    "Constant AND'd with True"
                    $ expectFalse
                    $ BE.BAnd (BE.BConst (BE.Positive "foo")) BE.BTrue
                , testCase
                    "Nested Constants AND'd with False within OR"
                    $ expectTrue
                    $ BE.BOr
                      (BE.BAnd BE.BFalse (BE.BConst (BE.Positive "foo")))
                      (BE.BAnd (BE.BConst (BE.Positive "bar")) BE.BFalse)
                , testCase
                    "Deeply nested Constants AND'd with False within OR with multiple negations"
                    $ expectTrue
                    $ BE.BOr
                      (BE.BAnd (BE.BNot BE.BTrue) (BE.BNot (BE.BNot (BE.BNot (BE.BConst (BE.Positive "foo"))))))
                      (BE.BAnd (BE.BConst (BE.Positive "bar")) (BE.BNot (BE.BNot BE.BFalse)))
                ]
            , testGroup
                "Effect of contradicting named constants"
                [ testCase
                    "via NOT operator"
                    $ expectTrue
                    $ BE.BAnd (BE.BNot (BE.BConst (BE.Positive "foo"))) (BE.BConst (BE.Positive "foo"))
                , testCase
                    "via signedness"
                    $ expectTrue
                    $ BE.BAnd (BE.BConst (BE.Positive "foo")) (BE.BConst (BE.Negative "foo"))
                ]
            ]
        ]
    , testGroup
        "Prerequisite expressions"
        [ testCase
            "A negated goal is completed"
            . assertBool "Should have returned true"
            $ WC.isUnwinnablePrereq (Set.singleton "b") demoPrereqs
        , testCase
            "A non-negated goal is completed"
            . assertBool "Should have returned false"
            . not
            $ WC.isUnwinnablePrereq (Set.singleton "c") demoPrereqs
        ]
    ]
 where
  expectTrue, expectFalse :: BE.BoolExpr String -> Assertion
  expectTrue = assertBool "Should have returned true" . Simplify.cannotBeTrue
  expectFalse = assertBool "Should have returned false" . not . Simplify.cannotBeTrue

  demoPrereqs :: Prerequisite ObjectiveLabel
  demoPrereqs =
    And $
      Id "a"
        :| [Not (Id "b"), Id "c"]