import pytest
from exploits.hashes.collisions import bsd
from test.exploits.dummy_output import DummyOutput


def bsd_hash(bytes, hash_table_size):
    checksum = 0
    for byte in bytes:
        checksum = (checksum >> 1) + ((checksum & 1) << 15)
        checksum += ord(byte)
        checksum &= 0xffff
    return checksum % hash_table_size


def test_run():
    output = DummyOutput()
    n_collisions = 10
    hash_table_size = 0xffff
    target = '42'
    length = 10
    bsd.options['n_collisions'] = n_collisions
    bsd.options['length'] = length
    bsd.options['target_type'] = 'image'
    bsd.options['target'] = target
    bsd.options['hash_table_size'] = hash_table_size
    bsd.run(output)
    assert output.count() == n_collisions
    for i in output:
        assert bsd_hash(i, hash_table_size) == int(target)
        assert len(i) == length


def test_more_collisions():
    output = DummyOutput()
    n_collisions = 50
    hash_table_size = 0xffff
    target = '42'
    length = 10
    bsd.options['n_collisions'] = n_collisions
    bsd.options['length'] = length
    bsd.options['target_type'] = 'image'
    bsd.options['target'] = target
    bsd.options['hash_table_size'] = hash_table_size
    bsd.run(output)
    assert output.count() == n_collisions
    for i in output:
        assert bsd_hash(i, hash_table_size) == int(target)
        assert len(i) == length


def test_short_length():
    output = DummyOutput()
    n_collisions = 10
    hash_table_size = 100
    target = '42'
    length = 5
    bsd.options['n_collisions'] = n_collisions
    bsd.options['length'] = length
    bsd.options['target_type'] = 'image'
    bsd.options['target'] = target
    bsd.options['hash_table_size'] = hash_table_size
    bsd.run(output)
    assert output.count() == n_collisions
    for i in output:
        assert bsd_hash(i, hash_table_size) == int(target)
        assert len(i) == length


def test_preimage():
    output = DummyOutput()
    n_collisions = 10
    hash_table_size = 0xffff
    target = 'hello'
    length = len(target)
    bsd.options['n_collisions'] = n_collisions
    bsd.options['length'] = length
    bsd.options['target_type'] = 'preimage'
    bsd.options['target'] = target
    bsd.options['hash_table_size'] = hash_table_size
    bsd.run(output)
    assert output.count() == n_collisions
    for i in output:
        assert bsd_hash(i, hash_table_size) == bsd_hash(target, hash_table_size)
        assert len(i) == length


def test_length_too_short():
    output = DummyOutput()
    n_collisions = 100
    hash_table_size = 0xffff
    target = '42'
    length = 1
    bsd.options['n_collisions'] = n_collisions
    bsd.options['length'] = length
    bsd.options['target_type'] = 'image'
    bsd.options['target'] = target
    bsd.options['hash_table_size'] = hash_table_size
    with pytest.raises(ValueError):
        bsd.run(output)
