How to test different classes/structs implementing an interface using google test? - c++

I am trying to use typed test concept available in google test. The description of this concept matches what I intend to do, but I cannot figure it out completely. I want to test structs which implements an interface, since they are totally different once, they need to be initialized with different values/instances.
Simply my code is as follows
struct Serializable
{
virtual sObj serialize() = 0;
virtual void unserialize(sObj) = 0;
};
struct s1 : serializable
{
int attrI1;
int attrI2;
sObj serialize()
{
//serialize an instance of this struct
}
void unserialize(sObj)
{
//unserialize data to instance of this struct
}
}
struct s2 : serializable
{
char attrC;
void serialize()
{
//serialize an instance of this struct
}
sObj unserialize()
{
//unserialize data to instance of this struct
}
}
And I want to test s1 and s2 with different instances/values. The test should look like:
template <typename T>
int testSerialzable(T& t)
{
sObj obj = t.pack();
T temp;
TEST_EQ(temp.unpack(obj), t);
}
Can someone please tell me if this is possible to do and how?
Many thanks in advance

I finally figured it out. for the example that I had above. It will be like:
template<class T>
struct TestSerializable : public ::testing::Test
{
static T serializedType;
};
TYPED_TEST_CASE_P(TestSerializable);
TYPED_TEST_P(TestSerializable, serializationTest)
{
sObj obj = t.serialize();
TypeParam temp;
ASSERT_EQ(temp.unserialize(obj), t);
}
REGISTER_TYPED_TEST_CASE_P(TestSerializable, serializationTest);
typedef ::testing::Types<s1, s2> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(MySerialiInstantiation, TestSerializable, MyTypes);
template<> s1 TestSerializable<s1>::serializedType(/*instance of s1 with proper values*/s1());
template<> s2 TestSerializable<s2>::serializedType(/*instance of s1 with proper values*/s2());

The original sample can't be compiled, I post my successful change for reference. If you need the full source code, please refer to the link(https://github.com/dougpuob/googletest-sample/blob/master/source/test-derived-func-by-interface/main.cpp).
#include "BinarySearchLoop.h"
#include "BinarySearchRecursive.h"
#include "gtest/gtest.h"
template <class T>
struct TestBinarySearch : public ::testing::Test {
static T Instance;
};
TYPED_TEST_CASE_P(TestBinarySearch);
TYPED_TEST_P(TestBinarySearch, PositiveInteger) {
std::vector<int> SortedArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};
ASSERT_EQ(4, Instance.search(SortedArray, 5));
ASSERT_EQ(5, Instance.search(SortedArray, 6));
ASSERT_EQ(0, Instance.search(SortedArray, 1));
ASSERT_EQ(8, Instance.search(SortedArray, 9));
}
TYPED_TEST_P(TestBinarySearch, NegativeInteger) {
std::vector<int> SortedArray = {-8, -7, -6, -5, -4, -3, -2, -1};
EXPECT_EQ(+6, Instance.search(SortedArray, -2));
EXPECT_EQ(-1, Instance.search(SortedArray, +0));
EXPECT_EQ(-1, Instance.search(SortedArray, -9));
}
TYPED_TEST_P(TestBinarySearch, Integer) {
std::vector<int> SortedArray = {-1, 0, 3, 5, 9, 12};
EXPECT_EQ(+4, Instance.search(SortedArray, 9));
EXPECT_EQ(-1, Instance.search(SortedArray, 2));
}
TYPED_TEST_P(TestBinarySearch, SingleElement) {
std::vector<int> SortedArray = {5};
EXPECT_EQ(+0, Instance.search(SortedArray, 5));
EXPECT_EQ(-1, Instance.search(SortedArray, 1));
EXPECT_EQ(-1, Instance.search(SortedArray, 6));
}
TYPED_TEST_P(TestBinarySearch, TwoElementsOnly) {
std::vector<int> SortedArray = {1, 5};
EXPECT_EQ(+1, Instance.search(SortedArray, 5));
EXPECT_EQ(+0, Instance.search(SortedArray, 1));
EXPECT_EQ(-1, Instance.search(SortedArray, 2)); // Not in the array.
EXPECT_EQ(-1, Instance.search(SortedArray, 7)); // Not in the array.
}
REGISTER_TYPED_TEST_CASE_P(TestBinarySearch,
PositiveInteger,
NegativeInteger,
Integer,
SingleElement,
TwoElementsOnly);
typedef ::testing::Types<BinarySearchLoop, BinarySearchRecursive> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(MyBinarySearchInstantiation,
TestBinarySearch,
MyTypes);
template <>
BinarySearchLoop TestBinarySearch<BinarySearchLoop>::Instance;
template <>
BinarySearchRecursive TestBinarySearch<BinarySearchRecursive>::Instance;
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
int Ret = RUN_ALL_TESTS();
return Ret;
}

It's more convenient to use value-parameterized tests for testing different implementations of an interface. Google Test's sample7 shows how to do that.

Related

how to read and write non-fixed-length structs to biniary file c++

I have vector of structs:
typedef struct
{
uint64_t id = 0;
std::string name;
std::vector<uint64_t> data;
} entry;
That I want to write to file:
FILE *testFile = nullptr;
testFile = fopen("test.b", "wb");
However the normal method for read/write
fwrite(vector.data(), sizeof vector[0], vector.size(), testFile);
fread(vector.data(), sizeof(entry), numberOfEntries, testFile);
does not work as the size of entry can vary wildly depending on the contents of
std::string name;
std::vector<uint64_t> data;
so I would like methods and pointers about how to do read/writing of this data to/from files.
When dealing with non-fixed size data it's important to keep track of the size somehow. You can simply specify the amount of fixed size elements or byte size of whole structure and calculate needed values when reading the struct. I'm in favour of the first one though it can sometimes make debugging a bit harder.
Here is an example how to make a flexible serialization system.
struct my_data
{
int a;
char c;
std::vector<other_data> data;
}
template<class T>
void serialize(const T& v, std::vector<std::byte>& out)
{
static_assert(false, "Unsupported type");
}
template<class T>
requires std::is_trivially_copy_constructible_v<T>
void serialize(const T& v, std::vector<std::byte>& out)
{
out.resize(std::size(out) + sizeof(T));
std::memcpy(std::data(out) + std::size(out) - sizeof(T), std::bit_cast<std::byte*>(&v), sizeof(T));
}
template<class T>
void serialize<std::vector<T>>(const std::vector<T>& v, std::vector<std::byte>& out)
{
serialize<size_t>(std::size(v), out); // add size
for(const auto& e : v)
serialize<T>(v, out);
}
template<>
void serialize<my_data>(const my_data& v, std::vector<std::byte>& out)
{
serialize(v.a, out);
serialize(v.c, out);
serialize(v.data, out);
}
// And likewise you would do for deserialize
int main()
{
std::vector<std::byte> data;
my_data a;
serialize(a, data);
// write vector of bytes to file
}
This is a tedious job and there are already libraries that do it for you like Google's Flatbuffers, Google's Protobuf or a single header BinaryLove3. Some of them work out of the box with aggregate types (meaning all member variables are public). Here is an example of BinaryLove3 in action.
#include <iostream>
#include <vector>
#include <string>
#include <cstdint>
#include <string>
#include <list>
#include "BinaryLove3.hpp"
struct foo
{
uint32_t v0 = 3;
uint32_t v1 = 2;
float_t v2 = 2.5f;
char v3 = 'c';
struct
{
std::vector<int> vec_of_trivial = { 1, 2, 3 };
std::vector<std::string> vec_of_nontrivial = { "I am a Fox!", "In a big Box!" };
std::string str = "Foxes can fly!";
std::list<int> non_random_access_container = { 3, 4, 5 };
} non_trivial;
struct
{
uint32_t v0 = 1;
uint32_t v1 = 2;
} trivial;
};
auto main() -> int32_t
{
foo out = { 4, 5, 6.7f, 'd', {{5, 4, 3, 2}, {"cc", "dd"}, "Fly me to the moon..." , {7, 8, 9}}, {3, 4} };
auto data = BinaryLove3::serialize(bobux);
foo in;
BinaryLove3::deserialize(data, in);
return int32_t(0);
}

std::vector<int> to std::vector<enum>

I have an array std::vector<int> and an enum class Foo : int. Is there a better way to cast or convert than a reinterpret_cast?
std::vector<int> v;
auto& c = *reinterpret_cast<std::vector<Foo>*>(&v);
https://godbolt.org/z/M6ofkF
Combine a static_cast with std::transform.
#include <algorithm>
#include <vector>
#include <cassert>
enum class Foo : int {
A = 0,
B = 1,
C = 2,
D = 3,
E = 4,
F = 5
};
std::vector<Foo> convert(std::vector<int> const &in) {
std::vector<Foo> out;
out.reserve(in.size());
std::transform(in.begin(), in.end(), std::back_inserter(out),
[](int n) { return static_cast<Foo>(n); });
return out;
}
int main() {
std::vector<int> vec{1, 2, 3, 4, 5};
std::vector<Foo> c = convert(vec);
assert(c[3] == Foo::E);
return 0;
}
Reference: How to cast int to enum in C++?

What's wrong with this c++ template function

What can be wrong this piece of code.
I'm trying to call countLessThan3 by instantiating with std::vector.
// 3. Lambdas
template<typename T>
const auto countLessThan3(const T & vec, int value)
{
const auto count = std::count(vec.begin(), vec.end(),
[](int i){ return i < 3;}
);
return count;
}
int main(int argc, char const *argv[])
{
// 3
std::vector<int> vector = {1, 2, 3, 4, 5, 2, 2, 2};
countLessThan3<std::vector<int>>(vector, 3);
return 0;
}
compiled with g++ -std=c++14 1.cpp -o 1 on linux.
Ther are some problems:
The code uses std::count, but a lambda is passed, so it should be std::count_if instead.
value is passed to the function as a parameter, but it is not used and there is an hard coded 3 in the lambda.
Other minor issues are fixed in the snippet below
#include <iostream>
#include <vector>
#include <algorithm>
template<typename T>
auto countLessThan(const T & vec, typename T::value_type value)
{
return std::count_if(vec.begin(), vec.end(),
[value](typename T::value_type i){ return i < value;}
);
}
int main()
{
std::vector<int> vector = {1, 2, 3, 4, 5, 2, 2, 2};
std::cout << countLessThan(vector, 3) << '\n';
return 0;
}

How to find out if deque's element has more than two same values

If I have
class myElement{
public:
int get_first();
int get_second();
int get_third();
int get_fourth();
private:
deque<int> numbers_ = {1, 2, 3, 4} // numbers are different in every element
};
and
class myElements{
public:
bool check(); // checks if theres more than two elements that contains the same number
private:
deque<myElement> elements_;
};
What would be a good way to find out if elements_ has more than two elements with atleast one same number?
for example: {1, 0, 0, 6} {1, 2, 3, 4} {4, 2, 3, 1} each of those have number 1 so check() would return true.
Probably, a vector is a better choice here than a deque. The straightforward way would be to check for each element of the first container if it occurs in all other containers:
The myElement 'container-like' class:
using namespace std;
class myElement{
public:
bool contains(int n) const {
return find(numbers_.begin(), numbers_.end(), n) != numbers_.end();
}
myElement(std::vector<int> ns):numbers_(ns){}
vector<int> values() const { return numbers_; }
private:
vector<int> numbers_;// = {1, 2, 3, 4} // numbers are different in every element
};
The myElements extensions:
class myElements{
public:
myElements(initializer_list<myElement>&& t): elements_(t){}
bool check() {
myElement& first = elements_.front();
for ( auto n: first.values()) {
if (occurs_in_each_but_first(n)) {
return true;
}
}
return false;
}
private:
bool occurs_in_each_but_first(int n) {
auto it = begin(elements_);
++it;
auto itEnd = end(elements_);
for (; it != itEnd; ++it) {
if (! it->contains(n)) return false;
}
return true;
}
vector<myElement> elements_;
};
A test:
#include <cassert>
void main(){
myElement e0({11,12,13,4});
myElement e1({11,12,13,41});
myElement e2({1,2,3,4});
myElement e3({1,2,3,4});
myElement e4({1,22,23,24});
assert(myElements({e0,e2,e3}).check());
assert(!myElements({e1,e2,e3}).check());
assert(myElements({e2,e3,e4}).check());
}
Where optimizations are possible, of course.

Templated static member functions in C++

I've written a simple test program to try to learn how to use template static member functions in C++. The code compiles, but doesn't work right (prints out some garbage). I guess I'm using the right syntax. I've read this or this and some other stuff but still don't know what I'm doing wrong. The code below:
#include <iostream>
using namespace std;
class Util {
public:
Util();
virtual ~Util();
template <typename T> static void printTab(T tab[]);
};
template <typename T>
void Util::printTab(T tab[]) {
for (unsigned int i=0; i<sizeof(tab)/sizeof(tab[0]); i++) {
cout << tab[0] << " ";
}
cout << endl;
}
int main() {
float tabFloat[5] {1, 2, 3, 4, 5};
unsigned char tabChar[3] {1, 2, 3};
Util::printTab(tabFloat);
Util::printTab(tabChar);
return 0;
}
Any hints appreciated.
You need to pass the size as another template argument :
#include <iostream>
using namespace std;
class Util {
public:
Util();
virtual ~Util();
template <typename T,int N> static void printTab(T (&tab)[N])
{
for (int i=0; i<N; i++) {
cout << tab[i] << " ";
}
cout << endl;
}
};
int main() {
float tabFloat[5] {1, 2, 3, 4, 5};
unsigned char tabChar[3] {1, 2, 3};
Util::printTab(tabFloat);
Util::printTab(tabChar);
}
sizeof(tab) is the size of a T*, it will not return the size of the whole array. You need to pass that in yourself as another argument to the function. See here for an explanation and another potential workaround: When a function has a specific-size array parameter, why is it replaced with a pointer?
Note that the second printTab will not output readable characters. If you want to see something printed out, try with:
unsigned char tabChar[3] {'1', '2', '3'};
How about trying, you need to send the size of the array when calling a function:
#include <iostream>
using namespace std;
class Util {
public:
Util();
virtual ~Util();
template <typename T> static void printTab(T tab[], size_t sz);
};
template <typename T>
void Util::printTab(T tab[], size_t sz) {
for (unsigned int i=0; i<sz; i++) {
cout << tab[i] << " ";
}
cout << endl;
}
int main() {
float tabFloat[5] {1, 2, 3, 4, 5};
unsigned char tabChar[3] {1, 2, 3};
Util::printTab(tabFloat, sizeof(tabFloat)/sizeof(float));
Util::printTab(tabChar, sizeof(tabChar)/sizeof(char));
return 0;
}
I'd pass the number of elements of T as a function argument or use a STD container such as a Vector.
Your for loop is just printing the first element tab[0] not tab[i]
Your initialization of tabFloat and tabChar are missing =
float tabFloat[5] {1, 2, 3, 4, 5};
unsigned char tabChar[3] {1, 2, 3};
(also I'd use 65, 66, 67 instead of 1,2,3 for console readability in your testing)
float tabFloat[5] = {1, 2, 3, 4, 5};
unsigned char tabChar[3] = { 65, 66, 67};