summaryrefslogtreecommitdiff
path: root/src-test/Tests.hs
blob: 51f3cacf2722baf6c27b6c6c5247651b9adaa00a (plain)
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
{-# LANGUAGE OverloadedLists   #-}
{-# LANGUAGE OverloadedStrings #-}

module Main(main) where

import           Data.Binary
import           Data.Char
import           Data.Maybe
import           Data.Monoid
import qualified Data.String               as D.S
import qualified Data.Text                 as T
import qualified Data.Text.Encoding        as T
import qualified Data.Text.Short           as IUT
import qualified Data.Text.Short.Partial   as IUT
import           Test.QuickCheck.Instances ()
import           Test.Tasty
import           Test.Tasty.HUnit
import           Test.Tasty.QuickCheck     as QC
import           Text.Show.Functions       ()

fromByteStringRef = either (const Nothing) (Just . IUT.fromText) . T.decodeUtf8'

main :: IO ()
main = defaultMain (adjustOption (QuickCheckTests 50000 `max`) $ tests)

tests :: TestTree
tests = testGroup "Tests" [unitTests,qcProps]

-- ShortText w/ in-bounds index
data STI = STI IUT.ShortText Int
         deriving (Eq,Show)

newtype ST = ST IUT.ShortText
           deriving (Eq,Show)

instance Arbitrary STI where
  arbitrary = do
    t <- arbitrary
    i <- choose (0, T.length t - 1)
    return $! STI (IUT.fromText t) i

instance Arbitrary ST where
  arbitrary = fmap (ST . IUT.fromText) arbitrary
  shrink (ST st) = map (ST . IUT.fromText) (shrink (IUT.toText st))

qcProps :: TestTree
qcProps = testGroup "Properties"
  [ QC.testProperty "length/fromText"   $ \t -> IUT.length (IUT.fromText t) == T.length t
  , QC.testProperty "length/fromString" $ \s -> IUT.length (IUT.fromString s) == length s
  , QC.testProperty "length/append"     $ \(ST t1) (ST t2) -> IUT.length t1 + IUT.length t2 == IUT.length (IUT.append t1 t2)
  , QC.testProperty "compare" $ \t1 t2 -> IUT.fromText t1 `compare` IUT.fromText t2  == t1 `compare` t2
  , QC.testProperty "(==)" $ \t1 t2 -> (IUT.fromText t1 == IUT.fromText t2)  == (t1 == t2)
  , QC.testProperty "(!?)" $ \t ->
      let t' = IUT.fromText t
      in and ([ mapMaybe (t' IUT.!?) ([0 .. T.length t -1 ] :: [Int]) == T.unpack t
              , mapMaybe (t' IUT.!?) [-5 .. -1] == []
              , mapMaybe (t' IUT.!?) [T.length t .. T.length t + 5] == []
              ] :: [Bool])
  , QC.testProperty "indexEndMaybe" $ \t ->
      let t' = IUT.fromText t
      in and ([ mapMaybe (IUT.indexEndMaybe t') [0 .. T.length t -1 ] == T.unpack (T.reverse t)
              , mapMaybe (IUT.indexEndMaybe t') [-5 .. -1] == []
              , mapMaybe (IUT.indexEndMaybe t') [T.length t .. T.length t + 5] == []
              ] :: [Bool])
  , QC.testProperty "toText.fromText"   $ \t -> (IUT.toText . IUT.fromText) t == t
  , QC.testProperty "fromByteString"    $ \b -> IUT.fromByteString b == fromByteStringRef b
  , QC.testProperty "fromByteString.toByteString" $ \t -> let ts = IUT.fromText t in (IUT.fromByteString . IUT.toByteString) ts == Just ts
  , QC.testProperty "toString.fromString" $ \s -> (IUT.toString . IUT.fromString) s == s
  , QC.testProperty "isAscii"  $ \s -> IUT.isAscii (IUT.fromString s) == all isAscii s
  , QC.testProperty "isAscii2" $ \t -> IUT.isAscii (IUT.fromText t)   == T.all isAscii t
  , QC.testProperty "splitAt" $ \t ->
      let t' = IUT.fromText t
          mapBoth f (x,y) = (f x, f y)
      in and [ mapBoth IUT.toText (IUT.splitAt i t') == T.splitAt i t | i <- [-5 .. 5+T.length t ] ]
  , QC.testProperty "intercalate/split" $ \t c ->
      let t' = IUT.fromText t
      in IUT.intercalate (IUT.singleton c) (IUT.split (== c) t') == t'

  , QC.testProperty "intersperse" $ \t c -> IUT.intersperse c (IUT.fromText t) == IUT.fromText (T.intersperse c t)
  , QC.testProperty "intercalate" $ \t1 t2 -> IUT.intercalate (IUT.fromText t1) (map IUT.fromText t2) == IUT.fromText (T.intercalate t1 t2)
  , QC.testProperty "reverse.singleton" $ \c -> IUT.reverse (IUT.singleton c) == IUT.singleton c
  , QC.testProperty "reverse"     $ \t -> IUT.reverse (IUT.fromText t) == IUT.fromText (T.reverse t)
  , QC.testProperty "filter"      $ \p t -> IUT.filter p (IUT.fromText t) == IUT.fromText (T.filter p t)
  , QC.testProperty "replicate"   $ \n t -> IUT.replicate n (IUT.fromText t) == IUT.fromText (T.replicate n t)
  , QC.testProperty "dropAround"  $ \p t -> IUT.dropAround p (IUT.fromText t) == IUT.fromText (T.dropAround p t)

  , QC.testProperty "foldl"       $ \f z t -> IUT.foldl f (z :: Char) (IUT.fromText t) == T.foldl f (z :: Char) t
  , QC.testProperty "foldl #2"    $ \t -> IUT.foldl (\n _ -> (n+1)) 0 (IUT.fromText t) == T.length t
  , QC.testProperty "foldl #3"    $ \t -> IUT.foldl (\s c -> c : s) [] (IUT.fromText t) == T.unpack (T.reverse t)

  , QC.testProperty "foldl'"      $ \f z t -> IUT.foldl' f (z :: Char) (IUT.fromText t) == T.foldl' f (z :: Char) t
  , QC.testProperty "foldl' #2"   $ \t -> IUT.foldl' (\n _ -> (n+1)) 0 (IUT.fromText t) == T.length t
  , QC.testProperty "foldl' #3"   $ \t -> IUT.foldl' (\s c -> c : s) [] (IUT.fromText t) == T.unpack (T.reverse t)

  , QC.testProperty "foldr"       $ \f z t -> IUT.foldr f (z :: Char) (IUT.fromText t) == T.foldr f (z :: Char) t
  , QC.testProperty "foldr #2"    $ \t -> IUT.foldr (\_ n -> (n+1)) 0 (IUT.fromText t) == T.length t
  , QC.testProperty "foldr #3"    $ \t -> IUT.foldr (:) [] (IUT.fromText t) == T.unpack t

  , QC.testProperty "foldr1"      $ \f t -> (not (T.null t)) ==> IUT.foldr1 f (IUT.fromText t) == T.foldr1 f t
  , QC.testProperty "foldl1"      $ \f t -> (not (T.null t)) ==> IUT.foldl1 f (IUT.fromText t) == T.foldl1 f t
  , QC.testProperty "foldl1'"     $ \f t -> (not (T.null t)) ==> IUT.foldl1' f (IUT.fromText t) == T.foldl1' f t

  , QC.testProperty "splitAtEnd" $ \t ->
      let t' = IUT.fromText t
          n' = IUT.length t'
      in and [ (IUT.splitAt (n'-i) t') == IUT.splitAtEnd i t' | i <- [-5 .. 5+n' ] ]

  , QC.testProperty "find" $ \t -> IUT.find Data.Char.isAscii (IUT.fromText t) == T.find Data.Char.isAscii t
  , QC.testProperty "findIndex" $ \t -> IUT.findIndex Data.Char.isAscii (IUT.fromText t) == T.findIndex Data.Char.isAscii t

  , QC.testProperty "isSuffixOf" $ \t1 t2 -> IUT.fromText t1 `IUT.isSuffixOf` IUT.fromText t2  == t1 `T.isSuffixOf` t2
  , QC.testProperty "isPrefixOf" $ \t1 t2 -> IUT.fromText t1 `IUT.isPrefixOf` IUT.fromText t2  == t1 `T.isPrefixOf` t2

  , QC.testProperty "stripPrefix" $ \t1 t2 -> IUT.stripPrefix (IUT.fromText t1) (IUT.fromText t2) ==
                                                fmap IUT.fromText (T.stripPrefix t1 t2)

  , QC.testProperty "stripSuffix" $ \t1 t2 -> IUT.stripSuffix (IUT.fromText t1) (IUT.fromText t2) ==
                                                fmap IUT.fromText (T.stripSuffix t1 t2)

  , QC.testProperty "stripPrefix 2" $ \(STI t i) ->
      let (pfx,sfx) = IUT.splitAt i t
      in IUT.stripPrefix pfx t == Just sfx

  , QC.testProperty "stripSuffix 2" $ \(STI t i) ->
      let (pfx,sfx) = IUT.splitAt i t
      in IUT.stripSuffix sfx t == Just pfx

  , QC.testProperty "cons" $ \c t -> IUT.singleton c <> IUT.fromText t == IUT.cons c (IUT.fromText t)
  , QC.testProperty "snoc" $ \c t -> IUT.fromText t <> IUT.singleton c == IUT.snoc (IUT.fromText t) c

  , QC.testProperty "uncons" $ \c t -> IUT.uncons (IUT.singleton c <> IUT.fromText t) == Just (c, IUT.fromText t)

  , QC.testProperty "unsnoc" $ \c t -> IUT.unsnoc (IUT.fromText t <> IUT.singleton c) == Just (IUT.fromText t, c)

  , QC.testProperty "break" $ \t -> let (l,r)   = IUT.break Data.Char.isAscii (IUT.fromText t)
                                    in  T.break Data.Char.isAscii t == (IUT.toText l,IUT.toText r)

  , QC.testProperty "span"  $ \t -> let (l,r)   = IUT.span Data.Char.isAscii (IUT.fromText t)
                                    in  T.span Data.Char.isAscii t == (IUT.toText l,IUT.toText r)

  , QC.testProperty "breakEnd" $ \t -> let (l,r)   = IUT.breakEnd Data.Char.isAscii (IUT.fromText t)
                                       in  t_breakEnd Data.Char.isAscii t == (IUT.toText l,IUT.toText r)

  , QC.testProperty "spanEnd"  $ \t -> let (l,r)   = IUT.spanEnd Data.Char.isAscii (IUT.fromText t)
                                       in  t_spanEnd Data.Char.isAscii t == (IUT.toText l,IUT.toText r)

  , QC.testProperty "splitAt/isPrefixOf" $ \t ->
      let t' = IUT.fromText t
      in and [ IUT.isPrefixOf (fst (IUT.splitAt i t')) t' | i <- [-5 .. 5+T.length t ] ]
  , QC.testProperty "splitAt/isSuffixOf" $ \t ->
      let t' = IUT.fromText t
      in and [ IUT.isSuffixOf (snd (IUT.splitAt i t')) t' | i <- [-5 .. 5+T.length t ] ]
  ]

t_breakEnd p t = t_spanEnd (not . p) t
t_spanEnd  p t = (T.dropWhileEnd p t, T.takeWhileEnd p t)

unitTests = testGroup "Unit-tests"
  [ testCase "fromText mempty" $ IUT.fromText mempty @?= mempty
  , testCase "fromShortByteString [0xc0,0x80]" $ IUT.fromShortByteString "\xc0\x80" @?= Nothing
  , testCase "fromByteString [0xc0,0x80]" $ IUT.fromByteString "\xc0\x80" @?= Nothing
  , testCase "fromByteString [0xf0,0x90,0x80,0x80]" $ IUT.fromByteString "\xf0\x90\x80\x80" @?= Just "\x10000"
  , testCase "fromByteString [0xf4,0x90,0x80,0x80]" $ IUT.fromByteString "\244\144\128\128" @?= Nothing
  , testCase "IsString U+D800" $ "\xFFFD" @?= (IUT.fromString "\xD800")
--  , testCase "IsString U+D800" $ (IUT.fromString "\xD800") @?= IUT.fromText ("\xD800" :: T.Text)

  , testCase "Binary.encode" $ encode ("Hello \8364 & \171581!\NUL" :: IUT.ShortText) @?= "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\DC2Hello \226\130\172 & \240\169\184\189!\NUL"
  , testCase "Binary.decode" $ decode ("\NUL\NUL\NUL\NUL\NUL\NUL\NUL\DC2Hello \226\130\172 & \240\169\184\189!\NUL") @?= ("Hello \8364 & \171581!\NUL" :: IUT.ShortText)
  , testCase "singleton" $ [ c | c <- [minBound..maxBound], IUT.singleton c /= IUT.fromText (T.singleton c) ] @?= []

  , testCase "splitAtEnd" $ IUT.splitAtEnd 1 "€€" @?= ("€","€")
  , testCase "split#1" $ IUT.split (== 'a') "aabbaca" @?= ["", "", "bb", "c", ""]
  , testCase "split#2" $ IUT.split (const False) "aabbaca" @?= ["aabbaca"]
  , testCase "split#3" $ IUT.split (const True) "abc" @?= ["","","",""]
  , testCase "split#4" $ IUT.split (const True) "" @?= [""]

  , testCase "literal0" $ IUT.unpack testLit0 @?= []
  , testCase "literal1" $ IUT.unpack testLit1 @?= ['€','\0','€','\0']
  , testCase "literal2" $ IUT.unpack testLit2 @?= ['\xFFFD','\xD7FF','\xFFFD','\xE000']
  , testCase "literal3" $ IUT.unpack testLit3 @?= ['\1'..'\x7f']
  , testCase "literal4" $ IUT.unpack testLit4 @?= map toEnum [0,1,126,127,128,129,130,256,2046,2047,2048,2049,2050,65530,65531,65532,65533,65534,65533,65535,65536,65537,65538,1114110,1114111]
  , testCase "literal5" $ IUT.unpack testLit5 @?= map toEnum [28961]
  , testCase "literal6" $ IUT.unpack testLit6 @?= map toEnum [0]
  , testCase "literal7" $ IUT.unpack testLit7 @?= map toEnum [66328]
  , testCase "literal8" $ IUT.unpack testLit8 @?= map toEnum [127]

    -- list literals
  , testCase "literal9"  $ [] @?= ("" :: IUT.ShortText)
  , testCase "literal10" $ ['¤','€','$'] @?= ("¤€$" :: IUT.ShortText)
  , testCase "literal12" $ IUT.unpack ['\xD800','\xD7FF','\xDFFF','\xE000'] @?= ['\xFFFD','\xD7FF','\xFFFD','\xE000']
 ]

-- isScalar :: Char -> Bool
-- isScalar c = c < '\xD800' || c >= '\xE000'


{-# NOINLINE testLit0 #-}
testLit0 :: IUT.ShortText
testLit0 = ""

{-# NOINLINE testLit1 #-}
testLit1 :: IUT.ShortText
testLit1 = "€\NUL€\NUL"

{-# NOINLINE testLit2 #-}
testLit2 :: IUT.ShortText
testLit2 = "\xD800\xD7FF\xDFFF\xE000"

{-# NOINLINE testLit3 #-}
testLit3 :: IUT.ShortText
testLit3 = "\SOH\STX\ETX\EOT\ENQ\ACK\a\b\t\n\v\f\r\SO\SI\DLE\DC1\DC2\DC3\DC4\NAK\SYN\ETB\CAN\EM\SUB\ESC\FS\GS\RS\US !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\DEL"

{-# NOINLINE testLit4 #-}
testLit4 :: IUT.ShortText
testLit4 = "\NUL\SOH~\DEL\128\129\130\256\2046\2047\2048\2049\2050\65530\65531\65532\65533\65534\65533\65535\65536\65537\65538\1114110\1114111"

{-# NOINLINE testLit5 #-}
testLit5 :: IUT.ShortText
testLit5 = "無"

{-# NOINLINE testLit6 #-}
testLit6 :: IUT.ShortText
testLit6 = "\NUL"

{-# NOINLINE testLit7 #-}
testLit7 :: IUT.ShortText
testLit7 = "𐌘"

{-# NOINLINE testLit8 #-}
testLit8 :: IUT.ShortText
testLit8 = "\x7f"