symplectic-chp-0.1.0.0: test/Test/UtilsSpec.hs
module Test.UtilsSpec where
import Test.Hspec
import Test.QuickCheck
import SymplecticCHP ((//))
import qualified Data.List as L
spec :: Spec
spec = describe "SymplecticCHP.Utils" $ do
describe "(//) list update operator" $ do
it "basic example from Vector semantics" $ do
let xs = [5,9,2,7 :: Int]
updates = [(2,1),(0,3),(2,8)]
xs // updates `shouldBe` [3,9,8,7]
it "handles empty update list" $ do
let xs = [1,2,3 :: Int]
xs // [] `shouldBe` xs
it "ignores out-of-bounds indices (too large)" $ do
let xs = [1,2,3 :: Int]
xs // [(5,99)] `shouldBe` xs
it "ignores negative indices" $ do
let xs = [1,2,3 :: Int]
xs // [(-1,99)] `shouldBe` xs
it "later update wins for same index" $ do
let xs = [1,2,3 :: Int]
xs // [(0,10),(0,20)] `shouldBe` [20,2,3]
xs // [(1,5),(1,6),(1,7)] `shouldBe` [1,7,3]
it "handles updates in any order" $ do
let xs = [0,0,0,0 :: Int]
updates1 = [(0,1),(2,3)]
updates2 = [(2,3),(0,1)] -- reversed
xs // updates1 `shouldBe` xs // updates2
it "handles single element list" $ do
let xs = [42 :: Int]
xs // [(0,100)] `shouldBe` [100]
xs // [(1,100)] `shouldBe` [42] -- out of bounds
it "handles all indices updated" $ do
let xs = [1,2,3 :: Int]
xs // [(0,10),(1,20),(2,30)] `shouldBe` [10,20,30]
describe "(//) property tests" $ do
it "preserves list length" $ do
property $ \xs updates ->
let xs' :: [Int]
xs' = take 50 xs
updates' :: [(Int, Int)]
updates' = take 20 updates
result = xs' // updates'
in length result === length xs'
it "is idempotent when applying same updates twice" $ do
property $ \xs updates ->
let xs' :: [Int]
xs' = take 30 xs
updates' :: [(Int, Int)]
updates' = [(i `mod` 40, v) | (i, v) <- take 10 updates, let ii = i `mod` 40, ii >= 0]
xs'' = xs' // updates'
in xs'' // updates' === xs''
it "lookup finds updated values correctly" $ do
property $ \xs updates (idx :: Int) ->
let xs' :: [Int]
xs' = take 20 xs
len = length xs'
in len > 0 ==> -- GUARD: ensure non-empty list
let -- Normalize indices to valid range [0, len-1]
updatesNorm = [(i `mod` len, v) | (i, v) <- take 10 updates]
-- Build effective update map: for each index, find LAST value
-- This MUST match what (//) does internally
effectiveValue i =
case [v | (ii, v) <- reverse updatesNorm, ii == i] of
(v:_) -> Just v -- first in reversed = last in original
[] -> Nothing
result = xs' // updatesNorm
i = idx `mod` len
in case effectiveValue i of
Just v -> result L.!! i === v
Nothing -> result L.!! i === (xs' L.!! i)
it "later updates override earlier ones" $ do
property $ \xs ->
let xs' :: [Int]
xs' = take 10 xs
updates :: [(Int, Int)]
updates = [(0, 1), (0, 2), (0, 3)]
in xs' // updates === (xs' // [(0, 3)] :: [Int])
it "empty updates is identity" $ do
property $ \xs ->
let xs' :: [Int] = take 100 xs
in xs' // [] === xs'