summaryrefslogtreecommitdiff
path: root/source/Felix/Test/Unit/Semantic.hs
diff options
context:
space:
mode:
Diffstat (limited to 'source/Felix/Test/Unit/Semantic.hs')
-rw-r--r--source/Felix/Test/Unit/Semantic.hs437
1 files changed, 437 insertions, 0 deletions
diff --git a/source/Felix/Test/Unit/Semantic.hs b/source/Felix/Test/Unit/Semantic.hs
new file mode 100644
index 0000000..f62a003
--- /dev/null
+++ b/source/Felix/Test/Unit/Semantic.hs
@@ -0,0 +1,437 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Felix.Test.Unit.Semantic (unitTests) where
+
+import Base
+import Felix.Checking.Authority qualified as Authority
+import Felix.Checking.Core qualified as Core
+import Felix.Checking.Foundation qualified as Foundation
+import Felix.Checking.Identity qualified as Identity
+import Felix.Checking.Semantic qualified as Semantic
+import Felix.Cache.Codec
+import Felix.Math.Codec
+import Felix.Module
+import Felix.Parsed.Identity
+import Felix.Source
+import Felix.Source.Content
+import Felix.Syntax.Interface qualified as Syntax
+import Felix.Syntax.Abstract qualified as Raw
+
+import Data.ByteString (ByteString)
+import Data.List qualified as List
+import Data.Map.Strict qualified as Map
+import Test.Tasty
+import Test.Tasty.HUnit
+
+
+unitTests :: TestTree
+unitTests =
+ testGroup "Semantic interfaces"
+ [ testCase "separates syntax and semantic Merkle identities"
+ separatesSyntaxAndSemantics
+ , testCase "round-trips closed semantic declaration state"
+ roundTripsSemanticState
+ , testCase "round-trips exact semantic global keys"
+ roundTripsSemanticGlobalKeys
+ , testCase "round-trips canonical structure descriptors"
+ roundTripsStructureDescriptors
+ , testCase "keys exact proof and module inputs"
+ keysExactInputs
+ ]
+
+separatesSyntaxAndSemantics :: Assertion
+separatesSyntaxAndSemantics = do
+ fixture <- makeFixture
+ firstSyntax <- makeSyntax "first"
+ secondSyntax <- makeSyntax "second"
+ assertBool
+ "notation changes syntax identity"
+ ( Syntax.moduleSyntaxAssertedId firstSyntax
+ /= Syntax.moduleSyntaxAssertedId secondSyntax
+ )
+ assertEqual
+ "notation does not enter semantic prefix identity"
+ (fixtureNextPrefix fixture)
+ (Semantic.nextPrefixContextId
+ (fixtureInitialPrefix fixture)
+ (fixtureDelta fixture))
+
+roundTripsSemanticState :: Assertion
+roundTripsSemanticState = do
+ fixture <- makeFixture
+ assertEqual
+ "semantic interface cache round trip"
+ (Right (fixtureInterface fixture))
+ (decodeCache
+ Semantic.getSemanticInterfaceCache
+ (encodeCache
+ (Semantic.putSemanticInterfaceCache
+ (fixtureInterface fixture))))
+ assertEqual
+ "walking-subset environment delta round trip"
+ (Right Semantic.emptySemanticEnvironmentDelta)
+ (decodeCache
+ Semantic.getSemanticEnvironmentDeltaCache
+ (encodeCache
+ (Semantic.putSemanticEnvironmentDeltaCache
+ Semantic.emptySemanticEnvironmentDelta)))
+
+roundTripsSemanticGlobalKeys :: Assertion
+roundTripsSemanticGlobalKeys = do
+ fixture <- makeFixture
+ let unary = Raw.HoleCons (Raw.TokenCons (Raw.Command "f") Raw.End)
+ plural = Raw.HoleCons (Raw.TokenCons (Raw.Word "things") Raw.End)
+ keys =
+ [ Semantic.SemanticLeftAdjective unary
+ , Semantic.SemanticRightAdjective unary
+ , Semantic.SemanticFunctionPhrase unary plural
+ , Semantic.SemanticNoun unary plural
+ , Semantic.SemanticVerb unary plural
+ , Semantic.SemanticRelation
+ (Raw.Command "rel")
+ (Raw.ParameterArity 2)
+ , Semantic.SemanticExpressionFunction unary
+ , Semantic.SemanticPrefixPredicate "Pred" 3
+ ]
+ target =
+ Identity.intrinsicObjectId
+ (fixtureTheory fixture)
+ Core.Empty
+ Core.TySet
+ bindings =
+ List.sortOn Semantic.semanticGlobalBindingKey
+ ( case keys of
+ [] -> []
+ first : rest ->
+ Semantic.semanticGlobalBinding
+ first
+ (Semantic.ContextualTransparentExpansion
+ target
+ (Map.singleton
+ (Raw.StructSymbol "operation")
+ target))
+ : [ Semantic.semanticGlobalBinding
+ key
+ (Semantic.GlobalReference target)
+ | key <- rest
+ ]
+ )
+ delta <- expectRight (Semantic.semanticEnvironmentDelta bindings)
+ assertEqual "binding cache round trip"
+ (Right delta)
+ (decodeCache
+ Semantic.getSemanticEnvironmentDeltaCache
+ (encodeCache
+ (Semantic.putSemanticEnvironmentDeltaCache delta)))
+ case bindings of
+ first : second : _ -> do
+ assertEqual "rejects noncanonical order"
+ (Left Semantic.NonCanonicalSemanticGlobalBindingOrder)
+ (Semantic.semanticEnvironmentDelta
+ (second : first : drop 2 bindings))
+ assertEqual "rejects duplicate key"
+ (Left
+ (Semantic.DuplicateSemanticGlobalKey
+ (Semantic.semanticGlobalBindingKey first)))
+ (Semantic.semanticEnvironmentDelta [first, first])
+ _ -> assertFailure "semantic key fixture is unexpectedly empty"
+
+roundTripsStructureDescriptors :: Assertion
+roundTripsStructureDescriptors = do
+ fixture <- makeFixture
+ let structurePhrase marker word =
+ Semantic.semanticStructurePhrase
+ (Raw.LexicalItemSgPl
+ (Raw.SgPl
+ (Raw.TokenCons (Raw.Word word) Raw.End)
+ (Raw.TokenCons (Raw.Word (word <> "s")) Raw.End))
+ marker)
+ base = structurePhrase "onesorted_structure" "base"
+ child = structurePhrase "ordered_structure" "ordered"
+ object =
+ Identity.intrinsicObjectId
+ (fixtureTheory fixture)
+ Core.Empty
+ Core.TySet
+ operation =
+ Semantic.semanticStructureOperation
+ (Raw.StructSymbol "carrier")
+ object
+ descriptor <- expectRight
+ (Semantic.semanticStructureDescriptor
+ child
+ (Just object)
+ [base]
+ [operation])
+ delta <- expectRight
+ (Semantic.semanticEnvironmentWithStructures [] [descriptor])
+ assertEqual
+ "structure environment cache round trip"
+ (Right delta)
+ (decodeCache
+ Semantic.getSemanticEnvironmentDeltaCache
+ (encodeCache
+ (Semantic.putSemanticEnvironmentDeltaCache delta)))
+ assertEqual
+ "duplicate local operation is rejected"
+ (Left
+ (Semantic.DuplicateSemanticStructureOperation
+ (Raw.StructSymbol "carrier")))
+ (Semantic.semanticStructureDescriptor
+ child
+ (Just object)
+ [base]
+ [operation, operation])
+
+keysExactInputs :: Assertion
+keysExactInputs = do
+ fixture <- makeFixture
+ let authority =
+ Authority.factAuthority
+ (fixtureTheorem fixture)
+ Authority.cleanAuthoritySafety
+ certificate <- expectRight
+ (Authority.validationCertificate
+ authority
+ (Authority.CheckedSourceProof []))
+ let theorem =
+ Identity.theoremId
+ (fixtureTheorem fixture)
+ firstProof =
+ Semantic.proofValidationKey
+ theorem
+ (Semantic.proofSyntaxId "proof-a")
+ (fixtureInitialPrefix fixture)
+ secondProof =
+ Semantic.proofValidationKey
+ theorem
+ (Semantic.proofSyntaxId "proof-b")
+ (fixtureInitialPrefix fixture)
+ laterContext =
+ Semantic.proofValidationKey
+ theorem
+ (Semantic.proofSyntaxId "proof-a")
+ (fixtureNextPrefix fixture)
+ assertBool
+ "proof syntax is an exact validation input"
+ (firstProof /= secondProof)
+ assertBool
+ "semantic predecessor is an exact validation input"
+ (firstProof /= laterContext)
+ let proofRecord =
+ Semantic.proofValidationRecord
+ firstProof certificate
+ declarationKey =
+ Semantic.declarationValidationKey
+ (Semantic.declarationSyntaxId "declaration")
+ (fixtureInitialPrefix fixture)
+ []
+ [theorem, theorem]
+ declarationRecord =
+ Semantic.declarationValidationRecord
+ declarationKey
+ [certificate, certificate]
+ assertEqual
+ "proof validation record cache round trip"
+ (Right proofRecord)
+ (decodeCache
+ Semantic.getProofValidationRecordCache
+ (encodeCache
+ (Semantic.putProofValidationRecordCache
+ proofRecord)))
+ assertEqual
+ "ordered declaration certificates retain repetitions"
+ (Right declarationRecord)
+ (decodeCache
+ Semantic.getDeclarationValidationRecordCache
+ (encodeCache
+ (Semantic.putDeclarationValidationRecordCache
+ declarationRecord)))
+ firstParsedKey <- expectRight
+ (parsedModuleKey
+ (fixtureSourceContentId "source-a")
+ Syntax.baseSyntaxInterfaceId
+ [])
+ secondParsedKey <- expectRight
+ (parsedModuleKey
+ (fixtureSourceContentId "source-b")
+ Syntax.baseSyntaxInterfaceId
+ [])
+ directSyntax <- makeSyntax "direct"
+ let directSyntaxId =
+ Syntax.moduleSyntaxAssertedId directSyntax
+ assertEqual
+ "parsed identity rejects duplicate direct syntax"
+ (Left
+ (DuplicateParsedDirectSyntaxInput
+ directSyntaxId))
+ (parsedModuleKey
+ (fixtureSourceContentId "source-a")
+ Syntax.baseSyntaxInterfaceId
+ [directSyntaxId, directSyntaxId])
+ let
+ firstParsed =
+ parsedModuleId firstParsedKey "parsed"
+ secondParsed =
+ parsedModuleId secondParsedKey "parsed"
+ firstArtifactKey <- expectRight
+ (Semantic.moduleArtifactKey
+ (fixtureOwner fixture)
+ firstParsed
+ []
+ (fixtureTheory fixture))
+ secondArtifactKey <- expectRight
+ (Semantic.moduleArtifactKey
+ (fixtureOwner fixture)
+ secondParsed
+ []
+ (fixtureTheory fixture))
+ let firstArtifact =
+ Semantic.moduleArtifactId firstArtifactKey
+ secondArtifact =
+ Semantic.moduleArtifactId secondArtifactKey
+ assertBool
+ "module artifact binds parsed source identity"
+ (firstArtifact /= secondArtifact)
+ let semanticId =
+ Semantic.semanticInterfaceAssertedId
+ (fixtureInterface fixture)
+ assertEqual
+ "prefix identity rejects duplicate direct semantics"
+ (Left
+ (Semantic.DuplicateInitialPrefixSemanticInput
+ semanticId))
+ (Semantic.initialPrefixContextId
+ (fixtureTheory fixture)
+ (fixtureOwner fixture)
+ [semanticId, semanticId])
+ assertEqual
+ "module artifact key cache round trip"
+ (Right firstArtifactKey)
+ (decodeCache
+ Semantic.getModuleArtifactKeyCache
+ (encodeCache
+ (Semantic.putModuleArtifactKeyCache
+ firstArtifactKey)))
+ syntax <- makeSyntax "artifact"
+ let artifactResult =
+ Semantic.moduleArtifactResult
+ firstArtifactKey
+ (Syntax.moduleSyntaxAssertedId syntax)
+ (Semantic.semanticInterfaceAssertedId
+ (fixtureInterface fixture))
+ assertEqual
+ "module artifact root round trip"
+ (Right artifactResult)
+ (decodeCache
+ (Semantic.getModuleArtifactResultCache
+ firstArtifact)
+ (encodeCache
+ (Semantic.putModuleArtifactResultCache
+ artifactResult)))
+
+
+data Fixture = Fixture
+ { fixtureTheory :: !Identity.TheoryId
+ , fixtureOwner :: !ModuleName
+ , fixtureTheorem :: !Identity.TheoremRef
+ , fixtureDelta :: !Semantic.DeclarationInterfaceDelta
+ , fixtureInterface :: !Semantic.SemanticInterface
+ , fixtureInitialPrefix :: !Semantic.PrefixContextId
+ , fixtureNextPrefix :: !Semantic.PrefixContextId
+ }
+
+makeFixture :: IO Fixture
+makeFixture = do
+ foundation <- expectRight Foundation.checkedFoundation
+ namespaceDigest <- expectRight
+ (hashCanonicalFields
+ "semantic-test-namespace"
+ ["root"])
+ relative <- expectRight (safeRelativePath "module.tex")
+ closure <- expectRight
+ (Identity.validateObjectClosure
+ (Identity.theoryId foundation)
+ [])
+ proposition <- expectRight
+ (Identity.validatePropositionContent
+ closure
+ Core.CFalsum)
+ let theory =
+ Identity.theoryId foundation
+ owner =
+ moduleNameFromParts
+ (sourceNamespaceIdFromDigest namespaceDigest)
+ relative
+ reference =
+ Identity.theoremRef
+ theory
+ (Identity.checkedPropositionId proposition)
+ authority =
+ Authority.factAuthority
+ reference
+ Authority.cleanAuthoritySafety
+ occurrence =
+ Semantic.semanticFactOccurrence
+ (Semantic.factSlot owner (localFactOrdinal 0))
+ authority
+ Semantic.SearchEligible
+ slot =
+ Semantic.declarationSlot
+ owner
+ (localDeclarationOrdinal 0)
+ delta <- expectRight
+ (Semantic.declarationInterfaceDelta
+ slot
+ [occurrence]
+ [ Semantic.semanticAlias
+ (Semantic.semanticName "theorem")
+ (Semantic.semanticFactOccurrenceFingerprint
+ (Semantic.factSlot owner (localFactOrdinal 0))
+ authority)
+ ]
+ []
+ [Identity.checkedPropositionId proposition]
+ Semantic.emptySemanticEnvironmentDelta)
+ interface <- expectRight
+ (Semantic.semanticInterface owner [] [delta])
+ initial <- expectRight
+ (Semantic.initialPrefixContextId theory owner [])
+ pure
+ Fixture
+ { fixtureTheory = theory
+ , fixtureOwner = owner
+ , fixtureTheorem = reference
+ , fixtureDelta = delta
+ , fixtureInterface = interface
+ , fixtureInitialPrefix = initial
+ , fixtureNextPrefix =
+ Semantic.nextPrefixContextId initial delta
+ }
+
+makeSyntax :: Text -> IO Syntax.ModuleSyntaxInterface
+makeSyntax command = do
+ delta <- expectRight
+ (Syntax.canonicalSyntaxDelta
+ [Syntax.CanonicalStructureOperation command])
+ expectRight (Syntax.moduleSyntaxInterface [] delta)
+
+fixtureSourceContentId :: ByteString -> SourceContentId
+fixtureSourceContentId bytes =
+ either
+ (impossible . show)
+ id
+ (decodeCache
+ getSourceContentIdCache
+ (encodeCache
+ (putCacheDigest
+ (hashCacheFields
+ "semantic-test-source"
+ [bytes]))))
+
+expectRight :: Show error => Either error value -> IO value
+expectRight = \case
+ Left err ->
+ assertFailure (show err) >> fail "unreachable"
+ Right value ->
+ pure value