Alexis Bauchu - On est pas des machines

Coders are also human beings

jeudi, décembre 3 2009

Unit tests with Boost.Test

I've just started using Boost.Test to get to know unit testing a bit better. I use it to try a Fixed Point number class that I made. For those who wonder what a fixed point number is, in short, it's a float in an integer. This is useful when your hardware doesn't have a FPU, like the Nintendo DS. But this is not my point, I'll discuss that in a proper blog post. This is about a problem I had when I tried to compile my unit test program on Linux.

Installing the Boost library on an Ubuntu system is really easy. It's just a sudo apt-get install libboost-dev or a click on this link (note: this link uses the apt protocole that only works on supporting systems, such as Debian and derivatives). On Windows, I preferred to recompile the library than to download the installer because it required me to fill a form for a company I didn't know and don't care about. This was actually not hard: Boost has it's own build system capable of... building itself! Yes! The builder first builds itself and then builds the library.

Compilation is actually not necessary. At least not for all the modules of Boost. In the case that interest me, Boost.Test can work either with headers only or with a runtime version. I still don't know what's in the runtime that you can't find in the headers, but I'll figure this out another time. Trying the Unit Test Framework was a breeze. Here's an extract:

#include "FixedPointMath.hpp"
 
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
 

BOOST_AUTO_TEST_CASE( Constructors )
{
    FP12 a(12);
    FP12 b(23);
    FP12 c;
    FP12 d( b );
    FP12 e( 3.4f );
    FP12 f = FP12(42) / FP12(6);
    FP12 ff = FP12(45) / FP12(6);

 
    BOOST_CHECK_EQUAL( a.toInt(), 12 );
    BOOST_CHECK_EQUAL( b.toInt(), 23 );
    BOOST_CHECK_EQUAL( c.toInt(), 0 );
    BOOST_CHECK_EQUAL( d.toInt(), 23 );
    BOOST_CHECK_EQUAL( e.toInt(), 3 );
    BOOST_CHECK_EQUAL( f.toInt(), 7 );
    BOOST_CHECK_EQUAL( ff.toInt(), 7 );

}

And this is all you have to do. The macro BOOST_TEST_MAIN takes care of initializing a main entry point for you and BOOST_AUTO_TEST_CASE creates a test case and a default test suite without any work from you. You just specify the name of the test case. Is the case where all the tests are ok, the output looks like this:

Running 1 test case...

*** No errors detected

Introducing an error on purpose gives you this;

Running 1 test case...
UnitTestFixedPoint.cpp(34): error in "Constructors": check ff.toInt() == 8 failed [7 != 8]

*** 1 failure detected in test suite "Master Test Suite"

You can then move on to more complicated tests. Anyway, this piece of code doesn't compile on Linux. You can't just do g++ UnitTestFixedPoint.cpp -o UnitTestFixedPoint because, obviously, you need to link against the Unit Test Framework and you have to tell GCC which library to pick. Automagically, there's nothing to do in Visual Studio and Boost.Test knows how to tell it what the right library is. Fine. Do:

g++ UnitTestFixedPoint.cpp \
   -o UnitTestFixedPoint \
   -lboost_unit_test_framework

>Undefined reference to `main'

Oh come on! Now what? Some research on the internet bring the following result:

For your program to successfully link with the dynamic library the flag BOOST_TEST_DYN_LINK needs to be defined both during dynamic library build and during your program compilation.

Why didn't you say so? Ok, now with some patching for the 'Nux version only, this works much better:

#define BOOST_TEST_MAIN
 
#if !defined( WIN32 )
    #define BOOST_TEST_DYN_LINK
#endif
 
#include <boost/test/unit_test.hpp>

More on that when I'm satisfiyed with my tests!

mercredi, novembre 25 2009

Meta-Balls

Don't confuse this with meat balls, this has absolutely nothing to do with it! My friend Garry Williams posted this video on Youtube. These are meta-balls he computed in real time:

Meta balls in 2D are quite simple. Take an image. Each of you meta balls emit a field, the closer you are from the emitter, the stronger is the field. Then, your color each pixel depending on the value of field at that position. The field doesn't necessarily describe a circle, it can be elipses or anything. Anyway, it can look like that:

2D Meta Balls

Is this case, pixels are colored in black where the field is the strongest and uses gradients of orange and yellow for the other values. So when these shapes move around and meet, they seem to "merge" in an hypnotic lava lamp effect!

Well that's pretty easy in 2D space, because you're computing your values in a discrete finite domain. But how do you do that in 3D space, where the values are in a continuous domain? Well you have make it discrete. And then use voxels (volumetric pixels) and find a way to render them, which is not trivial (our GPUs only know how to render triangles).

Any questions? Wanna know more? Well ask Garry! Hahaha, just kidding, ask your questions in the comments.

Note that this is work in progress.
Garry's meta-balls project page.
Download the application.
Download the source code.

jeudi, septembre 3 2009

Update of the 'Share on reader' command for Ubiquity

I've finally updated that command that allows to share content on your Google Reader Shared Items page (providing you've got one!). The new version allows you to type the name without the hyphens (no more share-on-reader), you can also type share using reader, or note using reader, or note with reader.For those of you who don't know Ubiquity, head up to the project page.

You can install the "Share on reader" command by going there and clicking subscribe in the popup above the page.

CmdUtils.CreateCommand({
  names: [ "share on Reader", "note with Reader", "share using Reader", "note using reader" ],
  homepage: "http://abauchu.net/",
  icon: "http://www.google.com/reader/ui/favicon.ico",
  author: { name: "Alexis Bauchu", email: "alexis.bauchu@gmail.com" },
  license: "MPL",
  description: "Shares content Google Reader's shared stuff page and allow you to add a note.",
  help: "This uses Google Reader, so you need a Google account to use this command.",

 
  arguments: [ 
    { role: 'object', nountype: noun_arb_text, label: 'extract' }
  ],

 
  execute: function (args) {
   var cmd = "var b=document.body;var GR________bookmarklet_domain='http://www.google.com';if(b&&!document.xmlVersion){void(z=document.createElement('script'));void(z.src='http://www.google.com/reader/ui/link-bookmarklet.js');void(b.appendChild(z));}else{}";
   var doc = Application.activeWindow.activeTab.document;
   var body = doc.body;
   void (cript = doc.createElement('script'));
   void (code = doc.createTextNode(cmd));
   void (cript.appendChild(code));
   void (body.appendChild(cript));
  }

});

lundi, juin 22 2009

Bash Optimisation

Lately at work, I've been working on optimizing a bash script that was used to build the data of a game. Its role is to parse the game directory and for each file, match it with a building rule. If there's no rule for a file, well, then nothing happens. If there's a rule, then dates of the source file and the built file are compared, and if the source file is more recent, then the file is rebuilt. Yes, that's pretty much the basics of a configuration manager, but in this precise case Make wasn't an option.

What really bothered me in that tool, was its extreme slowness. Rebuilding modified data lacked a bit of punch, but more importantly, parsing data that hadn't been modified was slow to death. Even when a single value was modified in a file, it was taking at least 15 seconds to rebuild the script. I know this isn't too bad, but let's put this into perspective: doing nothing on about 300 files with a quad-core processor at 2Ghz was taking 15 seconds. This was already pretty frustrating as it was. Now, consider that the game contains at least 7400 files, which means when someone changes a file, you can't always know which one, so you have to rebuild the whole directory. Do the math: this blocks your machine for at least 6 minutes, just to do nothing. And that's a minimum! Because many files are rebuilt anyway. And you do this many many times a day. Fast iterating is key to success in game development, and that was nothing like it. Here is a chart with approximative time I benchmarked:

Chart: Time to build x files

At first, I thought that the problem was Bash. Browsing every file in the directory recursively was maybe slow in shell script. But it wasn't. So I thought maybe comparing the dates was long. But it wasn't. And then I saw function that was matching file extensions with rules. It was looking up extension files in an array, but had to do string manipulations because each element was of the form "extension ruleName". And it was checking the whole array for every single file you had to build! So I made improvements there.

The array looked like this:

ACTIONS=( 
"txt DoStuffForTxt"
"avi EncodeVideo"
"lua PreCompileLua"
"etc"
)

It didn't go has I expected because the building time got longer. My solution was to replace the "extension rule" array with an associative array. But associative arrays are not supported in Bash! (even though the manual says so). So I did some hacking to circumvent that (I'll talk about it in another post). I finally understood that my solution was using to much "echo"s, and removing them improved the building time. But not much. I couln't think of anything else slowing the thing, because the rest was pretty trivial, so started a hunt for "echo"s. And there it was.

In each build rule, the second line was:

echo "Extension: $ext" >> log.txt

This seems harmless of course, nevertheless, this can eat a lot of time if log.txt is actually a big file. My intuition was right and I discovered that the file was about 8Mo big. After all this investigation, the real solution to the problem was to add

echo "" > log.txt

somewhere at the beginning of the script. Then the building time improved a lot:

In fact now, trying to rebuild even a 1000 files with no changes takes less than 5 seconds. So mark my words: when you do optimisation of code, any type of code, first get a real good overview of the performances and track the lines creating bottlenecks. Unlike me, you'll end up saving a lot of time! Plus, if you're doing some bash scripting, remember that echo is quite greedy and avoid it as much as you can!

jeudi, janvier 8 2009

Ubiquity command: share-on-reader and share-on-reader-secure

The share-on-reader command is the first ubiquity command I've ever coded. I made it just to try ubiquity's command editor, but I wouldn't expect people to use it. It's basically a wrapper of Google Reader's bookmarklet that allows you to select (or not) text from a web page, and share it with a note on your Google Reader Shared Stuff Page (providing you've got a Google Account and activated Google Reader). It's that simple. You can subscribe to that command on that page. Here is the code:

CmdUtils.CreateCommand({
  name: "share-on-reader",
  homepage: "http://abauchu.net/",
  icon: "http://www.google.com/reader/ui/favicon.ico",
  author: { name: "Alexis Bauchu", email: "alexis.bauchu@gmail.com"},
  license: "MPL",
  description: "Shares content Google Reader's shared stuff page and allow you to add a note.",
  help: "This uses Google Reader, so you need a Google account to use this command.",
 
  takes: {"your selection": noun_arb_text},
  preview: function( pblock, selection ) {
    pblock.innerHTML = "Shares \"" + selection.html + "\" with Google Reader and allows to add a note";
  },
  execute: function(selection) {
   var cmd = "var b=document.body;var GR________bookmarklet_domain='http://www.google.com';if(b&&!document.xmlVersion){void(z=document.createElement('script'));void(z.src='http://www.google.com/reader/ui/link-bookmarklet.js');void(b.appendChild(z));}else{}";
   var doc = Application.activeWindow.activeTab.document;
   var body = doc.body;
   void (cript = doc.createElement('script'));
   void (code = doc.createTextNode(cmd));
   void (cript.appendChild(code));
   void (body.appendChild(cript));
  }
});

The code is rather long, because at the time there wasn't any command like CmdUtils.makeBookmarkletCommant(). I could have updated it, but I like it like that. You know, it's my first one.

But this command doesn't work with the https version of Google Reader (I should say the bookmarklet doesn't work, because I virtually didn't code anything). Someone commented about it in this blog: he uses the customizegoogle extension which automatically redirects the normal url to the secured one (https). And it breaks the command. So I tried some stuff, and came with a solution: in fact, on the https version of the website, Google provides ANOTHER bookmarklet, working this time. Simple! You can subscribe here. Here's the code:

CmdUtils.makeBookmarkletCommand({
  name: "share-on-reader-secure",
  url: "javascript:var%20b=document.body;var%20GR________bookmarklet_domain='https://www.google.com';if(b&&!document.xmlVersion){void(z=document.createElement('script'));void(z.src='https://www.google.com/reader/ui/link-bookmarklet.js');void(b.appendChild(z));}else{}"
});

Reminder: makeBookmarkletCommand

This is just a reminder: how to make a command out of a bookmarklet. For example, a command that embed a Flickr photo (the example is taken from Aza Raskin's blog).

CmdUtils.makeBookmarkletCommand({
name: "Embed Flickr Photo",
url: "javascript:(function(){if(window.page_p)window.open('http://www.elsewhere.org/mbedr/?p='+window.page_p.id);%20else%20alert('No%20Flickr%20photo%20found.');})()"
});

Replace the url by your bookmarklet code.

mercredi, décembre 31 2008

Ubiquity: makeSearchCommand template

This is just a reminder of the search command template. It allows you to create in no time what must be the most used feature of Ubiquity at the moment.

Use the command-editor command, and type the following code in the editor:

CmdUtils.makeSearchCommand({
  name: "your-function-name",
  url: "http://url-to-my-search-engine/?q={QUERY}",
  icon: "http://url-to-my-search-engine/favicon.ico",
  description: "Searches 'my-search-engine' for your words."
});

And just replace your-function-name and url-to-my-search-engine by your own new values. You may have to adapt the query url though.

An example, which searches cplusplus.com:

CmdUtils.makeSearchCommand({
  name: "c++",
  url: "http://www.cplusplus.com/query/search.cgi?q={QUERY}",
  icon: "http://www.cplusplus.com/favicon.ico",
  description: "Searches cplusplus.com for your words."
});