Saturday, June 7, 2008

One Day In My Life As A Googler

Disclaimer: this is a personal entry. Which means that if you are here for technical revelations or to gather super secret information about Google, go away, or I'll bore you to death. Seriously. Don't tell me I didn't warn you.

6:00 am: The iPod station starts playing and lifts me gently from the land of dreams.
Stanford"... Don't run away the time is now the place is here ..."
Sasha's swinging voice finally makes me cautiously open my eyes: another sunny day in Mountain View. The weather gadget on my iPod informs me that the temperature is currently 11 degree Celsuis, probably rising to a comfortable 21 throughout the day. I realize that I still don't know how to convert Celsius to Farenheit without accessing the Internet.

6:40 am: Armed with a gbike and a helmet I finally leave the apartment, trying to get warmed up for my workout on the 3 mile ride to work. While I pass the sporadic early jogger I can see the first rays of sunlight painting the landscape in warm colors. Pushing the pedals gets my pulse up to 150. Sweet.

6:55 am: The gym is wonderfully empty at this time of the day. With just a handful of Googlers around me I enjoy a quite workout. Today I'm torturing my upper body.

7:45 am: I end my workout with an easy 50 minute run along Mountain View's beautiful shoreline.Jogging Track Squirrels pass my way while two chatting women overtake me. I realize that I've got a long way to go before I will become a decent runner. Well, at least I can program computers.

8:50 am: Breakfast time. I get myself some freshly made scrambled eggs with crispy bacon, two chocolate croissants, some healthy orange juice and a steaming cup of coffee. Throughout the next view minutes some of my colleagues from the build tools team arrive, and a discussion about espresso machines, barbecue grills or why we don't want to discuss Agile evolves. The day can come.

9:10 am: Time to do some work! And, no, that doesn't involve foosball. Or ping pong. I'm actually writing code. Unfortunately I am not allowed to chat about what we do, or even how we do it. So just imagine me typing. And talking to people. And typing again. More talking. More typing. You get the idea.

Lunch at Pintxo12:10 pm: The hardest question at lunch time is which cafe to choose. I just run with the crowd of people working next to me. Today we're heading over to Pintxo. One thing that constantly surprises me about food at Google is that I actually like the dessert. Chocolate cookies, sliced fruit, hot brownies, some chocolate cream topped with strawberries. Take your pick.

1:00 pm: Work, work, work. Getting some coffee. Work, work, work. More coffee. Work, work, work. Grabbing a snack, which leads to some discussion about pair programming and the state of the world in general. Work, work, work.

7:30 pm: Going to Charlie's to get my usual treat for the evening: a self-designed burger and a coke. This certainly feels like America. I meet Nicolai and his friend, who doesn't work for Google. I learn that we're allowed to bring visitors to dinner one or two times a month. We talk about real estate prices and I realize that living here is even more expensive than Munich. Dang!

Mountain View Shoreline8:15 pm: I finally arrive at the apartment. I kill half an hour by starting Yet Another Blog Post. I want to write something about unit test size that features some modestly comical adult references (go figure). After some time I realize that I don't have enough high energy content for this entry. I'll finish it eventually. Blogger's shortest joke.

9:00 pm: A new episode of House starts. For some strange reason the ability to watch a show that doesn't run in Germany yet makes me feel childishly happy. This probably reveals things about my personality I don't even want to think about.

10:00 pm: The favorite part of my day is talking to my wife. And this is PRIVATE! ...

10:30 pm: Another day at Google passed. I do some reading and eventually fall asleep. Just for the reference: my dreams are private, too. Just in case you'd hoped for something. Good night!

Tuesday, March 11, 2008

An Editor Independent Unittest Executor

Since I got test infected I'm somehow unable to write a single line of untested code without feeling uneasy. When I just want to write a tiny script containing a few lines of code in whatever text editor is installed in a system, it seems to be a daunting task to set up a programming environment that allows you to execute unit tests with a single click. But this single click is what makes writing unit tests unobtrusive enough to keep doing it.

So I'm quite fond of using a simple script to execute my script's unit tests whenever I save it. This concept is not new, and certainly not an original idea in itself, but the simplicity of an editor independent unit test executor in 10 lines of code has a certain appeal for me:

#!/bin/bash
stat_command="stat -c '%Y'"
file_name=$1
last_modification=""
while true; do
current_modification=$( $stat_command $file_name )
if [ "$current_modification" != "$last_modification" ]; then
clear
$file_name --test
last_modification=$current_modification
fi
sleep 1
done

This script stats the script file until it detects a change. Whenever a change is detected, the script is called with --test, which is my personal way to tell a script that it should just execute it's unit tests and exit. See my blog post about integrating unit tests in Ruby scripts to learn how this can be done in Ruby. A very similar approach is possible for Python:

#!/usr/bin/python
import unittest
import sys

if sys.argv.count("--test") > 0:
sys.argv.remove("--test")
unittest.main()


Now I can simply call the test bash script, giving it the script under test as parameter:

./run_tests.sh ./script_under_test.py


The beauty lies in the simplicity of the solution: Even when I remote edit a script on some server with vi, I can simply launch a new console and execute run_tests.sh, watching the test results whenever I type ":w".

Update: The "sleep 1" really helps to keep I/O load down. Thanks to Philip for pointing this out. And yet another nice example of how hard it is to write 10 lines of bugfree code without a test.

Monday, February 4, 2008

Integrating Unit Tests In Ruby Scripts

When I write ruby scripts I like to use a single file, containing the program and all unit tests. It took me some time to find out how to add a command line switch to my ruby scripts that makes them run in script mode with full access to the Test::Unit command line arguments, while being able to run the script without the test framework interfering in the execution:


#!/usr/bin/ruby -w
require 'test/unit'

if ARGV.include?("--test")
ARGV.delete_at(ARGV.index("--test"))
else
Test::Unit::run = true
puts "Running..."
end


Now you can simply run the program by typing

$ ./testme.rb
Running...

or run the tests with

$ ./testme.rb --test
Loaded suite ./testme
Started

Finished in 0.0 seconds.

0 tests, 0 assertions, 0 failures, 0 errors

The nice thing is that only the first "--test" will be removed, so you can still leverage the Test::Unit command line argument interface.

Tuesday, December 25, 2007

My First OCaml Tests - So Close To Heaven!

Some time ago a a discussion in the testdrivendevelopment Yahoo-Group evolved around the concept of "testable languages". I thought about this for a while and came up with the idea that I want to be able to have expressions as first class citizens:

assertThat { assertThat(false) } abortsWith TestFailedException

Today I played a little with OCaml, sorted my functional programming skillz out, and finally arrived at my first test driven unit test environment for OCaml! Note that it's nearly what I wanted to be able to write, but unfortunately there's not enough syntactic sugar for lambda expressions (or I didn't find out, yet), so I'm stuck with using the quite ugly ( function () -> expression ) syntax. But hey, it's really close to heaven.

exception TestFailed
exception TestError

let failsWith expectedError expression =
try
expression ();
false
with error ->
expectedError = error

let isTrue expression: bool =
expression

let isFalse expression: bool =
not (expression)

let assertThat expression conditionMatchesOn =
if not (conditionMatchesOn (expression)) then
raise TestFailed
else
()

let _ = (
assertThat true isTrue;
assertThat (isTrue true) isTrue;
assertThat (not (isTrue false)) isTrue;

assertThat false isFalse;
assertThat (isFalse false) isTrue;

assertThat (TestFailed = TestFailed) isTrue;
assertThat (failsWith TestError (function () -> raise TestError)) isTrue;
assertThat (failsWith TestFailed (function () -> raise TestError)) isFalse;
assertThat
( function () -> assertThat false isTrue )
( failsWith TestFailed );

Printf.printf "OK\n";
);;

Sunday, November 25, 2007

XPDays Germany 2007 - Ideas Going Mainstream

I had the opportunity to take part in the XPDays Germany last week. The company I work for enabled Uwe, our project lead, Holger and me to participate. It all started with a three and a half hour ride from Munich to Karlsruhe where we heroically overcame a nearly empty tank, a shaking car that felt like it just drank the wrong kind of gas and my own card reading skills - or lack thereof.

Day 1


In the end we arrived on time for a Randori session by Dave Nicolette and Rod Coffin. From the moment I learned of this experimental learning session where two people sit in front of a computer and test drive a piece of code while the whole audience is throwing in questions, I was kind of scared of the prospect of being watched while writing code by a hundred people - which is probably kind of normal, given that some people even dislike being watched while pair programming.

The session turned out to be very interesting. One of the key elements of this style of learning is that the audience's energy level stays high for a long time - you have to pay close attention, since due to the random selection of the next person to come to the front you could always be this person. And since you don't want to look like a fool when doing stuff in front of a hundred people (um, what was the problem, again?) my adrenaline level alone was enough to keep me awake.

The other thing I learned from this experience besides a new way to coach technical stuff is that there is a very good reason we do "pair programming" and not "group programming". Throwing two brains at a problem can be a very mighty tool to solve programming tasks, but in some situations throwing a hundred brains onto a very simple programming example felt like sitting in one of Dilbert's most unproductive meetings:

"So we add the game to the character: character.addGame(game)"
"But why don't you add the character to the game?"
"I see duplication, I see duplication!"
"But isn't this, um, less expressive?"
"Duplication is bad!"
"Why do you assign null to a variable, this is done automatically"

After some time of silent observation I realized that sometimes I am exactly like this! So to all of you who have to cope with me on a daily basis: just hit me on the head with a big club from time to time.

The next session was a series of "lightning talks" about all kind of Agile topics where I learned about Alistair Cockburn's Crystal, which is a set of methodologies built upon the Agile principles and an extreme tailoring approach.

Day 2


Despite the enormous amount of a whole liter of "badisches Helles" I had in the evening I was wide awake and ready to suck in new ideas during the conference's main part. The day started with an interesting presentation by Dave Nicolette about how to communicate TDD and design debt to your management. I was particularly stunned by the fact that he did talk about the cost of design debt and the refactoring part in the TDD cycle for a very long time without mentioning the cost of fixing an error in terms of when it is found and the shortened feedback cycles that TDD provides.

The next presentation was titled "why Agile projects fail", but turned out to be about why projects fail in general and provided some insight into the ideas of root cause analysis (5 Why), the dimensions in which failure can occur (The Broken Triangle), and the psychological factors that are the real cause of ineffective development practices.

After lunch the keynote by "Dark Side" Rod Austin from HBS showed how Agile development fits the icy wind of change that swirls today's leading companies in the world from a cost competitive to an innovative business model. After all, who doesn't want a designer trash bin?

Stfan Roock's talk on "Simplicity in Software Projects" was a very entertaining lecture on how easy it is to get so accustomed to complexity that you don't even realize how simple things could be. Well, that and that the Borg are the only entities in the universe who understand that when you travel through space aerodynamics is pointless.

In the end it was very interesting to see big German companies like SAP, EADS and Siemens to take interest in extreme programming. Looks like those ideas are finally going mainstream.

Friday, November 16, 2007

Does "Test After" Work For You?

"Why not write a test for this?"
"Why should I, it works..."

The idea of Test-After Development is to write a set of automated white-box tests after writing your production code. Since probably every CS student in the world has learned that unit tests are a good idea, you'd expect unit testing to be an industry state standard for quite a while now. Interestingly the idea of automated unit and integration test is lately becoming more popular due to the widespread use of Test-Driven Development.

So why do we need Test-Driven Development to be able to efficiently write automated unit tests?

  • If you write your code first and don't think about how to test the code, the code will not be testable. Thus testing becomes expensive and frustrating. Test-Driven Development will guide your software design by the old mantra of "how-do-I-want-to-use-this-class", leading to a highly decoupled design.

  • When you write your tests, you'll discover a lot of errors. But instead of the red bar in Test-Driven Development, which you expect, the red bar in Test-After Development is the demotivating sword of reality.

  • The most important reason why I have never seen Test-After Development work, is that developers just don't believe in errors once they wrote the code. This seems to be an eternal wisdom of software development psychology: once the code works, why bother testing it? Let's just implement the next feature.

Thursday, November 1, 2007

A Program Is Born: QCMake's First Functional Test

When you write a GUI that is just a thin layer for an existing business layer and you don't see how to integrate test fixtures into this business layer, you'll be down in the dirty functional testing work very quickly. This happened to me today when I tried to write my first test for a small Qt facade object for cmake.

I started the test very enthusiastically: To test cmake I create a directory, cd into that directory, create a CMakeLists.txt and let cmake create a CMakeCache.txt. In the end I know that cmake ran when CMakeCache.txt exists.

void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
QVERIFY(currentDirectory.mkdir(testDirectory.dirName()));
QVERIFY(QDir::setCurrent(testDirectory.path()));
}


I hit F5 and everything runs just fine. Once. The second time the directory ExecuteInCurrentDirectory already exists. Of course to have a nice and clean starting point the test must remove the test directory if it already exists. So I added:

void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
if(currentDirectory.exists(testDirectory.dirName()))
{
QVERIFY(currentDirectory.rmdir(testDirectory.dirName()));
}
QVERIFY(currentDirectory.mkdir(testDirectory.dirName()));
QVERIFY(QDir::setCurrent(testDirectory.path()));
}

Green. Perfect. Now let's create a CMakeLists.txt.

void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
if(currentDirectory.exists(testDirectory.dirName()))
{
QVERIFY(currentDirectory.rmdir(testDirectory.dirName()));
}
QVERIFY(QDir::setCurrent(testDirectory.path()));

QFile cmakeLists("CMakeLists.txt");
QVERIFY(cmakeLists.open(QIODevice::ReadWrite));
}

Green again. Once. The test fails the second time it's executed:

********* Start testing of QCMakeControlTest *********
Config: Using QTest library 4.3.2, Qt 4.3.2
PASS : QCMakeControlTest::initTestCase()
FAIL! : QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
'currentDirectory.rmdir(testDirectory.dirName())' returned FALSE. ()
..\..\..\..\..\Source\CMake\Source\QTDialog\qcmaketest\QCMakeControlTest.cpp(30) :
failure location
PASS : QCMakeControlTest::cleanupTestCase()
Totals: 2 passed, 1 failed, 0 skipped

Yep, no problem, all I need to do is to rmdir recursively. Just a quick glance into the Qt docs. But I found nothing. Well, it's not too hard to implement a recursive rm -rf, but still... I was so sure that this function would be hidden somewhere that I spent more time googling and doc-reading than implementing it when I finally realized that I was on my own. So in the end the test looked a little bloated:

#include "qcmaketest/QCMakeControlTest.h"

#include "qcmakeui/QCMakeControl.h"

bool removeRecursiveForced(QDir& directory, const QFileInfo& entry)
{
if(!entry.isDir())
{
return directory.remove(entry.fileName());
}
QDir directoryEntry(entry.filePath());
QList entries(directoryEntry.entryInfoList
(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot));
for(int entryIndex = 0; entryIndex < entries.count(); ++entryIndex)
{
if(!removeRecursiveForced(directoryEntry, entries.at(entryIndex)))
{
return false;
}
}
return directory.rmdir(entry.fileName());
}

void QCMakeControlTest::shouldExecuteCMakeInTheCurrentDirectory()
{
QDir currentDirectory;
QDir testDirectory(currentDirectory.path() + "/ExecuteInCurrentDirectory");
if(currentDirectory.exists(testDirectory.dirName()))
{
QVERIFY(removeRecursiveForced(currentDirectory,
QFileInfo(currentDirectory, testDirectory.dirName())));
}
QVERIFY(currentDirectory.mkdir(testDirectory.dirName()));
QVERIFY(QDir::setCurrent(testDirectory.path()));

QFile cmakeLists("CMakeLists.txt");
QVERIFY(cmakeLists.open(QIODevice::ReadWrite));

QCMakeControl qCMakeControl;
qCMakeControl.configure();

QFile cmakeCache("CMakeCache.txt");
QVERIFY(cmakeCache.exists());
}

#include "QCMakeControlTest.moc"

At least I have an idea where this could lead me - a nice class to generate a clean cmake directory. But let's see whether I'll be right, perhaps YAGNI will finally get back at me. And if you know an easier way to delete a directory recursively with Qt, please leave a comment.