packages feed

hydra-0.14.0: src/main/haskell/Hydra/Sources/Test/Lib/Maybes.hs

module Hydra.Sources.Test.Lib.Maybes 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


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

module_ :: Module
module_ = Module ns elements [] [] $
    Just "Test cases for hydra.lib.maybes primitives"
  where
    elements = [Phantoms.toTermDefinition allTests]

-- Helper to create Just terms for Int32 values
justInt32 :: Int -> TTerm Term
justInt32 x = Core.termMaybe $ just (int32 x)

-- Helper to create Nothing terms
nothingTerm :: TTerm Term
nothingTerm = Core.termMaybe nothing

-- Test groups for hydra.lib.maybes primitives

maybesIsJust :: TTerm TestGroup
maybesIsJust = subgroup "isJust" [
  test "just value" (justInt32 42) true,
  test "nothing" nothingTerm false]
  where
    test name x result = primCase name _maybes_isJust [x] result

maybesIsNothing :: TTerm TestGroup
maybesIsNothing = subgroup "isNothing" [
  test "just value" (justInt32 42) false,
  test "nothing" nothingTerm true]
  where
    test name x result = primCase name _maybes_isNothing [x] result

maybesFromMaybe :: TTerm TestGroup
maybesFromMaybe = subgroup "fromMaybe" [
  test "just value" 0 (justInt32 42) 42,
  test "nothing with default" 99 nothingTerm 99]
  where
    test name def x result = primCase name _maybes_fromMaybe [int32 def, x] (int32 result)

maybesMaybe :: TTerm TestGroup
maybesMaybe = subgroup "maybe" [
  test "just value applies function" 0 (justInt32 5) 10,
  test "nothing returns default" 99 nothingTerm 99]
  where
    test name def x result = primCase name _maybes_maybe [
      int32 def,
      lambda "x" (primitive _math_mul @@ var "x" @@ int32 2),
      x] (int32 result)

maybesPure :: TTerm TestGroup
maybesPure = subgroup "pure" [
  test "wraps integer" (int32 42) (justInt32 42),
  test "wraps string" (string "hello") (Core.termMaybe $ just (string "hello"))]
  where
    test name x expected = primCase name _maybes_pure [x] expected

maybesCat :: TTerm TestGroup
maybesCat = subgroup "cat" [
  test "filters nothings" [justInt32 1, nothingTerm, justInt32 2] [1, 2],
  test "all justs" [justInt32 1, justInt32 2] [1, 2],
  test "all nothings" [nothingTerm, nothingTerm] [],
  test "empty list" [] []]
  where
    test name input expected = primCase name _maybes_cat [
      list input
      ] (list $ fmap int32 expected)

maybesMap :: TTerm TestGroup
maybesMap = subgroup "map" [
  test "maps just value" (justInt32 5) (justInt32 10),
  test "nothing unchanged" nothingTerm nothingTerm]
  where
    test name x expected = primCase name _maybes_map [
      lambda "x" (primitive _math_mul @@ var "x" @@ int32 2),
      x] expected

maybesApply :: TTerm TestGroup
maybesApply = subgroup "apply" [
  test "both just" (Core.termMaybe $ just (primitive _math_add @@ int32 3)) (justInt32 5) (justInt32 8),
  test "nothing function" nothingTerm (justInt32 5) nothingTerm,
  test "nothing value" (Core.termMaybe $ just (primitive _math_add @@ int32 3)) nothingTerm nothingTerm]
  where
    test name f x expected = primCase name _maybes_apply [f, x] expected

maybesBind :: TTerm TestGroup
maybesBind = subgroup "bind" [
  test "just to just" (justInt32 5) (justInt32 10),
  test "nothing to nothing" nothingTerm nothingTerm]
  where
    test name x expected = primCase name _maybes_bind [
      x,
      lambda "x" (Core.termMaybe $ just (primitive _math_mul @@ var "x" @@ int32 2))] expected

maybesCases :: TTerm TestGroup
maybesCases = subgroup "cases" [
  test "just applies function" (justInt32 5) (int32 0) (int32 10),
  test "nothing returns default" nothingTerm (int32 99) (int32 99)]
  where
    test name opt def expected = primCase name _maybes_cases [
      opt,
      def,
      lambda "x" (primitive _math_mul @@ var "x" @@ int32 2)] expected

maybesFromJust :: TTerm TestGroup
maybesFromJust = subgroup "fromJust" [
  test "extract from just" (justInt32 42) (int32 42)]
  where
    test name x expected = primCase name _maybes_fromJust [x] expected

maybesMapMaybe :: TTerm TestGroup
maybesMapMaybe = subgroup "mapMaybe" [
  test "filter and transform" [1, 2, 3, 4, 5] [6, 8, 10],  -- x > 2: [3,4,5] doubled = [6,8,10]
  test "empty result" [1, 2] [],
  test "empty input" [] []]
  where
    -- Function that returns Just (x*2) if x > 2, else Nothing
    filterFn = lambda "x" (
      primitive _logic_ifElse
        @@ (primitive _equality_gt @@ var "x" @@ int32 2)
        @@ (Core.termMaybe $ just (primitive _math_mul @@ var "x" @@ int32 2))
        @@ nothingTerm)
    test name xs expected = primCase name _maybes_mapMaybe [
      filterFn,
      list $ Prelude.map int32 xs] (list $ Prelude.map int32 expected)

-- | Test cases for compose: Kleisli composition of two Maybe-returning functions
-- compose f g x = bind (f x) g
maybesCompose :: TTerm TestGroup
maybesCompose = subgroup "compose" [
  test "both succeed" 5 12,           -- f(5)=Just 6, g(6)=Just 12
  testFails "first fails" 10,         -- f(10)=Nothing (x > 5), so result is Nothing
  testFails "second fails" 3]         -- f(3)=Just 4, g(4)=Nothing (y < 5), so result is Nothing
  where
    -- f: x -> if x <= 5 then Just (x + 1) else Nothing
    funF = lambda "x" (
      primitive _logic_ifElse
        @@ (primitive _equality_lte @@ var "x" @@ int32 5)
        @@ (Core.termMaybe $ just (primitive _math_add @@ var "x" @@ int32 1))
        @@ nothingTerm)
    -- g: y -> if y >= 5 then Just (y * 2) else Nothing
    funG = lambda "y" (
      primitive _logic_ifElse
        @@ (primitive _equality_gte @@ var "y" @@ int32 5)
        @@ (Core.termMaybe $ just (primitive _math_mul @@ var "y" @@ int32 2))
        @@ nothingTerm)
    -- compose f g x computes bind (f x) g
    test name input expected = primCase name _maybes_compose [funF, funG, int32 input] (justInt32 expected)
    testFails name input = primCase name _maybes_compose [funF, funG, int32 input] nothingTerm

maybesToList :: TTerm TestGroup
maybesToList = subgroup "toList" [
  test "just value" (justInt32 42) [42],
  test "nothing" nothingTerm []]
  where
    test name x expected = primCase name _maybes_toList [x] (list $ fmap int32 expected)

allTests :: TBinding TestGroup
allTests = definitionInModule module_ "allTests" $
    Phantoms.doc "Test cases for hydra.lib.maybes primitives" $
    supergroup "hydra.lib.maybes primitives" [
      maybesApply,
      maybesBind,
      maybesCases,
      maybesCat,
      maybesCompose,
      maybesFromJust,
      maybesFromMaybe,
      maybesIsJust,
      maybesIsNothing,
      maybesMap,
      maybesMapMaybe,
      maybesMaybe,
      maybesPure,
      maybesToList]