1 /* 2 Copyright (c) DataStax, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #ifndef __INTEGRATION_HPP__ 18 #define __INTEGRATION_HPP__ 19 20 #include <gtest/gtest.h> 21 22 #include <iostream> 23 #include <string> 24 #include <vector> 25 26 #include "cassandra.h" 27 28 #include "bridge.hpp" 29 #include "logger.hpp" 30 #include "objects.hpp" 31 #include "options.hpp" 32 #include "policies.hpp" 33 #include "pretty_print.hpp" 34 #include "test_utils.hpp" 35 #include "tlog.hpp" 36 #include "values.hpp" 37 38 // Macros for grouping tests together 39 #define GROUP_TEST_F(group_name, test_case, test_name) TEST_F(test_case, group_name##_##test_name) 40 #define GROUP_TEST(group_name, test_case, test_name) TEST(test_case, group_name##_##test_name) 41 #define GROUP_TYPED_TEST_P(group_name, test_case, test_name) \ 42 TYPED_TEST_P(test_case, group_name##_##test_name) 43 44 // Macros to use for grouping integration tests together 45 #define INTEGRATION_TEST(server_type, test_case, test_name) \ 46 GROUP_TEST(Integration##_##server_type, test_case, test_name) 47 #define INTEGRATION_TEST_F(server_type, test_case, test_name) \ 48 GROUP_TEST_F(Integration##_##server_type, test_case, test_name) 49 #define INTEGRATION_TYPED_TEST_P(server_type, test_case, test_name) \ 50 GROUP_TYPED_TEST_P(Integration##_##server_type, test_case, test_name) 51 #define INTEGRATION_DISABLED_TEST_F(server_type, test_case, test_name) \ 52 GROUP_TEST_F(DISABLED##_##Integration##_##server_type, test_case, test_name) 53 #define INTEGRATION_DISABLED_TYPED_TEST_P(server_type, test_case, test_name) \ 54 GROUP_TYPED_TEST_P(DISABLED##_##Integration##_##server_type, test_case, est_name) 55 56 // Macros to use for grouping Cassandra integration tests together 57 #define CASSANDRA_INTEGRATION_TEST(test_case, test_name) \ 58 INTEGRATION_TEST(Cassandra, test_case, test_name) 59 #define CASSANDRA_INTEGRATION_TEST_F(test_case, test_name) \ 60 INTEGRATION_TEST_F(Cassandra, test_case, test_name) 61 #define CASSANDRA_INTEGRATION_TYPED_TEST_P(test_case, test_name) \ 62 INTEGRATION_TYPED_TEST_P(Cassandra, test_case, test_name) 63 #define CASSANDRA_INTEGRATION_DISABLED_TEST_F(test_case, test_name) \ 64 INTEGRATION_DISABLED_TEST_F(Cassandra, test_case, test_name) 65 #define CASSANDRA_INTEGRATION_DISABLED_TYPED_TEST_P(test_case, test_name) \ 66 INTEGRATION_DISABLED_TYPED_TEST_P(Cassandra, test_case, test_name) 67 68 // TODO: Create SKIP_SUITE macro; reduces noise and makes sense for certain suites 69 70 #define SKIP_TEST(message) \ 71 if (!Integration::skipped_message_displayed_) { \ 72 std::cout << "[ SKIPPED ] " << message << std::endl; \ 73 Integration::skipped_message_displayed_ = true; \ 74 } \ 75 return; 76 77 #define CHECK_FAILURE \ 78 if (this->HasFailure()) { \ 79 return; \ 80 } 81 82 #define SKIP_TEST_VERSION(server_version_string, version_string) \ 83 SKIP_TEST("Unsupported for Apache Cassandra Version " \ 84 << server_version_string << ": Server version " << version_string << "+ is required") 85 86 #define CHECK_VERSION(version) \ 87 do { \ 88 CCM::CassVersion cass_version = this->server_version_; \ 89 if (!Options::is_cassandra()) { \ 90 cass_version = static_cast<CCM::DseVersion>(cass_version).get_cass_version(); \ 91 } \ 92 if (cass_version < #version) { \ 93 SKIP_TEST_VERSION(cass_version.to_string(), #version) \ 94 } \ 95 } while (0) 96 97 #define CHECK_OPTIONS_VERSION(version) \ 98 if (Options::server_version() < #version) { \ 99 SKIP_TEST_VERSION(Options::server_version().to_string(), #version) \ 100 } 101 102 #define CHECK_VALUE_TYPE_VERSION(type) \ 103 CCM::CassVersion cass_version = this->server_version_; \ 104 if (!Options::is_cassandra()) { \ 105 cass_version = static_cast<CCM::DseVersion>(cass_version).get_cass_version(); \ 106 } \ 107 if (cass_version < type::supported_server_version()) { \ 108 SKIP_TEST_VERSION(cass_version.to_string(), type::supported_server_version()) \ 109 } 110 111 #define CHECK_CONTINUE(flag, message) ASSERT_TRUE(flag) << message; 112 113 #define CASSANDRA_KEY_VALUE_TABLE_FORMAT \ 114 "CREATE TABLE IF NOT EXISTS %s (key %s PRIMARY KEY, value %s)" 115 #define CASSANDRA_KEY_VALUE_QUALIFIED_TABLE_FORMAT \ 116 "CREATE TABLE IF NOT EXISTS %s.%s (key %s PRIMARY KEY, value %s)" 117 #define CASSANDRA_KEY_VALUE_INSERT_FORMAT "INSERT INTO %s (key, value) VALUES(%s, %s)" 118 #define CASSANDRA_KEY_VALUE_QUALIFIED_INSERT_FORMAT "INSERT INTO %s.%s (key, value) VALUES(%s, %s)" 119 #define CASSANDRA_SELECT_VALUE_FORMAT "SELECT value FROM %s WHERE key=%s" 120 #define CASSANDRA_DELETE_ROW_FORMAT "DELETE FROM %s WHERE key=%s" 121 #define CASSANDRA_UPDATE_VALUE_FORMAT "UPDATE %s SET value=%s WHERE key=%s" 122 #define SELECT_ALL_SYSTEM_LOCAL_CQL "SELECT * FROM system.local" 123 #define SELECT_COUNT_FORMAT "SELECT COUNT(*) FROM %s LIMIT 1000000" 124 125 #define CASSANDRA_COMPOSITE_KEY_VALUE_TABLE_FORMAT \ 126 "CREATE TABLE IF NOT EXISTS %s (primary_key %s, column_key timeuuid, value %s, PRIMARY " \ 127 "KEY(primary_key, column_key))" 128 #define CASSANDRA_COMPOSITE_KEY_VALUE_INSERT_FORMAT \ 129 "INSERT INTO %s (primary_key, column_key, value) VALUES(%s, %s, %s)" 130 #define CASSANDRA_COMPOSITE_SELECT_VALUE_FORMAT "SELECT value FROM %s WHERE primary_key=%s" 131 132 using namespace test; 133 using namespace test::driver; 134 135 /** 136 * Statement type enumeration to use for specifying type of statement to use 137 * when executing queries 138 */ 139 enum StatementType { 140 /** 141 * Batch statement 142 */ 143 STATEMENT_TYPE_BATCH, 144 /** 145 * Prepared statement 146 */ 147 STATEMENT_TYPE_PREPARED, 148 /** 149 * Simple statement 150 */ 151 STATEMENT_TYPE_SIMPLE 152 }; 153 154 /** 155 * Base class to provide common integration test functionality 156 */ 157 class Integration : public testing::Test { 158 public: 159 Integration(); 160 161 virtual ~Integration(); 162 163 virtual void SetUp(); 164 165 virtual void TearDown(); 166 167 protected: 168 /** 169 * Flag to indicate the skipped message display state 170 */ 171 static bool skipped_message_displayed_; 172 /** 173 * Handle for interacting with CCM 174 */ 175 SharedPtr<CCM::Bridge, StdDeleter<CCM::Bridge> > ccm_; 176 /** 177 * Logger instance for handling driver log messages 178 */ 179 driver::Logger logger_; 180 /** 181 * Cluster instance 182 */ 183 Cluster cluster_; 184 /** 185 * Connected database session 186 */ 187 Session session_; 188 /** 189 * Generated keyspace name for the integration test 190 */ 191 std::string keyspace_name_; 192 /** 193 * Generated table name for the integration test 194 */ 195 std::string table_name_; 196 /** 197 * Keyspaces schema table 198 */ 199 std::string system_schema_keyspaces_; 200 /** 201 * UUID generator 202 */ 203 UuidGen uuid_generator_; 204 /** 205 * Version of Cassandra/DSE the session is connected to 206 */ 207 CCM::CassVersion server_version_; 208 /** 209 * Number of nodes in data center one 210 * (DEFAULT: 1) 211 */ 212 unsigned short number_dc1_nodes_; 213 /** 214 * Number of nodes in data center two 215 * (DEFAULT: 0) 216 */ 217 unsigned short number_dc2_nodes_; 218 /** 219 * Replication factor override; default is calculated based on number of data 220 * center nodes; single data center is (nodes / 2) rounded up 221 */ 222 unsigned short replication_factor_; 223 /** 224 * Replication configuration strategy 225 */ 226 std::string replication_strategy_; 227 /** 228 * Default contact points generated based on the number of nodes requested 229 */ 230 std::string contact_points_; 231 /** 232 * Setting for password authenticator. True if password authenticator should 233 * be enabled; false otherwise. 234 * (DEFAULT: false) 235 * 236 * NOTE: Username and password is 'cassandra' 237 */ 238 bool is_password_authenticator_; 239 /** 240 * Setting for client authentication. True if client authentication should be 241 * enabled; false otherwise. 242 * (DEFAULT: false) 243 */ 244 bool is_client_authentication_; 245 /** 246 * Setting for SSL authentication. True if SSL should be enabled; false 247 * otherwise. 248 * (DEFAULT: false) 249 */ 250 bool is_ssl_; 251 /** 252 * Setting for v-nodes usage. True if v-nodes should be enabled; false 253 * otherwise. 254 * (DEFAULT: false) 255 */ 256 bool is_with_vnodes_; 257 /** 258 * Setting for randomized contact points. True if randomized contact points 259 * should be enabled; false otherwise. 260 * (DEFAULT: false) 261 */ 262 bool is_randomized_contact_points_; 263 /** 264 * Setting for schema metadata. True if schema metadata should be enabled; 265 * false otherwise. 266 * (DEFAULT: false) 267 */ 268 bool is_schema_metadata_; 269 /** 270 * Setting to determine if CCM instance should be created. True if CCM instance 271 * should be created; false otherwise. 272 * (DEFAULT: true) 273 */ 274 bool is_ccm_requested_; 275 /** 276 * Setting to determine if CCM cluster should be started. True if CCM cluster 277 * should be started; false otherwise. 278 * (DEFAULT: true) 279 */ 280 bool is_ccm_start_requested_; 281 /** 282 * Setting to determine if CCM cluster should be started normally or nodes 283 * should be started individually (and in order). True if CCM cluster should 284 * be started by starting all node individually; false otherwise (start 285 * cluster normally). 286 * (DEFAULT: false) 287 */ 288 bool is_ccm_start_node_individually_; 289 /** 290 * Setting to determine if session connection should be established. True if 291 * session connection should be established; false otherwise. 292 * (DEFAULT: true) 293 */ 294 bool is_session_requested_; 295 /** 296 * Flag to indicate if the newly created keyspace should be set for the session connection. 297 * (DEFAULT: true) 298 */ 299 bool is_keyspace_change_requested_; 300 /** 301 * Flag to indicate if a test is chaotic and should have its CCM cluster 302 * destroyed 303 */ 304 bool is_test_chaotic_; 305 /** 306 * Flag to indicate if the beta protocol should be enabled. True if beta 307 * protocol should be enabled (Cassandra must be >= v3.10.0); false 308 * otherwise. 309 * (DEFAULT: true) 310 */ 311 bool is_beta_protocol_; 312 /** 313 * Workload to apply to the cluster 314 */ 315 std::vector<CCM::DseWorkload> dse_workload_; 316 /** 317 * Execution profiles to associate with default cluster 318 */ 319 ExecutionProfile::Map profiles_; 320 /** 321 * Protocol version to associate with default cluster 322 */ 323 int protocol_version_; 324 /** 325 * Name of the test case/suite 326 */ 327 std::string test_case_name_; 328 /** 329 * Name of the test 330 */ 331 std::string test_name_; 332 /** 333 * Vector of nodes that have been stopped 334 */ 335 std::vector<unsigned int> stopped_nodes_; 336 /** 337 * Vector of nodes that have been paused 338 */ 339 std::vector<unsigned int> paused_nodes_; 340 341 /** 342 * Get the default keyspace name (based on the current test case and test 343 * name) 344 * 345 * @return Default keyspace name 346 */ 347 virtual std::string default_keyspace(); 348 349 /** 350 * Get the default replication factor (based on the number of nodes in the 351 * standard two data center configuration for the test harness) 352 * 353 * @return Default replication factor 354 */ 355 virtual unsigned short default_replication_factor(); 356 357 /** 358 * Get the default replication strategy for the keyspace (based on the 359 * default replication factor or the overridden value assigned during the test 360 * case setup process) 361 * 362 * @return Default replication strategy 363 */ 364 virtual std::string default_replication_strategy(); 365 366 /** 367 * Get the default select all CQL statement 368 * 369 * @return Default CQL statement to select all elements in the row 370 */ 371 virtual std::string default_select_all(); 372 373 /** 374 * Get the row count using the default table name (based on the test name) 375 * 376 * @return The number of rows in the table 377 */ 378 virtual int64_t default_select_count(); 379 380 /** 381 * Get the default table name (based on the test name) 382 * 383 * @return Default table name 384 */ 385 virtual std::string default_table(); 386 387 /** 388 * Drop a table from the current keyspace 389 * 390 * @param table_name Table to drop from the current keyspace 391 */ 392 virtual void drop_table(const std::string& table_name); 393 394 /** 395 * Drop a type from the current keyspace 396 * 397 * @param type_name Table to drop from the current keyspace 398 */ 399 virtual void drop_type(const std::string& type_name); 400 401 /** 402 * Update the current keyspace used by the session 403 * 404 * @param keyspace_name Keyspace to use 405 * @return True if keyspace was changed; false otherwise 406 */ 407 virtual bool use_keyspace(const std::string& keyspace_name); 408 409 /** 410 * Establish the session connection using provided cluster object. 411 * 412 * @param cluster Cluster object to use when creating session connection 413 */ 414 virtual void connect(Cluster cluster); 415 416 /** 417 * Create the cluster configuration and establish the session connection using 418 * provided cluster object. 419 */ 420 virtual void connect(); 421 422 /** 423 * Get the default cluster configuration 424 * 425 * @param is_with_default_contact_points True if default contact points 426 should be added to the cluster 427 * @return Cluster object (default) 428 */ 429 virtual Cluster default_cluster(bool is_with_default_contact_points = true); 430 431 /** 432 * Enable/Disable tracing on the cluster 433 * 434 * @param enable True if tracing should be enabled on the cluster; false 435 * otherwise (default: true) 436 */ 437 virtual void enable_cluster_tracing(bool enable = true); 438 439 /** 440 * Decommission a node and force the cluster to be removed after the test is 441 * completed 442 * 443 * @param node Node that should be decommissioned 444 * @oaram is_force True if decommission should be forced; false otherwise 445 * (default: false) 446 * @return True if node was decommissioned; false otherwise (the node is 447 * invalid or was already decommissioned) 448 */ 449 virtual bool decommission_node(unsigned int node, bool is_force = false); 450 451 /** 452 * Decommission a node by force and ensure the cluster is set to be removed 453 * after the test is completed 454 * 455 * NOTE: Alias for decommission_node(node, true) 456 * 457 * @param node Node that should be decommissioned forcefully 458 * @return True if node was decommissioned; false otherwise (the node is 459 * invalid or was already decommissioned) 460 */ 461 virtual bool force_decommission_node(unsigned int node); 462 463 /** 464 * Start a node that was previously stopped to ensure that it is not restarted after test is 465 * completed; paused nodes are ignored 466 * 467 * @param node Node that should be started 468 * @return True if node was started; false otherwise (the node is invalid or was already 469 * started or is paused) 470 */ 471 virtual bool start_node(unsigned int node); 472 473 /** 474 * Stop a node that should be restarted after test is completed 475 * 476 * @param node Node that should be stopped 477 * @param is_kill True if forced termination requested; false otherwise 478 * (default: false) 479 * @return True if node was stopped; false otherwise (the node is invalid or 480 * was already stopped) 481 */ 482 virtual bool stop_node(unsigned int node, bool is_kill = false); 483 484 /** 485 * Pause a node that should be resumed after test is completed 486 * 487 * @param node Node that should be paused 488 * @return True if node was paused; false otherwise (the node is invalid or 489 * was already paused) 490 */ 491 virtual bool pause_node(unsigned int node); 492 493 /** 494 * Resume a node that was previously paused to ensure that it is not resumed after test is 495 * completed 496 * 497 * @param node Node that should be resumed 498 * @return True if node was resumed; false otherwise 499 */ 500 virtual bool resume_node(unsigned int node); 501 502 /** 503 * Generate the contact points for the cluster 504 * 505 * @param ip_address IP address prefix 506 * @param number_of_nodes Total number of nodes in the cluster 507 * @return Comma delimited IP address (e.g. contact points) 508 */ 509 std::string generate_contact_points(const std::string& ip_prefix, size_t number_of_nodes); 510 511 /** 512 * Variable argument string formatter 513 * 514 * @param format Format string that follows printf specifications 515 * @param ... Additional arguments; depends on the format string 516 */ 517 std::string format_string(const char* format, ...) const; 518 519 /** 520 * Calculate the elapsed time in milliseconds 521 * 522 * @return Elapsed time in milliseconds 523 */ elapsed_time()524 inline uint64_t elapsed_time() { 525 if (start_time_ > 0) { 526 return (uv_hrtime() - start_time_) / 1000000UL; 527 } 528 return 0; 529 } 530 531 /** 532 * Start the timer to calculate the elapsed time 533 */ start_timer()534 inline void start_timer() { start_time_ = uv_hrtime(); } 535 536 /** 537 * Stop the timer - Calculate the elapsed time and reset the timer 538 * 539 * @return Elapsed time in milliseconds 540 */ stop_timer()541 inline uint64_t stop_timer() { 542 uint64_t duration = elapsed_time(); 543 start_time_ = 0ull; 544 return duration; 545 } 546 547 /** 548 * Get the current working directory 549 * 550 * @return Current working directory 551 */ cwd()552 inline static std::string cwd() { return Utils::cwd(); } 553 554 /** 555 * Determine if a string contains another string 556 * 557 * @param input String being evaluated 558 * @param search String to find 559 * @return True if string is contained in other string; false otherwise 560 */ contains(const std::string & input,const std::string & search)561 inline static bool contains(const std::string& input, const std::string& search) { 562 return Utils::contains(input, search); 563 } 564 565 /** 566 * Split a string into an array/vector 567 * 568 * @param input String to convert to array/vector 569 * @param delimiter Character to use split into elements (default: <space>) 570 * @return An array/vector representation of the string 571 */ explode(const std::string & input,const char delimiter=' ')572 inline static std::vector<std::string> explode(const std::string& input, 573 const char delimiter = ' ') { 574 return Utils::explode(input, delimiter); 575 } 576 577 /** 578 * Check to see if a file exists 579 * 580 * @param filename Absolute/Relative filename 581 * @return True if file exists; false otherwise 582 */ file_exists(const std::string & filename)583 inline static bool file_exists(const std::string& filename) { 584 return Utils::file_exists(filename); 585 } 586 587 /** 588 * Concatenate or join a vector into a string 589 * 590 * @param elements Vector of strings to concatenate 591 * @param delimiter Character to use between elements (default: <space>) 592 * @return A string concatenating all the vector strings with delimiter 593 * separation 594 */ implode(const std::vector<std::string> & elements,const char delimiter=' ')595 inline static std::string implode(const std::vector<std::string>& elements, 596 const char delimiter = ' ') { 597 return Utils::implode<std::string>(elements, delimiter); 598 } 599 600 /** 601 * Create the directory from a path 602 * 603 * @param path Directory/Path to create 604 */ mkdir(const std::string & path)605 inline static void mkdir(const std::string& path) { Utils::mkdir(path); } 606 607 /** 608 * Cross platform millisecond granularity sleep 609 * 610 * @param milliseconds Time in milliseconds to sleep 611 */ msleep(unsigned int milliseconds)612 inline static void msleep(unsigned int milliseconds) { Utils::msleep(milliseconds); } 613 614 /** 615 * Replace all occurrences of a string from the input string 616 * 617 * @param input String having occurrences being replaced 618 * @param from String to find for replacement 619 * @param to String to replace with 620 * @return Input string with replacement 621 */ replace_all(const std::string & input,const std::string & from,const std::string & to)622 inline static std::string replace_all(const std::string& input, const std::string& from, 623 const std::string& to) { 624 return Utils::replace_all(input, from, to); 625 } 626 627 /** 628 * Convert a string to lowercase 629 * 630 * @param input String to convert to lowercase 631 */ to_lower(const std::string & input)632 inline static std::string to_lower(const std::string& input) { return Utils::to_lower(input); } 633 634 /** 635 * Remove the leading and trailing whitespace from a string 636 * 637 * @param input String to trim 638 * @return Trimmed string 639 */ trim(const std::string & input)640 inline static std::string trim(const std::string& input) { return Utils::trim(input); } 641 642 /** 643 * Shrink the given name if the name is longer than allowable by the server 644 * 645 * NOTE: This is for keyspaces, tables, and other misc server side items 646 * 647 * @param name Name to shrink if name too long 648 */ 649 void maybe_shrink_name(std::string& name); 650 651 /** 652 * Wait for the logger count to reach expected count 653 * 654 * NOTE: This may wait up to LOGGER_MAXIMUM_WAIT_TIME_MS 655 * 656 * @param expected_count Expected logger count 657 * @return True if expected count is equal to logger count; false otherwise 658 */ 659 bool wait_for_logger(size_t expected_count); 660 661 /** 662 * Get the Murmur3 hash for a given value 663 * 664 * @param value Value to calculate Murmur3 hash for 665 * @return Murmur3 hash for value 666 */ 667 int64_t murmur3_hash(const std::string& value); 668 669 /** 670 * Get the time since epoch in microseconds 671 * 672 * @return Time since epoch in microseconds 673 */ 674 uint64_t time_since_epoch_in_ms(); 675 676 /** 677 * Get the time since epoch in microseconds 678 * 679 * @return Time since epoch in microseconds 680 */ 681 uint64_t time_since_epoch_us(); 682 683 /** 684 * Get the driver name as configured by the driver 685 * 686 * @return Driver name 687 */ 688 std::string driver_name(); 689 690 /** 691 * Get the driver version as configured by the driver 692 * 693 * @return Driver version 694 */ 695 std::string driver_version(); 696 697 private: 698 /** 699 * Keyspace creation query (generated via SetUp) 700 */ 701 std::string create_keyspace_query_; 702 /** 703 * High-resolution real time when the timer was started (in nanoseconds) 704 */ 705 uint64_t start_time_; 706 }; 707 708 #endif //__INTEGRATION_HPP__ 709