summaryrefslogtreecommitdiff
path: root/Test/Regions.hs
blob: 383fb57640ae193fbea4c78b5657dac7f4f5414f (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
{-# LANGUAGE TemplateHaskell #-}
import Test.QuickCheck
import Test.QuickCheck.All

import Data.List (sort, group)
import Data.Maybe (isNothing, isJust)

import Bustle.Regions

instance Arbitrary Stripe where
    arbitrary = do
        top <- fmap abs arbitrary
        bottom <- arbitrary `suchThat` (>= top)
        return $ Stripe top bottom

newtype NonOverlappingStripes = NonOverlappingStripes [Stripe]
  deriving
    (Show, Eq, Ord)

instance Arbitrary NonOverlappingStripes where
    arbitrary = do
        -- there is no orderedList1 sadly
        stripes <- fmap sort (listOf1 arbitrary) `suchThat` nonOverlapping
        return $ NonOverlappingStripes stripes

newtype ValidRegions a = ValidRegions (Regions a)
  deriving
    (Show, Eq, Ord)

instance (Eq a, Arbitrary a) => Arbitrary (ValidRegions a) where
    arbitrary = do
        NonOverlappingStripes stripes <- arbitrary
        values <- vector (length stripes) `suchThat` unique
        return $ ValidRegions (zip stripes values)
      where
        unique xs = all (== 1) . map length . group $ xs

instance (Eq a, Arbitrary a) => Arbitrary (RegionSelection a) where
    arbitrary = do
        ValidRegions rs <- arbitrary
        return $ regionSelectionNew rs

prop_InitiallyUnselected = \rs -> isNothing $ rsCurrent rs
prop_UpDoesNothing = \rs -> isNothing $ rsCurrent $ regionSelectionUp rs

prop_DownDoesNothing vr@(ValidRegions regions) =
    withRegions vr $ \rs ->
        let final = last regions
            rs'   = regionSelectionLast rs
        in
            rsCurrent (regionSelectionDown rs') == Just final

prop_DownWorks vr@(ValidRegions regions) =
    withRegions vr $ \rs ->
        rsCurrent (regionSelectionDown rs) == Just (head regions)

withRegions :: Testable t
            => ValidRegions a
            -> (RegionSelection a -> t)
            -> t
withRegions (ValidRegions regions) f = f (regionSelectionNew regions)

prop_UpdateToFirst :: (Eq a)
                  => ValidRegions a
                  -> Bool
prop_UpdateToFirst vr@(ValidRegions regions) = withRegions vr $ \rs ->
    Just first == rsCurrent (regionSelectionUpdate y rs) &&
    null (rsBefore rs)
  where
    first@(Stripe top bottom, _) = head regions
    y = (top + bottom) / 2

prop_SelectFirst :: (Eq a)
                 => ValidRegions a
                 -> Bool
prop_SelectFirst vr@(ValidRegions regions) = withRegions vr $ \rs ->
    Just (head regions) == rsCurrent (regionSelectionFirst rs)

prop_SelectLast :: (Eq a)
                => ValidRegions a
                -> Bool
prop_SelectLast vr@(ValidRegions regions) = withRegions vr $ \rs ->
    Just (last regions) == rsCurrent (regionSelectionLast rs)

prop_UpdateToAny :: (Eq a, Show a)
                => ValidRegions a
                -> Property
prop_UpdateToAny vr@(ValidRegions regions) =
    withRegions vr $ \rs ->
    forAll (elements regions) $ \ r@(s, _) ->
        rsCurrent (regionSelectionUpdate (midpoint s) rs) == Just r

shuffled :: [a] -> Gen [a]
shuffled [] = return []
shuffled xs = do
    i <- choose (0, length xs - 1)
    let x    = xs !! i
        pre  = take i xs
        post = drop (i + 1) xs
    xs' <- shuffled (pre ++ post)
    return (x:xs')

prop_UpdateToAll :: (Eq a, Show a)
                => ValidRegions a
                -> Property
prop_UpdateToAll vr@(ValidRegions regions) =
    withRegions vr $ \rs ->
    forAll (shuffled regions) $ \regions' ->
        updateAndForward rs regions'
  where
    updateAndForward rs [] = True
    updateAndForward rs (x:xs) =
        let rs' = regionSelectionUpdate (midpoint (fst x)) rs
        in rsCurrent rs' == Just x && updateAndForward rs' xs

randomMutation :: Gen (RegionSelection a -> RegionSelection a)
randomMutation = do
    y <- arbitrary
    elements [ regionSelectionUp
             , regionSelectionDown
             , regionSelectionFirst
             , regionSelectionLast
             , regionSelectionUpdate y
             ]

randomMutations :: Gen (RegionSelection a -> RegionSelection a)
randomMutations = do
    fs <- listOf randomMutation
    return $ foldr (.) id fs

prop_ClickAlwaysInSelection = \rs ->
    forAll (fmap Blind randomMutations) $ \(Blind f) ->
      let
        rs' = f rs
      in
        isJust (rsCurrent rs') ==>
          let
            Just (Stripe top bottom, _) = rsCurrent rs'
            y = rsLastClick rs'
          in
            top <= y && y <= bottom

prop_SelectWorks :: (Eq a, Show a)
                 => ValidRegions a
                 -> Property
prop_SelectWorks vr@(ValidRegions regions) =
    withRegions vr $ \rs ->
    forAll (elements regions) $ \ r@(s, x) ->
      Just r == rsCurrent (regionSelectionSelect x rs)

prop_Append :: (Eq a, Show a)
            => ValidRegions a
            -> Property
prop_Append vr@(ValidRegions regions) =
    forAll (choose (0, length regions - 1)) $ \i ->
        let as = take i regions
            bs = drop i regions
        in regionSelectionAppend bs (regionSelectionNew as) == regionSelectionNew regions

runTests = $quickCheckAll

main = do
    runTests
    return ()