Contents

Factoring Your Code

The task was to modify the map in the viewer to use the new S3 located multiresolution tile hierarchy to display the world map. There's already code to display the map located in llworldmap.cpp and llworldmapview.cpp that we happily hacked through to implement the new tile pulling and rendering code.

Now, unit tests are associated with cpp files on a 1:1 basis and, since llworldmap.cpp had no unit tests, we could have written a new llworldmap_test.cpp file.

Problem is, when writing unit tests, llworldmap.cpp gets compiled and linked along llworldmap_test.cpp in its own independent llworldmap_test project. That brings in a whole tangle of dependencies that needs to be cleared up, calls to be stubbed, etc... A discouraging task.

So, since we wrote a specific class to handle the new data structure, we choose to isolate that class in its own llworldmipmap.cpp file.

The moral of the story here is that, when writing unit tests for legacy code, you'll have to think before hand at to which set of classes you want to rework and, possibly, refactor and isolate somewhere. It's all for the goodness of the code structure for sure but that's work and you need to consider it carefully before rushing and write unit tests.

Adding the Test Structure

Since CMake is in the way, the first thing we did was to build the minimum scaffolding so that we could rerun CMake, rebuilt the solution and get something clean to start coding.

Create the Test File

The code to be unit tested being in indra/newview/llworldmipmap.cpp, we created a indra/newview/tests/llworldmipmap_test.cpp file. We had to create the tests folder as no unit tests had been written for newview yet.

As said before, do not try to be creative here: the location of the tests folder, its name (tests) and the name of the unit test file cannot depart from a rigid set of rules assumed in LLAddBuildTest.cmake. Of course, changing those rules is possible but it will make all other tests fail or not being build at all so don't even think about doing this... or at least, discuss it on all relevant mailing lists before hand.

Now, we put a very short piece of code in llworldmipmap_test.cpp. Besides the usual license comment header, here's the minimum code so that tut is getting compiled.

We have to include the LLAddBuildTest cmake file here to get the LL_ADD_PROJECT_UNIT_TESTS macro.

Our code (like most newview code) is depending heavily on the precompiled header. We need to add llviewerprecompiledheaders.cpp to the test project which will prevent lots of link errors (at least, for anything related to the precompiled header).

viewer : the target project this unit test must be added to as a dependency

"${viewer_TEST_SOURCE_FILES}" : the list of cpp files to test

OK, now we're ready to launch CMake again and rebuild the solution.

Rebuild the Solution

We built the solution as usual. We always delete the indra/build-platform folder and type the following command (after navigating to the indra folder):

./develop.py

Nothing unusual here.

Now, we open the Second Life project and verify that the projects PROJECT_viewer_TEST_llworldmipmap and viewer_tests are in the list of projects. Open the PROJECT_viewer_TEST_llworldmipmap project and verify that the following files are in the list:

We're not done with prep work: even if we are not doing anything with LLWorldMipmap objects yet, we're still compiling llworldmipmap.cpp and linking its object code. Now it's time to dive into writing code.

Writing the Test File

Stubbing Classes

The first thing we wanted to do was stubbing the code so that we could link. In our case, since the dependencies were well isolated, we just let the linker guide us listing the symbols it couldn't resolve and wrote stub implementation for them. Here's the code we added at the beginning of llworldmipmap_test.cpp:

There are actually other ways of writing unit tests. It's not required to write a wrapper class but we found it easier to follow and, potentially, a great trick to overload a couple of methods that could be impossible to test.

Writing the tests is the important and, actually, easiest part. The trick is to know what to test and how. In the case of llworldmipmap, we simply took the list of all public methods and wrote a test for each:

Committing the Test

There's nothing special to do to get the unit tests added to the list of tests being run. Just commit the code as you usually do. Don't forget to add the new files and folder, and commit the CMakeList.txt changes.