Testing…1…2…3…

18 08 2010

Before starting I’d just like to get it out of the way, yes I know there are libraries out there that do just that, heaps and heaps, I’ve used SDL, I’ve used Ogre3D, I’ve checked out UDK and Unity [I’m into game programming if you haven’t noticed] and I know there’s no need to reinvent the wheel. I don’t want wheels, I like walking and I want to write a lightweight rendering suite using OpenGL and built on [at the moment] the WIN32 API; portability is important and will be addressed by minimizing and localizing API dependent calls.

For this lovely suite I’m taking a different approach than what I usually would and am test-driving it. I’ve never really realized how interesting it would be to do so but I don’t want to complain about trying to force OOD into the WIN32 API. I want to talk about the process I’ve been using, namely the TDD approach.

Again, I’d like to stress here that I’m very much a beginner to TDD and the entire Agile methodology, so these concepts obviously do not represent the actual usage of the methodology [unless they really do and can someone please tell me if they do, because I’d love to know].

Ok, back to the window code.

On papar I made a few quick broadstrokes of the things that I wanted to achieve and the items I would need to have in order to achieve them. The list looks something like this;
GOAL: Render one pixel using OpenGL and the WIN32 API
THINGS I NEED:
1. Window.
2. DrawingSurface.
3. OpenGL Rendering Context.

So far, so good. What’s the first thing I wanted again? ah yes, a Window. A Win32::Window to be more precise [I like namespaces]. Um… Let’s pretend that I have one already, what’s the first thing I’d like to do with it?
TEST(check_window_name)
{
  Win32::Window window("TestWindow", "TestTitle", Size(640,480), Position(0,0));
  CHECK_EQUAL("TestWindow", window.Name());
};

We can see where this is going so let’s just jump the gun. Some refactoring later, adding a fixture or two and I’ve had a window class that had some nice attributes; now what? there’s still no actual window on the screen, obviously I need to create it.

TEST_FIXTURE(WindowFix, create_window)
{
  window.Create();
  CHECK_EQUAL(true, window.Created());
  window.Destroy();
  CHECK_EQUAL(false, window.Created());
};

Er… two checks in one call, I don’t like it very much and in my code I’ve actually refactored it away; but for the sake of brevity I’ll keep this test. The easy way to solve this is to just change a boolean value on calling both Create() and Destroy(). And I’ve done just that, TDD stating we need to add just enough functionality in order to pass the test. But… But… ARGH, it doesn’t really help me get an actual Window on the screen, does it???

I’ve banged my head on it for a while, I stared at the code for an hour then I started writing some reflective notes and then, then I had a lightbulb go in my head. Testing return values are only part of the tests [and probably not the most important part] that we have in the arsenal. Tests should also test object-object interactions and my object interacts with the WIN32 API.

A quick search through MSDN and I came up with this test;
TEST_FIXTURE(WindowFix, check_window_class_with_WIN32_API)
{
  WNDCLASSEX result;
  CHECK( ::GetClassInfoEx(window.hInstance(), window.Name(), &result) );
}

Run and… Watch it fail miserably. Now I needed to get this test passing, which means actually writing code to talk with the API. And there you have it, this little bit started me on the road to better understand what [I think] TDD is about.

Writing effective tests isn’t easy, doubly so for when they must test interactions. I’ve written before about needing to make assumptions when writing tests and I really thought that I understood that; I was wrong, the fact I nearly gave up here tells me I was still stuck in thinking about tests from a reverse point of view, meaning that code needs to be tested, rather than tests need to be coded. It’s lucky I talk to myself because what brought me to this test was a fairly simple question.
"Ok Amir, now that you've created the window, can you ask the WIN32 API to check whether its there or not?"

Next post would be about the other interesting thing that came up from test-driving the Win32::Window.

The heart of the dragon is screaming awaiting, to write the black last page

Advertisements




Don’t point, it’s impolite

14 08 2010

Before starting this I’ll post some code that gave me the headache.

It isn’t complex code, just a simple Win32 window class wrapper for the [surprise, surprise] window using WIN32 API.
class Win32
{
  public:
    void Create(std::string name);
    void Destroy();
  private:
    WNDCLASSEX mWindowClass;
    HINSTANCE mInstanceHandle;
};

void Win32::Create(std::string name)
{
  mWindowClass.cbSize = sizeof(WNDCLASSEX);
  mWindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  mWindowClass.lpfnWndProc = Tool::WinProc;
  mWindowClass.cbClsExtra = 0;
  mWindowClass.cbWndExtra = 0;
  mWindowClass.hInstance = mInstanceHandle;
  mWindowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
  mWindowClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
  mWindowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  mWindowClass.lpszMenuName = nullptr;
  mWindowClass.lpszClassName = name.c_str();
  mWindowClass.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);
  RegisterClassEx(&mWindowClass);
}

void Win32::Destroy()
{
  UnregisterClass(mWindowClass.lpszClassName, mInstanceHandle);
}

I’ve omitted some tests and some other bits which aren’t really important for this post. But the gist of it is there, and it’s mostly fairly trivial.
Can you stop my one, HUGE, mistake there?

Run this code and watch it go up in smoke. The UnregisterClass() function in the Destroy() method will break and quite badly, in fact when I ran it, it reported that the class in question doesn’t even exist. But, but, I’ve registered it, I know I did and the RegisterClass() function worked quite fine, nothing broken there, so, what’s the problem??

A bit of debugging later and I found that the mWindowClass.lpszClassName in the Destroy function contains garbage. Apparently somewhere betweeen calling Create() and Destroy() something bad happened to the lpszClassName [have I mentioned how much I dislike this naming convention??]

Bit more of debugging, running through the breakpoints and examining the contents of the memory and…

Pointers…. Who the hell had the brilliant idea to use a naked char* for the window class name in the WNDCLASSEX [that’s WIN32 API for you Linux, Apple, or other OS people other there].

That should’ve given you the answer by now, if not, then here it goes.

When we call the Create function it generates the name string temporary on the stack and continues on. Then at some later point we’re assigning the name.c_str() to the mWindowClass.lpszClassName – this is a pointer, it’s not a true assignment of values but rather the char* is now pointing to the beginning of the temporary array of characters that is name. Everything works fine inside the Create, we finish what we have to do and leave the function. Woosh! The function temporaries [being temporary] get deleted, erased and/or filled with garbage and the mWindowClass.lpszClassName is now pointing at, GARBAGE!

Yey, found the bug; argh, should have been more careful with my pointers. Fixing it isn’t hard, just introduce a string variable in the Win32 class to hold the name through the life-duration of the class itself.

I should have done it this way originally but I thought I’d be smart about things and save some memory. The moral of the story is, as usual, code correctly, make sure everything is expressed properly and stop trying to be too smart for my own good.

It’s been a long December and there’s reason to believe next year will be better than the last…