packages feed

moonlight-delta-0.1.0.0: test/patch/PatchSupport.hs

{-# LANGUAGE DerivingStrategies #-}

module PatchSupport where

import Data.Map.Strict qualified as Map
import PatchLaws
  ( PatchChain (..),
    PatchStaleCase (..),
  )
import Moonlight.Delta.Patch
import Test.QuickCheck
  ( Gen,
    chooseInt,
    elements,
    frequency,
    listOf,
    sublistOf,
    vectorOf,
  )

data RepresentativeKey = RepresentativeKey
  { representativeKeyOrder :: !Int,
    representativeKeyName :: !String
  }
  deriving stock (Show)

instance Eq RepresentativeKey where
  left == right =
    representativeKeyOrder left == representativeKeyOrder right

instance Ord RepresentativeKey where
  compare left right =
    compare (representativeKeyOrder left) (representativeKeyOrder right)

data PatchFacadeChain = PatchFacadeChain
  { pfcState0 :: !(Map.Map Int Int),
    pfcState1 :: !(Map.Map Int Int),
    pfcState2 :: !(Map.Map Int Int),
    pfcState3 :: !(Map.Map Int Int)
  }
  deriving stock (Eq, Show)
patchDeltaEntriesStrictlyAscending :: Patch Int String -> Bool
patchDeltaEntriesStrictlyAscending =
  keysStrictlyAscending . fmap fst . patchToAscList

patchCanonicalBefore :: Patch key value -> Map.Map key value
patchCanonicalBefore =
  mapMaybeWithKey (const cellBefore)

patchCanonicalAfter :: Patch key value -> Map.Map key value
patchCanonicalAfter =
  mapMaybeWithKey (const cellAfter)

singletonPatch :: key -> Maybe value -> Maybe value -> Patch key value
singletonPatch key before after =
  singleton key (cellFromEndpoints before after)

patchFromList :: (PatchKey key, PatchValue value) => [(key, CellPatch value)] -> Patch key value
patchFromList =
  fromList

patchToAscList :: Patch key value -> [(key, CellPatch value)]
patchToAscList =
  toAscList

keysStrictlyAscending :: [Int] -> Bool
keysStrictlyAscending keysList =
  case keysList of
    [] ->
      True
    [_key] ->
      True
    left : right : rest ->
      left < right && keysStrictlyAscending (right : rest)

patchCellValues :: [Maybe String]
patchCellValues =
  [ Nothing,
    Just "a",
    Just "b",
    Just "c"
  ]

patchCellValueGen :: Gen (Maybe String)
patchCellValueGen =
  elements patchCellValues

patchEntryGen :: Gen (Int, CellPatch String)
patchEntryGen =
  (,)
    <$> chooseInt (0, 16)
    <*> (cellFromEndpoints <$> patchCellValueGen <*> patchCellValueGen)

patchDeltaGen :: Gen (Patch Int String)
patchDeltaGen =
  patchFromList <$> listOf patchEntryGen

patchPairGen :: Gen (Patch Int String, Patch Int String)
patchPairGen =
  (,) <$> patchDeltaGen <*> patchDeltaGen

patchStateEntryGen :: Gen (Int, String)
patchStateEntryGen =
  (,) <$> chooseInt (0, 16) <*> elements ["a", "b", "c"]

patchStateGen :: Gen (Map.Map Int String)
patchStateGen =
  Map.fromList <$> listOf patchStateEntryGen

patchApplyCaseGen :: Gen (Map.Map Int String, Patch Int String)
patchApplyCaseGen =
  (,) <$> patchStateGen <*> patchDeltaGen

patchReplayCaseGen :: Gen (Map.Map Int String, [Patch Int String])
patchReplayCaseGen =
  (,) <$> patchStateGen <*> listOf patchDeltaGen

patchThresholdStraddlingSizes :: [Int]
patchThresholdStraddlingSizes =
  [0, 1, 8, 15, 16, 17, 32]

patchStraddlingGen :: Gen (Patch Int String)
patchStraddlingGen = do
  targetSize <- elements patchThresholdStraddlingSizes
  cells <- vectorOf targetSize (snd <$> patchEntryGen)
  pure (patchFromList (zip [0 ..] cells))

patchStraddlingApplyCaseGen :: Gen (Map.Map Int String, Patch Int String)
patchStraddlingApplyCaseGen =
  (,) <$> patchStateGen <*> patchStraddlingGen

patchStraddlingPairGen :: Gen (Patch Int String, Patch Int String)
patchStraddlingPairGen =
  (,) <$> patchStraddlingGen <*> patchStraddlingGen

patchStraddlingReplayCaseGen :: Gen (Map.Map Int String, [Patch Int String])
patchStraddlingReplayCaseGen =
  (,) <$> patchStateGen <*> listOf patchStraddlingGen

patchStatePairGen :: Gen (Map.Map Int String, Map.Map Int String)
patchStatePairGen =
  (,) <$> patchStateGen <*> patchStateGen

patchFacadeChainGen :: Gen PatchFacadeChain
patchFacadeChainGen =
  frequency
    [ (1, elements (patchFacadeChain <$> patchFacadeKeyFamilies)),
      (9, patchFacadeRandomChainGen)
    ]

patchFacadeRandomChainGen :: Gen PatchFacadeChain
patchFacadeRandomChainGen =
  PatchFacadeChain
    <$> patchFacadeRandomStateGen
    <*> patchFacadeRandomStateGen
    <*> patchFacadeRandomStateGen
    <*> patchFacadeRandomStateGen

patchFacadeKeyUniverse :: [Int]
patchFacadeKeyUniverse =
  [0 .. 8]

patchFacadeRandomStateGen :: Gen (Map.Map Int Int)
patchFacadeRandomStateGen =
  sublistOf patchFacadeKeyUniverse
    >>= fmap Map.fromList . traverse patchFacadeRandomStateEntryGen

patchFacadeRandomStateEntryGen :: Int -> Gen (Int, Int)
patchFacadeRandomStateEntryGen key =
  (,) key <$> chooseInt (0, 3)

patchAppliedEditListGen :: Gen [(Int, CellPatch Int)]
patchAppliedEditListGen =
  patchFacadeChainEdits <$> patchFacadeChainGen

patchFacadeKeyFamilies :: [([Int], [Int], [Int], [Int])]
patchFacadeKeyFamilies =
  [ ([0 .. 8], [0 .. 8], [0 .. 8], [0 .. 8]),
    ([0, 2, 4, 6, 8], [1, 3, 5, 7], [0, 1, 2, 6, 7, 8], [0 .. 8]),
    ([0 .. 4], [2 .. 6], [4 .. 8], [1, 3, 5, 7]),
    ([0, 1], [3, 4], [6, 7], [2, 5, 8]),
    ([0, 4, 8], [0, 1, 4, 7], [2, 4, 6, 8], [1, 2, 3, 5, 8])
  ]

patchFacadeChain :: ([Int], [Int], [Int], [Int]) -> PatchFacadeChain
patchFacadeChain (keys0, keys1, keys2, keys3) =
  PatchFacadeChain
    { pfcState0 = patchFacadeState 0 keys0,
      pfcState1 = patchFacadeState 1 keys1,
      pfcState2 = patchFacadeState 2 keys2,
      pfcState3 = patchFacadeState 3 keys3
    }

patchFacadeState :: Int -> [Int] -> Map.Map Int Int
patchFacadeState stage keys =
  Map.fromAscList
    [ (key, stage * 16 + key)
      | key <- keys
    ]

patchFacadeChainEdits :: PatchFacadeChain -> [(Int, CellPatch Int)]
patchFacadeChainEdits chain =
  toAscList (diff (pfcState0 chain) (pfcState1 chain))
    <> toAscList (diff (pfcState1 chain) (pfcState2 chain))
    <> toAscList (diff (pfcState2 chain) (pfcState3 chain))

patchChainGen :: Gen (PatchChain Int String)
patchChainGen =
  PatchChain
    <$> chooseInt (0, 16)
    <*> patchCellValueGen
    <*> patchCellValueGen
    <*> patchCellValueGen

patchStaleCaseGen :: Gen (PatchStaleCase Int String)
patchStaleCaseGen = do
  key <- chooseInt (0, 16)
  expected <- patchCellValueGen
  actual <- elements (filter (/= expected) patchCellValues)
  replacement <- patchCellValueGen
  pure
    PatchStaleCase
      { pscKey = key,
        pscExpectedValue = expected,
        pscActualValue = actual,
        pscReplacementValue = replacement
      }