Skip to content

Commit 1c6376a

Browse files
committed
Improve test assertions to show actual values on failure
Replace assertion patterns that don't show values on failure with NumPy and PyTorch testing utilities. Migrate from `self.assertTrue(np.all(...))` and `self.assertTrue(torch.allclose(...))` to `np.testing.assert_array_equal`, `np.testing.assert_array_compare`, `np.testing.assert_allclose`, and `torch.testing.assert_close`. These testing utilities automatically include summaries of actual vs expected values in their error messages, making test failures much easier to debug.
1 parent 9d6d236 commit 1c6376a

16 files changed

Lines changed: 95 additions & 83 deletions

tests/cuda/buffer_transfer_test.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test_transfer_buffer_to_cuda(self, media_type: str) -> None:
6969
self.assertTrue(cuda_tensor.is_cuda)
7070
self.assertEqual(cuda_tensor.device, torch.device(f"cuda:{DEFAULT_CUDA}"))
7171

72-
self.assertTrue(torch.allclose(cpu_tensor, cuda_tensor.cpu()))
72+
torch.testing.assert_close(cpu_tensor, cuda_tensor.cpu())
7373

7474
@parameterized.expand(
7575
[
@@ -128,7 +128,7 @@ def test() -> None:
128128
cuda_tensor = spdl.io.to_torch(cuda_buffer)
129129
self.assertTrue(cuda_tensor.is_cuda)
130130
self.assertEqual(cuda_tensor.device, torch.device(f"cuda:{DEFAULT_CUDA}"))
131-
self.assertTrue(torch.allclose(cpu_tensor, cuda_tensor.cpu()))
131+
torch.testing.assert_close(cpu_tensor, cuda_tensor.cpu())
132132

133133
print("Asserting deleter was not yet called")
134134
self.assertFalse(deleter_called)
@@ -161,7 +161,7 @@ def test(array) -> None:
161161
self.assertEqual(tensor.dtype, dtypes[array.dtype])
162162
self.assertEqual(tensor.shape, torch.Size(array.shape))
163163
self.assertEqual(tensor.device, torch.device(device))
164-
self.assertTrue(torch.allclose(tensor, torch.from_numpy(array).to(device)))
164+
torch.testing.assert_close(tensor, torch.from_numpy(array).to(device))
165165

166166
for dtype in [np.uint8, np.int32, np.int64]:
167167
max_val = np.iinfo(dtype).max
@@ -182,7 +182,7 @@ def test(cpu_tensor) -> None:
182182
self.assertEqual(cuda_tensor.dtype, cpu_tensor.dtype)
183183
self.assertEqual(cuda_tensor.shape, cpu_tensor.shape)
184184
self.assertEqual(cuda_tensor.device, device)
185-
self.assertTrue(torch.allclose(cuda_tensor, cpu_tensor.to(device)))
185+
torch.testing.assert_close(cuda_tensor, cpu_tensor.to(device))
186186

187187
for dtype in [np.uint8, np.int32, np.int64]:
188188
max_val = np.iinfo(dtype).max
@@ -205,7 +205,7 @@ def test_array_transfer_non_contiguous_torch(self) -> None:
205205
self.assertEqual(cuda_tensor.dtype, cpu_tensor.dtype)
206206
self.assertEqual(cuda_tensor.shape, cpu_tensor.shape)
207207
self.assertEqual(cuda_tensor.device, device)
208-
self.assertTrue(torch.allclose(cuda_tensor, cpu_tensor.to(device)))
208+
torch.testing.assert_close(cuda_tensor, cpu_tensor.to(device))
209209

210210
def test_array_transfer_non_contiguous_numpy(self) -> None:
211211
"""passing noncontiguous array/tensor to transfer_buffer works"""
@@ -224,7 +224,7 @@ def test_array_transfer_non_contiguous_numpy(self) -> None:
224224
self.assertEqual(tensor.dtype, torch.int64)
225225
self.assertEqual(tensor.shape, torch.Size(arr.shape))
226226
self.assertEqual(tensor.device, torch.device(device))
227-
self.assertTrue(torch.allclose(tensor, torch.from_numpy(arr).to(device)))
227+
torch.testing.assert_close(tensor, torch.from_numpy(arr).to(device))
228228

229229
def test_array_transfer_smoke_test(self) -> None:
230230
"""smoke test for transferring multiple arrays concurrently"""
@@ -253,4 +253,4 @@ def test_transfer_cpu(self) -> None:
253253
self.assertEqual(ref.dtype, cpu_tensor.dtype)
254254
self.assertEqual(ref.shape, cpu_tensor.shape)
255255
self.assertEqual(ref.device, cpu_tensor.device)
256-
self.assertTrue(torch.allclose(ref, cpu_tensor))
256+
torch.testing.assert_close(ref, cpu_tensor)

tests/cuda/nvjpeg_decode_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ def _test(data, pix_fmt):
4444
rgb_tensor = _test(sample.path, "rgb")
4545
bgr_tensor = _test(sample.path, "bgr")
4646

47-
self.assertTrue(torch.equal(rgb_tensor[0], bgr_tensor[2]))
48-
self.assertTrue(torch.equal(rgb_tensor[1], bgr_tensor[1]))
49-
self.assertTrue(torch.equal(rgb_tensor[2], bgr_tensor[0]))
47+
torch.testing.assert_close(rgb_tensor[0], bgr_tensor[2])
48+
torch.testing.assert_close(rgb_tensor[1], bgr_tensor[1])
49+
torch.testing.assert_close(rgb_tensor[2], bgr_tensor[0])
5050

5151
def test_decode_rubbish(self) -> None:
5252
"""When decoding fails, it should raise an error instead of segfault then,

tests/cuda/pin_memory_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def test_pin_memory_convert_array(self) -> None:
8888
self.assertEqual(tensor.shape, (10,))
8989
self.assertEqual(tensor.dtype, torch.int64)
9090
self.assertEqual(tensor.device, torch.device("cuda:0"))
91-
self.assertTrue((vals == tensor.cpu().numpy()).all())
91+
np.testing.assert_array_equal(tensor.cpu().numpy(), vals, strict=True)
9292

9393
def test_pin_memory_convert_array_invalid_size(self) -> None:
9494
"""convert_array fails if storage is small."""

tests/cuda/transfer_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ def test_gpu_transfer(self) -> None:
2020
cuda = transfer_tensor(ref)
2121
print(cuda)
2222
self.assertEqual(cuda.device.type, "cuda")
23-
self.assertTrue(torch.equal(cuda, ref.cuda()))
23+
torch.testing.assert_close(cuda, ref.cuda())

tests/dataloader/sampler_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ def test_weighted_sampling(self) -> None:
462462
print(f"{ref=}")
463463
print(f"{hyp=}")
464464

465-
self.assertTrue(np.allclose(hyp, ref, atol=1e-3))
465+
np.testing.assert_allclose(hyp, ref, rtol=1e-5, atol=1e-3)
466466

467467

468468
class TestDistributedSamplerEmbedShuffle(unittest.TestCase):

tests/io/array_test.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,20 @@ def test_load_npy_integral(
4848

4949
data = _dump_npy(ref)
5050
recon = spdl.io.load_npy(data)
51-
self.assertTrue(np.array_equal(recon, ref))
51+
np.testing.assert_array_equal(recon, ref, strict=True)
5252

5353
# Use bytearray to check if the change to the original is refrected to the recon
5454
# (which means that the recon is referring to the original, no copy)
5555
data = bytearray(data)
5656
print(f"{id(data)=}")
5757
print(f"{id(recon.data.obj)=}")
5858
recon = spdl.io.load_npy(data)
59-
self.assertTrue(np.array_equal(recon, ref))
59+
np.testing.assert_array_equal(recon, ref, strict=True)
6060

6161
self.assertTrue(np.any(recon))
6262
# Fill zeros. The header is cleared too, but it's already parsed, so not an issue.
6363
data[:] = b"\x00" * len(data)
64-
self.assertFalse(np.any(recon))
64+
np.testing.assert_array_equal(recon, 0)
6565

6666
@parameterized.expand(
6767
[
@@ -81,20 +81,20 @@ def test_load_npy_float(self, dtype: type[np.floating]) -> None:
8181

8282
data = _dump_npy(ref)
8383
recon = spdl.io.load_npy(data)
84-
self.assertTrue(np.array_equal(recon, ref))
84+
np.testing.assert_array_equal(recon, ref, strict=True)
8585

8686
# Use bytearray to check if the change to the original is refrected to the recon
8787
# (which means that the recon is referring to the original, no copy)
8888
data = bytearray(data)
8989
print(f"{id(data)=}")
9090
print(f"{id(recon.data.obj)=}")
9191
recon = spdl.io.load_npy(data)
92-
self.assertTrue(np.array_equal(recon, ref))
92+
np.testing.assert_array_equal(recon, ref, strict=True)
9393

9494
self.assertTrue(np.any(recon))
9595
# Fill zeros. The header is cleared too, but it's already parsed, so not an issue.
9696
data[:] = b"\x00" * len(data)
97-
self.assertFalse(np.any(recon))
97+
np.testing.assert_array_equal(recon, 0)
9898

9999

100100
##############################################################################

tests/io/async_test.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,14 @@ def _decode(num_frames=None):
9191
arr1 = _decode(num_frames=num_frames)
9292
self.assertEqual(arr1.dtype, np.int16)
9393
self.assertEqual(arr1.shape, (num_frames, 1))
94-
self.assertTrue(np.all(arr1 == arr0[:num_frames]))
94+
np.testing.assert_array_equal(arr1, arr0[:num_frames], strict=True)
9595

9696
num_frames = 32000
9797
arr2 = _decode(num_frames=num_frames)
9898
self.assertEqual(arr2.dtype, np.int16)
9999
self.assertEqual(arr2.shape, (num_frames, 1))
100-
self.assertTrue(np.all(arr2[:16000] == arr0))
101-
self.assertTrue(np.all(arr2[16000:] == 0))
100+
np.testing.assert_array_equal(arr2[:16000], arr0, strict=True)
101+
np.testing.assert_array_equal(arr2[16000:], 0)
102102

103103
def test_decode_audio_many_channels_6(self) -> None:
104104
"""Can decode audio with more than 6 channels.
@@ -229,28 +229,30 @@ def _decode(pix_fmt="rgb24", **kwargs):
229229
arr1 = _decode(num_frames=num_frames)
230230
self.assertEqual(arr1.dtype, np.uint8)
231231
self.assertEqual(arr1.shape, (num_frames, 240, 320, 3))
232-
self.assertTrue(np.all(arr1 == arr0[:num_frames]))
232+
np.testing.assert_array_equal(arr1, arr0[:num_frames], strict=True)
233233

234234
num_frames = 100
235235
arr2 = _decode(num_frames=num_frames)
236236
self.assertEqual(arr2.dtype, np.uint8)
237237
self.assertEqual(arr2.shape, (num_frames, 240, 320, 3))
238-
self.assertTrue(np.all(arr2[:50] == arr0))
239-
self.assertTrue(np.all(arr2[50:] == arr2[50]))
238+
np.testing.assert_array_equal(arr2[:50], arr0, strict=True)
239+
np.testing.assert_array_equal(
240+
arr2[50:], np.broadcast_to(arr2[50], arr2[50:].shape), strict=True
241+
)
240242

241243
num_frames = 100
242244
arr2 = _decode(num_frames=num_frames, pad_mode="black")
243245
self.assertEqual(arr2.dtype, np.uint8)
244246
self.assertEqual(arr2.shape, (num_frames, 240, 320, 3))
245-
self.assertTrue(np.all(arr2[:50] == arr0))
246-
self.assertTrue(np.all(arr2[50:] == 0))
247+
np.testing.assert_array_equal(arr2[:50], arr0, strict=True)
248+
np.testing.assert_array_equal(arr2[50:], 0)
247249

248250
num_frames = 100
249251
arr2 = _decode(num_frames=num_frames, pad_mode="white")
250252
self.assertEqual(arr2.dtype, np.uint8)
251253
self.assertEqual(arr2.shape, (num_frames, 240, 320, 3))
252-
self.assertTrue(np.all(arr2[:50] == arr0))
253-
self.assertTrue(np.all(arr2[50:] == 255))
254+
np.testing.assert_array_equal(arr2[:50], arr0, strict=True)
255+
np.testing.assert_array_equal(arr2[50:], 255)
254256

255257
def test_decode_video_frame_rate_pts(self) -> None:
256258
"""Applying frame rate outputs correct PTS."""
@@ -267,7 +269,7 @@ def test_decode_video_frame_rate_pts(self) -> None:
267269
pts = frames.get_timestamps()
268270
print(pts_ref, pts)
269271

270-
self.assertTrue(np.all(pts_ref[::2] == pts))
272+
np.testing.assert_array_equal(pts_ref[::2], pts, strict=True)
271273

272274

273275
class TestConvertFrames(unittest.TestCase):

tests/io/frames_clone_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def test_clone_frames(self, media_type: str) -> None:
5656
array1 = _load_from_frames(frames1)
5757
array2 = _load_from_frames(frames2)
5858

59-
self.assertTrue(np.all(array1 == array2))
59+
np.testing.assert_array_equal(array1, array2, strict=True)
6060

6161
@parameterized.expand(
6262
[
@@ -113,4 +113,4 @@ def test_clone_frames_multi(self, media_type: str) -> None:
113113
arrays = [_load_from_frames(c) for c in clones]
114114

115115
for i in range(N):
116-
self.assertTrue(np.all(array == arrays[i]))
116+
np.testing.assert_array_equal(array, arrays[i], strict=True)

tests/io/image_decoding_test.py

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
# pyre-unsafe
88

9+
import operator
910
import unittest
1011

1112
import numpy as np
@@ -21,6 +22,18 @@ def _load_image(src, filter_desc="format=pix_fmts=rgb24"):
2122
return spdl.io.to_numpy(spdl.io.load_image(src, filter_desc=filter_desc))
2223

2324

25+
def _assert_all_ge(x, val) -> None:
26+
np.testing.assert_array_compare(
27+
operator.ge, x, val, header=f"Arrays are not all >= {val}"
28+
)
29+
30+
31+
def _assert_all_le(x, val) -> None:
32+
np.testing.assert_array_compare(
33+
operator.le, x, val, header=f"Arrays are not all <= {val}"
34+
)
35+
36+
2437
class TestDecodeImage(unittest.TestCase):
2538
def test_decode_image_gray16_native(self) -> None:
2639
"""Can decode gray16 PNG image (16be) as-is"""
@@ -44,8 +57,8 @@ def test_decode_image_gray16_native(self) -> None:
4457

4558
ffmpeg8 = get_ffmpeg_versions()["libavutil"][0] >= 60
4659

47-
self.assertTrue(np.all(hyp[..., :32] == 65535 if ffmpeg8 else 65022))
48-
self.assertTrue(np.all(hyp[..., 32:] == 0 if ffmpeg8 else 256))
60+
np.testing.assert_array_equal(hyp[..., :32], 65535 if ffmpeg8 else 65022)
61+
np.testing.assert_array_equal(hyp[..., 32:], 0 if ffmpeg8 else 256)
4962

5063
def test_decode_image_16be_rgb24(self) -> None:
5164
"""Can decode gray16 PNG image (16be) as rgb24"""
@@ -67,8 +80,8 @@ def test_decode_image_16be_rgb24(self) -> None:
6780
ref = load_ref_image(sample.path, shape)
6881
np.testing.assert_array_equal(hyp, ref, strict=True)
6982

70-
self.assertTrue(np.all(hyp[:, :32, :] == 255))
71-
self.assertTrue(np.all(hyp[:, 32:, :] == 0))
83+
np.testing.assert_array_equal(hyp[:, :32, :], 255)
84+
np.testing.assert_array_equal(hyp[:, 32:, :], 0)
7285

7386
def test_decode_image_yuvj422_native(self) -> None:
7487
"""Can decode yuvj422p JPEG image as-is."""
@@ -153,17 +166,17 @@ def test_decode_image_yuvj420p_as_rgb24_edge_values(self) -> None:
153166

154167
red, green, blue = hyp[:, :width], hyp[:, width:2*width], hyp[:, 2*width:]
155168

156-
self.assertTrue(np.all(red[..., 0] >= 254))
157-
self.assertTrue(np.all(red[..., 1] <= 1))
158-
self.assertTrue(np.all(red[..., 2] == 0))
169+
_assert_all_ge(red[..., 0], 254)
170+
_assert_all_le(red[..., 1], 1)
171+
np.testing.assert_array_equal(red[..., 2], 0)
159172

160-
self.assertTrue(np.all(green[..., 0] == 0))
161-
self.assertTrue(np.all(green[..., 1] >= 253))
162-
self.assertTrue(np.all(green[..., 2] <= 1))
173+
np.testing.assert_array_equal(green[..., 0], 0)
174+
_assert_all_ge(green[..., 1], 253)
175+
_assert_all_le(green[..., 2], 1)
163176

164-
self.assertTrue(np.all(blue[..., 0] <= 1))
165-
self.assertTrue(np.all(blue[..., 1] <= 1))
166-
self.assertTrue(np.all(blue[..., 2] >= 254))
177+
_assert_all_le(blue[..., 0], 1)
178+
_assert_all_le(blue[..., 1], 1)
179+
_assert_all_ge(blue[..., 2], 254)
167180

168181
def test_decode_image_yuvj444p_native(self) -> None:
169182
"""Can decode yuvj444p JPEG image as-is."""
@@ -335,19 +348,19 @@ def test_load_image_batch_native_edge_values(self) -> None:
335348

336349
left, middle, right = arr[..., :w, :], arr[..., w:-w, :], arr[..., -w:, :]
337350
# Red
338-
self.assertTrue(np.all(left[..., 0] >= 252))
339-
self.assertTrue(np.all(left[..., 1] == 0))
340-
self.assertTrue(np.all(left[..., 2] == 0))
351+
_assert_all_ge(left[..., 0], 252)
352+
np.testing.assert_array_equal(left[..., 1], 0)
353+
np.testing.assert_array_equal(left[..., 2], 0)
341354

342355
# Green
343-
self.assertTrue(np.all(middle[..., 0] == 0))
344-
self.assertTrue(np.all(middle[..., 1] >= 253))
345-
self.assertTrue(np.all(middle[..., 2] == 0))
356+
np.testing.assert_array_equal(middle[..., 0], 0)
357+
_assert_all_ge(middle[..., 1], 253)
358+
np.testing.assert_array_equal(middle[..., 2], 0)
346359

347360
# Blue
348-
self.assertTrue(np.all(right[..., 0] == 0))
349-
self.assertTrue(np.all(right[..., 1] == 0))
350-
self.assertTrue(np.all(right[..., 2] >= 253))
361+
np.testing.assert_array_equal(right[..., 0], 0)
362+
np.testing.assert_array_equal(right[..., 1], 0)
363+
_assert_all_ge(right[..., 2], 253)
351364

352365
def test_batch_decode_image_handle_failure(self) -> None:
353366
"""load_image_batch dismisses failures when strict=False."""

0 commit comments

Comments
 (0)