mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-23 04:39:04 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf00d8fd27 |
@@ -278,4 +278,9 @@ listed by `cargo nextest list -p e2e_test`. Regenerate it when adding or
|
|||||||
moving e2e tests so acceptance numbers in the test-strategy issues
|
moving e2e tests so acceptance numbers in the test-strategy issues
|
||||||
(backlog#1147–#1155) stay auditable. When a profile membership change is
|
(backlog#1147–#1155) stay auditable. When a profile membership change is
|
||||||
intentional, review its JSON listing before updating the matching
|
intentional, review its JSON listing before updating the matching
|
||||||
`.config/e2e-*-selection.txt` test-ID digest.
|
`.config/e2e-*-selection.txt` test-ID digest. Update only the platform that
|
||||||
|
produced the listing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/check_test_wiring.py --update-profile e2e-full /path/to/listing.json linux
|
||||||
|
```
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ def check_s3_tests_runner(root: Path) -> list[str]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def profile_selection(root: Path, profile: str) -> str:
|
def profile_selection_entries(root: Path, profile: str) -> tuple[Path, list[str], dict[str, str]]:
|
||||||
if not re.fullmatch(r"e2e-[a-z0-9-]+", profile):
|
if not re.fullmatch(r"e2e-[a-z0-9-]+", profile):
|
||||||
raise ValueError(f"invalid e2e profile name: {profile}")
|
raise ValueError(f"invalid e2e profile name: {profile}")
|
||||||
path = root / f".config/{profile}-selection.txt"
|
path = root / f".config/{profile}-selection.txt"
|
||||||
@@ -227,6 +227,11 @@ def profile_selection(root: Path, profile: str) -> str:
|
|||||||
values = dict(line.split("=", 1) for line in lines if "=" in line)
|
values = dict(line.split("=", 1) for line in lines if "=" in line)
|
||||||
if len(values) != len(lines) or any(not re.fullmatch(r"sha256(?:-[a-z0-9]+)?", key) for key in values):
|
if len(values) != len(lines) or any(not re.fullmatch(r"sha256(?:-[a-z0-9]+)?", key) for key in values):
|
||||||
raise ValueError(f"{path.relative_to(root).as_posix()}: invalid sha256 entry")
|
raise ValueError(f"{path.relative_to(root).as_posix()}: invalid sha256 entry")
|
||||||
|
return path, lines, values
|
||||||
|
|
||||||
|
|
||||||
|
def profile_selection(root: Path, profile: str) -> str:
|
||||||
|
path, _, values = profile_selection_entries(root, profile)
|
||||||
key = f"sha256-{sys.platform}"
|
key = f"sha256-{sys.platform}"
|
||||||
digest = values.get(key, values.get("sha256", ""))
|
digest = values.get(key, values.get("sha256", ""))
|
||||||
if not re.fullmatch(r"[0-9a-f]{64}", digest):
|
if not re.fullmatch(r"[0-9a-f]{64}", digest):
|
||||||
@@ -234,6 +239,29 @@ def profile_selection(root: Path, profile: str) -> str:
|
|||||||
return digest
|
return digest
|
||||||
|
|
||||||
|
|
||||||
|
def profile_listing_digest(listing: Path) -> tuple[int, str]:
|
||||||
|
data = json.loads(listing.read_text())
|
||||||
|
selected = sorted(
|
||||||
|
f"{suite_id}::{test_name}"
|
||||||
|
for suite_id, suite in data["rust-suites"].items()
|
||||||
|
for test_name, testcase in suite["testcases"].items()
|
||||||
|
if testcase.get("filter-match", {}).get("status") == "matches"
|
||||||
|
)
|
||||||
|
return len(selected), hashlib.sha256(("\n".join(selected) + "\n").encode()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def update_profile_selection(root: Path, profile: str, listing: Path, platform: str) -> tuple[int, str, str]:
|
||||||
|
if not re.fullmatch(r"[a-z0-9]+", platform):
|
||||||
|
raise ValueError(f"invalid platform name: {platform}")
|
||||||
|
path, lines, values = profile_selection_entries(root, profile)
|
||||||
|
key = "sha256" if "sha256" in values else f"sha256-{platform}"
|
||||||
|
if key not in values:
|
||||||
|
raise ValueError(f"{path.relative_to(root).as_posix()}: missing {key} entry")
|
||||||
|
count, digest = profile_listing_digest(listing)
|
||||||
|
path.write_text("\n".join(f"{key}={digest}" if line.startswith(f"{key}=") else line for line in lines) + "\n")
|
||||||
|
return count, digest, key
|
||||||
|
|
||||||
|
|
||||||
def check_profile_definitions(root: Path) -> list[str]:
|
def check_profile_definitions(root: Path) -> list[str]:
|
||||||
config = tomllib.loads((root / ".config/nextest.toml").read_text())
|
config = tomllib.loads((root / ".config/nextest.toml").read_text())
|
||||||
profiles = {
|
profiles = {
|
||||||
@@ -547,22 +575,15 @@ def check_scheduled_alerts(root: Path) -> list[str]:
|
|||||||
def check_profile_listing(root: Path, profile: str, listing: Path) -> list[str]:
|
def check_profile_listing(root: Path, profile: str, listing: Path) -> list[str]:
|
||||||
try:
|
try:
|
||||||
expected_digest = profile_selection(root, profile)
|
expected_digest = profile_selection(root, profile)
|
||||||
data = json.loads(listing.read_text())
|
count, digest = profile_listing_digest(listing)
|
||||||
selected = sorted(
|
|
||||||
f"{suite_id}::{test_name}"
|
|
||||||
for suite_id, suite in data["rust-suites"].items()
|
|
||||||
for test_name, testcase in suite["testcases"].items()
|
|
||||||
if testcase.get("filter-match", {}).get("status") == "matches"
|
|
||||||
)
|
|
||||||
digest = hashlib.sha256(("\n".join(selected) + "\n").encode()).hexdigest()
|
|
||||||
except (FileNotFoundError, KeyError, TypeError, ValueError, json.JSONDecodeError) as error:
|
except (FileNotFoundError, KeyError, TypeError, ValueError, json.JSONDecodeError) as error:
|
||||||
return [f"cannot read {profile} nextest listing: {error}"]
|
return [f"cannot read {profile} nextest listing: {error}"]
|
||||||
if digest != expected_digest:
|
if digest != expected_digest:
|
||||||
return [
|
return [
|
||||||
f"{profile} selection changed: count={len(selected)} sha256={digest}; "
|
f"{profile} selection changed: count={count} sha256={digest}; "
|
||||||
f"expected sha256={expected_digest}"
|
f"expected sha256={expected_digest}"
|
||||||
]
|
]
|
||||||
print(f"{profile} selection OK: {len(selected)} tests, sha256={digest}")
|
print(f"{profile} selection OK: {count} tests, sha256={digest}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
@@ -707,6 +728,45 @@ class SelfTests(unittest.TestCase):
|
|||||||
with mock.patch.object(sys, "platform", "linux"):
|
with mock.patch.object(sys, "platform", "linux"):
|
||||||
self.assertEqual(len(check_profile_listing(root, "e2e-full", listing)), 1)
|
self.assertEqual(len(check_profile_listing(root, "e2e-full", listing)), 1)
|
||||||
|
|
||||||
|
def test_update_profile_selection_changes_only_requested_platform(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
root = Path(tmp)
|
||||||
|
(root / ".config").mkdir()
|
||||||
|
darwin_digest = "a" * 64
|
||||||
|
(root / ".config/e2e-full-selection.txt").write_text(
|
||||||
|
f"sha256-darwin={darwin_digest}\nsha256-linux={'b' * 64}\n"
|
||||||
|
)
|
||||||
|
listing = root / "listing.json"
|
||||||
|
listing.write_text(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"rust-suites": {
|
||||||
|
"suite": {
|
||||||
|
"testcases": {"linux": {"filter-match": {"status": "matches"}}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
count, digest, key = update_profile_selection(root, "e2e-full", listing, "linux")
|
||||||
|
|
||||||
|
self.assertEqual(count, 1)
|
||||||
|
self.assertEqual(key, "sha256-linux")
|
||||||
|
self.assertEqual(
|
||||||
|
(root / ".config/e2e-full-selection.txt").read_text(),
|
||||||
|
f"sha256-darwin={darwin_digest}\nsha256-linux={digest}\n",
|
||||||
|
)
|
||||||
|
with mock.patch.object(sys, "platform", "linux"):
|
||||||
|
self.assertEqual(check_profile_listing(root, "e2e-full", listing), [])
|
||||||
|
selection = root / ".config/e2e-smoke-selection.txt"
|
||||||
|
selection.write_text(f"sha256={'a' * 64}\n")
|
||||||
|
|
||||||
|
count, digest, key = update_profile_selection(root, "e2e-smoke", listing, "linux")
|
||||||
|
|
||||||
|
self.assertEqual((count, key), (1, "sha256"))
|
||||||
|
self.assertEqual(selection.read_text(), f"sha256={digest}\n")
|
||||||
|
|
||||||
def test_scheduled_alerts_require_completion_watchdog(self) -> None:
|
def test_scheduled_alerts_require_completion_watchdog(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
root = Path(tmp)
|
root = Path(tmp)
|
||||||
@@ -984,9 +1044,18 @@ def main() -> int:
|
|||||||
print(f"ERROR: {error}", file=sys.stderr)
|
print(f"ERROR: {error}", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
return 0
|
return 0
|
||||||
|
if len(sys.argv) == 5 and sys.argv[1] == "--update-profile":
|
||||||
|
try:
|
||||||
|
count, digest, key = update_profile_selection(ROOT, sys.argv[2], Path(sys.argv[3]), sys.argv[4])
|
||||||
|
except (FileNotFoundError, KeyError, TypeError, ValueError, json.JSONDecodeError) as error:
|
||||||
|
print(f"ERROR: cannot update {sys.argv[2]} selection: {error}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
print(f"Updated .config/{sys.argv[2]}-selection.txt: count={count} {key}={digest}")
|
||||||
|
return 0
|
||||||
if sys.argv[1:]:
|
if sys.argv[1:]:
|
||||||
print(
|
print(
|
||||||
"usage: check_test_wiring.py [--self-test | --check-profile PROFILE LISTING]",
|
"usage: check_test_wiring.py [--self-test | --check-profile PROFILE LISTING | "
|
||||||
|
"--update-profile PROFILE LISTING PLATFORM]",
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
return 2
|
return 2
|
||||||
|
|||||||
Reference in New Issue
Block a user