139 lines
4.8 KiB
Raku
139 lines
4.8 KiB
Raku
#| Utilities for interacting with idris and associated tooling
|
|
unit module IUtils;
|
|
|
|
need IUtils::IDEMode;
|
|
|
|
use Terminal::ANSIColor;
|
|
|
|
use IUtils::Regexes;
|
|
use IUtils::Compiler;
|
|
|
|
use paths;
|
|
|
|
#| Structure representing a test
|
|
class Test {
|
|
#| The name of the test
|
|
has Str:D $.name is required;
|
|
#| The expression name of the test
|
|
has Str:D $.expr is required;
|
|
#| The output type of the test
|
|
has ExprOutput:D $.output-type is required;
|
|
|
|
#| Run this test, and return true if it failed, false if it passed
|
|
method run(IO::Path:D $source, Int:D $indent-level? = 4 --> Bool) {
|
|
CATCH {
|
|
when ExpressionError {
|
|
say "{colored '+', 'red'} $.name: {colored 'FAIL', 'red bold'}"
|
|
.indent($indent-level);
|
|
say "{colored('exit code', 'red')}: {$_.exit-code}"
|
|
.indent($indent-level + 2);
|
|
if $_.out.trim {
|
|
say colored('stdout:', 'underline')
|
|
.indent($indent-level + 2);
|
|
say $_.out.trim.lines.map(*.indent($indent-level + 2))
|
|
.join("\n");
|
|
}
|
|
if $_.err.trim {
|
|
say colored('stderr:', 'underline')
|
|
.indent($indent-level + 2);
|
|
say $_.err.trim.lines.map(*.indent($indent-level + 2))
|
|
.join("\n");
|
|
}
|
|
return True;
|
|
}
|
|
}
|
|
|
|
idris-exec $.expr, $source.relative, $.output-type;
|
|
# The exception handler graps flow if the test failed, here the test passed
|
|
my $output =
|
|
"{colored '+', 'green'} $.name: {colored 'pass', 'green'}";
|
|
say $output.indent($indent-level);
|
|
return False;
|
|
}
|
|
}
|
|
|
|
#| Structure representing the tests in a module
|
|
class ModuleTests {
|
|
#| The name of this module
|
|
has Str:D $.name is required;
|
|
#| The source file of this module
|
|
has IO::Path:D $.source is required;
|
|
#| A list of the associated tests this module has
|
|
has Test:D @.tests is required;
|
|
}
|
|
|
|
#| Structure representing all of the runables assocated with a project
|
|
class PackageRunables {
|
|
#| The ipkg for this project
|
|
has IO::Path:D $.ipkg is required;
|
|
# TODO: Add benchmarks
|
|
#| A map from the name of the module to a list of tests
|
|
has ModuleTests:D %.tests is required;
|
|
}
|
|
|
|
#| Structure representing the root of what idris considers a package directory,
|
|
#| with the associated ipkg and source files. These can and will overlap within
|
|
#| the same directory.
|
|
class PackageInfo {
|
|
has IO::Path:D $.ipkg is required;
|
|
has IO::Path:D $.root is required;
|
|
has IO::Path:D @.sources is required;
|
|
|
|
method runnables {
|
|
# Locate the tests
|
|
my %tests = Hash.new;
|
|
for @.sources -> $source {
|
|
my $contents = $source.slurp;
|
|
if $contents ~~ &module-name {
|
|
my $module-name = $<name>.Str;
|
|
my @tests;
|
|
for $contents.match(&flagged-expression, :g) -> $match {
|
|
my $output-type = do
|
|
given $match<output-type> {
|
|
when * eq '()' {succeed Unit};
|
|
when * eq 'Bool' {succeed Boolean};
|
|
when * eq 'Either' {succeed Either};
|
|
};
|
|
my $test =
|
|
Test.new(name => $match<test-name>.Str.trim,
|
|
expr => $match<expression-name>.Str,
|
|
output-type => $output-type);
|
|
@tests.push($test);
|
|
}
|
|
if @tests.elems > 0 {
|
|
%tests{$module-name} =
|
|
ModuleTests.new(name => $module-name,
|
|
source => $source,
|
|
tests => @tests);
|
|
}
|
|
}
|
|
}
|
|
# Build and return the runnables
|
|
PackageRunables.new(ipkg => self.ipkg, tests => %tests)
|
|
}
|
|
}
|
|
|
|
#| Scan a particular ipkg for its associated sources
|
|
sub scan-ipkg(IO::Path:D $ipkg --> PackageInfo:D) {
|
|
my $contents = $ipkg.slurp;
|
|
my $src-dir =
|
|
($contents ~~
|
|
/ 'sourcedir' \h* '=' \h*
|
|
'"' $<value>=[<-["]>*] '"' /)<value>
|
|
// "src";
|
|
|
|
my IO::Path:D @sources =
|
|
paths($ipkg.parent.add($src-dir), :file(*.ends-with(".idr"))).map(*.IO);
|
|
PackageInfo.new(ipkg => $ipkg, root => $ipkg.parent, sources => @sources)
|
|
}
|
|
|
|
# TODO: Add some parsing of pack.toml to locate test packages and associate them
|
|
# with their source ipkg
|
|
|
|
#| Scan $*CWD to locate ipkgs and their associated sources
|
|
sub scan-packages(--> Array[PackageInfo:D]) is export {
|
|
my PackageInfo:D @ipkgs =
|
|
paths(:file(*.ends-with(".ipkg"))).map(*.IO.&scan-ipkg);
|
|
return @ipkgs;
|
|
}
|