Browse Source

protyping: POD / String types, and more tests

heck-rework
heck 3 years ago
parent
commit
e0634523a6
  1. 402
      test/test_nr1.cc

402
test/test_nr1.cc

@ -6,146 +6,37 @@
#include <pEp/identity_list.h> #include <pEp/identity_list.h>
#include <pEp/utils.hh> #include <pEp/utils.hh>
#include <pEp/pEpLog.hh> #include <pEp/pEpLog.hh>
#include <sstream> #include <pEp/inspect.hh>
#include <type_traits> #include <type_traits>
//using namespace pEp;
namespace pEp { namespace pEp {
// type ---------------------------------------- char* alloc_str(const std::string& str)
// same for pointer and value typee
template<class T>
std::string type_str(const T)
{
std::stringstream ss_type;
ss_type << typeid(T).name();
return ss_type.str();
}
// addr ----------------------------------------
template<class T>
struct addr_str_helper {
static std::string str(const T c)
{
std::stringstream ss_addr{};
ss_addr << static_cast<const void*>(&c);
return ss_addr.str();
}
};
template<class T>
struct addr_str_helper<T*> {
static std::string str(const T* c)
{
std::stringstream ss_addr{};
ss_addr << static_cast<const void*>(c);
return ss_addr.str();
}
};
// rertuns the address of c
template<class T>
std::string addr_str(T c)
{
return addr_str_helper<T>::str(c);
}
// val ----------------------------------------
template<class T>
struct val_str_helper {
static std::string str(T c, size_t val_len = 30)
{
//value
std::stringstream ss_val{};
ss_val << c;
return pEp::Utils::clip(ss_val.str(), val_len);
}
};
template<class T>
struct val_str_helper<T*> {
static std::string str(T* c, size_t val_len = 30)
{
//value
std::stringstream ss_val{};
if (c != nullptr) {
ss_val << &c;
} else {
ss_val << "<NULL>";
}
return pEp::Utils::clip(ss_val.str(), val_len);
}
};
// returns the value of c (if T is pointer type, the address p is pointing to)
template<class T>
std::string val_str(T c, size_t val_len = 30)
{
return val_str_helper<T>::str(c, val_len);
}
// type_addr_val ----------------------------------------
template<class T>
struct type_addr_val_helper {
static std::string str(T c, size_t val_len = 30)
{
std::stringstream ret{};
ret << "{ " << type_str(c) << " | " << addr_str(c) << " | " << val_str(c, val_len)
<< " }";
return ret.str();
}
};
template<class T>
struct type_addr_val_helper<T*> {
static std::string str(T* c, size_t val_len = 30)
{
std::stringstream ret{};
ret << "{ " << type_str(c) << " | " << addr_str(c) << " | " << val_str(c, val_len)
<< " }";
return ret.str();
}
};
template<class T>
std::string type_addr_val(T c, size_t val_len = 30)
{
return type_addr_val_helper<T>::str(c, val_len);
}
} // namespace pEp
namespace pEp {
char* alloc(const std::string& str)
{ {
char* ret = strdup(str.c_str()); char* ret = strdup(str.c_str());
pEpLog(type_addr_val(ret)); pEpLog(CXX::Inspect::all(ret));
return ret; return ret;
} }
template<class T> template<class T>
void free(T ptr_type) void free(T ptr_type)
{ {
pEpLog(type_addr_val(ptr_type)); pEpLog(CXX::Inspect::all(ptr_type));
::pEp_free(ptr_type); ::pEp_free(ptr_type);
} }
template<> template<>
void free(char* ptr_type) void free(char* ptr_type)
{ {
pEpLog(type_addr_val(ptr_type)); pEpLog(CXX::Inspect::all(ptr_type));
::pEp_free(ptr_type); ::pEp_free(ptr_type);
} }
template<> template<>
void free(::pEp_identity* ptr_type) void free(::pEp_identity* ptr_type)
{ {
pEpLog(type_addr_val(ptr_type)); pEpLog(CXX::Inspect::all(ptr_type));
::pEp_free(ptr_type); ::pEp_free(ptr_type);
} }
@ -153,20 +44,9 @@ namespace pEp {
//--------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------
#define EXSTR(msg) std::string(__FUNCTION__) + " - " + msg #define EXSTR(msg) std::string(__FUNCTION__) + " - " + msg
class PODBase {
public:
static bool log_enabled;
protected:
bool _is_initialized{ false };
Adapter::pEpLog::pEpLogger logger{ "pEp::Enum", log_enabled };
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger;
};
template<class T> template<class T>
class POD : public PODBase { class POD {
static_assert(std::is_pod<T>::value, "only POD types are allowed"); static_assert(std::is_pod<T>::value && !std::is_pointer<T>::value, "not an integral");
public: public:
POD() = delete; POD() = delete;
@ -174,10 +54,8 @@ namespace pEp {
explicit POD(T* pod_p) explicit POD(T* pod_p)
{ {
if (pod_p == nullptr) { if (pod_p == nullptr) {
throw Exception{ EXSTR("cant initialize on a nullptr") }; throw Exception{ EXSTR("cant init on a nullptr") };
} }
// init
_pod_p = pod_p; _pod_p = pod_p;
pEpLogClass(to_string() + " - taking ownership"); pEpLogClass(to_string() + " - taking ownership");
} }
@ -185,8 +63,7 @@ namespace pEp {
// make a copy // make a copy
POD& operator=(T value) POD& operator=(T value)
{ {
pEpLogClass("Before: " + to_string() + " - new val: '" + val_str(value) + "'"); pEpLogClass("Before: " + to_string() + " - new val: '" + CXX::Inspect::val(value) + "'");
*_pod_p = value; *_pod_p = value;
pEpLogClass("After: " + to_string()); pEpLogClass("After: " + to_string());
return *this; return *this;
@ -212,21 +89,26 @@ namespace pEp {
std::string to_string() const std::string to_string() const
{ {
std::string ret{ "[" + type_addr_val(_pod_p) + "]" }; std::string ret{ "[" + CXX::Inspect::all(_pod_p) + "]" };
return ret; return ret;
} }
static bool log_enabled;
private: private:
bool _is_initialized{ false };
T* _pod_p{ nullptr }; T* _pod_p{ nullptr };
Adapter::pEpLog::pEpLogger logger{ "pEp::POD", log_enabled };
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger;
class Exception : public std::runtime_error { class Exception : public std::runtime_error {
public: public:
explicit Exception(const std::string& msg) : std::runtime_error(msg) {} explicit Exception(const std::string& msg) : std::runtime_error(msg) {}
}; };
}; };
bool pEp::PODBase::log_enabled{ true }; template<class T>
bool pEp::POD<T>::log_enabled{ true };
template<class T> template<class T>
std::ostream& operator<<(std::ostream& o, pEp::POD<T> pEpEnum) std::ostream& operator<<(std::ostream& o, pEp::POD<T> pEpEnum)
@ -245,28 +127,43 @@ namespace pEp {
// * dont take ownership, only provide a C++ interface // * dont take ownership, only provide a C++ interface
// There must be the option to construct the object before the wrapped c_str (char*) // There must be the option to construct the object before the wrapped c_str (char*)
// is known. This leads to a pEp::String object in invalid state, and it must be initialized using // is known. This leads to a pEp::String object in invalid state, and it must be initialized using
// set(). Otherwise all(most) functions will throw. // init(). Otherwise all(most) functions will throw.
// set() can only be called once, will throw otherwise. // init() can only be called once, will throw otherwise.
// NON-owning mode, just does not free the string when it dies, owning mode does.
// Thats the only difference.
class String { class String {
public: public:
String() = default; String() = default;
// Best to use this constructor, as the object is in a valid state when the wrapped // Best to use this constructor, as the object is in a valid state when the wrapped
// c_str (char*) is known // c_str (char*) is known
explicit String(char** c_str_pp) String(bool take_ownership, char** c_str_pp)
{ {
initialize(c_str_pp); init(take_ownership, c_str_pp);
}
explicit String(bool take_ownership, char** c_str_pp, const std::string& init_val)
{
init(take_ownership, c_str_pp, init_val);
} }
~String() ~String()
{ {
_free(); if (_is_owning_mode) {
_free();
}
}
void init(bool take_ownership, char** c_str_pp, const std::string& init_val)
{
init(take_ownership, c_str_pp);
this->operator=(init_val);
} }
void initialize(char** c_str_pp) void init(bool take_ownership, char** c_str_pp)
{ {
if (c_str_pp == nullptr) { if (c_str_pp == nullptr) {
throw Exception{ EXSTR("cant initialize on a nullptr") }; throw Exception{ EXSTR("cant init on a nullptr") };
} }
if (_is_initialized) { if (_is_initialized) {
@ -277,7 +174,8 @@ namespace pEp {
_c_str_pp = c_str_pp; _c_str_pp = c_str_pp;
_c_str_p = *_c_str_pp; _c_str_p = *_c_str_pp;
_is_initialized = true; _is_initialized = true;
pEpLogClass(to_string() + " - taking ownership"); _is_owning_mode = take_ownership;
pEpLogClass(to_string() + " - init " + (take_ownership ? "/taking ownership" : ""));
} }
// make a copy // make a copy
@ -297,7 +195,7 @@ namespace pEp {
*_c_str_pp = nullptr; *_c_str_pp = nullptr;
_c_str_p = *_c_str_pp; _c_str_p = *_c_str_pp;
} else { } else {
*_c_str_pp = pEp::alloc(str); *_c_str_pp = pEp::alloc_str(str);
_c_str_p = *_c_str_pp; _c_str_p = *_c_str_pp;
} }
pEpLogClass("After: " + to_string()); pEpLogClass("After: " + to_string());
@ -336,14 +234,15 @@ namespace pEp {
throw Exception{ EXSTR("invalid state") }; throw Exception{ EXSTR("invalid state") };
} }
std::string ret{ "[" + type_addr_val(_c_str_pp) + " / " + type_addr_val(*_c_str_pp) + std::string ret{ "[" + CXX::Inspect::all(_c_str_pp) + " / " +
"]" }; CXX::Inspect::all(*_c_str_pp) + "]" };
return ret; return ret;
} }
static bool log_enabled; static bool log_enabled;
private: private:
// TODO: this is dodgy, do we really need _c_str_pp AND _c_str_p???
void _free() void _free()
{ {
// if the c_str points to a different address now, than the one we created // if the c_str points to a different address now, than the one we created
@ -352,8 +251,7 @@ namespace pEp {
pEpLog("raw access string change detected"); pEpLog("raw access string change detected");
pEp::free(*_c_str_pp); pEp::free(*_c_str_pp);
} else { } else {
// we anyways need to free the one we created // WE ASSUME THAT:
// NO: WE ASSUME THAT:
// if the char* has been replaced, it has been freed, as well // if the char* has been replaced, it has been freed, as well
if (_c_str_p != nullptr) { if (_c_str_p != nullptr) {
pEp::free(_c_str_p); pEp::free(_c_str_p);
@ -362,6 +260,7 @@ namespace pEp {
} }
bool _is_initialized{ false }; bool _is_initialized{ false };
bool _is_owning_mode{ false };
char** _c_str_pp{ nullptr }; char** _c_str_pp{ nullptr };
char* _c_str_p{ nullptr }; char* _c_str_p{ nullptr };
@ -397,16 +296,10 @@ namespace pEp {
_wrappee = ::new_identity(nullptr, nullptr, nullptr, nullptr); _wrappee = ::new_identity(nullptr, nullptr, nullptr, nullptr);
// set the pEp::String wrapper underlying c_str // set the pEp::String wrapper underlying c_str
this->address.initialize(&_wrappee->address); this->address.init(true, &_wrappee->address, address);
this->username.initialize(&_wrappee->username); this->username.init(true, &_wrappee->username, username);
this->user_id.initialize(&_wrappee->user_id); this->user_id.init(true, &_wrappee->user_id, user_id);
this->fpr.initialize(&_wrappee->fpr); this->fpr.init(true, &_wrappee->fpr, fpr);
// set the values
this->address = address;
this->username = username;
this->user_id = user_id;
this->fpr = fpr;
} }
~Identity() ~Identity()
@ -445,25 +338,28 @@ namespace pEp {
::PEP_SESSION session; ::PEP_SESSION session;
void test_getters(char const* const* const c_str_p, pEp::String& pstr) void test_getters(char** c_str_p, pEp::String& pstr, const std::string& expected)
{ {
pEpLog("to_string(): " + pstr.to_string()); pEpLog("to_string(): " + pstr.to_string());
// compare addresses
pEpLog("addresses == c_str_p");
assert(pstr.c_data() == *c_str_p); assert(pstr.c_data() == *c_str_p);
assert(pstr.data() == c_str_p); assert(pstr.data() == c_str_p);
// compare values
if (*c_str_p != nullptr) { if (*c_str_p != nullptr) {
std::string tmp{ pstr }; pEpLog("operator std::string(): " + std::string(pstr));
pEpLog("operator std::string(): " + tmp); assert(std::string(pstr) == std::string(*c_str_p));
assert(tmp == std::string(*c_str_p)); assert(std::string(pstr) == expected);
// will segfault with nullptr, and this is correct // will segfault with nullptr, and this is correct
std::string tmp2{ *pstr.data() }; pEpLog("data(): " + std::string(*pstr.data()));
pEpLog("data(): " + tmp2); assert(std::string(*pstr.data()) == std::string(*c_str_p));
assert(tmp2 == std::string(*c_str_p)); assert(std::string(*pstr.data()) == expected);
std::string tmp3{ pstr.c_data() }; pEpLog("c_data(): " + std::string(pstr.c_data()));
pEpLog("c_data(): " + tmp2); assert(std::string(pstr.c_data()) == std::string(*c_str_p));
assert(tmp3 == std::string(*c_str_p)); assert(std::string(pstr.c_data()) == expected);
} else { } else {
std::string tmp{ pstr }; std::string tmp{ pstr };
pEpLog("operator std::string(): " + tmp); pEpLog("operator std::string(): " + tmp);
@ -471,62 +367,124 @@ void test_getters(char const* const* const c_str_p, pEp::String& pstr)
} }
} }
void test_assign_and_getters(char** c_str_p, pEp::String& pstr) void test_assign(char** c_str_p, pEp::String& pstr)
{ {
test_getters(c_str_p, pstr); {
pEpLogH2("assign operator"); pEpLogH2("assign operator");
pstr = "assign operator"; std::string new_val{ "assign operator" };
test_getters(c_str_p, pstr); pstr = new_val;
test_getters(c_str_p, pstr, new_val);
pEpLogH2("raw c_str assign"); }
*c_str_p = strdup("raw c_str assign"); {
test_getters(c_str_p, pstr); pEpLogH2("raw c_str assign");
std::string new_val{ "raw c_str assign" };
free(*c_str_p);
*c_str_p = strdup(new_val.c_str());
test_getters(c_str_p, pstr, new_val);
}
}
void test_getters(int* c_int_p, pEp::POD<int>& pint, int expected)
{
pEpLog("to_string(): " + pint.to_string());
// compare addresses
pEpLog("addresses == c_int_p");
assert(pint.c_data() == c_int_p);
assert(pint.data() == c_int_p);
// compare values
if (c_int_p != nullptr) {
pEpLog("operator int(): " + std::to_string(pint));
assert(pint == *c_int_p);
assert(pint == expected);
// will segfault with nullptr, and this is correct
pEpLog("data(): " + std::to_string(*pint.data()));
assert(*pint.data() == *c_int_p);
assert(*pint.data() == expected);
pEpLog("c_data(): " + std::to_string(*pint.c_data()));
assert(*pint.c_data() == *c_int_p);
assert(*pint.c_data() == expected);
}
}
void test_assign(int* c_int_p, pEp::POD<int>& pint)
{
{
pEpLogH2("assign operator");
int new_val = 23;
pint = new_val;
test_getters(c_int_p, pint, new_val);
}
{
pEpLogH2("raw c_str assign");
int new_val = 23;
*c_int_p = new_val;
test_getters(c_int_p, pint, new_val);
}
} }
// We are testing wrappers for the c-datatypes:
// * integral e.g (int) here
// * enum
// * c-string
// * struct
// Those are all considered POD-types
extern "C" { extern "C" {
typedef enum typedef enum
{ {
SOBER, ONE,
WHIPPY, TWO,
YIPPIE, THREE
HOORAY } Test_enum;
} Drunk_level;
typedef struct { typedef struct {
char* name; int c_int;
Drunk_level drk; Test_enum c_enum;
int btc_fanlevel; char* c_str;
} SchlossInsasse; } Test_struct;
} }
int main() int main()
{ {
// c-types are always POD
// we need to handle pointer types and value types differently
// pointer types need to be memory-managed (heap)
// value types are being copied around (stack)
static_assert(std::is_pod<int>::value && !std::is_pointer<int>::value, "not an integral");
static_assert(
std::is_pod<Test_enum>::value && !std::is_pointer<Test_enum>::value,
"not an integral");
static_assert(
std::is_pod<Test_struct>::value && !std::is_pointer<Test_struct>::value,
"not an integral");
// static_assert(std::is_pod<char*>::value && !std::is_pointer<char*>::value, "not an integral");
// pEp::Utils::readKey(); // pEp::Utils::readKey();
pEp::Adapter::pEpLog::set_enabled(true); pEp::Adapter::pEpLog::set_enabled(true);
// POD
if (1) {
// VALID USAGE
int init_val = 0;
// new pEp::POD on int
{
pEpLogH1("new pEp::POD on int");
int c_int = init_val;
pEp::POD<int> pint(&c_int);
test_getters(&c_int, pint, init_val);
test_assign(&c_int, pint);
}
}
SchlossInsasse tschappy; // String
tschappy.name = strdup("tschappy");
tschappy.drk = YIPPIE;
tschappy.btc_fanlevel = 100;
pEp::POD<::Drunk_level> penum(&tschappy.drk);
pEpLog(tschappy.drk);
pEpLog(penum);
penum = HOORAY;
pEpLog(tschappy.drk);
pEpLog(penum);
pEp::POD<int> pint(&tschappy.btc_fanlevel);
pEpLog(tschappy.btc_fanlevel);
pEpLog(pint);
pint = 33;
pEpLog(tschappy.btc_fanlevel);
pEpLog(pint);
// pEp::String::log_enabled = false; // pEp::String::log_enabled = false;
if (0) { if (0) {
//TODO: Test non-owning mode
// INVALID USAGE // INVALID USAGE
// char* c_str_u; // uninitialized == INVALID // char* c_str_u; // uninitialized == INVALID
// undefined behaviour, most likely "pointer being freed was not allocated" // undefined behaviour, most likely "pointer being freed was not allocated"
@ -547,29 +505,32 @@ int main()
pEpLogH1("new pEp::String on char* pointing to NULL"); pEpLogH1("new pEp::String on char* pointing to NULL");
char* c_str = NULL; // nullptr char* c_str = NULL; // nullptr
pEp::String pstr(&c_str); pEp::String pstr(true, &c_str);
test_assign_and_getters(&c_str, pstr); test_getters(&c_str, pstr, "");
test_assign(&c_str, pstr);
} }
std::string init_val{ "initialized c string" };
// new pEp::String on already initalized char* // new pEp::String on already initalized char*
{ {
pEpLogH1("new pEp::String on already initalized char*"); pEpLogH1("new pEp::String on already initalized char*");
char* c_str = strdup("initialized c string"); char* c_str = strdup(init_val.c_str());
pEp::String pstr(&c_str); pEp::String pstr(true, &c_str);
test_assign_and_getters(&c_str, pstr); test_getters(&c_str, pstr, init_val);
test_assign(&c_str, pstr);
} }
// initialize() // initialize()
{ {
pEpLogH1("initialize()"); pEpLogH1("init()");
pEp::String pstr{}; pEp::String pstr{};
char* c_str = strdup("initialized c string"); char* c_str = strdup(init_val.c_str());
//TODO: PITYASSERT_THROWS //TODO: PITYASSERT_THROWS
pstr.initialize(&c_str); pstr.init(true, &c_str);
test_getters(&c_str, pstr, init_val);
test_assign_and_getters(&c_str, pstr); test_assign(&c_str, pstr);
} }
} }
@ -580,16 +541,17 @@ int main()
// create identity // create identity
pEp::Identity id1{ "wrong@entry.lol", "wrong", "23", "INVA_FPR" }; pEp::Identity id1{ "wrong@entry.lol", "wrong", "23", "INVA_FPR" };
pEpLog(*id1); pEpLog(id1);
id1.username = "alice"; id1.username = "alice";
id1.address = "alice@peptest.org"; id1.address = "alice@peptest.org";
pEpLog(id1.address); pEpLog(id1.address);
pEpLog(id1.username); pEpLog(id1.username);
::myself(session, id1); ::myself(session, id1);
pEpLog(*id1); pEpLog(id1);
pEp::Identity id2{ "bob" }; pEp::Identity id2{ "bob" };
::update_identity(session, id2); ::update_identity(session, id2);
pEpLog(*id2); pEpLog(id2);
} }
} }

Loading…
Cancel
Save