Testing C++ in Ruby with Automake and RSpec

Since I’m a C++ newbie, I felt I needed more confidence in my code. As I’ve been programming in Ruby for almost 2 years now, I decided to look for a way to test my C++ code in Ruby. I found an excellent post by Dean Wampler that deals with just this topic. Please take the time to read this fascinating article before continuing. I happened to have been playing with autotools for a different C++ project last week, so I decided to use them for this one too. I decided not to put the SWIG stuff into the Makefile, but rather use a shell script. Automake will make sure the modules get built the way they need to be built, and I can put everything described in Dean’s article in a little shell script which builds the wrapper object for Ruby. I’m using a file src/units.i which loads some module headers for SWIG to wrap. This code is now irrelevant. I’ve found a way to generate this by using mkmf

#!/bin/sh

CXX=/usr/bin/g++
SRC_DIR=src
CFLAGS="-fPIC -fno-strict-aliasing -g -O2"
BIN_DIR=bin
RUBY_LIBS=/usr/lib/ruby/1.8/i486-linux
SWIG=/usr/bin/swig

cd $SRC_DIR

SRCS=`ls *.cpp | sed "s/.*_wrap\.cpp//"`
OBJS=`ls *.o`

`${CXX} -I. -I${RUBY_LIBS} ${CFLAGS} -c ${SRCS}`
`${SWIG} -c++ -ruby -Wall -o units_wrap.cpp units.i`
`${CXX} -I. -I${RUBY_LIBS} ${CFLAGS} -c units_wrap.cpp`
`${CXX} -shared -L. -rdynamic -Wl,-export-dynamic -L/usr/local/lib -o units.so ${OBJS} -lruby1.8 -lpthread -ldl -lcrypt -lm -lc`

You could extend it to take options for which module to build. As in my previous autotools example I have a ‘src’ dir containing my C++ code, and a new ‘spec’ dir containing my RSpec specifications in Ruby. The spec_helper.rb in the spec dir is taken from Dean’s example. Here’s an example of a spec for a FileReader class

require File.dirname(__FILE__) + '/spec_helper'
require 'units'

describe Units::FileReader do
  it "should be a constant on module Units" do
    Units.constants.should include('FileReader')
  end
end

describe Units::FileReader, ".new" do
  it "should create a new object of the type FileReader" do
    fr = Units::FileReader.new("file.txt")
    fr.filename.should_be "file.txt"
  end
end

describe Units::FileReader, "#openFile" do
  it "should open a file" do
    fr = Units::FileReader.new
    fr.openFile("file.txt").should_be true
    fr.filename.should_be "file.txt"
  end
end

describe Units::FileReader, "#closeFile" do
  it "should close a file" do
    fr = Units::FileReader.new
    fr.closeFile.should_be true
    fr.filename.should_be ""
  end
end

describe Units::FileReader, "#readChar" do
  it "should read a char" do
    fr = Units::FileReader.new("file.txt")
    char = fr.readChar
    char.should_be fr.ch
    char.is_a?(String).should_be true
    char.length.should_be 1
  end
end

describe Units::FileReader, "#readLine" do
  it "should read a line from the file" do
    fr = Units::FileReader.new("file.txt")
    line = fr.readLine
    line.is_a?(String).should_be true
    line.match(/^.*\n$/).should_not be_nil
  end
end

One little caveat I ran into: you have to expose the private variables of you C++ class with public accessors to be able to use them. Dean’s example shows these two little methods that do this, but it wasn’t obvious to me as my brain is cooked by programming in Ruby for too long. Hope this was helpful for anyone who wants to get started on testing their C++ code in Ruby easily.


Trackbacks

Use the following link to trackback from your own site:
http://blog.aczid.nl/trackbacks?article_id=9


Me elsewhere: