104 lines
5.0 KiB
Python
104 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Failure-injection tests for Phase-1.0AM bounded copy/restore."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
import sys
|
|
import unittest
|
|
|
|
|
|
PARSER = argparse.ArgumentParser()
|
|
PARSER.add_argument("--root", type=Path, required=True)
|
|
ROOT = PARSER.parse_args().root
|
|
sys.path.insert(0, str(ROOT / "tools"))
|
|
|
|
from phase10am_bounded_copy_model import * # noqa: E402,F403
|
|
|
|
|
|
def prefix() -> list[FakeCopyEvent]:
|
|
return [FakeCopyEvent(BACKUP_AUTHID, OK), FakeCopyEvent(BACKUP_CAPS, OK),
|
|
FakeCopyEvent(SET_PRIV_AUTHID, OK), FakeCopyEvent(SET_PRIV_CAPS, OK)]
|
|
|
|
|
|
class BoundedCopyTests(unittest.TestCase):
|
|
def test_exact_multichunk_success_restores_both_fields(self) -> None:
|
|
events = prefix() + [FakeCopyEvent(COPY_CHUNK, OK, 4, MORE),
|
|
FakeCopyEvent(COPY_CHUNK, OK, 6, COMPLETE),
|
|
FakeCopyEvent(RESTORE_CAPS, OK),
|
|
FakeCopyEvent(RESTORE_AUTHID, OK)]
|
|
result = run_copy(CopyPlan(100, 200, 10), FakeCopyFacade(tuple(events)))
|
|
self.assertTrue(result.success)
|
|
self.assertEqual(result.copied, 10)
|
|
self.assertTrue(result.child_alive)
|
|
self.assertFalse(result.target_copy_performed)
|
|
self.assertTrue(result.service_available)
|
|
|
|
def test_partial_error_is_reported_and_child_is_reaped(self) -> None:
|
|
events = prefix() + [FakeCopyEvent(COPY_CHUNK, OK, 4, MORE),
|
|
FakeCopyEvent(COPY_CHUNK, ERROR, 0, MORE),
|
|
FakeCopyEvent(RESTORE_CAPS, OK),
|
|
FakeCopyEvent(RESTORE_AUTHID, OK),
|
|
FakeCopyEvent(KILL_AND_REAP_CHILD, OK)]
|
|
result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events)))
|
|
self.assertEqual(result.classification, "OFFLINE_PARTIAL_COPY_CONTAINED")
|
|
self.assertEqual(result.copied, 4)
|
|
self.assertFalse(result.child_alive)
|
|
|
|
def test_zero_and_oversized_progress_never_complete(self) -> None:
|
|
for progress in (0, 11):
|
|
events = prefix() + [FakeCopyEvent(COPY_CHUNK, OK, progress, MORE),
|
|
FakeCopyEvent(RESTORE_CAPS, OK),
|
|
FakeCopyEvent(RESTORE_AUTHID, OK)]
|
|
with self.subTest(progress=progress):
|
|
result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events)))
|
|
self.assertFalse(result.success)
|
|
self.assertEqual(result.copied, 0)
|
|
|
|
def test_both_restores_are_attempted_and_failure_contains_service(self) -> None:
|
|
events = prefix() + [FakeCopyEvent(COPY_CHUNK, ERROR, 0, MORE),
|
|
FakeCopyEvent(RESTORE_CAPS, ERROR),
|
|
FakeCopyEvent(RESTORE_AUTHID, ERROR),
|
|
FakeCopyEvent(KILL_AND_REAP_CHILD, OK),
|
|
FakeCopyEvent(TERMINATE_SERVICE, OK)]
|
|
result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events)))
|
|
self.assertEqual(result.restore_failure_bits, 3)
|
|
self.assertFalse(result.child_alive or result.service_available)
|
|
|
|
def test_caps_set_failure_still_restores_authid(self) -> None:
|
|
events = prefix()[:3] + [FakeCopyEvent(SET_PRIV_CAPS, ERROR),
|
|
FakeCopyEvent(RESTORE_AUTHID, OK)]
|
|
result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events)))
|
|
self.assertEqual(result.classification,
|
|
"OFFLINE_COPY_REJECTED_BEFORE_MUTATION")
|
|
self.assertTrue(result.child_alive and result.service_available)
|
|
|
|
def test_restore_or_child_termination_failure_is_hard(self) -> None:
|
|
events = prefix() + [FakeCopyEvent(COPY_CHUNK, OK, 1, MORE),
|
|
FakeCopyEvent(RESTORE_CAPS, OK),
|
|
FakeCopyEvent(RESTORE_AUTHID, OK),
|
|
FakeCopyEvent(KILL_AND_REAP_CHILD, ERROR)]
|
|
with self.assertRaises(CopyModelError):
|
|
run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events)))
|
|
|
|
def test_bounds_deadline_status_and_arithmetic(self) -> None:
|
|
for args in ((0, 0, 0), (MAX_U64, 0, 2), (0, MAX_U64, 2)):
|
|
with self.subTest(args=args), self.assertRaises(CopyModelError):
|
|
CopyPlan(*args)
|
|
events = prefix()
|
|
events[0] = FakeCopyEvent(BACKUP_AUTHID, OK, ticks=MAX_TICKS)
|
|
result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(events[:2])))
|
|
self.assertFalse(result.success)
|
|
incomplete = prefix() + [FakeCopyEvent(COPY_CHUNK, OK, 9, COMPLETE),
|
|
FakeCopyEvent(RESTORE_CAPS, OK),
|
|
FakeCopyEvent(RESTORE_AUTHID, OK),
|
|
FakeCopyEvent(KILL_AND_REAP_CHILD, OK)]
|
|
result = run_copy(CopyPlan(0, 32, 10), FakeCopyFacade(tuple(incomplete)))
|
|
self.assertFalse(result.success)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(argv=[__file__])
|