hdiff-0.0.0: tests/Data/Digems/Patch/MergeSpec.hs
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
module Data.Digems.Patch.MergeSpec (spec) where
import Generics.MRSOP.Base
import Data.Digems.Patch
import Data.Digems.Diff
import Data.Digems.Patch.Merge
import Data.Digems.Change
import Data.Digems.Change.Thinning
import Data.Digems.Change.Apply
import Languages.RTree
import Languages.RTree.Diff
import Test.QuickCheck
import Test.Hspec
import Control.Monad.Except
--------------------------------------------
-- ** Merge Properties
{-
merge_id :: Property
merge_id = forAll genSimilarTrees' $ \(t1 , t2)
-> let patch = digemRTree t1 t2
iden = digemRTree t1 t1
mpid = patch // iden
midp = iden // patch
in case (,) <$> noConflicts mpid <*> noConflicts midp of
Nothing -> expectationFailure
$ unwords [ "has conflicts:"
, unwords $ getConflicts mpid
, ";;"
, unwords $ getConflicts midp
]
Just (pid , idp) ->
case (,) <$> applyRTree pid t1 <*> applyRTree idp t2 of
Left err -> expectationFailure ("apply failed: " ++ err)
Right (r1 , r2) -> (r1 , r2) `shouldBe` (t2 , t2)
merge_diag :: Property
merge_diag = forAll genSimilarTrees' $ \(t1 , t2)
-> let patch = digemRTree t1 t2
in case noConflicts (patch // patch) of
Nothing -> expectationFailure "has conflicts"
Just p -> case applyRTree' p t2 of
Nothing -> expectationFailure "apply failed"
Just r -> r `shouldBe` t2
-}
--------------------------------------------
-- ** Manual Merge Examples
data MergeOutcome
= MergeOk
| MergeDiffers
| ApplyFailed
| HasConflicts
deriving (Eq , Show)
expectMerge :: DiffMode
-> MergeOutcome -> String -> RTree -> RTree -> RTree
-> SpecWith (Arg Property)
expectMerge mode expt lbl a o b = do
it (lbl ++ ": " ++ show expt) $
doMerge mode a o b `shouldBe` expt
doMerge :: DiffMode -> RTree -> RTree -> RTree -> MergeOutcome
doMerge mode a o b
= let a' = dfrom $ into @FamRTree a
b' = dfrom $ into @FamRTree b
o' = dfrom $ into @FamRTree o
-- VCM: Funny... with DM_ProperShare and DM_NoNested
-- we see the same hspec restuls, but with DM_Patience
-- we get a different result altogether.
oa = digemRTreeHM mode 1 o a
ob = digemRTreeHM mode 1 o b
oaob = oa // ob
oboa = ob // oa
in case (,) <$> noConflicts oaob <*> noConflicts oboa of
Just (ab , ba)
-> case (,) <$> apply ab b' <*> apply ba a' of
Right (c1 , c2)
| eqFix eqHO c1 c2 -> MergeOk
| otherwise -> MergeDiffers
Left _ -> ApplyFailed
Nothing -> HasConflicts
mustMerge :: DiffMode -> String -> RTree -> RTree -> RTree -> SpecWith (Arg Property)
mustMerge m = expectMerge m MergeOk
xexpectMerge :: MergeOutcome -> String -> String -> RTree -> RTree -> RTree
-> SpecWith (Arg Property)
xexpectMerge expt reason lbl a o b = do
it (lbl ++ ": " ++ show expt) $
pendingWith reason
----------------------
-- Example 1
a1 , o1 , b1 :: RTree
a1 = "a" :>: [ "b" :>: []
, "c" :>: []
, "d" :>: []
]
o1 = "a" :>: [ "b" :>: []
, "d" :>: []
]
b1 = "a" :>: [ "b'" :>: []
, "d" :>: []
]
-------------------
-- Example 2
a2, o2, b2 :: RTree
a2 = "b" :>: [ "u" :>: [ "3" :>: [] ] , ".." :>: [] ]
o2 = "b" :>: [ "b" :>: [ "u" :>: [ "3" :>: [] ] , ".." :>: [] ]
, "." :>: []
]
b2 = "b" :>: [ "b" :>: [ "u" :>: [ "4" :>: [] ] , "u" :>: [ ".." :>: [] ] ]
, "." :>: []
]
-----------------
-- Example 3
a3 , o3 , b3 :: RTree
a3 = "x'" :>: [ "y" :>: [] , "z" :>: [] ]
o3 = "x" :>: [ "y" :>: [] , "z" :>: [] ]
b3 = "x" :>: [ "y'" :>: [] ]
---------------------------------
-- Example 4
a4 , o4 , b4 :: RTree
a4 = "y" :>: []
o4 = "x" :>: []
b4 = "y" :>: []
---------------------------------
-- Example 5
a5 , o5 , b5 :: RTree
a5 = "x" :>: [ "k" :>: [] , "u" :>: []]
o5 = "x" :>: [ "u" :>: [] , "k" :>: []]
b5 = "x" :>: [ "y" :>: ["u" :>: [] , "k" :>: [] ]
, "u" :>: [] , "k" :>: [] ]
---------------------------------
-- Example 6
a6 , o6 , b6 :: RTree
a6 = "x" :>: [ "u" :>: []]
o6 = "x" :>: [ "u" :>: [] , "k" :>: []]
b6 = "x" :>: [ "y" :>: ["u" :>: [] , "k" :>: [] ]
, "u" :>: [] , "k" :>: [] ]
---------------------------------
-- Example 7
a7 , o7 , b7 :: RTree
a7 = "x" :>: [ "u" :>: [ "b" :>: [] ] , "l" :>: [] ]
o7 = "x" :>: [ "a" :>: [] , "u" :>: [ "b" :>: [] ] , "k" :>: [] , "l" :>: []]
b7 = "y" :>: [ "a" :>: [] , "u" :>: [ "b" :>: [] ] , "k" :>: [] , "new" :>: [] , "l" :>: []]
---------------------------------
-- Example 8
a8 , o8 , b8 :: RTree
a8 = "x" :>: [ "k" :>: [] , "u" :>: []]
o8 = "x" :>: [ "u" :>: [] , "k" :>: []]
b8 = "x" :>: [ "u" :>: [] , "a" :>: [] , "k" :>: []]
---------------------------------
-- Example 9
a9 , o9 , b9 :: RTree
a9 = "x" :>: [ "k" :>: [] , "u" :>: []]
o9 = "x" :>: [ "u" :>: [] , "k" :>: []]
b9 = "x" :>: [ "u'" :>: [] , "k" :>: []]
-- Now we follow with triples that must NOT merge
--------------------------------
-- Example 10
a10 , o10 , b10 :: RTree
a10 = "x" :>: [ "u" :>: [] , "a" :>: [] , "k" :>: []]
o10 = "x" :>: [ "u" :>: [] , "k" :>: []]
b10 = "x" :>: [ "u" :>: [] , "b" :>: [] , "k" :>: []]
------------------------------
-- Example 11
a11 , o11 , b11 :: RTree
a11 = "x" :>: [ "u" :>: [] , "a" :>: []]
o11 = "x" :>: [ "u" :>: [] , "b" :>: []]
b11 = "x" :>: [ "u" :>: [] , "c" :>: []]
-----------------------------
-- Example 12
a12 , o12 , b12 :: RTree
a12 = "f" :>: ["j" :>: []]
o12 = "f" :>: ["a" :>: []]
b12 = "e" :>: []
----------------------------
-- Example 13
a13 , o13 , b13 :: RTree
a13 = "a" :>: []
o13 = "d" :>: ["i" :>: []]
b13 = "a" :>: ["j" :>: ["i" :>: []]]
---------------------------
-- Example 14
a14 , o14 , b14 :: RTree
a14 = "l" :>: []
o14 = "k" :>: ["b" :>: [],"l" :>: []]
b14 = "f" :>: ["k" :>: [],"b" :>: []]
---------------------------
-- Example 15
a15 , o15 , b15 :: RTree
a15 = "g" :>: []
o15 = "i" :>: ["g" :>: [],"c" :>: []]
b15 = "g" :>: ["k" :>: [],"l" :>: []]
------------------------
-- Example 16
a16 , o16 , b16 :: RTree
a16 = "j" :>: []
o16 = "g" :>: ["f" :>: [],"j" :>: []]
b16 = "e" :>: ["a" :>: [],"a" :>: [],"f" :>: []]
------------------------
-- Example 17
a17 , o17 , b17 :: RTree
a17 = "j" :>: ["f" :>: []]
o17 = "e" :>: ["f" :>: [],"f" :>: [],"m" :>: []]
b17 = "j" :>: ["g" :>: ["c" :>: [],"c" :>: [],"h" :>: [],"f" :>: []]]
------------------------
-- Example 18
a18 , o18 , b18 :: RTree
a18 = "r" :>: [ "a" :>: [] , "a" :>: []]
o18 = "r" :>: [ "a" :>: [] , "c" :>: []]
b18 = "r" :>: [ "b" :>: [] , "c" :>: [] ]
-------------------------
-- Example 19
a19 , o19 , b19 :: RTree
a19 = "c" :>: ["c" :>: []]
o19 = "c" :>: ["m" :>: ["a" :>: []]]
b19 = "f" :>: ["c" :>: [],"c" :>: [],"c" :>: [],"k" :>: []]
------------------------
-- Example 20
a20 , o20 , b20 :: RTree
a20 = "x" :>: ["a" :>: [] , "c" :>: [] , "d" :>: [] , "b" :>: []]
o20 = "x" :>: ["a" :>: [] , "b" :>: []]
b20 = "x" :>: ["a" :>: [] , "c" :>: [] , "b" :>: []]
{-
cc :: RTree -> RTree -> RTree -> Bool
cc a o b =
let p = distrCChange $ digemRTree o a
q = distrCChange $ digemRTree o b
in case (,) <$> thin p (domain q) <*> thin q (domain p) of
Left err -> error "imp; its a span!"
Right (p' , q')
-> ( changeEq q q' && changeEq p p')
|| ( changeEq q q' && not (changeEq p p'))
|| (not (changeEq q q') && changeEq p p')
-}
oa9 = digemRTree o9 a9
ob9 = digemRTree o9 b9
oa8 = digemRTree o8 a8
ob8 = digemRTree o8 b8 `withFreshNamesFrom` oa8
coa8 = distrCChange oa8
cob8 = distrCChange ob8
myprocess ca cb =
let Right ca' = thin ca (domain cb)
Right cb' = thin cb (domain ca)
newinsa = pmatch (cCtxDel ca') (cCtxDel cb') >>= transport (cCtxIns ca')
in case runExcept newinsa of
Left err -> error ("impossible: " ++ show err)
Right r -> (r , cCtxDel cb' , cCtxIns cb')
{-
mymerge :: RTree -> RTree -> RTree -> IO ()
mymerge a o b = do
let oa = digemRTree o a
let ob = digemRTree o b `withFreshNamesFrom` oa
let ca' = distrCChange oa
let cb' = distrCChange ob
let (ca , d , cb) = myprocess ca' cb'
let (i , res) = diff' 0 d ca
let (_ , res') = diff' 0 d cb
print res
putStrLn "-----------------"
print res'
-}
{-
p = distrCChange oa8
q = distrCChange ob8
thinned p q = uncurry' cmatch <$> thin' (cCtxDel p :*: cCtxIns p)
(cCtxDel q :*: cCtxIns q)
mymerge p q = do
p' <- thin p (domain q)
q' <- thin q (domain p)
if changeEq q' q
then return p
else case tr p' q' of
Left err -> error $ show err
Right r -> return r
-}
myDigemRTree = digemRTreeHM DM_ProperShare 1
oa1 = myDigemRTree o1 a1
ob1 = myDigemRTree o1 b1
oa2 = myDigemRTree o2 a2
ob2 = myDigemRTree o2 b2
oa7 = myDigemRTree o7 a7
ob7 = myDigemRTree o7 b7
oa5 = myDigemRTree o5 a5
ob5 = myDigemRTree o5 b5
oa6 = myDigemRTree o6 a6
ob6 = myDigemRTree o6 b6
oa12 = myDigemRTree o12 a12
ob12 = myDigemRTree o12 b12
oa13 = myDigemRTree o13 a13
ob13 = myDigemRTree o13 b13
oa14 = myDigemRTree o14 a14
ob14 = myDigemRTree o14 b14
oa15 = myDigemRTree o15 a15
ob15 = myDigemRTree o15 b15
oa16 = myDigemRTree o16 a16
ob16 = myDigemRTree o16 b16
oa17 = myDigemRTree o17 a17
ob17 = myDigemRTree o17 b17
oa18 = myDigemRTree o18 a18
ob18 = myDigemRTree o18 b18
oa19 = myDigemRTree o19 a19
ob19 = myDigemRTree o19 b19
oa20 = myDigemRTree o20 a20
ob20 = myDigemRTree o20 b20
gen3Trees :: Gen (RTree , RTree , RTree)
gen3Trees = choose (0 , 4)
>>= genSimilarTreesN 3
>>= \[a , o , b] -> return (a , o , b)
spec :: Spec
spec = do
-- describe "properties" $ do
-- it "p // id == p && id // p == id" $ merge_id
-- it "p // p == id" $ merge_diag
flip mapM_ (enumFrom (toEnum 0)) $ \m -> do
describe ("merge: manual examples (" ++ show m ++ ")") $ do
mustMerge m "01" a1 o1 b1
mustMerge m "02" a2 o2 b2
mustMerge m "03" a3 o3 b3
mustMerge m "04" a4 o4 b4
if m == DM_Patience
then expectMerge m HasConflicts "05" a5 o5 b5
else mustMerge m "05" a5 o5 b5
if m == DM_Patience
then expectMerge m HasConflicts "06" a6 o6 b6
else mustMerge m "06" a6 o6 b6
mustMerge m "07" a7 o7 b7
mustMerge m "08" a8 o8 b8
mustMerge m "09" a9 o9 b9
expectMerge m HasConflicts "10" a10 o10 b10
expectMerge m HasConflicts "11" a11 o11 b11
expectMerge m HasConflicts "12" a12 o12 b12
mustMerge m "13" a13 o13 b13
expectMerge m HasConflicts "14" a14 o14 b14
expectMerge m HasConflicts "15" a15 o15 b15
expectMerge m HasConflicts "16" a16 o16 b16
if m == DM_Patience
then mustMerge m "17" a17 o17 b17
else expectMerge m HasConflicts "17" a17 o17 b17
mustMerge m "18" a18 o18 b18
mustMerge m "19" a19 o19 b19
xexpectMerge MergeOk "What to do with self-contained ins-ins?" "20" a20 o20 b20
describe ("merge: conflict or ok (" ++ show m ++ ")") $ do
it "contains no apply fail or merge differs" $ property $
forAll gen3Trees $ \(a , o , b)
-> doMerge m a o b `elem` [MergeOk , HasConflicts]