content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  
  module QPACK.QIFSpec where
  
  import Control.Monad
  import Data.ByteString (ByteString)
  import Data.Conduit.Attoparsec
  import Data.IORef
  import Data.Sequence (Seq, ViewR (..), viewr, (<|))
  import qualified Data.Sequence as Seq
  import Network.QUIC (StreamId)
  import System.IO
  import Test.Hspec
  
  import Network.QPACK
  import Network.QPACK.Internal
  
  import QIF
  
  spec :: Spec
  spec = do
      describe "simple decoder" $ do
          it "decodes with dynamic table" $ do
              forM_ ["fb-req-hq", "fb-resp-hq", "netbsd-hq"] $ \svc ->
                  forM_ ["f5", "ls-qpack", "nghttp3", "proxygen", "qthingey", "quinn"] $ \impl ->
                      forM_ [".0.0", ".0.1", ".100.0", ".100.1"] $ \suffix ->
                          forM_ [256, 512, 4096 :: Int] $ \size -> do
                              let inp =
                                      "qifs/encoded/qpack-05/" ++ impl ++ "/" ++ svc ++ ".out." ++ show size ++ suffix
                                  out = "qifs/qifs/" ++ svc ++ ".qif"
                              putStrLn inp
                              test size inp out
  
          it "decodes only with static table" $ do
              forM_ ["fb-req-hq", "fb-resp-hq", "netbsd-hq"] $ \svc ->
                  forM_ ["ls-qpack", "nghttp3", "qthingey", "quinn"] $ \impl ->
                      forM_ [".0.0", ".0.1", ".100.0", ".100.1"] $ \suffix -> do
                          let size = 0
                          let inp =
                                  "qifs/encoded/qpack-05/" ++ impl ++ "/" ++ svc ++ ".out." ++ show size ++ suffix
                              out = "qifs/qifs/" ++ svc ++ ".qif"
                          putStrLn inp
                          test size inp out
  
  ----------------------------------------------------------------
  
  test :: Int -> FilePath -> FilePath -> IO ()
  test size efile qfile = do
      (dec, insthdr) <-
          newQDecoderS
              defaultQDecoderConfig{dcMaxTableCapacity = size}
              (\_ -> return ())
              False
      _ <- encodeEncoderInstructions [SetDynamicTableCapacity size] False >>= insthdr
      ref <- newIORef Seq.empty
      withFile qfile ReadMode $ \h -> do
          processBlock efile $ testSwitch dec insthdr ref h
  
  testSwitch
      :: (StreamId -> ByteString -> IO (Maybe [Header]))
      -> EncoderInstructionHandlerS
      -> IORef (Seq Block)
      -> Handle
      -> (PositionRange, Block)
      -> IO ()
  testSwitch dec insthdr ref h (_, blk@(Block n bs))
      | n == 0 = do
          _ <- insthdr bs
          fifo <- readIORef ref
          loop fifo
      -- to avoid blocking by "dec", ask decoding to the other thread
      | otherwise = do
          mhdr <- dec n bs
          case mhdr of
              Nothing -> modifyIORef' ref (blk <|)
              Just hdr -> compareHeaders hdr
    where
      loop fifo = do
          case viewr fifo of
              EmptyR -> writeIORef ref Seq.empty
              fifo' :> Block n1 bs1 -> do
                  mhdr <- dec n1 bs1
                  case mhdr of
                      Nothing -> writeIORef ref fifo
                      Just hdr -> do
                          compareHeaders hdr
                          loop fifo'
      compareHeaders hdr = do
          hdr' <- fromCaseSensitive <$> headerlist h
          fromCaseSensitive hdr `shouldBe` hdr'
  