/** ========================================================================== * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes * with no warranties. This code is yours to share, use and modify with no * strings attached and no restrictions or obligations. * * For more information see g3log/LICENSE or refer refer to http://unlicense.org * ============================================================================*/ #include #include #include #include #include #include #include #include #include #include "testing_helpers.h" #include "g3log/logmessage.hpp" #include "g3log/logworker.hpp" #include "g3log/std2_make_unique.hpp" using namespace testing_helpers; using namespace std; TEST(Sink, OneSink) { using namespace g2; AtomicBoolPtr flag = make_shared < atomic> (false); AtomicIntPtr count = make_shared < atomic> (0); { auto worker = g2::LogWorker::createWithNoSink(); auto handle = worker->addSink(std2::make_unique(flag, count), &ScopedSetTrue::ReceiveMsg); EXPECT_FALSE(flag->load()); EXPECT_TRUE(0 == count->load()); LogMessagePtr message{std2::make_unique("test", 0, "test", DEBUG)}; message.get()->write().append("this message should trigger an atomic increment at the sink"); worker->save(message); } EXPECT_TRUE(flag->load()); EXPECT_TRUE(1 == count->load()); } namespace { typedef std::shared_ptr> AtomicBoolPtr; typedef std::shared_ptr> AtomicIntPtr; typedef vector BoolList; typedef vector IntVector; } TEST(ConceptSink, OneHundredSinks) { using namespace g2; BoolList flags; IntVector counts; size_t NumberOfItems = 100; for (size_t index = 0; index < NumberOfItems; ++index) { flags.push_back(make_shared < atomic> (false)); counts.push_back(make_shared < atomic> (0)); } { RestoreFileLogger logger{"./"}; g2::LogWorker* worker = logger._scope->get(); //g2LogWorker::createWithNoSink(); size_t index = 0; for (auto& flag : flags) { auto& count = counts[index++]; // ignore the handle worker->addSink(std2::make_unique(flag, count), &ScopedSetTrue::ReceiveMsg); } LOG(DEBUG) << "start message"; LogMessagePtr message1{std2::make_unique("test", 0, "test", DEBUG)}; LogMessagePtr message2{std2::make_unique("test", 0, "test", DEBUG)}; auto& write1 = message1.get()->write(); write1.append("Hello to 100 receivers :)"); worker->save(message1); auto& write2 = message2.get()->write(); write2.append("Hello to 100 receivers :)"); worker->save(message2); LOG(INFO) << "end message"; logger.reset(); } // at the curly brace above the ScopedLogger will go out of scope and all the // 100 logging receivers will get their message to exit after all messages are // are processed size_t index = 0; for (auto& flag : flags) { auto& count = counts[index++]; ASSERT_TRUE(flag->load()) << ", count : " << (index - 1); ASSERT_TRUE(4 == count->load()) << ", count : " << (index - 1); } cout << "test one hundred sinks is finished finished\n"; } struct VoidReceiver { std::atomic* _atomicCounter; explicit VoidReceiver(std::atomic* counter) : _atomicCounter(counter){} void receiveMsg(std::string msg){ /*ignored*/} void incrementAtomic(){ (*_atomicCounter)++; } }; TEST(ConceptSink, VoidCall__NoCall_ExpectingNoAdd) { std::atomic counter{0}; { std::unique_ptr worker{g2::LogWorker::createWithNoSink()}; auto handle = worker->addSink(std2::make_unique(&counter), &VoidReceiver::receiveMsg); } EXPECT_EQ(counter, 0); } TEST(ConceptSink, VoidCall__OneCall_ExpectingOneAdd) { std::atomic counter{0}; { std::unique_ptr worker{g2::LogWorker::createWithNoSink()}; auto handle = worker->addSink(std2::make_unique(&counter), &VoidReceiver::receiveMsg); std::future ignored = handle->call(&VoidReceiver::incrementAtomic); } EXPECT_EQ(counter, 1); } TEST(ConceptSink, VoidCall__TwoCalls_ExpectingTwoAdd) { std::atomic counter{0}; { std::unique_ptr worker{g2::LogWorker::createWithNoSink()}; auto handle = worker->addSink(std2::make_unique(&counter), &VoidReceiver::receiveMsg); auto voidFuture1 = handle->call(&VoidReceiver::incrementAtomic); auto voidFuture2 = handle->call(&VoidReceiver::incrementAtomic); voidFuture1.wait(); EXPECT_TRUE(counter >= 1); } EXPECT_EQ(counter, 2); } struct IntReceiver { std::atomic* _atomicCounter; explicit IntReceiver(std::atomic* counter) : _atomicCounter(counter){} void receiveMsgDoNothing(std::string msg){ /*ignored*/} void receiveMsgIncrementAtomic(std::string msg){ incrementAtomic(); } int incrementAtomic(){ (*_atomicCounter)++; int value = *_atomicCounter; return value; } }; TEST(ConceptSink, IntCall__TwoCalls_ExpectingTwoAdd) { std::atomic counter{0}; { std::unique_ptr worker{g2::LogWorker::createWithNoSink()}; auto handle = worker->addSink(std2::make_unique(&counter), &IntReceiver::receiveMsgDoNothing); std::future intFuture1 = handle->call(&IntReceiver::incrementAtomic); EXPECT_EQ(intFuture1.get(), 1); EXPECT_EQ(counter, 1); auto intFuture2 = handle->call(&IntReceiver::incrementAtomic); EXPECT_EQ(intFuture2.get(), 2); } EXPECT_EQ(counter, 2); } void DoLogCalls(std::atomic* doWhileTrue, size_t counter) { while(doWhileTrue->load()) { LOG(INFO) << "Calling from #" << counter; std::this_thread::yield(); } } TEST(ConceptSink, CannotCallSpawnTaskOnNullptrWorker) { auto FailedHelloWorld = []{ std::cout << "Hello World" << std::endl; }; kjellkod::Active* active = nullptr; auto failed = g2::spawn_task(FailedHelloWorld, active); EXPECT_ANY_THROW(failed.get()); } TEST(ConceptSink, AggressiveThreadCallsDuringShutdown) { std::atomic keepRunning{true}; std::vector threads; const size_t numberOfThreads = 100; threads.reserve(numberOfThreads); g2::internal::shutDownLogging(); // Avoid annoying printouts at log shutdown stringstream cerr_buffer; testing_helpers::ScopedOut guard1(std::cerr, &cerr_buffer); // these threads will continue to write to a logger // while the receiving logger is instantiated, and destroyed repeatedly for (size_t caller = 0; caller < numberOfThreads; ++ caller) { threads.push_back(std::thread(DoLogCalls, &keepRunning, caller)); } std::atomic atomicCounter{0}; size_t numberOfCycles = 25; std::cout << "Create logger, delete active logger, " << numberOfCycles << " times\n\tWhile " << numberOfThreads << " threads are continously doing LOG calls" << std::endl; std::cout << "Create/Destroy Times #"; for (size_t create = 0; create < numberOfCycles; ++create) { std::cout << create << " "; std::unique_ptr worker{g2::LogWorker::createWithNoSink()}; auto handle = worker->addSink(std2::make_unique(&atomicCounter), &IntReceiver::receiveMsgIncrementAtomic); g2::initializeLogging(worker.get()); // wait till some LOGS streaming in atomicCounter = 0; while(atomicCounter.load() < 10) { std::this_thread::sleep_for(std::chrono::milliseconds(5)); } } // g2log worker exists: 1) shutdownlogging 2) flush of queues and shutdown of sinks // exit the threads keepRunning = false; for (auto& t : threads) { t.join(); } std::cout << "\nAll threads are joined " << std::endl; }