packages feed

options-1.2.1.2: tests/OptionsTests/OptionTypes.hs

-- Copyright (C) 2012 John Millikin <jmillikin@gmail.com>
--
-- See license.txt for details
module OptionsTests.OptionTypes (suite_OptionTypes) where

import Data.Int (Int16, Int32, Int64, Int8)
import Data.Map qualified as Map
import Data.Set qualified as Set
import Data.Word (Word16, Word32, Word64, Word8)
import Options
import Test.Hspec (Expectation, Spec, context, shouldBe, specify)

suite_OptionTypes :: Spec
suite_OptionTypes = context "option-types" do
  test_Bool
  test_String
  test_Int
  test_Int8
  test_Int16
  test_Int32
  test_Int64
  test_Word
  test_Word8
  test_Word16
  test_Word32
  test_Word64
  test_Integer
  test_Float
  test_Double
  test_Maybe
  test_List
  test_Set
  test_Map
  test_Enum

parseValid :: (Show a, Eq a) => OptionType a -> String -> a -> Expectation
parseValid t s expected = shouldBe (optionTypeParse t s) (Right expected)

parseInvalid :: (Show a, Eq a) => OptionType a -> String -> String -> Expectation
parseInvalid t s err = shouldBe (optionTypeParse t s) (Left err)

test_Bool :: Spec
test_Bool = specify "bool" $ do
  parseValid optionType_bool "true" True
  parseValid optionType_bool "false" False
  parseInvalid optionType_bool "" "\"\" is not in {\"true\", \"false\"}."

test_String :: Spec
test_String = specify "string" $ do
  let valid = parseValid optionType_string

  valid "" ""
  valid "a" "a"
  valid "\12354" "\12354"
  valid "\56507" "\56507"
  valid "\61371" "\61371"

test_Int :: Spec
test_Int = specify "int" $ do
  let valid = parseValid optionType_int
  let invalid = parseInvalid optionType_int

  valid "-1" (-1 :: Int)
  valid "1" (1 :: Int)
  invalid "a" "\"a\" is not an integer."

  let pastMin = show (toInteger (minBound :: Int) - 1)
  let pastMax = show (toInteger (maxBound :: Int) + 1)
  let errBounds = " is not within bounds [" ++ show (minBound :: Int) ++ ":" ++ show (maxBound :: Int) ++ "] of type int."

  invalid pastMin (pastMin ++ errBounds)
  valid (show (minBound :: Int)) minBound
  valid (show (maxBound :: Int)) maxBound
  invalid pastMax (pastMax ++ errBounds)

test_Int8 :: Spec
test_Int8 = specify "int8" $ do
  let valid = parseValid optionType_int8
  let invalid = parseInvalid optionType_int8

  valid "-1" (-1 :: Int8)
  valid "1" (1 :: Int8)
  invalid "a" "\"a\" is not an integer."

  let pastMin = show (toInteger (minBound :: Int8) - 1)
  let pastMax = show (toInteger (maxBound :: Int8) + 1)
  invalid pastMin "-129 is not within bounds [-128:127] of type int8."
  valid (show (minBound :: Int8)) minBound
  valid (show (maxBound :: Int8)) maxBound
  invalid pastMax "128 is not within bounds [-128:127] of type int8."

test_Int16 :: Spec
test_Int16 = specify "int16" $ do
  let valid = parseValid optionType_int16
  let invalid = parseInvalid optionType_int16

  valid "-1" (-1 :: Int16)
  valid "1" (1 :: Int16)
  invalid "a" "\"a\" is not an integer."

  let pastMin = show (toInteger (minBound :: Int16) - 1)
  let pastMax = show (toInteger (maxBound :: Int16) + 1)
  invalid pastMin "-32769 is not within bounds [-32768:32767] of type int16."
  valid (show (minBound :: Int16)) minBound
  valid (show (maxBound :: Int16)) maxBound
  invalid pastMax "32768 is not within bounds [-32768:32767] of type int16."

test_Int32 :: Spec
test_Int32 = specify "int32" $ do
  let valid = parseValid optionType_int32
  let invalid = parseInvalid optionType_int32

  valid "-1" (-1 :: Int32)
  valid "1" (1 :: Int32)
  invalid "a" "\"a\" is not an integer."

  let pastMin = show (toInteger (minBound :: Int32) - 1)
  let pastMax = show (toInteger (maxBound :: Int32) + 1)
  invalid pastMin "-2147483649 is not within bounds [-2147483648:2147483647] of type int32."
  valid (show (minBound :: Int32)) minBound
  valid (show (maxBound :: Int32)) maxBound
  invalid pastMax "2147483648 is not within bounds [-2147483648:2147483647] of type int32."

test_Int64 :: Spec
test_Int64 = specify "int64" $ do
  let valid = parseValid optionType_int64
  let invalid = parseInvalid optionType_int64

  valid "-1" (-1 :: Int64)
  valid "1" (1 :: Int64)
  invalid "a" "\"a\" is not an integer."

  let pastMin = show (toInteger (minBound :: Int64) - 1)
  let pastMax = show (toInteger (maxBound :: Int64) + 1)
  invalid pastMin "-9223372036854775809 is not within bounds [-9223372036854775808:9223372036854775807] of type int64."
  valid (show (minBound :: Int64)) minBound
  valid (show (maxBound :: Int64)) maxBound
  invalid pastMax "9223372036854775808 is not within bounds [-9223372036854775808:9223372036854775807] of type int64."

test_Word :: Spec
test_Word = specify "word" $ do
  let valid = parseValid optionType_word
  let invalid = parseInvalid optionType_word

  let pastMax = show (toInteger (maxBound :: Word) + 1)
  let errBounds = " is not within bounds [0:" ++ show (maxBound :: Word) ++ "] of type uint."

  invalid "-1" ("-1" ++ errBounds)
  valid "0" (0 :: Word)
  valid "1" (1 :: Word)
  invalid "a" "\"a\" is not an integer."

  valid (show (maxBound :: Word)) maxBound
  invalid pastMax (pastMax ++ errBounds)

test_Word8 :: Spec
test_Word8 = specify "word8" $ do
  let valid = parseValid optionType_word8
  let invalid = parseInvalid optionType_word8

  invalid "-1" "-1 is not within bounds [0:255] of type uint8."
  valid "0" (0 :: Word8)
  valid "1" (1 :: Word8)
  invalid "a" "\"a\" is not an integer."

  let pastMax = show (toInteger (maxBound :: Word8) + 1)
  valid (show (maxBound :: Word8)) maxBound
  invalid pastMax "256 is not within bounds [0:255] of type uint8."

test_Word16 :: Spec
test_Word16 = specify "word16" $ do
  let valid = parseValid optionType_word16
  let invalid = parseInvalid optionType_word16

  invalid "-1" "-1 is not within bounds [0:65535] of type uint16."
  valid "0" (0 :: Word16)
  valid "1" (1 :: Word16)
  invalid "a" "\"a\" is not an integer."

  let pastMax = show (toInteger (maxBound :: Word16) + 1)
  valid (show (maxBound :: Word16)) maxBound
  invalid pastMax "65536 is not within bounds [0:65535] of type uint16."

test_Word32 :: Spec
test_Word32 = specify "word32" $ do
  let valid = parseValid optionType_word32
  let invalid = parseInvalid optionType_word32

  invalid "-1" "-1 is not within bounds [0:4294967295] of type uint32."
  valid "0" (0 :: Word32)
  valid "1" (1 :: Word32)
  invalid "a" "\"a\" is not an integer."

  let pastMax = show (toInteger (maxBound :: Word32) + 1)
  valid (show (maxBound :: Word32)) maxBound
  invalid pastMax "4294967296 is not within bounds [0:4294967295] of type uint32."

test_Word64 :: Spec
test_Word64 = specify "word64" $ do
  let valid = parseValid optionType_word64
  let invalid = parseInvalid optionType_word64

  invalid "-1" "-1 is not within bounds [0:18446744073709551615] of type uint64."
  valid "0" (0 :: Word64)
  valid "1" (1 :: Word64)
  invalid "a" "\"a\" is not an integer."

  let pastMax = show (toInteger (maxBound :: Word64) + 1)
  valid (show (maxBound :: Word64)) maxBound
  invalid pastMax "18446744073709551616 is not within bounds [0:18446744073709551615] of type uint64."

test_Integer :: Spec
test_Integer = specify "integer" $ do
  let valid = parseValid optionType_integer
  let invalid = parseInvalid optionType_integer

  invalid "" "\"\" is not an integer."
  valid "-1" (-1 :: Integer)
  valid "0" (0 :: Integer)
  valid "1" (1 :: Integer)
  invalid "a" "\"a\" is not an integer."

test_Float :: Spec
test_Float = specify "float" $ do
  let valid = parseValid optionType_float
  let invalid = parseInvalid optionType_float

  valid "-1" (-1 :: Float)
  valid "0" (0 :: Float)
  valid "1" (1 :: Float)
  valid "1.5" (1.5 :: Float)
  valid "3e5" (3e5 :: Float)
  invalid "a" "\"a\" is not a number."

test_Double :: Spec
test_Double = specify "double" $ do
  let valid = parseValid optionType_double
  let invalid = parseInvalid optionType_double

  valid "-1" (-1 :: Double)
  valid "0" (0 :: Double)
  valid "1" (1 :: Double)
  valid "1.5" (1.5 :: Double)
  valid "3e5" (3e5 :: Double)
  invalid "a" "\"a\" is not a number."

test_Maybe :: Spec
test_Maybe = specify "maybe" $ do
  let t = optionType_maybe optionType_int
  let valid = parseValid t
  let invalid = parseInvalid t

  valid "" Nothing
  valid "1" (Just 1)
  invalid "a" "\"a\" is not an integer."

test_List :: Spec
test_List = specify "list" $ do
  let t = optionType_list ',' optionType_int
  let valid = parseValid t
  let invalid = parseInvalid t

  valid "" []
  valid "1" [1]
  valid "1,2,3" [1, 2, 3]
  valid "1,1,2,3" [1, 1, 2, 3]
  invalid "1,a,3" "\"a\" is not an integer."

test_Set :: Spec
test_Set = specify "set" $ do
  let t = optionType_set ',' optionType_int
  let valid = parseValid t
  let invalid = parseInvalid t

  valid "" Set.empty
  valid "1" (Set.fromList [1])
  valid "1,2,3" (Set.fromList [1, 2, 3])
  valid "1,1,2,3" (Set.fromList [1, 2, 3])
  invalid "1,a,3" "\"a\" is not an integer."

test_Map :: Spec
test_Map = specify "map" $ do
  let t = optionType_map ',' '=' optionType_int optionType_int
  let valid = parseValid t
  let invalid = parseInvalid t

  valid "" Map.empty
  valid "1=100" (Map.fromList [(1, 100)])
  valid "1=100,2=200,3=300" (Map.fromList [(1, 100), (2, 200), (3, 300)])
  valid "1=100,2=200,1=300" (Map.fromList [(1, 300), (2, 200)])
  invalid "a=1" "\"a\" is not an integer."
  invalid "1=a" "\"a\" is not an integer."
  invalid "1=" "\"\" is not an integer."
  invalid "1" "Map item \"1\" has no value."

data TestEnum = Enum1 | Enum2 | Enum3
  deriving (Bounded, Enum, Eq, Show)

test_Enum :: Spec
test_Enum = specify "enum" $ do
  let t = optionType_enum "test enum"
  let valid = parseValid t
  let invalid = parseInvalid t

  valid "Enum1" Enum1
  valid "Enum2" Enum2
  invalid "Enum4" "\"Enum4\" is not in {\"Enum1\", \"Enum2\", \"Enum3\"}."