packages feed

Takusen-0.7: Foreign/C/Test/UTF8.hs

-- |
-- Module      :  Foreign.C.Test.UTF8
-- Copyright   :  (c) 2004 Oleg Kiselyov, Alistair Bayley
-- License     :  BSD-style
-- Maintainer  :  alistair@abayley.org
-- Stability   :  experimental
-- Portability :  non-portable
-- 
-- UTF8 decoder and encoder tests.

module Foreign.C.Test.UTF8 where

import Control.Exception
import Data.Char
import Foreign.C.String
import Foreign.C.UTF8
import Foreign.Marshal.Array
import Foreign.Ptr
import Test.MiniUnit
import qualified Test.QuickCheck as QC
import Word (Word8)


runTest :: a -> [String] -> IO ()
runTest _ args = runTestTT "UTF8" mkTestList >> return ()

--mkTestList :: Test
--mkTestList = TestList (map TestCase testlist)
mkTestList = testlist

testlist :: [IO ()]
testlist = 
  [ testUTF8RoundTrip
  , testUTF8StringLenRoundTrip
  , quickCheckUTF8RoundTrip
  , testFromUTF8Failure5Bytes
  , testFromUTF8Failure6Bytes
  -- , testLargeFromUTF8
  -- , testLargeFromUTF8Len
  ]


instance QC.Arbitrary Char where
  arbitrary = QC.choose (chr 1, chr 0x10FFFF)
  coarbitrary = undefined

prop_roundtrip s = s == fromUTF8 (toUTF8 s)
quickCheckUTF8RoundTrip = QC.test prop_roundtrip

testUTF8RoundTrip = do
  utf8RoundTrip "1ByteLow"   0x000001 [0x01]
  utf8RoundTrip "1ByteHigh"  0x00007F [0x7F]
  utf8RoundTrip "2BytesLow"  0x000080 [0xC2, 0x80]
  utf8RoundTrip "2BytesHigh" 0x0007FF [0xDF, 0xBF]
  utf8RoundTrip "3BytesLow"  0x000800 [0xE0, 0xA0, 0x80]
  utf8RoundTrip "3BytesHigh" 0x00FFFF [0xEF, 0xBF, 0xBF]
  utf8RoundTrip "4BytesLow"  0x010000 [0xF0, 0x90, 0x80, 0x80]
  -- chr 0x10FFFF is the largest code-point Haskell currently allows
  utf8RoundTrip "4BytesHigh" 0x10FFFF [0xF4, 0x8F, 0xBF, 0xBF]

utf8RoundTrip :: String -> Int -> [Word8] -> IO ()
utf8RoundTrip msg unicode utf8 = do
  assertEqual ("testToUTF8-" ++ msg) utf8 (toUTF8 [chr unicode])
  assertEqual ("testFromUTF8-" ++ msg) [chr unicode] (fromUTF8 utf8)
  withUTF8String [chr unicode] $ \cstr -> do
    w8 <- peekArray0 0 (castPtr cstr)
    assertEqual ("testToUTF8Ptr-" ++ msg) utf8 w8
  withCString (map (chr . fromIntegral) utf8) $ \cstr -> do
    s <- peekUTF8String cstr
    assertEqual ("testFromUTF8Ptr-" ++ msg) [chr unicode] s

testUTF8StringLenRoundTrip = do
  utf8StringLenRoundTrip "1ByteLow"   [0x000001, 0x000001] [0x01, 0x01]
  utf8StringLenRoundTrip "2BytesLow"  [0x000080, 0x00007F] [0xC2, 0x80, 0x7F]
  utf8StringLenRoundTrip "3BytesLow"  [0x000800, 0x000001] [0xE0, 0xA0, 0x80, 0x01]
  utf8StringLenRoundTrip "4BytesLow"  [0x010000, 0x00007F] [0xF0, 0x90, 0x80, 0x80, 0x7F]
  utf8StringLenRoundTrip "4BytesHigh" [0x10FFFF, 0x00007F] [0xF4, 0x8F, 0xBF, 0xBF, 0x01]

utf8StringLenRoundTrip :: String -> [Int] -> [Word8] -> IO ()
utf8StringLenRoundTrip msg codepoints utf8 = do
  let unicode = map chr codepoints
  let expect = take (length unicode - 1) unicode
  -- convert our unicode code-points (Haskell String)
  -- into a UTF8 CStringLen.
  -- Pass this back to peekUTF8StringLen to convert back into Haskell String.
  -- Chop off the last char - make sure it's only one byte.
  withUTF8StringLen unicode $ \(cstr, clen) -> do
    assertEqual ("testUTF8StringLenRoundTrip-" ++ msg) (length utf8) clen
    s <- peekUTF8StringLen (cstr, clen - 1)
    assertEqual ("testUTF8StringLenRoundTrip-" ++ msg) expect s



--testUTF8StringLenRoundTrip = do


testFromUTF8Failure5Bytes = do
  let utf8 = [0xF8, 0x80, 0x80, 0x80, 0x80]
  catchJust errorCalls (do
    assertEqual "testFromUTF8Failure5Bytes" [chr 0x10FFFF] (fromUTF8 utf8)
    assertBool "testFromUTF8Failure5Bytes: no error raised" False
    ) (\msg -> do
      assertEqual "testFromUTF8Failure5Bytes" "fromUTF8: illegal UTF-8 character 248" msg
    )

testFromUTF8Failure6Bytes = do
  let utf8 = [0xFD, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF]
  catchJust errorCalls (do
    assertEqual "testFromUTF8Failure6Bytes" [chr 0x10FFFF] (fromUTF8 utf8)
    assertBool "testFromUTF8Failure6Bytes: no error raised" False
    ) (\msg -> do
      assertEqual "testFromUTF8Failure6Bytes" "fromUTF8: illegal UTF-8 character 253" msg
    )

{-
testLargeFromUTF8 = do
  let sz = 1000000
  withCString (take sz (repeat 'a')) $ \cstr -> do
    s <- peekUTF8String cstr
    assertEqual "testLargeFromUTF8" sz (length s)
    s <- peekUTF8StringB cstr
    assertEqual "testLargeFromUTF8B" sz (length s)
    s <- peekUTF8StringLen (cstr, sz)
    assertEqual "testLargeFromUTF8Len" sz (length s)
    s <- peekUTF8StringLenB (cstr, sz)
    assertEqual "testLargeFromUTF8LenB" sz (length s)
-}

{-
testLargeFromUTF8Len = do
  let sz = 1000000
  withCStringLen (take sz (repeat 'a')) $ \(cstr, clen) -> do
    putStrLn "testLargeFromUTF8Len: call peekUTF8StringLen"
    s <- peekUTF8StringLen (cstr, clen)
    putStrLn "testLargeFromUTF8Len: assert length"
    assertEqual "testLargeFromUTF8Len" sz (length s)
-}