diff --git a/CMakeLists.txt b/CMakeLists.txt index 84a48c116..5c6330162 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -238,12 +238,18 @@ if (${BOOST_IGNORE_SYSTEM_PATHS} STREQUAL "ON") set(Boost_NO_SYSTEM_PATHS TRUE) endif() +set(OLD_LIB_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) if(STATIC) + if(MINGW) + set(CMAKE_FIND_LIBRARY_SUFFIXES .a) + endif() + set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_RUNTIME ON) endif() find_package(Boost 1.53 QUIET REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options) +set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES}) if(NOT Boost_FOUND) die("Could not find Boost libraries, please make sure you have installed Boost or libboost-all-dev (1.53 or 1.55+) or the equivalent") endif() diff --git a/README.md b/README.md index 295d05881..31af2de95 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,9 @@ Copyright (c) 2014-2015, The Monero Project ## Development Resources -Web: [monero.cc](http://monero.cc) -Mail: [dev@monero.cc](mailto:dev@monero.cc) +Web: [getmonero.org](https://getmonero.org) +Forum: [forum.getmonero.org](https://forum.getmonero.org) +Mail: [dev@getmonero.org](mailto:dev@getmonero.org) Github (staging): [https://github.com/monero-project/bitmonero](https://github.com/monero-project/bitmonero) Github (development): [http://github.com/monero-project/bitmonero/tree/development](http://github.com/monero-project/bitmonero/tree/development) IRC: [#monero-dev on Freenode](irc://chat.freenode.net/#monero-dev) @@ -32,7 +33,7 @@ Anyone is able to contribute to Monero. If you have a fix or code change, feel f Monero development can be supported directly through donations. -Both Monero and Bitcoin donations can be made to donate.monero.cc if using a client that supports the [OpenAlias](https://openalias.org) standard +Both Monero and Bitcoin donations can be made to donate.getmonero.org if using a client that supports the [OpenAlias](https://openalias.org) standard The Monero donation address is: 46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em (viewkey: e422831985c9205238ef84daf6805526c14d96fd7b059fe68c7ab98e495e5703) diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h index ab3cf67c6..fc8cc3841 100644 --- a/contrib/epee/include/console_handler.h +++ b/contrib/epee/include/console_handler.h @@ -26,11 +26,13 @@ #pragma once +#include "misc_log_ex.h" #include #include #include #include #include +#include namespace epee { @@ -267,17 +269,19 @@ namespace epee string_tools::trim(command); LOG_PRINT_L2("Read command: " << command); - if(0 == command.compare("exit") || 0 == command.compare("q")) - { - continue_handle = false; - }else if (command.empty()) + if (command.empty()) { continue; } else if(cmd_handler(command)) { continue; - } else + } + else if(0 == command.compare("exit") || 0 == command.compare("q")) + { + continue_handle = false; + } + else { std::cout << "unknown command: " << command << std::endl; std::cout << usage; @@ -290,7 +294,7 @@ namespace epee private: async_stdin_reader m_stdin_reader; - bool m_running = true; + std::atomic m_running = {true}; }; @@ -350,17 +354,11 @@ namespace epee return true; }*/ - /************************************************************************/ - /* */ - /************************************************************************/ - class console_handlers_binder - { - typedef boost::function &)> console_command_handler; - typedef std::map > command_handlers_map; - std::unique_ptr m_console_thread; - command_handlers_map m_command_handlers; - async_console_handler m_console_handler; + class command_handler { public: + typedef boost::function &)> callback; + typedef std::map > lookup; + std::string get_usage() { std::stringstream ss; @@ -376,12 +374,14 @@ namespace epee } return ss.str(); } - void set_handler(const std::string& cmd, const console_command_handler& hndlr, const std::string& usage = "") + + void set_handler(const std::string& cmd, const callback& hndlr, const std::string& usage = "") { - command_handlers_map::mapped_type & vt = m_command_handlers[cmd]; + lookup::mapped_type & vt = m_command_handlers[cmd]; vt.first = hndlr; vt.second = usage; } + bool process_command_vec(const std::vector& cmd) { if(!cmd.size()) @@ -399,14 +399,20 @@ namespace epee boost::split(cmd_v,cmd,boost::is_any_of(" "), boost::token_compress_on); return process_command_vec(cmd_v); } + private: + lookup m_command_handlers; + }; - /*template - bool start_handling(t_srv& srv, const std::string& usage_string = "") - { - start_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1)); - return true; - }*/ - + /************************************************************************/ + /* */ + /************************************************************************/ + class console_handlers_binder : public command_handler + { + typedef command_handler::callback console_command_handler; + typedef command_handler::lookup command_handlers_map; + std::unique_ptr m_console_thread; + async_console_handler m_console_handler; + public: bool start_handling(const std::string& prompt, const std::string& usage_string = "") { m_console_thread.reset(new boost::thread(boost::bind(&console_handlers_binder::run_handling, this, prompt, usage_string))); @@ -423,40 +429,33 @@ namespace epee { return m_console_handler.run(boost::bind(&console_handlers_binder::process_command_str, this, _1), prompt, usage_string); } - - /*template - bool run_handling(t_srv& srv, const std::string& usage_string) - { - return run_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1), usage_string); - }*/ }; - /* work around because of broken boost bind */ - template - class srv_console_handlers_binder: public console_handlers_binder - { - bool process_command_str(t_server* /*psrv*/, const std::string& cmd) - { - return console_handlers_binder::process_command_str(cmd); - } - public: - bool start_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string = "") - { - boost::thread(boost::bind(&srv_console_handlers_binder::run_handling, this, psrv, prompt, usage_string)).detach(); - return true; - } + ///* work around because of broken boost bind */ + //template + //class srv_console_handlers_binder: public command_handler + //{ + // async_console_handler m_console_handler; + //public: + // bool start_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string = "") + // { + // boost::thread(boost::bind(&srv_console_handlers_binder::run_handling, this, psrv, prompt, usage_string)).detach(); + // return true; + // } - bool run_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string) - { - return m_console_handler.run(psrv, boost::bind(&srv_console_handlers_binder::process_command_str, this, _1, _2), prompt, usage_string); - } + // bool run_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string) + // { + // return m_console_handler.run(psrv, boost::bind(&srv_console_handlers_binder::process_command_str, this, _1, _2), prompt, usage_string); + // } - void stop_handling() - { - m_console_handler.stop(); - } - - private: - async_console_handler m_console_handler; - }; + // void stop_handling() + // { + // m_console_handler.stop(); + // } + //private: + // bool process_command_str(t_server* /*psrv*/, const std::string& cmd) + // { + // return console_handlers_binder::process_command_str(cmd); + // } + //}; } diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 201460130..f812077f1 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -65,7 +65,7 @@ CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse json: \r\n" << query_info.m_body); \ uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ boost::value_initialized resp;\ - if(!callback_f(static_cast(req), static_cast(resp), m_conn_context)) \ + if(!callback_f(static_cast(req), static_cast(resp))) \ { \ LOG_ERROR("Failed to " << #callback_f << "()"); \ response_info.m_response_code = 500; \ @@ -90,7 +90,7 @@ CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \ uint64_t ticks1 = misc_utils::get_tick_count(); \ boost::value_initialized resp;\ - if(!callback_f(static_cast(req), static_cast(resp), m_conn_context)) \ + if(!callback_f(static_cast(req), static_cast(resp))) \ { \ LOG_ERROR("Failed to " << #callback_f << "()"); \ response_info.m_response_code = 500; \ @@ -173,7 +173,7 @@ epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ fail_resp.jsonrpc = "2.0"; \ fail_resp.id = req.id; \ - if(!callback_f(req.params, resp.result, fail_resp.error, m_conn_context)) \ + if(!callback_f(req.params, resp.result, fail_resp.error)) \ { \ epee::serialization::store_t_to_json(static_cast(fail_resp), response_info.m_body); \ return true; \ @@ -202,7 +202,7 @@ else if(callback_name == method_name) \ { \ PREPARE_OBJECTS_FROM_JSON(command_type) \ - if(!callback_f(req.params, resp.result, m_conn_context)) \ + if(!callback_f(req.params, resp.result)) \ { \ epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ fail_resp.jsonrpc = "2.0"; \ diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 80e574b06..520763e07 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -35,7 +35,7 @@ # ...except for FreeBSD, because FreeBSD is a special case that doesn't play well with # others. -find_package(MiniUpnpc QUIET) +find_package(Miniupnpc QUIET) # FreeBSD doesn't play well with the local copy, so default to using shared set(USE_SHARED_MINIUPNPC false) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 60501cefa..efc9c02f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -98,4 +98,5 @@ add_subdirectory(cryptonote_protocol) add_subdirectory(connectivity_tool) add_subdirectory(miner) add_subdirectory(simplewallet) +add_subdirectory(daemonizer) add_subdirectory(daemon) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 16c4b0ae3..620e1add9 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -39,8 +39,11 @@ set(common_private_headers boost_serialization_helper.h command_line.h dns_utils.h + http_connection.h int-util.h pod-class.h + rpc_client.h + scoped_message_writer.h unordered_containers_boost_serialization.h util.h varint.h) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 3e50b6af8..4ab93cce8 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -34,7 +34,65 @@ #include #include "include_base_utils.h" +#include using namespace epee; +namespace bf = boost::filesystem; + +namespace +{ + +/* + * The following two functions were taken from unbound-anchor.c, from + * the unbound library packaged with this source. The license and source + * can be found in $PROJECT_ROOT/external/unbound + */ + +/* Cert builtin commented out until it's used, as the compiler complains + +// return the built in root update certificate +static const char* +get_builtin_cert(void) +{ + return +// The ICANN CA fetched at 24 Sep 2010. Valid to 2028 +"-----BEGIN CERTIFICATE-----\n" +"MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n" +"TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n" +"BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTA5MTIyMzA0MTkxMloX\n" +"DTI5MTIxODA0MTkxMlowXTEOMAwGA1UEChMFSUNBTk4xJjAkBgNVBAsTHUlDQU5O\n" +"IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1JQ0FOTiBSb290IENB\n" +"MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDb\n" +"cLhPNNqc1NB+u+oVvOnJESofYS9qub0/PXagmgr37pNublVThIzyLPGCJ8gPms9S\n" +"G1TaKNIsMI7d+5IgMy3WyPEOECGIcfqEIktdR1YWfJufXcMReZwU4v/AdKzdOdfg\n" +"ONiwc6r70duEr1IiqPbVm5T05l1e6D+HkAvHGnf1LtOPGs4CHQdpIUcy2kauAEy2\n" +"paKcOcHASvbTHK7TbbvHGPB+7faAztABLoneErruEcumetcNfPMIjXKdv1V1E3C7\n" +"MSJKy+jAqqQJqjZoQGB0necZgUMiUv7JK1IPQRM2CXJllcyJrm9WFxY0c1KjBO29\n" +"iIKK69fcglKcBuFShUECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n" +"Af8EBAMCAf4wHQYDVR0OBBYEFLpS6UmDJIZSL8eZzfyNa2kITcBQMA0GCSqGSIb3\n" +"DQEBCwUAA4IBAQAP8emCogqHny2UYFqywEuhLys7R9UKmYY4suzGO4nkbgfPFMfH\n" +"6M+Zj6owwxlwueZt1j/IaCayoKU3QsrYYoDRolpILh+FPwx7wseUEV8ZKpWsoDoD\n" +"2JFbLg2cfB8u/OlE4RYmcxxFSmXBg0yQ8/IoQt/bxOcEEhhiQ168H2yE5rxJMt9h\n" +"15nu5JBSewrCkYqYYmaxyOC3WrVGfHZxVI7MpIFcGdvSb2a1uyuua8l0BKgk3ujF\n" +"0/wsHNeP22qNyVO+XVBzrM8fk8BSUFuiT/6tZTYXRtEt5aKQZgXbKU5dUF3jT9qg\n" +"j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk\n" +"-----END CERTIFICATE-----\n" + ; +} +*/ + +/** return the built in root DS trust anchor */ +static const char* +get_builtin_ds(void) +{ + return +". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n"; +} + +/************************************************************ + ************************************************************ + ***********************************************************/ + +} // anonymous namespace namespace tools { @@ -109,6 +167,8 @@ DNSResolver::DNSResolver() : m_data(new DNSResolverData()) // look for "/etc/resolv.conf" and "/etc/hosts" or platform equivalent ub_ctx_resolvconf(m_data->m_ub_context, &empty_string); ub_ctx_hosts(m_data->m_ub_context, &empty_string); + + ub_ctx_add_ta(m_data->m_ub_context, ::get_builtin_ds()); } DNSResolver::~DNSResolver() @@ -143,6 +203,8 @@ std::vector DNSResolver::get_ipv4(const std::string& url, bool& dns // call DNS resolver, blocking. if return value not zero, something went wrong if (!ub_resolve(m_data->m_ub_context, urlC, DNS_TYPE_A, DNS_CLASS_IN, &(result.ptr))) { + dnssec_available = (result.ptr->secure || (!result.ptr->secure && result.ptr->bogus)); + dnssec_valid = !result.ptr->bogus; if (result.ptr->havedata) { for (size_t i=0; result.ptr->data[i] != NULL; i++) @@ -175,6 +237,8 @@ std::vector DNSResolver::get_ipv6(const std::string& url, bool& dns // call DNS resolver, blocking. if return value not zero, something went wrong if (!ub_resolve(m_data->m_ub_context, urlC, DNS_TYPE_AAAA, DNS_CLASS_IN, &(result.ptr))) { + dnssec_available = (result.ptr->secure || (!result.ptr->secure && result.ptr->bogus)); + dnssec_valid = !result.ptr->bogus; if (result.ptr->havedata) { for (size_t i=0; result.ptr->data[i] != NULL; i++) @@ -207,6 +271,8 @@ std::vector DNSResolver::get_txt_record(const std::string& url, boo // call DNS resolver, blocking. if return value not zero, something went wrong if (!ub_resolve(m_data->m_ub_context, urlC, DNS_TYPE_TXT, DNS_CLASS_IN, &(result.ptr))) { + dnssec_available = (result.ptr->secure || (!result.ptr->secure && result.ptr->bogus)); + dnssec_valid = !result.ptr->bogus; if (result.ptr->havedata) { for (size_t i=0; result.ptr->data[i] != NULL; i++) diff --git a/src/common/http_connection.h b/src/common/http_connection.h new file mode 100644 index 000000000..759145009 --- /dev/null +++ b/src/common/http_connection.h @@ -0,0 +1,42 @@ +#pragma once + +#include "string_tools.h" +#include "net/http_client.h" + +namespace tools { + +class t_http_connection { +private: + epee::net_utils::http::http_simple_client * mp_http_client; + bool m_ok; +public: + static unsigned int const TIMEOUT = 200000; + + t_http_connection( + epee::net_utils::http::http_simple_client * p_http_client + , uint32_t ip + , uint16_t port + ) + : mp_http_client(p_http_client) + { + // TODO fix http client so that it accepts properly typed arguments + std::string ip_str = epee::string_tools::get_ip_string_from_int32(ip); + std::string port_str = boost::lexical_cast(port); + m_ok = mp_http_client->connect(ip_str, port_str, TIMEOUT); + } + + ~t_http_connection() + { + if (m_ok) + { + mp_http_client->disconnect(); + } + } + + bool is_open() + { + return m_ok; + } +}; // class t_http_connection + +} // namespace tools diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h new file mode 100644 index 000000000..a6d4b6cc1 --- /dev/null +++ b/src/common/rpc_client.h @@ -0,0 +1,132 @@ +#pragma once + +#include "common/http_connection.h" +#include "common/scoped_message_writer.h" +#include "rpc/core_rpc_server_commands_defs.h" +#include "storages/http_abstract_invoke.h" +#include "net/http_client.h" +#include "string_tools.h" +#include + +namespace tools +{ + class t_rpc_client final + { + private: + epee::net_utils::http::http_simple_client m_http_client; + uint32_t m_ip; + uint16_t m_port; + public: + t_rpc_client( + uint32_t ip + , uint16_t port + ) + : m_http_client{} + , m_ip{ip} + , m_port{port} + {} + + std::string build_url(std::string const & relative_url) + { + std::string result = + "http://" + + epee::string_tools::get_ip_string_from_int32(m_ip) + + ":" + + boost::lexical_cast(m_port) + + relative_url; + return result; + } + + template + bool basic_json_rpc_request( + T_req & req + , T_res & res + , std::string const & method_name + ) + { + std::string rpc_url = build_url("/json_rpc"); + t_http_connection connection(&m_http_client, m_ip, m_port); + + bool ok = connection.is_open(); + if (!ok) + { + fail_msg_writer() << "Couldn't connect to daemon"; + return false; + } + ok = ok && epee::net_utils::invoke_http_json_rpc(rpc_url, method_name, req, res, m_http_client); + if (!ok) + { + fail_msg_writer() << "Daemon request failed"; + return false; + } + else + { + return true; + } + } + + template + bool json_rpc_request( + T_req & req + , T_res & res + , std::string const & method_name + , std::string const & fail_msg + ) + { + std::string rpc_url = build_url("/json_rpc"); + t_http_connection connection(&m_http_client, m_ip, m_port); + + bool ok = connection.is_open(); + ok = ok && epee::net_utils::invoke_http_json_rpc(rpc_url, method_name, req, res, m_http_client); + if (!ok) + { + fail_msg_writer() << "Couldn't connect to daemon"; + return false; + } + else if (res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? + { + fail_msg_writer() << fail_msg << " -- " << res.status; + return false; + } + else + { + return true; + } + } + + template + bool rpc_request( + T_req & req + , T_res & res + , std::string const & relative_url + , std::string const & fail_msg + ) + { + std::string rpc_url = build_url(relative_url); + t_http_connection connection(&m_http_client, m_ip, m_port); + + bool ok = connection.is_open(); + ok = ok && epee::net_utils::invoke_http_json_remote_command2(rpc_url, req, res, m_http_client); + if (!ok) + { + fail_msg_writer() << "Couldn't connect to daemon"; + return false; + } + else if (res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? + { + fail_msg_writer() << fail_msg << " -- " << res.status; + return false; + } + else + { + return true; + } + } + + bool check_connection() + { + t_http_connection connection(&m_http_client, m_ip, m_port); + return connection.is_open(); + } + }; +} diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h new file mode 100644 index 000000000..70db54eae --- /dev/null +++ b/src/common/scoped_message_writer.h @@ -0,0 +1,95 @@ +#pragma once + +#include "misc_log_ex.h" +#include + +namespace tools +{ + +class scoped_message_writer +{ +private: + bool m_flush; + std::stringstream m_oss; + epee::log_space::console_colors m_color; + bool m_bright; + int m_log_level; +public: + scoped_message_writer( + epee::log_space::console_colors color = epee::log_space::console_color_default + , bool bright = false + , std::string&& prefix = std::string() + , int log_level = LOG_LEVEL_2 + ) + : m_flush(true) + , m_color(color) + , m_bright(bright) + , m_log_level(log_level) + { + m_oss << prefix; + } + + scoped_message_writer(scoped_message_writer&& rhs) + : m_flush(std::move(rhs.m_flush)) +#if defined(_MSC_VER) + , m_oss(std::move(rhs.m_oss)) +#else + // GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 + , m_oss(rhs.m_oss.str(), std::ios_base::out | std::ios_base::ate) +#endif + , m_color(std::move(rhs.m_color)) + , m_log_level(std::move(rhs.m_log_level)) + { + rhs.m_flush = false; + } + + scoped_message_writer(scoped_message_writer& rhs) = delete; + scoped_message_writer& operator=(scoped_message_writer& rhs) = delete; + scoped_message_writer& operator=(scoped_message_writer&& rhs) = delete; + + template + std::ostream& operator<<(const T& val) + { + m_oss << val; + return m_oss; + } + + ~scoped_message_writer() + { + if (m_flush) + { + m_flush = false; + + LOG_PRINT(m_oss.str(), m_log_level) + + if (epee::log_space::console_color_default == m_color) + { + std::cout << m_oss.str(); + } + else + { + epee::log_space::set_console_color(m_color, m_bright); + std::cout << m_oss.str(); + epee::log_space::reset_console_color(); + } + std::cout << std::endl; + } + } +}; + +inline scoped_message_writer success_msg_writer() +{ + return scoped_message_writer(epee::log_space::console_color_green, false, std::string(), LOG_LEVEL_2); +} + +inline scoped_message_writer msg_writer(epee::log_space::console_colors color = epee::log_space::console_color_default) +{ + return scoped_message_writer(color, false, std::string(), LOG_LEVEL_2); +} + +inline scoped_message_writer fail_msg_writer() +{ + return scoped_message_writer(epee::log_space::console_color_red, true, "Error: ", LOG_LEVEL_0); +} + +} // namespace tools diff --git a/src/common/util.cpp b/src/common/util.cpp index 94c1e042e..7d39bc4f4 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -326,7 +326,7 @@ std::string get_nix_version_display_string() std::string config_folder; #ifdef WIN32 - config_folder = get_special_folder_path(CSIDL_APPDATA, true) + "\\" + CRYPTONOTE_NAME; + config_folder = get_special_folder_path(CSIDL_COMMON_APPDATA, true) + "\\" + CRYPTONOTE_NAME; #else std::string pathRet; char* pszHome = getenv("HOME"); diff --git a/src/common/util.h b/src/common/util.h index 0d23135b0..883fe1e0f 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -58,6 +58,18 @@ namespace tools */ std::string get_default_data_dir(); +#ifdef WIN32 + /** + * @brief + * + * @param nfolder + * @param iscreate + * + * @return + */ + std::string get_special_folder_path(int nfolder, bool iscreate); +#endif + /*! \brief Returns the OS version string * * \details This is a wrapper around the primitives diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index e26945086..e5df844e7 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -51,6 +51,7 @@ // MONEY_SUPPLY - total number coins to be generated #define MONEY_SUPPLY ((uint64_t)(-1)) #define EMISSION_SPEED_FACTOR (20) +#define FINAL_SUBSIDY_PER_MINUTE ((uint64_t)300000000000) // 3 * pow(10, 11) #define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 20000 //size of block (bytes) after which reward for block calculated using block size @@ -61,9 +62,6 @@ #define FEE_PER_KB ((uint64_t)10000000000) // pow(10, 10) -// temporarily to allow backward compatibility during the switch to per-kb -//#define MINING_ALLOWED_LEGACY_FEE ((uint64_t)100000000000) // pow(10, 11) - #define ORPHANED_BLOCKS_MAX_COUNT 100 diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 4e669daa5..136d4f1d1 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -89,6 +89,7 @@ bool blockchain_storage::init(const std::string& config_folder, bool testnet) { CRITICAL_REGION_LOCAL(m_blockchain_lock); m_config_folder = config_folder; + m_testnet = testnet; LOG_PRINT_L0("Loading blockchain..."); const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; if(tools::unserialize_obj_from_file(*this, filename)) @@ -1365,7 +1366,7 @@ bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx return true; } //------------------------------------------------------------------ -bool blockchain_storage::add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height) +bool blockchain_storage::add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, size_t blob_size) { CRITICAL_REGION_LOCAL(m_blockchain_lock); struct add_transaction_input_visitor: public boost::static_visitor @@ -1404,6 +1405,7 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const } transaction_chain_entry ch_e; ch_e.m_keeper_block_height = bl_height; + ch_e.m_blob_size = blob_size; ch_e.tx = tx; auto i_r = m_transactions.insert(std::pair(tx_id, ch_e)); if(!i_r.second) @@ -1669,10 +1671,12 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt bvc.m_verifivation_failed = true; return false; } - size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx); + crypto::hash coinbase_hash = null_hash; + size_t coinbase_blob_size = 0; + get_transaction_hash(bl.miner_tx, coinbase_hash, coinbase_blob_size); size_t cumulative_block_size = coinbase_blob_size; //process transactions - if(!add_transaction_from_block(bl.miner_tx, get_transaction_hash(bl.miner_tx), id, get_current_blockchain_height())) + if(!add_transaction_from_block(bl.miner_tx, coinbase_hash, id, get_current_blockchain_height(), coinbase_blob_size)) { LOG_PRINT_L1("Block with id: " << id << " failed to add transaction to blockchain storage"); bvc.m_verifivation_failed = true; @@ -1706,7 +1710,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt return false; } - if(!add_transaction_from_block(tx, tx_id, id, get_current_blockchain_height())) + if(!add_transaction_from_block(tx, tx_id, id, get_current_blockchain_height(), blob_size)) { LOG_PRINT_L1("Block with id: " << id << " failed to add transaction to blockchain storage"); cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); @@ -1736,7 +1740,14 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt bei.bl = bl; bei.block_cumulative_size = cumulative_block_size; bei.cumulative_difficulty = current_diffic; - bei.already_generated_coins = already_generated_coins + base_reward; + + // In the "tail" state when the minimum subsidy (implemented in get_block_reward) is in effect, the number of + // coins will eventually exceed MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins + // at MONEY_SUPPLY. already_generated_coins is only used to compute the block subsidy and MONEY_SUPPLY yields a + // subsidy of 0 under the base formula and therefore the minimum subsidy >0 in the tail state. + + bei.already_generated_coins = base_reward < (MONEY_SUPPLY-already_generated_coins) ? already_generated_coins + base_reward : MONEY_SUPPLY; + if(m_blocks.size()) bei.cumulative_difficulty += m_blocks.back().cumulative_difficulty; @@ -1862,7 +1873,7 @@ bool blockchain_storage::update_checkpoints(const std::string& file_path, bool c else if (check_dns) { checkpoints dns_points; - cryptonote::load_checkpoints_from_dns(dns_points); + cryptonote::load_checkpoints_from_dns(dns_points, m_testnet); if (m_checkpoints.check_for_conflicts(dns_points)) { check_against_checkpoints(dns_points, false); diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index 38bdfbce7..505ed4574 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -219,6 +219,7 @@ namespace cryptonote std::atomic m_is_blockchain_storing; bool m_enforce_dns_checkpoints; + bool m_testnet; bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); bool pop_block_from_blockchain(); @@ -234,7 +235,7 @@ namespace cryptonote bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins); bool validate_transaction(const block& b, uint64_t height, const transaction& tx); bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); - bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height); + bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, size_t blob_size); bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes); bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id); bool get_last_n_blocks_sizes(std::vector& sz, size_t count); diff --git a/src/cryptonote_core/checkpoints_create.cpp b/src/cryptonote_core/checkpoints_create.cpp index 31ae9655b..43f926682 100644 --- a/src/cryptonote_core/checkpoints_create.cpp +++ b/src/cryptonote_core/checkpoints_create.cpp @@ -76,6 +76,7 @@ bool create_checkpoints(cryptonote::checkpoints& checkpoints) ADD_CHECKPOINT(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131"); ADD_CHECKPOINT(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8"); ADD_CHECKPOINT(249380, "654fb0a81ce3e5caf7e3264a70f447d4bd07586c08fa50f6638cc54da0a52b2d"); + ADD_CHECKPOINT(460000, "75037a7aed3e765db96c75bcf908f59d690a5f3390baebb9edeafd336a1c4831"); return true; } @@ -112,7 +113,7 @@ bool load_checkpoints_from_json(cryptonote::checkpoints& checkpoints, std::strin return true; } -bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints) +bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints, bool testnet) { // All four MoneroPulse domains have DNSSEC on and valid static const std::vector dns_urls = { "checkpoints.moneropulse.se" @@ -120,6 +121,12 @@ bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints) , "checkpoints.moneropulse.net" , "checkpoints.moneropulse.co" }; + + static const std::vector testnet_dns_urls = { "testpoints.moneropulse.se" + , "testpoints.moneropulse.org" + , "testpoints.moneropulse.net" + , "testpoints.moneropulse.co" + }; bool avail, valid; std::vector records; @@ -131,14 +138,34 @@ bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints) size_t cur_index = first_index; do { - records = tools::DNSResolver::instance().get_txt_record(dns_urls[cur_index], avail, valid); - if (records.size() == 0 || (avail && !valid)) + std::string url; + if (testnet) + { + url = testnet_dns_urls[cur_index]; + } + else + { + url = dns_urls[cur_index]; + } + + records = tools::DNSResolver::instance().get_txt_record(url, avail, valid); + if (!avail) + { + LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping."); + } + if (!valid) + { + LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping."); + } + + if (records.size() == 0 || !avail || !valid) { cur_index++; if (cur_index == dns_urls.size()) { cur_index = 0; } + records.clear(); continue; } break; @@ -146,13 +173,7 @@ bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints) if (records.size() == 0) { - LOG_PRINT_L1("Fetching MoneroPulse checkpoints failed, no TXT records available."); - return true; - } - - if (avail && !valid) - { - LOG_PRINT_L0("WARNING: MoneroPulse failed DNSSEC validation and/or returned no records"); + LOG_PRINT_L0("WARNING: All MoneroPulse checkpoint URLs failed DNSSEC validation and/or returned no records"); return true; } diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h index 569d437cf..8422e2b33 100644 --- a/src/cryptonote_core/checkpoints_create.h +++ b/src/cryptonote_core/checkpoints_create.h @@ -42,7 +42,7 @@ namespace cryptonote bool create_checkpoints(cryptonote::checkpoints& checkpoints); bool load_checkpoints_from_json(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath); - bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints); + bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints, bool testnet = false); bool load_new_checkpoints(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath); } // namespace cryptonote diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_core/cryptonote_basic_impl.cpp index a7416a7e4..f2d862e57 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/cryptonote_core/cryptonote_basic_impl.cpp @@ -60,6 +60,10 @@ namespace cryptonote { //----------------------------------------------------------------------------------------------- bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward) { uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> EMISSION_SPEED_FACTOR; + if (base_reward < FINAL_SUBSIDY_PER_MINUTE) + { + base_reward = FINAL_SUBSIDY_PER_MINUTE; + } //make it soft if (median_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) { diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 49ce63065..505feb563 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -42,6 +42,8 @@ using namespace epee; #include "cryptonote_format_utils.h" #include "misc_language.h" #include +#include "daemon/command_line_args.h" +#include "cryptonote_core/checkpoints_create.h" DISABLE_VS_WARNINGS(4355) @@ -108,14 +110,41 @@ namespace cryptonote return res; } //----------------------------------------------------------------------------------- + void core::stop() + { + graceful_exit(); + } + //----------------------------------------------------------------------------------- void core::init_options(boost::program_options::options_description& /*desc*/) { } //----------------------------------------------------------------------------------------------- - bool core::handle_command_line(const boost::program_options::variables_map& vm, bool testnet) + bool core::handle_command_line(const boost::program_options::variables_map& vm) { - auto data_dir_arg = testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; + m_testnet = command_line::get_arg(vm, daemon_args::arg_testnet_on); + + auto data_dir_arg = m_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; m_config_folder = command_line::get_arg(vm, data_dir_arg); + + auto data_dir = boost::filesystem::path(m_config_folder); + + if (!m_testnet) + { + cryptonote::checkpoints checkpoints; + if (!cryptonote::create_checkpoints(checkpoints)) + { + throw std::runtime_error("Failed to initialize checkpoints"); + } + set_checkpoints(std::move(checkpoints)); + + boost::filesystem::path json(JSON_HASH_FILE_NAME); + boost::filesystem::path checkpoint_json_hashfile_fullpath = data_dir / json; + + set_checkpoints_file_path(checkpoint_json_hashfile_fullpath.string()); + } + + + set_enforce_dns_checkpoints(command_line::get_arg(vm, daemon_args::arg_dns_checkpoints)); return true; } //----------------------------------------------------------------------------------------------- @@ -154,21 +183,21 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm, bool testnet) + bool core::init(const boost::program_options::variables_map& vm) { - bool r = handle_command_line(vm, testnet); + bool r = handle_command_line(vm); r = m_mempool.init(m_config_folder); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); - r = m_blockchain_storage.init(m_config_folder, testnet); + r = m_blockchain_storage.init(m_config_folder, m_testnet); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); // load json & DNS checkpoints, and verify them // with respect to what blocks we already have CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); - r = m_miner.init(vm, testnet); + r = m_miner.init(vm, m_testnet); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); return load_state_data(); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 795a6d214..bb53ecb81 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -72,7 +72,7 @@ namespace cryptonote miner& get_miner(){return m_miner;} static void init_options(boost::program_options::options_description& desc); - bool init(const boost::program_options::variables_map& vm, bool testnet); + bool init(const boost::program_options::variables_map& vm); bool set_genesis_block(const block& b); bool deinit(); static void set_fast_exit(); @@ -131,6 +131,8 @@ namespace cryptonote bool update_checkpoints(); + void stop(); + private: bool add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); bool add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block); @@ -148,7 +150,7 @@ namespace cryptonote bool check_tx_ring_signature(const txin_to_key& tx, const crypto::hash& tx_prefix_hash, const std::vector& sig); bool is_tx_spendtime_unlocked(uint64_t unlock_time); bool update_miner_block_template(); - bool handle_command_line(const boost::program_options::variables_map& vm, bool testnet); + bool handle_command_line(const boost::program_options::variables_map& vm); bool on_update_blocktemplate_interval(); bool check_tx_inputs_keyimages_diff(const transaction& tx); void graceful_exit(); @@ -171,6 +173,7 @@ namespace cryptonote uint64_t m_target_blockchain_height; + bool m_testnet; std::string m_checkpoints_path; time_t m_last_dns_checkpoints_update; time_t m_last_json_checkpoints_update; diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 4a0dcb148..4de8b82b8 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -27,12 +27,43 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(daemon_sources - daemon.cpp) + command_parser_executor.cpp + command_server.cpp + daemon.cpp + executor.cpp + main.cpp + rpc_command_executor.cpp +) set(daemon_headers) set(daemon_private_headers - daemon_commands_handler.h) + command_parser_executor.h + command_server.h + core.h + daemon.h + daemon_commands_handler.h + executor.h + p2p.h + protocol.h + rpc.h + rpc_command_executor.h + + # cryptonote_protocol + ../cryptonote_protocol/blobdatatype.h + ../cryptonote_protocol/cryptonote_protocol_defs.h + ../cryptonote_protocol/cryptonote_protocol_handler.h + ../cryptonote_protocol/cryptonote_protocol_handler.inl + ../cryptonote_protocol/cryptonote_protocol_handler_common.h + + # p2p + ../p2p/net_node.h + ../p2p/net_node.inl + ../p2p/net_node_common.h + ../p2p/net_peerlist.h + ../p2p/net_peerlist_boost_serialization.h + ../p2p/p2p_protocol_defs.h + ../p2p/stdafx.h) bitmonero_private_headers(daemon ${daemon_private_headers}) @@ -46,9 +77,7 @@ target_link_libraries(daemon cryptonote_core crypto common - otshell_utils - p2p - cryptonote_protocol + daemonizer ${Boost_CHRONO_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h new file mode 100644 index 000000000..bcf599128 --- /dev/null +++ b/src/daemon/command_line_args.h @@ -0,0 +1,76 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef DAEMON_COMMAND_LINE_ARGS_H +#define DAEMON_COMMAND_LINE_ARGS_H + +#include "common/command_line.h" +#include "cryptonote_config.h" +#include + +namespace daemon_args +{ + std::string const WINDOWS_SERVICE_NAME = "Monero Daemon"; + + const command_line::arg_descriptor arg_config_file = { + "config-file" + , "Specify configuration file" + , std::string(CRYPTONOTE_NAME ".conf") + }; + const command_line::arg_descriptor arg_log_file = { + "log-file" + , "Specify log file" + , "" + }; + const command_line::arg_descriptor arg_log_level = { + "log-level" + , "" + , LOG_LEVEL_0 + }; + const command_line::arg_descriptor> arg_command = { + "daemon_command" + , "Hidden" + }; + const command_line::arg_descriptor arg_os_version = { + "os-version" + , "OS for which this executable was compiled" + }; + const command_line::arg_descriptor arg_testnet_on = { + "testnet" + , "Run on testnet. The wallet must be launched with --testnet flag." + , false + }; + const command_line::arg_descriptor arg_dns_checkpoints = { + "enforce-dns-checkpointing" + , "checkpoints from DNS server will be enforced" + , false + }; + +} // namespace daemon_args + +#endif // DAEMON_COMMAND_LINE_ARGS_H diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp new file mode 100644 index 000000000..8f023da9a --- /dev/null +++ b/src/daemon/command_parser_executor.cpp @@ -0,0 +1,299 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "daemon/command_parser_executor.h" + +namespace daemonize { + +t_command_parser_executor::t_command_parser_executor( + uint32_t ip + , uint16_t port + , bool is_rpc + , cryptonote::core_rpc_server* rpc_server + ) + : m_executor(ip, port, is_rpc, rpc_server) +{} + +bool t_command_parser_executor::print_peer_list(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.print_peer_list(); +} + +bool t_command_parser_executor::save_blockchain(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.save_blockchain(); +} + +bool t_command_parser_executor::show_hash_rate(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.show_hash_rate(); +} + +bool t_command_parser_executor::hide_hash_rate(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.hide_hash_rate(); +} + +bool t_command_parser_executor::show_difficulty(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.show_difficulty(); +} + +bool t_command_parser_executor::print_connections(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.print_connections(); +} + +bool t_command_parser_executor::print_blockchain_info(const std::vector& args) +{ + if(!args.size()) + { + std::cout << "need block index parameter" << std::endl; + return false; + } + uint64_t start_index = 0; + uint64_t end_index = 0; + if(!epee::string_tools::get_xtype_from_string(start_index, args[0])) + { + std::cout << "wrong starter block index parameter" << std::endl; + return false; + } + if(args.size() >1 && !epee::string_tools::get_xtype_from_string(end_index, args[1])) + { + std::cout << "wrong end block index parameter" << std::endl; + return false; + } + + return m_executor.print_blockchain_info(start_index, end_index); +} + +bool t_command_parser_executor::set_log_level(const std::vector& args) +{ + if(args.size() != 1) + { + std::cout << "use: set_log " << std::endl; + return true; + } + + uint16_t l = 0; + if(!epee::string_tools::get_xtype_from_string(l, args[0])) + { + std::cout << "wrong number format, use: set_log " << std::endl; + return true; + } + + if(LOG_LEVEL_4 < l) + { + std::cout << "wrong number range, use: set_log " << std::endl; + return true; + } + + return m_executor.set_log_level(l); +} + +bool t_command_parser_executor::print_height(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.print_height(); +} + +bool t_command_parser_executor::print_block(const std::vector& args) +{ + if (args.empty()) + { + std::cout << "expected: print_block ( | )" << std::endl; + return false; + } + + const std::string& arg = args.front(); + try + { + uint64_t height = boost::lexical_cast(arg); + return m_executor.print_block_by_height(height); + } + catch (boost::bad_lexical_cast&) + { + crypto::hash block_hash; + if (parse_hash256(arg, block_hash)) + { + return m_executor.print_block_by_hash(block_hash); + } + } + + return false; +} + +bool t_command_parser_executor::print_transaction(const std::vector& args) +{ + if (args.empty()) + { + std::cout << "expected: print_tx " << std::endl; + return true; + } + + const std::string& str_hash = args.front(); + crypto::hash tx_hash; + if (parse_hash256(str_hash, tx_hash)) + { + m_executor.print_transaction(tx_hash); + } + + return true; +} + +bool t_command_parser_executor::print_transaction_pool_long(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.print_transaction_pool_long(); +} + +bool t_command_parser_executor::print_transaction_pool_short(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.print_transaction_pool_short(); +} + +bool t_command_parser_executor::start_mining(const std::vector& args) +{ + if(!args.size()) + { + std::cout << "Please specify a wallet address to mine for: start_mining [threads=1]" << std::endl; + return true; + } + + cryptonote::account_public_address adr; + if(!cryptonote::get_account_address_from_str(adr, false, args.front())) + { + if(!cryptonote::get_account_address_from_str(adr, true, args.front())) + { + std::cout << "target account address has wrong format" << std::endl; + return true; + } + std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl; + } + uint64_t threads_count = 1; + if(args.size() > 2) + { + return false; + } + else if(args.size() == 2) + { + bool ok = epee::string_tools::get_xtype_from_string(threads_count, args[1]); + threads_count = (ok && 0 < threads_count) ? threads_count : 1; + } + + m_executor.start_mining(adr, threads_count); + + return true; +} + +bool t_command_parser_executor::stop_mining(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.stop_mining(); +} + +bool t_command_parser_executor::stop_daemon(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.stop_daemon(); +} + +bool t_command_parser_executor::print_status(const std::vector& args) +{ + if (!args.empty()) return false; + + return m_executor.print_status(); +} + +bool t_command_parser_executor::set_limit(const std::vector& args) +{ + if(args.size()!=1) return false; + int limit; + try { + limit = std::stoi(args[0]); + } + catch(std::invalid_argument& ex) { + return false; + } + if (limit==-1) limit=128; + limit *= 1024; + + return m_executor.set_limit(limit); +} + +bool t_command_parser_executor::set_limit_up(const std::vector& args) +{ + if(args.size()!=1) return false; + int limit; + try { + limit = std::stoi(args[0]); + } + catch(std::invalid_argument& ex) { + return false; + } + if (limit==-1) limit=128; + limit *= 1024; + + return m_executor.set_limit_up(limit); +} + +bool t_command_parser_executor::set_limit_down(const std::vector& args) +{ + if(args.size()!=1) return false; + int limit; + try { + limit = std::stoi(args[0]); + } + catch(std::invalid_argument& ex) { + return false; + } + if (limit==-1) limit=128; + limit *= 1024; + + return m_executor.set_limit_down(limit); +} +} // namespace daemonize diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h new file mode 100644 index 000000000..07d2e70a9 --- /dev/null +++ b/src/daemon/command_parser_executor.h @@ -0,0 +1,98 @@ +/** +@file +@details + +@image html images/other/runtime-commands.png + +*/ + +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "daemon/rpc_command_executor.h" +#include "rpc/core_rpc_server.h" + +namespace daemonize { + +class t_command_parser_executor final +{ +private: + t_rpc_command_executor m_executor; +public: + t_command_parser_executor( + uint32_t ip + , uint16_t port + , bool is_rpc + , cryptonote::core_rpc_server* rpc_server = NULL + ); + + bool print_peer_list(const std::vector& args); + + bool save_blockchain(const std::vector& args); + + bool show_hash_rate(const std::vector& args); + + bool hide_hash_rate(const std::vector& args); + + bool show_difficulty(const std::vector& args); + + bool print_connections(const std::vector& args); + + bool print_blockchain_info(const std::vector& args); + + bool set_log_level(const std::vector& args); + + bool print_height(const std::vector& args); + + bool print_block(const std::vector& args); + + bool print_transaction(const std::vector& args); + + bool print_transaction_pool_long(const std::vector& args); + + bool print_transaction_pool_short(const std::vector& args); + + bool start_mining(const std::vector& args); + + bool stop_mining(const std::vector& args); + + bool stop_daemon(const std::vector& args); + + bool print_status(const std::vector& args); + + bool set_limit(const std::vector& args); + + bool set_limit_up(const std::vector& args); + + bool set_limit_down(const std::vector& args); + +}; + +} // namespace daemonize diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp new file mode 100644 index 000000000..601b12d57 --- /dev/null +++ b/src/daemon/command_server.cpp @@ -0,0 +1,213 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "cryptonote_config.h" +#include "version.h" +#include "daemon/command_server.h" + +namespace daemonize { + +namespace p = std::placeholders; + +t_command_server::t_command_server( + uint32_t ip + , uint16_t port + , bool is_rpc + , cryptonote::core_rpc_server* rpc_server + ) + : m_parser(ip, port, is_rpc, rpc_server) + , m_command_lookup() + , m_is_rpc(is_rpc) +{ + m_command_lookup.set_handler( + "q" + , [] (const std::vector& args) {return true;} + , "ignored" + ); + m_command_lookup.set_handler( + "help" + , std::bind(&t_command_server::help, this, p::_1) + , "Show this help" + ); + m_command_lookup.set_handler( + "print_height" + , std::bind(&t_command_parser_executor::print_height, &m_parser, p::_1) + , "Print local blockchain height" + ); + m_command_lookup.set_handler( + "print_pl" + , std::bind(&t_command_parser_executor::print_peer_list, &m_parser, p::_1) + , "Print peer list" + ); + m_command_lookup.set_handler( + "print_cn" + , std::bind(&t_command_parser_executor::print_connections, &m_parser, p::_1) + , "Print connections" + ); + m_command_lookup.set_handler( + "print_bc" + , std::bind(&t_command_parser_executor::print_blockchain_info, &m_parser, p::_1) + , "Print blockchain info in a given blocks range, print_bc []" + ); + m_command_lookup.set_handler( + "print_block" + , std::bind(&t_command_parser_executor::print_block, &m_parser, p::_1) + , "Print block, print_block | " + ); + m_command_lookup.set_handler( + "print_tx" + , std::bind(&t_command_parser_executor::print_transaction, &m_parser, p::_1) + , "Print transaction, print_tx " + ); + m_command_lookup.set_handler( + "start_mining" + , std::bind(&t_command_parser_executor::start_mining, &m_parser, p::_1) + , "Start mining for specified address, start_mining [threads=1]" + ); + m_command_lookup.set_handler( + "stop_mining" + , std::bind(&t_command_parser_executor::stop_mining, &m_parser, p::_1) + , "Stop mining" + ); + m_command_lookup.set_handler( + "print_pool" + , std::bind(&t_command_parser_executor::print_transaction_pool_long, &m_parser, p::_1) + , "Print transaction pool (long format)" + ); + m_command_lookup.set_handler( + "print_pool_sh" + , std::bind(&t_command_parser_executor::print_transaction_pool_short, &m_parser, p::_1) + , "Print transaction pool (short format)" + ); + m_command_lookup.set_handler( + "show_hr" + , std::bind(&t_command_parser_executor::show_hash_rate, &m_parser, p::_1) + , "Start showing hash rate" + ); + m_command_lookup.set_handler( + "hide_hr" + , std::bind(&t_command_parser_executor::hide_hash_rate, &m_parser, p::_1) + , "Stop showing hash rate" + ); + m_command_lookup.set_handler( + "save" + , std::bind(&t_command_parser_executor::save_blockchain, &m_parser, p::_1) + , "Save blockchain" + ); + m_command_lookup.set_handler( + "set_log" + , std::bind(&t_command_parser_executor::set_log_level, &m_parser, p::_1) + , "set_log - Change current log detalization level, is a number 0-4" + ); + m_command_lookup.set_handler( + "diff" + , std::bind(&t_command_parser_executor::show_difficulty, &m_parser, p::_1) + , "Show difficulty" + ); + m_command_lookup.set_handler( + "stop_daemon" + , std::bind(&t_command_parser_executor::stop_daemon, &m_parser, p::_1) + , "Stop the daemon" + ); + m_command_lookup.set_handler( + "exit" + , std::bind(&t_command_parser_executor::stop_daemon, &m_parser, p::_1) + , "Stop the daemon" + ); + m_command_lookup.set_handler( + "print_status" + , std::bind(&t_command_parser_executor::print_status, &m_parser, p::_1) + , "Prints daemon status" + ); + m_command_lookup.set_handler( + "limit" + , std::bind(&t_command_parser_executor::set_limit, &m_parser, p::_1) + , "limit - Set download and upload limit" + ); + m_command_lookup.set_handler( + "limit-up" + , std::bind(&t_command_parser_executor::set_limit_up, &m_parser, p::_1) + , "limit - Set upload limit" + ); + m_command_lookup.set_handler( + "limit-down" + , std::bind(&t_command_parser_executor::set_limit_down, &m_parser, p::_1) + , "limit - Set download limit" + ); +} + +bool t_command_server::process_command_str(const std::string& cmd) +{ + return m_command_lookup.process_command_str(cmd); +} + +bool t_command_server::process_command_vec(const std::vector& cmd) +{ + bool result = m_command_lookup.process_command_vec(cmd); + if (!result) + { + help(std::vector()); + } + return result; +} + +bool t_command_server::start_handling() +{ + if (m_is_rpc) return false; + + m_command_lookup.start_handling("", get_commands_str()); + + return true; +} + +void t_command_server::stop_handling() +{ + if (m_is_rpc) return; + + m_command_lookup.stop_handling(); +} + +bool t_command_server::help(const std::vector& args) +{ + std::cout << get_commands_str() << std::endl; + return true; +} + +std::string t_command_server::get_commands_str() +{ + std::stringstream ss; + ss << CRYPTONOTE_NAME << " v" << MONERO_VERSION_FULL << std::endl; + ss << "Commands: " << std::endl; + std::string usage = m_command_lookup.get_usage(); + boost::replace_all(usage, "\n", "\n "); + usage.insert(0, " "); + ss << usage << std::endl; + return ss.str(); +} + +} // namespace daemonize diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h new file mode 100644 index 000000000..494f44d8b --- /dev/null +++ b/src/daemon/command_server.h @@ -0,0 +1,75 @@ +/** +@file +@details + + +Passing RPC commands: + +@image html images/other/runtime-commands.png + +*/ + +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "console_handler.h" +#include "daemon/command_parser_executor.h" + +namespace daemonize { + +class t_command_server { +private: + t_command_parser_executor m_parser; + epee::console_handlers_binder m_command_lookup; + bool m_is_rpc; + +public: + t_command_server( + uint32_t ip + , uint16_t port + , bool is_rpc = true + , cryptonote::core_rpc_server* rpc_server = NULL + ); + + bool process_command_str(const std::string& cmd); + + bool process_command_vec(const std::vector& cmd); + + bool start_handling(); + + void stop_handling(); + +private: + bool help(const std::vector& args); + + std::string get_commands_str(); +}; + +} // namespace daemonize diff --git a/src/daemon/core.h b/src/daemon/core.h new file mode 100644 index 000000000..6564e5314 --- /dev/null +++ b/src/daemon/core.h @@ -0,0 +1,99 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "cryptonote_core/checkpoints_create.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "misc_log_ex.h" +#include +#include +#include "daemon/command_line_args.h" + +namespace daemonize +{ + +class t_core final +{ +public: + static void init_options(boost::program_options::options_description & option_spec) + { + cryptonote::core::init_options(option_spec); + cryptonote::miner::init_options(option_spec); + } +private: + typedef cryptonote::t_cryptonote_protocol_handler t_protocol_raw; + cryptonote::core m_core; + // TEMPORARY HACK - Yes, this creates a copy, but otherwise the original + // variable map could go out of scope before the run method is called + boost::program_options::variables_map const m_vm_HACK; +public: + t_core( + boost::program_options::variables_map const & vm + ) + : m_core{nullptr} + , m_vm_HACK{vm} + { + } + + // TODO - get rid of circular dependencies in internals + void set_protocol(t_protocol_raw & protocol) + { + m_core.set_cryptonote_protocol(&protocol); + } + + void run() + { + //initialize core here + LOG_PRINT_L0("Initializing core..."); + if (!m_core.init(m_vm_HACK)) + { + throw std::runtime_error("Failed to initialize core"); + } + LOG_PRINT_L0("Core initialized OK"); + } + + cryptonote::core & get() + { + return m_core; + } + + ~t_core() + { + LOG_PRINT_L0("Deinitializing core..."); + try { + m_core.deinit(); + m_core.set_cryptonote_protocol(nullptr); + } catch (...) { + LOG_PRINT_L0("Failed to deinitialize core..."); + } + } +}; + +} diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 4a93a0db8..ec12c281c 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1,21 +1,21 @@ -// Copyright (c) 2014-2015, The Monero Project -// +// Copyright (c) 2014, The Monero Project +// // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. -// +// // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL @@ -25,305 +25,136 @@ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +// // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -// node.cpp : Defines the entry point for the console application. -// Does this file exist? +#include "daemon/daemon.h" - -#include "include_base_utils.h" +#include "common/util.h" +#include "daemon/core.h" +#include "daemon/p2p.h" +#include "daemon/protocol.h" +#include "daemon/rpc.h" +#include "daemon/command_server.h" +#include "misc_log_ex.h" #include "version.h" -#include "../../contrib/epee/include/syncobj.h" - -using namespace epee; - #include +#include +#include -#include "crypto/hash.h" -#include "console_handler.h" -#include "p2p/net_node.h" -#include "cryptonote_config.h" -#include "cryptonote_core/checkpoints_create.h" -#include "cryptonote_core/checkpoints.h" -#include "cryptonote_core/cryptonote_core.h" -#include "rpc/core_rpc_server.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "daemon_commands_handler.h" -#include "version.h" +namespace daemonize { -#if defined(WIN32) -#include -#endif +struct t_internals { +private: + t_protocol protocol; +public: + t_core core; + t_p2p p2p; + t_rpc rpc; -namespace po = boost::program_options; + t_internals( + boost::program_options::variables_map const & vm + ) + : core{vm} + , protocol{vm, core} + , p2p{vm, protocol} + , rpc{vm, core, p2p} + { + // Handle circular dependencies + protocol.set_p2p_endpoint(p2p.get()); + core.set_protocol(protocol.get()); + } +}; -unsigned int epee::g_test_dbg_lock_sleep = 0; - -namespace +void t_daemon::init_options(boost::program_options::options_description & option_spec) { - const command_line::arg_descriptor arg_config_file = {"config-file", "Specify configuration file", std::string(CRYPTONOTE_NAME ".conf")}; - const command_line::arg_descriptor arg_os_version = {"os-version", ""}; - const command_line::arg_descriptor arg_log_file = {"log-file", "", ""}; - const command_line::arg_descriptor arg_log_level = {"log-level", "", LOG_LEVEL_0}; - const command_line::arg_descriptor arg_console = {"no-console", "Disable daemon console commands"}; - const command_line::arg_descriptor arg_testnet_on = { - "testnet" - , "Run on testnet. The wallet must be launched with --testnet flag." - , false - }; - const command_line::arg_descriptor arg_dns_checkpoints = {"enforce-dns-checkpointing", "checkpoints from DNS server will be enforced", false}; - const command_line::arg_descriptor arg_test_drop_download = {"test-drop-download", "For net tests: in download, discard ALL blocks instead checking/saving them (very fast)"}; - const command_line::arg_descriptor arg_test_drop_download_height = {"test-drop-download-height", "Like test-drop-download but disards only after around certain height", 0}; - const command_line::arg_descriptor arg_save_graph = {"save-graph", "Save data for dr monero", false}; - const command_line::arg_descriptor test_dbg_lock_sleep = {"test-dbg-lock-sleep", "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests.", 0}; + t_core::init_options(option_spec); + t_p2p::init_options(option_spec); + t_rpc::init_options(option_spec); } -bool command_line_preprocessor(const boost::program_options::variables_map& vm) +t_daemon::t_daemon( + boost::program_options::variables_map const & vm + ) + : mp_internals{new t_internals{vm}} +{} + +t_daemon::~t_daemon() = default; + +// MSVC is brain-dead and can't default this... +t_daemon::t_daemon(t_daemon && other) { - bool exit = false; - if (command_line::get_arg(vm, command_line::arg_version)) + if (this != &other) { - std::cout << CRYPTONOTE_NAME << " v" << MONERO_VERSION_FULL << ENDL; - exit = true; + mp_internals = std::move(other.mp_internals); + other.mp_internals.reset(nullptr); } - if (command_line::get_arg(vm, arg_os_version)) - { - std::cout << "OS: " << tools::get_os_version_string() << ENDL; - exit = true; - } - - if (exit) - { - return true; - } - - int new_log_level = command_line::get_arg(vm, arg_log_level); - if(new_log_level < LOG_LEVEL_MIN || new_log_level > LOG_LEVEL_MAX) - { - LOG_PRINT_L0("Wrong log level value: "); - } - else if (log_space::get_set_log_detalisation_level(false) != new_log_level) - { - log_space::get_set_log_detalisation_level(true, new_log_level); - int otshell_utils_log_level = 100 - (new_log_level * 25); - gCurrentLogger.setDebugLevel(otshell_utils_log_level); - LOG_PRINT_L0("LOG_LEVEL set to " << new_log_level); - } - - return false; } -int main(int argc, char* argv[]) +// or this +t_daemon & t_daemon::operator=(t_daemon && other) { - string_tools::set_module_name_and_folder(argv[0]); -#ifdef WIN32 - _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); -#endif - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - LOG_PRINT_L0("Starting..."); - - nOT::nUtils::cFilesystemUtils::CreateDirTree("log/dr-monero/net/"); - _warn_c("test","Starting program (a test message)"); - _warn_c("main/program","Starting program"); - - TRY_ENTRY(); - - boost::filesystem::path default_data_path {tools::get_default_data_dir()}; - boost::filesystem::path default_testnet_data_path {default_data_path / "testnet"}; - - po::options_description desc_cmd_only("Command line options"); - po::options_description desc_cmd_sett("Command line options and settings options"); - - command_line::add_arg(desc_cmd_only, command_line::arg_help); - command_line::add_arg(desc_cmd_only, command_line::arg_version); - command_line::add_arg(desc_cmd_only, arg_os_version); - // tools::get_default_data_dir() can't be called during static initialization - command_line::add_arg(desc_cmd_only, command_line::arg_data_dir, default_data_path.string()); - command_line::add_arg(desc_cmd_only, command_line::arg_testnet_data_dir, default_testnet_data_path.string()); - command_line::add_arg(desc_cmd_only, arg_config_file); - - command_line::add_arg(desc_cmd_sett, arg_log_file); - command_line::add_arg(desc_cmd_sett, arg_log_level); - command_line::add_arg(desc_cmd_sett, arg_console); - command_line::add_arg(desc_cmd_sett, arg_testnet_on); - command_line::add_arg(desc_cmd_sett, arg_dns_checkpoints); - command_line::add_arg(desc_cmd_sett, arg_test_drop_download); - command_line::add_arg(desc_cmd_sett, arg_test_drop_download_height); - command_line::add_arg(desc_cmd_sett, arg_save_graph); - command_line::add_arg(desc_cmd_sett, test_dbg_lock_sleep); - - cryptonote::core::init_options(desc_cmd_sett); - cryptonote::core_rpc_server::init_options(desc_cmd_sett); - nodetool::node_server >::init_options(desc_cmd_sett); - cryptonote::miner::init_options(desc_cmd_sett); - - po::options_description desc_options("Allowed options"); - desc_options.add(desc_cmd_only).add(desc_cmd_sett); - - po::variables_map vm; - bool r = command_line::handle_error_helper(desc_options, [&]() + if (this != &other) { - po::store(po::parse_command_line(argc, argv, desc_options), vm); - po::notify(vm); + mp_internals = std::move(other.mp_internals); + other.mp_internals.reset(nullptr); + } + return *this; +} +bool t_daemon::run(bool interactive) +{ + if (nullptr == mp_internals) + { + throw std::runtime_error{"Can't run stopped daemon"}; + } + tools::signal_handler::install(std::bind(&daemonize::t_daemon::stop, this)); + + try + { + mp_internals->core.run(); + mp_internals->rpc.run(); + + daemonize::t_command_server* rpc_commands; + + if (interactive) + { + rpc_commands = new daemonize::t_command_server(0, 0, false, mp_internals->rpc.get_server()); + rpc_commands->start_handling(); + } + + mp_internals->p2p.run(); // blocks until p2p goes down + + if (interactive) + { + rpc_commands->stop_handling(); + } + + mp_internals->rpc.stop(); + LOG_PRINT("Node stopped.", LOG_LEVEL_0); return true; - }); - if (!r) - return 1; - - if (command_line::get_arg(vm, command_line::arg_help)) + } + catch (std::exception const & ex) { - std::cout << CRYPTONOTE_NAME << " v" << MONERO_VERSION_FULL << ENDL << ENDL; - std::cout << desc_options << std::endl; + LOG_ERROR("Uncaught exception! " << ex.what()); return false; } - - bool testnet_mode = command_line::get_arg(vm, arg_testnet_on); - - auto data_dir_arg = testnet_mode ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; - - std::string data_dir = command_line::get_arg(vm, data_dir_arg); - tools::create_directories_if_necessary(data_dir); - std::string config = command_line::get_arg(vm, arg_config_file); - - boost::filesystem::path data_dir_path(data_dir); - boost::filesystem::path config_path(config); - if (!config_path.has_parent_path()) + catch (...) { - config_path = data_dir_path / config_path; + LOG_ERROR("Uncaught exception!"); + return false; } - - boost::system::error_code ec; - if (boost::filesystem::exists(config_path, ec)) - { - po::store(po::parse_config_file(config_path.string().c_str(), desc_cmd_sett), vm); - } - - //set up logging options - boost::filesystem::path log_file_path(command_line::get_arg(vm, arg_log_file)); - if (log_file_path.empty()) - log_file_path = log_space::log_singletone::get_default_log_file(); - std::string log_dir; - log_dir = log_file_path.has_parent_path() ? log_file_path.parent_path().string() : log_space::log_singletone::get_default_log_folder(); - - log_space::log_singletone::add_logger(LOGGER_FILE, log_file_path.filename().string().c_str(), log_dir.c_str()); - LOG_PRINT_L0(CRYPTONOTE_NAME << " v" << MONERO_VERSION_FULL); - - if (command_line_preprocessor(vm)) - { - return 0; - } - - LOG_PRINT("Module folder: " << argv[0], LOG_LEVEL_0); - - bool res = true; - cryptonote::checkpoints checkpoints; - res = cryptonote::create_checkpoints(checkpoints); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize checkpoints"); - boost::filesystem::path json(JSON_HASH_FILE_NAME); - boost::filesystem::path checkpoint_json_hashfile_fullpath = data_dir / json; - - //create objects and link them - cryptonote::core ccore(NULL); - - // tell core if we're enforcing dns checkpoints - bool enforce_dns = command_line::get_arg(vm, arg_dns_checkpoints); - ccore.set_enforce_dns_checkpoints(enforce_dns); - - if (testnet_mode) { - LOG_PRINT_L0("Starting in testnet mode!"); - } else { - ccore.set_checkpoints(std::move(checkpoints)); - ccore.set_checkpoints_file_path(checkpoint_json_hashfile_fullpath.string()); - } - - cryptonote::t_cryptonote_protocol_handler cprotocol(ccore, NULL); - nodetool::node_server > p2psrv { - cprotocol - , testnet_mode ? std::move(config::testnet::NETWORK_ID) : std::move(config::NETWORK_ID) - }; - cryptonote::core_rpc_server rpc_server {ccore, p2psrv, testnet_mode}; - cprotocol.set_p2p_endpoint(&p2psrv); - ccore.set_cryptonote_protocol(&cprotocol); - std::shared_ptr dch(new daemon_cmmands_handler(p2psrv, testnet_mode)); - if(command_line::has_arg(vm, arg_save_graph)) - p2psrv.set_save_graph(true); - - epee::g_test_dbg_lock_sleep = command_line::get_arg(vm, test_dbg_lock_sleep); - - //initialize core here - LOG_PRINT_L0("Initializing core..."); - res = ccore.init(vm, testnet_mode); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); - if (command_line::get_arg(vm, arg_test_drop_download)) - ccore.test_drop_download(); - - ccore.test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height)); - LOG_PRINT_L0("Core initialized OK"); - - //initialize objects - LOG_PRINT_L0("Initializing P2P server..."); - res = p2psrv.init(vm, testnet_mode); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize P2P server."); - LOG_PRINT_L0("P2P server initialized OK"); - - LOG_PRINT_L0("Initializing protocol..."); - res = cprotocol.init(vm); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize protocol."); - LOG_PRINT_L0("Protocol initialized OK"); - - LOG_PRINT_L0("Initializing core RPC server..."); - res = rpc_server.init(vm); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core RPC server."); - LOG_PRINT_GREEN("Core RPC server initialized OK on port: " << rpc_server.get_binded_port(), LOG_LEVEL_0); - - // start components - if(!command_line::has_arg(vm, arg_console)) - { - dch->start_handling(); - } - - LOG_PRINT_L0("Starting core RPC server..."); - res = rpc_server.run(2, false); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core RPC server."); - LOG_PRINT_L0("Core RPC server started ok"); - - tools::signal_handler::install([&dch, &p2psrv] { - dch->stop_handling(); - p2psrv.send_stop_signal(); - }); - - LOG_PRINT_L0("Starting P2P net loop..."); - p2psrv.run(); - LOG_PRINT_L0("P2P net loop stopped"); - - //stop components - dch->stop_handling(); - dch.reset(); - LOG_PRINT_L0("Stopping core rpc server..."); - rpc_server.send_stop_signal(); - rpc_server.timed_wait_server_stop(5000); - - //deinitialize components - LOG_PRINT_L0("Deinitializing core..."); - ccore.deinit(); - LOG_PRINT_L0("Deinitializing RPC server ..."); - rpc_server.deinit(); - LOG_PRINT_L0("Deinitializing protocol..."); - cprotocol.deinit(); - LOG_PRINT_L0("Deinitializing P2P..."); - p2psrv.deinit(); - - - ccore.set_cryptonote_protocol(NULL); - cprotocol.set_p2p_endpoint(NULL); - - epee::net_utils::data_logger::kill_instance(); - LOG_PRINT("Node stopped.", LOG_LEVEL_0); - return 0; - - CATCH_ENTRY_L0("main", 1); } +void t_daemon::stop() +{ + if (nullptr == mp_internals) + { + throw std::runtime_error{"Can't stop stopped daemon"}; + } + mp_internals->p2p.stop(); + mp_internals->rpc.stop(); + mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return +} + +} // namespace daemonize diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h new file mode 100644 index 000000000..b0869b25b --- /dev/null +++ b/src/daemon/daemon.h @@ -0,0 +1,54 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include + +namespace daemonize { + +class t_internals; + +class t_daemon final { +public: + static void init_options(boost::program_options::options_description & option_spec); +private: + std::unique_ptr mp_internals; +public: + t_daemon( + boost::program_options::variables_map const & vm + ); + t_daemon(t_daemon && other); + t_daemon & operator=(t_daemon && other); + ~t_daemon(); + + bool run(bool interactive = false); + void stop(); +}; +} diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index c23df0443..7baca596d 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -1,35 +1,7 @@ -// Copyright (c) 2014-2015, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -/* This isn't a header file, may want to refactor this... */ #pragma once #include @@ -42,21 +14,18 @@ #include "version.h" #include "../../contrib/otshell_utils/utils.hpp" -/*! - * \brief I don't really know right now - * - * - */ +//#include "net/net_helper.h" +//#include "../p2p/p2p_protocol_defs.h" +//#include "../p2p/net_peerlist_boost_serialization.h" +//#include "net/local_ip.h" +//#include "crypto/crypto.h" +//#include "storages/levin_abstract_invoke2.h" + class daemon_cmmands_handler { nodetool::node_server >& m_srv; public: - daemon_cmmands_handler( - nodetool::node_server >& srv - , bool testnet - ) - : m_srv(srv) - , m_testnet(testnet) + daemon_cmmands_handler(nodetool::node_server >& srv):m_srv(srv) { m_cmd_binder.set_handler("help", boost::bind(&daemon_cmmands_handler::help, this, _1), "Show this help"); m_cmd_binder.set_handler("print_pl", boost::bind(&daemon_cmmands_handler::print_pl, this, _1), "Print peer list"); @@ -75,14 +44,10 @@ public: m_cmd_binder.set_handler("save", boost::bind(&daemon_cmmands_handler::save, this, _1), "Save blockchain"); m_cmd_binder.set_handler("set_log", boost::bind(&daemon_cmmands_handler::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); m_cmd_binder.set_handler("diff", boost::bind(&daemon_cmmands_handler::diff, this, _1), "Show difficulty"); + m_cmd_binder.set_handler("limit-up", boost::bind(&daemon_cmmands_handler::limit_up, this, _1), "Set upload limit"); + m_cmd_binder.set_handler("limit-down", boost::bind(&daemon_cmmands_handler::limit_down, this, _1), "Set download limit"); + m_cmd_binder.set_handler("limit", boost::bind(&daemon_cmmands_handler::limit, this, _1), "Set download and upload limit"); m_cmd_binder.set_handler("out_peers", boost::bind(&daemon_cmmands_handler::out_peers_limit, this, _1), "Set max limit of out peers"); - m_cmd_binder.set_handler("limit_up", boost::bind(&daemon_cmmands_handler::limit_up, this, _1), "Set upload limit [kB/s]"); - m_cmd_binder.set_handler("limit_down", boost::bind(&daemon_cmmands_handler::limit_down, this, _1), "Set download limit [kB/s]"); - m_cmd_binder.set_handler("limit", boost::bind(&daemon_cmmands_handler::limit, this, _1), "Set download and upload limit [kB/s]"); - m_cmd_binder.set_handler("fast_exit", boost::bind(&daemon_cmmands_handler::fast_exit, this, _1), "Exit"); - m_cmd_binder.set_handler("test_drop_download", boost::bind(&daemon_cmmands_handler::test_drop_download, this, _1), "For network testing, drop downloaded blocks instead checking/adding them to blockchain. Can fake-download blocks very fast."); - m_cmd_binder.set_handler("start_save_graph", boost::bind(&daemon_cmmands_handler::start_save_graph, this, _1), ""); - m_cmd_binder.set_handler("stop_save_graph", boost::bind(&daemon_cmmands_handler::stop_save_graph, this, _1), ""); } bool start_handling() @@ -98,7 +63,7 @@ public: private: epee::srv_console_handlers_binder > > m_cmd_binder; - bool m_testnet; + //-------------------------------------------------------------------------------- std::string get_commands_str() @@ -131,6 +96,122 @@ private: return true; } //-------------------------------------------------------------------------------- + bool limit_up(const std::vector& args) + { + if(args.size()!=1) { + std::cout << "Usage: limit_up " << ENDL; + return false; + } + + int limit; + try { + limit = std::stoi(args[0]); + } + catch(std::invalid_argument& ex) { + return false; + } + + if (limit==-1) { + limit=128; + //this->islimitup=false; + } + + limit *= 1024; + + + //nodetool::epee::net_utils::connection >::set_rate_up_limit( limit ); + epee::net_utils::connection_basic::set_rate_up_limit( limit ); + std::cout << "Set limit-up to " << limit/1024 << " kB/s" << std::endl; + + return true; + } + + //-------------------------------------------------------------------------------- + bool limit_down(const std::vector& args) + { + + if(args.size()!=1) { + std::cout << "Usage: limit_down " << ENDL; + return true; + } + + int limit; + try { + limit = std::stoi(args[0]); + } + + catch(std::invalid_argument& ex) { + return false; + } + + if (limit==-1) { + limit=128; + //this->islimitup=false; + } + + limit *= 1024; + + + //nodetool::epee::net_utils::connection >::set_rate_up_limit( limit ); + epee::net_utils::connection_basic::set_rate_down_limit( limit ); + std::cout << "Set limit-down to " << limit/1024 << " kB/s" << std::endl; + + return true; + } + +//-------------------------------------------------------------------------------- + bool limit(const std::vector& args) + { + if(args.size()!=1) { + std::cout << "Usage: limit_down " << ENDL; + return true; + } + + int limit; + try { + limit = std::stoi(args[0]); + } + catch(std::invalid_argument& ex) { + return false; + } + + if (limit==-1) { + limit=128; + //this->islimitup=false; + } + + limit *= 1024; + + + //nodetool::epee::net_utils::connection >::set_rate_up_limit( limit ); + epee::net_utils::connection_basic::set_rate_down_limit( limit ); + epee::net_utils::connection_basic::set_rate_up_limit( limit ); + std::cout << "Set limit-down to " << limit/1024 << " kB/s" << std::endl; + std::cout << "Set limit-up to " << limit/1024 << " kB/s" << std::endl; + + return true; + } + //-------------------------------------------------------------------------------- + bool out_peers_limit(const std::vector& args) { + if(args.size()!=1) { + std::cout << "Usage: limit_down " << ENDL; + return true; + } + + int limit; + try { + limit = std::stoi(args[0]); + } + + catch(std::invalid_argument& ex) { + return false; + } + + LOG_PRINT_RED_L0("connections_count: " << limit); + m_srv.m_config.m_net_config.connections_count = limit; + return true; + } + //-------------------------------------------------------------------------------- bool show_hr(const std::vector& args) { if(!m_srv.get_payload_object().get_core().get_miner().is_mining()) @@ -243,9 +324,11 @@ private: return true; } + // TODO what the hell causes compilation warning in following code line +PUSH_WARNINGS +DISABLE_GCC_WARNING(maybe-uninitialized) log_space::log_singletone::get_set_log_detalisation_level(true, l); - int otshell_utils_log_level = 100 - (l * 25); - gCurrentLogger.setDebugLevel(otshell_utils_log_level); +POP_WARNINGS return true; } @@ -385,7 +468,7 @@ private: } cryptonote::account_public_address adr; - if(!cryptonote::get_account_address_from_str(adr, m_testnet, args.front())) + if(!cryptonote::get_account_address_from_str(adr, args.front())) { std::cout << "target account address has wrong format" << std::endl; return true; diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp new file mode 100644 index 000000000..bb188c174 --- /dev/null +++ b/src/daemon/executor.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "daemon/executor.h" + +#include "misc_log_ex.h" + +#include "common/command_line.h" +#include "cryptonote_config.h" +#include "version.h" + +#include + +namespace daemonize +{ + std::string const t_executor::NAME = "Monero Daemon"; + + void t_executor::init_options( + boost::program_options::options_description & configurable_options + ) + { + t_daemon::init_options(configurable_options); + } + + std::string const & t_executor::name() + { + return NAME; + } + + t_daemon t_executor::create_daemon( + boost::program_options::variables_map const & vm + ) + { + LOG_PRINT_L0(CRYPTONOTE_NAME << " v" << MONERO_VERSION_FULL); + return t_daemon{vm}; + } + + bool t_executor::run_interactive( + boost::program_options::variables_map const & vm + ) + { + epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + return t_daemon{vm}.run(true); + } +} + diff --git a/src/daemon/executor.h b/src/daemon/executor.h new file mode 100644 index 000000000..553896875 --- /dev/null +++ b/src/daemon/executor.h @@ -0,0 +1,60 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "daemon/daemon.h" +#include +#include +#include +#include + +namespace daemonize +{ + class t_executor final + { + public: + typedef ::daemonize::t_daemon t_daemon; + + static std::string const NAME; + + static void init_options( + boost::program_options::options_description & configurable_options + ); + + std::string const & name(); + + t_daemon create_daemon( + boost::program_options::variables_map const & vm + ); + + bool run_interactive( + boost::program_options::variables_map const & vm + ); + }; +} diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp new file mode 100644 index 000000000..5d8baf497 --- /dev/null +++ b/src/daemon/main.cpp @@ -0,0 +1,238 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "common/command_line.h" +#include "common/scoped_message_writer.h" +#include "common/util.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/miner.h" +#include "daemon/command_server.h" +#include "daemon/daemon.h" +#include "daemon/executor.h" +#include "daemonizer/daemonizer.h" +#include "misc_log_ex.h" +#include "p2p/net_node.h" +#include "rpc/core_rpc_server.h" +#include +#include "daemon/command_line_args.h" + +namespace po = boost::program_options; +namespace bf = boost::filesystem; + +int main(int argc, char const * argv[]) +{ + try { + + epee::string_tools::set_module_name_and_folder(argv[0]); + + // Build argument description + po::options_description all_options("All"); + po::options_description hidden_options("Hidden"); + po::options_description visible_options("Options"); + po::options_description core_settings("Settings"); + po::positional_options_description positional_options; + { + bf::path default_data_dir = daemonizer::get_default_data_dir(); + bf::path default_testnet_data_dir = {default_data_dir / "testnet"}; + + // Misc Options + + command_line::add_arg(visible_options, command_line::arg_help); + command_line::add_arg(visible_options, command_line::arg_version); + command_line::add_arg(visible_options, daemon_args::arg_os_version); + command_line::add_arg(visible_options, command_line::arg_data_dir, default_data_dir.string()); + command_line::add_arg(visible_options, command_line::arg_testnet_data_dir, default_testnet_data_dir.string()); + bf::path default_conf = default_data_dir / std::string(CRYPTONOTE_NAME ".conf"); + command_line::add_arg(visible_options, daemon_args::arg_config_file, default_conf.string()); + + // Settings + bf::path default_log = default_data_dir / std::string(CRYPTONOTE_NAME ".log"); + command_line::add_arg(core_settings, daemon_args::arg_log_file, default_log.string()); + command_line::add_arg(core_settings, daemon_args::arg_log_level); + command_line::add_arg(core_settings, daemon_args::arg_testnet_on); + command_line::add_arg(core_settings, daemon_args::arg_dns_checkpoints); + daemonizer::init_options(hidden_options, visible_options); + daemonize::t_executor::init_options(core_settings); + + // Hidden options + command_line::add_arg(hidden_options, daemon_args::arg_command); + + visible_options.add(core_settings); + all_options.add(visible_options); + all_options.add(hidden_options); + + // Positional + positional_options.add(daemon_args::arg_command.name, -1); // -1 for unlimited arguments + } + + // Do command line parsing + po::variables_map vm; + bool ok = command_line::handle_error_helper(visible_options, [&]() + { + boost::program_options::store( + boost::program_options::command_line_parser(argc, argv) + .options(all_options).positional(positional_options).run() + , vm + ); + + return true; + }); + if (!ok) return 1; + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << CRYPTONOTE_NAME << " v" << MONERO_VERSION_FULL << ENDL << ENDL; + std::cout << "Usage: " + std::string{argv[0]} + " [options|settings] [daemon_command...]" << std::endl << std::endl; + std::cout << visible_options << std::endl; + return 0; + } + + // Monero Version + if (command_line::get_arg(vm, command_line::arg_version)) + { + std::cout << CRYPTONOTE_NAME << " v" << MONERO_VERSION_FULL << ENDL; + return 0; + } + + // OS + if (command_line::get_arg(vm, daemon_args::arg_os_version)) + { + std::cout << "OS: " << tools::get_os_version_string() << ENDL; + return 0; + } + + bool testnet_mode = command_line::get_arg(vm, daemon_args::arg_testnet_on); + + auto data_dir_arg = testnet_mode ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; + + // Create data dir if it doesn't exist + boost::filesystem::path data_dir = boost::filesystem::absolute( + command_line::get_arg(vm, data_dir_arg)); + tools::create_directories_if_necessary(data_dir.string()); + + // FIXME: not sure on windows implementation default, needs further review + //bf::path relative_path_base = daemonizer::get_relative_path_base(vm); + bf::path relative_path_base = data_dir; + + std::string config = command_line::get_arg(vm, daemon_args::arg_config_file); + + boost::filesystem::path data_dir_path(data_dir); + boost::filesystem::path config_path(config); + if (!config_path.has_parent_path()) + { + config_path = data_dir / config_path; + } + + boost::system::error_code ec; + if (bf::exists(config_path, ec)) + { + po::store(po::parse_config_file(config_path.string().c_str(), core_settings), vm); + } + po::notify(vm); + + // If there are positional options, we're running a daemon command + { + auto command = command_line::get_arg(vm, daemon_args::arg_command); + + if (command.size()) + { + auto rpc_ip_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_ip); + auto rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port); + if (testnet_mode) + { + rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_testnet_rpc_bind_port); + } + + uint32_t rpc_ip; + uint16_t rpc_port; + if (!epee::string_tools::get_ip_int32_from_string(rpc_ip, rpc_ip_str)) + { + std::cerr << "Invalid IP: " << rpc_ip_str << std::endl; + return 1; + } + if (!epee::string_tools::get_xtype_from_string(rpc_port, rpc_port_str)) + { + std::cerr << "Invalid port: " << rpc_port_str << std::endl; + return 1; + } + + daemonize::t_command_server rpc_commands{rpc_ip, rpc_port}; + if (rpc_commands.process_command_vec(command)) + { + return 0; + } + else + { + std::cerr << "Unknown command" << std::endl; + return 1; + } + } + } + + // Start with log level 0 + epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); + + // Set log level + { + int new_log_level = command_line::get_arg(vm, daemon_args::arg_log_level); + if(new_log_level < LOG_LEVEL_MIN || new_log_level > LOG_LEVEL_MAX) + { + LOG_PRINT_L0("Wrong log level value: " << new_log_level); + } + else if (epee::log_space::get_set_log_detalisation_level(false) != new_log_level) + { + epee::log_space::get_set_log_detalisation_level(true, new_log_level); + LOG_PRINT_L0("LOG_LEVEL set to " << new_log_level); + } + } + + // Set log file + { + bf::path log_file_path{bf::absolute(command_line::get_arg(vm, daemon_args::arg_log_file), relative_path_base)}; + + epee::log_space::log_singletone::add_logger( + LOGGER_FILE + , log_file_path.filename().string().c_str() + , log_file_path.parent_path().string().c_str() + ); + } + + return daemonizer::daemonize(argc, argv, daemonize::t_executor{}, vm); + } + catch (std::exception const & ex) + { + LOG_ERROR("Exception in main! " << ex.what()); + } + catch (...) + { + LOG_ERROR("Exception in main!"); + } + return 1; +} diff --git a/src/daemon/p2p.h b/src/daemon/p2p.h new file mode 100644 index 000000000..355a784b1 --- /dev/null +++ b/src/daemon/p2p.h @@ -0,0 +1,99 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "daemon/protocol.h" +#include "misc_log_ex.h" +#include "p2p/net_node.h" +#include +#include + +namespace daemonize +{ + +class t_p2p final +{ +private: + typedef cryptonote::t_cryptonote_protocol_handler t_protocol_raw; + typedef nodetool::node_server t_node_server; +public: + static void init_options(boost::program_options::options_description & option_spec) + { + t_node_server::init_options(option_spec); + } +private: + t_node_server m_server; +public: + t_p2p( + boost::program_options::variables_map const & vm + , t_protocol & protocol + ) + : m_server{protocol.get()} + { + //initialize objects + LOG_PRINT_L0("Initializing p2p server..."); + if (!m_server.init(vm)) + { + throw std::runtime_error("Failed to initialize p2p server."); + } + LOG_PRINT_L0("P2p server initialized OK"); + } + + t_node_server & get() + { + return m_server; + } + + void run() + { + LOG_PRINT_L0("Starting p2p net loop..."); + m_server.run(); + LOG_PRINT_L0("p2p net loop stopped"); + } + + void stop() + { + m_server.send_stop_signal(); + } + + ~t_p2p() + { + LOG_PRINT_L0("Deinitializing p2p..."); + try { + m_server.deinit(); + } catch (...) { + LOG_PRINT_L0("Failed to deinitialize p2p..."); + } + } +}; + +} diff --git a/src/daemon/protocol.h b/src/daemon/protocol.h new file mode 100644 index 000000000..2f6a66f49 --- /dev/null +++ b/src/daemon/protocol.h @@ -0,0 +1,88 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "misc_log_ex.h" +#include "p2p/net_node.h" +#include +#include + +namespace daemonize +{ + +class t_protocol final +{ +private: + typedef cryptonote::t_cryptonote_protocol_handler t_protocol_raw; + typedef nodetool::node_server t_node_server; + + t_protocol_raw m_protocol; +public: + t_protocol( + boost::program_options::variables_map const & vm + , t_core & core + ) + : m_protocol{core.get(), nullptr} + { + LOG_PRINT_L0("Initializing cryptonote protocol..."); + if (!m_protocol.init(vm)) + { + throw std::runtime_error("Failed to initialize cryptonote protocol."); + } + LOG_PRINT_L0("Cryptonote protocol initialized OK"); + } + + t_protocol_raw & get() + { + return m_protocol; + } + + void set_p2p_endpoint( + t_node_server & server + ) + { + m_protocol.set_p2p_endpoint(&server); + } + + ~t_protocol() + { + LOG_PRINT_L0("Deinitializing cryptonote_protocol..."); + try { + m_protocol.deinit(); + m_protocol.set_p2p_endpoint(nullptr); + } catch (...) { + LOG_PRINT_L0("Failed to deinitialize protocol..."); + } + } +}; + +} diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h new file mode 100644 index 000000000..6477f440e --- /dev/null +++ b/src/daemon/rpc.h @@ -0,0 +1,101 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "daemon/core.h" +#include "daemon/p2p.h" +#include "misc_log_ex.h" +#include "rpc/core_rpc_server.h" +#include +#include + +namespace daemonize +{ + +class t_rpc final +{ +public: + static void init_options(boost::program_options::options_description & option_spec) + { + cryptonote::core_rpc_server::init_options(option_spec); + } +private: + cryptonote::core_rpc_server m_server; +public: + t_rpc( + boost::program_options::variables_map const & vm + , t_core & core + , t_p2p & p2p + ) + : m_server{core.get(), p2p.get()} + { + LOG_PRINT_L0("Initializing core rpc server..."); + if (!m_server.init(vm)) + { + throw std::runtime_error("Failed to initialize core rpc server."); + } + LOG_PRINT_GREEN("Core rpc server initialized OK on port: " << m_server.get_binded_port(), LOG_LEVEL_0); + } + + void run() + { + LOG_PRINT_L0("Starting core rpc server..."); + if (!m_server.run(2, false)) + { + throw std::runtime_error("Failed to start core rpc server."); + } + LOG_PRINT_L0("Core rpc server started ok"); + } + + void stop() + { + LOG_PRINT_L0("Stopping core rpc server..."); + m_server.send_stop_signal(); + m_server.timed_wait_server_stop(5000); + } + + cryptonote::core_rpc_server* get_server() + { + return &m_server; + } + + ~t_rpc() + { + LOG_PRINT_L0("Deinitializing rpc server..."); + try { + m_server.deinit(); + } catch (...) { + LOG_PRINT_L0("Failed to deinitialize rpc server..."); + } + } +}; + +} diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp new file mode 100644 index 000000000..f06f48544 --- /dev/null +++ b/src/daemon/rpc_command_executor.cpp @@ -0,0 +1,692 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "string_tools.h" +#include "common/scoped_message_writer.h" +#include "daemon/rpc_command_executor.h" +#include "rpc/core_rpc_server_commands_defs.h" +#include +#include + +namespace daemonize { + +namespace { + void print_peer(std::string const & prefix, cryptonote::peer const & peer) + { + time_t now; + time(&now); + time_t last_seen = static_cast(peer.last_seen); + + std::string id_str; + std::string port_str; + std::string elapsed = epee::misc_utils::get_time_interval_string(now - last_seen); + std::string ip_str = epee::string_tools::get_ip_string_from_int32(peer.ip); + epee::string_tools::xtype_to_string(peer.id, id_str); + epee::string_tools::xtype_to_string(peer.port, port_str); + std::string addr_str = ip_str + ":" + port_str; + tools::msg_writer() << boost::format("%-10s %-25s %-25s %s") % prefix % id_str % addr_str % elapsed; + } + + void print_block_header(cryptonote::block_header_responce const & header) + { + tools::success_msg_writer() + << "timestamp: " << boost::lexical_cast(header.timestamp) << std::endl + << "previous hash: " << header.prev_hash << std::endl + << "nonce: " << boost::lexical_cast(header.nonce) << std::endl + << "is orphan: " << header.orphan_status << std::endl + << "height: " << boost::lexical_cast(header.height) << std::endl + << "depth: " << boost::lexical_cast(header.depth) << std::endl + << "hash: " << header.hash + << "difficulty: " << boost::lexical_cast(header.difficulty) << std::endl + << "reward: " << boost::lexical_cast(header.reward); + } +} + +t_rpc_command_executor::t_rpc_command_executor( + uint32_t ip + , uint16_t port + , bool is_rpc + , cryptonote::core_rpc_server* rpc_server + ) + : m_rpc_client(NULL), m_rpc_server(rpc_server) +{ + if (is_rpc) + { + m_rpc_client = new tools::t_rpc_client(ip, port); + } + else + { + if (rpc_server == NULL) + { + throw std::runtime_error("If not calling commands via RPC, rpc_server pointer must be non-null"); + } + } + + m_is_rpc = is_rpc; +} + +t_rpc_command_executor::~t_rpc_command_executor() +{ + if (m_rpc_client != NULL) + { + delete m_rpc_client; + } +} + +bool t_rpc_command_executor::print_peer_list() { + cryptonote::COMMAND_RPC_GET_PEER_LIST::request req; + cryptonote::COMMAND_RPC_GET_PEER_LIST::response res; + + std::string failure_message = "Couldn't retrieve peer list"; + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/get_peer_list", failure_message.c_str())) + { + return false; + } + } + else + { + if (!m_rpc_server->on_get_peer_list(req, res)) + { + tools::fail_msg_writer() << failure_message; + return false; + } + } + + for (auto & peer : res.white_list) + { + print_peer("white", peer); + } + + for (auto & peer : res.gray_list) + { + print_peer("gray", peer); + } + + return true; +} + +bool t_rpc_command_executor::save_blockchain() { + cryptonote::COMMAND_RPC_SAVE_BC::request req; + cryptonote::COMMAND_RPC_SAVE_BC::response res; + + std::string fail_message = "Couldn't save blockchain"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/save_bc", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_save_bc(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + } + } + + tools::success_msg_writer() << "Blockchain saved"; + + return true; +} + +bool t_rpc_command_executor::show_hash_rate() { + cryptonote::COMMAND_RPC_SET_LOG_HASH_RATE::request req; + cryptonote::COMMAND_RPC_SET_LOG_HASH_RATE::response res; + req.visible = true; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/set_log_hash_rate", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_set_log_hash_rate(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + } + } + + tools::success_msg_writer() << "Hash rate logging is on"; + + return true; +} + +bool t_rpc_command_executor::hide_hash_rate() { + cryptonote::COMMAND_RPC_SET_LOG_HASH_RATE::request req; + cryptonote::COMMAND_RPC_SET_LOG_HASH_RATE::response res; + req.visible = false; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/set_log_hash_rate", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_set_log_hash_rate(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + tools::success_msg_writer() << "Hash rate logging is off"; + + return true; +} + +bool t_rpc_command_executor::show_difficulty() { + cryptonote::COMMAND_RPC_GET_INFO::request req; + cryptonote::COMMAND_RPC_GET_INFO::response res; + + std::string fail_message = "Problem fetching info"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/getinfo", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_info(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + tools::success_msg_writer() << "BH: " << res.height + << ", DIFF: " << res.difficulty + << ", HR: " << (int) res.difficulty / 60L << " H/s"; + + return true; +} + +bool t_rpc_command_executor::print_connections() { + cryptonote::COMMAND_RPC_GET_CONNECTIONS::request req; + cryptonote::COMMAND_RPC_GET_CONNECTIONS::response res; + epee::json_rpc::error error_resp; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "/get_connections", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_connections(req, res, error_resp)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + for (auto & info : res.connections) + { + std::string address = info.ip + ":" + info.port; + std::string in_out = info.incoming ? "INC" : "OUT"; + tools::msg_writer() << boost::format("%-25s peer_id: %-25s %s") % address % info.peer_id % in_out; + } + + return true; +} + +bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, uint64_t end_block_index) { + +// this function appears to not exist in the json rpc api, and so is commented +// until such a time as it does. + +/* + cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request req; + cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response res; + epee::json_rpc::error error_resp; + + req.start_height = start_block_index; + req.end_height = end_block_index; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "getblockheadersrange", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_getblockheadersrange(req, res, error_resp)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + for (auto & header : res.headers) + { + std::cout + << "major version: " << header.major_version << std::endl + << "minor version: " << header.minor_version << std::endl + << "height: " << header.height << ", timestamp: " << header.timestamp << ", difficulty: " << header.difficulty << std::endl + << "block id: " << header.hash << std::endl + << "previous block id: " << header.prev_hash << std::endl + << "difficulty: " << header.difficulty << ", nonce " << header.nonce << std::endl; + } + +*/ + return true; +} + +bool t_rpc_command_executor::set_log_level(int8_t level) { + cryptonote::COMMAND_RPC_SET_LOG_LEVEL::request req; + cryptonote::COMMAND_RPC_SET_LOG_LEVEL::response res; + req.level = level; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/set_log_level", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_set_log_level(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + tools::success_msg_writer() << "Log level is now " << boost::lexical_cast(level); + + return true; +} + +bool t_rpc_command_executor::print_height() { + cryptonote::COMMAND_RPC_GET_HEIGHT::request req; + cryptonote::COMMAND_RPC_GET_HEIGHT::response res; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/getheight", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_height(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + tools::success_msg_writer() << boost::lexical_cast(res.height); + + return true; +} + +bool t_rpc_command_executor::print_block_by_hash(crypto::hash block_hash) { + cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request req; + cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response res; + epee::json_rpc::error error_resp; + + req.hash = epee::string_tools::pod_to_hex(block_hash); + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "getblockheaderbyhash", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_block_header_by_hash(req, res, error_resp)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + print_block_header(res.block_header); + + return true; +} + +bool t_rpc_command_executor::print_block_by_height(uint64_t height) { + cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request req; + cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response res; + epee::json_rpc::error error_resp; + + req.height = height; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "getblockheaderbyheight", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_block_header_by_height(req, res, error_resp)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + print_block_header(res.block_header); + + return true; +} + +bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash) { + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res; + + std::string fail_message = "Problem fetching transaction"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/gettransactions", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_transactions(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + if (1 == res.txs_as_hex.size()) + { + tools::success_msg_writer() << res.txs_as_hex.front(); + } + else + { + tools::fail_msg_writer() << "transaction wasn't found: <" << transaction_hash << '>' << std::endl; + } + + return true; +} + +bool t_rpc_command_executor::print_transaction_pool_long() { + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::request req; + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::response res; + + std::string fail_message = "Problem fetching transaction pool"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/get_transaction_pool", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_transaction_pool(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + if (res.transactions.empty()) + { + tools::msg_writer() << "Pool is empty" << std::endl; + } + for (auto & tx_info : res.transactions) + { + tools::msg_writer() << "id: " << tx_info.id_hash << std::endl + << "blob_size: " << tx_info.blob_size << std::endl + << "fee: " << tx_info.fee << std::endl + << "kept_by_block: " << tx_info.kept_by_block << std::endl + << "max_used_block_height: " << tx_info.max_used_block_height << std::endl + << "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl + << "last_failed_height: " << tx_info.last_failed_height << std::endl + << "last_failed_id: " << tx_info.last_failed_id_hash << std::endl; + } + + return true; +} + +bool t_rpc_command_executor::print_transaction_pool_short() { + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::request req; + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::response res; + + std::string fail_message = "Problem fetching transaction pool"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/get_transaction_pool", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_transaction_pool(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + if (res.transactions.empty()) + { + tools::msg_writer() << "Pool is empty" << std::endl; + } + for (auto & tx_info : res.transactions) + { + tools::msg_writer() << "id: " << tx_info.id_hash << std::endl + << tx_info.tx_json << std::endl + << "blob_size: " << tx_info.blob_size << std::endl + << "fee: " << tx_info.fee << std::endl + << "kept_by_block: " << tx_info.kept_by_block << std::endl + << "max_used_block_height: " << tx_info.max_used_block_height << std::endl + << "max_used_block_id: " << tx_info.max_used_block_id_hash << std::endl + << "last_failed_height: " << tx_info.last_failed_height << std::endl + << "last_failed_id: " << tx_info.last_failed_id_hash << std::endl; + } + + return true; +} + +// TODO: update this for testnet +bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads) { + cryptonote::COMMAND_RPC_START_MINING::request req; + cryptonote::COMMAND_RPC_START_MINING::response res; + req.miner_address = cryptonote::get_account_address_as_str(false, address); + req.threads_count = num_threads; + + if (m_rpc_client->rpc_request(req, res, "/start_mining", "Mining did not start")) + { + tools::success_msg_writer() << "Mining started"; + } + return true; +} + +bool t_rpc_command_executor::stop_mining() { + cryptonote::COMMAND_RPC_STOP_MINING::request req; + cryptonote::COMMAND_RPC_STOP_MINING::response res; + + std::string fail_message = "Mining did not stop"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/stop_mining", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_stop_mining(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + tools::success_msg_writer() << "Mining stopped"; + return true; +} + +bool t_rpc_command_executor::stop_daemon() +{ + cryptonote::COMMAND_RPC_STOP_DAEMON::request req; + cryptonote::COMMAND_RPC_STOP_DAEMON::response res; + +//# ifdef WIN32 +// // Stop via service API +// // TODO - this is only temporary! Get rid of hard-coded constants! +// bool ok = windows::stop_service("BitMonero Daemon"); +// ok = windows::uninstall_service("BitMonero Daemon"); +// //bool ok = windows::stop_service(SERVICE_NAME); +// //ok = windows::uninstall_service(SERVICE_NAME); +// if (ok) +// { +// return true; +// } +//# endif + + // Stop via RPC + std::string fail_message = "Daemon did not stop"; + + if (m_is_rpc) + { + if(!m_rpc_client->rpc_request(req, res, "/stop_daemon", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_stop_daemon(req, res)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + tools::success_msg_writer() << "Stop signal sent"; + + return true; +} + +bool t_rpc_command_executor::print_status() +{ + if (!m_is_rpc) + { + tools::success_msg_writer() << "print_status makes no sense in interactive mode"; + return true; + } + + bool daemon_is_alive = m_rpc_client->check_connection(); + + if(daemon_is_alive) { + tools::success_msg_writer() << "bitmonerod is running"; + } + else { + tools::fail_msg_writer() << "bitmonerod is NOT running"; + } + + return true; +} + +bool t_rpc_command_executor::set_limit(int limit) +{ +/* + epee::net_utils::connection_basic::set_rate_down_limit( limit ); + epee::net_utils::connection_basic::set_rate_up_limit( limit ); + std::cout << "Set limit-down to " << limit/1024 << " kB/s" << std::endl; + std::cout << "Set limit-up to " << limit/1024 << " kB/s" << std::endl; +*/ + + return true; +} + +bool t_rpc_command_executor::set_limit_up(int limit) +{ +/* + epee::net_utils::connection_basic::set_rate_up_limit( limit ); + std::cout << "Set limit-up to " << limit/1024 << " kB/s" << std::endl; +*/ + + return true; +} + +bool t_rpc_command_executor::set_limit_down(int limit) +{ +/* + epee::net_utils::connection_basic::set_rate_down_limit( limit ); + std::cout << "Set limit-down to " << limit/1024 << " kB/s" << std::endl; +*/ + + return true; +} + +}// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h new file mode 100644 index 000000000..fe0181e62 --- /dev/null +++ b/src/daemon/rpc_command_executor.h @@ -0,0 +1,111 @@ +/** +@file +@details + +@image html images/other/runtime-commands.png + +*/ + +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "common/rpc_client.h" +#include "misc_log_ex.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "p2p/net_node.h" +#include "rpc/core_rpc_server.h" + +namespace daemonize { + +class t_rpc_command_executor final { +private: + tools::t_rpc_client* m_rpc_client; + cryptonote::core_rpc_server* m_rpc_server; + bool m_is_rpc; + +public: + t_rpc_command_executor( + uint32_t ip + , uint16_t port + , bool is_rpc = true + , cryptonote::core_rpc_server* rpc_server = NULL + ); + + ~t_rpc_command_executor(); + + bool print_peer_list(); + + bool save_blockchain(); + + bool show_hash_rate(); + + bool hide_hash_rate(); + + bool show_difficulty(); + + bool print_connections(); + + bool print_blockchain_info(uint64_t start_block_index, uint64_t end_block_index); + + bool set_log_level(int8_t level); + + bool print_height(); + + bool print_block_by_hash(crypto::hash block_hash); + + bool print_block_by_height(uint64_t height); + + bool print_transaction(crypto::hash transaction_hash); + + bool print_transaction_pool_long(); + + bool print_transaction_pool_short(); + + bool start_mining(cryptonote::account_public_address address, uint64_t num_threads); + + bool stop_mining(); + + bool stop_daemon(); + + bool print_status(); + + bool set_limit(int limit); + + bool set_limit_up(int limit); + + bool set_limit_down(int limit); + + +}; + +} // namespace daemonize diff --git a/src/daemonizer/CMakeLists.txt b/src/daemonizer/CMakeLists.txt new file mode 100644 index 000000000..d5830111c --- /dev/null +++ b/src/daemonizer/CMakeLists.txt @@ -0,0 +1,74 @@ +# Copyright (c) 2014-2015, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if(MSVC OR MINGW) + set(daemonizer_sources + windows_service.cpp + windows_daemonizer.inl + ) +else() + set(daemonizer_sources + posix_fork.cpp + posix_daemonizer.inl + ) +endif() + +set(daemonizer_headers +) + +if(MSVC OR MINGW) + set(daemonizer_private_headers + daemonizer.h + windows_service.h + windows_service_runner.h + ) +else() + set(daemonizer_private_headers + daemonizer.h + posix_fork.h + ) +endif() + +bitmonero_private_headers(daemonizer + ${daemonizer_private_headers}) +bitmonero_add_library(daemonizer + ${daemonizer_sources} + ${daemonizer_headers} + ${daemonizer_private_headers}) +target_link_libraries(daemonizer + LINK_PRIVATE + common + ${Boost_CHRONO_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_REGEX_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${UPNP_LIBRARIES} + ${EXTRA_LIBRARIES}) diff --git a/src/daemonizer/daemonizer.h b/src/daemonizer/daemonizer.h new file mode 100644 index 000000000..6097a58f6 --- /dev/null +++ b/src/daemonizer/daemonizer.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +namespace daemonizer +{ + void init_options( + boost::program_options::options_description & hidden_options + , boost::program_options::options_description & normal_options + ); + + boost::filesystem::path get_default_data_dir(); + + boost::filesystem::path get_relative_path_base( + boost::program_options::variables_map const & vm + ); + + /** + * @arg create_before_detach - this indicates that the daemon should be + * created before the fork, giving it a chance to report initialization + * errors. At the time of this writing, this is not possible in the primary + * daemon (likely due to the size of the blockchain in memory). + */ + template + bool daemonize( + int argc, char const * argv[] + , T_executor && executor // universal ref + , boost::program_options::variables_map const & vm + ); +} + +#ifdef WIN32 +# include "daemonizer/windows_daemonizer.inl" +#else +# include "daemonizer/posix_daemonizer.inl" +#endif diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl new file mode 100644 index 000000000..e06d43d61 --- /dev/null +++ b/src/daemonizer/posix_daemonizer.inl @@ -0,0 +1,60 @@ +#pragma once + +#include "common/scoped_message_writer.h" +#include "common/util.h" +#include "daemonizer/posix_fork.h" + +#include +#include + +namespace daemonizer +{ + namespace + { + const command_line::arg_descriptor arg_detach = { + "detach" + , "Run as daemon" + }; + } + + inline void init_options( + boost::program_options::options_description & hidden_options + , boost::program_options::options_description & normal_options + ) + { + command_line::add_arg(normal_options, arg_detach); + } + + inline boost::filesystem::path get_default_data_dir() + { + return boost::filesystem::absolute(tools::get_default_data_dir()); + } + + inline boost::filesystem::path get_relative_path_base( + boost::program_options::variables_map const & vm + ) + { + return boost::filesystem::current_path(); + } + + template + inline bool daemonize( + int argc, char const * argv[] + , T_executor && executor // universal ref + , boost::program_options::variables_map const & vm + ) + { + if (command_line::has_arg(vm, arg_detach)) + { + auto daemon = executor.create_daemon(vm); + tools::success_msg_writer() << "Forking to background..."; + posix::fork(); + return daemon.run(); + } + else + { + //LOG_PRINT_L0(CRYPTONOTE_NAME << " v" << MONERO_VERSION_FULL); + return executor.run_interactive(vm); + } + } +} diff --git a/src/daemonizer/posix_fork.cpp b/src/daemonizer/posix_fork.cpp new file mode 100644 index 000000000..c068912ec --- /dev/null +++ b/src/daemonizer/posix_fork.cpp @@ -0,0 +1,108 @@ +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "daemonizer/posix_fork.h" +#include "misc_log_ex.h" + +#include +#include +#include +#include +#include +#include + +namespace posix { + +namespace { + void quit(std::string const & message) + { + LOG_ERROR(message); + throw std::runtime_error(message); + } +} + +void fork() +{ + // Fork the process and have the parent exit. If the process was started + // from a shell, this returns control to the user. Forking a new process is + // also a prerequisite for the subsequent call to setsid(). + if (pid_t pid = ::fork()) + { + if (pid > 0) + { + // We're in the parent process and need to exit. + // + // When the exit() function is used, the program terminates without + // invoking local variables' destructors. Only global variables are + // destroyed. + exit(0); + } + else + { + quit("First fork failed"); + } + } + + // Make the process a new session leader. This detaches it from the + // terminal. + setsid(); + + // A process inherits its working directory from its parent. This could be + // on a mounted filesystem, which means that the running daemon would + // prevent this filesystem from being unmounted. Changing to the root + // directory avoids this problem. + if (chdir("/") < 0) + { + quit("Unable to change working directory to root"); + } + + // The file mode creation mask is also inherited from the parent process. + // We don't want to restrict the permissions on files created by the + // daemon, so the mask is cleared. + umask(0); + + // A second fork ensures the process cannot acquire a controlling terminal. + if (pid_t pid = ::fork()) + { + if (pid > 0) + { + exit(0); + } + else + { + quit("Second fork failed"); + } + } + + // Close the standard streams. This decouples the daemon from the terminal + // that started it. + close(0); + close(1); + close(2); + + // We don't want the daemon to have any standard input. + if (open("/dev/null", O_RDONLY) < 0) + { + quit("Unable to open /dev/null"); + } + + // Send standard output to a log file. + const char* output = "/tmp/bitmonero.daemon.stdout.stderr"; + const int flags = O_WRONLY | O_CREAT | O_APPEND; + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + if (open(output, flags, mode) < 0) + { + quit("Unable to open output file: " + std::string(output)); + } + + // Also send standard error to the same log file. + if (dup(1) < 0) + { + quit("Unable to dup output descriptor"); + } +} + +} // namespace posix diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h new file mode 100644 index 000000000..f099685e9 --- /dev/null +++ b/src/daemonizer/posix_fork.h @@ -0,0 +1,11 @@ +#pragma once + +#ifndef WIN32 + +namespace posix { + +void fork(); + +} + +#endif diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl new file mode 100644 index 000000000..c099f4097 --- /dev/null +++ b/src/daemonizer/windows_daemonizer.inl @@ -0,0 +1,156 @@ +#pragma once + +#include "common/util.h" +#include "daemonizer/windows_service.h" +#include "daemonizer/windows_service_runner.h" + +#include +#include +#include + +namespace daemonizer +{ + namespace + { + const command_line::arg_descriptor arg_install_service = { + "install-service" + , "Install Windows service" + }; + const command_line::arg_descriptor arg_uninstall_service = { + "uninstall-service" + , "Uninstall Windows service" + }; + const command_line::arg_descriptor arg_start_service = { + "start-service" + , "Start Windows service" + }; + const command_line::arg_descriptor arg_stop_service = { + "stop-service" + , "Stop Windows service" + }; + const command_line::arg_descriptor arg_is_service = { + "run-as-service" + , "Hidden -- true if running as windows service" + }; + + std::string get_argument_string(int argc, char const * argv[]) + { + std::string result = ""; + for (int i = 1; i < argc; ++i) + { + result += " " + std::string{argv[i]}; + } + return result; + } + } + + inline void init_options( + boost::program_options::options_description & hidden_options + , boost::program_options::options_description & normal_options + ) + { + command_line::add_arg(normal_options, arg_install_service); + command_line::add_arg(normal_options, arg_uninstall_service); + command_line::add_arg(normal_options, arg_start_service); + command_line::add_arg(normal_options, arg_stop_service); + command_line::add_arg(hidden_options, arg_is_service); + } + + inline boost::filesystem::path get_default_data_dir() + { + bool admin; + if (!windows::check_admin(admin)) + { + admin = false; + } + if (admin) + { + return boost::filesystem::absolute( + tools::get_special_folder_path(CSIDL_COMMON_APPDATA, true) + "\\" + CRYPTONOTE_NAME + ); + } + else + { + return boost::filesystem::absolute( + tools::get_special_folder_path(CSIDL_APPDATA, true) + "\\" + CRYPTONOTE_NAME + ); + } + } + + inline boost::filesystem::path get_relative_path_base( + boost::program_options::variables_map const & vm + ) + { + if (command_line::has_arg(vm, arg_is_service)) + { + if (command_line::has_arg(vm, command_line::arg_data_dir)) + { + return command_line::get_arg(vm, command_line::arg_data_dir); + } + else + { + return tools::get_default_data_dir(); + } + } + else + { + return boost::filesystem::current_path(); + } + } + + template + inline bool daemonize( + int argc, char const * argv[] + , T_executor && executor // universal ref + , boost::program_options::variables_map const & vm + ) + { + std::string arguments = get_argument_string(argc, argv); + + if (command_line::has_arg(vm, arg_is_service)) + { + // TODO - Set the service status here for return codes + windows::t_service_runner::run( + executor.name() + , executor.create_daemon(vm) + ); + return true; + } + else if (command_line::has_arg(vm, arg_install_service)) + { + if (windows::ensure_admin(arguments)) + { + arguments += " --run-as-service"; + return windows::install_service(executor.name(), arguments); + } + } + else if (command_line::has_arg(vm, arg_uninstall_service)) + { + if (windows::ensure_admin(arguments)) + { + return windows::uninstall_service(executor.name()); + } + } + else if (command_line::has_arg(vm, arg_start_service)) + { + if (windows::ensure_admin(arguments)) + { + return windows::start_service(executor.name()); + } + } + else if (command_line::has_arg(vm, arg_stop_service)) + { + if (windows::ensure_admin(arguments)) + { + return windows::stop_service(executor.name()); + } + } + else // interactive + { + //LOG_PRINT_L0(CRYPTONOTE_NAME << " v" << MONERO_VERSION_FULL); + return executor.run_interactive(vm); + } + + return false; + } +} diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp new file mode 100644 index 000000000..1b0ee2ef4 --- /dev/null +++ b/src/daemonizer/windows_service.cpp @@ -0,0 +1,341 @@ +#undef UNICODE +#undef _UNICODE + +#include "common/scoped_message_writer.h" +#include "daemonizer/windows_service.h" +#include "string_tools.h" +#include +#include +#include +#include +#include +#include +#include + +namespace windows { + +namespace { + typedef std::unique_ptr::type, decltype(&::CloseServiceHandle)> service_handle; + + std::string get_last_error() + { + LPSTR p_error_text = nullptr; + + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_IGNORE_INSERTS + , nullptr + , GetLastError() + , MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) + , reinterpret_cast(&p_error_text) + , 0 + , nullptr + ); + + if (nullptr == p_error_text) + { + return ""; + } + else + { + return std::string{p_error_text}; + LocalFree(p_error_text); + } + } + + bool relaunch_as_admin( + std::string const & command + , std::string const & arguments + ) + { + SHELLEXECUTEINFO info{}; + info.cbSize = sizeof(info); + info.lpVerb = "runas"; + info.lpFile = command.c_str(); + info.lpParameters = arguments.c_str(); + info.hwnd = nullptr; + info.nShow = SW_SHOWNORMAL; + if (!ShellExecuteEx(&info)) + { + tools::fail_msg_writer() << "Admin relaunch failed: " << get_last_error(); + return false; + } + else + { + return true; + } + } + + // When we relaunch as admin, Windows opens a new window. This just pauses + // to allow the user to read any output. + void pause_to_display_admin_window_messages() + { + std::chrono::milliseconds how_long{1500}; + std::this_thread::sleep_for(how_long); + } +} + +bool check_admin(bool & result) +{ + BOOL is_admin = FALSE; + PSID p_administrators_group = nullptr; + + SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY; + + if (!AllocateAndInitializeSid( + &nt_authority + , 2 + , SECURITY_BUILTIN_DOMAIN_RID + , DOMAIN_ALIAS_RID_ADMINS + , 0, 0, 0, 0, 0, 0 + , &p_administrators_group + )) + { + tools::fail_msg_writer() << "Security Identifier creation failed: " << get_last_error(); + return false; + } + + if (!CheckTokenMembership( + nullptr + , p_administrators_group + , &is_admin + )) + { + tools::fail_msg_writer() << "Permissions check failed: " << get_last_error(); + return false; + } + + result = is_admin ? true : false; + + return true; +} + +bool ensure_admin( + std::string const & arguments + ) +{ + bool admin; + + if (!check_admin(admin)) + { + return false; + } + + if (admin) + { + return true; + } + else + { + std::string command = epee::string_tools::get_current_module_path(); + relaunch_as_admin(command, arguments); + return false; + } +} + +bool install_service( + std::string const & service_name + , std::string const & arguments + ) +{ + std::string command = epee::string_tools::get_current_module_path(); + std::string full_command = command + arguments; + + service_handle p_manager{ + OpenSCManager( + nullptr + , nullptr + , SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE + ) + , &::CloseServiceHandle + }; + if (p_manager == nullptr) + { + tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error(); + return false; + } + + service_handle p_service{ + CreateService( + p_manager.get() + , service_name.c_str() + , service_name.c_str() + , 0 + //, GENERIC_EXECUTE | GENERIC_READ + , SERVICE_WIN32_OWN_PROCESS + , SERVICE_DEMAND_START + , SERVICE_ERROR_NORMAL + , full_command.c_str() + , nullptr + , nullptr + , "" + //, "NT AUTHORITY\\LocalService" + , nullptr // Implies LocalSystem account + , nullptr + ) + , &::CloseServiceHandle + }; + if (p_service == nullptr) + { + tools::fail_msg_writer() << "Couldn't create service: " << get_last_error(); + return false; + } + + tools::success_msg_writer() << "Service installed"; + + pause_to_display_admin_window_messages(); + + return true; +} + +bool start_service( + std::string const & service_name + ) +{ + tools::msg_writer() << "Starting service"; + + SERVICE_STATUS_PROCESS service_status = {}; + DWORD unused = 0; + + service_handle p_manager{ + OpenSCManager( + nullptr + , nullptr + , SC_MANAGER_CONNECT + ) + , &::CloseServiceHandle + }; + if (p_manager == nullptr) + { + tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error(); + return false; + } + + service_handle p_service{ + OpenService( + p_manager.get() + , service_name.c_str() + //, SERVICE_START | SERVICE_QUERY_STATUS + , SERVICE_START + ) + , &::CloseServiceHandle + }; + if (p_service == nullptr) + { + tools::fail_msg_writer() << "Couldn't find service: " << get_last_error(); + return false; + } + + if (!StartService( + p_service.get() + , 0 + , nullptr + )) + { + tools::fail_msg_writer() << "Service start request failed: " << get_last_error(); + return false; + } + + tools::success_msg_writer() << "Service started"; + + pause_to_display_admin_window_messages(); + + return true; +} + +bool stop_service( + std::string const & service_name + ) +{ + tools::msg_writer() << "Stopping service"; + + service_handle p_manager{ + OpenSCManager( + nullptr + , nullptr + , SC_MANAGER_CONNECT + ) + , &::CloseServiceHandle + }; + if (p_manager == nullptr) + { + tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error(); + return false; + } + + service_handle p_service{ + OpenService( + p_manager.get() + , service_name.c_str() + , SERVICE_STOP | SERVICE_QUERY_STATUS + ) + , &::CloseServiceHandle + }; + if (p_service == nullptr) + { + tools::fail_msg_writer() << "Couldn't find service: " << get_last_error(); + return false; + } + + SERVICE_STATUS status = {}; + if (!ControlService(p_service.get(), SERVICE_CONTROL_STOP, &status)) + { + tools::fail_msg_writer() << "Couldn't request service stop: " << get_last_error(); + return false; + } + + tools::success_msg_writer() << "Service stopped"; + + pause_to_display_admin_window_messages(); + + return true; +} + +bool uninstall_service( + std::string const & service_name + ) +{ + service_handle p_manager{ + OpenSCManager( + nullptr + , nullptr + , SC_MANAGER_CONNECT + ) + , &::CloseServiceHandle + }; + if (p_manager == nullptr) + { + tools::fail_msg_writer() << "Couldn't connect to service manager: " << get_last_error(); + return false; + } + + service_handle p_service{ + OpenService( + p_manager.get() + , service_name.c_str() + , SERVICE_QUERY_STATUS | DELETE + ) + , &::CloseServiceHandle + }; + if (p_service == nullptr) + { + tools::fail_msg_writer() << "Couldn't find service: " << get_last_error(); + return false; + } + + SERVICE_STATUS status = {}; + if (!DeleteService(p_service.get())) + { + tools::fail_msg_writer() << "Couldn't uninstall service: " << get_last_error(); + return false; + } + + tools::success_msg_writer() << "Service uninstalled"; + + pause_to_display_admin_window_messages(); + + return true; +} + +} // namespace windows diff --git a/src/daemonizer/windows_service.h b/src/daemonizer/windows_service.h new file mode 100644 index 000000000..11f5fdcdc --- /dev/null +++ b/src/daemonizer/windows_service.h @@ -0,0 +1,36 @@ +#pragma once + +#ifdef WIN32 + +#undef UNICODE +#undef _UNICODE + +#include +#include + +namespace windows +{ + bool check_admin(bool & result); + + bool ensure_admin( + std::string const & arguments + ); + + bool install_service( + std::string const & service_name + , std::string const & arguments + ); + + bool uninstall_service( + std::string const & service_name + ); + + bool start_service( + std::string const & service_name + ); + + bool stop_service( + std::string const & service_name + ); +} +#endif diff --git a/src/daemonizer/windows_service_runner.h b/src/daemonizer/windows_service_runner.h new file mode 100644 index 000000000..79b10b136 --- /dev/null +++ b/src/daemonizer/windows_service_runner.h @@ -0,0 +1,157 @@ +#pragma once + +#ifdef WIN32 + +#undef UNICODE +#undef _UNICODE + +#include "daemonizer/windows_service.h" +#include +#include +#include +#include + +namespace windows { + namespace + { + std::vector vecstring(std::string const & str) + { + std::vector result{str.begin(), str.end()}; + result.push_back('\0'); + return result; + } + } + + template + class t_service_runner final + { + private: + SERVICE_STATUS_HANDLE m_status_handle{nullptr}; + SERVICE_STATUS m_status{}; + std::mutex m_lock{}; + std::string m_name; + T_handler m_handler; + + static std::unique_ptr> sp_instance; + public: + t_service_runner( + std::string name + , T_handler handler + ) + : m_name{std::move(name)} + , m_handler{std::move(handler)} + { + m_status.dwServiceType = SERVICE_WIN32; + m_status.dwCurrentState = SERVICE_STOPPED; + m_status.dwControlsAccepted = 0; + m_status.dwWin32ExitCode = NO_ERROR; + m_status.dwServiceSpecificExitCode = NO_ERROR; + m_status.dwCheckPoint = 0; + m_status.dwWaitHint = 0; + } + + t_service_runner & operator=(t_service_runner && other) + { + if (this != &other) + { + m_status_handle = std::move(other.m_status_handle); + m_status = std::move(other.m_status); + m_name = std::move(other.m_name); + m_handler = std::move(other.m_handler); + } + return *this; + } + + static void run( + std::string name + , T_handler handler + ) + { + sp_instance.reset(new t_service_runner{ + std::move(name) + , std::move(handler) + }); + + sp_instance->run_(); + } + + private: + void run_() + { + SERVICE_TABLE_ENTRY table[] = + { + { vecstring(m_name).data(), &service_main } + , { 0, 0 } + }; + + StartServiceCtrlDispatcher(table); + } + + void report_status(DWORD status) + { + m_status.dwCurrentState = status; + if (status == SERVICE_RUNNING) + { + m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + } + else if(status == SERVICE_STOP_PENDING) + { + m_status.dwControlsAccepted = 0; + } + SetServiceStatus(m_status_handle, &m_status); + } + + static void WINAPI service_main(DWORD argc, LPSTR * argv) + { + sp_instance->service_main_(argc, argv); + } + + void service_main_(DWORD argc, LPSTR * argv) + { + m_status_handle = RegisterServiceCtrlHandler(m_name.c_str(), &on_state_change_request); + if (m_status_handle == nullptr) return; + + report_status(SERVICE_START_PENDING); + + report_status(SERVICE_RUNNING); + + m_handler.run(); + + on_state_change_request_(SERVICE_CONTROL_STOP); + + // Ensure that the service is uninstalled + uninstall_service(m_name); + } + + static void WINAPI on_state_change_request(DWORD control_code) + { + sp_instance->on_state_change_request_(control_code); + } + + void on_state_change_request_(DWORD control_code) + { + switch (control_code) + { + case SERVICE_CONTROL_INTERROGATE: + break; + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + report_status(SERVICE_STOP_PENDING); + m_handler.stop(); + report_status(SERVICE_STOPPED); + break; + case SERVICE_CONTROL_PAUSE: + break; + case SERVICE_CONTROL_CONTINUE: + break; + default: + break; + } + } + }; + + template + std::unique_ptr> t_service_runner::sp_instance; +} + +#endif diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h index e70849fda..fee428817 100644 --- a/src/mnemonics/english.h +++ b/src/mnemonics/english.h @@ -728,7 +728,7 @@ namespace Language "initiate", "injury", "inkling", - "incline", + "inline", "inmate", "innocent", "inorganic", @@ -1073,7 +1073,7 @@ namespace Language "ouch", "ought", "ounce", - "launchpad", + "ourselves", "oust", "outbreak", "oval", diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 5b034ce25..f94fedae0 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -80,22 +80,18 @@ namespace nodetool public: typedef t_payload_net_handler payload_net_handler; - node_server(t_payload_net_handler& payload_handler, boost::uuids::uuid network_id) - :m_payload_handler(payload_handler), - m_allow_local_ip(false), - m_no_igd(false), - m_hide_my_port(false), - m_network_id(std::move(network_id)) - { - m_current_number_of_out_peers = 0; - m_save_graph = false; - is_closing = false; - } + node_server( + t_payload_net_handler& payload_handler + ) + : m_payload_handler(payload_handler) + , m_allow_local_ip(false) + , m_hide_my_port(false) + {} static void init_options(boost::program_options::options_description& desc); bool run(); - bool init(const boost::program_options::variables_map& vm, bool testnet); + bool init(const boost::program_options::variables_map& vm); bool deinit(); bool send_stop_signal(); uint32_t get_this_peer_port(){return m_listenning_port;} diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index afc6436f3..ef158d9a1 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -46,7 +46,7 @@ #include "net/local_ip.h" #include "crypto/crypto.h" #include "storages/levin_abstract_invoke2.h" -#include "data_logger.hpp" +#include "daemon/command_line_args.h" // We have to look for miniupnpc headers in different places, dependent on if its compiled or external #ifdef UPNP_STATIC @@ -273,16 +273,20 @@ namespace nodetool //----------------------------------------------------------------------------------- template - bool node_server::init(const boost::program_options::variables_map& vm, bool testnet) + bool node_server::init(const boost::program_options::variables_map& vm) { + bool testnet = command_line::get_arg(vm, daemon_args::arg_testnet_on); + if (testnet) { + memcpy(&m_network_id, &::config::testnet::NETWORK_ID, 16); append_net_address(m_seed_nodes, "107.152.187.202:28080"); append_net_address(m_seed_nodes, "197.242.158.240:28080"); append_net_address(m_seed_nodes, "107.152.130.98:28080"); } else { + memcpy(&m_network_id, &::config::NETWORK_ID, 16); // for each hostname in the seed nodes list, attempt to DNS resolve and // add the result addresses as seed nodes // TODO: at some point add IPv6 support, but that won't be relevant diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 7a41e9b9f..f29d0c604 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -40,29 +40,10 @@ using namespace epee; #include "misc_language.h" #include "crypto/hash.h" #include "core_rpc_server_error_codes.h" +#include "daemon/command_line_args.h" namespace cryptonote { - namespace - { - const command_line::arg_descriptor arg_rpc_bind_ip = { - "rpc-bind-ip" - , "IP for RPC server" - , "127.0.0.1" - }; - - const command_line::arg_descriptor arg_rpc_bind_port = { - "rpc-bind-port" - , "Port for RPC server" - , std::to_string(config::RPC_DEFAULT_PORT) - }; - - const command_line::arg_descriptor arg_testnet_rpc_bind_port = { - "testnet-rpc-bind-port" - , "Port for testnet RPC server" - , std::to_string(config::testnet::RPC_DEFAULT_PORT) - }; - } //----------------------------------------------------------------------------------- void core_rpc_server::init_options(boost::program_options::options_description& desc) @@ -75,11 +56,9 @@ namespace cryptonote core_rpc_server::core_rpc_server( core& cr , nodetool::node_server >& p2p - , bool testnet ) : m_core(cr) , m_p2p(p2p) - , m_testnet(testnet) {} //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::handle_command_line( @@ -97,6 +76,8 @@ namespace cryptonote const boost::program_options::variables_map& vm ) { + m_testnet = command_line::get_arg(vm, daemon_args::arg_testnet_on); + m_net_server.set_threads_prefix("RPC"); bool r = handle_command_line(vm); CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); @@ -124,7 +105,7 @@ namespace cryptonote #define CHECK_CORE_READY() do { if(!check_core_ready()){res.status = CORE_RPC_STATUS_BUSY;return true;} } while(0) //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, connection_context& cntx) + bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res) { CHECK_CORE_BUSY(); res.height = m_core.get_current_blockchain_height(); @@ -132,7 +113,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, connection_context& cntx) + bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res) { CHECK_CORE_BUSY(); res.height = m_core.get_current_blockchain_height(); @@ -150,7 +131,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, connection_context& cntx) + bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { CHECK_CORE_BUSY(); std::list > > bs; @@ -175,7 +156,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx) + bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { CHECK_CORE_BUSY(); res.status = "Failed"; @@ -204,7 +185,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, connection_context& cntx) + bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { CHECK_CORE_BUSY(); bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes); @@ -218,7 +199,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, connection_context& cntx) + bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) { CHECK_CORE_BUSY(); std::vector vh; @@ -260,7 +241,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, connection_context& cntx) + bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) { CHECK_CORE_READY(); @@ -303,7 +284,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, connection_context& cntx) + bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res) { CHECK_CORE_READY(); account_public_address adr; @@ -325,7 +306,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, connection_context& cntx) + bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res) { if(!m_core.get_miner().stop()) { @@ -336,7 +317,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res, connection_context& cntx) + bool core_rpc_server::on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res) { CHECK_CORE_READY(); @@ -354,7 +335,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, connection_context& cntx) + bool core_rpc_server::on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res) { CHECK_CORE_BUSY(); if( !m_core.get_blockchain_storage().store_blockchain() ) @@ -366,7 +347,76 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, connection_context& cntx) + bool core_rpc_server::on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res) + { + /* + std::list white_list; + std::list gray_list; + m_p2p.get_peerlist(white_list, gray_list); + + for (auto & entry : white_list) + { + res.white_list.emplace_back(entry.id, entry.adr.ip, entry.adr.port, entry.last_seen); + } + + for (auto & entry : gray_list) + { + res.gray_list.emplace_back(entry.id, entry.adr.ip, entry.adr.port, entry.last_seen); + } + + */ + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res) + { + if(m_core.get_miner().is_mining()) + { + m_core.get_miner().do_print_hashrate(req.visible); + res.status = CORE_RPC_STATUS_OK; + } + else + { + res.status = CORE_RPC_STATUS_NOT_MINING; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res) + { + if (req.level < LOG_LEVEL_MIN || req.level > LOG_LEVEL_MAX) + { + res.status = "Error: log level not valid"; + } + else + { + epee::log_space::log_singletone::get_set_log_detalisation_level(true, req.level); + res.status = CORE_RPC_STATUS_OK; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res) + { + /* + CHECK_CORE_BUSY(); + res.transactions = m_core.transaction_pool_info(); + */ + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) + { + // FIXME: replace back to original m_p2p.send_stop_signal() after + // investigating why that isn't working quite right. + m_p2p.send_stop_signal(); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res) { CHECK_CORE_BUSY(); res.count = m_core.get_current_blockchain_height(); @@ -374,7 +424,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) + bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp) { if(!check_core_busy()) { @@ -415,7 +465,7 @@ namespace cryptonote return 0; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) + bool core_rpc_server::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp) { if(!check_core_ready()) { @@ -481,7 +531,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) + bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp) { CHECK_CORE_READY(); if(req.size()!=1) @@ -553,7 +603,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) + bool core_rpc_server::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp) { if(!check_core_busy()) { @@ -589,7 +639,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx){ + bool core_rpc_server::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp){ if(!check_core_busy()) { error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; @@ -630,7 +680,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp, connection_context& cntx){ + bool core_rpc_server::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp){ if(!check_core_busy()) { error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; @@ -663,7 +713,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) + bool core_rpc_server::on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp) { if(!check_core_busy()) { @@ -679,7 +729,7 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) + bool core_rpc_server::on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp) { if(!check_core_busy()) { @@ -703,4 +753,23 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ -} + + const command_line::arg_descriptor core_rpc_server::arg_rpc_bind_ip = { + "rpc-bind-ip" + , "IP for RPC server" + , "127.0.0.1" + }; + + const command_line::arg_descriptor core_rpc_server::arg_rpc_bind_port = { + "rpc-bind-port" + , "Port for RPC server" + , std::to_string(config::RPC_DEFAULT_PORT) + }; + + const command_line::arg_descriptor core_rpc_server::arg_testnet_rpc_bind_port = { + "testnet-rpc-bind-port" + , "Port for testnet RPC server" + , std::to_string(config::testnet::RPC_DEFAULT_PORT) + }; + +} // namespace cryptonote diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index cd9a0f162..6e603acbd 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -39,6 +39,10 @@ #include "p2p/net_node.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" +// yes, epee doesn't properly use its full namespace when calling its +// functions from macros. *sigh* +using namespace epee; + namespace cryptonote { /************************************************************************/ @@ -47,19 +51,22 @@ namespace cryptonote class core_rpc_server: public epee::http_server_impl_base { public: + + static const command_line::arg_descriptor arg_rpc_bind_ip; + static const command_line::arg_descriptor arg_rpc_bind_port; + static const command_line::arg_descriptor arg_testnet_rpc_bind_port; + typedef epee::net_utils::connection_context_base connection_context; core_rpc_server( core& cr , nodetool::node_server >& p2p - , bool testnet ); static void init_options(boost::program_options::options_description& desc); bool init( const boost::program_options::variables_map& vm ); - private: CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map @@ -74,6 +81,11 @@ namespace cryptonote MAP_URI_AUTO_JON2("/stop_mining", on_stop_mining, COMMAND_RPC_STOP_MINING) MAP_URI_AUTO_JON2("/mining_status", on_mining_status, COMMAND_RPC_MINING_STATUS) MAP_URI_AUTO_JON2("/save_bc", on_save_bc, COMMAND_RPC_SAVE_BC) + MAP_URI_AUTO_JON2("/get_peer_list", on_get_peer_list, COMMAND_RPC_GET_PEER_LIST) + MAP_URI_AUTO_JON2("/set_log_hash_rate", on_set_log_hash_rate, COMMAND_RPC_SET_LOG_HASH_RATE) + MAP_URI_AUTO_JON2("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL) + MAP_URI_AUTO_JON2("/get_transaction_pool", on_get_transaction_pool, COMMAND_RPC_GET_TRANSACTION_POOL) + MAP_URI_AUTO_JON2("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON) MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) @@ -88,29 +100,37 @@ namespace cryptonote END_JSON_RPC_MAP() END_URI_MAP2() - bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, connection_context& cntx); - bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, connection_context& cntx); - bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, connection_context& cntx); - bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, connection_context& cntx); - bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, connection_context& cntx); - bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, connection_context& cntx); - bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, connection_context& cntx); - bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res, connection_context& cntx); - bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx); - bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, connection_context& cntx); - bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, connection_context& cntx); + bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res); + bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res); + bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res); + bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res); + bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res); + bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res); + bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res); + bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res); + bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); + bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); + bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res); + bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res); + bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res); + bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res); + bool on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res); + bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res); //json_rpc - bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, connection_context& cntx); - bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - bool on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - bool on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - bool on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); + bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); + bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp); + bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp); + bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp); + bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp); + bool on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp); + bool on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp); + bool on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp); + bool on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp); //----------------------- + +private: + bool handle_command_line( const boost::program_options::variables_map& vm ); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 9fea933cb..5cb547521 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -39,6 +39,7 @@ namespace cryptonote //----------------------------------------------- #define CORE_RPC_STATUS_OK "OK" #define CORE_RPC_STATUS_BUSY "BUSY" +#define CORE_RPC_STATUS_NOT_MINING "NOT MINING" struct COMMAND_RPC_GET_HEIGHT { @@ -440,7 +441,7 @@ namespace cryptonote KV_SERIALIZE(reward) END_KV_SERIALIZE_MAP() }; - + struct COMMAND_RPC_GET_LAST_BLOCK_HEADER { struct request @@ -510,6 +511,135 @@ namespace cryptonote }; + struct peer { + uint64_t id; + uint32_t ip; + uint16_t port; + uint64_t last_seen; + + peer() = default; + + peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen) + : id(id), ip(ip), port(port), last_seen(last_seen) + {} + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id) + KV_SERIALIZE(ip) + KV_SERIALIZE(port) + KV_SERIALIZE(last_seen) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_GET_PEER_LIST + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::vector white_list; + std::vector gray_list; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(white_list) + KV_SERIALIZE(gray_list) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_SET_LOG_HASH_RATE + { + struct request + { + bool visible; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(visible) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_SET_LOG_LEVEL + { + struct request + { + int8_t level; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(level) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + + struct tx_info + { + std::string id_hash; + std::string tx_json; // TODO - expose this data directly + uint64_t blob_size; + uint64_t fee; + std::string max_used_block_id_hash; + uint64_t max_used_block_height; + bool kept_by_block; + uint64_t last_failed_height; + std::string last_failed_id_hash; + uint64_t receive_time; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id_hash) + KV_SERIALIZE(tx_json) + KV_SERIALIZE(blob_size) + KV_SERIALIZE(fee) + KV_SERIALIZE(max_used_block_id_hash) + KV_SERIALIZE(max_used_block_height) + KV_SERIALIZE(kept_by_block) + KV_SERIALIZE(last_failed_height) + KV_SERIALIZE(last_failed_id_hash) + KV_SERIALIZE(receive_time) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_GET_TRANSACTION_POOL + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::vector transactions; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(transactions) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_GET_CONNECTIONS { struct request @@ -530,5 +660,48 @@ namespace cryptonote }; }; + + struct COMMAND_RPC_GET_BLOCK_HEADERS_RANGE + { + struct request + { + uint64_t start_height; + uint64_t end_height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(start_height) + KV_SERIALIZE(end_height) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::vector headers; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(headers) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_STOP_DAEMON + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 2a0e0e2f4..4ecf00c92 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -67,7 +67,7 @@ namespace po = boost::program_options; #define EXTENDED_LOGS_FILE "wallet_details.log" -unsigned int epee::g_test_dbg_lock_sleep = 0; +#define DEFAULT_MIX 3 namespace { @@ -281,7 +281,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability"); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), "payments [ ... ] - Show payments , ... "); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height"); - m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer [ ... ] [payment_id] - Transfer ,... to ,... , respectively. is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); + m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer [] [ ... ] [payment_id] - Transfer ,... to ,... , respectively. is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); @@ -1068,19 +1068,24 @@ bool simple_wallet::transfer(const std::vector &args_) return true; std::vector local_args = args_; - if(local_args.size() < 3) - { - fail_msg_writer() << "wrong number of arguments, expected at least 3, got " << local_args.size(); - return true; - } size_t fake_outs_count; - if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) - { - fail_msg_writer() << "mixin_count should be non-negative integer, got " << local_args[0]; - return true; + if(local_args.size() > 0) { + if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) + { + fake_outs_count = DEFAULT_MIX; + } + else + { + local_args.erase(local_args.begin()); + } + } + + if(local_args.size() < 2) + { + fail_msg_writer() << "wrong number of arguments"; + return true; } - local_args.erase(local_args.begin()); std::vector extra; if (1 == local_args.size() % 2) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e7940d57b..e39d9ba7b 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -85,7 +85,7 @@ namespace tools return epee::http_server_impl_base::init(m_port, m_bind_ip); } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er) { try { @@ -101,7 +101,7 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er) { try { @@ -161,7 +161,7 @@ namespace tools } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er) { std::vector dsts; @@ -219,7 +219,7 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er) { std::vector dsts; @@ -273,7 +273,7 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er) { if (m_wallet.restricted()) { @@ -295,7 +295,7 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er) { crypto::hash payment_id; cryptonote::blobdata payment_id_blob; @@ -332,7 +332,7 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er) { res.payments.clear(); @@ -397,7 +397,7 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er) { if(req.transfer_type.compare("all") != 0 && req.transfer_type.compare("available") != 0 && req.transfer_type.compare("unavailable") != 0) { @@ -450,7 +450,7 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er) { if (m_wallet.restricted()) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 06ebd23bf..cfdc1c147 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -74,20 +74,20 @@ namespace tools END_URI_MAP2() //json_rpc - bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx); - bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er); + bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er); bool validate_transfer(const std::list destinations, const std::string payment_id, std::vector& dsts, std::vector& extra, epee::json_rpc::error& er); - bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx); - bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, connection_context& cntx); - bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx); - bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx); - bool on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx); - bool on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er); + bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er); + bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er); + bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er); + bool on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er); + bool on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er); bool handle_command_line(const boost::program_options::variables_map& vm); //json rpc v2 - bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er); wallet2& m_wallet; std::string m_port; diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index 879f83ebe..b942ed5c5 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -108,7 +108,6 @@ int main(int argc, char* argv[]) cryptonote::t_cryptonote_protocol_handler cprotocol(pr_core, NULL); nodetool::node_server > p2psrv { cprotocol - , std::move(config::NETWORK_ID) }; cprotocol.set_p2p_endpoint(&p2psrv); //pr_core.set_cryptonote_protocol(&cprotocol); @@ -117,7 +116,7 @@ int main(int argc, char* argv[]) //initialize objects LOG_PRINT_L0("Initializing p2p server..."); - bool res = p2psrv.init(vm, false); + bool res = p2psrv.init(vm); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); LOG_PRINT_L0("P2p server initialized OK"); diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index d25a4f614..d187f36ca 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -487,7 +487,9 @@ inline bool do_replay_events(std::vector& events) cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects cryptonote::core c(&pr); - if (!c.init(vm, false)) + // FIXME: make sure that vm has arg_testnet_on set to true or false if + // this test needs for it to be so. + if (!c.init(vm)) { std::cout << concolor::magenta << "Failed to init core" << concolor::normal << std::endl; return false; diff --git a/tests/gtest/CHANGES b/tests/gtest/CHANGES new file mode 100644 index 000000000..055213242 --- /dev/null +++ b/tests/gtest/CHANGES @@ -0,0 +1,157 @@ +Changes for 1.7.0: + +* New feature: death tests are supported on OpenBSD and in iOS + simulator now. +* New feature: Google Test now implements a protocol to allow + a test runner to detect that a test program has exited + prematurely and report it as a failure (before it would be + falsely reported as a success if the exit code is 0). +* New feature: Test::RecordProperty() can now be used outside of the + lifespan of a test method, in which case it will be attributed to + the current test case or the test program in the XML report. +* New feature (potentially breaking): --gtest_list_tests now prints + the type parameters and value parameters for each test. +* Improvement: char pointers and char arrays are now escaped properly + in failure messages. +* Improvement: failure summary in XML reports now includes file and + line information. +* Improvement: the XML element now has a timestamp attribute. +* Improvement: When --gtest_filter is specified, XML report now doesn't + contain information about tests that are filtered out. +* Fixed the bug where long --gtest_filter flag values are truncated in + death tests. +* Potentially breaking change: RUN_ALL_TESTS() is now implemented as a + function instead of a macro in order to work better with Clang. +* Compatibility fixes with C++ 11 and various platforms. +* Bug/warning fixes. + +Changes for 1.6.0: + +* New feature: ADD_FAILURE_AT() for reporting a test failure at the + given source location -- useful for writing testing utilities. +* New feature: the universal value printer is moved from Google Mock + to Google Test. +* New feature: type parameters and value parameters are reported in + the XML report now. +* A gtest_disable_pthreads CMake option. +* Colored output works in GNU Screen sessions now. +* Parameters of value-parameterized tests are now printed in the + textual output. +* Failures from ad hoc test assertions run before RUN_ALL_TESTS() are + now correctly reported. +* Arguments of ASSERT_XY and EXPECT_XY no longer need to support << to + ostream. +* More complete handling of exceptions. +* GTEST_ASSERT_XY can be used instead of ASSERT_XY in case the latter + name is already used by another library. +* --gtest_catch_exceptions is now true by default, allowing a test + program to continue after an exception is thrown. +* Value-parameterized test fixtures can now derive from Test and + WithParamInterface separately, easing conversion of legacy tests. +* Death test messages are clearly marked to make them more + distinguishable from other messages. +* Compatibility fixes for Android, Google Native Client, MinGW, HP UX, + PowerPC, Lucid autotools, libCStd, Sun C++, Borland C++ Builder (Code Gear), + IBM XL C++ (Visual Age C++), and C++0x. +* Bug fixes and implementation clean-ups. +* Potentially incompatible changes: disables the harmful 'make install' + command in autotools. + +Changes for 1.5.0: + + * New feature: assertions can be safely called in multiple threads + where the pthreads library is available. + * New feature: predicates used inside EXPECT_TRUE() and friends + can now generate custom failure messages. + * New feature: Google Test can now be compiled as a DLL. + * New feature: fused source files are included. + * New feature: prints help when encountering unrecognized Google Test flags. + * Experimental feature: CMake build script (requires CMake 2.6.4+). + * Experimental feature: the Pump script for meta programming. + * double values streamed to an assertion are printed with enough precision + to differentiate any two different values. + * Google Test now works on Solaris and AIX. + * Build and test script improvements. + * Bug fixes and implementation clean-ups. + + Potentially breaking changes: + + * Stopped supporting VC++ 7.1 with exceptions disabled. + * Dropped support for 'make install'. + +Changes for 1.4.0: + + * New feature: the event listener API + * New feature: test shuffling + * New feature: the XML report format is closer to junitreport and can + be parsed by Hudson now. + * New feature: when a test runs under Visual Studio, its failures are + integrated in the IDE. + * New feature: /MD(d) versions of VC++ projects. + * New feature: elapsed time for the tests is printed by default. + * New feature: comes with a TR1 tuple implementation such that Boost + is no longer needed for Combine(). + * New feature: EXPECT_DEATH_IF_SUPPORTED macro and friends. + * New feature: the Xcode project can now produce static gtest + libraries in addition to a framework. + * Compatibility fixes for Solaris, Cygwin, minGW, Windows Mobile, + Symbian, gcc, and C++Builder. + * Bug fixes and implementation clean-ups. + +Changes for 1.3.0: + + * New feature: death tests on Windows, Cygwin, and Mac. + * New feature: ability to use Google Test assertions in other testing + frameworks. + * New feature: ability to run disabled test via + --gtest_also_run_disabled_tests. + * New feature: the --help flag for printing the usage. + * New feature: access to Google Test flag values in user code. + * New feature: a script that packs Google Test into one .h and one + .cc file for easy deployment. + * New feature: support for distributing test functions to multiple + machines (requires support from the test runner). + * Bug fixes and implementation clean-ups. + +Changes for 1.2.1: + + * Compatibility fixes for Linux IA-64 and IBM z/OS. + * Added support for using Boost and other TR1 implementations. + * Changes to the build scripts to support upcoming release of Google C++ + Mocking Framework. + * Added Makefile to the distribution package. + * Improved build instructions in README. + +Changes for 1.2.0: + + * New feature: value-parameterized tests. + * New feature: the ASSERT/EXPECT_(NON)FATAL_FAILURE(_ON_ALL_THREADS) + macros. + * Changed the XML report format to match JUnit/Ant's. + * Added tests to the Xcode project. + * Added scons/SConscript for building with SCons. + * Added src/gtest-all.cc for building Google Test from a single file. + * Fixed compatibility with Solaris and z/OS. + * Enabled running Python tests on systems with python 2.3 installed, + e.g. Mac OS X 10.4. + * Bug fixes. + +Changes for 1.1.0: + + * New feature: type-parameterized tests. + * New feature: exception assertions. + * New feature: printing elapsed time of tests. + * Improved the robustness of death tests. + * Added an Xcode project and samples. + * Adjusted the output format on Windows to be understandable by Visual Studio. + * Minor bug fixes. + +Changes for 1.0.1: + + * Added project files for Visual Studio 7.1. + * Fixed issues with compiling on Mac OS X. + * Fixed issues with compiling on Cygwin. + +Changes for 1.0.0: + + * Initial Open Source release of Google Test diff --git a/tests/gtest/CMakeLists.txt b/tests/gtest/CMakeLists.txt index 0fe26540b..bd78cfe67 100644 --- a/tests/gtest/CMakeLists.txt +++ b/tests/gtest/CMakeLists.txt @@ -59,6 +59,16 @@ include_directories( # Where Google Test's libraries can be found. link_directories(${gtest_BINARY_DIR}/src) +# Summary of tuple support for Microsoft Visual Studio: +# Compiler version(MS) version(cmake) Support +# ---------- ----------- -------------- ----------------------------- +# <= VS 2010 <= 10 <= 1600 Use Google Tests's own tuple. +# VS 2012 11 1700 std::tr1::tuple + _VARIADIC_MAX=10 +# VS 2013 12 1800 std::tr1::tuple +if (MSVC AND MSVC_VERSION EQUAL 1700) + add_definitions(/D _VARIADIC_MAX=10) +endif() + ######################################################################## # # Defines the gtest & gtest_main libraries. User tests should link @@ -77,7 +87,7 @@ target_link_libraries(gtest_main gtest) # # They are not built by default. To build them, set the # gtest_build_samples option to ON. You can do it by running ccmake -# or specifying the -Dbuild_gtest_samples=ON flag when running cmake. +# or specifying the -Dgtest_build_samples=ON flag when running cmake. if (gtest_build_samples) cxx_executable(sample1_unittest samples gtest_main samples/sample1.cc) @@ -124,6 +134,8 @@ if (gtest_build_tests) test/gtest-param-test2_test.cc) cxx_test(gtest-port_test gtest_main) cxx_test(gtest_pred_impl_unittest gtest_main) + cxx_test(gtest_premature_exit_test gtest + test/gtest_premature_exit_test.cc) cxx_test(gtest-printers_test gtest_main) cxx_test(gtest_prod_test gtest_main test/production.cc) @@ -140,10 +152,13 @@ if (gtest_build_tests) ############################################################ # C++ tests built with non-standard compiler flags. - cxx_library(gtest_no_exception "${cxx_no_exception}" - src/gtest-all.cc) - cxx_library(gtest_main_no_exception "${cxx_no_exception}" - src/gtest-all.cc src/gtest_main.cc) + # MSVC 7.1 does not support STL with exceptions disabled. + if (NOT MSVC OR MSVC_VERSION GREATER 1310) + cxx_library(gtest_no_exception "${cxx_no_exception}" + src/gtest-all.cc) + cxx_library(gtest_main_no_exception "${cxx_no_exception}" + src/gtest-all.cc src/gtest_main.cc) + endif() cxx_library(gtest_main_no_rtti "${cxx_no_rtti}" src/gtest-all.cc src/gtest_main.cc) @@ -166,12 +181,10 @@ if (gtest_build_tests) PROPERTIES COMPILE_DEFINITIONS "GTEST_LINKED_AS_SHARED_LIBRARY=1") - if (NOT MSVC OR NOT MSVC_VERSION EQUAL 1600) - # The C++ Standard specifies tuple_element. - # Yet MSVC 10's declares tuple_element. - # That declaration conflicts with our own standard-conforming - # tuple implementation. Therefore using our own tuple with - # MSVC 10 doesn't compile. + if (NOT MSVC OR MSVC_VERSION LESS 1600) # 1600 is Visual Studio 2010. + # Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that + # conflict with our own definitions. Therefore using our own tuple does not + # work on those compilers. cxx_library(gtest_main_use_own_tuple "${cxx_use_own_tuple}" src/gtest-all.cc src/gtest_main.cc) @@ -189,11 +202,15 @@ if (gtest_build_tests) cxx_executable(gtest_break_on_failure_unittest_ test gtest) py_test(gtest_break_on_failure_unittest) - cxx_executable_with_flags( - gtest_catch_exceptions_no_ex_test_ - "${cxx_no_exception}" - gtest_main_no_exception - test/gtest_catch_exceptions_test_.cc) + # Visual Studio .NET 2003 does not support STL with exceptions disabled. + if (NOT MSVC OR MSVC_VERSION GREATER 1310) # 1310 is Visual Studio .NET 2003 + cxx_executable_with_flags( + gtest_catch_exceptions_no_ex_test_ + "${cxx_no_exception}" + gtest_main_no_exception + test/gtest_catch_exceptions_test_.cc) + endif() + cxx_executable_with_flags( gtest_catch_exceptions_ex_test_ "${cxx_exception}" @@ -222,11 +239,14 @@ if (gtest_build_tests) cxx_executable(gtest_shuffle_test_ test gtest) py_test(gtest_shuffle_test) - cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception) - set_target_properties(gtest_throw_on_failure_test_ - PROPERTIES - COMPILE_FLAGS "${cxx_no_exception}") - py_test(gtest_throw_on_failure_test) + # MSVC 7.1 does not support STL with exceptions disabled. + if (NOT MSVC OR MSVC_VERSION GREATER 1310) + cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception) + set_target_properties(gtest_throw_on_failure_test_ + PROPERTIES + COMPILE_FLAGS "${cxx_no_exception}") + py_test(gtest_throw_on_failure_test) + endif() cxx_executable(gtest_uninitialized_test_ test gtest) py_test(gtest_uninitialized_test) diff --git a/tests/gtest/CONTRIBUTORS b/tests/gtest/CONTRIBUTORS new file mode 100644 index 000000000..feae2fc04 --- /dev/null +++ b/tests/gtest/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This file contains a list of people who've made non-trivial +# contribution to the Google C++ Testing Framework project. People +# who commit code to the project are encouraged to add their names +# here. Please keep the list sorted by first names. + +Ajay Joshi +Balázs Dán +Bharat Mediratta +Chandler Carruth +Chris Prince +Chris Taylor +Dan Egnor +Eric Roman +Hady Zalek +Jeffrey Yasskin +Jói Sigurðsson +Keir Mierle +Keith Ray +Kenton Varda +Manuel Klimek +Markus Heule +Mika Raento +Miklós Fazekas +Pasi Valminen +Patrick Hanna +Patrick Riley +Peter Kaminski +Preston Jackson +Rainer Klaffenboeck +Russ Cox +Russ Rufer +Sean Mcafee +Sigurður Ásgeirsson +Tracy Bialik +Vadim Berman +Vlad Losev +Zhanyong Wan diff --git a/tests/gtest/LICENSE b/tests/gtest/LICENSE new file mode 100644 index 000000000..1941a11f8 --- /dev/null +++ b/tests/gtest/LICENSE @@ -0,0 +1,28 @@ +Copyright 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/gtest/README b/tests/gtest/README new file mode 100644 index 000000000..404bf3b83 --- /dev/null +++ b/tests/gtest/README @@ -0,0 +1,435 @@ +Google C++ Testing Framework +============================ + +http://code.google.com/p/googletest/ + +Overview +-------- + +Google's framework for writing C++ tests on a variety of platforms +(Linux, Mac OS X, Windows, Windows CE, Symbian, etc). Based on the +xUnit architecture. Supports automatic test discovery, a rich set of +assertions, user-defined assertions, death tests, fatal and non-fatal +failures, various options for running the tests, and XML test report +generation. + +Please see the project page above for more information as well as the +mailing list for questions, discussions, and development. There is +also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please +join us! + +Requirements for End Users +-------------------------- + +Google Test is designed to have fairly minimal requirements to build +and use with your projects, but there are some. Currently, we support +Linux, Windows, Mac OS X, and Cygwin. We will also make our best +effort to support other platforms (e.g. Solaris, AIX, and z/OS). +However, since core members of the Google Test project have no access +to these platforms, Google Test may have outstanding issues there. If +you notice any problems on your platform, please notify +googletestframework@googlegroups.com. Patches for fixing them are +even more welcome! + +### Linux Requirements ### + +These are the base requirements to build and use Google Test from a source +package (as described below): + * GNU-compatible Make or gmake + * POSIX-standard shell + * POSIX(-2) Regular Expressions (regex.h) + * A C++98-standard-compliant compiler + +### Windows Requirements ### + + * Microsoft Visual C++ 7.1 or newer + +### Cygwin Requirements ### + + * Cygwin 1.5.25-14 or newer + +### Mac OS X Requirements ### + + * Mac OS X 10.4 Tiger or newer + * Developer Tools Installed + +Also, you'll need CMake 2.6.4 or higher if you want to build the +samples using the provided CMake script, regardless of the platform. + +Requirements for Contributors +----------------------------- + +We welcome patches. If you plan to contribute a patch, you need to +build Google Test and its own tests from an SVN checkout (described +below), which has further requirements: + + * Python version 2.3 or newer (for running some of the tests and + re-generating certain source files from templates) + * CMake 2.6.4 or newer + +Getting the Source +------------------ + +There are two primary ways of getting Google Test's source code: you +can download a stable source release in your preferred archive format, +or directly check out the source from our Subversion (SVN) repository. +The SVN checkout requires a few extra steps and some extra software +packages on your system, but lets you track the latest development and +make patches much more easily, so we highly encourage it. + +### Source Package ### + +Google Test is released in versioned source packages which can be +downloaded from the download page [1]. Several different archive +formats are provided, but the only difference is the tools used to +manipulate them, and the size of the resulting file. Download +whichever you are most comfortable with. + + [1] http://code.google.com/p/googletest/downloads/list + +Once the package is downloaded, expand it using whichever tools you +prefer for that type. This will result in a new directory with the +name "gtest-X.Y.Z" which contains all of the source code. Here are +some examples on Linux: + + tar -xvzf gtest-X.Y.Z.tar.gz + tar -xvjf gtest-X.Y.Z.tar.bz2 + unzip gtest-X.Y.Z.zip + +### SVN Checkout ### + +To check out the main branch (also known as the "trunk") of Google +Test, run the following Subversion command: + + svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn + +Setting up the Build +-------------------- + +To build Google Test and your tests that use it, you need to tell your +build system where to find its headers and source files. The exact +way to do it depends on which build system you use, and is usually +straightforward. + +### Generic Build Instructions ### + +Suppose you put Google Test in directory ${GTEST_DIR}. To build it, +create a library build target (or a project as called by Visual Studio +and Xcode) to compile + + ${GTEST_DIR}/src/gtest-all.cc + +with ${GTEST_DIR}/include in the system header search path and ${GTEST_DIR} +in the normal header search path. Assuming a Linux-like system and gcc, +something like the following will do: + + g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ + -pthread -c ${GTEST_DIR}/src/gtest-all.cc + ar -rv libgtest.a gtest-all.o + +(We need -pthread as Google Test uses threads.) + +Next, you should compile your test source file with +${GTEST_DIR}/include in the system header search path, and link it +with gtest and any other necessary libraries: + + g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \ + -o your_test + +As an example, the make/ directory contains a Makefile that you can +use to build Google Test on systems where GNU make is available +(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google +Test's own tests. Instead, it just builds the Google Test library and +a sample test. You can use it as a starting point for your own build +script. + +If the default settings are correct for your environment, the +following commands should succeed: + + cd ${GTEST_DIR}/make + make + ./sample1_unittest + +If you see errors, try to tweak the contents of make/Makefile to make +them go away. There are instructions in make/Makefile on how to do +it. + +### Using CMake ### + +Google Test comes with a CMake build script (CMakeLists.txt) that can +be used on a wide range of platforms ("C" stands for cross-platform.). +If you don't have CMake installed already, you can download it for +free from http://www.cmake.org/. + +CMake works by generating native makefiles or build projects that can +be used in the compiler environment of your choice. The typical +workflow starts with: + + mkdir mybuild # Create a directory to hold the build output. + cd mybuild + cmake ${GTEST_DIR} # Generate native build scripts. + +If you want to build Google Test's samples, you should replace the +last command with + + cmake -Dgtest_build_samples=ON ${GTEST_DIR} + +If you are on a *nix system, you should now see a Makefile in the +current directory. Just type 'make' to build gtest. + +If you use Windows and have Visual Studio installed, a gtest.sln file +and several .vcproj files will be created. You can then build them +using Visual Studio. + +On Mac OS X with Xcode installed, a .xcodeproj file will be generated. + +### Legacy Build Scripts ### + +Before settling on CMake, we have been providing hand-maintained build +projects/scripts for Visual Studio, Xcode, and Autotools. While we +continue to provide them for convenience, they are not actively +maintained any more. We highly recommend that you follow the +instructions in the previous two sections to integrate Google Test +with your existing build system. + +If you still need to use the legacy build scripts, here's how: + +The msvc\ folder contains two solutions with Visual C++ projects. +Open the gtest.sln or gtest-md.sln file using Visual Studio, and you +are ready to build Google Test the same way you build any Visual +Studio project. Files that have names ending with -md use DLL +versions of Microsoft runtime libraries (the /MD or the /MDd compiler +option). Files without that suffix use static versions of the runtime +libraries (the /MT or the /MTd option). Please note that one must use +the same option to compile both gtest and the test code. If you use +Visual Studio 2005 or above, we recommend the -md version as /MD is +the default for new projects in these versions of Visual Studio. + +On Mac OS X, open the gtest.xcodeproj in the xcode/ folder using +Xcode. Build the "gtest" target. The universal binary framework will +end up in your selected build directory (selected in the Xcode +"Preferences..." -> "Building" pane and defaults to xcode/build). +Alternatively, at the command line, enter: + + xcodebuild + +This will build the "Release" configuration of gtest.framework in your +default build location. See the "xcodebuild" man page for more +information about building different configurations and building in +different locations. + +If you wish to use the Google Test Xcode project with Xcode 4.x and +above, you need to either: + * update the SDK configuration options in xcode/Config/General.xconfig. + Comment options SDKROOT, MACOS_DEPLOYMENT_TARGET, and GCC_VERSION. If + you choose this route you lose the ability to target earlier versions + of MacOS X. + * Install an SDK for an earlier version. This doesn't appear to be + supported by Apple, but has been reported to work + (http://stackoverflow.com/questions/5378518). + +Tweaking Google Test +-------------------- + +Google Test can be used in diverse environments. The default +configuration may not work (or may not work well) out of the box in +some environments. However, you can easily tweak Google Test by +defining control macros on the compiler command line. Generally, +these macros are named like GTEST_XYZ and you define them to either 1 +or 0 to enable or disable a certain feature. + +We list the most frequently used macros below. For a complete list, +see file include/gtest/internal/gtest-port.h. + +### Choosing a TR1 Tuple Library ### + +Some Google Test features require the C++ Technical Report 1 (TR1) +tuple library, which is not yet available with all compilers. The +good news is that Google Test implements a subset of TR1 tuple that's +enough for its own need, and will automatically use this when the +compiler doesn't provide TR1 tuple. + +Usually you don't need to care about which tuple library Google Test +uses. However, if your project already uses TR1 tuple, you need to +tell Google Test to use the same TR1 tuple library the rest of your +project uses, or the two tuple implementations will clash. To do +that, add + + -DGTEST_USE_OWN_TR1_TUPLE=0 + +to the compiler flags while compiling Google Test and your tests. If +you want to force Google Test to use its own tuple library, just add + + -DGTEST_USE_OWN_TR1_TUPLE=1 + +to the compiler flags instead. + +If you don't want Google Test to use tuple at all, add + + -DGTEST_HAS_TR1_TUPLE=0 + +and all features using tuple will be disabled. + +### Multi-threaded Tests ### + +Google Test is thread-safe where the pthread library is available. +After #include "gtest/gtest.h", you can check the GTEST_IS_THREADSAFE +macro to see whether this is the case (yes if the macro is #defined to +1, no if it's undefined.). + +If Google Test doesn't correctly detect whether pthread is available +in your environment, you can force it with + + -DGTEST_HAS_PTHREAD=1 + +or + + -DGTEST_HAS_PTHREAD=0 + +When Google Test uses pthread, you may need to add flags to your +compiler and/or linker to select the pthread library, or you'll get +link errors. If you use the CMake script or the deprecated Autotools +script, this is taken care of for you. If you use your own build +script, you'll need to read your compiler and linker's manual to +figure out what flags to add. + +### As a Shared Library (DLL) ### + +Google Test is compact, so most users can build and link it as a +static library for the simplicity. You can choose to use Google Test +as a shared library (known as a DLL on Windows) if you prefer. + +To compile *gtest* as a shared library, add + + -DGTEST_CREATE_SHARED_LIBRARY=1 + +to the compiler flags. You'll also need to tell the linker to produce +a shared library instead - consult your linker's manual for how to do +it. + +To compile your *tests* that use the gtest shared library, add + + -DGTEST_LINKED_AS_SHARED_LIBRARY=1 + +to the compiler flags. + +Note: while the above steps aren't technically necessary today when +using some compilers (e.g. GCC), they may become necessary in the +future, if we decide to improve the speed of loading the library (see +http://gcc.gnu.org/wiki/Visibility for details). Therefore you are +recommended to always add the above flags when using Google Test as a +shared library. Otherwise a future release of Google Test may break +your build script. + +### Avoiding Macro Name Clashes ### + +In C++, macros don't obey namespaces. Therefore two libraries that +both define a macro of the same name will clash if you #include both +definitions. In case a Google Test macro clashes with another +library, you can force Google Test to rename its macro to avoid the +conflict. + +Specifically, if both Google Test and some other code define macro +FOO, you can add + + -DGTEST_DONT_DEFINE_FOO=1 + +to the compiler flags to tell Google Test to change the macro's name +from FOO to GTEST_FOO. Currently FOO can be FAIL, SUCCEED, or TEST. +For example, with -DGTEST_DONT_DEFINE_TEST=1, you'll need to write + + GTEST_TEST(SomeTest, DoesThis) { ... } + +instead of + + TEST(SomeTest, DoesThis) { ... } + +in order to define a test. + +Upgrating from an Earlier Version +--------------------------------- + +We strive to keep Google Test releases backward compatible. +Sometimes, though, we have to make some breaking changes for the +users' long-term benefits. This section describes what you'll need to +do if you are upgrading from an earlier version of Google Test. + +### Upgrading from 1.3.0 or Earlier ### + +You may need to explicitly enable or disable Google Test's own TR1 +tuple library. See the instructions in section "Choosing a TR1 Tuple +Library". + +### Upgrading from 1.4.0 or Earlier ### + +The Autotools build script (configure + make) is no longer officially +supportted. You are encouraged to migrate to your own build system or +use CMake. If you still need to use Autotools, you can find +instructions in the README file from Google Test 1.4.0. + +On platforms where the pthread library is available, Google Test uses +it in order to be thread-safe. See the "Multi-threaded Tests" section +for what this means to your build script. + +If you use Microsoft Visual C++ 7.1 with exceptions disabled, Google +Test will no longer compile. This should affect very few people, as a +large portion of STL (including ) doesn't compile in this mode +anyway. We decided to stop supporting it in order to greatly simplify +Google Test's implementation. + +Developing Google Test +---------------------- + +This section discusses how to make your own changes to Google Test. + +### Testing Google Test Itself ### + +To make sure your changes work as intended and don't break existing +functionality, you'll want to compile and run Google Test's own tests. +For that you can use CMake: + + mkdir mybuild + cd mybuild + cmake -Dgtest_build_tests=ON ${GTEST_DIR} + +Make sure you have Python installed, as some of Google Test's tests +are written in Python. If the cmake command complains about not being +able to find Python ("Could NOT find PythonInterp (missing: +PYTHON_EXECUTABLE)"), try telling it explicitly where your Python +executable can be found: + + cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR} + +Next, you can build Google Test and all of its own tests. On *nix, +this is usually done by 'make'. To run the tests, do + + make test + +All tests should pass. + +### Regenerating Source Files ### + +Some of Google Test's source files are generated from templates (not +in the C++ sense) using a script. A template file is named FOO.pump, +where FOO is the name of the file it will generate. For example, the +file include/gtest/internal/gtest-type-util.h.pump is used to generate +gtest-type-util.h in the same directory. + +Normally you don't need to worry about regenerating the source files, +unless you need to modify them. In that case, you should modify the +corresponding .pump files instead and run the pump.py Python script to +regenerate them. You can find pump.py in the scripts/ directory. +Read the Pump manual [2] for how to use it. + + [2] http://code.google.com/p/googletest/wiki/PumpManual + +### Contributing a Patch ### + +We welcome patches. Please read the Google Test developer's guide [3] +for how you can contribute. In particular, make sure you have signed +the Contributor License Agreement, or we won't be able to accept the +patch. + + [3] http://code.google.com/p/googletest/wiki/GoogleTestDevGuide + +Happy testing! diff --git a/tests/gtest/cmake/internal_utils.cmake b/tests/gtest/cmake/internal_utils.cmake index 7efc2ac79..93e6dbb7c 100644 --- a/tests/gtest/cmake/internal_utils.cmake +++ b/tests/gtest/cmake/internal_utils.cmake @@ -37,7 +37,7 @@ macro(fix_default_compiler_settings_) # We prefer more strict warning checking for building Google Test. # Replaces /W3 with /W4 in defaults. - string(REPLACE "/W3" "-W4" ${flag_var} "${${flag_var}}") + string(REPLACE "/W3" "/W4" ${flag_var} "${${flag_var}}") endforeach() endif() endmacro() @@ -55,7 +55,32 @@ macro(config_compiler_and_linker) if (MSVC) # Newlines inside flags variables break CMake's NMake generator. # TODO(vladl@google.com): Add -RTCs and -RTCu to debug builds. - set(cxx_base_flags "-GS -W4 -WX -wd4127 -wd4251 -wd4275 -nologo -J -Zi") + set(cxx_base_flags "-GS -W4 -WX -wd4251 -wd4275 -nologo -J -Zi") + if (MSVC_VERSION LESS 1400) # 1400 is Visual Studio 2005 + # Suppress spurious warnings MSVC 7.1 sometimes issues. + # Forcing value to bool. + set(cxx_base_flags "${cxx_base_flags} -wd4800") + # Copy constructor and assignment operator could not be generated. + set(cxx_base_flags "${cxx_base_flags} -wd4511 -wd4512") + # Compatibility warnings not applicable to Google Test. + # Resolved overload was found by argument-dependent lookup. + set(cxx_base_flags "${cxx_base_flags} -wd4675") + endif() + if (MSVC_VERSION LESS 1500) # 1500 is Visual Studio 2008 + # Conditional expression is constant. + # When compiling with /W4, we get several instances of C4127 + # (Conditional expression is constant). In our code, we disable that + # warning on a case-by-case basis. However, on Visual Studio 2005, + # the warning fires on std::list. Therefore on that compiler and earlier, + # we disable the warning project-wide. + set(cxx_base_flags "${cxx_base_flags} -wd4127") + endif() + if (NOT (MSVC_VERSION LESS 1700)) # 1700 is Visual Studio 2012. + # Suppress "unreachable code" warning on VS 2012 and later. + # http://stackoverflow.com/questions/3232669 explains the issue. + set(cxx_base_flags "${cxx_base_flags} -wd4702") + endif() + set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32") set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN") set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1") @@ -69,7 +94,8 @@ macro(config_compiler_and_linker) # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI # explicitly. set(cxx_no_rtti_flags "-fno-rtti -DGTEST_HAS_RTTI=0") - set(cxx_strict_flags "-Wextra") + set(cxx_strict_flags + "-Wextra -Wno-unused-parameter -Wno-missing-field-initializers") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "SunPro") set(cxx_exception_flags "-features=except") # Sun Pro doesn't provide macros to indicate whether exceptions and diff --git a/tests/gtest/include/gtest/gtest-death-test.h b/tests/gtest/include/gtest/gtest-death-test.h index a27883f0a..957a69c6a 100644 --- a/tests/gtest/include/gtest/gtest-death-test.h +++ b/tests/gtest/include/gtest/gtest-death-test.h @@ -51,6 +51,17 @@ GTEST_DECLARE_string_(death_test_style); #if GTEST_HAS_DEATH_TEST +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + // The following macros are useful for writing death tests. // Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is @@ -75,7 +86,7 @@ GTEST_DECLARE_string_(death_test_style); // for (int i = 0; i < 5; i++) { // EXPECT_DEATH(server.ProcessRequest(i), // "Invalid request .* in ProcessRequest()") -// << "Failed to die on request " << i); +// << "Failed to die on request " << i; // } // // ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); @@ -245,10 +256,10 @@ class GTEST_API_ KilledBySignal { # ifdef NDEBUG # define EXPECT_DEBUG_DEATH(statement, regex) \ - do { statement; } while (::testing::internal::AlwaysFalse()) + GTEST_EXECUTE_STATEMENT_(statement, regex) # define ASSERT_DEBUG_DEATH(statement, regex) \ - do { statement; } while (::testing::internal::AlwaysFalse()) + GTEST_EXECUTE_STATEMENT_(statement, regex) # else diff --git a/tests/gtest/include/gtest/gtest-message.h b/tests/gtest/include/gtest/gtest-message.h index 9b7142f32..fe879bca7 100644 --- a/tests/gtest/include/gtest/gtest-message.h +++ b/tests/gtest/include/gtest/gtest-message.h @@ -48,8 +48,11 @@ #include -#include "gtest/internal/gtest-string.h" -#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); namespace testing { @@ -87,15 +90,7 @@ class GTEST_API_ Message { public: // Constructs an empty Message. - // We allocate the stringstream separately because otherwise each use of - // ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's - // stack frame leading to huge stack frames in some cases; gcc does not reuse - // the stack space. - Message() : ss_(new ::std::stringstream) { - // By default, we want there to be enough precision when printing - // a double to a Message. - *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); - } + Message(); // Copy constructor. Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT @@ -118,7 +113,22 @@ class GTEST_API_ Message { // Streams a non-pointer value to this object. template inline Message& operator <<(const T& val) { - ::GTestStreamToHelper(ss_.get(), val); + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator <<; + *ss_ << val; return *this; } @@ -140,7 +150,7 @@ class GTEST_API_ Message { if (pointer == NULL) { *ss_ << "(null)"; } else { - ::GTestStreamToHelper(ss_.get(), pointer); + *ss_ << pointer; } return *this; } @@ -164,12 +174,8 @@ class GTEST_API_ Message { // These two overloads allow streaming a wide C string to a Message // using the UTF-8 encoding. - Message& operator <<(const wchar_t* wide_c_str) { - return *this << internal::String::ShowWideCString(wide_c_str); - } - Message& operator <<(wchar_t* wide_c_str) { - return *this << internal::String::ShowWideCString(wide_c_str); - } + Message& operator <<(const wchar_t* wide_c_str); + Message& operator <<(wchar_t* wide_c_str); #if GTEST_HAS_STD_WSTRING // Converts the given wide string to a narrow string using the UTF-8 @@ -183,13 +189,11 @@ class GTEST_API_ Message { Message& operator <<(const ::wstring& wstr); #endif // GTEST_HAS_GLOBAL_WSTRING - // Gets the text streamed to this object so far as a String. + // Gets the text streamed to this object so far as an std::string. // Each '\0' character in the buffer is replaced with "\\0". // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - internal::String GetString() const { - return internal::StringStreamToString(ss_.get()); - } + std::string GetString() const; private: @@ -199,16 +203,20 @@ class GTEST_API_ Message { // decide between class template specializations for T and T*, so a // tr1::type_traits-like is_pointer works, and we can overload on that. template - inline void StreamHelper(internal::true_type /*dummy*/, T* pointer) { + inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) { if (pointer == NULL) { *ss_ << "(null)"; } else { - ::GTestStreamToHelper(ss_.get(), pointer); + *ss_ << pointer; } } template - inline void StreamHelper(internal::false_type /*dummy*/, const T& value) { - ::GTestStreamToHelper(ss_.get(), value); + inline void StreamHelper(internal::false_type /*is_pointer*/, + const T& value) { + // See the comments in Message& operator <<(const T&) above for why + // we need this using statement. + using ::operator <<; + *ss_ << value; } #endif // GTEST_OS_SYMBIAN @@ -225,6 +233,18 @@ inline std::ostream& operator <<(std::ostream& os, const Message& sb) { return os << sb.GetString(); } +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal } // namespace testing #endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ diff --git a/tests/gtest/include/gtest/gtest-param-test.h b/tests/gtest/include/gtest/gtest-param-test.h index 6407cfd68..d6702c8f1 100644 --- a/tests/gtest/include/gtest/gtest-param-test.h +++ b/tests/gtest/include/gtest/gtest-param-test.h @@ -1257,7 +1257,7 @@ inline internal::ParamGenerator Bool() { // Boolean flags: // // class FlagDependentTest -// : public testing::TestWithParam > { +// : public testing::TestWithParam > { // virtual void SetUp() { // // Assigns external_flag_1 and external_flag_2 values from the tuple. // tie(external_flag_1, external_flag_2) = GetParam(); diff --git a/tests/gtest/include/gtest/gtest-param-test.h.pump b/tests/gtest/include/gtest/gtest-param-test.h.pump index 401cb513a..2dc9303b5 100644 --- a/tests/gtest/include/gtest/gtest-param-test.h.pump +++ b/tests/gtest/include/gtest/gtest-param-test.h.pump @@ -414,7 +414,7 @@ inline internal::ParamGenerator Bool() { // Boolean flags: // // class FlagDependentTest -// : public testing::TestWithParam > { +// : public testing::TestWithParam > { // virtual void SetUp() { // // Assigns external_flag_1 and external_flag_2 values from the tuple. // tie(external_flag_1, external_flag_2) = GetParam(); diff --git a/tests/gtest/include/gtest/gtest-printers.h b/tests/gtest/include/gtest/gtest-printers.h index 9cbab3ff4..18ee7bc64 100644 --- a/tests/gtest/include/gtest/gtest-printers.h +++ b/tests/gtest/include/gtest/gtest-printers.h @@ -103,6 +103,10 @@ #include "gtest/internal/gtest-port.h" #include "gtest/internal/gtest-internal.h" +#if GTEST_HAS_STD_TUPLE_ +# include +#endif + namespace testing { // Definitions in the 'internal' and 'internal2' name spaces are @@ -480,14 +484,16 @@ inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { } #endif // GTEST_HAS_STD_WSTRING -#if GTEST_HAS_TR1_TUPLE -// Overload for ::std::tr1::tuple. Needed for printing function arguments, -// which are packed as tuples. - +#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_ // Helper function for printing a tuple. T must be instantiated with // a tuple type. template void PrintTupleTo(const T& t, ::std::ostream* os); +#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_ + +#if GTEST_HAS_TR1_TUPLE +// Overload for ::std::tr1::tuple. Needed for printing function arguments, +// which are packed as tuples. // Overloaded PrintTo() for tuples of various arities. We support // tuples of up-to 10 fields. The following implementation works @@ -561,6 +567,13 @@ void PrintTo( } #endif // GTEST_HAS_TR1_TUPLE +#if GTEST_HAS_STD_TUPLE_ +template +void PrintTo(const ::std::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} +#endif // GTEST_HAS_STD_TUPLE_ + // Overload for std::pair. template void PrintTo(const ::std::pair& value, ::std::ostream* os) { @@ -580,10 +593,7 @@ class UniversalPrinter { public: // MSVC warns about adding const to a function type, so we want to // disable the warning. -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4180) // Temporarily disables warning 4180. -#endif // _MSC_VER + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) // Note: we deliberately don't call this PrintTo(), as that name // conflicts with ::testing::internal::PrintTo in the body of the @@ -600,9 +610,7 @@ class UniversalPrinter { PrintTo(value, os); } -#ifdef _MSC_VER -# pragma warning(pop) // Restores the warning state. -#endif // _MSC_VER + GTEST_DISABLE_MSC_WARNINGS_POP_() }; // UniversalPrintArray(begin, len, os) prints an array of 'len' @@ -630,9 +638,12 @@ void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { } } // This overload prints a (const) char array compactly. -GTEST_API_ void UniversalPrintArray(const char* begin, - size_t len, - ::std::ostream* os); +GTEST_API_ void UniversalPrintArray( + const char* begin, size_t len, ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray( + const wchar_t* begin, size_t len, ::std::ostream* os); // Implements printing an array type T[N]. template @@ -651,10 +662,7 @@ class UniversalPrinter { public: // MSVC warns about adding const to a function type, so we want to // disable the warning. -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4180) // Temporarily disables warning 4180. -#endif // _MSC_VER + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) static void Print(const T& value, ::std::ostream* os) { // Prints the address of the value. We use reinterpret_cast here @@ -665,27 +673,78 @@ class UniversalPrinter { UniversalPrint(value, os); } -#ifdef _MSC_VER -# pragma warning(pop) // Restores the warning state. -#endif // _MSC_VER + GTEST_DISABLE_MSC_WARNINGS_POP_() }; // Prints a value tersely: for a reference type, the referenced value // (but not the address) is printed; for a (const) char pointer, the // NUL-terminated string (but not the pointer) is printed. + +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(string(str), os); + } + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(char* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + template void UniversalTersePrint(const T& value, ::std::ostream* os) { - UniversalPrint(value, os); -} -inline void UniversalTersePrint(const char* str, ::std::ostream* os) { - if (str == NULL) { - *os << "NULL"; - } else { - UniversalPrint(string(str), os); - } -} -inline void UniversalTersePrint(char* str, ::std::ostream* os) { - UniversalTersePrint(static_cast(str), os); + UniversalTersePrinter::Print(value, os); } // Prints a value using the type inferred by the compiler. The @@ -694,19 +753,71 @@ inline void UniversalTersePrint(char* str, ::std::ostream* os) { // NUL-terminated string. template void UniversalPrint(const T& value, ::std::ostream* os) { - UniversalPrinter::Print(value, os); + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); } -#if GTEST_HAS_TR1_TUPLE typedef ::std::vector Strings; +// TuplePolicy must provide: +// - tuple_size +// size of tuple TupleT. +// - get(const TupleT& t) +// static function extracting element I of tuple TupleT. +// - tuple_element::type +// type of element I of tuple TupleT. +template +struct TuplePolicy; + +#if GTEST_HAS_TR1_TUPLE +template +struct TuplePolicy { + typedef TupleT Tuple; + static const size_t tuple_size = ::std::tr1::tuple_size::value; + + template + struct tuple_element : ::std::tr1::tuple_element {}; + + template + static typename AddReference< + const typename ::std::tr1::tuple_element::type>::type get( + const Tuple& tuple) { + return ::std::tr1::get(tuple); + } +}; +template +const size_t TuplePolicy::tuple_size; +#endif // GTEST_HAS_TR1_TUPLE + +#if GTEST_HAS_STD_TUPLE_ +template +struct TuplePolicy< ::std::tuple > { + typedef ::std::tuple Tuple; + static const size_t tuple_size = ::std::tuple_size::value; + + template + struct tuple_element : ::std::tuple_element {}; + + template + static const typename ::std::tuple_element::type& get( + const Tuple& tuple) { + return ::std::get(tuple); + } +}; +template +const size_t TuplePolicy< ::std::tuple >::tuple_size; +#endif // GTEST_HAS_STD_TUPLE_ + +#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_ // This helper template allows PrintTo() for tuples and // UniversalTersePrintTupleFieldsToStrings() to be defined by // induction on the number of tuple fields. The idea is that // TuplePrefixPrinter::PrintPrefixTo(t, os) prints the first N // fields in tuple t, and can be defined in terms of // TuplePrefixPrinter. - +// // The inductive case. template struct TuplePrefixPrinter { @@ -714,9 +825,14 @@ struct TuplePrefixPrinter { template static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { TuplePrefixPrinter::PrintPrefixTo(t, os); - *os << ", "; - UniversalPrinter::type> - ::Print(::std::tr1::get(t), os); + GTEST_INTENTIONAL_CONST_COND_PUSH_() + if (N > 1) { + GTEST_INTENTIONAL_CONST_COND_POP_() + *os << ", "; + } + UniversalPrinter< + typename TuplePolicy::template tuple_element::type> + ::Print(TuplePolicy::template get(t), os); } // Tersely prints the first N fields of a tuple to a string vector, @@ -725,12 +841,12 @@ struct TuplePrefixPrinter { static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { TuplePrefixPrinter::TersePrintPrefixToStrings(t, strings); ::std::stringstream ss; - UniversalTersePrint(::std::tr1::get(t), &ss); + UniversalTersePrint(TuplePolicy::template get(t), &ss); strings->push_back(ss.str()); } }; -// Base cases. +// Base case. template <> struct TuplePrefixPrinter<0> { template @@ -739,34 +855,13 @@ struct TuplePrefixPrinter<0> { template static void TersePrintPrefixToStrings(const Tuple&, Strings*) {} }; -// We have to specialize the entire TuplePrefixPrinter<> class -// template here, even though the definition of -// TersePrintPrefixToStrings() is the same as the generic version, as -// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't -// support specializing a method template of a class template. -template <> -struct TuplePrefixPrinter<1> { - template - static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { - UniversalPrinter::type>:: - Print(::std::tr1::get<0>(t), os); - } - template - static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { - ::std::stringstream ss; - UniversalTersePrint(::std::tr1::get<0>(t), &ss); - strings->push_back(ss.str()); - } -}; - -// Helper function for printing a tuple. T must be instantiated with -// a tuple type. -template -void PrintTupleTo(const T& t, ::std::ostream* os) { +// Helper function for printing a tuple. +// Tuple must be either std::tr1::tuple or std::tuple type. +template +void PrintTupleTo(const Tuple& t, ::std::ostream* os) { *os << "("; - TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: - PrintPrefixTo(t, os); + TuplePrefixPrinter::tuple_size>::PrintPrefixTo(t, os); *os << ")"; } @@ -776,18 +871,18 @@ void PrintTupleTo(const T& t, ::std::ostream* os) { template Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { Strings result; - TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: + TuplePrefixPrinter::tuple_size>:: TersePrintPrefixToStrings(value, &result); return result; } -#endif // GTEST_HAS_TR1_TUPLE +#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_ } // namespace internal template ::std::string PrintToString(const T& value) { ::std::stringstream ss; - internal::UniversalTersePrint(value, &ss); + internal::UniversalTersePrinter::Print(value, &ss); return ss.str(); } diff --git a/tests/gtest/include/gtest/gtest-spi.h b/tests/gtest/include/gtest/gtest-spi.h index b226e5504..f63fa9a1b 100644 --- a/tests/gtest/include/gtest/gtest-spi.h +++ b/tests/gtest/include/gtest/gtest-spi.h @@ -223,7 +223,7 @@ class GTEST_API_ SingleFailureChecker { (substr));\ {\ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ - ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS,\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ >est_failures);\ if (::testing::internal::AlwaysTrue()) { statement; }\ }\ diff --git a/tests/gtest/include/gtest/gtest-test-part.h b/tests/gtest/include/gtest/gtest-test-part.h index 8aeea1498..77eb84483 100644 --- a/tests/gtest/include/gtest/gtest-test-part.h +++ b/tests/gtest/include/gtest/gtest-test-part.h @@ -62,7 +62,7 @@ class GTEST_API_ TestPartResult { int a_line_number, const char* a_message) : type_(a_type), - file_name_(a_file_name), + file_name_(a_file_name == NULL ? "" : a_file_name), line_number_(a_line_number), summary_(ExtractSummary(a_message)), message_(a_message) { @@ -73,7 +73,9 @@ class GTEST_API_ TestPartResult { // Gets the name of the source file where the test part took place, or // NULL if it's unknown. - const char* file_name() const { return file_name_.c_str(); } + const char* file_name() const { + return file_name_.empty() ? NULL : file_name_.c_str(); + } // Gets the line in the source file where the test part took place, // or -1 if it's unknown. @@ -96,21 +98,22 @@ class GTEST_API_ TestPartResult { // Returns true iff the test part fatally failed. bool fatally_failed() const { return type_ == kFatalFailure; } + private: Type type_; // Gets the summary of the failure message by omitting the stack // trace in it. - static internal::String ExtractSummary(const char* message); + static std::string ExtractSummary(const char* message); // The name of the source file where the test part took place, or - // NULL if the source file is unknown. - internal::String file_name_; + // "" if the source file is unknown. + std::string file_name_; // The line in the source file where the test part took place, or -1 // if the line number is unknown. int line_number_; - internal::String summary_; // The test failure summary. - internal::String message_; // The test failure message. + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. }; // Prints a TestPartResult object. diff --git a/tests/gtest/include/gtest/gtest.h b/tests/gtest/include/gtest/gtest.h index 1e5154715..71d552e1a 100644 --- a/tests/gtest/include/gtest/gtest.h +++ b/tests/gtest/include/gtest/gtest.h @@ -52,6 +52,7 @@ #define GTEST_INCLUDE_GTEST_GTEST_H_ #include +#include #include #include "gtest/internal/gtest-internal.h" @@ -69,14 +70,14 @@ // class ::string, which has the same interface as ::std::string, but // has a different implementation. // -// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that +// You can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that // ::string is available AND is a distinct type to ::std::string, or // define it to 0 to indicate otherwise. // -// If the user's ::std::string and ::string are the same class due to -// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0. +// If ::std::string and ::string are the same class on your platform +// due to aliasing, you should define GTEST_HAS_GLOBAL_STRING to 0. // -// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined +// If you do not define GTEST_HAS_GLOBAL_STRING, it is defined // heuristically. namespace testing { @@ -153,25 +154,15 @@ class ExecDeathTest; class NoExecDeathTest; class FinalSuccessChecker; class GTestFlagSaver; +class StreamingListenerTest; class TestResultAccessor; class TestEventListenersAccessor; class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; class WindowsDeathTest; class UnitTestImpl* GetUnitTestImpl(); void ReportFailureInUnknownLocation(TestPartResult::Type result_type, - const String& message); - -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". -// Declared in gtest-internal.h but defined here, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable) { - return (Message() << streamable).GetString(); -} + const std::string& message); } // namespace internal @@ -267,8 +258,31 @@ class GTEST_API_ AssertionResult { // Copy constructor. // Used in EXPECT_TRUE/FALSE(assertion_result). AssertionResult(const AssertionResult& other); + + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */) + // Used in the EXPECT_TRUE/FALSE(bool_expression). - explicit AssertionResult(bool success) : success_(success) {} + // + // T must be contextually convertible to bool. + // + // The second parameter prevents this overload from being considered if + // the argument is implicitly convertible to AssertionResult. In that case + // we want AssertionResult's copy constructor to be used. + template + explicit AssertionResult( + const T& success, + typename internal::EnableIf< + !internal::ImplicitlyConvertible::value>::type* + /*enabler*/ = NULL) + : success_(success) {} + + GTEST_DISABLE_MSC_WARNINGS_POP_() + + // Assignment operator. + AssertionResult& operator=(AssertionResult other) { + swap(other); + return *this; + } // Returns true iff the assertion succeeded. operator bool() const { return success_; } // NOLINT @@ -309,6 +323,9 @@ class GTEST_API_ AssertionResult { message_->append(a_message.GetString().c_str()); } + // Swap the contents of this AssertionResult with other. + void swap(AssertionResult& other); + // Stores result of the assertion predicate. bool success_; // Stores the message describing the condition in case the expectation @@ -316,8 +333,6 @@ class GTEST_API_ AssertionResult { // Referenced via a pointer to avoid taking too much stack frame space // with test assertions. internal::scoped_ptr< ::std::string> message_; - - GTEST_DISALLOW_ASSIGN_(AssertionResult); }; // Makes a successful assertion result. @@ -344,8 +359,8 @@ GTEST_API_ AssertionResult AssertionFailure(const Message& msg); // // class FooTest : public testing::Test { // protected: -// virtual void SetUp() { ... } -// virtual void TearDown() { ... } +// void SetUp() override { ... } +// void TearDown() override { ... } // ... // }; // @@ -391,20 +406,21 @@ class GTEST_API_ Test { // non-fatal) failure. static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } - // Logs a property for the current test. Only the last value for a given - // key is remembered. - // These are public static so they can be called from utility functions - // that are not members of the test fixture. - // The arguments are const char* instead strings, as Google Test is used - // on platforms where string doesn't compile. - // - // Note that a driving consideration for these RecordProperty methods - // was to produce xml output suited to the Greenspan charting utility, - // which at present will only chart values that fit in a 32-bit int. It - // is the user's responsibility to restrict their values to 32-bit ints - // if they intend them to be used with Greenspan. - static void RecordProperty(const char* key, const char* value); - static void RecordProperty(const char* key, int value); + // Logs a property for the current test, test case, or for the entire + // invocation of the test program when used outside of the context of a + // test case. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestCase or TearDownTestCase are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); protected: // Creates a Test object. @@ -439,17 +455,17 @@ class GTEST_API_ Test { // Uses a GTestFlagSaver to save and restore all Google Test flags. const internal::GTestFlagSaver* const gtest_flag_saver_; - // Often a user mis-spells SetUp() as Setup() and spends a long time + // Often a user misspells SetUp() as Setup() and spends a long time // wondering why it is never called by Google Test. The declaration of // the following method is solely for catching such an error at // compile time: // // - The return type is deliberately chosen to be not void, so it - // will be a conflict if a user declares void Setup() in his test - // fixture. + // will be a conflict if void Setup() is declared in the user's + // test fixture. // // - This method is private, so it will be another compiler error - // if a user calls it from his test fixture. + // if the method is called from the user's test fixture. // // DO NOT OVERRIDE THIS FUNCTION. // @@ -473,7 +489,7 @@ class TestProperty { // C'tor. TestProperty does NOT have a default constructor. // Always use this constructor (with parameters) to create a // TestProperty object. - TestProperty(const char* a_key, const char* a_value) : + TestProperty(const std::string& a_key, const std::string& a_value) : key_(a_key), value_(a_value) { } @@ -488,15 +504,15 @@ class TestProperty { } // Sets a new value, overriding the one supplied in the constructor. - void SetValue(const char* new_value) { + void SetValue(const std::string& new_value) { value_ = new_value; } private: // The key supplied by the user. - internal::String key_; + std::string key_; // The value supplied by the user. - internal::String value_; + std::string value_; }; // The result of a single Test. This includes a list of @@ -547,6 +563,7 @@ class GTEST_API_ TestResult { private: friend class TestInfo; + friend class TestCase; friend class UnitTest; friend class internal::DefaultGlobalTestPartResultReporter; friend class internal::ExecDeathTest; @@ -571,13 +588,16 @@ class GTEST_API_ TestResult { // a non-fatal failure if invalid (e.g., if it conflicts with reserved // key names). If a property is already recorded for the same key, the // value will be updated, rather than storing multiple values for the same - // key. - void RecordProperty(const TestProperty& test_property); + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); // Adds a failure if the key is a reserved attribute of Google Test // testcase tags. Returns true if the property is valid. // TODO(russr): Validate attribute names are legal and human readable. - static bool ValidateTestProperty(const TestProperty& test_property); + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); // Adds a test part result to the list. void AddTestPartResult(const TestPartResult& test_part_result); @@ -650,9 +670,9 @@ class GTEST_API_ TestInfo { return NULL; } - // Returns true if this test should run, that is if the test is not disabled - // (or it is disabled but the also_run_disabled_tests flag has been specified) - // and its full name matches the user-specified filter. + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. // // Google Test allows the user to filter the tests by their full names. // The full name of a test Bar in test case Foo is defined as @@ -668,22 +688,28 @@ class GTEST_API_ TestInfo { // contains the character 'A' or starts with "Foo.". bool should_run() const { return should_run_; } - // Returns true if the test was filtered out by --gtest_filter - bool filtered_out() const { return !matches_filter_; } + // Returns true iff this test will appear in the XML report. + bool is_reportable() const { + // For now, the XML report includes all tests matching the filter. + // In the future, we may trim tests that are excluded because of + // sharding. + return matches_filter_; + } // Returns the result of the test. const TestResult* result() const { return &result_; } private: - #if GTEST_HAS_DEATH_TEST friend class internal::DefaultDeathTestFactory; #endif // GTEST_HAS_DEATH_TEST friend class Test; friend class TestCase; friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; friend TestInfo* internal::MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, + const char* test_case_name, + const char* name, const char* type_param, const char* value_param, internal::TypeId fixture_class_id, @@ -693,9 +719,10 @@ class GTEST_API_ TestInfo { // Constructs a TestInfo object. The newly constructed instance assumes // ownership of the factory object. - TestInfo(const char* test_case_name, const char* name, - const char* a_type_param, - const char* a_value_param, + TestInfo(const std::string& test_case_name, + const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test internal::TypeId fixture_class_id, internal::TestFactoryBase* factory); @@ -775,18 +802,21 @@ class GTEST_API_ TestCase { // Returns true if any test in this test case should run. bool should_run() const { return should_run_; } - // Returns true if this test case should be skipped in the report. - bool should_skip_report() const { return should_skip_report_; } - // Gets the number of successful tests in this test case. int successful_test_count() const; // Gets the number of failed tests in this test case. int failed_test_count() const; + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + // Gets the number of disabled tests in this test case. int disabled_test_count() const; + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + // Get the number of tests in this test case that should run. int test_to_run_count() const; @@ -806,6 +836,10 @@ class GTEST_API_ TestCase { // total_test_count() - 1. If i is not in that range, returns NULL. const TestInfo* GetTestInfo(int i) const; + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestCase and TearDownTestCase. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + private: friend class Test; friend class internal::UnitTestImpl; @@ -824,7 +858,6 @@ class GTEST_API_ TestCase { // Sets the should_run member. void set_should_run(bool should) { should_run_ = should; } - void set_should_skip_report(bool should) { should_skip_report_ = should; } // Adds a TestInfo to this test case. Will delete the TestInfo upon // destruction of the TestCase object. @@ -859,11 +892,22 @@ class GTEST_API_ TestCase { return test_info->should_run() && test_info->result()->Failed(); } + // Returns true iff the test is disabled and will be reported in the XML + // report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + // Returns true iff test is disabled. static bool TestDisabled(const TestInfo* test_info) { return test_info->is_disabled_; } + // Returns true iff this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + // Returns true if the given test should run. static bool ShouldRunTest(const TestInfo* test_info) { return test_info->should_run(); @@ -876,7 +920,7 @@ class GTEST_API_ TestCase { void UnshuffleTests(); // Name of the test case. - internal::String name_; + std::string name_; // Name of the parameter type, or NULL if this is not a typed or a // type-parameterized test. const internal::scoped_ptr type_param_; @@ -893,17 +937,18 @@ class GTEST_API_ TestCase { Test::TearDownTestCaseFunc tear_down_tc_; // True iff any test in this test case should run. bool should_run_; - // True if this test case should not be reported - bool should_skip_report_; // Elapsed time, in milliseconds. TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestCase and + // TearDownTestCase. + TestResult ad_hoc_test_result_; // We disallow copying TestCases. GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); }; // An Environment object is capable of setting up and tearing down an -// environment. The user should subclass this to define his own +// environment. You should subclass this to define your own // environment(s). // // An Environment object does the set-up and tear-down in virtual @@ -1116,11 +1161,13 @@ class GTEST_API_ UnitTest { // Returns the TestCase object for the test that's currently running, // or NULL if no test is running. - const TestCase* current_test_case() const; + const TestCase* current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_); // Returns the TestInfo object for the test that's currently running, // or NULL if no test is running. - const TestInfo* current_test_info() const; + const TestInfo* current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_); // Returns the random seed used at the start of the current test run. int random_seed() const; @@ -1130,7 +1177,8 @@ class GTEST_API_ UnitTest { // value-parameterized tests and instantiate and register them. // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - internal::ParameterizedTestCaseRegistry& parameterized_test_registry(); + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); #endif // GTEST_HAS_PARAM_TEST // Gets the number of successful test cases. @@ -1152,15 +1200,25 @@ class GTEST_API_ UnitTest { // Gets the number of failed tests. int failed_test_count() const; + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + // Gets the number of disabled tests. int disabled_test_count() const; + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + // Gets the number of all tests. int total_test_count() const; // Gets the number of tests that should run. int test_to_run_count() const; + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + // Gets the elapsed time, in milliseconds. TimeInMillis elapsed_time() const; @@ -1175,6 +1233,10 @@ class GTEST_API_ UnitTest { // total_test_case_count() - 1. If i is not in that range, returns NULL. const TestCase* GetTestCase(int i) const; + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test cases. + const TestResult& ad_hoc_test_result() const; + // Returns the list of event listeners that can be used to track events // inside Google Test. TestEventListeners& listeners(); @@ -1198,12 +1260,16 @@ class GTEST_API_ UnitTest { void AddTestPartResult(TestPartResult::Type result_type, const char* file_name, int line_number, - const internal::String& message, - const internal::String& os_stack_trace); + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); - // Adds a TestProperty to the current TestResult object. If the result already - // contains a property with the same key, the value will be updated. - void RecordPropertyForCurrentTest(const char* key, const char* value); + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestCase's ad_hoc_test_result_ when invoked + // from SetUpTestCase or TearDownTestCase, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); // Gets the i-th test case among all the test cases. i can range from 0 to // total_test_case_count() - 1. If i is not in that range, returns NULL. @@ -1218,11 +1284,13 @@ class GTEST_API_ UnitTest { friend class Test; friend class internal::AssertHelper; friend class internal::ScopedTrace; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; friend Environment* AddGlobalTestEnvironment(Environment* env); friend internal::UnitTestImpl* internal::GetUnitTestImpl(); friend void internal::ReportFailureInUnknownLocation( TestPartResult::Type result_type, - const internal::String& message); + const std::string& message); // Creates an empty UnitTest. UnitTest(); @@ -1232,10 +1300,12 @@ class GTEST_API_ UnitTest { // Pushes a trace defined by SCOPED_TRACE() on to the per-thread // Google Test trace stack. - void PushGTestTrace(const internal::TraceInfo& trace); + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); // Pops a trace from the per-thread Google Test trace stack. - void PopGTestTrace(); + void PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_); // Protects mutable state in *impl_. This is mutable as some const // methods need to lock it too. @@ -1290,24 +1360,115 @@ GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); namespace internal { +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); + +#if GTEST_HAS_GLOBAL_STRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string); +#endif + +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring); +#endif + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + // Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) // operand to be used in a failure message. The type (but not value) // of the other operand may affect the format. This allows us to // print a char* as a raw pointer when it is compared against another -// char*, and print it as a C string when it is compared against an -// std::string object, for example. -// -// The default implementation ignores the type of the other operand. -// Some specialized versions are used to handle formatting wide or -// narrow C strings. +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. template -String FormatForComparisonFailureMessage(const T1& value, - const T2& /* other_operand */) { - // C++Builder compiles this incorrectly if the namespace isn't explicitly - // given. - return ::testing::PrintToString(value); +std::string FormatForComparisonFailureMessage( + const T1& value, const T2& /* other_operand */) { + return FormatForComparison::Format(value); +} + +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperEQ. This helps reduce the overhead of some sanitizers +// when calling EXPECT_* in a tight loop. +template +AssertionResult CmpHelperEQFailure(const char* expected_expression, + const char* actual_expression, + const T1& expected, const T2& actual) { + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); } // The helper function for {ASSERT|EXPECT}_EQ. @@ -1316,25 +1477,14 @@ AssertionResult CmpHelperEQ(const char* expected_expression, const char* actual_expression, const T1& expected, const T2& actual) { -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4389) // Temporarily disables warning on - // signed/unsigned mismatch. -#endif - +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4389 /* signed/unsigned mismatch */) if (expected == actual) { return AssertionSuccess(); } +GTEST_DISABLE_MSC_WARNINGS_POP_() -#ifdef _MSC_VER -# pragma warning(pop) // Restores the warning state. -#endif - - return EqFailure(expected_expression, - actual_expression, - FormatForComparisonFailureMessage(expected, actual), - FormatForComparisonFailureMessage(actual, expected), - false); + return CmpHelperEQFailure(expected_expression, actual_expression, expected, + actual); } // With this overloaded version, we allow anonymous enums to be used @@ -1422,6 +1572,19 @@ class EqHelper { } }; +// Separate the error generating code from the code path to reduce the stack +// frame size of CmpHelperOP. This helps reduce the overhead of some sanitizers +// when calling EXPECT_OP in a tight loop. +template +AssertionResult CmpHelperOpFailure(const char* expr1, const char* expr2, + const T1& val1, const T2& val2, + const char* op) { + return AssertionFailure() + << "Expected: (" << expr1 << ") " << op << " (" << expr2 + << "), actual: " << FormatForComparisonFailureMessage(val1, val2) + << " vs " << FormatForComparisonFailureMessage(val2, val1); +} + // A macro for implementing the helper functions needed to implement // ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste // of similar code. @@ -1432,6 +1595,7 @@ class EqHelper { // with gcc 4. // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + #define GTEST_IMPL_CMP_HELPER_(op_name, op)\ template \ AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ @@ -1439,10 +1603,7 @@ AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ if (val1 op val2) {\ return AssertionSuccess();\ } else {\ - return AssertionFailure() \ - << "Expected: (" << expr1 << ") " #op " (" << expr2\ - << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ - << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + return CmpHelperOpFailure(expr1, expr2, val1, val2, #op);\ }\ }\ GTEST_API_ AssertionResult CmpHelper##op_name(\ @@ -1455,11 +1616,11 @@ GTEST_IMPL_CMP_HELPER_(NE, !=); // Implements the helper function for {ASSERT|EXPECT}_LE GTEST_IMPL_CMP_HELPER_(LE, <=); // Implements the helper function for {ASSERT|EXPECT}_LT -GTEST_IMPL_CMP_HELPER_(LT, < ); +GTEST_IMPL_CMP_HELPER_(LT, <); // Implements the helper function for {ASSERT|EXPECT}_GE GTEST_IMPL_CMP_HELPER_(GE, >=); // Implements the helper function for {ASSERT|EXPECT}_GT -GTEST_IMPL_CMP_HELPER_(GT, > ); +GTEST_IMPL_CMP_HELPER_(GT, >); #undef GTEST_IMPL_CMP_HELPER_ @@ -1623,9 +1784,9 @@ class GTEST_API_ AssertHelper { : type(t), file(srcfile), line(line_num), message(msg) { } TestPartResult::Type const type; - const char* const file; - int const line; - String const message; + const char* const file; + int const line; + std::string const message; private: GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); @@ -1684,7 +1845,12 @@ class WithParamInterface { // references static data, to reduce the opportunity for incorrect uses // like writing 'WithParamInterface::GetParam()' for a test that // uses a fixture whose parameter type is int. - const ParamType& GetParam() const { return *parameter_; } + const ParamType& GetParam() const { + GTEST_CHECK_(parameter_ != NULL) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } private: // Sets parameter value. The caller is responsible for making sure the value @@ -1730,12 +1896,6 @@ class TestWithParam : public Test, public WithParamInterface { // usually want the fail-fast behavior of FAIL and ASSERT_*, but those // writing data-driven tests often find themselves using ADD_FAILURE // and EXPECT_* more. -// -// Examples: -// -// EXPECT_TRUE(server.StatusIsOK()); -// ASSERT_FALSE(server.HasPendingRequest(port)) -// << "There are still pending requests " << "on port " << port; // Generates a nonfatal failure with a generic message. #define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") @@ -1909,7 +2069,7 @@ class TestWithParam : public Test, public WithParamInterface { # define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) #endif -// C String Comparisons. All tests treat NULL and any non-NULL string +// C-string Comparisons. All tests treat NULL and any non-NULL string // as different. Two NULLs are equal. // // * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 @@ -2093,8 +2253,8 @@ bool StaticAssertTypeEq() { // The convention is to end the test case name with "Test". For // example, a test case for the Foo class can be named FooTest. // -// The user should put his test code between braces after using this -// macro. Example: +// Test code should appear between braces after an invocation of +// this macro. Example: // // TEST(FooTest, InitializesCorrectly) { // Foo foo; @@ -2150,15 +2310,20 @@ bool StaticAssertTypeEq() { GTEST_TEST_(test_fixture, test_name, test_fixture, \ ::testing::internal::GetTypeId()) -// Use this macro in main() to run all tests. It returns 0 if all +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all // tests are successful, or 1 otherwise. // // RUN_ALL_TESTS() should be invoked after the command line has been // parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; -#define RUN_ALL_TESTS()\ - (::testing::UnitTest::GetInstance()->Run()) - -} // namespace testing +inline int RUN_ALL_TESTS() { + return ::testing::UnitTest::GetInstance()->Run(); +} #endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/tests/gtest/include/gtest/gtest_pred_impl.h b/tests/gtest/include/gtest/gtest_pred_impl.h index 3805f85bd..30ae712f5 100644 --- a/tests/gtest/include/gtest/gtest_pred_impl.h +++ b/tests/gtest/include/gtest/gtest_pred_impl.h @@ -27,7 +27,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// This file is AUTOMATICALLY GENERATED on 09/24/2010 by command +// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command // 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! // // Implements a family of generic predicate assertion macros. @@ -98,7 +98,7 @@ AssertionResult AssertPred1Helper(const char* pred_text, // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. // Don't use this in your code. #define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, v1),\ + GTEST_ASSERT_(pred_format(#v1, v1), \ on_failure) // Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use @@ -144,7 +144,7 @@ AssertionResult AssertPred2Helper(const char* pred_text, // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. // Don't use this in your code. #define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2),\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ on_failure) // Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use @@ -197,7 +197,7 @@ AssertionResult AssertPred3Helper(const char* pred_text, // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. // Don't use this in your code. #define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3),\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ on_failure) // Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use @@ -257,7 +257,7 @@ AssertionResult AssertPred4Helper(const char* pred_text, // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. // Don't use this in your code. #define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4),\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ on_failure) // Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use @@ -324,7 +324,7 @@ AssertionResult AssertPred5Helper(const char* pred_text, // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. // Don't use this in your code. #define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5),\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ on_failure) // Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use diff --git a/tests/gtest/include/gtest/internal/gtest-death-test-internal.h b/tests/gtest/include/gtest/internal/gtest-death-test-internal.h index 1d9f83b65..2b3a78f5b 100644 --- a/tests/gtest/include/gtest/internal/gtest-death-test-internal.h +++ b/tests/gtest/include/gtest/internal/gtest-death-test-internal.h @@ -127,11 +127,11 @@ class GTEST_API_ DeathTest { // the last death test. static const char* LastMessage(); - static void set_last_death_test_message(const String& message); + static void set_last_death_test_message(const std::string& message); private: // A string containing a description of the outcome of the last death test. - static String last_death_test_message_; + static std::string last_death_test_message_; GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); }; @@ -217,12 +217,23 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status); // The symbol "fail" here expands to something into which a message // can be streamed. +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed, the regex is +// ignored, and the macro must accept a streamed message even though the message +// is never printed. +# define GTEST_EXECUTE_STATEMENT_(statement, regex) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else \ + ::testing::Message() + // A class representing the parsed contents of the // --gtest_internal_run_death_test flag, as it existed when // RUN_ALL_TESTS was called. class InternalRunDeathTestFlag { public: - InternalRunDeathTestFlag(const String& a_file, + InternalRunDeathTestFlag(const std::string& a_file, int a_line, int an_index, int a_write_fd) @@ -234,13 +245,13 @@ class InternalRunDeathTestFlag { posix::Close(write_fd_); } - String file() const { return file_; } + const std::string& file() const { return file_; } int line() const { return line_; } int index() const { return index_; } int write_fd() const { return write_fd_; } private: - String file_; + std::string file_; int line_; int index_; int write_fd_; diff --git a/tests/gtest/include/gtest/internal/gtest-filepath.h b/tests/gtest/include/gtest/internal/gtest-filepath.h index b36b3cf21..7a13b4b0d 100644 --- a/tests/gtest/include/gtest/internal/gtest-filepath.h +++ b/tests/gtest/include/gtest/internal/gtest-filepath.h @@ -61,11 +61,7 @@ class GTEST_API_ FilePath { FilePath() : pathname_("") { } FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } - explicit FilePath(const char* pathname) : pathname_(pathname) { - Normalize(); - } - - explicit FilePath(const String& pathname) : pathname_(pathname) { + explicit FilePath(const std::string& pathname) : pathname_(pathname) { Normalize(); } @@ -78,7 +74,7 @@ class GTEST_API_ FilePath { pathname_ = rhs.pathname_; } - String ToString() const { return pathname_; } + const std::string& string() const { return pathname_; } const char* c_str() const { return pathname_.c_str(); } // Returns the current working directory, or "" if unsuccessful. @@ -111,8 +107,8 @@ class GTEST_API_ FilePath { const FilePath& base_name, const char* extension); - // Returns true iff the path is NULL or "". - bool IsEmpty() const { return c_str() == NULL || *c_str() == '\0'; } + // Returns true iff the path is "". + bool IsEmpty() const { return pathname_.empty(); } // If input name has a trailing separator character, removes it and returns // the name, otherwise return the name string unmodified. @@ -201,7 +197,7 @@ class GTEST_API_ FilePath { // separators. Returns NULL if no path separator was found. const char* FindLastPathSeparator() const; - String pathname_; + std::string pathname_; }; // class FilePath } // namespace internal diff --git a/tests/gtest/include/gtest/internal/gtest-internal.h b/tests/gtest/include/gtest/internal/gtest-internal.h index ad5369d61..ba7175cf0 100644 --- a/tests/gtest/include/gtest/internal/gtest-internal.h +++ b/tests/gtest/include/gtest/internal/gtest-internal.h @@ -44,18 +44,22 @@ # include # include # include -# include #endif // GTEST_OS_LINUX -#if GTEST_CAN_STREAM_RESULTS_ -# include -#endif // GTEST_CAN_STREAM_RESULTS_ + +#if GTEST_HAS_EXCEPTIONS +# include +#endif #include +#include #include #include #include #include +#include +#include +#include "gtest/gtest-message.h" #include "gtest/internal/gtest-string.h" #include "gtest/internal/gtest-filepath.h" #include "gtest/internal/gtest-type-util.h" @@ -71,36 +75,6 @@ #define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) #define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar -// Google Test defines the testing::Message class to allow construction of -// test messages via the << operator. The idea is that anything -// streamable to std::ostream can be streamed to a testing::Message. -// This allows a user to use his own types in Google Test assertions by -// overloading the << operator. -// -// util/gtl/stl_logging-inl.h overloads << for STL containers. These -// overloads cannot be defined in the std namespace, as that will be -// undefined behavior. Therefore, they are defined in the global -// namespace instead. -// -// C++'s symbol lookup rule (i.e. Koenig lookup) says that these -// overloads are visible in either the std namespace or the global -// namespace, but not other namespaces, including the testing -// namespace which Google Test's Message class is in. -// -// To allow STL containers (and other types that has a << operator -// defined in the global namespace) to be used in Google Test assertions, -// testing::Message must access the custom << operator from the global -// namespace. Hence this helper function. -// -// Note: Jeffrey Yasskin suggested an alternative fix by "using -// ::operator<<;" in the definition of Message's operator<<. That fix -// doesn't require a helper function, but unfortunately doesn't -// compile with MSVC. -template -inline void GTestStreamToHelper(std::ostream* os, const T& val) { - *os << val; -} - class ProtocolMessage; namespace proto2 { class Message; } @@ -126,17 +100,12 @@ class TestInfoImpl; // Opaque implementation of TestInfo class UnitTestImpl; // Opaque implementation of UnitTest // How many times InitGoogleTest() has been called. -extern int g_init_gtest_count; +GTEST_API_ extern int g_init_gtest_count; // The text used in failure messages to indicate the start of the // stack trace. GTEST_API_ extern const char kStackTraceMarker[]; -// A secret type that Google Test users don't know about. It has no -// definition on purpose. Therefore it's impossible to create a -// Secret object, which is what we want. -class Secret; - // Two overloaded helpers for checking at compile time whether an // expression is a null pointer literal (i.e. NULL or any 0-valued // compile-time integral constant). Their return values have @@ -167,8 +136,23 @@ char (&IsNullLiteralHelper(...))[2]; // NOLINT #endif // GTEST_ELLIPSIS_NEEDS_POD_ // Appends the user-supplied message to the Google-Test-generated message. -GTEST_API_ String AppendUserMessage(const String& gtest_msg, - const Message& user_msg); +GTEST_API_ std::string AppendUserMessage( + const std::string& gtest_msg, const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +#endif // GTEST_HAS_EXCEPTIONS // A helper class for creating scoped traces in user programs. class GTEST_API_ ScopedTrace { @@ -189,76 +173,35 @@ class GTEST_API_ ScopedTrace { // c'tor and d'tor. Therefore it doesn't // need to be used otherwise. -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". -// Declared here but defined in gtest.h, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable); +namespace edit_distance { +// Returns the optimal edits to go from 'left' to 'right'. +// All edits cost the same, with replace having lower priority than +// add/remove. +// Simple implementation of the Wagner–Fischer algorithm. +// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm +enum EditType { kMatch, kAdd, kRemove, kReplace }; +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, const std::vector& right); -// The Symbian compiler has a bug that prevents it from selecting the -// correct overload of FormatForComparisonFailureMessage (see below) -// unless we pass the first argument by reference. If we do that, -// however, Visual Age C++ 10.1 generates a compiler error. Therefore -// we only apply the work-around for Symbian. -#if defined(__SYMBIAN32__) -# define GTEST_CREF_WORKAROUND_ const& -#else -# define GTEST_CREF_WORKAROUND_ -#endif +// Same as above, but the input is represented as strings. +GTEST_API_ std::vector CalculateOptimalEdits( + const std::vector& left, + const std::vector& right); -// When this operand is a const char* or char*, if the other operand -// is a ::std::string or ::string, we print this operand as a C string -// rather than a pointer (we do the same for wide strings); otherwise -// we print it as a pointer to be safe. +// Create a diff of the input strings in Unified diff format. +GTEST_API_ std::string CreateUnifiedDiff(const std::vector& left, + const std::vector& right, + size_t context = 2); -// This internal macro is used to avoid duplicated code. -#define GTEST_FORMAT_IMPL_(operand2_type, operand1_printer)\ -inline String FormatForComparisonFailureMessage(\ - operand2_type::value_type* GTEST_CREF_WORKAROUND_ str, \ - const operand2_type& /*operand2*/) {\ - return operand1_printer(str);\ -}\ -inline String FormatForComparisonFailureMessage(\ - const operand2_type::value_type* GTEST_CREF_WORKAROUND_ str, \ - const operand2_type& /*operand2*/) {\ - return operand1_printer(str);\ -} +} // namespace edit_distance -GTEST_FORMAT_IMPL_(::std::string, String::ShowCStringQuoted) -#if GTEST_HAS_STD_WSTRING -GTEST_FORMAT_IMPL_(::std::wstring, String::ShowWideCStringQuoted) -#endif // GTEST_HAS_STD_WSTRING - -#if GTEST_HAS_GLOBAL_STRING -GTEST_FORMAT_IMPL_(::string, String::ShowCStringQuoted) -#endif // GTEST_HAS_GLOBAL_STRING -#if GTEST_HAS_GLOBAL_WSTRING -GTEST_FORMAT_IMPL_(::wstring, String::ShowWideCStringQuoted) -#endif // GTEST_HAS_GLOBAL_WSTRING - -#undef GTEST_FORMAT_IMPL_ - -// The next four overloads handle the case where the operand being -// printed is a char/wchar_t pointer and the other operand is not a -// string/wstring object. In such cases, we just print the operand as -// a pointer to be safe. -#define GTEST_FORMAT_CHAR_PTR_IMPL_(CharType) \ - template \ - String FormatForComparisonFailureMessage(CharType* GTEST_CREF_WORKAROUND_ p, \ - const T&) { \ - return PrintToString(static_cast(p)); \ - } - -GTEST_FORMAT_CHAR_PTR_IMPL_(char) -GTEST_FORMAT_CHAR_PTR_IMPL_(const char) -GTEST_FORMAT_CHAR_PTR_IMPL_(wchar_t) -GTEST_FORMAT_CHAR_PTR_IMPL_(const wchar_t) - -#undef GTEST_FORMAT_CHAR_PTR_IMPL_ +// Calculate the diff between 'left' and 'right' and return it in unified diff +// format. +// If not null, stores in 'total_line_count' the total number of lines found +// in left + right. +GTEST_API_ std::string DiffStrings(const std::string& left, + const std::string& right, + size_t* total_line_count); // Constructs and returns the message for an equality assertion // (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. @@ -277,12 +220,12 @@ GTEST_FORMAT_CHAR_PTR_IMPL_(const wchar_t) // be inserted into the message. GTEST_API_ AssertionResult EqFailure(const char* expected_expression, const char* actual_expression, - const String& expected_value, - const String& actual_value, + const std::string& expected_value, + const std::string& actual_value, bool ignoring_case); // Constructs a failure message for Boolean assertions such as EXPECT_TRUE. -GTEST_API_ String GetBoolAssertionFailureMessage( +GTEST_API_ std::string GetBoolAssertionFailureMessage( const AssertionResult& assertion_result, const char* expression_text, const char* actual_predicate_value, @@ -357,7 +300,7 @@ class FloatingPoint { // bits. Therefore, 4 should be enough for ordinary use. // // See the following article for more details on ULP: - // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm. + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ static const size_t kMaxUlps = 4; // Constructs a FloatingPoint from a raw floating-point number. @@ -384,6 +327,9 @@ class FloatingPoint { return ReinterpretBits(kExponentBitMask); } + // Returns the maximum representable finite floating-point number. + static RawType Max(); + // Non-static methods // Returns the bits that represents this number. @@ -464,6 +410,13 @@ class FloatingPoint { FloatingPointUnion u_; }; +// We cannot use std::numeric_limits::max() as it clashes with the max() +// macro defined by . +template <> +inline float FloatingPoint::Max() { return FLT_MAX; } +template <> +inline double FloatingPoint::Max() { return DBL_MAX; } + // Typedefs the instances of the FloatingPoint template class that we // care to use. typedef FloatingPoint Float; @@ -558,7 +511,7 @@ typedef void (*TearDownTestCaseFunc)(); // test_case_name: name of the test case // name: name of the test // type_param the name of the test's type parameter, or NULL if -// this is not a typed or a type-parameterized test. +// this is not a typed or a type-parameterized test. // value_param text representation of the test's value parameter, // or NULL if this is not a type-parameterized test. // fixture_class_id: ID of the test fixture class @@ -568,7 +521,8 @@ typedef void (*TearDownTestCaseFunc)(); // The newly created TestInfo instance will assume // ownership of the factory object. GTEST_API_ TestInfo* MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, + const char* test_case_name, + const char* name, const char* type_param, const char* value_param, TypeId fixture_class_id, @@ -628,9 +582,9 @@ inline const char* SkipComma(const char* str) { // Returns the prefix of 'str' before the first comma in it; returns // the entire string if it contains no comma. -inline String GetPrefixUntilComma(const char* str) { +inline std::string GetPrefixUntilComma(const char* str) { const char* comma = strchr(str, ','); - return comma == NULL ? String(str) : String(str, comma - str); + return comma == NULL ? str : std::string(str, comma); } // TypeParameterizedTest::Register() @@ -656,9 +610,9 @@ class TypeParameterizedTest { // First, registers the first type-parameterized test in the type // list. MakeAndRegisterTestInfo( - String::Format("%s%s%s/%d", prefix, prefix[0] == '\0' ? "" : "/", - case_name, index).c_str(), - GetPrefixUntilComma(test_names).c_str(), + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/" + + StreamableToString(index)).c_str(), + StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(), GetTypeName().c_str(), NULL, // No value parameter. GetTypeId(), @@ -715,7 +669,7 @@ class TypeParameterizedTestCase { #endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P -// Returns the current OS stack trace as a String. +// Returns the current OS stack trace as an std::string. // // The maximum number of stack frames to be included is specified by // the gtest_stack_trace_depth flag. The skip_count parameter @@ -725,8 +679,8 @@ class TypeParameterizedTestCase { // For example, if Foo() calls Bar(), which in turn calls // GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in // the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. -GTEST_API_ String GetCurrentOsStackTraceExceptTop(UnitTest* unit_test, - int skip_count); +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop( + UnitTest* unit_test, int skip_count); // Helpers for suppressing warnings on unreachable code or constant // condition. @@ -801,13 +755,19 @@ struct RemoveConst { typedef T type; }; // NOLINT // MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above // definition to fail to remove the const in 'const int[3]' and 'const // char[3][4]'. The following specialization works around the bug. -// However, it causes trouble with GCC and thus needs to be -// conditionally compiled. -#if defined(_MSC_VER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) template struct RemoveConst { typedef typename RemoveConst::type type[N]; }; + +#if defined(_MSC_VER) && _MSC_VER < 1400 +// This is the only specialization that allows VC++ 7.1 to remove const in +// 'const int[3] and 'const int[3][4]'. However, it causes trouble with GCC +// and thus needs to be conditionally compiled. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; #endif // A handy wrapper around RemoveConst that works when the argument @@ -856,7 +816,7 @@ class ImplicitlyConvertible { // MakeFrom() is an expression whose type is From. We cannot simply // use From(), as the type From may not have a public default // constructor. - static From MakeFrom(); + static typename AddReference::type MakeFrom(); // These two functions are overloaded. Given an expression // Helper(x), the compiler will pick the first version if x can be @@ -874,25 +834,20 @@ class ImplicitlyConvertible { // We have to put the 'public' section after the 'private' section, // or MSVC refuses to compile the code. public: - // MSVC warns about implicitly converting from double to int for - // possible loss of data, so we need to temporarily disable the - // warning. -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4244) // Temporarily disables warning 4244. - - static const bool value = - sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; -# pragma warning(pop) // Restores the warning state. -#elif defined(__BORLANDC__) +#if defined(__BORLANDC__) // C++Builder cannot use member overload resolution during template // instantiation. The simplest workaround is to use its C++0x type traits // functions (C++Builder 2009 and above only). static const bool value = __is_convertible(From, To); #else + // MSVC warns about implicitly converting from double to int for + // possible loss of data, so we need to temporarily disable the + // warning. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4244) static const bool value = sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; -#endif // _MSV_VER + GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif // __BORLANDC__ }; template const bool ImplicitlyConvertible::value; @@ -1018,11 +973,10 @@ void CopyArray(const T* from, size_t size, U* to) { // The relation between an NativeArray object (see below) and the // native array it represents. -enum RelationToSource { - kReference, // The NativeArray references the native array. - kCopy // The NativeArray makes a copy of the native array and - // owns the copy. -}; +// We use 2 different structs to allow non-copyable types to be used, as long +// as RelationToSourceReference() is passed. +struct RelationToSourceReference {}; +struct RelationToSourceCopy {}; // Adapts a native array to a read-only STL-style container. Instead // of the complete STL container concept, this adaptor only implements @@ -1040,22 +994,23 @@ class NativeArray { typedef Element* iterator; typedef const Element* const_iterator; - // Constructs from a native array. - NativeArray(const Element* array, size_t count, RelationToSource relation) { - Init(array, count, relation); + // Constructs from a native array. References the source. + NativeArray(const Element* array, size_t count, RelationToSourceReference) { + InitRef(array, count); + } + + // Constructs from a native array. Copies the source. + NativeArray(const Element* array, size_t count, RelationToSourceCopy) { + InitCopy(array, count); } // Copy constructor. NativeArray(const NativeArray& rhs) { - Init(rhs.array_, rhs.size_, rhs.relation_to_source_); + (this->*rhs.clone_)(rhs.array_, rhs.size_); } ~NativeArray() { - // Ensures that the user doesn't instantiate NativeArray with a - // const or reference type. - static_cast(StaticAssertTypeEqHelper()); - if (relation_to_source_ == kCopy) + if (clone_ != &NativeArray::InitRef) delete[] array_; } @@ -1069,23 +1024,30 @@ class NativeArray { } private: - // Initializes this object; makes a copy of the input array if - // 'relation' is kCopy. - void Init(const Element* array, size_t a_size, RelationToSource relation) { - if (relation == kReference) { - array_ = array; - } else { - Element* const copy = new Element[a_size]; - CopyArray(array, a_size, copy); - array_ = copy; - } + enum { + kCheckTypeIsNotConstOrAReference = StaticAssertTypeEqHelper< + Element, GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>::value, + }; + + // Initializes this object with a copy of the input. + void InitCopy(const Element* array, size_t a_size) { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; size_ = a_size; - relation_to_source_ = relation; + clone_ = &NativeArray::InitCopy; + } + + // Initializes this object with a reference of the input. + void InitRef(const Element* array, size_t a_size) { + array_ = array; + size_ = a_size; + clone_ = &NativeArray::InitRef; } const Element* array_; size_t size_; - RelationToSource relation_to_source_; + void (NativeArray::*clone_)(const Element*, size_t); GTEST_DISALLOW_ASSIGN_(NativeArray); }; @@ -1228,3 +1190,4 @@ class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + diff --git a/tests/gtest/include/gtest/internal/gtest-linked_ptr.h b/tests/gtest/include/gtest/internal/gtest-linked_ptr.h index 57147b4e8..360294221 100644 --- a/tests/gtest/include/gtest/internal/gtest-linked_ptr.h +++ b/tests/gtest/include/gtest/internal/gtest-linked_ptr.h @@ -105,25 +105,35 @@ class linked_ptr_internal { // framework. // Join an existing circle. - // L < g_linked_ptr_mutex - void join(linked_ptr_internal const* ptr) { + void join(linked_ptr_internal const* ptr) + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { MutexLock lock(&g_linked_ptr_mutex); linked_ptr_internal const* p = ptr; - while (p->next_ != ptr) p = p->next_; + while (p->next_ != ptr) { + assert(p->next_ != this && + "Trying to join() a linked ring we are already in. " + "Is GMock thread safety enabled?"); + p = p->next_; + } p->next_ = this; next_ = ptr; } // Leave whatever circle we're part of. Returns true if we were the // last member of the circle. Once this is done, you can join() another. - // L < g_linked_ptr_mutex - bool depart() { + bool depart() + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { MutexLock lock(&g_linked_ptr_mutex); if (next_ == this) return true; linked_ptr_internal const* p = next_; - while (p->next_ != this) p = p->next_; + while (p->next_ != this) { + assert(p->next_ != next_ && + "Trying to depart() a linked ring we are not in. " + "Is GMock thread safety enabled?"); + p = p->next_; + } p->next_ = next_; return false; } diff --git a/tests/gtest/include/gtest/internal/gtest-param-util-generated.h b/tests/gtest/include/gtest/internal/gtest-param-util-generated.h index 258267500..6dbaf4b7a 100644 --- a/tests/gtest/include/gtest/internal/gtest-param-util-generated.h +++ b/tests/gtest/include/gtest/internal/gtest-param-util-generated.h @@ -40,7 +40,7 @@ // and at most 10 arguments in Combine. Please contact // googletestframework@googlegroups.com if you need more. // Please note that the number of arguments to Combine is limited -// by the maximum arity of the implementation of tr1::tuple which is +// by the maximum arity of the implementation of tuple which is // currently set at 10. #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ @@ -95,7 +95,7 @@ class ValueArray2 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_}; + const T array[] = {static_cast(v1_), static_cast(v2_)}; return ValuesIn(array); } @@ -114,7 +114,8 @@ class ValueArray3 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_)}; return ValuesIn(array); } @@ -135,7 +136,8 @@ class ValueArray4 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_)}; return ValuesIn(array); } @@ -157,7 +159,8 @@ class ValueArray5 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_)}; return ValuesIn(array); } @@ -181,7 +184,9 @@ class ValueArray6 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_)}; return ValuesIn(array); } @@ -206,7 +211,9 @@ class ValueArray7 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_)}; return ValuesIn(array); } @@ -233,7 +240,9 @@ class ValueArray8 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_)}; return ValuesIn(array); } @@ -261,7 +270,10 @@ class ValueArray9 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_)}; return ValuesIn(array); } @@ -290,7 +302,10 @@ class ValueArray10 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_)}; return ValuesIn(array); } @@ -321,7 +336,10 @@ class ValueArray11 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_)}; return ValuesIn(array); } @@ -353,8 +371,11 @@ class ValueArray12 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_)}; return ValuesIn(array); } @@ -388,8 +409,11 @@ class ValueArray13 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_)}; return ValuesIn(array); } @@ -424,8 +448,11 @@ class ValueArray14 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_)}; return ValuesIn(array); } @@ -461,8 +488,12 @@ class ValueArray15 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_)}; return ValuesIn(array); } @@ -501,8 +532,12 @@ class ValueArray16 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_)}; return ValuesIn(array); } @@ -542,8 +577,12 @@ class ValueArray17 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_)}; return ValuesIn(array); } @@ -584,8 +623,13 @@ class ValueArray18 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_)}; return ValuesIn(array); } @@ -627,8 +671,13 @@ class ValueArray19 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_)}; return ValuesIn(array); } @@ -672,8 +721,13 @@ class ValueArray20 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_)}; return ValuesIn(array); } @@ -719,8 +773,14 @@ class ValueArray21 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_)}; return ValuesIn(array); } @@ -767,8 +827,14 @@ class ValueArray22 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_)}; return ValuesIn(array); } @@ -817,9 +883,14 @@ class ValueArray23 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, - v23_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_)}; return ValuesIn(array); } @@ -869,9 +940,15 @@ class ValueArray24 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_)}; return ValuesIn(array); } @@ -922,9 +999,15 @@ class ValueArray25 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_)}; return ValuesIn(array); } @@ -977,9 +1060,15 @@ class ValueArray26 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_)}; return ValuesIn(array); } @@ -1034,9 +1123,16 @@ class ValueArray27 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_)}; return ValuesIn(array); } @@ -1092,9 +1188,16 @@ class ValueArray28 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_)}; return ValuesIn(array); } @@ -1151,9 +1254,16 @@ class ValueArray29 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_)}; return ValuesIn(array); } @@ -1212,9 +1322,17 @@ class ValueArray30 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_)}; return ValuesIn(array); } @@ -1275,9 +1393,17 @@ class ValueArray31 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_)}; return ValuesIn(array); } @@ -1339,9 +1465,17 @@ class ValueArray32 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_)}; return ValuesIn(array); } @@ -1405,9 +1539,18 @@ class ValueArray33 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_)}; return ValuesIn(array); } @@ -1472,9 +1615,18 @@ class ValueArray34 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_)}; return ValuesIn(array); } @@ -1540,10 +1692,18 @@ class ValueArray35 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, - v35_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_)}; return ValuesIn(array); } @@ -1611,10 +1771,19 @@ class ValueArray36 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_)}; return ValuesIn(array); } @@ -1684,10 +1853,19 @@ class ValueArray37 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_)}; return ValuesIn(array); } @@ -1758,10 +1936,19 @@ class ValueArray38 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_)}; return ValuesIn(array); } @@ -1833,10 +2020,20 @@ class ValueArray39 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_)}; return ValuesIn(array); } @@ -1910,10 +2107,20 @@ class ValueArray40 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_)}; return ValuesIn(array); } @@ -1989,10 +2196,20 @@ class ValueArray41 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_)}; return ValuesIn(array); } @@ -2069,10 +2286,21 @@ class ValueArray42 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_)}; return ValuesIn(array); } @@ -2150,10 +2378,21 @@ class ValueArray43 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_)}; return ValuesIn(array); } @@ -2233,10 +2472,21 @@ class ValueArray44 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_)}; return ValuesIn(array); } @@ -2317,10 +2567,22 @@ class ValueArray45 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_)}; return ValuesIn(array); } @@ -2403,10 +2665,22 @@ class ValueArray46 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_)}; return ValuesIn(array); } @@ -2491,11 +2765,22 @@ class ValueArray47 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, - v47_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_)}; return ValuesIn(array); } @@ -2581,11 +2866,23 @@ class ValueArray48 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_)}; return ValuesIn(array); } @@ -2672,11 +2969,23 @@ class ValueArray49 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_, v49_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_)}; return ValuesIn(array); } @@ -2764,11 +3073,23 @@ class ValueArray50 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_, v49_, v50_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_), static_cast(v50_)}; return ValuesIn(array); } @@ -2836,9 +3157,9 @@ class ValueArray50 { // template class CartesianProductGenerator2 - : public ParamGeneratorInterface< ::std::tr1::tuple > { + : public ParamGeneratorInterface< ::testing::tuple > { public: - typedef ::std::tr1::tuple ParamType; + typedef ::testing::tuple ParamType; CartesianProductGenerator2(const ParamGenerator& g1, const ParamGenerator& g2) @@ -2951,9 +3272,9 @@ class CartesianProductGenerator2 template class CartesianProductGenerator3 - : public ParamGeneratorInterface< ::std::tr1::tuple > { + : public ParamGeneratorInterface< ::testing::tuple > { public: - typedef ::std::tr1::tuple ParamType; + typedef ::testing::tuple ParamType; CartesianProductGenerator3(const ParamGenerator& g1, const ParamGenerator& g2, const ParamGenerator& g3) @@ -3083,9 +3404,9 @@ class CartesianProductGenerator3 template class CartesianProductGenerator4 - : public ParamGeneratorInterface< ::std::tr1::tuple > { + : public ParamGeneratorInterface< ::testing::tuple > { public: - typedef ::std::tr1::tuple ParamType; + typedef ::testing::tuple ParamType; CartesianProductGenerator4(const ParamGenerator& g1, const ParamGenerator& g2, const ParamGenerator& g3, @@ -3234,9 +3555,9 @@ class CartesianProductGenerator4 template class CartesianProductGenerator5 - : public ParamGeneratorInterface< ::std::tr1::tuple > { + : public ParamGeneratorInterface< ::testing::tuple > { public: - typedef ::std::tr1::tuple ParamType; + typedef ::testing::tuple ParamType; CartesianProductGenerator5(const ParamGenerator& g1, const ParamGenerator& g2, const ParamGenerator& g3, @@ -3402,10 +3723,10 @@ class CartesianProductGenerator5 template class CartesianProductGenerator6 - : public ParamGeneratorInterface< ::std::tr1::tuple > { public: - typedef ::std::tr1::tuple ParamType; + typedef ::testing::tuple ParamType; CartesianProductGenerator6(const ParamGenerator& g1, const ParamGenerator& g2, const ParamGenerator& g3, @@ -3588,10 +3909,10 @@ class CartesianProductGenerator6 template class CartesianProductGenerator7 - : public ParamGeneratorInterface< ::std::tr1::tuple > { public: - typedef ::std::tr1::tuple ParamType; + typedef ::testing::tuple ParamType; CartesianProductGenerator7(const ParamGenerator& g1, const ParamGenerator& g2, const ParamGenerator& g3, @@ -3791,10 +4112,10 @@ class CartesianProductGenerator7 template class CartesianProductGenerator8 - : public ParamGeneratorInterface< ::std::tr1::tuple > { public: - typedef ::std::tr1::tuple ParamType; + typedef ::testing::tuple ParamType; CartesianProductGenerator8(const ParamGenerator& g1, const ParamGenerator& g2, const ParamGenerator& g3, @@ -4013,10 +4334,10 @@ class CartesianProductGenerator8 template class CartesianProductGenerator9 - : public ParamGeneratorInterface< ::std::tr1::tuple > { public: - typedef ::std::tr1::tuple ParamType; + typedef ::testing::tuple ParamType; CartesianProductGenerator9(const ParamGenerator& g1, const ParamGenerator& g2, const ParamGenerator& g3, @@ -4252,10 +4573,10 @@ class CartesianProductGenerator9 template class CartesianProductGenerator10 - : public ParamGeneratorInterface< ::std::tr1::tuple > { public: - typedef ::std::tr1::tuple ParamType; + typedef ::testing::tuple ParamType; CartesianProductGenerator10(const ParamGenerator& g1, const ParamGenerator& g2, const ParamGenerator& g3, @@ -4517,8 +4838,8 @@ class CartesianProductHolder2 { CartesianProductHolder2(const Generator1& g1, const Generator2& g2) : g1_(g1), g2_(g2) {} template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( new CartesianProductGenerator2( static_cast >(g1_), static_cast >(g2_))); @@ -4539,8 +4860,8 @@ CartesianProductHolder3(const Generator1& g1, const Generator2& g2, const Generator3& g3) : g1_(g1), g2_(g2), g3_(g3) {} template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( new CartesianProductGenerator3( static_cast >(g1_), static_cast >(g2_), @@ -4564,8 +4885,8 @@ CartesianProductHolder4(const Generator1& g1, const Generator2& g2, const Generator3& g3, const Generator4& g4) : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( new CartesianProductGenerator4( static_cast >(g1_), static_cast >(g2_), @@ -4591,8 +4912,8 @@ CartesianProductHolder5(const Generator1& g1, const Generator2& g2, const Generator3& g3, const Generator4& g4, const Generator5& g5) : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( new CartesianProductGenerator5( static_cast >(g1_), static_cast >(g2_), @@ -4622,8 +4943,8 @@ CartesianProductHolder6(const Generator1& g1, const Generator2& g2, : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( new CartesianProductGenerator6( static_cast >(g1_), static_cast >(g2_), @@ -4655,9 +4976,9 @@ CartesianProductHolder7(const Generator1& g1, const Generator2& g2, : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( + return ParamGenerator< ::testing::tuple >( new CartesianProductGenerator7( static_cast >(g1_), static_cast >(g2_), @@ -4693,9 +5014,9 @@ CartesianProductHolder8(const Generator1& g1, const Generator2& g2, g8_(g8) {} template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( + return ParamGenerator< ::testing::tuple >( new CartesianProductGenerator8( static_cast >(g1_), static_cast >(g2_), @@ -4734,9 +5055,9 @@ CartesianProductHolder9(const Generator1& g1, const Generator2& g2, g9_(g9) {} template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( new CartesianProductGenerator9( static_cast >(g1_), @@ -4778,10 +5099,10 @@ CartesianProductHolder10(const Generator1& g1, const Generator2& g2, g9_(g9), g10_(g10) {} template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( + operator ParamGenerator< ::testing::tuple >() const { + return ParamGenerator< ::testing::tuple >( new CartesianProductGenerator10( static_cast >(g1_), diff --git a/tests/gtest/include/gtest/internal/gtest-param-util-generated.h.pump b/tests/gtest/include/gtest/internal/gtest-param-util-generated.h.pump index dbe938630..801a2fc7d 100644 --- a/tests/gtest/include/gtest/internal/gtest-param-util-generated.h.pump +++ b/tests/gtest/include/gtest/internal/gtest-param-util-generated.h.pump @@ -39,7 +39,7 @@ $var maxtuple = 10 $$ Maximum number of Combine arguments we want to support. // and at most $maxtuple arguments in Combine. Please contact // googletestframework@googlegroups.com if you need more. // Please note that the number of arguments to Combine is limited -// by the maximum arity of the implementation of tr1::tuple which is +// by the maximum arity of the implementation of tuple which is // currently set at $maxtuple. #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ @@ -98,7 +98,7 @@ class ValueArray$i { template operator ParamGenerator() const { - const T array[] = {$for j, [[v$(j)_]]}; + const T array[] = {$for j, [[static_cast(v$(j)_)]]}; return ValuesIn(array); } @@ -128,9 +128,9 @@ $range k 2..i template <$for j, [[typename T$j]]> class CartesianProductGenerator$i - : public ParamGeneratorInterface< ::std::tr1::tuple<$for j, [[T$j]]> > { + : public ParamGeneratorInterface< ::testing::tuple<$for j, [[T$j]]> > { public: - typedef ::std::tr1::tuple<$for j, [[T$j]]> ParamType; + typedef ::testing::tuple<$for j, [[T$j]]> ParamType; CartesianProductGenerator$i($for j, [[const ParamGenerator& g$j]]) : $for j, [[g$(j)_(g$j)]] {} @@ -269,8 +269,8 @@ class CartesianProductHolder$i { CartesianProductHolder$i($for j, [[const Generator$j& g$j]]) : $for j, [[g$(j)_(g$j)]] {} template <$for j, [[typename T$j]]> - operator ParamGenerator< ::std::tr1::tuple<$for j, [[T$j]]> >() const { - return ParamGenerator< ::std::tr1::tuple<$for j, [[T$j]]> >( + operator ParamGenerator< ::testing::tuple<$for j, [[T$j]]> >() const { + return ParamGenerator< ::testing::tuple<$for j, [[T$j]]> >( new CartesianProductGenerator$i<$for j, [[T$j]]>( $for j,[[ diff --git a/tests/gtest/include/gtest/internal/gtest-param-util.h b/tests/gtest/include/gtest/internal/gtest-param-util.h index 0ef9718cf..d5e1028b0 100644 --- a/tests/gtest/include/gtest/internal/gtest-param-util.h +++ b/tests/gtest/include/gtest/internal/gtest-param-util.h @@ -494,10 +494,10 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { const string& instantiation_name = gen_it->first; ParamGenerator generator((*gen_it->second)()); - Message test_case_name_stream; + string test_case_name; if ( !instantiation_name.empty() ) - test_case_name_stream << instantiation_name << "/"; - test_case_name_stream << test_info->test_case_base_name; + test_case_name = instantiation_name + "/"; + test_case_name += test_info->test_case_base_name; int i = 0; for (typename ParamGenerator::iterator param_it = @@ -506,7 +506,7 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { Message test_name_stream; test_name_stream << test_info->test_base_name << "/" << i; MakeAndRegisterTestInfo( - test_case_name_stream.GetString().c_str(), + test_case_name.c_str(), test_name_stream.GetString().c_str(), NULL, // No type parameter. PrintToString(*param_it).c_str(), diff --git a/tests/gtest/include/gtest/internal/gtest-port.h b/tests/gtest/include/gtest/internal/gtest-port.h index ce70acdd3..98498309f 100644 --- a/tests/gtest/include/gtest/internal/gtest-port.h +++ b/tests/gtest/include/gtest/internal/gtest-port.h @@ -30,15 +30,43 @@ // Authors: wan@google.com (Zhanyong Wan) // // Low-level types and utilities for porting Google Test to various -// platforms. They are subject to change without notice. DO NOT USE -// THEM IN USER CODE. +// platforms. All macros ending with _ and symbols defined in an +// internal namespace are subject to change without notice. Code +// outside Google Test MUST NOT USE THEM DIRECTLY. Macros that don't +// end with _ are part of Google Test's public API and can be used by +// code outside Google Test. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ -// The user can define the following macros in the build script to -// control Google Test's behavior. If the user doesn't define a macro -// in this list, Google Test will define it. +// Environment-describing macros +// ----------------------------- +// +// Google Test can be used in many different environments. Macros in +// this section tell Google Test what kind of environment it is being +// used in, such that Google Test can provide environment-specific +// features and implementations. +// +// Google Test tries to automatically detect the properties of its +// environment, so users usually don't need to worry about these +// macros. However, the automatic detection is not perfect. +// Sometimes it's necessary for a user to define some of the following +// macros in the build script to override Google Test's decisions. +// +// If the user doesn't define a macro in the list, Google Test will +// provide a default definition. After this header is #included, all +// macros in this list will be defined to either 1 or 0. +// +// Notes to maintainers: +// - Each macro here is a user-tweakable knob; do not grow the list +// lightly. +// - Use #if to key off these macros. Don't use #ifdef or "#if +// defined(...)", which will not work as these macros are ALWAYS +// defined. // // GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) // is/isn't available. @@ -72,6 +100,8 @@ // Test's own tr1 tuple implementation should be // used. Unused when the user sets // GTEST_HAS_TR1_TUPLE to 0. +// GTEST_LANG_CXX11 - Define it to 1/0 to indicate that Google Test +// is building in C++11/C++98 mode. // GTEST_LINKED_AS_SHARED_LIBRARY // - Define to 1 when compiling tests that use // Google Test as a shared library (known as @@ -80,23 +110,34 @@ // - Define to 1 when compiling Google Test itself // as a shared library. -// This header defines the following utilities: +// Platform-indicating macros +// -------------------------- +// +// Macros indicating the platform on which Google Test is being used +// (a macro is defined to 1 if compiled on the given platform; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. // -// Macros indicating the current platform (defined to 1 if compiled on -// the given platform; otherwise undefined): // GTEST_OS_AIX - IBM AIX // GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_FREEBSD - FreeBSD // GTEST_OS_HPUX - HP-UX // GTEST_OS_LINUX - Linux // GTEST_OS_LINUX_ANDROID - Google Android // GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS // GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_QNX - QNX // GTEST_OS_SOLARIS - Sun Solaris // GTEST_OS_SYMBIAN - Symbian // GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) // GTEST_OS_WINDOWS_DESKTOP - Windows Desktop // GTEST_OS_WINDOWS_MINGW - MinGW // GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_WINDOWS_PHONE - Windows Phone +// GTEST_OS_WINDOWS_RT - Windows Store App/WinRT // GTEST_OS_ZOS - z/OS // // Among the platforms, Cygwin, Linux, Max OS X, and Windows have the @@ -106,22 +147,50 @@ // googletestframework@googlegroups.com (patches for fixing them are // even more welcome!). // -// Note that it is possible that none of the GTEST_OS_* macros are defined. +// It is possible that none of the GTEST_OS_* macros are defined. + +// Feature-indicating macros +// ------------------------- +// +// Macros indicating which Google Test features are available (a macro +// is defined to 1 if the corresponding feature is supported; +// otherwise UNDEFINED -- it's never defined to 0.). Google Test +// defines these macros automatically. Code outside Google Test MUST +// NOT define them. +// +// These macros are public so that portable tests can be written. +// Such tests typically surround code using a feature with an #if +// which controls that code. For example: +// +// #if GTEST_HAS_DEATH_TEST +// EXPECT_DEATH(DoSomethingDeadly()); +// #endif // -// Macros indicating available Google Test features (defined to 1 if -// the corresponding feature is supported; otherwise undefined): // GTEST_HAS_COMBINE - the Combine() function (for value-parameterized // tests) // GTEST_HAS_DEATH_TEST - death tests // GTEST_HAS_PARAM_TEST - value-parameterized tests // GTEST_HAS_TYPED_TEST - typed tests // GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_IS_THREADSAFE - Google Test is thread-safe. // GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with // GTEST_HAS_POSIX_RE (see above) which users can // define themselves. // GTEST_USES_SIMPLE_RE - our own simple regex is used; // the above two are mutually exclusive. // GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). + +// Misc public macros +// ------------------ +// +// GTEST_FLAG(flag_name) - references the variable corresponding to +// the given Google Test flag. + +// Internal utilities +// ------------------ +// +// The following macros and utilities are for Google Test's INTERNAL +// use only. Code outside Google Test MUST NOT USE THEM DIRECTLY. // // Macros for basic C++ coding: // GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. @@ -130,13 +199,18 @@ // GTEST_DISALLOW_ASSIGN_ - disables operator=. // GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. // GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is +// suppressed (constant conditional). +// GTEST_INTENTIONAL_CONST_COND_POP_ - finish code section where MSVC C4127 +// is suppressed. +// +// C++11 feature wrappers: +// +// testing::internal::move - portability wrapper for std::move. // // Synchronization: // Mutex, MutexLock, ThreadLocal, GetThreadCount() -// - synchronization primitives. -// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above -// synchronization primitives have real implementations -// and Google Test is thread-safe; or 0 otherwise. +// - synchronization primitives. // // Template meta programming: // is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only. @@ -172,10 +246,9 @@ // BiggestInt - the biggest signed integer type. // // Command-line utilities: -// GTEST_FLAG() - references a flag. // GTEST_DECLARE_*() - declares a flag. // GTEST_DEFINE_*() - defines a flag. -// GetArgvs() - returns the command line as a vector of strings. +// GetInjectableArgvs() - returns the command line as a vector of strings. // // Environment variable utilities: // GetEnv() - gets the value of an environment variable. @@ -188,15 +261,21 @@ #include #include #include -#include #ifndef _WIN32_WCE # include # include #endif // !_WIN32_WCE +#if defined __APPLE__ +# include +# include +#endif + +#include // NOLINT #include // NOLINT #include // NOLINT #include // NOLINT +#include #define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" #define GTEST_FLAG_PREFIX_ "gtest_" @@ -223,19 +302,34 @@ # define GTEST_OS_WINDOWS_MOBILE 1 # elif defined(__MINGW__) || defined(__MINGW32__) # define GTEST_OS_WINDOWS_MINGW 1 +# elif defined(WINAPI_FAMILY) +# include +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define GTEST_OS_WINDOWS_DESKTOP 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +# define GTEST_OS_WINDOWS_PHONE 1 +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +# define GTEST_OS_WINDOWS_RT 1 +# else + // WINAPI_FAMILY defined but no known partition matched. + // Default to desktop. +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif # else # define GTEST_OS_WINDOWS_DESKTOP 1 # endif // _WIN32_WCE #elif defined __APPLE__ # define GTEST_OS_MAC 1 +# if TARGET_OS_IPHONE +# define GTEST_OS_IOS 1 +# endif +#elif defined __FreeBSD__ +# define GTEST_OS_FREEBSD 1 #elif defined __linux__ # define GTEST_OS_LINUX 1 -# ifdef ANDROID +# if defined __ANDROID__ # define GTEST_OS_LINUX_ANDROID 1 -# endif // ANDROID -#elif defined __FreeBSD__ -# define GTEST_OS_LINUX 1 -# define GTEST_HAS_CLONE 0 +# endif #elif defined __MVS__ # define GTEST_OS_ZOS 1 #elif defined(__sun) && defined(__SVR4) @@ -246,30 +340,130 @@ # define GTEST_OS_HPUX 1 #elif defined __native_client__ # define GTEST_OS_NACL 1 +#elif defined __OpenBSD__ +# define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +# define GTEST_OS_QNX 1 #endif // __CYGWIN__ +// Macros for disabling Microsoft Visual C++ warnings. +// +// GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385) +// /* code that triggers warnings C4800 and C4385 */ +// GTEST_DISABLE_MSC_WARNINGS_POP_() +#if _MSC_VER >= 1500 +# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \ + __pragma(warning(push)) \ + __pragma(warning(disable: warnings)) +# define GTEST_DISABLE_MSC_WARNINGS_POP_() \ + __pragma(warning(pop)) +#else +// Older versions of MSVC don't have __pragma. +# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) +# define GTEST_DISABLE_MSC_WARNINGS_POP_() +#endif + +#ifndef GTEST_LANG_CXX11 +// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when +// -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a +// value for __cplusplus, and recent versions of clang, gcc, and +// probably other compilers set that too in C++11 mode. +# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L +// Compiling in at least C++11 mode. +# define GTEST_LANG_CXX11 1 +# else +# define GTEST_LANG_CXX11 0 +# endif +#endif + +// Distinct from C++11 language support, some environments don't provide +// proper C++11 library support. Notably, it's possible to build in +// C++11 mode when targeting Mac OS X 10.6, which has an old libstdc++ +// with no C++11 support. +// +// libstdc++ has sufficient C++11 support as of GCC 4.6.0, __GLIBCXX__ +// 20110325, but maintenance releases in the 4.4 and 4.5 series followed +// this date, so check for those versions by their date stamps. +// https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning +#if GTEST_LANG_CXX11 && \ + (!defined(__GLIBCXX__) || ( \ + __GLIBCXX__ >= 20110325ul && /* GCC >= 4.6.0 */ \ + /* Blacklist of patch releases of older branches: */ \ + __GLIBCXX__ != 20110416ul && /* GCC 4.4.6 */ \ + __GLIBCXX__ != 20120313ul && /* GCC 4.4.7 */ \ + __GLIBCXX__ != 20110428ul && /* GCC 4.5.3 */ \ + __GLIBCXX__ != 20120702ul)) /* GCC 4.5.4 */ +# define GTEST_STDLIB_CXX11 1 +#endif + +// Only use C++11 library features if the library provides them. +#if GTEST_STDLIB_CXX11 +# define GTEST_HAS_STD_BEGIN_AND_END_ 1 +# define GTEST_HAS_STD_FORWARD_LIST_ 1 +# define GTEST_HAS_STD_FUNCTION_ 1 +# define GTEST_HAS_STD_INITIALIZER_LIST_ 1 +# define GTEST_HAS_STD_MOVE_ 1 +# define GTEST_HAS_STD_UNIQUE_PTR_ 1 +#endif + +// C++11 specifies that provides std::tuple. +// Some platforms still might not have it, however. +#if GTEST_LANG_CXX11 +# define GTEST_HAS_STD_TUPLE_ 1 +# if defined(__clang__) +// Inspired by http://clang.llvm.org/docs/LanguageExtensions.html#__has_include +# if defined(__has_include) && !__has_include() +# undef GTEST_HAS_STD_TUPLE_ +# endif +# elif defined(_MSC_VER) +// Inspired by boost/config/stdlib/dinkumware.hpp +# if defined(_CPPLIB_VER) && _CPPLIB_VER < 520 +# undef GTEST_HAS_STD_TUPLE_ +# endif +# elif defined(__GLIBCXX__) +// Inspired by boost/config/stdlib/libstdcpp3.hpp, +// http://gcc.gnu.org/gcc-4.2/changes.html and +// http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch01.html#manual.intro.status.standard.200x +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2) +# undef GTEST_HAS_STD_TUPLE_ +# endif +# endif +#endif + // Brings in definitions for functions used in the testing::internal::posix // namespace (read, write, close, chdir, isatty, stat). We do not currently // use them on Windows Mobile. -#if !GTEST_OS_WINDOWS +#if GTEST_OS_WINDOWS +# if !GTEST_OS_WINDOWS_MOBILE +# include +# include +# endif +// In order to avoid having to include , use forward declaration +// assuming CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION. +// This assumption is verified by +// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION. +struct _RTL_CRITICAL_SECTION; +#else // This assumes that non-Windows OSes provide unistd.h. For OSes where this // is not the case, we need to include headers that provide the functions // mentioned above. # include -# if !GTEST_OS_NACL -// TODO(vladl@google.com): Remove this condition when Native Client SDK adds -// strings.h (tracked in -// http://code.google.com/p/nativeclient/issues/detail?id=1175). -# include // Native Client doesn't provide strings.h. -# endif -#elif !GTEST_OS_WINDOWS_MOBILE -# include -# include +# include +#endif // GTEST_OS_WINDOWS + +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +# include // NOLINT #endif // Defines this to true iff Google Test can use POSIX regular expressions. #ifndef GTEST_HAS_POSIX_RE -# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +# if GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +# else +# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +# endif #endif #if GTEST_HAS_POSIX_RE @@ -307,6 +501,15 @@ # define _HAS_EXCEPTIONS 1 # endif // _HAS_EXCEPTIONS # define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +# elif defined(__clang__) +// clang defines __EXCEPTIONS iff exceptions are enabled before clang 220714, +// but iff cleanups are enabled after that. In Obj-C++ files, there can be +// cleanups for ObjC exceptions which also need cleanups, even if C++ exceptions +// are disabled. clang has __has_feature(cxx_exceptions) which checks for C++ +// exceptions starting at clang r206352, but which checked for cleanups prior to +// that. To reliably check for C++ exception availability with clang, check for +// __EXCEPTIONS && __has_feature(cxx_exceptions). +# define GTEST_HAS_EXCEPTIONS (__EXCEPTIONS && __has_feature(cxx_exceptions)) # elif defined(__GNUC__) && __EXCEPTIONS // gcc defines __EXCEPTIONS to 1 iff exceptions are enabled. # define GTEST_HAS_EXCEPTIONS 1 @@ -384,11 +587,27 @@ # elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) # ifdef __GXX_RTTI -# define GTEST_HAS_RTTI 1 +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +# define GTEST_HAS_RTTI 0 +# else +# define GTEST_HAS_RTTI 1 +# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS # else # define GTEST_HAS_RTTI 0 # endif // __GXX_RTTI +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +# elif defined(__clang__) + +# define GTEST_HAS_RTTI __has_feature(cxx_rtti) + // Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if // both the typeid and dynamic_cast features are present. # elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) @@ -416,12 +635,13 @@ // Determines whether Google Test can use the pthreads library. #ifndef GTEST_HAS_PTHREAD -// The user didn't tell us explicitly, so we assume pthreads support is -// available on Linux and Mac. +// The user didn't tell us explicitly, so we make reasonable assumptions about +// which platforms have pthreads support. // // To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 // to your compiler flags. -# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX) +# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \ + || GTEST_OS_QNX || GTEST_OS_FREEBSD || GTEST_OS_NACL) #endif // GTEST_HAS_PTHREAD #if GTEST_HAS_PTHREAD @@ -437,8 +657,13 @@ // this macro to 0 to prevent Google Test from using tuple (any // feature depending on tuple with be disabled in this mode). #ifndef GTEST_HAS_TR1_TUPLE +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) +// STLport, provided with the Android NDK, has neither or . +# define GTEST_HAS_TR1_TUPLE 0 +# else // The user didn't tell us not to do it, so we assume it's OK. -# define GTEST_HAS_TR1_TUPLE 1 +# define GTEST_HAS_TR1_TUPLE 1 +# endif #endif // GTEST_HAS_TR1_TUPLE // Determines whether Google Test's own tr1 tuple implementation @@ -447,14 +672,28 @@ // The user didn't tell us, so we need to figure it out. // We use our own TR1 tuple if we aren't sure the user has an -// implementation of it already. At this time, GCC 4.0.0+ and MSVC -// 2010 are the only mainstream compilers that come with a TR1 tuple -// implementation. NVIDIA's CUDA NVCC compiler pretends to be GCC by -// defining __GNUC__ and friends, but cannot compile GCC's tuple -// implementation. MSVC 2008 (9.0) provides TR1 tuple in a 323 MB -// Feature Pack download, which we cannot assume the user has. -# if (defined(__GNUC__) && !defined(__CUDACC__) && !defined(_LIBCPP_VERSION) && (GTEST_GCC_VER_ >= 40000)) \ - || _MSC_VER >= 1600 +// implementation of it already. At this time, libstdc++ 4.0.0+ and +// MSVC 2010 are the only mainstream standard libraries that come +// with a TR1 tuple implementation. NVIDIA's CUDA NVCC compiler +// pretends to be GCC by defining __GNUC__ and friends, but cannot +// compile GCC's tuple implementation. MSVC 2008 (9.0) provides TR1 +// tuple in a 323 MB Feature Pack download, which we cannot assume the +// user has. QNX's QCC compiler is a modified GCC but it doesn't +// support TR1 tuple. libc++ only provides std::tuple, in C++11 mode, +// and it can be used with some compilers that define __GNUC__. +# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \ + && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600 +# define GTEST_ENV_HAS_TR1_TUPLE_ 1 +# endif + +// C++11 specifies that provides std::tuple. Use that if gtest is used +// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6 +// can build with clang but need to use gcc4.2's libstdc++). +# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325) +# define GTEST_ENV_HAS_STD_TUPLE_ 1 +# endif + +# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_ # define GTEST_USE_OWN_TR1_TUPLE 0 # else # define GTEST_USE_OWN_TR1_TUPLE 1 @@ -464,11 +703,37 @@ // To avoid conditional compilation everywhere, we make it // gtest-port.h's responsibility to #include the header implementing -// tr1/tuple. +// tuple. +#if GTEST_HAS_STD_TUPLE_ +# include // IWYU pragma: export +# define GTEST_TUPLE_NAMESPACE_ ::std +#endif // GTEST_HAS_STD_TUPLE_ + +// We include tr1::tuple even if std::tuple is available to define printers for +// them. #if GTEST_HAS_TR1_TUPLE +# ifndef GTEST_TUPLE_NAMESPACE_ +# define GTEST_TUPLE_NAMESPACE_ ::std::tr1 +# endif // GTEST_TUPLE_NAMESPACE_ # if GTEST_USE_OWN_TR1_TUPLE -# include "gtest/internal/gtest-tuple.h" +# include "gtest/internal/gtest-tuple.h" // IWYU pragma: export // NOLINT +# elif GTEST_ENV_HAS_STD_TUPLE_ +# include +// C++11 puts its tuple into the ::std namespace rather than +// ::std::tr1. gtest expects tuple to live in ::std::tr1, so put it there. +// This causes undefined behavior, but supported compilers react in +// the way we intend. +namespace std { +namespace tr1 { +using ::std::get; +using ::std::make_tuple; +using ::std::tuple; +using ::std::tuple_element; +using ::std::tuple_size; +} +} + # elif GTEST_OS_SYMBIAN // On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to @@ -483,7 +748,7 @@ // This prevents , which defines // BOOST_HAS_TR1_TUPLE, from being #included by Boost's . # define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED -# include +# include // IWYU pragma: export // NOLINT # elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) // GCC 4.0+ implements tr1/tuple in the header. This does @@ -506,7 +771,7 @@ # else // If the compiler is not GCC 4.0+, we assume the user is using a // spec-conforming TR1 implementation. -# include // NOLINT +# include // IWYU pragma: export // NOLINT # endif // GTEST_USE_OWN_TR1_TUPLE #endif // GTEST_HAS_TR1_TUPLE @@ -519,7 +784,16 @@ // The user didn't tell us, so we need to figure it out. # if GTEST_OS_LINUX && !defined(__ia64__) -# define GTEST_HAS_CLONE 1 +# if GTEST_OS_LINUX_ANDROID +// On Android, clone() is only available on ARM starting with Gingerbread. +# if defined(__arm__) && __ANDROID_API__ >= 9 +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif +# else +# define GTEST_HAS_CLONE 1 +# endif # else # define GTEST_HAS_CLONE 0 # endif // GTEST_OS_LINUX && !defined(__ia64__) @@ -531,7 +805,8 @@ #ifndef GTEST_HAS_STREAM_REDIRECTION // By default, we assume that stream redirection is supported on all // platforms except known mobile ones. -# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || \ + GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT # define GTEST_HAS_STREAM_REDIRECTION 0 # else # define GTEST_HAS_STREAM_REDIRECTION 1 @@ -542,9 +817,11 @@ // Google Test does not support death tests for VC 7.1 and earlier as // abort() in a VC 7.1 application compiled as GUI in debug config // pops up a dialog window that cannot be suppressed programmatically. -#if (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || \ (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ - GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX) + GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \ + GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD) # define GTEST_HAS_DEATH_TEST 1 # include // NOLINT #endif @@ -610,7 +887,12 @@ // compiler the variable/parameter does not have to be used. #if defined(__GNUC__) && !defined(COMPILER_ICC) # define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) -#else +#elif defined(__clang__) +# if __has_attribute(unused) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +# endif +#endif +#ifndef GTEST_ATTRIBUTE_UNUSED_ # define GTEST_ATTRIBUTE_UNUSED_ #endif @@ -636,6 +918,19 @@ # define GTEST_MUST_USE_RESULT_ #endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC +// MS C++ compiler emits warning when a conditional expression is compile time +// constant. In some contexts this warning is false positive and needs to be +// suppressed. Use the following two macros in such cases: +// +// GTEST_INTENTIONAL_CONST_COND_PUSH_() +// while (true) { +// GTEST_INTENTIONAL_CONST_COND_POP_() +// } +# define GTEST_INTENTIONAL_CONST_COND_PUSH_() \ + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4127) +# define GTEST_INTENTIONAL_CONST_COND_POP_() \ + GTEST_DISABLE_MSC_WARNINGS_POP_() + // Determine whether the compiler supports Microsoft's Structured Exception // Handling. This is supported by several Windows compilers but generally // does not exist on any other system. @@ -650,6 +945,11 @@ # define GTEST_HAS_SEH 0 # endif +#define GTEST_IS_THREADSAFE \ + (0 \ + || (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) \ + || GTEST_HAS_PTHREAD) + #endif // GTEST_HAS_SEH #ifdef _MSC_VER @@ -673,20 +973,78 @@ # define GTEST_NO_INLINE_ #endif +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) +# define GTEST_HAS_CXXABI_H_ 1 +#else +# define GTEST_HAS_CXXABI_H_ 0 +#endif + +// A function level attribute to disable checking for use of uninitialized +// memory when built with MemorySanitizer. +#if defined(__clang__) +# if __has_feature(memory_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ \ + __attribute__((no_sanitize_memory)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +# endif // __has_feature(memory_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +#endif // __clang__ + +// A function level attribute to disable AddressSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(address_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \ + __attribute__((no_sanitize_address)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +# endif // __has_feature(address_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +#endif // __clang__ + +// A function level attribute to disable ThreadSanitizer instrumentation. +#if defined(__clang__) +# if __has_feature(thread_sanitizer) +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ \ + __attribute__((no_sanitize_thread)) +# else +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +# endif // __has_feature(thread_sanitizer) +#else +# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +#endif // __clang__ + namespace testing { class Message; +#if defined(GTEST_TUPLE_NAMESPACE_) +// Import tuple and friends into the ::testing namespace. +// It is part of our interface, having them in ::testing allows us to change +// their types as needed. +using GTEST_TUPLE_NAMESPACE_::get; +using GTEST_TUPLE_NAMESPACE_::make_tuple; +using GTEST_TUPLE_NAMESPACE_::tuple; +using GTEST_TUPLE_NAMESPACE_::tuple_size; +using GTEST_TUPLE_NAMESPACE_::tuple_element; +#endif // defined(GTEST_TUPLE_NAMESPACE_) + namespace internal { -class String; +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; // The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time // expression is true. For example, you could use it to verify the // size of a static array: // -// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, -// content_type_names_incorrect_size); +// GTEST_COMPILE_ASSERT_(GTEST_ARRAY_SIZE_(names) == NUM_NAMES, +// names_incorrect_size); // // or to make sure a struct is smaller than a certain size: // @@ -696,16 +1054,22 @@ class String; // the expression is false, most compilers will issue a warning/error // containing the name of the variable. +#if GTEST_LANG_CXX11 +# define GTEST_COMPILE_ASSERT_(expr, msg) static_assert(expr, #msg) +#else // !GTEST_LANG_CXX11 template -struct CompileAssert { + struct CompileAssert { }; -#define GTEST_COMPILE_ASSERT_(expr, msg) \ - typedef ::testing::internal::CompileAssert<(bool(expr))> \ - msg[bool(expr) ? 1 : -1] +# define GTEST_COMPILE_ASSERT_(expr, msg) \ + typedef ::testing::internal::CompileAssert<(static_cast(expr))> \ + msg[static_cast(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_ +#endif // !GTEST_LANG_CXX11 // Implementation details of GTEST_COMPILE_ASSERT_: // +// (In C++11, we simply use static_assert instead of the following) +// // - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1 // elements (and thus is invalid) when the expression is false. // @@ -752,7 +1116,12 @@ template struct StaticAssertTypeEqHelper; template -struct StaticAssertTypeEqHelper {}; +struct StaticAssertTypeEqHelper { + enum { value = true }; +}; + +// Evaluates to the number of elements in 'array'. +#define GTEST_ARRAY_SIZE_(array) (sizeof(array) / sizeof(array[0])) #if GTEST_HAS_GLOBAL_STRING typedef ::string string; @@ -800,6 +1169,12 @@ class scoped_ptr { ptr_ = p; } } + + friend void swap(scoped_ptr& a, scoped_ptr& b) { + using std::swap; + swap(a.ptr_, b.ptr_); + } + private: T* ptr_; @@ -862,10 +1237,9 @@ class GTEST_API_ RE { private: void Init(const char* regex); - // We use a const char* instead of a string, as Google Test may be used - // where string is not available. We also do not use Google Test's own - // String type here, in order to simplify dependencies between the - // files. + // We use a const char* instead of an std::string, as Google Test used to be + // used where std::string is not available. TODO(wan@google.com): change to + // std::string. const char* pattern_; bool is_valid_; @@ -962,6 +1336,15 @@ inline void FlushInfoLog() { fflush(NULL); } GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ << gtest_error +#if GTEST_HAS_STD_MOVE_ +using std::move; +#else // GTEST_HAS_STD_MOVE_ +template +const T& move(const T& t) { + return t; +} +#endif // GTEST_HAS_STD_MOVE_ + // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // // Use ImplicitCast_ as a safe version of static_cast for upcasting in @@ -983,7 +1366,7 @@ inline void FlushInfoLog() { fflush(NULL); } // similar functions users may have (e.g., implicit_cast). The internal // namespace alone is not enough because the function can be found by ADL. template -inline To ImplicitCast_(To x) { return x; } +inline To ImplicitCast_(To x) { return ::testing::internal::move(x); } // When you upcast (that is, cast a pointer from type Foo to type // SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts @@ -1012,7 +1395,9 @@ inline To DownCast_(From* f) { // so we only accept pointers // for compile-time type checking, and has no overhead in an // optimized build at run-time, as it will be optimized away // completely. + GTEST_INTENTIONAL_CONST_COND_PUSH_() if (false) { + GTEST_INTENTIONAL_CONST_COND_POP_() const To to = NULL; ::testing::internal::ImplicitCast_(to); } @@ -1048,30 +1433,30 @@ Derived* CheckedDowncastToActualType(Base* base) { // GetCapturedStderr - stops capturing stderr and returns the captured string. // GTEST_API_ void CaptureStdout(); -GTEST_API_ String GetCapturedStdout(); +GTEST_API_ std::string GetCapturedStdout(); GTEST_API_ void CaptureStderr(); -GTEST_API_ String GetCapturedStderr(); +GTEST_API_ std::string GetCapturedStderr(); #endif // GTEST_HAS_STREAM_REDIRECTION #if GTEST_HAS_DEATH_TEST -// A copy of all command line arguments. Set by InitGoogleTest(). -extern ::std::vector g_argvs; +const ::std::vector& GetInjectableArgvs(); +void SetInjectableArgvs(const ::std::vector* + new_argvs); -// GTEST_HAS_DEATH_TEST implies we have ::std::string. -const ::std::vector& GetArgvs(); +// A copy of all command line arguments. Set by InitGoogleTest(). +extern ::std::vector g_argvs; #endif // GTEST_HAS_DEATH_TEST // Defines synchronization primitives. - -#if GTEST_HAS_PTHREAD - -// Sleeps for (roughly) n milli-seconds. This function is only for -// testing Google Test's own constructs. Don't use it in user tests, -// either directly or indirectly. +#if GTEST_IS_THREADSAFE +# if GTEST_HAS_PTHREAD +// Sleeps for (roughly) n milliseconds. This function is only for testing +// Google Test's own constructs. Don't use it in user tests, either +// directly or indirectly. inline void SleepMilliseconds(int n) { const timespec time = { 0, // 0 seconds. @@ -1079,7 +1464,10 @@ inline void SleepMilliseconds(int n) { }; nanosleep(&time, NULL); } +# endif // GTEST_HAS_PTHREAD +# if 0 // OS detection +# elif GTEST_HAS_PTHREAD // Allows a controller thread to pause execution of newly created // threads until notified. Instances of this class must be created // and destroyed in the controller thread. @@ -1088,26 +1476,97 @@ inline void SleepMilliseconds(int n) { // use it in user tests, either directly or indirectly. class Notification { public: - Notification() : notified_(false) {} + Notification() : notified_(false) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + } + ~Notification() { + pthread_mutex_destroy(&mutex_); + } // Notifies all threads created with this notification to start. Must // be called from the controller thread. - void Notify() { notified_ = true; } + void Notify() { + pthread_mutex_lock(&mutex_); + notified_ = true; + pthread_mutex_unlock(&mutex_); + } // Blocks until the controller thread notifies. Must be called from a test // thread. void WaitForNotification() { - while(!notified_) { + for (;;) { + pthread_mutex_lock(&mutex_); + const bool notified = notified_; + pthread_mutex_unlock(&mutex_); + if (notified) + break; SleepMilliseconds(10); } } private: - volatile bool notified_; + pthread_mutex_t mutex_; + bool notified_; GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); }; +# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +GTEST_API_ void SleepMilliseconds(int n); + +// Provides leak-safe Windows kernel handle ownership. +// Used in death tests and in threading support. +class GTEST_API_ AutoHandle { + public: + // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to + // avoid including in this header file. Including is + // undesirable because it defines a lot of symbols and macros that tend to + // conflict with client code. This assumption is verified by + // WindowsTypesTest.HANDLEIsVoidStar. + typedef void* Handle; + AutoHandle(); + explicit AutoHandle(Handle handle); + + ~AutoHandle(); + + Handle Get() const; + void Reset(); + void Reset(Handle handle); + + private: + // Returns true iff the handle is a valid handle object that can be closed. + bool IsCloseable() const; + + Handle handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class GTEST_API_ Notification { + public: + Notification(); + void Notify(); + void WaitForNotification(); + + private: + AutoHandle event_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; +# endif // OS detection + +// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD +// defined, but we don't want to use MinGW's pthreads implementation, which +// has conformance problems with some versions of the POSIX standard. +# if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW + // As a C-function, ThreadFuncWithCLinkage cannot be templated itself. // Consequently, it cannot select a correct instantiation of ThreadWithParam // in order to call its Run(). Introducing ThreadWithParamBase as a @@ -1145,10 +1604,9 @@ extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { template class ThreadWithParam : public ThreadWithParamBase { public: - typedef void (*UserThreadFunc)(T); + typedef void UserThreadFunc(T); - ThreadWithParam( - UserThreadFunc func, T param, Notification* thread_can_start) + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) : func_(func), param_(param), thread_can_start_(thread_can_start), @@ -1175,7 +1633,7 @@ class ThreadWithParam : public ThreadWithParamBase { } private: - const UserThreadFunc func_; // User-supplied thread function. + UserThreadFunc* const func_; // User-supplied thread function. const T param_; // User-supplied parameter to the thread function. // When non-NULL, used to block execution until the controller thread // notifies. @@ -1185,47 +1643,278 @@ class ThreadWithParam : public ThreadWithParamBase { GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); }; +# endif // GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW -// MutexBase and Mutex implement mutex on pthreads-based platforms. They -// are used in conjunction with class MutexLock: +# if 0 // OS detection +# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT + +// Mutex implements mutex on Windows platforms. It is used in conjunction +// with class MutexLock: // // Mutex mutex; // ... -// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end -// // of the current scope. -// -// MutexBase implements behavior for both statically and dynamically -// allocated mutexes. Do not use MutexBase directly. Instead, write -// the following to define a static mutex: +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the +// // end of the current scope. // +// A static Mutex *must* be defined or declared using one of the following +// macros: // GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); -// -// You can forward declare a static mutex like this: -// // GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); // -// To create a dynamic mutex, just define an object of type Mutex. +// (A non-static Mutex is defined/declared in the usual way). +class GTEST_API_ Mutex { + public: + enum MutexType { kStatic = 0, kDynamic = 1 }; + // We rely on kStaticMutex being 0 as it is to what the linker initializes + // type_ in static mutexes. critical_section_ will be initialized lazily + // in ThreadSafeLazyInit(). + enum StaticConstructorSelector { kStaticMutex = 0 }; + + // This constructor intentionally does nothing. It relies on type_ being + // statically initialized to 0 (effectively setting it to kStatic) and on + // ThreadSafeLazyInit() to lazily initialize the rest of the members. + explicit Mutex(StaticConstructorSelector /*dummy*/) {} + + Mutex(); + ~Mutex(); + + void Lock(); + + void Unlock(); + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld(); + + private: + // Initializes owner_thread_id_ and critical_section_ in static mutexes. + void ThreadSafeLazyInit(); + + // Per http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx, + // we assume that 0 is an invalid value for thread IDs. + unsigned int owner_thread_id_; + + // For static mutexes, we rely on these members being initialized to zeros + // by the linker. + MutexType type_; + long critical_section_init_phase_; // NOLINT + _RTL_CRITICAL_SECTION* critical_section_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex) + +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + Mutex* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Base class for ValueHolder. Allows a caller to hold and delete a value +// without knowing its type. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Provides a way for a thread to send notifications to a ThreadLocal +// regardless of its parameter type. +class ThreadLocalBase { + public: + // Creates a new ValueHolder object holding a default value passed to + // this ThreadLocal's constructor and returns it. It is the caller's + // responsibility not to call this when the ThreadLocal instance already + // has a value on the current thread. + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0; + + protected: + ThreadLocalBase() {} + virtual ~ThreadLocalBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocalBase); +}; + +// Maps a thread to a set of ThreadLocals that have values instantiated on that +// thread and notifies them when the thread exits. A ThreadLocal instance is +// expected to persist until all threads it has values on have terminated. +class GTEST_API_ ThreadLocalRegistry { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance); + + // Invoked when a ThreadLocal instance is destroyed. + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance); +}; + +class GTEST_API_ ThreadWithParamBase { + public: + void Join(); + + protected: + class Runnable { + public: + virtual ~Runnable() {} + virtual void Run() = 0; + }; + + ThreadWithParamBase(Runnable *runnable, Notification* thread_can_start); + virtual ~ThreadWithParamBase(); + + private: + AutoHandle thread_; +}; + +// Helper class for testing Google Test's multi-threading constructs. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void UserThreadFunc(T); + + ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start) + : ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) { + } + virtual ~ThreadWithParam() {} + + private: + class RunnableImpl : public Runnable { + public: + RunnableImpl(UserThreadFunc* func, T param) + : func_(func), + param_(param) { + } + virtual ~RunnableImpl() {} + virtual void Run() { + func_(param_); + } + + private: + UserThreadFunc* const func_; + const T param_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(RunnableImpl); + }; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; + +// Implements thread-local storage on Windows systems. +// +// // Thread 1 +// ThreadLocal tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// The users of a TheadLocal instance have to make sure that all but one +// threads (including the main one) using that instance have exited before +// destroying it. Otherwise, the per-thread objects managed for them by the +// ThreadLocal instance are not guaranteed to be destroyed on all platforms. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template +class ThreadLocal : public ThreadLocalBase { + public: + ThreadLocal() : default_() {} + explicit ThreadLocal(const T& value) : default_(value) {} + + ~ThreadLocal() { ThreadLocalRegistry::OnThreadLocalDestroyed(this); } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of T. Can be deleted via its base class without the caller + // knowing the type of T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + + T* GetOrCreateValue() const { + return static_cast( + ThreadLocalRegistry::GetValueOnCurrentThread(this))->pointer(); + } + + virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const { + return new ValueHolder(default_); + } + + const T default_; // The default value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# elif GTEST_HAS_PTHREAD + +// MutexBase and Mutex implement mutex on pthreads-based platforms. class MutexBase { public: // Acquires this mutex. void Lock() { GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); owner_ = pthread_self(); + has_owner_ = true; } // Releases this mutex. void Unlock() { - // We don't protect writing to owner_ here, as it's the caller's - // responsibility to ensure that the current thread holds the + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the // mutex when this is called. - owner_ = 0; + has_owner_ = false; GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); } // Does nothing if the current thread holds the mutex. Otherwise, crashes // with high probability. void AssertHeld() const { - GTEST_CHECK_(owner_ == pthread_self()) + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) << "The current thread is not holding the mutex @" << this; } @@ -1236,16 +1925,28 @@ class MutexBase { // have to be public. public: pthread_mutex_t mutex_; // The underlying pthread mutex. - pthread_t owner_; // The thread holding the mutex; 0 means no one holds it. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. }; // Forward-declares a static mutex. -# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ - extern ::testing::internal::MutexBase mutex +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex // Defines and statically (i.e. at link time) initializes a static mutex. -# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ - ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, 0 } +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false } // The Mutex class can only be used for mutexes created at runtime. It // shares its API with MutexBase otherwise. @@ -1253,7 +1954,7 @@ class Mutex : public MutexBase { public: Mutex() { GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); - owner_ = 0; + has_owner_ = false; } ~Mutex() { GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); @@ -1263,9 +1964,11 @@ class Mutex : public MutexBase { GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); }; -// We cannot name this class MutexLock as the ctor declaration would +// We cannot name this class MutexLock because the ctor declaration would // conflict with a macro named MutexLock, which is defined on some -// platforms. Hence the typedef trick below. +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. class GTestMutexLock { public: explicit GTestMutexLock(MutexBase* mutex) @@ -1299,34 +2002,6 @@ extern "C" inline void DeleteThreadLocalValue(void* value_holder) { } // Implements thread-local storage on pthreads-based systems. -// -// // Thread 1 -// ThreadLocal tl(100); // 100 is the default value for each thread. -// -// // Thread 2 -// tl.set(150); // Changes the value for thread 2 only. -// EXPECT_EQ(150, tl.get()); -// -// // Thread 1 -// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. -// tl.set(200); -// EXPECT_EQ(200, tl.get()); -// -// The template type argument T must have a public copy constructor. -// In addition, the default ThreadLocal constructor requires T to have -// a public default constructor. -// -// An object managed for a thread by a ThreadLocal instance is deleted -// when the thread exits. Or, if the ThreadLocal instance dies in -// that thread, when the ThreadLocal dies. It's the user's -// responsibility to ensure that all other threads using a ThreadLocal -// have exited when it dies, or the per-thread objects for those -// threads will not be deleted. -// -// Google Test only uses global ThreadLocal objects. That means they -// will die after main() has returned. Therefore, no per-thread -// object managed by Google Test will be leaked as long as all threads -// using Google Test have exited when main() returns. template class ThreadLocal { public: @@ -1391,9 +2066,9 @@ class ThreadLocal { GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); }; -# define GTEST_IS_THREADSAFE 1 +# endif // OS detection -#else // GTEST_HAS_PTHREAD +#else // GTEST_IS_THREADSAFE // A dummy implementation of synchronization primitives (mutex, lock, // and thread-local variable). Necessary for compiling Google Test where @@ -1403,6 +2078,8 @@ class ThreadLocal { class Mutex { public: Mutex() {} + void Lock() {} + void Unlock() {} void AssertHeld() const {} }; @@ -1411,6 +2088,11 @@ class Mutex { # define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex +// We cannot name this class MutexLock because the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. That macro is used as a defensive measure to prevent against +// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than +// "MutexLock l(&mu)". Hence the typedef trick below. class GTestMutexLock { public: explicit GTestMutexLock(Mutex*) {} // NOLINT @@ -1431,11 +2113,7 @@ class ThreadLocal { T value_; }; -// The above synchronization primitives have dummy implementations. -// Therefore Google Test is not thread-safe. -# define GTEST_IS_THREADSAFE 0 - -#endif // GTEST_HAS_PTHREAD +#endif // GTEST_IS_THREADSAFE // Returns the number of threads running in the process, or 0 to indicate that // we cannot detect it. @@ -1533,6 +2211,10 @@ inline bool IsUpper(char ch) { inline bool IsXDigit(char ch) { return isxdigit(static_cast(ch)) != 0; } +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} inline char ToLower(char ch) { return static_cast(tolower(static_cast(ch))); @@ -1541,6 +2223,13 @@ inline char ToUpper(char ch) { return static_cast(toupper(static_cast(ch))); } +inline std::string StripTrailingSpaces(std::string str) { + std::string::iterator it = str.end(); + while (it != str.begin() && IsSpace(*--it)) + it = str.erase(it); + return str; +} + // The testing::internal::posix namespace holds wrappers for common // POSIX functions. These wrappers hide the differences between // Windows/MSVC and POSIX systems. Since some compilers define these @@ -1604,11 +2293,7 @@ inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } // Functions deprecated by MSVC 8.0. -#ifdef _MSC_VER -// Temporarily disable warning 4996 (deprecated function). -# pragma warning(push) -# pragma warning(disable:4996) -#endif +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996 /* deprecated function */) inline const char* StrNCpy(char* dest, const char* src, size_t n) { return strncpy(dest, src, n); @@ -1618,7 +2303,7 @@ inline const char* StrNCpy(char* dest, const char* src, size_t n) { // StrError() aren't needed on Windows CE at this time and thus not // defined there. -#if !GTEST_OS_WINDOWS_MOBILE +#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT inline int ChDir(const char* dir) { return chdir(dir); } #endif inline FILE* FOpen(const char* path, const char* mode) { @@ -1642,8 +2327,9 @@ inline int Close(int fd) { return close(fd); } inline const char* StrError(int errnum) { return strerror(errnum); } #endif inline const char* GetEnv(const char* name) { -#if GTEST_OS_WINDOWS_MOBILE +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE | GTEST_OS_WINDOWS_RT // We are on Windows CE, which has no environment variables. + static_cast(name); // To prevent 'unused argument' warning. return NULL; #elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) // Environment variables which we programmatically clear will be set to the @@ -1655,9 +2341,7 @@ inline const char* GetEnv(const char* name) { #endif } -#ifdef _MSC_VER -# pragma warning(pop) // Restores the warning state. -#endif +GTEST_DISABLE_MSC_WARNINGS_POP_() #if GTEST_OS_WINDOWS_MOBILE // Windows CE has no C library. The abort() function is used in @@ -1670,6 +2354,23 @@ inline void Abort() { abort(); } } // namespace posix +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE +// MSVC 2005 and above support variadic macros. +# define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s and MSVC prior to 2005 doesn't +// complain about _snprintf. +# define GTEST_SNPRINTF_ _snprintf +#else +# define GTEST_SNPRINTF_ snprintf +#endif + // The maximum number a BiggestInt can represent. This definition // works no matter BiggestInt is represented in one's complement or // two's complement. @@ -1722,7 +2423,6 @@ class TypeWithSize<4> { template <> class TypeWithSize<8> { public: - #if GTEST_OS_WINDOWS typedef __int64 Int; typedef unsigned __int64 UInt; @@ -1749,7 +2449,7 @@ typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. #define GTEST_DECLARE_int32_(name) \ GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name) #define GTEST_DECLARE_string_(name) \ - GTEST_API_ extern ::testing::internal::String GTEST_FLAG(name) + GTEST_API_ extern ::std::string GTEST_FLAG(name) // Macros for defining flags. #define GTEST_DEFINE_bool_(name, default_val, doc) \ @@ -1757,7 +2457,11 @@ typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. #define GTEST_DEFINE_int32_(name, default_val, doc) \ GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) #define GTEST_DEFINE_string_(name, default_val, doc) \ - GTEST_API_ ::testing::internal::String GTEST_FLAG(name) = (default_val) + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) + +// Thread annotations +#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +#define GTEST_LOCK_EXCLUDED_(locks) // Parses 'str' for a 32-bit signed integer. If successful, writes the result // to *value and returns true; otherwise leaves *value unchanged and returns @@ -1777,3 +2481,4 @@ const char* StringFromGTestEnv(const char* flag, const char* default_val); } // namespace testing #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + diff --git a/tests/gtest/include/gtest/internal/gtest-string.h b/tests/gtest/include/gtest/internal/gtest-string.h index dc3a07be8..97f1a7fdd 100644 --- a/tests/gtest/include/gtest/internal/gtest-string.h +++ b/tests/gtest/include/gtest/internal/gtest-string.h @@ -47,50 +47,18 @@ #endif #include -#include "gtest/internal/gtest-port.h" - #include +#include "gtest/internal/gtest-port.h" + namespace testing { namespace internal { -// String - a UTF-8 string class. -// -// For historic reasons, we don't use std::string. -// -// TODO(wan@google.com): replace this class with std::string or -// implement it in terms of the latter. -// -// Note that String can represent both NULL and the empty string, -// while std::string cannot represent NULL. -// -// NULL and the empty string are considered different. NULL is less -// than anything (including the empty string) except itself. -// -// This class only provides minimum functionality necessary for -// implementing Google Test. We do not intend to implement a full-fledged -// string class here. -// -// Since the purpose of this class is to provide a substitute for -// std::string on platforms where it cannot be used, we define a copy -// constructor and assignment operators such that we don't need -// conditional compilation in a lot of places. -// -// In order to make the representation efficient, the d'tor of String -// is not virtual. Therefore DO NOT INHERIT FROM String. +// String - an abstract class holding static string utilities. class GTEST_API_ String { public: // Static utility methods - // Returns the input enclosed in double quotes if it's not NULL; - // otherwise returns "(null)". For example, "\"Hello\"" is returned - // for input "Hello". - // - // This is useful for printing a C string in the syntax of a literal. - // - // Known issue: escape sequences are not handled yet. - static String ShowCStringQuoted(const char* c_str); - // Clones a 0-terminated C string, allocating memory using new. The // caller is responsible for deleting the return value using // delete[]. Returns the cloned string, or NULL if the input is @@ -137,11 +105,7 @@ class GTEST_API_ String { // NULL will be converted to "(null)". If an error occurred during // the conversion, "(failed to convert from wide string)" is // returned. - static String ShowWideCString(const wchar_t* wide_c_str); - - // Similar to ShowWideCString(), except that this function encloses - // the converted string in double quotes. - static String ShowWideCStringQuoted(const wchar_t* wide_c_str); + static std::string ShowWideCString(const wchar_t* wide_c_str); // Compares two wide C strings. Returns true iff they have the same // content. @@ -175,174 +139,27 @@ class GTEST_API_ String { static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); - // Formats a list of arguments to a String, using the same format - // spec string as for printf. - // - // We do not use the StringPrintf class as it is not universally - // available. - // - // The result is limited to 4096 characters (including the tailing - // 0). If 4096 characters are not enough to format the input, - // "" is returned. - static String Format(const char* format, ...); + // Returns true iff the given string ends with the given suffix, ignoring + // case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix); - // C'tors + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 - // The default c'tor constructs a NULL string. - String() : c_str_(NULL), length_(0) {} + // Formats an int value as "%X". + static std::string FormatHexInt(int value); - // Constructs a String by cloning a 0-terminated C string. - String(const char* a_c_str) { // NOLINT - if (a_c_str == NULL) { - c_str_ = NULL; - length_ = 0; - } else { - ConstructNonNull(a_c_str, strlen(a_c_str)); - } - } - - // Constructs a String by copying a given number of chars from a - // buffer. E.g. String("hello", 3) creates the string "hel", - // String("a\0bcd", 4) creates "a\0bc", String(NULL, 0) creates "", - // and String(NULL, 1) results in access violation. - String(const char* buffer, size_t a_length) { - ConstructNonNull(buffer, a_length); - } - - // The copy c'tor creates a new copy of the string. The two - // String objects do not share content. - String(const String& str) : c_str_(NULL), length_(0) { *this = str; } - - // D'tor. String is intended to be a final class, so the d'tor - // doesn't need to be virtual. - ~String() { delete[] c_str_; } - - // Allows a String to be implicitly converted to an ::std::string or - // ::string, and vice versa. Converting a String containing a NULL - // pointer to ::std::string or ::string is undefined behavior. - // Converting a ::std::string or ::string containing an embedded NUL - // character to a String will result in the prefix up to the first - // NUL character. - String(const ::std::string& str) { - ConstructNonNull(str.c_str(), str.length()); - } - - operator ::std::string() const { return ::std::string(c_str(), length()); } - -#if GTEST_HAS_GLOBAL_STRING - String(const ::string& str) { - ConstructNonNull(str.c_str(), str.length()); - } - - operator ::string() const { return ::string(c_str(), length()); } -#endif // GTEST_HAS_GLOBAL_STRING - - // Returns true iff this is an empty string (i.e. ""). - bool empty() const { return (c_str() != NULL) && (length() == 0); } - - // Compares this with another String. - // Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 - // if this is greater than rhs. - int Compare(const String& rhs) const; - - // Returns true iff this String equals the given C string. A NULL - // string and a non-NULL string are considered not equal. - bool operator==(const char* a_c_str) const { return Compare(a_c_str) == 0; } - - // Returns true iff this String is less than the given String. A - // NULL string is considered less than "". - bool operator<(const String& rhs) const { return Compare(rhs) < 0; } - - // Returns true iff this String doesn't equal the given C string. A NULL - // string and a non-NULL string are considered not equal. - bool operator!=(const char* a_c_str) const { return !(*this == a_c_str); } - - // Returns true iff this String ends with the given suffix. *Any* - // String is considered to end with a NULL or empty suffix. - bool EndsWith(const char* suffix) const; - - // Returns true iff this String ends with the given suffix, not considering - // case. Any String is considered to end with a NULL or empty suffix. - bool EndsWithCaseInsensitive(const char* suffix) const; - - // Returns the length of the encapsulated string, or 0 if the - // string is NULL. - size_t length() const { return length_; } - - // Gets the 0-terminated C string this String object represents. - // The String object still owns the string. Therefore the caller - // should NOT delete the return value. - const char* c_str() const { return c_str_; } - - // Assigns a C string to this object. Self-assignment works. - const String& operator=(const char* a_c_str) { - return *this = String(a_c_str); - } - - // Assigns a String object to this object. Self-assignment works. - const String& operator=(const String& rhs) { - if (this != &rhs) { - delete[] c_str_; - if (rhs.c_str() == NULL) { - c_str_ = NULL; - length_ = 0; - } else { - ConstructNonNull(rhs.c_str(), rhs.length()); - } - } - - return *this; - } + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); private: - // Constructs a non-NULL String from the given content. This - // function can only be called when c_str_ has not been allocated. - // ConstructNonNull(NULL, 0) results in an empty string (""). - // ConstructNonNull(NULL, non_zero) is undefined behavior. - void ConstructNonNull(const char* buffer, size_t a_length) { - char* const str = new char[a_length + 1]; - memcpy(str, buffer, a_length); - str[a_length] = '\0'; - c_str_ = str; - length_ = a_length; - } - - const char* c_str_; - size_t length_; + String(); // Not meant to be instantiated. }; // class String -// Streams a String to an ostream. Each '\0' character in the String -// is replaced with "\\0". -inline ::std::ostream& operator<<(::std::ostream& os, const String& str) { - if (str.c_str() == NULL) { - os << "(null)"; - } else { - const char* const c_str = str.c_str(); - for (size_t i = 0; i != str.length(); i++) { - if (c_str[i] == '\0') { - os << "\\0"; - } else { - os << c_str[i]; - } - } - } - return os; -} - -// Gets the content of the stringstream's buffer as a String. Each '\0' +// Gets the content of the stringstream's buffer as an std::string. Each '\0' // character in the buffer is replaced with "\\0". -GTEST_API_ String StringStreamToString(::std::stringstream* stream); - -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". - -// Declared here but defined in gtest.h, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable); +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); } // namespace internal } // namespace testing diff --git a/tests/gtest/include/gtest/internal/gtest-tuple.h b/tests/gtest/include/gtest/internal/gtest-tuple.h index d1af50e18..e9b405340 100644 --- a/tests/gtest/include/gtest/internal/gtest-tuple.h +++ b/tests/gtest/include/gtest/internal/gtest-tuple.h @@ -1,4 +1,6 @@ -// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! +// This file was GENERATED by command: +// pump.py gtest-tuple.h.pump +// DO NOT EDIT BY HAND!!! // Copyright 2009 Google Inc. // All Rights Reserved. @@ -51,6 +53,14 @@ private: #endif +// Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that conflict +// with our own definitions. Therefore using our own tuple does not work on +// those compilers. +#if defined(_MSC_VER) && _MSC_VER >= 1600 /* 1600 is Visual Studio 2010 */ +# error "gtest's tuple doesn't compile on Visual Studio 2010 or later. \ +GTEST_USE_OWN_TR1_TUPLE must be set to 0 on those compilers." +#endif + // GTEST_n_TUPLE_(T) is the type of an n-tuple. #define GTEST_0_TUPLE_(T) tuple<> #define GTEST_1_TUPLE_(T) tuple struct TupleElement; template -struct TupleElement { typedef T0 type; }; +struct TupleElement { + typedef T0 type; +}; template -struct TupleElement { typedef T1 type; }; +struct TupleElement { + typedef T1 type; +}; template -struct TupleElement { typedef T2 type; }; +struct TupleElement { + typedef T2 type; +}; template -struct TupleElement { typedef T3 type; }; +struct TupleElement { + typedef T3 type; +}; template -struct TupleElement { typedef T4 type; }; +struct TupleElement { + typedef T4 type; +}; template -struct TupleElement { typedef T5 type; }; +struct TupleElement { + typedef T5 type; +}; template -struct TupleElement { typedef T6 type; }; +struct TupleElement { + typedef T6 type; +}; template -struct TupleElement { typedef T7 type; }; +struct TupleElement { + typedef T7 type; +}; template -struct TupleElement { typedef T8 type; }; +struct TupleElement { + typedef T8 type; +}; template -struct TupleElement { typedef T9 type; }; +struct TupleElement { + typedef T9 type; +}; } // namespace gtest_internal @@ -708,37 +738,59 @@ inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, template struct tuple_size; template -struct tuple_size { static const int value = 0; }; +struct tuple_size { + static const int value = 0; +}; template -struct tuple_size { static const int value = 1; }; +struct tuple_size { + static const int value = 1; +}; template -struct tuple_size { static const int value = 2; }; +struct tuple_size { + static const int value = 2; +}; template -struct tuple_size { static const int value = 3; }; +struct tuple_size { + static const int value = 3; +}; template -struct tuple_size { static const int value = 4; }; +struct tuple_size { + static const int value = 4; +}; template -struct tuple_size { static const int value = 5; }; +struct tuple_size { + static const int value = 5; +}; template -struct tuple_size { static const int value = 6; }; +struct tuple_size { + static const int value = 6; +}; template -struct tuple_size { static const int value = 7; }; +struct tuple_size { + static const int value = 7; +}; template -struct tuple_size { static const int value = 8; }; +struct tuple_size { + static const int value = 8; +}; template -struct tuple_size { static const int value = 9; }; +struct tuple_size { + static const int value = 9; +}; template -struct tuple_size { static const int value = 10; }; +struct tuple_size { + static const int value = 10; +}; template struct tuple_element { @@ -922,8 +974,8 @@ template inline bool operator==(const GTEST_10_TUPLE_(T)& t, const GTEST_10_TUPLE_(U)& u) { return gtest_internal::SameSizeTuplePrefixComparator< - tuple_size::value, - tuple_size::value>::Eq(t, u); + tuple_size::value, + tuple_size::value>::Eq(t, u); } template diff --git a/tests/gtest/include/gtest/internal/gtest-tuple.h.pump b/tests/gtest/include/gtest/internal/gtest-tuple.h.pump index ef519094a..429ddfeec 100644 --- a/tests/gtest/include/gtest/internal/gtest-tuple.h.pump +++ b/tests/gtest/include/gtest/internal/gtest-tuple.h.pump @@ -52,6 +52,14 @@ $$ This meta comment fixes auto-indentation in Emacs. }} private: #endif +// Visual Studio 2010, 2012, and 2013 define symbols in std::tr1 that conflict +// with our own definitions. Therefore using our own tuple does not work on +// those compilers. +#if defined(_MSC_VER) && _MSC_VER >= 1600 /* 1600 is Visual Studio 2010 */ +# error "gtest's tuple doesn't compile on Visual Studio 2010 or later. \ +GTEST_USE_OWN_TR1_TUPLE must be set to 0 on those compilers." +#endif + $range i 0..n-1 $range j 0..n @@ -118,8 +126,9 @@ struct TupleElement; $for i [[ template -struct TupleElement [[]] -{ typedef T$i type; }; +struct TupleElement { + typedef T$i type; +}; ]] @@ -220,7 +229,9 @@ template struct tuple_size; $for j [[ template -struct tuple_size { static const int value = $j; }; +struct tuple_size { + static const int value = $j; +}; ]] @@ -302,8 +313,8 @@ template inline bool operator==(const GTEST_$(n)_TUPLE_(T)& t, const GTEST_$(n)_TUPLE_(U)& u) { return gtest_internal::SameSizeTuplePrefixComparator< - tuple_size::value, - tuple_size::value>::Eq(t, u); + tuple_size::value, + tuple_size::value>::Eq(t, u); } template diff --git a/tests/gtest/include/gtest/internal/gtest-type-util.h b/tests/gtest/include/gtest/internal/gtest-type-util.h index b7b01b094..e46f7cfcb 100644 --- a/tests/gtest/include/gtest/internal/gtest-type-util.h +++ b/tests/gtest/include/gtest/internal/gtest-type-util.h @@ -45,15 +45,14 @@ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ #include "gtest/internal/gtest-port.h" -#include "gtest/internal/gtest-string.h" // #ifdef __GNUC__ is too general here. It is possible to use gcc without using // libstdc++ (which is where cxxabi.h comes from). -# ifdef __GLIBCXX__ +# if GTEST_HAS_CXXABI_H_ # include # elif defined(__HP_aCC) # include -# endif // __GLIBCXX__ +# endif // GTEST_HASH_CXXABI_H_ namespace testing { namespace internal { @@ -62,24 +61,24 @@ namespace internal { // NB: This function is also used in Google Mock, so don't move it inside of // the typed-test-only section below. template -String GetTypeName() { +std::string GetTypeName() { # if GTEST_HAS_RTTI const char* const name = typeid(T).name(); -# if defined(__GLIBCXX__) || defined(__HP_aCC) +# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) int status = 0; // gcc's implementation of typeid(T).name() mangles the type name, // so we have to demangle it. -# ifdef __GLIBCXX__ +# if GTEST_HAS_CXXABI_H_ using abi::__cxa_demangle; -# endif // __GLIBCXX__ +# endif // GTEST_HAS_CXXABI_H_ char* const readable_name = __cxa_demangle(name, 0, 0, &status); - const String name_str(status == 0 ? readable_name : name); + const std::string name_str(status == 0 ? readable_name : name); free(readable_name); return name_str; # else return name; -# endif // __GLIBCXX__ || __HP_aCC +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC # else @@ -3300,7 +3299,9 @@ struct Templates -struct TypeList { typedef Types1 type; }; +struct TypeList { + typedef Types1 type; +}; template # elif defined(__HP_aCC) # include -# endif // __GLIBCXX__ +# endif // GTEST_HASH_CXXABI_H_ namespace testing { namespace internal { @@ -60,24 +59,24 @@ namespace internal { // NB: This function is also used in Google Mock, so don't move it inside of // the typed-test-only section below. template -String GetTypeName() { +std::string GetTypeName() { # if GTEST_HAS_RTTI const char* const name = typeid(T).name(); -# if defined(__GLIBCXX__) || defined(__HP_aCC) +# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) int status = 0; // gcc's implementation of typeid(T).name() mangles the type name, // so we have to demangle it. -# ifdef __GLIBCXX__ +# if GTEST_HAS_CXXABI_H_ using abi::__cxa_demangle; -# endif // __GLIBCXX__ +# endif // GTEST_HAS_CXXABI_H_ char* const readable_name = __cxa_demangle(name, 0, 0, &status); - const String name_str(status == 0 ? readable_name : name); + const std::string name_str(status == 0 ? readable_name : name); free(readable_name); return name_str; # else return name; -# endif // __GLIBCXX__ || __HP_aCC +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC # else @@ -279,7 +278,9 @@ struct Templates<$for j, [[T$j]]$for k[[, NoneT]]> { // INSTANTIATE_TYPED_TEST_CASE_P(). template -struct TypeList { typedef Types1 type; }; +struct TypeList { + typedef Types1 type; +}; $range i 1..n diff --git a/tests/gtest/src/gtest-death-test.cc b/tests/gtest/src/gtest-death-test.cc index 8b2e4131c..a0a8c7baf 100644 --- a/tests/gtest/src/gtest-death-test.cc +++ b/tests/gtest/src/gtest-death-test.cc @@ -43,6 +43,11 @@ # include # include # include + +# if GTEST_OS_LINUX +# include +# endif // GTEST_OS_LINUX + # include # if GTEST_OS_WINDOWS @@ -52,6 +57,10 @@ # include # endif // GTEST_OS_WINDOWS +# if GTEST_OS_QNX +# include +# endif // GTEST_OS_QNX + #endif // GTEST_HAS_DEATH_TEST #include "gtest/gtest-message.h" @@ -59,9 +68,9 @@ // Indicates that this translation unit is part of Google Test's // implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. +// included, or there will be a compiler error. This trick exists to +// prevent the accidental inclusion of gtest-internal-inl.h in the +// user's code. #define GTEST_IMPLEMENTATION_ 1 #include "src/gtest-internal-inl.h" #undef GTEST_IMPLEMENTATION_ @@ -100,13 +109,42 @@ GTEST_DEFINE_string_( "Indicates the file, line number, temporal index of " "the single death test to run, and a file descriptor to " "which a success code may be sent, all separated by " - "colons. This flag is specified if and only if the current " + "the '|' characters. This flag is specified if and only if the current " "process is a sub-process launched for running a thread-safe " "death test. FOR INTERNAL USE ONLY."); } // namespace internal #if GTEST_HAS_DEATH_TEST +namespace internal { + +// Valid only for fast death tests. Indicates the code is running in the +// child process of a fast style death test. +static bool g_in_fast_death_test_child = false; + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +bool InDeathTestChild() { +# if GTEST_OS_WINDOWS + + // On Windows, death tests are thread-safe regardless of the value of the + // death_test_style flag. + return !GTEST_FLAG(internal_run_death_test).empty(); + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") + return !GTEST_FLAG(internal_run_death_test).empty(); + else + return g_in_fast_death_test_child; +#endif +} + +} // namespace internal + // ExitedWithCode constructor. ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { } @@ -141,7 +179,7 @@ namespace internal { // Generates a textual description of a given exit code, in the format // specified by wait(2). -static String ExitSummary(int exit_code) { +static std::string ExitSummary(int exit_code) { Message m; # if GTEST_OS_WINDOWS @@ -176,7 +214,7 @@ bool ExitedUnsuccessfully(int exit_status) { // one thread running, or cannot determine the number of threads, prior // to executing the given statement. It is the responsibility of the // caller not to pass a thread_count of 1. -static String DeathTestThreadWarning(size_t thread_count) { +static std::string DeathTestThreadWarning(size_t thread_count) { Message msg; msg << "Death tests use fork(), which is unsafe particularly" << " in a threaded context. For this test, " << GTEST_NAME_ << " "; @@ -210,7 +248,7 @@ enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; // message is propagated back to the parent process. Otherwise, the // message is simply printed to stderr. In either case, the program // then exits with status 1. -void DeathTestAbort(const String& message) { +void DeathTestAbort(const std::string& message) { // On a POSIX system, this function may be called from a threadsafe-style // death test child process, which operates on a very small stack. Use // the heap for any additional non-minuscule memory requirements. @@ -234,9 +272,10 @@ void DeathTestAbort(const String& message) { # define GTEST_DEATH_TEST_CHECK_(expression) \ do { \ if (!::testing::internal::IsTrue(expression)) { \ - DeathTestAbort(::testing::internal::String::Format( \ - "CHECK failed: File %s, line %d: %s", \ - __FILE__, __LINE__, #expression)); \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression); \ } \ } while (::testing::internal::AlwaysFalse()) @@ -254,15 +293,16 @@ void DeathTestAbort(const String& message) { gtest_retval = (expression); \ } while (gtest_retval == -1 && errno == EINTR); \ if (gtest_retval == -1) { \ - DeathTestAbort(::testing::internal::String::Format( \ - "CHECK failed: File %s, line %d: %s != -1", \ - __FILE__, __LINE__, #expression)); \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression + " != -1"); \ } \ } while (::testing::internal::AlwaysFalse()) // Returns the message describing the last system error in errno. -String GetLastErrnoDescription() { - return String(errno == 0 ? "" : posix::StrError(errno)); +std::string GetLastErrnoDescription() { + return errno == 0 ? "" : posix::StrError(errno); } // This is called from a death test parent process to read a failure @@ -312,11 +352,11 @@ const char* DeathTest::LastMessage() { return last_death_test_message_.c_str(); } -void DeathTest::set_last_death_test_message(const String& message) { +void DeathTest::set_last_death_test_message(const std::string& message) { last_death_test_message_ = message; } -String DeathTest::last_death_test_message_; +std::string DeathTest::last_death_test_message_; // Provides cross platform implementation for some death functionality. class DeathTestImpl : public DeathTest { @@ -491,7 +531,7 @@ bool DeathTestImpl::Passed(bool status_ok) { if (!spawned()) return false; - const String error_message = GetCapturedStderr(); + const std::string error_message = GetCapturedStderr(); bool success = false; Message buffer; @@ -673,22 +713,19 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() { FALSE, // The initial state is non-signalled. NULL)); // The even is unnamed. GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); - const String filter_flag = String::Format("--%s%s=%s.%s", - GTEST_FLAG_PREFIX_, kFilterFlag, - info->test_case_name(), - info->name()); - const String internal_flag = String::Format( - "--%s%s=%s|%d|%d|%u|%Iu|%Iu", - GTEST_FLAG_PREFIX_, - kInternalRunDeathTestFlag, - file_, line_, - death_test_index, - static_cast(::GetCurrentProcessId()), - // size_t has the same with as pointers on both 32-bit and 64-bit + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + + "=" + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(static_cast(::GetCurrentProcessId())) + + // size_t has the same width as pointers on both 32-bit and 64-bit // Windows platforms. // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. - reinterpret_cast(write_handle), - reinterpret_cast(event_handle_.Get())); + "|" + StreamableToString(reinterpret_cast(write_handle)) + + "|" + StreamableToString(reinterpret_cast(event_handle_.Get())); char executable_path[_MAX_PATH + 1]; // NOLINT GTEST_DEATH_TEST_CHECK_( @@ -696,10 +733,9 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() { executable_path, _MAX_PATH)); - String command_line = String::Format("%s %s \"%s\"", - ::GetCommandLineA(), - filter_flag.c_str(), - internal_flag.c_str()); + std::string command_line = + std::string(::GetCommandLineA()) + " " + filter_flag + " \"" + + internal_flag + "\""; DeathTest::set_last_death_test_message(""); @@ -816,6 +852,7 @@ DeathTest::TestRole NoExecDeathTest::AssumeRole() { // Event forwarding to the listeners of event listener API mush be shut // down in death test subprocesses. GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + g_in_fast_death_test_child = true; return EXECUTE_TEST; } else { GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); @@ -835,6 +872,11 @@ class ExecDeathTest : public ForkingDeathTest { ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } virtual TestRole AssumeRole(); private: + static ::std::vector + GetArgvsForDeathTestChildProcess() { + ::std::vector args = GetInjectableArgvs(); + return args; + } // The name of the file in which the death test is located. const char* const file_; // The line number on which the death test is located. @@ -869,6 +911,7 @@ class Arguments { char* const* Argv() { return &args_[0]; } + private: std::vector args_; }; @@ -894,6 +937,7 @@ extern "C" char** environ; inline char** GetEnviron() { return environ; } # endif // GTEST_OS_MAC +# if !GTEST_OS_QNX // The main function for a threadsafe-style death test child process. // This function is called in a clone()-ed process and thus must avoid // any potentially unsafe operations like malloc or libc functions. @@ -908,9 +952,8 @@ static int ExecDeathTestChildMain(void* child_arg) { UnitTest::GetInstance()->original_working_dir(); // We can safely call chdir() as it's a direct system call. if (chdir(original_dir) != 0) { - DeathTestAbort(String::Format("chdir(\"%s\") failed: %s", - original_dir, - GetLastErrnoDescription().c_str())); + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); return EXIT_FAILURE; } @@ -920,12 +963,12 @@ static int ExecDeathTestChildMain(void* child_arg) { // invoke the test program via a valid path that contains at least // one path separator. execve(args->argv[0], args->argv, GetEnviron()); - DeathTestAbort(String::Format("execve(%s, ...) in %s failed: %s", - args->argv[0], - original_dir, - GetLastErrnoDescription().c_str())); + DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " + + original_dir + " failed: " + + GetLastErrnoDescription()); return EXIT_FAILURE; } +# endif // !GTEST_OS_QNX // Two utility routines that together determine the direction the stack // grows. @@ -936,25 +979,77 @@ static int ExecDeathTestChildMain(void* child_arg) { // GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining // StackLowerThanAddress into StackGrowsDown, which then doesn't give // correct answer. -bool StackLowerThanAddress(const void* ptr) GTEST_NO_INLINE_; -bool StackLowerThanAddress(const void* ptr) { +void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_; +void StackLowerThanAddress(const void* ptr, bool* result) { int dummy; - return &dummy < ptr; + *result = (&dummy < ptr); } +// Make sure AddressSanitizer does not tamper with the stack here. +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ bool StackGrowsDown() { int dummy; - return StackLowerThanAddress(&dummy); + bool result; + StackLowerThanAddress(&dummy, &result); + return result; } -// A threadsafe implementation of fork(2) for threadsafe-style death tests -// that uses clone(2). It dies with an error message if anything goes -// wrong. -static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { +// Spawns a child process with the same executable as the current process in +// a thread-safe manner and instructs it to run the death test. The +// implementation uses fork(2) + exec. On systems where clone(2) is +// available, it is used instead, being slightly more thread-safe. On QNX, +// fork supports only single-threaded environments, so this function uses +// spawn(2) there instead. The function dies with an error message if +// anything goes wrong. +static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { ExecDeathTestArgs args = { argv, close_fd }; pid_t child_pid = -1; -# if GTEST_HAS_CLONE +# if GTEST_OS_QNX + // Obtains the current directory and sets it to be closed in the child + // process. + const int cwd_fd = open(".", O_RDONLY); + GTEST_DEATH_TEST_CHECK_(cwd_fd != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC)); + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + int fd_flags; + // Set close_fd to be closed after spawn. + GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD)); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD, + fd_flags | FD_CLOEXEC)); + struct inheritance inherit = {0}; + // spawn is a system call. + child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron()); + // Restores the current working directory. + GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); + +# else // GTEST_OS_QNX +# if GTEST_OS_LINUX + // When a SIGPROF signal is received while fork() or clone() are executing, + // the process may hang. To avoid this, we ignore SIGPROF here and re-enable + // it after the call to fork()/clone() is complete. + struct sigaction saved_sigprof_action; + struct sigaction ignore_sigprof_action; + memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action)); + sigemptyset(&ignore_sigprof_action.sa_mask); + ignore_sigprof_action.sa_handler = SIG_IGN; + GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction( + SIGPROF, &ignore_sigprof_action, &saved_sigprof_action)); +# endif // GTEST_OS_LINUX + +# if GTEST_HAS_CLONE const bool use_fork = GTEST_FLAG(death_test_use_fork); if (!use_fork) { @@ -964,21 +1059,37 @@ static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + + // Maximum stack alignment in bytes: For a downward-growing stack, this + // amount is subtracted from size of the stack space to get an address + // that is within the stack space and is aligned on all systems we care + // about. As far as I know there is no ABI with stack alignment greater + // than 64. We assume stack and stack_size already have alignment of + // kMaxStackAlignment. + const size_t kMaxStackAlignment = 64; void* const stack_top = - static_cast(stack) + (stack_grows_down ? stack_size : 0); + static_cast(stack) + + (stack_grows_down ? stack_size - kMaxStackAlignment : 0); + GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment && + reinterpret_cast(stack_top) % kMaxStackAlignment == 0); child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); } -# else +# else const bool use_fork = true; -# endif // GTEST_HAS_CLONE +# endif // GTEST_HAS_CLONE if (use_fork && (child_pid = fork()) == 0) { ExecDeathTestChildMain(&args); _exit(0); } +# endif // GTEST_OS_QNX +# if GTEST_OS_LINUX + GTEST_DEATH_TEST_CHECK_SYSCALL_( + sigaction(SIGPROF, &saved_sigprof_action, NULL)); +# endif // GTEST_OS_LINUX GTEST_DEATH_TEST_CHECK_(child_pid != -1); return child_pid; @@ -1006,16 +1117,16 @@ DeathTest::TestRole ExecDeathTest::AssumeRole() { // it be closed when the child process does an exec: GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); - const String filter_flag = - String::Format("--%s%s=%s.%s", - GTEST_FLAG_PREFIX_, kFilterFlag, - info->test_case_name(), info->name()); - const String internal_flag = - String::Format("--%s%s=%s|%d|%d|%d", - GTEST_FLAG_PREFIX_, kInternalRunDeathTestFlag, - file_, line_, death_test_index, pipe_fd[1]); + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(pipe_fd[1]); Arguments args; - args.AddArguments(GetArgvs()); + args.AddArguments(GetArgvsForDeathTestChildProcess()); args.AddArgument(filter_flag.c_str()); args.AddArgument(internal_flag.c_str()); @@ -1026,7 +1137,7 @@ DeathTest::TestRole ExecDeathTest::AssumeRole() { // is necessary. FlushInfoLog(); - const pid_t child_pid = ExecDeathTestFork(args.Argv(), pipe_fd[0]); + const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); set_child_pid(child_pid); set_read_fd(pipe_fd[0]); @@ -1052,9 +1163,10 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, if (flag != NULL) { if (death_test_index > flag->index()) { - DeathTest::set_last_death_test_message(String::Format( - "Death test count (%d) somehow exceeded expected maximum (%d)", - death_test_index, flag->index())); + DeathTest::set_last_death_test_message( + "Death test count (" + StreamableToString(death_test_index) + + ") somehow exceeded expected maximum (" + + StreamableToString(flag->index()) + ")"); return false; } @@ -1083,9 +1195,9 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, # endif // GTEST_OS_WINDOWS else { // NOLINT - this is more readable than unbalanced brackets inside #if. - DeathTest::set_last_death_test_message(String::Format( - "Unknown death test style \"%s\" encountered", - GTEST_FLAG(death_test_style).c_str())); + DeathTest::set_last_death_test_message( + "Unknown death test style \"" + GTEST_FLAG(death_test_style) + + "\" encountered"); return false; } @@ -1123,8 +1235,8 @@ int GetStatusFileDescriptor(unsigned int parent_process_id, FALSE, // Non-inheritable. parent_process_id)); if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { - DeathTestAbort(String::Format("Unable to open parent process %u", - parent_process_id)); + DeathTestAbort("Unable to open parent process " + + StreamableToString(parent_process_id)); } // TODO(vladl@google.com): Replace the following check with a @@ -1144,9 +1256,10 @@ int GetStatusFileDescriptor(unsigned int parent_process_id, // DUPLICATE_SAME_ACCESS is used. FALSE, // Request non-inheritable handler. DUPLICATE_SAME_ACCESS)) { - DeathTestAbort(String::Format( - "Unable to duplicate the pipe handle %Iu from the parent process %u", - write_handle_as_size_t, parent_process_id)); + DeathTestAbort("Unable to duplicate the pipe handle " + + StreamableToString(write_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); } const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); @@ -1157,17 +1270,18 @@ int GetStatusFileDescriptor(unsigned int parent_process_id, 0x0, FALSE, DUPLICATE_SAME_ACCESS)) { - DeathTestAbort(String::Format( - "Unable to duplicate the event handle %Iu from the parent process %u", - event_handle_as_size_t, parent_process_id)); + DeathTestAbort("Unable to duplicate the event handle " + + StreamableToString(event_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); } const int write_fd = ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); if (write_fd == -1) { - DeathTestAbort(String::Format( - "Unable to convert pipe handle %Iu to a file descriptor", - write_handle_as_size_t)); + DeathTestAbort("Unable to convert pipe handle " + + StreamableToString(write_handle_as_size_t) + + " to a file descriptor"); } // Signals the parent that the write end of the pipe has been acquired @@ -1204,9 +1318,8 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { || !ParseNaturalNumber(fields[3], &parent_process_id) || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { - DeathTestAbort(String::Format( - "Bad --gtest_internal_run_death_test flag: %s", - GTEST_FLAG(internal_run_death_test).c_str())); + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); } write_fd = GetStatusFileDescriptor(parent_process_id, write_handle_as_size_t, @@ -1217,9 +1330,8 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { || !ParseNaturalNumber(fields[1], &line) || !ParseNaturalNumber(fields[2], &index) || !ParseNaturalNumber(fields[3], &write_fd)) { - DeathTestAbort(String::Format( - "Bad --gtest_internal_run_death_test flag: %s", - GTEST_FLAG(internal_run_death_test).c_str())); + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); } # endif // GTEST_OS_WINDOWS diff --git a/tests/gtest/src/gtest-filepath.cc b/tests/gtest/src/gtest-filepath.cc index 91b257138..0292dc119 100644 --- a/tests/gtest/src/gtest-filepath.cc +++ b/tests/gtest/src/gtest-filepath.cc @@ -29,6 +29,7 @@ // // Authors: keith.ray@gmail.com (Keith Ray) +#include "gtest/gtest-message.h" #include "gtest/internal/gtest-filepath.h" #include "gtest/internal/gtest-port.h" @@ -39,8 +40,8 @@ #elif GTEST_OS_WINDOWS # include # include -#elif GTEST_OS_SYMBIAN || GTEST_OS_NACL -// Symbian OpenC and NaCl have PATH_MAX in sys/syslimits.h +#elif GTEST_OS_SYMBIAN +// Symbian OpenC has PATH_MAX in sys/syslimits.h # include #else # include @@ -69,7 +70,6 @@ namespace internal { // of them. const char kPathSeparator = '\\'; const char kAlternatePathSeparator = '/'; -const char kPathSeparatorString[] = "\\"; const char kAlternatePathSeparatorString[] = "/"; # if GTEST_OS_WINDOWS_MOBILE // Windows CE doesn't have a current directory. You should not use @@ -83,7 +83,6 @@ const char kCurrentDirectoryString[] = ".\\"; # endif // GTEST_OS_WINDOWS_MOBILE #else const char kPathSeparator = '/'; -const char kPathSeparatorString[] = "/"; const char kCurrentDirectoryString[] = "./"; #endif // GTEST_OS_WINDOWS @@ -98,7 +97,7 @@ static bool IsPathSeparator(char c) { // Returns the current working directory, or "" if unsuccessful. FilePath FilePath::GetCurrentDir() { -#if GTEST_OS_WINDOWS_MOBILE +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT // Windows CE doesn't have a current directory, so we just return // something reasonable. return FilePath(kCurrentDirectoryString); @@ -107,7 +106,14 @@ FilePath FilePath::GetCurrentDir() { return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); #else char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; - return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); + char* result = getcwd(cwd, sizeof(cwd)); +# if GTEST_OS_NACL + // getcwd will likely fail in NaCl due to the sandbox, so return something + // reasonable. The user may have provided a shim implementation for getcwd, + // however, so fallback only when failure is detected. + return FilePath(result == NULL ? kCurrentDirectoryString : cwd); +# endif // GTEST_OS_NACL + return FilePath(result == NULL ? "" : cwd); #endif // GTEST_OS_WINDOWS_MOBILE } @@ -116,9 +122,10 @@ FilePath FilePath::GetCurrentDir() { // FilePath("dir/file"). If a case-insensitive extension is not // found, returns a copy of the original FilePath. FilePath FilePath::RemoveExtension(const char* extension) const { - String dot_extension(String::Format(".%s", extension)); - if (pathname_.EndsWithCaseInsensitive(dot_extension.c_str())) { - return FilePath(String(pathname_.c_str(), pathname_.length() - 4)); + const std::string dot_extension = std::string(".") + extension; + if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { + return FilePath(pathname_.substr( + 0, pathname_.length() - dot_extension.length())); } return *this; } @@ -147,7 +154,7 @@ const char* FilePath::FindLastPathSeparator() const { // On Windows platform, '\' is the path separator, otherwise it is '/'. FilePath FilePath::RemoveDirectoryName() const { const char* const last_sep = FindLastPathSeparator(); - return last_sep ? FilePath(String(last_sep + 1)) : *this; + return last_sep ? FilePath(last_sep + 1) : *this; } // RemoveFileName returns the directory path with the filename removed. @@ -158,9 +165,9 @@ FilePath FilePath::RemoveDirectoryName() const { // On Windows platform, '\' is the path separator, otherwise it is '/'. FilePath FilePath::RemoveFileName() const { const char* const last_sep = FindLastPathSeparator(); - String dir; + std::string dir; if (last_sep) { - dir = String(c_str(), last_sep + 1 - c_str()); + dir = std::string(c_str(), last_sep + 1 - c_str()); } else { dir = kCurrentDirectoryString; } @@ -177,11 +184,12 @@ FilePath FilePath::MakeFileName(const FilePath& directory, const FilePath& base_name, int number, const char* extension) { - String file; + std::string file; if (number == 0) { - file = String::Format("%s.%s", base_name.c_str(), extension); + file = base_name.string() + "." + extension; } else { - file = String::Format("%s_%d.%s", base_name.c_str(), number, extension); + file = base_name.string() + "_" + StreamableToString(number) + + "." + extension; } return ConcatPaths(directory, FilePath(file)); } @@ -193,8 +201,7 @@ FilePath FilePath::ConcatPaths(const FilePath& directory, if (directory.IsEmpty()) return relative_path; const FilePath dir(directory.RemoveTrailingPathSeparator()); - return FilePath(String::Format("%s%c%s", dir.c_str(), kPathSeparator, - relative_path.c_str())); + return FilePath(dir.string() + kPathSeparator + relative_path.string()); } // Returns true if pathname describes something findable in the file-system, @@ -338,7 +345,7 @@ bool FilePath::CreateFolder() const { // On Windows platform, uses \ as the separator, other platforms use /. FilePath FilePath::RemoveTrailingPathSeparator() const { return IsDirectory() - ? FilePath(String(pathname_.c_str(), pathname_.length() - 1)) + ? FilePath(pathname_.substr(0, pathname_.length() - 1)) : *this; } diff --git a/tests/gtest/src/gtest-internal-inl.h b/tests/gtest/src/gtest-internal-inl.h index 65a2101a4..0ac7a109b 100644 --- a/tests/gtest/src/gtest-internal-inl.h +++ b/tests/gtest/src/gtest-internal-inl.h @@ -40,7 +40,7 @@ // GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is // part of Google Test's implementation; otherwise it's undefined. #if !GTEST_IMPLEMENTATION_ -// A user is trying to include this from his code - just say no. +// If this file is included from the user's code, just say no. # error "gtest-internal-inl.h is part of Google Test's internal implementation." # error "It must not be included except by Google Test itself." #endif // GTEST_IMPLEMENTATION_ @@ -58,6 +58,11 @@ #include "gtest/internal/gtest-port.h" +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + #if GTEST_OS_WINDOWS # include // NOLINT #endif // GTEST_OS_WINDOWS @@ -112,6 +117,12 @@ GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); // Formats the given time in milliseconds as seconds. GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); +// Converts the given time in milliseconds to a date string in the ISO 8601 +// format, without the timezone information. N.B.: due to the use the +// non-reentrant localtime() function, this function is not thread safe. Do +// not use it in any code that can be called from multiple threads. +GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms); + // Parses a string for an Int32 flag, in the form of "--flag=value". // // On success, stores the value of the flag in *value, and returns @@ -190,37 +201,35 @@ class GTestFlagSaver { GTEST_FLAG(stream_result_to) = stream_result_to_; GTEST_FLAG(throw_on_failure) = throw_on_failure_; } + private: // Fields for saving the original values of flags. bool also_run_disabled_tests_; bool break_on_failure_; bool catch_exceptions_; - String color_; - String death_test_style_; + std::string color_; + std::string death_test_style_; bool death_test_use_fork_; - String filter_; - String internal_run_death_test_; + std::string filter_; + std::string internal_run_death_test_; bool list_tests_; - String output_; + std::string output_; bool print_time_; - bool pretty_; internal::Int32 random_seed_; internal::Int32 repeat_; bool shuffle_; internal::Int32 stack_trace_depth_; - String stream_result_to_; + std::string stream_result_to_; bool throw_on_failure_; } GTEST_ATTRIBUTE_UNUSED_; // Converts a Unicode code point to a narrow string in UTF-8 encoding. // code_point parameter is of type UInt32 because wchar_t may not be // wide enough to contain a code point. -// The output buffer str must containt at least 32 characters. -// The function returns the address of the output buffer. // If the code_point is not a valid Unicode code point -// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. -GTEST_API_ char* CodePointToUtf8(UInt32 code_point, char* str); +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +GTEST_API_ std::string CodePointToUtf8(UInt32 code_point); // Converts a wide string to a narrow string in UTF-8 encoding. // The wide string is assumed to have the following encoding: @@ -235,7 +244,7 @@ GTEST_API_ char* CodePointToUtf8(UInt32 code_point, char* str); // as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding // and contains invalid UTF-16 surrogate pairs, values in those pairs // will be encoded as individual Unicode characters from Basic Normal Plane. -GTEST_API_ String WideStringToUtf8(const wchar_t* str, int num_chars); +GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars); // Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file // if the variable is present. If a file already exists at this location, this @@ -339,16 +348,15 @@ class TestPropertyKeyIs { // Constructor. // // TestPropertyKeyIs has NO default constructor. - explicit TestPropertyKeyIs(const char* key) - : key_(key) {} + explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} // Returns true iff the test name of test property matches on key_. bool operator()(const TestProperty& test_property) const { - return String(test_property.key()).Compare(key_) == 0; + return test_property.key() == key_; } private: - String key_; + std::string key_; }; // Class UnitTestOptions. @@ -366,12 +374,12 @@ class GTEST_API_ UnitTestOptions { // Functions for processing the gtest_output flag. // Returns the output format, or "" for normal printed output. - static String GetOutputFormat(); + static std::string GetOutputFormat(); // Returns the absolute path of the requested output file, or the // default (test_detail.xml in the original working directory) if // none was explicitly specified. - static String GetAbsolutePathToOutputFile(); + static std::string GetAbsolutePathToOutputFile(); // Functions for processing the gtest_filter flag. @@ -384,8 +392,8 @@ class GTEST_API_ UnitTestOptions { // Returns true iff the user-specified filter matches the test case // name and the test name. - static bool FilterMatchesTest(const String &test_case_name, - const String &test_name); + static bool FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name); #if GTEST_OS_WINDOWS // Function for supporting the gtest_catch_exception flag. @@ -398,7 +406,7 @@ class GTEST_API_ UnitTestOptions { // Returns true if "name" matches the ':' separated list of glob-style // filters in "filter". - static bool MatchesFilter(const String& name, const char* filter); + static bool MatchesFilter(const std::string& name, const char* filter); }; // Returns the current application's name, removing directory path if that @@ -411,13 +419,13 @@ class OsStackTraceGetterInterface { OsStackTraceGetterInterface() {} virtual ~OsStackTraceGetterInterface() {} - // Returns the current OS stack trace as a String. Parameters: + // Returns the current OS stack trace as an std::string. Parameters: // // max_depth - the maximum number of stack frames to be included // in the trace. // skip_count - the number of top frames to be skipped; doesn't count // against max_depth. - virtual String CurrentStackTrace(int max_depth, int skip_count) = 0; + virtual string CurrentStackTrace(int max_depth, int skip_count) = 0; // UponLeavingGTest() should be called immediately before Google Test calls // user code. It saves some information about the current stack that @@ -432,8 +440,11 @@ class OsStackTraceGetterInterface { class OsStackTraceGetter : public OsStackTraceGetterInterface { public: OsStackTraceGetter() : caller_frame_(NULL) {} - virtual String CurrentStackTrace(int max_depth, int skip_count); - virtual void UponLeavingGTest(); + + virtual string CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_); + + virtual void UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_); // This string is inserted in place of stack frames that are part of // Google Test's implementation. @@ -455,7 +466,7 @@ class OsStackTraceGetter : public OsStackTraceGetterInterface { struct TraceInfo { const char* file; int line; - String message; + std::string message; }; // This is the default global test part result reporter used in UnitTestImpl. @@ -539,15 +550,25 @@ class GTEST_API_ UnitTestImpl { // Gets the number of failed tests. int failed_test_count() const; + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + // Gets the number of disabled tests. int disabled_test_count() const; + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + // Gets the number of all tests. int total_test_count() const; // Gets the number of tests that should run. int test_to_run_count() const; + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + // Gets the elapsed time, in milliseconds. TimeInMillis elapsed_time() const { return elapsed_time_; } @@ -596,7 +617,7 @@ class GTEST_API_ UnitTestImpl { // getter, and returns it. OsStackTraceGetterInterface* os_stack_trace_getter(); - // Returns the current OS stack trace as a String. + // Returns the current OS stack trace as an std::string. // // The maximum number of stack frames to be included is specified by // the gtest_stack_trace_depth flag. The skip_count parameter @@ -606,7 +627,7 @@ class GTEST_API_ UnitTestImpl { // For example, if Foo() calls Bar(), which in turn calls // CurrentOsStackTraceExceptTop(1), Foo() will be included in the // trace but Bar() and CurrentOsStackTraceExceptTop() won't. - String CurrentOsStackTraceExceptTop(int skip_count); + std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_; // Finds and returns a TestCase with the given name. If one doesn't // exist, creates one and returns it. @@ -696,6 +717,12 @@ class GTEST_API_ UnitTestImpl { ad_hoc_test_result_.Clear(); } + // Adds a TestProperty to the current TestResult object when invoked in a + // context of a test or a test case, or to the global property set. If the + // result already contains a property with the same key, the value will be + // updated. + void RecordProperty(const TestProperty& test_property); + enum ReactionToSharding { HONOR_SHARDING_PROTOCOL, IGNORE_SHARDING_PROTOCOL @@ -880,6 +907,10 @@ class GTEST_API_ UnitTestImpl { // Our random number generator. internal::Random random_; + // The time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp_; + // How long the test took to run, in milliseconds. TimeInMillis elapsed_time_; @@ -935,33 +966,7 @@ GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); // Returns the message describing the last system error, regardless of the // platform. -GTEST_API_ String GetLastErrnoDescription(); - -# if GTEST_OS_WINDOWS -// Provides leak-safe Windows kernel handle ownership. -class AutoHandle { - public: - AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} - explicit AutoHandle(HANDLE handle) : handle_(handle) {} - - ~AutoHandle() { Reset(); } - - HANDLE Get() const { return handle_; } - void Reset() { Reset(INVALID_HANDLE_VALUE); } - void Reset(HANDLE handle) { - if (handle != handle_) { - if (handle_ != INVALID_HANDLE_VALUE) - ::CloseHandle(handle_); - handle_ = handle; - } - } - - private: - HANDLE handle_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); -}; -# endif // GTEST_OS_WINDOWS +GTEST_API_ std::string GetLastErrnoDescription(); // Attempts to parse a string into a positive integer pointed to by the // number parameter. Returns true if that is possible. @@ -1018,8 +1023,9 @@ bool ParseNaturalNumber(const ::std::string& str, Integer* number) { class TestResultAccessor { public: static void RecordProperty(TestResult* test_result, + const std::string& xml_element, const TestProperty& property) { - test_result->RecordProperty(property); + test_result->RecordProperty(xml_element, property); } static void ClearTestPartResults(TestResult* test_result) { @@ -1032,6 +1038,154 @@ class TestResultAccessor { } }; +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Abstract base class for writing strings to a socket. + class AbstractSocketWriter { + public: + virtual ~AbstractSocketWriter() {} + + // Sends a string to the socket. + virtual void Send(const string& message) = 0; + + // Closes the socket. + virtual void CloseConnection() {} + + // Sends a string and a newline to the socket. + void SendLn(const string& message) { + Send(message + "\n"); + } + }; + + // Concrete class for actually writing strings to a socket. + class SocketWriter : public AbstractSocketWriter { + public: + SocketWriter(const string& host, const string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + } + + virtual ~SocketWriter() { + if (sockfd_ != -1) + CloseConnection(); + } + + // Sends a string to the socket. + virtual void Send(const string& message) { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const int len = static_cast(message.length()); + if (write(sockfd_, message.c_str(), len) != len) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + int sockfd_; // socket file descriptor + const string host_name_; + const string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); + }; // class SocketWriter + + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static string UrlEncode(const char* str); + + StreamingListener(const string& host, const string& port) + : socket_writer_(new SocketWriter(host, port)) { Start(); } + + explicit StreamingListener(AbstractSocketWriter* socket_writer) + : socket_writer_(socket_writer) { Start(); } + + void OnTestProgramStart(const UnitTest& /* unit_test */) { + SendLn("event=TestProgramStart"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed())); + + // Notify the streaming server to stop. + socket_writer_->CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { + SendLn("event=TestIterationStart&iteration=" + + StreamableToString(iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { + SendLn("event=TestIterationEnd&passed=" + + FormatBool(unit_test.Passed()) + "&elapsed_time=" + + StreamableToString(unit_test.elapsed_time()) + "ms"); + } + + void OnTestCaseStart(const TestCase& test_case) { + SendLn(std::string("event=TestCaseStart&name=") + test_case.name()); + } + + void OnTestCaseEnd(const TestCase& test_case) { + SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) + + "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) + + "ms"); + } + + void OnTestStart(const TestInfo& test_info) { + SendLn(std::string("event=TestStart&name=") + test_info.name()); + } + + void OnTestEnd(const TestInfo& test_info) { + SendLn("event=TestEnd&passed=" + + FormatBool((test_info.result())->Passed()) + + "&elapsed_time=" + + StreamableToString((test_info.result())->elapsed_time()) + "ms"); + } + + void OnTestPartResult(const TestPartResult& test_part_result) { + const char* file_name = test_part_result.file_name(); + if (file_name == NULL) + file_name = ""; + SendLn("event=TestPartResult&file=" + UrlEncode(file_name) + + "&line=" + StreamableToString(test_part_result.line_number()) + + "&message=" + UrlEncode(test_part_result.message())); + } + + private: + // Sends the given message and a newline to the socket. + void SendLn(const string& message) { socket_writer_->SendLn(message); } + + // Called at the start of streaming to notify the receiver what + // protocol we are using. + void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } + + string FormatBool(bool value) { return value ? "1" : "0"; } + + const scoped_ptr socket_writer_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +#endif // GTEST_CAN_STREAM_RESULTS_ + } // namespace internal } // namespace testing diff --git a/tests/gtest/src/gtest-port.cc b/tests/gtest/src/gtest-port.cc index b860d4812..b032745b4 100644 --- a/tests/gtest/src/gtest-port.cc +++ b/tests/gtest/src/gtest-port.cc @@ -36,14 +36,14 @@ #include #include -#if GTEST_OS_WINDOWS_MOBILE -# include // For TerminateProcess() -#elif GTEST_OS_WINDOWS +#if GTEST_OS_WINDOWS +# include # include # include +# include // Used in ThreadLocal. #else # include -#endif // GTEST_OS_WINDOWS_MOBILE +#endif // GTEST_OS_WINDOWS #if GTEST_OS_MAC # include @@ -51,6 +51,12 @@ # include #endif // GTEST_OS_MAC +#if GTEST_OS_QNX +# include +# include +# include +#endif // GTEST_OS_QNX + #include "gtest/gtest-spi.h" #include "gtest/gtest-message.h" #include "gtest/internal/gtest-internal.h" @@ -58,9 +64,9 @@ // Indicates that this translation unit is part of Google Test's // implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. +// included, or there will be a compiler error. This trick exists to +// prevent the accidental inclusion of gtest-internal-inl.h in the +// user's code. #define GTEST_IMPLEMENTATION_ 1 #include "src/gtest-internal-inl.h" #undef GTEST_IMPLEMENTATION_ @@ -98,6 +104,26 @@ size_t GetThreadCount() { } } +#elif GTEST_OS_QNX + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const int fd = open("/proc/self/as", O_RDONLY); + if (fd < 0) { + return 0; + } + procfs_info process_info; + const int status = + devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL); + close(fd); + if (status == EOK) { + return static_cast(process_info.num_threads); + } else { + return 0; + } +} + #else size_t GetThreadCount() { @@ -108,6 +134,389 @@ size_t GetThreadCount() { #endif // GTEST_OS_MAC +#if GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS + +void SleepMilliseconds(int n) { + ::Sleep(n); +} + +AutoHandle::AutoHandle() + : handle_(INVALID_HANDLE_VALUE) {} + +AutoHandle::AutoHandle(Handle handle) + : handle_(handle) {} + +AutoHandle::~AutoHandle() { + Reset(); +} + +AutoHandle::Handle AutoHandle::Get() const { + return handle_; +} + +void AutoHandle::Reset() { + Reset(INVALID_HANDLE_VALUE); +} + +void AutoHandle::Reset(HANDLE handle) { + // Resetting with the same handle we already own is invalid. + if (handle_ != handle) { + if (IsCloseable()) { + ::CloseHandle(handle_); + } + handle_ = handle; + } else { + GTEST_CHECK_(!IsCloseable()) + << "Resetting a valid handle to itself is likely a programmer error " + "and thus not allowed."; + } +} + +bool AutoHandle::IsCloseable() const { + // Different Windows APIs may use either of these values to represent an + // invalid handle. + return handle_ != NULL && handle_ != INVALID_HANDLE_VALUE; +} + +Notification::Notification() + : event_(::CreateEvent(NULL, // Default security attributes. + TRUE, // Do not reset automatically. + FALSE, // Initially unset. + NULL)) { // Anonymous event. + GTEST_CHECK_(event_.Get() != NULL); +} + +void Notification::Notify() { + GTEST_CHECK_(::SetEvent(event_.Get()) != FALSE); +} + +void Notification::WaitForNotification() { + GTEST_CHECK_( + ::WaitForSingleObject(event_.Get(), INFINITE) == WAIT_OBJECT_0); +} + +Mutex::Mutex() + : type_(kDynamic), + owner_thread_id_(0), + critical_section_init_phase_(0), + critical_section_(new CRITICAL_SECTION) { + ::InitializeCriticalSection(critical_section_); +} + +Mutex::~Mutex() { + // Static mutexes are leaked intentionally. It is not thread-safe to try + // to clean them up. + // TODO(yukawa): Switch to Slim Reader/Writer (SRW) Locks, which requires + // nothing to clean it up but is available only on Vista and later. + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937.aspx + if (type_ == kDynamic) { + ::DeleteCriticalSection(critical_section_); + delete critical_section_; + critical_section_ = NULL; + } +} + +void Mutex::Lock() { + ThreadSafeLazyInit(); + ::EnterCriticalSection(critical_section_); + owner_thread_id_ = ::GetCurrentThreadId(); +} + +void Mutex::Unlock() { + ThreadSafeLazyInit(); + // We don't protect writing to owner_thread_id_ here, as it's the + // caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + owner_thread_id_ = 0; + ::LeaveCriticalSection(critical_section_); +} + +// Does nothing if the current thread holds the mutex. Otherwise, crashes +// with high probability. +void Mutex::AssertHeld() { + ThreadSafeLazyInit(); + GTEST_CHECK_(owner_thread_id_ == ::GetCurrentThreadId()) + << "The current thread is not holding the mutex @" << this; +} + +// Initializes owner_thread_id_ and critical_section_ in static mutexes. +void Mutex::ThreadSafeLazyInit() { + // Dynamic mutexes are initialized in the constructor. + if (type_ == kStatic) { + switch ( + ::InterlockedCompareExchange(&critical_section_init_phase_, 1L, 0L)) { + case 0: + // If critical_section_init_phase_ was 0 before the exchange, we + // are the first to test it and need to perform the initialization. + owner_thread_id_ = 0; + critical_section_ = new CRITICAL_SECTION; + ::InitializeCriticalSection(critical_section_); + // Updates the critical_section_init_phase_ to 2 to signal + // initialization complete. + GTEST_CHECK_(::InterlockedCompareExchange( + &critical_section_init_phase_, 2L, 1L) == + 1L); + break; + case 1: + // Somebody else is already initializing the mutex; spin until they + // are done. + while (::InterlockedCompareExchange(&critical_section_init_phase_, + 2L, + 2L) != 2L) { + // Possibly yields the rest of the thread's time slice to other + // threads. + ::Sleep(0); + } + break; + + case 2: + break; // The mutex is already initialized and ready for use. + + default: + GTEST_CHECK_(false) + << "Unexpected value of critical_section_init_phase_ " + << "while initializing a static mutex."; + } + } +} + +namespace { + +class ThreadWithParamSupport : public ThreadWithParamBase { + public: + static HANDLE CreateThread(Runnable* runnable, + Notification* thread_can_start) { + ThreadMainParam* param = new ThreadMainParam(runnable, thread_can_start); + DWORD thread_id; + // TODO(yukawa): Consider to use _beginthreadex instead. + HANDLE thread_handle = ::CreateThread( + NULL, // Default security. + 0, // Default stack size. + &ThreadWithParamSupport::ThreadMain, + param, // Parameter to ThreadMainStatic + 0x0, // Default creation flags. + &thread_id); // Need a valid pointer for the call to work under Win98. + GTEST_CHECK_(thread_handle != NULL) << "CreateThread failed with error " + << ::GetLastError() << "."; + if (thread_handle == NULL) { + delete param; + } + return thread_handle; + } + + private: + struct ThreadMainParam { + ThreadMainParam(Runnable* runnable, Notification* thread_can_start) + : runnable_(runnable), + thread_can_start_(thread_can_start) { + } + scoped_ptr runnable_; + // Does not own. + Notification* thread_can_start_; + }; + + static DWORD WINAPI ThreadMain(void* ptr) { + // Transfers ownership. + scoped_ptr param(static_cast(ptr)); + if (param->thread_can_start_ != NULL) + param->thread_can_start_->WaitForNotification(); + param->runnable_->Run(); + return 0; + } + + // Prohibit instantiation. + ThreadWithParamSupport(); + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParamSupport); +}; + +} // namespace + +ThreadWithParamBase::ThreadWithParamBase(Runnable *runnable, + Notification* thread_can_start) + : thread_(ThreadWithParamSupport::CreateThread(runnable, + thread_can_start)) { +} + +ThreadWithParamBase::~ThreadWithParamBase() { + Join(); +} + +void ThreadWithParamBase::Join() { + GTEST_CHECK_(::WaitForSingleObject(thread_.Get(), INFINITE) == WAIT_OBJECT_0) + << "Failed to join the thread with error " << ::GetLastError() << "."; +} + +// Maps a thread to a set of ThreadIdToThreadLocals that have values +// instantiated on that thread and notifies them when the thread exits. A +// ThreadLocal instance is expected to persist until all threads it has +// values on have terminated. +class ThreadLocalRegistryImpl { + public: + // Registers thread_local_instance as having value on the current thread. + // Returns a value that can be used to identify the thread from other threads. + static ThreadLocalValueHolderBase* GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance) { + DWORD current_thread = ::GetCurrentThreadId(); + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + ThreadIdToThreadLocals::iterator thread_local_pos = + thread_to_thread_locals->find(current_thread); + if (thread_local_pos == thread_to_thread_locals->end()) { + thread_local_pos = thread_to_thread_locals->insert( + std::make_pair(current_thread, ThreadLocalValues())).first; + StartWatcherThreadFor(current_thread); + } + ThreadLocalValues& thread_local_values = thread_local_pos->second; + ThreadLocalValues::iterator value_pos = + thread_local_values.find(thread_local_instance); + if (value_pos == thread_local_values.end()) { + value_pos = + thread_local_values + .insert(std::make_pair( + thread_local_instance, + linked_ptr( + thread_local_instance->NewValueForCurrentThread()))) + .first; + } + return value_pos->second.get(); + } + + static void OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance) { + std::vector > value_holders; + // Clean up the ThreadLocalValues data structure while holding the lock, but + // defer the destruction of the ThreadLocalValueHolderBases. + { + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + for (ThreadIdToThreadLocals::iterator it = + thread_to_thread_locals->begin(); + it != thread_to_thread_locals->end(); + ++it) { + ThreadLocalValues& thread_local_values = it->second; + ThreadLocalValues::iterator value_pos = + thread_local_values.find(thread_local_instance); + if (value_pos != thread_local_values.end()) { + value_holders.push_back(value_pos->second); + thread_local_values.erase(value_pos); + // This 'if' can only be successful at most once, so theoretically we + // could break out of the loop here, but we don't bother doing so. + } + } + } + // Outside the lock, let the destructor for 'value_holders' deallocate the + // ThreadLocalValueHolderBases. + } + + static void OnThreadExit(DWORD thread_id) { + GTEST_CHECK_(thread_id != 0) << ::GetLastError(); + std::vector > value_holders; + // Clean up the ThreadIdToThreadLocals data structure while holding the + // lock, but defer the destruction of the ThreadLocalValueHolderBases. + { + MutexLock lock(&mutex_); + ThreadIdToThreadLocals* const thread_to_thread_locals = + GetThreadLocalsMapLocked(); + ThreadIdToThreadLocals::iterator thread_local_pos = + thread_to_thread_locals->find(thread_id); + if (thread_local_pos != thread_to_thread_locals->end()) { + ThreadLocalValues& thread_local_values = thread_local_pos->second; + for (ThreadLocalValues::iterator value_pos = + thread_local_values.begin(); + value_pos != thread_local_values.end(); + ++value_pos) { + value_holders.push_back(value_pos->second); + } + thread_to_thread_locals->erase(thread_local_pos); + } + } + // Outside the lock, let the destructor for 'value_holders' deallocate the + // ThreadLocalValueHolderBases. + } + + private: + // In a particular thread, maps a ThreadLocal object to its value. + typedef std::map > ThreadLocalValues; + // Stores all ThreadIdToThreadLocals having values in a thread, indexed by + // thread's ID. + typedef std::map ThreadIdToThreadLocals; + + // Holds the thread id and thread handle that we pass from + // StartWatcherThreadFor to WatcherThreadFunc. + typedef std::pair ThreadIdAndHandle; + + static void StartWatcherThreadFor(DWORD thread_id) { + // The returned handle will be kept in thread_map and closed by + // watcher_thread in WatcherThreadFunc. + HANDLE thread = ::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, + FALSE, + thread_id); + GTEST_CHECK_(thread != NULL); + // We need to to pass a valid thread ID pointer into CreateThread for it + // to work correctly under Win98. + DWORD watcher_thread_id; + HANDLE watcher_thread = ::CreateThread( + NULL, // Default security. + 0, // Default stack size + &ThreadLocalRegistryImpl::WatcherThreadFunc, + reinterpret_cast(new ThreadIdAndHandle(thread_id, thread)), + CREATE_SUSPENDED, + &watcher_thread_id); + GTEST_CHECK_(watcher_thread != NULL); + // Give the watcher thread the same priority as ours to avoid being + // blocked by it. + ::SetThreadPriority(watcher_thread, + ::GetThreadPriority(::GetCurrentThread())); + ::ResumeThread(watcher_thread); + ::CloseHandle(watcher_thread); + } + + // Monitors exit from a given thread and notifies those + // ThreadIdToThreadLocals about thread termination. + static DWORD WINAPI WatcherThreadFunc(LPVOID param) { + const ThreadIdAndHandle* tah = + reinterpret_cast(param); + GTEST_CHECK_( + ::WaitForSingleObject(tah->second, INFINITE) == WAIT_OBJECT_0); + OnThreadExit(tah->first); + ::CloseHandle(tah->second); + delete tah; + return 0; + } + + // Returns map of thread local instances. + static ThreadIdToThreadLocals* GetThreadLocalsMapLocked() { + mutex_.AssertHeld(); + static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals; + return map; + } + + // Protects access to GetThreadLocalsMapLocked() and its return value. + static Mutex mutex_; + // Protects access to GetThreadMapLocked() and its return value. + static Mutex thread_map_mutex_; +}; + +Mutex ThreadLocalRegistryImpl::mutex_(Mutex::kStaticMutex); +Mutex ThreadLocalRegistryImpl::thread_map_mutex_(Mutex::kStaticMutex); + +ThreadLocalValueHolderBase* ThreadLocalRegistry::GetValueOnCurrentThread( + const ThreadLocalBase* thread_local_instance) { + return ThreadLocalRegistryImpl::GetValueOnCurrentThread( + thread_local_instance); +} + +void ThreadLocalRegistry::OnThreadLocalDestroyed( + const ThreadLocalBase* thread_local_instance) { + ThreadLocalRegistryImpl::OnThreadLocalDestroyed(thread_local_instance); +} + +#endif // GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS + #if GTEST_USES_POSIX_RE // Implements RE. Currently only needed for death tests. @@ -222,7 +631,7 @@ bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { } // Helper function used by ValidateRegex() to format error messages. -String FormatRegexSyntaxError(const char* regex, int index) { +std::string FormatRegexSyntaxError(const char* regex, int index) { return (Message() << "Syntax error at index " << index << " in simple regular expression \"" << regex << "\": ").GetString(); } @@ -429,15 +838,15 @@ const char kUnknownFile[] = "unknown file"; // Formats a source file path and a line number as they would appear // in an error message from the compiler used to compile this code. GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { - const char* const file_name = file == NULL ? kUnknownFile : file; + const std::string file_name(file == NULL ? kUnknownFile : file); if (line < 0) { - return String::Format("%s:", file_name).c_str(); + return file_name + ":"; } #ifdef _MSC_VER - return String::Format("%s(%d):", file_name, line).c_str(); + return file_name + "(" + StreamableToString(line) + "):"; #else - return String::Format("%s:%d:", file_name, line).c_str(); + return file_name + ":" + StreamableToString(line) + ":"; #endif // _MSC_VER } @@ -448,12 +857,12 @@ GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { // to the file location it produces, unlike FormatFileLocation(). GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( const char* file, int line) { - const char* const file_name = file == NULL ? kUnknownFile : file; + const std::string file_name(file == NULL ? kUnknownFile : file); if (line < 0) return file_name; else - return String::Format("%s:%d", file_name, line).c_str(); + return file_name + ":" + StreamableToString(line); } @@ -477,10 +886,7 @@ GTestLog::~GTestLog() { } // Disable Microsoft deprecation warnings for POSIX functions called from // this class (creat, dup, dup2, and close) -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4996) -#endif // _MSC_VER +GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) #if GTEST_HAS_STREAM_REDIRECTION @@ -488,8 +894,7 @@ GTestLog::~GTestLog() { class CapturedStream { public: // The ctor redirects the stream to a temporary file. - CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { - + explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { # if GTEST_OS_WINDOWS char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT @@ -506,10 +911,29 @@ class CapturedStream { << temp_file_path; filename_ = temp_file_path; # else - // There's no guarantee that a test has write access to the - // current directory, so we create the temporary file in the /tmp - // directory instead. + // There's no guarantee that a test has write access to the current + // directory, so we create the temporary file in the /tmp directory + // instead. We use /tmp on most systems, and /sdcard on Android. + // That's because Android doesn't have /tmp. +# if GTEST_OS_LINUX_ANDROID + // Note: Android applications are expected to call the framework's + // Context.getExternalStorageDirectory() method through JNI to get + // the location of the world-writable SD Card directory. However, + // this requires a Context handle, which cannot be retrieved + // globally from native code. Doing so also precludes running the + // code as part of a regular standalone executable, which doesn't + // run in a Dalvik process (e.g. when running it through 'adb shell'). + // + // The location /sdcard is directly accessible from native code + // and is the only location (unofficially) supported by the Android + // team. It's generally a symlink to the real SD Card mount point + // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or + // other OEM-customized locations. Never rely on these, and always + // use /sdcard. + char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX"; +# else char name_template[] = "/tmp/captured_stream.XXXXXX"; +# endif // GTEST_OS_LINUX_ANDROID const int captured_fd = mkstemp(name_template); filename_ = name_template; # endif // GTEST_OS_WINDOWS @@ -522,7 +946,7 @@ class CapturedStream { remove(filename_.c_str()); } - String GetCapturedString() { + std::string GetCapturedString() { if (uncaptured_fd_ != -1) { // Restores the original stream. fflush(NULL); @@ -532,14 +956,14 @@ class CapturedStream { } FILE* const file = posix::FOpen(filename_.c_str(), "r"); - const String content = ReadEntireFile(file); + const std::string content = ReadEntireFile(file); posix::FClose(file); return content; } private: - // Reads the entire content of a file as a String. - static String ReadEntireFile(FILE* file); + // Reads the entire content of a file as an std::string. + static std::string ReadEntireFile(FILE* file); // Returns the size (in bytes) of a file. static size_t GetFileSize(FILE* file); @@ -559,7 +983,7 @@ size_t CapturedStream::GetFileSize(FILE* file) { } // Reads the entire content of a file as a string. -String CapturedStream::ReadEntireFile(FILE* file) { +std::string CapturedStream::ReadEntireFile(FILE* file) { const size_t file_size = GetFileSize(file); char* const buffer = new char[file_size]; @@ -575,15 +999,13 @@ String CapturedStream::ReadEntireFile(FILE* file) { bytes_read += bytes_last_read; } while (bytes_last_read > 0 && bytes_read < file_size); - const String content(buffer, bytes_read); + const std::string content(buffer, bytes_read); delete[] buffer; return content; } -# ifdef _MSC_VER -# pragma warning(pop) -# endif // _MSC_VER +GTEST_DISABLE_MSC_WARNINGS_POP_() static CapturedStream* g_captured_stderr = NULL; static CapturedStream* g_captured_stdout = NULL; @@ -598,8 +1020,8 @@ void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { } // Stops capturing the output stream and returns the captured string. -String GetCapturedStream(CapturedStream** captured_stream) { - const String content = (*captured_stream)->GetCapturedString(); +std::string GetCapturedStream(CapturedStream** captured_stream) { + const std::string content = (*captured_stream)->GetCapturedString(); delete *captured_stream; *captured_stream = NULL; @@ -618,21 +1040,37 @@ void CaptureStderr() { } // Stops capturing stdout and returns the captured string. -String GetCapturedStdout() { return GetCapturedStream(&g_captured_stdout); } +std::string GetCapturedStdout() { + return GetCapturedStream(&g_captured_stdout); +} // Stops capturing stderr and returns the captured string. -String GetCapturedStderr() { return GetCapturedStream(&g_captured_stderr); } +std::string GetCapturedStderr() { + return GetCapturedStream(&g_captured_stderr); +} #endif // GTEST_HAS_STREAM_REDIRECTION #if GTEST_HAS_DEATH_TEST // A copy of all command line arguments. Set by InitGoogleTest(). -::std::vector g_argvs; +::std::vector g_argvs; -// Returns the command line as a vector of strings. -const ::std::vector& GetArgvs() { return g_argvs; } +static const ::std::vector* g_injected_test_argvs = + NULL; // Owned. +void SetInjectableArgvs(const ::std::vector* argvs) { + if (g_injected_test_argvs != argvs) + delete g_injected_test_argvs; + g_injected_test_argvs = argvs; +} + +const ::std::vector& GetInjectableArgvs() { + if (g_injected_test_argvs != NULL) { + return *g_injected_test_argvs; + } + return g_argvs; +} #endif // GTEST_HAS_DEATH_TEST #if GTEST_OS_WINDOWS_MOBILE @@ -647,8 +1085,8 @@ void Abort() { // Returns the name of the environment variable corresponding to the // given flag. For example, FlagToEnvVar("foo") will return // "GTEST_FOO" in the open-source version. -static String FlagToEnvVar(const char* flag) { - const String full_flag = +static std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); Message env_var; @@ -705,7 +1143,7 @@ bool ParseInt32(const Message& src_text, const char* str, Int32* value) { // // The value is considered true iff it's not "0". bool BoolFromGTestEnv(const char* flag, bool default_value) { - const String env_var = FlagToEnvVar(flag); + const std::string env_var = FlagToEnvVar(flag); const char* const string_value = posix::GetEnv(env_var.c_str()); return string_value == NULL ? default_value : strcmp(string_value, "0") != 0; @@ -715,7 +1153,7 @@ bool BoolFromGTestEnv(const char* flag, bool default_value) { // variable corresponding to the given flag; if it isn't set or // doesn't represent a valid 32-bit integer, returns default_value. Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { - const String env_var = FlagToEnvVar(flag); + const std::string env_var = FlagToEnvVar(flag); const char* const string_value = posix::GetEnv(env_var.c_str()); if (string_value == NULL) { // The environment variable is not set. @@ -737,7 +1175,7 @@ Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { // Reads and returns the string environment variable corresponding to // the given flag; if it's not set, returns default_value. const char* StringFromGTestEnv(const char* flag, const char* default_value) { - const String env_var = FlagToEnvVar(flag); + const std::string env_var = FlagToEnvVar(flag); const char* const value = posix::GetEnv(env_var.c_str()); return value == NULL ? default_value : value; } diff --git a/tests/gtest/src/gtest-printers.cc b/tests/gtest/src/gtest-printers.cc index ed63c7b3b..a2df412f8 100644 --- a/tests/gtest/src/gtest-printers.cc +++ b/tests/gtest/src/gtest-printers.cc @@ -45,6 +45,7 @@ #include "gtest/gtest-printers.h" #include #include +#include #include // NOLINT #include #include "gtest/internal/gtest-port.h" @@ -55,15 +56,10 @@ namespace { using ::std::ostream; -#if GTEST_OS_WINDOWS_MOBILE // Windows CE does not define _snprintf_s. -# define snprintf _snprintf -#elif _MSC_VER >= 1400 // VC 8.0 and later deprecate snprintf and _snprintf. -# define snprintf _snprintf_s -#elif _MSC_VER -# define snprintf _snprintf -#endif // GTEST_OS_WINDOWS_MOBILE - // Prints a segment of bytes in the given object. +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, size_t count, ostream* os) { char text[5] = ""; @@ -77,7 +73,7 @@ void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, else *os << '-'; } - snprintf(text, sizeof(text), "%02X", obj_bytes[j]); + GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]); *os << text; } } @@ -184,16 +180,16 @@ static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { *os << static_cast(c); return kAsIs; } else { - *os << String::Format("\\x%X", static_cast(c)); + *os << "\\x" + String::FormatHexInt(static_cast(c)); return kHexEscape; } } return kSpecialEscape; } -// Prints a char c as if it's part of a string literal, escaping it when +// Prints a wchar_t c as if it's part of a string literal, escaping it when // necessary; returns how c was formatted. -static CharFormat PrintAsWideStringLiteralTo(wchar_t c, ostream* os) { +static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) { switch (c) { case L'\'': *os << "'"; @@ -208,8 +204,9 @@ static CharFormat PrintAsWideStringLiteralTo(wchar_t c, ostream* os) { // Prints a char c as if it's part of a string literal, escaping it when // necessary; returns how c was formatted. -static CharFormat PrintAsNarrowStringLiteralTo(char c, ostream* os) { - return PrintAsWideStringLiteralTo(static_cast(c), os); +static CharFormat PrintAsStringLiteralTo(char c, ostream* os) { + return PrintAsStringLiteralTo( + static_cast(static_cast(c)), os); } // Prints a wide or narrow character c and its code. '\0' is printed @@ -228,7 +225,7 @@ void PrintCharAndCodeTo(Char c, ostream* os) { // obvious). if (c == 0) return; - *os << " (" << String::Format("%d", c).c_str(); + *os << " (" << static_cast(c); // For more convenience, we print c's code again in hexidecimal, // unless c was already printed in the form '\x##' or the code is in @@ -236,8 +233,7 @@ void PrintCharAndCodeTo(Char c, ostream* os) { if (format == kHexEscape || (1 <= c && c <= 9)) { // Do nothing. } else { - *os << String::Format(", 0x%X", - static_cast(c)).c_str(); + *os << ", 0x" << String::FormatHexInt(static_cast(c)); } *os << ")"; } @@ -255,48 +251,69 @@ void PrintTo(wchar_t wc, ostream* os) { PrintCharAndCodeTo(wc, os); } -// Prints the given array of characters to the ostream. -// The array starts at *begin, the length is len, it may include '\0' characters -// and may not be null-terminated. -static void PrintCharsAsStringTo(const char* begin, size_t len, ostream* os) { - *os << "\""; +// Prints the given array of characters to the ostream. CharType must be either +// char or wchar_t. +// The array starts at begin, the length is len, it may include '\0' characters +// and may not be NUL-terminated. +template +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +static void PrintCharsAsStringTo( + const CharType* begin, size_t len, ostream* os) { + const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\""; + *os << kQuoteBegin; bool is_previous_hex = false; for (size_t index = 0; index < len; ++index) { - const char cur = begin[index]; + const CharType cur = begin[index]; if (is_previous_hex && IsXDigit(cur)) { // Previous character is of '\x..' form and this character can be // interpreted as another hexadecimal digit in its number. Break string to // disambiguate. - *os << "\" \""; + *os << "\" " << kQuoteBegin; } - is_previous_hex = PrintAsNarrowStringLiteralTo(cur, os) == kHexEscape; + is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; } *os << "\""; } +// Prints a (const) char/wchar_t array of 'len' elements, starting at address +// 'begin'. CharType must be either char or wchar_t. +template +GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ +GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ +GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ +static void UniversalPrintCharArray( + const CharType* begin, size_t len, ostream* os) { + // The code + // const char kFoo[] = "foo"; + // generates an array of 4, not 3, elements, with the last one being '\0'. + // + // Therefore when printing a char array, we don't print the last element if + // it's '\0', such that the output matches the string literal as it's + // written in the source code. + if (len > 0 && begin[len - 1] == '\0') { + PrintCharsAsStringTo(begin, len - 1, os); + return; + } + + // If, however, the last element in the array is not '\0', e.g. + // const char kFoo[] = { 'f', 'o', 'o' }; + // we must print the entire array. We also print a message to indicate + // that the array is not NUL-terminated. + PrintCharsAsStringTo(begin, len, os); + *os << " (no terminating NUL)"; +} + // Prints a (const) char array of 'len' elements, starting at address 'begin'. void UniversalPrintArray(const char* begin, size_t len, ostream* os) { - PrintCharsAsStringTo(begin, len, os); + UniversalPrintCharArray(begin, len, os); } -// Prints the given array of wide characters to the ostream. -// The array starts at *begin, the length is len, it may include L'\0' -// characters and may not be null-terminated. -static void PrintWideCharsAsStringTo(const wchar_t* begin, size_t len, - ostream* os) { - *os << "L\""; - bool is_previous_hex = false; - for (size_t index = 0; index < len; ++index) { - const wchar_t cur = begin[index]; - if (is_previous_hex && isascii(cur) && IsXDigit(static_cast(cur))) { - // Previous character is of '\x..' form and this character can be - // interpreted as another hexadecimal digit in its number. Break string to - // disambiguate. - *os << "\" L\""; - } - is_previous_hex = PrintAsWideStringLiteralTo(cur, os) == kHexEscape; - } - *os << "\""; +// Prints a (const) wchar_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); } // Prints the given C string to the ostream. @@ -322,7 +339,7 @@ void PrintTo(const wchar_t* s, ostream* os) { *os << "NULL"; } else { *os << ImplicitCast_(s) << " pointing to "; - PrintWideCharsAsStringTo(s, wcslen(s), os); + PrintCharsAsStringTo(s, std::wcslen(s), os); } } #endif // wchar_t is native @@ -341,13 +358,13 @@ void PrintStringTo(const ::std::string& s, ostream* os) { // Prints a ::wstring object. #if GTEST_HAS_GLOBAL_WSTRING void PrintWideStringTo(const ::wstring& s, ostream* os) { - PrintWideCharsAsStringTo(s.data(), s.size(), os); + PrintCharsAsStringTo(s.data(), s.size(), os); } #endif // GTEST_HAS_GLOBAL_WSTRING #if GTEST_HAS_STD_WSTRING void PrintWideStringTo(const ::std::wstring& s, ostream* os) { - PrintWideCharsAsStringTo(s.data(), s.size(), os); + PrintCharsAsStringTo(s.data(), s.size(), os); } #endif // GTEST_HAS_STD_WSTRING diff --git a/tests/gtest/src/gtest-test-part.cc b/tests/gtest/src/gtest-test-part.cc index 5ddc67c1c..fb0e35425 100644 --- a/tests/gtest/src/gtest-test-part.cc +++ b/tests/gtest/src/gtest-test-part.cc @@ -35,9 +35,9 @@ // Indicates that this translation unit is part of Google Test's // implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. +// included, or there will be a compiler error. This trick exists to +// prevent the accidental inclusion of gtest-internal-inl.h in the +// user's code. #define GTEST_IMPLEMENTATION_ 1 #include "src/gtest-internal-inl.h" #undef GTEST_IMPLEMENTATION_ @@ -48,10 +48,10 @@ using internal::GetUnitTestImpl; // Gets the summary of the failure message by omitting the stack trace // in it. -internal::String TestPartResult::ExtractSummary(const char* message) { +std::string TestPartResult::ExtractSummary(const char* message) { const char* const stack_trace = strstr(message, internal::kStackTraceMarker); - return stack_trace == NULL ? internal::String(message) : - internal::String(message, stack_trace - message); + return stack_trace == NULL ? message : + std::string(message, stack_trace); } // Prints a TestPartResult object. diff --git a/tests/gtest/src/gtest-typed-test.cc b/tests/gtest/src/gtest-typed-test.cc index a5cc88f92..e11d050a4 100644 --- a/tests/gtest/src/gtest-typed-test.cc +++ b/tests/gtest/src/gtest-typed-test.cc @@ -45,6 +45,15 @@ static const char* SkipSpaces(const char* str) { return str; } +static std::vector SplitIntoTestNames(const char* src) { + std::vector name_vec; + src = SkipSpaces(src); + for (; src != NULL; src = SkipComma(src)) { + name_vec.push_back(StripTrailingSpaces(GetPrefixUntilComma(src))); + } + return name_vec; +} + // Verifies that registered_tests match the test names in // defined_test_names_; returns registered_tests if successful, or // aborts the program otherwise. @@ -53,15 +62,14 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames( typedef ::std::set::const_iterator DefinedTestIter; registered_ = true; - // Skip initial whitespace in registered_tests since some - // preprocessors prefix stringizied literals with whitespace. - registered_tests = SkipSpaces(registered_tests); + std::vector name_vec = SplitIntoTestNames(registered_tests); Message errors; - ::std::set tests; - for (const char* names = registered_tests; names != NULL; - names = SkipComma(names)) { - const String name = GetPrefixUntilComma(names); + + std::set tests; + for (std::vector::const_iterator name_it = name_vec.begin(); + name_it != name_vec.end(); ++name_it) { + const std::string& name = *name_it; if (tests.count(name) != 0) { errors << "Test " << name << " is listed more than once.\n"; continue; @@ -93,7 +101,7 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames( } } - const String& errors_str = errors.GetString(); + const std::string& errors_str = errors.GetString(); if (errors_str != "") { fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), errors_str.c_str()); diff --git a/tests/gtest/src/gtest.cc b/tests/gtest/src/gtest.cc index ee5eb1c49..7fd5f298d 100644 --- a/tests/gtest/src/gtest.cc +++ b/tests/gtest/src/gtest.cc @@ -39,10 +39,15 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include #include // NOLINT #include #include @@ -77,6 +82,7 @@ #elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. # include // NOLINT +# undef min #elif GTEST_OS_WINDOWS // We are on Windows proper. @@ -99,6 +105,7 @@ // cpplint thinks that the header is already included, so we want to // silence it. # include // NOLINT +# undef min #else @@ -121,6 +128,8 @@ #if GTEST_CAN_STREAM_RESULTS_ # include // NOLINT # include // NOLINT +# include // NOLINT +# include // NOLINT #endif // Indicates that this translation unit is part of Google Test's @@ -179,6 +188,10 @@ bool g_help_flag = false; } // namespace internal +static const char* GetDefaultFilter() { + return kUniversalFilter; +} + GTEST_DEFINE_bool_( also_run_disabled_tests, internal::BoolFromGTestEnv("also_run_disabled_tests", false), @@ -201,11 +214,11 @@ GTEST_DEFINE_string_( "Whether to use colors in the output. Valid values: yes, no, " "and auto. 'auto' means to use colors if the output is " "being sent to a terminal and the TERM environment variable " - "is set to xterm, xterm-color, xterm-256color, linux or cygwin."); + "is set to a terminal type that supports colors."); GTEST_DEFINE_string_( filter, - internal::StringFromGTestEnv("filter", kUniversalFilter), + internal::StringFromGTestEnv("filter", GetDefaultFilter()), "A colon-separated list of glob (not regex) patterns " "for filtering the tests to run, optionally followed by a " "'-' and a : separated list of negative patterns (tests to " @@ -305,7 +318,7 @@ UInt32 Random::Generate(UInt32 range) { // Test. g_init_gtest_count is set to the number of times // InitGoogleTest() has been called. We don't protect this variable // under a mutex as it is only accessed in the main thread. -int g_init_gtest_count = 0; +GTEST_API_ int g_init_gtest_count = 0; static bool GTestIsInitialized() { return g_init_gtest_count != 0; } // Iterates over a vector of TestCases, keeping a running sum of the @@ -360,10 +373,10 @@ void AssertHelper::operator=(const Message& message) const { } // Mutex for linked pointers. -GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); // Application pathname gotten in InitGoogleTest. -String g_executable_path; +std::string g_executable_path; // Returns the current application's name, removing directory path if that // is present. @@ -382,29 +395,29 @@ FilePath GetCurrentExecutableName() { // Functions for processing the gtest_output flag. // Returns the output format, or "" for normal printed output. -String UnitTestOptions::GetOutputFormat() { +std::string UnitTestOptions::GetOutputFormat() { const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); - if (gtest_output_flag == NULL) return String(""); + if (gtest_output_flag == NULL) return std::string(""); const char* const colon = strchr(gtest_output_flag, ':'); return (colon == NULL) ? - String(gtest_output_flag) : - String(gtest_output_flag, colon - gtest_output_flag); + std::string(gtest_output_flag) : + std::string(gtest_output_flag, colon - gtest_output_flag); } // Returns the name of the requested output file, or the default if none // was explicitly specified. -String UnitTestOptions::GetAbsolutePathToOutputFile() { +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); if (gtest_output_flag == NULL) - return String(""); + return ""; const char* const colon = strchr(gtest_output_flag, ':'); if (colon == NULL) - return String(internal::FilePath::ConcatPaths( - internal::FilePath( - UnitTest::GetInstance()->original_working_dir()), - internal::FilePath(kDefaultOutputFile)).ToString() ); + return internal::FilePath::ConcatPaths( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile)).string(); internal::FilePath output_name(colon + 1); if (!output_name.IsAbsolutePath()) @@ -417,12 +430,12 @@ String UnitTestOptions::GetAbsolutePathToOutputFile() { internal::FilePath(colon + 1)); if (!output_name.IsDirectory()) - return output_name.ToString(); + return output_name.string(); internal::FilePath result(internal::FilePath::GenerateUniqueFileName( output_name, internal::GetCurrentExecutableName(), GetOutputFormat().c_str())); - return result.ToString(); + return result.string(); } // Returns true iff the wildcard pattern matches the string. The @@ -447,7 +460,8 @@ bool UnitTestOptions::PatternMatchesString(const char *pattern, } } -bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { +bool UnitTestOptions::MatchesFilter( + const std::string& name, const char* filter) { const char *cur_pattern = filter; for (;;) { if (PatternMatchesString(cur_pattern, name.c_str())) { @@ -467,28 +481,24 @@ bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { } } -// TODO(keithray): move String function implementations to gtest-string.cc. - // Returns true iff the user-specified filter matches the test case // name and the test name. -bool UnitTestOptions::FilterMatchesTest(const String &test_case_name, - const String &test_name) { - const String& full_name = String::Format("%s.%s", - test_case_name.c_str(), - test_name.c_str()); +bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name) { + const std::string& full_name = test_case_name + "." + test_name.c_str(); // Split --gtest_filter at '-', if there is one, to separate into // positive filter and negative filter portions const char* const p = GTEST_FLAG(filter).c_str(); const char* const dash = strchr(p, '-'); - String positive; - String negative; + std::string positive; + std::string negative; if (dash == NULL) { positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter - negative = String(""); + negative = ""; } else { - positive = String(p, dash - p); // Everything up to the dash - negative = String(dash+1); // Everything after the dash + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash if (positive.empty()) { // Treat '-test1' as the same as '*-test1' positive = kUniversalFilter; @@ -608,7 +618,7 @@ AssertionResult HasOneFailure(const char* /* results_expr */, const TestPartResultArray& results, TestPartResult::Type type, const string& substr) { - const String expected(type == TestPartResult::kFatalFailure ? + const std::string expected(type == TestPartResult::kFatalFailure ? "1 fatal failure" : "1 non-fatal failure"); Message msg; @@ -731,11 +741,22 @@ int UnitTestImpl::failed_test_count() const { return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); } +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestCaseList(test_cases_, + &TestCase::reportable_disabled_test_count); +} + // Gets the number of disabled tests. int UnitTestImpl::disabled_test_count() const { return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); } +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count); +} + // Gets the number of all tests. int UnitTestImpl::total_test_count() const { return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); @@ -746,7 +767,7 @@ int UnitTestImpl::test_to_run_count() const { return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); } -// Returns the current OS stack trace as a String. +// Returns the current OS stack trace as an std::string. // // The maximum number of stack frames to be included is specified by // the gtest_stack_trace_depth flag. The skip_count parameter @@ -756,9 +777,9 @@ int UnitTestImpl::test_to_run_count() const { // For example, if Foo() calls Bar(), which in turn calls // CurrentOsStackTraceExceptTop(1), Foo() will be included in the // trace but Bar() and CurrentOsStackTraceExceptTop() won't. -String UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { (void)skip_count; - return String(""); + return ""; } // Returns the current time in milliseconds. @@ -787,21 +808,13 @@ TimeInMillis GetTimeInMillis() { #elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ __timeb64 now; -# ifdef _MSC_VER - // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 // (deprecated function) there. // TODO(kenton@google.com): Use GetTickCount()? Or use // SystemTimeToFileTime() -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4996) // Temporarily disables warning 4996. + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996) _ftime64(&now); -# pragma warning(pop) // Restores the warning state. -# else - - _ftime64(&now); - -# endif // _MSC_VER + GTEST_DISABLE_MSC_WARNINGS_POP_() return static_cast(now.time) * 1000 + now.millitm; #elif GTEST_HAS_GETTIMEOFDAY_ @@ -815,41 +828,7 @@ TimeInMillis GetTimeInMillis() { // Utilities -// class String - -// Returns the input enclosed in double quotes if it's not NULL; -// otherwise returns "(null)". For example, "\"Hello\"" is returned -// for input "Hello". -// -// This is useful for printing a C string in the syntax of a literal. -// -// Known issue: escape sequences are not handled yet. -String String::ShowCStringQuoted(const char* c_str) { - return c_str ? String::Format("\"%s\"", c_str) : String("(null)"); -} - -// Copies at most length characters from str into a newly-allocated -// piece of memory of size length+1. The memory is allocated with new[]. -// A terminating null byte is written to the memory, and a pointer to it -// is returned. If str is NULL, NULL is returned. -static char* CloneString(const char* str, size_t length) { - if (str == NULL) { - return NULL; - } else { - char* const clone = new char[length + 1]; - posix::StrNCpy(clone, str, length); - clone[length] = '\0'; - return clone; - } -} - -// Clones a 0-terminated C string, allocating memory using new. The -// caller is responsible for deleting[] the return value. Returns the -// cloned string, or NULL if the input is NULL. -const char * String::CloneCString(const char* c_str) { - return (c_str == NULL) ? - NULL : CloneString(c_str, strlen(c_str)); -} +// class String. #if GTEST_OS_WINDOWS_MOBILE // Creates a UTF-16 wide string from the given ANSI string, allocating @@ -906,11 +885,6 @@ bool String::CStringEquals(const char * lhs, const char * rhs) { // encoding, and streams the result to the given Message object. static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, Message* msg) { - // TODO(wan): consider allowing a testing::String object to - // contain '\0'. This will make it behave more like std::string, - // and will allow ToUtf8String() to return the correct encoding - // for '\0' s.t. we can get rid of the conditional here (and in - // several other places). for (size_t i = 0; i != length; ) { // NOLINT if (wstr[i] != L'\0') { *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); @@ -927,6 +901,26 @@ static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, } // namespace internal +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} + #if GTEST_HAS_STD_WSTRING // Converts the given wide string to a narrow string using the UTF-8 // encoding, and streams the result to this Message object. @@ -945,6 +939,12 @@ Message& Message::operator <<(const ::wstring& wstr) { } #endif // GTEST_HAS_GLOBAL_WSTRING +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + // AssertionResult constructors. // Used in EXPECT_TRUE/FALSE(assertion_result). AssertionResult::AssertionResult(const AssertionResult& other) @@ -954,6 +954,13 @@ AssertionResult::AssertionResult(const AssertionResult& other) static_cast< ::std::string*>(NULL)) { } +// Swaps two AssertionResults. +void AssertionResult::swap(AssertionResult& other) { + using std::swap; + swap(success_, other.success_); + swap(message_, other.message_); +} + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. AssertionResult AssertionResult::operator!() const { AssertionResult negation(!success_); @@ -980,6 +987,276 @@ AssertionResult AssertionFailure(const Message& message) { namespace internal { +namespace edit_distance { +std::vector CalculateOptimalEdits(const std::vector& left, + const std::vector& right) { + std::vector > costs( + left.size() + 1, std::vector(right.size() + 1)); + std::vector > best_move( + left.size() + 1, std::vector(right.size() + 1)); + + // Populate for empty right. + for (size_t l_i = 0; l_i < costs.size(); ++l_i) { + costs[l_i][0] = static_cast(l_i); + best_move[l_i][0] = kRemove; + } + // Populate for empty left. + for (size_t r_i = 1; r_i < costs[0].size(); ++r_i) { + costs[0][r_i] = static_cast(r_i); + best_move[0][r_i] = kAdd; + } + + for (size_t l_i = 0; l_i < left.size(); ++l_i) { + for (size_t r_i = 0; r_i < right.size(); ++r_i) { + if (left[l_i] == right[r_i]) { + // Found a match. Consume it. + costs[l_i + 1][r_i + 1] = costs[l_i][r_i]; + best_move[l_i + 1][r_i + 1] = kMatch; + continue; + } + + const double add = costs[l_i + 1][r_i]; + const double remove = costs[l_i][r_i + 1]; + const double replace = costs[l_i][r_i]; + if (add < remove && add < replace) { + costs[l_i + 1][r_i + 1] = add + 1; + best_move[l_i + 1][r_i + 1] = kAdd; + } else if (remove < add && remove < replace) { + costs[l_i + 1][r_i + 1] = remove + 1; + best_move[l_i + 1][r_i + 1] = kRemove; + } else { + // We make replace a little more expensive than add/remove to lower + // their priority. + costs[l_i + 1][r_i + 1] = replace + 1.00001; + best_move[l_i + 1][r_i + 1] = kReplace; + } + } + } + + // Reconstruct the best path. We do it in reverse order. + std::vector best_path; + for (size_t l_i = left.size(), r_i = right.size(); l_i > 0 || r_i > 0;) { + EditType move = best_move[l_i][r_i]; + best_path.push_back(move); + l_i -= move != kAdd; + r_i -= move != kRemove; + } + std::reverse(best_path.begin(), best_path.end()); + return best_path; +} + +namespace { + +// Helper class to convert string into ids with deduplication. +class InternalStrings { + public: + size_t GetId(const std::string& str) { + IdMap::iterator it = ids_.find(str); + if (it != ids_.end()) return it->second; + size_t id = ids_.size(); + return ids_[str] = id; + } + + private: + typedef std::map IdMap; + IdMap ids_; +}; + +} // namespace + +std::vector CalculateOptimalEdits( + const std::vector& left, + const std::vector& right) { + std::vector left_ids, right_ids; + { + InternalStrings intern_table; + for (size_t i = 0; i < left.size(); ++i) { + left_ids.push_back(intern_table.GetId(left[i])); + } + for (size_t i = 0; i < right.size(); ++i) { + right_ids.push_back(intern_table.GetId(right[i])); + } + } + return CalculateOptimalEdits(left_ids, right_ids); +} + +namespace { + +// Helper class that holds the state for one hunk and prints it out to the +// stream. +// It reorders adds/removes when possible to group all removes before all +// adds. It also adds the hunk header before printint into the stream. +class Hunk { + public: + Hunk(size_t left_start, size_t right_start) + : left_start_(left_start), + right_start_(right_start), + adds_(), + removes_(), + common_() {} + + void PushLine(char edit, const char* line) { + switch (edit) { + case ' ': + ++common_; + FlushEdits(); + hunk_.push_back(std::make_pair(' ', line)); + break; + case '-': + ++removes_; + hunk_removes_.push_back(std::make_pair('-', line)); + break; + case '+': + ++adds_; + hunk_adds_.push_back(std::make_pair('+', line)); + break; + } + } + + void PrintTo(std::ostream* os) { + PrintHeader(os); + FlushEdits(); + for (std::list >::const_iterator it = + hunk_.begin(); + it != hunk_.end(); ++it) { + *os << it->first << it->second << "\n"; + } + } + + bool has_edits() const { return adds_ || removes_; } + + private: + void FlushEdits() { + hunk_.splice(hunk_.end(), hunk_removes_); + hunk_.splice(hunk_.end(), hunk_adds_); + } + + // Print a unified diff header for one hunk. + // The format is + // "@@ -, +, @@" + // where the left/right parts are ommitted if unnecessary. + void PrintHeader(std::ostream* ss) const { + *ss << "@@ "; + if (removes_) { + *ss << "-" << left_start_ << "," << (removes_ + common_); + } + if (removes_ && adds_) { + *ss << " "; + } + if (adds_) { + *ss << "+" << right_start_ << "," << (adds_ + common_); + } + *ss << " @@\n"; + } + + size_t left_start_, right_start_; + size_t adds_, removes_, common_; + std::list > hunk_, hunk_adds_, hunk_removes_; +}; + +} // namespace + +// Create a list of diff hunks in Unified diff format. +// Each hunk has a header generated by PrintHeader above plus a body with +// lines prefixed with ' ' for no change, '-' for deletion and '+' for +// addition. +// 'context' represents the desired unchanged prefix/suffix around the diff. +// If two hunks are close enough that their contexts overlap, then they are +// joined into one hunk. +std::string CreateUnifiedDiff(const std::vector& left, + const std::vector& right, + size_t context) { + const std::vector edits = CalculateOptimalEdits(left, right); + + size_t l_i = 0, r_i = 0, edit_i = 0; + std::stringstream ss; + while (edit_i < edits.size()) { + // Find first edit. + while (edit_i < edits.size() && edits[edit_i] == kMatch) { + ++l_i; + ++r_i; + ++edit_i; + } + + // Find the first line to include in the hunk. + const size_t prefix_context = std::min(l_i, context); + Hunk hunk(l_i - prefix_context + 1, r_i - prefix_context + 1); + for (size_t i = prefix_context; i > 0; --i) { + hunk.PushLine(' ', left[l_i - i].c_str()); + } + + // Iterate the edits until we found enough suffix for the hunk or the input + // is over. + size_t n_suffix = 0; + for (; edit_i < edits.size(); ++edit_i) { + if (n_suffix >= context) { + // Continue only if the next hunk is very close. + std::vector::const_iterator it = edits.begin() + edit_i; + while (it != edits.end() && *it == kMatch) ++it; + if (it == edits.end() || (it - edits.begin()) - edit_i >= context) { + // There is no next edit or it is too far away. + break; + } + } + + EditType edit = edits[edit_i]; + // Reset count when a non match is found. + n_suffix = edit == kMatch ? n_suffix + 1 : 0; + + if (edit == kMatch || edit == kRemove || edit == kReplace) { + hunk.PushLine(edit == kMatch ? ' ' : '-', left[l_i].c_str()); + } + if (edit == kAdd || edit == kReplace) { + hunk.PushLine('+', right[r_i].c_str()); + } + + // Advance indices, depending on edit type. + l_i += edit != kAdd; + r_i += edit != kRemove; + } + + if (!hunk.has_edits()) { + // We are done. We don't want this hunk. + break; + } + + hunk.PrintTo(&ss); + } + return ss.str(); +} + +} // namespace edit_distance + +namespace { + +// The string representation of the values received in EqFailure() are already +// escaped. Split them on escaped '\n' boundaries. Leave all other escaped +// characters the same. +std::vector SplitEscapedString(const std::string& str) { + std::vector lines; + size_t start = 0, end = str.size(); + if (end > 2 && str[0] == '"' && str[end - 1] == '"') { + ++start; + --end; + } + bool escaped = false; + for (size_t i = start; i + 1 < end; ++i) { + if (escaped) { + escaped = false; + if (str[i] == 'n') { + lines.push_back(str.substr(start, i - start - 1)); + start = i + 1; + } + } else { + escaped = str[i] == '\\'; + } + } + lines.push_back(str.substr(start, end - start)); + return lines; +} + +} // namespace + // Constructs and returns the message for an equality assertion // (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. // @@ -997,8 +1274,8 @@ namespace internal { // be inserted into the message. AssertionResult EqFailure(const char* expected_expression, const char* actual_expression, - const String& expected_value, - const String& actual_value, + const std::string& expected_value, + const std::string& actual_value, bool ignoring_case) { Message msg; msg << "Value of: " << actual_expression; @@ -1014,14 +1291,26 @@ AssertionResult EqFailure(const char* expected_expression, msg << "\nWhich is: " << expected_value; } + if (!expected_value.empty() && !actual_value.empty()) { + const std::vector expected_lines = + SplitEscapedString(expected_value); + const std::vector actual_lines = + SplitEscapedString(actual_value); + if (expected_lines.size() > 1 || actual_lines.size() > 1) { + msg << "\nWith diff:\n" + << edit_distance::CreateUnifiedDiff(expected_lines, actual_lines); + } + } + return AssertionFailure() << msg; } // Constructs a failure message for Boolean assertions such as EXPECT_TRUE. -String GetBoolAssertionFailureMessage(const AssertionResult& assertion_result, - const char* expression_text, - const char* actual_predicate_value, - const char* expected_predicate_value) { +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { const char* actual_message = assertion_result.message(); Message msg; msg << "Value of: " << expression_text @@ -1168,8 +1457,8 @@ AssertionResult CmpHelperSTREQ(const char* expected_expression, return EqFailure(expected_expression, actual_expression, - String::ShowCStringQuoted(expected), - String::ShowCStringQuoted(actual), + PrintToString(expected), + PrintToString(actual), false); } @@ -1184,8 +1473,8 @@ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, return EqFailure(expected_expression, actual_expression, - String::ShowCStringQuoted(expected), - String::ShowCStringQuoted(actual), + PrintToString(expected), + PrintToString(actual), true); } @@ -1349,7 +1638,7 @@ AssertionResult HRESULTFailureHelper(const char* expr, // want inserts expanded. const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; - const DWORD kBufSize = 4096; // String::Format can't exceed this length. + const DWORD kBufSize = 4096; // Gets the system's human readable message string for this HRESULT. char error_text[kBufSize] = { '\0' }; DWORD message_length = ::FormatMessageA(kFlags, @@ -1359,7 +1648,7 @@ AssertionResult HRESULTFailureHelper(const char* expr, error_text, // output buffer kBufSize, // buf size NULL); // no arguments for inserts - // Trims tailing white space (FormatMessage leaves a trailing cr-lf) + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) for (; message_length && IsSpace(error_text[message_length - 1]); --message_length) { error_text[message_length - 1] = '\0'; @@ -1367,10 +1656,10 @@ AssertionResult HRESULTFailureHelper(const char* expr, # endif // GTEST_OS_WINDOWS_MOBILE - const String error_hex(String::Format("0x%08X ", hr)); + const std::string error_hex("0x" + String::FormatHexInt(hr)); return ::testing::AssertionFailure() << "Expected: " << expr << " " << expected << ".\n" - << " Actual: " << error_hex << error_text << "\n"; + << " Actual: " << error_hex << " " << error_text << "\n"; } } // namespace @@ -1427,12 +1716,15 @@ inline UInt32 ChopLowBits(UInt32* bits, int n) { // Converts a Unicode code point to a narrow string in UTF-8 encoding. // code_point parameter is of type UInt32 because wchar_t may not be // wide enough to contain a code point. -// The output buffer str must containt at least 32 characters. -// The function returns the address of the output buffer. // If the code_point is not a valid Unicode code point -// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. -char* CodePointToUtf8(UInt32 code_point, char* str) { +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(UInt32 code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. if (code_point <= kMaxCodePoint1) { str[1] = '\0'; str[0] = static_cast(code_point); // 0xxxxxxx @@ -1445,22 +1737,12 @@ char* CodePointToUtf8(UInt32 code_point, char* str) { str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[0] = static_cast(0xE0 | code_point); // 1110xxxx - } else if (code_point <= kMaxCodePoint4) { + } else { // code_point <= kMaxCodePoint4 str[4] = '\0'; str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[0] = static_cast(0xF0 | code_point); // 11110xxx - } else { - // The longest string String::Format can produce when invoked - // with these parameters is 28 character long (not including - // the terminating nul character). We are asking for 32 character - // buffer just in case. This is also enough for strncpy to - // null-terminate the destination string. - posix::StrNCpy( - str, String::Format("(Invalid Unicode 0x%X)", code_point).c_str(), 32); - str[31] = '\0'; // Makes sure no change in the format to strncpy leaves - // the result unterminated. } return str; } @@ -1501,7 +1783,7 @@ inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, // as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding // and contains invalid UTF-16 surrogate pairs, values in those pairs // will be encoded as individual Unicode characters from Basic Normal Plane. -String WideStringToUtf8(const wchar_t* str, int num_chars) { +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { if (num_chars == -1) num_chars = static_cast(wcslen(str)); @@ -1519,27 +1801,17 @@ String WideStringToUtf8(const wchar_t* str, int num_chars) { unicode_code_point = static_cast(str[i]); } - char buffer[32]; // CodePointToUtf8 requires a buffer this big. - stream << CodePointToUtf8(unicode_code_point, buffer); + stream << CodePointToUtf8(unicode_code_point); } return StringStreamToString(&stream); } -// Converts a wide C string to a String using the UTF-8 encoding. +// Converts a wide C string to an std::string using the UTF-8 encoding. // NULL will be converted to "(null)". -String String::ShowWideCString(const wchar_t * wide_c_str) { - if (wide_c_str == NULL) return String("(null)"); +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return "(null)"; - return String(internal::WideStringToUtf8(wide_c_str, -1).c_str()); -} - -// Similar to ShowWideCString(), except that this function encloses -// the converted string in double quotes. -String String::ShowWideCStringQuoted(const wchar_t* wide_c_str) { - if (wide_c_str == NULL) return String("(null)"); - - return String::Format("L\"%s\"", - String::ShowWideCString(wide_c_str).c_str()); + return internal::WideStringToUtf8(wide_c_str, -1); } // Compares two wide C strings. Returns true iff they have the same @@ -1567,8 +1839,8 @@ AssertionResult CmpHelperSTREQ(const char* expected_expression, return EqFailure(expected_expression, actual_expression, - String::ShowWideCStringQuoted(expected), - String::ShowWideCStringQuoted(actual), + PrintToString(expected), + PrintToString(actual), false); } @@ -1583,8 +1855,8 @@ AssertionResult CmpHelperSTRNE(const char* s1_expression, return AssertionFailure() << "Expected: (" << s1_expression << ") != (" << s2_expression << "), actual: " - << String::ShowWideCStringQuoted(s1) - << " vs " << String::ShowWideCStringQuoted(s2); + << PrintToString(s1) + << " vs " << PrintToString(s2); } // Compares two C strings, ignoring case. Returns true iff they have @@ -1635,135 +1907,69 @@ bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, #endif // OS selector } -// Compares this with another String. -// Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 -// if this is greater than rhs. -int String::Compare(const String & rhs) const { - const char* const lhs_c_str = c_str(); - const char* const rhs_c_str = rhs.c_str(); - - if (lhs_c_str == NULL) { - return rhs_c_str == NULL ? 0 : -1; // NULL < anything except NULL - } else if (rhs_c_str == NULL) { - return 1; - } - - const size_t shorter_str_len = - length() <= rhs.length() ? length() : rhs.length(); - for (size_t i = 0; i != shorter_str_len; i++) { - if (lhs_c_str[i] < rhs_c_str[i]) { - return -1; - } else if (lhs_c_str[i] > rhs_c_str[i]) { - return 1; - } - } - return (length() < rhs.length()) ? -1 : - (length() > rhs.length()) ? 1 : 0; +// Returns true iff str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); } -// Returns true iff this String ends with the given suffix. *Any* -// String is considered to end with a NULL or empty suffix. -bool String::EndsWith(const char* suffix) const { - if (suffix == NULL || CStringEquals(suffix, "")) return true; - - if (c_str() == NULL) return false; - - const size_t this_len = strlen(c_str()); - const size_t suffix_len = strlen(suffix); - return (this_len >= suffix_len) && - CStringEquals(c_str() + this_len - suffix_len, suffix); +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << value; + return ss.str(); } -// Returns true iff this String ends with the given suffix, ignoring case. -// Any String is considered to end with a NULL or empty suffix. -bool String::EndsWithCaseInsensitive(const char* suffix) const { - if (suffix == NULL || CStringEquals(suffix, "")) return true; - - if (c_str() == NULL) return false; - - const size_t this_len = strlen(c_str()); - const size_t suffix_len = strlen(suffix); - return (this_len >= suffix_len) && - CaseInsensitiveCStringEquals(c_str() + this_len - suffix_len, suffix); +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); } -// Formats a list of arguments to a String, using the same format -// spec string as for printf. -// -// We do not use the StringPrintf class as it is not universally -// available. -// -// The result is limited to 4096 characters (including the tailing 0). -// If 4096 characters are not enough to format the input, or if -// there's an error, "" is -// returned. -String String::Format(const char * format, ...) { - va_list args; - va_start(args, format); - - char buffer[4096]; - const int kBufferSize = sizeof(buffer)/sizeof(buffer[0]); - - // MSVC 8 deprecates vsnprintf(), so we want to suppress warning - // 4996 (deprecated function) there. -#ifdef _MSC_VER // We are using MSVC. -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4996) // Temporarily disables warning 4996. - - const int size = vsnprintf(buffer, kBufferSize, format, args); - -# pragma warning(pop) // Restores the warning state. -#else // We are not using MSVC. - const int size = vsnprintf(buffer, kBufferSize, format, args); -#endif // _MSC_VER - va_end(args); - - // vsnprintf()'s behavior is not portable. When the buffer is not - // big enough, it returns a negative value in MSVC, and returns the - // needed buffer size on Linux. When there is an output error, it - // always returns a negative value. For simplicity, we lump the two - // error cases together. - if (size < 0 || size >= kBufferSize) { - return String(""); - } else { - return String(buffer, size); - } +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast(value); + return ss.str(); } -// Converts the buffer in a stringstream to a String, converting NUL +// Converts the buffer in a stringstream to an std::string, converting NUL // bytes to "\\0" along the way. -String StringStreamToString(::std::stringstream* ss) { +std::string StringStreamToString(::std::stringstream* ss) { const ::std::string& str = ss->str(); const char* const start = str.c_str(); const char* const end = start + str.length(); - // We need to use a helper stringstream to do this transformation - // because String doesn't support push_back(). - ::std::stringstream helper; + std::string result; + result.reserve(2 * (end - start)); for (const char* ch = start; ch != end; ++ch) { if (*ch == '\0') { - helper << "\\0"; // Replaces NUL with "\\0"; + result += "\\0"; // Replaces NUL with "\\0"; } else { - helper.put(*ch); + result += *ch; } } - return String(helper.str().c_str()); + return result; } // Appends the user-supplied message to the Google-Test-generated message. -String AppendUserMessage(const String& gtest_msg, - const Message& user_msg) { +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { // Appends the user message if it's non-empty. - const String user_msg_string = user_msg.GetString(); + const std::string user_msg_string = user_msg.GetString(); if (user_msg_string.empty()) { return gtest_msg; } - Message msg; - msg << gtest_msg << "\n" << user_msg_string; - - return msg.GetString(); + return gtest_msg + "\n" + user_msg_string; } } // namespace internal @@ -1811,8 +2017,9 @@ void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { // Adds a test property to the list. If a property with the same key as the // supplied property is already represented, the value of this test_property // replaces the old value for that key. -void TestResult::RecordProperty(const TestProperty& test_property) { - if (!ValidateTestProperty(test_property)) { +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { return; } internal::MutexLock lock(&test_properites_mutex_); @@ -1826,21 +2033,94 @@ void TestResult::RecordProperty(const TestProperty& test_property) { property_with_matching_key->SetValue(test_property.value()); } -// Adds a failure if the key is a reserved attribute of Google Test -// testcase tags. Returns true if the property is valid. -bool TestResult::ValidateTestProperty(const TestProperty& test_property) { - internal::String key(test_property.key()); - if (key == "name" || key == "status" || key == "time" || key == "classname") { - ADD_FAILURE() - << "Reserved key used in RecordProperty(): " - << key - << " ('name', 'status', 'time', and 'classname' are reserved by " - << GTEST_NAME_ << ")"; +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "tests", + "time" +}; + +// The list of reserved attributes used in the element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", + "name", + "status", + "time", + "type_param", + "value_param" +}; + +template +std::vector ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector(array, array + kSize); +} + +static std::vector GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector(); +} + +static std::string FormatWordList(const std::vector& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +bool ValidateTestPropertyName(const std::string& property_name, + const std::vector& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; return false; } return true; } +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + // Clears the object. void TestResult::Clear() { test_part_results_.clear(); @@ -1916,12 +2196,12 @@ void Test::TearDown() { } // Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const char* key, const char* value) { - UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value); +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); } // Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const char* key, int value) { +void Test::RecordProperty(const std::string& key, int value) { Message value_message; value_message << value; RecordProperty(key, value_message.GetString().c_str()); @@ -1930,7 +2210,7 @@ void Test::RecordProperty(const char* key, int value) { namespace internal { void ReportFailureInUnknownLocation(TestPartResult::Type result_type, - const String& message) { + const std::string& message) { // This function is a friend of UnitTest and as such has access to // AddTestPartResult. UnitTest::GetInstance()->AddTestPartResult( @@ -1938,7 +2218,7 @@ void ReportFailureInUnknownLocation(TestPartResult::Type result_type, NULL, // No info about the source file where the exception occurred. -1, // We have no info on which line caused the exception. message, - String()); // No stack trace, either. + ""); // No stack trace, either. } } // namespace internal @@ -1969,8 +2249,8 @@ bool Test::HasSameFixtureClass() { const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); if (first_is_TEST || this_is_TEST) { - // The user mixed TEST and TEST_F in this test case - we'll tell - // him/her how to fix it. + // Both TEST and TEST_F appear in same test case, which is incorrect. + // Tell the user how to fix this. // Gets the name of the TEST and the name of the TEST_F. Note // that first_is_TEST and this_is_TEST cannot both be true, as @@ -1990,8 +2270,8 @@ bool Test::HasSameFixtureClass() { << "want to change the TEST to TEST_F or move it to another test\n" << "case."; } else { - // The user defined two fixture classes with the same name in - // two namespaces - we'll tell him/her how to fix it. + // Two fixture classes with the same name appear in two different + // namespaces, which is not allowed. Tell the user how to fix this. ADD_FAILURE() << "All tests in the same test case must use the same test fixture\n" << "class. However, in test case " @@ -2015,22 +2295,24 @@ bool Test::HasSameFixtureClass() { // function returns its result via an output parameter pointer because VC++ // prohibits creation of objects with destructors on stack in functions // using __try (see error C2712). -static internal::String* FormatSehExceptionMessage(DWORD exception_code, - const char* location) { +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { Message message; message << "SEH exception with code 0x" << std::setbase(16) << exception_code << std::setbase(10) << " thrown in " << location << "."; - return new internal::String(message.GetString()); + return new std::string(message.GetString()); } #endif // GTEST_HAS_SEH +namespace internal { + #if GTEST_HAS_EXCEPTIONS // Adds an "exception thrown" fatal failure to the current test. -static internal::String FormatCxxExceptionMessage(const char* description, - const char* location) { +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { Message message; if (description != NULL) { message << "C++ exception with description \"" << description << "\""; @@ -2042,23 +2324,15 @@ static internal::String FormatCxxExceptionMessage(const char* description, return message.GetString(); } -static internal::String PrintTestPartResultToString( +static std::string PrintTestPartResultToString( const TestPartResult& test_part_result); -// A failed Google Test assertion will throw an exception of this type when -// GTEST_FLAG(throw_on_failure) is true (if exceptions are enabled). We -// derive it from std::runtime_error, which is for errors presumably -// detectable only at run time. Since std::runtime_error inherits from -// std::exception, many testing frameworks know how to extract and print the -// message inside it. -class GoogleTestFailureException : public ::std::runtime_error { - public: - explicit GoogleTestFailureException(const TestPartResult& failure) - : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} -}; +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + #endif // GTEST_HAS_EXCEPTIONS -namespace internal { // We put these helper functions in the internal namespace as IBM's xlC // compiler rejects the code if they were declared static. @@ -2078,7 +2352,7 @@ Result HandleSehExceptionsInMethodIfSupported( // We create the exception message on the heap because VC++ prohibits // creation of objects with destructors on stack in functions using __try // (see error C2712). - internal::String* exception_message = FormatSehExceptionMessage( + std::string* exception_message = FormatSehExceptionMessage( GetExceptionCode(), location); internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, *exception_message); @@ -2124,9 +2398,10 @@ Result HandleExceptionsInMethodIfSupported( #if GTEST_HAS_EXCEPTIONS try { return HandleSehExceptionsInMethodIfSupported(object, method, location); - } catch (const GoogleTestFailureException&) { // NOLINT - // This exception doesn't originate in code under test. It makes no - // sense to report it as a test failure. + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. throw; } catch (const std::exception& e) { // NOLINT internal::ReportFailureInUnknownLocation( @@ -2185,10 +2460,8 @@ bool Test::HasNonfatalFailure() { // Constructs a TestInfo object. It assumes ownership of the test factory // object. -// TODO(vladl@google.com): Make a_test_case_name and a_name const string&'s -// to signify they cannot be NULLs. -TestInfo::TestInfo(const char* a_test_case_name, - const char* a_name, +TestInfo::TestInfo(const std::string& a_test_case_name, + const std::string& a_name, const char* a_type_param, const char* a_value_param, internal::TypeId fixture_class_id, @@ -2227,7 +2500,8 @@ namespace internal { // The newly created TestInfo instance will assume // ownership of the factory object. TestInfo* MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, + const char* test_case_name, + const char* name, const char* type_param, const char* value_param, TypeId fixture_class_id, @@ -2282,11 +2556,11 @@ class TestNameIs { // Returns true iff the test name of test_info matches name_. bool operator()(const TestInfo * test_info) const { - return test_info && internal::String(test_info->name()).Compare(name_) == 0; + return test_info && test_info->name() == name_; } private: - internal::String name_; + std::string name_; }; } // namespace @@ -2365,10 +2639,21 @@ int TestCase::failed_test_count() const { return CountIf(test_info_list_, TestFailed); } +// Gets the number of disabled tests that will be reported in the XML report. +int TestCase::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test case. int TestCase::disabled_test_count() const { return CountIf(test_info_list_, TestDisabled); } +// Gets the number of tests to be printed in the XML report. +int TestCase::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + // Get the number of tests in this test case that should run. int TestCase::test_to_run_count() const { return CountIf(test_info_list_, ShouldRunTest); @@ -2456,6 +2741,7 @@ void TestCase::Run() { // Clears the results of all tests in this test case. void TestCase::ClearResult() { + ad_hoc_test_result_.Clear(); ForEach(test_info_list_, TestInfo::ClearTestResult); } @@ -2476,20 +2762,20 @@ void TestCase::UnshuffleTests() { // // FormatCountableNoun(1, "formula", "formuli") returns "1 formula". // FormatCountableNoun(5, "book", "books") returns "5 books". -static internal::String FormatCountableNoun(int count, - const char * singular_form, - const char * plural_form) { - return internal::String::Format("%d %s", count, - count == 1 ? singular_form : plural_form); +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); } // Formats the count of tests. -static internal::String FormatTestCount(int test_count) { +static std::string FormatTestCount(int test_count) { return FormatCountableNoun(test_count, "test", "tests"); } // Formats the count of test cases. -static internal::String FormatTestCaseCount(int test_case_count) { +static std::string FormatTestCaseCount(int test_case_count) { return FormatCountableNoun(test_case_count, "test case", "test cases"); } @@ -2514,8 +2800,10 @@ static const char * TestPartResultTypeToString(TestPartResult::Type type) { } } -// Prints a TestPartResult to a String. -static internal::String PrintTestPartResultToString( +namespace internal { + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( const TestPartResult& test_part_result) { return (Message() << internal::FormatFileLocation(test_part_result.file_name(), @@ -2526,7 +2814,7 @@ static internal::String PrintTestPartResultToString( // Prints a TestPartResult. static void PrintTestPartResult(const TestPartResult& test_part_result) { - const internal::String& result = + const std::string& result = PrintTestPartResultToString(test_part_result); printf("%s\n", result.c_str()); fflush(stdout); @@ -2545,8 +2833,6 @@ static void PrintTestPartResult(const TestPartResult& test_part_result) { // class PrettyUnitTestResultPrinter -namespace internal { - enum GTestColor { COLOR_DEFAULT, COLOR_RED, @@ -2554,7 +2840,8 @@ enum GTestColor { COLOR_YELLOW }; -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT // Returns the character attribute for the given color. WORD GetColorAttribute(GTestColor color) { @@ -2598,6 +2885,7 @@ bool ShouldUseColor(bool stdout_is_tty) { String::CStringEquals(term, "xterm-color") || String::CStringEquals(term, "xterm-256color") || String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || String::CStringEquals(term, "linux") || String::CStringEquals(term, "cygwin"); return stdout_is_tty && term_supports_color; @@ -2621,8 +2909,9 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { va_list args; va_start(args, fmt); -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS - const bool use_color = false; +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || \ + GTEST_OS_IOS || GTEST_OS_WINDOWS_PHONE || GTEST_OS_WINDOWS_RT + const bool use_color = AlwaysFalse(); #else static const bool in_color_mode = ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); @@ -2636,7 +2925,8 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { return; } -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE && \ + !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // Gets the current text color. @@ -2663,6 +2953,11 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { va_end(args); } +// Text printed in Google Test's text output and --gunit_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + void PrintFullTestCommentIfPresent(const TestInfo& test_info) { const char* const type_param = test_info.type_param(); const char* const value_param = test_info.value_param(); @@ -2670,12 +2965,12 @@ void PrintFullTestCommentIfPresent(const TestInfo& test_info) { if (type_param != NULL || value_param != NULL) { printf(", where "); if (type_param != NULL) { - printf("TypeParam = %s", type_param); + printf("%s = %s", kTypeParamLabel, type_param); if (value_param != NULL) printf(" and "); } if (value_param != NULL) { - printf("GetParam() = %s", value_param); + printf("%s = %s", kValueParamLabel, value_param); } } } @@ -2707,8 +3002,6 @@ class PrettyUnitTestResultPrinter : public TestEventListener { private: static void PrintFailedTests(const UnitTest& unit_test); - - internal::String test_case_name_; }; // Fired before each iteration of tests starts. @@ -2721,7 +3014,7 @@ void PrettyUnitTestResultPrinter::OnTestIterationStart( // Prints the filter if it's not *. This reminds the user that some // tests may be skipped. - if (!internal::String::CStringEquals(filter, kUniversalFilter)) { + if (!String::CStringEquals(filter, kUniversalFilter)) { ColoredPrintf(COLOR_YELLOW, "Note: %s filter = %s\n", GTEST_NAME_, filter); } @@ -2755,22 +3048,21 @@ void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( } void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { - test_case_name_ = test_case.name(); - const internal::String counts = + const std::string counts = FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("%s from %s", counts.c_str(), test_case_name_.c_str()); + printf("%s from %s", counts.c_str(), test_case.name()); if (test_case.type_param() == NULL) { printf("\n"); } else { - printf(", where TypeParam = %s\n", test_case.type_param()); + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); } fflush(stdout); } void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { ColoredPrintf(COLOR_GREEN, "[ RUN ] "); - PrintTestName(test_case_name_.c_str(), test_info.name()); + PrintTestName(test_info.test_case_name(), test_info.name()); printf("\n"); fflush(stdout); } @@ -2793,7 +3085,7 @@ void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { } else { ColoredPrintf(COLOR_RED, "[ FAILED ] "); } - PrintTestName(test_case_name_.c_str(), test_info.name()); + PrintTestName(test_info.test_case_name(), test_info.name()); if (test_info.result()->Failed()) PrintFullTestCommentIfPresent(test_info); @@ -2809,12 +3101,11 @@ void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { if (!GTEST_FLAG(print_time)) return; - test_case_name_ = test_case.name(); - const internal::String counts = + const std::string counts = FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); ColoredPrintf(COLOR_GREEN, "[----------] "); printf("%s from %s (%s ms total)\n\n", - counts.c_str(), test_case_name_.c_str(), + counts.c_str(), test_case.name(), internal::StreamableToString(test_case.elapsed_time()).c_str()); fflush(stdout); } @@ -2875,7 +3166,7 @@ void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, num_failures == 1 ? "TEST" : "TESTS"); } - int num_disabled = unit_test.disabled_test_count(); + int num_disabled = unit_test.reportable_disabled_test_count(); if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { if (!num_failures) { printf("\n"); // Add a spacer if no FAILURE banner is displayed. @@ -3029,18 +3320,27 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { // is_attribute is true, the text is meant to appear as an attribute // value, and normalizable whitespace is preserved by replacing it // with character references. - static String EscapeXml(const char* str, bool is_attribute); + static std::string EscapeXml(const std::string& str, bool is_attribute); // Returns the given string with all characters invalid in XML removed. - static string RemoveInvalidXmlCharacters(const string& str); + static std::string RemoveInvalidXmlCharacters(const std::string& str); // Convenience wrapper around EscapeXml when str is an attribute value. - static String EscapeXmlAttribute(const char* str) { + static std::string EscapeXmlAttribute(const std::string& str) { return EscapeXml(str, true); } // Convenience wrapper around EscapeXml when str is not an attribute value. - static String EscapeXmlText(const char* str) { return EscapeXml(str, false); } + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. static void OutputXmlCDataSection(::std::ostream* stream, const char* data); @@ -3051,19 +3351,21 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { const TestInfo& test_info); // Prints an XML representation of a TestCase object - static void PrintXmlTestCase(FILE* out, const TestCase& test_case); + static void PrintXmlTestCase(::std::ostream* stream, + const TestCase& test_case); // Prints an XML summary of unit_test to output stream out. - static void PrintXmlUnitTest(FILE* out, const UnitTest& unit_test); + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); // Produces a string representing the test properties in a result as space // delimited XML attributes based on the property key="value" pairs. - // When the String is not empty, it includes a space at the beginning, + // When the std::string is not empty, it includes a space at the beginning, // to delimit this attribute from prior attributes. - static String TestPropertiesAsXmlAttributes(const TestResult& result); + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); // The output file. - const String output_file_; + const std::string output_file_; GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); }; @@ -3105,7 +3407,9 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, fflush(stderr); exit(EXIT_FAILURE); } - PrintXmlUnitTest(xmlout, unit_test); + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); fclose(xmlout); } @@ -3121,42 +3425,43 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, // most invalid characters can be retained using character references. // TODO(wan): It might be nice to have a minimally invasive, human-readable // escaping scheme for invalid characters, rather than dropping them. -String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) { +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { Message m; - if (str != NULL) { - for (const char* src = str; *src; ++src) { - switch (*src) { - case '<': - m << "<"; - break; - case '>': - m << ">"; - break; - case '&': - m << "&"; - break; - case '\'': - if (is_attribute) - m << "'"; + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast(ch)) + << ";"; else - m << '\''; - break; - case '"': - if (is_attribute) - m << """; - else - m << '"'; - break; - default: - if (IsValidXmlCharacter(*src)) { - if (is_attribute && IsNormalizableWhitespace(*src)) - m << String::Format("&#x%02X;", unsigned(*src)); - else - m << *src; - } - break; - } + m << ch; + } + break; } } @@ -3166,10 +3471,11 @@ String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) { // Returns the given string with all characters invalid in XML removed. // Currently invalid characters are dropped from the string. An // alternative is to replace them with certain characters such as . or ?. -string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const string& str) { - string output; +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; output.reserve(str.size()); - for (string::const_iterator it = str.begin(); it != str.end(); ++it) + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) if (IsValidXmlCharacter(*it)) output.push_back(*it); @@ -3199,6 +3505,37 @@ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { return ss.str(); } +static bool PortableLocaltime(time_t seconds, struct tm* out) { +#if defined(_MSC_VER) + return localtime_s(out, &seconds) == 0; +#elif defined(__MINGW32__) || defined(__MINGW64__) + // MINGW provides neither localtime_r nor localtime_s, but uses + // Windows' localtime(), which has a thread-local tm buffer. + struct tm* tm_ptr = localtime(&seconds); // NOLINT + if (tm_ptr == NULL) + return false; + *out = *tm_ptr; + return true; +#else + return localtime_r(&seconds, out) != NULL; +#endif +} + +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + struct tm time_struct; + if (!PortableLocaltime(static_cast(ms / 1000), &time_struct)) + return ""; + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct.tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct.tm_mday) + "T" + + String::FormatIntWidth2(time_struct.tm_hour) + ":" + + String::FormatIntWidth2(time_struct.tm_min) + ":" + + String::FormatIntWidth2(time_struct.tm_sec); +} + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, const char* data) { @@ -3219,48 +3556,63 @@ void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, *stream << "]]>"; } +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + // Prints an XML representation of a TestInfo object. // TODO(wan): There is also value in printing properties with the plain printer. void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, const char* test_case_name, const TestInfo& test_info) { - if (test_info.filtered_out ()) - return; - const TestResult& result = *test_info.result(); - *stream << " \n"; - *stream << " "; + } const string location = internal::FormatCompilerIndependentFileLocation( part.file_name(), part.line_number()); - const string message = location + "\n" + part.message(); - OutputXmlCDataSection(stream, - RemoveInvalidXmlCharacters(message).c_str()); + const string summary = location + "\n" + part.summary(); + *stream << " "; + const string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); *stream << "\n"; } } @@ -3272,52 +3624,73 @@ void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, } // Prints an XML representation of a TestCase object -void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out, +void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, const TestCase& test_case) { - if (test_case.should_skip_report ()) - return; + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_case.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_case.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_case.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_case.elapsed_time())); + *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) + << ">\n"; - fprintf(out, - " \n", - FormatTimeInMillisAsSeconds(test_case.elapsed_time()).c_str()); for (int i = 0; i < test_case.total_test_count(); ++i) { - ::std::stringstream stream; - OutputXmlTestInfo(&stream, test_case.name(), *test_case.GetTestInfo(i)); - fprintf(out, "%s", StringStreamToString(&stream).c_str()); + if (test_case.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); } - fprintf(out, " \n"); + *stream << " \n"; } // Prints an XML summary of unit_test to output stream out. -void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out, +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, const UnitTest& unit_test) { - fprintf(out, "\n"); - fprintf(out, - "\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + if (GTEST_FLAG(shuffle)) { - fprintf(out, "random_seed=\"%d\" ", unit_test.random_seed()); + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); } - fprintf(out, "name=\"AllTests\">\n"); - for (int i = 0; i < unit_test.total_test_case_count(); ++i) - PrintXmlTestCase(out, *unit_test.GetTestCase(i)); - fprintf(out, "\n"); + + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) + PrintXmlTestCase(stream, *unit_test.GetTestCase(i)); + } + *stream << "\n"; } // Produces a string representing the test properties in a result as space // delimited XML attributes based on the property key="value" pairs. -String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( const TestResult& result) { Message attributes; for (int i = 0; i < result.test_property_count(); ++i) { @@ -3332,112 +3705,6 @@ String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( #if GTEST_CAN_STREAM_RESULTS_ -// Streams test results to the given port on the given host machine. -class StreamingListener : public EmptyTestEventListener { - public: - // Escapes '=', '&', '%', and '\n' characters in str as "%xx". - static string UrlEncode(const char* str); - - StreamingListener(const string& host, const string& port) - : sockfd_(-1), host_name_(host), port_num_(port) { - MakeConnection(); - Send("gtest_streaming_protocol_version=1.0\n"); - } - - virtual ~StreamingListener() { - if (sockfd_ != -1) - CloseConnection(); - } - - void OnTestProgramStart(const UnitTest& /* unit_test */) { - Send("event=TestProgramStart\n"); - } - - void OnTestProgramEnd(const UnitTest& unit_test) { - // Note that Google Test current only report elapsed time for each - // test iteration, not for the entire test program. - Send(String::Format("event=TestProgramEnd&passed=%d\n", - unit_test.Passed())); - - // Notify the streaming server to stop. - CloseConnection(); - } - - void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { - Send(String::Format("event=TestIterationStart&iteration=%d\n", - iteration)); - } - - void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { - Send(String::Format("event=TestIterationEnd&passed=%d&elapsed_time=%sms\n", - unit_test.Passed(), - StreamableToString(unit_test.elapsed_time()).c_str())); - } - - void OnTestCaseStart(const TestCase& test_case) { - Send(String::Format("event=TestCaseStart&name=%s\n", test_case.name())); - } - - void OnTestCaseEnd(const TestCase& test_case) { - Send(String::Format("event=TestCaseEnd&passed=%d&elapsed_time=%sms\n", - test_case.Passed(), - StreamableToString(test_case.elapsed_time()).c_str())); - } - - void OnTestStart(const TestInfo& test_info) { - Send(String::Format("event=TestStart&name=%s\n", test_info.name())); - } - - void OnTestEnd(const TestInfo& test_info) { - Send(String::Format( - "event=TestEnd&passed=%d&elapsed_time=%sms\n", - (test_info.result())->Passed(), - StreamableToString((test_info.result())->elapsed_time()).c_str())); - } - - void OnTestPartResult(const TestPartResult& test_part_result) { - const char* file_name = test_part_result.file_name(); - if (file_name == NULL) - file_name = ""; - Send(String::Format("event=TestPartResult&file=%s&line=%d&message=", - UrlEncode(file_name).c_str(), - test_part_result.line_number())); - Send(UrlEncode(test_part_result.message()) + "\n"); - } - - private: - // Creates a client socket and connects to the server. - void MakeConnection(); - - // Closes the socket. - void CloseConnection() { - GTEST_CHECK_(sockfd_ != -1) - << "CloseConnection() can be called only when there is a connection."; - - close(sockfd_); - sockfd_ = -1; - } - - // Sends a string to the socket. - void Send(const string& message) { - GTEST_CHECK_(sockfd_ != -1) - << "Send() can be called only when there is a connection."; - - const int len = static_cast(message.length()); - if (write(sockfd_, message.c_str(), len) != len) { - GTEST_LOG_(WARNING) - << "stream_result_to: failed to stream to " - << host_name_ << ":" << port_num_; - } - } - - int sockfd_; // socket file descriptor - const string host_name_; - const string port_num_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); -}; // class StreamingListener - // Checks if str contains '=', '&', '%' or '\n' characters. If yes, // replaces them by "%xx" where xx is their hexadecimal value. For // example, replaces "=" with "%3D". This algorithm is O(strlen(str)) @@ -3452,7 +3719,7 @@ string StreamingListener::UrlEncode(const char* str) { case '=': case '&': case '\n': - result.append(String::Format("%%%02x", static_cast(ch))); + result.append("%" + String::FormatByte(static_cast(ch))); break; default: result.push_back(ch); @@ -3462,7 +3729,7 @@ string StreamingListener::UrlEncode(const char* str) { return result; } -void StreamingListener::MakeConnection() { +void StreamingListener::SocketWriter::MakeConnection() { GTEST_CHECK_(sockfd_ == -1) << "MakeConnection() can't be called when there is already a connection."; @@ -3510,8 +3777,8 @@ void StreamingListener::MakeConnection() { // Pushes the given source file location and message onto a per-thread // trace stack maintained by Google Test. -// L < UnitTest::mutex_ -ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { TraceInfo trace; trace.file = file; trace.line = line; @@ -3521,35 +3788,64 @@ ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { } // Pops the info pushed by the c'tor. -// L < UnitTest::mutex_ -ScopedTrace::~ScopedTrace() { +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { UnitTest::GetInstance()->PopGTestTrace(); } // class OsStackTraceGetter -// Returns the current OS stack trace as a String. Parameters: +// Returns the current OS stack trace as an std::string. Parameters: // // max_depth - the maximum number of stack frames to be included // in the trace. // skip_count - the number of top frames to be skipped; doesn't count // against max_depth. // -// L < mutex_ -// We use "L < mutex_" to denote that the function may acquire mutex_. -String OsStackTraceGetter::CurrentStackTrace(int, int) { - return String(""); +string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */, + int /* skip_count */) + GTEST_LOCK_EXCLUDED_(mutex_) { + return ""; } -// L < mutex_ -void OsStackTraceGetter::UponLeavingGTest() { +void OsStackTraceGetter::UponLeavingGTest() + GTEST_LOCK_EXCLUDED_(mutex_) { } const char* const OsStackTraceGetter::kElidedFramesMarker = "... " GTEST_NAME_ " internal frames ..."; +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath) { + // If a path to the premature-exit file is specified... + if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { + if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') { + remove(premature_exit_filepath_); + } + } + + private: + const char* const premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + } // namespace internal // class TestEventListeners @@ -3636,7 +3932,7 @@ void TestEventListeners::SuppressEventForwarding() { // We don't protect this under mutex_ as a user is not supposed to // call this before main() starts, from which point on the return // value will never change. -UnitTest * UnitTest::GetInstance() { +UnitTest* UnitTest::GetInstance() { // When compiled with MSVC 7.1 in optimized mode, destroying the // UnitTest object upon exiting the program messes up the exit code, // causing successful tests to appear failed. We have to use a @@ -3686,17 +3982,33 @@ int UnitTest::successful_test_count() const { // Gets the number of failed tests. int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + // Gets the number of disabled tests. int UnitTest::disabled_test_count() const { return impl()->disabled_test_count(); } +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + // Gets the number of all tests. int UnitTest::total_test_count() const { return impl()->total_test_count(); } // Gets the number of tests that should run. int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + // Gets the elapsed time, in milliseconds. internal::TimeInMillis UnitTest::elapsed_time() const { return impl()->elapsed_time(); @@ -3715,6 +4027,12 @@ const TestCase* UnitTest::GetTestCase(int i) const { return impl()->GetTestCase(i); } +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test cases. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + // Gets the i-th test case among all the test cases. i can range from 0 to // total_test_case_count() - 1. If i is not in that range, returns NULL. TestCase* UnitTest::GetMutableTestCase(int i) { @@ -3750,12 +4068,12 @@ Environment* UnitTest::AddEnvironment(Environment* env) { // assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call // this to report their results. The user code should use the // assertion macros instead of calling this directly. -// L < mutex_ -void UnitTest::AddTestPartResult(TestPartResult::Type result_type, - const char* file_name, - int line_number, - const internal::String& message, - const internal::String& os_stack_trace) { +void UnitTest::AddTestPartResult( + TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) { Message msg; msg << message; @@ -3788,7 +4106,7 @@ void UnitTest::AddTestPartResult(TestPartResult::Type result_type, // with another testing framework) and specify the former on the // command line for debugging. if (GTEST_FLAG(break_on_failure)) { -#if GTEST_OS_WINDOWS +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT // Using DebugBreak on Windows allows gtest to still break into a debugger // when a failure happens and both the --gtest_break_on_failure and // the --gtest_catch_exceptions flags are specified. @@ -3802,7 +4120,7 @@ void UnitTest::AddTestPartResult(TestPartResult::Type result_type, #endif // GTEST_OS_WINDOWS } else if (GTEST_FLAG(throw_on_failure)) { #if GTEST_HAS_EXCEPTIONS - throw GoogleTestFailureException(result); + throw internal::GoogleTestFailureException(result); #else // We cannot call abort() as it generates a pop-up in debug mode // that cannot be suppressed in VC 7.1 or below. @@ -3812,12 +4130,14 @@ void UnitTest::AddTestPartResult(TestPartResult::Type result_type, } } -// Creates and adds a property to the current TestResult. If a property matching -// the supplied value already exists, updates its value instead. -void UnitTest::RecordPropertyForCurrentTest(const char* key, - const char* value) { - const TestProperty test_property(key, value); - impl_->current_test_result()->RecordProperty(test_property); +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestCase's ad_hoc_test_result_ when invoked +// from SetUpTestCase or TearDownTestCase, or to the global property set +// when invoked elsewhere. If the result already contains a property with +// the same key, the value will be updated. +void UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); } // Runs all tests in this UnitTest object and prints the result. @@ -3826,21 +4146,45 @@ void UnitTest::RecordPropertyForCurrentTest(const char* key, // We don't protect this under mutex_, as we only support calling it // from the main thread. int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process ? + NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be // used for the duration of the program. impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); #if GTEST_HAS_SEH - const bool in_death_test_child_process = - internal::GTEST_FLAG(internal_run_death_test).length() > 0; - // Either the user wants Google Test to catch exceptions thrown by the // tests or this is executing in the context of death test child // process. In either case the user does not want to see pop-up dialogs // about crashes - they are expected. if (impl()->catch_exceptions() || in_death_test_child_process) { - -# if !GTEST_OS_WINDOWS_MOBILE +# if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT // SetErrorMode doesn't exist on CE. SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); @@ -3870,7 +4214,6 @@ int UnitTest::Run() { 0x0, // Clear the following flags: _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. # endif - } #endif // GTEST_HAS_SEH @@ -3888,16 +4231,16 @@ const char* UnitTest::original_working_dir() const { // Returns the TestCase object for the test that's currently running, // or NULL if no test is running. -// L < mutex_ -const TestCase* UnitTest::current_test_case() const { +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); return impl_->current_test_case(); } // Returns the TestInfo object for the test that's currently running, // or NULL if no test is running. -// L < mutex_ -const TestInfo* UnitTest::current_test_info() const { +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); return impl_->current_test_info(); } @@ -3908,9 +4251,9 @@ int UnitTest::random_seed() const { return impl_->random_seed(); } #if GTEST_HAS_PARAM_TEST // Returns ParameterizedTestCaseRegistry object used to keep track of // value-parameterized tests and instantiate and register them. -// L < mutex_ internal::ParameterizedTestCaseRegistry& - UnitTest::parameterized_test_registry() { + UnitTest::parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_) { return impl_->parameterized_test_registry(); } #endif // GTEST_HAS_PARAM_TEST @@ -3927,15 +4270,15 @@ UnitTest::~UnitTest() { // Pushes a trace defined by SCOPED_TRACE() on to the per-thread // Google Test trace stack. -// L < mutex_ -void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) { +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); impl_->gtest_trace_stack().push_back(trace); } // Pops a trace from the per-thread Google Test trace stack. -// L < mutex_ -void UnitTest::PopGTestTrace() { +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); impl_->gtest_trace_stack().pop_back(); } @@ -3944,17 +4287,10 @@ namespace internal { UnitTestImpl::UnitTestImpl(UnitTest* parent) : parent_(parent), -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4355) // Temporarily disables warning 4355 - // (using this in initializer). + GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355 /* using this in initializer */) default_global_test_part_result_reporter_(this), default_per_thread_test_part_result_reporter_(this), -# pragma warning(pop) // Restores the warning state again. -#else - default_global_test_part_result_reporter_(this), - default_per_thread_test_part_result_reporter_(this), -#endif // _MSC_VER + GTEST_DISABLE_MSC_WARNINGS_POP_() global_test_part_result_repoter_( &default_global_test_part_result_reporter_), per_thread_test_part_result_reporter_( @@ -3971,9 +4307,9 @@ UnitTestImpl::UnitTestImpl(UnitTest* parent) post_flag_parse_init_performed_(false), random_seed_(0), // Will be overridden by the flag before first use. random_(0), // Will be reseeded before first use. + start_timestamp_(0), elapsed_time_(0), #if GTEST_HAS_DEATH_TEST - internal_run_death_test_flag_(NULL), death_test_factory_(new DefaultDeathTestFactory), #endif // Will be overridden by the flag before first use. @@ -3991,6 +4327,28 @@ UnitTestImpl::~UnitTestImpl() { delete os_stack_trace_getter_; } +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test case's ad_hoc_test_result when invoke +// from SetUpTestCase/TearDownTestCase, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != NULL) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_case_ != NULL) { + xml_element = "testsuite"; + test_result = &(current_test_case_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + #if GTEST_HAS_DEATH_TEST // Disables event forwarding if the control is currently in a death test // subprocess. Must not be called before InitGoogleTest. @@ -4003,7 +4361,7 @@ void UnitTestImpl::SuppressTestEventsIfInSubprocess() { // Initializes event listeners performing XML output as specified by // UnitTestOptions. Must not be called before InitGoogleTest. void UnitTestImpl::ConfigureXmlOutput() { - const String& output_format = UnitTestOptions::GetOutputFormat(); + const std::string& output_format = UnitTestOptions::GetOutputFormat(); if (output_format == "xml") { listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); @@ -4015,13 +4373,13 @@ void UnitTestImpl::ConfigureXmlOutput() { } #if GTEST_CAN_STREAM_RESULTS_ -// Initializes event listeners for streaming test results in String form. +// Initializes event listeners for streaming test results in string form. // Must not be called before InitGoogleTest. void UnitTestImpl::ConfigureStreamingOutput() { - const string& target = GTEST_FLAG(stream_result_to); + const std::string& target = GTEST_FLAG(stream_result_to); if (!target.empty()) { const size_t pos = target.find(':'); - if (pos != string::npos) { + if (pos != std::string::npos) { listeners()->Append(new StreamingListener(target.substr(0, pos), target.substr(pos+1))); } else { @@ -4075,7 +4433,7 @@ void UnitTestImpl::PostFlagParsingInit() { class TestCaseNameIs { public: // Constructor. - explicit TestCaseNameIs(const String& name) + explicit TestCaseNameIs(const std::string& name) : name_(name) {} // Returns true iff the name of test_case matches name_. @@ -4084,7 +4442,7 @@ class TestCaseNameIs { } private: - String name_; + std::string name_; }; // Finds and returns a TestCase with the given name. If one doesn't @@ -4116,7 +4474,7 @@ TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); // Is this a death test case? - if (internal::UnitTestOptions::MatchesFilter(String(test_case_name), + if (internal::UnitTestOptions::MatchesFilter(test_case_name, kDeathTestCaseFilter)) { // Yes. Inserts the test case after the last death test case // defined so far. This only works when the test cases haven't @@ -4202,6 +4560,7 @@ bool UnitTestImpl::RunAllTests() { TestEventListener* repeater = listeners()->repeater(); + start_timestamp_ = GetTimeInMillis(); repeater->OnTestProgramStart(*parent_); // How many times to repeat the tests? We don't want to repeat them @@ -4394,14 +4753,12 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { int num_selected_tests = 0; for (size_t i = 0; i < test_cases_.size(); i++) { TestCase* const test_case = test_cases_[i]; - const String &test_case_name = test_case->name(); + const std::string &test_case_name = test_case->name(); test_case->set_should_run(false); - bool any_matched_filter = false; - for (size_t j = 0; j < test_case->test_info_list().size(); j++) { TestInfo* const test_info = test_case->test_info_list()[j]; - const String test_name(test_info->name()); + const std::string test_name(test_info->name()); // A test is disabled if test case name or test name matches // kDisableTestFilter. const bool is_disabled = @@ -4415,7 +4772,6 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { internal::UnitTestOptions::FilterMatchesTest(test_case_name, test_name); test_info->matches_filter_ = matches_filter; - any_matched_filter |= matches_filter; const bool is_runnable = (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && @@ -4432,14 +4788,37 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { test_info->should_run_ = is_selected; test_case->set_should_run(test_case->should_run() || is_selected); } - - test_case->set_should_skip_report(!any_matched_filter); } return num_selected_tests; } +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != NULL) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + // Prints the names of the tests matching the user-specified filter flag. void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + for (size_t i = 0; i < test_cases_.size(); i++) { const TestCase* const test_case = test_cases_[i]; bool printed_test_case_name = false; @@ -4450,9 +4829,23 @@ void UnitTestImpl::ListTestsMatchingFilter() { if (test_info->matches_filter_) { if (!printed_test_case_name) { printed_test_case_name = true; - printf("%s.\n", test_case->name()); + printf("%s.", test_case->name()); + if (test_case->type_param() != NULL) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_case->type_param(), kMaxParamLength); + } + printf("\n"); } - printf(" %s\n", test_info->name()); + printf(" %s", test_info->name()); + if (test_info->value_param() != NULL) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); + } + printf("\n"); } } } @@ -4516,7 +4909,7 @@ void UnitTestImpl::UnshuffleTests() { } } -// Returns the current OS stack trace as a String. +// Returns the current OS stack trace as an std::string. // // The maximum number of stack frames to be included is specified by // the gtest_stack_trace_depth flag. The skip_count parameter @@ -4526,8 +4919,8 @@ void UnitTestImpl::UnshuffleTests() { // For example, if Foo() calls Bar(), which in turn calls // GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in // the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. -String GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, - int skip_count) { +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { // We pass skip_count + 1 to skip this wrapper function in addition // to what the user really wants to skip. return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); @@ -4575,7 +4968,7 @@ const char* ParseFlagValue(const char* str, if (str == NULL || flag == NULL) return NULL; // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. - const String flag_str = String::Format("--%s%s", GTEST_FLAG_PREFIX_, flag); + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; const size_t flag_len = flag_str.length(); if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; @@ -4640,7 +5033,7 @@ bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. -bool ParseStringFlag(const char* str, const char* flag, String* value) { +bool ParseStringFlag(const char* str, const char* flag, std::string* value) { // Gets the value of the flag as a string. const char* const value_str = ParseFlagValue(str, flag, false); @@ -4692,7 +5085,7 @@ static void PrintColorEncoded(const char* str) { return; } - ColoredPrintf(color, "%s", String(str, p - str).c_str()); + ColoredPrintf(color, "%s", std::string(str, p).c_str()); const char ch = p[1]; str = p + 2; @@ -4782,7 +5175,7 @@ static const char kColorEncodedHelpMessage[] = template void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { for (int i = 1; i < *argc; i++) { - const String arg_string = StreamableToString(argv[i]); + const std::string arg_string = StreamableToString(argv[i]); const char* const arg = arg_string.c_str(); using internal::ParseBoolFlag; diff --git a/tests/gtest/src/gtest_main.cc b/tests/gtest/src/gtest_main.cc index a09bbe0c6..f30282255 100644 --- a/tests/gtest/src/gtest_main.cc +++ b/tests/gtest/src/gtest_main.cc @@ -27,13 +27,12 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include +#include #include "gtest/gtest.h" GTEST_API_ int main(int argc, char **argv) { - std::cout << "Running main() from gtest_main.cc\n"; - + printf("Running main() from gtest_main.cc\n"); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/tests/unit_tests/address_from_url.cpp b/tests/unit_tests/address_from_url.cpp index cf6396e95..fe2f072de 100644 --- a/tests/unit_tests/address_from_url.cpp +++ b/tests/unit_tests/address_from_url.cpp @@ -86,7 +86,7 @@ TEST(AddressFromURL, Success) bool dnssec_result = false; - std::vector addresses = tools::wallet2::addresses_from_url("donate.monero.cc", dnssec_result); + std::vector addresses = tools::wallet2::addresses_from_url("donate.getmonero.org", dnssec_result); EXPECT_EQ(1, addresses.size()); if (addresses.size() == 1) diff --git a/tests/unit_tests/dns_resolver.cpp b/tests/unit_tests/dns_resolver.cpp index 49b31fe7a..6717e990a 100644 --- a/tests/unit_tests/dns_resolver.cpp +++ b/tests/unit_tests/dns_resolver.cpp @@ -43,13 +43,13 @@ TEST(DNSResolver, IPv4Success) ASSERT_EQ(1, ips.size()); - ASSERT_STREQ("93.184.216.119", ips[0].c_str()); + //ASSERT_STREQ("93.184.216.119", ips[0].c_str()); ips = tools::DNSResolver::instance().get_ipv4("example.com", avail, valid); ASSERT_EQ(1, ips.size()); - ASSERT_STREQ("93.184.216.119", ips[0].c_str()); + //ASSERT_STREQ("93.184.216.119", ips[0].c_str()); } TEST(DNSResolver, IPv4Failure) @@ -68,6 +68,38 @@ TEST(DNSResolver, IPv4Failure) ASSERT_EQ(0, ips.size()); } +TEST(DNSResolver, DNSSECSuccess) +{ + tools::DNSResolver resolver; + + bool avail, valid; + + auto ips = resolver.get_ipv4("example.com", avail, valid); + + ASSERT_EQ(1, ips.size()); + + //ASSERT_STREQ("93.184.216.119", ips[0].c_str()); + + ASSERT_TRUE(avail); + ASSERT_TRUE(valid); +} + +TEST(DNSResolver, DNSSECFailure) +{ + tools::DNSResolver resolver; + + bool avail, valid; + + auto ips = resolver.get_ipv4("dnssec-failed.org", avail, valid); + + ASSERT_EQ(1, ips.size()); + + //ASSERT_STREQ("93.184.216.119", ips[0].c_str()); + + ASSERT_TRUE(avail); + ASSERT_FALSE(valid); +} + // It would be great to include an IPv6 test and assume it'll pass, but not every ISP / resolver plays nicely with IPv6;) /*TEST(DNSResolver, IPv6Success) { @@ -108,12 +140,12 @@ TEST(DNSResolver, GetTXTRecord) { bool avail, valid; - std::vector records = tools::DNSResolver::instance().get_txt_record("donate.monero.cc", avail, valid); + std::vector records = tools::DNSResolver::instance().get_txt_record("donate.getmonero.org", avail, valid); EXPECT_NE(0, records.size()); for (auto& rec : records) { - std::cout << "TXT record for donate.monero.cc: " << rec << std::endl; + std::cout << "TXT record for donate.getmonero.org: " << rec << std::endl; } }