DEV Community

Alexey Melezhik
Alexey Melezhik

Posted on

Multi-platform testing of Raku modules using Sparrow6

Few days ago I filled a ticket with a proposal to use Sparrow6 as a tool for cross-platform testing of community Raku modules. While the discussion is being held I'd like to share a few examples of how one can use Sparrow6 to test Raku modules.

Install Sparrowdo

Sparrowdo is a command line utility to run Sparrow6 scenarios on docker containers.

zef install --/test Sparrowdo

Sparrowdo has just a few dependencies so it won't take long for it to install:

===> Searching for: Sparrowdo ===> Updating cpan mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/cpan1.json ===> Updating p6c mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/p6c1.json ===> Updated p6c mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/p6c1.json ===> Updated cpan mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/cpan1.json ===> Searching for missing dependencies: Sparrow6 ===> Searching for missing dependencies: File::Directory::Tree, Hash::Merge, YAMLish, JSON::Tiny ===> Updating cpan mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/cpan1.json ===> Searching for missing dependencies: MIME::Base64 ===> Installing: File::Directory::Tree:auth<labster> ===> Installing: Hash::Merge:ver<1.0.0>:auth<github:scriptkitties>:api<1> ===> Installing: MIME::Base64:ver<1.2.1>:auth<github:retupmoca> ===> Installing: YAMLish:ver<0.0.5> ===> Installing: JSON::Tiny:ver<1.0> ===> Installing: Sparrow6:ver<0.0.11> ===> Installing: Sparrowdo:ver<0.1.2> 
Enter fullscreen mode Exit fullscreen mode

Get a source code of examples

git clone https://github.com/melezhik/RakuDist && cd RakuDist && ls -l

drwxr-xr-x. 4 user1 wheel 28 янв 2 17:27 modules -rw-r--r--. 1 user1 wheel 10266 янв 2 17:27 README.md drwxr-xr-x. 2 user1 wheel 50 янв 2 17:27 reports 
Enter fullscreen mode Exit fullscreen mode

Folder modules/ will contain examples of test scenarios written on Sparrow6 DSL that we run remotely on running docker instances.

You can use those examples as starting point when creating your own scenarios.

We will go back to one of them a little bit later.

Spin up a docker container

Choose a docker image with OS you want to run tests against and spin it up.

I am choosing an alpine OS image as it extremely light ( 5MB in size ) and does not take long to download:

docker pull alpine && container_name='alpine-rakudist' && docker run -d -t --rm --name $container_name alpine && docker ps

A literally few second and we can see our docker container up and running:

Using default tag: latest latest: Pulling from library/alpine e6b0cf9c0882: Pull complete Digest: sha256:2171658620155679240babee0a7714f6509fae66898db422ad803b951257db78 Status: Downloaded newer image for alpine:latest docker.io/library/alpine:latest 14404faef5589e9228f9a75c2cfc900ed08b50b3e9f44c269a6741864809acff CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 14404faef558 alpine "/bin/sh" 1 second ago Up Less than a second alpine-rakudist 
Enter fullscreen mode Exit fullscreen mode

Run tests

Let's change to module directory from RakuDist project and find some examples.

Say, I want to run tests for Red module:

cd modules/red/

We have a couple of files here:

config.pl6

config.pl6 - a file that contains all configuration data, it's just a easy way to hold configuration parameters and pass them as a Raku Hash to a test scenario.

cat config.pl6

%( user => "red", scm => "https://github.com/FCO/Red.git" ) 
Enter fullscreen mode Exit fullscreen mode

sparrowfile

sparrowfile - is a test scenario itself, written on Sparrow6 DSL and defining all testing logic:

  • check out module source code from a git repository

  • create a system user - we might want to reuse the same docker container to test other modules as well, so it's reasonable to have a separate user for every module to avoid overlaps between module Raku dependencies, because we install those dependencies under a dedicated user.

  • install module external dependencies (sqlite library), for simplicity assumption is made that sqlite is already included in other linux distros, so we only install it for alpine os. But see modules/Cro/sparrowfile how external libraries for a variety of OS could be installed through the same scenario, that to write true multi-platform tests.

  • install module dependencies through zef install --deps-only

  • run module unit tests through zef --test .

cat sparrowfile

my $user = config()<user>; my $directory = "/data/test/{$user}"; my $scm = config()<scm>; user $user; directory $directory, %( owner => $user, group => $user ); git-scm $scm, %( to => $directory, user => $user, ); bash "cd {$directory} && git log --name-status HEAD^..HEAD", %( description => "last commit" ); bash "cd {$directory} && ls -l"; zef "Test::META", %( notest => True ); if os() eq 'alpine' { # this is needed for alpine rakudo installation unless "/bin/zef".IO ~~ :e { copy "/opt/rakudo-pkg/share/perl6/core/bin/zef", "/bin/zef" } # this is needed for alpine rakudo installation unless "/bin/perl6".IO ~~ :e { copy "/opt/rakudo-pkg/bin/perl6", "/bin/perl6" } package-install "sqlite-libs"; } zef $directory, %( force => False, depsonly => True, notest => True, user => $user ); bash "cd {$directory} && zef test .", %( description => "zef test", user => $user ); 
Enter fullscreen mode Exit fullscreen mode

Run tests

Sparrowdo is a command line client to run Sparrow6 remotely ( over ssh or docker ).

When I run sparrowdo I add --bootstrap flag that ensures that Rakudo and Sparrow6 are installed on a docker container.

Luckily when Sparrowdo was designed it had this --bootstrap option, which makes it so convenient tool to test Raku modules!

It worth to mention that Sparrow6 has a small dependency tree (File::Directory::Tree, Hash::Merge, YAMLish, JSON::Tiny) that means it gets installed really quick and almost also does not "pollute" a testing environment with dependencies installed system wide.

It also worth to say, that we only use --boostrap once and don't pass it every time we run tests.

We also need to change to a specific directory so that sparrowdo will pick up mentioned files ( sparrowfile and config.pl6 )

I also pass _no_sudo flag, as we already run all the commands under root user when using a docker.

Finally I pass a --repo argument pointing to a public Sparrow6 repository as Sparrow6 DSL needs to download some dependencies ( plugins ) when run.

So a full command like like this:

cd modules/red && sparrowdo --bootstrap --no_sudo --docker=$container_name --repo=http://repo.westus.cloudapp.azure.com

And we have a test report:

alpine fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/main/x86_64/APKINDEX.tar.gz fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/community/x86_64/APKINDEX.tar.gz v3.11.2-29-ge1c437ff58 [http://dl-cdn.alpinelinux.org/alpine/v3.11/main] v3.11.2-28-g72cc761b5b [http://dl-cdn.alpinelinux.org/alpine/v3.11/community] OK: 11261 distinct packages available (1/17) Installing ncurses-terminfo-base (6.1_p20191130-r0) (2/17) Installing ncurses-terminfo (6.1_p20191130-r0) (3/17) Installing ncurses-libs (6.1_p20191130-r0) (4/17) Installing readline (8.0.1-r0) (5/17) Installing bash (5.0.11-r1) Executing bash-5.0.11-r1.post-install (6/17) Installing ca-certificates (20191127-r0) (7/17) Installing nghttp2-libs (1.40.0-r0) (8/17) Installing libcurl (7.67.0-r0) (9/17) Installing curl (7.67.0-r0) (10/17) Installing expat (2.2.9-r1) (11/17) Installing pcre2 (10.34-r1) (12/17) Installing git (2.24.1-r0) (13/17) Installing libbz2 (1.0.8-r1) (14/17) Installing perl (5.30.1-r0) (15/17) Installing perl-error (0.17028-r0) (16/17) Installing perl-git (2.24.1-r0) (17/17) Installing git-perl (2.24.1-r0) Executing busybox-1.31.1-r8.trigger Executing ca-certificates-20191127-r0.trigger OK: 68 MiB in 31 packages (1/1) Installing rakudo-pkg (2019.11-01) OK: 68 MiB in 32 packages ===> Searching for missing dependencies: File::Directory::Tree, Hash::Merge, YAMLish, JSON::Tiny ===> Updating cpan mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/cpan1.json ===> Searching for missing dependencies: MIME::Base64 ===> Installing: File::Directory::Tree:auth<labster> ===> Installing: Hash::Merge:ver<1.0.0>:auth<github:scriptkitties>:api<1> ===> Installing: MIME::Base64:ver<1.2.1>:auth<github:retupmoca> ===> Installing: YAMLish:ver<0.0.5> ===> Installing: JSON::Tiny:ver<1.0> ===> Installing: Sparrow6:ver<0.0.11> 1 bin/ script [s6] installed to: /opt/rakudo-pkg/share/perl6/site/bin ===> Updating p6c mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/p6c1.json ===> Updated p6c mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/p6c1.json ===> Updated cpan mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/cpan1.json 22:03:46 01/05/2020 [repository] index updated from http://repo.westus.cloudapp.azure.com/api/v1/index 22:03:53 01/05/2020 [create user red] uid=1000(red) gid=1000(red) groups=1000(red) 22:03:53 01/05/2020 [create user red] user red created [task check] stdout match <created> True 22:03:57 01/05/2020 [create directory /data/test/red] directory path: /data/test/red 22:03:57 01/05/2020 [create directory /data/test/red] directory owner: <red> 22:03:57 01/05/2020 [create directory /data/test/red] directory group: <red> 22:03:57 01/05/2020 [create directory /data/test/red] directory access rights: drwxr-xr-x [task check] stdout match <owner: <red>> True [task check] stdout match <group: <red>> True 22:04:01 01/05/2020 [bash: git checkout https://github.com/FCO/Red.git] /data/test/red 22:04:01 01/05/2020 [bash: git checkout https://github.com/FCO/Red.git] stderr: Cloning into '.'... 22:04:04 01/05/2020 [bash: last commit] commit e59b3146e2e2f7040aaa1d4af1b1924d79246a41 22:04:04 01/05/2020 [bash: last commit] Author: Fernando Correa de Oliveira <fernandocorrea@gmail.com> 22:04:04 01/05/2020 [bash: last commit] Date: Fri Jan 3 00:12:12 2020 +0000 22:04:04 01/05/2020 [bash: last commit] 22:04:04 01/05/2020 [bash: last commit] 0.1.4 22:04:04 01/05/2020 [bash: last commit] 22:04:04 01/05/2020 [bash: last commit] M Changes 22:04:04 01/05/2020 [bash: last commit] M META6.json 22:04:04 01/05/2020 [bash: last commit] M lib/Red.pm6 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] total 68 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 592 Jan 5 22:04 CONTRIBUTING.md 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 2598 Jan 5 22:04 Changes 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 384 Jan 5 22:04 Dockerfile 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 332 Jan 5 22:04 Dockerfile-no-config 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 427 Jan 5 22:04 Dockerfile-no-run 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 8902 Jan 5 22:04 LICENSE 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 5955 Jan 5 22:04 META6.json 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 123 Jan 5 22:04 Makefile 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 10509 Jan 5 22:04 README.md 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 482 Jan 5 22:04 Red.iml 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] drwxr-xr-x 2 red red 17 Jan 5 22:04 bin 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] -rw-r--r-- 1 red red 100 Jan 5 22:04 dist.ini 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] drwxr-xr-x 4 red red 83 Jan 5 22:04 docs 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] drwxr-xr-x 8 red red 82 Jan 5 22:04 examples 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] drwxr-xr-x 6 red red 69 Jan 5 22:04 lib 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] drwxr-xr-x 3 red red 4096 Jan 5 22:04 t 22:04:07 01/05/2020 [bash: cd /data/test/red && ls -l] drwxr-xr-x 2 red red 27 Jan 5 22:04 tools 22:04:12 01/05/2020 [bash: zef install Test::META] ===> Searching for: Test::META 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Searching for missing dependencies: META6, URI, License::SPDX 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Searching for missing dependencies: JSON::Class:ver<0.0.14+>, JSON::Class:ver<0.0.5+>, JSON::Fast 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Searching for missing dependencies: JSON::Marshal:ver<0.0.18+>, JSON::Unmarshal:ver<0.08+> 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Searching for missing dependencies: JSON::Name 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Installing: JSON::Fast:ver<0.10> 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Installing: JSON::Name:ver<0.0.4>:auth<github:jonathanstowe>:api<1.0> 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Installing: JSON::Marshal:ver<0.0.18>:auth<github:jonathanstowe>:api<1.0> 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Installing: JSON::Unmarshal:ver<0.08> 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Installing: JSON::Class:ver<0.0.14>:auth<github:jonathanstowe>:api<1.0> 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Installing: META6:ver<0.0.23>:auth<github:jonathanstowe>:api<1.0> 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Installing: URI:ver<0.3.0> 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Installing: License::SPDX:ver<3.7.1>:auth<github:jonathanstowe>:api<1.0> 22:06:55 01/05/2020 [bash: zef install Test::META] ===> Installing: Test::META:ver<0.0.16>:auth<github:jonathanstowe>:api<1.0> 22:06:57 01/05/2020 [install package(s): sqlite-libs.perl] fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/main/x86_64/APKINDEX.tar.gz 22:06:57 01/05/2020 [install package(s): sqlite-libs.perl] fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/community/x86_64/APKINDEX.tar.gz 22:06:57 01/05/2020 [install package(s): sqlite-libs.perl] v3.11.2-29-ge1c437ff58 [http://dl-cdn.alpinelinux.org/alpine/v3.11/main] 22:06:57 01/05/2020 [install package(s): sqlite-libs.perl] v3.11.2-28-g72cc761b5b [http://dl-cdn.alpinelinux.org/alpine/v3.11/community] 22:06:57 01/05/2020 [install package(s): sqlite-libs.perl] OK: 11262 distinct packages available 22:06:58 01/05/2020 [install package(s): sqlite-libs.perl] trying to install sqlite-libs ... 22:06:58 01/05/2020 [install package(s): sqlite-libs.perl] installer - apk 22:06:58 01/05/2020 [install package(s): sqlite-libs.perl] (1/1) Installing sqlite-libs (3.30.1-r1) 22:06:59 01/05/2020 [install package(s): sqlite-libs.perl] OK: 69 MiB in 33 packages 22:06:59 01/05/2020 [install package(s): sqlite-libs.perl] Installed: Available: 22:06:59 01/05/2020 [install package(s): sqlite-libs.perl] sqlite-libs-3.30.1-r1 = 3.30.1-r1 22:06:59 01/05/2020 [install package(s): sqlite-libs.perl] sqlite-libs 22:07:04 01/05/2020 [bash: zef install /data/test/red] ===> Searching for missing dependencies: DBIish, DB::Pg, UUID 22:07:04 01/05/2020 [bash: zef install /data/test/red] stderr: ===> Updating cpan mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/cpan1.json 22:08:36 01/05/2020 [bash: zef install /data/test/red] ===> Searching for missing dependencies: LibUUID, NativeHelpers::Blob 22:08:36 01/05/2020 [bash: zef install /data/test/red] ===> Searching for missing dependencies: NativeLibs:auth<github:salortiz> 22:08:36 01/05/2020 [bash: zef install /data/test/red] ===> Installing: UUID:ver<1.0.0>:auth<github:retupmoca> 22:08:36 01/05/2020 [bash: zef install /data/test/red] ===> Installing: NativeLibs:ver<0.0.7>:auth<github:salortiz> 22:08:36 01/05/2020 [bash: zef install /data/test/red] ===> Installing: LibUUID:ver<0.5>:auth<github:CurtTilmes> 22:08:36 01/05/2020 [bash: zef install /data/test/red] ===> Installing: DB::Pg:ver<0.6> 22:08:36 01/05/2020 [bash: zef install /data/test/red] ===> Installing: NativeHelpers::Blob:ver<0.1.12>:auth<github:salortiz> 22:08:36 01/05/2020 [bash: zef install /data/test/red] ===> Installing: DBIish:ver<0.5.19> 22:08:36 01/05/2020 [bash: zef install /data/test/red] stderr: ===> Updating p6c mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/p6c1.json ===> Updated p6c mirror: ht 22:08:36 01/05/2020 [bash: zef install /data/test/red] stderr: tps://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/p6c1.json ===> Updated cpan mirror: https://raw.githubusercontent.com/ugexe/Perl6-ecosystems/master/cpan1.json 22:08:40 01/05/2020 [bash: zef test] ===> Testing: Red:ver<0.1.4>:auth<Fernando Correa de Oliveira>:api<2> 22:12:23 01/05/2020 [bash: zef test] [Red] Use of Nil in numeric context 22:12:23 01/05/2020 [bash: zef test] [Red] in block at /data/test/red/.precomp/902863C6FF81B0B9901E5C42393B9B7181A4AE04/F2/F2E53992C6FFEDC5DC3B09E6E9D69BBEB965D56B line 1 22:12:23 01/05/2020 [bash: zef test] ===> Testing [OK] for Red:ver<0.1.4>:auth<Fernando Correa de Oliveira>:api<2> 
Enter fullscreen mode Exit fullscreen mode

Further thoughts

It's possible to test against various Rakudo versions once I'll add ability to pass a Rakudo version to bootstrap process ( now the latest version is installed ).

If one need to add some custom tasks that are not covered by Sparrow6 DSL, it's possible through Sparrow6 tasks API:

cat modules/red/data/tasks/hello/task.pl6

say "Hello Raku!", config()<var1>; 
Enter fullscreen mode Exit fullscreen mode

Then in sparrowfile just say this:

task-run "data/tasks/hello", %( var1 => "value" # parameter ) 
Enter fullscreen mode Exit fullscreen mode

You can even share your custom tasks across team converting them into Sparrow6 plugins, and make reusable across many test scenarios.

Say you create a plugin named "hello", based on "data/tasks/hello" task. This is how you run it in a test scenario:

task-run "my hello plugin", "hello", %( var1 => "value" # parameter ) 
Enter fullscreen mode Exit fullscreen mode

Conclusion

Sparrow6 is simple to use and flexible tool to automate test scenarios to check Raku modules against different OS and Rakudos, making multi-platform testing dead easy.

Follow to a proposal ticket and share your opinion.


Thank you for reading.

Top comments (0)