#! /usr/bin/env python3

from binascii import hexlify
import json
from os import path
import subprocess

HERE = path.dirname(__file__)
TEST_VECTORS_PATH = path.join(HERE, "..", "test_vectors", "test_vectors.json")
TEST_VECTORS = json.load(open(TEST_VECTORS_PATH))


def run_blake3(args, input):
    output = subprocess.run([path.join(HERE, "blake3")] + args,
                            input=input,
                            stdout=subprocess.PIPE,
                            check=True)
    return output.stdout.decode().strip()


# Fill the input with a repeating byte pattern. We use a cycle length of 251,
# because that's the largest prime number less than 256. This makes it unlikely
# to swapping any two adjacent input blocks or chunks will give the same
# answer.
def make_test_input(length):
    i = 0
    buf = bytearray()
    while len(buf) < length:
        buf.append(i)
        i = (i + 1) % 251
    return buf


def main():
    for case in TEST_VECTORS["cases"]:
        input_len = case["input_len"]
        input = make_test_input(input_len)
        hex_key = hexlify(TEST_VECTORS["key"].encode())
        context_string = TEST_VECTORS["context_string"]
        expected_hash_xof = case["hash"]
        expected_hash = expected_hash_xof[:64]
        expected_keyed_hash_xof = case["keyed_hash"]
        expected_keyed_hash = expected_keyed_hash_xof[:64]
        expected_derive_key_xof = case["derive_key"]
        expected_derive_key = expected_derive_key_xof[:64]

        # Test the default hash.
        test_hash = run_blake3([], input)
        for line in test_hash.splitlines():
            assert expected_hash == line, \
                "hash({}): {} != {}".format(input_len, expected_hash, line)

        # Test the extended hash.
        xof_len = len(expected_hash_xof) // 2
        test_hash_xof = run_blake3(["--length", str(xof_len)], input)
        for line in test_hash_xof.splitlines():
            assert expected_hash_xof == line, \
                "hash_xof({}): {} != {}".format(
                    input_len, expected_hash_xof, line)

        # Test the default keyed hash.
        test_keyed_hash = run_blake3(["--keyed", hex_key], input)
        for line in test_keyed_hash.splitlines():
            assert expected_keyed_hash == line, \
                "keyed_hash({}): {} != {}".format(
                    input_len, expected_keyed_hash, line)

        # Test the extended keyed hash.
        xof_len = len(expected_keyed_hash_xof) // 2
        test_keyed_hash_xof = run_blake3(
            ["--keyed", hex_key, "--length",
             str(xof_len)], input)
        for line in test_keyed_hash_xof.splitlines():
            assert expected_keyed_hash_xof == line, \
                "keyed_hash_xof({}): {} != {}".format(
                    input_len, expected_keyed_hash_xof, line)

        # Test the default derive key.
        test_derive_key = run_blake3(["--derive-key", context_string], input)
        for line in test_derive_key.splitlines():
            assert expected_derive_key == line, \
                "derive_key({}): {} != {}".format(
                    input_len, expected_derive_key, line)

        # Test the extended derive key.
        xof_len = len(expected_derive_key_xof) // 2
        test_derive_key_xof = run_blake3(
            ["--derive-key", context_string, "--length",
             str(xof_len)], input)
        for line in test_derive_key_xof.splitlines():
            assert expected_derive_key_xof == line, \
                "derive_key_xof({}): {} != {}".format(
                    input_len, expected_derive_key_xof, line)


if __name__ == "__main__":
    main()
