Useful but limited polymorphism
This commit is contained in:
354
test/Spec.hs
354
test/Spec.hs
@@ -25,7 +25,7 @@ import System.FilePath ((</>))
|
||||
import Data.Bits (xor)
|
||||
import Data.Char (digitToInt)
|
||||
import Data.List (find, isInfixOf)
|
||||
import Data.Text (Text, unpack)
|
||||
import Data.Text (Text, unpack, pack)
|
||||
import Data.Word (Word8)
|
||||
import Test.Tasty
|
||||
import Test.Tasty.HUnit
|
||||
@@ -77,25 +77,25 @@ allTestLibsEnv = unsafePerformIO $ do
|
||||
tests :: TestTree
|
||||
tests = testGroup "Tricu Tests"
|
||||
[ lexer
|
||||
, parser
|
||||
, simpleEvaluation
|
||||
, lambdas
|
||||
, providedLibraries
|
||||
, maybeTests
|
||||
, fileEval
|
||||
, demos
|
||||
, decoding
|
||||
, elimLambdaSingle
|
||||
, stressElimLambda
|
||||
, byteMarshallingTests
|
||||
, wireTests
|
||||
, tricuReaderTests
|
||||
, byteListUtilities
|
||||
, binaryParserTests
|
||||
, httpParsingTests
|
||||
, contentStoreTests
|
||||
--, parser
|
||||
--, simpleEvaluation
|
||||
--, lambdas
|
||||
--, providedLibraries
|
||||
--, maybeTests
|
||||
--, fileEval
|
||||
--, demos
|
||||
--, decoding
|
||||
--, elimLambdaSingle
|
||||
--, stressElimLambda
|
||||
--, byteMarshallingTests
|
||||
--, wireTests
|
||||
--, tricuReaderTests
|
||||
--, byteListUtilities
|
||||
--, binaryParserTests
|
||||
--, httpParsingTests
|
||||
--, contentStoreTests
|
||||
, viewContractTests
|
||||
, ioDriverTests
|
||||
--, ioDriverTests
|
||||
]
|
||||
|
||||
lexer :: TestTree
|
||||
@@ -1569,10 +1569,11 @@ contentStoreTests = testGroup "Content Store Tests"
|
||||
(ObjectRef (unDomain treeTermDomain) "222")
|
||||
"arboricx.abi.tree.v1"
|
||||
(Just (ObjectRef viewContractTypeKind "333"))
|
||||
(Just ViewChecked)
|
||||
]
|
||||
encoded = encodeManifest manifest
|
||||
decodeManifest encoded @?= Right manifest
|
||||
hashObject manifestDomain encoded @?= "7c3cb85454744894a403d2d12c7ece6d391c0cfbeb4bf3adfc7e69ae70ec4f5c"
|
||||
hashObject manifestDomain encoded @?= "1392e0d406d5d1f2e013b0bff27ec3def4f68c045c75780ccb0380a1995f42c7"
|
||||
|
||||
, testCase "View Contract type artifacts: encode/decode round trip" $ do
|
||||
let view = VTFn [VTList (VTName "String"), VTPair (VTName "Byte") (VTMaybe (VTRef 7))]
|
||||
@@ -1583,6 +1584,11 @@ contentStoreTests = testGroup "Content Store Tests"
|
||||
let view = VTFn [VTRefText "Nat"] (VTPair (VTRefText "Box") (VTName "String"))
|
||||
decodeViewType (encodeViewType view) @?= Right view
|
||||
|
||||
, testCase "View Contract type artifacts: encode/decode quantified views" $ do
|
||||
let view = VTForall [0] (VTFn [VTVar 0] (VTVar 0))
|
||||
decodeViewType (encodeViewType view) @?= Right view
|
||||
treeToViewType (viewTypeToTree view) @?= Right view
|
||||
|
||||
, testCase "View Contract type artifacts: encode/decode guarded views with opaque guard trees" $ do
|
||||
let guardTree = Fork (Stem Leaf) Leaf
|
||||
view = VTGuarded (VTRefText "UserId") guardTree
|
||||
@@ -1615,6 +1621,7 @@ contentStoreTests = testGroup "Content Store Tests"
|
||||
(ObjectRef (unDomain treeTermDomain) root)
|
||||
"arboricx.abi.tree.v1"
|
||||
Nothing
|
||||
Nothing
|
||||
]
|
||||
root <- putTreeTerm store term
|
||||
h <- putManifest store (manifestFor root)
|
||||
@@ -1632,6 +1639,7 @@ contentStoreTests = testGroup "Content Store Tests"
|
||||
(ObjectRef (unDomain treeTermDomain) termH)
|
||||
"arboricx.abi.tree.v1"
|
||||
Nothing
|
||||
Nothing
|
||||
]
|
||||
manifestBytes = encodeManifest manifest
|
||||
manifestH = hashObject manifestDomain manifestBytes
|
||||
@@ -1896,6 +1904,7 @@ contentStoreTests = testGroup "Content Store Tests"
|
||||
(ObjectRef (unDomain treeTermDomain) root)
|
||||
"arboricx.abi.tree.v1"
|
||||
Nothing
|
||||
Nothing
|
||||
]
|
||||
root <- putTreeTerm store term
|
||||
manifestHash <- putManifest store (manifestFor root)
|
||||
@@ -1928,7 +1937,7 @@ contentStoreTests = testGroup "Content Store Tests"
|
||||
, testCase "Module resolver diagnostics: missing tree term names export and hash" $ do
|
||||
let root = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
manifest = ModuleManifest []
|
||||
[ ModuleExport "value" (ObjectRef (unDomain treeTermDomain) root) "arboricx.abi.tree.v1" Nothing ]
|
||||
[ ModuleExport "value" (ObjectRef (unDomain treeTermDomain) root) "arboricx.abi.tree.v1" Nothing Nothing ]
|
||||
resolver = ObjectResolver
|
||||
{ resolverAlias = \kind name -> return $ if kind == ModuleAlias && name == "demo"
|
||||
then Just (ObjectRef (unDomain manifestDomain) "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
|
||||
@@ -2762,7 +2771,7 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
, testCase "Portable View Contract self-tests all pass" $ do
|
||||
let input = "viewContractSelfTests"
|
||||
env = evalTricu allTestLibsEnv (parseTricu input)
|
||||
result env @?= ofList (replicate 32 (ofString "ok"))
|
||||
result env @?= ofList (replicate 35 (ofString "ok"))
|
||||
|
||||
, testCase "Structured diagnostic tag reports required-view failures" $ do
|
||||
let input = "checkerResultErrorTag (checkTypedProgramWith policyStrict listMapWrongOutputContract)"
|
||||
@@ -2812,25 +2821,25 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
output @?= "symbol 1 (external bar) expected Fn [Bool] Bool but got Any"
|
||||
|
||||
, testCase "tricu check accepts trusted imported View Contract facts" $ do
|
||||
let imported = [ImportedView "Ext.id" (VTFn [VTName "Bool"] (VTName "Bool"))]
|
||||
let imported = [ImportedView "Ext.id" (VTFn [VTName "Bool"] (VTName "Bool")) ViewChecked]
|
||||
output <- checkSourceWithEnvAndImportedViews allTestLibsEnv imported "foo x@Bool =@Bool Ext.id x\n"
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "tricu check judges imported View Contract facts in checker" $ do
|
||||
let imported = [ImportedView "Ext.id" (VTFn [VTName "Bool"] (VTName "String"))]
|
||||
let imported = [ImportedView "Ext.id" (VTFn [VTName "Bool"] (VTName "String")) ViewChecked]
|
||||
output <- checkSourceWithEnvAndImportedViews allTestLibsEnv imported "foo x@Bool =@Bool Ext.id x\n"
|
||||
output @?= "symbol 3 (Ext.id application result) expected Bool but got String"
|
||||
|
||||
, testCase "tricu lower emits imported View Contract facts as view-tree nodes" $ do
|
||||
let imported = [ImportedView "Ext.id" (VTFn [VTName "Bool"] (VTName "Bool"))]
|
||||
let imported = [ImportedView "Ext.id" (VTFn [VTName "Bool"] (VTName "Bool")) ViewChecked]
|
||||
case lowerSourceWithImportedViews imported "foo x@Bool =@Bool Ext.id x\n" of
|
||||
Left err -> assertFailure err
|
||||
Right lowered -> lowered @?= "typedProgram 3 [(typedValue 1 (viewFn [(viewBool)] (viewBool)) t) (typedValue 0 (viewFn [(viewBool)] (viewBool)) t) (typedValue 2 (viewBool) t) (typedRequire 2 (viewBool) t) (typedApply 3 1 2 t) (typedRequire 3 (viewBool) t)]"
|
||||
Right lowered -> lowered @?= "typedProgram 3 [(typedValueWithProvenance 1 (viewFn [(viewBool)] (viewBool)) t viewProvenanceChecked) (typedValueWithProvenance 0 (viewFn [(viewBool)] (viewBool)) t viewProvenanceChecked) (typedValueWithProvenance 2 (viewBool) t viewProvenanceChecked) (typedRequire 2 (viewBool) t) (typedApply 3 1 2 t) (typedRequire 3 (viewBool) t)]"
|
||||
|
||||
, testCase "tricu lower emits symbolic View Contract refs in view-tree nodes" $ do
|
||||
case lowerSource "foo x@(Ref \"UserId\") =@(Ref \"UserId\") x\n" of
|
||||
Left err -> assertFailure err
|
||||
Right lowered -> lowered @?= "typedProgram 1 [(typedValue 0 (viewFn [(viewRef \"UserId\")] (viewRef \"UserId\")) t) (typedValue 1 (viewRef \"UserId\") t) (typedRequire 1 (viewRef \"UserId\") t)]"
|
||||
Right lowered -> lowered @?= "typedProgram 1 [(typedValueWithProvenance 0 (viewFn [(viewRef \"UserId\")] (viewRef \"UserId\")) t viewProvenanceChecked) (typedValueWithProvenance 1 (viewRef \"UserId\") t viewProvenanceChecked) (typedRequire 1 (viewRef \"UserId\") t)]"
|
||||
|
||||
, testCase "tricu check converts resolved module export views into imported facts" $ do
|
||||
let viewRef = ObjectRef viewContractTypeKind "abc123"
|
||||
@@ -2840,6 +2849,7 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
, resolvedExportObject = ObjectRef (unDomain treeTermDomain) "def456"
|
||||
, resolvedExportAbi = "arboricx.abi.tree.v1"
|
||||
, resolvedExportView = Just viewRef
|
||||
, resolvedExportProvenance = Just ViewChecked
|
||||
, resolvedExportTerm = Leaf
|
||||
}
|
||||
resolvedModule = ResolvedModule "ext" "Ext" "manifest-hash" [resolvedExport]
|
||||
@@ -2847,10 +2857,28 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
then Just (VTFn [VTName "Bool"] (VTName "Bool"))
|
||||
else Nothing
|
||||
imported <- importedViewsFromResolvedModules loadView [resolvedModule]
|
||||
imported @?= [ImportedView "Ext.id" (VTFn [VTName "Bool"] (VTName "Bool"))]
|
||||
imported @?= [ImportedView "Ext.id" (VTFn [VTName "Bool"] (VTName "Bool")) ViewChecked]
|
||||
output <- checkSourceWithEnvAndImportedViews allTestLibsEnv imported "foo x@Bool =@Bool Ext.id x\n"
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "tricu check marks missing import provenance as unchecked" $ do
|
||||
let viewRef = ObjectRef viewContractTypeKind "abc123"
|
||||
resolvedExport = ResolvedExport
|
||||
{ resolvedExportSourceName = "id"
|
||||
, resolvedExportLocalName = "Ext.id"
|
||||
, resolvedExportObject = ObjectRef (unDomain treeTermDomain) "def456"
|
||||
, resolvedExportAbi = "arboricx.abi.tree.v1"
|
||||
, resolvedExportView = Just viewRef
|
||||
, resolvedExportProvenance = Nothing
|
||||
, resolvedExportTerm = Leaf
|
||||
}
|
||||
resolvedModule = ResolvedModule "ext" "Ext" "manifest-hash" [resolvedExport]
|
||||
loadView ref = pure $ if ref == viewRef
|
||||
then Just (VTFn [VTName "Bool"] (VTName "Bool"))
|
||||
else Nothing
|
||||
imported <- importedViewsFromResolvedModules loadView [resolvedModule]
|
||||
imported @?= [ImportedView "Ext.id" (VTFn [VTName "Bool"] (VTName "Bool")) ViewUnchecked]
|
||||
|
||||
, testCase "tricu check reports missing resolved View Contract artifacts" $ do
|
||||
let viewRef = ObjectRef viewContractTypeKind "abc123"
|
||||
resolvedExport = ResolvedExport
|
||||
@@ -2859,6 +2887,7 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
, resolvedExportObject = ObjectRef (unDomain treeTermDomain) "def456"
|
||||
, resolvedExportAbi = "arboricx.abi.tree.v1"
|
||||
, resolvedExportView = Just viewRef
|
||||
, resolvedExportProvenance = Just ViewChecked
|
||||
, resolvedExportTerm = Leaf
|
||||
}
|
||||
resolvedModule = ResolvedModule "ext" "Ext" "manifest-hash" [resolvedExport]
|
||||
@@ -3018,7 +3047,7 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
assertBool "expected String payload requirement" $
|
||||
"typedRequire 1 (viewString)" `isInfixOf` lowered
|
||||
assertBool "expected Maybe String constructor declaration" $
|
||||
"typedValue 2 (viewMaybe (viewString))" `isInfixOf` lowered
|
||||
"typedValueWithProvenance 2 (viewMaybe (viewString))" `isInfixOf` lowered
|
||||
|
||||
, testCase "tricu check lowerSource emits expected Fn argument typed nodes" $ do
|
||||
case lowerSource "f x@String =@String x\ny =@String f 1\n" of
|
||||
@@ -3032,7 +3061,7 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
Left err -> assertFailure err
|
||||
Right lowered -> do
|
||||
assertBool "expected lambda binder declaration" $
|
||||
"typedValue 1 (viewString) t" `isInfixOf` lowered
|
||||
"typedValueWithProvenance 1 (viewString) t viewProvenanceChecked" `isInfixOf` lowered
|
||||
assertBool "expected lambda body requirement" $
|
||||
"typedRequire 1 (viewString) t" `isInfixOf` lowered
|
||||
|
||||
@@ -3041,9 +3070,9 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
Left err -> assertFailure err
|
||||
Right lowered -> do
|
||||
assertBool "expected Byte evidence for literal element" $
|
||||
"typedValue 1 (viewByte)" `isInfixOf` lowered
|
||||
"typedValueWithProvenance 1 (viewByte)" `isInfixOf` lowered
|
||||
assertBool "expected actual Byte tree payload for literal element" $
|
||||
"typedValue 1 (viewByte) (t (t t) t)" `isInfixOf` lowered
|
||||
"typedValueWithProvenance 1 (viewByte) (t (t t) t) viewProvenanceChecked" `isInfixOf` lowered
|
||||
assertBool "expected String requirement for list element" $
|
||||
"typedRequire 1 (viewString)" `isInfixOf` lowered
|
||||
|
||||
@@ -3061,7 +3090,7 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
Left err -> assertFailure err
|
||||
Right lowered -> do
|
||||
assertBool "expected callback lambda declaration" $
|
||||
"typedValue 12 (viewFn [(viewString)] (viewMaybe (viewString))) t" `isInfixOf` lowered
|
||||
"typedValueWithProvenance 12 (viewFn [(viewString)] (viewMaybe (viewString))) t viewProvenanceChecked" `isInfixOf` lowered
|
||||
assertBool "expected bind application to declared callback" $
|
||||
"typedApply 13 9 12 t" `isInfixOf` lowered
|
||||
|
||||
@@ -3131,14 +3160,14 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
|
||||
, testCase "imported VTGuarded lowers to portable viewGuarded" $ do
|
||||
let failGuard = result (evalTricu allTestLibsEnv (parseTricu "(x : guardFail)"))
|
||||
imported = [ImportedView "Ext.id" (VTFn [VTGuarded (VTName "String") failGuard] (VTName "String"))]
|
||||
imported = [ImportedView "Ext.id" (VTFn [VTGuarded (VTName "String") failGuard] (VTName "String")) ViewChecked]
|
||||
case lowerSourceWithImportedViews imported "main =@String Ext.id \"x\"\n" of
|
||||
Left err -> assertFailure err
|
||||
Right lowered -> assertBool "expected imported guarded view to survive lowering" $ "viewGuarded" `isInfixOf` lowered
|
||||
|
||||
, testCase "tricu check runs imported guarded argument failure" $ do
|
||||
let failGuard = result (evalTricu allTestLibsEnv (parseTricu "(x : guardFail)"))
|
||||
imported = [ImportedView "Ext.id" (VTFn [VTGuarded (VTName "String") failGuard] (VTName "String"))]
|
||||
imported = [ImportedView "Ext.id" (VTFn [VTGuarded (VTName "String") failGuard] (VTName "String")) ViewChecked]
|
||||
output <- checkSourceWithEnvAndImportedViews allTestLibsEnv imported "main =@String Ext.id \"x\"\n"
|
||||
output @?= "guard failed at typedRequire symbol 2 for Guarded String"
|
||||
|
||||
@@ -3187,6 +3216,30 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
view <- getViewType store viewRef
|
||||
view @?= Right (VTFn [VTRef 10] (VTRef 10))
|
||||
|
||||
, testCase "Workspace modules publish explicitly quantified polymorphic views" $
|
||||
withSystemTempDirectory "tricu-workspace-polymorphic-view" $ \dir -> do
|
||||
let store = StorePath (dir </> "store")
|
||||
utilPath = dir </> "util.tri"
|
||||
mainPath = dir </> "main.tri"
|
||||
writeFile (dir </> "tricu.workspace") "module util = util.tri\n"
|
||||
writeFile utilPath "idP x@_a =@_a x\n"
|
||||
writeFile mainPath "!import \"util\" Util\n\nmain =@String Util.idP \"hi\"\n"
|
||||
_ <- evaluateFileWithStore (Just store) mainPath
|
||||
mAlias <- readAlias store ModuleAlias "util"
|
||||
case mAlias of
|
||||
Nothing -> assertFailure "expected util module alias"
|
||||
Just ref -> do
|
||||
mManifest <- getManifest store (objectRefHash ref)
|
||||
case mManifest of
|
||||
Nothing -> assertFailure "expected util module manifest"
|
||||
Just manifest -> case find ((== "idP") . unpack . moduleExportName) (moduleManifestExports manifest) of
|
||||
Nothing -> assertFailure "expected idP export"
|
||||
Just ex -> case moduleExportView ex of
|
||||
Nothing -> assertFailure "expected idP view ref"
|
||||
Just viewRef -> do
|
||||
view <- getViewType store viewRef
|
||||
view @?= Right (VTForall [0] (VTFn [VTVar 0] (VTVar 0)))
|
||||
|
||||
, testCase "Workspace modules publish string custom view aliases" $
|
||||
withSystemTempDirectory "tricu-workspace-string-view-alias" $ \dir -> do
|
||||
let store = StorePath (dir </> "store")
|
||||
@@ -3234,6 +3287,7 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
Just ex -> do
|
||||
objectRefKind (moduleExportObject ex) @?= viewTreeKind
|
||||
moduleExportAbi ex @?= "arboricx.abi.view-tree.v1"
|
||||
moduleExportViewProvenance ex @?= Just ViewChecked
|
||||
loadedTree <- getViewTree store (moduleExportObject ex)
|
||||
case moduleExportView ex of
|
||||
Nothing -> assertFailure "expected idUser view ref"
|
||||
@@ -3245,7 +3299,8 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
Left err -> assertFailure err
|
||||
Right tree -> do
|
||||
rootTerm <- either assertFailure pure (viewTreeRootTerm tree)
|
||||
tree @?= singletonViewTree (Just expectedView) rootTerm
|
||||
viewTreeRootViewFact tree @?= Right (Just (expectedView, ViewChecked))
|
||||
tree @?= singletonViewTreeWithProvenance (Just (expectedView, ViewChecked)) rootTerm
|
||||
|
||||
, testCase "Workspace modules reject malformed custom view aliases" $
|
||||
withSystemTempDirectory "tricu-workspace-malformed-view-alias" $ \dir -> do
|
||||
@@ -3266,6 +3321,233 @@ viewContractTests = testGroup "View Contract Tests"
|
||||
]
|
||||
readAlias store ModuleAlias "util" >>= (@?= Nothing)
|
||||
|
||||
, testCase "tricu check lowers free View variables under explicit Forall" $ do
|
||||
case lowerSource "idP x@_a =@_a x\n" of
|
||||
Left err -> assertFailure err
|
||||
Right lowered -> do
|
||||
assertBool "expected polymorphic declaration to be explicitly quantified" $ "viewForall [(0)]" `isInfixOf` lowered
|
||||
assertBool "expected quantified identity function body" $ "viewFn [(viewVar 0)] (viewVar 0)" `isInfixOf` lowered
|
||||
|
||||
, testCase "tricu check supports first-order polymorphic identity View variables" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "idP x@_a =@_a x\nmain =@String idP \"hi\"\n"
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "tricu check propagates first-order polymorphic result relationships" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "constP x@_a y@_b =@_a x\nmain =@String constP \"hi\" 1\n"
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "tricu check instantiates quantified Views at higher-order boundaries" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "idP x@_a =@_a x\ncomposeP f@(Fn [_b] _c) g@(Fn [_a] _b) x@_a =@_c f (g x)\nmain =@String composeP idP idP \"hi\"\n"
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "tricu check matches quantified values against concrete Fn requirements" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "idP x@_a =@_a x\nacceptSS f@(Fn [String] String) =@String f \"hi\"\nmain =@String acceptSS idP\n"
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "tricu check propagates nested polymorphic List relationships" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "idList xs@(List _a) =@(List _a) xs\nmain =@(List String) idList [(\"hi\")]\n"
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "tricu check keeps polymorphic instantiation acyclic for reciprocal higher-order constraints" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "idP x@_a =@_a x\nrel f@(Fn [_a] _b) g@(Fn [_b] _a) =@String \"ok\"\nmain =@String rel idP idP\n"
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "tricu check supports first-principles parametric stdlib island shapes" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "idV x@_a =@_a x\nconstV x@_a y@_b =@_a x\ncomposeV f@(Fn [_b] _c) g@(Fn [_a] _b) x@_a =@_c f (g x)\nmain =@String composeV idV (constV \"hi\") 1\n"
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "tricu check rejects raw triage in parametric annotated definitions" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "bad x@_a =@String triage \"leaf\" (_ : \"stem\") (_ _ : \"fork\") x\n"
|
||||
output `containsAll` ["parametric View definition \"bad\"", "uses raw triage directly", "trusted eliminator boundary"]
|
||||
|
||||
, testCase "tricu check rejects raw t in parametric annotated definitions" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "bad x@_a =@_a t\n"
|
||||
output `containsAll` ["parametric View definition \"bad\"", "uses raw t directly", "trusted eliminator boundary"]
|
||||
|
||||
, testCase "tricu check rejects parametric definitions depending on local raw helpers" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "raw x = triage \"leaf\" (_ : \"stem\") (_ _ : \"fork\") x\nbad x@_a =@String raw x\n"
|
||||
output `containsAll` ["parametric View definition \"bad\"", "raw-tainted local helper \"raw\"", "uses raw triage directly"]
|
||||
|
||||
, testCase "tricu check rejects parametric definitions depending on unchecked imported facts" $ do
|
||||
let imported = [ImportedView "Ext.raw" (VTFn [VTVar 0] (VTName "String")) ViewUnchecked]
|
||||
output <- checkSourceWithEnvAndImportedViews allTestLibsEnv imported "bad x@_a =@String Ext.raw x\n"
|
||||
output `containsAll` ["parametric View definition \"bad\"", "unchecked or unknown external name \"Ext.raw\""]
|
||||
|
||||
, testCase "tricu check accepts parametric code through value-level trusted stdlib facts" $ do
|
||||
facts <- either assertFailure pure (valueViewFactsFromEnv allTestLibsEnv)
|
||||
let source = "idP x@_a =@_a x\nmaybeOrV default@_a m@(Maybe _a) =@_a matchMaybe default idP m\n"
|
||||
output <- checkSourceWithEnvAndImportedViews allTestLibsEnv facts source
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "unused value-level trusted facts do not perturb root selection" $ do
|
||||
facts <- either assertFailure pure (valueViewFactsFromEnv allTestLibsEnv)
|
||||
output <- checkSourceWithEnvAndImportedViews allTestLibsEnv facts "idP x@_a =@_a x\n"
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "value-level trusted stdlib facts lower with Trusted provenance" $ do
|
||||
facts <- either assertFailure pure (valueViewFactsFromEnv allTestLibsEnv)
|
||||
case lowerSourceWithImportedViews facts "notV x@Bool =@Bool matchBool false true x\n" of
|
||||
Left err -> assertFailure err
|
||||
Right lowered -> assertBool "expected trusted provenance in lowered view tree" $ "typedValueWithProvenance" `isInfixOf` lowered && "viewProvenanceTrusted" `isInfixOf` lowered
|
||||
|
||||
, testCase "tricu check uses annotated id const compose through re-export modules" $
|
||||
withSystemTempDirectory "tricu-stdlib-prelude-views" $ \dir -> do
|
||||
let store = StorePath (dir </> "store")
|
||||
basePath = dir </> "mybase.tri"
|
||||
preludePath = dir </> "myprelude.tri"
|
||||
mainPath = dir </> "main.tri"
|
||||
writeFile (dir </> "tricu.workspace") "module mybase = mybase.tri\nmodule myprelude = myprelude.tri\n"
|
||||
writeFile basePath "id a@_a =@_a a\nconst a@_a b@_b =@_a a\ncompose f@(Fn [_b] _c) g@(Fn [_a] _b) x@_a =@_c f (g x)\n"
|
||||
writeFile preludePath "!import \"mybase\" !Local\n"
|
||||
writeFile mainPath "!import \"myprelude\" !Local\nmain =@String compose id (const \"hi\") 1\n"
|
||||
output <- checkFileWithStore store mainPath
|
||||
output @?= "ok"
|
||||
|
||||
, testCase "Workspace value-level viewFacts export and re-export Trusted provenance" $
|
||||
withSystemTempDirectory "tricu-workspace-value-view-facts" $ \dir -> do
|
||||
let store = StorePath (dir </> "store")
|
||||
depPath = dir </> "dep.tri"
|
||||
shimPath = dir </> "shim.tri"
|
||||
mainPath = dir </> "main.tri"
|
||||
factBlock = unlines
|
||||
[ "factsPair = t"
|
||||
, "factsFact name provenance view = factsPair name (factsPair provenance view)"
|
||||
, "factsTrusted = 1"
|
||||
, "factsField tag value = factsPair tag value"
|
||||
, "factsRecord tag fields = factsPair tag fields"
|
||||
, "factsVar id = factsRecord 8 [(factsField 10 id)]"
|
||||
, "factsForall binders body = factsRecord 9 [(factsField 11 binders) (factsField 12 body)]"
|
||||
, "factsFn args result = factsRecord 1 [(factsField 0 args) (factsField 1 result)]"
|
||||
, "viewFacts = [(factsFact \"rawId\" factsTrusted (factsForall [0] (factsFn [(factsVar 0)] (factsVar 0))))]"
|
||||
]
|
||||
expected = VTForall [0] (VTFn [VTVar 0] (VTVar 0))
|
||||
writeFile (dir </> "tricu.workspace") "module dep = dep.tri\nmodule shim = shim.tri\n"
|
||||
writeFile depPath ("rawId x = x\n" ++ factBlock)
|
||||
writeFile shimPath "!import \"dep\" !Local\n"
|
||||
writeFile mainPath "!import \"shim\" Shim\nmain x@_a =@_a Shim.rawId x\n"
|
||||
output <- checkFileWithStore store mainPath
|
||||
output @?= "ok"
|
||||
forM_ [("dep", "rawId"), ("shim", "rawId")] $ \(moduleName, exportName) -> do
|
||||
mAlias <- readAlias store ModuleAlias (pack moduleName)
|
||||
case mAlias of
|
||||
Nothing -> assertFailure ("expected " ++ moduleName ++ " module alias")
|
||||
Just ref -> do
|
||||
mManifest <- getManifest store (objectRefHash ref)
|
||||
case mManifest of
|
||||
Nothing -> assertFailure ("expected " ++ moduleName ++ " module manifest")
|
||||
Just manifest -> do
|
||||
assertBool ("viewFacts should not be exported from " ++ moduleName) $
|
||||
all ((/= "viewFacts") . unpack . moduleExportName) (moduleManifestExports manifest)
|
||||
case find ((== exportName) . unpack . moduleExportName) (moduleManifestExports manifest) of
|
||||
Nothing -> assertFailure ("expected " ++ exportName ++ " export from " ++ moduleName)
|
||||
Just ex -> do
|
||||
moduleExportViewProvenance ex @?= Just ViewTrusted
|
||||
case moduleExportView ex of
|
||||
Nothing -> assertFailure "expected trusted value-level view ref"
|
||||
Just viewRef -> do
|
||||
view <- getViewType store viewRef
|
||||
view @?= Right expected
|
||||
|
||||
, testCase "value-level viewFacts decoder reports malformed fact context" $ do
|
||||
let env = evalTricu Map.empty (parseTricu "viewFacts = [(t \"bad\" (t 9 t))]\n")
|
||||
case valueViewFactsFromEnv env of
|
||||
Right _ -> assertFailure "expected malformed provenance error"
|
||||
Left err -> err `containsAll` ["viewFacts[0]", "bad", "invalid provenance", "unknown provenance tag 9"]
|
||||
|
||||
, testCase "value-level viewFacts decoder reports malformed View context" $ do
|
||||
let env = evalTricu Map.empty (parseTricu "viewFacts = [(t \"bad\" (t 1 (t 9 [])))]\n")
|
||||
case valueViewFactsFromEnv env of
|
||||
Right _ -> assertFailure "expected malformed View error"
|
||||
Left err -> err `containsAll` ["viewFacts[0]", "bad", "malformed View"]
|
||||
|
||||
, testCase "value-level viewFacts decoder rejects duplicate fact names" $ do
|
||||
let env = evalTricu Map.empty (parseTricu "v = t 9 [(t 11 []) (t 12 (t 0 []))]\nviewFacts = [(t \"dup\" (t 1 v)) (t \"dup\" (t 1 v))]\n")
|
||||
case valueViewFactsFromEnv env of
|
||||
Right _ -> assertFailure "expected duplicate viewFacts error"
|
||||
Left err -> err `containsAll` ["duplicate viewFacts entry", "dup"]
|
||||
|
||||
, testCase "Workspace modules reject viewFacts for non-exported names" $
|
||||
withSystemTempDirectory "tricu-workspace-view-facts-nonexport" $ \dir -> do
|
||||
let store = StorePath (dir </> "store")
|
||||
depPath = dir </> "dep.tri"
|
||||
mainPath = dir </> "main.tri"
|
||||
writeFile (dir </> "tricu.workspace") "module dep = dep.tri\n"
|
||||
writeFile depPath "rawId x = x\nv = t 9 [(t 11 []) (t 12 (t 0 []))]\nviewFacts = [(t \"missing\" (t 1 v))]\n"
|
||||
writeFile mainPath "!import \"dep\" Dep\nmain = Dep.rawId t\n"
|
||||
outcome <- try (evaluateFileWithStore (Just store) mainPath) :: IO (Either SomeException Env)
|
||||
case outcome of
|
||||
Right _ -> assertFailure "expected non-exported viewFacts rejection"
|
||||
Left err -> show err `containsAll` ["viewFacts for non-exported name", "missing"]
|
||||
|
||||
, testCase "stdlib list value-level facts publish Trusted contracts" $
|
||||
withSystemTempDirectory "tricu-stdlib-list-view-facts" $ \dir -> do
|
||||
let store = StorePath (dir </> "store")
|
||||
basePath = dir </> "base.tri"
|
||||
listPath = dir </> "list.tri"
|
||||
mainPath = dir </> "main.tri"
|
||||
baseSource <- readFile "./lib/base.tri"
|
||||
listSource <- readFile "./lib/list.tri"
|
||||
writeFile (dir </> "tricu.workspace") "module base = base.tri\nmodule list = list.tri\n"
|
||||
writeFile basePath baseSource
|
||||
writeFile listPath listSource
|
||||
writeFile mainPath "!import \"list\" L\ninc x@Byte =@Byte x\nmain xs@(List Byte) =@(List Byte) L.map inc xs\n"
|
||||
output <- checkFileWithStore store mainPath
|
||||
output @?= "ok"
|
||||
mAlias <- readAlias store ModuleAlias (pack "list")
|
||||
case mAlias of
|
||||
Nothing -> assertFailure "expected list module alias"
|
||||
Just ref -> do
|
||||
mManifest <- getManifest store (objectRefHash ref)
|
||||
case mManifest of
|
||||
Nothing -> assertFailure "expected list module manifest"
|
||||
Just manifest -> do
|
||||
let trustedNames =
|
||||
[ "emptyList?", "tail", "append", "lExist?", "map", "filter"
|
||||
, "foldl", "foldr", "length", "reverse", "snoc", "count"
|
||||
, "all?", "any?", "intersect", "headMaybe", "lastMaybe"
|
||||
, "nthMaybe", "take", "drop", "splitAt", "concatMap", "find"
|
||||
, "partition", "strLength", "strAppend", "strEq?", "strEmpty?"
|
||||
, "startsWith?", "endsWith?", "contains?", "lines", "unlines"
|
||||
, "words", "unwords", "zipWith"
|
||||
]
|
||||
forM_ trustedNames $ \exportName ->
|
||||
case find ((== exportName) . unpack . moduleExportName) (moduleManifestExports manifest) of
|
||||
Nothing -> assertFailure ("expected " ++ exportName ++ " export")
|
||||
Just ex -> moduleExportViewProvenance ex @?= Just ViewTrusted
|
||||
|
||||
, testCase "Workspace re-export-only modules preserve imported View Contracts" $
|
||||
withSystemTempDirectory "tricu-workspace-reexport-views" $ \dir -> do
|
||||
let store = StorePath (dir </> "store")
|
||||
depPath = dir </> "dep.tri"
|
||||
shimPath = dir </> "shim.tri"
|
||||
mainPath = dir </> "main.tri"
|
||||
writeFile (dir </> "tricu.workspace") "module dep = dep.tri\nmodule shim = shim.tri\n"
|
||||
writeFile depPath "idP x@_a =@_a x\n"
|
||||
writeFile shimPath "!import \"dep\" !Local\n"
|
||||
writeFile mainPath "!import \"shim\" Shim\nmain =@String Shim.idP \"hi\"\n"
|
||||
output <- checkFileWithStore store mainPath
|
||||
output @?= "ok"
|
||||
mAlias <- readAlias store ModuleAlias "shim"
|
||||
case mAlias of
|
||||
Nothing -> assertFailure "expected shim module alias"
|
||||
Just ref -> do
|
||||
mManifest <- getManifest store (objectRefHash ref)
|
||||
case mManifest of
|
||||
Nothing -> assertFailure "expected shim module manifest"
|
||||
Just manifest -> case find ((== "idP") . unpack . moduleExportName) (moduleManifestExports manifest) of
|
||||
Nothing -> assertFailure "expected idP re-export"
|
||||
Just ex -> do
|
||||
moduleExportViewProvenance ex @?= Just ViewChecked
|
||||
case moduleExportView ex of
|
||||
Nothing -> assertFailure "expected idP re-export view ref"
|
||||
Just viewRef -> do
|
||||
view <- getViewType store viewRef
|
||||
view @?= Right (VTForall [0] (VTFn [VTVar 0] (VTVar 0)))
|
||||
|
||||
, testCase "tricu check rejects inconsistent first-order polymorphic View bindings" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "same x@_a y@_a =@_a x\nmain =@String same \"hi\" 1\n"
|
||||
output @?= "symbol 6 (byte literal) expected String but got Byte"
|
||||
|
||||
, testCase "tricu check catches undersaturated annotated function calls via residual Fn view" $ do
|
||||
output <- checkSourceWithEnv allTestLibsEnv "f x@String y@String =@String x\nmain =@String f \"a\"\n"
|
||||
output @?= "symbol 5 (f application result) expected String but got Fn [String] String"
|
||||
|
||||
Reference in New Issue
Block a user