packages feed

hydra-0.15.0: src/main/haskell/Hydra/Sources/Test/Lib/Maps.hs

{-# LANGUAGE FlexibleContexts #-}

module Hydra.Sources.Test.Lib.Maps where

-- Standard imports for shallow DSL tests
import Hydra.Kernel
import Hydra.Dsl.Meta.Testing                 as Testing
import Hydra.Dsl.Meta.Terms                   as Terms
import Hydra.Sources.Kernel.Types.All
import qualified Hydra.Dsl.Meta.Core          as Core
import qualified Hydra.Dsl.Meta.Phantoms      as Phantoms
import qualified Hydra.Dsl.Meta.Types         as T
import qualified Hydra.Sources.Test.TestGraph as TestGraph
import qualified Hydra.Sources.Test.TestTerms as TestTerms
import qualified Hydra.Sources.Test.TestTypes as TestTypes
import qualified Data.List                    as L
import qualified Data.Map                     as M

-- Additional imports specific to this file
import Hydra.Testing
import Hydra.Sources.Libraries
import qualified Hydra.Dsl.Meta.Lib.Equality as Equality
import qualified Hydra.Dsl.Meta.Lib.Literals as Literals
import qualified Hydra.Dsl.Meta.Lib.Maps as Maps
import qualified Hydra.Dsl.Meta.Lib.Math as Math
import qualified Hydra.Dsl.Meta.Lib.Maybes as Maybes
import qualified Hydra.Dsl.Meta.Lib.Strings as Strings
import qualified Hydra.Dsl.Meta.Lib.Chars as Chars
import qualified Hydra.Sources.Kernel.Terms.Show.Core as ShowCore


ns :: Namespace
ns = Namespace "hydra.test.lib.maps"

module_ :: Module
module_ = Module {
            moduleNamespace = ns,
            moduleDefinitions = definitions,
            moduleTermDependencies = [Namespace "hydra.reduction", ShowCore.ns],
            moduleTypeDependencies = kernelTypesNamespaces,
            moduleDescription = Just "Test cases for hydra.lib.maps primitives"}
  where
    definitions = [Phantoms.toDefinition allTests]

(#) :: (AsTerm f (a -> b), AsTerm g a) => f -> g -> TTerm b
(#) = (Phantoms.@@)
infixl 1 #

showInt32 :: TTerm (Int -> String)
showInt32 = Phantoms.lambda "n" $ Literals.showInt32 (Phantoms.var "n")

showString' :: TTerm (String -> String)
showString' = Phantoms.lambda "s" $ Literals.showString (Phantoms.var "s")

showBool :: TTerm (Bool -> String)
showBool = Phantoms.lambda "b" $ Literals.showBoolean (Phantoms.var "b")

showIntStringMap :: TTerm (M.Map Int String -> String)
showIntStringMap = Phantoms.lambda "m" $ ShowCore.map_ # showInt32 # showString' # Phantoms.var "m"

showMaybeString :: TTerm (Maybe String -> String)
showMaybeString = Phantoms.lambda "mx" $ ShowCore.maybe_ # showString' # Phantoms.var "mx"

showIntList :: TTerm ([Int] -> String)
showIntList = Phantoms.lambda "xs" $ ShowCore.list_ # showInt32 # Phantoms.var "xs"

showStringList :: TTerm ([String] -> String)
showStringList = Phantoms.lambda "xs" $ ShowCore.list_ # showString' # Phantoms.var "xs"

showPairList :: TTerm ([(Int, String)] -> String)
showPairList = Phantoms.lambda "xs" $ ShowCore.list_ # (Phantoms.lambda "p" $ ShowCore.pair_ # showInt32 # showString' # Phantoms.var "p") # Phantoms.var "xs"

-- Phantom-typed map helper
pMap :: [(Int, String)] -> TTerm (M.Map Int String)
pMap pairs = Phantoms.map $ M.fromList $ fmap (\(k, v) -> (Phantoms.int32 k, Phantoms.string v)) pairs

pPairList :: [(Int, String)] -> TTerm [(Int, String)]
pPairList pairs = Phantoms.list $ fmap (\(k, v) -> Phantoms.pair (Phantoms.int32 k) (Phantoms.string v)) pairs

-- Test groups

mapsEmpty :: TTerm TestGroup
mapsEmpty = subgroup "empty" [
  test "empty map"]
  where
    test name = evalPair name showIntStringMap
      (Maps.empty :: TTerm (M.Map Int String))
      (pMap [])

mapsSingleton :: TTerm TestGroup
mapsSingleton = subgroup "singleton" [
  test "single entry" 42 "hello" [(42, "hello")]]
  where
    test name k v result = evalPair name showIntStringMap
      (Maps.singleton (Phantoms.int32 k) (Phantoms.string v))
      (pMap result)

mapsFromList :: TTerm TestGroup
mapsFromList = subgroup "fromList" [
  test "create from pairs" [(1, "a"), (2, "b")] [(1, "a"), (2, "b")],
  test "duplicate keys" [(1, "a"), (1, "b")] [(1, "b")],
  test "empty list" [] []]
  where
    test name input expected = evalPair name showIntStringMap
      (Maps.fromList (pPairList input))
      (pMap expected)

mapsToList :: TTerm TestGroup
mapsToList = subgroup "toList" [
  test "convert to pairs" [(1, "a"), (2, "b")] [(1, "a"), (2, "b")],
  test "unsorted keys" [(3, "c"), (1, "a"), (2, "b")] [(1, "a"), (2, "b"), (3, "c")],
  test "empty map" [] []]
  where
    test name input expected = evalPair name showPairList
      (Maps.toList (pMap input))
      (pPairList expected)

mapsInsert :: TTerm TestGroup
mapsInsert = subgroup "insert" [
  test "insert new key" 3 "c" [(1, "a"), (2, "b")] [(1, "a"), (2, "b"), (3, "c")],
  test "update existing" 2 "updated" [(1, "a"), (2, "b")] [(1, "a"), (2, "updated")],
  test "insert into empty" 1 "x" [] [(1, "x")]]
  where
    test name k v m result = evalPair name showIntStringMap
      (Maps.insert (Phantoms.int32 k) (Phantoms.string v) (pMap m))
      (pMap result)

mapsRemove :: TTerm TestGroup
mapsRemove = subgroup "remove" [
  test "remove existing" 2 [(1, "a"), (2, "b"), (3, "c")] [(1, "a"), (3, "c")],
  test "remove non-existing" 4 [(1, "a"), (2, "b")] [(1, "a"), (2, "b")],
  test "remove from empty" 1 [] []]
  where
    test name k m result = evalPair name showIntStringMap
      (Maps.delete (Phantoms.int32 k) (pMap m))
      (pMap result)

mapsLookup :: TTerm TestGroup
mapsLookup = subgroup "lookup" [
  test "find existing key" 2 [(1, "a"), (2, "b")] (Just "b"),
  test "key not found" 3 [(1, "a"), (2, "b")] Nothing,
  test "lookup in empty" 1 [] Nothing]
  where
    test name k m result = evalPair name showMaybeString
      (Maps.lookup (Phantoms.int32 k) (pMap m))
      (maybe Phantoms.nothing (Phantoms.just . Phantoms.string) result)

mapsMember :: TTerm TestGroup
mapsMember = subgroup "member" [
  test "key exists" 2 [(1, "a"), (2, "b")] True,
  test "key missing" 3 [(1, "a"), (2, "b")] False,
  test "empty map" 1 [] False]
  where
    test name k m result = evalPair name showBool
      (Maps.member (Phantoms.int32 k) (pMap m))
      (Phantoms.boolean result)

mapsSize :: TTerm TestGroup
mapsSize = subgroup "size" [
  test "three entries" [(1, "a"), (2, "b"), (3, "c")] 3,
  test "single entry" [(42, "test")] 1,
  test "empty map" [] 0]
  where
    test name m result = evalPair name showInt32
      (Maps.size (pMap m))
      (Phantoms.int32 result)

mapsNull :: TTerm TestGroup
mapsNull = subgroup "null" [
  test "empty map" [] True,
  test "non-empty map" [(1, "a")] False]
  where
    test name m result = evalPair name showBool
      (Maps.null (pMap m))
      (Phantoms.boolean result)

mapsKeys :: TTerm TestGroup
mapsKeys = subgroup "keys" [
  test "get all keys" [(1, "a"), (2, "b"), (3, "c")] [1, 2, 3],
  test "unsorted keys" [(3, "c"), (1, "a"), (2, "b")] [1, 2, 3],
  test "empty map" [] []]
  where
    test name m result = evalPair name showIntList
      (Maps.keys (pMap m))
      (Phantoms.list $ Phantoms.int32 <$> result)

mapsElems :: TTerm TestGroup
mapsElems = subgroup "elems" [
  test "get all elements" [(1, "a"), (2, "b")] ["a", "b"],
  test "unsorted keys" [(3, "c"), (1, "a"), (2, "b")] ["a", "b", "c"],
  test "empty map" [] []]
  where
    test name m result = evalPair name showStringList
      (Maps.elems (pMap m))
      (Phantoms.list $ Phantoms.string <$> result)

mapsMap :: TTerm TestGroup
mapsMap = subgroup "map" [
  test "map over values" [(1, "a"), (2, "b")] [(1, "A"), (2, "B")],
  test "map empty" [] []]
  where
    test name m result = evalPair name showIntStringMap
      (Maps.map (Phantoms.lambda "s" $ Strings.toUpper (Phantoms.var "s")) (pMap m))
      (pMap result)

mapsFindWithDefault :: TTerm TestGroup
mapsFindWithDefault = subgroup "findWithDefault" [
  test "find existing" "default" 2 [(1, "a"), (2, "b")] "b",
  test "use default" "default" 3 [(1, "a"), (2, "b")] "default"]
  where
    test name def k m result = stringEvalPair name
      (Maps.findWithDefault (Phantoms.string def) (Phantoms.int32 k) (pMap m))
      (Phantoms.string result)

mapsUnion :: TTerm TestGroup
mapsUnion = subgroup "union" [
  test "union two maps" [(1, "a"), (2, "b")] [(2, "x"), (3, "c")] [(1, "a"), (2, "b"), (3, "c")],
  test "union with empty" [(1, "a")] [] [(1, "a")],
  test "empty with map" [] [(1, "a")] [(1, "a")]]
  where
    test name m1 m2 result = evalPair name showIntStringMap
      (Maps.union (pMap m1) (pMap m2))
      (pMap result)

mapsMapKeys :: TTerm TestGroup
mapsMapKeys = subgroup "mapKeys" [
  test "double keys" [(1, "a"), (2, "b")] [(2, "a"), (4, "b")],
  test "empty map" [] []]
  where
    test name m result = evalPair name showIntStringMap
      (Maps.mapKeys (Phantoms.lambda "k" $ Math.mul (Phantoms.var "k") (Phantoms.int32 2)) (pMap m))
      (pMap result)

mapsFilter :: TTerm TestGroup
mapsFilter = subgroup "filter" [
  test "filter values starting with a" [(1, "a"), (2, "b"), (3, "ab")] [(1, "a"), (3, "ab")],
  test "filter all" [(1, "b"), (2, "c")] [],
  test "empty map" [] []]
  where
    test name m result = evalPair name showIntStringMap
      (Maps.filter
        (Phantoms.lambda "v" $ Equality.equal
          (Maybes.fromMaybe (Phantoms.int32 0)
            (Maybes.map (Phantoms.lambda "c" $ Chars.toLower (Phantoms.var "c"))
              (Strings.maybeCharAt (Phantoms.int32 0) (Phantoms.var "v"))))
          (Phantoms.int32 97))
        (pMap m))
      (pMap result)

mapsFilterWithKey :: TTerm TestGroup
mapsFilterWithKey = subgroup "filterWithKey" [
  test "filter by key > 1" [(1, "a"), (2, "b"), (3, "c")] [(2, "b"), (3, "c")],
  test "filter all" [(1, "a")] [],
  test "empty map" [] []]
  where
    test name m result = evalPair name showIntStringMap
      (Maps.filterWithKey (Phantoms.lambda "k" $ Phantoms.lambda "v" $ Equality.gt (Phantoms.var "k") (Phantoms.int32 1)) (pMap m))
      (pMap result)

mapsBimap :: TTerm TestGroup
mapsBimap = subgroup "bimap" [
  test "transform both" [(1, "a"), (2, "b")] [(2, "A"), (4, "B")],
  test "empty map" [] []]
  where
    test name m result = evalPair name showIntStringMap
      (Maps.bimap
        (Phantoms.lambda "k" $ Math.mul (Phantoms.var "k") (Phantoms.int32 2))
        (Phantoms.lambda "v" $ Strings.toUpper (Phantoms.var "v"))
        (pMap m))
      (pMap result)

mapsAlter :: TTerm TestGroup
mapsAlter = subgroup "alter" [
  testInsert "insert new key" 3 [(1, "a"), (2, "b")] [(1, "a"), (2, "b"), (3, "new")],
  testUpdate "update existing key" 2 [(1, "a"), (2, "b")] [(1, "a"), (2, "updated")],
  testDelete "delete key" 2 [(1, "a"), (2, "b")] [(1, "a")]]
  where
    testInsert name k m result = evalPair name showIntStringMap
      (Maps.alter (Phantoms.lambda "_" $ Phantoms.just (Phantoms.string "new")) (Phantoms.int32 k) (pMap m))
      (pMap result)
    testUpdate name k m result = evalPair name showIntStringMap
      (Maps.alter (Phantoms.lambda "_" $ Phantoms.just (Phantoms.string "updated")) (Phantoms.int32 k) (pMap m))
      (pMap result)
    testDelete name k m result = evalPair name showIntStringMap
      (Maps.alter (Phantoms.lambda "_" $ (Phantoms.nothing :: TTerm (Maybe String))) (Phantoms.int32 k) (pMap m))
      (pMap result)

allTests :: TTermDefinition TestGroup
allTests = definitionInModule module_ "allTests" $
    Phantoms.doc "Test cases for hydra.lib.maps primitives" $
    supergroup "hydra.lib.maps primitives" [
      mapsAlter,
      mapsBimap,
      mapsElems,
      mapsEmpty,
      mapsFilter,
      mapsFilterWithKey,
      mapsFindWithDefault,
      mapsFromList,
      mapsInsert,
      mapsKeys,
      mapsLookup,
      mapsMap,
      mapsMapKeys,
      mapsMember,
      mapsNull,
      mapsRemove,
      mapsSingleton,
      mapsSize,
      mapsToList,
      mapsUnion]