1
0
Fork 0
arangodb/3rdParty/iresearch/tests/tests_main.cpp

338 lines
10 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 by EMC Corporation, All Rights Reserved
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is EMC Corporation
///
/// @author Andrey Abramov
/// @author Vasiliy Nabatchikov
////////////////////////////////////////////////////////////////////////////////
#if defined(_MSC_VER)
#pragma warning(disable: 4101)
#pragma warning(disable: 4267)
#endif
#include <cmdline.h>
#if defined(_MSC_VER)
#pragma warning(default: 4267)
#pragma warning(default: 4101)
#endif
#if !defined(_MSC_VER)
#include <dlfcn.h> // for RTLD_NEXT
#endif
#include <signal.h> // for signal(...)/raise(...)
#if defined(_MSC_VER)
#pragma warning(disable: 4229)
#endif
#include <unicode/uclean.h> // for u_cleanup
#if defined(_MSC_VER)
#pragma warning(default: 4229)
#endif
#include "tests_shared.hpp"
#include "tests_config.hpp"
#include <ctime>
#include <formats/formats.hpp>
#include <utils/attributes.hpp>
#include "index/doc_generator.hpp"
#include "utils/file_utils.hpp"
#include "utils/log.hpp"
#include "utils/network_utils.hpp"
#include "utils/bitset.hpp"
#include "utils/runtime_utils.hpp"
#ifdef _MSC_VER
// +1 for \0 at end of string
#define mkdtemp(templ) 0 == _mkdir(0 == _mktemp_s(templ, strlen(templ) + 1) ? templ : nullptr) ? templ : nullptr
#endif
/* -------------------------------------------------------------------
* iteration_tracker
* ------------------------------------------------------------------*/
struct iteration_tracker : ::testing::Environment {
virtual void SetUp() {
++iteration;
}
static uint32_t iteration;
};
uint32_t iteration_tracker::iteration = (std::numeric_limits<uint32_t>::max)();
/* -------------------------------------------------------------------
* test_base
* ------------------------------------------------------------------*/
const std::string IRES_HELP("help");
const std::string IRES_LOG_LEVEL("ires_log_level");
const std::string IRES_LOG_STACK("ires_log_stack");
const std::string IRES_OUTPUT("ires_output");
const std::string IRES_OUTPUT_PATH("ires_output_path");
const std::string IRES_RESOURCE_DIR("ires_resource_dir");
const std::string test_env::test_results("test_detail.xml");
irs::utf8_path test_env::exec_path_;
irs::utf8_path test_env::exec_dir_;
irs::utf8_path test_env::exec_file_;
irs::utf8_path test_env::out_dir_;
irs::utf8_path test_env::resource_dir_;
irs::utf8_path test_env::res_dir_;
irs::utf8_path test_env::res_path_;
std::string test_env::test_name_;
int test_env::argc_;
char** test_env::argv_;
decltype(test_env::argv_ires_output_) test_env::argv_ires_output_;
uint32_t test_env::iteration() {
return iteration_tracker::iteration;
}
std::string test_env::resource( const std::string& name ) {
auto path = resource_dir_;
path /= name;
return path.utf8();
}
void test_env::prepare(const cmdline::parser& parser) {
if (parser.exist(IRES_HELP)) {
return;
}
make_directories();
{
auto log_level = parser.get<irs::logger::level_t>(IRES_LOG_LEVEL);
irs::logger::output_le(log_level, stderr); // set desired log level
if (parser.get<bool>(IRES_LOG_STACK)) {
irs::logger::stack_trace_level(log_level); // force enable stack trace
}
}
if (parser.exist(IRES_OUTPUT)) {
std::unique_ptr<char*[]> argv(new char*[2 + argc_]);
std::memcpy(argv.get(), argv_, sizeof(char*)*(argc_));
argv_ires_output_.append("--gtest_output=xml:").append(res_path_.utf8());
argv[argc_++] = &argv_ires_output_[0];
/* let last argv equals to nullptr */
argv[argc_] = nullptr;
argv_ = argv.release();
}
}
void test_env::make_directories() {
irs::utf8_path exec_path(argv_[0]);
auto exec_native = exec_path.native();
auto path_parts = irs::file_utils::path_parts(&exec_native[0]);
exec_path_ = exec_path;
exec_file_ = path_parts.basename;
exec_dir_ = path_parts.dirname;
test_name_ = irs::utf8_path(path_parts.stem).utf8();
if (out_dir_.native().empty()) {
out_dir_ = exec_dir_;
}
std::cout << "launching: " << exec_path_.utf8() << std::endl;
std::cout << "options:" << std::endl;
std::cout << "\t" << IRES_OUTPUT_PATH << ": " << out_dir_.utf8() << std::endl;
std::cout << "\t" << IRES_RESOURCE_DIR << ": " << resource_dir_.utf8() << std::endl;
out_dir_ = out_dir_.utf8_absolute();
(res_dir_ = out_dir_) /= test_name_;
// add timestamp to res_dir_
{
std::tm tinfo;
if (iresearch::localtime(tinfo, std::time(nullptr))) {
char buf[21]{};
strftime(buf, sizeof buf, "_%Y_%m_%d_%H_%M_%S", &tinfo);
res_dir_ += irs::string_ref(buf, sizeof buf - 1);
} else {
res_dir_ += "_unknown";
}
}
// add temporary string to res_dir_
{
char templ[] = "_XXXXXX";
res_dir_ += irs::string_ref(templ, sizeof templ - 1);
}
auto res_dir_templ = res_dir_.utf8();
res_dir_ = mkdtemp(&(res_dir_templ[0]));
(res_path_ = res_dir_) /= test_results;
}
void test_env::parse_command_line(cmdline::parser& cmd) {
static const auto log_level_reader = [](const std::string &s)->irs::logger::level_t {
static const std::map<std::string, irs::logger::level_t> log_levels = {
{ "FATAL", irs::logger::level_t::IRL_FATAL },
{ "ERROR", irs::logger::level_t::IRL_ERROR },
{ "WARN", irs::logger::level_t::IRL_WARN },
{ "INFO", irs::logger::level_t::IRL_INFO },
{ "DEBUG", irs::logger::level_t::IRL_DEBUG },
{ "TRACE", irs::logger::level_t::IRL_TRACE },
};
auto itr = log_levels.find(s);
if (itr == log_levels.end()) {
throw std::invalid_argument(s);
}
return itr->second;
};
cmd.add(IRES_HELP, '?', "print this message");
cmd.add(IRES_LOG_LEVEL, 0, "threshold log level <FATAL|ERROR|WARN|INFO|DEBUG|TRACE>", false, irs::logger::level_t::IRL_FATAL, log_level_reader);
cmd.add(IRES_LOG_STACK, 0, "always log stack trace", false, false);
cmd.add(IRES_OUTPUT, 0, "generate an XML report");
cmd.add(IRES_OUTPUT_PATH, 0, "output directory", false, out_dir_.utf8());
cmd.add(IRES_RESOURCE_DIR, 0, "resource directory", false, irs::utf8_path(IResearch_test_resource_dir).utf8());
cmd.parse(argc_, argv_);
if (cmd.exist(IRES_HELP)) {
std::cout << cmd.usage() << std::endl;
return;
}
resource_dir_ = cmd.get<std::string>(IRES_RESOURCE_DIR);
out_dir_ = cmd.get<std::string>(IRES_OUTPUT_PATH);
}
int test_env::initialize(int argc, char* argv[]) {
argc_ = argc;
argv_ = argv;
cmdline::parser cmd;
parse_command_line(cmd);
prepare(cmd);
::testing::AddGlobalTestEnvironment(new iteration_tracker());
::testing::InitGoogleTest(&argc_, argv_);
return RUN_ALL_TESTS();
}
void stack_trace_handler(int sig) {
// reset to default handler
signal(sig, SIG_DFL);
// print stack trace
iresearch::logger::stack_trace(iresearch::logger::IRL_FATAL); // IRL_FATAL is logged to stderr above
// re-signal to default handler (so we still get core dump if needed...)
raise(sig);
}
void install_stack_trace_handler() {
signal(SIGILL, stack_trace_handler);
signal(SIGSEGV, stack_trace_handler);
signal(SIGABRT, stack_trace_handler);
#ifndef _MSC_VER
signal(SIGBUS, stack_trace_handler);
#endif
}
#ifndef _MSC_VER
// override GCC 'throw' handler to print stack trace before throw
extern "C" {
#if defined(__APPLE__)
void __cxa_throw(void* ex, struct std::type_info* info, void(*dest)(void *)) {
static void(*rethrow)(void*,struct std::type_info*,void(*)(void*)) =
(void(*)(void*,struct std::type_info*,void(*)(void*)))dlsym(RTLD_NEXT, "__cxa_throw");
IR_FRMT_DEBUG("exception type: %s", reinterpret_cast<const std::type_info*>(info)->name());
IR_LOG_STACK_TRACE();
rethrow(ex, info, dest);
abort(); // otherwise MacOS reports "function declared 'noreturn' should not return"
}
#else
void __cxa_throw(void* ex, void* info, void(*dest)(void*)) {
static void(*rethrow)(void*,void*,void(*)(void*)) __attribute__ ((noreturn)) =
(void(*)(void*,void*,void(*)(void*)))dlsym(RTLD_NEXT, "__cxa_throw");
IR_FRMT_DEBUG("exception type: %s", reinterpret_cast<const std::type_info*>(info)->name());
IR_LOG_STACK_TRACE();
rethrow(ex, info, dest);
}
#endif
}
#endif
void test_base::SetUp() {
namespace tst = ::testing;
const tst::TestInfo* ti = tst::UnitTest::GetInstance()->current_test_info();
if (!ti) {
throw std::runtime_error("not a test suite");
}
test_case_dir_ = test_results_dir();
if (::testing::FLAGS_gtest_repeat > 1 || ::testing::FLAGS_gtest_repeat < 0) {
test_case_dir_ /=
std::string("iteration ").append(std::to_string(iteration()));
}
test_case_dir_ /= ti->test_case_name();
(test_dir_ = test_case_dir_) /= ti->name();
test_dir_.mkdir(false);
}
// -----------------------------------------------------------------------------
// --SECTION-- main
// -----------------------------------------------------------------------------
int main( int argc, char* argv[] ) {
install_stack_trace_handler();
const int code = test_env::initialize( argc, argv );
std::cout << "Path to test result directory: "
<< test_env::test_results_dir().utf8()
<< std::endl;
u_cleanup(); // cleanup ICU resources
return code;
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------