Introducing Lemon
I’ve coded A LOT of C++ since I started college, most of it relatively simple and not requiring much testing. However, now that I’m getting into the higher level classes, I noticed that unit testing would be amazingly helpful in tracking down some of the nasty bugs that pop up when you try to implement a quicksort algorithm or something else slightly more than trivial. After much frustration with several larger “production” frameworks I decided to write my own amazingly simple C++ unit-testing framework based on Symfony’s lime framework, which is in turn based on perl’s Test::More.
So without much more adieu I present to you Lemon, a simple unit testing framework along with a few examples. First of all, the most basic function is initializing the lemon class:
Lemon lemon(5); lemon.end();
What does this do? Well, firstly it initializes the lemon library and the number passed in to the constructor tells it how many tests your are expecting to run with this particular instance. This feature allows you to chunk up tests into cases, and also shows you that this framework is so simple it doesn’t even count tests for you. Finally, lemon.end() ends the current test harness and prints out the results, the output would look something like this:
1...5 Looks like you passed all 0 tests
Now let’s have a look at some code I actually used to test a heap that I wrote. First I tested that heap initially contained no items:
Heap heap; lemon.is(heap.isEmpty(), "Heap is initially empty");
Now naturally if we’re doing test-driven development this test should fail right off the bat, and our output should look something like this:
1..7 not ok 1 - Heap is initially empty # Failed test # Looks like you failed 1 tests of 7.
Now we tweak our isEmpty function to check properly whether or not the heap is empty, and we should get something like this:
1..7 ok 1 - Heap is initially empty # Looks like you passed 1 tests of 7.
Now to add another function, let’s add a function to get the value on the top of the heap. Since we have no data, we’ll make our heap throw an exception:
try {
heap.dequeue();
lemon.fail("Dequeue with no items throws an error");
} catch(std::exception& error) {
lemon.pass("Dequeue with no items throws an error");
}
And that is pretty much all there is to getting up and running with lemon. I’ve attached the C++ file, please post any improvements to library as I’m always looking to make it better
DevLog
What I’m Working On
Today I started work on an open-source (once it’s more complete and production worthy) system for converting string data to and from Unicode, namely UTF-8. This will hopefully be integrated into the symphony engine, but I’m building it in a separate namespace I use for my more compartmentalized experiments, the namespace I call fugue. It contains a lot of useful little code chunks that I’ve spent time developing but that aren’t really part of any larger whole, so I decided to give them their own whole.
So what else is in this namespace? Well actually not a whole lot right now, but in time that will probably change as I add even more little nuggets of goodness to it. Right now I’ve got a generic template based algorithsm for the merge sort and the selection sort, as well as a ge neric binary search also based on templates. After today it also contains some code for converting C-Strings to UTF-8 and then converting UTF-8 to C-Strings. The code is very preliminary and I plan to refactor it a bit tomorrow. However, all this work only took a small fraction of the day. The rest of the day I spent investigating the GNU make utility, because I decided that if any of my projects were gonna grow much larger they would have to be compiled somehow, and also as I added files I didn’t want to continue to add make targets. I spent the rest of day finding out how exactly to do this.
Adventures In GNU make
So far in my computer sciences courses I’ve received a very basic education in using the GNU make utility, but it was nowhere near comprehensive, so today I decided to elaborate on it a little bit. Here’s the make script I started off with, it’s nowhere near monstrous, but I’m lazy so I decided adding even one more make target manually was going to be unbearable
# Makefile for fugue::unicode # Copyright (C) 2007 Eric Scrivner CXX=g++ CXXFLAGS= -g -W -Wall -Werror -ansi -pedantic -I ./include CXXFLAGS += -I ~/c++/unittest++/UnitTest++/src LIBS = libs/libUnitTest++.a## Make the whole enchilada ## default: all all: $(OBJECTS) @echo "Compiling fugue::unicode tests..." @$(CXX) $(CXXFLAGS) -o bin/a.out TestUnicode.cpp $(OBJECTS) $(LIBS) ## Pattern replacement for compiling ## unicode.o : unicode.cpp @echo "Compiling unicode.cpp..." @$(CXX) $(CXXFLAGS) -o objs/unicode.o -c src/unicode.cpp ## Cleanup when we're done ## clean: @echo "Cleaning build directory..." @-rm -rf src/*~ include/*~ objs/*.o bin/a.out
Not a horrible script by any stretch, but it could certainly use a little bit of work to make it meet the goals I had in mind. So I set out to figure out how I could do this. It took an awful lot of reading, but I found some decent documentation here. Pattern substitution looked pretty tantalizing after reading a bit I decided to take a crack at it using the always enjoyable Vim. Firstly I needed to define objects in terms of all the source files, so I had to change a few lines and ended up with a new definition of OBJECTS:
SRC = src/objects.cpp
OBJECTS = $(pathsubst %.cpp, %.o, $(SRC))
...
%.o : %.cpp
@echo "Compiling $<..."
@$(CXX) $(CXXFLAGS) -o $@ -c $<
This, however, doesn’t work quite like expected, and is happy enough to spit out all the .o files in the src/ directory. So it was back to the books to engineer some sort of trickery that would let this work. Here’s what I eventually came up with in its whole a complet’ish form:
# Makefile for fugue::unicode # Copyright (C) 2007 Eric Scrivner CXX=g++ CXXFLAGS= -g -W -Wall -Werror -ansi -pedantic -I ./include CXXFLAGS += -I ~/c++/unittest++/UnitTest++/src LIBS = libs/libUnitTest++.a VPATH = src SRC = unicode.cpp OBJECTS = $(patsubst %.cpp, objs/%.o, $(SRC))## Make the whole enchilada ## default: all all: $(OBJECTS) @echo "Compiling fugue::unicode tests..." @$(CXX) $(CXXFLAGS) -o bin/a.out TestUnicode.cpp $(OBJECTS) $(LIBS) ## Pattern replacement for compiling ## objs/%.o : %.cpp @echo "Compiling $<..." @$(CXX) $(CXXFLAGS) -o $@ -c $< ## Cleanup when we're done ## clean: @echo "Cleaning build directory..." @-rm -rf src/*~ include/*~ objs/*.o bin/a.out
It’s not beautiful, but it works for now, and I suppose that is what matters at first - before the refactoring takes place.
Conclusion
My first little trip into the land of GNU make was actually quite enjoyable. I enjoy using GNU tools for whatever reason, some might call it a masochistic impulse, but I usually find the time spent learning how to use the tools is rewarded with a boost in productivity. My writing style is developing, and hopefully in time these logs will become more fluid and enjoyable to read, but that’s why I’m doing this. Next time perhaps I shall discuss my generic sorting/searching algorithms, I’m actually quite proud of them. Until next time
—————-
Now playing: Murder By Death - The Big Sleep
via FoxyTunes
Adventures In Agile Development
For the last few days now I’ve been trying my hand at developing software with some planning done beforehand. My usual style is to just dig right in and design as I go - something which has been leading me astray lately as my software projects increase in size. I noticed that just as all the objects in my program should have been working together, they instead would start to degenerate into clunky behemoths with nasty interfaces. Thus, as I abstracted farther from the low-level the designs got worse and worse. I had heard about agile development from a lot of sources, but I found most of the unit-testing libraries available (back then) clunky, and decided the whole thing wasn’t worth the trouble.
Taking The Dive
So why did I get back into it? The ugly code was a big part of it, but it was until I saw the blog of Noel Llopis that I decided the whole thing might be worth getting into again. Firstly because he has some great advice for applying agile
development to game programming and secondly because he had an incredibly helpful and fairly comprehensive review of current unit testing solutions for c++. All of this got me excited about unit testing again, because I finally saw that with a little ingenuity it can be applied to game programming in a remarkably elegant way.
After carefully going over all the unit testing solutions and reading his opinions as well as checking them out myself, I finally decided that the library Llopis wrote, UnitTest++. Of all the libraries in his article - several of which I had already tried, and several of which I tried for the first time - I found UnitTest++ was actually the easiest to set up and start using. This is where I was at about three days ago, and now that I’ve got some initial practice working a new toy game engine from scratch with these methodologies, I see just how much fun writing with unit testing can be, and I may very well be hooked.
How It Works (For Those Who Are Interested)
The idea behind Test Driven Development (TDD), which is the part of agile development I’m mostly talking about is that before you write a single line of code for some object you test the functionality of that object. This you do one piece at a time, for instance if you’re working on a function to add two objects your unit test might look like this:
TEST(TestThatAddingTwoZerosIsZero)
{
CHECK_EQUAL(0, Add(0, 0));
}
At the point above the function Add shouldn’t even exist, so the above code won’t even compile. Now we add a dummy function so that we can compile and get this test to work, something like:
int Add(int lhs, int rhs)
{
return 0;
}
Now we write another test to break this form of Add as we haven’t yet achieved an actual addition function. Something like this will do:
TEST(TestThatAddingAnyNumberAndZeroYieldsTheCorrectResult)
{
CHECK_EQUAL(3, Add(0, 3));
}
Now if we compile we can watch this test fail miserably, which is good, because we know it should. Now we revise add a little bit to fix this:
int Add(int lhs, int rhs)
{
return (lhs + rhs);
}
Perfect, now our new test should pass and so should our old one. In this way you gradually build up to a true addition function built to all your specifications and fully tested so if future changes break it, you will know.
While this is an incredibly simplified view of the agile development process, it shows many of the basic facets such as incremental building and to “sanity check” your tests to make sure they fail before they succeed.
The Result So Far
In a strange way just using unit tests has made me more passionate about programming than I’ve been for a long time. I see now that there is still so much about programming to learn. I’ve started reading and actually learning from Exception C++, More Exceptional C++ and Code Complete 2, and I’m starting to see that writing software is a field that is much richer than I’ve been giving it credit for being. I’m glad I gave Test Driven Development (TDD) a second try.
—————-
Now playing: The Mars Volta - Take the Veil Cerpin Taxt
via FoxyTunes