Eric Pierce

Job Title: 
Engineer

I'm a software tester, developer, and graphic designer living in Colorado Springs with my wife and son. I have five years of experience with automated testing, ten years of software engineering and development, and fifteen years in illustration and 3D graphics.

In my spare time, I enjoy programming in Python and learning new languages such as Haskell. I'm a strong proponent of open source and open content. I created Kelp, Cukable, Grinder Webtest, CSVSee, tovid, PySci and other software, and have contributed to several open-source projects. I've also added many illustrations and articles to Wikipedia.

I think any IT professional should have a large repertoire of skills and be open to learning new technologies; nevertheless, I do have some clear favorites:

My web presences:

Resume: 

My Blog

Tue, 01/31/2012 - 18:00
  • Bart: I better go check out this Mongo character.
  • [Bart reaches for his gun]
  • Jim: Oh no, don't do that, don't do that. If you shoot him, you'll just make him mad.

So I've got this Rails application that uses a MongoDB database. There have been occasional problems in the past, and a few times I've gotten frustrated with Mongo's general bone-headedness, but never anything serious. Then today, I had a face-off that firmly cemented my negative opinions of MongoDB.

It all started when something went wrong with the application. Suddenly, Passenger reports that Rails is unable to connect to Mongo. If it were any other Unix service, a simple service mongod start might do the trick. So we start there:

# service mongod start
Starting mongod: all output going to: /var/log/mongo/mongod.log
forked process: 19583
        [  OK  ]

Peachy, right? Well, when mongod says "OK", what it really means is "I may have started fine, or I may have exploded. I'm not saying." When you check, you might be surprised:

# service mongod status
mongod dead but subsys locked

That's no good. It seems to be in some kind of limbo-state, not started, but not really stopped either. So before doing further debugging, let's stop the service:

# service mongod stop
Stopping mongod:      [FAILED]

What? It failed to stop too? Now what? So tell us Mongo, what's your status now that you've pretented to successfully start, then failed to successfully stop?

# service mongod status
mongod is stopped

At this point, it's hardly a surprise. Well OK, so we check the log file and it's complaining that it needs to be repaired or something. So let's try this:

# mongod --repair
MongoDB starting : pid=4507 port=27017 dbpath=/data/db/ 64-bit
....
exception in initAndListen std::exception: dbpath (/data/db/) does not exist, terminating

That doesn't seem right. It's looking for databases where they don't belong. My databases are in /var/lib/mongo, not in /data/db:

# ls -l /var/lib/mongo
drwxr-xr-x 2 mongod mongod      4096 Jan 31 15:50 db_development
-rw------- 1 mongod mongod  67108864 Jan 31 15:50 db_development.0
-rw------- 1 mongod mongod 134217728 Jan 31 15:50 db_development.1
-rw------- 1 mongod mongod  16777216 Jan 31 15:50 db_development.ns

And I'm pretty sure that's already configured:

# grep dbpath /etc/mongod.conf
dbpath=/var/lib/mongo

Yup. Fine, so I guess mongod doesn't know where to find its own configuration file. Let's tell it, shall we?

# mongod --config /etc/mongod.conf --repair
all output going to: /var/log/mongo/mongod.log

Alrighty, so everything should be fine now, right?

# service mongod start
Starting mongod: all output going to: /var/log/mongo/mongod.log
forked process: 27310
        [  OK  ]

But we know better than to trust this particular "OK" by now, right? Sure enough:

# service mongod status
mongod dead but subsys locked

What now? Oh yeah, the log says:

couldn't open /var/lib/mongo/db_development.ns errno:13 Permission denied

This makes no sense. The file was owned by mongod, right? A simple --repair shouldn't change that, right? I mean, if --repair instead broke things that were already working, it should probably be called --break, right?

# ls -l /var/lib/mongo
drwxr-xr-x 2 mongod mongod       4096 Jan 31 15:50 db_development
-rw------- 1 root   root     67108864 Jan 31 15:50 db_development.0
-rw------- 1 root   root    134217728 Jan 31 15:50 db_development.1
-rw------- 1 root   root     16777216 Jan 31 15:50 db_development.ns

Sigh. So I gotta manually fix the stuff that --repair broke:

# chown -R mongod:mongod /var/lib/mongo

And then stop, start, and once again verify the status of mongod, to discover that it's finally working properly now.

What's wrong with this picture? The mongod service flagrantly violates several of the unspoken (and some of the spoken) rules about software, not least of which is the fact that errors should not pass silently. Sure, they go to the log file, but if your service failed to start, then it shouldn't be saying "OK" when you go to start it. Similarly, it shouldn't mislead you by saying "FAILED" when in fact there was some measure of success. What about not being able to find your own configuration file? The mongod service itself seems happy to look in /etc/mongod.conf, but when you run mongod from the command-line, somehow it forgets where to find it. The fact that the --repair command can silently muck up your permissions is fairly disturbing as well; there's no good reason that these files should have their ownership changed.

Maybe it's just an old version (this is CentOS, after all); maybe these problems are fixed in newer versions of MongoDB. I'm not holding my breath, though. Sorry Mongo--I don't want you in my town anymore. Consider this to be your exploding candy-gram.

Tue, 10/11/2011 - 18:00

Are you bored with your slick Windows 7 or OSX GUI? Have Ubuntu's cutting-edge package updates got you down? Do you like installing archaic Enterprise editions of Linux just for fun? CentOS might be right up your alley. This is a brief tutorial on how to install CentOS within VirtualBox. You'll need a few hours to kill, and a working VirtualBox installation. (I won't go into the details of how to install VBox; it's pretty easy, and besides, if you're thinking of installing CentOS for fun, you probably already have it.)

So, you have a choice of installation media. Option A: Download 5 GBs worth of .iso images. Option B: Download a 10 MB "net install" image, then install just what you need over the network. This is pretty much a no-brainer. Grab the netinstall iso for i386 (32-bit) or x86_64 (64-bit) depending on what kind of engine you got in that there racecar. Fire up VBox and do:

  • New machine (Ctrl+N)
  • Name: CentOS
  • Operating system: Linux
  • Version: Red Hat
  • Base memory: As much as you can spare
  • Create new hard disk
  • Dynamically expanding storage
  • Size: As much as you can spare (at least 8 GB)

Select the new machine and press Start. When the First Run Wizard prompts for installation media, click the folder icon. You should see a tab of CD/DVD images, probably including some .iso files if you've installed other OSes in VBox. Click "Add", browse to where you saved the netinstall.iso file, select it and press Open. Back in the CD/DVD tab, select the netinstall.iso and press Select. Click Next, then Finish. The virtual machine should start up with a boot: prompt. Press Enter to start the installation.

Shortly, you'll get a text-based installer that prompts for some basic options such as Language and Keyboard type. When you get to the "Installation Method" prompt, choose "HTTP" and hit OK. Next you'll be asked for TCP/IP configuration options--just leave these at the default, arrow down to OK and press Enter. On the HTTP Setup page, you'll be asked for a server name and directory. These are where the net installer will fetch packages for installation. You can use any of the available mirrors that provide packages for the version you're installing, or just go with the main CentOS vault:

  • Web site name: vault.centos.org
  • CentOS directory: /5.5/os/i386 (or /5.5/os/x86_64)

Enter those and hit OK. Eventually, you'll be taken to a GUI screen with the CentOS logo and a "Next" button. Hit Next, and you'll be prompted to initialize your new virtual disk. Unless something looks wonky, go ahead and click Yes. After this completes, you'll be informed that your hard drive needs to be partitioned. Unless you're an expert knob twiddler when it comes to partitions, just leave the defaults and click Next. On subsequent screens you can specify network settings, declare what time zone you're in, and set a root password.

The next choice you'll have to make is what kind of environment you want to have. You can pick Gnome or KDE GUIs, and extra stuff like Virtualization and Clustering. Make any changes you want here, or just leave it alone and click Next. From here on out, it should be pretty self-explanatory. Be prepared to wait a couple of hours, especially if your internet connection is anything short of magnificent.

Once it's installed, it's time to reboot. Go up to the "Devices / CD/DVD Devices" menu in VBox and unmount the installation .iso (otherwise it'll just try to install again when you reboot). Once you get logged in, it may tell you about some upgrades--you can go ahead and do those, but be prepared to wait another couple of hours.

There's one last wrinkle; if you've used VBox before, you've probably become quite enamored of the VBox guest additions. This is what allows your virtual OS to do cool things like automatically fill up the VBox window, have seamless mouse integration, and share network devices with your host OS. Installing them requires a couple extra steps, however. Here's how to make it happen:

  • Go to the VBox "Devices" menu, and click on "Install Guest Additions"
  • In the CentOS desktop, open a terminal window (Applications / Accessories / Terminal)
  • Become root: su - root
  • Install some things you'll need in order to build kernel modules: yum install gcc kernel-devel
  • Change to the guest additions folder: cd /media/VBOXADDITIONS_3.2.whatever
  • Run the guest installer: ./VBoxLinuxAdditions-amd64.run or ./VBoxLinuxAdditions-x86.run

When this is done, reboot CentOS. When you login the next time, your mouse should be integrated seamlessly, and the CentOS desktop will fill up your VBox window. What will you do with this newfound power? That's entirely up to you, and I take no responsibility for it. Enjoy!

Tue, 07/19/2011 - 02:43

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to cut away. -- Antoine de Saint-Exupéry

Manual and automated software tests often include redundant and unnecessary words which can distract the tester from the real purpose of the test. We're so accustomed to seeing them that they often pass unnoticed.

Consider the following generic test description:

Verify that users should be allowed to login to the system with the correct username and password.

This seemingly innocuous sentence contains several unnecessary words. Clearly, this test is related to something that requires authentication, and the test is supposed to check that legitimate users are granted access.

Let's start with the word "verify". In my experience, this is one of the most heavily-abused words in software testing. Why do we need this word? All testing is about verification. There's no reason to state the fact that you're verifying something; that's what tests are all about. If a test is not verifying anything, then it's a futile exercise. You don't need to come out and say "verify this" or "verify that". Can we just assume that all tests (and all steps within those tests) are verifying, and leave that bit out?

Users should be allowed to login to the system with the correct username and password.

This sentence is just as clear as it was before, and two words shorter. Similar sentiments could apply to words like "test", "ensure", "check", "confirm" and so on--they're all synonyms for "verify".

Next up is the word "should". This one is up for debate; it's very common in automated systems like RSpec and Cucumber, and it seems to express something important. By asserting that the software should have some behavior, we're saying that this behavior is deliberate, designed in, and considered correct. But really, what kind of assertion are we making when we say that "X should Y"? How is this assertion different from saying "X does Y"?

More concretely, if it is true that users should be allowed to login, then what we want to test is whether users actually are allowed to login. We cannot test the truth of a statement like "X should Y"; we are already assuming that statement is true within the context of the test. If instead we say "X does Y", or "X can Y", we're making an assertion about the system that is either true or false. If X actually does Y, then "X does Y" is true. If X does something other than Y, then "X does Y" is false.

We might say "The Earth should go around the Sun," but that sounds ridiculous to anyone versed in the scientific method. Does it or doesn't it? Our test is a hypothesis that the system works correctly; by observing reality, we determine whether the hypothesis is true.

In short, it's a lot like the word "verify". We can say "should" of every assertion that a test makes; the test should be correct, the system should do what it's supposed to. That's a given in testing. What we care about in testing is whether it does.

If we take out the word "should", and make a grammatical adjustment, we now have:

Users are allowed to login to the system with the correct username and password.

This sentence expresses everything it did before, only now the assertion is falsifiable. Users are, or are not, allowed to login. Our test says they can login, and our job now is to confirm or refute that hypothesis.

OK, so what's up with "are allowed to" or "be allowed to"? Isn't there a much shorter phrase that means the same thing? How many parents have corrected children who ask "can I" when they mean "may I"? In this case, it's more about permission than physical ability, so why not substitute "may" for "are allowed to"?

Users may login to the system with the correct username and password.

Personally, I think "can" would work just as well here; we're testing the authentication system, rather than the mental and physical faculties of our users. But if you're a "can" vs. "may" stickler, choose the one that seems most correct to you. Either is miles better than "are allowed to". The same goes for "are able to" or "are capable of".

The next unnecessary word is "system". We often refer to the "system under test" (SUT), so it seems natural to refer to what it is you're testing. But here, the usage is so vague as to be meaningless. If the person running this test doesn't already know what SUT is being tested, they're not going to get any useful information from the phrase "the system". Let's take it out:

Users may login with the correct username and password.

What about "correct"? We're using that here to disambiguate from the case where a user tries to login using an incorrect username or password. Obviously this is an important distinction in testing an authentication process, but could it be phrased more succinctly?

It could be argued that "user" implies someone who has valid credentials, and that without those, they're not really a user. It's possible that we may have some former users whose account has expired or been deactivated, and those users should not be granted access. Maybe we should be saying "Users with an active account" or "Users with an active account and a membership role" or any number of other qualifiers, depending on what we're testing. Those qualifiers, while legitimate uses of extra words, were not part of the original description, and that description is our reference point for this exercise. We're trying to cull unnecessary words without losing meaning; increased specificity is not the goal here.

If we take it as given that each user has one username and one password belonging to them, we could use the possessive article:

Users may login with their username and password.

Has any information been lost from the original description? It doesn't seem so. We might go as far as:

Users may login.

But here we've reduced the original meaning. May users login with any username and password? Do users need a password? We don't know anymore. This version is too short, because we've taken away important words.

All of this leads up to a core principle that I've come to believe strongly in my years of writing and running software tests. Tests should be all about assertion--any verification they do should be stated as factual. This makes them inherently falsifiable. Don't beat around the bush with words that distract from the assertions your test is making. State your hypothesis (usually some form of "it works") as if you are certain that it is true. Running the test confirms or refutes that hypothesis. Writing your tests this way will make them not just good science, but good communication as well.