Spaces:
Sleeping
Sleeping
| """ | |
| Unit tests for the BPE Tokenizer. | |
| """ | |
| import os | |
| import sys | |
| import tempfile | |
| import pytest | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) | |
| from data.tokenizer import BPETokenizer | |
| class TestBPETokenizer: | |
| """Test suite for BPETokenizer.""" | |
| def setup_method(self): | |
| """Create a small tokenizer for testing.""" | |
| self.tokenizer = BPETokenizer(vocab_size=300) | |
| def test_initialization(self): | |
| """Test tokenizer initializes with correct vocab size.""" | |
| tok = BPETokenizer(vocab_size=32000) | |
| assert tok.vocab_size == 32000 | |
| assert len(tok.SPECIAL_TOKENS) == 6 | |
| assert tok.eos_token_id == 0 | |
| assert tok.pad_token_id == 1 | |
| assert tok.unk_token_id == 2 | |
| def test_byte_level_encoding(self): | |
| """Test encoding without training (byte-level fallback).""" | |
| tok = BPETokenizer() | |
| text = "hi" | |
| ids = tok.encode(text) | |
| decoded = tok.decode(ids) | |
| assert decoded == text | |
| def test_encode_decode_roundtrip_ascii(self): | |
| """Test encode->decode preserves ASCII text.""" | |
| tok = BPETokenizer() | |
| texts = [ | |
| "Hello, world!", | |
| "The quick brown fox", | |
| "Testing 123", | |
| "Special chars: @#$%", | |
| ] | |
| for text in texts: | |
| ids = tok.encode(text) | |
| decoded = tok.decode(ids) | |
| assert decoded == text, f"Roundtrip failed for: {text}" | |
| def test_encode_decode_roundtrip_unicode(self): | |
| """Test encode->decode preserves Unicode text.""" | |
| tok = BPETokenizer() | |
| texts = [ | |
| "café résumé", | |
| "日本語テスト", | |
| "emoji 🎉🚀", | |
| "über straße", | |
| ] | |
| for text in texts: | |
| ids = tok.encode(text) | |
| decoded = tok.decode(ids) | |
| assert decoded == text, f"Unicode roundtrip failed for: {text}" | |
| def test_special_tokens(self): | |
| """Test special token encoding/decoding.""" | |
| tok = BPETokenizer() | |
| text = "Hello<|endoftext|>World" | |
| # Without allowing special tokens | |
| ids_no_special = tok.encode(text) | |
| # The special token should be encoded as regular bytes | |
| assert tok.eos_token_id not in ids_no_special | |
| # With allowing special tokens | |
| ids_with_special = tok.encode(text, allowed_special={"<|endoftext|>"}) | |
| assert tok.eos_token_id in ids_with_special | |
| def test_special_tokens_all(self): | |
| """Test allowing all special tokens.""" | |
| tok = BPETokenizer() | |
| text = "<|system|>Hello<|endoftext|>" | |
| ids = tok.encode(text, allowed_special={"all"}) | |
| assert 3 in ids # system token | |
| assert 0 in ids # endoftext token | |
| def test_training(self): | |
| """Test tokenizer training on a small corpus.""" | |
| tok = BPETokenizer(vocab_size=300) | |
| corpus = "the cat sat on the mat. the cat sat on the hat. " * 100 | |
| tok.train(corpus, verbose=False) | |
| # Should have learned some merges | |
| assert len(tok.merge_list) > 0 | |
| assert len(tok.vocab) > 262 # 6 special + 256 bytes + merges | |
| # Encoding should produce fewer tokens than byte-level | |
| text = "the cat sat" | |
| ids = tok.encode(text) | |
| assert len(ids) < len(text.encode("utf-8")) | |
| def test_save_and_load(self): | |
| """Test saving and loading tokenizer.""" | |
| tok = BPETokenizer(vocab_size=300) | |
| corpus = "hello world " * 100 | |
| tok.train(corpus, verbose=False) | |
| with tempfile.TemporaryDirectory() as tmpdir: | |
| tok.save(tmpdir, "test_tok") | |
| # Check files exist | |
| assert os.path.exists(os.path.join(tmpdir, "test_tok_vocab.json")) | |
| assert os.path.exists(os.path.join(tmpdir, "test_tok_merges.txt")) | |
| assert os.path.exists(os.path.join(tmpdir, "tokenizer.json")) | |
| # Load and verify | |
| loaded = BPETokenizer.load(tmpdir, "test_tok") | |
| assert len(loaded.vocab) == len(tok.vocab) | |
| assert len(loaded.merge_list) == len(tok.merge_list) | |
| # Verify encoding consistency | |
| test_text = "hello world" | |
| assert tok.encode(test_text) == loaded.encode(test_text) | |
| def test_empty_input(self): | |
| """Test handling of empty input.""" | |
| tok = BPETokenizer() | |
| assert tok.encode("") == [] | |
| assert tok.decode([]) == "" | |
| def test_repr(self): | |
| """Test string representation.""" | |
| tok = BPETokenizer(vocab_size=32000) | |
| repr_str = repr(tok) | |
| assert "BPETokenizer" in repr_str | |
| assert "vocab_size" in repr_str | |
| if __name__ == "__main__": | |
| pytest.main([__file__, "-v"]) | |