forked from MakazhanAlpamys/Soup
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_crash_reporter.py
More file actions
184 lines (153 loc) · 6.36 KB
/
Copy pathtest_crash_reporter.py
File metadata and controls
184 lines (153 loc) · 6.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
"""Tests for v0.34.0 Part D — crash reporter."""
from __future__ import annotations
import json
import pytest
from soup_cli.utils.crash import (
CRASH_FORMAT_VERSION,
MAX_BUNDLE_BYTES,
build_crash_bundle,
classify_crash,
redact_secrets,
write_crash_bundle,
)
class TestClassify:
def test_oom(self):
assert classify_crash("CUDA out of memory") == "oom"
assert classify_crash("torch.cuda.OutOfMemoryError: …") == "oom"
def test_nan(self):
assert classify_crash("loss is NaN at step 100") == "nan"
assert classify_crash("infinite loss detected") == "nan"
def test_cuda(self):
assert classify_crash("CUDA error: device-side assert triggered") == "cuda"
def test_dataloader(self):
assert classify_crash("DataLoader worker exited unexpectedly") == "dataloader"
def test_nccl(self):
assert classify_crash("NCCL error encountered") == "nccl"
def test_other(self):
assert classify_crash("ZeroDivisionError") == "other"
def test_non_string(self):
assert classify_crash(None) == "other"
class TestRedact:
def test_hf_token(self):
out = redact_secrets("token=hf_abcdef0123456789012345 broken")
assert "hf_abcdef" not in out
assert "<redacted>" in out
def test_openai_key(self):
out = redact_secrets("OPENAI_KEY=sk-abc1234567890123456789")
assert "sk-abc" not in out
assert "<redacted>" in out
def test_bearer_token(self):
out = redact_secrets("Authorization: Bearer abc12345678901234567890")
assert "<redacted>" in out
def test_no_change_when_clean(self):
clean = "everything fine here"
assert redact_secrets(clean) == clean
class TestBuildBundle:
def test_minimal(self):
try:
raise RuntimeError("boom")
except RuntimeError as exc:
bundle = build_crash_bundle(error=exc)
assert bundle["format_version"] == CRASH_FORMAT_VERSION
assert bundle["error"]["type"] == "RuntimeError"
assert bundle["error"]["message"] == "boom"
assert "traceback" in bundle["error"]
assert "gpu_state" in bundle
assert "environment" in bundle
def test_classifies_kind(self):
try:
raise RuntimeError("CUDA out of memory; tried to allocate 80GB")
except RuntimeError as exc:
bundle = build_crash_bundle(error=exc)
assert bundle["error"]["kind"] == "oom"
def test_redacts_secrets_in_error_message(self):
try:
raise RuntimeError("auth failed with hf_abcdef0123456789012345token")
except RuntimeError as exc:
bundle = build_crash_bundle(error=exc)
assert "hf_abcdef" not in bundle["error"]["message"]
def test_metrics_tail_capped(self):
rows = [{"step": i, "loss": 1.0 / (i + 1)} for i in range(200)]
try:
raise RuntimeError("x")
except RuntimeError as exc:
bundle = build_crash_bundle(error=exc, metrics=rows)
assert len(bundle["metrics_tail"]) == 50
assert bundle["metrics_tail"][-1]["step"] == 199
def test_config_serialised(self):
try:
raise RuntimeError("x")
except RuntimeError as exc:
bundle = build_crash_bundle(error=exc, config={"base": "x", "lr": 1e-4})
assert bundle["config"]["base"] == "x"
def test_config_secrets_redacted(self):
# H1: hf_TOKEN-shaped value inside config must be redacted.
try:
raise RuntimeError("x")
except RuntimeError as exc:
bundle = build_crash_bundle(
error=exc,
config={"hub": {"token": "hf_abcdef0123456789012345"}},
)
assert "hf_abcdef" not in str(bundle["config"])
assert "<redacted>" in str(bundle["config"])
def test_config_unserialisable_handled(self):
try:
raise RuntimeError("x")
except RuntimeError as exc:
bundle = build_crash_bundle(error=exc, config={"set": {1, 2, 3}})
# Falls back to either serialised-via-default-str or unserialisable marker.
assert "config" in bundle
def test_output_dir_basename_only(self):
# Output dir like /home/alice/.cache/x must not leak the path.
try:
raise RuntimeError("x")
except RuntimeError as exc:
bundle = build_crash_bundle(
error=exc, output_dir="/home/alice/.cache/models/run_x",
)
assert "alice" not in str(bundle.get("output_dir") or "")
assert bundle["output_dir"] == "run_x"
class TestWriteBundle:
def test_writes_to_target_dir(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
target = tmp_path / "out"
try:
raise RuntimeError("x")
except RuntimeError as exc:
bundle = build_crash_bundle(error=exc)
path = write_crash_bundle(bundle, target_dir=target)
assert path.exists()
assert path.suffix == ".crash"
assert path.parent == target.resolve()
# Re-parse
data = json.loads(path.read_text(encoding="utf-8"))
assert data["error"]["type"] == "RuntimeError"
def test_default_dir_is_under_cwd(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
try:
raise RuntimeError("x")
except RuntimeError as exc:
bundle = build_crash_bundle(error=exc)
path = write_crash_bundle(bundle)
assert (tmp_path / ".soup-crashes") in path.parents
def test_outside_cwd_rejected(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
outside = tmp_path.parent / "outside-crashes"
try:
raise RuntimeError("x")
except RuntimeError as exc:
bundle = build_crash_bundle(error=exc)
with pytest.raises(ValueError, match="not under cwd"):
write_crash_bundle(bundle, target_dir=outside)
def test_oversize_truncated(self, tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
try:
raise RuntimeError("x")
except RuntimeError as exc:
bundle = build_crash_bundle(error=exc)
# Stuff a giant string
bundle["junk"] = "x" * (MAX_BUNDLE_BYTES + 100)
path = write_crash_bundle(bundle, target_dir=tmp_path)
data = json.loads(path.read_text(encoding="utf-8"))
assert data.get("_truncated") is True