{-# 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"]