Coverage for tests/test_sensor_size_metadata.py: 100%

66 statements  

« prev     ^ index     » next       coverage.py v7.15.1, created at 2026-07-18 05:24 +0000

1"""Tests for lightweight EventArray metadata (sensor_size) and its io wiring.""" 

2import numpy as np 

3import pytest 

4 

5from evutils.io import EventReader, EventWriter 

6from evutils.types import Event_dtype, EventArray 

7 

8 

9def _events(n=3000, w=320, h=240): 

10 return EventArray( 

11 t=np.arange(n, dtype=np.int64) * 10, 

12 x=(np.arange(n) % w).astype(np.uint16), 

13 y=(np.arange(n) % h).astype(np.uint16), 

14 p=(np.arange(n) % 2).astype(np.uint8), 

15 ) 

16 

17 

18# --------------------------------------------------------------------------- # 

19# Core type behaviour 

20# --------------------------------------------------------------------------- # 

21 

22def test_metadata_defaults_none_and_roundtrips(): 

23 e = EventArray(t=[1], x=[2], y=[3], p=[1]) 

24 assert e.metadata is None 

25 assert e.sensor_size is None 

26 

27 e.sensor_size = (64, 48) 

28 assert e.metadata == {"sensor_size": (64, 48)} 

29 assert e.sensor_size == (64, 48) 

30 

31 

32def test_metadata_survives_slice_and_subset(): 

33 e = EventArray(t=[1, 2, 3], x=[1, 2, 3], y=[1, 2, 3], p=[1, 0, 1], 

34 metadata={"sensor_size": (64, 48)}) 

35 assert e[:2].sensor_size == (64, 48) 

36 assert e[["t", "x"]].sensor_size == (64, 48) 

37 

38 

39def test_copy_isolates_metadata(): 

40 e = EventArray(t=[1], x=[2], y=[3], p=[1], metadata={"sensor_size": (64, 48)}) 

41 c = e.copy() 

42 c.metadata["sensor_size"] = (1, 1) 

43 assert e.sensor_size == (64, 48) # original untouched 

44 assert c.sensor_size == (1, 1) 

45 

46 

47def test_from_aos_accepts_metadata(): 

48 aos = np.zeros(3, dtype=Event_dtype) 

49 e = EventArray.from_aos(aos, metadata={"sensor_size": (10, 20)}) 

50 assert e.sensor_size == (10, 20) 

51 

52 

53# --------------------------------------------------------------------------- # 

54# Reader populates / Writer consumes 

55# --------------------------------------------------------------------------- # 

56 

57def test_reader_populates_sensor_size(tmp_path): 

58 p = tmp_path / "geom.raw" 

59 with EventWriter(p, format="evt3", width=640, height=480) as w: 

60 w.write(_events()) 

61 

62 assert EventReader(p).read_all().sensor_size == (640, 480) 

63 

64 r = EventReader(p, n_events=500) 

65 assert r.read().sensor_size == (640, 480) 

66 

67 for chunk in EventReader(p, n_events=500): 

68 assert chunk.sensor_size == (640, 480) 

69 break 

70 

71 

72def test_writer_infers_dims_from_metadata(tmp_path): 

73 ev = _events(w=320, h=240) 

74 ev.sensor_size = (320, 240) 

75 p = tmp_path / "infer.raw" 

76 with EventWriter(p, format="evt3") as w: # no width/height given 

77 w.write(ev) 

78 assert EventReader(p).shape() == (320, 240) 

79 

80 

81def test_writer_explicit_dims_override_metadata(tmp_path): 

82 ev = _events() 

83 ev.sensor_size = (320, 240) 

84 p = tmp_path / "explicit.raw" 

85 with EventWriter(p, format="evt3", width=100, height=50) as w: 

86 w.write(ev) 

87 assert EventReader(p).shape() == (100, 50) 

88 

89 

90def test_writer_falls_back_without_metadata(tmp_path): 

91 p = tmp_path / "fallback.raw" 

92 with EventWriter(p, format="evt3") as w: 

93 w.write(_events()) # no sensor_size metadata 

94 assert EventReader(p).shape() == (1280, 720) 

95 

96 

97def test_read_write_read_roundtrip_preserves_geometry(tmp_path): 

98 """Geometry read off one file flows into the next via metadata alone.""" 

99 src = tmp_path / "src.raw" 

100 with EventWriter(src, format="evt3", width=346, height=260) as w: 

101 w.write(_events(w=346, h=260)) 

102 

103 events = EventReader(src).read_all() 

104 assert events.sensor_size == (346, 260) 

105 

106 dst = tmp_path / "dst.raw" 

107 with EventWriter(dst, format="evt3") as w: # dims inferred from events 

108 w.write(events) 

109 assert EventReader(dst).shape() == (346, 260)