Generic cleanup and bugfix after massive merge... still ongoing

--HG--
rename : g2log/test_unit/test_sink.cpp => g2log/test_unit/test_sink_concept.cpp
This commit is contained in:
KjellKod 2013-08-18 23:18:18 -06:00
parent 17cf2ee910
commit d254879890
8 changed files with 305 additions and 32 deletions

4
.hgignore Normal file
View File

@ -0,0 +1,4 @@
syntax: glob
g2log-sinks/*
3rdParty/*
g2log/build/*

View File

@ -127,7 +127,7 @@ ENDIF(MSVC)
# before it can be "cmake'd" and compiled --- leaving it as OFF for now
# ============================================================================
# 1. a simple test example 'g2log-FATAL-example'
option (USE_SIMPLE_EXAMPLE "Simple (fatal-crash) example " OFF)
option (USE_SIMPLE_EXAMPLE "Simple (fatal-crash) example " ON)
# 2. performance test (average + worst case) for KjellKod's g2log
option (USE_G2LOG_PERFORMANCE "g2log performance test" OFF)
@ -225,7 +225,7 @@ ENDIF(MSVC)
# and this: http://stackoverflow.com/questions/2257464/google-test-and-visual-studio-2010-rc
SET(tests_to_run test_filechange test_io test_configuration test_sink)
SET(tests_to_run test_filechange test_io test_configuration test_sink_concept test_sink)
SET(helper ${DIR_UNIT_TEST}/testing_helpers.h ${DIR_UNIT_TEST}/testing_helpers.cpp)
include_directories(${DIR_UNIT_TEST})

View File

@ -152,7 +152,7 @@ std::string signalName(int signal_number)
// --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT
void exitWithDefaultSignalHandler(int signal_number)
{
std::cerr << "Exiting - FATAL SIGNAL: " << signal_number << " " << std::flush;
std::cerr << "\nExiting - FATAL SIGNAL: " << signal_number << " " << std::flush;
struct sigaction action;
memset(&action, 0, sizeof(action)); //
sigemptyset(&action.sa_mask);

View File

@ -0,0 +1,124 @@
#ifndef G2_FILESINK_HELPER_IPP_
#define G2_FILESINK_HELPER_IPP_
#include <memory>
#include <string>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
//#include <fstream>
//#include <algorithm>
//#include <future>
//#include <cassert>
//#include <chrono>
namespace g2 {
namespace internal {
static const std::string file_name_time_formatted = "%Y%m%d-%H%M%S";
// check for filename validity - filename should not be part of PATH
bool isValidFilename(const std::string& prefix_filename) {
std::string illegal_characters("/,|<>:#$%{}()[]\'\"^!?+* ");
size_t pos = prefix_filename.find_first_of(illegal_characters, 0);
if (pos != std::string::npos) {
std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl;
return false;
} else if (prefix_filename.empty()) {
std::cerr << "Empty filename prefix is not allowed" << std::endl;
return false;
}
return true;
}
std::string prefixSanityFix(std::string prefix) {
prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end());
prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end());
prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end());
prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end());
if (!isValidFilename(prefix)) {
return
{
};
}
return prefix;
}
std::string pathSanityFix(std::string path, std::string file_name) {
// Unify the delimeters,. maybe sketchy solution but it seems to work
// on at least win7 + ubuntu. All bets are off for older windows
std::replace(path.begin(), path.end(), '\\', '/');
// clean up in case of multiples
auto contains_end = [&](std::string & in) -> bool {
size_t size = in.size();
if (!size) return false;
char end = in[size - 1];
return (end == '/' || end == ' ');
};
while (contains_end(path)) {
path.erase(path.size() - 1);
}
if (!path.empty()) {
path.insert(path.end(), '/');
}
path.insert(path.size(), file_name);
return path;
}
std::string header() {
std::ostringstream ss_entry;
// Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012
ss_entry << "\t\tg2log created log file at: " << g2::localtime_formatted(g2::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n";
ss_entry << "\t\tLOG format: [YYYY/MM/DD hh:mm:ss.uuu* LEVEL FILE:LINE] message\n\n"; // TODO: if(header)
return ss_entry.str();
}
std::string createLogFileName(const std::string& verified_prefix) {
std::stringstream oss_name;
oss_name << verified_prefix << ".g2log.";
oss_name << g2::localtime_formatted(g2::systemtime_now(), file_name_time_formatted);
oss_name << ".log";
return oss_name.str();
}
bool openLogFile(const std::string& complete_file_with_path, std::ofstream& outstream) {
std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream
mode |= std::ios_base::trunc;
outstream.open(complete_file_with_path, mode);
if (!outstream.is_open()) {
std::ostringstream ss_error;
ss_error << "FILE ERROR: could not open log file:[" << complete_file_with_path << "]";
ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate();
std::cerr << ss_error.str().c_str() << std::endl;
outstream.close();
return false;
}
return true;
}
std::unique_ptr<std::ofstream> createLogFile(const std::string& file_with_full_path) {
std::unique_ptr<std::ofstream> out(new std::ofstream);
std::ofstream & stream(*(out.get()));
bool success_with_open_file = openLogFile(file_with_full_path, stream);
if (false == success_with_open_file) {
out.release();
}
return out;
}
}
}
#endif // G2_FILESINK_HELPER_IPP_

View File

@ -43,10 +43,16 @@ int main(int argc, char** argv)
double pi_d = 3.1415926535897932384626433832795;
float pi_f = 3.1415926535897932384626433832795f;
g2LogWorker logger(argv[0], path_to_log_file);
g2::initializeLogging(&logger);
std::future<std::string> log_file_name = logger.logFileName();
// auto logger = g2LogWorker::createWithNoSink();
// auto sinkptr = std2::make_unique<g2::g2FileSink>(argv[0], path_to_log_file);
// auto log_handle = logger->addSink(std::move(singkptr), &g2::g2FileSink::fileWrite);
// g2::initializeLogging(logger.get());
auto logger_n_handle = g2LogWorker::createWithDefaultFileSink(argv[0], path_to_log_file);
g2::initializeLogging(logger_n_handle.first.get());
std::future<std::string> log_file_name = logger_n_handle.second->call(&g2::g2FileSink::fileName);
std::cout << "*** This is an example of g2log " << std::endl;
std::cout << "*** It WILL exit by a FATAL trigger in the end" << std::endl;
std::cout << "*** Please see the generated log and compare to " << std::endl;

View File

@ -19,7 +19,7 @@
#include "g2logworker.h"
#include "testing_helpers.h"
using namespace testing_helper__cleaner;
using namespace testing_helpers;
namespace { // anonymous
@ -28,9 +28,7 @@ namespace { // anonymous
g2LogWorker* g_logger_ptr = nullptr;
g2::SinkHandle<g2::g2FileSink>* g_filesink_handler = nullptr;
LogFileCleaner* g_cleaner_ptr = nullptr;
bool isTextAvailableInContent(const std::string &total_text, std::string msg_to_find) {
std::string content(total_text);
size_t location = content.find(msg_to_find);
@ -64,20 +62,19 @@ namespace { // anonymous
return add_count;
}
std::string setLogName(std::string new_file_to_create) {
std::string setLogName(std::string new_file_to_create) {
auto future_new_log = g_filesink_handler->call(&g2::g2FileSink::changeLogFile, new_file_to_create);
auto new_log = future_new_log.get();
if (!new_log.empty()) g_cleaner_ptr->addLogToClean(new_log);
return new_log;
}
std::string getLogName() {
return g_filesink_handler->call(&g2::g2FileSink::fileName).get();
return g_filesink_handler->call(&g2::g2FileSink::fileName).get();
}
} // anonymous
TEST(TestOf_GetFileName, Expecting_ValidLogFile) {
LOG(INFO) << "test_filechange, Retrieving file name: ";
@ -86,14 +83,14 @@ TEST(TestOf_GetFileName, Expecting_ValidLogFile) {
}
TEST(TestOf_ChangingLogFile, Expecting_NewLogFileUsed) {
auto old_log = g_filesink_handler->call(&g2::g2FileSink::fileName).get();
auto old_log = getLogName();
std::string name = setLogNameAndAddCount(name_path_1);
auto new_log = setLogName(name);
ASSERT_NE(old_log, new_log);
}
TEST(TestOf_ManyThreadsChangingLogFileName, Expecting_EqualNumberLogsCreated) {
auto old_log = g_filesink_handler->call(&g2::g2FileSink::fileName).get();
auto old_log = g_filesink_handler->call(&g2::g2FileSink::fileName).get();
if (!old_log.empty()) g_cleaner_ptr->addLogToClean(old_log);
LOG(INFO) << "SoManyThreadsAllDoingChangeFileName";
@ -113,32 +110,36 @@ TEST(TestOf_ManyThreadsChangingLogFileName, Expecting_EqualNumberLogsCreated) {
TEST(TestOf_IllegalLogFileName, Expecting_NoChangeToOriginalFileName) {
std::string original = getLogName();
std::cerr << "Below WILL print 'FiLE ERROR'. This is part of the testing and perfectly OK" << std::endl;
std::cerr << "****" << std::endl;
auto perhaps_a_name = setLogName("XY:/"); // does not exist
ASSERT_TRUE(perhaps_a_name.empty());
std::cerr << "****" << std::endl;
std::string post_illegal = getLogName();
ASSERT_STREQ(original.c_str(), post_illegal.c_str());
}
int main(int argc, char *argv[]) {
LogFileCleaner cleaner;
g_cleaner_ptr = &cleaner;
int return_value = 1;
std::stringstream cerrDump;
std::string last_log_file;
{
auto pair = g2LogWorker::createWithDefaultFileSink("ReplaceLogFile", name_path_2);
testing_helpers::ScopedOut scopedCerr(std::cerr, &cerrDump);
auto pair = g2LogWorker::createWithDefaultFileSink("ReplaceLogFile", name_path_1);
//g2LogWorker logger("ReplaceLogFile", name_path_2);
testing::InitGoogleTest(&argc, argv);
g_logger_ptr = pair.first.get(); // ugly but fine for this test
g_filesink_handler = pair.second.get();
g2::initializeLogging(g_logger_ptr);
cleaner.addLogToClean(g_filesink_handler->call(&g2::g2FileSink::fileName).get());
LOG(INFO) << "test_filechange demo*" << std::endl;
testing::InitGoogleTest(&argc, argv);
return_value = RUN_ALL_TESTS();
last_log_file = g_filesink_handler->call(&g2::g2FileSink::fileName).get();
std::cout << "log file at: " << last_log_file << std::endl;
//g2::shutDownLogging();
}
std::cout << "FINISHED WITH THE TESTING" << std::endl;

View File

@ -30,21 +30,17 @@ namespace {
std::ifstream in;
in.open(filename.c_str(), std::ios_base::in);
if (!in.is_open()) {
return ""; // error just return empty string - test will 'fault'
return {}; // error just return empty string - test will 'fault'
}
std::ostringstream oss;
oss << in.rdbuf();
std::string content(oss.str());
return content;
return oss.str();
}
} // end anonymous namespace
using namespace testing_helpers;
// LOG
TEST(IO_RestoreLogger, Expecting_Fine_To_ShutDownMultipleTimes)
{

View File

@ -0,0 +1,142 @@
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <vector>
#include <thread>
#include <chrono>
#include "testing_helpers.h"
#include "std2_make_unique.hpp"
#include "g2sink.h"
#include "g2sinkwrapper.h"
#include "g2sinkhandle.h"
#include "g2logmessage.hpp"
using namespace std;
using namespace std2;
using namespace testing_helpers;
class CoutSink {
stringstream buffer;
unique_ptr<ScopedOut> scope_ptr;
CoutSink() : scope_ptr(std2::make_unique<ScopedOut>(std::cout, &buffer)) { }
public:
void clear() { buffer.str(""); }
std::string string() { return buffer.str(); }
void save(g2::internal::LogEntry msg) { std::cout << msg; }
virtual ~CoutSink() final { }
static std::unique_ptr<CoutSink> createSink()
{ return std::unique_ptr<CoutSink>(new CoutSink); }
};
namespace {
typedef std::shared_ptr<g2::internal::SinkWrapper> SinkWrapperPtr;
typedef g2::internal::LogEntry LogEntry;
}
namespace g2 {
class Worker {
std::vector<SinkWrapperPtr> _container; // should be hidden in a pimple with a bg active object
std::unique_ptr<kjellkod::Active> _bg;
void bgSave(LogEntry msg) {
for (auto& sink : _container) {
sink->send(msg);
}
}
public:
Worker() : _bg {
kjellkod::Active::createActive()
}
{
}
~Worker() {
_bg->send([this] { _container.clear(); });
}
void save(LogEntry msg) {
_bg->send([this, msg] { bgSave(msg); });
} // will this be copied?
//this is guaranteed to work std::bind(&Worker::bgSave, this, msg)); }
template<typename T, typename DefaultLogCall>
std::unique_ptr< SinkHandle<T> > addSink(std::unique_ptr<T> unique, DefaultLogCall call) {
auto shared = std::shared_ptr<T>(unique.release());
auto sink = std::make_shared < internal::Sink<T> > (shared, call);
auto add_sink_call = [this, sink] { _container.push_back(sink);
};
auto wait_result = g2::spawn_task(add_sink_call, _bg.get());
wait_result.wait();
auto handle = std2::make_unique< SinkHandle<T> >(sink);
return handle;
}
};
} // g2
using namespace g2;
using namespace g2::internal;
TEST(Sink, CreateHandle) {
Worker worker;
auto handle = worker.addSink(CoutSink::createSink(), &CoutSink::save);
ASSERT_NE(nullptr, handle.get());
}
TEST(Sink, OneSink__VerifyMsgIn) {
Worker worker;
auto handle = worker.addSink(CoutSink::createSink(), &CoutSink::save);
worker.save("Hello World!");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
auto output = handle->call(&CoutSink::string);
ASSERT_EQ("Hello World!", output.get());
}
struct StringSink {
std::string raw;
void append(LogEntry entry) { raw.append(entry); }
std::string string(){return raw; }
};
TEST(Sink, DualSink__VerifyMsgIn) {
Worker worker;
auto h1 = worker.addSink(CoutSink::createSink(), &CoutSink::save);
auto h2 = worker.addSink(std2::make_unique<StringSink>(), &StringSink::append);
worker.save("Hello World!");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
auto first = h1->call(&CoutSink::string);
auto second = h2->call(&StringSink::string);
ASSERT_EQ("Hello World!", first.get());
ASSERT_EQ("Hello World!", second.get());
}
TEST(Sink, DeletedSink__Exptect_badweak_ptr___exception) {
auto worker = std2::make_unique<Worker>();
auto h1 = worker->addSink(CoutSink::createSink(), &CoutSink::save);
worker->save("Hello World!");
worker.reset();
auto first = h1->call(&CoutSink::string);
EXPECT_THROW(first.get(), std::bad_weak_ptr);
}