I spent a lot of time exploring Test::More in the last step. That’s because I want to start building larger projects, and testing is a vital part of most projects. Another major part is a properly organized workspace with a script that can simplify testing or other tasks.
Creating a Simple Project
A nice Parrot project layout includes a
t folder for tests, a
lib folder for library code, and a
setup.pir file to drive the whole thing.
$ mkdir simple-pir $ mkdir simple-pir/t $ mkdir simple-pir/lib $ cd simple-pir
What gets placed in
Not much, considering how much it does.
setup.pir takes advantage of the Parrot distutils module for a whole range of tasks.
All I’m concerned about today is testing, so my setup is going to be rather lightweight.
This is not exciting code, but it is enough to see what distutils can give me.
The first command line parameter is shifted onto a dummy register variable, because I don’t really care about the name of
setup.pir from within
Then I load the distutils bytecode so I can get access to the
setup.pir will get more complicated as we go on, and you will definitely see more complex
setup.pir files out in the wild, but this will get us started.
$ parrot setup.pir help usage: parrot setup.pir [target|--key value]* Default targets are : build: Build the library. test: Run the test suite. install: Install the library. uninstall: Uninstall the library. clean: Basic cleaning up. update: Update from the repository. plumage: Output a skeleton for Plumage sdist: Create a source distribution bdist: Create a binary distribution help: Print this help message.
What happens when I tell
setup.pir that I want to test?
$ parrot setup.pir test Files=0, Tests=0, 0.000 wallclock secs Result: NOTESTS
Well of course it failed.
There aren’t any test files, and
setup.pir wouldn’t know how to run them if there were!
I’ll fix the second part first.
Parrot allows you to use named parameters for some subroutines, and
setup takes full advantage of that feature.
If you’re used to Perl or Ruby, named parameters look a lot like a hash.
That’s close enough for our purposes.
A named parameter generally follows a simple format:
'<key-1>' => '<value-1>'
distutils.pir is a well-documented module, and you can find details about the many options by checking the documentation.
$ perldoc /usr/local/lib/parrot/3.0.0/library/distutils.pir
I only care about a single option:
prove_exec, which tells
setup what program will be used to run the tests.
Well, Parrot is a VM.
Your tests can be in PIR, NQP, Rakudo, or even a language of your own design.
These Babysteps are about Parrot PIR, so it makes sense that the tests will be in the same language.
Oh yes, the tests. Let’s write one. I’ll follow the convention I see in the Perl world of a number followed by a description for the test filename, and the test itself will be for a simple area calculating function.
So - this should fail, right?
$ parrot setup.pir test t/01-radius.t .. Dubious, test returned 1 Failed 1/1 subtests Test Summary Report ------------------- t/01-radius.t (Tests: 0 Failed: 0) Non-zero exit status: 1 Parse errors: Unknown TAP token: "Could not find sub area_of_circle" Unknown TAP token: "current instr.: 'main' pc 40 (t/01-radius.t:13)" Bad plan. You planned 1 tests but ran 0. Files=1, Tests=0, 0.021 wallclock secs Result: FAIL test fails current instr.: 'setup' pc 883 (runtime/parrot/library/distutils.pir:376) called from Sub 'main' pc 29 (setup.pir:18)
Parrot didn’t just tell us that the test failed.
It also told us about some unexpected output from our test script.
What’s that unexpected output?
Oh, something about not having a subroutine called
Let’s fix that by adding a new library file called
lib/area.pir, and adding the missing subroutine.
This is code borrowed from step 2 and dropped into a subroutine.
Don’t forget to include this library code from your test file.
Did it work?
$ parrot setup.pir test t/01-radius.t .. ok All tests successful. Files=1, Tests=1, 0.016 wallclock secs Result: PASS
Hold on a second.
I snuck an extra argument back when I wrote the
What was that all about?
Well, Jonathan Leto explained to me that
is takes an additional argument for precision.
This is useful in the fuzzy world of floating point math on a modern computer.
1e-6 requirement asks Parrot to make sure
actual_area look the same down to six places past the decimal point.
This approach of writing the tests before you write the code is called TDD, for Test Driven Development. I like TDD because I’m basically describing the next thing I want my library or application to do. That’s perfect for me, since I’m such a chatty person. Well, I’m chatty when typing at the computer.
You don’t need to follow a test driven approach, but other developers will like you more if you consistently test the code you write. The easiest way to consistently test it is to write the test before you write the code.
Combining what we’ve learned about Test::More with
setup.pir allows us to confidently build more complicated applications, testing as we go along.
It is true that all we know how to do with
setup.pir at this point is ask it to run tests for us, but even that can save a lot of work.
I don’t know about you, but I’m ready to take another look at that star catalog.