TDD and BDD for Native C++ applications


Challenges using TDD and BDD while developing and maintaining native C++ applications

Though native C++ is extremely powerful language, number of challenges faced by C++ developers. According to TIOBE, C++ is the 4'th most popular language used by programming community. http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

1) Lack of mechanisms like reflection that allows easy investigation of format of objects at runtime, invoking methods, access fields of the objects. As a result very difficult to find good programmer friendly,  easily configurable ( via an xml file), Aspect oriented ( configurable through annotations) productivity tools for programmers, like IOC containers, Mock libraries, Behaviour driven development tools etc. As a result deep testing of native C++ code is very costly.Component Object Model frameworks like ATL being used in C++ projects adds to the complexity of using TDD in C++. Need to have a good design to isolate business logic from frameworks boiler plate code. Plain C++ objects are easily testable than COM objects.
2) Not as readable and predictable as other modern languages due to more operators, pointers, multiple inheritance, global variables, memory management issues, weak type system, friend keyword, large private codebase not exposed via public interfaces, runtime library differences between dependendent modules, and other language features.
3) Legacy systems are pretty old systems with no test cases. Due to hard dependencies that were introduced through years, difficult to re-factor and make it easily maintainable and extensible.
4) Isolating functionality and improving testability on legacy systems would be very difficult to achieve. Very few choices of tools to isolate classes, mock dependencies and test.
4) Most of the existing legacy systems that were developed using C++ are matured(Completed number of development/sustenance life cycles) before the introduction of extreme programming techniques like Test Driven Development, Behavioural development etc.
6) Legacy systems might have got fragile as layering and other architectural restrictions got violated during its long existence.
7) Legacy systems might have got large amount of dead code.
8) Logic of algorithms got complicated during its evolution. Business rules gets scattered with each patch release. Each quick fix to a bug results in complex algorithms.
9) Sometimes legacy systems might get fragile also due to bad coding practices.
10) Lack of documentation, code comments, and unit tests would require C++ programmers to spend more time to understand the code. Multiple responsibilities to each component. Refactoring would be difficult as developer is not confident about what each function does.
11) Code duplication of legacy systems would add to maintenance problem. Though two different work flows have the same steps,they might be having two different code bases. Resulting in programmer to modify the similar code at two places. Increasing the chances of failure due to the change impact.
12) Testability characteristics of legacy code being low, programmers cannot mock the dependencies(third party API etc). Forcing the developer to depend on integration testing more, thus reduces code coverage of tests.
13) Due to the lack of testability characteristics of legacy code, for quick fixes programmer need to check-in the code without unit testing, rely on official build from the build server. And build process being slow, would increase the cost of a bug fix and its verification.
14) Behavioural Driven Development and Test Driven Development can be done for Native C++ projects by encapsulating Native C++ code in a C++/CLI (Managed C++). And on that wrapper can use SpecFlow and MSTest frameworks.  
Test Driven Development

Test Driven Development = Test First Development + Refactoring




·         In TDD, test code is written before functional code.
·         We need not write all test cases before writing the functional code. Focus one test at a time. Incrementally consider the next expected behaviour of the unit under test.
·         In TDD after each test gets passed, it represents a new working piece of behaviour is added to the system.
·         But capture your thoughts about upcoming tests in a test list.
·         After each test, perform refactoring to remove the code smells and make the code easily maintainable. Ensure business logic is not scattered, each unit follows the single responsibility principle and all other OOAD principles.

Setup of TDD Environment for native C++:-
Assuming that the development is a new development from scratch or legacy code is made testable by refactoring the code. All hard dependencies are eliminated, factory classes and other frameworks are used to inject dependencies.
1.       Download Gmock & CppUnit . Gmock can be integrated with any of the unit testing framework like boost unit testing framework, google test or CppUTest. But since cppunit is one of the older frameworks that’s already been used for unit testing in legacy systems, considering integration of google mock with CPPUNIT.


2.       Open the Visual studio solution present in google mock folder:- gmock\msvc\2010. And build the project.
3.       While building the google mock solution make sure that runtime library configuration is set as used by cppunit ( Configuration Properties-> C/C++ -> Code Generation -> Runtime Library). If cppunit using Multi Threaded DLL , we should set the same for google mock as well.
4.       If Visual Studio version lesser than 2013 is being used, configure the project to use the November 2012 CTP. From the project properties, navigate to Configuration Properties -> General -> Platform Toolset and select the CTP.
5.       Since Visual Studio 2013 has support for variadic templates that are used by Google mock, we will not get any compilation errors while building Google Mock.
6.       Note that after building google mock, gmock.lib and other libraries will be generated @ appropriate output directory ($(SolutionDir)$(Configuration)\)
7.       Open the CppUnit Visual studio solution (Build2010.sln), and build the solution.
8.       Ensure that the Runtime Library setting is same as that of google mock.
9.       Note that after building cppunit, cppunit libraries will be generated in appropriate output directory.
10.   Create a new Win32 Console project in Visual Studio.
11.   Configure the project to use google mock and cppunit. Add the Additional Include Directories

12.   Configure the Additional Library Directories,


13.   You can also create environment variables for the dependent library locations and use them.
14.   In main, include gmock.h and CppUnit header files for TestRunner.h and TestFactoryRegistry.h.
15.   Add code to initialize the Google Mock and code to run the CppUnit Test Runner.

16.   Define a new CppUnit test fixture class. Add new test case methods and register them with CppUnit.

17.   Define the member functions of the test fixture class. Include gmock and gtest header files to use the macros of gmock and gtest.

18.   Declare a mock class derived from interface, which you wanted to mock it up.
Context

   
         Reference:- http://www.cplusplus.com/articles/jL18T05o/

         Consider we have the following interface for database access, and it has multiple implementations for SQLServer , SQLite database, Oracle database access etc. And we wanted to mock that interface as data access layer is always costly and testing the business model with mock data access layer would be the best way to achieve coverage for the Business model objects.



    First create the mock objects for the above interfaces. Mocking can be done manually using macros or automatically using mock generator.




    Automatic Generation of Mock Objects
    Gmock provides gmock_gen.py tool in Google Mock's scripts/generator/ directory, If you give it a C++ file and the name of an abstract class defined in it, it will print the definition of the mock class for you. That mock object would be a concrete class that can be instantiated.

     This tool requires Python to be installed.


    Once we run the tool, concrete mock objects gets created automatically.

    19.   Identify the code which have the instances of the interface injected. We can pass the real object or the mock object.


    20.   Write the test case for the actual code that you wanted to test. Set the expectations of the mock call that would happen in the actual method, pass the mock object to the method.


    21.   Assert the state of actual object/function when the dependent object (Mock object) behaves in a particular way.



    Practicing Memory Leak Analysis

    Memory Leak Analysis

     

    Following are the tools and the sample application that will be used for the following simple memory leak analysis.

    Application:-
                   http://windbg.info/apps/46-crashme.html

    CrashMe is a simple application that implements several common debug situations and scenarios. We can download the source code, build and run it. We can initiate the debugging scenario by clicking on the appropriate button.


    Tools:-
        
    1) gflags & umdh.
    The user-mode dump heap (UMDH) utility works with the operating system to analyze Windows heap allocations for a specific process. UMDH locates which routine in a specific process is leaking memory.

    umdh and gflags are the tools that are part of Windows debugger package. Windows debugger is part of Windows SDK package.
    The most important data in the UMDH logs are the stack traces of the heap allocations. 

    Before using umdh , we must configure system properties to collect stack traces, enable page heap.

     Following are the steps to analyze memory leaks using umdh tool.

    Step 1:- Set the Symbol path for the executable


    Copy the symbol file for the executable :- CrashMe , to a folder C:\Symbols and set the environment variable _NT_SYMBOL_PATH. Set both the windows server path and the folder path where the executables symbols are copied.
    _NT_SYMBOL_PATH   :-

    symsrv*symsrv.dll*C:\localsymbols*http://msdl.microsoft.com/download/symbols;C:\Symbols;


    Step 2:- Enable stack trace database and page heaps

    Run gflags to set the global flags and to set flags for the application to enable page heaps and stack trace.




     We also can set the flags for the application at command line as follows.
                  gflags /i CrashMe.exe +ust
                  gflags /i CrashMe.exe +uht
     

    Step 3:- Start the application



    Step 4:- Collect the initial umdh log




    The process name argument to umdh is the PID as seen in the process explorer.


    Step 5:- Perform the scenario (Suspected scenario that is leaking memory).

    For this application , CrashMe.exe the scenario is obvious. Memory leaks when the Memory leak button is clicked.

    But in realistic scenarios, leaks might be in multiple threads , in different sub modules(dlls) and due to different memory allocators. We might need to collect multiple umdh logs at different instances.

    But our context is very simple here, collect the logs again after performing the scenario(clicking the button).


    Step 7:- Collect the umdh log after the scenario.







    Step 6:- Compare UMDH logs ("Before log" with "After Log").

     UMDH can compare two different log files and display the change in their respective allocation sizes. You can use the greater-than symbol (>) to redirect the results into a third text file. 




    Step 7:- Analyze the umdh diff log file.

    From the log file we could figure out the following
    The increase in bytes between before and after scenario is    :- 269484032
    Increase in allocations between before and after scenario is :- 257   
    And the stack trace which resulted in this memory allocation.
     




    Step 8:- Code Review with the help of stack text obtained in previous step.

    From the above logs, its clear that the function to be reviewed is :- CCrashMeDlg::OnBnClicked_MemoryLeak . And the line number is 455.
     CrashMe!CCrashMeDlg::OnBnClicked_MemoryLeak+3E (c:\windbg\crashme\crashme\crashmedlg.cpp, 455)





    2) DebugDiag

    All the analysis tools have broadly two steps 1) Collect the information/memory dumps 2) Analyze the Dumps.
    DebugDiag has two tools 1)    DebugDiag Collection tool 2) DebugDiag Analysis tool.

    Since the current scenario  is simple, we need not require Debug Diag to collect the memory dumps, we also can use sysinternal tool like procdump (provided it collects all the counters required for our analysis scenario).

    Debug Diagnostic Tool   allows us to specify the rules to collect the dumps. Or it also allows the user to select the process and collect dumps whenever required.

    Debug Diagnostic Tool allows to specify the triggers for crash dump generation. We can specify the memory thresholds or processing thresholds at which it should collect the dumps.

    If we are working with a service that never terminates or applications that run for long periods, we can configure the rules based on the type of analysis we wanted to perform.
    For memory leak analysis, this tool will inject a DLL into the specified process and monitor memory allocations over time.  A dump is then generated, and the dump is analyzed to determine what allocations are not being freed and most likely causing the memory leak.  Allocations generally fall into 3 groups:  caching, short term allocations that will be freed later, and memory leaks.  All three allocation methods have very distinct allocation patterns when measured over time.  The leak tracking feature calculates a leak probability using a formula that is based on these allocation patterns as measured over a specific time period.
    Note:- We need to disable the pageheap flag that was enabled in previous scenario for DebugDiag to successfully collect information.


    Difference between Debug Diagnostic Tool and UMDH tool
    UMDH tool only focuses on Windows heap. But Debug Diag can also hook into other allocators like COM allocators and Virtual Memory allocators.

    Step 1:- Set the Symbol path environment vairable _NT_SYMBOL_PATH

    Step 2:- Launch the application.

    Step 3:- Launch DebugDiag Collection tool and configure the rule to collect dumps

    Selecting the rule type allows the Debug Diag to collect required counters for the type of analysis we wanted to do.
     
    Since our scenario is simple, we know when the leak happens and can explicitly collect dumps after the scenario is performed. But for realistic scenarios we need to specify the configuration :- how and when the tool should collect the memory dumps. A single dump wouldn't help us analyze the memory leaks. Multiple dumps needs to be collected at regular intervals or when a particular threshold is reached or any other triggering condition specified in the rule, so that they can be compared and most probable unclaimed memory and its allocator can be identified.


    After configuring the collection rule, need to select the application for which we are collecting the memory dumps.


    Then configure the location where the dumps needs to be stored.

    Save the rule to collect the dumps. 

    For our scenario, we need not configure completely the dump collection rule(leave the defaults) since we wanted to collect the dumps explicitly before the scenario and after the scenario.





    Step 4:- Take the dump before the scenario is executed

     Click on the processes view, select the process and take the full dump . This would be the information before the suspected scenario is executed.



    Step 5:- Execute the scenario


    Step 6:- Take the memory dump after the scenario is executed.


    Step 7:- Launch the DebugDiag Analysis tool 

    Using the DebugDiag Analysis tool, we can choose the analyzer , set the symbol paths , select the dumps that were collected previously and trigger analysis.
    Currently we use the tool to analyze the memory dumps for possible memory leaks. After selecting the analyzer, click on the settings icon and set the symbol path for the application binaries and windows binaries.


    Step 7:- Start the Analysis

    Click on Add Data Files, select the dumps to be analyzed and select the button Start Analysis


     Once the analysis is done, it generates a detailed report related to memory allocations.

    Step 8:-Analyze the report

     

    Step 9:-Review the pointed code and fix the leak

    3) !heap extension in WinDbg

    If tools doesn't give much information about the leak, then using the WinDbg debugger is the last resort. We should manually walk through the heap, figure out the stack traces that allocated the suspected heap. Then review and correct the function pointed out in the stack text.

    Debugger can be used for both 1) Live Debugging 2) Post Mortem debugging.

    1) Live Debugging


        Run the application with debugger.
        Attaching a process with debugger can be done in two ways
            a) Invasive attach
                   Break-in thread is created. There can be only one invasive debugger attached to a process at any time.
            b) Noninvasive attach
                   We don't attach to the process as a debugger. We can examine memory, but cannot step through the application. We can attach several noninvasive debuggers to a process.

    2) Post mortem debugging.
        Take the dump of the application. Open the dump in the debugger(WinDbg) , load the symbols and analyse the dump independent to the application.


    In this example will be doing the post mortem debugging.


    Step 1 :- Set the global flags to enable pageheap and stack trace database



    Step 2 :- Launch the application

    Step 2 :- Execute the scenario

    Step 3 :-Take a full dump of the process using Process Explorer

    Step 4:- Launch WinDbg and set the symbols location for application and windows binaries

    Step 5:- Open the memory dump in WinDbg

    Step 6:- Ensure symbols are loaded for the module

    Step 4:- List down handle specific allocation statistics for every Alloc size.

    Command :- !heap -stat -h 0
    -stat :- Displays usage statistics for the specified heap.
    -h <handle> :- Displays usage statistics for only the heap at Handle. If Handle is 0 or omitted, then usage statistics for all heaps are displayed.

    Step 5:- Choose the AllocSize with maximum TotalMem and display summary information for that heap.

    Step 6:- Get the call-stack with source information for the first user address

    Since page heap is enabled -p  option displays various forms of page heap information.

    Step 7:- Review the code in the function @ line number reported in stack text