1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
{-# LANGUAGE CPP, BlockArguments, GADTs #-}
module ICU.CharSpec
( spec
) where
import Control.Applicative (Alternative(..))
import Data.Foldable (traverse_)
import Data.Version (showVersion, versionBranch)
import Numeric (showHex)
import Test.Hspec
( describe
, expectationFailure
, it
, pendingWith
, Spec
, HasCallStack, SpecWith )
import qualified ICU.Char as ICU
import qualified Unicode.Char as U
spec :: Spec
spec = do
describe "General" do
checkAndGatherErrors
"charType"
(GeneralCategory . U.generalCategory)
(GeneralCategory . ICU.toGeneralCategory . ICU.charType)
checkAndGatherErrors
"isNoncharacter"
(GeneralCategory . U.isNoncharacter)
(GeneralCategory . ICU.isNoncharacter)
-- TODO: other functions
where
ourUnicodeVersion = versionBranch U.unicodeVersion
theirUnicodeVersion = versionBranch ICU.unicodeVersion
showCodePoint c = ("U+" ++) . fmap U.toUpper . showHex (U.ord c)
-- There is no feature to display warnings other than `trace`, so
-- hack our own:
-- 1. Compare given functions in pure code and gather warning & errors
-- 2. Create dummy spec that throw an expectation failure, if relevant.
-- 3. Create pending spec for each Char that raises a Unicode version
-- mismatch between ICU and unicode-data.
checkAndGatherErrors
:: forall a. (HasCallStack, Eq a, Show a)
=> String
-> (Char -> a)
-> (Char -> a)
-> SpecWith ()
checkAndGatherErrors label f fRef = do
it label (maybe (pure ()) expectationFailure err)
if null ws
then pure ()
else describe (label ++ " (Unicode version conflict)")
(traverse_ mkWarning ws)
where
Acc ws err = foldr check (Acc [] Nothing) [minBound..maxBound]
check c acc
-- Test passed
| n == nRef = acc
-- Unicode version mismatch: char is not mapped in one of the libs:
-- add warning.
| age' > ourUnicodeVersion || age' > theirUnicodeVersion
= acc{warnings=c : warnings acc}
-- Error
| otherwise =
let !msg = mconcat
[ showCodePoint c ": expected "
, show nRef
, ", got ", show n, "" ]
in acc{firstError = firstError acc <|> Just msg}
where
!n = f c
!nRef = fRef c
age = ICU.charAge c
age' = take 3 (versionBranch age)
mkWarning c = it (showCodePoint c "") . pendingWith $ mconcat
[ "Incompatible ICU Unicode version: expected "
, showVersion U.unicodeVersion
, ", got: "
, showVersion ICU.unicodeVersion
, " (ICU character age is: "
, showVersion (ICU.charAge c)
, ")" ]
-- | Helper to compare our GeneralCategory to 'Data.Char.GeneralCategory'.
data GeneralCategory = forall c. (Show c, Enum c) => GeneralCategory c
instance Show GeneralCategory where
show (GeneralCategory a) = show a
instance Eq GeneralCategory where
GeneralCategory a == GeneralCategory b = fromEnum a == fromEnum b
-- | Warning accumulator
data Acc = Acc { warnings :: ![Char], firstError :: !(Maybe String) }
|