Browse Source

add .clang-format / reformat

If you dont like reformatting
of complete codebases, the solution is to USE this.
master
heck 3 years ago
parent
commit
eaa77767a7
  1. 45
      .clang-format
  2. 23
      src/bloblist.cc
  3. 114
      src/bloblist.hh
  4. 36
      src/crlf.cc
  5. 3
      src/crlf.hh
  6. 48
      src/identity.cc
  7. 42
      src/stringlist.cc
  8. 87
      src/types.cc
  9. 14
      src/types.hh
  10. 404
      src/wrapper.hh
  11. 32
      test/unittest_identity.cc
  12. 239
      test/unittest_message.cc
  13. 134
      test/unittest_nfc.cc
  14. 106
      test/unittest_nfc16.cc
  15. 101
      test/unittest_nfcstring.cc
  16. 64
      test/unittest_stringlist.cc
  17. 60
      test/unittest_stringpair.cc

45
.clang-format

@ -0,0 +1,45 @@
BasedOnStyle: LLVM
Language: Cpp
Standard: c++17
DerivePointerAlignment: true
SortIncludes: Never
ReflowComments: false
PointerAlignment: Left
AlignAfterOpenBracket: AlwaysBreak
AlignOperands: AlignAfterOperator
BreakConstructorInitializers: AfterColon
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
ExperimentalAutoDetectBinPacking: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
ColumnLimit: 100
AllowAllConstructorInitializersOnNextLine: false
#BreakConstructorInitializersBeforeComma: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
PenaltyBreakBeforeFirstCallParameter: 0
PenaltyReturnTypeOnItsOwnLine: 1000000
PenaltyBreakAssignment: 1000000
PenaltyExcessCharacter: 10
IndentCaseLabels: true
IndentWidth: 4
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
SpaceAfterTemplateKeyword: false
AccessModifierOffset: -4
AllowShortBlocksOnASingleLine: Always
IndentPPDirectives: BeforeHash
IndentExternBlock: Indent
Cpp11BracedListStyle: false
BreakStringLiterals: false

23
src/bloblist.cc

@ -2,44 +2,43 @@
#include <pEp/bloblist.h> #include <pEp/bloblist.h>
namespace pEp namespace pEp {
{
template<> template<>
void Wrapper<::bloblist_t*>::_free(::bloblist_t* bl) void Wrapper<::bloblist_t*>::_free(::bloblist_t* bl)
{ {
::free_bloblist(bl); ::free_bloblist(bl);
} }
// template<>
// template<>
int BlobList::size() const int BlobList::size() const
{ {
return bloblist_length(value); return bloblist_length(value);
} }
// faster than .size()==0 because it's not necessary to iterate throgh the whole list // faster than .size()==0 because it's not necessary to iterate throgh the whole list
// template<> // template<>
bool BlobList::empty() const bool BlobList::empty() const
{ {
return !(value && value->value); return !(value && value->value);
} }
// template<> // template<>
void BlobList::clear() void BlobList::clear()
{ {
free_bloblist(value); free_bloblist(value);
value = nullptr; value = nullptr;
} }
// template<> // template<>
void BlobList::push_back(Blob&& s) void BlobList::push_back(Blob&& s)
{ {
// TODO // TODO
} }
//////////////// ////////////////
// no explicit instantiation noecessary, because of fully specialized class template. Okay. :-D // no explicit instantiation noecessary, because of fully specialized class template. Okay. :-D
// template class ListWrapper<::bloblist_t*, void>; // template class ListWrapper<::bloblist_t*, void>;
} // end of namespace pEp } // end of namespace pEp

114
src/bloblist.hh

@ -7,55 +7,79 @@
#include "wrapper.hh" #include "wrapper.hh"
#include <pEp/bloblist.h> #include <pEp/bloblist.h>
namespace pEp namespace pEp {
{
template<> template<>
class ListWrapper<::bloblist_t*, void> : public Wrapper<::bloblist_t*> class ListWrapper<::bloblist_t*, void> : public Wrapper<::bloblist_t*> {
{
public:
typedef ::bloblist_t Blob;
typedef Wrapper<Blob*> Base;
typedef ListWrapper<Blob*, void> LW;
// does not own the *value
class iterator
{
public: public:
iterator() = default; typedef ::bloblist_t Blob;
typedef Wrapper<Blob*> Base;
iterator operator++() { return (value ? value = value->next : value); } typedef ListWrapper<Blob*, void> LW;
Blob& operator*() { return *value; }
Blob* operator->() { return value; } // does not own the *value
const Blob& operator*() const { return *value; } class iterator {
const Blob* operator->() const { return value; } public:
bool operator==(const iterator& other) const { return value == other.value; } iterator() = default;
bool operator!=(const iterator& other) const { return value != other.value; }
iterator operator++()
private: {
iterator(::bloblist_t* _t) : value{_t} {} return (value ? value = value->next : value);
::bloblist_t* value = nullptr; }
friend class ListWrapper<::bloblist_t*, void>; Blob& operator*()
{
return *value;
}
Blob* operator->()
{
return value;
}
const Blob& operator*() const
{
return *value;
}
const Blob* operator->() const
{
return value;
}
bool operator==(const iterator& other) const
{
return value == other.value;
}
bool operator!=(const iterator& other) const
{
return value != other.value;
}
private:
iterator(::bloblist_t* _t) : value{ _t } {}
::bloblist_t* value = nullptr;
friend class ListWrapper<::bloblist_t*, void>;
};
using Base::value;
ListWrapper() : Base() {}
iterator begin()
{
return iterator{ value };
}
iterator end() const
{
return iterator{};
}
int size() const;
bool empty() const;
void clear();
void push_back(Blob&&);
void emplace_back(char* data, size_t size, const char* mime_type, const char* filename);
}; };
using BlobList = ListWrapper<::bloblist_t*, void>;
using Base::value;
ListWrapper() : Base() {}
iterator begin() { return iterator{value}; }
iterator end() const { return iterator{}; }
int size() const;
bool empty() const;
void clear();
void push_back(Blob&&);
void emplace_back(char* data, size_t size, const char* mime_type, const char* filename);
};
using BlobList = ListWrapper<::bloblist_t*, void>;
} // end of namespace pEp } // end of namespace pEp

36
src/crlf.cc

@ -1,26 +1,24 @@
#include "crlf.hh" #include "crlf.hh"
namespace pEp namespace pEp {
{
std::string operator""_CRLF(const char* str, size_t length) std::string operator""_CRLF(const char* str, size_t length)
{
static const std::string CRLF{"\r\n"};
std::string ret;
ret.reserve(length + ((length+29)/30) + 2 ); // rough guess for average line length of 30.
const char* end = str + length;
// N.B.: Loop could be more optimized, but not necessary because it is only used for string literals.
for(; str != end; ++str)
{ {
if(*str == '\n') static const std::string CRLF{ "\r\n" };
ret += CRLF;
else std::string ret;
ret += *str; ret.reserve(length + ((length + 29) / 30) + 2); // rough guess for average line length of 30.
const char* end = str + length;
// N.B.: Loop could be more optimized, but not necessary because it is only used for string literals.
for (; str != end; ++str) {
if (*str == '\n')
ret += CRLF;
else
ret += *str;
}
return ret;
} }
return ret;
}
} // end of namespace pEp } // end of namespace pEp

3
src/crlf.hh

@ -6,8 +6,7 @@
#include <string> #include <string>
namespace pEp namespace pEp {
{
// creates a string where \n ("linefeed" a.k.a. LF) are replaced // creates a string where \n ("linefeed" a.k.a. LF) are replaced
// by \r\n ("carriage return + linefeed" a.k.a. CRLF). // by \r\n ("carriage return + linefeed" a.k.a. CRLF).
// Useful to define strings in NET-ASCII or Net-Unicode (RFC5198) format // Useful to define strings in NET-ASCII or Net-Unicode (RFC5198) format

48
src/identity.cc

@ -4,15 +4,14 @@
#include <string> #include <string>
namespace pEp namespace pEp {
{
template<> template<>
template<> template<>
::pEp_identity* Wrapper<::pEp_identity*>::_new<const char*, const char*, const char*, const char*> ::pEp_identity* Wrapper<::pEp_identity*>::_new<const char*, const char*, const char*, const char*>(
( const char* address,
const char *address, const char *fpr, const char *user_id, const char* fpr,
const char *username const char* user_id,
) const char* username)
{ {
return ::new_identity(address, fpr, user_id, username); return ::new_identity(address, fpr, user_id, username);
} }
@ -22,14 +21,14 @@ namespace pEp
{ {
::free_identity(id); ::free_identity(id);
} }
template<> template<>
::pEp_identity* Wrapper<::pEp_identity*>::copy_out() const ::pEp_identity* Wrapper<::pEp_identity*>::copy_out() const
{ {
return identity_dup(value); return identity_dup(value);
} }
////////////// //////////////
template<> template<>
void Wrapper<::identity_list*>::_free(::identity_list* sl) void Wrapper<::identity_list*>::_free(::identity_list* sl)
@ -44,8 +43,8 @@ namespace pEp
} }
template<> template<>
::pEp_identity* identity_list::* const ListWrapper<::identity_list*, ::pEp_identity*>::Value ::pEp_identity* identity_list::*const
= &identity_list::ident; ListWrapper<::identity_list*, ::pEp_identity*>::Value = &identity_list::ident;
template<> template<>
int IdentityList::size() const int IdentityList::size() const
@ -59,41 +58,40 @@ namespace pEp
{ {
return !(value && value->ident); return !(value && value->ident);
} }
template<> template<>
void IdentityList::clear() void IdentityList::clear()
{ {
free_identity_list(value); free_identity_list(value);
value = nullptr; value = nullptr;
} }
template<> template<>
void IdentityList::push_back(pEp_identity*&& id) void IdentityList::push_back(pEp_identity*&& id)
{ {
auto last = identity_list_add(value, id); auto last = identity_list_add(value, id);
if(value==nullptr) if (value == nullptr)
value = last; value = last;
} }
template<> template<>
ListWrapper<::identity_list*, pEp_identity*>::ListWrapper(const std::initializer_list<Wrapper<pEp_identity*>>& il) ListWrapper<::identity_list*, pEp_identity*>::ListWrapper(
: Base{} const std::initializer_list<Wrapper<pEp_identity*>>& il) :
Base{}
{ {
::identity_list* last = nullptr; ::identity_list* last = nullptr;
for(const Wrapper<pEp_identity*>& id : il) for (const Wrapper<pEp_identity*>& id : il) {
{
last = identity_list_add(last, identity_dup(id.get())); last = identity_list_add(last, identity_dup(id.get()));
if(last==nullptr) if (last == nullptr) {
{
throw std::runtime_error("Cannot create StringPairList from {}: Out Of Memory."); throw std::runtime_error("Cannot create StringPairList from {}: Out Of Memory.");
} }
if(value==nullptr) if (value == nullptr)
value = last; // save the head of linked list. value = last; // save the head of linked list.
} }
} }
//////////////// ////////////////
template class ListWrapper<::identity_list*, ::pEp_identity*>; template class ListWrapper<::identity_list*, ::pEp_identity*>;

42
src/stringlist.cc

@ -5,17 +5,16 @@
#include <string> #include <string>
namespace pEp namespace pEp {
{
template<> template<>
void Wrapper<::stringlist_t*>::_free(::stringlist_t* sl) void Wrapper<::stringlist_t*>::_free(::stringlist_t* sl)
{ {
::free_stringlist(sl); ::free_stringlist(sl);
} }
template<> template<>
const char* stringlist_t::* const ListWrapper<stringlist_t*, const char*>::Value const char* stringlist_t::*const ListWrapper<stringlist_t*, const char*>::Value =
= const_cast<const char* stringlist_t::*>(&stringlist_t::value); const_cast<const char * stringlist_t::*>(&stringlist_t::value);
template<> template<>
int StringList::size() const int StringList::size() const
@ -29,50 +28,47 @@ namespace pEp
{ {
return !(value && value->value); return !(value && value->value);
} }
template<> template<>
void StringList::erase( const StringList::iterator& it) void StringList::erase(const StringList::iterator& it)
{ {
if(it.value && it.value->value) if (it.value && it.value->value) {
{
value = stringlist_delete(value, it.value->value); value = stringlist_delete(value, it.value->value);
} }
} }
template<> template<>
void StringList::clear() void StringList::clear()
{ {
free_stringlist(value); free_stringlist(value);
value = nullptr; value = nullptr;
} }
template<> template<>
void StringList::push_back(const char*&& s) void StringList::push_back(const char*&& s)
{ {
auto last = stringlist_add(value, s); auto last = stringlist_add(value, s);
if(value==nullptr) if (value == nullptr)
value = last; value = last;
} }
template<> template<>
ListWrapper<::stringlist_t*, const char*>::ListWrapper(const std::initializer_list<const char*>& il) ListWrapper<::stringlist_t*, const char*>::ListWrapper(const std::initializer_list<const char*>& il) :
: StringList{} StringList{}
{ {
::stringlist_t* last = nullptr; ::stringlist_t* last = nullptr;
for(const char* s : il) for (const char* s : il) {
{
last = stringlist_add(last, s); last = stringlist_add(last, s);
if(last==nullptr) if (last == nullptr) {
{
throw std::runtime_error("Cannot create StringPairList from {}: Out Of Memory."); throw std::runtime_error("Cannot create StringPairList from {}: Out Of Memory.");
} }
if(value==nullptr) if (value == nullptr)
value = last; // save the head of linked list. value = last; // save the head of linked list.
} }
} }
//////////////// ////////////////
template class ListWrapper<::stringlist_t*, const char*>; template class ListWrapper<::stringlist_t*, const char*>;

87
src/types.cc

@ -13,31 +13,31 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
namespace pEp namespace pEp {
{ EngineError::EngineError(PEP_STATUS status, const char* message) :
EngineError::EngineError(PEP_STATUS status, const char* message) std::runtime_error(
: std::runtime_error( std::string{ "EngineError: " } +
std::string{"EngineError: "} (message ? '"' + std::string{ message } + "\" " : std::string{}) +
+ (message ? '"' + std::string{message} + "\" " : std::string{} ) status_to_string(status))
+ status_to_string(status) {
) }
{}
//////////////// ////////////////
template<> template<>
template<> template<>
message* Wrapper<::message*>::_new<PEP_msg_direction, const char*>(PEP_msg_direction dir, const char* src) message* Wrapper<::message*>::_new<PEP_msg_direction, const char*>(
PEP_msg_direction dir,
const char* src)
{ {
message* m = nullptr; message* m = nullptr;
bool pep_msg = false; bool pep_msg = false;
PEP_STATUS status = mime_decode_message(src, strlen(src), &m, &pep_msg); PEP_STATUS status = mime_decode_message(src, strlen(src), &m, &pep_msg);
if(status != PEP_STATUS_OK) if (status != PEP_STATUS_OK) {
{
throw EngineError(status, "mime_decode_message()"); throw EngineError(status, "mime_decode_message()");
} }
m->dir = dir; m->dir = dir;
return m; return m;
} }
@ -54,30 +54,28 @@ namespace pEp
return ::message_dup(value); return ::message_dup(value);
} }
//////////////// ////////////////
template<> template<>
template<> template<>
::stringpair_t* Wrapper<::stringpair_t*>::_new(const char* key, const char* value) ::stringpair_t* Wrapper<::stringpair_t*>::_new(const char* key, const char* value)
{ {
stringpair_t* sp = new_stringpair(key, value); stringpair_t* sp = new_stringpair(key, value);
if(!sp) if (!sp) {
{
throw EngineError(PEP_OUT_OF_MEMORY, "new_stringpair()"); throw EngineError(PEP_OUT_OF_MEMORY, "new_stringpair()");
} }
return sp; return sp;
} }
template<> template<>
template<> template<>
::stringpair_t* Wrapper<::stringpair_t*>::_new(char* key, char* value) ::stringpair_t* Wrapper<::stringpair_t*>::_new(char* key, char* value)
{ {
return _new<const char*, const char*>( return _new<const char*, const char*>(
const_cast<const char*>(key), const_cast<const char*>(key),
const_cast<const char*>(value) const_cast<const char*>(value));
);
} }
template<> template<>
template<> template<>
::stringpair_t* Wrapper<::stringpair_t*>::_new(const std::string& key, const std::string& value) ::stringpair_t* Wrapper<::stringpair_t*>::_new(const std::string& key, const std::string& value)
@ -102,9 +100,10 @@ namespace pEp
{ {
free_stringpair_list(spl); free_stringpair_list(spl);
} }
template<> template<>
stringpair_t* stringpair_list_t::* const ListWrapper<stringpair_list_t*, stringpair_t*>::Value = &stringpair_list_t::value; stringpair_t* stringpair_list_t::*const
ListWrapper<stringpair_list_t*, stringpair_t*>::Value = &stringpair_list_t::value;
template<> template<>
int StringPairList::size() const int StringPairList::size() const
@ -118,59 +117,57 @@ namespace pEp
{ {
return !(value && value->value); return !(value && value->value);
} }
template<> template<>
void StringPairList::erase( const StringPairList::iterator& it) void StringPairList::erase(const StringPairList::iterator& it)
{ {
if(it.value && it.value->value && it.value->value->key) if (it.value && it.value->value && it.value->value->key) {
{
value = stringpair_list_delete_by_key(value, it.value->value->key); value = stringpair_list_delete_by_key(value, it.value->value->key);
} }
} }
template<> template<>
void StringPairList::clear() void StringPairList::clear()
{ {
free_stringpair_list(value); free_stringpair_list(value);
value = nullptr; value = nullptr;
} }
template<> template<>
void StringPairList::push_back(::stringpair_t*&& sp) void StringPairList::push_back(::stringpair_t*&& sp)
{ {
auto last = stringpair_list_add(value, sp); auto last = stringpair_list_add(value, sp);
if(value==nullptr) if (value == nullptr)
value = last; value = last;
sp = nullptr; sp = nullptr;
} }
template<> template<>
void StringPairList::push_back(StringPair&& sp) void StringPairList::push_back(StringPair&& sp)
{ {
auto last = stringpair_list_add(value, sp.move_out()); auto last = stringpair_list_add(value, sp.move_out());
if(value==nullptr) if (value == nullptr)
value = last; value = last;
} }
template<> template<>
ListWrapper<::stringpair_list_t*, stringpair_t*>::ListWrapper(const std::initializer_list<StringPair>& il) ListWrapper<::stringpair_list_t*, stringpair_t*>::ListWrapper(
: StringPairList{} const std::initializer_list<StringPair>& il) :
StringPairList{}
{ {
::stringpair_list_t* last = nullptr; ::stringpair_list_t* last = nullptr;
for(const StringPair& sp : il) for (const StringPair& sp : il) {
{
last = stringpair_list_add(last, stringpair_dup(sp.get())); last = stringpair_list_add(last, stringpair_dup(sp.get()));
if(last==nullptr) if (last == nullptr) {
{
throw std::runtime_error("Cannot create StringPairList from {}: Out Of Memory."); throw std::runtime_error("Cannot create StringPairList from {}: Out Of Memory.");
} }
if(value==nullptr) if (value == nullptr)
value = last; // save the head of linked list. value = last; // save the head of linked list.
} }
} }
//////////////// ////////////////
template class Wrapper<::pEp_identity*>; template class Wrapper<::pEp_identity*>;
template class Wrapper<::stringpair_t*>; template class Wrapper<::stringpair_t*>;

14
src/types.hh

@ -12,23 +12,21 @@
#include <pEp/stringpair.h> #include <pEp/stringpair.h>
#include <pEp/message.h> #include <pEp/message.h>
namespace pEp namespace pEp {
{ class EngineError : std::runtime_error {
class EngineError : std::runtime_error
{
public: public:
EngineError(PEP_STATUS status, const char* message = nullptr); EngineError(PEP_STATUS status, const char* message = nullptr);
}; };
using Identity = Wrapper<::pEp_identity*>; using Identity = Wrapper<::pEp_identity*>;
using IdentityList = ListWrapper<::identity_list*, ::pEp_identity*>; using IdentityList = ListWrapper<::identity_list*, ::pEp_identity*>;
using StringPair = Wrapper<::stringpair_t*>; using StringPair = Wrapper<::stringpair_t*>;
using StringPairList = ListWrapper<::stringpair_list_t*, ::stringpair_t*>; using StringPairList = ListWrapper<::stringpair_list_t*, ::stringpair_t*>;
using StringList = ListWrapper<::stringlist_t*, const char*>; using StringList = ListWrapper<::stringlist_t*, const char*>;
using BlobList = ListWrapper<::bloblist_t*, void>; using BlobList = ListWrapper<::bloblist_t*, void>;
using Message = Wrapper<::message*>; using Message = Wrapper<::message*>;
} // end of namespace pEp } // end of namespace pEp

404
src/wrapper.hh

@ -7,193 +7,231 @@
#include <initializer_list> #include <initializer_list>
#include <iterator> #include <iterator>
namespace pEp namespace pEp {
{
/// A generalized wrapper around pEpEngine's datatypes.
/// A generalized wrapper around pEpEngine's datatypes. template<class T>
template<class T> class Wrapper {
class Wrapper
{
public:
typedef T c_type;
template<class... Args>
Wrapper(Args... args) : value{ this->_new(args...) } {}
// no implicit copying... (yet?)
Wrapper(const Wrapper<T>&) = delete;
void operator=(const Wrapper<T>&) = delete;
// must be implemented separately for each T
Wrapper(Wrapper<T>&& victim);
Wrapper<T>& operator=(Wrapper<T>&& victim);
~Wrapper();
Wrapper<T> copy() const;
bool operator==(const Wrapper<T>& b) const
{
return value==b.value;
}
bool operator!=(const Wrapper<T>& b) const
{
return value!=b.value;
}
private:
// must be defined for each wrapped type:
template<class... Args>
T _new(Args...);
T value;
};
// many wrapped datatypes are pointers, we can generalize a lot for them:
template<class T>
class Wrapper<T*>
{
public:
typedef T* c_type;
Wrapper() : value{nullptr} {}
template<class... Args>
Wrapper(Args... args) : value{ this->_new(args...) } {}
// move is easy, efficient and generic:
Wrapper(Wrapper<T*>&& victim) noexcept
: value{ victim.value}
{
victim.value = nullptr;
}
Wrapper<T*>& operator=(Wrapper<T*>&& victim) noexcept
{
_free(value);
value = victim.value;
victim.value = nullptr;
return *this;
}
Wrapper(const Wrapper<T*>& orig)
: value{ orig.copy_out() }
{}
Wrapper<T*>& operator=(const Wrapper<T*>& orig)
{
if(&orig == this) return *this;
_free(value);
value = orig.copy_out();
return *this;
}
~Wrapper()
{
_free(value);
}
bool operator==(const Wrapper<T*>& b) const
{
return value==b.value;
}
bool operator!=(const Wrapper<T*>& b) const
{
return value!=b.value;
}
// Get read-only access to the value itself
// Beware: 'const' is not transitive in C, so the 2nd indirect data
// allows r/w access!
const T* operator->() const { return value; }
const T* get() const { return value; }
// Dangerous: Get R/W access to the value!
T* operator->() { return value; }
T* get() { return value;}
// Releases ownership of the value. Wrapper becomes valueless.
T* move_out() { T* r = value; value=nullptr; return r;}
// only implemented for the datatypes where necessay.
// other implementations can follow if necessary.
T* copy_out() const;
protected:
Wrapper(T* _value) : value{_value} {}
// must be defined for each wrapped type:
template<class... Args>
T* _new(Args...);
void _free(T*);
T* value;
};
// Wraps single-linked lists and provides an interface compatible
// to std::forward_list
template<class T, class Element>
class ListWrapper;
template<class T, class Element>
class ListWrapper<T*, Element> : public Wrapper<T*>
{
public:
typedef Wrapper<T*> Base;
typedef ListWrapper<T*, Element> LW;
static Element T::* const Value; // to access the current value
// does not own the *value
class iterator : public std::iterator< std::forward_iterator_tag, Element, ptrdiff_t>
{
public: public:
typedef T c_type;
iterator() = default;
template<class... Args>
iterator operator++() { return (value ? value = value->next : value); } Wrapper(Args... args) : value{ this->_new(args...) }
Element operator*() { return value->*LW::Value; } {
Element operator->() { return value->*LW::Value; } }
bool operator==(const iterator& other) const { return value == other.value; }
bool operator!=(const iterator& other) const { return value != other.value; } // no implicit copying... (yet?)
Wrapper(const Wrapper<T>&) = delete;
void operator=(const Wrapper<T>&) = delete;
// must be implemented separately for each T
Wrapper(Wrapper<T>&& victim);
Wrapper<T>& operator=(Wrapper<T>&& victim);
~Wrapper();
Wrapper<T> copy() const;
bool operator==(const Wrapper<T>& b) const
{
return value == b.value;
}
bool operator!=(const Wrapper<T>& b) const
{
return value != b.value;
}
private: private:
iterator(T* _t) : value{_t} {} // must be defined for each wrapped type:
T* value = nullptr; template<class... Args>
friend class ListWrapper<T*, Element>; T _new(Args...);
T value;
};
// many wrapped datatypes are pointers, we can generalize a lot for them:
template<class T>
class Wrapper<T*> {
public:
typedef T* c_type;
Wrapper() : value{ nullptr } {}
template<class... Args>
Wrapper(Args... args) : value{ this->_new(args...) }
{
}
// move is easy, efficient and generic:
Wrapper(Wrapper<T*>&& victim) noexcept : value{ victim.value }
{
victim.value = nullptr;
}
Wrapper<T*>& operator=(Wrapper<T*>&& victim) noexcept
{
_free(value);
value = victim.value;
victim.value = nullptr;
return *this;
}
Wrapper(const Wrapper<T*>& orig) : value{ orig.copy_out() } {}
Wrapper<T*>& operator=(const Wrapper<T*>& orig)
{
if (&orig == this)
return *this;
_free(value);
value = orig.copy_out();
return *this;
}
~Wrapper()
{
_free(value);
}
bool operator==(const Wrapper<T*>& b) const
{
return value == b.value;
}
bool operator!=(const Wrapper<T*>& b) const
{
return value != b.value;
}
// Get read-only access to the value itself
// Beware: 'const' is not transitive in C, so the 2nd indirect data
// allows r/w access!
const T* operator->() const
{
return value;
}
const T* get() const
{
return value;
}
// Dangerous: Get R/W access to the value!
T* operator->()
{
return value;
}
T* get()
{
return value;
}
// Releases ownership of the value. Wrapper becomes valueless.
T* move_out()
{
T* r = value;
value = nullptr;
return r;
}
// only implemented for the datatypes where necessay.
// other implementations can follow if necessary.
T* copy_out() const;
protected:
Wrapper(T* _value) : value{ _value } {}
// must be defined for each wrapped type:
template<class... Args>
T* _new(Args...);
void _free(T*);
T* value;
};
// Wraps single-linked lists and provides an interface compatible
// to std::forward_list
template<class T, class Element>
class ListWrapper;
template<class T, class Element>
class ListWrapper<T*, Element> : public Wrapper<T*> {
public:
typedef Wrapper<T*> Base;
typedef ListWrapper<T*, Element> LW;
static Element T::*const Value; // to access the current value
// does not own the *value
class iterator : public std::iterator<std::forward_iterator_tag, Element, ptrdiff_t> {
public:
iterator() = default;
iterator operator++()
{
return (value ? value = value->next : value);
}
Element operator*()
{
return value->*LW::Value;
}
Element operator->()
{
return value->*LW::Value;
}
bool operator==(const iterator& other) const
{
return value == other.value;
}
bool operator!=(const iterator& other) const
{
return value != other.value;
}
private:
iterator(T* _t) : value{ _t } {}
T* value = nullptr;
friend class ListWrapper<T*, Element>;
};
typedef const iterator const_iterator;
using Base::value;
ListWrapper() : Base() {}
ListWrapper(const std::initializer_list<pEp::Wrapper<Element>>& i);
ListWrapper(const std::initializer_list<Element>& i);
iterator begin()
{
return iterator{ value };
}
iterator end() const
{
return iterator{};
}
const_iterator cbegin() const
{
return const_iterator{ value };
}
const_iterator cend() const
{
return const_iterator{};
}
int size() const;
bool empty() const;
void erase(const iterator& it);
void clear();
void push_back(Element&&);
void push_back(Wrapper<Element>&&);
}; };
typedef const iterator const_iterator;
using Base::value;
ListWrapper() : Base() {}
ListWrapper(const std::initializer_list<pEp::Wrapper<Element>>& i);
ListWrapper(const std::initializer_list<Element>& i);
iterator begin() { return iterator{value}; }
iterator end() const { return iterator{}; }
const_iterator cbegin() const { return const_iterator{value}; }
const_iterator cend() const { return const_iterator{}; }
int size() const;
bool empty() const;
void erase(const iterator& it);
void clear();
void push_back(Element&&);
void push_back(Wrapper<Element>&&);
};
} // end of namespace pEp } // end of namespace pEp

32
test/unittest_identity.cc

@ -3,32 +3,38 @@
#include "../src/types.hh" #include "../src/types.hh"
TEST( PepIdentity, Simple ) TEST(PepIdentity, Simple)
{ {
pEp::Identity id{"test1@pEpdatatypes.lol", "FF00112233445566778899AABBCCDDEEFF001122", "Test User ID", "Test User Name"}; pEp::Identity id{ "test1@pEpdatatypes.lol",
"FF00112233445566778899AABBCCDDEEFF001122",
"Test User ID",
"Test User Name" };
} }
TEST( IdentityList, Simple ) TEST(IdentityList, Simple)
{ {
pEp::IdentityList il; pEp::IdentityList il;
EXPECT_TRUE(il.empty()); EXPECT_TRUE(il.empty());
EXPECT_EQ(il.size(), 0); EXPECT_EQ(il.size(), 0);
} }
TEST( IdentityList, InitList ) TEST(IdentityList, InitList)
{ {
pEp::IdentityList il( pEp::IdentityList il({ pEp::Identity{ "test-init1@pEpdatatypes.lol",
{ "0100112233445566778899AABBCCDDEEFF001122",
pEp::Identity{"test-init1@pEpdatatypes.lol", "0100112233445566778899AABBCCDDEEFF001122", "Test User ID1", "Test User 1 Name"}, "Test User ID1",
pEp::Identity{"test-init2@pEpdatatypes.lol", "0200112233445566778899AABBCCDDEEFF001122", "Test User ID2", "Test User 2 Name"} "Test User 1 Name" },
} ); pEp::Identity{ "test-init2@pEpdatatypes.lol",
"0200112233445566778899AABBCCDDEEFF001122",
EXPECT_EQ( il.size(), 2); "Test User ID2",
"Test User 2 Name" } });
EXPECT_EQ(il.size(), 2);
il.clear(); il.clear();
EXPECT_EQ(il.size(), 0); EXPECT_EQ(il.size(), 0);
EXPECT_TRUE( il.empty() ); EXPECT_TRUE(il.empty());
} }

239
test/unittest_message.cc

@ -2,113 +2,112 @@
#include "../src/types.hh" #include "../src/types.hh"
namespace namespace {
{ static const char*
static const char* mail1_eml = mail1_eml = "Return-Path: <alice@pep-project.org>\r\n"
"Return-Path: <alice@pep-project.org>\r\n" "X-Original-To: alice@pep-project.org\r\n"
"X-Original-To: alice@pep-project.org\r\n" "Delivered-To: alice@pep-project.org\r\n"
"Delivered-To: alice@pep-project.org\r\n" "Received: from localhost (localhost [127.0.0.1])\r\n"
"Received: from localhost (localhost [127.0.0.1])\r\n" "\tby dragon.pibit.ch (Postfix) with ESMTP id B84AF171C06F\r\n"
"\tby dragon.pibit.ch (Postfix) with ESMTP id B84AF171C06F\r\n" "\tfor <alice@pep-project.org>; Wed, 16 Jan 2019 16:29:39 +0100 (CET)\r\n"
"\tfor <alice@pep-project.org>; Wed, 16 Jan 2019 16:29:39 +0100 (CET)\r\n" "Received: from dragon.pibit.ch ([127.0.0.1])\r\n"
"Received: from dragon.pibit.ch ([127.0.0.1])\r\n" "\tby localhost (dragon.pibit.ch [127.0.0.1]) (amavisd-new, port 10024)\r\n"
"\tby localhost (dragon.pibit.ch [127.0.0.1]) (amavisd-new, port 10024)\r\n" "\twith ESMTP id q0wZqHMoT1gS for <alice@pep-project.org>;\r\n"
"\twith ESMTP id q0wZqHMoT1gS for <alice@pep-project.org>;\r\n" "\tWed, 16 Jan 2019 16:29:37 +0100 (CET)\r\n"
"\tWed, 16 Jan 2019 16:29:37 +0100 (CET)\r\n" "Received: from Alice-PC.local (unknown [192.168.128.20])\r\n"
"Received: from Alice-PC.local (unknown [192.168.128.20])\r\n" "\tby dragon.pibit.ch (Postfix) with ESMTPSA id 563DD171C06A\r\n"
"\tby dragon.pibit.ch (Postfix) with ESMTPSA id 563DD171C06A\r\n" "\tfor <alice@pep-project.org>; Wed, 16 Jan 2019 16:29:37 +0100 (CET)\r\n"
"\tfor <alice@pep-project.org>; Wed, 16 Jan 2019 16:29:37 +0100 (CET)\r\n" "To: Bob <bob@pep-project.org>\r\n"
"To: Bob <bob@pep-project.org>\r\n" "From: Alice <alice@pep-project.org>\r\n"
"From: Alice <alice@pep-project.org>\r\n" "Cc: Carol Couscous <carol.couscous+private@pep.lol>, Dave Doe (III) \r\n"
"Cc: Carol Couscous <carol.couscous+private@pep.lol>, Dave Doe (III) \r\n" " Dexter <dave-dexter@pep.ooo>, dodo@pep.lol, \"Eve @ Evil\" <eve@evil.lol>, \r\n"
" Dexter <dave-dexter@pep.ooo>, dodo@pep.lol, \"Eve @ Evil\" <eve@evil.lol>, \r\n" " Mallory =?UTF-8?B?TcO2bGxlcg==?= (private) <\"mallory @ moeller\"@sinister.aq>\r\n"
" Mallory =?UTF-8?B?TcO2bGxlcg==?= (private) <\"mallory @ moeller\"@sinister.aq>\r\n" "Subject: =?UTF-8?B?UsO4ZGdyw7hkIG1lZCBmbMO4ZGU=?=\r\n"
"Subject: =?UTF-8?B?UsO4ZGdyw7hkIG1lZCBmbMO4ZGU=?=\r\n" "Openpgp: preference=signencrypt\r\n"
"Openpgp: preference=signencrypt\r\n" "Organization: =?UTF-8?B?8J+Ukg==?=\r\n"
"Organization: =?UTF-8?B?8J+Ukg==?=\r\n" "Message-ID: <65a2df2c-ddc8-0875-a142-21acf62ed467@pep-project.org>\r\n"
"Message-ID: <65a2df2c-ddc8-0875-a142-21acf62ed467@pep-project.org>\r\n" "References: <msg-alfa@pep.id> <msg-bravo@pep.aq> <lol-123456789@intern.sc.ful-lol.example>\r\n"
"References: <msg-alfa@pep.id> <msg-bravo@pep.aq> <lol-123456789@intern.sc.ful-lol.example>\r\n" " <msg-charlie@pep.aq>\r\n"
" <msg-charlie@pep.aq>\r\n" "In-Reply-To: <msg-reply-0815@pep.aq>\r\n"
"In-Reply-To: <msg-reply-0815@pep.aq>\r\n" "Date: Wed, 16 Jan 2019 16:29:30 +0100\r\n"
"Date: Wed, 16 Jan 2019 16:29:30 +0100\r\n" "User-Agent: B\r\n"
"User-Agent: B\r\n" "MIME-Version: 1.0\r\n"
"MIME-Version: 1.0\r\n" "Content-Type: multipart/mixed;\r\n"
"Content-Type: multipart/mixed;\r\n" " boundary=\"==pEp_01==\"\r\n"
" boundary=\"==pEp_01==\"\r\n" "\r\n"
"\r\n" "This is a Multipart MIME message.\r\n"
"This is a Multipart MIME message.\r\n" "--==pEp_01==\r\n"
"--==pEp_01==\r\n" "Content-Type: multipart/alternative; boundary=\"==pEp_02==\";\r\n"
"Content-Type: multipart/alternative; boundary=\"==pEp_02==\";\r\n" " protected-headers=\"v1\"\r\n"
" protected-headers=\"v1\"\r\n" "\r\n"
"\r\n" "--==pEp_02==\r\n"
"--==pEp_02==\r\n" "Content-Type: text/plain; charset=\"utf-8\"\r\n"
"Content-Type: text/plain; charset=\"utf-8\"\r\n" "Content-Language: en-US\r\n"
"Content-Language: en-US\r\n" "Content-Transfer-Encoding: quoted-printable\r\n"
"Content-Transfer-Encoding: quoted-printable\r\n" "\r\n"
"\r\n" "R=C3=B8dgr=C3=B8d med fl=C3=B8de?\r\n"
"R=C3=B8dgr=C3=B8d med fl=C3=B8de?\r\n" "\r\n"
"\r\n" "--==pEp_02==\r\n"
"--==pEp_02==\r\n" "Content-Type: multipart/related; boundary=\"==pEp_LoL==\";\r\n"
"Content-Type: multipart/related; boundary=\"==pEp_LoL==\";\r\n" "\r\n"
"\r\n" "--==pEp_LoL==\r\n"
"--==pEp_LoL==\r\n" "Content-Type: text/html; charset=\"ISO-8859-1\";\r\n"
"Content-Type: text/html; charset=\"ISO-8859-1\";\r\n" "Content-Transfer-Encoding: quoted-printable\r\n"
"Content-Transfer-Encoding: quoted-printable\r\n" "\r\n"
"\r\n" "<html lang=3D=22de=22><body>=DCbergr=F6=DFen=E4nderung: 1=\r\n"
"<html lang=3D=22de=22><body>=DCbergr=F6=DFen=E4nderung: 1=\r\n" "0=80.</body></html>\r\n"
"0=80.</body></html>\r\n" "\r\n"
"\r\n" "--==pEp_LoL==\r\n"
"--==pEp_LoL==\r\n" "Content-Type: image/png; name=\"rebeccapurple-circle.png\"\r\n"
"Content-Type: image/png; name=\"rebeccapurple-circle.png\"\r\n" "Content-Language: en-US\r\n"
"Content-Language: en-US\r\n" "Content-ID: <rebeccapurple-circle-fb25fbb3-fd0b-46af-b567-7d1aa5725c49@pep.lol>\r\n"
"Content-ID: <rebeccapurple-circle-fb25fbb3-fd0b-46af-b567-7d1aa5725c49@pep.lol>\r\n" "Content-Transfer-Encoding: base64\r\n"
"Content-Transfer-Encoding: base64\r\n" "Content-Disposition: inline;\r\n"
"Content-Disposition: inline;\r\n" " filename*0*=utf-8'en-US'rebeccapurple;\r\n"
" filename*0*=utf-8'en-US'rebeccapurple;\r\n" " filename*1*=%2Dcircle.png;\r\n"
" filename*1*=%2Dcircle.png;\r\n" "\r\n"
"\r\n" "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEVmM5n///9dvR/iAAAA\r\n"
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEVmM5n///9dvR/iAAAA\r\n" "H0lEQVQIHWP4Ic/wgJ3hADNDAyMIYQKIOFABUNkPeQC4LQeH3BOsvgAAAABJRU5ErkJggg==\r\n"
"H0lEQVQIHWP4Ic/wgJ3hADNDAyMIYQKIOFABUNkPeQC4LQeH3BOsvgAAAABJRU5ErkJggg==\r\n" "\r\n"
"\r\n" "--==pEp_LoL==--\r\n" // end of multipart/related
"--==pEp_LoL==--\r\n" // end of multipart/related "\r\n"
"\r\n" "--==pEp_02==--\r\n" // end of multipart/alternative
"--==pEp_02==--\r\n" // end of multipart/alternative "\r\n"
"\r\n" "--==pEp_01==\r\n" // first "real" attachment, 2nd in bloblist
"--==pEp_01==\r\n" // first "real" attachment, 2nd in bloblist "Content-Type: application/octet-stream; name=\"This is a long\r\n"
"Content-Type: application/octet-stream; name=\"This is a long\r\n" " file name so it is split to multiple\r\n"
" file name so it is split to multiple\r\n" " physical lines.bin\"\r\n"
" physical lines.bin\"\r\n" "Content-Language: en-US\r\n"
"Content-Language: en-US\r\n" "Content-Transfer-Encoding: base64\r\n"
"Content-Transfer-Encoding: base64\r\n" "Content-Disposition: attachment;\r\n"
"Content-Disposition: attachment;\r\n" " filename*0=\"This is a long file name so it is split to\";\r\n"
" filename*0=\"This is a long file name so it is split to\";\r\n" " filename*1=\" multiple physical lines.bin\";\r\n"
" filename*1=\" multiple physical lines.bin\";\r\n" "\r\n"
"\r\n" "w5xiZXJncsO2w59lbsOkbmRlcnVuZyEK\r\n"
"w5xiZXJncsO2w59lbsOkbmRlcnVuZyEK\r\n" "\r\n"
"\r\n" "--==pEp_01==\r\n" // another text/plain part, 3rd in bloblist
"--==pEp_01==\r\n" // another text/plain part, 3rd in bloblist "Content-Type: text/plain; charset=\"ISO-8859-15\";\r\n"
"Content-Type: text/plain; charset=\"ISO-8859-15\";\r\n" "Content-Transfer-Encoding: quoted-printable\r\n"
"Content-Transfer-Encoding: quoted-printable\r\n" "\r\n"
"\r\n" "=DCbergr=F6=DFen=E4nderung: 10=A4.\r\n"
"=DCbergr=F6=DFen=E4nderung: 10=A4.\r\n" "--==pEp_01==\r\n" // an attached PNG image with bizarre filename as 4th and last element in bloblist
"--==pEp_01==\r\n" // an attached PNG image with bizarre filename as 4th and last element in bloblist "Content-Type: image/png; name=\"=?UTF-8?B?8J+SqSDwn5iAIPCf?="
"Content-Type: image/png; name=\"=?UTF-8?B?8J+SqSDwn5iAIPCf?=" " =?UTF-8?B?kqkg8J+YgCDwn5KpIPCfmIAg8J+SqSDwn5iAIPCfkqkg8J+YgCDwn5KpIPCfm?="
" =?UTF-8?B?kqkg8J+YgCDwn5KpIPCfmIAg8J+SqSDwn5iAIPCfkqkg8J+YgCDwn5KpIPCfm?=" " =?UTF-8?B?IAg8J+SqSDwn5iAIPCfkqkg8J+YgC5wbmc=?=\"\r\n"
" =?UTF-8?B?IAg8J+SqSDwn5iAIPCfkqkg8J+YgC5wbmc=?=\"\r\n" "Content-Language: en-US\r\n"
"Content-Language: en-US\r\n" "Content-Transfer-Encoding: base64\r\n"
"Content-Transfer-Encoding: base64\r\n" "Content-Disposition: attachment;\r\n"
"Content-Disposition: attachment;\r\n" " filename*0*=utf-8''%F0%9F%92%A9%20%F0%9F%98%80%20%F0%9F%92%A9%20%F0;\r\n"
" filename*0*=utf-8''%F0%9F%92%A9%20%F0%9F%98%80%20%F0%9F%92%A9%20%F0;\r\n" " filename*1*=%9F%98%80%20%F0%9F%92%A9%20%F0%9F%98%80%20%F0%9F%92%A9;\r\n"
" filename*1*=%9F%98%80%20%F0%9F%92%A9%20%F0%9F%98%80%20%F0%9F%92%A9;\r\n" " filename*2*=%20%F0%9F%98%80%20%F0%9F%92%A9%20%F0%9F%98%80%20%F0%9F;\r\n"
" filename*2*=%20%F0%9F%98%80%20%F0%9F%92%A9%20%F0%9F%98%80%20%F0%9F;\r\n" " filename*3*=%92%A9%20%F0%9F%98%80%20%F0%9F%92%A9%20%F0%9F%98%80%20;\r\n"
" filename*3*=%92%A9%20%F0%9F%98%80%20%F0%9F%92%A9%20%F0%9F%98%80%20;\r\n" " filename*4*=%F0%9F%92%A9%20%F0%9F%98%80.png\r\n"
" filename*4*=%F0%9F%92%A9%20%F0%9F%98%80.png\r\n" "\r\n"
"\r\n" "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEVmM5n///9dvR/iAAAA\r\n"
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEVmM5n///9dvR/iAAAA\r\n" " H0lEQVQIHWP4Ic/wgJ3hADN\r\n"
" H0lEQVQIHWP4Ic/wgJ3hADN\r\n" "DAyMIYQKIOFABUNkPeQC4LQeH3BOsvgAAAABJRU5ErkJggg==\r\n"
"DAyMIYQKIOFABUNkPeQC4LQeH3BOsvgAAAABJRU5ErkJggg==\r\n" "--==pEp_01==--\r\n"
"--==pEp_01==--\r\n" "\r\n";
"\r\n";
PEP_STATUS dummy_message(::message* msg) PEP_STATUS dummy_message(::message* msg)
@ -120,21 +119,21 @@ namespace
} // end of anonymous namespace } // end of anonymous namespace
TEST( MessageTest, Simple ) TEST(MessageTest, Simple)
{ {
pEp::Message msg{PEP_dir_outgoing, mail1_eml}; pEp::Message msg{ PEP_dir_outgoing, mail1_eml };
EXPECT_EQ(msg->dir, PEP_dir_outgoing); EXPECT_EQ(msg->dir, PEP_dir_outgoing);
EXPECT_STREQ( msg->shortmsg, "Rødgrød med fløde" ); EXPECT_STREQ(msg->shortmsg, "Rødgrød med fløde");
EXPECT_STREQ( msg->longmsg , "Rødgrød med fløde?\r\n" ); EXPECT_STREQ(msg->longmsg, "Rødgrød med fløde?\r\n");
EXPECT_EQ( identity_list_length(msg->to), 1 ); EXPECT_EQ(identity_list_length(msg->to), 1);
EXPECT_EQ( identity_list_length(msg->cc), 5 ); EXPECT_EQ(identity_list_length(msg->cc), 5);
EXPECT_EQ( identity_list_length(msg->bcc), 0 ); EXPECT_EQ(identity_list_length(msg->bcc), 0);
EXPECT_EQ( bloblist_length(msg->attachments), 4 ); EXPECT_EQ(bloblist_length(msg->attachments), 4);
ASSERT_NE(msg->from, nullptr); ASSERT_NE(msg->from, nullptr);
EXPECT_STREQ( msg->from->username, "Alice"); EXPECT_STREQ(msg->from->username, "Alice");
EXPECT_EQ( dummy_message(msg.get()), PEP_STATUS_OK); EXPECT_EQ(dummy_message(msg.get()), PEP_STATUS_OK);
EXPECT_STREQ( msg->shortmsg, "Hello World"); EXPECT_STREQ(msg->shortmsg, "Hello World");
} }

134
test/unittest_nfc.cc

@ -1,6 +1,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "../src/nfc.hh" // for illegal_utf8 exception #include "../src/nfc.hh" // for illegal_utf8 exception
#include <vector> #include <vector>
using namespace pEp; using namespace pEp;
@ -8,70 +8,82 @@ using std::string_view;
namespace { namespace {
struct TestEntry struct TestEntry {
{ string_view input;
string_view input; bool is_nfc;
bool is_nfc; IsNFC quick;
IsNFC quick; string_view nfc;
string_view nfc; };
typedef TestEntry TE;
std::ostream& operator<<(std::ostream& o, const TestEntry& tt)
{
return o << "input=«" << tt.input << "», isNfc=" << tt.is_nfc << ", quick=" << tt.quick
<< ". ";
}
const char nullo[4] = { 0, 0, 0, 0 };
const std::vector<TestEntry> testValues = {
{ "", true, IsNFC::Yes, "" }, // always start with the simple case ;-)
{ "123", true, IsNFC::Yes, "123" }, // some ASCII digits. Still easy.
{ "\n\\\b", true, IsNFC::Yes, "\n\\\b" }, // backslash escapes for ASCII and control chars
{ "ä", true, IsNFC::Yes, "ä" }, // <U+00E4> small a with diaeresis
{ "\xc4\x85", true, IsNFC::Yes, "\xc4\x85" }, // <U+0105> small a with ogonek
{ "a\xcc\x88", false, IsNFC::Maybe, "ä" }, // a + <U+0308> combining diaresis
{ "a\xcc\xa8", false, IsNFC::Maybe, "\xc4\x85" }, // a + <U+0328> combining ogonek
{ "a\xcc\xa8\xcc\x88",
false,
IsNFC::Maybe,
"\xc4\x85\xcc\x88" }, // a + <U+0328> + <U+0308> (ogonek + diaeresis)
{ "a\xcc\x88\xcc\xa8",
false,
IsNFC::Maybe,
"\xc4\x85\xcc\x88" }, // a + <U+0308> + <U+0328> (diaeresis + ogonek)
{ "\xc4\x85\xcc\x88",
true,
IsNFC::Maybe,
"\xc4\x85\xcc\x88" }, // <U+0105> small a with ogonek + combining diaeresis
{ "ä\xcc\xa8", false, IsNFC::Maybe, "\xc4\x85\xcc\x88" }, // a diaeresis + <U+0328> combining ogonek
// Already implemented, because <U+305> and <U+33C> have neither "No" nor "Maybe" NFC class:
{ "a\xcc\x85\xcc\xbc",
false,
IsNFC::No,
"a\xcc\xbc\xcc\x85" }, // a + <U+0305> + <U+033C> (overline + seagull_below)
{ "a\xcc\xbc\xcc\x85",
true,
IsNFC::Yes,
"a\xcc\xbc\xcc\x85" }, // a + <U+033C> + <U+0305> (seagull_below + overline)
{ string_view(nullo, 1), true, IsNFC::Yes, string_view(nullo, 1) }, // Yeah, 1 NUL byte
{ string_view(nullo, 4), true, IsNFC::Yes, string_view(nullo, 4) }, // Yeah, 4 NUL bytes
{ "EOF", true, IsNFC::Yes, "EOF" }
};
} // namespace
class NfcTest : public ::testing::TestWithParam<TestEntry> {
// intentionally left blank for now.
}; };
typedef TestEntry TE; INSTANTIATE_TEST_SUITE_P(NfcTestInstance, NfcTest, testing::ValuesIn(testValues));
std::ostream& operator<<(std::ostream& o, const TestEntry& tt)
{
return o << "input=«" << tt.input << "», isNfc=" << tt.is_nfc << ", quick=" << tt.quick << ". ";
}
const char nullo[4] = {0,0,0,0};
const std::vector<TestEntry> testValues =
{
{ "" , true, IsNFC::Yes, "" }, // always start with the simple case ;-)
{ "123" , true, IsNFC::Yes, "123" }, // some ASCII digits. Still easy.
{ "\n\\\b" , true, IsNFC::Yes, "\n\\\b" }, // backslash escapes for ASCII and control chars
{ "ä" , true, IsNFC::Yes, "ä" }, // <U+00E4> small a with diaeresis
{ "\xc4\x85" , true, IsNFC::Yes, "\xc4\x85" }, // <U+0105> small a with ogonek
{ "a\xcc\x88", false, IsNFC::Maybe, "ä" }, // a + <U+0308> combining diaresis TEST_P(NfcTest, Meh)
{ "a\xcc\xa8", false, IsNFC::Maybe, "\xc4\x85" }, // a + <U+0328> combining ogonek
{ "a\xcc\xa8\xcc\x88", false, IsNFC::Maybe, "\xc4\x85\xcc\x88" }, // a + <U+0328> + <U+0308> (ogonek + diaeresis)
{ "a\xcc\x88\xcc\xa8", false, IsNFC::Maybe, "\xc4\x85\xcc\x88" }, // a + <U+0308> + <U+0328> (diaeresis + ogonek)
{ "\xc4\x85\xcc\x88" , true, IsNFC::Maybe, "\xc4\x85\xcc\x88" }, // <U+0105> small a with ogonek + combining diaeresis
{ "ä\xcc\xa8" , false, IsNFC::Maybe, "\xc4\x85\xcc\x88" }, // a diaeresis + <U+0328> combining ogonek
// Already implemented, because <U+305> and <U+33C> have neither "No" nor "Maybe" NFC class:
{ "a\xcc\x85\xcc\xbc", false, IsNFC::No , "a\xcc\xbc\xcc\x85"}, // a + <U+0305> + <U+033C> (overline + seagull_below)
{ "a\xcc\xbc\xcc\x85", true, IsNFC::Yes , "a\xcc\xbc\xcc\x85"}, // a + <U+033C> + <U+0305> (seagull_below + overline)
{ string_view(nullo, 1), true, IsNFC::Yes, string_view(nullo, 1) }, // Yeah, 1 NUL byte
{ string_view(nullo, 4), true, IsNFC::Yes, string_view(nullo, 4) }, // Yeah, 4 NUL bytes
{ "EOF", true, IsNFC::Yes, "EOF" }
};
}
class NfcTest : public ::testing::TestWithParam<TestEntry>
{ {
// intentionally left blank for now. const auto& v = GetParam();
}; EXPECT_EQ(v.quick, UTF8::isNFC_quick_check(v.input));
INSTANTIATE_TEST_SUITE_P(NfcTestInstance, NfcTest, testing::ValuesIn(testValues) ); EXPECT_EQ(v.is_nfc, UTF8::isNFC(v.input));
EXPECT_EQ(v.nfc, UTF8::toNFC(v.input));
TEST_P( NfcTest, Meh ) if (v.is_nfc) {
{ EXPECT_EQ(v.input, UTF8::toNFC(v.input));
const auto& v = GetParam(); }
EXPECT_EQ( v.quick, UTF8::isNFC_quick_check(v.input) );
EXPECT_EQ( v.is_nfc, UTF8::isNFC(v.input) );
EXPECT_EQ( v.nfc , UTF8::toNFC(v.input) );
if(v.is_nfc)
{
EXPECT_EQ( v.input, UTF8::toNFC(v.input) );
}
} }

106
test/unittest_nfc16.cc

@ -1,85 +1,81 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "../src/nfc.hh" // for illegal_utf8 exception #include "../src/nfc.hh" // for illegal_utf8 exception
#include <vector> #include <vector>
using namespace pEp; using namespace pEp;
using std::u16string_view; using std::u16string_view;
namespace pEp namespace pEp {
{ std::string escape_utf16(u16string_view s);
std::string escape_utf16(u16string_view s);
} }
namespace { namespace {
struct TestEntry struct TestEntry {
{ u16string_view input;
u16string_view input; bool is_nfc;
bool is_nfc; IsNFC quick;
IsNFC quick; u16string_view nfc;
u16string_view nfc; };
};
typedef TestEntry TE; typedef TestEntry TE;
std::ostream& operator<<(std::ostream& o, const TestEntry& tt) std::ostream& operator<<(std::ostream& o, const TestEntry& tt)
{ {
return o << "input=«" << pEp::escape_utf16(tt.input) << "», isNfc=" << tt.is_nfc << ", quick=" << tt.quick << ". "; return o << "input=«" << pEp::escape_utf16(tt.input) << "», isNfc=" << tt.is_nfc
} << ", quick=" << tt.quick << ". ";
}
const char16_t nullo[4] = {0,0,0,0}; const char16_t nullo[4] = { 0, 0, 0, 0 };
const std::vector<TestEntry> testValues = const std::vector<TestEntry> testValues = {
{ { u"", true, IsNFC::Yes, u"" }, // always start with the simple case ;-)
{ u"" , true, IsNFC::Yes, u"" }, // always start with the simple case ;-) { u"123", true, IsNFC::Yes, u"123" }, // some ASCII digits. Still easy.
{ u"123" , true, IsNFC::Yes, u"123" }, // some ASCII digits. Still easy. { u"\n\\\b", true, IsNFC::Yes, u"\n\\\b" }, // backslash escapes for ASCII and control chars
{ u"\n\\\b" , true, IsNFC::Yes, u"\n\\\b" }, // backslash escapes for ASCII and control chars { u"ä", true, IsNFC::Yes, u"ä" }, // <U+00E4> small a with diaeresis
{ u"ä" , true, IsNFC::Yes, u"ä" }, // <U+00E4> small a with diaeresis { u"\u0105", true, IsNFC::Yes, u"\u0105" }, // <U+0105> small a with ogonek
{ u"\u0105" , true, IsNFC::Yes, u"\u0105" }, // <U+0105> small a with ogonek
{ u"a\u0308" , false, IsNFC::Maybe, u"ä" }, // a + <U+0308> combining diaresis { u"a\u0308", false, IsNFC::Maybe, u"ä" }, // a + <U+0308> combining diaresis
{ u"a\u0328" , false, IsNFC::Maybe, u"\u0105" }, // a + <U+0328> combining ogonek { u"a\u0328", false, IsNFC::Maybe, u"\u0105" }, // a + <U+0328> combining ogonek
{ u"a\u0328\u0308", false, IsNFC::Maybe, u"\u0105\u0308" }, // a + <U+0328> + <U+0308> (ogonek + diaeresis) { u"a\u0328\u0308", false, IsNFC::Maybe, u"\u0105\u0308" }, // a + <U+0328> + <U+0308> (ogonek + diaeresis)
{ u"a\u0308\u0328", false, IsNFC::Maybe, u"\u0105\u0308" }, // a + <U+0308> + <U+0328> (diaeresis + ogonek) { u"a\u0308\u0328", false, IsNFC::Maybe, u"\u0105\u0308" }, // a + <U+0308> + <U+0328> (diaeresis + ogonek)
{ u"\u0105\u0308" , true, IsNFC::Maybe, u"\u0105\u0308" }, // <U+0105> small a with ogonek + combining diaeresis { u"\u0105\u0308", true, IsNFC::Maybe, u"\u0105\u0308" }, // <U+0105> small a with ogonek + combining diaeresis
{ u"ä\u0328" , false, IsNFC::Maybe, u"\u0105\u0308" }, // a diaeresis + <U+0328> combining ogonek { u"ä\u0328", false, IsNFC::Maybe, u"\u0105\u0308" }, // a diaeresis + <U+0328> combining ogonek
// Already implemented, because <U+305> and <U+33C> have neither "No" nor "Maybe" NFC class: // Already implemented, because <U+305> and <U+33C> have neither "No" nor "Maybe" NFC class:
{ u"a\u0305\u033c", false, IsNFC::No , u"a\u033c\u0305"}, // a + <U+0305> + <U+033C> (overline + seagull_below) { u"a\u0305\u033c", false, IsNFC::No, u"a\u033c\u0305" }, // a + <U+0305> + <U+033C> (overline + seagull_below)
{ u"a\u033c\u0305", true, IsNFC::Yes , u"a\u033c\u0305"}, // a + <U+033C> + <U+0305> (seagull_below + overline) { u"a\u033c\u0305", true, IsNFC::Yes, u"a\u033c\u0305" }, // a + <U+033C> + <U+0305> (seagull_below + overline)
// MUSICAL SYMBOL SIXTEENTH NOTE -> will be decomposed and not re-composed according to Unicode data, for whatever reason. :-/ // MUSICAL SYMBOL SIXTEENTH NOTE -> will be decomposed and not re-composed according to Unicode data, for whatever reason. :-/
{ u"\U0001D161", false, IsNFC::No, u"\U0001d15f\U0001d16f" }, { u"\U0001D161", false, IsNFC::No, u"\U0001d15f\U0001d16f" },
{ u16string_view(nullo, 1), true, IsNFC::Yes, u16string_view(nullo, 1) }, // Yeah, 1 NUL byte { u16string_view(nullo, 1), true, IsNFC::Yes, u16string_view(nullo, 1) }, // Yeah, 1 NUL byte
{ u16string_view(nullo, 4), true, IsNFC::Yes, u16string_view(nullo, 4) }, // Yeah, 4 NUL bytes { u16string_view(nullo, 4), true, IsNFC::Yes, u16string_view(nullo, 4) }, // Yeah, 4 NUL bytes
{ u"EOF", true, IsNFC::Yes, u"EOF" } { u"EOF", true, IsNFC::Yes, u"EOF" }
}; };
} // end of anonymous namespace } // end of anonymous namespace
class Nfc16Test : public ::testing::TestWithParam<TestEntry> class Nfc16Test : public ::testing::TestWithParam<TestEntry> {
{ // intentionally left blank for now.
// intentionally left blank for now.
}; };
INSTANTIATE_TEST_SUITE_P(Nfc16TestInstance, Nfc16Test, testing::ValuesIn(testValues) ); INSTANTIATE_TEST_SUITE_P(Nfc16TestInstance, Nfc16Test, testing::ValuesIn(testValues));
TEST_P( Nfc16Test, Meh ) TEST_P(Nfc16Test, Meh)
{ {
const auto& v = GetParam(); const auto& v = GetParam();
EXPECT_EQ( v.quick, UTF16::isNFC_quick_check(v.input) ); EXPECT_EQ(v.quick, UTF16::isNFC_quick_check(v.input));
EXPECT_EQ( v.is_nfc, UTF16::isNFC(v.input) ); EXPECT_EQ(v.is_nfc, UTF16::isNFC(v.input));
EXPECT_EQ( v.nfc , UTF16::toNFC(v.input) ); EXPECT_EQ(v.nfc, UTF16::toNFC(v.input));
if(v.is_nfc) if (v.is_nfc) {
{ EXPECT_EQ(v.input, UTF16::toNFC(v.input));
EXPECT_EQ( v.input, UTF16::toNFC(v.input) ); }
}
} }

101
test/unittest_nfcstring.cc

@ -1,6 +1,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "../src/nfc.hh" // for illegal_utf8 exception #include "../src/nfc.hh" // for illegal_utf8 exception
#include <vector> #include <vector>
using namespace pEp; using namespace pEp;
@ -8,67 +8,64 @@ using std::string_view;
namespace { namespace {
struct TestEntry struct TestEntry {
{ string_view input;
string_view input; string_view nfc;
string_view nfc; };
};
typedef TestEntry TE; typedef TestEntry TE;
std::ostream& operator<<(std::ostream& o, const TestEntry& tt) std::ostream& operator<<(std::ostream& o, const TestEntry& tt)
{ {
return o << "input=«" << tt.input << "», nfc=«" << tt.nfc << "» "; return o << "input=«" << tt.input << "», nfc=«" << tt.nfc << "» ";
} }
} // end of anonymous namespace } // end of anonymous namespace
TEST( NfcTestString, Simple ) TEST(NfcTestString, Simple)
{ {
pEp::nfc_string s; pEp::nfc_string s;
EXPECT_TRUE( s.empty() ); EXPECT_TRUE(s.empty());
EXPECT_EQ( s.size(), 0 ); EXPECT_EQ(s.size(), 0);
s.reserve(1000); s.reserve(1000);
EXPECT_GE(s.capacity(), 1000); EXPECT_GE(s.capacity(), 1000);
s = "Ha\u0308user"; // non-NFC input will be normalized. s = "Ha\u0308user"; // non-NFC input will be normalized.
EXPECT_NE( s.get(), std::string("Ha\u0308user") ); EXPECT_NE(s.get(), std::string("Ha\u0308user"));
EXPECT_EQ( s.get(), std::string("Häuser") ); EXPECT_EQ(s.get(), std::string("Häuser"));
s.insert(0, "U\u0308ber"); s.insert(0, "U\u0308ber");
EXPECT_EQ( s.get(), std::string("ÜberHäuser") ); EXPECT_EQ(s.get(), std::string("ÜberHäuser"));
s += "\u0328\u030c"; // COMBINING OGONEK (below), COMBINING CARON (above) s += "\u0328\u030c"; // COMBINING OGONEK (below), COMBINING CARON (above)
// r absorbs the caron into U+0159, the combining ogonek remains separate // r absorbs the caron into U+0159, the combining ogonek remains separate
EXPECT_EQ( s.get(), std::string("ÜberHäuse\u0159\u0328") ); EXPECT_EQ(s.get(), std::string("ÜberHäuse\u0159\u0328"));
EXPECT_EQ( s.size(), 15); EXPECT_EQ(s.size(), 15);
EXPECT_THROW( s.substr(1), pEp::illegal_utf ); EXPECT_THROW(s.substr(1), pEp::illegal_utf);
EXPECT_THROW( s.substr(2, 10), pEp::illegal_utf ); EXPECT_THROW(s.substr(2, 10), pEp::illegal_utf);
// removal of the r-with-caron let the remaining ogonek combine with the e to U+0119 (E WITH OGONEK) // removal of the r-with-caron let the remaining ogonek combine with the e to U+0119 (E WITH OGONEK)
EXPECT_EQ( s.erase(11, 2).get(), "ÜberHäus\u0119" ); EXPECT_EQ(s.erase(11, 2).get(), "ÜberHäus\u0119");
EXPECT_TRUE ( s.starts_with("Üb") ); EXPECT_TRUE(s.starts_with("Üb"));
EXPECT_FALSE( s.starts_with("Üx") ); EXPECT_FALSE(s.starts_with("Üx"));
EXPECT_TRUE ( s.ends_with("s\u0119") ); EXPECT_TRUE(s.ends_with("s\u0119"));
EXPECT_FALSE( s.ends_with("ss\u0119") ); EXPECT_FALSE(s.ends_with("ss\u0119"));
EXPECT_EQ( s.find("Über"), 0u ); EXPECT_EQ(s.find("Über"), 0u);
EXPECT_EQ( s.find("ber") , 2u ); EXPECT_EQ(s.find("ber"), 2u);
EXPECT_EQ( s.find("über"), UTF8::nfc_string::npos ); EXPECT_EQ(s.find("über"), UTF8::nfc_string::npos);
EXPECT_EQ( s.find("Übel"), UTF8::nfc_string::npos ); EXPECT_EQ(s.find("Übel"), UTF8::nfc_string::npos);
} }
TEST( NfcTestString, Exceptions ) TEST(NfcTestString, Exceptions)
{ {
pEp::nfc_string s; pEp::nfc_string s;
EXPECT_THROW( s = "Meep\xc0\x80.", pEp::illegal_utf ); EXPECT_THROW(s = "Meep\xc0\x80.", pEp::illegal_utf);
EXPECT_THROW( s += '\377', pEp::illegal_utf ); EXPECT_THROW(s += '\377', pEp::illegal_utf);
} }

64
test/unittest_stringlist.cc

@ -3,70 +3,68 @@
#include "../src/types.hh" #include "../src/types.hh"
TEST( StringList, Simple ) TEST(StringList, Simple)
{ {
pEp::StringList sl; pEp::StringList sl;
EXPECT_TRUE(sl.empty()); EXPECT_TRUE(sl.empty());
EXPECT_EQ(sl.size(), 0); EXPECT_EQ(sl.size(), 0);
} }
TEST( StringList, InitList ) TEST(StringList, InitList)
{ {
pEp::StringList sl( {"Hello", "world"} ); pEp::StringList sl({ "Hello", "world" });
EXPECT_EQ( sl.size(), 2); EXPECT_EQ(sl.size(), 2);
sl.clear(); sl.clear();
EXPECT_EQ(sl.size(), 0); EXPECT_EQ(sl.size(), 0);
EXPECT_TRUE( sl.empty() ); EXPECT_TRUE(sl.empty());
} }
TEST( StringList, Algorithm ) TEST(StringList, Algorithm)
{ {
pEp::StringList sl( {"Alice", "Bob", "Carol", "Dave", "Eve", "Frank", "George", "Harry"} ); pEp::StringList sl({ "Alice", "Bob", "Carol", "Dave", "Eve", "Frank", "George", "Harry" });
auto qe = std::find(sl.begin(), sl.end(), std::string("Santa Claus")); // not there! auto qe = std::find(sl.begin(), sl.end(), std::string("Santa Claus")); // not there!
EXPECT_EQ( qe, sl.end() ); EXPECT_EQ(qe, sl.end());
// BEWARE: DOES NOT WORK, because find() uses operator== on char* pointers. :-( // BEWARE: DOES NOT WORK, because find() uses operator== on char* pointers. :-(
// auto q = std::find(sl.begin(), sl.end(), "Eve"); // auto q = std::find(sl.begin(), sl.end(), "Eve");
auto q = std::find(sl.begin(), sl.end(), std::string("Eve")); auto q = std::find(sl.begin(), sl.end(), std::string("Eve"));
EXPECT_NE( q, sl.end() ); EXPECT_NE(q, sl.end());
EXPECT_STREQ( *q, "Eve" ); EXPECT_STREQ(*q, "Eve");
} }
TEST( StringList, Dynamic ) TEST(StringList, Dynamic)
{ {
static const unsigned NumberOfElements = 17; static const unsigned NumberOfElements = 17;
pEp::StringList sl; pEp::StringList sl;
EXPECT_EQ(sl.size(), 0); EXPECT_EQ(sl.size(), 0);
EXPECT_TRUE( sl.empty() ); EXPECT_TRUE(sl.empty());
char buffer[16]; char buffer[16];
for(unsigned u=0; u<NumberOfElements; ++u) for (unsigned u = 0; u < NumberOfElements; ++u) {
{
EXPECT_EQ(sl.size(), u); EXPECT_EQ(sl.size(), u);
snprintf(buffer, 15, "k%u", u); snprintf(buffer, 15, "k%u", u);
sl.push_back( buffer ); sl.push_back(buffer);
} }
auto find_value = [&buffer](const char* v){ return strcmp(v, buffer)==0; }; auto find_value = [&buffer](const char* v) { return strcmp(v, buffer) == 0; };
// delete random elements. // delete random elements.
for(unsigned u=0; u<NumberOfElements; ++u) for (unsigned u = 0; u < NumberOfElements; ++u) {
{ EXPECT_EQ(sl.size(), NumberOfElements - u);
EXPECT_EQ(sl.size(), NumberOfElements-u); snprintf(buffer, 15, "k%u", (u * 7) % NumberOfElements); // permutate keys order
snprintf(buffer, 15, "k%u", (u*7) % NumberOfElements); // permutate keys order auto q = std::find_if(sl.begin(), sl.end(), find_value);
auto q = std::find_if( sl.begin(), sl.end(), find_value );
ASSERT_NE(q, sl.end()); // element with key is found
ASSERT_NE( q, sl.end() ); // element with key is found EXPECT_STREQ(*q, buffer);
EXPECT_STREQ( *q, buffer);
sl.erase(q); sl.erase(q);
q = std::find_if( sl.begin(), sl.end(), find_value ); q = std::find_if(sl.begin(), sl.end(), find_value);
EXPECT_EQ( q, sl.end() ); // element with that key is no longer found EXPECT_EQ(q, sl.end()); // element with that key is no longer found
} }
} }

60
test/unittest_stringpair.cc

@ -5,62 +5,60 @@
typedef pEp::StringPair SP; typedef pEp::StringPair SP;
TEST( StringPair, Simple ) TEST(StringPair, Simple)
{ {
pEp::StringPair s1{"key", "value"}; pEp::StringPair s1{ "key", "value" };
pEp::StringPairList spl; pEp::StringPairList spl;
EXPECT_TRUE(spl.empty()); EXPECT_TRUE(spl.empty());
EXPECT_EQ(spl.size(), 0); EXPECT_EQ(spl.size(), 0);
// pEp::StringPair s2{ std::string{"key2"}, std::string{"value"} }; // pEp::StringPair s2{ std::string{"key2"}, std::string{"value"} };
} }
TEST( StringPair, InitList ) TEST(StringPair, InitList)
{ {
const std::initializer_list<pEp::StringPair> il{ SP{"key0", "value0"}, SP{"key1", "value1"} }; const std::initializer_list<pEp::StringPair> il{ SP{ "key0", "value0" }, SP{ "key1", "value1" } };
pEp::StringPairList spl( il ); pEp::StringPairList spl(il);
EXPECT_EQ( spl.size(), 2); EXPECT_EQ(spl.size(), 2);
spl.clear(); spl.clear();
EXPECT_EQ(spl.size(), 0); EXPECT_EQ(spl.size(), 0);
EXPECT_TRUE( spl.empty() ); EXPECT_TRUE(spl.empty());
} }
TEST( StringPair, Dynamic ) TEST(StringPair, Dynamic)
{ {
static const unsigned NumberOfElements = 17; static const unsigned NumberOfElements = 17;
char key[16]; char key[16];
char value[16]; char value[16];
pEp::StringPairList spl; pEp::StringPairList spl;
EXPECT_EQ(spl.size(), 0); EXPECT_EQ(spl.size(), 0);
EXPECT_TRUE( spl.empty() ); EXPECT_TRUE(spl.empty());
for(unsigned u=0; u<NumberOfElements; ++u) for (unsigned u = 0; u < NumberOfElements; ++u) {
{
EXPECT_EQ(spl.size(), u); EXPECT_EQ(spl.size(), u);
snprintf(key, 15, "k%u", u); snprintf(key, 15, "k%u", u);
snprintf(value, 15, "v%u", u*91); snprintf(value, 15, "v%u", u * 91);
spl.push_back( pEp::StringPair{key, value} ); spl.push_back(pEp::StringPair{ key, value });
} }
auto find_by_key = [&key](const ::stringpair_t* sp){ return strcmp(sp->key, key)==0; }; auto find_by_key = [&key](const ::stringpair_t* sp) { return strcmp(sp->key, key) == 0; };
// delete random elements. // delete random elements.
for(unsigned u=0; u<NumberOfElements; ++u) for (unsigned u = 0; u < NumberOfElements; ++u) {
{ EXPECT_EQ(spl.size(), NumberOfElements - u);
EXPECT_EQ(spl.size(), NumberOfElements-u); snprintf(key, 15, "k%u", (u * 7) % NumberOfElements); // permutate keys order
snprintf(key, 15, "k%u", (u*7) % NumberOfElements); // permutate keys order auto q = std::find_if(spl.begin(), spl.end(), find_by_key);
auto q = std::find_if( spl.begin(), spl.end(), find_by_key );
ASSERT_NE(q, spl.end()); // element with key is found
ASSERT_NE( q, spl.end() ); // element with key is found EXPECT_STREQ(q->key, key);
EXPECT_STREQ( q->key, key);
spl.erase(q); spl.erase(q);
q = std::find_if( spl.begin(), spl.end(), find_by_key ); q = std::find_if(spl.begin(), spl.end(), find_by_key);
EXPECT_EQ( q, spl.end() ); // element with that key is no longer found EXPECT_EQ(q, spl.end()); // element with that key is no longer found
} }
} }

Loading…
Cancel
Save