packages feed

capnp-0.17.0.0: tests/Module/Capnp/Canonicalize.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ViewPatterns #-}

module Module.Capnp.Canonicalize
  ( canonicalizeTests,
  )
where

import Capnp (Parsed, Raw (..), createPure, encode, msgToLBS)
import qualified Capnp.Basics as B
import Capnp.Canonicalize
import qualified Capnp.Message as M
import qualified Capnp.Untyped as U
import Control.Monad (unless)
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Vector as V
import Instances ()
import Test.Hspec
import Test.QuickCheck (property)
import Test.QuickCheck.IO (propertyIO)
import Util (capnpCanonicalize)

canonicalizeTests :: Spec
canonicalizeTests =
  describe "canonicalization tests" $ do
    it "agrees with reference implementation" $
      property $ \case
        B.Struct (V.toList -> []) (V.toList -> []) ->
          -- skip this; it fails due to a bug in the reference implementation:
          --
          -- https://github.com/capnproto/capnproto/issues/1084
          --
          -- TODO: when that issue is fixed, stop skipping this case.
          propertyIO $ pure ()
        struct ->
          propertyIO $ implsAgreeOn struct

implsAgreeOn :: Parsed B.AnyStruct -> IO ()
implsAgreeOn struct = do
  let Just ourMsg = ourImplCanonicalize struct
  refMsg <- refImplCanonicalize struct
  unless (ourMsg == refMsg) $
    error $
      concat
        [ "Our implementation disagrees with the reference implementation on " ++ show struct,
          ".\n\nWe produce:\n\n",
          show $ LBS.unpack $ msgToLBS ourMsg,
          "\n\n",
          "But the reference implementation generates:\n\n",
          show $ LBS.unpack $ msgToLBS refMsg
        ]

ourImplCanonicalize :: Parsed B.AnyStruct -> Maybe (M.Message 'M.Const)
ourImplCanonicalize struct = createPure maxBound $ do
  msg <- M.newMessage Nothing
  Raw rawStruct <- encode msg struct
  (msg, _) <- canonicalizeMut rawStruct
  pure msg

refImplCanonicalize :: Parsed B.AnyStruct -> IO (M.Message 'M.Const)
refImplCanonicalize struct = do
  msg <- createPure maxBound $ do
    msg <- M.newMessage Nothing
    Raw rawStruct <- encode msg struct
    U.setRoot rawStruct
    pure msg
  lbs <- capnpCanonicalize (msgToLBS msg)
  let segment = M.fromByteString $ LBS.toStrict lbs
  pure $ M.singleSegment segment