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


def truncate_32(x):
    return x & 0xFFFFFFFF


def hash_function(x, y, z):
    return truncate_32(truncate_32(x * y) + z)


def test_custom_hash():
    output = DummyOutput()
    n_collisions = 10
    image = 0
    custom_hash.options['n_collisions'] = n_collisions
    custom_hash.options['variable_width'] = 32
    custom_hash.options['target_type'] = 'image'
    custom_hash.options['image'] = image
    custom_hash.options['hash'] = '+ * x y z'
    custom_hash.run(output)
    assert len(output) == n_collisions
    for i in output:
        assert hash_function(int(i[i[2]].as_long()),
                             int(i[i[1]].as_long()),
                             int(i[i[0]].as_long())) == image


def test_custom_hash_preimage():
    output = DummyOutput()
    n_collisions = 10
    preimage = 'x = 5, y = 10, z = 30'
    image = 80
    custom_hash.options['n_collisions'] = n_collisions
    custom_hash.options['variable_width'] = 32
    custom_hash.options['target_type'] = 'preimage'
    custom_hash.options['preimage'] = preimage
    custom_hash.options['hash'] = '+ * x y z'
    custom_hash.run(output)
    assert len(output) == n_collisions
    for i in output:
        assert hash_function(int(i[i[2]].as_long()),
                             int(i[i[1]].as_long()),
                             int(i[i[0]].as_long())) == image


def test_custom_hash_malformed_hash():
    output = DummyOutput()
    n_collisions = 10
    image = 0
    custom_hash.options['n_collisions'] = n_collisions
    custom_hash.options['variable_width'] = 32
    custom_hash.options['target_type'] = 'image'
    custom_hash.options['image'] = image
    custom_hash.options['hash'] = '+ * x y z q'
    with pytest.raises(AssertionError):
        custom_hash.run(output)


def test_custom_hash_unknown_variable():
    output = DummyOutput()
    n_collisions = 10
    preimage = 'x = 5, y = 10, z = 30'
    custom_hash.options['n_collisions'] = n_collisions
    custom_hash.options['variable_width'] = 32
    custom_hash.options['target_type'] = 'preimage'
    custom_hash.options['preimage'] = preimage
    custom_hash.options['hash'] = '+ * x y q'
    with pytest.raises(ValueError):
        custom_hash.run(output)


def test_custom_hash_missing_preimage_variable():
    output = DummyOutput()
    n_collisions = 10
    preimage = 'x = 5, y = 10'
    custom_hash.options['n_collisions'] = n_collisions
    custom_hash.options['variable_width'] = 32
    custom_hash.options['target_type'] = 'preimage'
    custom_hash.options['preimage'] = preimage
    custom_hash.options['hash'] = '+ * x y z'
    with pytest.raises(ValueError):
        custom_hash.run(output)


def test_custom_hash_preimage_parsing_error_missing_comma():
    output = DummyOutput()
    n_collisions = 10
    preimage = 'x = 5 y = 10, z = 30'
    custom_hash.options['n_collisions'] = n_collisions
    custom_hash.options['variable_width'] = 32
    custom_hash.options['target_type'] = 'preimage'
    custom_hash.options['preimage'] = preimage
    custom_hash.options['hash'] = '+ * x y z'
    with pytest.raises(ValueError):
        custom_hash.run(output)


def test_custom_hash_preimage_parsing_error_equals():
    output = DummyOutput()
    n_collisions = 10
    preimage = 'x 5, y = 10, z = 30'
    custom_hash.options['n_collisions'] = n_collisions
    custom_hash.options['variable_width'] = 32
    custom_hash.options['target_type'] = 'preimage'
    custom_hash.options['preimage'] = preimage
    custom_hash.options['hash'] = '+ * x y z'
    with pytest.raises(ValueError):
        custom_hash.run(output)


def test_custom_hash_preimage_parsing_error_multiple_assignments():
    output = DummyOutput()
    n_collisions = 10
    preimage = 'x = 5, x = 10, z = 30'
    custom_hash.options['n_collisions'] = n_collisions
    custom_hash.options['variable_width'] = 32
    custom_hash.options['target_type'] = 'preimage'
    custom_hash.options['preimage'] = preimage
    custom_hash.options['hash'] = '+ * x y z'
    with pytest.raises(ValueError):
        custom_hash.run(output)


def test_custom_hash_preimage_parsing_error_non_integer():
    output = DummyOutput()
    n_collisions = 10
    preimage = 'x = 5.5, y = 10, z = 30'
    custom_hash.options['n_collisions'] = n_collisions
    custom_hash.options['variable_width'] = 32
    custom_hash.options['target_type'] = 'preimage'
    custom_hash.options['preimage'] = preimage
    custom_hash.options['hash'] = '+ * x y z'
    with pytest.raises(ValueError):
        custom_hash.run(output)
