1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-12 14:28:08 +03:00
juvix/test/Package/Negative.hs

116 lines
3.6 KiB
Haskell
Raw Normal View History

module Package.Negative where
import Base
import Juvix.Compiler.Pipeline.Package
Use a Juvix source file to define a package (#2434) Depends on: * ~~https://github.com/anoma/juvix/pull/2459~~ * https://github.com/anoma/juvix/pull/2462 This PR is part of a series implementing: * https://github.com/anoma/juvix/issues/2336 This PR adds the package file loading function, including a file evaluation effect. It integrates this with the existing `readPackage` function and adds tests / smoke tests. ## Package.juvix format Instead of `juvix.yaml` (which is still supported currently) users can now place a `Package.juvix` file in the root of their project. The simplest `Package.juvix` file you can write is: ``` module Package; import PackageDescription open; package : Package := defaultPackage; ``` The [PackageDescription](https://github.com/anoma/juvix/blob/35b2f618f093895f32929ac0f2c0affcdab8d627/include/package/PackageDescription.juvix) module defines the `Package` type. Users can use "go-to definition" in their IDE from the Package file to see the documentation and definitions. Users may also import `Stdlib.Prelude` in their Package file. This is loaded from the global project. No other module imports are supported. Notes: * If a directory contains both `Package.juvix` and `juvix.yaml` then `Package.juvix` is used in preference. ## Default stdlib dependency The `Dependency` type has a constructor called `defaultStdlib`. This means that any project can use the compiler builtin standard library dependency. With `juvix.yaml` this dependency is only available when the `dependencies` field is unspecified. ``` module Package; import PackageDescription open; package : Package := defaultPackage { dependencies := [defaultStdlib] }; ``` ## Validation As well as the standard type checking validation that the Juvix compiler provides additional validation is made on the file. * The Package module must contain the identifier `package` and it must have type `Package` that's obtained from the global `PackageDescription` module. * Every dependency specified in the Package.juvix must be unique. * Closes https://github.com/anoma/juvix/issues/2336 ## Examples ### Package with name and version ``` module Package; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0}; ``` ### Package with GitHub dependency ``` module Package; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0; dependencies := [defaultStdlib; github (org := "anoma"; repo := "juvix-containers"; ref := "v0.7.1")]}; ``` ## Package with main and buildDir fields ``` module Package; import Stdlib.Prelude open; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0; dependencies := [defaultStdlib; github (org := "anoma"; repo := "juvix-containers"; ref := "v0.7.1")]; buildDir := just "/tmp/build"; main := just "HelloWorld.juvix" }; ```
2023-10-27 14:35:20 +03:00
import Juvix.Compiler.Pipeline.Package.Loader.EvalEff.IO
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
Use a Juvix source file to define a package (#2434) Depends on: * ~~https://github.com/anoma/juvix/pull/2459~~ * https://github.com/anoma/juvix/pull/2462 This PR is part of a series implementing: * https://github.com/anoma/juvix/issues/2336 This PR adds the package file loading function, including a file evaluation effect. It integrates this with the existing `readPackage` function and adds tests / smoke tests. ## Package.juvix format Instead of `juvix.yaml` (which is still supported currently) users can now place a `Package.juvix` file in the root of their project. The simplest `Package.juvix` file you can write is: ``` module Package; import PackageDescription open; package : Package := defaultPackage; ``` The [PackageDescription](https://github.com/anoma/juvix/blob/35b2f618f093895f32929ac0f2c0affcdab8d627/include/package/PackageDescription.juvix) module defines the `Package` type. Users can use "go-to definition" in their IDE from the Package file to see the documentation and definitions. Users may also import `Stdlib.Prelude` in their Package file. This is loaded from the global project. No other module imports are supported. Notes: * If a directory contains both `Package.juvix` and `juvix.yaml` then `Package.juvix` is used in preference. ## Default stdlib dependency The `Dependency` type has a constructor called `defaultStdlib`. This means that any project can use the compiler builtin standard library dependency. With `juvix.yaml` this dependency is only available when the `dependencies` field is unspecified. ``` module Package; import PackageDescription open; package : Package := defaultPackage { dependencies := [defaultStdlib] }; ``` ## Validation As well as the standard type checking validation that the Juvix compiler provides additional validation is made on the file. * The Package module must contain the identifier `package` and it must have type `Package` that's obtained from the global `PackageDescription` module. * Every dependency specified in the Package.juvix must be unique. * Closes https://github.com/anoma/juvix/issues/2336 ## Examples ### Package with name and version ``` module Package; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0}; ``` ### Package with GitHub dependency ``` module Package; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0; dependencies := [defaultStdlib; github (org := "anoma"; repo := "juvix-containers"; ref := "v0.7.1")]}; ``` ## Package with main and buildDir fields ``` module Package; import Stdlib.Prelude open; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0; dependencies := [defaultStdlib; github (org := "anoma"; repo := "juvix-containers"; ref := "v0.7.1")]; buildDir := just "/tmp/build"; main := just "HelloWorld.juvix" }; ```
2023-10-27 14:35:20 +03:00
res <-
withTempDir'
Replace `polysemy` by `effectful` (#2663) The following benchmark compares juvix 0.6.0 with polysemy and a new version (implemented in this pr) which replaces polysemy by effectful. # Typecheck standard library without caching ``` hyperfine --warmup 2 --prepare 'juvix-polysemy clean' 'juvix-polysemy typecheck Stdlib/Prelude.juvix' 'juvix-effectful typecheck Stdlib/Prelude.juvix' Benchmark 1: juvix-polysemy typecheck Stdlib/Prelude.juvix Time (mean ± σ): 3.924 s ± 0.143 s [User: 3.787 s, System: 0.084 s] Range (min … max): 3.649 s … 4.142 s 10 runs Benchmark 2: juvix-effectful typecheck Stdlib/Prelude.juvix Time (mean ± σ): 2.558 s ± 0.074 s [User: 2.430 s, System: 0.084 s] Range (min … max): 2.403 s … 2.646 s 10 runs Summary juvix-effectful typecheck Stdlib/Prelude.juvix ran 1.53 ± 0.07 times faster than juvix-polysemy typecheck Stdlib/Prelude.juvix ``` # Typecheck standard library with caching ``` hyperfine --warmup 1 'juvix-effectful typecheck Stdlib/Prelude.juvix' 'juvix-polysemy typecheck Stdlib/Prelude.juvix' --min-runs 20 Benchmark 1: juvix-effectful typecheck Stdlib/Prelude.juvix Time (mean ± σ): 1.194 s ± 0.068 s [User: 0.979 s, System: 0.211 s] Range (min … max): 1.113 s … 1.307 s 20 runs Benchmark 2: juvix-polysemy typecheck Stdlib/Prelude.juvix Time (mean ± σ): 1.237 s ± 0.083 s [User: 0.997 s, System: 0.231 s] Range (min … max): 1.061 s … 1.476 s 20 runs Summary juvix-effectful typecheck Stdlib/Prelude.juvix ran 1.04 ± 0.09 times faster than juvix-polysemy typecheck Stdlib/Prelude.juvix ```
2024-03-21 15:09:34 +03:00
( runM
Use a Juvix source file to define a package (#2434) Depends on: * ~~https://github.com/anoma/juvix/pull/2459~~ * https://github.com/anoma/juvix/pull/2462 This PR is part of a series implementing: * https://github.com/anoma/juvix/issues/2336 This PR adds the package file loading function, including a file evaluation effect. It integrates this with the existing `readPackage` function and adds tests / smoke tests. ## Package.juvix format Instead of `juvix.yaml` (which is still supported currently) users can now place a `Package.juvix` file in the root of their project. The simplest `Package.juvix` file you can write is: ``` module Package; import PackageDescription open; package : Package := defaultPackage; ``` The [PackageDescription](https://github.com/anoma/juvix/blob/35b2f618f093895f32929ac0f2c0affcdab8d627/include/package/PackageDescription.juvix) module defines the `Package` type. Users can use "go-to definition" in their IDE from the Package file to see the documentation and definitions. Users may also import `Stdlib.Prelude` in their Package file. This is loaded from the global project. No other module imports are supported. Notes: * If a directory contains both `Package.juvix` and `juvix.yaml` then `Package.juvix` is used in preference. ## Default stdlib dependency The `Dependency` type has a constructor called `defaultStdlib`. This means that any project can use the compiler builtin standard library dependency. With `juvix.yaml` this dependency is only available when the `dependencies` field is unspecified. ``` module Package; import PackageDescription open; package : Package := defaultPackage { dependencies := [defaultStdlib] }; ``` ## Validation As well as the standard type checking validation that the Juvix compiler provides additional validation is made on the file. * The Package module must contain the identifier `package` and it must have type `Package` that's obtained from the global `PackageDescription` module. * Every dependency specified in the Package.juvix must be unique. * Closes https://github.com/anoma/juvix/issues/2336 ## Examples ### Package with name and version ``` module Package; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0}; ``` ### Package with GitHub dependency ``` module Package; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0; dependencies := [defaultStdlib; github (org := "anoma"; repo := "juvix-containers"; ref := "v0.7.1")]}; ``` ## Package with main and buildDir fields ``` module Package; import Stdlib.Prelude open; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0; dependencies := [defaultStdlib; github (org := "anoma"; repo := "juvix-containers"; ref := "v0.7.1")]; buildDir := just "/tmp/build"; main := just "HelloWorld.juvix" }; ```
2023-10-27 14:35:20 +03:00
. runError
. runFilesIO
. mapError (JuvixError @PackageLoaderError)
Run test suite in parallel (#2507) ## Overview This PR makes the compiler pipeline thread-safe so that the test suite can be run in parallel. This is achieved by: * Removing use of `{get, set, with}CurrentDir` functions. * Adding locking around shared file resources like the the global-project and internal build directory. NB: **Locking is disabled for the main compiler target**, as it is single threaded they are not required. ## Run test suite in parallel To run the test suite in parallel you must add `--ta '+RTS -N -RTS'` to your stack test arguments. For example: ``` stack test --fast --ta '+RTS -N -RTS' ``` The `-N` instructs the Haskell runtime to choose the number of threads to use based on how many processors there are on your machine. You can use `-Nn` to see the number of threads to `n`. These flags are already [set in the Makefile](https://github.com/anoma/juvix/blob/e6dca22cfdcff936add5e7134f9c6f20416504a5/Makefile#L26) when you or CI uses `stack test`. ## Locking The Haskell package [filelock](https://hackage.haskell.org/package/filelock) is used for locking. File locks are used instead of MVars because Juvix code does not control when new threads are created, they are created by the test suite. This means that MVars created by Juvix code will have no effect, because they are created independently on each test-suite thread. Additionally the resources we're locking live on the filesystem and so can be conveniently tagged by path. ### FileLock The filelock library is wrapped in a FileLock effect: https://github.com/anoma/juvix/blob/e6dca22cfdcff936add5e7134f9c6f20416504a5/src/Juvix/Data/Effect/FileLock/Base.hs#L6-L8 There is an [IO interpreter](https://github.com/anoma/juvix/blob/e6dca22cfdcff936add5e7134f9c6f20416504a5/src/Juvix/Data/Effect/FileLock/IO.hs#L8) that uses filelock and an [no-op interpreter](https://github.com/anoma/juvix/blob/e6dca22cfdcff936add5e7134f9c6f20416504a5/src/Juvix/Data/Effect/FileLock/Permissive.hs#L7) that just runs actions unconditionally. ### TaggedLock To make the file locks simpler to use a TaggedLock effect is introduced: https://github.com/anoma/juvix/blob/e6dca22cfdcff936add5e7134f9c6f20416504a5/src/Juvix/Data/Effect/TaggedLock/Base.hs#L5-L11 And convenience function: https://github.com/anoma/juvix/blob/e6dca22cfdcff936add5e7134f9c6f20416504a5/src/Juvix/Data/Effect/TaggedLock.hs#L28 This allows an action to be locked, tagged by a directory that may or may not exist. For example in the following code, an action is performed on a directory `root` that may delete the directory before repopulating the files. So the lockfile cannot be stored in the `root` itself. https://github.com/anoma/juvix/blob/e6dca22cfdcff936add5e7134f9c6f20416504a5/src/Juvix/Extra/Files.hs#L55-L60 ## Pipeline As noted above, we only use locking in the test suite. The main app target pipeline is single threaded and so locking is unnecessary. So the interpretation of locks is parameterised so that locking can be disabled https://github.com/anoma/juvix/blob/e6dca22cfdcff936add5e7134f9c6f20416504a5/src/Juvix/Compiler/Pipeline/Run.hs#L64
2023-11-16 18:19:52 +03:00
. runTaggedLock LockModeExclusive
Use a Juvix source file to define a package (#2434) Depends on: * ~~https://github.com/anoma/juvix/pull/2459~~ * https://github.com/anoma/juvix/pull/2462 This PR is part of a series implementing: * https://github.com/anoma/juvix/issues/2336 This PR adds the package file loading function, including a file evaluation effect. It integrates this with the existing `readPackage` function and adds tests / smoke tests. ## Package.juvix format Instead of `juvix.yaml` (which is still supported currently) users can now place a `Package.juvix` file in the root of their project. The simplest `Package.juvix` file you can write is: ``` module Package; import PackageDescription open; package : Package := defaultPackage; ``` The [PackageDescription](https://github.com/anoma/juvix/blob/35b2f618f093895f32929ac0f2c0affcdab8d627/include/package/PackageDescription.juvix) module defines the `Package` type. Users can use "go-to definition" in their IDE from the Package file to see the documentation and definitions. Users may also import `Stdlib.Prelude` in their Package file. This is loaded from the global project. No other module imports are supported. Notes: * If a directory contains both `Package.juvix` and `juvix.yaml` then `Package.juvix` is used in preference. ## Default stdlib dependency The `Dependency` type has a constructor called `defaultStdlib`. This means that any project can use the compiler builtin standard library dependency. With `juvix.yaml` this dependency is only available when the `dependencies` field is unspecified. ``` module Package; import PackageDescription open; package : Package := defaultPackage { dependencies := [defaultStdlib] }; ``` ## Validation As well as the standard type checking validation that the Juvix compiler provides additional validation is made on the file. * The Package module must contain the identifier `package` and it must have type `Package` that's obtained from the global `PackageDescription` module. * Every dependency specified in the Package.juvix must be unique. * Closes https://github.com/anoma/juvix/issues/2336 ## Examples ### Package with name and version ``` module Package; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0}; ``` ### Package with GitHub dependency ``` module Package; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0; dependencies := [defaultStdlib; github (org := "anoma"; repo := "juvix-containers"; ref := "v0.7.1")]}; ``` ## Package with main and buildDir fields ``` module Package; import Stdlib.Prelude open; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0; dependencies := [defaultStdlib; github (org := "anoma"; repo := "juvix-containers"; ref := "v0.7.1")]; buildDir := just "/tmp/build"; main := just "HelloWorld.juvix" }; ```
2023-10-27 14:35:20 +03:00
. runEvalFileEffIO
. readPackage tRoot
. CustomBuildDir
. Abs
)
case mapLeft fromJuvixError res of
Left (Just err) -> whenJust (_checkErr err) assertFailure
2024-01-08 15:27:18 +03:00
Left Nothing -> assertFailure "An error occurred 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
Use a Juvix source file to define a package (#2434) Depends on: * ~~https://github.com/anoma/juvix/pull/2459~~ * https://github.com/anoma/juvix/pull/2462 This PR is part of a series implementing: * https://github.com/anoma/juvix/issues/2336 This PR adds the package file loading function, including a file evaluation effect. It integrates this with the existing `readPackage` function and adds tests / smoke tests. ## Package.juvix format Instead of `juvix.yaml` (which is still supported currently) users can now place a `Package.juvix` file in the root of their project. The simplest `Package.juvix` file you can write is: ``` module Package; import PackageDescription open; package : Package := defaultPackage; ``` The [PackageDescription](https://github.com/anoma/juvix/blob/35b2f618f093895f32929ac0f2c0affcdab8d627/include/package/PackageDescription.juvix) module defines the `Package` type. Users can use "go-to definition" in their IDE from the Package file to see the documentation and definitions. Users may also import `Stdlib.Prelude` in their Package file. This is loaded from the global project. No other module imports are supported. Notes: * If a directory contains both `Package.juvix` and `juvix.yaml` then `Package.juvix` is used in preference. ## Default stdlib dependency The `Dependency` type has a constructor called `defaultStdlib`. This means that any project can use the compiler builtin standard library dependency. With `juvix.yaml` this dependency is only available when the `dependencies` field is unspecified. ``` module Package; import PackageDescription open; package : Package := defaultPackage { dependencies := [defaultStdlib] }; ``` ## Validation As well as the standard type checking validation that the Juvix compiler provides additional validation is made on the file. * The Package module must contain the identifier `package` and it must have type `Package` that's obtained from the global `PackageDescription` module. * Every dependency specified in the Package.juvix must be unique. * Closes https://github.com/anoma/juvix/issues/2336 ## Examples ### Package with name and version ``` module Package; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0}; ``` ### Package with GitHub dependency ``` module Package; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0; dependencies := [defaultStdlib; github (org := "anoma"; repo := "juvix-containers"; ref := "v0.7.1")]}; ``` ## Package with main and buildDir fields ``` module Package; import Stdlib.Prelude open; import PackageDescription open; package : Package := defaultPackage {name := "a-package"; version := mkVersion 0 1 0; dependencies := [defaultStdlib; github (org := "anoma"; repo := "juvix-containers"; ref := "v0.7.1")]; buildDir := just "/tmp/build"; main := just "HelloWorld.juvix" }; ```
2023-10-27 14:35:20 +03:00
_ -> wrongError,
NegTest
"Package.juvix doesn't compile"
$(mkRelDir "PackageJuvixDoesntCompile")
$ \case
PackageLoaderError _ ErrPackageJuvixError {} -> Nothing
_ -> wrongError,
NegTest
"Package.juvix no package symbol"
$(mkRelDir "PackageJuvixNoPackageSymbol")
$ \case
PackageLoaderError _ ErrPackageSymbolNotFound {} -> Nothing
_ -> wrongError,
NegTest
"Package.juvix package symbol has wrong type1"
$(mkRelDir "PackageJuvixPackageSymbolWrongType1")
$ \case
PackageLoaderError _ ErrPackageTypeError {} -> Nothing
_ -> wrongError,
NegTest
"Package.juvix package symbol has wrong type2"
$(mkRelDir "PackageJuvixPackageSymbolWrongType2")
$ \case
PackageLoaderError _ ErrPackageTypeError {} -> Nothing
_ -> wrongError,
NegTest
"Package.juvix duplicate dependencies"
$(mkRelDir "PackageJuvixDuplicateDependencies")
$ \case
PackageLoaderError _ ErrDuplicateDependencyError {} -> Nothing
_ -> wrongError,
NegTest
"Package.juvix lockfile YAML parse error"
$(mkRelDir "InvalidLockfile")
$ \case
PackageLoaderError _ ErrLockfileYamlParseError {} -> Nothing
_ -> wrongError
]