diff --git a/test/test_nr1.cc b/test/test_nr1.cc index f9a75cd..03f323f 100644 --- a/test/test_nr1.cc +++ b/test/test_nr1.cc @@ -6,146 +6,37 @@ #include #include #include -#include +#include #include -//using namespace pEp; namespace pEp { - // type ---------------------------------------- - // same for pointer and value typee - template - std::string type_str(const T) - { - std::stringstream ss_type; - ss_type << typeid(T).name(); - return ss_type.str(); - } - - - // addr ---------------------------------------- - template - struct addr_str_helper { - static std::string str(const T c) - { - std::stringstream ss_addr{}; - ss_addr << static_cast(&c); - return ss_addr.str(); - } - }; - - template - struct addr_str_helper { - static std::string str(const T* c) - { - std::stringstream ss_addr{}; - ss_addr << static_cast(c); - return ss_addr.str(); - } - }; - - // rertuns the address of c - template - std::string addr_str(T c) - { - return addr_str_helper::str(c); - } - - // val ---------------------------------------- - template - 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 - struct val_str_helper { - 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 << ""; - } - - 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 - std::string val_str(T c, size_t val_len = 30) - { - return val_str_helper::str(c, val_len); - } - - // type_addr_val ---------------------------------------- - template - 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 - 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 - std::string type_addr_val(T c, size_t val_len = 30) - { - return type_addr_val_helper::str(c, val_len); - } - -} // namespace pEp - -namespace pEp { - - char* alloc(const std::string& str) + char* alloc_str(const std::string& str) { char* ret = strdup(str.c_str()); - pEpLog(type_addr_val(ret)); + pEpLog(CXX::Inspect::all(ret)); return ret; } template void free(T ptr_type) { - pEpLog(type_addr_val(ptr_type)); + pEpLog(CXX::Inspect::all(ptr_type)); ::pEp_free(ptr_type); } template<> void free(char* ptr_type) { - pEpLog(type_addr_val(ptr_type)); + pEpLog(CXX::Inspect::all(ptr_type)); ::pEp_free(ptr_type); } template<> void free(::pEp_identity* ptr_type) { - pEpLog(type_addr_val(ptr_type)); + pEpLog(CXX::Inspect::all(ptr_type)); ::pEp_free(ptr_type); } @@ -153,20 +44,9 @@ namespace pEp { //--------------------------------------------------------------------------------------------- #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 POD : public PODBase { - static_assert(std::is_pod::value, "only POD types are allowed"); + class POD { + static_assert(std::is_pod::value && !std::is_pointer::value, "not an integral"); public: POD() = delete; @@ -174,10 +54,8 @@ namespace pEp { explicit POD(T* pod_p) { 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; pEpLogClass(to_string() + " - taking ownership"); } @@ -185,8 +63,7 @@ namespace pEp { // make a copy 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; pEpLogClass("After: " + to_string()); return *this; @@ -212,21 +89,26 @@ namespace pEp { std::string to_string() const { - std::string ret{ "[" + type_addr_val(_pod_p) + "]" }; + std::string ret{ "[" + CXX::Inspect::all(_pod_p) + "]" }; return ret; } + static bool log_enabled; + private: - bool _is_initialized{ false }; 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 { public: explicit Exception(const std::string& msg) : std::runtime_error(msg) {} }; }; - bool pEp::PODBase::log_enabled{ true }; + template + bool pEp::POD::log_enabled{ true }; template std::ostream& operator<<(std::ostream& o, pEp::POD pEpEnum) @@ -245,28 +127,43 @@ namespace pEp { // * dont take ownership, only provide a C++ interface // 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 - // set(). Otherwise all(most) functions will throw. - // set() can only be called once, will throw otherwise. + // init(). Otherwise all(most) functions will throw. + // 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 { public: String() = default; // Best to use this constructor, as the object is in a valid state when the wrapped // 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() { - _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) { - throw Exception{ EXSTR("cant initialize on a nullptr") }; + throw Exception{ EXSTR("cant init on a nullptr") }; } if (_is_initialized) { @@ -277,7 +174,8 @@ namespace pEp { _c_str_pp = c_str_pp; _c_str_p = *_c_str_pp; _is_initialized = true; - pEpLogClass(to_string() + " - taking ownership"); + _is_owning_mode = take_ownership; + pEpLogClass(to_string() + " - init " + (take_ownership ? "/taking ownership" : "")); } // make a copy @@ -297,7 +195,7 @@ namespace pEp { *_c_str_pp = nullptr; _c_str_p = *_c_str_pp; } else { - *_c_str_pp = pEp::alloc(str); + *_c_str_pp = pEp::alloc_str(str); _c_str_p = *_c_str_pp; } pEpLogClass("After: " + to_string()); @@ -336,14 +234,15 @@ namespace pEp { 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; } static bool log_enabled; private: + // TODO: this is dodgy, do we really need _c_str_pp AND _c_str_p??? void _free() { // 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"); pEp::free(*_c_str_pp); } else { - // we anyways need to free the one we created - // NO: WE ASSUME THAT: + // WE ASSUME THAT: // if the char* has been replaced, it has been freed, as well if (_c_str_p != nullptr) { pEp::free(_c_str_p); @@ -362,6 +260,7 @@ namespace pEp { } bool _is_initialized{ false }; + bool _is_owning_mode{ false }; char** _c_str_pp{ nullptr }; char* _c_str_p{ nullptr }; @@ -397,16 +296,10 @@ namespace pEp { _wrappee = ::new_identity(nullptr, nullptr, nullptr, nullptr); // set the pEp::String wrapper underlying c_str - this->address.initialize(&_wrappee->address); - this->username.initialize(&_wrappee->username); - this->user_id.initialize(&_wrappee->user_id); - this->fpr.initialize(&_wrappee->fpr); - - // set the values - this->address = address; - this->username = username; - this->user_id = user_id; - this->fpr = fpr; + this->address.init(true, &_wrappee->address, address); + this->username.init(true, &_wrappee->username, username); + this->user_id.init(true, &_wrappee->user_id, user_id); + this->fpr.init(true, &_wrappee->fpr, fpr); } ~Identity() @@ -445,25 +338,28 @@ namespace pEp { ::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()); + // compare addresses + pEpLog("addresses == c_str_p"); assert(pstr.c_data() == *c_str_p); assert(pstr.data() == c_str_p); + // compare values if (*c_str_p != nullptr) { - std::string tmp{ pstr }; - pEpLog("operator std::string(): " + tmp); - assert(tmp == std::string(*c_str_p)); + pEpLog("operator std::string(): " + std::string(pstr)); + assert(std::string(pstr) == std::string(*c_str_p)); + assert(std::string(pstr) == expected); // will segfault with nullptr, and this is correct - std::string tmp2{ *pstr.data() }; - pEpLog("data(): " + tmp2); - assert(tmp2 == std::string(*c_str_p)); + pEpLog("data(): " + std::string(*pstr.data())); + assert(std::string(*pstr.data()) == std::string(*c_str_p)); + assert(std::string(*pstr.data()) == expected); - std::string tmp3{ pstr.c_data() }; - pEpLog("c_data(): " + tmp2); - assert(tmp3 == std::string(*c_str_p)); + pEpLog("c_data(): " + std::string(pstr.c_data())); + assert(std::string(pstr.c_data()) == std::string(*c_str_p)); + assert(std::string(pstr.c_data()) == expected); } else { std::string tmp{ pstr }; 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"); - pstr = "assign operator"; - test_getters(c_str_p, pstr); - - pEpLogH2("raw c_str assign"); - *c_str_p = strdup("raw c_str assign"); - test_getters(c_str_p, pstr); + { + pEpLogH2("assign operator"); + std::string new_val{ "assign operator" }; + pstr = new_val; + test_getters(c_str_p, pstr, new_val); + } + { + 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& 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& 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" { typedef enum { - SOBER, - WHIPPY, - YIPPIE, - HOORAY - } Drunk_level; + ONE, + TWO, + THREE + } Test_enum; typedef struct { - char* name; - Drunk_level drk; - int btc_fanlevel; - } SchlossInsasse; + int c_int; + Test_enum c_enum; + char* c_str; + } Test_struct; } 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::value && !std::is_pointer::value, "not an integral"); + static_assert( + std::is_pod::value && !std::is_pointer::value, + "not an integral"); + static_assert( + std::is_pod::value && !std::is_pointer::value, + "not an integral"); + + // static_assert(std::is_pod::value && !std::is_pointer::value, "not an integral"); + // pEp::Utils::readKey(); 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 pint(&c_int); + test_getters(&c_int, pint, init_val); + test_assign(&c_int, pint); + } + } - SchlossInsasse tschappy; - 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 pint(&tschappy.btc_fanlevel); - pEpLog(tschappy.btc_fanlevel); - pEpLog(pint); - pint = 33; - pEpLog(tschappy.btc_fanlevel); - pEpLog(pint); - - + // String // pEp::String::log_enabled = false; if (0) { + //TODO: Test non-owning mode + // INVALID USAGE // char* c_str_u; // uninitialized == INVALID // 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"); char* c_str = NULL; // nullptr - pEp::String pstr(&c_str); - test_assign_and_getters(&c_str, pstr); + pEp::String pstr(true, &c_str); + test_getters(&c_str, pstr, ""); + test_assign(&c_str, pstr); } + std::string init_val{ "initialized c string" }; // 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); - test_assign_and_getters(&c_str, pstr); + pEp::String pstr(true, &c_str); + test_getters(&c_str, pstr, init_val); + test_assign(&c_str, pstr); } // initialize() { - pEpLogH1("initialize()"); + pEpLogH1("init()"); pEp::String pstr{}; - char* c_str = strdup("initialized c string"); + char* c_str = strdup(init_val.c_str()); //TODO: PITYASSERT_THROWS - pstr.initialize(&c_str); - - test_assign_and_getters(&c_str, pstr); + pstr.init(true, &c_str); + test_getters(&c_str, pstr, init_val); + test_assign(&c_str, pstr); } } @@ -580,16 +541,17 @@ int main() // create identity pEp::Identity id1{ "wrong@entry.lol", "wrong", "23", "INVA_FPR" }; - pEpLog(*id1); + pEpLog(id1); id1.username = "alice"; id1.address = "alice@peptest.org"; + pEpLog(id1.address); pEpLog(id1.username); ::myself(session, id1); - pEpLog(*id1); + pEpLog(id1); pEp::Identity id2{ "bob" }; ::update_identity(session, id2); - pEpLog(*id2); + pEpLog(id2); } }