diff --git a/META6.json b/META6.json index c3f0ccb..0918894 100644 --- a/META6.json +++ b/META6.json @@ -12,7 +12,8 @@ "provides": { "IUtils": "lib/IUtils.rakumod", "IUtils::IDEMode": "lib/IUtils/IDEMode.rakumod", - "IUtils::Comments": "lib/IUtils/Comments.rakumod" + "IUtils::Regexs": "lib/IUtils/Regexes.rakumod", + "IUtils::Compiler": "lib/IUtils/Compiler.rakumod" }, "bin": { "iutils": "bin/iutils" diff --git a/bin/iutils b/bin/iutils index 9f99a00..9c2671d 100755 --- a/bin/iutils +++ b/bin/iutils @@ -1,8 +1,10 @@ #!/usr/bin/env raku use v6.d; use IUtils; -use IUtils::Comments; +use IUtils::Regexes; +use IUtils::Compiler; +# TODO: Add filtering for tests based on module/name #| Execute the tests in an idris project multi MAIN( "test", @@ -12,13 +14,38 @@ multi MAIN( chdir($project-path.IO.resolve: :completely) if $project-path; # Scan for our packages my @packages = scan-packages; - # Collect tests - my @tests; - for @packages -> $package { - # FIXME - say 'Finding tests for ', $package.ipkg.relative; - for $package.sources -> $source { - say 'Scanning for tests in ', $source; + # Collect runables + my @runables = @packages.map: *.runnables; + # Run the tests + my $basedir = $*CWD; + for @runables -> $runable { + # Make sure the package is built + pack-build $runable.ipkg.relative; + # CD to the local directory to make sure idris can exec the expressions + indir $runable.ipkg.parent, { + next unless $runable.tests.elems > 0; + say "** Testing {$runable.ipkg.relative: $basedir}"; + for $runable.tests.keys -> $module-name { + my $module = $runable.tests{$module-name}; + next unless $module.tests.elems > 0; + say "-- Testing $module-name".indent(2); + for $module.tests -> $test { + try { + idris-exec $test, $module.source.relative; + } + if $! { + my $stdout = $1.err.lines.map(*.indent(8)).join("\n"); + say "+ $test: FAIL".indent(4); + say "stdout:".indent(6); + $!.err.lines.map(*.indent(8)).join("\n"); + say "stderr:".indent(6); + $!.err.lines.map(*.indent(8)).join("\n"); + say "exit code: {$!.exit-code}" + } else { + say "+ $test: Pass".indent(4); + } + } + } } } } diff --git a/lib/IUtils.rakumod b/lib/IUtils.rakumod index d41c8f8..9d7f1df 100644 --- a/lib/IUtils.rakumod +++ b/lib/IUtils.rakumod @@ -3,8 +3,29 @@ unit module IUtils; need IUtils::IDEMode; +use IUtils::Regexes; + use paths; +#| 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 Str: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. @@ -12,6 +33,29 @@ 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 = $.Str; + my @tests; + for $contents.match(&flagged-expression, :g) -> $match { + @tests.push($match.Str); + } + 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 @@ -37,74 +81,3 @@ sub scan-packages(--> Array[PackageInfo:D]) is export { paths(:file(*.ends-with(".ipkg"))).map(*.IO.&scan-ipkg); return @ipkgs; } - - -# Utility functions for pack - -#| Invoke a pack command -sub pack-run(*@cmd) is export { - my $proc = run "pack", @cmd, :out, :err; - my $out = $proc.out.slurp(:close); - my $err = $proc.err.slurp(:close); - unless $proc { - die qq:to/END/; - Pack Failure! - Captured Output: - $out - - Captured StdErr: - $err - END - } -} - -#| Build a package with pack -sub pack-build($pkg) is export { - pack-run 'build', $pkg -} - -#| Test a package with pack -sub pack-test($pkg) is export { - pack-run 'build', $pkg -} - -#| Clean a package with pack -sub pack-clean($pkg) is export { - pack-run 'clean', $pkg -} - -# Utility functions for idris - -#| An error coming from the idris compiler -class IdrisError is Exception { - has Str $.out; - has Str $.err; - has Int $.exit-code; - has Str $.command; - - method message { - qq:to/END/; - Error running idris command: $.command - Command exited with $.exit-code - END - } -} - -#| Invoke an idris command -sub idris-run(*@cmd) is export { - my $proc = run "idris2", @cmd, :out, :err; - my $out = $proc.out.slurp(:close); - my $err = $proc.err.slurp(:close); - unless $proc { - IdrisError.new( - out => $out, err => $err, - exit-code => $proc.exitcode, command => @cmd.Str) - .throw; - } - return $out; -} - -#| Exec the expression with the given name in the given file -sub idris-exec($expr, $file) is export { - idris-run '--find-ipkg', '--exec', $expr, $file -} diff --git a/lib/IUtils/Compiler.rakumod b/lib/IUtils/Compiler.rakumod new file mode 100644 index 0000000..d8bfcba --- /dev/null +++ b/lib/IUtils/Compiler.rakumod @@ -0,0 +1,72 @@ +#| Utilities for interacting with the idris compiler and package manager +unit module IUtils::Compiler; + +# Utility functions for pack + +#| Invoke a pack command +sub pack-run(*@cmd) is export { + my $proc = run "pack", @cmd, :out, :err; + my $out = $proc.out.slurp(:close); + my $err = $proc.err.slurp(:close); + unless $proc { + die qq:to/END/; + Pack Failure! + Captured Output: + $out + + Captured StdErr: + $err + END + } +} + +#| Build a package with pack +sub pack-build($pkg) is export { + pack-run 'build', $pkg +} + +#| Test a package with pack +sub pack-test($pkg) is export { + pack-run 'build', $pkg +} + +#| Clean a package with pack +sub pack-clean($pkg) is export { + pack-run 'clean', $pkg +} + +# Utility functions for idris + +#| An error coming from the idris compiler +class IdrisError is Exception { + has Str $.out; + has Str $.err; + has Int $.exit-code; + has Str $.command; + + method message { + qq:to/END/; + Error running idris command: $.command + Command exited with $.exit-code + END + } +} + +#| Invoke an idris command +sub idris-run(*@cmd) is export { + my $proc = run "idris2", @cmd, :out, :err; + my $out = $proc.out.slurp(:close); + my $err = $proc.err.slurp(:close); + unless $proc { + IdrisError.new( + out => $out, err => $err, + exit-code => $proc.exitcode, command => @cmd.Str) + .throw; + } + return $out; +} + +#| Exec the expression with the given name in the given file +sub idris-exec($expr, $file) is export { + idris-run '--find-ipkg', '--exec', $expr, $file +} diff --git a/lib/IUtils/Comments.rakumod b/lib/IUtils/Regexes.rakumod similarity index 76% rename from lib/IUtils/Comments.rakumod rename to lib/IUtils/Regexes.rakumod index edd7e9d..2b9cc71 100644 --- a/lib/IUtils/Comments.rakumod +++ b/lib/IUtils/Regexes.rakumod @@ -1,4 +1,4 @@ -unit module IUtils::Comments; +unit module IUtils::Regexes; my token comment-start { \- \- } my token type { @@ -12,3 +12,7 @@ my regex flagged-expression is export { [<&comment-start> \V* \v]* \h+ \: \V* \v } + +my regex module-name is export { + 'module' \h* $=(\S+) +}