File: router_component_test.h

package info (click to toggle)
mysql-8.0 8.0.43-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,273,924 kB
  • sloc: cpp: 4,684,605; ansic: 412,450; pascal: 108,398; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; sh: 24,181; python: 21,816; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,076; makefile: 2,194; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (215 lines) | stat: -rw-r--r-- 7,646 bytes parent folder | download
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
212
213
214
215
/*
  Copyright (c) 2017, 2025, Oracle and/or its affiliates.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License, version 2.0,
  as published by the Free Software Foundation.

  This program is designed to work with certain software (including
  but not limited to OpenSSL) that is licensed under separate terms,
  as designated in a particular file or component or in included license
  documentation.  The authors of MySQL hereby grant you an additional
  permission to link the program and your derivative works with the
  separately licensed software that they have either included with
  the program or referenced in the documentation.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef _ROUTER_COMPONENT_TEST_H_
#define _ROUTER_COMPONENT_TEST_H_

#include <chrono>

#include <gmock/gmock.h>

#include "mysql/harness/stdx/attribute.h"
#include "mysqlrouter/cluster_metadata.h"
#include "mysqlrouter/mysql_session.h"
#include "process_manager.h"
#include "process_wrapper.h"
#include "tcp_port_pool.h"

/** @class RouterComponentTest
 *
 * Base class for the MySQLRouter component-like tests.
 * Enables creating processes, intercepting their output, writing to input, etc.
 *
 **/
class RouterComponentTest : public ProcessManager, public ::testing::Test {
 public:
  using MySQLSession = mysqlrouter::MySQLSession;

  /** @brief Initializes the test
   */
  void SetUp() override;

  /** @brief Deinitializes the test
   */
  void TearDown() override;

  /** @brief Wait until the process' log contains a given pattern
   *
   * @param process process handle
   * @param pattern pattern in the log file to wait for
   * @param timeout maximum time to wait for a given pattern
   *
   * @return bool value indicating if the pattern was found in the log file or
   * not
   */
  [[nodiscard]] bool wait_log_contains(const ProcessWrapper &process,
                                       const std::string &pattern,
                                       std::chrono::milliseconds timeout);

  /** @brief Sleep for a duration given as a parameter. The duration is
   * increased 10 times for the run with VALGRIND.
   */
  static void sleep_for(std::chrono::milliseconds duration);

  std::pair<uint16_t, std::unique_ptr<MySQLSession>> make_new_connection_ok(
      uint16_t router_port, std::vector<uint16_t> expected_node_ports) {
    std::unique_ptr<MySQLSession> session{std::make_unique<MySQLSession>()};
    EXPECT_NO_THROW(session->connect("127.0.0.1", router_port, "username",
                                     "password", "", ""));

    auto result{session->query_one("select @@port")};
    const auto port =
        static_cast<uint16_t>(std::strtoul((*result)[0], nullptr, 10));
    EXPECT_THAT(expected_node_ports, ::testing::Contains(port));

    return std::make_pair(port, std::move(session));
  }

  std::unique_ptr<MySQLSession> make_new_connection_ok(
      uint16_t router_port, uint16_t expected_node_port) {
    std::unique_ptr<MySQLSession> session{std::make_unique<MySQLSession>()};
    EXPECT_NO_THROW(session->connect("127.0.0.1", router_port, "username",
                                     "password", "", ""));

    auto result{session->query_one("select @@port")};
    EXPECT_EQ(std::strtoul((*result)[0], nullptr, 10), expected_node_port);

    return session;
  }

  uint16_t make_new_connection_ok(uint16_t router_port) {
    MySQLSession session;
    EXPECT_NO_THROW(session.connect("127.0.0.1", router_port, "username",
                                    "password", "", ""));

    auto result{session.query_one("select @@port")};
    return static_cast<uint16_t>(std::strtoul((*result)[0], nullptr, 10));
  }

  void verify_new_connection_fails(uint16_t router_port) {
    MySQLSession session;
    ASSERT_ANY_THROW(session.connect("127.0.0.1", router_port, "username",
                                     "password", "", ""));
  }

  void verify_existing_connection_ok(
      MySQLSession *session,
      uint16_t expected_node = 0 /*0 means do not verify the port*/) {
    auto result{session->query_one("select @@port")};
    if (expected_node > 0) {
      EXPECT_EQ(std::strtoul((*result)[0], nullptr, 10), expected_node);
    }
  }

  void verify_existing_connection_dropped(
      MySQLSession *session,
      std::chrono::milliseconds timeout = std::chrono::seconds(5)) {
    if (getenv("WITH_VALGRIND")) {
      timeout *= 10;
    }

    const auto MSEC_STEP = std::chrono::milliseconds(50);
    const auto started = std::chrono::steady_clock::now();
    do {
      try {
        session->query_one("select @@port");
      } catch (mysqlrouter::MySQLSession::Error &) {
        // query failed, connection dropped, all good
        return;
      }

      auto step = std::min(timeout, MSEC_STEP);
      RouterComponentTest::sleep_for(step);
      timeout -= step;
    } while (timeout > std::chrono::steady_clock::now() - started);

    FAIL() << "Timed out waiting for the connection to drop";
  }

 protected:
  TcpPortPool port_pool_;
};

/** @class CommonBootstrapTest
 *
 * Base class for the MySQLRouter component-like bootstrap tests.
 *
 **/
class RouterComponentBootstrapTest : virtual public RouterComponentTest {
 public:
  using OutputResponder = ProcessWrapper::OutputResponder;

  static const OutputResponder kBootstrapOutputResponder;

 protected:
  TempDirectory bootstrap_dir;
  std::string config_file;

  struct Config {
    std::string ip;
    unsigned int port;
    uint16_t http_port;
    std::string js_filename;
    bool unaccessible{false};
    std::string cluster_specific_id{"cluster-specific-id"};
  };

  void bootstrap_failover(
      const std::vector<Config> &mock_server_configs,
      const mysqlrouter::ClusterType cluster_type,
      const std::vector<std::string> &router_options = {},
      int expected_exitcode = 0,
      const std::vector<std::string> &expected_output_regex = {},
      std::chrono::milliseconds wait_for_exit_timeout =
          std::chrono::seconds(30),
      const mysqlrouter::MetadataSchemaVersion &metadata_version = {2, 0, 3},
      const std::vector<std::string> &extra_router_options = {});

  friend std::ostream &operator<<(
      std::ostream &os,
      const std::vector<std::tuple<ProcessWrapper &, unsigned int>> &T);

  ProcessWrapper &launch_router_for_bootstrap(
      std::vector<std::string> params, int expected_exit_code = EXIT_SUCCESS,
      const bool disable_rest = true, const bool add_report_host = true,
      const bool catch_stderr = true,
      ProcessWrapper::OutputResponder output_responder =
          RouterComponentBootstrapTest::kBootstrapOutputResponder) {
    if (disable_rest) params.push_back("--disable-rest");
    if (add_report_host) params.push_back("--report-host=dont.query.dns");

    return ProcessManager::launch_router(
        params, expected_exit_code, catch_stderr, /*with_sudo=*/false,
        /*wait_for_notify_ready=*/std::chrono::seconds(-1), output_responder);
  }

  static constexpr const char kRootPassword[] = "fake-pass";
};

std::ostream &operator<<(
    std::ostream &os,
    const std::vector<std::tuple<ProcessWrapper &, unsigned int>> &T);

#endif  // _ROUTER_COMPONENT_TEST_H_