From 47d334179fbc42bcb779a4af49c2a0c933dfb3d9 Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Fri, 20 Oct 2023 11:35:56 +0100 Subject: [PATCH 1/4] Use JuvixError instead of Text for errors in Package file loading --- app/Commands/Repl.hs | 2 +- .../FromParsed/Analysis/PathResolver.hs | 30 +++---- .../Pipeline/Artifacts/PathResolver.hs | 2 +- src/Juvix/Compiler/Pipeline/Lockfile.hs | 19 ++++- src/Juvix/Compiler/Pipeline/Package.hs | 63 ++++++++------ .../Compiler/Pipeline/Package/Loader/Error.hs | 85 +++++++++++++++++++ .../Commands/compile-dependencies.smoke.yaml | 2 + 7 files changed, 157 insertions(+), 46 deletions(-) create mode 100644 src/Juvix/Compiler/Pipeline/Package/Loader/Error.hs diff --git a/app/Commands/Repl.hs b/app/Commands/Repl.hs index d333203ae6..af8ce4bbdc 100644 --- a/app/Commands/Repl.hs +++ b/app/Commands/Repl.hs @@ -147,7 +147,7 @@ loadDefaultPrelude = whenJustM defaultPreludeEntryPoint $ \e -> do . runM . evalInternet hasInternet . runFilesIO - . runError @Text + . runError @JuvixError . runReader e . runLogIO . runProcessIO diff --git a/src/Juvix/Compiler/Concrete/Translation/FromParsed/Analysis/PathResolver.hs b/src/Juvix/Compiler/Concrete/Translation/FromParsed/Analysis/PathResolver.hs index 7c1d452053..fd4dcee95a 100644 --- a/src/Juvix/Compiler/Concrete/Translation/FromParsed/Analysis/PathResolver.hs +++ b/src/Juvix/Compiler/Concrete/Translation/FromParsed/Analysis/PathResolver.hs @@ -30,7 +30,7 @@ import Juvix.Prelude mkPackage :: forall r. - (Members '[Files, Error Text, Reader ResolverEnv, GitClone] r) => + (Members '[Files, Error JuvixError, Reader ResolverEnv, GitClone] r) => Maybe EntryPoint -> Path Abs Dir -> Sem r Package @@ -43,7 +43,7 @@ mkPackage mpackageEntry _packageRoot = do mkPackageInfo :: forall r. - (Members '[Files, Error Text, Reader ResolverEnv, Error DependencyError, GitClone] r) => + (Members '[Files, Error JuvixError, Reader ResolverEnv, Error DependencyError, GitClone] r) => Maybe EntryPoint -> Path Abs Dir -> Package -> @@ -162,7 +162,7 @@ resolveDependency i = case i ^. packageDepdendencyInfoDependency of registerDependencies' :: forall r. - (Members '[Reader EntryPoint, State ResolverState, Reader ResolverEnv, Files, Error Text, Error DependencyError, GitClone] r) => + (Members '[Reader EntryPoint, State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone] r) => DependenciesConfig -> Sem r () registerDependencies' conf = do @@ -186,7 +186,7 @@ registerDependencies' conf = do addRootDependency :: forall r. - (Members '[State ResolverState, Reader ResolverEnv, Files, Error Text, Error DependencyError, GitClone] r) => + (Members '[State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone] r) => DependenciesConfig -> EntryPoint -> Path Abs Dir -> @@ -207,7 +207,7 @@ addRootDependency conf e root = do addDependency :: forall r. - (Members '[State ResolverState, Reader ResolverEnv, Files, Error Text, Error DependencyError, GitClone] r) => + (Members '[State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone] r) => Maybe EntryPoint -> PackageDependencyInfo -> Sem r LockfileDependency @@ -224,7 +224,7 @@ addDependency me d = do addDependency' :: forall r. - (Members '[State ResolverState, Reader ResolverEnv, Files, Error Text, Error DependencyError, GitClone] r) => + (Members '[State ResolverState, Reader ResolverEnv, Files, Error JuvixError, Error DependencyError, GitClone] r) => Package -> Maybe EntryPoint -> ResolvedDependency -> @@ -319,7 +319,7 @@ expectedPath' actualPath m = do re :: forall r a. - (Members '[Reader EntryPoint, Files, Error Text, Error DependencyError, GitClone] r) => + (Members '[Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone] r) => Sem (PathResolver ': r) a -> Sem (Reader ResolverEnv ': State ResolverState ': r) a re = reinterpret2H helper @@ -342,13 +342,13 @@ re = reinterpret2H helper Right (r, _) -> r raise (evalPathResolver' st' root' (a' x')) -evalPathResolver' :: (Members '[Reader EntryPoint, Files, Error Text, Error DependencyError, GitClone] r) => ResolverState -> Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r a +evalPathResolver' :: (Members '[Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone] r) => ResolverState -> Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r a evalPathResolver' st root = fmap snd . runPathResolver' st root -runPathResolver :: (Members '[Reader EntryPoint, Files, Error Text, Error DependencyError, GitClone] r) => Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) +runPathResolver :: (Members '[Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone] r) => Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) runPathResolver = runPathResolver' iniResolverState -runPathResolver' :: (Members '[Reader EntryPoint, Files, Error Text, Error DependencyError, GitClone] r) => ResolverState -> Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) +runPathResolver' :: (Members '[Reader EntryPoint, Files, Error JuvixError, Error DependencyError, GitClone] r) => ResolverState -> Path Abs Dir -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) runPathResolver' st root x = do e <- ask let _envSingleFile :: Maybe (Path Abs File) @@ -364,15 +364,15 @@ runPathResolver' st root x = do } runState st (runReader env (re x)) -runPathResolverPipe' :: (Members '[Files, Reader EntryPoint, Error DependencyError, GitClone] r) => ResolverState -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) +runPathResolverPipe' :: (Members '[Files, Reader EntryPoint, Error DependencyError, GitClone, Error JuvixError] r) => ResolverState -> Sem (PathResolver ': r) a -> Sem r (ResolverState, a) runPathResolverPipe' iniState a = do r <- asks (^. entryPointResolverRoot) - runError (runPathResolver' iniState r (raiseUnder a)) >>= either error return + runPathResolver' iniState r a -runPathResolverPipe :: (Members '[Files, Reader EntryPoint, Error DependencyError, GitClone] r) => Sem (PathResolver ': r) a -> Sem r (ResolverState, a) +runPathResolverPipe :: (Members '[Files, Reader EntryPoint, Error DependencyError, GitClone, Error JuvixError] r) => Sem (PathResolver ': r) a -> Sem r (ResolverState, a) runPathResolverPipe a = do r <- asks (^. entryPointResolverRoot) - runError (runPathResolver r (raiseUnder a)) >>= either error return + runPathResolver r a -evalPathResolverPipe :: (Members '[Files, Reader EntryPoint, Error DependencyError, GitClone] r) => Sem (PathResolver ': r) a -> Sem r a +evalPathResolverPipe :: (Members '[Files, Reader EntryPoint, Error DependencyError, GitClone, Error JuvixError] r) => Sem (PathResolver ': r) a -> Sem r a evalPathResolverPipe = fmap snd . runPathResolverPipe diff --git a/src/Juvix/Compiler/Pipeline/Artifacts/PathResolver.hs b/src/Juvix/Compiler/Pipeline/Artifacts/PathResolver.hs index 2d0e658b9c..4e6c528925 100644 --- a/src/Juvix/Compiler/Pipeline/Artifacts/PathResolver.hs +++ b/src/Juvix/Compiler/Pipeline/Artifacts/PathResolver.hs @@ -6,5 +6,5 @@ import Juvix.Compiler.Pipeline.EntryPoint import Juvix.Data.Effect.Git import Juvix.Prelude -runPathResolverArtifacts :: (Members '[Files, Reader EntryPoint, State Artifacts, Error DependencyError, GitClone] r) => Sem (PathResolver ': r) a -> Sem r a +runPathResolverArtifacts :: (Members '[Files, Reader EntryPoint, State Artifacts, Error DependencyError, GitClone, Error JuvixError] r) => Sem (PathResolver ': r) a -> Sem r a runPathResolverArtifacts = runStateLikeArtifacts runPathResolverPipe' artifactResolver diff --git a/src/Juvix/Compiler/Pipeline/Lockfile.hs b/src/Juvix/Compiler/Pipeline/Lockfile.hs index bc73a48e41..a0dea58b53 100644 --- a/src/Juvix/Compiler/Pipeline/Lockfile.hs +++ b/src/Juvix/Compiler/Pipeline/Lockfile.hs @@ -10,6 +10,7 @@ import Data.String.Interpolate (i) import Data.Yaml import Data.Yaml.Pretty import Juvix.Compiler.Pipeline.Package.Dependency +import Juvix.Compiler.Pipeline.Package.Loader.Error import Juvix.Extra.Paths import Juvix.Extra.Strings qualified as Str import Juvix.Extra.Version @@ -84,21 +85,33 @@ mkPackageLockfilePath = ( juvixLockfile) mayReadLockfile :: forall r. - (Members '[Files, Error Text] r) => + (Members '[Files, Error PackageLoaderError] r) => Path Abs Dir -> Sem r (Maybe LockfileInfo) mayReadLockfile root = do - let lockfilePath = mkPackageLockfilePath root lockfileExists <- fileExists' lockfilePath if | lockfileExists -> do bs <- readFileBS' lockfilePath - either (throw . pack . prettyPrintParseException) ((return . Just) . mkLockfileInfo lockfilePath) (decodeEither' @Lockfile bs) + either (throwErr . pack . prettyPrintParseException) ((return . Just) . mkLockfileInfo lockfilePath) (decodeEither' @Lockfile bs) | otherwise -> return Nothing where mkLockfileInfo :: Path Abs File -> Lockfile -> LockfileInfo mkLockfileInfo _lockfileInfoPath _lockfileInfoLockfile = LockfileInfo {..} + lockfilePath :: Path Abs File + lockfilePath = mkPackageLockfilePath root + + throwErr :: Text -> Sem r a + throwErr e = + throw + PackageLoaderError + { _packageLoaderErrorPath = lockfilePath, + _packageLoaderErrorCause = + ErrLockfileYamlParseError + LockfileYamlParseError {_lockfileYamlParseErrorError = e} + } + lockfileEncodeConfig :: Config lockfileEncodeConfig = setConfCompare keyCompare defConfig where diff --git a/src/Juvix/Compiler/Pipeline/Package.hs b/src/Juvix/Compiler/Pipeline/Package.hs index 7ee3dc874d..63b2e62115 100644 --- a/src/Juvix/Compiler/Pipeline/Package.hs +++ b/src/Juvix/Compiler/Pipeline/Package.hs @@ -13,10 +13,11 @@ import Data.Versions import Data.Yaml import Juvix.Compiler.Pipeline.Lockfile import Juvix.Compiler.Pipeline.Package.Base +import Juvix.Compiler.Pipeline.Package.Loader.Error import Juvix.Extra.Paths import Juvix.Prelude -processPackage :: forall r. (Members '[Error Text] r) => Path Abs File -> BuildDir -> Maybe LockfileInfo -> RawPackage -> Sem r Package +processPackage :: forall r. (Members '[Error PackageLoaderError] r) => Path Abs File -> BuildDir -> Maybe LockfileInfo -> RawPackage -> Sem r Package processPackage _packageFile buildDir lockfile pkg = do let _packageName = fromMaybe defaultPackageName (pkg ^. packageName) _packageDependencies = resolveDependencies @@ -35,7 +36,14 @@ processPackage _packageFile buildDir lockfile pkg = do Nothing -> return defaultVersion Just ver -> case semver ver of Right v -> return v - Left err -> throw (pack (errorBundlePretty err)) + Left err -> + throw + PackageLoaderError + { _packageLoaderErrorCause = + ErrVersionParseError + VersionParseError {_versionParseErrorError = (pack (errorBundlePretty err))}, + _packageLoaderErrorPath = _packageFile + } checkNoDuplicateDepNames :: [Dependency] -> Sem r () checkNoDuplicateDepNames deps = go HashSet.empty (deps ^.. traversed . _GitDependency . gitDependencyName) @@ -43,15 +51,17 @@ processPackage _packageFile buildDir lockfile pkg = do go :: HashSet Text -> [Text] -> Sem r () go _ [] = return () go s (x : xs) - | x `HashSet.member` s = throw (errMsg x) + | x `HashSet.member` s = + throw + PackageLoaderError + { _packageLoaderErrorPath = _packageFile, + _packageLoaderErrorCause = + ErrDuplicateDependencyError + DuplicateDependencyError + { _duplicateDependencyErrorName = x + } + } | otherwise = go (HashSet.insert x s) xs - where - errMsg :: Text -> Text - errMsg dupName = - "Juvix package file at: " - <> pack (toFilePath _packageFile) - <> " contains the duplicate dependency name: " - <> dupName resolveDependencies :: [Dependency] resolveDependencies = fromMaybe [stdlib] (pkg ^. packageDependencies) @@ -62,36 +72,37 @@ processPackage _packageFile buildDir lockfile pkg = do -- | Given some directory d it tries to read the file d/juvix.yaml and parse its contents readPackage :: forall r. - (Members '[Files, Error Text] r) => + (Members '[Files, Error JuvixError] r) => Path Abs Dir -> BuildDir -> Sem r Package -readPackage root buildDir = do +readPackage root buildDir = mapError (JuvixError @PackageLoaderError) $ do bs <- readFileBS' yamlPath mLockfile <- mayReadLockfile root if | ByteString.null bs -> return (emptyPackage buildDir yamlPath) - | otherwise -> either (throw . pack . prettyPrintParseException) (processPackage yamlPath buildDir mLockfile) (decodeEither' bs) + | otherwise -> either (throwErr . pack . prettyPrintParseException) (processPackage yamlPath buildDir mLockfile) (decodeEither' bs) where yamlPath = mkPackageFilePath root + throwErr e = + throw + PackageLoaderError + { _packageLoaderErrorPath = yamlPath, + _packageLoaderErrorCause = + ErrPackageYamlParseError + PackageYamlParseError + { _packageYamlParseErrorError = e + } + } + readPackageIO :: Path Abs Dir -> BuildDir -> IO Package -readPackageIO root buildDir = do - let x :: Sem '[Error Text, Files, Embed IO] Package - x = readPackage root buildDir - m <- runM $ runFilesIO (runError x) - case m of - Left err -> putStrLn err >> exitFailure - Right r -> return r +readPackageIO root buildDir = runM (runFilesIO (runErrorIO' @JuvixError (readPackage root buildDir))) readGlobalPackageIO :: IO Package -readGlobalPackageIO = do - m <- runM . runFilesIO . runError $ readGlobalPackage - case m of - Left err -> putStrLn err >> exitFailure - Right r -> return r +readGlobalPackageIO = runM (runFilesIO . runErrorIO' @JuvixError $ readGlobalPackage) -readGlobalPackage :: (Members '[Error Text, Files] r) => Sem r Package +readGlobalPackage :: (Members '[Error JuvixError, Files] r) => Sem r Package readGlobalPackage = do yamlPath <- globalYaml unlessM (fileExists' yamlPath) writeGlobalPackage diff --git a/src/Juvix/Compiler/Pipeline/Package/Loader/Error.hs b/src/Juvix/Compiler/Pipeline/Package/Loader/Error.hs new file mode 100644 index 0000000000..b1e65c9180 --- /dev/null +++ b/src/Juvix/Compiler/Pipeline/Package/Loader/Error.hs @@ -0,0 +1,85 @@ +module Juvix.Compiler.Pipeline.Package.Loader.Error where + +import Juvix.Data.CodeAnn +import Juvix.Prelude + +data PackageLoaderError = PackageLoaderError + { _packageLoaderErrorPath :: Path Abs File, + _packageLoaderErrorCause :: PackageLoaderErrorCause + } + +data PackageLoaderErrorCause + = ErrPackageYamlParseError PackageYamlParseError + | ErrLockfileYamlParseError LockfileYamlParseError + | ErrVersionParseError VersionParseError + | ErrDuplicateDependencyError DuplicateDependencyError + +newtype PackageYamlParseError = PackageYamlParseError + { _packageYamlParseErrorError :: Text + } + +newtype LockfileYamlParseError = LockfileYamlParseError + { _lockfileYamlParseErrorError :: Text + } + +newtype VersionParseError = VersionParseError + { _versionParseErrorError :: Text + } + +newtype DuplicateDependencyError = DuplicateDependencyError + { _duplicateDependencyErrorName :: Text + } + +makeLenses ''PackageLoaderError +makeLenses ''PackageYamlParseError +makeLenses ''LockfileYamlParseError +makeLenses ''VersionParseError +makeLenses ''DuplicateDependencyError + +instance ToGenericError PackageLoaderError where + genericError e = do + let msg = mkAnsiText (ppCodeAnn e) + return + GenericError + { _genericErrorMessage = msg, + _genericErrorLoc = i, + _genericErrorIntervals = [i] + } + where + i = getLoc e + +instance PrettyCodeAnn PackageLoaderError where + ppCodeAnn e = ppCodeAnn (e ^. packageLoaderErrorCause) + +instance PrettyCodeAnn PackageLoaderErrorCause where + ppCodeAnn = \case + ErrPackageYamlParseError e -> ppCodeAnn e + ErrLockfileYamlParseError e -> ppCodeAnn e + ErrVersionParseError e -> ppCodeAnn e + ErrDuplicateDependencyError e -> ppCodeAnn e + +instance PrettyCodeAnn PackageYamlParseError where + ppCodeAnn e = + "The package file is invalid" + <> line + <+> pretty (e ^. packageYamlParseErrorError) + +instance PrettyCodeAnn LockfileYamlParseError where + ppCodeAnn e = + "The lock file is invalid" + <> line + <+> pretty (e ^. lockfileYamlParseErrorError) + +instance PrettyCodeAnn VersionParseError where + ppCodeAnn e = + "The package version is invalid" + <> line + <+> pretty (e ^. versionParseErrorError) + +instance PrettyCodeAnn DuplicateDependencyError where + ppCodeAnn e = + "Juvix package file contains the duplicate dependency name:" + <+> pretty (e ^. duplicateDependencyErrorName) + +instance HasLoc PackageLoaderError where + getLoc e = singletonInterval (mkInitialLoc (e ^. packageLoaderErrorPath)) diff --git a/tests/smoke/Commands/compile-dependencies.smoke.yaml b/tests/smoke/Commands/compile-dependencies.smoke.yaml index 8c43d38472..18aff473c3 100644 --- a/tests/smoke/Commands/compile-dependencies.smoke.yaml +++ b/tests/smoke/Commands/compile-dependencies.smoke.yaml @@ -1056,6 +1056,8 @@ tests: # compile project juvix compile HelloWorld.juvix stdout: + contains: "" + stderr: contains: duplicate exit-status: 1 From 270ed7bec5dc331a951b8146f7158abdd05b573d Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Fri, 20 Oct 2023 13:33:43 +0100 Subject: [PATCH 2/4] Add negative Package loading tests --- test/Main.hs | 4 +- test/Package.hs | 10 +++ test/Package/Negative.hs | 68 +++++++++++++++++++ .../Package/InvalidLockfile/juvix.lock.yaml | 1 + .../Package/InvalidLockfile/juvix.yaml | 0 .../YamlDuplicateDependencies/juvix.yaml | 10 +++ .../Package/YamlInvalidVersion/juvix.yaml | 2 + .../Package/YamlParseError/juvix.yaml | 1 + 8 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 test/Package.hs create mode 100644 test/Package/Negative.hs create mode 100644 tests/negative/Package/InvalidLockfile/juvix.lock.yaml create mode 100644 tests/negative/Package/InvalidLockfile/juvix.yaml create mode 100644 tests/negative/Package/YamlDuplicateDependencies/juvix.yaml create mode 100644 tests/negative/Package/YamlInvalidVersion/juvix.yaml create mode 100644 tests/negative/Package/YamlParseError/juvix.yaml diff --git a/test/Main.hs b/test/Main.hs index ce3e4fa03e..74153c3e99 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -10,6 +10,7 @@ import Examples qualified import Format qualified import Formatter qualified import Internal qualified +import Package qualified import Parsing qualified import Reachability qualified import Runtime qualified @@ -43,7 +44,8 @@ fastTests = Typecheck.allTests, Reachability.allTests, Format.allTests, - Formatter.allTests + Formatter.allTests, + Package.allTests ] main :: IO () diff --git a/test/Package.hs b/test/Package.hs new file mode 100644 index 0000000000..1d5eebce0a --- /dev/null +++ b/test/Package.hs @@ -0,0 +1,10 @@ +module Package + ( allTests, + ) +where + +import Base +import Package.Negative qualified as N + +allTests :: TestTree +allTests = testGroup "Package loading tests" [N.allTests] diff --git a/test/Package/Negative.hs b/test/Package/Negative.hs new file mode 100644 index 0000000000..9d17497c90 --- /dev/null +++ b/test/Package/Negative.hs @@ -0,0 +1,68 @@ +module Package.Negative where + +import Base +import Juvix.Compiler.Pipeline.Package +import Juvix.Compiler.Pipeline.Package.Loader.Error + +type FailMsg = String + +data NegTest a = NegTest + { _name :: String, + _relDir :: Path Rel Dir, + _checkErr :: a -> Maybe FailMsg + } + +root :: Path Abs Dir +root = relToProject $(mkRelDir "tests/negative/Package") + +testDescr :: (Typeable a) => NegTest a -> TestDescr +testDescr NegTest {..} = + let tRoot = root _relDir + in TestDescr + { _testName = _name, + _testRoot = tRoot, + _testAssertion = Single $ do + res <- withTempDir' (runM . runError . runFilesIO . readPackage tRoot . CustomBuildDir . Abs) + case mapLeft fromJuvixError res of + Left (Just err) -> whenJust (_checkErr err) assertFailure + Left Nothing -> assertFailure "An error ocurred but it was not when reading the package." + Right {} -> assertFailure "There was no error when reading the package" + } + +allTests :: TestTree +allTests = + testGroup + "Package loading negative tests" + ( map (mkTest . testDescr) packageErrorTests + ) + +wrongError :: Maybe FailMsg +wrongError = Just "Incorrect error" + +packageErrorTests :: [NegTest PackageLoaderError] +packageErrorTests = + [ NegTest + "package YAML parse error" + $(mkRelDir "YamlParseError") + $ \case + PackageLoaderError _ ErrPackageYamlParseError {} -> Nothing + _ -> wrongError, + NegTest + "lockfile YAML parse error" + $(mkRelDir "InvalidLockfile") + $ \case + PackageLoaderError _ ErrLockfileYamlParseError {} -> Nothing + _ -> wrongError, + NegTest + "package YAML invalid version" + $(mkRelDir "YamlInvalidVersion") + $ \case + PackageLoaderError _ ErrVersionParseError {} -> Nothing + _ -> wrongError, + NegTest + "package YAML duplicate dependencies" + $(mkRelDir "YamlDuplicateDependencies") + $ \case + PackageLoaderError _ ErrDuplicateDependencyError {} -> Nothing + _ -> wrongError + ] diff --git a/tests/negative/Package/InvalidLockfile/juvix.lock.yaml b/tests/negative/Package/InvalidLockfile/juvix.lock.yaml new file mode 100644 index 0000000000..846803f036 --- /dev/null +++ b/tests/negative/Package/InvalidLockfile/juvix.lock.yaml @@ -0,0 +1 @@ +dependencies: 123 diff --git a/tests/negative/Package/InvalidLockfile/juvix.yaml b/tests/negative/Package/InvalidLockfile/juvix.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/negative/Package/YamlDuplicateDependencies/juvix.yaml b/tests/negative/Package/YamlDuplicateDependencies/juvix.yaml new file mode 100644 index 0000000000..864845c2e3 --- /dev/null +++ b/tests/negative/Package/YamlDuplicateDependencies/juvix.yaml @@ -0,0 +1,10 @@ +name: abc +dependencies: + - git: + url: repo + name: dep + ref: abc + - git: + url: repo + name: dep + ref: abc diff --git a/tests/negative/Package/YamlInvalidVersion/juvix.yaml b/tests/negative/Package/YamlInvalidVersion/juvix.yaml new file mode 100644 index 0000000000..ac8223db55 --- /dev/null +++ b/tests/negative/Package/YamlInvalidVersion/juvix.yaml @@ -0,0 +1,2 @@ +name: abc +version: def diff --git a/tests/negative/Package/YamlParseError/juvix.yaml b/tests/negative/Package/YamlParseError/juvix.yaml new file mode 100644 index 0000000000..cf6586294e --- /dev/null +++ b/tests/negative/Package/YamlParseError/juvix.yaml @@ -0,0 +1 @@ +name: 123 From 904df62a654c292f4c47f9f2f4891498a4792096 Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Fri, 20 Oct 2023 14:16:08 +0100 Subject: [PATCH 3/4] Add positive tests for package loading --- test/Package.hs | 3 +- test/Package/Positive.hs | 62 +++++++++++++++++++ .../PackageLoader/YamlEmpty/juvix.yaml | 0 .../YamlEmptyDependencies/juvix.yaml | 2 + .../YamlNoDependencies/juvix.yaml | 1 + 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 test/Package/Positive.hs create mode 100644 tests/positive/PackageLoader/YamlEmpty/juvix.yaml create mode 100644 tests/positive/PackageLoader/YamlEmptyDependencies/juvix.yaml create mode 100644 tests/positive/PackageLoader/YamlNoDependencies/juvix.yaml diff --git a/test/Package.hs b/test/Package.hs index 1d5eebce0a..71496f6a3b 100644 --- a/test/Package.hs +++ b/test/Package.hs @@ -5,6 +5,7 @@ where import Base import Package.Negative qualified as N +import Package.Positive qualified as P allTests :: TestTree -allTests = testGroup "Package loading tests" [N.allTests] +allTests = testGroup "Package loading tests" [N.allTests, P.allTests] diff --git a/test/Package/Positive.hs b/test/Package/Positive.hs new file mode 100644 index 0000000000..e4cd7a49d4 --- /dev/null +++ b/test/Package/Positive.hs @@ -0,0 +1,62 @@ +module Package.Positive where + +import Base +import Juvix.Compiler.Pipeline.Package + +type FailMsg = String + +data PosTest = PosTest + { _name :: String, + _relDir :: Path Rel Dir, + _checkPackage :: Package -> BuildDir -> Maybe FailMsg + } + +root :: Path Abs Dir +root = relToProject $(mkRelDir "tests/positive/PackageLoader") + +testDescr :: PosTest -> TestDescr +testDescr PosTest {..} = + let tRoot = root _relDir + in TestDescr + { _testName = _name, + _testRoot = tRoot, + _testAssertion = Single $ do + withTempDir' $ \d -> do + let buildDir = CustomBuildDir (Abs d) + res <- runM . runError @JuvixError . runFilesIO . readPackage tRoot $ buildDir + case res of + Right p -> whenJust (_checkPackage p buildDir) assertFailure + Left {} -> assertFailure "An error ocurred when reading the package." + } + +allTests :: TestTree +allTests = + testGroup + "Package loading positive tests" + ( map (mkTest . testDescr) packageLoadingTests + ) + +packageLoadingTests :: [PosTest] +packageLoadingTests = + [ + PosTest + "empty YAML is valid" + $(mkRelDir "YamlEmpty") + $ \p _ -> if + | p ^. packageName == defaultPackageName -> Nothing + | otherwise -> Just "Package did not have default name", + PosTest + "no dependencies uses default stdlib" + $(mkRelDir "YamlNoDependencies") + $ \p b -> case p ^? packageDependencies . _head of + Just d -> if + | d == defaultStdlibDep b -> Nothing + | otherwise -> Just "Package dependency is not the default standard library" + _ -> Just "The package has no dependencies", + PosTest + "empty dependencies does not use default stdlib" + $(mkRelDir "YamlEmptyDependencies") + $ \p _ -> if + | null (p ^. packageDependencies) -> Nothing + | otherwise -> Just "Expected dependencies to be empty" + ] diff --git a/tests/positive/PackageLoader/YamlEmpty/juvix.yaml b/tests/positive/PackageLoader/YamlEmpty/juvix.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/positive/PackageLoader/YamlEmptyDependencies/juvix.yaml b/tests/positive/PackageLoader/YamlEmptyDependencies/juvix.yaml new file mode 100644 index 0000000000..f6637dfb49 --- /dev/null +++ b/tests/positive/PackageLoader/YamlEmptyDependencies/juvix.yaml @@ -0,0 +1,2 @@ +name: abc +dependencies: [] diff --git a/tests/positive/PackageLoader/YamlNoDependencies/juvix.yaml b/tests/positive/PackageLoader/YamlNoDependencies/juvix.yaml new file mode 100644 index 0000000000..a1eaae409a --- /dev/null +++ b/tests/positive/PackageLoader/YamlNoDependencies/juvix.yaml @@ -0,0 +1 @@ +name: abc From 0ba8b93b921368d60cae001ee7817ac13e3eac4e Mon Sep 17 00:00:00 2001 From: Paul Cadman Date: Mon, 23 Oct 2023 12:45:28 +0100 Subject: [PATCH 4/4] Fix formatting --- test/Package/Positive.hs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test/Package/Positive.hs b/test/Package/Positive.hs index e4cd7a49d4..aa496c0c91 100644 --- a/test/Package/Positive.hs +++ b/test/Package/Positive.hs @@ -38,25 +38,27 @@ allTests = packageLoadingTests :: [PosTest] packageLoadingTests = - [ - PosTest + [ PosTest "empty YAML is valid" $(mkRelDir "YamlEmpty") - $ \p _ -> if - | p ^. packageName == defaultPackageName -> Nothing - | otherwise -> Just "Package did not have default name", + $ \p _ -> + if + | p ^. packageName == defaultPackageName -> Nothing + | otherwise -> Just "Package did not have default name", PosTest "no dependencies uses default stdlib" $(mkRelDir "YamlNoDependencies") $ \p b -> case p ^? packageDependencies . _head of - Just d -> if - | d == defaultStdlibDep b -> Nothing - | otherwise -> Just "Package dependency is not the default standard library" - _ -> Just "The package has no dependencies", + Just d -> + if + | d == defaultStdlibDep b -> Nothing + | otherwise -> Just "Package dependency is not the default standard library" + _ -> Just "The package has no dependencies", PosTest "empty dependencies does not use default stdlib" $(mkRelDir "YamlEmptyDependencies") - $ \p _ -> if - | null (p ^. packageDependencies) -> Nothing - | otherwise -> Just "Expected dependencies to be empty" + $ \p _ -> + if + | null (p ^. packageDependencies) -> Nothing + | otherwise -> Just "Expected dependencies to be empty" ]