File: test_server.h

package info (click to toggle)
tango 10.0.2%2Bdfsg1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 89,936 kB
  • sloc: cpp: 201,786; sh: 1,645; python: 953; java: 800; perl: 467; javascript: 447; xml: 325; makefile: 272; sql: 72; ruby: 24
file content (154 lines) | stat: -rw-r--r-- 4,430 bytes parent folder | download | duplicates (3)
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
#ifndef TANGO_TESTS_CATCH2_UTILS_TEST_SERVER_H
#define TANGO_TESTS_CATCH2_UTILS_TEST_SERVER_H

#include <chrono>
#include <memory>
#include <string>
#include <optional>
#include <vector>

namespace TangoTest
{

class Logger
{
  public:
    virtual void log(const std::string &) = 0;

    virtual ~Logger() { }
};

struct ExitStatus
{
    enum class Kind
    {
        Normal,          // The TestServer exited normally, code is active.
        Aborted,         // The TestServer was Aborted, signal is active.
        AbortedNoSignal, // The TestServer was Aborted, neither code nor signal
                         // are defined.
    };

    Kind kind;

    union
    {
        int code;   // exit code of the TestServer
        int signal; // signal used to abort the TestServer
    };

    bool is_success()
    {
        return kind == Kind::Normal && code == 0;
    }
};

std::ostream &operator<<(std::ostream &os, const ExitStatus &status);

/* RAII class for a TestServer process
 */
class TestServer
{
  public:
    constexpr static const int k_num_port_tries = 5;
    constexpr static const char *k_ready_string = "Ready to accept request";
    constexpr static const char *k_port_in_use_string = "INITIALIZE_TransportError";
    constexpr static std::chrono::milliseconds k_default_timeout{5000};

    TestServer() = default;

    TestServer(const TestServer &) = delete;
    TestServer &operator=(TestServer &) = delete;

    // TODO: Run with TangoDB?

    /** Starts a TestServer instance with a single device of the specified class.
     *
     *  The ctor only finishes after the ready string ("Ready to accept request")
     *  has been output.
     *
     *  The first time the `start` is called a free port is randomly chosen.  On
     *  subsequent calls the same port is always used, is that port is
     *  unavailable `start` will fail.
     *
     *  @param instance_name -- name of the TestServer instance
     *  @param extra_args -- additional cli arguments to pass to the TestServer
     *  @param extra_env -- additional environment variables to set for the TestServer
     *  @param timeout -- how long to wait for the ready string
     *
     *  Throws a std::runtime_exception in case the server fails to start, or
     *  takes too long to output the ready string, or we fail to find a free port
     *  after a `k_num_port_tries` attempts.
     *
     *  Expects: `!this->is_running()` and `instance_name != ""`
     */
    void start(const std::string &instance_name,
               const std::vector<std::string> &extra_args,
               const std::vector<std::string> &extra_env,
               std::chrono::milliseconds timeout = k_default_timeout);

    /** Stop the 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.
     *
     *  After the server has been stopped, it can be started again using the
     *  same port by calling `start`.
     *
     *  Does nothing if `!is_running()`.
     *
     *  @param timeout -- how long to wait for the server to exit
     */
    void stop(std::chrono::milliseconds timeout = k_default_timeout);

    /** Wait until the server has exited.
     *
     *  Returns the exit status of the server.
     *
     *  Raises an exception if the timeout is exceed.
     *
     *  @param timeout -- how long to wait for the server to exit
     *
     *  Expects: `is_running()`
     */
    ExitStatus wait_for_exit(std::chrono::milliseconds timeout = k_default_timeout);

    ~TestServer();

    /** Returns `true` if the server has been start()'d and not yet stop()'d.
     */
    bool is_running()
    {
        return m_handle != nullptr;
    }

    /** Return the port that the server is connected to.
     */
    int get_port() const
    {
        return m_port;
    }

    const std::string &get_redirect_file() const
    {
        return m_redirect_file;
    }

    struct Handle;

    // We store the next port here so that it can be controlled from a test (for
    // internal testing).
    static int s_next_port;
    static std::unique_ptr<Logger> s_logger;

  private:
    Handle *m_handle = nullptr;
    int m_port = -1;
    std::string m_redirect_file;

    // Set if the test has called wait_for_exit() and it didn't timeout.
    std::optional<ExitStatus> m_exit_status = std::nullopt;
};

} // namespace TangoTest

#endif