Skip to content

Commit 92eb1ea

Browse files
committed
Use newtypes for Arbitrary sorted lists
Makes property tests a little nicer.
1 parent d82a404 commit 92eb1ea

10 files changed

Lines changed: 236 additions & 224 deletions

containers-tests/containers-tests.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ library test-utils
329329
IntSetValidity
330330
Utils.ArbitrarySetMap
331331
Utils.MergeFunc
332-
Utils.NubSorted
332+
Utils.QuickCheck
333333
Utils.QuickCheckClasses
334334
Utils.Strictness
335335

containers-tests/test-utils/Utils/NubSorted.hs renamed to containers-tests/test-utils/Utils/QuickCheck.hs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1-
module Utils.NubSorted
1+
{-# LANGUAGE CPP #-}
2+
3+
-- | Useful newtypes with Arbitrary instances.
4+
module Utils.QuickCheck
25
( NubSorted(..)
36
, NubSortedOnFst(..)
7+
, SortedOnFst(..)
48
) where
59

610
import qualified Data.List as List
711
import qualified Data.List.NonEmpty as NonEmpty
812
import Data.Ord (comparing)
913
import Test.QuickCheck
14+
#if !MIN_VERSION_QuickCheck(2,17,0)
15+
import Data.Coerce (coerce)
16+
import Data.Ord (Down(..))
17+
#endif
1018

1119
newtype NubSorted a = NubSorted { getNubSorted :: [a] }
1220
deriving Show
@@ -31,3 +39,17 @@ nubSortBy cmp =
3139
map NonEmpty.head .
3240
NonEmpty.groupBy (\x y -> cmp x y == EQ) .
3341
List.sortBy cmp
42+
43+
newtype SortedOnFst a b = SortedOnFst { getSortedOnFst :: [(a, b)] }
44+
deriving Show
45+
46+
instance (Ord a, Arbitrary a, Arbitrary b) => Arbitrary (SortedOnFst a b) where
47+
arbitrary = SortedOnFst . List.sortBy (comparing fst) <$> arbitrary
48+
shrink =
49+
map (SortedOnFst . List.sortBy (comparing fst)) . shrink . getSortedOnFst
50+
51+
#if !MIN_VERSION_QuickCheck(2,17,0)
52+
instance Arbitrary a => Arbitrary (Down a) where
53+
arbitrary = Down <$> arbitrary
54+
shrink (Down x) = coerce (shrink x)
55+
#endif

containers-tests/tests/intmap-properties.hs

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Control.Applicative (Applicative(..))
1818
import Control.Arrow ((&&&))
1919
import Control.Monad ((<=<))
2020
import Control.Monad.Trans.Writer.Lazy
21+
import Data.Coerce (coerce)
2122
import qualified Data.Either as Either
2223
import qualified Data.Foldable as Foldable
2324
import Data.Monoid
@@ -44,6 +45,7 @@ import Test.QuickCheck.Function (apply)
4445
import Test.QuickCheck.Poly (A, B, C, OrdA)
4546
import qualified Test.QuickCheck.Classes.Base as Laws
4647

48+
import Utils.QuickCheck (NubSortedOnFst(..), SortedOnFst(..))
4749
import Utils.QuickCheckClasses (testLaws)
4850

4951
default (Int)
@@ -2151,64 +2153,58 @@ prop_mapKeysMonotonic (Positive a) b m =
21512153
prop_compare :: IntMap OrdA -> IntMap OrdA -> Property
21522154
prop_compare m1 m2 = compare m1 m2 === compare (toList m1) (toList m2)
21532155

2154-
prop_fromAscList :: [(Int, A)] -> Property
2155-
prop_fromAscList kxs =
2156+
prop_fromAscList :: SortedOnFst Int A -> Property
2157+
prop_fromAscList (SortedOnFst kxs) =
21562158
valid t .&&.
2157-
t === fromList sortedKxs
2159+
t === fromList kxs
21582160
where
2159-
sortedKxs = List.sortBy (comparing fst) kxs
2160-
t = fromAscList sortedKxs
2161+
t = fromAscList kxs
21612162

2162-
prop_fromAscListWith :: Fun (A, A) A -> [(Int, A)] -> Property
2163-
prop_fromAscListWith f kxs =
2163+
prop_fromAscListWith :: Fun (A, A) A -> SortedOnFst Int A -> Property
2164+
prop_fromAscListWith f (SortedOnFst kxs) =
21642165
valid t .&&.
2165-
t === fromListWith (applyFun2 f) sortedKxs
2166+
t === fromListWith (applyFun2 f) kxs
21662167
where
2167-
sortedKxs = List.sortBy (comparing fst) kxs
2168-
t = fromAscListWith (applyFun2 f) sortedKxs
2168+
t = fromAscListWith (applyFun2 f) kxs
21692169

2170-
prop_fromAscListWithKey :: Fun (Int, A, A) A -> [(Int, A)] -> Property
2171-
prop_fromAscListWithKey f kxs =
2170+
prop_fromAscListWithKey :: Fun (Int, A, A) A -> SortedOnFst Int A -> Property
2171+
prop_fromAscListWithKey f (SortedOnFst kxs) =
21722172
valid t .&&.
2173-
t === fromListWithKey (applyFun3 f) sortedKxs
2173+
t === fromListWithKey (applyFun3 f) kxs
21742174
where
2175-
sortedKxs = List.sortBy (comparing fst) kxs
2176-
t = fromAscListWithKey (applyFun3 f) sortedKxs
2175+
t = fromAscListWithKey (applyFun3 f) kxs
21772176

2178-
prop_fromAscListUpsert :: Fun (A, Maybe B) B -> [(Int, A)] -> Property
2179-
prop_fromAscListUpsert f kxs =
2177+
prop_fromAscListUpsert :: Fun (A, Maybe B) B -> SortedOnFst Int A -> Property
2178+
prop_fromAscListUpsert f (SortedOnFst kxs) =
21802179
valid t .&&.
2181-
t === fromListUpsert (applyFun2 f) sortedKxs
2180+
t === fromListUpsert (applyFun2 f) kxs
21822181
where
2183-
sortedKxs = List.sortBy (comparing fst) kxs
2184-
t = fromAscListUpsert (applyFun2 f) sortedKxs
2182+
t = fromAscListUpsert (applyFun2 f) kxs
21852183

2186-
prop_fromDistinctAscList :: [(Int, A)] -> Property
2187-
prop_fromDistinctAscList kxs =
2184+
prop_fromDistinctAscList :: NubSortedOnFst Int A -> Property
2185+
prop_fromDistinctAscList (NubSortedOnFst kxs) =
21882186
valid t .&&.
2189-
toList t === nubSortedKxs
2187+
t === fromList kxs .&&.
2188+
toList t === kxs
21902189
where
2191-
nubSortedKxs =
2192-
List.map NE.head $
2193-
NE.groupBy ((==) `on` fst) $
2194-
List.sortBy (comparing fst) kxs
2195-
t = fromDistinctAscList nubSortedKxs
2190+
t = fromDistinctAscList kxs
21962191

2197-
prop_fromDescList :: [(Int, A)] -> Property
2198-
prop_fromDescList kxs =
2192+
prop_fromDescList :: SortedOnFst (Down Int) A -> Property
2193+
prop_fromDescList kxs' =
21992194
valid t .&&.
2200-
t === fromList sortedKxs
2195+
t === fromList kxs
22012196
where
2202-
sortedKxs = List.sortBy (comparing (Down . fst)) kxs
2203-
t = fromDescList sortedKxs
2197+
kxs = coerce kxs' :: [(Int, A)]
2198+
t = fromDescList kxs
22042199

2205-
prop_fromDescListUpsert :: Fun (A, Maybe B) B -> [(Int, A)] -> Property
2206-
prop_fromDescListUpsert f kxs =
2200+
prop_fromDescListUpsert
2201+
:: Fun (A, Maybe B) B -> SortedOnFst (Down Int) A -> Property
2202+
prop_fromDescListUpsert f kxs' =
22072203
valid t .&&.
2208-
t === fromListUpsert (applyFun2 f) sortedKxs
2204+
t === fromListUpsert (applyFun2 f) kxs
22092205
where
2210-
sortedKxs = List.sortBy (comparing (Down . fst)) kxs
2211-
t = fromDescListUpsert (applyFun2 f) sortedKxs
2206+
kxs = coerce kxs' :: [(Int, A)]
2207+
t = fromDescListUpsert (applyFun2 f) kxs
22122208

22132209
prop_fromListWith :: Fun (A, A) A -> [(Int, A)] -> Property
22142210
prop_fromListWith f kxs =

containers-tests/tests/intmap-strictness.hs

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ import Data.Function (on)
1212
import Data.Functor.Compose
1313
import Data.Functor.Identity (Identity(..))
1414
import qualified Data.List as List
15-
import qualified Data.List.NonEmpty as NE
1615
import Data.Maybe (catMaybes, mapMaybe)
17-
import Data.Ord (Down(..), comparing)
16+
import Data.Ord (Down(..))
1817
import Test.ChasingBottoms.IsBottom
1918
import Test.Tasty (TestTree, defaultMain, testGroup)
2019
import Test.Tasty.QuickCheck (testProperty)
@@ -39,7 +38,7 @@ import qualified Data.IntMap.Merge.Lazy as LMerge
3938
import Data.Containers.ListUtils
4039

4140
import Utils.MergeFunc (WhenMatchedFunc(..), WhenMissingFunc(..))
42-
import Utils.NubSorted (NubSortedOnFst(..))
41+
import Utils.QuickCheck (NubSortedOnFst(..), SortedOnFst(..))
4342
import Utils.Strictness
4443
(Bot(..), Func, Func2, Func3, applyFunc, applyFunc2, applyFunc3)
4544

@@ -162,100 +161,102 @@ prop_lazyFromListUpsert fun kvs = isNotBottomProp (L.fromListUpsert f kvs')
162161
f = coerce (applyFunc2 fun) :: A -> Maybe B -> B
163162
kvs' = coerce kvs :: [(Key, A)]
164163

165-
prop_strictFromAscList :: [(Key, Bot A)] -> Property
164+
prop_strictFromAscList :: SortedOnFst Key (Bot A) -> Property
166165
prop_strictFromAscList kvs =
167166
isBottom (M.fromAscList kvs') === isBottom (M.fromList kvs')
168167
where
169-
kvs' = List.sortBy (comparing fst) (coerce kvs) :: [(Key, A)]
168+
kvs' = coerce kvs :: [(Key, A)]
170169

171-
prop_lazyFromAscList :: [(Key, Bot A)] -> Property
170+
prop_lazyFromAscList :: SortedOnFst Key (Bot A) -> Property
172171
prop_lazyFromAscList kvs = isNotBottomProp (L.fromAscList kvs')
173172
where
174-
kvs' = List.sortBy (comparing fst) (coerce kvs) :: [(Key, A)]
173+
kvs' = coerce kvs :: [(Key, A)]
175174

176-
prop_strictFromAscListWith :: Func2 A A (Bot A) -> [(Key, Bot A)] -> Property
175+
prop_strictFromAscListWith
176+
:: Func2 A A (Bot A) -> SortedOnFst Key (Bot A) -> Property
177177
prop_strictFromAscListWith fun kvs =
178178
isBottom (M.fromAscListWith f kvs') === isBottom (M.fromListWith f kvs')
179179
where
180180
f = coerce (applyFunc2 fun)
181-
kvs' = List.sortBy (comparing fst) (coerce kvs) :: [(Key, A)]
181+
kvs' = coerce kvs :: [(Key, A)]
182182

183-
prop_lazyFromAscListWith :: Func2 A A (Bot A) -> [(Key, Bot A)] -> Property
183+
prop_lazyFromAscListWith
184+
:: Func2 A A (Bot A) -> SortedOnFst Key (Bot A) -> Property
184185
prop_lazyFromAscListWith fun kvs = isNotBottomProp (L.fromAscListWith f kvs')
185186
where
186187
f = coerce (applyFunc2 fun)
187-
kvs' = List.sortBy (comparing fst) (coerce kvs) :: [(Key, A)]
188+
kvs' = coerce kvs :: [(Key, A)]
188189

189190
prop_strictFromAscListWithKey
190-
:: Func3 Key A A (Bot A) -> [(Key, Bot A)] -> Property
191+
:: Func3 Key A A (Bot A) -> SortedOnFst Key (Bot A) -> Property
191192
prop_strictFromAscListWithKey fun kvs =
192193
isBottom (M.fromAscListWithKey f kvs') ===
193194
isBottom (M.fromListWithKey f kvs')
194195
where
195196
f = coerce (applyFunc3 fun)
196-
kvs' = List.sortBy (comparing fst) (coerce kvs) :: [(Key, A)]
197+
kvs' = coerce kvs :: [(Key, A)]
197198

198199
prop_lazyFromAscListWithKey
199-
:: Func3 Key A A (Bot A) -> [(Key, Bot A)] -> Property
200+
:: Func3 Key A A (Bot A) -> SortedOnFst Key (Bot A) -> Property
200201
prop_lazyFromAscListWithKey fun kvs =
201202
isNotBottomProp (L.fromAscListWithKey f kvs')
202203
where
203204
f = coerce (applyFunc3 fun)
204-
kvs' = List.sortBy (comparing fst) (coerce kvs) :: [(Key, A)]
205+
kvs' = coerce kvs :: [(Key, A)]
205206

206207
prop_strictFromAscListUpsert
207-
:: Func2 A (Maybe B) (Bot B) -> [(Key, Bot A)] -> Property
208+
:: Func2 A (Maybe B) (Bot B) -> SortedOnFst Key (Bot A) -> Property
208209
prop_strictFromAscListUpsert fun kvs =
209210
isBottom (M.fromAscListUpsert f kvs') === isBottom (M.fromListUpsert f kvs')
210211
where
211212
f = coerce (applyFunc2 fun) :: A -> Maybe B -> B
212-
kvs' = List.sortBy (comparing fst) (coerce kvs) :: [(Key, A)]
213+
kvs' = coerce kvs :: [(Key, A)]
213214

214215
prop_lazyFromAscListUpsert
215-
:: Func2 A (Maybe B) (Bot B) -> [(Key, Bot A)] -> Property
216+
:: Func2 A (Maybe B) (Bot B) -> SortedOnFst Key (Bot A) -> Property
216217
prop_lazyFromAscListUpsert fun kvs =
217218
isNotBottomProp (L.fromAscListUpsert f kvs')
218219
where
219220
f = coerce (applyFunc2 fun) :: A -> Maybe B -> B
220-
kvs' = List.sortBy (comparing fst) (coerce kvs) :: [(Key, A)]
221+
kvs' = coerce kvs :: [(Key, A)]
221222

222-
prop_strictFromDistinctAscList :: [(Key, Bot A)] -> Property
223+
prop_strictFromDistinctAscList :: NubSortedOnFst Key (Bot A) -> Property
223224
prop_strictFromDistinctAscList kvs =
224225
isBottom (M.fromDistinctAscList kvs') === isBottom (M.fromList kvs')
225226
where
226-
kvs' = uniqOn fst $ List.sortBy (comparing fst) (coerce kvs) :: [(Key, A)]
227+
kvs' = coerce kvs :: [(Key, A)]
227228

228-
prop_lazyFromDistinctAscList :: [(Key, Bot A)] -> Property
229+
prop_lazyFromDistinctAscList :: NubSortedOnFst Key (Bot A) -> Property
229230
prop_lazyFromDistinctAscList kvs = isNotBottomProp (L.fromDistinctAscList kvs')
230231
where
231-
kvs' = uniqOn fst $ List.sortBy (comparing fst) (coerce kvs) :: [(Key, A)]
232+
kvs' = coerce kvs :: [(Key, A)]
232233

233-
prop_strictFromDescList :: [(Key, Bot A)] -> Property
234+
prop_strictFromDescList :: SortedOnFst (Down Key) (Bot A) -> Property
234235
prop_strictFromDescList kvs =
235236
isBottom (M.fromDescList kvs') === isBottom (M.fromList kvs')
236237
where
237-
kvs' = List.sortBy (comparing (Down . fst)) (coerce kvs) :: [(Key, A)]
238+
kvs' = coerce kvs :: [(Key, A)]
238239

239-
prop_lazyFromDescList :: [(Key, Bot A)] -> Property
240+
prop_lazyFromDescList :: SortedOnFst (Down Key) (Bot A) -> Property
240241
prop_lazyFromDescList kvs = isNotBottomProp (L.fromDescList kvs')
241242
where
242-
kvs' = List.sortBy (comparing (Down . fst)) (coerce kvs) :: [(Key, A)]
243+
kvs' = coerce kvs :: [(Key, A)]
243244

244245
prop_strictFromDescListUpsert
245-
:: Func2 A (Maybe B) (Bot B) -> [(Key, Bot A)] -> Property
246+
:: Func2 A (Maybe B) (Bot B) -> SortedOnFst (Down Key) (Bot A) -> Property
246247
prop_strictFromDescListUpsert fun kvs =
247248
isBottom (M.fromDescListUpsert f kvs') === isBottom (M.fromListUpsert f kvs')
248249
where
249250
f = coerce (applyFunc2 fun) :: A -> Maybe B -> B
250-
kvs' = List.sortBy (comparing (Down . fst)) (coerce kvs) :: [(Key, A)]
251+
kvs' = coerce kvs :: [(Key, A)]
251252

252253
prop_lazyFromDescListUpsert
253-
:: Func2 A (Maybe B) (Bot B) -> [(Key, Bot A)] -> Property
254+
:: Func2 A (Maybe B) (Bot B) -> SortedOnFst (Down Key) (Bot A) -> Property
254255
prop_lazyFromDescListUpsert fun kvs =
255256
isNotBottomProp (L.fromDescListUpsert f kvs')
256257
where
257258
f = coerce (applyFunc2 fun) :: A -> Maybe B -> B
258-
kvs' = List.sortBy (comparing (Down . fst)) (coerce kvs) :: [(Key, A)]
259+
kvs' = coerce kvs :: [(Key, A)]
259260

260261
prop_strictInsert :: Key -> Bot A -> IntMap A -> Property
261262
prop_strictInsert k (Bot x) m = isBottom (M.insert k x m) === isBottom x
@@ -1186,10 +1187,6 @@ const2 x _ _ = x
11861187
const3 :: a -> b -> c -> d -> a
11871188
const3 x _ _ _ = x
11881189

1189-
-- | Keep the first of adjacent equal elements.
1190-
uniqOn :: Eq b => (a -> b) -> [a] -> [a]
1191-
uniqOn f = map NE.head . NE.groupBy ((==) `on` f)
1192-
11931190
{--------------------------------------------------------------------
11941191
Merge stuff
11951192
--------------------------------------------------------------------}

containers-tests/tests/intset-properties.hs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
{-# LANGUAGE CPP #-}
22
import Control.Applicative (Const(..))
33
import Data.Bits ((.&.), popCount)
4+
import Data.Coerce (coerce)
45
import Data.Word (Word)
56
import Data.IntSet
67
import Data.List (nub,sort)
78
import qualified Data.List as List
89
import Data.Maybe (listToMaybe)
910
import qualified Data.Maybe as Maybe
1011
import Data.Monoid (mempty)
12+
import Data.Ord (Down(..))
1113
import Data.Proxy (Proxy(..))
1214
import Data.List.NonEmpty (NonEmpty(..))
1315
import qualified Data.List.NonEmpty as NE
@@ -20,6 +22,7 @@ import Test.Tasty.HUnit
2022
import Test.Tasty.QuickCheck hiding ((.&.))
2123
import qualified Test.QuickCheck.Classes.Base as Laws
2224

25+
import Utils.QuickCheck (NubSorted(..))
2326
import Utils.QuickCheckClasses (testLaws)
2427

2528
main :: IO ()
@@ -567,31 +570,32 @@ prop_deleteMin s = toList (deleteMin s) === if null s then [] else tail (toList
567570
prop_deleteMax :: IntSet -> Property
568571
prop_deleteMax s = toList (deleteMax s) === if null s then [] else init (toList s)
569572

570-
prop_fromAscList :: [Int] -> Property
571-
prop_fromAscList xs =
573+
prop_fromAscList :: SortedList Int -> Property
574+
prop_fromAscList (Sorted xs) =
572575
valid t .&&.
573-
toList t === nubSortedXs
576+
t === fromList xs .&&.
577+
toList t === nubXs
574578
where
575-
sortedXs = sort xs
576-
nubSortedXs = List.map NE.head $ NE.group sortedXs
577-
t = fromAscList sortedXs
579+
nubXs = List.map NE.head $ NE.group xs
580+
t = fromAscList xs
578581

579-
prop_fromDistinctAscList :: [Int] -> Property
580-
prop_fromDistinctAscList xs =
582+
prop_fromDistinctAscList :: NubSorted Int -> Property
583+
prop_fromDistinctAscList (NubSorted xs) =
581584
valid t .&&.
582-
toList t === nubSortedXs
585+
t === fromList xs .&&.
586+
toList t === xs
583587
where
584-
nubSortedXs = List.map NE.head $ NE.group $ sort xs
585-
t = fromDistinctAscList nubSortedXs
588+
t = fromDistinctAscList xs
586589

587-
prop_fromDescList :: [Int] -> Property
588-
prop_fromDescList xs =
590+
prop_fromDescList :: SortedList (Down Int) -> Property
591+
prop_fromDescList xs' =
589592
valid t .&&.
590-
toList t === nubSortedXs
593+
t === fromList xs .&&.
594+
toList t === reverse nubXs
591595
where
592-
sortedXs = sort xs
593-
nubSortedXs = List.map NE.head $ NE.group sortedXs
594-
t = fromDescList (reverse sortedXs)
596+
xs = coerce xs' :: [Int]
597+
nubXs = List.map NE.head $ NE.group xs
598+
t = fromDescList xs
595599

596600
prop_compareSize :: IntSet -> Int -> Property
597601
prop_compareSize t c = compareSize t c === compare (size t) c

0 commit comments

Comments
 (0)