packages feed

glsl-0.0.1.0: test/Language/GLSL/BitCodeSpec.hs

{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ViewPatterns        #-}
module Language.GLSL.BitCodeSpec where

import           Test.GLSL.Arbitrary              ()
import           Test.Hspec                       (Spec, describe, it, runIO,
                                                   shouldBe, shouldNotBe)
import           Test.Hspec.QuickCheck            (prop)
import           Test.QuickCheck                  (forAllShrink, shrink)
import qualified Test.QuickCheck.Gen              as Gen

import           Control.Monad                    (forM_, unless, when)
import qualified Data.Text.Lazy.IO                as IO
import qualified Data.Vector                      as V
import           Language.GLSL.AST
import           Language.GLSL.BitCode            (encodeStmt)
import qualified Language.GLSL.ConstExpr          as ConstExpr
import           Language.GLSL.Parser             (parseShader)
import           Language.GLSL.StructuralEquality (eqStmt)
import qualified System.Directory                 as Dir

-- Run an exhaustive n^2 test comparing every line to every other line in the
-- test file. This takes around 13 seconds on my laptop.
exhaustive :: Bool
exhaustive = False

getStatements :: FilePath -> IO (V.Vector (Stmt ()))
getStatements path = do
  inTopDir <- Dir.doesDirectoryExist "glsl"
  when inTopDir $ Dir.setCurrentDirectory "glsl"
  glsl <- parseShader <$> IO.readFile path
  case glsl of
    Left err ->
      fail err
    Right (GLSL _ (reverse -> (ProcDecl _ _ ss):_)) ->
      return . V.fromList . map unAnnot $ ss
    Right _ ->
      fail "test file does not end with a main function"


encodeStmtSpec :: Maybe ConstExpr.ConstExprs -> Spec
encodeStmtSpec ce = do
  prop "should produce the same encoding for structurally equal statements" $
    \(s1 :: Stmt ()) (s2 :: Stmt ()) ->
      let enc1 = encodeStmt ce s1
          enc2 = encodeStmt ce s2
      in
      when (eqStmt ce s1 s2) $
        enc1 `shouldBe` enc2

  prop "should produce different encoding for structurally different statements" $
    \(s1 :: Stmt ()) (s2 :: Stmt ()) ->
      let enc1 = encodeStmt ce s1
          enc2 = encodeStmt ce s2
      in
      unless (eqStmt ce s1 s2) $
        enc1 `shouldNotBe` enc2


testFileSpec :: FilePath -> Spec
testFileSpec filename = do
  stmts <- runIO $ getStatements $ "test/data/large-shaders/" <> filename
  let forAllStmt p = forAllShrink (
          (stmts V.!) <$> Gen.chooseBoundedIntegral (0, V.length stmts - 1))
        shrink p

  it ("should be able to encode all statements from " <> filename) $ do
    forM_ stmts $ \s ->
      encodeStmt Nothing s
      `shouldBe`
      encodeStmt Nothing s

  prop ("should obey (a = b) => enc a = enc b for all pairs in " <> filename) $
    forAllStmt $ \s1 -> forAllStmt $ \s2 ->
        let enc1 = encodeStmt Nothing s1
            enc2 = encodeStmt Nothing s2
        in
        if eqStmt Nothing s1 s2
          then enc1 `shouldBe` enc2
          else enc1 `shouldNotBe` enc2

  when exhaustive $
    it ("should obey (a = b) => enc a = enc b for every single pair in " <> filename) $
      forM_ [0..V.length stmts - 1] $ \i -> forM_ [i..V.length stmts - 1] $ \j ->
          let s1 = stmts V.! i
              s2 = stmts V.! j
              enc1 = encodeStmt Nothing s1
              enc2 = encodeStmt Nothing s2
          in
          if eqStmt Nothing s1 s2
            then enc1 `shouldBe` enc2
            else enc1 `shouldNotBe` enc2



spec :: Spec
spec = do
  describe "encodeStmt" $ do
    encodeStmtSpec Nothing

  describe "encodeStmt with ConstExpr" $ do
    encodeStmtSpec $ Just ConstExpr.empty

  -- testFileSpec "lambdacnc.frag"
  -- testFileSpec "lambdacnc.vert"
  testFileSpec "xax.frag"
  -- testFileSpec "xax.vert"