#!/usr/bin/env python # -*- coding: utf-8 -*- # vim: fenc=utf-8:et:ts=4:sts=4:sw=4:fdm=marker import os from shutil import which import subprocess from typing import List, Optional VALIDATION_CONSTANTS = [3, 2, 7, 6, 5, 4, 3, 2] def validation_digit(first_eight: str) -> int: dot_product = 0 for i, char in enumerate(first_eight): dot_product += VALIDATION_CONSTANTS[i] * int(char) mod = dot_product % 11 if mod == 0: return 0 if mod == 1: # If the modulus returns 1 then the random number is not usable return None else: return 11 - mod def generate_kennitala(first_six: str, randoms: str, century: str) -> Optional[str]: first_eight = str(first_six) + str(randoms) validation = validation_digit(first_eight) kt = None if validation is not None: kt = first_eight + str(validation) + str(century) return kt def generate_kennitalas( first_six: str, century: str, random_from: int = 20, random_to: int = 99 ) -> List[str]: kennitalas = [] for n in range(random_from, random_to + 1): kt = generate_kennitala(first_six, n, century) if kt is not None: kennitalas.append(kt) return kennitalas def rust_from_kennitalas(kennitalas: List[str]) -> str: num = len(kennitalas) test = f"const KENNITALAS: [&'static str; {num}] = [\n" for kt in kennitalas: test += f' "{kt}",\n' test += "];\n" return test def test_from_kennitalas(test_name: str, kennitalas: List[str], bad=True) -> str: assert len(test_name) > 0 assert all(ord(c) < 128 for c in test_name) test = "#[test]\n" test += f"fn {test_name}()" + "{\n" test += rust_from_kennitalas(kennitalas) test += " for kt_str in KENNITALAS.iter()" + "{\n" test += " let kt_result = Kennitala::new(kt_str);\n" if bad: test += " assert!(kt_result.is_err());\n" else: test += "let kt = kt_result.unwrap();\n" if not bad: test += " assert_eq!(kt.to_string(), kt_str.to_owned());" test += " }\n" test += "}\n" return test def run_rustfmt(rust_code): """ Run `rustfmt` on the given Rust code if it is available on the current system. """ if which("rustfmt"): rustfmt = subprocess.Popen( ["rustfmt"], stdin=subprocess.PIPE, stdout=subprocess.PIPE ) rust_code = rustfmt.communicate(rust_code.encode("utf-8"))[0].decode("utf-8") return rust_code def dump_to_file(string: str, file_path: str): with open(file_path, "w", encoding="utf-8") as f: f.write(string) def main(): BAD_PARAMS = [ ["month_is_zero_1", "220001", "0"], ["month_is_zero_2", "030012", "9"], ["day_is_zero_1", "000300", "9"], ["day_is_zero_2", "001175", "0"], ["leap_day_in_2001", "290201", "0"], ] GOOD_PARAMS = [ ["new_years_1999", "311299", "9"], ["new_years_2000", "311200", "0"], ["first_january_2001", "010101", "0"], ["leap_day_in_2000", "290200", "0"], ] file_contents = "// DO NOT EDIT! This file was automatically generated by" file_contents += " the\n// `autogenerate_tests.py` Python script.\n" file_contents += "use kennitolur::Kennitala;\n" file_contents += "use std::string::ToString;\n\n" for (test_name, first_six, century) in BAD_PARAMS: kennitalas = generate_kennitalas(first_six, century) file_contents += test_from_kennitalas(test_name, kennitalas, bad=True) for (test_name, first_six, century) in GOOD_PARAMS: kennitalas = generate_kennitalas(first_six, century) file_contents += test_from_kennitalas(test_name, kennitalas, bad=False) # Run rustfmt on the generated code file_contents = run_rustfmt(file_contents) # Generate the tests/ directory this_dir = os.path.abspath(os.path.dirname(__file__)) target_dir = os.path.join(this_dir, "tests") os.makedirs(target_dir, exist_ok=True) # Dump the generated tests to tests/autogenerated_tests.rs file_path = os.path.join(target_dir, "autogenerated_tests.rs") dump_to_file(file_contents, file_path) if __name__ == "__main__": main()