Coloured output from Erlang EUnit tests using Rake

I’m spending more and more time fiddling around with Erlang lately, and I’ve recently started using EUnit to write unit tests for my code. As I spend my working days writing Ruby code, I’ve found using Rake files to compile and run my code a lot easier to deal with than scary Makefiles.

The Rakefile I’ve been using to compile my code is from Building Erlang with Rake , and whilst working on a Rake task to run my unit tests I came across Using Rake for Erlang Unit Testing . Based on this, I’ve written a rake task to run my EUnit tests and give me the coloured output I’m used to from Ruby unit testing frameworks such a Rspec.

Here's the Rakefile:

require 'rake/clean'

INCLUDE = "include"
ERLC_FLAGS = "-I#{INCLUDE} +warn_unused_vars +warn_unused_import"

SRC = FileList['src/*.erl']
OBJ = SRC.pathmap("%{src,ebin}X.beam")
CLEAN.include("ebin/*.beam")

directory 'ebin'

rule ".beam" => ["%{ebin,src}X.erl"] do |t|
  sh "erlc -pa ebin -W #{ERLC_FLAGS} -o ebin #{t.source}"
end

desc "Compiles all .erl files in src to .beam files in ebin"
task :compile => ['ebin'] + OBJ
task :default => :compile

desc "Compiles all .erl files in src to .beam files in ebin"
task :compile_with_tests do
  FileList['src/**/*.erl'].each do |src|
    sh "erlc -D EUNIT -pa ebin -W #{ERLC_FLAGS} -o ebin #{src}"
  end
end

desc "Run all tests"
task :test => :compile_with_tests do
  modules = OBJ.map {|o| File.basename(o, ".beam") }
  output = `erl \
  -noshell \
  -pa ebin \
  -eval 'eunit:test([#{modules.join(",")}], [verbose])' \
  -s init stop`

  output.each_line do |line|
    case line
    when /= (EUnit) =/
      print line.gsub($1, green($1))
    when /\*failed\*/
      print red(line)
    when /(\.\.\..*ok)/
      print line.gsub($1,green($1))
    when /Failed:\s+(\d+)\.\s+Skipped:\s+(\d+)\.\s+Passed:\s+(\d+)\./
      puts "#{red("Failed: #{$1}")} Skipped: #{$2} #{green("Passed: #{$3}")}"
    when/(All \d+ tests passed.)/
      print green(line)
    else
      print line
    end
  end
end

def green(text)
  "\e[32m#{text}\e[0m"
end

def red(text)
  "\e[31m#{text}\e[0m"
end

The :compile_with_tests task will, as expected, compile all the *.erl source files in the src/ directory with the -D EUNIT flag which will compile the tests in. The :test task will run the tests and give simple coloured output. We can test the task using the following simple Erlang module:

-module(utils).
-compile(export_all).

-ifdef(EUNIT).
-include_lib("eunit/include/eunit.hrl").
-endif.

double(X) ->
  X * 2.

triple(X) ->
  X * 3.

% Tests

-ifdef(EUNIT).

double_test_() ->
  {inparallel,
    [
      ?_assertEqual( 100, utils:double(50)),
      ?_assertEqual( 50, utils:double(20))
    ]
  }.

triple_test_() ->
  {inparallel,
    [
      ?_assertEqual( 9, utils:triple(3)),
      ?_assertEqual( 75, utils:triple(25))
    ]
  }.

-endif.

Running rake test produces the following:

barry@pinga:~/code/erlang$rake test
(in /Users/barry/code/erlang)
erlc -D EUNIT -pa ebin -W -Iinclude +warn_unused_vars +warn_unused_import -o ebin src/utils.erl
======================== EUnit ========================
module 'utils'
  utils:21: double_test_...ok
  utils:22: double_test_...*failed*
::error:{assertEqual_failed,[{module,utils},
                           {line,22},
                           {expression,"utils : double ( 20 )"},
                           {expected,50},
                           {value,40}]}
  in function utils:'-double_test_/0-fun-2-'/1

  utils:29: triple_test_...ok
  utils:30: triple_test_...ok
  [done in 0.005 s]
=======================================================
Failed: 1 Skipped: 0 Passed: 3

Fixing this error and rerunning the rake task outputs:

barry@pinga:~/code/erlang$rake test
(in /Users/barry/code/erlang)
erlc -D EUNIT -pa ebin -W -Iinclude +warn_unused_vars +warn_unused_import -o ebin src/utils.erl
======================== EUnit ========================
module 'utils'
  utils:21: double_test_...ok
  utils:22: double_test_...ok
  utils:29: triple_test_...ok
  utils:30: triple_test_...ok
  [done in 0.005 s]
=======================================================
  All 4 tests passed.

One issue with this is that the :compile_with_tests task will always recompile source files regardless of whether they need recompiling or not, the standard :compile task only compiles when the source files have been modified. Other than that, this should work as intended. I'm sure someone will now tell me that this functionality is available in EUnit as it is, but I did Google for this, I promise.


Tags : erlang, eunit, programming, rake, ruby

blog comments powered by Disqus