{-# LANGUAGE RankNTypes #-}

module Clash.Tests.Laws.Enum (tests) where

import Control.DeepSeq (NFData)
import Data.Proxy
import Test.Tasty
import Test.Tasty.HUnit

import Clash.Sized.Index (Index)
import Clash.Sized.Signed (Signed)
import Clash.Sized.Unsigned (Unsigned)

import Test.Tasty.HUnit.Extra

succMaxBoundLaw ::
  forall a .
  (NFData a, Show a, Enum a, Bounded a) =>
  Proxy a ->
  Assertion
succMaxBoundLaw Proxy = expectException (succ @a maxBound)

predMinBoundLaw ::
  forall a .
  (NFData a, Show a, Enum a, Bounded a) =>
  Proxy a ->
  Assertion
predMinBoundLaw Proxy = expectException (pred @a minBound)

enumLaws ::
  (NFData a, Show a, Enum a, Bounded a) =>
  Proxy a ->
  [TestTree]
enumLaws proxy =
  [ testCase "succ maxBound ~ _|_" (succMaxBoundLaw proxy)
  , testCase "pred minBound ~ _|_" (predMinBoundLaw proxy)
  ]

testEnumLaws ::
  (NFData a, Show a, Enum a, Bounded a) =>
  String ->
  Proxy a ->
  TestTree
testEnumLaws typeName proxy = testGroup typeName (enumLaws proxy)

tests :: TestTree
tests = testGroup "Enum"
  [ testEnumLaws "Index 1" (Proxy @(Index 1))
  , testEnumLaws "Index 2" (Proxy @(Index 2))
  , testEnumLaws "Index 128" (Proxy @(Index 128))

  , testEnumLaws "Unsigned 0" (Proxy @(Unsigned 0))
  , testEnumLaws "Unsigned 1" (Proxy @(Unsigned 1))
  , testEnumLaws "Unsigned 32" (Proxy @(Unsigned 32))
  , testEnumLaws "Unsigned 127" (Proxy @(Unsigned 127))
  , testEnumLaws "Unsigned 128" (Proxy @(Unsigned 128))

  , testEnumLaws "Signed 0" (Proxy @(Signed 0))
  , testEnumLaws "Signed 1" (Proxy @(Signed 1))
  , testEnumLaws "Signed 32" (Proxy @(Signed 32))
  , testEnumLaws "Signed 127" (Proxy @(Signed 127))
  , testEnumLaws "Signed 128" (Proxy @(Signed 128))

  -- Note Fixed is tested elsewhere.
  ]