AutoRegister doesn't work from .lib file

Help
2005-04-27
2013-04-22
  • Stephen Jones
    Stephen Jones
    2005-04-27

    Using Visual Studio.NET 2003

    I'm trying to use cppunit for unit tests in a .lib file. The unit tests are contained in .cxx files that register themselves perfectly when I include them directly from a .exe or .dll. But, if I link to the .lib that contains these .cxx files, the tests are never registered.

    I thought the static auto-register varaibles might be getting optimized out, but it fails in both Debug and Release builds and turning off /OPT:REF doesn't seem to help.

    I can make this work by manaully adding each test, but I love the autoregister feature. Simply adding a file adds the unit test (theoretically).

    Has anyone experienced this? Any suggestions?
    Thanks,
    - Stephen

     
    • Tom Plunket
      Tom Plunket
      2005-04-27

      Yeah, I've seen this.  It's irritating, to be sure.

      The easiest workaround that's been presented to me is to put the code under test into a .lib, and then build the tests themselves as the EXE project that links in the code under test.  Alternatively, there's a dialog somewhere in the project settings where you need to manually add every single symbol that you want to keep.  As far as those that I've worked with have been able to determine, that was the ONLY way to get tests in a LIB to link, and as you can guess it's a huge barrier to effectively having unit tests in a library file.

       
    • GenghisBob
      GenghisBob
      2005-11-08

      I encountered the same problem while working on a regression architecture for several static library projects.

      I belive the problem is that when linking to a static lib you only import the code that is referenced where as with a .dll you always get everything. To overcome the problem you simply need to make reference the test classes that you wish to run to get the associated code for those classes linked in.

      e.g. If <code>TestSomeClass</code> is a class in the static lib then you would simply have a member of type <code>TestSomeClass</code> in the application. This ensures that the associated code gets linked in during the complie and the test is registered with the <code>TestFactoryRegistry</code>.

      You can do this one by one but it gets messy pretty quickly. I instead added a "TestSuite" class to each lib that has as members an instance of each test class in the library (this is just a generic class, please don't confuse this with any of the test suites native to CppUnit). Then in my application I only need to reference the "TestSuite" class which in turn references each test in the library.

      This solution has worked well for me, I hope you find it useful.

       
    • The problem is caused by the linker that discard 'non referenced' static variables in libraries. This behavior is usually optional and can be disabled.

      Have a look at you linker option, and get back to us!

      Baptiste.

       
    • Robin Davies
      Robin Davies
      2005-11-17

      Actually, more specifically, the linker discards unreferenced *object* modules. RStatic constructors in unreferenced object modules never get called.

      Here's a cleaner (but still messy solution).

      TestUnit1.cpp:
      ... test cass ...
      void ReferenceTestUnit1() {
      }

      TestUnit2.cpp
      ... test class ...
      void ReferenceTestUnit2() {
      }

      CppUnitReferences.cpp
      #define REFERENCE(unit) \ extern void Reference##unit(); \ Reference##unit(); // i.e. call a function in   the target obj file.

      void CppUnitReferences()
      {
          REFERENCE(TestUnit1);
          REFERENCE(TestUnit2);
      }

      Now, call CppUnitReferences() from your main app. That will link all of the referenced modules.

      This avoids having to include headers for each of the test classes.

      Yet another solution is to put a dummy virtual function in the header declaration of the class under test, and implement it in the test class file. Unfortunately, this problem happens, more often than not when testing template classes, so this isn't an option.

       
    • Robin Davies
      Robin Davies
      2005-11-17

      Oh yeah. Placing the test class at the end of the .CPP file containing the implementation of classes under test would work too (as long as there's at least one method in the CPP file of the class under test).

       
  • Conrad Braam
    Conrad Braam
    2009-12-09

    Thank you for your solution genghisbob. 
    I agree, it's not elegant, but because you do not have to instantiate the test, just declare a pointer to force the linker to see sense. I think calling your test constructor early is a bit evil, a uninitialized pointer fixes that.
    You could apply this logic to rerdavies solution too to eliminate the memory getting a bump.