Download tests/test_coder_limits.py from LIghtJUNction/LightNest: direct link, hf CLI and curl.
- Browser
- Download file 7.16 kB
-
https://huggingface.co/LIghtJUNction/LightNest/resolve/main/tests/test_coder_limits.py
- Command line
-
hf download hf://LIghtJUNction/LightNest/tests/test_coder_limits.py
-
curl -L -o test_coder_limits.py https://huggingface.co/LIghtJUNction/LightNest/resolve/main/tests/test_coder_limits.py
7.16 kB
| import random | |
| from pathlib import Path | |
| import pytest | |
| import torch | |
| from lightnest import coder | |
| def test_input_limits_are_explicit(): | |
| with pytest.raises(ValueError, match='character'): | |
| coder.encode_say('返' * 257, None) | |
| with pytest.raises(ValueError, match='unsupported'): | |
| coder.encode_say('\U0001f9ea', None) | |
| with pytest.raises(ValueError, match='limit'): | |
| coder.encode_explain(b'\0' * 96, None) | |
| with pytest.raises(ValueError, match='aligned'): | |
| coder.encode_edit(b'\0', '返回第一个数', None) | |
| def test_batch_fails_instead_of_retrying(monkeypatch): | |
| calls = [] | |
| def oversize(rng): | |
| calls.append(1) | |
| return [0] * 257, 1 | |
| monkeypatch.setattr(coder, '_sample', oversize) | |
| with pytest.raises(ValueError, match='context'): | |
| coder.make_batch(random.Random(0), 1, torch.device('cpu')) | |
| assert len(calls) == 1 | |
| with pytest.raises(ValueError, match='positive'): | |
| coder.make_batch(random.Random(0), 0, torch.device('cpu')) | |
| def test_generation_rejects_context_overflow(): | |
| model = coder.Coder(dim=8, layers=1, heads=2) | |
| with pytest.raises(ValueError, match='context'): | |
| coder._generate(model, [coder.BOS] * 250, coder.BYTE_IDS, 96) | |
| with pytest.raises(ValueError, match='length'): | |
| model(torch.zeros((1, 257), dtype=torch.long)) | |
| def test_checkpoint_exact_vocab(tmp_path): | |
| path = tmp_path / 'small.pt' | |
| model = coder.Coder(dim=8, layers=1, heads=2) | |
| coder.save_model(model, path) | |
| loaded = coder.load_model(path, torch.device('cpu')) | |
| assert loaded.dim == 8 | |
| blob = torch.load(path, weights_only=True) | |
| blob['vocab'][0], blob['vocab'][1] = blob['vocab'][1], blob['vocab'][0] | |
| torch.save(blob, path) | |
| with pytest.raises(ValueError, match='incompatible'): | |
| coder.load_model(path, torch.device('cpu')) | |
| def test_explain_file_does_not_read_whole_file(tmp_path, monkeypatch): | |
| path = tmp_path / 'large.bin' | |
| with path.open('wb') as f: | |
| f.truncate(1024 * 1024) | |
| monkeypatch.setattr(Path, 'read_bytes', lambda *a: pytest.fail('unbounded read')) | |
| with pytest.raises(ValueError): | |
| coder.explain_file(None, path) | |
| def test_training_subset_applies_to_all_modes(monkeypatch): | |
| chosen = tuple(coder.JOBS[:2]) | |
| names = {job.name for job in chosen} | |
| codes = {job.code for job in chosen} | |
| observed = set() | |
| def say(sentence, program): | |
| observed.add('say') | |
| assert program in codes | |
| assert sentence in {s for job in chosen for s in job.say_train} | |
| return [0], 0 | |
| def edit(prior, sentence, program): | |
| observed.add('edit') | |
| assert prior in codes and program in codes | |
| assert sentence in {s for s, name in coder.EDIT_TRAIN if name in names} | |
| return [0], 0 | |
| def explain(program, sentence): | |
| observed.add('explain') | |
| assert program in codes | |
| assert sentence in {job.explain for job in chosen} | |
| return [0], 0 | |
| monkeypatch.setattr(coder, 'encode_say', say) | |
| monkeypatch.setattr(coder, 'encode_edit', edit) | |
| monkeypatch.setattr(coder, 'encode_explain', explain) | |
| rng = random.Random(10) | |
| for _ in range(100): | |
| coder._sample(rng, chosen) | |
| assert observed == {'say', 'edit', 'explain'} | |
| def test_semantic_augmentation_changes_bytes_preserves_branches(): | |
| from lightnest.phrase import by_name, heldout_probes | |
| from lightnest.rv32 import simulate | |
| order = coder.TEMP_REGISTERS[1:] + coder.TEMP_REGISTERS[:1] | |
| for name in ('add', 'max', 'sum', 'range', 'count_neg'): | |
| job = by_name(name) | |
| transformed = coder.augment_program(job.code, register_order=order, nop_prefix=1) | |
| assert transformed != job.code | |
| assert len(transformed) == len(job.code) + 4 | |
| assert all(simulate(transformed, xs) == expected | |
| for xs, expected in heldout_probes(name, seed=88212, trials=12)) | |
| with pytest.raises(ValueError, match='permute'): | |
| coder.augment_program(by_name('add').code, register_order=(0, 10, 11, 12)) | |
| def test_augmentation_bank_is_train_only_and_context_bounded(): | |
| withheld = {'abs', 'mul', 'sum_squares', 'count_even', 'last'} | |
| jobs = tuple(job for job in coder.JOBS if job.name not in withheld) | |
| bank = coder.training_augmentations(jobs, 91241) | |
| assert set(bank) == {job.name for job in jobs} | |
| assert set(bank).isdisjoint(withheld) | |
| for job in jobs: | |
| for variant in bank[job.name]: | |
| assert len(variant) <= coder.MAX_PROGRAM_BYTES | |
| ids, _ = coder.encode_explain(variant, job.explain) | |
| assert len(ids) <= coder.MAX_LEN | |
| for target in jobs: | |
| sentence = max(target.say_train, key=len) | |
| ids, _ = coder.encode_edit(variant, '改成' + sentence, target.code) | |
| assert len(ids) <= coder.MAX_LEN | |
| for _ in range(10): | |
| tokens, _ = coder.make_batch(random.Random(15), 8, torch.device('cpu'), jobs, bank) | |
| assert tokens.shape[1] <= coder.MAX_LEN | |
| def test_augmentation_only_changes_training_inputs(monkeypatch): | |
| jobs = tuple(coder.JOBS[:2]) | |
| bank = {job.name: (b'\x13\0\0\0' + job.code,) for job in jobs} | |
| canonical = {job.code for job in jobs} | |
| variants = {value for values in bank.values() for value in values} | |
| modes = set() | |
| def say(sentence, target): | |
| modes.add('say') | |
| assert target in canonical | |
| return [0], 0 | |
| def edit(prior, sentence, target): | |
| modes.add('edit') | |
| assert prior in variants and target in canonical | |
| return [0], 0 | |
| def explain(prior, sentence): | |
| modes.add('explain') | |
| assert prior in variants | |
| return [0], 0 | |
| monkeypatch.setattr(coder, 'encode_say', say) | |
| monkeypatch.setattr(coder, 'encode_edit', edit) | |
| monkeypatch.setattr(coder, 'encode_explain', explain) | |
| rng = random.Random(721) | |
| for _ in range(100): | |
| coder._sample(rng, jobs, bank) | |
| assert modes == {'say', 'edit', 'explain'} | |
| def test_augmentation_rejects_unknown_opcodes_and_oversize(): | |
| with pytest.raises(ValueError, match='opcode'): | |
| coder.augment_program(b'\xff' * 4) | |
| with pytest.raises(ValueError, match='limit'): | |
| coder.augment_program(bytes.fromhex('13000000') * 23, nop_prefix=2) | |
| def test_checkpoint_size_budget_checked_before_load(tmp_path, monkeypatch): | |
| path = tmp_path / 'large.pt' | |
| with path.open('wb') as out: | |
| out.truncate(100 * 1024**2) | |
| monkeypatch.setattr(torch, 'load', lambda *a, **k: pytest.fail('must reject before tensor load')) | |
| with pytest.raises(ValueError, match='size budget'): | |
| coder.load_model(path, torch.device('cpu')) | |
| def test_checkpoint_shape_budget_checked_before_construction(tmp_path, monkeypatch): | |
| path = tmp_path / 'small.pt' | |
| coder.save_model(coder.Coder(16, 1, 4), path) | |
| data = torch.load(path, weights_only=True) | |
| data['dim'] = 1000000 | |
| torch.save(data, path) | |
| monkeypatch.setattr(coder, 'Coder', lambda *a, **k: pytest.fail('must reject before model allocation')) | |
| with pytest.raises(ValueError, match='parameter budget'): | |
| coder.load_model(path, torch.device('cpu')) | |