As the title says, I want to get all test suites or test cases (name) from a test application, ether in the console or as xml output.
Test framework is the boost test library.
Is there an option to achieve this? I did not found anything useful in the documentation.
There is simply the --list_content command line option, exactly for that purpose.
This can be done without much intrusion using a global fixture. Assuming you have a translation unit (cpp file) that contains main explicitly or auto generated, you can intercept test execution when a certain command line argument is provided. Then you can traverse the test tree using a customized visitor, which lists all available tests. Here is a small working example, which creates a test runner by compiling and linking files main_test.cpp, a.cpp and b.cpp:
main_test.cpp
#include <string>
#include <iostream>
// --- Boost Includes ---
#define BOOST_TEST_MODULE MyTestSuite
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
using namespace boost::unit_test;
struct Visitor : test_tree_visitor
{
size_t level = 0;
void visit( test_case const& test )
{
std::string indentation( level, '.' );
std::cout << indentation << test.p_name << std::endl;
}
bool test_suite_start( test_suite const& suite )
{
std::string indentation( level, '.' );
level++;
std::cout << indentation << "Suite: " << suite.p_name << std::endl;
return true;
}
void test_suite_finish( test_suite const& suite )
{
level--;
}
};
struct GlobalFixture
{
GlobalFixture( )
{
int argc = framework::master_test_suite( ).argc;
for ( int i = 0; i < argc; i++ )
{
std::string argument( framework::master_test_suite( ).argv[i] );
if ( argument == "list" )
{
Visitor visitor;
traverse_test_tree( framework::master_test_suite( ), visitor );
exit( EXIT_SUCCESS );
}
}
}
};
BOOST_GLOBAL_FIXTURE( GlobalFixture )
a.cpp
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE ( TestA )
BOOST_AUTO_TEST_CASE ( TestFoo )
{
BOOST_CHECK(true);
}
BOOST_AUTO_TEST_CASE ( TestBar )
{
BOOST_CHECK(true);
}
BOOST_AUTO_TEST_SUITE_END() // TestA
b.cpp
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE ( TestB )
BOOST_AUTO_TEST_CASE ( TestFoo )
{
BOOST_CHECK(true);
}
BOOST_AUTO_TEST_CASE ( TestBar )
{
BOOST_CHECK(true);
}
BOOST_AUTO_TEST_SUITE_END() // TestA
Invoking the runner without any arguments results in
./somelib_testrunner1
Running 4 test cases...
*** No errors detected
Passing the argument list used in the fixture defined above results in
Suite: MyTestSuite
.Suite: TestA
..TestFoo
..TestBar
.Suite: TestB
..TestFoo
..TestBar
I am afraid there is no easy way to do this without actually running the tests.
And even then the only thing coming to my mind without touching the code would be --log_level=test_suite and some sort of parsing script that finds the "Entering test suite", "Leaving test suite" and "Entering test case" messages (or parsing the xml given when you also put --log_format=XML, if you have a xml parser at hand).
But even that spoils the log with your added BOOST_TEST_MESSAGEs and of course encountered errors and warnings.
Seeing how the boost test framework is not much more than a bunch of macros, defined in the header, you could of course add the functionality yourself, probably easiest by adding another log format just listing the suites and tests, assuming you are fine with running the tests while finding the list.
Related
I have a unit test file using Boost Test, like so:
#include <boost/test/unit_test.hpp>
#include <cppx/auto/Cloner_.hpp>
#include <utility>
namespace {
struct S
{
static auto count() -> int& { static int c; return c; }
S(){ ++count(); }
S( const S& ) { ++count(); }
S( S&& ) { ++count(); }
~S() { --count(); }
};
}
BOOST_AUTO_TEST_SUITE( Cloner_ )
BOOST_AUTO_TEST_CASE( default_construction )
{
cppx::Cloner_<int> cloner1;
BOOST_TEST( cloner1.p() == nullptr );
}
BOOST_AUTO_TEST_CASE( auto_cleanup )
{
BOOST_REQUIRE( S::count() == 0 );
{
cppx::Cloner_<S> cloner1( new S );
BOOST_TEST( S::count() == 1 );
cppx::Cloner_<S> cloner2 = cloner1.clone();
BOOST_TEST( S::count() == 2 );
}
BOOST_TEST( S::count() == 0 );
}
BOOST_AUTO_TEST_SUITE_END()
My purpose with the test suite name Cloner_ is to collect all the test cases for the Cloner_ class, under a node in the Boost test cases hierarchy.
When I choose to run all tests in Visual Studio it detects this hierarchy:
[22.09.2018 06:18:37 Informational] ------ Run test started ------
[22.09.2018 06:18:39 Informational] Found test: Cloner_/auto_cleanup
[22.09.2018 06:18:39 Informational] Found test: Cloner_/default_construction
[22.09.2018 06:18:39 Informational] Executing: -> [Cloner_/auto_cleanup]
[22.09.2018 06:18:40 Informational] Executing: -> [Cloner_/default_construction]
[22.09.2018 06:18:40 Informational] ========== Run test finished: 2 run (0:00:03,1048741) ==========
However, it doesn't display the hierarchy, except that it uses the main Boost test module name cppx_tests (defined in a separate file) as root:
(Visual Studio's Test Explorer's presentation of the test cases is up to the right in the above screenshot.)
I'd like to avoid old-fashioned C style name prefixes since Boost Test does provide a means of defining a hierarchy, and since Test Explorer reports paths in that hierarchy when it searches for tests to execute, so that it apparently knows about it.
So, how can I make VS Test Explorer display the test case hierarchy for tests using Boost Test, so that I can readily identify e.g. default_construction testing of class X versus class Y or class Z, or is that not possible?
I have a component I am testing:
namespace myproj {
class MyComp {
public:
void doSome();
};
}
using namespace myproj;
void MyComp::doSome() {
// Some code...
// And in the end, for debugging purposes, I print in one of the streams
EV_DEBUG << "Some debugging info" << endl;
}
My unit test
My test is an ordinary OmNET++ Unit Test using the opp_test tool:
%description:
Tests my component
%includes:
#include "myproj/MyComp.h"
%global:
using namespace ::inet::test::myproj;
%activity:
MyCompTest test = MyCompTest();
test.testDoSome();
EV << ".\n";
%contains: stdout
my test out
Of course a class MyCompTest is using MyComp in order to run some logic which will eventually invoke MyComp::doSome.
Note that testDoSome will cause the string "my test out" to be printed.
The problem
The issue is that, because of that EV_DEBUG (same happens if I change it into EV_INFO, EV_DETAIL and all other EVs) in my original class (source/simulation code), the test output is poisoned and the final output is:
Some debugging info
my test out
Which makes the test fail. If I comment out EV_DEBUG << ... then the test is fine.
How to solve this problem? Do I really have to remove all EV printouts in my original code?
You can turn off printing by EV_DEBUG by adding these lines in %activity section:
#undef EV_DEBUG
#define EV_DEBUG true ? EV : EV
I have a single cpp file of about 100 lines with the following contents.
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/FrontendActions.h>
#include <iostream>
// The filename that will be processed (twice).
static const char* FILENAME = "simple.cpp";
// System header locations, you may need to
// adjust these.
static const char* SYSTEM_HEADERS[] =
{
"/usr/include/c++/5.4.0",
"/usr/include/x86_64-linux-gnu/c++/5.4.0",
"/usr/include/c++/5.4.0/backward",
"/usr/local/lib/clang/4.0.0/include",
"/usr/include/x86_64-linux-gnu",
"/usr/include"
};
// Location for builtin headers. You may need to
// adjust this.
static const char* RESOURCE_DIR = "/usr/local/lib/clang/4.0.0";
// Uncomment this to see header search paths.
// #define PRINT_HEADER_SEARCH_PATHS
// Constructs a CompilerInvocation
// that must be fed to a CompilerInstance.
clang::CompilerInvocation* makeInvocation();
// Executes a single SyntaxOnlyAction on
// the given CompilerInstance.
void secondCallThisFunctionFails(clang::CompilerInstance& instance);
int main()
{
using namespace clang;
CompilerInstance instance;
instance.createDiagnostics();
instance.setInvocation(makeInvocation());
instance.getFrontendOpts().Inputs.emplace_back
(
FILENAME,
FrontendOptions::getInputKindForExtension(FILENAME)
);
// First call is OK.
secondCallThisFunctionFails(instance);
// Second call results in assertion failures.
secondCallThisFunctionFails(instance);
return 0;
}
clang::CompilerInvocation* makeInvocation()
{
using namespace clang;
auto invocation = new CompilerInvocation();
invocation->TargetOpts->Triple = llvm::sys::getDefaultTargetTriple();
invocation->setLangDefaults(
*invocation->getLangOpts(),
IK_CXX,
llvm::Triple(invocation->TargetOpts->Triple),
invocation->getPreprocessorOpts(),
LangStandard::lang_cxx11);
auto& headerSearchOpts = invocation->getHeaderSearchOpts();
#ifdef PRINT_HEADER_SEARCH_PATHS
headerSearchOpts.Verbose = true;
#else
headerSearchOpts.Verbose = false;
#endif
headerSearchOpts.UseBuiltinIncludes = true;
headerSearchOpts.UseStandardSystemIncludes = true;
headerSearchOpts.UseStandardCXXIncludes = true;
headerSearchOpts.ResourceDir = RESOURCE_DIR;
for (const auto sytemHeader : SYSTEM_HEADERS)
{
headerSearchOpts.AddPath(sytemHeader, frontend::System, false, false);
}
return invocation;
}
void secondCallThisFunctionFails(clang::CompilerInstance& instance)
{
using namespace clang;
SyntaxOnlyAction action;
if (instance.ExecuteAction(action))
{
std::cout << "Action succeeded.\n";
}
else
{
std::cout << "Action failed.\n";
}
}
As you can see, the main function is quite simple, and calls a function twice at the end. The second time this function is called I get an assertion failure, which surprises me.
The contents of the file simple.cpp is
// test wether we actually configured C++11 or greater
#include <thread>
int main() { return 0; }
The output of this program on my machine is:
Action succeeded.
clangapitest: ../tools/clang/lib/Basic/SourceManager.cpp:819: clang::FileID clang::SourceManager::getFileIDLoaded(unsigned int) const: Assertion `0 && "Invalid SLocOffset or bad function choice"' failed.
Aborted (core dumped)
The problem is: I want to execute more than one action on a CompilerInstance. What state do I have to reset in order to not get assertion failures?
To build it yourself you have to link with some static clang and llvm libraries. Here's the CMakeLists.txt file if interested:
add_clang_executable(clangapitest clangapitest.cpp)
target_link_libraries(clangapitest clangFrontend)
I made a new directory path/to/llvm/tools/clang/tools/clangapitest and adjusted the CMakeLists.txt file in path/to/llvm/tools/clang/tools/CMakeLists.txt to have an extra line add_subdirectory(clangapitest).
Well, I figured it out. In the doxygen documentation of CompilerInstance::ExecuteAction, it states that an invocation object and diagnostics object should have been initialized, and no other state (hence no source nor filemanager). So the following works:
SyntaxOnlyAction action;
instance.setSourceManager(nullptr);
instance.createDiagnostics();
if (instance.ExecuteAction(action))
{
std::cout << "Action succeeded.\n";
}
else
{
std::cout << "Action failed.\n";
}
I'm wondering how I you can create and register a function from the C++-side that returns a table when called from the Lua-side.
I've tried a lot of things but nothing did really work. :/
(sorry for the long code)
This for example won't work, because Register() expects a "luaCFunction"-styled function:
LuaPlus::LuaObject Test( LuaPlus::LuaState* state ) {
int top = state->GetTop();
std::string var( state->ToString(1) );
LuaPlus::LuaObject tableObj(state);
tableObj.AssignNewTable(state);
if (var == "aaa")
tableObj.SetString("x", "ABC");
else if (var == "bbb")
tableObj.SetString("x", "DEF");
tableObj.SetString("y", "XYZ");
return tableObj;
}
int main()
{
LuaPlus::LuaState* L = LuaPlus::LuaState::Create(true);
//without true I can't access the standard libraries like "math.","string."...
//with true, GetLastError returns 2 though (ERROR_FILE_NOT_FOUND)
//no side effects noticed though
LuaPlus::LuaObject globals = L->GetGlobals();
globals.Register("Test",Test);
char pPath[MAX_PATH];
GetCurrentDirectory(MAX_PATH,pPath);
strcat_s(pPath,MAX_PATH,"\\test.lua");
if(L->DoFile(pPath)) {
if( L->GetTop() == 1 ) // An error occured
std::cout << "An error occured: " << L->CheckString(1) << std::endl;
}
}
When I try to set it up as a luaCFunction-function it just crashes (0x3) and says:
Assertion failed: 0, file C:\......\luafunction.h, line 41
int Test( LuaPlus::LuaState* state ) {
int top = state->GetTop();
std::string var( state->ToString(1) );
LuaPlus::LuaObject tableObj(state);
tableObj.AssignNewTable(state);
if (var == "aaa")
tableObj.SetString("x", "ABC");
else if (var == "bbb")
tableObj.SetString("x", "DEF");
tableObj.SetString("y", "XYZ");
tableObj.Push();
return state->GetTop() - top;
}
For clarification: from the Lua side I wanted it to be callable like:
myVar = Test("aaa")
Print(myVar) -- output: ABC
EDIT: The Print function comes from here. And was basically the cause for this to not work. Print can only print strings not tables... The C++ code from above works fine if you just return 1.
This is the documentation that came with my LuaPlus version btw: http://luaplus.funpic.de/
I really hope you can help me.. I'm already starting to think that it is not possible. :'(
edit:
I totally forgot to say that using PushStack() lead into an error because "the member does not exist"...
After some painstaking probing from the long comment discussion, I'm posting this answer to help summary the situation and hopefully to offer some useful advice.
The main issue the OP was running into was that the wrong print function was being called in the lua test script. Contrary to the original code shown the real code the OP was testing against was calling Print(myVar) which is a custom provided lua_CFunction and not the builtin print function.
Somehow along the way, this ended up creating some instantiation of template <typename RT> class LuaFunction and calling the overloaded operator()(). From inspecting the luafunction.h from luaPlus any lua errors that occurs inside this call will get swallowed up without any kind of logging (not a good design decision on luaPlus's part):
if (lua_pcall(L, 0, 1, 0)) {
const char* errorString = lua_tostring(L, -1); (void)errorString;
luaplus_assert(0);
}
To help catch future errors like this, I suggest adding a new luaplus_assertlog macro. Specifically, this macro will include the errorString so that the context isn't completely lost and hopefully help with debugging. This change hopefully won't break existing uses of luaplua_assert from other parts of the API. In the long run though, it's probably better to modify luaplus_assert so it actually includes something meaningful.
Anyway here's a diff of the changes made:
LuaPlusInternal.h
## -81,5 +81,6 ##
} // namespace LuaPlus
#if !LUAPLUS_EXCEPTIONS
+#include <stdio.h>
#include <assert.h>
#define luaplus_assert(e) if (!(e)) assert(0)
## -84,5 +85,6 ##
#include <assert.h>
#define luaplus_assert(e) if (!(e)) assert(0)
+#define luaplus_assertlog(e, msg) if (!(e)) { fprintf(stderr, msg); assert(0); }
//(void)0
#define luaplus_throw(e) assert(0)
//(void)0
LuaFunction.h
## -21,7 +21,7 ##
class LuaFunction
{
public:
- LuaFunction(LuaObject& _functionObj)
+ LuaFunction(const LuaObject& _functionObj)
: functionObj(_functionObj) {
}
## -36,7 +36,7 ##
if (lua_pcall(L, 0, 1, 0)) {
const char* errorString = lua_tostring(L, -1); (void)errorString;
- luaplus_assert(0);
+ luaplus_assertlog(0, errorString);
}
return LPCD::Type<RT>::Get(L, -1);
}
In the change above, I opted not to use std::cerr simply because C++ streams tend to be heavier than plain-old C-style io functions. This is especially true if you're using mingw as your toolchain -- the ld linker is unable to eliminate unused C++ stream symbols even if your program never uses it.
With that in place, here's an example where an unprotected call is made to a lua function so you can see the errorString printed out prior to the crash:
// snip...
int main(int argc, const char *argv[])
{
LuaStateAuto L ( LuaState::Create(true) );
LuaObject globals = L->GetGlobals();
globals.Register("Test", Test);
globals.Register("Print", Print);
if(argc > 1)
{
/*
if (L->DoFile(argv[argc - 1]))
std::cout << L->CheckString(1) << '\n';
/*/
L->LoadFile( argv[argc - 1] );
LuaFunction<int> f ( LuaObject (L, -1) );
f();
//*/
}
}
Running the above will trigger the crash but will include a semi-helpful error message:
g++ -Wall -pedantic -O0 -g -I ./Src -I ./Src/LuaPlus/lua51-luaplus/src plustest.cpp -o plustest.exe lua51-luaplus.dll
plustest.exe plustest.lua
plustest.lua:2: bad argument #1 to 'Print' (string expected, got table)Assertion failed!
Program: G:\OSS\luaplus51-all\plustest.exe
File: ./Src/LuaPlus/LuaFunction.h, Line 39
Expression: 0
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
first you may try to register the function using RegisterDirect(), this may avoid lua_CFunction's problem, check the luaplus manual.like this
LuaPlus::LuaObject globals = L->GetGlobals();
globals.RegisterDirect("Test",Test);
second if I remeber to create a table have two solutions,like this
//first
LuaObject globalsObj = state->GetGlobals();
LuaObject myArrayOfStuffTableObj = globalsObj.CreateTable("MyArrayOfStuff");
//second
LuaObject aStandaloneTableObj;
aStandaloneTableObj.AssignNewTable(state);
check whether you have use the right function.
third I remember the lua stack object is not the luaobject, they have a conversion, may be you can try this
LuaStackObject stack1Obj(state, 1);
LuaObject nonStack1Obj = stack1Obj;
forth, like the function Test() you have give above, the table tableObj you have pushing onto the lua stack, you must remember to clear the object.
One of my favorite unit testing frameworks is PHPUnit because it supports test dependencies (i.e. the ability to mark tests as dependent upon other tests, running the dependent tests conditionally on the success of their dependencies). I've been using the Boost testing framework more recently to test my C++ code, and while it suits most of my unit testing needs, it doesn't appear to support test dependencies.
I've scoured the documentation for the Boost testing framework and have found various hints that Boost supports this feature, but I've yet to find a documentation page or any concrete examples of test dependency support in Boost. Are the previously given pages red herrings, or does the Boost testing framework actually support test dependencies?
Well, you've found yourself that the feature is there and according to the links, it's there for end user to use.
It's not documented by accident and "should be added soon" (as per links).
Now, here's a nother post I've found which uses feature:
http://boost.2283326.n4.nabble.com/Unit-Test-Framework-strange-behaviour-of-test-unit-depends-on-td2653654.html
Sample from there (unfortunately, looks like no BOOST_AUTO_TEST_CASE works there).
Also note that code is incorrect because Dependency() is never called and therefore dependent test doesn't run as well.
#include <boost/test/included/unit_test.hpp>
using boost::unit_test::test_suite;
void Dependency()
{
BOOST_MESSAGE( "Dependency!" );
BOOST_CHECK( 1 );
}
void TC_TestCase()
{
BOOST_MESSAGE( "A test case!" );
BOOST_CHECK( 1 );
}
test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
test_suite* ts = BOOST_TEST_SUITE( "Test_Test" );
ts->add( BOOST_TEST_CASE( &TC_TestCase ) );
/*1*/ ts->depends_on( BOOST_TEST_CASE( &Dependency ) );
return ts;
}
Update
Performed some experimenting and here's an example with automatic test/suits and dependencies.
Some notes about code:
Boost here is 1.42, there may be slight differences for newer versions.
If you put test_suite2 after test_suite1 in the cpp file, keeping dependencies the same, test_suite1 will be always skipped because test_suite2 is not run before it.
I made test_case4 to fail so that test_suite1 is skipped, but if test_case4 succeeds, test_suite1 does execute.
I'm pretty sure you'll be able to make dependencies registration much prettier and shorter.
The code:
#include <boost/test/included/unit_test.hpp>
using namespace boost::unit_test;
BOOST_AUTO_TEST_SUITE(test_suite2)
BOOST_AUTO_TEST_CASE(test_case4)
{
BOOST_CHECK(false);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(test_suite1)
BOOST_AUTO_TEST_CASE(test_case1)
{
BOOST_CHECK(true);
}
BOOST_AUTO_TEST_CASE(test_case2)
{
BOOST_CHECK(true);
}
BOOST_AUTO_TEST_SUITE_END()
//____________________________________________________________________________//
test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
const auto testSuite1Id = framework::master_test_suite().get("test_suite1");
if( testSuite1Id != INV_TEST_UNIT_ID ) {
auto test_suite1 = &framework::get<test_suite>( testSuite1Id );
const auto testSuite2Id = framework::master_test_suite().get("test_suite2");
if (testSuite2Id != INV_TEST_UNIT_ID) {
auto testSuite2 = &framework::get<test_suite>( testSuite2Id );
const auto testCase4Id = testSuite2->get("test_case4");
if (testCase4Id != INV_TEST_UNIT_ID) {
// test_suite1 depends on test_suite2/test_case4
test_suite1->depends_on( &framework::get<test_case>( testCase4Id ));
}
}
}
return 0;
}
Output:
Running 3 test cases...
Entering test suite "Master Test Suite"
Entering test suite "test_suite2"
Entering test case "test_case4"
<blahblahblah>/consoleapplication5/consoleapplication5.cpp(10): error in "test_case4": check false failed
Leaving test case "test_case4"; testing time: 14ms
Leaving test suite "test_suite2"
Test suite "test_suite1"is skipped
Leaving test suite "Master Test Suite"
*** 1 failure detected in test suite "Master Test Suite"
Starting on Boost 1.59 you have better tools for this: depends_on: http://www.boost.org/doc/libs/1_59_0/libs/test/doc/html/boost_test/utf_reference/test_org_reference/decorator_depends_on.html
Usage is pretty simple (from Boost docu):
namespace utf = boost::unit_test;
BOOST_AUTO_TEST_CASE(test3, * utf::depends_on("s1/test1"))
{
BOOST_TEST(false);
}
BOOST_AUTO_TEST_CASE(test4, * utf::depends_on("test3"))
{
BOOST_TEST(false);
}
BOOST_AUTO_TEST_SUITE(s1)
BOOST_AUTO_TEST_CASE(test1)
{
BOOST_TEST(true);
}
BOOST_AUTO_TEST_SUITE_END()