aboutsummaryrefslogtreecommitdiffstats
path: root/tests/Properties.hs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/Properties.hs')
-rw-r--r--tests/Properties.hs1320
1 files changed, 138 insertions, 1182 deletions
diff --git a/tests/Properties.hs b/tests/Properties.hs
index ae3f2e7..70d7e18 100644
--- a/tests/Properties.hs
+++ b/tests/Properties.hs
@@ -1,1191 +1,147 @@
-{-# OPTIONS -fglasgow-exts -w #-}
-module Properties where
+import Test.QuickCheck
+
+-- Our QC instances and properties.
+import Instances
+import Properties.Delete
+import Properties.Failure
+import Properties.Floating
+import Properties.Focus
+import Properties.GreedyView
+import Properties.Insert
+import Properties.Screen
+import Properties.Shift
+import Properties.Stack
+import Properties.StackSet
+import Properties.Swap
+import Properties.View
+import Properties.Workspace
+import Properties.Layout.Full
+import Properties.Layout.Tall
-import XMonad.StackSet hiding (filter)
-import XMonad.Layout
-import XMonad.Core hiding (workspaces,trace)
-import XMonad.Operations ( applyResizeIncHint, applyMaxSizeHint )
-import qualified XMonad.StackSet as S (filter)
-
-import Debug.Trace
-import Data.Word
-import Graphics.X11.Xlib.Types (Rectangle(..),Position,Dimension)
-import Data.Ratio
-import Data.Maybe
import System.Environment
-import Control.Exception (assert)
-import qualified Control.Exception.Extensible as C
-import Control.Monad
-import Test.QuickCheck hiding (promote)
-import System.IO.Unsafe
-import System.IO
-import System.Random hiding (next)
import Text.Printf
-import Data.List (nub,sort,sortBy,group,sort,intersperse,genericLength)
-import qualified Data.List as L
-import Data.Char (ord)
-import Data.Map (keys,elems)
-import qualified Data.Map as M
-
--- ---------------------------------------------------------------------
--- QuickCheck properties for the StackSet
-
--- Some general hints for creating StackSet properties:
---
--- * ops that mutate the StackSet are usually local
--- * most ops on StackSet should either be trivially reversible, or
--- idempotent, or both.
-
---
--- The all important Arbitrary instance for StackSet.
---
-instance (Integral i, Integral s, Eq a, Arbitrary a, Arbitrary l, Arbitrary sd)
- => Arbitrary (StackSet i l a s sd) where
- arbitrary = do
- sz <- choose (1,10) -- number of workspaces
- n <- choose (0,sz-1) -- pick one to be in focus
- sc <- choose (1,sz) -- a number of physical screens
- lay <- arbitrary -- pick any layout
- sds <- replicateM sc arbitrary
- ls <- vector sz -- a vector of sz workspaces
-
- -- pick a random item in each stack to focus
- fs <- sequence [ if null s then return Nothing
- else liftM Just (choose ((-1),length s-1))
- | s <- ls ]
-
- return $ fromList (fromIntegral n, sds,fs,ls,lay)
-
-
--- | fromList. Build a new StackSet from a list of list of elements,
--- keeping track of the currently focused workspace, and the total
--- number of workspaces. If there are duplicates in the list, the last
--- occurence wins.
---
--- 'o' random workspace
--- 'm' number of physical screens
--- 'fs' random focused window on each workspace
--- 'xs' list of list of windows
---
-fromList :: (Integral i, Integral s, Eq a) => (i, [sd], [Maybe Int], [[a]], l) -> StackSet i l a s sd
-fromList (_,_,_,[],_) = error "Cannot build a StackSet from an empty list"
-
-fromList (o,m,fs,xs,l) =
- let s = view o $
- foldr (\(i,ys) s ->
- foldr insertUp (view i s) ys)
- (new l [0..genericLength xs-1] m) (zip [0..] xs)
- in foldr (\f t -> case f of
- Nothing -> t
- Just i -> foldr (const focusUp) t [0..i] ) s fs
-
-------------------------------------------------------------------------
-
---
--- Just generate StackSets with Char elements.
---
-type T = StackSet (NonNegative Int) Int Char Int Int
-
--- Useful operation, the non-local workspaces
-hidden_spaces x = map workspace (visible x) ++ hidden x
-
--- Basic data invariants of the StackSet
---
--- With the new zipper-based StackSet, tracking focus is no longer an
--- issue: the data structure enforces focus by construction.
---
--- But we still need to ensure there are no duplicates, and master/and
--- the xinerama mapping aren't checked by the data structure at all.
---
--- * no element should ever appear more than once in a StackSet
--- * the xinerama screen map should be:
--- -- keys should always index valid workspaces
--- -- monotonically ascending in the elements
--- * the current workspace should be a member of the xinerama screens
---
-invariant (s :: T) = and
- -- no duplicates
- [ noDuplicates
-
- -- all this xinerama stuff says we don't have the right structure
--- , validScreens
--- , validWorkspaces
--- , inBounds
- ]
-
- where
- ws = concat [ focus t : up t ++ down t
- | w <- workspace (current s) : map workspace (visible s) ++ hidden s
- , t <- maybeToList (stack w)] :: [Char]
- noDuplicates = nub ws == ws
-
--- validScreens = monotonic . sort . M. . (W.current s : W.visible : W$ s
-
--- validWorkspaces = and [ w `elem` allworkspaces | w <- (M.keys . screens) s ]
--- where allworkspaces = map tag $ current s : prev s ++ next s
-
--- inBounds = and [ w >=0 && w < size s | (w,sc) <- M.assocs (screens s) ]
-
-monotonic [] = True
-monotonic (x:[]) = True
-monotonic (x:y:zs) | x == y-1 = monotonic (y:zs)
- | otherwise = False
-
-prop_invariant = invariant
-
--- and check other ops preserve invariants
-prop_empty_I (n :: Positive Int) l = forAll (choose (1,fromIntegral n)) $ \m ->
- forAll (vector m) $ \ms ->
- invariant $ new l [0..fromIntegral n-1] ms
-
-prop_view_I (n :: NonNegative Int) (x :: T) =
- invariant $ view (fromIntegral n) x
-
-prop_greedyView_I (n :: NonNegative Int) (x :: T) =
- invariant $ greedyView (fromIntegral n) x
-
-prop_focusUp_I (n :: NonNegative Int) (x :: T) =
- invariant $ foldr (const focusUp) x [1..n]
-prop_focusMaster_I (n :: NonNegative Int) (x :: T) =
- invariant $ foldr (const focusMaster) x [1..n]
-prop_focusDown_I (n :: NonNegative Int) (x :: T) =
- invariant $ foldr (const focusDown) x [1..n]
-
-prop_focus_I (n :: NonNegative Int) (x :: T) =
- case peek x of
- Nothing -> True
- Just _ -> let w = focus . fromJust . stack . workspace . current $ foldr (const focusUp) x [1..n]
- in invariant $ focusWindow w x
-
-prop_insertUp_I n (x :: T) = invariant $ insertUp n x
-
-prop_delete_I (x :: T) = invariant $
- case peek x of
- Nothing -> x
- Just i -> delete i x
-
-prop_swap_master_I (x :: T) = invariant $ swapMaster x
-
-prop_swap_left_I (n :: NonNegative Int) (x :: T) =
- invariant $ foldr (const swapUp ) x [1..n]
-prop_swap_right_I (n :: NonNegative Int) (x :: T) =
- invariant $ foldr (const swapDown) x [1..n]
-
-prop_shift_I (n :: NonNegative Int) (x :: T) =
- n `tagMember` x ==> invariant $ shift (fromIntegral n) x
-
-prop_shift_win_I (n :: NonNegative Int) (w :: Char) (x :: T) =
- n `tagMember` x && w `member` x ==> invariant $ shiftWin (fromIntegral n) w x
-
-
--- ---------------------------------------------------------------------
--- 'new'
-
--- empty StackSets have no windows in them
-prop_empty (EmptyStackSet x) =
- all (== Nothing) [ stack w | w <- workspace (current x)
- : map workspace (visible x) ++ hidden x ]
-
--- empty StackSets always have focus on first workspace
-prop_empty_current (NonEmptyNubList ns) (NonEmptyNubList sds) l =
- -- TODO, this is ugly
- length sds <= length ns ==>
- tag (workspace $ current x) == head ns
- where x = new l ns sds :: T
-
--- no windows will be a member of an empty workspace
-prop_member_empty i (EmptyStackSet x)
- = member i x == False
-
--- ---------------------------------------------------------------------
--- viewing workspaces
-
--- view sets the current workspace to 'n'
-prop_view_current (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
- tag (workspace $ current (view i x)) == i
- where
- i = fromIntegral n
-
--- view *only* sets the current workspace, and touches Xinerama.
--- no workspace contents will be changed.
-prop_view_local (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
- workspaces x == workspaces (view i x)
- where
- workspaces a = sortBy (\s t -> tag s `compare` tag t) $
- workspace (current a)
- : map workspace (visible a) ++ hidden a
- i = fromIntegral n
-
--- view should result in a visible xinerama screen
--- prop_view_xinerama (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
--- M.member i (screens (view i x))
--- where
--- i = fromIntegral n
-
--- view is idempotent
-prop_view_idem (x :: T) (i :: NonNegative Int) = i `tagMember` x ==> view i (view i x) == (view i x)
-
--- view is reversible, though shuffles the order of hidden/visible
-prop_view_reversible (i :: NonNegative Int) (x :: T) =
- i `tagMember` x ==> normal (view n (view i x)) == normal x
- where n = tag (workspace $ current x)
-
--- ---------------------------------------------------------------------
--- greedyViewing workspaces
-
--- greedyView sets the current workspace to 'n'
-prop_greedyView_current (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
- tag (workspace $ current (greedyView i x)) == i
- where
- i = fromIntegral n
-
--- greedyView leaves things unchanged for invalid workspaces
-prop_greedyView_current_id (x :: T) (n :: NonNegative Int) = not (i `tagMember` x) ==>
- tag (workspace $ current (greedyView i x)) == j
- where
- i = fromIntegral n
- j = tag (workspace (current x))
-
--- greedyView *only* sets the current workspace, and touches Xinerama.
--- no workspace contents will be changed.
-prop_greedyView_local (x :: T) (n :: NonNegative Int) = i `tagMember` x ==>
- workspaces x == workspaces (greedyView i x)
- where
- workspaces a = sortBy (\s t -> tag s `compare` tag t) $
- workspace (current a)
- : map workspace (visible a) ++ hidden a
- i = fromIntegral n
-
--- greedyView is idempotent
-prop_greedyView_idem (x :: T) (i :: NonNegative Int) = i `tagMember` x ==> greedyView i (greedyView i x) == (greedyView i x)
-
--- greedyView is reversible, though shuffles the order of hidden/visible
-prop_greedyView_reversible (i :: NonNegative Int) (x :: T) =
- i `tagMember` x ==> normal (greedyView n (greedyView i x)) == normal x
- where n = tag (workspace $ current x)
-
--- normalise workspace list
-normal s = s { hidden = sortBy g (hidden s), visible = sortBy f (visible s) }
- where
- f = \a b -> tag (workspace a) `compare` tag (workspace b)
- g = \a b -> tag a `compare` tag b
-
--- ---------------------------------------------------------------------
--- Xinerama
-
--- every screen should yield a valid workspace
--- prop_lookupWorkspace (n :: NonNegative Int) (x :: T) =
--- s < M.size (screens x) ==>
--- fromJust (lookupWorkspace s x) `elem` (map tag $ current x : prev x ++ next x)
--- where
--- s = fromIntegral n
-
--- ---------------------------------------------------------------------
--- peek/index
-
--- peek either yields nothing on the Empty workspace, or Just a valid window
-prop_member_peek (x :: T) =
- case peek x of
- Nothing -> True {- then we don't know anything -}
- Just i -> member i x
-
--- ---------------------------------------------------------------------
--- index
-
--- the list returned by index should be the same length as the actual
--- windows kept in the zipper
-prop_index_length (x :: T) =
- case stack . workspace . current $ x of
- Nothing -> length (index x) == 0
- Just it -> length (index x) == length (focus it : up it ++ down it)
-
--- ---------------------------------------------------------------------
--- rotating focus
---
-
--- master/focus
---
--- The tiling order, and master window, of a stack is unaffected by focus changes.
---
-prop_focus_left_master (n :: NonNegative Int) (x::T) =
- index (foldr (const focusUp) x [1..n]) == index x
-prop_focus_right_master (n :: NonNegative Int) (x::T) =
- index (foldr (const focusDown) x [1..n]) == index x
-prop_focus_master_master (n :: NonNegative Int) (x::T) =
- index (foldr (const focusMaster) x [1..n]) == index x
-
-prop_focusWindow_master (n :: NonNegative Int) (x :: T) =
- case peek x of
- Nothing -> True
- Just _ -> let s = index x
- i = fromIntegral n `mod` length s
- in index (focusWindow (s !! i) x) == index x
-
--- shifting focus is trivially reversible
-prop_focus_left (x :: T) = (focusUp (focusDown x)) == x
-prop_focus_right (x :: T) = (focusDown (focusUp x)) == x
-
--- focus master is idempotent
-prop_focusMaster_idem (x :: T) = focusMaster x == focusMaster (focusMaster x)
-
--- focusWindow actually leaves the window focused...
-prop_focusWindow_works (n :: NonNegative Int) (x :: T) =
- case peek x of
- Nothing -> True
- Just _ -> let s = index x
- i = fromIntegral n `mod` length s
- in (focus . fromJust . stack . workspace . current) (focusWindow (s !! i) x) == (s !! i)
-
--- rotation through the height of a stack gets us back to the start
-prop_focus_all_l (x :: T) = (foldr (const focusUp) x [1..n]) == x
- where n = length (index x)
-prop_focus_all_r (x :: T) = (foldr (const focusDown) x [1..n]) == x
- where n = length (index x)
-
--- prop_rotate_all (x :: T) = f (f x) == f x
--- f x' = foldr (\_ y -> rotate GT y) x' [1..n]
-
--- focus is local to the current workspace
-prop_focus_down_local (x :: T) = hidden_spaces (focusDown x) == hidden_spaces x
-prop_focus_up_local (x :: T) = hidden_spaces (focusUp x) == hidden_spaces x
-
-prop_focus_master_local (x :: T) = hidden_spaces (focusMaster x) == hidden_spaces x
-
-prop_focusWindow_local (n :: NonNegative Int) (x::T ) =
- case peek x of
- Nothing -> True
- Just _ -> let s = index x
- i = fromIntegral n `mod` length s
- in hidden_spaces (focusWindow (s !! i) x) == hidden_spaces x
-
--- On an invalid window, the stackset is unmodified
-prop_focusWindow_identity (n :: Char) (x::T ) =
- not (n `member` x) ==> focusWindow n x == x
-
--- ---------------------------------------------------------------------
--- member/findTag
-
---
--- For all windows in the stackSet, findTag should identify the
--- correct workspace
---
-prop_findIndex (x :: T) =
- and [ tag w == fromJust (findTag i x)
- | w <- workspace (current x) : map workspace (visible x) ++ hidden x
- , t <- maybeToList (stack w)
- , i <- focus t : up t ++ down t
- ]
-
-prop_allWindowsMember w (x :: T) = (w `elem` allWindows x) ==> member w x
-
-prop_currentTag (x :: T) =
- currentTag x == tag (workspace (current x))
-
--- ---------------------------------------------------------------------
--- 'insert'
-
--- inserting a item into an empty stackset means that item is now a member
-prop_insert_empty i (EmptyStackSet x)= member i (insertUp i x)
-
--- insert should be idempotent
-prop_insert_idem i (x :: T) = insertUp i x == insertUp i (insertUp i x)
-
--- insert when an item is a member should leave the stackset unchanged
-prop_insert_duplicate i (x :: T) = member i x ==> insertUp i x == x
-
--- push shouldn't change anything but the current workspace
-prop_insert_local (x :: T) i = not (member i x) ==> hidden_spaces x == hidden_spaces (insertUp i x)
-
--- Inserting a (unique) list of items into an empty stackset should
--- result in the last inserted element having focus.
-prop_insert_peek (EmptyStackSet x) (NonEmptyNubList is) =
- peek (foldr insertUp x is) == Just (head is)
-
--- insert >> delete is the identity, when i `notElem` .
--- Except for the 'master', which is reset on insert and delete.
---
-prop_insert_delete n x = not (member n x) ==> delete n (insertUp n y) == (y :: T)
- where
- y = swapMaster x -- sets the master window to the current focus.
- -- otherwise, we don't have a rule for where master goes.
-
--- inserting n elements increases current stack size by n
-prop_size_insert is (EmptyStackSet x) =
- size (foldr insertUp x ws ) == (length ws)
- where
- ws = nub is
- size = length . index
-
-
--- ---------------------------------------------------------------------
--- 'delete'
-
--- deleting the current item removes it.
-prop_delete x =
- case peek x of
- Nothing -> True
- Just i -> not (member i (delete i x))
- where _ = x :: T
-
--- delete is reversible with 'insert'.
--- It is the identiy, except for the 'master', which is reset on insert and delete.
---
-prop_delete_insert (x :: T) =
- case peek x of
- Nothing -> True
- Just n -> insertUp n (delete n y) == y
- where
- y = swapMaster x
-
--- delete should be local
-prop_delete_local (x :: T) =
- case peek x of
- Nothing -> True
- Just i -> hidden_spaces x == hidden_spaces (delete i x)
-
--- delete should not affect focus unless the focused element is what is being deleted
-prop_delete_focus n (x :: T) = member n x && Just n /= peek x ==> peek (delete n x) == peek x
-
--- focus movement in the presence of delete:
--- when the last window in the stack set is focused, focus moves `up'.
--- usual case is that it moves 'down'.
-prop_delete_focus_end (x :: T) =
- length (index x) > 1
- ==>
- peek (delete n y) == peek (focusUp y)
- where
- n = last (index x)
- y = focusWindow n x -- focus last window in stack
-
--- focus movement in the presence of delete:
--- when not in the last item in the stack, focus moves down
-prop_delete_focus_not_end (x :: T) =
- length (index x) > 1 &&
- n /= last (index x)
- ==>
- peek (delete n x) == peek (focusDown x)
- where
- Just n = peek x
-
--- ---------------------------------------------------------------------
--- filter
-
--- preserve order
-prop_filter_order (x :: T) =
- case stack $ workspace $ current x of
- Nothing -> True
- Just s@(Stack i _ _) -> integrate' (S.filter (/= i) s) == filter (/= i) (integrate' (Just s))
-
--- ---------------------------------------------------------------------
--- swapUp, swapDown, swapMaster: reordiring windows
-
--- swap is trivially reversible
-prop_swap_left (x :: T) = (swapUp (swapDown x)) == x
-prop_swap_right (x :: T) = (swapDown (swapUp x)) == x
--- TODO swap is reversible
--- swap is reversible, but involves moving focus back the window with
--- master on it. easy to do with a mouse...
-{-
-prop_promote_reversible x b = (not . null . fromMaybe [] . flip index x . current $ x) ==>
- (raiseFocus y . promote . raiseFocus z . promote) x == x
- where _ = x :: T
- dir = if b then LT else GT
- (Just y) = peek x
- (Just (z:_)) = flip index x . current $ x
--}
-
--- swap doesn't change focus
-prop_swap_master_focus (x :: T) = peek x == (peek $ swapMaster x)
--- = case peek x of
--- Nothing -> True
--- Just f -> focus (stack (workspace $ current (swap x))) == f
-prop_swap_left_focus (x :: T) = peek x == (peek $ swapUp x)
-prop_swap_right_focus (x :: T) = peek x == (peek $ swapDown x)
-
--- swap is local
-prop_swap_master_local (x :: T) = hidden_spaces x == hidden_spaces (swapMaster x)
-prop_swap_left_local (x :: T) = hidden_spaces x == hidden_spaces (swapUp x)
-prop_swap_right_local (x :: T) = hidden_spaces x == hidden_spaces (swapDown x)
-
--- rotation through the height of a stack gets us back to the start
-prop_swap_all_l (x :: T) = (foldr (const swapUp) x [1..n]) == x
- where n = length (index x)
-prop_swap_all_r (x :: T) = (foldr (const swapDown) x [1..n]) == x
- where n = length (index x)
-
-prop_swap_master_idempotent (x :: T) = swapMaster (swapMaster x) == swapMaster x
-
--- ---------------------------------------------------------------------
--- shift
-
--- shift is fully reversible on current window, when focus and master
--- are the same. otherwise, master may move.
-prop_shift_reversible i (x :: T) =
- i `tagMember` x ==> case peek y of
- Nothing -> True
- Just _ -> normal ((view n . shift n . view i . shift i) y) == normal y
- where
- y = swapMaster x
- n = tag (workspace $ current y)
-
-------------------------------------------------------------------------
--- shiftMaster
-
--- focus/local/idempotent same as swapMaster:
-prop_shift_master_focus (x :: T) = peek x == (peek $ shiftMaster x)
-prop_shift_master_local (x :: T) = hidden_spaces x == hidden_spaces (shiftMaster x)
-prop_shift_master_idempotent (x :: T) = shiftMaster (shiftMaster x) == shiftMaster x
--- ordering is constant modulo the focused window:
-prop_shift_master_ordering (x :: T) = case peek x of
- Nothing -> True
- Just m -> L.delete m (index x) == L.delete m (index $ shiftMaster x)
-
--- ---------------------------------------------------------------------
--- shiftWin
-
--- shiftWin on current window is the same as shift
-prop_shift_win_focus i (x :: T) =
- i `tagMember` x ==> case peek x of
- Nothing -> True
- Just w -> shiftWin i w x == shift i x
-
--- shiftWin on a non-existant window is identity
-prop_shift_win_indentity i w (x :: T) =
- i `tagMember` x && not (w `member` x) ==> shiftWin i w x == x
-
--- shiftWin leaves the current screen as it is, if neither i is the tag
--- of the current workspace nor w on the current workspace
-prop_shift_win_fix_current i w (x :: T) =
- i `tagMember` x && w `member` x && i /= n && findTag w x /= Just n
- ==> (current $ x) == (current $ shiftWin i w x)
- where
- n = tag (workspace $ current x)
-
-------------------------------------------------------------------------
--- properties for the floating layer:
-
-prop_float_reversible n (x :: T) =
- n `member` x ==> sink n (float n geom x) == x
- where
- geom = RationalRect 100 100 100 100
-
-prop_float_geometry n (x :: T) =
- n `member` x ==> let s = float n geom x
- in M.lookup n (floating s) == Just geom
- where
- geom = RationalRect 100 100 100 100
-
-prop_float_delete n (x :: T) =
- n `member` x ==> let s = float n geom x
- t = delete n s
- in not (n `member` t)
- where
- geom = RationalRect 100 100 100 100
-
-
-------------------------------------------------------------------------
-
-prop_screens (x :: T) = n `elem` screens x
- where
- n = current x
-
-prop_differentiate xs =
- if null xs then differentiate xs == Nothing
- else (differentiate xs) == Just (Stack (head xs) [] (tail xs))
- where _ = xs :: [Int]
-
--- looking up the tag of the current workspace should always produce a tag.
-prop_lookup_current (x :: T) = lookupWorkspace scr x == Just tg
- where
- (Screen (Workspace tg _ _) scr _) = current x
-
--- looking at a visible tag
-prop_lookup_visible (x :: T) =
- visible x /= [] ==>
- fromJust (lookupWorkspace scr x) `elem` tags
- where
- tags = [ tag (workspace y) | y <- visible x ]
- scr = last [ screen y | y <- visible x ]
--- ---------------------------------------------------------------------
--- testing for failure
-
--- and help out hpc
-prop_abort x = unsafePerformIO $ C.catch (abort "fail")
- (\(C.SomeException e) -> return $ show e == "xmonad: StackSet: fail" )
- where
- _ = x :: Int
-
--- new should fail with an abort
-prop_new_abort x = unsafePerformIO $ C.catch f
- (\(C.SomeException e) -> return $ show e == "xmonad: StackSet: non-positive argument to StackSet.new" )
- where
- f = new undefined{-layout-} [] [] `seq` return False
-
- _ = x :: Int
-
--- prop_view_should_fail = view {- with some bogus data -}
-
--- screens makes sense
-prop_screens_works (x :: T) = screens x == current x : visible x
-
-------------------------------------------------------------------------
--- renaming tags
-
--- | Rename a given tag if present in the StackSet.
--- 408 renameTag :: Eq i => i -> i -> StackSet i l a s sd -> StackSet i l a s sd
-
-prop_rename1 (x::T) o n = o `tagMember` x && not (n `tagMember` x) ==>
- let y = renameTag o n x
- in n `tagMember` y
-
--- |
--- Ensure that a given set of workspace tags is present by renaming
--- existing workspaces and\/or creating new hidden workspaces as
--- necessary.
---
-prop_ensure (x :: T) l xs = let y = ensureTags l xs x
- in and [ n `tagMember` y | n <- xs ]
-
--- adding a tag should create a new hidden workspace
-prop_ensure_append (x :: T) l n =
- not (n `tagMember` x)
- ==>
- (hidden y /= hidden x -- doesn't append, renames
- &&
- and [ isNothing (stack z) && layout z == l | z <- hidden y, tag z == n ]
- )
- where
- y = ensureTags l (n:ts) x
- ts = [ tag z | z <- workspaces x ]
-
-prop_mapWorkspaceId (x::T) = x == mapWorkspace id x
-
-prop_mapWorkspaceInverse (x::T) = x == mapWorkspace predTag (mapWorkspace succTag x)
- where predTag w = w { tag = pred $ tag w }
- succTag w = w { tag = succ $ tag w }
-
-prop_mapLayoutId (x::T) = x == mapLayout id x
-
-prop_mapLayoutInverse (x::T) = x == mapLayout pred (mapLayout succ x)
-
-------------------------------------------------------------------------
--- The Tall layout
-
--- 1 window should always be tiled fullscreen
-prop_tile_fullscreen rect = tile pct rect 1 1 == [rect]
- where pct = 1/2
-
--- multiple windows
-prop_tile_non_overlap rect windows nmaster = noOverlaps (tile pct rect nmaster windows)
- where _ = rect :: Rectangle
- pct = 3 % 100
-
--- splitting horizontally yields sensible results
-prop_split_hoziontal (NonNegative n) x =
-{-
- trace (show (rect_x x
- ,rect_width x
- ,rect_x x + fromIntegral (rect_width x)
- ,map rect_x xs))
- $
--}
-
- sum (map rect_width xs) == rect_width x
- &&
- all (== rect_height x) (map rect_height xs)
- &&
- (map rect_x xs) == (sort $ map rect_x xs)
-
- where
- xs = splitHorizontally n x
-
--- splitting horizontally yields sensible results
-prop_splitVertically (r :: Rational) x =
-
- rect_x x == rect_x a && rect_x x == rect_x b
- &&
- rect_width x == rect_width a && rect_width x == rect_width b
-
-{-
- trace (show (rect_x x
- ,rect_width x
- ,rect_x x + fromIntegral (rect_width x)
- ,map rect_x xs))
- $
--}
-
- where
- (a,b) = splitVerticallyBy r x
-
-
--- pureLayout works.
-prop_purelayout_tall n r1 r2 rect (t :: T) =
- isJust (peek t) ==>
- length ts == length (index t)
- &&
- noOverlaps (map snd ts)
- &&
- description layoot == "Tall"
- where layoot = Tall n r1 r2
- st = fromJust . stack . workspace . current $ t
- ts = pureLayout layoot rect st
-
--- Test message handling of Tall
-
--- what happens when we send a Shrink message to Tall
-prop_shrink_tall (NonNegative n) (NonZero (NonNegative delta)) (NonNegative frac) =
- n == n' && delta == delta' -- these state components are unchanged
- && frac' <= frac && (if frac' < frac then frac' == 0 || frac' == frac - delta
- else frac == 0 )
- -- remaining fraction should shrink
- where
- l1 = Tall n delta frac
- Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Shrink)
- -- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-
-
--- what happens when we send a Shrink message to Tall
-prop_expand_tall (NonNegative n)
- (NonZero (NonNegative delta))
- (NonNegative n1)
- (NonZero (NonNegative d1)) =
-
- n == n'
- && delta == delta' -- these state components are unchanged
- && frac' >= frac
- && (if frac' > frac
- then frac' == 1 || frac' == frac + delta
- else frac == 1 )
-
- -- remaining fraction should shrink
- where
- frac = min 1 (n1 % d1)
- l1 = Tall n delta frac
- Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage Expand)
- -- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-
--- what happens when we send an IncMaster message to Tall
-prop_incmaster_tall (NonNegative n) (NonZero (NonNegative delta)) (NonNegative frac)
- (NonNegative k) =
- delta == delta' && frac == frac' && n' == n + k
- where
- l1 = Tall n delta frac
- Just l2@(Tall n' delta' frac') = l1 `pureMessage` (SomeMessage (IncMasterN k))
- -- pureMessage :: layout a -> SomeMessage -> Maybe (layout a)
-
-
-
- -- toMessage LT = SomeMessage Shrink
- -- toMessage EQ = SomeMessage Expand
- -- toMessage GT = SomeMessage (IncMasterN 1)
-
-
-------------------------------------------------------------------------
--- Full layout
-
--- pureLayout works for Full
-prop_purelayout_full rect (t :: T) =
- isJust (peek t) ==>
- length ts == 1 -- only one window to view
- &&
- snd (head ts) == rect -- and sets fullscreen
- &&
- fst (head ts) == fromJust (peek t) -- and the focused window is shown
-
- where layoot = Full
- st = fromJust . stack . workspace . current $ t
- ts = pureLayout layoot rect st
-
--- what happens when we send an IncMaster message to Full --- Nothing
-prop_sendmsg_full (NonNegative k) =
- isNothing (Full `pureMessage` (SomeMessage (IncMasterN k)))
-
-prop_desc_full = description Full == show Full
-
-------------------------------------------------------------------------
-
-prop_desc_mirror n r1 r2 = description (Mirror $! t) == "Mirror Tall"
- where t = Tall n r1 r2
-
-------------------------------------------------------------------------
-
-noOverlaps [] = True
-noOverlaps [_] = True
-noOverlaps xs = and [ verts a `notOverlap` verts b
- | a <- xs
- , b <- filter (a /=) xs
- ]
- where
- verts (Rectangle a b w h) = (a,b,a + fromIntegral w - 1, b + fromIntegral h - 1)
-
- notOverlap (left1,bottom1,right1,top1)
- (left2,bottom2,right2,top2)
- = (top1 < bottom2 || top2 < bottom1)
- || (right1 < left2 || right2 < left1)
-
-------------------------------------------------------------------------
--- Aspect ratios
-
-prop_resize_inc (NonZero (NonNegative inc_w),NonZero (NonNegative inc_h)) b@(w,h) =
- w' `mod` inc_w == 0 && h' `mod` inc_h == 0
- where (w',h') = applyResizeIncHint a b
- a = (inc_w,inc_h)
-
-prop_resize_inc_extra ((NonNegative inc_w)) b@(w,h) =
- (w,h) == (w',h')
- where (w',h') = applyResizeIncHint a b
- a = (-inc_w,0::Dimension)-- inc_h)
-
-prop_resize_max (NonZero (NonNegative inc_w),NonZero (NonNegative inc_h)) b@(w,h) =
- w' <= inc_w && h' <= inc_h
- where (w',h') = applyMaxSizeHint a b
- a = (inc_w,inc_h)
-
-prop_resize_max_extra ((NonNegative inc_w)) b@(w,h) =
- (w,h) == (w',h')
- where (w',h') = applyMaxSizeHint a b
- a = (-inc_w,0::Dimension)-- inc_h)
-
-------------------------------------------------------------------------
-
main :: IO ()
main = do
- args <- fmap (drop 1) getArgs
- let n = if null args then 100 else read (head args)
- (results, passed) <- liftM unzip $ mapM (\(s,a) -> printf "%-40s: " s >> a n) tests
- printf "Passed %d tests!\n" (sum passed)
- when (not . and $ results) $ fail "Not all tests passed!"
- where
-
- tests =
- [("StackSet invariants" , mytest prop_invariant)
-
- ,("empty: invariant" , mytest prop_empty_I)
- ,("empty is empty" , mytest prop_empty)
- ,("empty / current" , mytest prop_empty_current)
- ,("empty / member" , mytest prop_member_empty)
-
- ,("view : invariant" , mytest prop_view_I)
- ,("view sets current" , mytest prop_view_current)
- ,("view idempotent" , mytest prop_view_idem)
- ,("view reversible" , mytest prop_view_reversible)
--- ,("view / xinerama" , mytest prop_view_xinerama)
- ,("view is local" , mytest prop_view_local)
-
- ,("greedyView : invariant" , mytest prop_greedyView_I)
- ,("greedyView sets current" , mytest prop_greedyView_current)
- ,("greedyView is safe " , mytest prop_greedyView_current_id)
- ,("greedyView idempotent" , mytest prop_greedyView_idem)
- ,("greedyView reversible" , mytest prop_greedyView_reversible)
- ,("greedyView is local" , mytest prop_greedyView_local)
---
--- ,("valid workspace xinerama", mytest prop_lookupWorkspace)
-
- ,("peek/member " , mytest prop_member_peek)
-
- ,("index/length" , mytest prop_index_length)
-
- ,("focus left : invariant", mytest prop_focusUp_I)
- ,("focus master : invariant", mytest prop_focusMaster_I)
- ,("focus right: invariant", mytest prop_focusDown_I)
- ,("focusWindow: invariant", mytest prop_focus_I)
- ,("focus left/master" , mytest prop_focus_left_master)
- ,("focus right/master" , mytest prop_focus_right_master)
- ,("focus master/master" , mytest prop_focus_master_master)
- ,("focusWindow master" , mytest prop_focusWindow_master)
- ,("focus left/right" , mytest prop_focus_left)
- ,("focus right/left" , mytest prop_focus_right)
- ,("focus all left " , mytest prop_focus_all_l)
- ,("focus all right " , mytest prop_focus_all_r)
- ,("focus down is local" , mytest prop_focus_down_local)
- ,("focus up is local" , mytest prop_focus_up_local)
- ,("focus master is local" , mytest prop_focus_master_local)
- ,("focus master idemp" , mytest prop_focusMaster_idem)
-
- ,("focusWindow is local", mytest prop_focusWindow_local)
- ,("focusWindow works" , mytest prop_focusWindow_works)
- ,("focusWindow identity", mytest prop_focusWindow_identity)
-
- ,("findTag" , mytest prop_findIndex)
- ,("allWindows/member" , mytest prop_allWindowsMember)
- ,("currentTag" , mytest prop_currentTag)
-
- ,("insert: invariant" , mytest prop_insertUp_I)
- ,("insert/new" , mytest prop_insert_empty)
- ,("insert is idempotent", mytest prop_insert_idem)
- ,("insert is reversible", mytest prop_insert_delete)
- ,("insert is local" , mytest prop_insert_local)
- ,("insert duplicates" , mytest prop_insert_duplicate)
- ,("insert/peek " , mytest prop_insert_peek)
- ,("insert/size" , mytest prop_size_insert)
-
- ,("delete: invariant" , mytest prop_delete_I)
- ,("delete/empty" , mytest prop_empty)
- ,("delete/member" , mytest prop_delete)
- ,("delete is reversible", mytest prop_delete_insert)
- ,("delete is local" , mytest prop_delete_local)
- ,("delete/focus" , mytest prop_delete_focus)
- ,("delete last/focus up", mytest prop_delete_focus_end)
- ,("delete ~last/focus down", mytest prop_delete_focus_not_end)
-
- ,("filter preserves order", mytest prop_filter_order)
-
- ,("swapMaster: invariant", mytest prop_swap_master_I)
- ,("swapUp: invariant" , mytest prop_swap_left_I)
- ,("swapDown: invariant", mytest prop_swap_right_I)
- ,("swapMaster id on focus", mytest prop_swap_master_focus)
- ,("swapUp id on focus", mytest prop_swap_left_focus)
- ,("swapDown id on focus", mytest prop_swap_right_focus)
- ,("swapMaster is idempotent", mytest prop_swap_master_idempotent)
- ,("swap all left " , mytest prop_swap_all_l)
- ,("swap all right " , mytest prop_swap_all_r)
- ,("swapMaster is local" , mytest prop_swap_master_local)
- ,("swapUp is local" , mytest prop_swap_left_local)
- ,("swapDown is local" , mytest prop_swap_right_local)
-
- ,("shiftMaster id on focus", mytest prop_shift_master_focus)
- ,("shiftMaster is local", mytest prop_shift_master_local)
- ,("shiftMaster is idempotent", mytest prop_shift_master_idempotent)
- ,("shiftMaster preserves ordering", mytest prop_shift_master_ordering)
-
- ,("shift: invariant" , mytest prop_shift_I)
- ,("shift is reversible" , mytest prop_shift_reversible)
- ,("shiftWin: invariant" , mytest prop_shift_win_I)
- ,("shiftWin is shift on focus" , mytest prop_shift_win_focus)
- ,("shiftWin fix current" , mytest prop_shift_win_fix_current)
-
- ,("floating is reversible" , mytest prop_float_reversible)
- ,("floating sets geometry" , mytest prop_float_geometry)
- ,("floats can be deleted", mytest prop_float_delete)
- ,("screens includes current", mytest prop_screens)
-
- ,("differentiate works", mytest prop_differentiate)
- ,("lookupTagOnScreen", mytest prop_lookup_current)
- ,("lookupTagOnVisbleScreen", mytest prop_lookup_visible)
- ,("screens works", mytest prop_screens_works)
- ,("renaming works", mytest prop_rename1)
- ,("ensure works", mytest prop_ensure)
- ,("ensure hidden semantics", mytest prop_ensure_append)
-
- ,("mapWorkspace id", mytest prop_mapWorkspaceId)
- ,("mapWorkspace inverse", mytest prop_mapWorkspaceInverse)
- ,("mapLayout id", mytest prop_mapLayoutId)
- ,("mapLayout inverse", mytest prop_mapLayoutInverse)
-
- -- testing for failure:
- ,("abort fails", mytest prop_abort)
- ,("new fails with abort", mytest prop_new_abort)
- ,("shiftWin identity", mytest prop_shift_win_indentity)
-
- -- tall layout
-
- ,("tile 1 window fullsize", mytest prop_tile_fullscreen)
- ,("tiles never overlap", mytest prop_tile_non_overlap)
- ,("split hozizontally", mytest prop_split_hoziontal)
- ,("split verticalBy", mytest prop_splitVertically)
-
- ,("pure layout tall", mytest prop_purelayout_tall)
- ,("send shrink tall", mytest prop_shrink_tall)
- ,("send expand tall", mytest prop_expand_tall)
- ,("send incmaster tall", mytest prop_incmaster_tall)
-
- -- full layout
-
- ,("pure layout full", mytest prop_purelayout_full)
- ,("send message full", mytest prop_sendmsg_full)
- ,("describe full", mytest prop_desc_full)
-
- ,("describe mirror", mytest prop_desc_mirror)
-
- -- resize hints
- ,("window hints: inc", mytest prop_resize_inc)
- ,("window hints: inc all", mytest prop_resize_inc_extra)
- ,("window hints: max", mytest prop_resize_max)
- ,("window hints: max all ", mytest prop_resize_max_extra)
-
- ]
-
-------------------------------------------------------------------------
---
--- QC driver
---
-
-debug = False
-
-mytest :: Testable a => a -> Int -> IO (Bool, Int)
-mytest a n = mycheck defaultConfig
- { configMaxTest=n
- , configEvery = \n args -> let s = show n in s ++ [ '\b' | _ <- s ] } a
- -- , configEvery= \n args -> if debug then show n ++ ":\n" ++ unlines args else [] } a
-
-mycheck :: Testable a => Config -> a -> IO (Bool, Int)
-mycheck config a = do
- rnd <- newStdGen
- mytests config (evaluate a) rnd 0 0 []
-
-mytests :: Config -> Gen Result -> StdGen -> Int -> Int -> [[String]] -> IO (Bool, Int)
-mytests config gen rnd0 ntest nfail stamps
- | ntest == configMaxTest config = done "OK," ntest stamps >> return (True, ntest)
- | nfail == configMaxFail config = done "Arguments exhausted after" ntest stamps >> return (True, ntest)
- | otherwise =
- do putStr (configEvery config ntest (arguments result)) >> hFlush stdout
- case ok result of
- Nothing ->
- mytests config gen rnd1 ntest (nfail+1) stamps
- Just True ->
- mytests config gen rnd1 (ntest+1) nfail (stamp result:stamps)
- Just False ->
- putStr ( "Falsifiable after "
- ++ show ntest
- ++ " tests:\n"
- ++ unlines (arguments result)
- ) >> hFlush stdout >> return (False, ntest)
- where
- result = generate (configSize config ntest) rnd2 gen
- (rnd1,rnd2) = split rnd0
-
-done :: String -> Int -> [[String]] -> IO ()
-done mesg ntest stamps = putStr ( mesg ++ " " ++ show ntest ++ " tests" ++ table )
- where
- table = display
- . map entry
- . reverse
- . sort
- . map pairLength
- . group
- . sort
- . filter (not . null)
- $ stamps
-
- display [] = ".\n"
- display [x] = " (" ++ x ++ ").\n"
- display xs = ".\n" ++ unlines (map (++ ".") xs)
-
- pairLength xss@(xs:_) = (length xss, xs)
- entry (n, xs) = percentage n ntest
- ++ " "
- ++ concat (intersperse ", " xs)
-
- percentage n m = show ((100 * n) `div` m) ++ "%"
-
-------------------------------------------------------------------------
-
-instance Arbitrary Char where
- arbitrary = choose ('a','z')
- coarbitrary n = coarbitrary (ord n)
-
-instance Random Word8 where
- randomR = integralRandomR
- random = randomR (minBound,maxBound)
-
-instance Arbitrary Word8 where
- arbitrary = choose (minBound,maxBound)
- coarbitrary n = variant (fromIntegral ((fromIntegral n) `rem` 4))
-
-instance Random Word64 where
- randomR = integralRandomR
- random = randomR (minBound,maxBound)
-
-instance Arbitrary Word64 where
- arbitrary = choose (minBound,maxBound)
- coarbitrary n = variant (fromIntegral ((fromIntegral n) `rem` 4))
-
-integralRandomR :: (Integral a, RandomGen g) => (a,a) -> g -> (a,g)
-integralRandomR (a,b) g = case randomR (fromIntegral a :: Integer,
- fromIntegral b :: Integer) g of
- (x,g) -> (fromIntegral x, g)
-
-instance Arbitrary Position where
- arbitrary = do n <- arbitrary :: Gen Word8
- return (fromIntegral n)
- coarbitrary = undefined
-
-instance Arbitrary Dimension where
- arbitrary = do n <- arbitrary :: Gen Word8
- return (fromIntegral n)
- coarbitrary = undefined
-
-instance Arbitrary Rectangle where
- arbitrary = do
- sx <- arbitrary
- sy <- arbitrary
- sw <- arbitrary
- sh <- arbitrary
- return $ Rectangle sx sy sw sh
- coarbitrary = undefined
-
-instance Arbitrary Rational where
- arbitrary = do
- n <- arbitrary
- d' <- arbitrary
- let d = if d' == 0 then 1 else d'
- return (n % d)
- coarbitrary = undefined
-
-------------------------------------------------------------------------
--- QC 2
-
--- from QC2
--- | NonEmpty xs: guarantees that xs is non-empty.
-newtype NonEmptyList a = NonEmpty [a]
- deriving ( Eq, Ord, Show, Read )
-
-instance Arbitrary a => Arbitrary (NonEmptyList a) where
- arbitrary = NonEmpty `fmap` (arbitrary `suchThat` (not . null))
- coarbitrary = undefined
-
-newtype NonEmptyNubList a = NonEmptyNubList [a]
- deriving ( Eq, Ord, Show, Read )
-
-instance (Eq a, Arbitrary a) => Arbitrary (NonEmptyNubList a) where
- arbitrary = NonEmptyNubList `fmap` ((liftM nub arbitrary) `suchThat` (not . null))
- coarbitrary = undefined
-
-type Positive a = NonZero (NonNegative a)
-
-newtype NonZero a = NonZero a
- deriving ( Eq, Ord, Num, Integral, Real, Enum, Show, Read )
-
-instance (Num a, Ord a, Arbitrary a) => Arbitrary (NonZero a) where
- arbitrary = fmap NonZero $ arbitrary `suchThat` (/= 0)
- coarbitrary = undefined
-
-newtype NonNegative a = NonNegative a
- deriving ( Eq, Ord, Num, Integral, Real, Enum, Show, Read )
-
-instance (Num a, Ord a, Arbitrary a) => Arbitrary (NonNegative a) where
- arbitrary =
- frequency
- [ (5, (NonNegative . abs) `fmap` arbitrary)
- , (1, return 0)
- ]
- coarbitrary = undefined
-
-newtype EmptyStackSet = EmptyStackSet T deriving Show
-
-instance Arbitrary EmptyStackSet where
- arbitrary = do
- (NonEmptyNubList ns) <- arbitrary
- (NonEmptyNubList sds) <- arbitrary
- l <- arbitrary
- -- there cannot be more screens than workspaces:
- return . EmptyStackSet . new l ns $ take (min (length ns) (length sds)) sds
- coarbitrary = error "coarbitrary EmptyStackSet"
+ arg <- fmap (drop 1) getArgs
+ let n = if null arg then 100 else read $ head arg
+ args = stdArgs { maxSuccess = n, maxSize = 100 }
+ qc = quickCheckWithResult args
+ perform (s, t) = printf "%-35s: " s >> qc t
+ mapM_ perform tests
+
+
+tests =
+ [("StackSet invariants", property prop_invariant)
+ ,("empty: invariant", property prop_empty_I)
+ ,("empty is empty", property prop_empty)
+ ,("empty / current", property prop_empty_current)
+ ,("empty / member", property prop_member_empty)
+
+
+ ,("view : invariant", property prop_view_I)
+ ,("view sets current", property prop_view_current)
+ ,("view idempotent", property prop_view_idem)
+ ,("view reversible", property prop_view_reversible)
+
+ ,("view is local", property prop_view_local)
+
+ ,("greedyView : invariant", property prop_greedyView_I)
+ ,("greedyView sets current", property prop_greedyView_current)
+ ,("greedyView is safe", property prop_greedyView_current_id)
+ ,("greedyView idempotent", property prop_greedyView_idem)
+ ,("greedyView reversible", property prop_greedyView_reversible)
+ ,("greedyView is local", property prop_greedyView_local)
+
+ ,("peek/member", property prop_member_peek)
+
+ ,("index/length", property prop_index_length)
+
+ ,("focus left : invariant", property prop_focusUp_I)
+ ,("focus master : invariant", property prop_focusMaster_I)
+ ,("focus right: invariant", property prop_focusDown_I)
+ ,("focusWindow: invariant", property prop_focus_I)
+ ,("focus left/master", property prop_focus_left_master)
+ ,("focus right/master", property prop_focus_right_master)
+ ,("focus master/master", property prop_focus_master_master)
+ ,("focusWindow master", property prop_focusWindow_master)
+ ,("focus left/right", property prop_focus_left)
+ ,("focus right/left", property prop_focus_right)
+ ,("focus all left", property prop_focus_all_l)
+ ,("focus all right", property prop_focus_all_r)
+ ,("focus down is local", property prop_focus_down_local)
+ ,("focus up is local", property prop_focus_up_local)
+ ,("focus master is local", property prop_focus_master_local)
+ ,("focus master idemp", property prop_focusMaster_idem)
+
+ ,("focusWindow is local", property prop_focusWindow_local)
+ ,("focusWindow works" , property prop_focusWindow_works)
+ ,("focusWindow identity", property prop_focusWindow_identity)
+
+ ,("findTag", property prop_findIndex)
+ ,("allWindows/member", property prop_allWindowsMember)
+ ,("currentTag", property prop_currentTag)
+
+ ,("insert: invariant", property prop_insertUp_I)
+ ,("insert/new", property prop_insert_empty)
+ ,("insert is idempotent", property prop_insert_idem)
+ ,("insert is reversible", property prop_insert_delete)
+ ,("insert is local", property prop_insert_local)
+ ,("insert duplicates", property prop_insert_duplicate)
+ ,("insert/peek", property prop_insert_peek)
+ ,("insert/size", property prop_size_insert)
+
+ ,("delete: invariant", property prop_delete_I)
+ ,("delete/empty", property prop_empty)
+ ,("delete/member", property prop_delete)
+ ,("delete is reversible", property prop_delete_insert)
+ ,("delete is local", property prop_delete_local)
+ ,("delete/focus", property prop_delete_focus)
+ ,("delete last/focus up", property prop_delete_focus_end)
+ ,("delete ~last/focus down", property prop_delete_focus_not_end)
+
+ ,("filter preserves order", property prop_filter_order)
+
+ ,("swapMaster: invariant", property prop_swap_master_I)
+ ,("swapUp: invariant" , property prop_swap_left_I)
+ ,("swapDown: invariant", property prop_swap_right_I)
+ ,("swapMaster id on focus", property prop_swap_master_focus)
+ ,("swapUp id on focus", property prop_swap_left_focus)
+ ,("swapDown id on focus", property prop_swap_right_focus)
+ ,("swapMaster is idempotent", property prop_swap_master_idempotent)
+ ,("swap all left", property prop_swap_all_l)
+ ,("swap all right", property prop_swap_all_r)
+ ,("swapMaster is local", property prop_swap_master_local)
+ ,("swapUp is local", property prop_swap_left_local)
+ ,("swapDown is local", property prop_swap_right_local)
+
+ ,("shiftMaster id on focus", property prop_shift_master_focus)
+ ,("shiftMaster is local", property prop_shift_master_local)
+ ,("shiftMaster is idempotent", property prop_shift_master_idempotent)
+ ,("shiftMaster preserves ordering", property prop_shift_master_ordering)
+
+ ,("shift: invariant" , property prop_shift_I)
+ ,("shift is reversible" , property prop_shift_reversible)
+ ,("shiftWin: invariant" , property prop_shift_win_I)
+ ,("shiftWin is shift on focus", property prop_shift_win_focus)
+ ,("shiftWin fix current" , property prop_shift_win_fix_current)
+
+ ,("floating is reversible" , property prop_float_reversible)
+ ,("floating sets geometry" , property prop_float_geometry)
+ ,("floats can be deleted", property prop_float_delete)
+ ,("screens includes current", property prop_screens)
+
+ ,("differentiate works", property prop_differentiate)
+ ,("lookupTagOnScreen", property prop_lookup_current)
+ ,("lookupTagOnVisbleScreen", property prop_lookup_visible)
+ ,("screens works", property prop_screens_works)
+ ,("renaming works", property prop_rename1)
+ ,("ensure works", property prop_ensure)
+ ,("ensure hidden semantics", property prop_ensure_append)
+
+ ,("mapWorkspace id", property prop_mapWorkspaceId)
+ ,("mapWorkspace inverse", property prop_mapWorkspaceInverse)
+ ]
--- | Generates a value that satisfies a predicate.
-suchThat :: Gen a -> (a -> Bool) -> Gen a
-gen `suchThat` p =
- do mx <- gen `suchThatMaybe` p
- case mx of
- Just x -> return x
- Nothing -> sized (\n -> resize (n+1) (gen `suchThat` p))
--- | Tries to generate a value that satisfies a predicate.
-suchThatMaybe :: Gen a -> (a -> Bool) -> Gen (Maybe a)
-gen `suchThatMaybe` p = sized (try 0 . max 1)
- where
- try _ 0 = return Nothing
- try k n = do x <- resize (2*k+n) gen
- if p x then return (Just x) else try (k+1) (n-1)