assertica / test /Test /StdLib /Setoid.hs
SNAPKITTYWEST's picture
October 2026 main drop: mirror from GitHub
a10b189 verified
Raw History Blame Contribute Delete
14.5 kB
{-# LANGUAGE OverloadedStrings #-}
{-|
Module : Test.StdLib.Setoid
Description : Comprehensive tests for setoid structure
Copyright : (c) 2026 Ahmad Ali Parr
-}
module Test.StdLib.Setoid
( tests
) where
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck as QC
import qualified Data.Set as Set
import Assertica.Core.AST
import Assertica.Core.ProofTerm
import Assertica.StdLib.Setoid
-- ============================================================================
-- UNIT TESTS: SETOID STRUCTURE
-- ============================================================================
tests :: TestTree
tests = testGroup "Assertica.StdLib.Setoid"
[ testGroup "Trivial Setoid"
[ testCase "Trivial setoid carrier is Unit" testTrivialCarrier
, testCase "Trivial setoid reflexivity" testTrivialRefl
, testCase "Trivial setoid symmetry" testTrivialSymm
, testCase "Trivial setoid transitivity" testTrivialTrans
, testCase "Trivial setoid all elements equivalent" testTrivialAllEq
]
, testGroup "Discrete Setoid"
[ testCase "Discrete setoid reflexivity" testDiscreteRefl
, testCase "Discrete setoid symmetry" testDiscreteSymm
, testCase "Discrete setoid transitivity" testDiscreteTrans
]
, testGroup "Product Setoid"
[ testCase "Product setoid carrier" testProductCarrier
, testCase "Product setoid reflexivity" testProductRefl
, testCase "Product setoid pair equality" testProductPairEq
, testCase "Product setoid commutativity" testProductComm
]
, testGroup "Setoid Morphisms"
[ testCase "Identity morphism preserves equivalence" testIdMorphismPreserves
, testCase "Identity morphism is left identity" testIdMorphismLeftId
, testCase "Identity morphism is right identity" testIdMorphismRightId
, testCase "Morphism composition" testMorphismComposition
, testCase "Composed morphism preserves equivalence" testComposedMorphismPreserves
]
, testGroup "Proof Terms"
[ testCase "Reflexivity proof checks" testReflProofChecks
, testCase "Symmetry proof checks" testSymmProofChecks
, testCase "Transitivity proof checks" testTransProofChecks
]
, testGroup "Equivalence Laws"
[ testCase "Reflexivity law holds" testReflLaw
, testCase "Symmetry law holds" testSymmLaw
, testCase "Transitivity law holds" testTransLaw
, testCase "Equivalence is reflexive everywhere" testReflEvery
, testCase "Equivalence is symmetric everywhere" testSymmEvery
, testCase "Equivalence is transitive everywhere" testTransEvery
]
, testGroup "Setoid Morphism Laws"
[ testCase "Morphism preserves reflexivity" testMorphismReflPreserved
, testCase "Morphism preserves symmetry" testMorphismSymmPreserved
, testCase "Morphism preserves transitivity" testMorphismTransPreserved
]
, testGroup "Determinism Tests"
[ testCase "Equivalence check is deterministic" testDeterminism
, testCase "Morphism application is deterministic" testMorphismDeterminism
]
, testGroup "Negative Tests"
[ testCase "Reflexivity fails for unequal terms" testReflFails
, testCase "Symmetry proof is not involutive" testSymmNotInvolutive
]
]
-- ============================================================================
-- TRIVIAL SETOID TESTS
-- ============================================================================
testTrivialCarrier :: Assertion
testTrivialCarrier =
let s = trivialSetoid
in setoidCarrier s @?= TConst (QName [] "Unit")
testTrivialRefl :: Assertion
testTrivialRefl =
let s = trivialSetoid
proof = setoidRefl s (Const UnitConst)
in proof @?= Refl
testTrivialSymm :: Assertion
testTrivialSymm =
let s = trivialSetoid
innerProof = setoidRefl s (Const UnitConst)
proof = setoidSymm s (Const UnitConst) (Const UnitConst) innerProof
in proof @?= Refl
testTrivialTrans :: Assertion
testTrivialTrans =
let s = trivialSetoid
proof1 = setoidRefl s (Const UnitConst)
proof2 = setoidRefl s (Const UnitConst)
proof = setoidTrans s (Const UnitConst) (Const UnitConst) (Const UnitConst) proof1 proof2
in proof @?= Trans Refl Refl
testTrivialAllEq :: Assertion
testTrivialAllEq =
let s = trivialSetoid
prop1 = setoidEq s (Const UnitConst) (Const UnitConst)
prop2 = setoidEq s (Const UnitConst) (Const UnitConst)
in prop1 @?= prop2
-- ============================================================================
-- DISCRETE SETOID TESTS
-- ============================================================================
testDiscreteRefl :: Assertion
testDiscreteRefl =
let s = discreteSetoid
a = Const (IntLit 42)
proof = setoidRefl s a
in proof @?= Refl
testDiscreteSymm :: Assertion
testDiscreteSymm =
let s = discreteSetoid
a = Const (IntLit 42)
innerProof = Refl
proof = setoidSymm s a a innerProof
in proof @?= Symm Refl
testDiscreteTrans :: Assertion
testDiscreteTrans =
let s = discreteSetoid
a = Const (IntLit 42)
proof1 = Refl
proof2 = Refl
proof = setoidTrans s a a a proof1 proof2
in proof @?= Trans Refl Refl
-- ============================================================================
-- PRODUCT SETOID TESTS
-- ============================================================================
testProductCarrier :: Assertion
testProductCarrier =
let s1 = trivialSetoid
s2 = discreteSetoid
prod = productSetoid s1 s2
carrier = setoidCarrier prod
in case carrier of
TPair {} -> assertBool "Product carrier is a pair" True
_ -> assertFailure "Product carrier should be a pair"
testProductRefl :: Assertion
testProductRefl =
let s1 = trivialSetoid
s2 = discreteSetoid
prod = productSetoid s1 s2
pair = TPair (Const UnitConst) (Const (IntLit 1))
proof = setoidRefl prod pair
in proof @?= Refl
testProductPairEq :: Assertion
testProductPairEq =
let s1 = trivialSetoid
s2 = discreteSetoid
prod = productSetoid s1 s2
a1 = Const UnitConst
b1 = Const (IntLit 1)
a2 = Const UnitConst
b2 = Const (IntLit 1)
p1 = TPair a1 b1
p2 = TPair a2 b2
prop = setoidEq prod p1 p2
in case prop of
ConjunctionProp {} -> assertBool "Product equality is a conjunction" True
_ -> assertFailure "Product equality should be a conjunction"
testProductComm :: Assertion
testProductComm =
let s1 = trivialSetoid
s2 = discreteSetoid
prod = productSetoid s1 s2
p1 = TPair (Const UnitConst) (Const (IntLit 1))
p2 = TPair (Const UnitConst) (Const (IntLit 1))
proof1 = setoidSymm prod p1 p2 Refl
proof2 = setoidSymm prod p1 p2 Refl
in proof1 @?= proof2
-- ============================================================================
-- SETOID MORPHISM TESTS
-- ============================================================================
testIdMorphismPreserves :: Assertion
testIdMorphismPreserves =
let s = trivialSetoid
morph = idMorphism s
a = Const UnitConst
b = Const UnitConst
eqProof = Refl
result = morphismPreservesEq morph a b eqProof
in result @?= Refl
testIdMorphismLeftId :: Assertion
testIdMorphismLeftId =
let s = discreteSetoid
id_s = idMorphism s
a = Const (IntLit 42)
fa = morphismMap id_s a
in fa @?= a
testIdMorphismRightId :: Assertion
testIdMorphismRightId =
let s = discreteSetoid
id_s = idMorphism s
b = Const (IntLit 42)
gb = morphismMap id_s b
in gb @?= b
testMorphismComposition :: Assertion
testMorphismComposition =
let s1 = trivialSetoid
s2 = discreteSetoid
s3 = trivialSetoid
f = SetoidMorphism
{ morphismSource = s1
, morphismTarget = s2
, morphismMap = \a -> Const (IntLit 1)
, morphismProof = \_ _ _ -> Refl
}
g = SetoidMorphism
{ morphismSource = s2
, morphismTarget = s3
, morphismMap = \_ -> Const UnitConst
, morphismProof = \_ _ _ -> Refl
}
composed = composeMorphisms f g
in morphismSource composed @?= s1 && morphismTarget composed @?= s3
testComposedMorphismPreserves :: Assertion
testComposedMorphismPreserves =
let s1 = trivialSetoid
s2 = discreteSetoid
s3 = trivialSetoid
f = idMorphism s2
g = idMorphism s3
composed = composeMorphisms f g
a = Const (IntLit 42)
b = Const (IntLit 42)
eqProof = Refl
result = morphismPreservesEq composed a b eqProof
in result @?= Refl
-- ============================================================================
-- PROOF TERM TESTS
-- ============================================================================
testReflProofChecks :: Assertion
testReflProofChecks =
let s = discreteSetoid
a = Const (IntLit 42)
proof = setoidRefl s a
prop = EqualityProp a a
result = proofChecks proof prop
in case result of
Right () -> assertBool "Reflexivity proof checks" True
Left err -> assertFailure $ "Proof should check: " ++ show err
testSymmProofChecks :: Assertion
testSymmProofChecks =
let s = discreteSetoid
a = Const (IntLit 42)
innerProof = Refl
proof = setoidSymm s a a innerProof
prop = EqualityProp a a
result = proofChecks proof prop
in case result of
Right () -> assertBool "Symmetry proof checks" True
Left err -> assertFailure $ "Proof should check: " ++ show err
testTransProofChecks :: Assertion
testTransProofChecks =
let s = discreteSetoid
a = Const (IntLit 42)
proof1 = Refl
proof2 = Refl
proof = setoidTrans s a a a proof1 proof2
prop = EqualityProp a a
result = proofChecks proof prop
in case result of
Right () -> assertBool "Transitivity proof checks" True
Left err -> assertFailure $ "Proof should check: " ++ show err
-- ============================================================================
-- EQUIVALENCE LAW TESTS
-- ============================================================================
testReflLaw :: Assertion
testReflLaw =
let s = discreteSetoid
a = Const (IntLit 42)
proof = setoidRefl s a
prop = EqualityProp a a
in proofChecks proof prop `shouldSucceed` ()
testSymmLaw :: Assertion
testSymmLaw =
let s = discreteSetoid
a = Const (IntLit 42)
proof1 = setoidRefl s a
proof2 = setoidSymm s a a proof1
prop = EqualityProp a a
in proofChecks proof2 prop `shouldSucceed` ()
testTransLaw :: Assertion
testTransLaw =
let s = discreteSetoid
a = Const (IntLit 42)
proof1 = setoidRefl s a
proof2 = setoidRefl s a
proof3 = setoidTrans s a a a proof1 proof2
prop = EqualityProp a a
in proofChecks proof3 prop `shouldSucceed` ()
testReflEvery :: Assertion
testReflEvery =
let s = discreteSetoid
elements = [Const (IntLit 1), Const (IntLit 2), Const UnitConst]
results = map (\a -> (a, setoidRefl s a)) elements
in all (\(_, proof) -> proofChecks proof (EqualityProp (Const (IntLit 1)) (Const (IntLit 1))) == Right ())
results
@? "All elements satisfy reflexivity"
testSymmEvery :: Assertion
testSymmEvery =
let s = discreteSetoid
a = Const (IntLit 42)
proof1 = Refl
proof2 = setoidSymm s a a proof1
in proofChecks proof2 (EqualityProp a a) `shouldSucceed` ()
testTransEvery :: Assertion
testTransEvery =
let s = discreteSetoid
a = Const (IntLit 42)
proof1 = Refl
proof2 = Refl
proof3 = setoidTrans s a a a proof1 proof2
in proofChecks proof3 (EqualityProp a a) `shouldSucceed` ()
-- ============================================================================
-- MORPHISM LAW TESTS
-- ============================================================================
testMorphismReflPreserved :: Assertion
testMorphismReflPreserved =
let s1 = discreteSetoid
s2 = discreteSetoid
morph = idMorphism s1
a = Const (IntLit 42)
reflProof = setoidRefl s1 a
result = morphismPreservesEq morph a a reflProof
in result @?= Refl
testMorphismSymmPreserved :: Assertion
testMorphismSymmPreserved =
let s1 = discreteSetoid
morph = idMorphism s1
a = Const (IntLit 42)
symmProof = Symm Refl
result = morphismPreservesEq morph a a symmProof
in result @?= Symm Refl
testMorphismTransPreserved :: Assertion
testMorphismTransPreserved =
let s1 = discreteSetoid
morph = idMorphism s1
a = Const (IntLit 42)
transProof = Trans Refl Refl
result = morphismPreservesEq morph a a transProof
in result @?= Trans Refl Refl
-- ============================================================================
-- DETERMINISM TESTS
-- ============================================================================
testDeterminism :: Assertion
testDeterminism =
let s = discreteSetoid
a = Const (IntLit 42)
proof1 = setoidRefl s a
proof2 = setoidRefl s a
in proof1 @?= proof2
testMorphismDeterminism :: Assertion
testMorphismDeterminism =
let s = discreteSetoid
morph = idMorphism s
a = Const (IntLit 42)
result1 = morphismMap morph a
result2 = morphismMap morph a
in result1 @?= result2
-- ============================================================================
-- NEGATIVE TESTS
-- ============================================================================
testReflFails :: Assertion
testReflFails =
let a = Const (IntLit 42)
b = Const (IntLit 43)
proof = Refl
prop = EqualityProp a b
result = proofChecks proof prop
in case result of
Left _ -> assertBool "Reflexivity correctly fails" True
Right () -> assertFailure "Reflexivity should fail for unequal terms"
testSymmNotInvolutive :: Assertion
testSymmNotInvolutive =
let proof1 = Refl
proof2 = Symm proof1
proof3 = Symm proof2
in proof3 @?= Refl
-- ============================================================================
-- HELPER FUNCTIONS
-- ============================================================================
shouldSucceed :: Either e a -> a -> Assertion
shouldSucceed (Right x) expected = x @?= expected
shouldSucceed (Left err) _ = assertFailure $ "Expected success, got error: " ++ show err
-- Alias for property tests
_propTest :: String -> Bool -> TestTree
_propTest name prop = testCase name $ assertBool name prop