Google Tests ValuesIn with a global vector - c++

I'm trying to run a bunch of google tests based off configuration files in a directory. This way I can just add a new file and run the tests without having to add it to my parametrized test and recompile. Here is my code:
typedef std::pair<QString, int> TestParam;
QString generatedLogic;
std::vector<TestParam> badExpressionTests;
class LogicBuilderTest : public ::testing::TestWithParam<TestParam> {};
GTEST_API_ int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
::generatedLogic = "/home/mitydsp/trunk/System/logicExecutionEngine/engine/include/GeneratedLogic.h";
QString dir = "/home/mitydsp/trunk/System/logicExecutionEngine/unittest/expressions/bad/";
QDirIterator it(dir, QStringList() << "*.txt", QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString path = it.next();
QStringList nameParts = it.fileName().split("_");
int exitCode = nameParts[0].toInt();
::badExpressionTests.push_back(TestParam(path, exitCode));
}
std::cout << "Size of vector: " << badExpressionTests.size() << std::endl;
return RUN_ALL_TESTS();
}
/**
* Run parameterized test
*/
TEST_P(LogicBuilderTest, TestExitWithCode)
{
::testing::FLAGS_gtest_death_test_style = "threadsafe";
// Simulate fake main()
char arg0[] = "logicTest";
char* argv[] = { &arg0[0], NULL };
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
EXPECT_EXIT({
// Need to run Qt Application because logic builder uses async QTimer::singleShot()
QCoreApplication app(argc, argv);
// Create a logic builder instance
LogicBuilder logicBuilder(
(int) ParameterStoreInterface::DEFAULT_PARAM_STORE_VALUES_KEY,
(int) ParameterStoreInterface::DEFAULT_PARAM_STORE_NAMES_KEY,
(int) ParameterStoreInterface::DEFAULT_PARAM_STORE_LOCK_KEY,
QString(GetParam().first),
QStringList(""),
QStringList(generatedLogic),
QString(LogicBuilder::DEFAULT_INTERMEDIATE_DIR),
QString(LogicBuilder::DEFAULT_OUTPUT_SRC),
QString(LogicBuilder::DEFAULT_OUTPUT_LIB),
true );
app.exec();
}, ::testing::ExitedWithCode(GetParam().second), "");
}
INSTANTIATE_TEST_CASE_P(TestBadExpressions, LogicBuilderTest,
::testing::ValuesIn(::badExpressionTests));
When I run this, it shows that 0 tests are being ran even though the vector says its size is two. How come these parametrized tests are not being ran?
Size of vector: 2
[==========] Running 0 tests from 0 test cases.
[==========] 0 tests from 0 test cases ran. (0 ms total)
[ PASSED ] 0 tests.
Originally I was running the tests by manually defining the configuration files but I don't like this:
INSTANTIATE_TEST_CASE_P(TestBadExpressions, LogicBuilderTest,
::testing::Values(
TestParam("unittest/expressions/bad/17_no_lhs.txt", LogicBuilder::LB_VARIABLE_NO_LHS),
TestParam("unittest/expressions/bad/25_incomplete_rhs.txt", LogicBuilder::LB_PARSE_ERROR)
));

I've spent the last hour trying to figure this out and then as soon as I post I come up with the solution. I'll leave this here because it may help someone in the future:
Instead of trying to create the list of files in main, pass the ValuesIn a function which loads the files and returns a vector.
std::vector<TestParam> GetFilesInDir()
{
std::vector<TestParam> values;
QString dir = "/home/mitydsp/trunk/System/logicExecutionEngine/unittest/expressions/bad/";
QDirIterator it(dir, QStringList() << "*.txt", QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString path = it.next();
QStringList nameParts = it.fileName().split("_");
int exitCode = nameParts[0].toInt();
values.push_back(TestParam(path, exitCode));
}
return values;
}
INSTANTIATE_TEST_CASE_P(TestBadExpressions, LogicBuilderTest,
::testing::ValuesIn(GetFilesInDir()));
Thanks to Maksim Solovjov for reminding me that INSTANTIATE_TEST_CASE_P macro was being executed before main.

we spent some hours on this issue and thought this snippet will be helpful.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#include "gtest/gtest.h"
GTEST_API_ main( int argc, char* argv[] ) {
testing::InitGoogleTest( &argc, argv );
return RUN_ALL_TESTS();
}
struct test_parms_s {
string name; // Simple name of this test
string description; // Description of this test
void* function_parms; // Pointer to test function specific data
};
std::vector<test_parms_s*> BuildTestData() {
test_parms_s* pTestDataA = new test_parms_s();
test_parms_s* pTestDataB = new test_parms_s();
test_parms_s* pTestDataC = new test_parms_s();
pTestDataA->name.assign("testA");
pTestDataA->description.assign("testA_desc");
pTestDataA->function_parms = NULL;
pTestDataB->name.assign("testB");
pTestDataB->description.assign("testB_desc");
pTestDataB->function_parms = NULL;
pTestDataC->name.assign("testC");
pTestDataC->description.assign("testC_desc");
pTestDataC->function_parms = NULL;
std::vector<test_parms_s*> values;
values.push_back(pTestDataA);
values.push_back(pTestDataB);
values.push_back(pTestDataC);
cout << "BuildTestData" << endl;
return values;
}
//------------------------------------------------------------------------
class Testy {
private:
string testname_;
public:
Testy( string testname );
~Testy();
string GetTestName();
void SetTestName( string testname );
};
Testy::Testy( string testname ) {
testname_.assign(testname);
}
Testy::~Testy() {}
string Testy::GetTestName() { return testname_; }
void Testy::SetTestName( string testname ) { testname_ = testname; }
//------------------------------------------------
class TestFixture : public ::testing::TestWithParam<test_parms_s*> {
protected:
Testy* testy;
virtual void SetUp() {
testy = new Testy( "Test");
}
virtual void TearDown() {
delete( testy );
}
};
//------------------------------------------------
TEST_P( TestFixture, ParamTest ) {
test_parms_s* pTestParms = GetParam();
cout << pTestParms->name << endl;
}
//------------------------------------------------
INSTANTIATE_TEST_CASE_P( TestFixture_instance, TestFixture,
::testing::ValuesIn(BuildTestData()));
//========================================================================
the output will be
BuildTestData
[==========] Running 3 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 3 tests from TestFixture_instance/TestFixture
[ RUN ] TestFixture_instance/TestFixture.ParamTest/0
testA
[ OK ] TestFixture_instance/TestFixture.ParamTest/0 (0 ms)
[ RUN ] TestFixture_instance/TestFixture.ParamTest/1
testB
[ OK ] TestFixture_instance/TestFixture.ParamTest/1 (0 ms)
[ RUN ] TestFixture_instance/TestFixture.ParamTest/2
testC
[ OK ] TestFixture_instance/TestFixture.ParamTest/2 (0 ms)
[----------] 3 tests from TestFixture_instance/TestFixture (0 ms total)
[----------] Global test environment tear-down
[==========] 3 tests from 1 test case ran. (0 ms total)
[ PASSED ] 3 tests.

I'm not an expert in manually instantiating Google Tests, but the way you did it can't possibly work: you call INSTANTIATE_TEST_CASE_P in the static context, so it is being evaluated before main is ever called.
You could try moving instantiation just before RUN_ALL_TESTS; however, I don't know what that macro does and this might be illegal. In that case, I think that you can't create your tests with INSTANTIATE_TEST_CASE_P in the dynamic way that you want.

Related

GoogleMock - Leaked mock objects due to cross-referencing mock classes

I have two classes, Server and Client, each with their own mock class and has a getClients and getServer method respectively (the actual classes are much bigger, but this is the minimal case).
I want to add a variable in the MockServer that's returned by the getClients method, allowing me to modify this variable in my tests whenever appropriate (note the clients variable).
While this works, it will cause the two mock objects to keep each other alive, each keeping a shared_ptr reference to the other, causing the test to leak objects.
Please, look at the example provided below.
Question: How can I modify the Mock Classes, without modifying the interfaces or the create functions, to prevent leaked mock objects while preserving the clients variable?
#include <gmock/gmock.h>
#include <gtest/gtest.h>
struct Client;
struct Server
{
virtual const std::vector<std::shared_ptr<Client>>& getClients() = 0;
};
struct Client
{
virtual std::shared_ptr<Server> getServer() = 0;
};
struct MockServer : virtual Server
{
MOCK_METHOD0(getClients, const std::vector<std::shared_ptr<Client>>&());
std::vector<std::shared_ptr<Client>> clients; // I want to add this variable...
protected:
MockServer()
{
ON_CALL(*this, getClients()).WillByDefault(testing::ReturnRef(this->clients)); // ...and return it whenever getClients is called.
}
~MockServer()
{
clients.clear(); // This doesn't help...
std::cout << __func__ << " - This is never printed." << std::endl;
}
public:
static std::shared_ptr<MockServer> create()
{
return std::make_shared<testing::NiceMock<MockServer>>();
}
};
struct MockClient : virtual Client
{
MOCK_METHOD0(getServer, std::shared_ptr<Server>());
protected:
MockClient(std::shared_ptr<Server> server)
{
ON_CALL(*this, getServer()).WillByDefault(testing::Return(server));
}
~MockClient()
{
std::cout << __func__ << " - This is never printed." << std::endl;
}
public:
static std::shared_ptr<MockClient> create(std::shared_ptr<Server> server)
{
return std::make_shared<testing::NiceMock<MockClient>>(server);
}
};
TEST(ExampleOfLeak, testLeakedMockObjects)
{
auto server = MockServer::create();
auto client1 = MockClient::create(server);
auto client2 = MockClient::create(server);
EXPECT_EQ(server, client1->getServer());
EXPECT_EQ(server, client2->getServer());
EXPECT_TRUE(server->getClients().empty());
server->clients.push_back(client1); // This causes leaked mock objects!
server->clients.push_back(client2); // And this too...
EXPECT_EQ(client1, server->getClients().front());
EXPECT_EQ(client2, server->getClients().back());
// server->clients.clear(); // This prevents mock objects from leaking, but doing this manually everywhere is highly unfeasible.
}
Result:
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from ExampleOfLeak
[ RUN ] ExampleOfLeak.testLeakedMockObjects
[ OK ] ExampleOfLeak.testLeakedMockObjects (0 ms)
[----------] 1 test from ExampleOfLeak (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[ PASSED ] 1 test.
foo.cc:43: ERROR: this mock object (used in test ExampleOfLeak.testLeakedMockObjects) should be deleted but never is. Its address is #0x2fc3170.
foo.cc:43: ERROR: this mock object (used in test ExampleOfLeak.testLeakedMockObjects) should be deleted but never is. Its address is #0x2fcbcb0.
foo.cc:22: ERROR: this mock object (used in test ExampleOfLeak.testLeakedMockObjects) should be deleted but never is. Its address is #0x2fd6570.
ERROR: 3 leaked mock objects found at program exit.
It's possible to convert a weak_ptr into a shared_ptr using the lock() method.
This way you can store a weak_ptr in you mock class and later convert it.
Though do be careful and make sure the weak_ptr hasn't expired().
#include <gmock/gmock.h>
#include <gtest/gtest.h>
struct Client;
struct Server
{
virtual const std::vector<std::shared_ptr<Client>>& getClients() = 0;
};
struct Client
{
virtual std::shared_ptr<Server> getServer() = 0;
};
struct MockServer : virtual Server
{
MOCK_METHOD0(getClients, const std::vector<std::shared_ptr<Client>>&());
std::vector<std::shared_ptr<Client>> clients;
protected:
MockServer()
{
ON_CALL(*this, getClients()).WillByDefault(testing::ReturnRef(this->clients));
}
~MockServer()
{
clients.clear();
std::cout << __func__ << " - This is printed." << std::endl;
}
public:
static std::shared_ptr<MockServer> create()
{
return std::make_shared<testing::NiceMock<MockServer>>();
}
};
ACTION_P(ReturnWeakToShared, wp)
{
bool isWeakPtrExpired = wp.expired();
EXPECT_FALSE(isWeakPtrExpired) << "Dangling pointer.";
return wp.lock();
}
struct MockClient : virtual Client
{
MOCK_METHOD0(getServer, std::shared_ptr<Server>());
std::weak_ptr<Server> server;
protected:
MockClient(std::shared_ptr<Server> server)
{
this->server = server;
ON_CALL(*this, getServer()).WillByDefault(ReturnWeakToShared(this->server));
}
~MockClient()
{
std::cout << __func__ << " - This is printed." << std::endl;
}
public:
static std::shared_ptr<MockClient> create(std::shared_ptr<Server> server)
{
return std::make_shared<testing::NiceMock<MockClient>>(server);
}
};
TEST(ExampleOfLeak, testLeakedMockObjects)
{
auto server = MockServer::create();
auto client1 = MockClient::create(server);
auto client2 = MockClient::create(server);
EXPECT_EQ(server, client1->getServer());
EXPECT_EQ(server, client2->getServer());
EXPECT_TRUE(server->getClients().empty());
server->clients.push_back(client1);
server->clients.push_back(client2);
EXPECT_EQ(client1, server->getClients().front());
EXPECT_EQ(client2, server->getClients().back());
}
the two mock objects to keep each other alive
One of them definitely should use a weak reference to the other.
For example
#include <gmock/gmock.h>
#include <gtest/gtest.h>
struct Client;
struct Server
{
virtual const std::vector<std::weak_ptr<Client>>& getClients() = 0;
};
struct Client
{
virtual std::shared_ptr<Server> getServer() = 0;
};
struct MockServer : virtual Server
{
MOCK_METHOD0(getClients, const std::vector<std::weak_ptr<Client>>&());
std::vector<std::weak_ptr<Client>> clients; // I want to add this variable...
protected:
MockServer()
{
ON_CALL(*this, getClients()).WillByDefault(testing::ReturnRef(this->clients)); // ...and return it whenever getClients is called.
}
~MockServer()
{
clients.clear(); // This doesn't help...
std::cout << __func__ << " - This is never printed." << std::endl;
}
public:
static std::shared_ptr<MockServer> create()
{
return std::make_shared<testing::NiceMock<MockServer>>();
}
};
struct MockClient : virtual Client
{
MOCK_METHOD0(getServer, std::shared_ptr<Server>());
protected:
MockClient(std::shared_ptr<Server> server)
{
ON_CALL(*this, getServer()).WillByDefault(testing::Return(server));
}
~MockClient()
{
std::cout << __func__ << " - This is never printed." << std::endl;
}
public:
static std::shared_ptr<MockClient> create(std::shared_ptr<Server> server)
{
return std::make_shared<testing::NiceMock<MockClient>>(server);
}
};
TEST(ExampleOfLeak, testLeakedMockObjects)
{
auto server = MockServer::create();
auto client1 = MockClient::create(server);
auto client2 = MockClient::create(server);
EXPECT_EQ(server, client1->getServer());
EXPECT_EQ(server, client2->getServer());
EXPECT_TRUE(server->getClients().empty());
server->clients.push_back(client1); // This causes leaked mock objects!
server->clients.push_back(client2); // And this too...
EXPECT_EQ(client1, server->getClients().front().lock());
EXPECT_EQ(client2, server->getClients().back().lock());
// server->clients.clear(); // This prevents mock objects from leaking, but doing this manually everywhere is highly unfeasible.
}
Running main() from gmock_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from ExampleOfLeak
[ RUN ] ExampleOfLeak.testLeakedMockObjects
~MockClient - This is never printed.
~MockClient - This is never printed.
~MockServer - This is never printed.
[ OK ] ExampleOfLeak.testLeakedMockObjects (0 ms)
[----------] 1 test from ExampleOfLeak (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.

Occasional SEGFAULT in my c++ code with flatbuffers

I'm working on my C++ project with flatbuffers. I started with google's online example and wrote a google test. However, this test sometimes failed with SEGFAULT.
Following are the code snippets.
// moster.fbs
namespace MyGame.Sample;
enum Color:byte { Red = 0, Green, Blue = 2 }
union Equipment { Weapon } // Optionally add more tables.
struct Vec3 {
x:float;
y:float;
z:float;
}
table Monster {
pos:Vec3; // Struct.
mana:short = 150;
hp:short = 100;
name:string;
friendly:bool = false (deprecated);
inventory:[ubyte]; // Vector of scalars.
color:Color = Blue; // Enum.
weapons:[Weapon]; // Vector of tables.
equipped:Equipment; // Union.
path:[Vec3]; // Vector of structs.
}
table Weapon {
name:string;
damage:short;
}
root_type Monster;
// test.cpp
#include <fstream>
#include <string>
#include "gtest/gtest.h"
#include "monster_generated.h"
using namespace MyGame::Sample;
void WriteMonsterToFile(const std::string& filename)
{
// Build up a serialized buffer algorithmically:
flatbuffers::FlatBufferBuilder builder;
// First, lets serialize some weapons for the Monster: A 'sword' and an 'axe'.
auto weapon_one_name = builder.CreateString("Sword");
short weapon_one_damage = 3;
auto weapon_two_name = builder.CreateString("Axe");
short weapon_two_damage = 5;
// Use the `CreateWeapon` shortcut to create Weapons with all fields set.
auto sword = CreateWeapon(builder, weapon_one_name, weapon_one_damage);
auto axe = CreateWeapon(builder, weapon_two_name, weapon_two_damage);
// Create a FlatBuffer's `vector` from the `std::vector`.
std::vector<flatbuffers::Offset<Weapon>> weapons_vector;
weapons_vector.push_back(sword);
weapons_vector.push_back(axe);
auto weapons = builder.CreateVector(weapons_vector);
// Second, serialize the rest of the objects needed by the Monster.
auto position = Vec3(1.0f, 2.0f, 3.0f);
auto name = builder.CreateString("MyMonster");
unsigned char inv_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto inventory = builder.CreateVector(inv_data, 10);
// Shortcut for creating monster with all fields set:
auto orc = CreateMonster(builder, &position, 150, 80, name, inventory,
Color_Red, weapons, Equipment_Weapon, axe.Union());
builder.Finish(orc); // Serialize the root of the object.
// We now have a FlatBuffer we can store on disk or send over a network.
std::ofstream outfile(filename, std::ios::binary);
outfile.write((char*)builder.GetBufferPointer(), builder.GetSize());
outfile.flush();
outfile.close();
}
TEST(FlatbuffersTest, Monster)
{
WriteMonsterToFile("monster.bin");
// ** file/network code goes here :) **
std::ifstream infile;
infile.open("monster.bin", std::ios::binary | std::ios::in);
infile.seekg(0, std::ios::end);
int length = infile.tellg();
infile.seekg(0, std::ios::beg);
char* data = new char[length];
infile.read(data, length);
infile.close();
// Get access to the root:
auto monster = GetMonster(data);
delete[] data;
// Get and test some scalar types from the FlatBuffer.
EXPECT_EQ(monster->hp(), 80);
EXPECT_EQ(monster->mana(), 150);
EXPECT_EQ(monster->name()->str(), "MyMonster");
// Get and test a field of the FlatBuffer's `struct`.
auto pos = monster->pos();
EXPECT_EQ(pos->z(), 3.0f);
// Get a test an element from the `inventory` FlatBuffer's `vector`.
auto inv = monster->inventory();
EXPECT_EQ(inv->Get(9), 9);
// Get and test the `weapons` FlatBuffers's `vector`.
std::string expected_weapon_names[] = {"Sword", "Axe"};
short expected_weapon_damages[] = {3, 5};
auto weps = monster->weapons();
for (unsigned int i = 0; i < weps->size(); i++) {
EXPECT_EQ(weps->Get(i)->name()->str(), expected_weapon_names[i]);
EXPECT_EQ(weps->Get(i)->damage(), expected_weapon_damages[i]);
}
// Get and test the `Equipment` union (`equipped` field).
EXPECT_EQ(monster->equipped_type(), Equipment_Weapon);
auto equipped = static_cast<const Weapon*>(monster->equipped());
EXPECT_EQ(equipped->name()->str(), "Axe");
EXPECT_EQ(equipped->damage(), 5);
}
test output:
$ ctest --rerun-failed --output-on-failure
Test project /root/cpcos/build_linux64
Start 3: FlatbuffersTest.Monster
1/1 Test #3: FlatbuffersTest.Monster ..........***Exception: SegFault 0.01 sec
Running main() from /home/conan/w/BuildSingleReference/.conan/data/gtest/1.11.0/_/_/build/7320405f83ec32d8556b524cdda87ee295bb7b84/source_subfolder/googletest/src/gtest_main.cc
Note: Google Test filter = FlatbuffersTest.Monster
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from FlatbuffersTest
[ RUN ] FlatbuffersTest.Monster
/root/cpcos/tests/flatbuffers_gtest.cpp:71: Failure
Expected equality of these values:
monster->hp()
Which is: 100
80
0% tests passed, 1 tests failed out of 1
Total Test time (real) = 0.04 sec
The following tests FAILED:
3 - FlatbuffersTest.Monster (SEGFAULT)
Errors while running CTest
I compiled the code and ran it many times, strangely, it sometimes PASS while sometimes failed with SEGFAULT.
What did I do wrong?
Thanks.

Google Test Returning Garbage Values For Working Operations

I have tested the .decreaseInventory() manually in my main file so I know it works correctly but when I run a google test on it, it fails and gives me an error. How could I fix this?
Player class:
#ifndef PLAYER_H
#define PLAYER_H
#include <iostream>
using namespace std;
class Player
{
int inventory;
public:
Player();
int decreaseInventory(int numOfBeers);
void setInventory(int newInventory);
int getBackOrder();
int getCost();
int getInventory();
bool operator ==(Player& p);
};
Player::Player()
{
cout << " Default Player Constructor\n";
inventory = 12;
backorder = 0;
cost = 0;
orderDelay = 0;
shipmentDeplay = 0;
}
void Player::setInventory(int newInventory)
{
inventory = newInventory;
}
int Player::decreaseInventory(int numOfBeers)
{
inventory = inventory - numOfBeers;
}
int Player::getInventory()
{
return inventory;
}
test.cpp:
#include "gtest/gtest.h"
#include "Player.h"
TEST(playerTest, decreaseInventoryTest ) {
Player p;
int curr_inv = p.getInventory();
EXPECT_EQ(curr_inv-3, p.decreaseInventory(3));
}
Error:
Running main() from gtest_main.cc
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from playerTest
[ RUN ] playerTest.decreaseInventoryTest
Default Player Constructor
/home/hammad/se-02-team-21/tests.cpp:13: Failure
Expected: curr_inv-3
Which is: 9
To be equal to: p.decreaseInventory(3)
Which is: 1740894128
[ FAILED ] playerTest.decreaseInventoryTest (1 ms)
[----------] 1 test from playerTest (1 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] playerTest.decreaseInventoryTest
1 FAILED TEST
Why does it fail and give a garbage value? I am already using a default constructor and in general it works properly.
You are missing a return value in your decreaseInventory function definition. To fix this, simply return the (modified) value of the inventory member variable:
int Player::decreaseInventory(int numOfBeers)
{
inventory = inventory - numOfBeers;
return inventory; // You need to return the modified value!
}
Your method is:
int Player::decreaseInventory(int numOfBeers)
{
inventory = inventory - numOfBeers;
}
Your test is:
EXPECT_EQ(curr_inv-3, p.decreaseInventory(3));
The macro EXPECT_EQ compares the value returned from p.decreaseInventory(3) with curr_inv-3. The method does not return anything.
Not returning something from a method that is declared to return something is undefined behavior. Your compiler should have warned you about it and your test was successful in the sense that you have a failing test that is caused by a real bug in your code that you now can fix.

C++ Multiple parameters with GTest TYPED_TEST

I have fallowing sets of tests:
TEST_F(FactoryShould, createAFromAModule)
{
const auto stateMachine = createStateMachine(EModule_A);
const auto* typedStateMachine =
dynamic_cast<const BackEnd<transitiontable::A, Guard>*>(stateMachine.get());
ASSERT_TRUE(typedStateMachine);
}
TEST_F(FactoryShould, createBFromBModule)
{
const auto stateMachine = createStateMachine(EModule_B);
const auto* typedStateMachine =
dynamic_cast<const BackEnd<transitiontable::B, Guard>*>(stateMachine.get());
ASSERT_TRUE(typedStateMachine);
}
Is there any way to turn them into Typed Tests? All I seen is solution for only one changing parameter, there I have 2 changing parameters, EModule can be used for multiple transitiontables, so something like map looks good, but it is doable?
With std::pair you can
make one type out of any other two. (And with std::tuple
you can make one type out of any other N).
You can write googletest TYPED_TESTs in which TypeParam assumes values from
a list of std::pair<X,Y>, for paired parameter-types X and Y, so that each instantiation of such a TYPED_TEST
has X defined as TypeParam::first_type and Y defined as TypeParam::second_type. E.g:
gtester.cpp
#include <gtest/gtest.h>
#include <utility>
#include <cctype>
struct A1 {
char ch = 'A';
};
struct A2 {
char ch = 'a';
};
struct B1 {
char ch = 'B';
};
struct B2 {
char ch = 'b';
};
template <typename T>
class pair_test : public ::testing::Test {};
using test_types = ::testing::Types<std::pair<A1,A2>, std::pair<B1,B2>>;
TYPED_TEST_CASE(pair_test, test_types);
TYPED_TEST(pair_test, compare_no_case)
{
typename TypeParam::first_type param1;
typename TypeParam::second_type param2;
ASSERT_TRUE(param1.ch == std::toupper(param2.ch));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Compile, link, run:
$ g++ -Wall -o gtester gtester.cpp -lgtest -pthread && ./gtester
[==========] Running 2 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 1 test from pair_test/0, where TypeParam = std::pair<A1, A2>
[ RUN ] pair_test/0.compare_no_case
[ OK ] pair_test/0.compare_no_case (0 ms)
[----------] 1 test from pair_test/0 (0 ms total)
[----------] 1 test from pair_test/1, where TypeParam = std::pair<B1, B2>
[ RUN ] pair_test/1.compare_no_case
[ OK ] pair_test/1.compare_no_case (0 ms)
[----------] 1 test from pair_test/1 (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 2 test cases ran. (0 ms total)
[ PASSED ] 2 tests.

Calling a boost unit_test test within code

I'm trying to call a boost::unit_test from code.
In a number of files I've got
BOOST_AUTO_TEST_SUITE(DataAccessSuite)
BOOST_AUTO_TEST_CASE(DateAppender)
{
...
}
BOOST_AUTO_TEST_SUITE_END()
For my dialog box I have a visitor to gather the IDs and names of all the test cases/suites
namespace {
unit_test::test_suite* init_unit_test_suite(int argc, char** argv) {
return 0;
}
using namespace std::string_literals;
struct test_visitor : unit_test::test_tree_visitor {
test_visitor(std::vector<std::tuple<std::string, unit_test::test_unit_id>>& tests) : m_tests(tests) {}
void visit(unit_test::test_case const& test) {
m_tests.emplace_back(std::make_tuple(suite + "/"s + static_cast<std::string>(test.p_name),test.p_id));
}
virtual bool test_suite_start(unit_test::test_suite const& ts) {
suite = ts.p_name;
return true;
}
virtual void test_suite_finish(unit_test::test_suite const&) {
suite = std::string();
}
std::string suite;
std::vector<std::tuple<std::string, unit_test::test_unit_id>>& m_tests;
};
}
TestDialogImpl::TestDialogImpl(wxWindow* parent) : TestDialog(parent)
{
// Make a list of test cases to show in my dialog box
unit_test::traverse_test_tree(unit_test::framework::master_test_suite(), test_visitor(m_tests), true);
for (auto& test : m_tests) {
m_listBox2->Append(wxString(std::get<0>(test)));
}
}
And here's my call to the test case
void TestDialogImpl::OnClick_RunButton(wxCommandEvent & event)
{
auto selection = m_listBox2->GetStringSelection();
char* argv[] = { "OptionModeller.exe","--run_test=DataAccessSuite/DateAppender" };
unit_test::framework::init(init_unit_test_suite, 2, argv);
auto finder = std::find_if(std::begin(m_tests), std::end(m_tests), [&selection](auto& v) { return std::get<0>(v) == selection; });
// This fails with setup_error(), but I don't know why?
unit_test::framework::run(std::get<1>(*finder), true);
}
Is there a way I could call the test and utilize the framework. I know I could alternatively call the free functions but that defeats the point of using BOOST_TEST
UPDATE
From #A Fagrell's idea
I changed the call of the test executor to
void TestDialogImpl::OnClick_RunButton(wxCommandEvent & event)
{
wxString selection = "--run_test=" + m_listBox2->GetStringSelection();
const char* option = static_cast<const char*>(selection);
char* argv[] = { "OptionModeller.exe" , (char*)(option)};
unit_test::unit_test_main(&init_unit_test_suite, 2, argv);
}
Seems to work ok, but does seem the wrong way of doing things. I would have expected to be able to call a test explicitly by id, rather than by fudging the command line args.
Is there a better way?