1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
#ifndef TANGO_TESTS_CATCH2_UTILS_UTILS_H
#define TANGO_TESTS_CATCH2_UTILS_UTILS_H
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/generators/catch_generators_range.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>
#include "utils/auto_device_class.h"
#include "utils/test_server.h"
#include "utils/callback_mock.h"
#include "utils/matchers.h"
#include "utils/generators.h"
#include <tango/tango.h>
#include <memory>
namespace TangoTest
{
std::string make_nodb_fqtrl(int port, std::string_view device_name);
const char *get_current_log_file_path();
/* @brief Return a disc location where a FileDatabase can be created
*
*/
std::string get_next_file_database_location();
// TODO: Multiple devices and/or multiple device servers
// TODO: Maybe we want a builder API for this
class Context
{
public:
/**
* @brief Create a Tango Test Context with a single device server in nodb mode
*
* @param instance_name Name of the device server instance
* @param class_name Name of the device class to instantiate
* @param env environment entries of the form "key1=value1", "key2=value2"
*/
Context(const std::string &instance_name, const std::string &class_name, std::vector<std::string> env = {});
/**
* @brief Create a Tango Test Context with a single device server in nodb mode
*
* @param instance_name Name of the device server instance
* @param tmpl_name Name of the template device class to instantiate
* @param idlversion IDL version of the device class to instantiate
* @param env environment entries of the form "key1=value1", "key2=value2"
*/
Context(const std::string &instance_name,
const std::string &tmpl_name,
int idlversion,
std::vector<std::string> env = {});
/**
* @brief Create a Tango Test Context with a single device server in filedb mode
*
* TODO: Do not require users to pass the filedb_contents
*
* @param instance_name Name of the device server instance
* @param tmpl_name Name of the template device class to instantiate
* @param idlversion IDL version of the device class to instantiate
* @param extra_filedb_contents Contents of the filedb to use
* @param env environment entries of the form "key1=value1", "key2=value2"
*/
Context(const std::string &instance_name,
const std::string &tmpl_name,
int idlversion,
const std::string &extra_filedb_contents,
std::vector<std::string> env = {});
Context(const Context &) = delete;
Context &operator=(Context &) = delete;
~Context();
std::unique_ptr<Tango::DeviceProxy> get_proxy();
std::unique_ptr<Tango::DeviceProxy> get_admin_proxy();
/** Wait until the server stops.
*
* Intended to be called if the server is being stopped by some means
* outside of the test infrastructure, e.g. the DServer Kill command.
*
* @param timeout to wait for the server to stop
*
* Returns the exit status of the server.
*
* Throws: a `runtime_error` if the timeout is exceeded.
*/
ExitStatus wait_for_exit(std::chrono::milliseconds timeout = TestServer::k_default_timeout);
/** Stop the associated TestServer instance if it has been started.
*
* If the instance has a non-zero exit status, then diagnostics will be
* output with the Catch2 WARN macro.
*
* Returns when the server has stopped or the timeout has been reached
*
* @param timeout if the server takes longer than this timeout,
* a warning log will be generated
*/
void stop_server(std::chrono::milliseconds timeout = TestServer::k_default_timeout);
/*
* Return the disc location of the FileDatabase, throws if there is none.
*/
std::string get_file_database_path();
/*
* Return the name of the Tango device class
*/
std::string get_class_name();
/**
* Get the server redirection file
*/
const std::string &get_redirect_file() const;
/** Restart the server if it has been stopped using the same port.
*
* @param timeout -- how long to wait for the server to start
*
* Throws: a `runtime_error` if the server cannot be restarted
*
* Expects: `stop_server()` has been called previously
*/
void restart_server(std::chrono::milliseconds timeout = TestServer::k_default_timeout);
private:
std::optional<std::string> m_filedb_path = std::nullopt;
TestServer m_server;
std::string m_class_name;
std::string m_instance_name;
std::vector<std::string> m_extra_args;
std::vector<std::string> m_extra_env;
};
namespace detail
{
constexpr const char *k_log_file_env_var = "TANGO_TEST_LOG_FILE";
// Setup a log appender to the file specified in the environment variable
// TANGO_TEST_LOG_FILE. Each log is prefixed with `topic`, this allows
// multiple processes to log to this file at the same time.
//
// If the environment variable is not set, this does nothing.
//
// Expects: Tango::Logging::get_core_logger() != nullptr
void setup_topic_log_appender(std::string_view topic, const char *filename = nullptr);
// A unique identifier representing the random seed used by the test run to make
// it easier for the user to identify log files. This is constructed during the
// testRunStarting Catch2 event.
extern std::string g_log_filename_prefix;
// Return a filename containing the test_case_name, starting with
// g_log_filename_prefix and ending with suffix.
//
// The filename will not exceed the path component size limit on the platform.
// And any spaces (' ') in the test_case_name will be replaced with underscores
// ('_').
std::string filename_from_test_case_name(std::string_view test_case_name, std::string_view suffix);
} // namespace detail
} // namespace TangoTest
/**
* @brief Instantiate a TangoTest::AutoDeviceClass for template class DEVICE
*
* This macro expects that the DEVICE takes its base class as a template
* parameter:
*
* template<typename Base>
* class DEVICE : public Base {
* ...
* };
*
* It will instantiate a TangoTest::AutoDeviceClass with a base class using IDL
* version MIN and onwards.
*
* @param DEVICE class template
* @param MIN minimum DeviceImpl version to use
*/
#define TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE(DEVICE, MIN) TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_##MIN(DEVICE)
#define TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_1(DEVICE) \
TANGO_TEST_AUTO_DEV_CLASS_INSTANTIATE(DEVICE<Tango::DeviceImpl>, DEVICE##_1) \
TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_2(DEVICE)
#define TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_2(DEVICE) \
TANGO_TEST_AUTO_DEV_CLASS_INSTANTIATE(DEVICE<Tango::Device_2Impl>, DEVICE##_2) \
TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_3(DEVICE)
#define TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_3(DEVICE) \
TANGO_TEST_AUTO_DEV_CLASS_INSTANTIATE(DEVICE<Tango::Device_3Impl>, DEVICE##_3) \
TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_4(DEVICE)
#define TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_4(DEVICE) \
TANGO_TEST_AUTO_DEV_CLASS_INSTANTIATE(DEVICE<Tango::Device_4Impl>, DEVICE##_4) \
TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_5(DEVICE)
#define TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_5(DEVICE) \
TANGO_TEST_AUTO_DEV_CLASS_INSTANTIATE(DEVICE<Tango::Device_5Impl>, DEVICE##_5) \
TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_6(DEVICE)
#define TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE_6(DEVICE) \
TANGO_TEST_AUTO_DEV_CLASS_INSTANTIATE(DEVICE<Tango::Device_6Impl>, DEVICE##_6)
#endif
|