Extracts the badblocks shell-command construction into _build_badblocks_cmd(devname) so it can be unit-tested without spinning up an asyncssh connection. Behavior unchanged. Three tests guard: 1. Defaults match disk-burnin.sh recommendation (-b 4096 -c 64 -p 1) 2. Operator-set tunables actually propagate to the command 3. The PID-capture wrapper (sh -c 'echo PID:\$\$; exec ...') stays intact — without it, cancel cannot kill the remote process because asyncssh's signal channel is silently ignored by sshd.
77 lines
2.9 KiB
Python
77 lines
2.9 KiB
Python
"""Verifies the Spearfoot tunables (block_size, block_buffer, passes)
|
|
actually thread through to the badblocks command line.
|
|
|
|
These three settings are exposed in Settings → Burn-in. Without a test,
|
|
nothing catches if a future refactor drops one of the flags or reads
|
|
from the wrong attribute. The defaults match the Spearfoot disk-burnin.sh
|
|
community script; non-defaults can roughly halve runtime on multi-TB
|
|
drives at the cost of more RAM.
|
|
|
|
Run inside the container image so app deps are present.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from app.burnin.stages import _build_badblocks_cmd
|
|
from app.config import settings
|
|
|
|
|
|
class TestBadblocksCmd(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
# Snapshot defaults so each test can mutate freely without
|
|
# polluting siblings or the running process.
|
|
self._snap = (
|
|
settings.surface_validate_block_size,
|
|
settings.surface_validate_block_buffer,
|
|
settings.surface_validate_passes,
|
|
)
|
|
|
|
def tearDown(self):
|
|
(
|
|
settings.surface_validate_block_size,
|
|
settings.surface_validate_block_buffer,
|
|
settings.surface_validate_passes,
|
|
) = self._snap
|
|
|
|
def test_defaults_match_spearfoot(self):
|
|
"""Out of the box: -b 4096 -c 64 -p 1 — matches the
|
|
disk-burnin.sh community script's recommendation for HDDs."""
|
|
cmd = _build_badblocks_cmd("sda")
|
|
self.assertIn("-b 4096", cmd)
|
|
self.assertIn("-c 64", cmd)
|
|
self.assertIn("-p 1", cmd)
|
|
self.assertIn("/dev/sda", cmd)
|
|
# Destructive write+verify mode must always be present — anything
|
|
# else (read-only, non-destructive) defeats the purpose of burn-in.
|
|
self.assertIn("-wsv", cmd)
|
|
|
|
def test_tunables_propagate_to_cmd(self):
|
|
"""Operator-set values (e.g. for paranoid 3-pass burn-in on a
|
|
suspect drive, or 8 KiB blocks for faster scan on a 24 TB HDD)
|
|
must end up in the shell command."""
|
|
settings.surface_validate_block_size = 8192
|
|
settings.surface_validate_block_buffer = 128
|
|
settings.surface_validate_passes = 3
|
|
cmd = _build_badblocks_cmd("sdb")
|
|
self.assertIn("-b 8192", cmd)
|
|
self.assertIn("-c 128", cmd)
|
|
self.assertIn("-p 3", cmd)
|
|
self.assertNotIn("-b 4096", cmd) # no leak from defaults
|
|
self.assertNotIn("-c 64", cmd)
|
|
self.assertIn("/dev/sdb", cmd)
|
|
|
|
def test_pid_capture_wrapper_intact(self):
|
|
"""The `sh -c 'echo PID:$$; exec ...'` wrapper is what makes
|
|
out-of-band kill -9 work over a fresh SSH session — asyncssh's
|
|
signal channel is silently ignored by sshd. If a future refactor
|
|
drops the wrapper, a cancel won't actually stop the test."""
|
|
cmd = _build_badblocks_cmd("sda")
|
|
self.assertTrue(cmd.startswith("sh -c 'echo PID:$$; exec badblocks"))
|
|
self.assertTrue(cmd.endswith("'"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|