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


def fletcher_hash(bytes, hash_table_size, modulus):
    v1 = 0
    v2 = 0
    for byte in bytes:
        v1 = (v1 + ord(byte)) % modulus
        v2 = (v2 + v1) % modulus
    return (v2 * (modulus+1) + v1) % hash_table_size


def test_run():
    output = DummyOutput()
    n_collisions = 1
    length = 10
    width = 16
    hash_table_size = 100
    target = '42'
    fletcher.options['n_collisions'] = n_collisions
    fletcher.options['length'] = length
    fletcher.options['hash_table_size'] = hash_table_size
    fletcher.options['width'] = width
    fletcher.options['target_type'] = 'image'
    fletcher.options['target'] = target
    fletcher.run(output)
    assert output.count() == n_collisions
    for i in output:
        assert fletcher_hash(i, hash_table_size, (2**(width/2))-1) == int(target)
        assert len(i) == length


def test_more_collisions():
    output = DummyOutput()
    n_collisions = 4
    length = 5
    width = 16
    hash_table_size = 5
    target = '2'
    fletcher.options['n_collisions'] = n_collisions
    fletcher.options['length'] = length
    fletcher.options['hash_table_size'] = hash_table_size
    fletcher.options['width'] = width
    fletcher.options['target_type'] = 'image'
    fletcher.options['target'] = target
    fletcher.run(output)
    assert output.count() == n_collisions
    for i in output:
        assert fletcher_hash(i, hash_table_size, (2**(width/2))-1) == int(target)
        assert len(i) == length


def test_larger_width():
    output = DummyOutput()
    n_collisions = 1
    length = 5
    width = 32
    hash_table_size = 100
    target = '42'
    fletcher.options['n_collisions'] = n_collisions
    fletcher.options['length'] = length
    fletcher.options['hash_table_size'] = hash_table_size
    fletcher.options['width'] = width
    fletcher.options['target_type'] = 'image'
    fletcher.options['target'] = target
    fletcher.run(output)
    assert output.count() == n_collisions
    for i in output:
        assert fletcher_hash(i, hash_table_size, (2**(width/2))-1) == int(target)
        assert len(i) == length


def test_length_too_short():
    output = DummyOutput()
    n_collisions = 300
    length = 2
    width = 16
    hash_table_size = 100
    target = '42'
    fletcher.options['n_collisions'] = n_collisions
    fletcher.options['length'] = length
    fletcher.options['hash_table_size'] = hash_table_size
    fletcher.options['width'] = width
    fletcher.options['target_type'] = 'image'
    fletcher.options['target'] = target
    with pytest.raises(ValueError):
        fletcher.run(output)
