mirror of https://github.com/citusdata/citus.git
209 lines
6.0 KiB
Python
Executable File
209 lines
6.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import os
|
|
import pathlib
|
|
import random
|
|
import re
|
|
import shutil
|
|
import sys
|
|
from collections import OrderedDict
|
|
from glob import glob
|
|
from typing import Optional
|
|
|
|
import common
|
|
|
|
import config
|
|
|
|
args = argparse.ArgumentParser()
|
|
args.add_argument(
|
|
"test_name", help="Test name (must be included in a schedule.)", nargs="?"
|
|
)
|
|
args.add_argument(
|
|
"-p",
|
|
"--path",
|
|
required=False,
|
|
help="Relative path for test file (must have a .sql or .spec extension)",
|
|
type=pathlib.Path,
|
|
)
|
|
args.add_argument("-r", "--repeat", help="Number of test to run", type=int, default=1)
|
|
args.add_argument(
|
|
"-b",
|
|
"--use-base-schedule",
|
|
required=False,
|
|
help="Choose base-schedules rather than minimal-schedules",
|
|
action="store_true",
|
|
)
|
|
args.add_argument(
|
|
"-w",
|
|
"--use-whole-schedule-line",
|
|
required=False,
|
|
help="Use the whole line found in related schedule",
|
|
action="store_true",
|
|
)
|
|
args.add_argument(
|
|
"--valgrind",
|
|
required=False,
|
|
help="Run the test with valgrind enabled",
|
|
action="store_true",
|
|
)
|
|
|
|
args = vars(args.parse_args())
|
|
|
|
regress_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
|
test_file_path = args["path"]
|
|
test_file_name = args["test_name"]
|
|
use_base_schedule = args["use_base_schedule"]
|
|
use_whole_schedule_line = args["use_whole_schedule_line"]
|
|
|
|
|
|
class TestDeps:
|
|
schedule: Optional[str]
|
|
direct_extra_tests: list[str]
|
|
|
|
def __init__(self, schedule, extra_tests=None, repeatable=True):
|
|
self.schedule = schedule
|
|
self.direct_extra_tests = extra_tests or []
|
|
self.repeatable = repeatable
|
|
|
|
def extra_tests(self):
|
|
all_deps = OrderedDict()
|
|
for direct_dep in self.direct_extra_tests:
|
|
if direct_dep in deps:
|
|
for indirect_dep in deps[direct_dep].extra_tests():
|
|
all_deps[indirect_dep] = True
|
|
all_deps[direct_dep] = True
|
|
|
|
return list(all_deps.keys())
|
|
|
|
|
|
deps = {
|
|
"multi_cluster_management": TestDeps(
|
|
None, ["multi_test_helpers_superuser"], repeatable=False
|
|
),
|
|
"create_role_propagation": TestDeps(None, ["multi_cluster_management"]),
|
|
"single_node_enterprise": TestDeps(None),
|
|
"multi_extension": TestDeps(None, repeatable=False),
|
|
"multi_test_helpers": TestDeps(None),
|
|
"multi_insert_select": TestDeps("base_schedule"),
|
|
}
|
|
|
|
if not (test_file_name or test_file_path):
|
|
print("FATAL: No test given.")
|
|
sys.exit(2)
|
|
|
|
|
|
if test_file_path:
|
|
test_file_path = os.path.join(os.getcwd(), args["path"])
|
|
|
|
if not os.path.isfile(test_file_path):
|
|
print(f"ERROR: test file '{test_file_path}' does not exist")
|
|
sys.exit(2)
|
|
|
|
test_file_extension = pathlib.Path(test_file_path).suffix
|
|
test_file_name = pathlib.Path(test_file_path).stem
|
|
|
|
if test_file_extension not in ".spec.sql":
|
|
print(
|
|
"ERROR: Unrecognized test extension. Valid extensions are: .sql and .spec"
|
|
)
|
|
sys.exit(1)
|
|
|
|
test_schedule = ""
|
|
dependencies = []
|
|
|
|
# find related schedule
|
|
for schedule_file_path in sorted(glob(os.path.join(regress_dir, "*_schedule"))):
|
|
for schedule_line in open(schedule_file_path, "r"):
|
|
if re.search(r"\b" + test_file_name + r"\b", schedule_line):
|
|
test_schedule = pathlib.Path(schedule_file_path).stem
|
|
if use_whole_schedule_line:
|
|
test_schedule_line = schedule_line
|
|
else:
|
|
test_schedule_line = f"test: {test_file_name}\n"
|
|
break
|
|
else:
|
|
continue
|
|
break
|
|
else:
|
|
raise Exception("Test could not be found in any schedule")
|
|
|
|
|
|
def default_base_schedule(test_schedule):
|
|
if "isolation" in test_schedule:
|
|
return "base_isolation_schedule"
|
|
|
|
if "failure" in test_schedule:
|
|
return "failure_base_schedule"
|
|
|
|
if "enterprise" in test_schedule:
|
|
return "enterprise_minimal_schedule"
|
|
|
|
if "split" in test_schedule:
|
|
return "minimal_schedule"
|
|
|
|
if "mx" in test_schedule:
|
|
if use_base_schedule:
|
|
return "mx_base_schedule"
|
|
return "mx_minimal_schedule"
|
|
|
|
if "operations" in test_schedule:
|
|
return "minimal_schedule"
|
|
|
|
if test_schedule in config.ARBITRARY_SCHEDULE_NAMES:
|
|
print(f"WARNING: Arbitrary config schedule ({test_schedule}) is not supported.")
|
|
sys.exit(0)
|
|
|
|
if use_base_schedule:
|
|
return "base_schedule"
|
|
return "minimal_schedule"
|
|
|
|
|
|
if test_file_name in deps:
|
|
dependencies = deps[test_file_name]
|
|
else:
|
|
dependencies = TestDeps(default_base_schedule(test_schedule))
|
|
|
|
# copy base schedule to a temp file and append test_schedule_line
|
|
# to be able to run tests in parallel (if test_schedule_line is a parallel group.)
|
|
tmp_schedule_path = os.path.join(
|
|
regress_dir, f"tmp_schedule_{ random.randint(1, 10000)}"
|
|
)
|
|
# some tests don't need a schedule to run
|
|
# e.g tests that are in the first place in their own schedule
|
|
if dependencies.schedule:
|
|
shutil.copy2(os.path.join(regress_dir, dependencies.schedule), tmp_schedule_path)
|
|
with open(tmp_schedule_path, "a") as myfile:
|
|
for dependency in dependencies.extra_tests():
|
|
myfile.write(f"test: {dependency}\n")
|
|
|
|
repetition_cnt = args["repeat"]
|
|
if repetition_cnt > 1 and not dependencies.repeatable:
|
|
repetition_cnt = 1
|
|
print(f"WARNING: Cannot repeatably run this test: '{test_file_name}'")
|
|
for _ in range(repetition_cnt):
|
|
myfile.write(test_schedule_line)
|
|
|
|
|
|
# find suitable make recipe
|
|
if dependencies.schedule == "base_isolation_schedule":
|
|
make_recipe = "check-isolation-custom-schedule"
|
|
elif dependencies.schedule == "failure_base_schedule":
|
|
make_recipe = "check-failure-custom-schedule"
|
|
else:
|
|
make_recipe = "check-custom-schedule"
|
|
|
|
if args["valgrind"]:
|
|
make_recipe += "-vg"
|
|
|
|
# prepare command to run tests
|
|
test_command = f"make -C {regress_dir} {make_recipe} SCHEDULE='{pathlib.Path(tmp_schedule_path).stem}'"
|
|
|
|
# run test command n times
|
|
try:
|
|
print(f"Executing.. {test_command}")
|
|
result = common.run(test_command)
|
|
finally:
|
|
# remove temp schedule file
|
|
os.remove(tmp_schedule_path)
|