1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-02 10:47:32 +03:00
juvix/test/BackendMarkdown/Negative.hs
Jan Mas Rovira 6fcc9f21d2
Improve performance of formatting a project (#2863)
Currently formatting a project is equivalent to running `juvix format`
on each individual file. Hence, the performance is quadratic wrt the
number of modules in the project. This pr fixes that and we now we only
process each module once.

# Benchmark (1236% faster 🚀)
Checking the standard library
```
hyperfine --warmup 1 'juvix format --check' 'juvix-main format --check'
Benchmark 1: juvix format --check
  Time (mean ± σ):     450.6 ms ±  33.7 ms    [User: 707.2 ms, System: 178.7 ms]
  Range (min … max):   396.0 ms … 497.0 ms    10 runs

Benchmark 2: juvix-main format --check
  Time (mean ± σ):      6.019 s ±  0.267 s    [User: 9.333 s, System: 1.512 s]
  Range (min … max):    5.598 s …  6.524 s    10 runs

Summary
  juvix format --check ran
   13.36 ± 1.16 times faster than juvix-main format --check
```

# Other changes:
1. The `EntryPoint` field `entryPointModulePath` is now optional.
2. I've introduced a new type `TopModulePathKey` which is analogous to
`TopModulePath` but wihout location information. It is used in hashmap
keys where the location in the key is never used. This is useful as we
can now get a `TopModulePathKey` from a `Path Rel File`.
3. I've refactored the `_formatInput` field in `FormatOptions` so that
it doesn't need to be a special case anymore.
4. I've introduced a new effect `Forcing` that allows to individually
force fields of a record type with a convenient syntax.
5. I've refactored some of the constraints in scoping so that they only
require `Reader Package` instead of `Reader EntryPoint`.
6. I've introduced a new type family so that local modules are no longer
required to have `ModuleId` from their type. Before, they were assigned
one, but it was never used.


# Future work:
1. For project-wise formatting, the compilation is done in parallel, but
the formatting is still done sequentially. That should be improved.
2024-07-01 18:05:24 +02:00

106 lines
3.7 KiB
Haskell

module BackendMarkdown.Negative where
import Base
import Juvix.Compiler.Backend.Markdown.Error
import Juvix.Compiler.Backend.Markdown.Translation.FromTyped.Source
import Juvix.Compiler.Concrete qualified as Concrete
import Juvix.Compiler.Concrete.Translation.FromParsed.Analysis.Scoping qualified as Scoper
import Juvix.Parser.Error
type FailMsg = String
data NegTest = NegTest
{ _name :: String,
_relDir :: Path Rel Dir,
_file :: Path Rel File,
_checkErr :: JuvixError -> Maybe FailMsg
}
testDescr :: NegTest -> TestDescr
testDescr NegTest {..} =
let tRoot = root <//> _relDir
file' = tRoot <//> _file
in TestDescr
{ _testName = _name,
_testRoot = tRoot,
_testAssertion = Single $ do
entryPoint <- testDefaultEntryPointIO tRoot file'
result <- testTaggedLockedToIO (runIOEither entryPoint upToScopingEntry)
case result of
Left err -> whenJust (_checkErr err) assertFailure
Right (_, pipelineRes) -> checkResult pipelineRes
}
where
checkResult :: PipelineResult Scoper.ScoperResult -> IO ()
checkResult PipelineResult {..} = do
let m = _pipelineResult ^. Scoper.resultModule
opts =
ProcessJuvixBlocksArgs
{ _processJuvixBlocksArgsConcreteOpts = Concrete.defaultOptions,
_processJuvixBlocksArgsUrlPrefix = "",
_processJuvixBlocksArgsIdPrefix = "",
_processJuvixBlocksArgsNoPath = True,
_processJuvixBlocksArgsExt = ".html",
_processJuvixBlocksArgsStripPrefix = "",
_processJuvixBlocksArgsComments = Comments mempty,
_processJuvixBlocksArgsFolderStructure = False,
_processJuvixBlocksArgsModule = m,
_processJuvixBlocksArgsOutputDir =
root <//> $(mkRelDir "markdown")
}
res = fromJuvixMarkdown' opts
case res of
Left err -> whenJust (_checkErr (JuvixError err)) assertFailure
Right _ -> assertFailure "Unexpected success"
allTests :: TestTree
allTests =
testGroup
"Markdown negative tests"
(map (mkTest . testDescr) tests)
root :: Path Abs Dir
root = relToProject $(mkRelDir "tests/negative")
wrongError :: JuvixError -> Maybe FailMsg
wrongError e = Just ("unexpected error: " <> unpack (renderTextDefault e))
tests :: [NegTest]
tests =
[ NegTest
"Empty file"
$(mkRelDir "Markdown")
$(mkRelFile "Empty.juvix.md")
$ \e -> case fromJuvixError e of
Just (ErrMarkdownBackend (ErrNoJuvixCodeBlocks _)) -> Nothing
_ -> wrongError e,
NegTest
"No Juvix code blocks"
$(mkRelDir "Markdown")
$(mkRelFile "NoJuvixCodeBlocks.juvix.md")
$ \e -> case fromJuvixError e of
Just (ErrMarkdownBackend (ErrNoJuvixCodeBlocks _)) -> Nothing
_ -> wrongError e,
NegTest
"extract-module-statements with no local module"
$(mkRelDir "Markdown")
$(mkRelFile "InvalidExtractModuleBlockNoModule.juvix.md")
$ \e -> case fromJuvixError e of
Just (ErrInvalidExtractModuleBlock _) -> Nothing
_ -> wrongError e,
NegTest
"extract-module-statements with no local module"
$(mkRelDir "Markdown")
$(mkRelFile "InvalidExtractModuleBlockNotJustModule.juvix.md")
$ \e -> case fromJuvixError e of
Just (ErrInvalidExtractModuleBlock _) -> Nothing
_ -> wrongError e,
NegTest
"code block with both hide and extract-module-statements"
$(mkRelDir "Markdown")
$(mkRelFile "InvalidCodeBlockExtraAttribute.juvix.md")
$ \e -> case fromJuvixError e of
Just (ErrInvalidCodeBlockAttribtues _) -> Nothing
_ -> wrongError e
]