| 1 | from __future__ import annotations
|
| 2 |
|
| 3 | import os
|
| 4 | import shutil
|
| 5 | import stat
|
| 6 | import sys
|
| 7 | import tempfile
|
| 8 | import unittest
|
| 9 | from pathlib import Path
|
| 10 | from unittest import mock
|
| 11 |
|
| 12 | sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
|
| 13 |
|
| 14 | import bench # noqa: E402
|
| 15 | from oakbench.execution import TimedCapture # noqa: E402
|
| 16 | from oakbench.rows import validate_rows # noqa: E402
|
| 17 | from oakbench.subjects import Subject # noqa: E402
|
| 18 |
|
| 19 | GIT = shutil.which("git")
|
| 20 | GIT_LFS = shutil.which("git-lfs")
|
| 21 |
|
| 22 |
|
| 23 | def make_binary_fixture(root: Path, file_names: list[str], size: int = 256) -> Path:
|
| 24 | """A minimal fixture tree shaped like ensure_fixture output (group-*/...)."""
|
| 25 | fixture = root / "fixture"
|
| 26 | for name in file_names:
|
| 27 | bench.write_pattern_file(fixture / "group-000" / name, size, f"test:{name}", True)
|
| 28 | return fixture
|
| 29 |
|
| 30 |
|
| 31 | def write_git_lfs_shim(root: Path) -> Path:
|
| 32 | """A fake git-lfs on PATH: git dispatches `git lfs ...` to it, so the
|
| 33 | subprocess layer is faked without git-lfs being installed."""
|
| 34 | shim_dir = root / "shim-bin"
|
| 35 | shim_dir.mkdir()
|
| 36 | shim = shim_dir / "git-lfs"
|
| 37 | shim.write_text("#!/bin/sh\nexit 0\n")
|
| 38 | shim.chmod(shim.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
| 39 | return shim_dir
|
| 40 |
|
| 41 |
|
| 42 | @unittest.skipIf(GIT is None, "git binary not installed")
|
| 43 | class HugeBinaryGitDiffLimitTest(unittest.TestCase):
|
| 44 | def metadata(self) -> dict:
|
| 45 | return {
|
| 46 | "bench_id": "test",
|
| 47 | "profile": "large",
|
| 48 | "benchmark_track": "agent-default",
|
| 49 | "command_semantics_version": bench.COMMAND_SEMANTICS_VERSION,
|
| 50 | "timestamp_utc": "test",
|
| 51 | }
|
| 52 |
|
| 53 | def test_stock_git_huge_binary_diff_rows_are_explicit_skips(self) -> None:
|
| 54 | scenario = bench.Scenario("single_huge_binary_1gb", 1, 1024 * 1024 * 1024, 1, True, 1)
|
| 55 | with tempfile.TemporaryDirectory() as tmp:
|
| 56 | root = Path(tmp)
|
| 57 | fixture = make_binary_fixture(root, ["file-0000.bin"], size=256)
|
| 58 | rows = bench.run_subject_scenario(
|
| 59 | Subject(name="git", kind="git", label="Git", bin=Path(GIT)),
|
| 60 | scenario,
|
| 61 | fixture,
|
| 62 | 0,
|
| 63 | root / "runs",
|
| 64 | self.metadata(),
|
| 65 | skip_diff=False,
|
| 66 | admitted_output_chars=20_000,
|
| 67 | track="agent-default",
|
| 68 | skip_determinism_probe=False,
|
| 69 | skip_remote=True,
|
| 70 | )
|
| 71 |
|
| 72 | by_op = {row["operation"]: row for row in rows}
|
| 73 | for op in (
|
| 74 | "diff.dirty",
|
| 75 | "diff.dirty.determinism",
|
| 76 | "diff.dirty.inforecall",
|
| 77 | "diff.full.inforecall",
|
| 78 | ):
|
| 79 | self.assertEqual(by_op[op]["returncode"], 77, op)
|
| 80 | self.assertTrue(by_op[op]["skipped"], op)
|
| 81 | self.assertIn("git_huge_binary_diff_fixture_limit", by_op[op]["skip_reason"])
|
| 82 | self.assertTrue(by_op[op]["command"])
|
| 83 | self.assertEqual(validate_rows(rows, "core"), [])
|
| 84 |
|
| 85 | def test_git_lfs_huge_binary_diff_is_not_preflight_skipped(self) -> None:
|
| 86 | scenario = bench.Scenario("single_huge_binary_1gb", 1, 1024 * 1024 * 1024, 1, True, 1)
|
| 87 | subject = Subject(name="git_lfs", kind="git", label="Git (lfs)", bin=Path(GIT), git_mode="lfs")
|
| 88 | self.assertIsNone(bench.git_huge_binary_diff_limit_reason(subject, scenario, "diff.dirty"))
|
| 89 |
|
| 90 | def test_diagnostic_probe_rows_keep_failure_stderr(self) -> None:
|
| 91 | scenario = bench.Scenario("tiny_text", 1, 1, 1, False, 1)
|
| 92 | subject = Subject(name="git", kind="git", label="Git", bin=Path(GIT))
|
| 93 | with tempfile.TemporaryDirectory() as tmp:
|
| 94 | row = bench.run_determinism_probe(
|
| 95 | subject,
|
| 96 | scenario,
|
| 97 | "diff.dirty",
|
| 98 | Path(tmp),
|
| 99 | ["/bin/sh", "-c", "echo probe-boom >&2; exit 2"],
|
| 100 | 0,
|
| 101 | self.metadata(),
|
| 102 | )
|
| 103 | self.assertEqual(row["returncode"], 2)
|
| 104 | self.assertIn("probe-boom", row["stderr"])
|
| 105 |
|
| 106 | def test_information_probe_rows_keep_failure_stderr(self) -> None:
|
| 107 | scenario = bench.Scenario("tiny_text", 1, 1, 1, False, 1)
|
| 108 | subject = Subject(name="git", kind="git", label="Git", bin=Path(GIT))
|
| 109 |
|
| 110 | def fake_run_timed(*_args, **_kwargs) -> TimedCapture:
|
| 111 | return TimedCapture(
|
| 112 | elapsed_ms=1.0,
|
| 113 | returncode=2,
|
| 114 | stdout_text="",
|
| 115 | stdout_bytes=0,
|
| 116 | stdout_truncated=False,
|
| 117 | stderr_text="info-boom\n",
|
| 118 | stderr_bytes=10,
|
| 119 | stderr_truncated=False,
|
| 120 | )
|
| 121 |
|
| 122 | with tempfile.TemporaryDirectory() as tmp, mock.patch.object(bench, "run_timed", fake_run_timed):
|
| 123 | row = bench.run_information_probe(
|
| 124 | subject,
|
| 125 | scenario,
|
| 126 | "diff.dirty",
|
| 127 | Path(tmp),
|
| 128 | ["/bin/sh", "-c", "exit 2"],
|
| 129 | 0,
|
| 130 | self.metadata(),
|
| 131 | ["group-000/file-0000.txt"],
|
| 132 | )
|
| 133 | self.assertEqual(row["returncode"], 2)
|
| 134 | self.assertIn("info-boom", row["stderr"])
|
| 135 |
|
| 136 |
|
| 137 | class LargeBinaryLfsModeTest(unittest.TestCase):
|
| 138 | def subject(self, mode: str = "lfs") -> Subject:
|
| 139 | return Subject(name=f"git_{mode}", kind="git", label=f"Git ({mode})", bin=Path("git"), git_mode=mode)
|
| 140 |
|
| 141 | def metadata(self) -> dict:
|
| 142 | return {
|
| 143 | "bench_id": "test",
|
| 144 | "profile": "micro",
|
| 145 | "benchmark_track": "agent-default",
|
| 146 | "command_semantics_version": bench.COMMAND_SEMANTICS_VERSION,
|
| 147 | "timestamp_utc": "test",
|
| 148 | }
|
| 149 |
|
| 150 | def run_scenario(self, scenario: bench.Scenario, fixture: Path, run_root: Path) -> list[dict]:
|
| 151 | return bench.run_subject_scenario(
|
| 152 | self.subject(),
|
| 153 | scenario,
|
| 154 | fixture,
|
| 155 | 0,
|
| 156 | run_root,
|
| 157 | self.metadata(),
|
| 158 | skip_diff=True,
|
| 159 | admitted_output_chars=20_000,
|
| 160 | track="agent-default",
|
| 161 | skip_determinism_probe=True,
|
| 162 | skip_remote=True,
|
| 163 | )
|
| 164 |
|
| 165 | def test_expand_git_modes_accepts_lfs(self) -> None:
|
| 166 | subjects = [Subject(name="git", kind="git", label="Git", bin=Path("git"))]
|
| 167 | expanded = bench.expand_git_modes(subjects, "lfs")
|
| 168 | self.assertEqual([subject.name for subject in expanded], ["git", "git_lfs"])
|
| 169 | self.assertEqual(expanded[1].git_mode, "lfs")
|
| 170 |
|
| 171 | def test_lfs_mode_skips_non_binary_scenarios(self) -> None:
|
| 172 | with tempfile.TemporaryDirectory() as tmp:
|
| 173 | rows = self.run_scenario(
|
| 174 | bench.Scenario("tiny_text", 2, 64, 1, False, 1),
|
| 175 | Path(tmp) / "unused-fixture",
|
| 176 | Path(tmp) / "runs",
|
| 177 | )
|
| 178 | self.assertEqual(len(rows), 1)
|
| 179 | row = rows[0]
|
| 180 | self.assertEqual(row["operation"], "scenario.mode_skipped")
|
| 181 | self.assertEqual(row["returncode"], 77)
|
| 182 | self.assertTrue(row["skipped"])
|
| 183 | self.assertIn("git_lfs_not_applicable", row["skip_reason"])
|
| 184 | self.assertEqual(validate_rows(rows, "core"), [])
|
| 185 |
|
| 186 | def test_applies_to_list_in_config_is_the_source_of_truth(self) -> None:
|
| 187 | """config/git_modes.json git_lfs.applies_to names concrete scenarios;
|
| 188 | membership decides applicability, not the binary flag alone."""
|
| 189 | listed = bench.Scenario("single_large_binary", 1, 64, 1, True, 1)
|
| 190 | applies, reason = bench.git_mode_applies_to_scenario("lfs", listed)
|
| 191 | self.assertTrue(applies)
|
| 192 | self.assertIsNone(reason)
|
| 193 |
|
| 194 | # Binary, but not in the applies_to list (smoke's few_large_binaries).
|
| 195 | unlisted = bench.Scenario("few_large_binaries", 4, 64, 2, True, 1)
|
| 196 | applies, reason = bench.git_mode_applies_to_scenario("lfs", unlisted)
|
| 197 | self.assertFalse(applies)
|
| 198 | self.assertIn("git_lfs_not_applicable", reason)
|
| 199 | self.assertIn("applies_to", reason)
|
| 200 |
|
| 201 | # Modes whose applies_to entries are fixture-class tags (no scenario
|
| 202 | # named "large-tree" exists) keep the legacy applies-everywhere
|
| 203 | # behavior until the tags are mapped to scenario names.
|
| 204 | tiny = bench.Scenario("tiny_text", 2, 64, 1, False, 1)
|
| 205 | self.assertEqual(bench.git_mode_applies_to_scenario("untracked_cache", tiny), (True, None))
|
| 206 | self.assertEqual(bench.git_mode_applies_to_scenario(None, tiny), (True, None))
|
| 207 |
|
| 208 | def test_lfs_applies_to_pins_large_and_multigb_binary_scenarios(self) -> None:
|
| 209 | """The git_lfs comparator is explicit scenario coverage, not a generic
|
| 210 | binary flag. This pins the active runner's large binary tiers without
|
| 211 | generating their multi-GB fixtures in unit tests."""
|
| 212 | scenarios = {scenario.name: scenario for profile in bench.PROFILES.values() for scenario in profile}
|
| 213 | expected = {
|
| 214 | "single_large_binary": 128 * 1024 * 1024,
|
| 215 | "many_large_binaries": 32 * 8 * 1024 * 1024,
|
| 216 | "single_huge_binary_1gb": 1024 * 1024 * 1024,
|
| 217 | "many_large_binaries_multigb": 128 * 32 * 1024 * 1024,
|
| 218 | }
|
| 219 |
|
| 220 | lfs_applies_to = set(bench.git_modes_config()["git_lfs"]["applies_to"])
|
| 221 | self.assertEqual(lfs_applies_to, set(expected))
|
| 222 | for name, total_bytes in expected.items():
|
| 223 | scenario = scenarios[name]
|
| 224 | self.assertTrue(scenario.binary, name)
|
| 225 | self.assertEqual(scenario.file_count * scenario.file_size, total_bytes)
|
| 226 | self.assertEqual(bench.git_mode_applies_to_scenario("lfs", scenario), (True, None))
|
| 227 |
|
| 228 | self.assertGreaterEqual(
|
| 229 | scenarios["single_huge_binary_1gb"].file_size,
|
| 230 | 1024 * 1024 * 1024,
|
| 231 | )
|
| 232 | self.assertGreaterEqual(
|
| 233 | scenarios["many_large_binaries_multigb"].file_count
|
| 234 | * scenarios["many_large_binaries_multigb"].file_size,
|
| 235 | 4 * 1024 * 1024 * 1024,
|
| 236 | )
|
| 237 |
|
| 238 | def test_binary_scenario_outside_applies_to_emits_mode_skipped_row(self) -> None:
|
| 239 | with tempfile.TemporaryDirectory() as tmp:
|
| 240 | rows = self.run_scenario(
|
| 241 | bench.Scenario("single_medium_binary", 1, 64, 1, True, 1),
|
| 242 | Path(tmp) / "unused-fixture",
|
| 243 | Path(tmp) / "runs",
|
| 244 | )
|
| 245 | self.assertEqual(len(rows), 1)
|
| 246 | row = rows[0]
|
| 247 | self.assertEqual(row["operation"], "scenario.mode_skipped")
|
| 248 | self.assertEqual(row["returncode"], 77)
|
| 249 | self.assertTrue(row["skipped"])
|
| 250 | self.assertIn("git_lfs_not_applicable", row["skip_reason"])
|
| 251 | self.assertEqual(validate_rows(rows, "core"), [])
|
| 252 |
|
| 253 | def test_lfs_mode_skips_binary_scenario_when_git_lfs_is_unavailable(self) -> None:
|
| 254 | with tempfile.TemporaryDirectory() as tmp, mock.patch.object(bench, "git_lfs_available", return_value=False):
|
| 255 | rows = self.run_scenario(
|
| 256 | bench.Scenario("single_huge_binary_1gb", 1, 64, 1, True, 1),
|
| 257 | Path(tmp) / "unused-fixture",
|
| 258 | Path(tmp) / "runs",
|
| 259 | )
|
| 260 | self.assertEqual(len(rows), 1)
|
| 261 | row = rows[0]
|
| 262 | self.assertEqual(row["operation"], "scenario.mode_skipped")
|
| 263 | self.assertEqual(row["returncode"], 77)
|
| 264 | self.assertTrue(row["skipped"])
|
| 265 | self.assertIn("git_lfs_unavailable", row["skip_reason"])
|
| 266 | self.assertEqual(validate_rows(rows, "core"), [])
|
| 267 |
|
| 268 | def test_lfs_setup_commands_track_generated_binary_files(self) -> None:
|
| 269 | self.assertEqual(
|
| 270 | bench.WIRED_GIT_MODES["lfs"],
|
| 271 | [
|
| 272 | ("lfs_install", ["lfs", "install", "--local"]),
|
| 273 | ("lfs_track", ["lfs", "track", "*.bin"]),
|
| 274 | ],
|
| 275 | )
|
| 276 |
|
| 277 | def test_lfs_untracked_extensions_flags_files_the_patterns_miss(self) -> None:
|
| 278 | with tempfile.TemporaryDirectory() as tmp:
|
| 279 | repo = Path(tmp) / "repo"
|
| 280 | (repo / "group-000").mkdir(parents=True)
|
| 281 | (repo / "group-000" / "file-0000.bin").write_bytes(b"covered")
|
| 282 | (repo / ".gitattributes").write_text("*.bin filter=lfs\n")
|
| 283 | (repo / ".git").mkdir()
|
| 284 | (repo / ".git" / "config").write_text("") # never scanned
|
| 285 | self.assertEqual(bench.lfs_untracked_extensions(repo), [])
|
| 286 | (repo / "group-000" / "file-0001.dat").write_bytes(b"missed")
|
| 287 | self.assertEqual(bench.lfs_untracked_extensions(repo), ["dat"])
|
| 288 |
|
| 289 |
|
| 290 | @unittest.skipIf(GIT is None, "git binary not installed")
|
| 291 | class MockedLfsSuccessPathTest(unittest.TestCase):
|
| 292 | """Success path with the git subprocess layer's lfs leg faked: a git-lfs
|
| 293 | shim on PATH answers `git lfs install/track`, so the full operation
|
| 294 | sequence and row stream are observable without git-lfs installed."""
|
| 295 |
|
| 296 | def subject(self) -> Subject:
|
| 297 | return Subject(name="git_lfs", kind="git", label="Git (lfs)", bin=Path(GIT), git_mode="lfs")
|
| 298 |
|
| 299 | def metadata(self) -> dict:
|
| 300 | return {
|
| 301 | "bench_id": "test",
|
| 302 | "profile": "micro",
|
| 303 | "benchmark_track": "agent-default",
|
| 304 | "command_semantics_version": bench.COMMAND_SEMANTICS_VERSION,
|
| 305 | "timestamp_utc": "test",
|
| 306 | }
|
| 307 |
|
| 308 | def run_listed_scenario(self, fixture_files: list[str]) -> list[dict]:
|
| 309 | scenario = bench.Scenario("single_large_binary", len(fixture_files), 256, 1, True, 1)
|
| 310 | with tempfile.TemporaryDirectory() as tmp:
|
| 311 | root = Path(tmp)
|
| 312 | fixture = make_binary_fixture(root, fixture_files)
|
| 313 | shim_dir = write_git_lfs_shim(root)
|
| 314 | shim_path = f"{shim_dir}{os.pathsep}{os.environ.get('PATH', '')}"
|
| 315 | with mock.patch.object(bench, "git_lfs_available", return_value=True), \
|
| 316 | mock.patch.dict(os.environ, {"PATH": shim_path}):
|
| 317 | return bench.run_subject_scenario(
|
| 318 | self.subject(),
|
| 319 | scenario,
|
| 320 | fixture,
|
| 321 | 0,
|
| 322 | root / "runs",
|
| 323 | self.metadata(),
|
| 324 | skip_diff=True,
|
| 325 | admitted_output_chars=20_000,
|
| 326 | track="agent-default",
|
| 327 | skip_determinism_probe=True,
|
| 328 | skip_remote=True,
|
| 329 | )
|
| 330 |
|
| 331 | def test_listed_scenario_runs_install_then_track_then_initial_snapshot(self) -> None:
|
| 332 | rows = self.run_listed_scenario(["file-0000.bin", "file-0001.bin"])
|
| 333 | ops = [row["operation"] for row in rows]
|
| 334 |
|
| 335 | # Full setup sequence in order, between repo.init and snapshot.initial.
|
| 336 | self.assertIn("repo.init", ops)
|
| 337 | self.assertIn("mode.setup.lfs_install", ops)
|
| 338 | self.assertIn("mode.setup.lfs_track", ops)
|
| 339 | self.assertIn("snapshot.initial", ops)
|
| 340 | self.assertLess(ops.index("repo.init"), ops.index("mode.setup.lfs_install"))
|
| 341 | self.assertLess(ops.index("mode.setup.lfs_install"), ops.index("mode.setup.lfs_track"))
|
| 342 | self.assertLess(ops.index("mode.setup.lfs_track"), ops.index("snapshot.initial"))
|
| 343 |
|
| 344 | by_op = {row["operation"]: row for row in rows}
|
| 345 | self.assertEqual(by_op["mode.setup.lfs_install"]["command"], [GIT, "lfs", "install", "--local"])
|
| 346 | self.assertEqual(by_op["mode.setup.lfs_track"]["command"], [GIT, "lfs", "track", "*.bin"])
|
| 347 | self.assertEqual(by_op["mode.setup.lfs_install"]["returncode"], 0)
|
| 348 | self.assertEqual(by_op["mode.setup.lfs_track"]["returncode"], 0)
|
| 349 |
|
| 350 | # Measured rows all carry the git_lfs subject; nothing was skipped.
|
| 351 | for row in rows:
|
| 352 | self.assertEqual(row["subject"], "git_lfs")
|
| 353 | self.assertEqual(row["git_mode"], "lfs")
|
| 354 | self.assertNotEqual(row["returncode"], 77, row["operation"])
|
| 355 | for op in ("status.clean", "status.dirty", "snapshot.dirty", "branch.create", "task.snapshot"):
|
| 356 | self.assertIn(op, ops)
|
| 357 | self.assertEqual(validate_rows(rows, "core"), [])
|
| 358 |
|
| 359 | def test_fixture_extension_outside_track_patterns_fails_setup_closed(self) -> None:
|
| 360 | """An untracked comparator is an unfair comparator (config/git_modes.json
|
| 361 | git_lfs fairness_note): a .dat fixture file no pattern covers must turn
|
| 362 | the scenario into an honest skip with the exact reason."""
|
| 363 | rows = self.run_listed_scenario(["file-0000.bin", "file-0001.dat"])
|
| 364 | ops = [row["operation"] for row in rows]
|
| 365 |
|
| 366 | self.assertIn("mode.setup.lfs_track", ops) # track itself succeeded
|
| 367 | measured = [row["operation"] for row in rows if not row.get("skipped")]
|
| 368 | self.assertNotIn("snapshot.initial", measured) # nothing measured under the unfair label
|
| 369 | abandoned = [row for row in rows if row["operation"] == "snapshot.initial"]
|
| 370 | self.assertTrue(abandoned and all(row["skip_reason"] == "aborted_after_failed_prerequisite:repo.init"
|
| 371 | for row in abandoned))
|
| 372 | coverage = next(row for row in rows if row["operation"] == "mode.setup.lfs_track_coverage")
|
| 373 | self.assertEqual(coverage["operation"], "mode.setup.lfs_track_coverage")
|
| 374 | self.assertEqual(coverage["returncode"], 77)
|
| 375 | self.assertTrue(coverage["skipped"])
|
| 376 | self.assertEqual(coverage["skip_reason"], "lfs_track_incomplete:dat")
|
| 377 | self.assertEqual(validate_rows(rows, "core"), [])
|
| 378 |
|
| 379 |
|
| 380 | @unittest.skipUnless(GIT and GIT_LFS, "git-lfs not installed")
|
| 381 | class LiveGitLfsSmokeTest(unittest.TestCase):
|
| 382 | """Real git-lfs smoke: tiny fixture, one run. Skips on hosts without
|
| 383 | git-lfs; runs the genuine install/track/snapshot path where it exists."""
|
| 384 |
|
| 385 | def test_real_lfs_run_emits_setup_rows_and_measured_rows(self) -> None:
|
| 386 | if not bench.git_lfs_available(Path(GIT)):
|
| 387 | self.skipTest("git lfs version failed despite git-lfs on PATH")
|
| 388 | scenario = bench.Scenario("single_large_binary", 1, 1024, 1, True, 1)
|
| 389 | with tempfile.TemporaryDirectory() as tmp:
|
| 390 | root = Path(tmp)
|
| 391 | fixture = make_binary_fixture(root, ["file-0000.bin"], size=1024)
|
| 392 | rows = bench.run_subject_scenario(
|
| 393 | Subject(name="git_lfs", kind="git", label="Git (lfs)", bin=Path(GIT), git_mode="lfs"),
|
| 394 | scenario,
|
| 395 | fixture,
|
| 396 | 0,
|
| 397 | root / "runs",
|
| 398 | {
|
| 399 | "bench_id": "test",
|
| 400 | "profile": "micro",
|
| 401 | "benchmark_track": "agent-default",
|
| 402 | "command_semantics_version": bench.COMMAND_SEMANTICS_VERSION,
|
| 403 | "timestamp_utc": "test",
|
| 404 | },
|
| 405 | skip_diff=True,
|
| 406 | admitted_output_chars=20_000,
|
| 407 | track="agent-default",
|
| 408 | skip_determinism_probe=True,
|
| 409 | skip_remote=True,
|
| 410 | )
|
| 411 | ops = [row["operation"] for row in rows]
|
| 412 | by_op = {row["operation"]: row for row in rows}
|
| 413 | self.assertEqual(by_op["mode.setup.lfs_install"]["returncode"], 0)
|
| 414 | self.assertEqual(by_op["mode.setup.lfs_track"]["returncode"], 0)
|
| 415 | self.assertIn("snapshot.initial", ops)
|
| 416 | self.assertEqual(by_op["snapshot.initial"]["returncode"], 0)
|
| 417 | self.assertTrue(all(row["subject"] == "git_lfs" for row in rows))
|
| 418 | self.assertEqual(validate_rows(rows, "core"), [])
|
| 419 |
|
| 420 |
|
| 421 | if __name__ == "__main__":
|
| 422 | unittest.main()
|