mirror of https://github.com/sysown/proxysql
Signed-off-by: Wazir Ahmed <wazir@proxysql.com>feature/gtid-range-update
parent
57edeb5ce0
commit
06b6343dca
@ -1,171 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tap.h"
|
||||
#include "unit_test.h"
|
||||
#include "proxysql_gtid.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
int testGtidIntervalFromString_Count() {
|
||||
return 4;
|
||||
}
|
||||
void testGtidIntervalFromString() {
|
||||
ok(gtid_interval_t("123-456") == gtid_interval_t(123, 456), "GTID interval from range C string");
|
||||
ok(gtid_interval_t(std::string("789-1234")) == gtid_interval_t(789, 1234), "GTID interval from range C++ string");
|
||||
ok(gtid_interval_t("111") == gtid_interval_t(111, 111), "GTID interval from single GTID C string");
|
||||
ok(gtid_interval_t(std::string("222")) == gtid_interval_t(222, 222), "GTID interval from single GTID C++ string");
|
||||
}
|
||||
|
||||
int testGtidIntervalContains_Count() {
|
||||
return 8;
|
||||
}
|
||||
void testGtidIntervalContains() {
|
||||
auto iv = gtid_interval_t(123, 456);
|
||||
|
||||
ok(iv.contains(123), "GTID interval contains start");
|
||||
ok(iv.contains(456), "GTID interval contains end");
|
||||
ok(iv.contains(300), "GTID interval contains middle");
|
||||
ok(!iv.contains(100), "GTID interval doesn't contain before start");
|
||||
ok(!iv.contains(500), "GTID interval doesn't contain past end");
|
||||
ok(!iv.contains(gtid_interval_t(100, 300)), "GTID interval doesn't contain range before start");
|
||||
ok(!iv.contains(gtid_interval_t(300, 500)), "GTID interval doesn't contain range past end");
|
||||
ok(iv.contains(gtid_interval_t(150, 310)), "GTID interval contains range");
|
||||
}
|
||||
|
||||
int testGtidIntervalAppend_Count() {
|
||||
return 7;
|
||||
}
|
||||
void testGtidIntervalAppend() {
|
||||
auto iv = gtid_interval_t(123, 456);
|
||||
|
||||
ok(!iv.append(gtid_interval_t(90, 100)), "cannot append before range start");
|
||||
ok(!iv.append(gtid_interval_t(100, 200)), "cannot append at start");
|
||||
ok(!iv.append(gtid_interval_t(500, 600)), "cannot append past end");
|
||||
ok(iv.append(gtid_interval_t(457, 490)), "append");
|
||||
ok(iv.to_string() == "123-490", "append result");
|
||||
|
||||
iv = gtid_interval_t(123, 456);
|
||||
ok(iv.append(gtid_interval_t(200, 600)), "append with overlap");
|
||||
ok(iv.to_string() == "123-600", "append with overlap result");
|
||||
}
|
||||
|
||||
int testGtidIntervalMerge_Count() {
|
||||
return 14;
|
||||
}
|
||||
void testGtidIntervalMerge() {
|
||||
auto iv = gtid_interval_t(123, 456);
|
||||
ok(!iv.merge(gtid_interval_t(90, 100)), "cannot merge before range start");
|
||||
ok(!iv.merge(gtid_interval_t(500, 600)), "cannot merge past range end");
|
||||
ok(iv.merge(gtid_interval_t(90, 200)), "merge at start");
|
||||
auto want = gtid_interval_t(90, 456);
|
||||
ok(iv == want, "merge at start result");
|
||||
|
||||
iv = gtid_interval_t(123, 456);
|
||||
ok(iv.merge(gtid_interval_t(300, 500)), "merge at end");
|
||||
want = gtid_interval_t(123, 500);
|
||||
ok(iv == want, "merge at end result");
|
||||
|
||||
iv = gtid_interval_t(123, 456);
|
||||
ok(iv.merge(gtid_interval_t(200, 300)), "merge at middle");
|
||||
want = gtid_interval_t(123, 456);
|
||||
ok(iv == want, "merge at middle result");
|
||||
|
||||
iv = gtid_interval_t(123, 456);
|
||||
ok(iv.merge(gtid_interval_t(100, 500)), "merge overlap");
|
||||
want = gtid_interval_t(100, 500);
|
||||
ok(iv == want, "merge overlap result");
|
||||
|
||||
iv = gtid_interval_t(123, 456);
|
||||
ok(iv.merge(gtid_interval_t(100, 122)), "merge append at start");
|
||||
want = gtid_interval_t(100, 456);
|
||||
ok(iv == want, "merge append at start result");
|
||||
|
||||
iv = gtid_interval_t(123, 456);
|
||||
want = gtid_interval_t(123, 600);
|
||||
ok(iv.merge(gtid_interval_t(457, 600)), "merge append at end");
|
||||
ok(iv == want, "merge append at end result");
|
||||
}
|
||||
|
||||
int testGtidSetAdd_Count() {
|
||||
return 9;
|
||||
}
|
||||
void testGtidSetAdd() {
|
||||
gtid_set_t gtid_set;
|
||||
|
||||
ok(gtid_set.add("aaaaaaaa000011112222aaaaaaaaaaaa", gtid_interval_t(10, 20)), "new GTID range for server A");
|
||||
ok(!gtid_set.add("aaaaaaaa000011112222aaaaaaaaaaaa", gtid_t(14)), "single GTID range for server A, contained");
|
||||
ok(gtid_set.add("aaaaaaaa000011112222aaaaaaaaaaaa", "9-22"), "new GTID range with partial overlap for server A");
|
||||
ok(gtid_set.add("aaaaaaaa000011112222aaaaaaaaaaaa", std::string("18-30")), "new GTID range with partial overlap for server A");
|
||||
ok(!gtid_set.add("aaaaaaaa000011112222aaaaaaaaaaaa", gtid_interval_t(15, 22)), "GTID range is already fully contained for server A");
|
||||
ok(gtid_set.add("aaaaaaaa000011112222aaaaaaaaaaaa", gtid_t(40), gtid_t(50)), "new GTID range with gap for server A");
|
||||
ok(gtid_set.add("bbbbbbbb333344445555bbbbbbbbbbbb", gtid_interval_t(10, 30)), "new GTID range for server B");
|
||||
ok(gtid_set.add("bbbbbbbb333344445555bbbbbbbbbbbb", "31-50"), "new GTID range for server B");
|
||||
|
||||
ok(gtid_set.to_string() == "aaaaaaaa-0000-1111-2222-aaaaaaaaaaaa:10-30,aaaaaaaa-0000-1111-2222-aaaaaaaaaaaa:40-50,bbbbbbbb-3333-4444-5555-bbbbbbbbbbbb:10-50", "add interval result");
|
||||
}
|
||||
|
||||
int testGtidSetContains_Count() {
|
||||
return 19;
|
||||
}
|
||||
void testGtidSetContains() {
|
||||
gtid_set_t gtid_set;
|
||||
|
||||
ok(gtid_set.add("aaaaaaaa000011112222aaaaaaaaaaaa", "10-20"), "new GTID range for server A");
|
||||
ok(gtid_set.add("aaaaaaaa000011112222aaaaaaaaaaaa", "30-40"), "split GTID range for server A");
|
||||
ok(gtid_set.add("bbbbbbbb333344445555bbbbbbbbbbbb", gtid_interval_t(100, 200)), "new GTID range for server B");
|
||||
|
||||
ok(!gtid_set.has_gtid("aaaaaaaa000011112222aaaaaaaaaaaa", 7), "before GTID range for server A");
|
||||
ok(gtid_set.has_gtid("aaaaaaaa000011112222aaaaaaaaaaaa", 10), "in GTID range for server A");
|
||||
ok(gtid_set.has_gtid("aaaaaaaa000011112222aaaaaaaaaaaa", 16), "in GTID range for server A");
|
||||
ok(gtid_set.has_gtid("aaaaaaaa000011112222aaaaaaaaaaaa", 20), "in GTID range for server A");
|
||||
ok(!gtid_set.has_gtid("aaaaaaaa000011112222aaaaaaaaaaaa", 55), "past GTID range for server A");
|
||||
|
||||
ok(!gtid_set.has_gtid("bbbbbbbb333344445555bbbbbbbbbbbb", 74), "before GTID range for server B");
|
||||
ok(gtid_set.has_gtid("bbbbbbbb333344445555bbbbbbbbbbbb", 100), "in GTID range for server B");
|
||||
ok(gtid_set.has_gtid("bbbbbbbb333344445555bbbbbbbbbbbb", 168), "in GTID range for server B");
|
||||
ok(gtid_set.has_gtid("bbbbbbbb333344445555bbbbbbbbbbbb", 200), "in GTID range for server B");
|
||||
ok(!gtid_set.has_gtid("bbbbbbbb333344445555bbbbbbbbbbbb", 201), "past GTID range for server B");
|
||||
|
||||
gtid_set.clear();
|
||||
|
||||
ok(!gtid_set.has_gtid("aaaaaaaa000011112222aaaaaaaaaaaa", 7), "no GTID ranges after clear for server A");
|
||||
ok(!gtid_set.has_gtid("aaaaaaaa000011112222aaaaaaaaaaaa", 16), "no GTID ranges after clear for server A");
|
||||
ok(!gtid_set.has_gtid("aaaaaaaa000011112222aaaaaaaaaaaa", 55), "no GTID ranges after clear for server A");
|
||||
|
||||
ok(!gtid_set.has_gtid("bbbbbbbb333344445555bbbbbbbbbbbb", 74), "no GTID ranges after clear for server B");
|
||||
ok(!gtid_set.has_gtid("bbbbbbbb333344445555bbbbbbbbbbbb", 168), "no GTID ranges after clear for server B");
|
||||
ok(!gtid_set.has_gtid("bbbbbbbb333344445555bbbbbbbbbbbb", 345), "no GTID ranges after clear for server B");
|
||||
}
|
||||
|
||||
std::function<int(void)> testFunctionCounts[] = {
|
||||
testGtidIntervalFromString_Count,
|
||||
testGtidIntervalContains_Count,
|
||||
testGtidIntervalAppend_Count,
|
||||
testGtidIntervalMerge_Count,
|
||||
testGtidSetAdd_Count,
|
||||
testGtidSetContains_Count,
|
||||
};
|
||||
std::function<void(void)> testFunctions[] = {
|
||||
testGtidIntervalFromString,
|
||||
testGtidIntervalContains,
|
||||
testGtidIntervalAppend,
|
||||
testGtidIntervalMerge,
|
||||
testGtidSetAdd,
|
||||
testGtidSetContains,
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Set up unit tests...
|
||||
int n = 0;
|
||||
for (auto f : testFunctionCounts) {
|
||||
n += f();
|
||||
}
|
||||
plan(n);
|
||||
|
||||
// ...and run them.
|
||||
for (auto f : testFunctions) {
|
||||
f();
|
||||
}
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
@ -0,0 +1,312 @@
|
||||
/**
|
||||
* @file gtid_set_unit-t.cpp
|
||||
* @brief Unit tests for GTID_Set.
|
||||
*
|
||||
* Tests the pure data-structure operations in lib/proxysql_gtid.cpp:
|
||||
* - GTID_Set::add() overloads — add intervals, single trxids, string ranges
|
||||
* - GTID_Set::has_gtid() — membership checks across UUID-keyed intervals
|
||||
* - GTID_Set::to_string() — serialization to MySQL GTID format
|
||||
* - GTID_Set::clear() — reset to empty state
|
||||
* - GTID_Set::copy() — deep copy with independence
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tap.h"
|
||||
|
||||
#include "proxysql_gtid.h"
|
||||
|
||||
static const std::string UUID_A = "aaaaaaaa000011112222aaaaaaaaaaaa";
|
||||
static const std::string UUID_B = "bbbbbbbb333344445555bbbbbbbbbbbb";
|
||||
|
||||
/**
|
||||
* @brief add() with TrxId_Interval: new range, append fast-path, partial overlap, gap.
|
||||
*/
|
||||
static void test_add_interval() {
|
||||
GTID_Set gs;
|
||||
|
||||
ok(gs.add(UUID_A, TrxId_Interval(10, 20)), "add interval: new range for UUID A");
|
||||
ok(gs.add(UUID_A, TrxId_Interval(15, 22)), "add interval: contained range handled via append fast-path");
|
||||
ok(gs.add(UUID_A, TrxId_Interval(9, 22)), "add interval: partial overlap extends");
|
||||
ok(gs.add(UUID_A, TrxId_Interval(40, 50)), "add interval: gap creates new interval");
|
||||
ok(gs.map.size() == 1, "add interval: one UUID");
|
||||
ok(gs.map[UUID_A].size() == 2, "add interval: two intervals");
|
||||
auto it = gs.map[UUID_A].begin();
|
||||
ok(it->start == 9 && it->end == 22, "add interval: first interval is [9,22]");
|
||||
++it;
|
||||
ok(it->start == 40 && it->end == 50, "add interval: second interval is [40,50]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add() with a single trxid_t: new entry and duplicate does not create an extra interval.
|
||||
*/
|
||||
static void test_add_trxid() {
|
||||
GTID_Set gs;
|
||||
|
||||
ok(gs.add(UUID_A, trxid_t(14)), "add single: new trxid");
|
||||
gs.add(UUID_A, trxid_t(14));
|
||||
ok(gs.map[UUID_A].size() == 1, "add single: duplicate does not create new interval");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add() with C string range for single and multiple UUIDs.
|
||||
*/
|
||||
static void test_add_cstring_range() {
|
||||
GTID_Set gs;
|
||||
|
||||
ok(gs.add(UUID_A, "9-22"), "add C string range: new range");
|
||||
ok(gs.add(UUID_B, "31-50"), "add C string range: new range for UUID B");
|
||||
ok(gs.map.size() == 2, "add C string range: two UUIDs");
|
||||
auto it_a = gs.map[UUID_A].begin();
|
||||
ok(it_a->start == 9 && it_a->end == 22, "add C string range: UUID A interval is [9,22]");
|
||||
auto it_b = gs.map[UUID_B].begin();
|
||||
ok(it_b->start == 31 && it_b->end == 50, "add C string range: UUID B interval is [31,50]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add() with std::string range.
|
||||
*/
|
||||
static void test_add_string_range() {
|
||||
GTID_Set gs;
|
||||
|
||||
ok(gs.add(UUID_A, std::string("18-30")), "add string range: new range");
|
||||
ok(gs.map.size() == 1, "add string range: one UUID");
|
||||
auto it = gs.map[UUID_A].begin();
|
||||
ok(it->start == 18 && it->end == 30, "add string range: interval is [18,30]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add() with explicit start, end parameters.
|
||||
*/
|
||||
static void test_add_start_end() {
|
||||
GTID_Set gs;
|
||||
|
||||
ok(gs.add(UUID_A, trxid_t(40), trxid_t(50)), "add start,end: new range");
|
||||
ok(gs.map.size() == 1, "add start,end: one UUID");
|
||||
auto it = gs.map[UUID_A].begin();
|
||||
ok(it->start == 40 && it->end == 50, "add start,end: interval is [40,50]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Multiple UUIDs are tracked independently in the same GTID_Set.
|
||||
*/
|
||||
static void test_add_multi_uuid() {
|
||||
GTID_Set gs;
|
||||
|
||||
gs.add(UUID_A, TrxId_Interval(10, 20));
|
||||
gs.add(UUID_B, TrxId_Interval(30, 40));
|
||||
|
||||
ok(gs.map.count(UUID_A) == 1, "multi-uuid: UUID A exists");
|
||||
ok(gs.map.count(UUID_B) == 1, "multi-uuid: UUID B exists");
|
||||
ok(gs.map.size() == 2, "multi-uuid: exactly two UUIDs");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add() with consecutive trxid values (1,2,3) merges into a single interval [1,3].
|
||||
*/
|
||||
static void test_add_consecutive_trxid() {
|
||||
GTID_Set gs;
|
||||
|
||||
gs.add(UUID_A, trxid_t(1));
|
||||
gs.add(UUID_A, trxid_t(2));
|
||||
gs.add(UUID_A, trxid_t(3));
|
||||
|
||||
ok(gs.map[UUID_A].size() == 1, "consecutive adds: merged into one interval");
|
||||
auto& iv = gs.map[UUID_A].front();
|
||||
ok(iv.start == 1 && iv.end == 3, "consecutive adds: interval is [1,3]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add() with non-consecutive trxid values (1,5) produces two separate intervals.
|
||||
*/
|
||||
static void test_add_non_consecutive_trxid() {
|
||||
GTID_Set gs;
|
||||
|
||||
gs.add(UUID_A, trxid_t(1));
|
||||
gs.add(UUID_A, trxid_t(5));
|
||||
|
||||
ok(gs.map[UUID_A].size() == 2, "non-consecutive adds: two separate intervals");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add() with reverse-order trxid values (5,4,3,2,1) still produces [1,5].
|
||||
*/
|
||||
static void test_add_reverse_order_trxid() {
|
||||
GTID_Set gs;
|
||||
|
||||
for (trxid_t i = 5; i >= 1; i--) {
|
||||
gs.add(UUID_A, i);
|
||||
}
|
||||
|
||||
ok(gs.map[UUID_A].size() == 1, "reverse order: merged into one interval");
|
||||
auto& iv = gs.map[UUID_A].front();
|
||||
ok(iv.start == 1 && iv.end == 5, "reverse order: interval is [1,5]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add() with a duplicate trxid does not create a new interval.
|
||||
*/
|
||||
static void test_add_duplicate_trxid() {
|
||||
GTID_Set gs;
|
||||
|
||||
gs.add(UUID_A, trxid_t(3));
|
||||
gs.add(UUID_A, trxid_t(3));
|
||||
|
||||
ok(gs.map[UUID_A].size() == 1, "duplicate single: still one interval");
|
||||
auto& iv = gs.map[UUID_A].front();
|
||||
ok(iv.start == 3 && iv.end == 3, "duplicate single: interval unchanged [3,3]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add() merges two intervals when a trxid fills the gap between them.
|
||||
*/
|
||||
static void test_add_merge_trxid() {
|
||||
GTID_Set gs;
|
||||
|
||||
gs.add(UUID_A, trxid_t(1));
|
||||
gs.add(UUID_A, trxid_t(3));
|
||||
ok(gs.map[UUID_A].size() == 2, "bridge merge: two intervals before bridge");
|
||||
|
||||
gs.add(UUID_A, trxid_t(2));
|
||||
ok(gs.map[UUID_A].size() == 1, "bridge merge: merged into one interval");
|
||||
auto& iv = gs.map[UUID_A].front();
|
||||
ok(iv.start == 1 && iv.end == 3, "bridge merge: interval is [1,3]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief add() merges two intervals when an interval fills the gap between them.
|
||||
*/
|
||||
static void test_add_merge_interval() {
|
||||
GTID_Set gs;
|
||||
|
||||
gs.add(UUID_A, TrxId_Interval(1, 10));
|
||||
gs.add(UUID_A, TrxId_Interval(20, 30));
|
||||
ok(gs.map[UUID_A].size() == 2, "gap fill: two intervals before fill");
|
||||
|
||||
gs.add(UUID_A, TrxId_Interval(11, 19));
|
||||
ok(gs.map[UUID_A].size() == 1, "gap fill: merged into one interval");
|
||||
auto& iv = gs.map[UUID_A].front();
|
||||
ok(iv.start == 1 && iv.end == 30, "gap fill: interval is [1,30]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief has_gtid() at boundaries, in gap, past ranges, and for unknown UUIDs.
|
||||
*/
|
||||
static void test_has_gtid() {
|
||||
GTID_Set gs;
|
||||
gs.add(UUID_A, "10-20");
|
||||
gs.add(UUID_A, "30-40");
|
||||
gs.add(UUID_B, TrxId_Interval(100, 200));
|
||||
|
||||
ok(!gs.has_gtid("cccccccc666677778888cccccccccccc", 10), "has_gtid: unknown UUID returns false");
|
||||
|
||||
ok(!gs.has_gtid(UUID_A, 7), "has_gtid: before range");
|
||||
ok(gs.has_gtid(UUID_A, 10), "has_gtid: at start");
|
||||
ok(gs.has_gtid(UUID_A, 16), "has_gtid: in range");
|
||||
ok(gs.has_gtid(UUID_A, 20), "has_gtid: at end");
|
||||
ok(!gs.has_gtid(UUID_A, 25), "has_gtid: in gap");
|
||||
ok(!gs.has_gtid(UUID_A, 55), "has_gtid: past all ranges");
|
||||
|
||||
ok(!gs.has_gtid(UUID_B, 74), "has_gtid: before range (UUID B)");
|
||||
ok(gs.has_gtid(UUID_B, 100), "has_gtid: at start (UUID B)");
|
||||
ok(gs.has_gtid(UUID_B, 168), "has_gtid: in range (UUID B)");
|
||||
ok(gs.has_gtid(UUID_B, 200), "has_gtid: at end (UUID B)");
|
||||
ok(!gs.has_gtid(UUID_B, 201), "has_gtid: past range (UUID B)");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief clear() removes all entries; has_gtid() returns false afterwards.
|
||||
*/
|
||||
static void test_clear() {
|
||||
GTID_Set gs;
|
||||
|
||||
gs.add(UUID_A, "10-20");
|
||||
gs.add(UUID_B, "100-200");
|
||||
|
||||
gs.clear();
|
||||
|
||||
ok(!gs.has_gtid(UUID_A, 16), "clear: UUID A gone");
|
||||
ok(!gs.has_gtid(UUID_B, 168), "clear: UUID B gone");
|
||||
ok(gs.map.empty(), "clear: map is empty");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief to_string() serialization: empty set, single interval, and multiple intervals/UUIDs.
|
||||
*/
|
||||
static void test_to_string() {
|
||||
// empty set
|
||||
{
|
||||
GTID_Set gs;
|
||||
ok(gs.to_string().empty(), "to_string: empty set returns empty string");
|
||||
}
|
||||
|
||||
// single UUID with single interval
|
||||
{
|
||||
GTID_Set gs;
|
||||
gs.add("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", TrxId_Interval(1, 10));
|
||||
std::string result = gs.to_string();
|
||||
std::string expected = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1-10";
|
||||
ok(result == expected, "to_string: single UUID single interval matches");
|
||||
}
|
||||
|
||||
// multiple intervals and UUIDs
|
||||
{
|
||||
GTID_Set gs;
|
||||
gs.add(UUID_A, TrxId_Interval(10, 20));
|
||||
gs.add(UUID_A, "9-22");
|
||||
gs.add(UUID_A, std::string("18-30"));
|
||||
gs.add(UUID_A, TrxId_Interval(40, 50));
|
||||
gs.add(UUID_B, TrxId_Interval(10, 30));
|
||||
gs.add(UUID_B, "31-50");
|
||||
std::string result = gs.to_string();
|
||||
ok(result.find("aaaaaaaa-0000-1111-2222-aaaaaaaaaaaa:9-30") != std::string::npos,
|
||||
"to_string: contains UUID A range 9-30");
|
||||
ok(result.find("aaaaaaaa-0000-1111-2222-aaaaaaaaaaaa:40-50") != std::string::npos,
|
||||
"to_string: contains UUID A range 40-50");
|
||||
ok(result.find("bbbbbbbb-3333-4444-5555-bbbbbbbbbbbb:10-50") != std::string::npos,
|
||||
"to_string: contains UUID B range 10-50");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief copy() produces a deep copy with the same data, and modifying the copy does not affect the original.
|
||||
*/
|
||||
static void test_copy() {
|
||||
GTID_Set gs;
|
||||
|
||||
gs.add(UUID_A, TrxId_Interval(10, 20));
|
||||
gs.add(UUID_B, TrxId_Interval(30, 40));
|
||||
|
||||
GTID_Set cp = gs.copy();
|
||||
|
||||
ok(cp.map.size() == 2, "copy: same number of UUIDs");
|
||||
ok(cp.has_gtid(UUID_A, 15), "copy: has UUID A trxid");
|
||||
ok(cp.has_gtid(UUID_B, 35), "copy: has UUID B trxid");
|
||||
|
||||
cp.add(UUID_A, TrxId_Interval(30, 40));
|
||||
|
||||
ok(!gs.has_gtid(UUID_A, 35), "copy: original not affected by copy mutation");
|
||||
ok(cp.has_gtid(UUID_A, 35), "copy: copy has new data");
|
||||
}
|
||||
|
||||
int main() {
|
||||
plan(62);
|
||||
|
||||
test_add_interval(); // 8 assertions
|
||||
test_add_trxid(); // 2 assertions
|
||||
test_add_cstring_range(); // 5 assertions
|
||||
test_add_string_range(); // 3 assertions
|
||||
test_add_start_end(); // 3 assertions
|
||||
test_add_multi_uuid(); // 3 assertions
|
||||
test_add_consecutive_trxid(); // 2 assertions
|
||||
test_add_non_consecutive_trxid(); // 1 assertion
|
||||
test_add_reverse_order_trxid(); // 2 assertions
|
||||
test_add_duplicate_trxid(); // 2 assertions
|
||||
test_add_merge_trxid(); // 3 assertions
|
||||
test_add_merge_interval(); // 3 assertions
|
||||
test_has_gtid(); // 12 assertions
|
||||
test_clear(); // 3 assertions
|
||||
test_to_string(); // 5 assertions
|
||||
test_copy(); // 5 assertions
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
@ -0,0 +1,202 @@
|
||||
/**
|
||||
* @file trxid_interval_unit-t.cpp
|
||||
* @brief Unit tests for TrxId_Interval.
|
||||
*
|
||||
* Tests the pure data-structure operations in lib/proxysql_gtid.cpp:
|
||||
* - TrxId_Interval constructors — from range, single, C string, C++ string
|
||||
* - contains() — membership checks for trxid and interval
|
||||
* - to_string() — serialization to "start-end" or "n" format
|
||||
* - append() — extend interval at end
|
||||
* - merge() — merge two overlapping/adjacent intervals
|
||||
* - cmp() and comparison operators
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tap.h"
|
||||
|
||||
#include "proxysql_gtid.h"
|
||||
|
||||
/**
|
||||
* @brief Construct from (start, end) in normal and reversed order.
|
||||
*
|
||||
* Verifies that reversed arguments are swapped so start <= end.
|
||||
*/
|
||||
static void test_construct_from_range() {
|
||||
TrxId_Interval iv(123, 456);
|
||||
ok(iv.start == 123 && iv.end == 456, "construct from (start, end): normal order");
|
||||
|
||||
TrxId_Interval iv_rev(456, 123);
|
||||
ok(iv_rev.start == 123 && iv_rev.end == 456, "construct from (start, end): reversed order swaps");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct from a single trxid_t value.
|
||||
*
|
||||
* Produces a degenerate interval [n,n].
|
||||
*/
|
||||
static void test_construct_from_single() {
|
||||
TrxId_Interval iv(42);
|
||||
ok(iv.start == 42 && iv.end == 42, "construct from single trxid: [42,42]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct from string literal in "start-end" and "n" formats.
|
||||
*/
|
||||
static void test_construct_from_string_literal() {
|
||||
TrxId_Interval iv_range("123-456");
|
||||
ok(iv_range.start == 123 && iv_range.end == 456, "construct from string literal range: 123-456");
|
||||
|
||||
TrxId_Interval iv_single("111");
|
||||
ok(iv_single.start == 111 && iv_single.end == 111, "construct from string literal single: 111");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct from std::string in "start-end" and "n" formats.
|
||||
*/
|
||||
static void test_construct_from_string() {
|
||||
TrxId_Interval iv_range(std::string("789-1234"));
|
||||
ok(iv_range.start == 789 && iv_range.end == 1234, "construct from C++ string range: 789-1234");
|
||||
|
||||
TrxId_Interval iv_single(std::string("222"));
|
||||
ok(iv_single.start == 222 && iv_single.end == 222, "construct from C++ string single: 222");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct from negative values.
|
||||
*/
|
||||
static void test_construct_negative() {
|
||||
TrxId_Interval iv(-10, -5);
|
||||
ok(iv.start == -10 && iv.end == -5, "construct from negative range: [-10,-5]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief contains(trxid) at boundaries, middle, and outside the interval.
|
||||
*/
|
||||
static void test_contains_trxid() {
|
||||
TrxId_Interval iv(123, 456);
|
||||
|
||||
ok(iv.contains(123), "contains trxid: start boundary");
|
||||
ok(iv.contains(456), "contains trxid: end boundary");
|
||||
ok(iv.contains(300), "contains trxid: middle");
|
||||
ok(!iv.contains(100), "contains trxid: before start");
|
||||
ok(!iv.contains(500), "contains trxid: past end");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief contains(TrxId_Interval) for partial, full, and exact containment.
|
||||
*/
|
||||
static void test_contains_interval() {
|
||||
TrxId_Interval iv(123, 456);
|
||||
|
||||
ok(!iv.contains(TrxId_Interval(100, 300)), "contains interval: range before start");
|
||||
ok(!iv.contains(TrxId_Interval(300, 500)), "contains interval: range past end");
|
||||
ok(iv.contains(TrxId_Interval(150, 310)), "contains interval: fully contained");
|
||||
ok(iv.contains(TrxId_Interval(123, 456)), "contains interval: exact match");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief to_string() produces "start-end" for ranges and "n" for single values.
|
||||
*/
|
||||
static void test_to_string() {
|
||||
ok(TrxId_Interval(123, 456).to_string() == "123-456", "to_string: range format");
|
||||
ok(TrxId_Interval(111).to_string() == "111", "to_string: single format");
|
||||
ok(TrxId_Interval(0, 0).to_string() == "0", "to_string: zero interval");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief append() extends the interval at end, rejects before start and past end.
|
||||
*/
|
||||
static void test_append() {
|
||||
TrxId_Interval iv(123, 456);
|
||||
|
||||
ok(!iv.append(TrxId_Interval(90, 100)), "append: before start, rejected");
|
||||
ok(!iv.append(TrxId_Interval(100, 200)), "append: overlapping at start, rejected");
|
||||
ok(!iv.append(TrxId_Interval(500, 600)), "append: past end with gap, rejected");
|
||||
|
||||
ok(iv.append(TrxId_Interval(457, 490)), "append: adjacent at end");
|
||||
ok(iv.to_string() == "123-490", "append: adjacent result");
|
||||
|
||||
TrxId_Interval iv2(123, 456);
|
||||
ok(iv2.append(TrxId_Interval(200, 600)), "append: overlapping at end");
|
||||
ok(iv2.to_string() == "123-600", "append: overlapping result");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief merge() handles all overlap/adjacency cases and rejects non-overlapping intervals.
|
||||
*/
|
||||
static void test_merge() {
|
||||
{
|
||||
TrxId_Interval iv(123, 456);
|
||||
ok(!iv.merge(TrxId_Interval(90, 100)), "merge: no overlap before, rejected");
|
||||
}
|
||||
{
|
||||
TrxId_Interval iv(123, 456);
|
||||
ok(!iv.merge(TrxId_Interval(500, 600)), "merge: no overlap after, rejected");
|
||||
}
|
||||
{
|
||||
TrxId_Interval iv(123, 456);
|
||||
ok(iv.merge(TrxId_Interval(200, 300)), "merge: contained in middle");
|
||||
ok(iv.start == 123 && iv.end == 456, "merge: contained result unchanged");
|
||||
}
|
||||
{
|
||||
TrxId_Interval iv(123, 456);
|
||||
ok(iv.merge(TrxId_Interval(90, 200)), "merge: overlap at start");
|
||||
ok(iv.start == 90 && iv.end == 456, "merge: overlap at start result");
|
||||
}
|
||||
{
|
||||
TrxId_Interval iv(123, 456);
|
||||
ok(iv.merge(TrxId_Interval(300, 500)), "merge: overlap at end");
|
||||
ok(iv.start == 123 && iv.end == 500, "merge: overlap at end result");
|
||||
}
|
||||
{
|
||||
TrxId_Interval iv(123, 456);
|
||||
ok(iv.merge(TrxId_Interval(100, 500)), "merge: full overlap");
|
||||
ok(iv.start == 100 && iv.end == 500, "merge: full overlap result");
|
||||
}
|
||||
{
|
||||
TrxId_Interval iv(123, 456);
|
||||
ok(iv.merge(TrxId_Interval(100, 122)), "merge: append at start (adjacent)");
|
||||
ok(iv.start == 100 && iv.end == 456, "merge: append at start result");
|
||||
}
|
||||
{
|
||||
TrxId_Interval iv(123, 456);
|
||||
ok(iv.merge(TrxId_Interval(457, 600)), "merge: append at end (adjacent)");
|
||||
ok(iv.start == 123 && iv.end == 600, "merge: append at end result");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief cmp() and operator<, operator==, operator!= for strict weak ordering.
|
||||
*/
|
||||
static void test_cmp_operators() {
|
||||
TrxId_Interval a(10, 20);
|
||||
TrxId_Interval b(10, 30);
|
||||
TrxId_Interval c(20, 30);
|
||||
TrxId_Interval d(10, 20);
|
||||
|
||||
ok(a < b, "operator<: same start, smaller end");
|
||||
ok(a < c, "operator<: smaller start");
|
||||
ok(!(b < a), "operator<: not reversed");
|
||||
ok(a == d, "operator==: identical");
|
||||
ok(!(a == b), "operator==: different");
|
||||
ok(a != b, "operator!=: different");
|
||||
ok(!(a != d), "operator!=: identical");
|
||||
}
|
||||
|
||||
int main() {
|
||||
plan(48);
|
||||
|
||||
test_construct_from_range(); // 2 assertions
|
||||
test_construct_from_single(); // 1 assertion
|
||||
test_construct_from_string_literal(); // 2 assertions
|
||||
test_construct_from_string(); // 2 assertions
|
||||
test_construct_negative(); // 1 assertion
|
||||
test_contains_trxid(); // 5 assertions
|
||||
test_contains_interval(); // 4 assertions
|
||||
test_to_string(); // 3 assertions
|
||||
test_append(); // 7 assertions
|
||||
test_merge(); // 14 assertions
|
||||
test_cmp_operators(); // 7 assertions
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
@ -1,372 +0,0 @@
|
||||
/**
|
||||
* @file gtid_utils_unit-t.cpp
|
||||
* @brief Unit tests for GTID helper functions.
|
||||
*
|
||||
* Tests the pure data-structure operations in lib/GTID_Server_Data.cpp:
|
||||
* - addGtid() — add a single GTID, merge intervals
|
||||
* - addGtidInterval() — add a [from,to] interval
|
||||
* - gtid_executed_to_string() — serialize a gtid_set_t to MySQL format
|
||||
*
|
||||
* These are free functions that operate on gtid_set_t (an unordered_map
|
||||
* of UUID -> list of intervals) and require no I/O or event-loop setup.
|
||||
*/
|
||||
|
||||
#include "tap.h"
|
||||
#include "test_globals.h"
|
||||
#include "test_init.h"
|
||||
|
||||
#include "proxysql.h"
|
||||
#include "proxysql_gtid.h"
|
||||
|
||||
// Free function declarations (defined in lib/GTID_Server_Data.cpp,
|
||||
// declared in include/Base_HostGroups_Manager.h)
|
||||
extern void addGtid(const gtid_t& gtid, gtid_set_t& gtid_executed);
|
||||
extern bool addGtidInterval(gtid_set_t& gtid_executed, std::string server_uuid,
|
||||
int64_t txid_start, int64_t txid_end);
|
||||
extern std::string gtid_executed_to_string(gtid_set_t& gtid_executed);
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// addGtid tests
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @brief Adding a single GTID to an empty set creates one interval [n,n].
|
||||
*/
|
||||
static void test_addGtid_single_to_empty() {
|
||||
gtid_set_t gs;
|
||||
gtid_t g = std::make_pair(std::string("uuid1"), (int64_t)5);
|
||||
addGtid(g, gs);
|
||||
|
||||
ok(gs.count("uuid1") == 1,
|
||||
"addGtid: uuid1 key exists after first insert");
|
||||
ok(gs["uuid1"].size() == 1,
|
||||
"addGtid: exactly one interval after first insert");
|
||||
auto &iv = gs["uuid1"].front();
|
||||
ok(iv.first == 5 && iv.second == 5,
|
||||
"addGtid: interval is [5,5]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Consecutive GTIDs merge into a single interval.
|
||||
*/
|
||||
static void test_addGtid_consecutive_merge() {
|
||||
gtid_set_t gs;
|
||||
std::string uuid = "uuid1";
|
||||
// Insert 1, 2, 3 in order
|
||||
for (int64_t i = 1; i <= 3; i++) {
|
||||
addGtid(std::make_pair(uuid, i), gs);
|
||||
}
|
||||
|
||||
ok(gs[uuid].size() == 1,
|
||||
"addGtid consecutive: merged into one interval");
|
||||
auto &iv = gs[uuid].front();
|
||||
ok(iv.first == 1 && iv.second == 3,
|
||||
"addGtid consecutive: interval is [1,3]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Non-consecutive GTIDs produce separate intervals.
|
||||
*/
|
||||
static void test_addGtid_non_consecutive() {
|
||||
gtid_set_t gs;
|
||||
std::string uuid = "uuid1";
|
||||
addGtid(std::make_pair(uuid, (int64_t)1), gs);
|
||||
addGtid(std::make_pair(uuid, (int64_t)5), gs);
|
||||
|
||||
ok(gs[uuid].size() == 2,
|
||||
"addGtid non-consecutive: two separate intervals");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Duplicate GTID is a no-op.
|
||||
*/
|
||||
static void test_addGtid_duplicate() {
|
||||
gtid_set_t gs;
|
||||
std::string uuid = "uuid1";
|
||||
addGtid(std::make_pair(uuid, (int64_t)3), gs);
|
||||
addGtid(std::make_pair(uuid, (int64_t)3), gs);
|
||||
|
||||
ok(gs[uuid].size() == 1,
|
||||
"addGtid duplicate: still one interval");
|
||||
auto &iv = gs[uuid].front();
|
||||
ok(iv.first == 3 && iv.second == 3,
|
||||
"addGtid duplicate: interval unchanged [3,3]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Multiple UUIDs are tracked independently.
|
||||
*/
|
||||
static void test_addGtid_multiple_uuids() {
|
||||
gtid_set_t gs;
|
||||
addGtid(std::make_pair(std::string("aaa"), (int64_t)1), gs);
|
||||
addGtid(std::make_pair(std::string("bbb"), (int64_t)2), gs);
|
||||
|
||||
ok(gs.size() == 2,
|
||||
"addGtid multiple UUIDs: two entries in map");
|
||||
ok(gs["aaa"].front().first == 1,
|
||||
"addGtid multiple UUIDs: aaa has trxid 1");
|
||||
ok(gs["bbb"].front().first == 2,
|
||||
"addGtid multiple UUIDs: bbb has trxid 2");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adding a GTID that bridges two intervals merges them.
|
||||
*/
|
||||
static void test_addGtid_bridge_merge() {
|
||||
gtid_set_t gs;
|
||||
std::string uuid = "uuid1";
|
||||
// Create two separate intervals: [1,1] and [3,3]
|
||||
addGtid(std::make_pair(uuid, (int64_t)1), gs);
|
||||
addGtid(std::make_pair(uuid, (int64_t)3), gs);
|
||||
ok(gs[uuid].size() == 2,
|
||||
"addGtid bridge: two intervals before bridge");
|
||||
|
||||
// Insert 2 — should merge [1,1] and [3,3] into [1,3]
|
||||
addGtid(std::make_pair(uuid, (int64_t)2), gs);
|
||||
ok(gs[uuid].size() == 1,
|
||||
"addGtid bridge: merged into one interval after bridge");
|
||||
auto &iv = gs[uuid].front();
|
||||
ok(iv.first == 1 && iv.second == 3,
|
||||
"addGtid bridge: interval is [1,3]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adding GTIDs in reverse order still merges correctly.
|
||||
*/
|
||||
static void test_addGtid_reverse_order() {
|
||||
gtid_set_t gs;
|
||||
std::string uuid = "uuid1";
|
||||
for (int64_t i = 5; i >= 1; i--) {
|
||||
addGtid(std::make_pair(uuid, i), gs);
|
||||
}
|
||||
|
||||
ok(gs[uuid].size() == 1,
|
||||
"addGtid reverse: merged into one interval");
|
||||
auto &iv = gs[uuid].front();
|
||||
ok(iv.first == 1 && iv.second == 5,
|
||||
"addGtid reverse: interval is [1,5]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief GTID inside an existing interval is a no-op.
|
||||
*/
|
||||
static void test_addGtid_within_interval() {
|
||||
gtid_set_t gs;
|
||||
std::string uuid = "uuid1";
|
||||
addGtid(std::make_pair(uuid, (int64_t)1), gs);
|
||||
addGtid(std::make_pair(uuid, (int64_t)2), gs);
|
||||
addGtid(std::make_pair(uuid, (int64_t)3), gs);
|
||||
// Now [1,3] exists. Adding 2 again should be a no-op.
|
||||
addGtid(std::make_pair(uuid, (int64_t)2), gs);
|
||||
|
||||
ok(gs[uuid].size() == 1,
|
||||
"addGtid within: still one interval");
|
||||
auto &iv = gs[uuid].front();
|
||||
ok(iv.first == 1 && iv.second == 3,
|
||||
"addGtid within: interval unchanged [1,3]");
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// addGtidInterval tests
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @brief Adding an interval to an empty set.
|
||||
*/
|
||||
static void test_addGtidInterval_empty() {
|
||||
gtid_set_t gs;
|
||||
bool updated = addGtidInterval(gs, "uuid1", 1, 10);
|
||||
|
||||
ok(updated == true,
|
||||
"addGtidInterval empty: returns true (updated)");
|
||||
ok(gs["uuid1"].size() == 1,
|
||||
"addGtidInterval empty: one interval");
|
||||
auto &iv = gs["uuid1"].front();
|
||||
ok(iv.first == 1 && iv.second == 10,
|
||||
"addGtidInterval empty: interval is [1,10]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updating an existing interval with the same start but larger end.
|
||||
*/
|
||||
static void test_addGtidInterval_update_same_start() {
|
||||
gtid_set_t gs;
|
||||
addGtidInterval(gs, "uuid1", 1, 10);
|
||||
bool updated = addGtidInterval(gs, "uuid1", 1, 19);
|
||||
|
||||
ok(updated == true,
|
||||
"addGtidInterval update: returns true when end changed");
|
||||
ok(gs["uuid1"].size() == 1,
|
||||
"addGtidInterval update: still one interval");
|
||||
auto &iv = gs["uuid1"].front();
|
||||
ok(iv.first == 1 && iv.second == 19,
|
||||
"addGtidInterval update: interval is [1,19]");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Re-adding an identical interval returns false (no update).
|
||||
*/
|
||||
static void test_addGtidInterval_duplicate() {
|
||||
gtid_set_t gs;
|
||||
addGtidInterval(gs, "uuid1", 1, 10);
|
||||
bool updated = addGtidInterval(gs, "uuid1", 1, 10);
|
||||
|
||||
ok(updated == false,
|
||||
"addGtidInterval duplicate: returns false (no change)");
|
||||
ok(gs["uuid1"].size() == 1,
|
||||
"addGtidInterval duplicate: still one interval");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adding a non-overlapping interval creates a second entry.
|
||||
*/
|
||||
static void test_addGtidInterval_separate() {
|
||||
gtid_set_t gs;
|
||||
addGtidInterval(gs, "uuid1", 1, 10);
|
||||
bool updated = addGtidInterval(gs, "uuid1", 20, 30);
|
||||
|
||||
ok(updated == true,
|
||||
"addGtidInterval separate: returns true");
|
||||
ok(gs["uuid1"].size() == 2,
|
||||
"addGtidInterval separate: two intervals");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Multiple UUIDs are tracked independently.
|
||||
*/
|
||||
static void test_addGtidInterval_multi_uuid() {
|
||||
gtid_set_t gs;
|
||||
addGtidInterval(gs, "aaa", 1, 5);
|
||||
addGtidInterval(gs, "bbb", 10, 20);
|
||||
|
||||
ok(gs.size() == 2,
|
||||
"addGtidInterval multi-uuid: two UUIDs");
|
||||
ok(gs["aaa"].front().second == 5,
|
||||
"addGtidInterval multi-uuid: aaa interval correct");
|
||||
ok(gs["bbb"].front().first == 10,
|
||||
"addGtidInterval multi-uuid: bbb interval correct");
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// gtid_executed_to_string tests
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @brief Empty set produces empty string.
|
||||
*/
|
||||
static void test_toString_empty() {
|
||||
gtid_set_t gs;
|
||||
std::string result = gtid_executed_to_string(gs);
|
||||
ok(result.empty(),
|
||||
"toString empty: returns empty string");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Single UUID with a single interval.
|
||||
*
|
||||
* gtid_executed_to_string inserts dashes into the UUID at positions
|
||||
* 8, 13, 18, 23 to produce standard MySQL GTID format:
|
||||
* xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:from-to
|
||||
*
|
||||
* The stored UUID must be 32 hex chars (no dashes).
|
||||
*/
|
||||
static void test_toString_single_uuid_single_interval() {
|
||||
gtid_set_t gs;
|
||||
// 32-char hex UUID (no dashes) — the format stored internally
|
||||
std::string uuid = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
gs[uuid].emplace_back(1, 10);
|
||||
|
||||
std::string result = gtid_executed_to_string(gs);
|
||||
std::string expected = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1-10";
|
||||
ok(result == expected,
|
||||
"toString single interval: '%s' == '%s'",
|
||||
result.c_str(), expected.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Single UUID with multiple intervals.
|
||||
*/
|
||||
static void test_toString_single_uuid_multi_interval() {
|
||||
gtid_set_t gs;
|
||||
std::string uuid = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
||||
gs[uuid].emplace_back(1, 5);
|
||||
gs[uuid].emplace_back(10, 20);
|
||||
|
||||
std::string result = gtid_executed_to_string(gs);
|
||||
// Each interval gets its own "uuid:from-to" segment, comma-separated
|
||||
std::string expected =
|
||||
"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb:1-5,"
|
||||
"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb:10-20";
|
||||
ok(result == expected,
|
||||
"toString multi-interval: '%s' == '%s'",
|
||||
result.c_str(), expected.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Multiple UUIDs.
|
||||
*
|
||||
* Since gtid_set_t is an unordered_map, iteration order is not
|
||||
* guaranteed. We check that both UUIDs appear and the total length
|
||||
* is correct.
|
||||
*/
|
||||
static void test_toString_multi_uuid() {
|
||||
gtid_set_t gs;
|
||||
std::string uuid_a = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
std::string uuid_b = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
||||
gs[uuid_a].emplace_back(1, 5);
|
||||
gs[uuid_b].emplace_back(10, 20);
|
||||
|
||||
std::string result = gtid_executed_to_string(gs);
|
||||
|
||||
// Both UUIDs should appear (with dashes)
|
||||
std::string dashed_a = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
|
||||
std::string dashed_b = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb";
|
||||
ok(result.find(dashed_a) != std::string::npos,
|
||||
"toString multi-uuid: contains uuid_a");
|
||||
ok(result.find(dashed_b) != std::string::npos,
|
||||
"toString multi-uuid: contains uuid_b");
|
||||
// Should have exactly one comma separating the two entries
|
||||
size_t comma_count = 0;
|
||||
for (char c : result) { if (c == ',') comma_count++; }
|
||||
ok(comma_count == 1,
|
||||
"toString multi-uuid: exactly one comma separator");
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// main
|
||||
// =========================================================================
|
||||
|
||||
int main() {
|
||||
plan(37);
|
||||
test_init_minimal();
|
||||
|
||||
// addGtid tests
|
||||
test_addGtid_single_to_empty(); // 3 checks
|
||||
test_addGtid_consecutive_merge(); // 2 checks
|
||||
test_addGtid_non_consecutive(); // 1 check
|
||||
test_addGtid_duplicate(); // 2 checks
|
||||
test_addGtid_multiple_uuids(); // 3 checks
|
||||
test_addGtid_bridge_merge(); // 3 checks
|
||||
test_addGtid_reverse_order(); // 2 checks
|
||||
test_addGtid_within_interval(); // 2 checks
|
||||
|
||||
// addGtidInterval tests
|
||||
test_addGtidInterval_empty(); // 3 checks
|
||||
test_addGtidInterval_update_same_start(); // 3 checks
|
||||
test_addGtidInterval_duplicate(); // 2 checks
|
||||
test_addGtidInterval_separate(); // 2 checks
|
||||
test_addGtidInterval_multi_uuid(); // 3 checks
|
||||
|
||||
// gtid_executed_to_string tests
|
||||
test_toString_empty(); // 1 check
|
||||
test_toString_single_uuid_single_interval(); // 1 check
|
||||
test_toString_single_uuid_multi_interval(); // 1 check
|
||||
test_toString_multi_uuid(); // 3 checks
|
||||
|
||||
test_cleanup_minimal();
|
||||
return exit_status();
|
||||
}
|
||||
Loading…
Reference in new issue