{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}

-- | This module defines a test that tries to walk over the
-- CodeGeneratorRequest in `tests/data/schema-codegenreq`,
-- failing if any of the data is not as expected.
module WalkSchemaCodeGenRequest (walkSchemaCodeGenRequestTest) where

import Capnp
  ( Raw,
    bsToRaw,
    hasField,
    index,
    length,
    parseField,
    readField,
    textBytes,
  )
import qualified Capnp.Gen.Capnp.Schema as Schema
import qualified Capnp.Message as M
import Capnp.TraversalLimit (LimitT, evalLimitT, execLimitT)
import Control.Monad (when)
import Control.Monad.Trans.Class (lift)
import qualified Data.ByteString as BS
import Data.Function ((&))
import qualified Data.Vector as V
import Test.Hspec
import Prelude hiding (length)

nodeNames :: V.Vector BS.ByteString
nodeNames =
  V.fromList
    [ "Import",
      "annotation",
      "Value",
      "Type"
    ]

-- TODO: This contains a bit of copypasta from some of the untyped tests; should
-- factor that out.
walkSchemaCodeGenRequestTest :: Spec
walkSchemaCodeGenRequestTest =
  describe "Various sanity checks on a known schema CodeGeneratorRequest" $
    it "Should match misc. expectations" $ do
      -- TODO: the above description betrays that this test isn't
      -- especially well defined at a granularity that I(zenhack)
      -- know how to tell hspec about, because there are data
      -- dependencies between conceptual tests (we walk over the
      -- data checking various things as we go).
      --
      -- It would be nice if we could mark off individual checks for
      -- hspec's reporting somehow.
      bytes <- BS.readFile "tests/data/schema-codegenreq"
      root <- evalLimitT maxBound (bsToRaw bytes)
      endQuota <- execLimitT 4096 (reader root)
      endQuota `shouldBe` 3409
  where
    reader :: Raw Schema.CodeGeneratorRequest 'M.Const -> LimitT IO ()
    reader req = do
      nodes <- req & readField #nodes
      requestedFiles <- req & readField #requestedFiles
      lift $ length nodes `shouldBe` 37
      lift $ length requestedFiles `shouldBe` 1
      mapM_ (walkNode nodes) [0, 1 .. 36]
    walkNode nodes i = do
      node <- index i nodes
      -- None of the nodes in the schema have parameters:
      False <- node & hasField #parameters
      -- And none of them are generic:
      False <- node & parseField #isGeneric

      nameList <- node & readField #displayName
      name <- textBytes nameList
      prefixLen <- parseField #displayNamePrefixLength node
      let baseName = BS.drop (fromIntegral prefixLen) name

      when (i < V.length nodeNames && baseName /= (nodeNames V.! i)) $
        error "Incorrect name."

      has <- node & hasField #annotations

      -- there are two annotations in all of the nodes, at these indicies:
      case (has, i `elem` [4, 9]) of
        (False, False) -> return ()
        (True, True) -> do
          1 <- length <$> readField #annotations node
          return ()
        (False, True) ->
          error $
            "Node at index "
              ++ show i
              ++ " should have had"
              ++ "an annotation."
        (True, False) ->
          error $
            "Node at index "
              ++ show i
              ++ " should not "
              ++ "have had an annotation."