import sys
sys.path.insert(0, '/Users/scotthandley/Code/biometal/.venv/lib/python3.14/site-packages')
import biometal
import tempfile
import os
import struct
def create_minimal_tbi():
data = bytearray()
data.extend(b"TBI\x01")
data.extend(struct.pack('<i', 2))
data.extend(struct.pack('<i', 2))
data.extend(struct.pack('<i', 0))
data.extend(struct.pack('<i', 1))
data.extend(struct.pack('<i', 0))
data.extend(struct.pack('<i', ord('#')))
data.extend(struct.pack('<i', 0))
data.extend(struct.pack('<i', 10))
data.extend(b"chr1\0chr2\0")
data.extend(struct.pack('<i', 2))
data.extend(struct.pack('<I', 0))
data.extend(struct.pack('<i', 1))
data.extend(struct.pack('<Q', 0x1000))
data.extend(struct.pack('<Q', 0x2000))
data.extend(struct.pack('<I', 4681))
data.extend(struct.pack('<i', 1))
data.extend(struct.pack('<Q', 0x1500))
data.extend(struct.pack('<Q', 0x1800))
data.extend(struct.pack('<i', 2))
data.extend(struct.pack('<Q', 0x1000))
data.extend(struct.pack('<Q', 0x1500))
data.extend(struct.pack('<i', 1))
data.extend(struct.pack('<I', 0))
data.extend(struct.pack('<i', 1))
data.extend(struct.pack('<Q', 0x3000))
data.extend(struct.pack('<Q', 0x4000))
data.extend(struct.pack('<i', 1))
data.extend(struct.pack('<Q', 0x3000))
return bytes(data)
def test_tbi_load_and_query():
with tempfile.NamedTemporaryFile(mode='wb', suffix='.tbi', delete=False) as f:
tbi_path = f.name
f.write(create_minimal_tbi())
try:
print("Loading TBI index...")
index = biometal.TbiIndex.from_path(tbi_path)
print(f"✓ Loaded TBI index: {index}")
assert index.format() == "Vcf", f"Expected VCF format, got {index.format()}"
print(f"✓ Format: {index.format()}")
assert index.col_seq() == 0, f"Expected col_seq=0, got {index.col_seq()}"
print(f"✓ Sequence column: {index.col_seq()}")
assert index.col_beg() == 1, f"Expected col_beg=1, got {index.col_beg()}"
print(f"✓ Start column: {index.col_beg()}")
assert index.meta_char() == '#', f"Expected meta_char='#', got {index.meta_char()}"
print(f"✓ Comment character: '{index.meta_char()}'")
refs = index.references()
assert len(refs) == 2, f"Expected 2 references, got {len(refs)}"
assert refs == ['chr1', 'chr2'], f"Unexpected references: {refs}"
print(f"✓ References: {refs}")
assert len(index) == 2, f"Expected len=2, got {len(index)}"
print(f"✓ Index length: {len(index)}")
assert index.contains('chr1'), "chr1 should exist"
assert index.contains('chr2'), "chr2 should exist"
assert not index.contains('chr99'), "chr99 should not exist"
print("✓ contains() works correctly")
chr1_info = index.get_info('chr1')
assert chr1_info is not None, "chr1 info should exist"
assert chr1_info['name'] == 'chr1', f"Expected name='chr1', got {chr1_info['name']}"
assert chr1_info['n_bins'] == 2, f"Expected 2 bins, got {chr1_info['n_bins']}"
print(f"✓ chr1 info: {chr1_info}")
chr2_info = index.get_info('chr2')
assert chr2_info is not None, "chr2 info should exist"
assert chr2_info['name'] == 'chr2'
print(f"✓ chr2 info: {chr2_info}")
chunks = index.query('chr1', 0, 100000)
assert len(chunks) > 0, "Should return at least one chunk"
print(f"✓ Query chr1:0-100000 returned {len(chunks)} chunks")
for i, (start, end) in enumerate(chunks):
assert isinstance(start, int), "Chunk start should be int"
assert isinstance(end, int), "Chunk end should be int"
assert start < end, "Chunk start should be < end"
print(f" Chunk {i}: {start:016x} - {end:016x}")
chunks_chr2 = index.query('chr2', 0, 50000)
assert len(chunks_chr2) > 0, "Should return chunks for chr2"
print(f"✓ Query chr2:0-50000 returned {len(chunks_chr2)} chunks")
try:
index.query('chr99', 0, 100)
assert False, "Should raise error for non-existent reference"
except ValueError as e:
assert "not found" in str(e).lower()
print(f"✓ Correctly raises error for non-existent reference")
try:
index.query('chr1', 100, 50)
assert False, "Should raise error for invalid range"
except ValueError as e:
assert "invalid range" in str(e).lower()
print(f"✓ Correctly raises error for invalid range")
print("\n✅ All TBI Python binding tests passed!")
finally:
os.unlink(tbi_path)
if __name__ == '__main__':
test_tbi_load_and_query()