2017-05-19 18:54:05 +00:00
|
|
|
import difflib
|
2018-09-03 14:06:00 +00:00
|
|
|
import time
|
2021-06-11 12:00:40 +00:00
|
|
|
import logging
|
2020-10-02 16:54:07 +00:00
|
|
|
from io import IOBase
|
2017-05-19 18:54:05 +00:00
|
|
|
|
2020-09-16 04:26:10 +00:00
|
|
|
|
2017-05-19 18:54:05 +00:00
|
|
|
class TSV:
|
|
|
|
"""Helper to get pretty diffs between expected and actual tab-separated value files"""
|
|
|
|
|
|
|
|
def __init__(self, contents):
|
2020-10-02 16:54:07 +00:00
|
|
|
if isinstance(contents, IOBase):
|
2020-05-07 02:44:11 +00:00
|
|
|
raw_lines = contents.readlines()
|
2020-10-02 16:54:07 +00:00
|
|
|
elif isinstance(contents, str) or isinstance(contents, str):
|
2020-05-07 02:44:11 +00:00
|
|
|
raw_lines = contents.splitlines(True)
|
|
|
|
elif isinstance(contents, list):
|
2022-03-22 16:39:58 +00:00
|
|
|
raw_lines = [
|
|
|
|
"\t".join(map(str, l)) if isinstance(l, list) else str(l)
|
|
|
|
for l in contents
|
|
|
|
]
|
2020-08-12 08:55:04 +00:00
|
|
|
elif isinstance(contents, TSV):
|
|
|
|
self.lines = contents.lines
|
|
|
|
return
|
2020-05-07 02:44:11 +00:00
|
|
|
else:
|
2022-03-22 16:39:58 +00:00
|
|
|
raise TypeError(
|
|
|
|
"contents must be either file or string or list, actual type: "
|
|
|
|
+ type(contents).__name__
|
|
|
|
)
|
2018-09-03 14:06:00 +00:00
|
|
|
self.lines = [l.strip() for l in raw_lines if l.strip()]
|
2017-05-19 18:54:05 +00:00
|
|
|
|
|
|
|
def __eq__(self, other):
|
2020-05-07 02:44:11 +00:00
|
|
|
if not isinstance(other, TSV):
|
|
|
|
return self == TSV(other)
|
2017-05-19 18:54:05 +00:00
|
|
|
return self.lines == other.lines
|
|
|
|
|
2018-09-03 14:06:00 +00:00
|
|
|
def __ne__(self, other):
|
2020-05-07 02:44:11 +00:00
|
|
|
if not isinstance(other, TSV):
|
|
|
|
return self != TSV(other)
|
2018-09-03 14:06:00 +00:00
|
|
|
return self.lines != other.lines
|
|
|
|
|
2022-03-22 16:39:58 +00:00
|
|
|
def diff(self, other, n1="", n2=""):
|
2020-05-07 02:44:11 +00:00
|
|
|
if not isinstance(other, TSV):
|
|
|
|
return self.diff(TSV(other), n1=n1, n2=n2)
|
2022-03-22 16:39:58 +00:00
|
|
|
return list(
|
|
|
|
line.rstrip()
|
|
|
|
for line in difflib.unified_diff(
|
|
|
|
self.lines, other.lines, fromfile=n1, tofile=n2
|
|
|
|
)
|
|
|
|
)[2:]
|
2018-09-03 14:06:00 +00:00
|
|
|
|
|
|
|
def __str__(self):
|
2022-03-22 16:39:58 +00:00
|
|
|
return "\n".join(self.lines)
|
2017-05-30 11:49:17 +00:00
|
|
|
|
2021-07-12 17:23:19 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return self.__str__()
|
|
|
|
|
2021-01-26 15:12:08 +00:00
|
|
|
def __len__(self):
|
|
|
|
return len(self.lines)
|
|
|
|
|
2017-05-30 11:49:17 +00:00
|
|
|
@staticmethod
|
|
|
|
def toMat(contents):
|
|
|
|
return [line.split("\t") for line in contents.split("\n") if line.strip()]
|
2018-09-03 14:06:00 +00:00
|
|
|
|
2020-09-16 04:26:10 +00:00
|
|
|
|
2022-03-22 16:39:58 +00:00
|
|
|
def assert_eq_with_retry(
|
|
|
|
instance,
|
|
|
|
query,
|
|
|
|
expectation,
|
|
|
|
retry_count=20,
|
|
|
|
sleep_time=0.5,
|
|
|
|
stdin=None,
|
|
|
|
timeout=None,
|
|
|
|
settings=None,
|
|
|
|
user=None,
|
|
|
|
ignore_error=False,
|
|
|
|
get_result=lambda x: x,
|
|
|
|
):
|
2018-09-03 14:06:00 +00:00
|
|
|
expectation_tsv = TSV(expectation)
|
2020-10-02 16:54:07 +00:00
|
|
|
for i in range(retry_count):
|
2018-09-03 14:06:00 +00:00
|
|
|
try:
|
2022-03-22 16:39:58 +00:00
|
|
|
if (
|
|
|
|
TSV(
|
|
|
|
get_result(
|
|
|
|
instance.query(
|
|
|
|
query,
|
|
|
|
user=user,
|
|
|
|
stdin=stdin,
|
|
|
|
timeout=timeout,
|
|
|
|
settings=settings,
|
|
|
|
ignore_error=ignore_error,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
== expectation_tsv
|
|
|
|
):
|
2018-09-03 14:06:00 +00:00
|
|
|
break
|
|
|
|
time.sleep(sleep_time)
|
|
|
|
except Exception as ex:
|
2021-06-11 12:00:40 +00:00
|
|
|
logging.exception(f"assert_eq_with_retry retry {i+1} exception {ex}")
|
2018-09-03 14:06:00 +00:00
|
|
|
time.sleep(sleep_time)
|
|
|
|
else:
|
2022-03-22 16:39:58 +00:00
|
|
|
val = TSV(
|
|
|
|
get_result(
|
|
|
|
instance.query(
|
|
|
|
query,
|
|
|
|
user=user,
|
|
|
|
stdin=stdin,
|
|
|
|
timeout=timeout,
|
|
|
|
settings=settings,
|
|
|
|
ignore_error=ignore_error,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
2018-09-03 14:06:00 +00:00
|
|
|
if expectation_tsv != val:
|
2022-03-22 16:39:58 +00:00
|
|
|
raise AssertionError(
|
|
|
|
"'{}' != '{}'\n{}".format(
|
|
|
|
expectation_tsv,
|
|
|
|
val,
|
|
|
|
"\n".join(expectation_tsv.diff(val, n1="expectation", n2="query")),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2020-09-03 16:52:08 +00:00
|
|
|
|
|
|
|
def assert_logs_contain(instance, substring):
|
|
|
|
if not instance.contains_in_log(substring):
|
|
|
|
raise AssertionError("'{}' not found in logs".format(substring))
|
|
|
|
|
2022-03-22 16:39:58 +00:00
|
|
|
|
2020-09-03 16:52:08 +00:00
|
|
|
def assert_logs_contain_with_retry(instance, substring, retry_count=20, sleep_time=0.5):
|
2020-10-02 16:54:07 +00:00
|
|
|
for i in range(retry_count):
|
2020-09-03 16:52:08 +00:00
|
|
|
try:
|
|
|
|
if instance.contains_in_log(substring):
|
|
|
|
break
|
|
|
|
time.sleep(sleep_time)
|
|
|
|
except Exception as ex:
|
2021-06-11 12:00:40 +00:00
|
|
|
logging.exception(f"contains_in_log_with_retry retry {i+1} exception {ex}")
|
2020-09-03 16:52:08 +00:00
|
|
|
time.sleep(sleep_time)
|
|
|
|
else:
|
|
|
|
raise AssertionError("'{}' not found in logs".format(substring))
|
2021-05-15 12:33:01 +00:00
|
|
|
|
2022-03-22 16:39:58 +00:00
|
|
|
|
|
|
|
def exec_query_with_retry(
|
2024-05-13 16:33:38 +00:00
|
|
|
instance, query, retry_count=40, sleep_time=0.5, silent=False, settings={}, timeout=30
|
2022-03-22 16:39:58 +00:00
|
|
|
):
|
2021-05-15 12:33:01 +00:00
|
|
|
exception = None
|
2021-11-11 08:12:54 +00:00
|
|
|
for cnt in range(retry_count):
|
2021-05-15 12:33:01 +00:00
|
|
|
try:
|
2021-11-11 08:12:54 +00:00
|
|
|
res = instance.query(query, timeout=30, settings=settings)
|
|
|
|
if not silent:
|
|
|
|
logging.debug(f"Result of {query} on {cnt} try is {res}")
|
2021-05-15 12:33:01 +00:00
|
|
|
break
|
|
|
|
except Exception as ex:
|
|
|
|
exception = ex
|
2021-10-19 17:39:44 +00:00
|
|
|
if not silent:
|
2022-03-22 16:39:58 +00:00
|
|
|
logging.exception(
|
|
|
|
f"Failed to execute query '{query}' on {cnt} try on instance '{instance.name}' will retry"
|
|
|
|
)
|
2021-05-15 12:33:01 +00:00
|
|
|
time.sleep(sleep_time)
|
|
|
|
else:
|
|
|
|
raise exception
|
2022-01-10 11:34:16 +00:00
|
|
|
|
2022-03-22 16:39:58 +00:00
|
|
|
|
2022-01-10 11:34:16 +00:00
|
|
|
def csv_compare(result, expected):
|
|
|
|
csv_result = TSV(result)
|
|
|
|
csv_expected = TSV(expected)
|
|
|
|
mismatch = []
|
2022-03-22 16:39:58 +00:00
|
|
|
max_len = (
|
|
|
|
len(csv_result) if len(csv_result) > len(csv_expected) else len(csv_expected)
|
|
|
|
)
|
2022-01-10 11:34:16 +00:00
|
|
|
for i in range(max_len):
|
|
|
|
if i >= len(csv_result):
|
|
|
|
mismatch.append("-[%d]=%s" % (i, csv_expected.lines[i]))
|
|
|
|
elif i >= len(csv_expected):
|
|
|
|
mismatch.append("+[%d]=%s" % (i, csv_result.lines[i]))
|
|
|
|
elif csv_expected.lines[i] != csv_result.lines[i]:
|
|
|
|
mismatch.append("-[%d]=%s" % (i, csv_expected.lines[i]))
|
|
|
|
mismatch.append("+[%d]=%s" % (i, csv_result.lines[i]))
|
|
|
|
|
|
|
|
return "\n".join(mismatch)
|