packages feed

himari-1.1.4.1: test/Himari/CharSpec.hs

-- このテストモジュール自体が避けるべき関数のラッパーを作れているか検査するため、
-- 避けるべき関数を呼び出して振る舞いの互換性の検査などを行う必要があります。
{- HLINT ignore "Avoid restricted function"  -}
module Himari.CharSpec (spec) where

import Data.Char (chr, digitToInt, intToDigit, isHexDigit, ord, toUpper)
import Himari hiding (elements)
import Test.QuickCheck
import Test.Syd

spec :: Spec
spec = do
  describe "digitToIntMay" $ do
    describe "boundary values" $ do
      it "returns Just 0 for '0'" $ do
        digitToIntMay '0' `shouldBe` Just 0

      it "returns Just 9 for '9'" $ do
        digitToIntMay '9' `shouldBe` Just 9

      it "returns Just 10 for 'a'" $ do
        digitToIntMay 'a' `shouldBe` Just 10

      it "returns Just 10 for 'A'" $ do
        digitToIntMay 'A' `shouldBe` Just 10

      it "returns Just 15 for 'f'" $ do
        digitToIntMay 'f' `shouldBe` Just 15

      it "returns Just 15 for 'F'" $ do
        digitToIntMay 'F' `shouldBe` Just 15

      it "returns Nothing for 'g'" $ do
        digitToIntMay 'g' `shouldBe` Nothing

      it "returns Nothing for 'G'" $ do
        digitToIntMay 'G' `shouldBe` Nothing

      it "returns Nothing for ' '" $ do
        digitToIntMay ' ' `shouldBe` Nothing

    describe "all valid hex digits" $ do
      it "returns Just for all decimal digits" $ do
        forM_ ['0' .. '9'] $ \c ->
          digitToIntMay c `shouldSatisfy` isJust

      it "returns Just for all lowercase hex letters" $ do
        forM_ ['a' .. 'f'] $ \c ->
          digitToIntMay c `shouldSatisfy` isJust

      it "returns Just for all uppercase hex letters" $ do
        forM_ ['A' .. 'F'] $ \c ->
          digitToIntMay c `shouldSatisfy` isJust

      it "matches digitToInt for all valid characters" $ do
        let validChars = ['0' .. '9'] <> ['a' .. 'f'] <> ['A' .. 'F']
        forM_ validChars $ \c ->
          digitToIntMay c `shouldBe` Just (digitToInt c)

    describe "QuickCheck properties" $ do
      it "returns Just for hex digit characters" $ do
        property . forAll (elements (['0' .. '9'] <> ['a' .. 'f'] <> ['A' .. 'F'])) $ \c ->
          isJust (digitToIntMay c)

      it "returns Nothing for non-hex characters" $ do
        property . forAll (elements (['g' .. 'z'] <> ['G' .. 'Z'] <> [' ', '!', '@', '#'])) $ \c ->
          isNothing (digitToIntMay c)

      it "matches digitToInt for all valid hex digit inputs" $ do
        property . forAll (elements (['0' .. '9'] <> ['a' .. 'f'] <> ['A' .. 'F'])) $ \c ->
          digitToIntMay c == Just (digitToInt c)

      it "agrees with isHexDigit on validity" $ do
        property $ \c ->
          isJust (digitToIntMay c) == isHexDigit c

      it "returns values in range 0-15 for valid inputs" $ do
        property . forAll (elements (['0' .. '9'] <> ['a' .. 'f'] <> ['A' .. 'F'])) $ \c ->
          case digitToIntMay c of
            Just n -> n >= 0 && n <= 15
            Nothing -> False

      it "lowercase and uppercase give same result" $ do
        property . forAll (elements ['a' .. 'f']) $ \c ->
          digitToIntMay c == digitToIntMay (toUpper c)

  describe "intToDigitMay" $ do
    describe "boundary values" $ do
      it "returns Just '0' for 0" $ do
        intToDigitMay 0 `shouldBe` Just '0'

      it "returns Just 'f' for 15" $ do
        intToDigitMay 15 `shouldBe` Just 'f'

      it "returns Nothing for -1" $ do
        intToDigitMay (-1) `shouldBe` Nothing

      it "returns Nothing for 16" $ do
        intToDigitMay 16 `shouldBe` Nothing

    describe "all valid values (0-15)" $ do
      it "returns Just for all values in range" $ do
        let validRange = [0 .. 15]
        forM_ validRange $ \n ->
          intToDigitMay n `shouldSatisfy` isJust

      it "matches intToDigit for all valid values" $ do
        let validRange = [0 .. 15]
        forM_ validRange $ \n ->
          intToDigitMay n `shouldBe` Just (intToDigit n)

    describe "QuickCheck properties" $ do
      it "returns Just for values in range 0-15" $ do
        property . forAll (choose (0, 15)) $ \n ->
          isJust (intToDigitMay n)

      it "returns Nothing for negative values" $ do
        property . forAll (choose (-1000, -1)) $ \n ->
          isNothing (intToDigitMay n)

      it "returns Nothing for values greater than 15" $ do
        property . forAll (choose (16, 1000)) $ \n ->
          isNothing (intToDigitMay n)

      it "matches intToDigit for all valid inputs" $ do
        property . forAll (choose (0, 15)) $ \n ->
          intToDigitMay n == Just (intToDigit n)

      it "produces lowercase hex digits for 10-15" $ do
        property . forAll (choose (10, 15)) $ \n ->
          case intToDigitMay n of
            Just c -> c `elem` ['a' .. 'f']
            Nothing -> False

      it "produces decimal digits for 0-9" $ do
        property . forAll (choose (0, 9)) $ \n ->
          case intToDigitMay n of
            Just c -> c `elem` ['0' .. '9']
            Nothing -> False

  describe "chrMay" $ do
    describe "boundary values" $ do
      it "returns Just for 0 (NUL)" $ do
        chrMay 0 `shouldBe` Just '\0'

      it "returns Just 'A' for 65" $ do
        chrMay 65 `shouldBe` Just 'A'

      it "returns Just for max valid code point (0x10FFFF)" $ do
        chrMay 0x10FFFF `shouldBe` Just '\x10FFFF'

      it "returns Nothing for -1" $ do
        chrMay (-1) `shouldBe` Nothing

      it "returns Nothing for 0x110000 (beyond max)" $ do
        chrMay 0x110000 `shouldBe` Nothing

      it "returns Nothing for surrogate start (0xD800)" $ do
        chrMay 0xD800 `shouldBe` Nothing

      it "returns Nothing for surrogate end (0xDFFF)" $ do
        chrMay 0xDFFF `shouldBe` Nothing

      it "returns Nothing for surrogate middle (0xDC00)" $ do
        chrMay 0xDC00 `shouldBe` Nothing

    describe "valid ranges" $ do
      it "returns Just for code points before surrogate range" $ do
        chrMay 0xD7FF `shouldSatisfy` isJust

      it "returns Just for code points after surrogate range" $ do
        chrMay 0xE000 `shouldSatisfy` isJust

      it "returns Just for common characters" $ do
        forM_ [('a', 97), ('z', 122), ('0', 48), ('9', 57)] $ \(expected, n) ->
          chrMay n `shouldBe` Just expected

    describe "QuickCheck properties" $ do
      it "returns Just for valid BMP code points (excluding surrogates)" $ do
        property . forAll (choose (0, 0xD7FF)) $ \n ->
          isJust (chrMay n)

      it "returns Just for valid code points after surrogates" $ do
        property . forAll (choose (0xE000, 0x10FFFF)) $ \n ->
          isJust (chrMay n)

      it "returns Nothing for surrogate range" $ do
        property . forAll (choose (0xD800, 0xDFFF)) $ \n ->
          isNothing (chrMay n)

      it "returns Nothing for negative values" $ do
        property . forAll (choose (-1000, -1)) $ \n ->
          isNothing (chrMay n)

      it "returns Nothing for values beyond max code point" $ do
        property . forAll (choose (0x110000, 0x200000)) $ \n ->
          isNothing (chrMay n)

      it "matches chr for all valid inputs" $ do
        property . forAll (choose (0, 0xD7FF)) $ \n ->
          chrMay n == Just (chr n)

      it "is inverse of ord for valid characters" $ do
        property $ \c ->
          chrMay (ord c) == Just c

      it "never throws an exception for any Int value" $ do
        withNumTests 10000 . property $ \n ->
          case chrMay n of
            Just _ -> True
            Nothing -> True

#if !MIN_VERSION_QuickCheck(2,18,0)
-- | `withNumTests`は`QuickCheck 2.18.0.0`で、
-- `withMaxSuccess`から改名された関数です。
-- それより古いQuickCheckには存在しないため、
-- `withMaxSuccess`で互換定義します。
withNumTests :: (Testable prop) => Int -> prop -> Property
withNumTests = withMaxSuccess
#endif