packages feed

swarm-0.7.0.0: test/unit/TestPretty.hs

{-# LANGUAGE OverloadedStrings #-}

-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Swarm unit tests
module TestPretty where

import Data.Fix (Fix (..))
import Swarm.Language.Syntax hiding (mkOp)
import Swarm.Language.Types
import Swarm.Pretty
import Test.Tasty
import Test.Tasty.HUnit
import Witch (into)

testPrettyConst :: TestTree
testPrettyConst =
  testGroup
    "Language - pretty"
    [ testCase
        "operators #8 - function application unchanged"
        ( equalPrettyLine "f say" $
            TApp (TVar "f") (TConst Say)
        )
    , testCase
        "operators #8 - double function application unchanged"
        ( equalPrettyLine "f () ()" $
            TApp (TApp (TVar "f") TUnit) TUnit
        )
    , testCase
        "operators #8 - embrace operator parameter"
        ( equalPrettyLine "f (==)" $
            TApp (TVar "f") (TConst Eq)
        )
    , testCase
        "operators #8 - unary negation"
        ( equalPrettyLine "-3" $
            TApp (TConst Neg) (TInt 3)
        )
    , testCase
        "operators #8 - double unary negation"
        ( equalPrettyLine "-(-1)" $
            TApp (TConst Neg) $
              TApp (TConst Neg) (TInt 1)
        )
    , testCase
        "operators #8 - unary negation with strongly fixing binary operator"
        ( equalPrettyLine "-1 ^ (-2)"
            . TApp (TConst Neg)
            . mkOp' Exp (TInt 1)
            $ TApp (TConst Neg) (TInt 2)
        )
    , testCase
        "operators #8 - unary negation with weakly fixing binary operator"
        ( equalPrettyLine "-(1 + -2)"
            . TApp (TConst Neg)
            . mkOp' Add (TInt 1)
            $ TApp (TConst Neg) (TInt 2)
        )
    , testCase
        "operators #8 - simple infix operator"
        ( equalPrettyLine "1 == 2" $
            mkOp' Eq (TInt 1) (TInt 2)
        )
    , testCase
        "operators #8 - infix operator with less fixing inner operator"
        ( equalPrettyLine "1 * (2 + 3)" $
            mkOp' Mul (TInt 1) (mkOp' Add (TInt 2) (TInt 3))
        )
    , testCase
        "operators #8 - infix operator with more fixing inner operator"
        ( equalPrettyLine "1 + 2 * 3" $
            mkOp' Add (TInt 1) (mkOp' Mul (TInt 2) (TInt 3))
        )
    , testCase
        "operators #8 - infix operator right associativity"
        ( equalPrettyLine "2 ^ 4 ^ 8" $
            mkOp' Exp (TInt 2) (mkOp' Exp (TInt 4) (TInt 8))
        )
    , testCase
        "operators #8 - infix operator right associativity not applied to left"
        ( equalPrettyLine "(2 ^ 4) ^ 8" $
            mkOp' Exp (mkOp' Exp (TInt 2) (TInt 4)) (TInt 8)
        )
    , testCase
        "pairs #225 - nested pairs are printed right-associative"
        ( equalPrettyLine "(1, 2, 3)" $
            TPair (TInt 1) (TPair (TInt 2) (TInt 3))
        )
    , testCase
        "type ascription"
        ( equalPrettyLine "1 : Int" $
            TAnnotate (TInt 1) (mkTrivPoly TyInt)
        )
    , testCase
        "lambda precedence (#1468)"
        ( equalPrettyLine "\\m. case m (\\x. x + 1) (\\y. y * 2)" $
            TLam
              "m"
              Nothing
              ( TConst Case
                  :$: STerm (TVar "m")
                  :$: STerm (TLam "x" Nothing (mkOp' Add (TVar "x") (TInt 1)))
                  :$: STerm (TLam "y" Nothing (mkOp' Mul (TVar "y") (TInt 2)))
              )
        )
    , testGroup
        "types"
        [ testCase
            "Void type"
            (equalPrettyLine "Void" TyVoid)
        , testCase
            "Unit type"
            (equalPrettyLine "Unit" TyUnit)
        , testCase
            "Function type"
            (equalPrettyLine "Int -> Cmd Unit" $ TyInt :->: TyCmd TyUnit)
        , testCase
            "Cmd type"
            (equalPrettyLine "Cmd (Int -> Int)" $ TyCmd (TyInt :->: TyInt))
        , testCase
            "Product type"
            (equalPrettyLine "Int * Int" $ TyInt :*: TyInt)
        , testCase
            "Sum type"
            (equalPrettyLine "Int + Int" $ TyInt :+: TyInt)
        , testCase
            "Sum of sum right"
            (equalPrettyLine "Int + (Unit + Bool)" $ TyInt :+: (TyUnit :+: TyBool))
        , testCase
            "Sum of sum left"
            (equalPrettyLine "(Int + Unit) + Bool" $ (TyInt :+: TyUnit) :+: TyBool)
        , testCase
            "Product of product right"
            (equalPrettyLine "Int * (Unit * Bool)" $ TyInt :*: (TyUnit :*: TyBool))
        , testCase
            "Product of product left"
            (equalPrettyLine "(Int * Unit) * Bool" $ (TyInt :*: TyUnit) :*: TyBool)
        , testCase
            "Product of sum"
            (equalPrettyLine "Int * (Unit + Bool)" $ TyInt :*: (TyUnit :+: TyBool))
        , testCase
            "Sum of product"
            (equalPrettyLine "Int + (Unit * Bool)" $ TyInt :+: (TyUnit :*: TyBool))
        , testCase
            "Product of function"
            (equalPrettyLine "Int * (Unit -> Bool)" $ TyInt :*: (TyUnit :->: TyBool))
        , testCase
            "Function of product"
            (equalPrettyLine "Int -> (Unit * Bool)" $ TyInt :->: (TyUnit :*: TyBool))
        , testCase
            "Function of function right"
            (equalPrettyLine "Int -> Unit -> Bool" $ TyInt :->: (TyUnit :->: TyBool))
        , testCase
            "Function of function left"
            (equalPrettyLine "(Int -> Unit) -> Bool" $ (TyInt :->: TyUnit) :->: TyBool)
        , testCase
            "density (two nested products)"
            ( equalPrettyLine "((Int * Int) * (Int * Int)) -> Cmd Int" $
                ((TyInt :*: TyInt) :*: (TyInt :*: TyInt)) :->: TyCmd TyInt
            )
        ]
    , testGroup
        "types but with limited width for pretty printing"
        [ testCase
            "Void type"
            (equalPrettyWidth 10 "Void" TyVoid)
        , testCase
            "Function type"
            (equalPrettyWidth 20 "Int -> Cmd Unit" $ TyInt :->: TyCmd TyUnit)
        , testCase
            "Cmd type"
            (equalPrettyWidth 20 "Cmd (Int -> Int)" $ TyCmd (TyInt :->: TyInt))
        , testCase
            "Product type"
            (equalPrettyWidth 20 "Int * Int" $ TyInt :*: TyInt)
        , testCase
            "Function of function right"
            (equalPrettyWidth 10 "Int ->\nUnit ->\nBool" $ TyInt :->: (TyUnit :->: TyBool))
        , testCase
            "Function of function left"
            (equalPrettyWidth 20 "(Int -> Unit) ->\nBool" $ (TyInt :->: TyUnit) :->: TyBool)
        , testCase
            "density (two nested products) with  nested indentation"
            ( equalPrettyWidth 20 "(\n  (Int * Int) * (\n    Int * Int\n  )\n) -> Cmd Int" $
                ((TyInt :*: TyInt) :*: (TyInt :*: TyInt)) :->: TyCmd TyInt
            )
        , testCase
            "Resonate"
            ( equalPrettyWidth 40 "Text -> ((Int * Int) * (Int * Int)) ->\nCmd (Unit + (Int * Int))" $
                TyText :->: ((TyInt :*: TyInt) :*: (TyInt :*: TyInt)) :->: TyCmd (TyUnit :+: (TyInt :*: TyInt))
            )
        ]
    , testGroup
        "tydef"
        [ testCase "tydef alias" $
            equalPrettyLine "tydef X = Int end" $
              TTydef "X" (mkTrivPoly TyInt) Nothing (TConst Noop)
        , testCase "tydef Maybe" $
            equalPrettyLine "tydef Maybe a = Unit + a end" $
              TTydef "Maybe" (mkQPoly (TyUnit :+: TyVar "a")) Nothing (TConst Noop)
        , testCase "tydef multi-arg" $
            equalPrettyLine "tydef Foo a b c d = Unit + ((a * b) + ((c -> d) * a)) end" $
              TTydef
                "Foo"
                ( mkQPoly
                    (TyUnit :+: (TyVar "a" :*: TyVar "b") :+: ((TyVar "c" :->: TyVar "d") :*: TyVar "a"))
                )
                Nothing
                (TConst Noop)
        , testCase "consecutive tydef" $
            equalPrettyLine "tydef X = Int end\n\ntydef Y = Bool end" $
              TTydef "X" (mkTrivPoly TyInt) Nothing (TTydef "Y" (mkTrivPoly TyBool) Nothing (TConst Noop))
        ]
    , testGroup
        "recursive types"
        [ testCase "nat" $
            equalPrettyLine "rec n. Unit + n" $
              TyRec "n" (TyUnit :+: Fix (TyRecVarF NZ))
        , testCase "list" $
            equalPrettyLine "rec list. Unit + (a * list)" $
              TyRec "list" (TyUnit :+: (TyVar "a" :*: Fix (TyRecVarF NZ)))
        , testCase "rose" $
            equalPrettyLine "rec r. a * (rec l. Unit + (r * l))" $
              TyRec "r" (TyVar "a" :*: TyRec "l" (TyUnit :+: (Fix (TyRecVarF (NS NZ)) :*: Fix (TyRecVarF NZ))))
        ]
    ]
 where
  equalPrettyLine :: PrettyPrec a => String -> a -> Assertion
  equalPrettyLine expected = assertEqual "" expected . into @String . prettyTextLine

  equalPrettyWidth :: PrettyPrec a => Int -> String -> a -> Assertion
  equalPrettyWidth width expected doc = assertEqual "" expected . into @String $ prettyTextWidth doc width