382 lines
14 KiB
Python
Executable File
382 lines
14 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import subprocess
|
|
import os
|
|
import sys
|
|
import time
|
|
import argparse
|
|
import platform
|
|
import urllib.request
|
|
from glob import glob
|
|
from docker import start_database
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("-t", "--target")
|
|
parser.add_argument("-e", "--target-exact")
|
|
parser.add_argument("-l", "--list-targets", action="store_true")
|
|
parser.add_argument("--test")
|
|
parser.add_argument("--clippy", action="store_true")
|
|
|
|
argv, unknown = parser.parse_known_args()
|
|
|
|
_list_targets_seen = set()
|
|
|
|
# base dir of sqlx workspace
|
|
dir_workspace = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
|
|
|
# dir of tests
|
|
dir_tests = os.path.join(dir_workspace, "tests")
|
|
|
|
RUNTIMES = ["async-std", "async-global-executor", "smol", "tokio"]
|
|
CHECK_TLS = ["native-tls", "rustls", "rustls-ring", "rustls-aws-lc-rs", "none"]
|
|
TLS_VARIANTS = ["native-tls", "rustls-ring", "rustls-aws-lc-rs", "none"]
|
|
POSTGRES_VERSIONS = ["17", "16", "15", "14", "13"]
|
|
MYSQL_VERSIONS = ["8", "5_7"]
|
|
MARIADB_VERSIONS = ["verylatest", "11_8", "11_4", "10_11", "10_6"]
|
|
|
|
|
|
def maybe_fetch_sqlite_extension():
|
|
"""
|
|
For supported platforms, if we're testing SQLite and the file isn't
|
|
already present, grab a simple extension for testing.
|
|
|
|
Returns the extension name if it was downloaded successfully or `None` if not.
|
|
"""
|
|
BASE_URL = "https://github.com/nalgeon/sqlean/releases/download/0.15.2/"
|
|
if platform.system() == "Darwin":
|
|
if platform.machine() == "arm64":
|
|
download_url = BASE_URL + "/ipaddr.arm64.dylib"
|
|
filename = "ipaddr.dylib"
|
|
else:
|
|
download_url = BASE_URL + "/ipaddr.dylib"
|
|
filename = "ipaddr.dylib"
|
|
elif platform.system() == "Linux":
|
|
download_url = BASE_URL + "/ipaddr.so"
|
|
filename = "ipaddr.so"
|
|
else:
|
|
# Unsupported OS
|
|
return None
|
|
|
|
if not os.path.exists(filename):
|
|
content = urllib.request.urlopen(download_url).read()
|
|
with open(filename, "wb") as fd:
|
|
fd.write(content)
|
|
|
|
return filename.split(".")[0]
|
|
|
|
|
|
def required_feature_for_test(test_name):
|
|
for feature in ["postgres", "mysql", "sqlite", "any"]:
|
|
if test_name.startswith(feature):
|
|
return feature
|
|
return None
|
|
|
|
|
|
def extract_features(command):
|
|
tokens = command.split(" ")
|
|
for i, token in enumerate(tokens):
|
|
if token == "--features" and i + 1 < len(tokens):
|
|
return set(tokens[i + 1].split(","))
|
|
return None
|
|
|
|
|
|
def core_tls_features(tls):
|
|
if tls == "rustls":
|
|
return ["_tls-rustls-ring-webpki"]
|
|
if tls == "rustls-ring":
|
|
return ["_tls-rustls-ring-webpki", "_tls-rustls-ring-native-roots"]
|
|
if tls == "rustls-aws-lc-rs":
|
|
return ["_tls-rustls-aws-lc-rs"]
|
|
return [f"_tls-{tls}"]
|
|
|
|
|
|
def run(command, comment=None, env=None, service=None, tag=None, args=None, database_url_args=None):
|
|
if argv.list_targets:
|
|
if tag:
|
|
if tag not in _list_targets_seen:
|
|
print(f"{tag}")
|
|
_list_targets_seen.add(tag)
|
|
|
|
return
|
|
|
|
if argv.target and not tag.startswith(argv.target):
|
|
return
|
|
|
|
if argv.target_exact and tag != argv.target_exact:
|
|
return
|
|
|
|
if comment is not None:
|
|
print(f"\x1b[2m # {comment}\x1b[0m")
|
|
|
|
environ = env or {}
|
|
|
|
if service == "sqlite":
|
|
if maybe_fetch_sqlite_extension() is not None:
|
|
if environ.get("RUSTFLAGS"):
|
|
environ["RUSTFLAGS"] += " --cfg sqlite_ipaddr"
|
|
else:
|
|
environ["RUSTFLAGS"] = "--cfg sqlite_ipaddr"
|
|
if platform.system() == "Linux":
|
|
if os.environ.get("LD_LIBRARY_PATH"):
|
|
environ["LD_LIBRARY_PATH"]= os.environ.get("LD_LIBRARY_PATH") + ":"+ os.getcwd()
|
|
else:
|
|
environ["LD_LIBRARY_PATH"]=os.getcwd()
|
|
|
|
|
|
if service is not None:
|
|
database_url = start_database(service, database="sqlite/sqlite.db" if service == "sqlite" else "sqlx", cwd=dir_tests)
|
|
|
|
if database_url_args:
|
|
database_url += "?" + database_url_args
|
|
|
|
environ["DATABASE_URL"] = database_url
|
|
|
|
# show the database url
|
|
print(f"\x1b[94m @ {database_url}\x1b[0m")
|
|
|
|
command_args = []
|
|
|
|
if argv.test:
|
|
if command.startswith("cargo c") or command.startswith("cargo check") or command.startswith("cargo clippy"):
|
|
return
|
|
if "--manifest-path" in command:
|
|
return
|
|
required = required_feature_for_test(argv.test)
|
|
if required is not None:
|
|
features = extract_features(command)
|
|
if features is None or (required not in features and "all-databases" not in features):
|
|
return
|
|
if command.startswith("cargo test"):
|
|
command_args.extend(["--test", argv.test])
|
|
|
|
if unknown:
|
|
command_args.extend(["--", *unknown])
|
|
|
|
if args is not None:
|
|
command_args.extend(args)
|
|
|
|
print(f"\x1b[93m $ {command} {' '.join(command_args)}\x1b[0m")
|
|
|
|
cwd = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
|
res = subprocess.run(
|
|
[
|
|
*command.split(" "),
|
|
*command_args
|
|
],
|
|
env=dict(list(os.environ.items()) + list(environ.items())),
|
|
cwd=cwd,
|
|
)
|
|
|
|
if res.returncode != 0:
|
|
sys.exit(res.returncode)
|
|
|
|
|
|
def postgres_env(version):
|
|
env = {}
|
|
rustflags = os.environ.get("RUSTFLAGS", "").strip()
|
|
version_flag = f'--cfg postgres="{version}"'
|
|
if rustflags:
|
|
env["RUSTFLAGS"] = f"{rustflags} {version_flag}"
|
|
else:
|
|
env["RUSTFLAGS"] = version_flag
|
|
return env
|
|
|
|
|
|
# before we start, we clean previous profile data
|
|
# keeping these around can cause weird errors
|
|
for path in glob(os.path.join(os.path.dirname(__file__), "target/**/*.gc*"), recursive=True):
|
|
os.remove(path)
|
|
|
|
#
|
|
# check
|
|
#
|
|
|
|
CHECK_CMD = "cargo clippy" if argv.clippy else "cargo c"
|
|
|
|
for runtime in RUNTIMES:
|
|
for tls in CHECK_TLS:
|
|
run(
|
|
f"{CHECK_CMD} --no-default-features --features all-databases,_unstable-all-types,macros,sqlite-preupdate-hook,runtime-{runtime},tls-{tls}",
|
|
comment=f"check {runtime} {tls}",
|
|
tag=f"check_{runtime}_{tls}",
|
|
)
|
|
|
|
if argv.clippy:
|
|
sys.exit(0)
|
|
|
|
#
|
|
# unit test
|
|
#
|
|
|
|
for runtime in RUNTIMES:
|
|
for tls in TLS_VARIANTS:
|
|
core_features = [
|
|
"json",
|
|
"offline",
|
|
"migrate",
|
|
"sqlx-toml",
|
|
f"_rt-{runtime}",
|
|
*core_tls_features(tls),
|
|
]
|
|
run(
|
|
"cargo test --no-default-features --manifest-path sqlx-core/Cargo.toml "
|
|
f"--features {','.join(core_features)}",
|
|
comment=f"unit test core {runtime} {tls}",
|
|
tag=f"unit_{runtime}_{tls}",
|
|
)
|
|
|
|
run(
|
|
"cargo test -p sqlx-mysql --no-default-features --features rsa --lib",
|
|
comment="unit test sqlx-mysql rsa",
|
|
tag="unit_mysql_rsa",
|
|
)
|
|
|
|
#
|
|
# integration tests
|
|
#
|
|
|
|
for runtime in RUNTIMES:
|
|
for tls in TLS_VARIANTS:
|
|
#
|
|
# sqlite
|
|
#
|
|
|
|
run(
|
|
f"cargo test --no-default-features "
|
|
f"--features any,sqlite,macros,migrate,sqlite-preupdate-hook,_unstable-all-types,runtime-{runtime},tls-{tls}",
|
|
comment="test sqlite",
|
|
env={"RUST_TEST_THREADS": "1"},
|
|
service="sqlite",
|
|
tag=f"sqlite_{runtime}",
|
|
)
|
|
|
|
#
|
|
# postgres
|
|
#
|
|
|
|
for version in POSTGRES_VERSIONS:
|
|
run(
|
|
f"cargo test --no-default-features "
|
|
f"--features any,postgres,macros,migrate,_unstable-all-types,runtime-{runtime},tls-{tls}",
|
|
comment=f"test postgres {version}",
|
|
env=postgres_env(version),
|
|
service=f"postgres_{version}",
|
|
tag=f"postgres_{version}_{runtime}",
|
|
)
|
|
|
|
if tls != "none":
|
|
## +ssl
|
|
run(
|
|
f"cargo test --no-default-features "
|
|
f"--features any,postgres,macros,migrate,_unstable-all-types,runtime-{runtime},tls-{tls}",
|
|
comment=f"test postgres {version} ssl",
|
|
database_url_args="sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt",
|
|
env=postgres_env(version),
|
|
service=f"postgres_{version}",
|
|
tag=f"postgres_{version}_ssl_{runtime}",
|
|
)
|
|
|
|
## +client-ssl
|
|
run(
|
|
f"cargo test --no-default-features "
|
|
f"--features any,postgres,macros,migrate,_unstable-all-types,runtime-{runtime},tls-{tls}",
|
|
comment=f"test postgres {version}_client_ssl no-password",
|
|
database_url_args="sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt&sslkey=.%2Ftests%2Fcerts%2Fkeys%2Fclient.key&sslcert=.%2Ftests%2Fcerts%2Fclient.crt",
|
|
env=postgres_env(version),
|
|
service=f"postgres_{version}_client_ssl",
|
|
tag=f"postgres_{version}_client_ssl_no_password_{runtime}",
|
|
)
|
|
|
|
#
|
|
# mysql
|
|
#
|
|
|
|
for version in MYSQL_VERSIONS:
|
|
base_features = f"any,mysql,macros,migrate,_unstable-all-types,runtime-{runtime},tls-{tls}"
|
|
rsa_features = f"any,mysql,mysql-rsa,macros,migrate,_unstable-all-types,runtime-{runtime},tls-{tls}"
|
|
features = rsa_features if tls == "none" else base_features
|
|
base_url_args = "ssl-mode=disabled" if tls == "none" else "ssl-mode=required"
|
|
client_ssl_ca = ".%2Ftests%2Fcerts%2Fca.crt"
|
|
client_ssl_key = ".%2Ftests%2Fcerts%2Fkeys%2Fclient.key"
|
|
client_ssl_cert = ".%2Ftests%2Fcerts%2Fclient.crt"
|
|
if version == "5_7":
|
|
# MySQL 5.7 cannot load Ed25519 certs; use the RSA set for client-SSL targets.
|
|
client_ssl_ca = ".%2Ftests%2Fcerts%2Frsa%2Fca.crt"
|
|
client_ssl_key = ".%2Ftests%2Fcerts%2Frsa%2Fkeys%2Fclient.key"
|
|
client_ssl_cert = ".%2Ftests%2Fcerts%2Frsa%2Fclient.crt"
|
|
client_ssl_args = (
|
|
f"sslmode=verify_ca&ssl-ca={client_ssl_ca}"
|
|
f"&ssl-key={client_ssl_key}&ssl-cert={client_ssl_cert}"
|
|
)
|
|
|
|
# Since docker mysql 5.7 using yaSSL(It only supports TLSv1.1), avoid running when using rustls.
|
|
# https://github.com/docker-library/mysql/issues/567
|
|
# only run when using native-tls
|
|
if not (version == "5_7" and tls in ["rustls-ring", "rustls-aws-lc-rs"]):
|
|
run(
|
|
f"cargo test --no-default-features --features {features}",
|
|
comment=f"test mysql {version}",
|
|
database_url_args=base_url_args,
|
|
service=f"mysql_{version}",
|
|
tag=f"mysql_{version}_{runtime}",
|
|
)
|
|
|
|
## +client-ssl
|
|
if tls != "none" and not (version == "5_7" and tls in ["rustls-ring", "rustls-aws-lc-rs"]):
|
|
run(
|
|
f"cargo test --no-default-features --features {base_features}",
|
|
comment=f"test mysql {version}_client_ssl no-password",
|
|
database_url_args=client_ssl_args,
|
|
service=f"mysql_{version}_client_ssl",
|
|
tag=f"mysql_{version}_client_ssl_no_password_{runtime}",
|
|
)
|
|
|
|
if tls == "native-tls" and runtime == "tokio" and version == "8":
|
|
run(
|
|
f"cargo test --no-default-features --features {rsa_features}",
|
|
comment=f"test mysql {version} tls with rsa",
|
|
database_url_args="ssl-mode=required",
|
|
service=f"mysql_{version}",
|
|
tag=f"mysql_{version}_tls_rsa_{runtime}",
|
|
)
|
|
|
|
#
|
|
# mariadb
|
|
#
|
|
|
|
for version in MARIADB_VERSIONS:
|
|
base_features = f"any,mysql,macros,migrate,_unstable-all-types,runtime-{runtime},tls-{tls}"
|
|
rsa_features = f"any,mysql,mysql-rsa,macros,migrate,_unstable-all-types,runtime-{runtime},tls-{tls}"
|
|
features = rsa_features if tls == "none" else base_features
|
|
base_url_args = "ssl-mode=disabled" if tls == "none" else "ssl-mode=required"
|
|
|
|
run(
|
|
f"cargo test --no-default-features --features {features}",
|
|
comment=f"test mariadb {version}",
|
|
database_url_args=base_url_args,
|
|
service=f"mariadb_{version}",
|
|
tag=f"mariadb_{version}_{runtime}",
|
|
)
|
|
|
|
## +client-ssl
|
|
if tls != "none":
|
|
run(
|
|
f"cargo test --no-default-features --features {base_features}",
|
|
comment=f"test mariadb {version}_client_ssl no-password",
|
|
database_url_args="sslmode=verify_ca&ssl-ca=.%2Ftests%2Fcerts%2Fca.crt&ssl-key=%2Ftests%2Fcerts%2Fkeys%2Fclient.key&ssl-cert=.%2Ftests%2Fcerts%2Fclient.crt",
|
|
service=f"mariadb_{version}_client_ssl",
|
|
tag=f"mariadb_{version}_client_ssl_no_password_{runtime}",
|
|
)
|
|
|
|
if tls == "native-tls" and runtime == "tokio" and version == "10_11":
|
|
run(
|
|
f"cargo test --no-default-features --features {rsa_features}",
|
|
comment=f"test mariadb {version} tls with rsa",
|
|
database_url_args="ssl-mode=required",
|
|
service=f"mariadb_{version}",
|
|
tag=f"mariadb_{version}_tls_rsa_{runtime}",
|
|
)
|
|
|
|
# TODO: Use [grcov] if available
|
|
# ~/.cargo/bin/grcov tests/.cache/target/debug -s sqlx-core/ -t html --llvm --branch -o ./target/debug/coverage
|