Make run_test.py and create_test.py importable without errors (#6736)

Allowing scripts to be importable is good practice in general and it's
required for the pytest testing framework that I'm adding in a follow up
PR.
pull/6726/head
Jelte Fennema 2023-02-27 22:34:42 +01:00 committed by GitHub
parent c018e29bec
commit 17ad61678f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 182 additions and 181 deletions

View File

@ -4,25 +4,26 @@ import os
import random import random
import sys import sys
if len(sys.argv) != 2: if __name__ == "__main__":
print( if len(sys.argv) != 2:
"ERROR: Expected the name of the new test as an argument, such as:\n" print(
"src/test/regress/bin/create_test.py my_awesome_test" "ERROR: Expected the name of the new test as an argument, such as:\n"
) "src/test/regress/bin/create_test.py my_awesome_test"
sys.exit(1) )
sys.exit(1)
test_name = sys.argv[1] test_name = sys.argv[1]
regress_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) regress_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
filename = os.path.join(regress_dir, "sql", f"{test_name}.sql") filename = os.path.join(regress_dir, "sql", f"{test_name}.sql")
if os.path.isfile(filename): if os.path.isfile(filename):
print(f"ERROR: test file '{filename}' already exists") print(f"ERROR: test file '{filename}' already exists")
sys.exit(1) sys.exit(1)
shard_id = random.randint(1, 999999) * 100 shard_id = random.randint(1, 999999) * 100
contents = f"""CREATE SCHEMA {test_name}; contents = f"""CREATE SCHEMA {test_name};
SET search_path TO {test_name}; SET search_path TO {test_name};
SET citus.shard_count TO 4; SET citus.shard_count TO 4;
SET citus.shard_replication_factor TO 1; SET citus.shard_replication_factor TO 1;
@ -34,9 +35,8 @@ SET client_min_messages TO WARNING;
DROP SCHEMA {test_name} CASCADE; DROP SCHEMA {test_name} CASCADE;
""" """
with open(filename, "w") as f:
f.write(contents)
with open(filename, "w") as f: print(f"Created {filename}")
f.write(contents) print(f"Don't forget to add '{test_name}' in multi_schedule somewhere")
print(f"Created {filename}")
print(f"Don't forget to add '{test_name}' in multi_schedule somewhere")

View File

@ -15,194 +15,195 @@ import common
import config import config
args = argparse.ArgumentParser() if __name__ == "__main__":
args.add_argument( args = argparse.ArgumentParser()
"test_name", help="Test name (must be included in a schedule.)", nargs="?" args.add_argument(
) "test_name", help="Test name (must be included in a schedule.)", nargs="?"
args.add_argument( )
"-p", args.add_argument(
"--path", "-p",
required=False, "--path",
help="Relative path for test file (must have a .sql or .spec extension)", required=False,
type=pathlib.Path, 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( args.add_argument(
"-b", "-r", "--repeat", help="Number of test to run", type=int, default=1
"--use-base-schedule", )
required=False, args.add_argument(
help="Choose base-schedules rather than minimal-schedules", "-b",
action="store_true", "--use-base-schedule",
) required=False,
args.add_argument( help="Choose base-schedules rather than minimal-schedules",
"-w", action="store_true",
"--use-whole-schedule-line", )
required=False, args.add_argument(
help="Use the whole line found in related schedule", "-w",
action="store_true", "--use-whole-schedule-line",
) required=False,
args.add_argument( help="Use the whole line found in related schedule",
"--valgrind", action="store_true",
required=False, )
help="Run the test with valgrind enabled", args.add_argument(
action="store_true", "--valgrind",
) required=False,
help="Run the test with valgrind enabled",
action="store_true",
)
args = vars(args.parse_args()) args = vars(args.parse_args())
regress_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) regress_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
test_file_path = args["path"] test_file_path = args["path"]
test_file_name = args["test_name"] test_file_name = args["test_name"]
use_base_schedule = args["use_base_schedule"] use_base_schedule = args["use_base_schedule"]
use_whole_schedule_line = args["use_whole_schedule_line"] use_whole_schedule_line = args["use_whole_schedule_line"]
class TestDeps:
schedule: Optional[str]
direct_extra_tests: list[str]
class TestDeps: def __init__(self, schedule, extra_tests=None, repeatable=True):
schedule: Optional[str] self.schedule = schedule
direct_extra_tests: list[str] self.direct_extra_tests = extra_tests or []
self.repeatable = repeatable
def __init__(self, schedule, extra_tests=None, repeatable=True): def extra_tests(self):
self.schedule = schedule all_deps = OrderedDict()
self.direct_extra_tests = extra_tests or [] for direct_dep in self.direct_extra_tests:
self.repeatable = repeatable if direct_dep in deps:
for indirect_dep in deps[direct_dep].extra_tests():
all_deps[indirect_dep] = True
all_deps[direct_dep] = True
def extra_tests(self): return list(all_deps.keys())
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):
deps = { print("FATAL: No test given.")
"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) sys.exit(2)
test_file_extension = pathlib.Path(test_file_path).suffix if test_file_path:
test_file_name = pathlib.Path(test_file_path).stem test_file_path = os.path.join(os.getcwd(), args["path"])
if test_file_extension not in ".spec.sql": if not os.path.isfile(test_file_path):
print( print(f"ERROR: test file '{test_file_path}' does not exist")
"ERROR: Unrecognized test extension. Valid extensions are: .sql and .spec" sys.exit(2)
)
sys.exit(1)
test_schedule = "" test_file_extension = pathlib.Path(test_file_path).suffix
dependencies = [] test_file_name = pathlib.Path(test_file_path).stem
# find related schedule if test_file_extension not in ".spec.sql":
for schedule_file_path in sorted(glob(os.path.join(regress_dir, "*_schedule"))): print(
for schedule_line in open(schedule_file_path, "r"): "ERROR: Unrecognized test extension. Valid extensions are: .sql and .spec"
if re.search(r"\b" + test_file_name + r"\b", schedule_line): )
test_schedule = pathlib.Path(schedule_file_path).stem sys.exit(1)
if use_whole_schedule_line:
test_schedule_line = schedule_line test_schedule = ""
else: dependencies = []
test_schedule_line = f"test: {test_file_name}\n"
break # 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: else:
continue raise Exception("Test could not be found in any schedule")
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"
def default_base_schedule(test_schedule): if "failure" in test_schedule:
if "isolation" in test_schedule: return "failure_base_schedule"
return "base_isolation_schedule"
if "failure" in test_schedule: if "enterprise" in test_schedule:
return "failure_base_schedule" return "enterprise_minimal_schedule"
if "enterprise" in test_schedule: if "split" in test_schedule:
return "enterprise_minimal_schedule" return "minimal_schedule"
if "split" in test_schedule: if "mx" in test_schedule:
return "minimal_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 "mx" in test_schedule:
if use_base_schedule: if use_base_schedule:
return "mx_base_schedule" return "base_schedule"
return "mx_minimal_schedule"
if "operations" in test_schedule:
return "minimal_schedule" return "minimal_schedule"
if test_schedule in config.ARBITRARY_SCHEDULE_NAMES: if test_file_name in deps:
print(f"WARNING: Arbitrary config schedule ({test_schedule}) is not supported.") dependencies = deps[test_file_name]
sys.exit(0) else:
dependencies = TestDeps(default_base_schedule(test_schedule))
if use_base_schedule: # copy base schedule to a temp file and append test_schedule_line
return "base_schedule" # to be able to run tests in parallel (if test_schedule_line is a parallel group.)
return "minimal_schedule" 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)
if test_file_name in deps: # find suitable make recipe
dependencies = deps[test_file_name] if dependencies.schedule == "base_isolation_schedule":
else: make_recipe = "check-isolation-custom-schedule"
dependencies = TestDeps(default_base_schedule(test_schedule)) elif dependencies.schedule == "failure_base_schedule":
make_recipe = "check-failure-custom-schedule"
else:
make_recipe = "check-custom-schedule"
# copy base schedule to a temp file and append test_schedule_line if args["valgrind"]:
# to be able to run tests in parallel (if test_schedule_line is a parallel group.) make_recipe += "-vg"
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"] # prepare command to run tests
if repetition_cnt > 1 and not dependencies.repeatable: test_command = f"make -C {regress_dir} {make_recipe} SCHEDULE='{pathlib.Path(tmp_schedule_path).stem}'"
repetition_cnt = 1
print(f"WARNING: Cannot repeatably run this test: '{test_file_name}'")
for _ in range(repetition_cnt):
myfile.write(test_schedule_line)
# run test command n times
# find suitable make recipe try:
if dependencies.schedule == "base_isolation_schedule": print(f"Executing.. {test_command}")
make_recipe = "check-isolation-custom-schedule" result = common.run(test_command)
elif dependencies.schedule == "failure_base_schedule": finally:
make_recipe = "check-failure-custom-schedule" # remove temp schedule file
else: os.remove(tmp_schedule_path)
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)