packages feed

hasktorch-indef-0.0.1.0: tests/Torch/Indef/StorageSpec.hs

{-# LANGUAGE ScopedTypeVariables #-}
module Torch.Indef.StorageSpec where

import Control.Monad
import GHC.Int
import Test.Hspec
import Test.Hspec.QuickCheck hiding (resize)
import Test.QuickCheck ((.&&.), (==>), (===), property, Arbitrary(..))
import qualified Test.QuickCheck as QC
import Test.QuickCheck.Monadic
import GHC.Exts
import Debug.Trace
import Control.Monad.IO.Class
import Data.List
import Data.Word
import GHC.Natural

import Torch.Indef.Storage

data ListWithIx a = ListWithIx Word [a]
  deriving (Eq, Show)

instance Arbitrary a => Arbitrary (ListWithIx a) where
  arbitrary = do
    xs <- QC.listOf1 arbitrary
    ix <- QC.choose (0, genericLength xs - 1)
    pure $ ListWithIx ix xs

data ListWithOOB a = ListWithOOB Word [a]
  deriving (Eq, Show)

instance Arbitrary a => Arbitrary (ListWithOOB a) where
  arbitrary = do
    xs <- QC.listOf1 arbitrary
    ix <- QC.choose (genericLength xs, genericLength xs * 5)
    pure $ ListWithOOB ix xs

main :: IO ()
main = hspec spec

spec :: Spec
spec = do
  describe "fromList / storagedata" $ do
    let
      xs = [0..500]
      st = fromList xs

    it "is instantiated with an offset of 0" $
      size st `shouldBe` length xs

    it "returns xs for storagedata" $
      storagedata st `shouldBe` xs

    it ("can access the first 50 elements by `get`") $ do
      forM_ [0..50] $ \i ->
        get st i `shouldBe` fromIntegral i

    it "should continue to be consistent after gets" $
      storagedata st `shouldBe` xs

  prop "fromList / toList should form the identity" $ \xs ->
    toList (fromList xs :: Storage) `shouldBe` xs

  prop "size should be the identical to the length" $ \xs ->
    size (fromList xs) `shouldBe` (length xs)

  describe "set / get" $ do
    it "can update any index" $ property $ \ (x, ListWithIx i xs) -> do
      let st = fromList xs
      get st (fromIntegral i) /= x ==> do
        set st (fromIntegral i) x
        get st (fromIntegral i) `shouldBe` x

  describe "empty" $ do
    it "has a size of 0" $ do
      let st = empty
      size empty `shouldBe` 0

  prop "newWithSize returns a size of input argument" $ \(i::Word16) ->
    size (newWithSize $ fromIntegral i) `shouldBe` fromIntegral i

  prop "newWithSize1 has size 1 and sets the argument" $ \v0 ->
    let st = newWithSize1 v0
    in size st === 1 .&&. get st 0 === v0

  prop "newWithSize2 has size 2 and sets the arguments" $ \v0 v1 ->
    let st = newWithSize2 v0 v1
    in   size st === 2
    .&&. get st 0 === v0
    .&&. get st 1 === v1

  prop "newWithSize3 has size 3 and sets the arguments" $ \v0 v1 v2 ->
    let st = newWithSize3 v0 v1 v2
    in   size st === 3
    .&&. get st 0 === v0
    .&&. get st 1 === v1
    .&&. get st 2 === v2

  prop "newWithSize4 has size 4 and sets the arguments" $ \v0 v1 v2 v3 ->
    let st = newWithSize4 v0 v1 v2 v3
    in   size st === 4
    .&&. get st 0 === v0
    .&&. get st 1 === v1
    .&&. get st 2 === v2
    .&&. get st 3 === v3

  prop "fill will overwrite the storage contents" $ \xs v ->
    length xs > 1 ==> do
    let st = fromList xs
    toList st `shouldBe` xs
    fill st v
    toList st `shouldSatisfy` (all (== v))

  describe "resize" $ do
    prop "will truncate storage if the new size is less than the old one (old size < 100)" $
      \(ListWithIx i xs) -> length xs < 100 ==> do
        let st = fromList xs
        resize st (fromIntegral i)
        toList st `shouldBe` take (fromIntegral i) xs

    prop "will expand storage if the new size is greater than the old one (new size <100)" $
      \(ListWithOOB i xs) -> i < 100 ==> do
        let st = fromList xs
        resize st (fromIntegral i)
        take (length xs) (toList st) `shouldBe` xs