Is it worth killing trees over another C++ Book? 144

Posted by Brett Schuchert Tue, 17 Aug 2010 05:17:00 GMT

I’ve taught a few C++ courses recently to people primarily moving from C to C++. I know C++ has been around for years and it’s not in vogue like it was 15 years ago. I stopped using it full time in 1997. Even so, there’s been quite a bit of work on the library and language standard. And there are still a lot of places developing new systems with C++. I know some people are still be learning C++ in school.

In my most recent classes, I’ve been teaching students who have recently taken a class on OO A & D based on the work of Craig Larman. The C++ class attempts to follow that class by dovetailing into what it covers. Because of this, I did not use ObjectMentors’ standard C++ and OOD class. Good as it is, it has different starting assumptions. I instead wrote a class, using two problems as the entire basis of all the material I cover. I know, “not build here syndrom.” It pained me to do this, I did my research and I considered retro-fitting the OM course. The discrepancy was too large to consider reuse. And refactoring the existing class to accomodate changes, which I also considered, wasn’t practical. Looking back I made the right decision.

So this course has a few key design elements:
  • Problem focused
  • Test oriented (sometimes test-first, other times test-driven, occasionally refactor-oriented)

The problem-focus limits the topic coverage. If something about the language doesn’t come up somewhat naturally (not contrived) in the two projects I use, I don’t cover the material. For example, I don’t mention placement new, I only cover Multiple Inheritance if asked about it. I also try to focus on classes in the standard library. For example, std::array, std::vector, std::map, std::shared_ptr.

Additionally, there’s an early focus on testing. That’s another thing that’s different from how I taught C++ say before 1995 – I really didn’t teach it from 1995 – 2007, so no comments on what I might have done differently in that span of years.

To give you an idea of how early the focus is on test, I only show cout if asked. The first main() calls CommandLineTestRunner::RunAllTests and everything after that runs within a unit test. the last time I taught the class, I demonstrated tests executing with cslim, but still, a test focus.

I have them use unit tests as a way to experiment with the language. In one example, I have them write tests that force a method to become virtual that was not virtual before. In another case, I do the same thing with a virtual destructor. I have them test from raw pointers into shared_ptr and then update their code accordingly.

Because of the test focus, I make certain recommendations that impact overall class design. That means learning C++ with designs supporting testability early on. In C++ this means (among other things):
  • Dependency Injection (OK, this is not just C++)
  • Virtual functions and by corollary a virtual destructor
  • Storing pointers because 1. calling methods through an auto object are not virtually dispatched, and 2. you cannot put references in the standard collections
  • Use std::shared_ptr to avoid memory leaks, which are detected by the unit testing tool I have them use.

I take the class to a certain level, but I make it clear I’m just scratching the surface. I believe it’s not really possible to learn C++ in a 1-week class. You can get the beginnings of proficiency and be in a good position to continue learning – that’s an assumption I state up front after the students have had their first exercise – about 5 minutes into the start of the class. I try to only go into detail as the students ask questions, but at times I just want to really open up the beast and get into what’s really happening.

To address this, I’ve started novelizing the class I’ve been teaching. I’m following the same outline as the class, but I dive under the surface and at times get to quite a bit of detail that I would not typically get into in a 4.5 day class.

I know this material will augment the class. This will give my students three sources of information:
  • The class itself, which is exercise-driven
  • Online videos
  • A novelization of the class, going into much more detail

The thing I’m wondering is, would the book be worth making more generally available? I’m writing it so that it can stand on its own. There’s a certain advantage to knowing my students have taken the previous OO A & D class I mentioned because it uses a problem that I’ve used on and off since 1992. This allows me to give examples from a problem they have looked at in the past and then at the current problem. I’ve not yet made references to that problem in the book I’m writing. I could, I just have not done so yet – it’s more natural in the second problem and I’m still working on the first problem (and therefor the first half of the book). I’m certain I can make those references without the previous class experience. (It’s the Monopoly problem.)

In any case, I need to do some research. If I do start the publishing process, a key step involves competitive research. Can you recommend any books published for the first time this century (really in the past 7 years) that have any of these characteristics:
  • Cover a minimal set of C++, enough for decent OO solutions
  • Have any kind of emphasis on test – at least half the book?
  • Cover the language strictly through a problem-based approach rather than from a language perspective?
  • Involve deliberately making mistakes and observing those mistakes to learn how the language works?

Additionally, can you recommend any great C&#43&#43 books? I can list many of the ones I’ve read and enjoyed, but the last time I bought a book for myself on the topic, Amazon did not exist as an online company.

Independent of whether I deal with trying to get this thing published as a printed book, I’m going to finish it because I think it will be useful to my students. I suspect I’ll be teaching this class in the future, so I will find it useful in the future as well. If I don’t attempt publishing it through a major publisher, I’ll put it on my wiki at the very least. Though it’s going to be quite a bit more effort than my typical wiki articles.

But the question I keep coming back to, is this: Is there really a need for this book dead tree version, or it is primarily useful as supplemental material for a class I teach?

I’d like to hear your opinions.

Improving Testability of GUI Code, an Eample 60

Posted by Brett Schuchert Fri, 11 Sep 2009 04:08:00 GMT

Just finished first draft. More to come, questions/comments appreciated.

http://schuchert.wikispaces.com/tdd.Refactoring.UiExample

Infinitest for Java, have you tried it? 10

Posted by Brett Schuchert Thu, 18 Jun 2009 16:35:00 GMT

Background

About 2 years ago, I was working with Ben Rady and he demonstrated something he was working on at the time: Infinitest. As often I am, I was a little interested but mostly skeptical – don’t know if that came across or not.

Since then he and others (e.g., Rod Coffin have made amazing strides and created plugins for both Eclipse and IntelliJ.

Taking it for a test run

Earlier this month, I finally decided to give it a test drive. When I made that announcement, Ben made the following (bold?) statement:
For me, using Infinitest is as different from TDD as TDD is from not testing

So is that true? Is using a continuous test execution tool as different from not using one as using TDD is from not testing? I’m not sure I’m there yet. However, I will say my brain is having some difficulty getting used to the cool feedback.

Here are two recent experiences I had using it.

Classpath Issues

Back at the end of 2006 I wrote a class on using JPA and EJB 3. I’ve not really done much to update that material in some years but recently I had an email from someone trying to work through the first tutorial with little success. Over the past few years there’s been some bit-rot. The embeddable container is not really up to date, EJB 3.1 includes an embeddable container as part of its spec, Hibernate has been updated, etc. So I spent a few hours tracking down updated jar files and building my classpath. I had already installed Infinitest and I noticed as added I something to my classpath, Infinitest would kick off and show a stack trace (my code was doing that in a @Before method). So I sped up what I was doing:
  • I directly edited the .classpath file in Eclipse
  • Saved what I was doing
  • Waited about a second
  • Noticed the new stack trace
  • Found the next jar file I needed to add
  • Repeat until tests passed.

Might sound like a bit of overkill, but in the end I built a classpath from scratch and I ended up adding 13 jar files. So it saved some time.

Note, I wasn’t looking for this. I had only installed Infinitest the day before so this was unexpected and welcome! Oh, and before my @Before method was handling the exception properly, Infinitest showed that the code had a problem (it was in the @After with a null pointer exception), which it indicated as an error like a syntax error or a validation error. Nice!

Using Mockito

I’ve recently been using Mockito. You can review a previous blog entry for that example. Today we’re holding the first coding dojo at the recently opened OkC CoCo. Last night I started working on the next problem I want to use for the next dojo. It involves practicing using a mockist approach. I set up my classpath, started writing tests and immediately I noticed what looked like a syntax error on the verification step of my first unit test. I was confused thinking I had an actual syntax error since I’m not quite to the point of touch-typing Mockito based tests (I did update Eclipse so I could more easily find the static imports).

Next, I updated my test to use the @Mock annotation. I removed the hand-written initialization and immediately I noticed a “syntax” error – null pointer exception. I was immediately (OK 1 second later) showed the impact of removing a single line of code. I added the missing line to auto-initialize the @Mock annotated fields but I did it incorrectly, so the error remained. I finally got the line correct and the “syntax error” went away.

Observations

Wow. That’s what I have to say so far. I’m not entirely sure the before and after of using Infinitest is the same size as moving from not testing to using TDD. Maybe it’s the same as moving from being Test Infected to practicing TDD. I was Test Infected several years before I practiced TDD.

I was also about as skeptical that moving to TDD from being Test Infected was useful. I was wrong. History tends to repeat itself, so I’m guessing, based on my initial resistance, that this is the future.

Embrace it.

Hiding global methods, a C++ example 32

Posted by Brett Schuchert Fri, 12 Jun 2009 15:13:00 GMT

Background

This is a continuation of this discussion. A few of the postings by GBGames have been swallowed so the context is somewhat lost. What follows is an excerpt from an email he sent me and some example C++ code that demonstrates making global methods “testable” in a sense. Or rather, making your code not use global methods during test.

The Offending Method

Here is a method that GBGames has under test:
void SDLHardwareLayer::initializeHardware()
{
    const SDL_version * sdlVersion = SDL_Linked_Version();
    if (sdlVersion->minor < 2 && sdlVersion->major < 2)
    {
        //Error message
    }
    else if (SDL_Init(SDL_INIT_VIDEO |
            SDL_INIT_AUDIO |
            SDL_INIT_NOPARACHUTE) < 0)
    {
        //Error message
    }
    else
    {
        m_screen = SDL_SetVideoMode(m_x, m_y, m_bitDepth, SDL_DOUBLEBUF);
        if (NULL != m_screen)
        {
            SDL_WM_SetCaption(m_title.c_str(), 0);
        }
    }
}

This is how this code is organized:

So What’s Wrong?

One problem he notes is that the test code driving this production code causes windows to pop up during testing. Is this a problem? If it works, then it may be OK. However, you can remove that and also make test-ability a bit easier. You’ll be testing interactions and responses rather than directly using the library through its global functions.

Global Functions Considered Harmful

First off, this code as written directly uses global functions. If you leave this code unchanged, then the only option you have to “fix” the problem of windows popping up is a link-seam (see Working Effective with Legacy Code, page 233-234. In a nutshell, a link seam will link different versions of a library, one for testing, one for actual execution. So you’ll need some “make fu” to get the link seam working in a manner that easily allows building for testing versus building for execution. Since we’re using C++ and we own this code, I chose a different route:
  • Change this code to use an object that uses the library
  • That object implements an interface (a base class with all pure virtual methods)
  • Change this code so that it acquires this object through a factory (I could have simply used constructor injection, but I’ve already written it using a configurable factory [really singleton] – but it would have been simpler if I had injected the interface)
  • Created a test double that can be used when writing new test doubles making code maintenance a bit easier (mentioned in the other blog posting).
  • Write test-method specific test doubles

Changing Code to Use Factory + Object w/virtual Methods

Rather than directly calling the various global SDL_* methods, e.g.:
const SDL_version * sdlVersion = SDL_Linked_Version();
I instead use a factory to get an instance and call
const SDL_version * sdlVersion = SdlLayerFactory::getInstance()->SDL_Linked_Version();

Base Interface

To make this work, I created a base interface representing all of the methods needed by the SDLHardwareLayer::initializeHardware:
#pragma once

struct SDL_version;

class SdlLayerInstanceDelegator {
public:
  virtual ~SdlLayerInstanceDelegator(void) = 0;

  virtual const SDL_version *SDL_Linked_Version() = 0;
  virtual int SDL_Init(int bitMask) = 0;
  virtual void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode) = 0;
  virtual void SDL_WM_SetCaption(const char *title, int someIntValue) = 0;

protected:
  SdlLayerInstanceDelegator();

private:
  SdlLayerInstanceDelegator(const SdlLayerInstanceDelegator &rhs);
  SdlLayerInstanceDelegator &operator = (const SdlLayerInstanceDelegator &);
};

Real Calls Global Methods

Getting rid of the direct call to the global method makes this work. However, you still need to call those global methods somewhere. That’s where the “real” version of the interface comes in. The real version of this class simply calls the global methods (code only, not showing header file right now):
const SDL_version *RealSdlLayerInstanceDelegator::SDL_Linked_Version() {
  return ::SDL_Linked_Version();
}

int RealSdlLayerInstanceDelegator::SDL_Init(int bitMask) {
  return ::SDL_Init(bitMask);
}

void *RealSdlLayerInstanceDelegator::SDL_SetVideoMode(int x, int y, int bitDepth, int mode) {
  return ::SDL_SetVideoMode(x, y, bitDepth, mode);
}

void RealSdlLayerInstanceDelegator::SDL_WM_SetCaption(const char *title, int someIntValue) {
  ::SDL_WM_SetCaption(title, someIntValue);
}

The Factory: Inversion of Control

The original code directly used global methods. We need to invert this relationship so that rather than depending directly on those methods, it depends on an interface. Then if it happens that it uses a “real” version, the underlying code will call the actual library. If, however, it is instead using a test double, it will not. It will do whatever the test double dictates.

We cannot leave the decision of which object to talk to in the SdlHardwareLayer class. Instead, I’ve used a configurable factory (singleton). A test can configure the factory, call the method under test, and then reset the factory back after the test.

The SdlLayerFactory allows such use(again, code only, no header file):
SdlLayerInstanceDelegator *SdlLayerFactory::instance = 0;

SdlLayerInstanceDelegator *SdlLayerFactory::getInstance() {
  return instance;
}

SdlLayerInstanceDelegator *SdlLayerFactory::replaceInstance(SdlLayerInstanceDelegator *replacement) {
  SdlLayerInstanceDelegator *original = instance;
  instance = replacement;
  return original;
}

Handling Growing Interfaces

One problem with “interfaces” in any language is that as you add methods to them, their implementations need to be updated. Since the SdlLayerInstanceDelegator class is not complete yet, this will case problems with test doubles. There are three ways to handle this problem: * Deal with it, as you add new methods to the interface, update all existing classes * Use a mocking library – I have not used any for C++, but if this were Java I’d use Mockito and if this were .Net I’d use Moq. * Create a base test double that implements all of the methods. All test doubles derive from that and only implement the methods needed for test.

The last option does not fix the problem, it controls it. When new methods are added, you update one test double class and all other classes still work.

This may not sound like much of an issue, but in general you’ll have many small test doubles rather than a few large test doubles (or at least that’s the standard recommendation). Why? Because a small, focused test double is less likely to be broken. It also better expressed your intention.

Here is that test double (header file excluded again): TestDoubleSdlLayerInstanceDelegator.h

#pragma once
#include "SdlLayerInstanceDelegator.h"

class TestDoubleSdlLayerInstanceDelegator: public SdlLayerInstanceDelegator {
public:
  TestDoubleSdlLayerInstanceDelegator();
  virtual ~TestDoubleSdlLayerInstanceDelegator();

  const SDL_version *SDL_Linked_Version();
  int SDL_Init(int bitMask);
  void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode);
  void SDL_WM_SetCaption(const char *title, int someIntValue);
};

TestDoubleSdlLayerInstanceDelegator.cpp

#include "TestDoubleSdlLayerInstanceDelegator.h"

TestDoubleSdlLayerInstanceDelegator::TestDoubleSdlLayerInstanceDelegator(){}

TestDoubleSdlLayerInstanceDelegator::~TestDoubleSdlLayerInstanceDelegator(){}

const SDL_version *TestDoubleSdlLayerInstanceDelegator::SDL_Linked_Version() {
  return 0;
}

int TestDoubleSdlLayerInstanceDelegator::SDL_Init(int bitMask) {
  return 0;
}

void *TestDoubleSdlLayerInstanceDelegator::SDL_SetVideoMode(int x, int y, int bitDepth, int mode) {
  return 0;
}

void TestDoubleSdlLayerInstanceDelegator::SDL_WM_SetCaption(const char *title, int someIntValue){}

A Test

Finally, we need some tests and some test-specific test-doubles:
#include <CppUTest/TestHarness.h>

#include "sdl.h"
#include "SdlLayerFactory.h"
#include "SdlHardwareLayer.h"
#include "TestDoubleSdlLayerInstanceDelegator.h"

TEST_GROUP(SdlHardwareLayerTest) {
  virtual void setup() {
    original = SdlLayerFactory::getInstance();
    layer = new SdlHardwareLayer;
  }

  virtual void teardown() {
    if(SdlLayerFactory::getInstance() != original)
      delete SdlLayerFactory::getInstance();

    SdlLayerFactory::replaceInstance(original);
    delete layer;
  }

  SdlLayerInstanceDelegator *original;
  SdlHardwareLayer *layer;
};

struct MajorMinorSdlhardwareLayer : public TestDoubleSdlLayerInstanceDelegator {
  struct SDL_version v;

  MajorMinorSdlhardwareLayer() {
    v.major = 0;
    v.minor = 0;
  }

  const SDL_version *SDL_Linked_Version() {
    return &v;
  }
};

TEST(SdlHardwareLayerTest, ItGeneratsErrorWithLowMajorMinorVersionNumber) {
  SdlLayerFactory::replaceInstance(new MajorMinorSdlhardwareLayer);
  try {
    layer->initializeHardware();
    FAIL("Should have thrown int value");
  } catch (int value) {
    LONGS_EQUAL(1, value);
  }
}

struct InitFailingSdlHardwareLayer : public MajorMinorSdlhardwareLayer {
  InitFailingSdlHardwareLayer () {
    v.major = 3;
    v.minor = 3;
  }

  int SDL_Init(int bitMask) {
    return -1;
  }
};

TEST(SdlHardwareLayerTest, ItGeneratesErrorWhenInitReturnsLessThan0) {
  SdlLayerFactory::replaceInstance(new InitFailingSdlHardwareLayer);
  try {
    layer->initializeHardware();
    FAIL("Should have thrown int value");
  } catch (int value) {
    LONGS_EQUAL(2, value);
  }
}

Notice that there are two test doubles. Also notice that I create them as full (implicitly) inlined classes. Virtual methods and inline methods do not interact well. C++ will make a copy of the method bodies in every .o and increase link times. However, these test classes are only used in one place, so this really does not cause any problems.

In lieu of a mocking library, this is how I’d really write this code.

The Final Big Picture

Here’s an image of the completed product:

Summary

So is this overkill? Is using the actual SDL library causing problems? Is it OK for windows to appear during test? I’m neutral on that subject. The question I want to know is: are the tests working?

  • Do they add value or are they busy work.
  • Do they allow me to make small, incremental steps towards a complete, working implementation
  • Do they run fast enough?
  • Will they run in any environment? (Can I run on a headless system?)
  • Can I run multiple tests at the same time? (Not generally an issue, but it can be.)

If the SDL library is not causing a problem, then this might be overkill. However, on a long-lived project, this little amount of extra work will pay big dividends.

Oh, how long did this really take? Around 1.5 hours. But that included:
  • Starting a Windows XP VM
  • Creating a new projec
  • Setting up the link paths and include paths
  • Creating the initial file and the necessary support to get it to compile
  • Writing the additional classes

In fact, the actual work was probably < 30 minutes total. So while this might look big, in fact it is not. It’s nearly an idiom, so it’s mostly a matter of putting the hooks in place. So is full isolation from SDL worth 30 minutes?

Yes!

All your source files are belong to us

If this looks like a complete example, it is. I have this building and running in Visual Studio 2008. I took GBGame’s original method and got it to compile and link with a minimal set of additional source files. Then I made the structural changes to support test. And here’s all of the source code, file by file:

sdl.h

#pragma once

extern "C" {
  const int SDL_INIT_VIDEO = 1;
  const int SDL_INIT_AUDIO = 2;
  const int SDL_INIT_NOPARACHUTE = 4;
  const int SDL_DOUBLEBUF = 8;

  struct SDL_version {
    int major;
    int minor;
  };

  const SDL_version *SDL_Linked_Version();
  int SDL_Init(int bitMask);
  void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode);
  void SDL_WM_SetCaption(const char *title, int someIntValue);
};

sdl_implementation.cpp

#include "sdl.h"

const SDL_version *SDL_Linked_Version() {
  return 0;
}

int SDL_Init(int bitMask) {
  return  - 1;
}

void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode) {
  return 0;
}

void SDL_WM_SetCaption(const char *title, int someIntValue){}

SdlHardwareLayer.h
#pragma once

#include <string>

class SdlHardwareLayer {
public:
  SdlHardwareLayer();
  virtual ~SdlHardwareLayer();
  void initializeHardware();

private:
  int m_x;
  int m_y;
  int m_bitDepth;
  std::string m_title;
  void *m_screen;
};

SdlHardwarelayer.cpp

#include "SdlHardwareLayer.h"

#include "sdl.h"
#include "SdlLayerFactory.h"
#include "SdlLayerInstanceDelegator.h"

SdlHardwareLayer::SdlHardwareLayer() {
}

SdlHardwareLayer::~SdlHardwareLayer() {
}

void SdlHardwareLayer::initializeHardware() {
    const SDL_version * sdlVersion = SdlLayerFactory::getInstance()->SDL_Linked_Version();
    if (sdlVersion->minor < 2 && sdlVersion->major < 2) {
      throw 1;
    }
    else if (SdlLayerFactory::getInstance()->SDL_Init(SDL_INIT_VIDEO |
            SDL_INIT_AUDIO |
            SDL_INIT_NOPARACHUTE) < 0) {
              throw 2;
    }
    else {
        m_screen = SDL_SetVideoMode(m_x, m_y, m_bitDepth, SDL_DOUBLEBUF);
        if (0 != m_screen) {
            SDL_WM_SetCaption(m_title.c_str(), 0);
        }
    }
}

SdlLayerFactory.h
#pragma once

class SdlLayerInstanceDelegator;

class SdlLayerFactory
{
public:
  static SdlLayerInstanceDelegator *getInstance();
  static SdlLayerInstanceDelegator *replaceInstance(SdlLayerInstanceDelegator *replacement);

private:
  static SdlLayerInstanceDelegator *instance;

  SdlLayerFactory();
  ~SdlLayerFactory();
};

SdlLayerFactory.cpp

#include "SdlLayerFactory.h"

SdlLayerInstanceDelegator *SdlLayerFactory::instance = 0;

SdlLayerFactory::SdlLayerFactory(){}

SdlLayerFactory::~SdlLayerFactory(){}

SdlLayerInstanceDelegator *SdlLayerFactory::getInstance() {
  return instance;
}

SdlLayerInstanceDelegator *SdlLayerFactory::replaceInstance(SdlLayerInstanceDelegator *replacement) {
  SdlLayerInstanceDelegator *original = instance;
  instance = replacement;
  return original;
}

SdlLayerInstanceDelegator.h
#pragma once

struct SDL_version;

class SdlLayerInstanceDelegator {
public:
  virtual ~SdlLayerInstanceDelegator(void) = 0;

  virtual const SDL_version *SDL_Linked_Version() = 0;
  virtual int SDL_Init(int bitMask) = 0;
  virtual void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode) = 0;
  virtual void SDL_WM_SetCaption(const char *title, int someIntValue) = 0;

protected:
  SdlLayerInstanceDelegator();

private:
  SdlLayerInstanceDelegator(const SdlLayerInstanceDelegator &rhs);
  SdlLayerInstanceDelegator &operator = (const SdlLayerInstanceDelegator &);
};

SdlLayerInstanceDelegator.cpp

#include "SdlLayerInstanceDelegator.h"

SdlLayerInstanceDelegator::SdlLayerInstanceDelegator(){}

SdlLayerInstanceDelegator::~SdlLayerInstanceDelegator(){}

TestDoubleSdlLayerInstanceDelegator.h
#pragma once
#include "SdlLayerInstanceDelegator.h"

class TestDoubleSdlLayerInstanceDelegator: public SdlLayerInstanceDelegator {
public:
  TestDoubleSdlLayerInstanceDelegator();
  virtual ~TestDoubleSdlLayerInstanceDelegator();

  const SDL_version *SDL_Linked_Version();
  int SDL_Init(int bitMask);
  void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode);
  void SDL_WM_SetCaption(const char *title, int someIntValue);
};

TestDoubleSdlLayerInstanceDelegator.cpp

#include "TestDoubleSdlLayerInstanceDelegator.h"

TestDoubleSdlLayerInstanceDelegator::TestDoubleSdlLayerInstanceDelegator(){}

TestDoubleSdlLayerInstanceDelegator::~TestDoubleSdlLayerInstanceDelegator(){}

const SDL_version *TestDoubleSdlLayerInstanceDelegator::SDL_Linked_Version() {
  return 0;
}

int TestDoubleSdlLayerInstanceDelegator::SDL_Init(int bitMask) {
  return 0;
}

void *TestDoubleSdlLayerInstanceDelegator::SDL_SetVideoMode(int x, int y, int bitDepth, int mode) {
  return 0;
}

void TestDoubleSdlLayerInstanceDelegator::SDL_WM_SetCaption(const char *title, int someIntValue){}

RealSdlLayerInstanceDelegator.h
#pragma once
#include "SdlLayerInstanceDelegator.h"

class RealSdlLayerInstanceDelegator : public SdlLayerInstanceDelegator
{
public:
  RealSdlLayerInstanceDelegator();
  virtual ~RealSdlLayerInstanceDelegator();

  const SDL_version *SDL_Linked_Version();
  int SDL_Init(int bitMask);
  void *SDL_SetVideoMode(int x, int y, int bitDepth, int mode);
  void SDL_WM_SetCaption(const char *title, int someIntValue);
};

RealSdlLayerInstanceDelegator.cpp

#include "RealSdlLayerInstanceDelegator.h"
#include "sdl.h"

RealSdlLayerInstanceDelegator::RealSdlLayerInstanceDelegator(){}

RealSdlLayerInstanceDelegator::~RealSdlLayerInstanceDelegator(){}

const SDL_version *RealSdlLayerInstanceDelegator::SDL_Linked_Version() {
  return ::SDL_Linked_Version();
}

int RealSdlLayerInstanceDelegator::SDL_Init(int bitMask) {
  return ::SDL_Init(bitMask);
}

void *RealSdlLayerInstanceDelegator::SDL_SetVideoMode(int x, int y, int bitDepth, int mode) {
  return ::SDL_SetVideoMode(x, y, bitDepth, mode);
}

void RealSdlLayerInstanceDelegator::SDL_WM_SetCaption(const char *title, int someIntValue) {
  ::SDL_WM_SetCaption(title, someIntValue);
}

SdlHardwareLayerTest.h
#include <CppUTest/TestHarness.h>

#include "sdl.h"
#include "SdlLayerFactory.h"
#include "SdlHardwareLayer.h"
#include "TestDoubleSdlLayerInstanceDelegator.h"

TEST_GROUP(SdlHardwareLayerTest) {
  virtual void setup() {
    original = SdlLayerFactory::getInstance();
    layer = new SdlHardwareLayer;
  }

  virtual void teardown() {
    if(SdlLayerFactory::getInstance() != original)
      delete SdlLayerFactory::getInstance();

    SdlLayerFactory::replaceInstance(original);
    delete layer;
  }

  SdlLayerInstanceDelegator *original;
  SdlHardwareLayer *layer;
};

struct MajorMinorSdlhardwareLayer : public TestDoubleSdlLayerInstanceDelegator {
  struct SDL_version v;

  MajorMinorSdlhardwareLayer() {
    v.major = 0;
    v.minor = 0;
  }

  const SDL_version *SDL_Linked_Version() {
    return &v;
  }
};

TEST(SdlHardwareLayerTest, ItGeneratsErrorWithLowMajorMinorVersionNumber) {
  SdlLayerFactory::replaceInstance(new MajorMinorSdlhardwareLayer);
  try {
    layer->initializeHardware();
    FAIL("Should have thrown int value");
  } catch (int value) {
    LONGS_EQUAL(1, value);
  }
}

struct InitFailingSdlHardwareLayer : public MajorMinorSdlhardwareLayer {
  InitFailingSdlHardwareLayer () {
    v.major = 3;
    v.minor = 3;
  }

  int SDL_Init(int bitMask) {
    return -1;
  }
};

TEST(SdlHardwareLayerTest, ItGeneratesErrorWhenInitReturnsLessThan0) {
  SdlLayerFactory::replaceInstance(new InitFailingSdlHardwareLayer);
  try {
    layer->initializeHardware();
    FAIL("Should have thrown int value");
  } catch (int value) {
    LONGS_EQUAL(2, value);
  }
}

RunAllTests.cpp
#include <CppUTest/CommandLineTestRunner.h>

int main(int ac, char **av) {
  return CommandLineTestRunner::RunAllTests(ac, av);
}

Wormholes, FitNesse and the return of SetUp and TearDown links 38

Posted by Brett Schuchert Thu, 16 Apr 2009 18:33:00 GMT

Recently, I was working on adding back a feature to FitNesse that had been removed, links at the bottom of each page to add a setup or teardown page to the current page. After racking my brain and spending time in git with file histories, I had discovered a point in time where the feature was there and the next commit where it was gone. It was not obvious to me what had changed to break the feature until I talked with Bob Martin (much of this has to do with my lack of experience using git). He mentioned a change in the handling of importing header and footer pages (related to my problem) and sure enough, when I took a look in the debugger, I found out that the information I needed to reintroduce the feature had essentially be removed as a result of a bug fix.

This was, apparently, not a heavily used feature. In fact, I had not used it much until I recently started working on tutorials for FitNesse. And given the release criterion for FitNesse, removal of the feature did not break anything (no acceptance tests nor unit tests).

Anyway, the point at which the information was available that I needed and the point where I needed to use the information were may steps away from each other both in the call stack as well as the instance hierarchy (think composite pattern). I did not want to significantly change the method call hierarchy so I instead decided to hand-roll the wormhole pattern as it manifests itself in AspectJ (and not the wormhole anti-pattern).

For background on AspectJ and AOP in general, have a look at these self-study tutorials.

The wormhole pattern is well-documented in AspectJ In Action. It consists of two point cuts that combine two different, separated, parts of the system. It grabs information available at (in this case) the entry point to the system and makes that information available deeper in the system without passing the parameter directly. That’s where the name comes from, the pattern bridges information across to unconnected points via the wormhole. It is my favorite patter name (not favorite pattern, just name).

AspectJ happens to store certain runtime information in thread local storage and the wormhole pattern exploits this fact. Here’s another example that’s close to the wormhole pattern. This is actually a common technique. If you’ve read up on the recommendations on using Hibernate in a JSE environment either in “Hibernate in Action” or the more recent Javer Persistence with Hibernate, one recommendation is to pass around sessions and such in thread local variables. Under the covers, JEE containers do the same thing.

Even though this is a “common” technique, I’m not a huge fan of using thread-locals. 1. They are thread-specific global variables. 1. You better be sure there’s no threading between the two points. In this case, the threading has already happened. Once a FitNesse responder gets a hold of an HTTP request, the remaining processing is in a single thread.

On the other hand, if I did not use thread local storage, the change required to get information I needed would have either require changing one of the objects already in the parameters being passed (very ugly) or changing method signatures all over the place (somewhat less ugly but a stronger violation of LSP). So in this case, I think thread local variables are the least ugly of the available options.

(As a side note, that’s my definition of design: Select the solution that sucks the least.)

If you’re like me, you don’t use thread locals very often. I wanted to make sure that the thread local information would get properly cleaned up and I wanted to hide all of the magic in one place, so I created a simple class called ThreadLocalUtil. I used TDD to create the class and when I though I was done, I wanted to make sure that I had written the clear() method correctly. I knew that for a single thread the clear() method worked as expected, but I wanted to make sure it did not affect other threads.

So my problem was I needed 2 threads and I wanted a particular ordering of events:
  • T1: Store value in thread-local storage.
  • T2: Clear its local storage.
  • T1: Read its local storage, value stored should still be available.
This really isn’t a hard test to write other than the ordering of events. To make that work, I used latches and I had the threads manually single each other. Here’s the test:
package fitnesse.threadlocal;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import java.util.concurrent.CountDownLatch;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class ThreadLocalUtilTest {
  private String valueFound;

  // snip - several other tests removed to focus on this test

  CountDownLatch t1Latch = new CountDownLatch(1);
  CountDownLatch t2Latch = new CountDownLatch(1);

  class T1 implements Runnable {
    public void run() {
      try {
        ThreadLocalUtil.setValue("t1", "value");
        t2Latch.countDown();
        Thread.yield();
        t1Latch.await();
        valueFound = ThreadLocalUtil.getValue("t1");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  class T2 implements Runnable {
    public void run() {
      try {
        t2Latch.await();
        ThreadLocalUtil.clear();
        t1Latch.countDown();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  @Test
  public void assertThatClearInOneThreadDoesNotMessUpAnotherThread()
      throws InterruptedException {
    Thread t1 = new Thread(new T1());
    Thread t2 = new Thread(new T2());
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    assertEquals("value", valueFound);
  }
}

This example illustrates using the java.util.concurrent.CountDownLatch class to signal between threads.

Main Test Method
  • Creates two threads and start them.
  • Wait for both threads to complete.
T1.run(), T2.run()
  • If T2 starts running before T1, no problem it waits on the countdown latch.
  • When T1 starts, it stores a value in a thread local using the util class.
  • T1 then counts down, releasing T2.
  • T1 then yields, it is done until its countdown latch is signaled.
  • T1 waits for a signal.
  • T2 is released from its countdown latch.
  • T2 sends clear to the thread local util class (there’s a test that verifies clear() works as named in a single thread.
  • T2 then signals T1 to continue by calling its countdown latch.
  • T2 completes at some point later.
  • T1 starts running again, grabs the value out of thread local storage and puts it in the valueFound.
  • T1 completes.
Main Test Method
  • The completion of T1 and T2 make the join methods called in the original test method to return, whereupon the test can complete.
  • Verifies that the variable valueFound, set in T1.run, stores the expected result.

This kind of hand-rolled synchronization is certainly error prone. This is where having a second pair of eyes can really help. However, this seemed to verify that the clear method as written worked in multiple threads as expected.

If you’re interested in seeing the ThreadLocalUtil class or ThreadLocalUtilTest class, grab a copy of FitNesse from github.

Twitter Does Not Allow For Nuance 20

Posted by Brett Schuchert Mon, 13 Apr 2009 03:26:00 GMT

If you have something deep to say, 140 characters is not going to cut it very often. And sometimes, when it does, it’s almost too opaque to be grasped by anybody who doesn’t already grok it.

Here’s one example:
Polymorphsim – Same request different response.

That’s the essence of polymorphism, which can help with the SRP and the OCP. That one happens to fit in to a single bullet of < 140 characters. However, there’s a lot there. In fact, while that could be the definition, it has many ramifications and the context matters. So this probably falls in the opaque category.

A few days ago I made a pithy statement on twitter:
Test is Definition (TDD), therefore code w/o Test, not defined. Therefore, it is broken (or never wrong, take your pick).
I wanted to make that fit in a single tweet. I forgot to put in to whom I was replying (and now I cannot remember[sorry]). Anyway, I got the following replies:
@dws – Test is a form of definition. It’s a very good form, but it’s not the only one.
@jamesmarcusbach – If test is definition, then * must be the same as +, because 2+2=4 & 2 * 2=4. No, a test is just an event. (via )
@ecomba I love this statement!
I wanted to reply with a little more length, so I figured this was a good place to do it.
To @ecomba, thank you. I’m assuming your reply was in response to that statement, but if not, then thanks anyway!-)
To jamesmarcusbach – I do not agree. When I assess “Test is Definition (TDD)”, that suggests to me that there are many, many unit tests (and even acceptance test, load tests, smoke tests, manual tests, exploratory tests, debugging, ...). The union of all of those tests form the definition. You’ve picked one example and erroneously extrapolated from it to discount a tweet, and I don’t think you did it. (And to be clear, I strongly prefer certain forms of tests over others.)

I also do not agree with your interpretation of testing as an event. At the very least it is a process. Even more so, it is a continuous process that is only done when the project is done, which is when the customer stops paying. So I think you and I are using the same 4 letters (test) in very different ways. I suspect, however, that I’ve committed the same error in interpreting your use of the word event as you’ve interpreted my use of the word test.

And finally, I don’t agree with your example for many reasons, two of which are:
  • I mentioned TDD, I don’t check the compiler very often, so I won’t be testing 2+2 or 2*2.
  • However, if I am, I would not only pick your two examples. I’d have many trying to capture all of the equivalence classes.

To @dws – Sure, test is a form a definition. And I also agree that it is not the only form. I never said it was the only form (I think that came from you). As for not including the word form in my tweet, I did include TDD. Does that not invoke a large context, part of which is that TDD can possibly be a form of definition? Of course, saying Test is Definition is actually a metaphor, right?
OK, having replied with > 140 characters, I’m going to restate the tweet. Since the restatement is longer than > 140 characters it will have the luxury of being wrong in many more ways than the original.
Test is one form of Definition (TDD). If you do not have any other form [the context of the original tweet I believe, to which I was responding but mistakenly forgot to include the @..] (e.g., some requirements specification or a verbal agreement with some sales person), then the tests are one good definition of what is/is not correct. If we go with the definition of our system in terms of the tests, then where there are no tests, there is no definition and therefore any behavior is OK. Sure you can argue, the system should not crash when a user enters a character into a field that expects numbers, but really, if that behavior is not defined, saying “it should not do that because of common sense” really is saying “Well I assumed it would not do that, you violated my assumption therefore I will prove you wrong in a battle royal.” Even more, it’s great because when it happens, the user will be so mad that s/he will make sure to let you know your definition of the system is incorrect. You can respond by writing another test to improved the fidelity of your understanding of the system.
This last part is really a tip of the hat to Jerry Weinberg who said (and I paraphrase probably incorrectly):
If there are no requirements, any solution will do.
Of course, he was probably referring to Alice in Wonderland…
There’s a lot more to this subject. For example, I don’t believe in proving systems correct. Why? Even if you’ve proven that your system conforms 100% to your formal specification, there’s no reasonable way to prove:
  • Your formal description is complete (yes you can for simple data structures, BFD, don’t care about formally proving a simple data structure).
  • For any complex system described in terms of a formal language, the original inception of the system was in natural language. Prove the transformation, and then prove the natural language specification is correct/complete.
  • I’m a big fan of Gödel’s incompleteness theorem. It is related to this at least loosely because.

By logical extension, you can pick up from this that I’m not a big fan of using formal languages like UML to build my system “from the diagrams.”

So in conclusion I like my original statement. I understand that it is not literally true or “The Truth.” It’s a way of thinking about things. I think the feedback was valuable because communication is ambiguous.

I could have been more clear. Maybe I should not have tried to even express the idea on Twitter. By throwing something out there, it gets refined through feedback and there’s a bettering understanding to be had. At some point we can create some pithy statement that has all of the meaning and none of the meaning at the same time. When we’ve done that, we get to start all over again.

Maybe the statement is simply wrong. At the very least it has an agenda. Question is, does it help, hinder or simply represent a single drop in an infinite bucket?

Flame on. I deserve it.

!define TEST_SYSTEM {fit:A} 55

Posted by Brett Schuchert Tue, 04 Nov 2008 00:08:00 GMT

Uncle Bob has been busy with FitNesse lately. If you have been following him on Twitter or if you read his blog post on the subject, then you are aware of his work on Slim.

This post, however, is not about that. It is about something he did to make it possible to execute different tests in different VM’s.

By default, when you click on the test or suite buttons to run tests, FitNesse finds the tests it will run and executes them in a single VM.

If you want to select a particular test runner in FitNesse, you can add the following to a page:
!define TEST_SYSTEM {fit}
Or you could add the following:
!define TEST_SYSTEM {slim}

If you do not define this variable, then fit is the test system used to execute tests, making the first example redundant…almost.

These variable definitions are inherited. FitNesse will search up the page hierarchy to find variable definitions. If you do not define TEST_SYSTEM anywhere in a page’s hierarchy, then that test will be executed with fit. However, if any of the pages above the current page changed the runner to slim, then Slim will be the test runner.

The other thing that you can do is add a “logical-vm name” to the end of the runner. Here are two examples:

On some page

!define TEST_SYSTEM {fit:vm1}

On a different page

!define TEST_SYSTEM {fit:vm2}

All tests under the page containing the first define run in a vm with the logical name vm1. The same is true for vm2.

By default (i.e., you have not defined TEST_SYSTEM anywhere), all tests are run in the same vm. More precisely:
  • When you click the test button, all tests executed as a result of that button click run in one VM.
  • When you click the suite button, all tests executed as a result are executed in the same VM.

As soon as you introduce the TEST_SYSTEM variable, the tests might execute in the same VM or different VM’s.

Conceptually, there’s a default or unnamed VM under which all tests execute. As soon as a page contains a TEST_SYSTEM with the added :VMName syntax, that page and all pages hierarchically below it run in a different VM.

If for some reason you want to have two unrelated page hierarchies execute in the same VM, you can. Define the TEST_SYSTEM variable with the same logical VM name.

Why did he add this feature

I asked him to. He was working on that part of FitNesse so I figured he’d be able to add the feature for a project I’m working on.

It has to do with service-level testing of a SOA-based solution. If you’re interested in hearing about that and the rationale for adding this feature to FitNesse, let me know in the comments and I’ll describe the background.

Test Driven Meetings 13

Posted by Brett Schuchert Thu, 12 Jun 2008 05:28:00 GMT

I’ve seen it as I’m sure you have. You look in to a conference room, there’s a spreadhseet or a word document or some such “deliverable” displayed on the overhead. There’s one person engaged, talking about it and N – 1 people with glassy-eyed stairs, hoping for the meeting to end.

What’s even worse. You have the next meeting in that same room and you know your meeting is going to be a repeat of the previous meeting. Even so, it irritates you when they go over because that meeting room is yours.

Make an agenda! That’ll solve it.

Maybe, but probably not. Most agendas are task-orietned rather than goal-oritned. So you might make it through the agenda but what have you accomplished besides following an agenda?

When people discuss wasted time, do they joke about meetings? Do they? Are you listening? If people are joking about meetings being a complete waste of time, then the meetings are probably a complete waste of time. (Ever consider keeping a count of the number of times you hear the same thing during a single day? You’d be amazed what you can learn by just listenting to your team.)

Does that mean all meetings need to be that way?
No
Why were we told to write agendas?
To keep focus and know we’re making progress
What purpose does a meeting serve in the delivery of value to someone who’s got $$ to spend?
That varies by the meeting
What do tests written first (unit or acceptance) accomplish?
Exress how we know somethign has worked

Can we do this with meetings? If we did, what might it look like? Would the very act of trying to express success critera for a meeting have a profound effect on how a meeting progresses? Isn’t this really just Covey’s 2nd habit (begin with the end in mind).

What if you wanted to try? How might you begin?
It’s all about he benjamins.

The first thing you need to ask is how what you’re going to do in a given meeting directly or indireclty adds value. Of coruse, to do that you need to know what is valuable.

Suppose, for example, you need to have a traditional requirements meeting of some sort (there is a traditional requirements meeting, it is usually bogged down in implementation details or it’s stuck in “how” mode versus “what” mode – the more you talk about database columns, the more you’re deep into requirements … NOT!).

OK, so take a step back. You think you need this meeting on requirements.

Why?
So we can figure out what this feature needs to do
Why?
So we know what to what to write, what schema changes we need
Why?
So we can implement it
Why?
Because customer X has a contract and we’ll be in breech of contract if we don’t write it

AH! That sounds like we’re getting close to something valuable. We have some feature that’s been promised to a current customer. OK, so before we go any further, that information needs to be part of the context for the meeting.

What’s been promised? What does the customer think has been promised?
If you cannot answer this, you are in deep trouble

OK, so you have promised some feature. Apparently that feature is ill defined (or you wouldn’t need the requirements meeting). So, what problem does that feature address?

If you know this, then you have something to grow. Maybe your first attempt at an acceptance test for your meeting is something like this:
This meeting is a success if we have described scenarios that cover what this feature will do for at the top three uses of this feature
OK, so now here’s your agend:
  • This requirements meeting is to address feature X
  • We need to discuss this because customer X has been promised this feature and if we do not do it by (insert a real date here), we are in breech of contract
  • This meeting will be a success if we can describe scenarios covering the top three uses of this feature
That’s a start. I can think of ways to improve that. Here are a few examples:
  • How do we define top 3? Is that something we know or something we need to accomplish?
  • What do we mean by “describe scenarios” does it include:
    • Acceptance tests
    • One or more stories (or maybe it is just a scenario in a use case, pick your requirements coolaid)
    • Used by whom? Are there different roles? Is that important?
    • What about a timebox? (Maybe that’s implicit because it’d be an electronic request)
    • ...

Here’s the thing, do you think you’d be more inclined to think a meeting described thusly would have some value?

It's All Data 8

Posted by Brett Schuchert Wed, 14 May 2008 21:37:00 GMT

<sarcasm>

“So, you managed to pick stories that only involved changing data…”

“Yep.”

Great, there’s no need to test it, right?

Therefore, testing Java classes is unnecessary.
  1. The JVM simply reads files in a particular format (the Java Class format)
  2. The JVM processes those files
  3. Java class files are just data to the JVM
  4. Java source files are just data to the Java Compiler to generate data used by the JVM
  5. We do not need to test data changes
  6. Therefore we do not need to test Java classes.

q.e.d.

Then Norbert asked “Oh, by the way, did you version your data changes?”

</sarcasm>