mirror of
https://github.com/ruvnet/RuView.git
synced 2026-10-22 14:42:44 -05:00
154 lines
7.3 KiB
Python
154 lines
7.3 KiB
Python
"""Synthetic intake boundary tests. No captured CSI enters the repository."""
|
|
import io
|
|
import hashlib
|
|
import zipfile
|
|
import json
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch
|
|
from pathlib import Path
|
|
import sys
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
import numpy as np
|
|
from scipy.io import savemat
|
|
from csi_training import espfi
|
|
|
|
|
|
class EspfiTests(unittest.TestCase):
|
|
def mat(self, x):
|
|
b = io.BytesIO()
|
|
savemat(b, {'CSIamp': x})
|
|
return b.getvalue()
|
|
|
|
def test_pool_and_orientation(self):
|
|
x = np.arange(950 * 52, dtype=np.float64).reshape(950, 52)
|
|
expected = x.reshape(190, 5, 52).mean(1).T.astype(np.float32)
|
|
np.testing.assert_array_equal(espfi.decode_amplitude(self.mat(x)), expected)
|
|
np.testing.assert_array_equal(espfi.decode_amplitude(self.mat(x.T)), expected)
|
|
|
|
def test_reject_nonfinite_negative_complex_shape(self):
|
|
for x in (np.full((950, 52), np.nan), -np.ones((950, 52)),
|
|
np.ones((950, 52), dtype=complex), np.ones((2, 52))):
|
|
with self.assertRaises(ValueError):
|
|
espfi.decode_amplitude(self.mat(x))
|
|
|
|
def test_identity_and_traversal(self):
|
|
self.assertEqual(espfi.parse_identity('room/mat/4-8-7-10.mat'), (4, 8, 7, 10))
|
|
for name in ('../1-1-1-1.mat', '/1-1-1-1.mat', '1-9-1-1.mat',
|
|
'1-1-1-11.mat', 'foo\\1-1-1-1.mat'):
|
|
with self.assertRaises(ValueError):
|
|
espfi.parse_identity(name)
|
|
|
|
def test_joint_split_is_disjoint(self):
|
|
groups = {s: (set(), set()) for s in ('train', 'validation', 'test')}
|
|
counts = dict.fromkeys(groups, 0)
|
|
for r in range(1, 5):
|
|
for p in range(1, 9):
|
|
split = espfi.split_identity((r, p, 1, 1))
|
|
if split:
|
|
groups[split][0].add(r)
|
|
groups[split][1].add(p)
|
|
counts[split] += 70
|
|
self.assertEqual(counts, {'train': 840, 'validation': 70, 'test': 70})
|
|
for a, b in [('train', 'validation'), ('train', 'test'), ('validation', 'test')]:
|
|
self.assertFalse(groups[a][0] & groups[b][0])
|
|
self.assertFalse(groups[a][1] & groups[b][1])
|
|
|
|
def test_archive_pin_rejects_before_parsing(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
p = Path(tmp) / 'source.rar'
|
|
p.write_bytes(b'not a dataset')
|
|
with self.assertRaisesRegex(ValueError, 'pinned'):
|
|
espfi.prepare(p, Path(tmp) / 'out')
|
|
|
|
def fixture(self, tmp):
|
|
records, arrays = [], {}
|
|
for split in ('train', 'validation', 'test'):
|
|
ids = [(r, p, a, t) for r in range(1, 5) for p in range(1, 9)
|
|
for a in range(1, 8) for t in range(1, 11)
|
|
if espfi.split_identity((r, p, a, t)) == split]
|
|
arrays[split + '_identity'] = np.array(ids, dtype=np.int64)
|
|
arrays[split + '_y'] = np.array([i[2] - 1 for i in ids], dtype=np.int64)
|
|
arrays[split + '_x'] = np.stack([np.full((52, 10),
|
|
i[0] * 10000 + i[1] * 1000 + i[2] * 100 + i[3], dtype=np.float32) for i in ids])
|
|
selected = {tuple(i): hashlib.sha256(x.tobytes()).hexdigest()
|
|
for split in ('train', 'validation', 'test')
|
|
for i, x in zip(arrays[split + '_identity'], arrays[split + '_x'])}
|
|
for r in range(1, 5):
|
|
for p in range(1, 9):
|
|
for a in range(1, 8):
|
|
for t in range(1, 11):
|
|
i = (r, p, a, t)
|
|
records.append({'identity': i, 'split': espfi.split_identity(i),
|
|
'signal_sha256': selected.get(i, hashlib.sha256(str(i).encode()).hexdigest())})
|
|
npz = Path(tmp) / 'amplitudes.npz'
|
|
np.savez_compressed(npz, **arrays)
|
|
meta = dict(schema_version=1, classes=espfi.CLASSES, source_revision=espfi.REVISION,
|
|
source_url=espfi.SOURCE_URL, archive_sha256=espfi.ARCHIVE_SHA256,
|
|
license='CC-BY-4.0', split_policy='joint_room_subject_intersection_v1',
|
|
temporal_mean_pool=95, window_samples=10, records=records,
|
|
counts={s: len(arrays[s + '_y']) for s in ('train', 'validation', 'test')},
|
|
npz=npz.name, npz_sha256=espfi.digest(npz))
|
|
manifest = Path(tmp) / 'manifest.json'
|
|
manifest.write_text(json.dumps(meta))
|
|
return manifest, npz, arrays, meta
|
|
|
|
def test_loader_signal_binding_and_duplicate_identity(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
manifest, npz, arrays, meta = self.fixture(tmp)
|
|
parts, _ = espfi.load_dataset(manifest)
|
|
self.assertEqual(len(parts['train']['y']), 840)
|
|
arrays['train_identity'][1] = arrays['train_identity'][0]
|
|
np.savez_compressed(npz, **arrays)
|
|
meta['npz_sha256'] = espfi.digest(npz)
|
|
manifest.write_text(json.dumps(meta))
|
|
with self.assertRaisesRegex(ValueError, 'provenance'):
|
|
espfi.load_dataset(manifest)
|
|
|
|
def test_development_never_deserializes_test_arrays(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
manifest, _, _, _ = self.fixture(tmp)
|
|
npz_class = np.lib.npyio.NpzFile
|
|
original = npz_class.__getitem__
|
|
accessed = []
|
|
def guarded(data, key):
|
|
if key.startswith('test_'):
|
|
raise AssertionError('test payload accessed during development')
|
|
accessed.append(key)
|
|
return original(data, key)
|
|
with patch.object(npz_class, '__getitem__', guarded):
|
|
parts, meta = espfi.load_development(manifest)
|
|
self.assertEqual(set(parts), {'train', 'validation'})
|
|
self.assertEqual(set(accessed), {s + suffix for s in parts
|
|
for suffix in ('_x', '_y', '_identity')})
|
|
self.assertEqual(parts['train']['identity'].shape, (840, 4))
|
|
self.assertEqual(meta['loaded_partitions'], ['train', 'validation'])
|
|
|
|
def test_forged_numpy_shape_rejected_before_allocation(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
manifest, npz, arrays, meta = self.fixture(tmp)
|
|
with zipfile.ZipFile(npz) as z:
|
|
members = {i.filename: z.read(i) for i in z.infolist()}
|
|
header = io.BytesIO()
|
|
np.lib.format.write_array_header_1_0(header,
|
|
{'descr': '<f4', 'fortran_order': False, 'shape': (10**12, 52, 10)})
|
|
members['train_x.npy'] = header.getvalue()
|
|
with zipfile.ZipFile(npz, 'w') as z:
|
|
for name, content in members.items():
|
|
z.writestr(name, content)
|
|
meta['npz_sha256'] = espfi.digest(npz)
|
|
manifest.write_text(json.dumps(meta))
|
|
with self.assertRaisesRegex(ValueError, 'numpy header'):
|
|
espfi.load_dataset(manifest)
|
|
|
|
def test_unverified_manifest_rejected(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
p = Path(tmp) / 'manifest.json'
|
|
p.write_text(json.dumps({'schema_version': 1, 'classes': espfi.CLASSES}))
|
|
with self.assertRaisesRegex(ValueError, 'provenance'):
|
|
espfi.load_dataset(p)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|