diff --git a/test/test_counting_semaphore.cc b/test/test_counting_semaphore.cc new file mode 100644 index 0000000..cf241a8 --- /dev/null +++ b/test/test_counting_semaphore.cc @@ -0,0 +1,110 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#include +#include +#include +#include +#include "../src/CountingSemaphore.hh" + +using namespace std; +using namespace pEp; + +thread make_two_threads_for_many_threads() +{ + thread res([&]() { + CountingSemaphore semaphore; + semaphore = 3; + + thread thread1([&]() { + semaphore.p (); + semaphore.p (); + semaphore.p (); + // Now the count is at 0, assuming the other thread has not + // started yet; otherwise it should be at 1. + + semaphore.p (); + //cout << "1: done\n"; + }); + + Utils::sleep_millis(1000); + + thread thread2([&]() { + semaphore.v(); + }); + + thread1.join(); + thread2.join(); + + assert (semaphore.load() == 0); + }); + return res; +} + +void many_threads() +{ + int i; + +#define N 1000 + thread threads[N]; + std::cout << "starting " << N + << " threads, each starting and joining a thread pair...\n"; + for (i = 0; i < N; i ++) + threads [i] = make_two_threads_for_many_threads (); + std::cout << "joining threads...\n"; + for (i = 0; i < N; i ++) + threads [i].join(); +#undef N + std::cout << "many_threads done.\n\n"; +} + +void v_times (CountingSemaphore &semaphore, + int times) +{ + std::cout << "V'ing " << times << " times...\n"; + for (int i = 0; i < times; i ++) + semaphore.v (); + std::cout << "... I V'd " << times << " times.\n"; +} + +void p_times (CountingSemaphore &semaphore, + int times) +{ + std::cout << "P'ing " << times << " times...\n"; + for (int i = 0; i < times; i ++) + semaphore.p (); + std::cout << "... I P'd " << times << " times.\n"; +} + +void few_threads() +{ +#define HALF_N (1000 * 1000 * 10) +#define N (HALF_N * 2) + CountingSemaphore semaphore; + + thread thread_p1([&]() { + p_times (semaphore, HALF_N); + }); + thread thread_v([&]() { + v_times (semaphore, N); + }); + thread thread_p2([&]() { + p_times (semaphore, HALF_N); + }); +#undef HALF_N +#undef N + thread_v.join(); + thread_p1.join(); + thread_p2.join(); + + assert (semaphore.load() == 0); + + std::cout << "few_thrads done.\n\n"; +} + +int main() +{ + many_threads (); + few_threads (); + return 0; +} \ No newline at end of file diff --git a/test/test_file_rw_bin.cc b/test/test_file_rw_bin.cc new file mode 100644 index 0000000..92f5db8 --- /dev/null +++ b/test/test_file_rw_bin.cc @@ -0,0 +1,24 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt +#include +#include + +#include +#include + +int main(int argc, char* argv[]) +{ + pEp::Adapter::pEpLog::set_enabled(true); + std::string filename = "test_rw.bin"; + + std::vector v_out{}; + for (int i = 0; i < 1000; i++) { + v_out.push_back(pEp::Utils::random_char(0, 255)); + } + + pEp::Utils::file_write_bin(filename, v_out); + + std::vector v_in = pEp::Utils::file_read_bin(filename); + + assert(v_in == v_out); +} diff --git a/test/test_hexbin.cc b/test/test_hexbin.cc new file mode 100644 index 0000000..5c73458 --- /dev/null +++ b/test/test_hexbin.cc @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include + +using namespace pEp; + +int main() +{ + pEp::Adapter::pEpLog::set_enabled(true); + + { + // Valid hex string + std::string str_in{ "FFABCD00EF123200" }; + std::vector bin = Utils::hex2bin(str_in); + PITYASSERT(str_in.length() == bin.size() * 2, "Size error"); + + std::string str_out = pEp::Utils::bin2hex(bin); + pEpLog("Hex_IN : '" + Utils::to_lower(str_in) + "'"); + pEpLog("Hex_OUT : '" + Utils::to_lower(str_out) + "'"); + + PITYASSERT(Utils::to_lower(str_in) == Utils::to_lower(str_out), "roundtrip failed"); + } + + { + // Uneven string throws + std::string str_in{ "FFA" }; + PITYASSERT_THROWS(Utils::hex2bin(str_in), "Uneven string should throw"); + } + + { + // Non-hex chars + std::string str_in{ "pEp!" }; + PITYASSERT_THROWS(Utils::hex2bin(str_in), "Invalid hex chars should throw"); + } + + pEpLog("All tests passsed"); +} \ No newline at end of file diff --git a/test/test_library.cc b/test/test_library.cc new file mode 100644 index 0000000..900772c --- /dev/null +++ b/test/test_library.cc @@ -0,0 +1,105 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#include "pc_container.hh" +#include +#include +#include +#include + +using namespace pEp; + +// Producer's data: +class P { +public: + P(int i) : data{ new char[64] } + { + snprintf(data, 63, "%i", i); + } + + P(const P&) = delete; + void operator=(const P&) = delete; + + ~P() + { + std::cerr << "~P(): data=" << (data ? data : "(NULL)") << '\n'; + delete[] data; + } + + char* data; +}; + + +// Consumer's data: +class C { +public: + C(int _i) : i(_i) {} + int i; +}; + +pc_container pc; + +void consumer_thread() +{ + bool keep_running = true; + int sum = 0; + while (keep_running) { + for (auto& q : pc) { + switch (q.state()) { + case PC_State::Created: { + const int value = atoi(q.pdata->data); + if (value < 0) { + std::cerr << "Q\n"; + keep_running = false; + } else { + std::cerr << "C"; + q.cdata = new C(value); + sum += q.cdata->i; + } + break; + } + case PC_State::Deleted: { + std::cerr << "D"; + sum -= q.cdata->i; + delete q.cdata; + break; + } + case PC_State::Changed: { + std::cerr << "X"; + sum -= q.cdata->i; + delete q.cdata; + q.cdata = new C(atoi(q.pdata->data)); + sum += q.cdata->i; + break; + } + default: + throw "Illegal state"; + } + } + } + + std::cout << "Consumer sum: " << sum << ".\n"; +} + +int main() +{ + for (int i = 0; i < 10; ++i) { + pc.insert(new P(i)); + } + + std::thread t{ &consumer_thread }; + + for (int i = 10; i < 100; ++i) { + pc.insert(new P(i)); + } + + while (!pc.empty()) { + auto q = pc.begin(); + delete q->pdata; + pc.erase(q); + } + + pc.insert(new P(-1)); + + t.join(); +} diff --git a/test/test_rand.cc b/test/test_rand.cc new file mode 100644 index 0000000..122b7d5 --- /dev/null +++ b/test/test_rand.cc @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + +using namespace pEp; + + +int test_random_string_fast() +{ + pEpLog("Test test_random_string_fast() correct length, min & max"); + for (int i = 0; i < 1000; i++) { + uint len = Utils::random_fast(5000); + std::string res = Utils::random_string_fast(0, 255, len); + PITYASSERT( + res.length() == len, + "random_string_fast: wrong length: " + + std::to_string(res.length()) + " is not " + std::to_string(len)); + } + return 0; +} + +int main() +{ + pEp::Adapter::pEpLog::set_enabled(true); + + test_random_string_fast(); + + pEpLog("Random functions benchmark"); + int nr_mb = 10; + int nr_bytes = 1024 * 1024 * nr_mb; + pEpLog("generating " + std::to_string(nr_mb) + "MB's of random"); + + { + pEpLog("Utils::random_char()"); + auto time_start = std::chrono::steady_clock::now(); + for (int i = 0; i < nr_bytes; i++) { + Utils::random_char(0, 255); + } + auto time_end = std::chrono::steady_clock::now(); + double duration_ms = (double)std::chrono::duration_cast( + time_end - time_start) + .count() / + 1000; + pEpLog("Time [ms/mbyte]:" + std::to_string(duration_ms / double(nr_mb))); + } + std::cout << "" << std::endl; + { + pEpLog("Utils::Fastrand()"); + auto time_start = std::chrono::steady_clock::now(); + for (int i = 0; i < nr_bytes; i++) { + Utils::random_fast(255); + } + auto time_end = std::chrono::steady_clock::now(); + double duration_ms = (double)std::chrono::duration_cast( + time_end - time_start) + .count() / + 1000; + + pEpLog("Time [ms/mbyte]:" + std::to_string(duration_ms / double(nr_mb))); + } + return 0; +} diff --git a/test/test_semaphore.cc b/test/test_semaphore.cc new file mode 100644 index 0000000..497652d --- /dev/null +++ b/test/test_semaphore.cc @@ -0,0 +1,37 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +#include +#include +#include +#include "../src/Semaphore.hh" + +using namespace std; +using namespace pEp; + +int main() +{ + Semaphore semaphore; + + thread thread1([&]() { + cout << "1: before stop\n"; + semaphore.stop(); + cout << "1: now on stop\n"; + semaphore.try_wait(); + cout << "1: and on go again\n"; + semaphore.try_wait(); + cout << "1: keep going\n"; + }); + + Utils::sleep_millis(1000); + + thread thread2([&]() { + cout << "2: setting go\n"; + semaphore.go(); + }); + + thread1.join(); + thread2.join(); + + return 0; +}