darcs-2.16.1: harness/Darcs/Test/Patch/Arbitrary/PrimFileUUID.hs
{-# 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.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
import Darcs.Util.Hash( Hash(..) )
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 (WithEndState FileUUIDModel (Prim wX) wY)
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 "") NoHash) ) -- 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 $ 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 (WithEndState FileUUIDModel ((Prim :> Prim) wX) wY)
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 $ WithEndState (p1 :> p2) repo''
)
, ( 1
, do Sealed wesP <- arbitraryState repo
return $ unsafeCoerceP1 wesP
)
]
where
repoFiles = [ (uuid, Blob x y) | (uuid, Blob x y) <- repoObjects repo ]
----------------------------------------------------------------------
-- Arbitrary instances
instance ArbitraryState Prim where
arbitraryState s = seal <$> aPrim s
-- use the special generator for pairs
arbitraryPair :: Gen (Sealed2 (WithState (Prim :> Prim)))
arbitraryPair = do
repo <- aSmallRepo
WithEndState pp repo' <- aPrimPair repo
return $ seal2 $ WithState repo pp repo'
instance Arbitrary (Sealed2 Prim) where
arbitrary = makeS2Gen aSmallRepo
instance Arbitrary (Sealed2 (Prim :> Prim)) where
arbitrary = mapSeal2 wsPatch <$> arbitraryPair
instance Arbitrary (Sealed2 (WithState Prim)) where
arbitrary = makeWS2Gen aSmallRepo
instance Arbitrary (Sealed2 (WithState (Prim :> Prim))) where
arbitrary = arbitraryPair