Stefano Tommesani

  • Increase font size
  • Default font size
  • Decrease font size
Home SDLC Unit testing with Visual C++ 2012

Unit testing with Visual C++ 2012

The article related to multi-threading and SSE2 optimizations (you can find it here) uses a quick-and-dirty way to check if the optimized code is correct, i.e. it runs an iteration on a given input image with a reference serial code, stores the resulting output image, runs an iteration of the optimized code on the same input image, checks if the output image matches that obtained with the reference code. This is a valid approach, but the location is clearly wrong: even if we are writing a demo application, the testing code should be separate in unit tests that can be automated and repeated before check-ins and builds. It's time to modify that code to use the awesome support for unit testing contained in Microsoft Visual C++ 2012.

screen shot of unit testing with Microsoft Visual C++ 2012

Below you will find the full source code of unit tests (and here you can download the complete Visual C++ project), but before reading that code, let's see which steps are required to get there:

  1. every image processing routine must be extracted out of the main C++ file and put in a separate C++ source/header file (named TBBDemoRoutines.cpp and TBBDemoRoutines.h), so that the code that we want to test is separate from the other stuff
  2. create a new Visual C++ solution using the wizard for a unit testing project for native code
  3. add the files containing the code to test (TBBDemoRoutines.[cpp,h]) to this new project, and set the project properties so that it finds the required library (Intel TBB)
  4. the Visual C++ wizard creates a skeleton of the unit testing implementation that we need to flesh out: we have a class named TBBUnitTest defined with TEST_CLASS, and a method named TestTBB1 defined with TEST_METHOD. Each TEST_METHOD should test an aspect of a TEST_CLASS, so in this unit testing project we will define a TEST_METHOD for every possible implementation of optimized image loops, using Intel TBB, SSE2 or both.
  5. most TEST_METHODs are likely to share an great deal of initialization and finalization code, so it is not useful to include such code in every TEST_METHOD definition, but it is recommended to create a TEST_METHOD_INITIALIZE (where initialization code is run before calling each TEST_METHOD) and a TEST_METHOD_CLEANUP (whre finalization cod is run after calling each TEST_METHOD). In this demo, TEST_METHOD_INITIALIZE creates both the input (RGBAImage) and output image buffers, fills the input image buffer with random data, runs the reference serial code on the input image buffer and stores the result in an output image buffer (SerialGrayImage), and clears the other output image buffer (ParallelTBBGrayImage); TEST_METHOD_CLEANUP deallocates the image buffers so that there are no memory leaks. The sequence of calls is as follows:
    • TEST_METHOD_INITIALIZE(SetupBitmaps)
    • TEST_METHOD(TestTBB1)
    • TEST_METHOD_CLEANUP(FreeBitmaps)
    • TEST_METHOD_INITIALIZE(SetupBitmaps)
    • TEST_METHOD(TestTBB2)
    • TEST_METHOD_CLEANUP(FreeBitmaps)
    • TEST_METHOD_INITIALIZE(SetupBitmaps)
    • TEST_METHOD(TestTBB3)
    • TEST_METHOD_CLEANUP(FreeBitmaps)
    and so on...It is also possible to use TEST_CLASS_INITIALIZE and TEST_CLASS_CLEANUP, so that the sequence of calls becomes:
    • TEST_CLASS_INITIALIZE(SetupBitmaps)
    • TEST_METHOD(TestTBB1)
    • TEST_METHOD(TestTBB2)
    • TEST_METHOD(TestTBB3)
    • ...all other testing methods
    • TEST_CLASS_CLEANUP(FreeBitmaps)
    This sequence does the initialization of reference images just once, so it will run faster, but I recommend the other approach, as it clearly separates every single test method from the other ones, and no corruption of shared data from a previous test method can affect the result of the currently evaluated test method.
  6. the body of each TEST_METHOD is very short: it calls a specific version of the optimized code, and then compares the obtained output image with the reference one inside an Assert::IsTrue check: if the image buffers match, the test will be passed, if not, the test will fail. In this demo, these is only one check, but usually, when dealing with more complex data structures and/or data groups, there are multiple Assert::X calls, each checking for a specific error.
  7. compile the solution, and if compilation was successful, invoke the unit tester by clicking on Test | Run | All tests, after a few seconds the Test explorer window will show which tests have passed and which ones did not. The unit testing project is now complete, and ready to be integrated in your software building process.
// Intel TBB Demo
// by Stefano Tommesani (www.tommesani.com) 2013
// this code is release under the Code Project Open License (CPOL) http://www.codeproject.com/info/cpol10.aspx
// The main points subject to the terms of the License are:
// -   Source Code and Executable Files can be used in commercial applications;
// -   Source Code and Executable Files can be redistributed; and
// -   Source Code can be modified to create derivative works.
// -   No claim of suitability, guarantee, or any warranty whatsoever is provided. The software is provided "as-is".
// -   The Article(s) accompanying the Work may not be distributed or republished without the Author's consent

#include "stdafx.h"
#include "CppUnitTest.h"

#include <TBBDemoRoutines.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace TBBDemoUnitTest
{        
    const int DEFAULT_IMAGE_WIDTH = 1 * 1024;
    const int DEFAULT_IMAGE_HEIGHT = 1 * 1024;
    const int DEFAULT_IMAGE_SIZE = (DEFAULT_IMAGE_WIDTH * DEFAULT_IMAGE_HEIGHT);
    const int RGBA_PIXEL_SIZE = 4;

    TEST_CLASS(TBBUnitTest)
    {
    public:
        unsigned char *RGBAImage;
        unsigned char *SerialGrayImage;
        unsigned char *ParallelTBBGrayImage;

        TEST_METHOD_INITIALIZE(SetupBitmaps)
        {
            DisableBenchmarkMode();
            // create input image
            RGBAImage = new unsigned char[DEFAULT_IMAGE_SIZE * RGBA_PIXEL_SIZE];
            // fill RGBA image with random data
            srand(0x5555);
            for (int j = 0; j < (DEFAULT_IMAGE_SIZE * RGBA_PIXEL_SIZE); j++)
                RGBAImage[j] = rand() % 256;

            SerialGrayImage = new unsigned char[DEFAULT_IMAGE_SIZE];
            ParallelTBBGrayImage = new unsigned char[DEFAULT_IMAGE_SIZE];
            // build a reference gray image that will be compared to those built with multi-threaded code
            ProcessRGBSerial(RGBAImage, SerialGrayImage, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, RGBA_PIXEL_SIZE);
            memset(ParallelTBBGrayImage, 0, DEFAULT_IMAGE_SIZE);  //< clear output image so that results from a previous run are zeroed
        }

        TEST_METHOD_CLEANUP(FreeBitmaps)
        {
            // free images
            delete[] SerialGrayImage;
            delete[] ParallelTBBGrayImage;
            delete[] RGBAImage;
        }
        
        TEST_METHOD(TestTBB1)
        {
            ProcessRGBTBB1(RGBAImage, ParallelTBBGrayImage, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, RGBA_PIXEL_SIZE);
            Assert::IsTrue(memcmp(SerialGrayImage, ParallelTBBGrayImage, DEFAULT_IMAGE_SIZE) == 0);
        }

        TEST_METHOD(TestTBB2)
        {
            ProcessRGBTBB2(RGBAImage, ParallelTBBGrayImage, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, RGBA_PIXEL_SIZE);
            Assert::IsTrue(memcmp(SerialGrayImage, ParallelTBBGrayImage, DEFAULT_IMAGE_SIZE) == 0);
        }

        TEST_METHOD(TestTBB3)
        {
            ProcessRGBTBB3(RGBAImage, ParallelTBBGrayImage, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, RGBA_PIXEL_SIZE);
            Assert::IsTrue(memcmp(SerialGrayImage, ParallelTBBGrayImage, DEFAULT_IMAGE_SIZE) == 0);
        }

        TEST_METHOD(TestSIMD)
        {
            ProcessRGBSIMD(RGBAImage, ParallelTBBGrayImage, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, RGBA_PIXEL_SIZE);
            Assert::IsTrue(memcmp(SerialGrayImage, ParallelTBBGrayImage, DEFAULT_IMAGE_SIZE) == 0);
        }

        TEST_METHOD(TestTBBSIMD)
        {
            ProcessRGBTBBSIMD(RGBAImage, ParallelTBBGrayImage, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, RGBA_PIXEL_SIZE);
            Assert::IsTrue(memcmp(SerialGrayImage, ParallelTBBGrayImage, DEFAULT_IMAGE_SIZE) == 0);
        }        
    };
}
Quote this article on your site

To create link towards this article on your website,
copy and paste the text below in your page.




Preview :

Unit testing with Visual C++ 2012
Wednesday, 24 April 2013

Powered by QuoteThis © 2008
Last Updated on Thursday, 25 April 2013 13:59  
View Stefano Tommesani's profile on LinkedIn

Latest Articles

Unit-testing file I/O 26 November 2017, 12.09 Testing
Unit-testing file I/O
Two good news: file I/O is unit-testable, and it is surprisingly easy to do. Let's see how it works! A software no-one asked for First, we need a piece of software that deals with files and that has to be unit-tested. The
Fixing Git pull errors in SourceTree 10 April 2017, 01.44 Software
Fixing Git pull errors in SourceTree
If you encounter the following error when pulling a repository in SourceTree: VirtualAlloc pointer is null, Win32 error 487 it is due to to the Cygwin system failing to allocate a 5 MB large chunk of memory for its heap at
Castle on the hill of crappy audio quality 19 March 2017, 01.53 Audio
Castle on the hill of crappy audio quality
As the yearly dynamic range day is close (March 31st), let's have a look at one of the biggest audio massacres of the year, Ed Sheeran's "Castle on the hill". First time I heard the song, I thought my headphones just got
Necessary evil: testing private methods 29 January 2017, 21.41 Testing
Necessary evil: testing private methods
Some might say that testing private methods should be avoided because it means not testing the contract, that is the interface implemented by the class, but the internal implementation of the class itself. Still, not all
I am right and you are wrong 28 December 2016, 14.23 Web
I am right and you are wrong
Have you ever convinced anyone that disagreed with you about a deeply held belief? Better yet, have you changed your mind lately on an important topic after discussing with someone else that did not share your point of

Translate