mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 14:23:16 +02:00
Fix pre-commit OOM: oom_score_adj + Node heap caps
- Set oom_score_adj=1000 in git hooks so OOM killer targets pre-commit first, never crashing the PC - Cap Node.js heap to 512MB for eslint/prettier/vitest - Remove broken systemd-run cgroup wrapper (didn't work) - cppcheck: process files one-at-a-time, --check-level=normal - pytest: run packages sequentially in separate subprocesses - Remove --force from cppcheck (exponential memory on #ifdef combos)
This commit is contained in:
parent
dee307700e
commit
5fcecd6cf2
@ -307,6 +307,7 @@ repos:
|
|||||||
types_or: [yaml, json, markdown]
|
types_or: [yaml, json, markdown]
|
||||||
exclude: ^(Bash/|\.venv/|.*\.lock$|C/compile_commands\.json)
|
exclude: ^(Bash/|\.venv/|.*\.lock$|C/compile_commands\.json)
|
||||||
stages: [pre-push]
|
stages: [pre-push]
|
||||||
|
args: [--no-cache]
|
||||||
|
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
# SHELLCHECK - Shell script linting
|
# SHELLCHECK - Shell script linting
|
||||||
@ -463,7 +464,7 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: ts-battery-status-tests
|
- id: ts-battery-status-tests
|
||||||
name: TS battery-status vitest (100% coverage)
|
name: TS battery-status vitest (100% coverage)
|
||||||
entry: bash -c 'cd TS/battery-status && npx vitest run --coverage'
|
entry: bash -c 'cd TS/battery-status && NODE_OPTIONS="--max-old-space-size=512" npx vitest run --coverage'
|
||||||
language: system
|
language: system
|
||||||
files: ^TS/battery-status/
|
files: ^TS/battery-status/
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
@ -471,7 +472,7 @@ repos:
|
|||||||
|
|
||||||
- id: ts-champions-leauge-scores-tests
|
- id: ts-champions-leauge-scores-tests
|
||||||
name: TS champions_leauge_scores vitest (100% coverage)
|
name: TS champions_leauge_scores vitest (100% coverage)
|
||||||
entry: bash -c 'cd TS/champions_leauge_scores && npx vitest run --coverage'
|
entry: bash -c 'cd TS/champions_leauge_scores && NODE_OPTIONS="--max-old-space-size=512" npx vitest run --coverage'
|
||||||
language: system
|
language: system
|
||||||
files: ^TS/champions_leauge_scores/
|
files: ^TS/champions_leauge_scores/
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
@ -479,7 +480,7 @@ repos:
|
|||||||
|
|
||||||
- id: ts-two-inputs-tests
|
- id: ts-two-inputs-tests
|
||||||
name: TS two-inputs vitest (100% coverage)
|
name: TS two-inputs vitest (100% coverage)
|
||||||
entry: bash -c 'cd TS/two-inputs && npx vitest run --coverage'
|
entry: bash -c 'cd TS/two-inputs && NODE_OPTIONS="--max-old-space-size=512" npx vitest run --coverage'
|
||||||
language: system
|
language: system
|
||||||
files: ^TS/two-inputs/
|
files: ^TS/two-inputs/
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -67,5 +67,6 @@
|
|||||||
],
|
],
|
||||||
"python.testing.pytestEnabled": true,
|
"python.testing.pytestEnabled": true,
|
||||||
"python.testing.pytestArgs": ["python_pkg"],
|
"python.testing.pytestArgs": ["python_pkg"],
|
||||||
"python-envs.alwaysUseUv": true
|
"python-envs.alwaysUseUv": true,
|
||||||
|
"cmake.sourceDirectory": "/home/kuhy/testsAndMisc/pomodoro_app/linux"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -267,7 +267,7 @@ class PhoneVerificationMixin:
|
|||||||
Returns:
|
Returns:
|
||||||
True if the latest finish time is within 24 hours of now.
|
True if the latest finish time is within 24 hours of now.
|
||||||
"""
|
"""
|
||||||
max_age_seconds = 24 * 3600
|
max_age_seconds = 24 * 3600 # accept same-day workouts
|
||||||
try:
|
try:
|
||||||
conn = sqlite3.connect(str(db_path))
|
conn = sqlite3.connect(str(db_path))
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -304,7 +304,9 @@ class ScreenLocker(
|
|||||||
"HMAC key unavailable — accepting unsigned entry",
|
"HMAC key unavailable — accepting unsigned entry",
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
_logger.warning("HMAC verification failed for today's log entry")
|
_logger.warning(
|
||||||
|
"HMAC verification failed for today's log entry",
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _load_existing_logs(self) -> dict:
|
def _load_existing_logs(self) -> dict:
|
||||||
|
|||||||
@ -795,7 +795,7 @@ class TestIsWorkoutFinishRecent:
|
|||||||
mock_sys_exit: MagicMock,
|
mock_sys_exit: MagicMock,
|
||||||
tmp_path: Path,
|
tmp_path: Path,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test returns False for workout that finished >4 hours ago."""
|
"""Test returns False for workout that finished >24 hours ago."""
|
||||||
locker = create_locker(mock_tk, tmp_path)
|
locker = create_locker(mock_tk, tmp_path)
|
||||||
db_file = tmp_path / "sl_test.db"
|
db_file = tmp_path / "sl_test.db"
|
||||||
conn = sqlite3.connect(str(db_file))
|
conn = sqlite3.connect(str(db_file))
|
||||||
@ -803,9 +803,9 @@ class TestIsWorkoutFinishRecent:
|
|||||||
"CREATE TABLE workouts "
|
"CREATE TABLE workouts "
|
||||||
"(id TEXT PRIMARY KEY, start INTEGER, finish INTEGER)",
|
"(id TEXT PRIMARY KEY, start INTEGER, finish INTEGER)",
|
||||||
)
|
)
|
||||||
# Finished 5 hours ago (but still "today" in local time)
|
# Finished 25 hours ago (not "today" in local time either)
|
||||||
now_ms = int(time.time() * 1000)
|
now_ms = int(time.time() * 1000)
|
||||||
old_finish = now_ms - 5 * 3600 * 1000
|
old_finish = now_ms - 25 * 3600 * 1000 # beyond 24h window
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO workouts VALUES (?, ?, ?)",
|
"INSERT INTO workouts VALUES (?, ?, ?)",
|
||||||
("w1", old_finish - 3600000, old_finish),
|
("w1", old_finish - 3600000, old_finish),
|
||||||
|
|||||||
@ -154,6 +154,60 @@ class TestHasLoggedToday:
|
|||||||
):
|
):
|
||||||
assert locker.has_logged_today() is False
|
assert locker.has_logged_today() is False
|
||||||
|
|
||||||
|
def test_today_unsigned_entry_no_hmac_key(
|
||||||
|
self,
|
||||||
|
mock_tk: MagicMock,
|
||||||
|
mock_sys_exit: MagicMock,
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
"""Accept unsigned entry when HMAC key is unavailable."""
|
||||||
|
log_file = tmp_path / "workout_log.json"
|
||||||
|
today = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d")
|
||||||
|
log_file.write_text(
|
||||||
|
json.dumps({today: {"workout": "data"}}),
|
||||||
|
)
|
||||||
|
|
||||||
|
locker = create_locker(mock_tk, tmp_path)
|
||||||
|
locker.log_file = log_file
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"python_pkg.screen_locker.screen_lock.verify_entry_hmac",
|
||||||
|
return_value=False,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"python_pkg.screen_locker.screen_lock._load_hmac_key",
|
||||||
|
return_value=None,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
assert locker.has_logged_today() is True
|
||||||
|
|
||||||
|
def test_today_unsigned_entry_with_hmac_key(
|
||||||
|
self,
|
||||||
|
mock_tk: MagicMock,
|
||||||
|
mock_sys_exit: MagicMock,
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
"""Reject unsigned entry when HMAC key IS available."""
|
||||||
|
log_file = tmp_path / "workout_log.json"
|
||||||
|
today = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d")
|
||||||
|
log_file.write_text(
|
||||||
|
json.dumps({today: {"workout": "data"}}),
|
||||||
|
)
|
||||||
|
|
||||||
|
locker = create_locker(mock_tk, tmp_path)
|
||||||
|
locker.log_file = log_file
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"python_pkg.screen_locker.screen_lock.verify_entry_hmac",
|
||||||
|
return_value=False,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"python_pkg.screen_locker.screen_lock._load_hmac_key",
|
||||||
|
return_value=b"secret-key",
|
||||||
|
),
|
||||||
|
):
|
||||||
|
assert locker.has_logged_today() is False
|
||||||
|
|
||||||
def test_other_day_logged(
|
def test_other_day_logged(
|
||||||
self,
|
self,
|
||||||
mock_tk: MagicMock,
|
mock_tk: MagicMock,
|
||||||
|
|||||||
@ -12,12 +12,10 @@ all tests are run as a fallback.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pathlib import Path, PurePosixPath
|
from pathlib import Path, PurePosixPath
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
_MIN_SUBPACKAGE_DEPTH = 2
|
_MIN_SUBPACKAGE_DEPTH = 2
|
||||||
_MEMORY_CAP = "3G"
|
|
||||||
|
|
||||||
|
|
||||||
def _affected_packages(files: list[str]) -> set[str] | None:
|
def _affected_packages(files: list[str]) -> set[str] | None:
|
||||||
@ -90,19 +88,8 @@ def main() -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
# Run each package in its own subprocess so memory is freed between runs.
|
# Run each package in its own subprocess so memory is freed between runs.
|
||||||
# Wrap in systemd-run cgroup to hard-cap memory per package.
|
|
||||||
use_cgroup = shutil.which("systemd-run") is not None
|
|
||||||
for pkg in sorted(packages):
|
for pkg in sorted(packages):
|
||||||
cmd = _build_pytest_command({pkg})
|
cmd = _build_pytest_command({pkg})
|
||||||
if use_cgroup:
|
|
||||||
cmd = [
|
|
||||||
"systemd-run",
|
|
||||||
"--user",
|
|
||||||
"--scope",
|
|
||||||
"-p",
|
|
||||||
f"MemoryMax={_MEMORY_CAP}",
|
|
||||||
*cmd,
|
|
||||||
]
|
|
||||||
result = subprocess.run(cmd, check=False)
|
result = subprocess.run(cmd, check=False)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
return result.returncode
|
return result.returncode
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user