The corrosion of Aaron Stone

Writing C Unit Tests in Ruby

Let’s say you usually code in Ruby, and your company and its build systems are built around Rakefiles and the like. Today you’ve written some C code, and you want to add unit tests. In this blog post, I present a method of writing those C unit tests in Ruby using FFI and RSpec.

/**
 * This is a very silly function that clearly requires some unit tests.
 */
int foo_count_letters(const char *source, size_t *count)
{
  if (!source || !count) return 0;

  for (*count = 0; *source; (*count)++, source++)
    ;

  return 1;
}

First, compile your C code as position independent and symbols exported. This allows you to dlopen() the executable:

$ gcc -pie -rdynamic -o foo foo.c

Next, add the FFI gem to your Gemset, in Gemfile:

source :rubygems

gem 'ffi'

Then write your rspec tests spec/foo_spec.rb:

#!/usr/bin/env ruby
require 'ffi'

# This module is your bridge from Ruby to C and back
module FOO
  extend FFI::Library

  # Use an absolute path to the executable under test, otherwise ffi will search LD_LIBRARY_PATH.
  ffi_lib File.absolute_path(File.join(File.dirname(__FILE__), "..", "foo"))

  # Function signatures for each function to be tested
  attach_function :foo_count_letters, [:string, :pointer], :int
end

describe "unit tests for foo.c" do
  before(:each) do
  end

  it "should really foo" do
    # This function takes a pointer-to-uint32 out-param
    out = FFI::MemoryPointer.new :uint32

    res = FOO.foo_count_letters("Hello", out)

    # Read back the pointers to Ruby data types, then use rspec's verification functions
    out.get_uint32(0).should == 5
    res.should == 1
  end
end

I’m excited about this approach because the tests run under rspec along with the rest of your spec tests.




Moving my Blog to GitHub

Today I moved my blog from my own server to GitHub. I exported my blog from Movable Type to Jekyll source files, checked it a GitHub repo, and pointed my domain’s DNS at GitHub Pages.




Introducing a Memcached Module for Varnish 3

Varnish makes your websites go faster. Memcached, makes your websites go faster. Blog posts abound on using Varnish’s excellent embedded C functionality to link libmemcached into your VCLs, and Poul himself has written about his surprise in learning that many VCLs were actually thin wrappers around a C library where all of the business logic of a VCL would live.

Starting in Varnish 3, the balance can now shift back towards the VCL itself as the driver of business logic, with first-class “vmod” modules providing new VCL functions. (If you’ve been around a while, you might recognize the same progression that PHP took in the late 90’s and early 2000’s, back when PHP looked like a promising future.)

Without further ado, I present libvmod-memcached, a Varnish Memcached Module. Here’s a quick example script that flushed the cache for a page every 20 hits.

import memcached;

sub vcl_init {
	memcached.servers("localhost");
}

sub vcl_hit {
	if( memcached.incr(req.url, 1) == 0 ) {
		memcached.set(req.url, 0, 86400, 0);
	}
	if( memcached.get(req.url) == "20" {
		purge_url(req.url);
	}
}

Note that VCL does not allow arbitrary variables, so I’m doing two memcached queries here. Fixing that will be another blog post!




Things Dilburn learned at Burning Man

The Dilburn campers jotted some useful notes on a little whiteboard at our camp. Eventually I wanted to erase the whiteboard, so here’s what it said!

  • Burning Man is what you make it.
  • RVs are really nice.
  • Art cars are fun!
  • Hexayurts are the way of the future.
  • Tents are dusty.
  • White carports don't reflect enough sun.
  • Bring extra rebar and sledgehammers. Not just extra. Bring too much.
  • Coffee.
  • Couches.
  • NO PACKAGING!
  • Repack everything into bins, sorted by type-of-thing.
  • Bring power tools. Charged.
  • Try camping on the 7:30 side. It's crowded, and different than the 4:30 side.
  • Create wind and sun breaks.
  • Designate a shit table.
  • Give foot baths.
  • Drinks are communal. Expect your beer cooler to be raided, so just have everyone pay in upfront.
  • There's no such thing as a quick trip at Burning Man - never say, "I'll be right back!"



Upgrading an HP dv2000

My gal has a laptop that she really likes, an HP dv2000 - dv2225nr to be exact - even though it’s slooow. I’d gotten pretty near to talking her into replacing it when the new job provided a spiffy ThinkPad that she can bring home now and then. Needing only a personal machine to sync her iPhone and surf the web, I said, “What if we just put in a hundred bucks to make it less slow and do a Windows Vista detox?”

Ok, $185 bucks later, and I have some recommendations to make to the lazywebs about this laptop model.

Things I learned:

The AMD Turion 64 x2 with GeForce Go 6150 chipset in this system will happily accept sticks of 4GB DDR2 RAM, probably for a maximum capacity of 8GB. But I stopped at mixing a 4GB stick with an existing 1GB stick, for a total of 5GB.

Check in the BIOS how much RAM is set aside for that GeForce Go 6150. The default is probably 64MB from the factory (with the default 1GB memory configuration, this makes sense I suppose), but you can go to 128MB. Now that 128MB is basically a rounding error on the total memory size, use it.

I tried to clone the original hard drive, but taking it out of the machine caused it to lose its magic smoke. That meant my Windows 7 Upgrade edition was no longer valid for a clean install on a new 7200RPM drive. Follow these directions to get around that:

  • Finish the installation with the activate-later option.
  • Once you’re up and running, run regedit as Administrator
  • Change this key from 1 to 0:
    HKEY_LOCAL_MACHINE/Software/Microsoft/Windows/CurrentVersion/Setup/OOBE/MediaBootInstall
  • Close regedit
  • Run cmd as Administrator
  • Run slmgr /rearm at the command line
  • Close cmd
  • Activate by going to System -> Change product key

Windows 7 64-bit runs great, uses all of the RAM and all that jazz. [Insert pointless argument about pointer sizes and 64-bit bloat and how I’d be better off with 3.5GB usable RAM at 32-bits. I don’t care; everything else I touch these days is 64-bit. Consistency FTW.]

Finally, the 1.6GHz Turion TL-50 CPU sits in an S1G1 socket, so $20 got me a 2GHz TL-60 with double the L1 cache and a 31W TDP, down from 35W. Runs faster, cooler, and I pretend that the battery lasts a little more. That’s what I told my girlfriend, anyhow! Winning!