This is an automated email from the ASF dual-hosted git repository.
isapego pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 5e6a5e8b2b2 IGNITE-26843 C++ Client: Add compatibility test suite
(#6856)
5e6a5e8b2b2 is described below
commit 5e6a5e8b2b23cb03cc046fc64d017d9abbd92d52
Author: Ed Rakhmankulov <[email protected]>
AuthorDate: Tue Oct 28 17:05:36 2025 +0300
IGNITE-26843 C++ Client: Add compatibility test suite (#6856)
---
modules/platforms/cpp/CMakeLists.txt | 7 +-
modules/platforms/cpp/DEVNOTES.md | 8 +
.../cpp/tests/compatibility-tests/CMakeLists.txt | 28 +++
.../cpp/tests/compatibility-tests/basic_test.cpp | 44 ++++
.../ignite_xml_unit_test_result_printer.cpp | 127 +++++++++++
.../ignite_xml_unit_test_result_printer.h | 57 +++++
.../cpp/tests/compatibility-tests/main.cpp | 236 +++++++++++++++++++++
.../cpp/tests/test-common/detail/unix_process.h | 1 +
.../cpp/tests/test-common/ignite_runner.cpp | 82 ++++++-
.../cpp/tests/test-common/ignite_runner.h | 16 ++
.../platforms/cpp/tests/test-common/process.cpp | 2 +-
.../platforms/cpp/tests/test-common/test_utils.cpp | 32 +++
.../platforms/cpp/tests/test-common/test_utils.h | 8 +
13 files changed, 638 insertions(+), 10 deletions(-)
diff --git a/modules/platforms/cpp/CMakeLists.txt
b/modules/platforms/cpp/CMakeLists.txt
index 4fe5f899604..015c8e4c64b 100644
--- a/modules/platforms/cpp/CMakeLists.txt
+++ b/modules/platforms/cpp/CMakeLists.txt
@@ -35,6 +35,7 @@ option(ENABLE_CLIENT "Build Ignite.C++ Client module" ON)
option(ENABLE_ODBC "Build Ignite ODBC driver module" OFF)
option(ENABLE_PROTOCOL "Build Ignite Protocol library" ON)
option(ENABLE_TESTS "Build Ignite.C++ tests" OFF)
+option(ENABLE_COMPATIBILITY_TESTS "Build Ignite.C++ compatibility tests" OFF)
option(ENABLE_ADDRESS_SANITIZER "If address sanitizer is enabled" OFF)
option(ENABLE_UB_SANITIZER "If undefined behavior sanitizer is enabled" OFF)
option(WARNINGS_AS_ERRORS "Treat warning as errors" OFF)
@@ -144,7 +145,7 @@ endif()
# Add integration tests.
if (${ENABLE_TESTS})
- if (${ENABLE_CLIENT} OR ${ENABLE_ODBC})
+ if (${ENABLE_CLIENT} OR ${ENABLE_ODBC} OR ${ENABLE_COMPATIBILITY_TESTS})
add_subdirectory(tests/test-common)
endif()
@@ -155,6 +156,10 @@ if (${ENABLE_TESTS})
if (${ENABLE_ODBC})
add_subdirectory(tests/odbc-test)
endif()
+
+ if (${ENABLE_COMPATIBILITY_TESTS})
+ add_subdirectory(tests/compatibility-tests)
+ endif()
endif()
# Source code formatting with clang-format.
diff --git a/modules/platforms/cpp/DEVNOTES.md
b/modules/platforms/cpp/DEVNOTES.md
index 1757984f981..24edc6e639c 100644
--- a/modules/platforms/cpp/DEVNOTES.md
+++ b/modules/platforms/cpp/DEVNOTES.md
@@ -186,3 +186,11 @@ To run a specific test:
To debug or profile the Java side of the tests, run the
`org.apache.ignite.internal.runner.app.PlatformTestNodeRunner`
class in IDEA with a debugger or profiler, then run C++ tests as usual,
optionally with debugger.
+
+
+### Running compatibility Tests
+
+To enable compatibility test pass `-DENABLE_COMPATIBILITY_TESTS=ON` to cmake
build.
+If you are debugging then it is recommended to run one version at once by
providing environment variable e.g
`IGNITE_CPP_TESTS_COMPATIBILITY_VERSIONS_OVERRIDE=3.0.0`
+To start tests run in modules/platforms/cpp/tests/compatibility-tests
+`./cmake-build-debug/bin/ignite-client-compatibility-test`
\ No newline at end of file
diff --git a/modules/platforms/cpp/tests/compatibility-tests/CMakeLists.txt
b/modules/platforms/cpp/tests/compatibility-tests/CMakeLists.txt
new file mode 100644
index 00000000000..4fe0aeed1ef
--- /dev/null
+++ b/modules/platforms/cpp/tests/compatibility-tests/CMakeLists.txt
@@ -0,0 +1,28 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+project(ignite-client-compatibility-test)
+
+set(TARGET ${PROJECT_NAME})
+
+set(SOURCES
+ main.cpp
+ ignite_xml_unit_test_result_printer.cpp
+ basic_test.cpp
+)
+
+ignite_test(${TARGET} SOURCES ${SOURCES} LIBS ignite-test-common
ignite3-client)
diff --git a/modules/platforms/cpp/tests/compatibility-tests/basic_test.cpp
b/modules/platforms/cpp/tests/compatibility-tests/basic_test.cpp
new file mode 100644
index 00000000000..670880c0610
--- /dev/null
+++ b/modules/platforms/cpp/tests/compatibility-tests/basic_test.cpp
@@ -0,0 +1,44 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "tests/client-test/ignite_runner_suite.h"
+
+using namespace ignite;
+
+class basic_test_ign_version : public ignite::ignite_runner_suite {
+private:
+ static ignite_client ConnectToCluster() {
+ ignite_client_configuration cfg{get_node_addrs()};
+ cfg.set_logger(get_logger());
+ return ignite_client::start(cfg, std::chrono::seconds(30));
+ }
+
+protected:
+ void SetUp() override {
+ m_client = ConnectToCluster();
+
+ std::cout << "CompatibilityServer version" <<
ignite_runner::COMPATIBILITY_VERSION << "\n";
+ }
+
+ ignite_client m_client;
+};
+
+
+TEST_F(basic_test_ign_version, get_cluster_nodes_successful) {
+ auto cluster_nodes = m_client.get_cluster_nodes();
+
+ ASSERT_GE(cluster_nodes.size(), 1);
+}
\ No newline at end of file
diff --git
a/modules/platforms/cpp/tests/compatibility-tests/ignite_xml_unit_test_result_printer.cpp
b/modules/platforms/cpp/tests/compatibility-tests/ignite_xml_unit_test_result_printer.cpp
new file mode 100644
index 00000000000..53c5539cb6e
--- /dev/null
+++
b/modules/platforms/cpp/tests/compatibility-tests/ignite_xml_unit_test_result_printer.cpp
@@ -0,0 +1,127 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "ignite_xml_unit_test_result_printer.h"
+
+namespace ignite::detail {
+
+ignite_xml_unit_test_result_printer::ignite_xml_unit_test_result_printer(
+ testing::TestEventListener *delegate, std::string version)
+ : m_delegate(delegate)
+ , m_version(std::move(version)){}
+
+void ignite_xml_unit_test_result_printer::OnTestProgramStart(const
::testing::UnitTest &unit_test) {
+ m_delegate->OnTestProgramStart(unit_test);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestIterationStart(const
::testing::UnitTest &unit_test, int iteration) {
+ m_delegate->OnTestIterationStart(unit_test, iteration);
+}
+
+void ignite_xml_unit_test_result_printer::OnEnvironmentsSetUpStart(const
::testing::UnitTest &unit_test) {
+ m_delegate->OnEnvironmentsSetUpStart(unit_test);
+}
+
+void ignite_xml_unit_test_result_printer::OnEnvironmentsSetUpEnd(const
::testing::UnitTest &unit_test) {
+ m_delegate->OnEnvironmentsSetUpEnd(unit_test);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestSuiteStart(const
::testing::TestSuite &test_suite) {
+ m_delegate->OnTestSuiteStart(test_suite);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestSuiteEnd(const
::testing::TestSuite &test_suite) {
+ m_delegate->OnTestSuiteEnd(test_suite);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestCaseStart(const
::testing::TestCase &test_case) {
+ m_delegate->OnTestCaseStart(test_case);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestCaseEnd(const
::testing::TestCase &test_case) {
+ m_delegate->OnTestCaseEnd(test_case);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestStart(const
::testing::TestInfo &test_info) {
+ m_delegate->OnTestStart(test_info);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestDisabled(const
testing::TestInfo &test_info) {
+ m_delegate->OnTestDisabled(test_info);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestPartResult(const
::testing::TestPartResult &test_part_result) {
+ m_delegate->OnTestPartResult(test_part_result);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestEnd(const ::testing::TestInfo
&test_info) {
+ m_delegate->OnTestEnd(test_info);
+}
+
+void ignite_xml_unit_test_result_printer::OnEnvironmentsTearDownStart(const
::testing::UnitTest &unit_test) {
+ m_delegate->OnEnvironmentsTearDownStart(unit_test);
+}
+
+void ignite_xml_unit_test_result_printer::OnEnvironmentsTearDownEnd(const
::testing::UnitTest &unit_test) {
+ m_delegate->OnEnvironmentsTearDownEnd(unit_test);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestIterationEnd(const
::testing::UnitTest &unit_test, int iteration) {
+ for (int i = 0; i < unit_test.total_test_case_count(); ++i) {
+ // We are extracting test suite info to add version info
+ const testing::TestSuite *ts = unit_test.GetTestSuite(i);
+
+ // because underlying storage is std::string we able to override it
content without changing length.
+ char *s = const_cast<char *>(ts->name());
+
+ std::string_view sw = s;
+
+ std::string_view suffix = "_ign_version";
+
+ if (sw.rfind(suffix) != sw.size() - suffix.size()) {
+ std::stringstream ss;
+ ss << "Expected test suite name to have suffix '"<< suffix <<"'
but got [name = "<< sw << "]";
+ throw std::runtime_error(ss.str());
+ }
+
+ // it is possible to have more complex version text like 9.1.18-p3,
etc.
+ if (m_version.size() >= suffix.size()) {
+ std::stringstream ss;
+ ss << "Expected version string to be shorter than a suffix but got
"
+ << "[version = " << m_version << "; suf fix = "<< suffix <<"]";
+ throw std::runtime_error(ss.str());
+ }
+
+ auto s_it = s + (sw.size() - suffix.size() + 1 /*skip leading '_'*/);
+ auto s_end = s + sw.size();
+ for (auto it = m_version.begin(); it != m_version.end(); ++it, ++s_it)
{
+ char c = *it;
+ c = c == '.' ? '_' : c;// Teamcity treats '.' specifically.
+ *s_it = c;
+ }
+
+ while (s_it != s_end) {
+ *s_it = '_';
+ ++s_it;
+ }
+ }
+ m_delegate->OnTestIterationEnd(unit_test, iteration);
+}
+
+void ignite_xml_unit_test_result_printer::OnTestProgramEnd(const
::testing::UnitTest &unit_test) {
+ m_delegate->OnTestProgramEnd(unit_test);
+}
+} // namespace ignite::detail
\ No newline at end of file
diff --git
a/modules/platforms/cpp/tests/compatibility-tests/ignite_xml_unit_test_result_printer.h
b/modules/platforms/cpp/tests/compatibility-tests/ignite_xml_unit_test_result_printer.h
new file mode 100644
index 00000000000..8d6d68d85a4
--- /dev/null
+++
b/modules/platforms/cpp/tests/compatibility-tests/ignite_xml_unit_test_result_printer.h
@@ -0,0 +1,57 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+namespace ignite::detail {
+/**
+ * Wrapper for default GTest event listener responsible for xml report.
+ * We override test suite name to include version information in order to
distinguish results on TeamCity output.
+ * GTest do not provide any API to manipulate test information so some hacks
were introduced
+ * at ignite::detail::ignite_xml_unit_test_result_printer::OnTestIterationEnd
+ * Name requirement for test suite is enforced: test suite should end with
'_ign_version' which would be replaced
+ * with actual version in xml report.
+ */
+class ignite_xml_unit_test_result_printer : public
::testing::EmptyTestEventListener {
+ TestEventListener *m_delegate;
+ std::string m_version;
+public:
+ ignite_xml_unit_test_result_printer(::testing::TestEventListener
*delegate, std::string version);
+
+ ~ignite_xml_unit_test_result_printer() override {
+ delete m_delegate;
+ }
+
+ void OnTestProgramStart(const testing::UnitTest &) override;
+ void OnTestIterationStart(const testing::UnitTest &, int) override;
+ void OnEnvironmentsSetUpStart(const testing::UnitTest &) override;
+ void OnEnvironmentsSetUpEnd(const testing::UnitTest &) override;
+ void OnTestSuiteStart(const testing::TestSuite &) override;
+ void OnTestCaseStart(const testing::TestCase &) override;
+ void OnTestStart(const testing::TestInfo &) override;
+ void OnTestDisabled(const testing::TestInfo &) override;
+ void OnTestPartResult(const testing::TestPartResult &) override;
+ void OnTestEnd(const testing::TestInfo &) override;
+ void OnTestSuiteEnd(const testing::TestSuite &) override;
+ void OnTestCaseEnd(const testing::TestCase &) override;
+ void OnEnvironmentsTearDownStart(const testing::UnitTest &) override;
+ void OnEnvironmentsTearDownEnd(const testing::UnitTest &) override;
+ void OnTestIterationEnd(const testing::UnitTest &, int) override;
+ void OnTestProgramEnd(const testing::UnitTest &) override;
+};
+} // namespace ignite::detail
\ No newline at end of file
diff --git a/modules/platforms/cpp/tests/compatibility-tests/main.cpp
b/modules/platforms/cpp/tests/compatibility-tests/main.cpp
new file mode 100644
index 00000000000..183bcdfc2c7
--- /dev/null
+++ b/modules/platforms/cpp/tests/compatibility-tests/main.cpp
@@ -0,0 +1,236 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "ignite_runner.h"
+#include "ignite_xml_unit_test_result_printer.h"
+#include "test_utils.h"
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <csignal>
+#include <cstring>
+
+namespace {
+
+/** Shutdown handler that cleans up resources. */
+std::function<void(int)> shutdown_handler;
+
+/**
+ * Receives OS signal and handles it.
+ *
+ * @param signum Signal value.
+ */
+void signal_handler(int signum) {
+ shutdown_handler(signum);
+ signal(signum, SIG_DFL);
+ raise(signum);
+}
+
+} // namespace
+
+/**
+ * Sets process abortion (SIGABRT, SIGINT, SIGSEGV signals) handler.
+ *
+ * @param handler Abortion handler.
+ */
+void set_process_abort_handler(std::function<void(int)> handler) {
+ shutdown_handler = std::move(handler);
+
+ // Install signal handlers to clean up resources on early exit.
+ signal(SIGABRT, signal_handler);
+ signal(SIGINT, signal_handler);
+ signal(SIGSEGV, signal_handler);
+}
+
+using namespace ignite;
+
+const std::vector<std::string> DEFAULT_VERSIONS = {"9.1.0", "9.1.1", "9.1.2",
"9.1.3", "9.1.4"};
+
+/**
+ * Structure to store argument values for automatic memory management.
+ */
+struct ArgumentValuesHolder {
+private:
+ int m_argc;
+ std::vector<std::string> m_vals;
+ char **m_argv;
+
+public:
+ ArgumentValuesHolder(int argc, std::vector<std::string> vals)
+ : m_argc(argc)
+ , m_vals(std::move(vals))
+ , m_argv(new char *[argc])
+ {
+
+ int idx = 0;
+ for (auto& val: m_vals) {
+ m_argv[idx++] = &val.front();
+ }
+ }
+
+ ~ArgumentValuesHolder() {
+ delete[] m_argv;
+ }
+
+ ArgumentValuesHolder(const ArgumentValuesHolder &other) = delete;
+
+ ArgumentValuesHolder(ArgumentValuesHolder &&other) = delete;
+
+ ArgumentValuesHolder &operator=(const ArgumentValuesHolder &other) =
delete;
+
+ ArgumentValuesHolder &operator=(ArgumentValuesHolder &&other) = delete;
+
+ char** get_argv() {
+ return m_argv;
+ }
+};
+
+/**
+ * Creates modified copy of argument values in order to influence test
framework behavior for different versions.
+ * @param argc Argument count.
+ * @param argv Argument values.
+ * @param version Compatibility version.
+ * @return An object which contains modified argument values.
+ */
+ArgumentValuesHolder override_xml_output_parameter(int argc, char **argv,
const std::string &version) {
+ std::vector<std::string> vals;
+ vals.reserve(argc);
+ for (int i = 0; i < argc; ++i) {
+ std::string s = argv[i];
+
+ if (s.find("--gtest_output=xml:") == 0) {
+ if (auto pos = s.rfind(".xml"); pos == s.size() - 4) {
+ s.insert(pos, version);
+ }
+ }
+
+ vals.push_back(s);
+ }
+
+ return ArgumentValuesHolder{argc, std::move(vals)};
+}
+
+/**
+ * Replaces default xml printer with ours which adds version information to
report.
+ * @param version Compatibility server version
+ */
+void replace_xml_print(const std::string &version) {
+ auto &test_event_listeners =
::testing::UnitTest::GetInstance()->listeners();
+
+ ::testing::TestEventListener *xml =
test_event_listeners.default_xml_generator();
+
+ if (xml) {
+ test_event_listeners.Release(xml);
+
+ test_event_listeners.Append(new
detail::ignite_xml_unit_test_result_printer(xml, version));
+ }
+}
+
+int run_tests(int argc, char **argv, const std::string &version) {
+ // In case when RUN_ALL_TESTS() not started;
+ int res = 4;
+ try {
+ std::cout << "[**********] " << "Compatibility suite for version " <<
version << " started." << std::endl;
+
+ auto holder = override_xml_output_parameter(argc, argv, version);
+
+ ::testing::InitGoogleTest(&argc, holder.get_argv());
+
+ replace_xml_print(version);
+
+ res = RUN_ALL_TESTS();
+
+ std::cout << "[**********] " << "Compatibility suite for version " <<
version << " finished" << std::endl;
+ } catch (const std::exception &err) {
+ std::cout << "[" << version << "] Uncaught error: " << err.what() <<
std::endl;
+ res = std::min(res, 2);
+ } catch (...) {
+ std::cout << "[" << version << "] Unknown uncaught error" << std::endl;
+ res = std::min(res, 3);
+ }
+
+ return res;
+}
+
+int main(int argc, char **argv) {
+ auto ver_override =
detail::get_env("IGNITE_CPP_TESTS_COMPATIBILITY_VERSIONS_OVERRIDE");
+
+ const auto &versions = ver_override
+ ? [&ver_override]() {
+ std::cout << "Parsing compatibility version override: " <<
*ver_override << "\n";
+
+ std::vector<std::string> v;
+ size_t l = 0;
+ size_t r;
+ do {
+ r = ver_override->find(',', l);
+ v.push_back(ver_override->substr(l, r - l));
+ l = r + 1;
+ } while (r != std::string::npos);
+
+ return v;
+ }()
+ : DEFAULT_VERSIONS;
+
+ int res = 0;
+ for (size_t i = 0; i < versions.size(); ++i) {
+ const std::string& version = versions[i];
+
+ ignite_runner runner{version};
+
+ set_process_abort_handler([&](int signal) {
+ std::cout << "Caught signal " << signal << " during tests" <<
std::endl;
+
+ runner.stop();
+ });
+
+ if (!check_test_node_connectable(std::chrono::seconds(5))) {
+ runner.start();
+ ensure_node_connectable(std::chrono::seconds(90));
+ }
+ int run_res;
+#ifdef _WIN32
+ run_res = run_tests(argc, argv, version);
+#else
+
+ // When debugging we most likely would choose one version
+ // For debug reasons last version will run in the same process
+ if (i == versions.size() - 1) {
+ run_res = run_tests(argc, argv, version);
+ } else {
+ // We fork because GTest initializes a lot of global variables
which interfere with subsequent runs.
+ int pid = ::fork();
+
+ if (pid == -1) {
+ std::stringstream ss;
+ ss << "Can't fork process for version " << version;
+ throw std::runtime_error(ss.str());
+ }
+
+ if (pid == 0) {
+ run_res = run_tests(argc, argv, version);
+ break;
+ }
+
+ waitpid(pid, &run_res, 0);
+ }
+#endif
+ res = std::max(res, run_res);
+ }
+
+ return res;
+}
\ No newline at end of file
diff --git a/modules/platforms/cpp/tests/test-common/detail/unix_process.h
b/modules/platforms/cpp/tests/test-common/detail/unix_process.h
index 7cfa5f5c79a..a94b18118fe 100644
--- a/modules/platforms/cpp/tests/test-common/detail/unix_process.h
+++ b/modules/platforms/cpp/tests/test-common/detail/unix_process.h
@@ -83,6 +83,7 @@ public:
}
std::vector<const char *> args;
+ args.reserve(m_args.size() + 2);
args.push_back(m_command.c_str());
for (auto &arg : m_args) {
diff --git a/modules/platforms/cpp/tests/test-common/ignite_runner.cpp
b/modules/platforms/cpp/tests/test-common/ignite_runner.cpp
index 614c9dbdfae..925b115dc94 100644
--- a/modules/platforms/cpp/tests/test-common/ignite_runner.cpp
+++ b/modules/platforms/cpp/tests/test-common/ignite_runner.cpp
@@ -16,17 +16,31 @@
*/
#include "ignite_runner.h"
+
+#include "ignite/client/ignite_client_configuration.h"
+#include "ignite_type.h"
#include "test_utils.h"
#include "ignite/common/detail/config.h"
#include <filesystem>
-#include <iostream>
#include <sstream>
#include <stdexcept>
+#ifdef WIN32
+# include <Windows.h>
+#endif
+
namespace {
+void set_environment_variable(const char* name, const char* value) {
+#ifdef WIN32
+ SetEnvironmentVariable(name, value);
+#else
+ setenv(name, value, 1);
+#endif
+}
+
/**
* System shell command string.
*/
@@ -34,19 +48,40 @@ constexpr std::string_view SYSTEM_SHELL =
IGNITE_SWITCH_WIN_OTHER("cmd.exe", "/b
constexpr std::string_view SYSTEM_SHELL_ARG_0 = IGNITE_SWITCH_WIN_OTHER("/c ",
"-c");
constexpr std::string_view GRADLEW_SCRIPT =
IGNITE_SWITCH_WIN_OTHER("gradlew.bat", "./gradlew");
-const std::string SERVER_ADDRESS = "127.0.0.1";
+const char* IGNITE_CLUSTER_HOST = getenv("IGNITE_CLUSTER_HOST");
+const std::string SERVER_ADDRESS = IGNITE_CLUSTER_HOST ? IGNITE_CLUSTER_HOST :
"127.0.0.1";
+
+const std::string ADDITIONAL_JVM_OPTIONS_ENV = "CPP_ADDITIONAL_JVM_OPTIONS";
} // anonymous namespace
namespace ignite {
+static int calc_node_port_offset(std::string_view version);
+
std::vector<std::string> ignite_runner::SINGLE_NODE_ADDR = {SERVER_ADDRESS +
":10942"};
std::vector<std::string> ignite_runner::NODE_ADDRS = {SERVER_ADDRESS +
":10942", SERVER_ADDRESS + ":10943"};
std::vector<std::string> ignite_runner::SSL_NODE_ADDRS = {SERVER_ADDRESS +
":10944"};
std::vector<std::string> ignite_runner::SSL_NODE_CA_ADDRS = {SERVER_ADDRESS +
":10945"};
+std::vector<std::string> ignite_runner::COMPATIBILITY_NODE_ADDRS = {};
+
+ignite_runner::ignite_runner(std::string_view version) : m_version(version) {
+ COMPATIBILITY_MODE = true;
+ COMPATIBILITY_NODE_ADDRS = {SERVER_ADDRESS + ":"
+ + std::to_string(ignite_client_configuration::DEFAULT_PORT +
calc_node_port_offset(version))};
+ COMPATIBILITY_VERSION = version;
+}
void ignite_runner::start() {
std::string home = resolve_ignite_home();
+
+ std::string work_dir;
+ if (m_version) {
+ work_dir = resolve_temp_dir("IgniteCompatibility",
*m_version).string();
+ } else {
+ work_dir = home;
+ }
+
if (home.empty())
throw std::runtime_error("Can not resolve Ignite home directory. Try
setting IGNITE_HOME explicitly");
@@ -54,14 +89,37 @@ void ignite_runner::start() {
args.emplace_back(SYSTEM_SHELL_ARG_0);
std::string command{GRADLEW_SCRIPT};
- command += " :ignite-runner:runnerPlatformTest"
- " --no-daemon"
- " -x compileJava"
- " -x compileTestFixturesJava"
- " -x compileIntegrationTestJava"
- " -x compileTestJava";
+
+ if (m_version) {
+ command += "
:ignite-compatibility-tests:runnerPlatformCompatibilityTest --parallel";
+ } else {
+ command += " :ignite-runner:runnerPlatformTest"
+ " --no-daemon"
+ " -x compileJava"
+ " -x compileTestFixturesJava"
+ " -x compileIntegrationTestJava"
+ " -x compileTestJava";
+ }
+
+ if (auto additional_opts = detail::get_env(ADDITIONAL_JVM_OPTIONS_ENV)) {
+ command += " " + *additional_opts;
+ }
args.emplace_back(command);
+ if (m_version) {
+ set_environment_variable("IGNITE_OLD_SERVER_VERSION",
m_version->c_str());
+ set_environment_variable("IGNITE_OLD_SERVER_WORK_DIR",
work_dir.c_str());
+
+ int node_port_offset = calc_node_port_offset(*m_version);
+
+ std::string port = std::to_string(3344 + 20000 + node_port_offset);
+ std::string http_port = std::to_string(10300 + node_port_offset);
+ std::string client_port =
std::to_string(ignite_client_configuration::DEFAULT_PORT + node_port_offset);
+
+ set_environment_variable("IGNITE_OLD_SERVER_PORT", port.c_str());
+ set_environment_variable("IGNITE_OLD_SERVER_HTTP_PORT",
http_port.c_str());
+ set_environment_variable("IGNITE_OLD_SERVER_CLIENT_PORT",
client_port.c_str());
+ }
m_process = CmdProcess::make(std::string(SYSTEM_SHELL), args, home);
if (!m_process->start()) {
@@ -85,4 +143,12 @@ void ignite_runner::join(std::chrono::milliseconds timeout)
{
m_process->join(timeout);
}
+static int calc_node_port_offset(std::string_view version) {
+ std::string port_shift;
+
+ std::copy_if(version.begin(), version.end(),
std::back_inserter(port_shift), [](auto c) { return c >= '0' && c <= '9'; });
+
+ return stoi(port_shift);
+}
+
} // namespace ignite
\ No newline at end of file
diff --git a/modules/platforms/cpp/tests/test-common/ignite_runner.h
b/modules/platforms/cpp/tests/test-common/ignite_runner.h
index 37484821384..f05872ea986 100644
--- a/modules/platforms/cpp/tests/test-common/ignite_runner.h
+++ b/modules/platforms/cpp/tests/test-common/ignite_runner.h
@@ -39,6 +39,17 @@ public:
static std::vector<std::string> NODE_ADDRS;
static std::vector<std::string> SSL_NODE_ADDRS;
static std::vector<std::string> SSL_NODE_CA_ADDRS;
+ static std::vector<std::string> COMPATIBILITY_NODE_ADDRS;
+ inline static bool COMPATIBILITY_MODE = false;
+ inline static std::string COMPATIBILITY_VERSION;
+
+ ignite_runner() = default;
+
+ /**
+ *
+ * @param version If present then should start server of that particular
version otherwise current version.
+ */
+ ignite_runner(std::string_view version);
/**
* Destructor.
@@ -75,6 +86,10 @@ public:
* @return Addresses.
*/
static std::vector<std::string> get_node_addrs() {
+ if (COMPATIBILITY_MODE) {
+ return COMPATIBILITY_NODE_ADDRS;
+ }
+
if (single_node_mode())
return SINGLE_NODE_ADDR;
@@ -98,6 +113,7 @@ public:
private:
/** Underlying process. */
std::unique_ptr<CmdProcess> m_process;
+ std::optional<std::string> m_version = std::nullopt;
};
} // namespace ignite
diff --git a/modules/platforms/cpp/tests/test-common/process.cpp
b/modules/platforms/cpp/tests/test-common/process.cpp
index a562c35d534..04979eb1ee0 100644
--- a/modules/platforms/cpp/tests/test-common/process.cpp
+++ b/modules/platforms/cpp/tests/test-common/process.cpp
@@ -31,7 +31,7 @@ namespace ignite {
std::unique_ptr<CmdProcess> CmdProcess::make(std::string command,
std::vector<std::string> args, std::string workDir) {
#ifdef _WIN32
- return std::unique_ptr<CmdProcess>(new
detail::WinProcess(std::move(command), std::move(args), std::move(workDir)));
+ return std::make_unique<detail::WinProcess>(std::move(command),
std::move(args), std::move(workDir));
#else
return std::unique_ptr<CmdProcess>(
new detail::UnixProcess(std::move(command), std::move(args),
std::move(workDir)));
diff --git a/modules/platforms/cpp/tests/test-common/test_utils.cpp
b/modules/platforms/cpp/tests/test-common/test_utils.cpp
index 790b4637286..620a98189a6 100644
--- a/modules/platforms/cpp/tests/test-common/test_utils.cpp
+++ b/modules/platforms/cpp/tests/test-common/test_utils.cpp
@@ -25,6 +25,7 @@
#include <functional>
#include <iostream>
#include <thread>
+#include <random>
namespace ignite {
@@ -99,6 +100,37 @@ std::filesystem::path resolve_test_dir() {
throw ignite_error("Can not find a 'tests' directory in the current Ignite
Home: " + home);
}
+std::filesystem::path resolve_temp_dir(std::string_view subDir,
std::string_view prefix) {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<> dis(0, 15);
+
+ std::stringstream ss;
+
+ if (!prefix.empty()) {
+ ss << prefix << "_";
+ }
+
+
+ for (int i = 0; i < 16; ++i) {
+ int num = dis(gen);
+
+ if (num <= 9) {
+ ss << num;
+ } else {
+ ss << static_cast<char>('a' + (num - 10));
+ }
+ }
+
+ auto path = std::filesystem::temp_directory_path();
+
+ if (!subDir.empty()) {
+ path /= subDir;
+ }
+
+ return path / ss.str();
+}
+
bool check_test_node_connectable(std::chrono::seconds timeout) {
try {
ensure_node_connectable(timeout);
diff --git a/modules/platforms/cpp/tests/test-common/test_utils.h
b/modules/platforms/cpp/tests/test-common/test_utils.h
index 88f57696421..9c661c3dae4 100644
--- a/modules/platforms/cpp/tests/test-common/test_utils.h
+++ b/modules/platforms/cpp/tests/test-common/test_utils.h
@@ -49,6 +49,14 @@ std::string resolve_ignite_home(const std::string &path =
"");
*/
std::filesystem::path resolve_test_dir();
+/**
+ * Generates a path in the temporary directory.
+ * @param subDir Optional subdirectory.
+ * @param prefix Optional prefix.
+ * @return Filesystem path to the generated temporary directory.
+ */
+std::filesystem::path resolve_temp_dir(std::string_view subDir = "",
std::string_view prefix = "");
+
/**
* Check async operation result and propagate error to the promise if there is
* any.