darcs-2.18.5: harness/Darcs/Test/Patch/Arbitrary/PrimFileUUID.hs
{-# OPTIONS_GHC -Wno-orphans #-}
{-# LANGUAGE OverloadedStrings #-}
module Darcs.Test.Patch.Arbitrary.PrimFileUUID where
import Prelude ()
import Darcs.Prelude
import Darcs.Test.Patch.Arbitrary.Generic
import Darcs.Test.Patch.Arbitrary.Shrink
import Darcs.Test.Patch.RepoModel
import Test.QuickCheck
import Darcs.Test.Patch.WithState
import Darcs.Test.Patch.Types.Pair ( Pair(..) )
import Darcs.Patch.Witnesses.Sealed
import Darcs.Patch.Witnesses.Eq
import Darcs.Patch.Witnesses.Unsafe
import Darcs.Patch.Witnesses.Ordered
import Darcs.Patch.Prim.FileUUID ()
import Darcs.Patch.Prim.FileUUID.Core ( Prim(..), Location(..), Hunk(..), UUID(..) )
import Darcs.Test.Patch.FileUUIDModel
import Darcs.Test.Util.QuickCheck ( notIn, maybeOf )
import qualified Data.ByteString as B
import Data.Maybe ( fromJust, isJust )
import qualified Data.Map as M
type instance ModelOf Prim = FileUUIDModel
instance ArbitraryPrim Prim where
runCoalesceTests = Nothing
hasPrimConstruct = Nothing
usesV1Model = Nothing
-- TODO add some useful shrinking, at least to
-- shrinkAtEnd/shrinkAtStart
instance Shrinkable Prim where
shrinkInternally _ = []
shrinkAtEnd _ = []
shrinkAtStart _ = []
instance MightBeEmptyHunk Prim
instance MightHaveDuplicate Prim
instance NullPatch Prim where
nullPatch Identity = IsEq
nullPatch (Hunk _ (H _ old new))
| old == new = unsafeCoerceP IsEq
nullPatch _ = NotEq
instance PropagateShrink Prim Prim where
propagateShrink = propagatePrim
instance ShrinkModel Prim where
-- no shrinking for now
shrinkModelPatch _ = []
----------------------------------------------------------------------
-- * QuickCheck generators
aHunk :: B.ByteString -> Gen (Hunk wX wY)
aHunk content = do
pos <- choose (0, B.length content)
oldLen <- choose (0, B.length content - pos)
new <- scale (`div` 8) aContent
let old = B.take oldLen $ B.drop pos $ content
return $ H pos old new
aTextHunk :: (UUID, Object Fail) -> Gen (Prim wX wY)
aTextHunk (uuid, (Blob text _)) =
do h <- aHunk (unFail text)
return $ Hunk uuid h
aTextHunk _ = error "impossible case"
aManifest :: UUID -> (UUID, Object Fail) -> Gen (Prim wX wY)
aManifest uuid (dirId, Directory dir) =
do filename <- aFilename `notIn` (M.keys dir)
return $ Manifest uuid (L dirId filename)
aManifest _ _ = error "impossible case"
aDemanifest :: UUID -> Location -> Gen (Prim wX wY)
aDemanifest uuid loc = return $ Demanifest uuid loc
-- | Generates any type of 'Prim' patch, except binary and setpref patches.
aPrim :: FileUUIDModel wX -> Gen (Sealed (WithEndState FileUUIDModel (Prim wX)))
aPrim repo
= do mbFile <- maybeOf repoFiles -- some file, not necessarily manifested
dir <- elements repoDirs -- some directory, not necessarily manifested
-- note, the root directory always exists and is never manifested nor demanifested
mbDemanifested <- maybeOf notManifested -- something not manifested
mbManifested <- maybeOf manifested -- something manifested
fresh <- anUUID `notIn` repoIds repo -- a fresh uuid
let whenjust m x = if isJust m then x else 0
whenfile = whenjust mbFile
whendemanifested = whenjust mbDemanifested
whenmanifested = whenjust mbManifested
patch <- frequency
[ ( whenfile 12, aTextHunk $ fromJust mbFile ) -- edit an existing file
, ( 2, aTextHunk (fresh, Blob (return "") Nothing) ) -- edit a new file
, ( whendemanifested 2 -- manifest an existing object
, aManifest (fromJust mbDemanifested) dir
)
, ( whenmanifested 2
, uncurry aDemanifest $ fromJust mbManifested
)
]
let repo' = unFail $ repoApply repo patch
return $ seal $ WithEndState patch repo'
where
manifested = [ (uuid, (L dirid name)) | (dirid, Directory dir) <- repoDirs
, (name, uuid) <- M.toList dir ]
notManifested = [ uuid | (uuid, _) <- nonRootObjects
, not (uuid `elem` map fst manifested) ]
repoFiles = [ (uuid, Blob x y) | (uuid, Blob x y) <- repoObjects repo ]
repoDirs = [ (uuid, Directory x) | (uuid, Directory x) <- repoObjects repo ]
nonRootObjects = filter notRoot $ repoObjects repo where
notRoot (uuid, _) = uuid == rootId
----------------------------------------------------------------------
-- *** Pairs of primitive patches
-- Try to generate commutable pairs of hunks
hunkPair :: (UUID, Object Fail) -> Gen ((Prim :> Prim) wX wY)
hunkPair (uuid, (Blob file _)) =
do h1@(H off1 old1 new1) <- aHunk (unFail file)
(delta, content') <- selectChunk h1 (unFail file)
H off2' old2 new2 <- aHunk content'
let off2 = off2' + delta
return (Hunk uuid (H off1 old1 new1) :> Hunk uuid (H off2 old2 new2))
where
selectChunk (H off old new) content = elements [prefix, suffix]
where prefix = (0, B.take off content)
suffix = (off + B.length new, B.drop (off + B.length old) content)
hunkPair _ = error "impossible case"
aPrimPair :: FileUUIDModel wX
-> Gen (Sealed (WithEndState FileUUIDModel (Pair Prim wX)))
aPrimPair repo
= do mbFile <- maybeOf repoFiles
frequency
[ ( if isJust mbFile then 1 else 0
, do p1 :> p2 <- hunkPair $ fromJust mbFile
let repo' = unFail $ repoApply repo p1
repo'' = unFail $ repoApply repo' p2
return $ seal $ WithEndState (Pair (p1 :> p2)) repo''
)
, ( 1
, do
-- construct the underlying pair directly to avoid any
-- risk of indirectly calling arbitraryStatePair (which
-- would cause a loop).
Sealed (WithEndState pair repo') <- arbitraryState repo
return $ seal $ WithEndState (Pair pair) repo'
)
]
where
repoFiles = [ (uuid, Blob x y) | (uuid, Blob x y) <- repoObjects repo ]
----------------------------------------------------------------------
-- Arbitrary instances
instance ArbitraryState Prim where
arbitraryState = aPrim
arbitraryStatePair = aPrimPair
instance ArbitraryWS Prim where
arbitraryWS = makeWS2Gen aSmallRepo