Tuesday, November 10, 2015

Include mock

Although recently I write mostly in (server side) JavaScript, I still have to return to my old C++ project once in a while. The other day I got notified that one of our integration tests will stop working due to changes in the database layer. It was testing a fix for a crash caused by inconsistent data coming from the db. So this integration test was trying to reproduce the situation by manipulating the data in the db. Actually reproducing such special cases with an integration test might be very difficult and unit tests are usually a better tool for this task. In the end I wanted to test some safety checks in our code, not an end to end scenario.

Still the code under test was a big and hairy lump of C++ code with many hard-wired dependencies. It certainly was not written with testability in mind. While looking around for mocking solutions in C++, I came across the section about testing from Michael Feathers' book Working Effectively with Legacy Code. He describes several approaches to mocking in C/C++, as he calls them seams. I was most impressed by the preprocessing seams. As Michael says, the preprocessor in C/C++ is kind of compensation for its stiffness compared to dynamic languages. It turns out the preprocessing seams are very powerful. You can take a C/C++ source file and compile it in a different environment thus making it do something very different.

So inspired by preprocessing seams I derived my own mocking approach in C/C++ that allows mocking of any function or class (even static, global and non-virtual) without changing the source file where these are called.

Here is the overall structure of a test file:
  1. Disable the original header that defines the dependency to be mocked using include guards
  2. Provide alternative/mock definition of that dependency
  3. #include the source file to be tested
  4. Write the tests
Let's see a simple example.
First, the header that defines the dependency that we want to mock.
Notice that this header uses include guards.

store.h

#ifndef STORE_H
#define STORE_H

class Connection;

class Store
{
public:
    Store(Conection& conn);
    //...
    const char* fetch(const char* query);
    void store(const char* data);
    //...
};

#endif // STORE_H

Next, the code that we want to test.

consumer.cpp
#include <string.h>

#include "store.h"

int measure(Store& store, const char* query)
{
  const char* v = store.fetch(query);
  return strlen(v);
}

And now the test.
We want to mock Store. To do this, we disable the original header by defining its include guard STORE_H. Then we provide our mock implementation. Note the mock does not need to be compatible to the original class. We just need to provide the minimum so that the code under test can compile and execute. So we implement only the methods used during the test.
Then we include the code to be tested consumer.cpp so it will compile in our mocked environment.
Finally, we run our test.

test.cpp
#include <iostream>
#include <cassert>

using namespace std;

#define STORE_H
class Store
{
public:
    const char* fetch(const char* query)
    {
        return "ola";
    }
};

#include "consumer.cpp"

int main(int argc, char *argv[])
{
    Store mock_store;
    assert(measure(mock_store, "query") == 3);
    cout << "OK" << endl;
}

With this approach all the code related to the test is in one place. You don't need to tweak any additional compiler/linker configurations. Also notice that we did not change the original code consumer.cpp, still we changed its behavior by compiling it in a mock environment.

Also described briefly this technique on SO.

Sunday, March 22, 2015

Checking out GitHub pull requests locally

When working with GitHub you often need to checkout a pull request (PR) locally so you can load it in your favorite tools and run/test it.

GitHub help suggests you can use a command similar to:

git fetch origin pull/ID/head && git checkout FETCH_HEAD
(here ID is the number of the pull request)

While this will give you the original code of the PR, it might be different from what you will get if you actually merge the PR. The reason is that you might have parallel changes in your target (master) branch not yet merged in the PR. While you can do the merge also locally, it turns out this is not necessary as GitHub had alsready done it for you. All you need is to use this command instead:

git fetch origin pull/ID/merge && git checkout FETCH_HEAD
(notice the difference in the refspec 'head' vs. 'merge')

This will give you a merged version of the PR which contains all parallel commits in the target branch even those merged after the PR was created.

Sunday, January 11, 2015

Rip audio CDs on LINUX

I still use audio CDs sometimes. But they tend to get lost or damaged easily. So it is a good practice to convert them to MP3.
So far I used Asunder for CD ripping. It is very easy to use ... when it works. But with some discs, usually lower quality CD-R, it just hangs right from the start. So I searched for another tool to rip audio CDs on LINUX.
It turned out you can do this very quickly with two command line tools - cdparanoia & lame. As usual you can quickly install them on Ubuntu with a single command line:

$ sudo apt-get install cdparanoia lame

Assuming the CD is loaded in the CD drive, running this simple command will copy all audio tracks in WAV files in the current directory:

$ cdparanoia -B

Next to convert those WAV files to MP3, run this command:

$ ls -1 | xargs -L 1 lame --preset standard

This will compress the audio files about 10 times using VBR ~190kbps.
If you are satisfied with the result, you can delete all WAV files:

$ rm *.wav

This will leave only MP3 files named like track04.cdda.mp3.
Of course these tools have many more options so you can tweak them as much as you like. For example lame option --ta sets the artist and --tl the album in ID3 tags inside MP3 files.
You can also script and automate this process as you see fit, but these are the tools that do the job nice and quickly.

BTW did you know that "disk" refers to magnetic storage while "disc" refers to optical storage? See Wikipedia.