crjdt-haskell-0.1.0.0: test/Data/FigureSpec.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
module Data.FigureSpec where
import Test.Hspec
import Data.Map as Map hiding (insert, keys)
import Data.Set as Set
import Control.Monad.State
import Data.Crjdt as C
import Data.Crjdt.Internal
spec :: Spec
spec = describe "Figures from CRJDT paper" $ do
let putRemote ops = modify (\ctx -> ctx { received = ops `mappend` (received ctx)})
it "Figure 1" $ do
let
initial = execute (key "key" doc =: "A")
r1 = initial
r2 = initial
r1next = r1 *> execute (key "key" doc =: "C")
r2next = r2 *> execute (key "key" doc =: "D")
(fr, firstState) = run 1 r1next
(sr, secondState) = run 2 r2next
r1yield = r1next *> putRemote (queue secondState) *> execute yield
r2yield = r2next *> putRemote (queue firstState) *> execute yield
(rr, r1Result) = run 1 r1yield
(r2r, r2Result) = run 2 r2yield
_ <- traverse (\x -> x `shouldBe` Right ()) [fr, sr, rr, r2r]
document firstState `shouldSatisfy` (/= (document secondState))
document r1Result `shouldBe` document r2Result
history r1Result `shouldBe` history r2Result
let
p = Set.fromList [mkId 1 1, mkId 1 2, mkId 2 1, mkId 2 2]
docPresence = Map.fromList [(Key DocKey, p)]
keyPresence = Map.fromList [("key", p)]
leaf = RegDocument $ Map.fromList $ [(mkId 2 1, "C"),(mkId 2 2, "D")]
innerMap = Branch
{ children = Map.fromList [(tagWith RegT (Str "key"), LeafDocument leaf)]
, presence = keyPresence
, keyOrder = mempty
, branchTag = MapT
}
parent = Branch
{ children = Map.fromList [(tagWith MapT DocKey, BranchDocument innerMap)]
, presence = docPresence
, keyOrder = mempty
, branchTag = MapT
}
d = BranchDocument parent
document r1Result `shouldBe` d
it "Figure 2" $ do
let r1 = do
var <- bind "var" (key "colors" doc)
key "blue" var =: "#0000ff"
(_, r1result) = run 1 $ execute r1
r1next = do
r1
key "red" "var" =: "#ff0000"
r2next = putRemote (queue r1result) *> execute yield *> execute (do
key "colors" doc =: emptyMap
key "green" (key "colors" doc) =: "#00ff00”"
)
(r1r, r1State) = run 1 $ execute (r1next *> keys (key "colors" doc))
(r2r, r2State) = run 2 (r2next *> execute (keys (key "colors" doc)))
r1Final = execute r1next *> putRemote (queue r2State) *> execute (yield *> keys (key "colors" doc))
r2Final = r2next *> putRemote (queue r1State) *> execute (yield *> keys (key "colors" doc))
(Right keys1, finalResult1) = run 1 r1Final
(Right keys2, finalResult2) = run 2 r2Final
keys1 `shouldBe` keys2
keys1 `shouldBe` Set.fromList ["red", "green"]
document finalResult1 `shouldBe` document finalResult2
history finalResult1 `shouldBe` history finalResult2
it "Figure 3" $ do
let cmd1 = do
key "grocery" doc =: emptyList
C.insert "eggs" (iter $ key "grocery" doc)
eggs <- bind "eggs" (next (iter (key "grocery" doc)))
C.insert "ham" eggs
let cmd2 = do
key "grocery" doc =: emptyMap
C.insert "milk" (iter (key "grocery" doc))
milk <- bind "milk" (next (iter (key "grocery" doc)))
C.insert "flour" milk
let (Right (), r1State) = run 1 $ execute cmd1
(Right (), r2State) = run 2 $ execute cmd2
let getValues = do
eggs <- values "eggs"
milk <- values (next $ "eggs")
ham <- values (next $ next $ "eggs")
flour <- values (next $ next $ next $ "eggs")
pure (eggs ++ milk ++ ham ++ flour)
let (Right xs, r1Final) = run 1 (execute cmd1 *> putRemote (queue r2State) *> execute (yield *> getValues))
(Right (), r2Final) = run 2 (execute cmd2 *> putRemote (queue r1State) *> execute yield)
xs `shouldBe` ["eggs", "milk", "ham", "flour"]
document r1Final `shouldBe` document r2Final
-- grocery `shouldBe` expectedGrocery
describe "Empty updates" $ do
let test what = do
let cmd = key "g" doc =: what
cmd1 = cmd
(Right (), r) = run 1 $ execute cmd
(Right (), r1) = run 2 $ execute cmd1
let (Right (), x1) = run 1 (execute cmd *> putRemote (queue r1) *> execute yield)
(Right (), x2) = run 2 (execute cmd1 *> putRemote (queue r) *> execute yield)
document x1 `shouldBe` document x2
it "Empty object update" $ test emptyMap
it "Empty list update" $ test emptyList
it "Figure 4" $ do
let cmd = do
todo <- "todo" -< iter (key "todo" doc)
C.insert emptyMap "todo"
key "title" (next $ "todo") =: "buy milk"
key "done" (next $ "todo") =: "false"
(Right (), cmdResult) = run 1 $ execute cmd
r1next = cmd *> C.delete (next $ "todo")
r2 = key "done" (next $ iter $ key "todo" doc) =: "true"
r2next = putRemote (queue cmdResult) *> execute yield *> execute r2
(Right (), r1St) = run 1 $ execute r1next
(Right (), r2St) = run 2 $ r2next
(Right keys1, r1Final) = run 1 (execute r1next *> putRemote (queue r2St) *> execute (yield *> keys (next $ iter $ key "todo" doc)))
(Right keys2, r2Final) = run 2 (r2next *> putRemote (queue r1St) *> execute (yield *> keys (next $ iter $ key "todo" doc)))
keys1 `shouldBe` keys2
keys1 `shouldBe` Set.fromList ["done"]
document r1Final `shouldBe` document r2Final
it "Figure 6" $ do
let cmd = do
doc =: emptyMap
list <- bind "list" $ doc .> key "shopping" .> iter
C.insert "eggs" list
eggs <- bind "eggs" (next list)
C.insert "milk" eggs
C.insert "cheese" list
(Right xs, _) = run 1 $ execute $ cmd *> do
eggs <- values "eggs"
milk <- values ("eggs" .> next)
cheese <- values ("eggs" .> next .> next)
pure (eggs ++ milk ++ cheese)
xs `shouldBe` ["eggs", "milk", "cheese"]