packages feed

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

module CodecSpec
  ( codecTests,
  )
where

import Data.Map.Strict qualified as Map
import Moonlight.Delta.Patch
import Moonlight.Delta.Patch.Internal.Builder (toPaged)
import Moonlight.Delta.Patch.Internal.Types (CodecStats (..), debugCodecStats)
import Moonlight.Delta.Patch.Internal.Types qualified as Internal
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (assertFailure, (@?=), testCase)

codecTests :: TestTree
codecTests =
  testGroup
    "codec"
    [ testCase "constructor packs repeated endpoints as constant columns" $
        debugCodecStats repeatedEndpointPatch
          @?=
            CodecStats
              { codecExplicitKeyColumns = 0,
                codecRangeKeyColumns = 1,
                codecAffineKeyColumns = 0,
                codecAbsentColumns = 0,
                codecConstantColumns = 2,
                codecRunColumns = 0,
                codecAffineValueColumns = 0,
                codecDenseColumns = 0
              },
      testCase "constructor packs repeated endpoint runs as run columns" $
        debugCodecStats runEndpointPatch
          @?=
            CodecStats
              { codecExplicitKeyColumns = 0,
                codecRangeKeyColumns = 1,
                codecAffineKeyColumns = 0,
                codecAbsentColumns = 0,
                codecConstantColumns = 0,
                codecRunColumns = 2,
                codecAffineValueColumns = 0,
                codecDenseColumns = 0
              },
      testCase "constructor packs strided Int support as affine key columns" $
        debugCodecStats affineKeyPatch
          @?=
            CodecStats
              { codecExplicitKeyColumns = 0,
                codecRangeKeyColumns = 0,
                codecAffineKeyColumns = 1,
                codecAbsentColumns = 0,
                codecConstantColumns = 2,
                codecRunColumns = 0,
                codecAffineValueColumns = 0,
                codecDenseColumns = 0
              },
      testCase "constructor keeps non-Int support in explicit key columns" $
        debugCodecStats genericKeyPatch
          @?=
            CodecStats
              { codecExplicitKeyColumns = 1,
                codecRangeKeyColumns = 0,
                codecAffineKeyColumns = 0,
                codecAbsentColumns = 0,
                codecConstantColumns = 2,
                codecRunColumns = 0,
                codecAffineValueColumns = 0,
                codecDenseColumns = 0
              },
      testCase "constructor packs strided Int endpoints as affine value columns" $
        debugCodecStats affineValuePatch
          @?=
            CodecStats
              { codecExplicitKeyColumns = 0,
                codecRangeKeyColumns = 1,
                codecAffineKeyColumns = 0,
                codecAbsentColumns = 0,
                codecConstantColumns = 0,
                codecRunColumns = 0,
                codecAffineValueColumns = 2,
                codecDenseColumns = 0
              },
      testCase "normalization downshifts an under-threshold paged patch" $
        case Internal.normalize (toPaged smallPatch) of
          Internal.SmallPatch _cells -> pure ()
          Internal.PagedPatch _count _pages -> assertFailure "under-threshold patch remained paged",
      testCase "diff returns the canonical small representation" $
        case diff (Map.empty :: Map.Map Int String) Map.empty of
          Internal.SmallPatch _cells -> pure ()
          Internal.PagedPatch _count _pages -> assertFailure "empty diff remained paged"
    ]

repeatedEndpointPatch :: Patch Int String
repeatedEndpointPatch =
  fromAscList
    [ (key, replace "before" "after")
      | key <- [0 .. 63]
    ]

runEndpointPatch :: Patch Int String
runEndpointPatch =
  fromAscList
    [ (key, replace (beforeValue key) (afterValue key))
      | key <- [0 .. 63]
    ]
  where
    beforeValue :: Int -> String
    beforeValue key =
      if key < 32 then "before-left" else "before-right"

    afterValue :: Int -> String
    afterValue key =
      if key < 32 then "after-left" else "after-right"

affineKeyPatch :: Patch Int String
affineKeyPatch =
  fromAscList
    [ (key, replace "before" "after")
      | key <- fmap (* 2) [0 .. 63]
    ]

newtype GenericCodecKey = GenericCodecKey Int
  deriving stock (Eq, Ord)

genericKeyPatch :: Patch GenericCodecKey String
genericKeyPatch =
  fromAscList
    [ (GenericCodecKey key, replace "before" "after")
      | key <- [0 .. 63]
    ]

affineValuePatch :: Patch Int Int
affineValuePatch =
  fromAscList
    [ (key, replace (key * 2) (key * 2 + 1))
      | key <- [0 .. 63]
    ]

smallPatch :: Patch Int String
smallPatch =
  fromAscList
    [ (key, replace "before" "after")
      | key <- [0 .. 7]
    ]