packages feed

geomancy-0.4.0.0: test/Spec/Vec64.hs

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TypeApplications #-}

module Spec.Vec64 (tests) where

import Geomancy
import Hedgehog
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Hedgehog (testPropertyNamed)

import Data.Proxy (Proxy(..))
import Data.Word (Word64)
import Foreign (allocaBytes, peekByteOff, pokeByteOff)
import Graphics.Gl.Block qualified as Block

import Geomancy.I64Vec3 qualified
import Geomancy.U64Vec3 qualified

import Support.Gen (genInt64, genWord64)
import Support.Property (pattern PROP_TESTS, layout140, storableRoundtrip)

tests :: TestTree
tests = testGroup "Vec64"
  [ testPropertyNamed "storable_roundtrip" "prop_vec64_storable_roundtrip" prop_vec64_storable_roundtrip
  , testPropertyNamed "packed_write_bounds" "prop_vec64_packed_write_bounds" prop_vec64_packed_write_bounds
  , testPropertyNamed "abs" "prop_vec64_abs" prop_vec64_abs
  , testPropertyNamed "block_layout" "prop_vec64_block_layout" prop_vec64_block_layout
  ]

prop_vec64_storable_roundtrip :: Property
prop_vec64_storable_roundtrip = withTests PROP_TESTS $ property do
  i2 <- forAll $ i64vec2 <$> genInt64 <*> genInt64
  storableRoundtrip i2 >>= (i2 ===)

  i3 <- forAll $ i64vec3 <$> genInt64 <*> genInt64 <*> genInt64
  storableRoundtrip i3 >>= (i3 ===)

  let i3p = Geomancy.I64Vec3.Packed i3
  storableRoundtrip i3p >>= (i3p ===)

  i4 <- forAll $ i64vec4 <$> genInt64 <*> genInt64 <*> genInt64 <*> genInt64
  storableRoundtrip i4 >>= (i4 ===)

  u2 <- forAll $ u64vec2 <$> genWord64 <*> genWord64
  storableRoundtrip u2 >>= (u2 ===)

  u3 <- forAll $ u64vec3 <$> genWord64 <*> genWord64 <*> genWord64
  storableRoundtrip u3 >>= (u3 ===)

  let u3p = Geomancy.U64Vec3.Packed u3
  storableRoundtrip u3p >>= (u3p ===)

  u4 <- forAll $ u64vec4 <$> genWord64 <*> genWord64 <*> genWord64 <*> genWord64
  storableRoundtrip u4 >>= (u4 ===)

prop_vec64_packed_write_bounds :: Property
prop_vec64_packed_write_bounds = withTests PROP_TESTS $ property do
  i3 <- forAll $ i64vec3 <$> genInt64 <*> genInt64 <*> genInt64
  u3 <- forAll $ u64vec3 <$> genWord64 <*> genWord64 <*> genWord64
  let sentinel = 0x5EAF00D :: Word64
  (i3', u3', afterI, afterU) <- evalIO $ allocaBytes 64 \ptr -> do
    pokeByteOff ptr 24 sentinel
    pokeByteOff ptr 56 sentinel
    pokeByteOff ptr 0 (Block.Packed i3)
    pokeByteOff ptr 32 (Block.Packed u3)
    (,,,)
      <$> fmap Block.getPacked (peekByteOff ptr 0)
      <*> fmap Block.getPacked (peekByteOff ptr 32)
      <*> peekByteOff ptr 24
      <*> peekByteOff ptr 56
  i3 === i3'
  u3 === u3'
  sentinel === afterI
  sentinel === afterU

prop_vec64_abs :: Property
prop_vec64_abs = withTests 1 $ property do
  i64vec2 1 2 === abs (i64vec2 (-1) 2)
  i64vec3 1 2 3 === abs (i64vec3 (-1) 2 (-3))
  i64vec4 1 2 3 4 === abs (i64vec4 (-1) 2 (-3) 4)

prop_vec64_block_layout :: Property
prop_vec64_block_layout = withTests 1 $ property do
  (16, 16) === layout140 (Proxy @I64Vec2)
  (32, 32) === layout140 (Proxy @I64Vec3)
  (32, 24) === layout140 (Proxy @Geomancy.I64Vec3.Packed)
  (32, 32) === layout140 (Proxy @I64Vec4)
  (16, 16) === layout140 (Proxy @U64Vec2)
  (32, 32) === layout140 (Proxy @U64Vec3)
  (32, 24) === layout140 (Proxy @Geomancy.U64Vec3.Packed)
  (32, 32) === layout140 (Proxy @U64Vec4)