diff --git a/CLAUDE.md b/CLAUDE.md index 3b531e8d..9a6cac5f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -96,6 +96,14 @@ is mirrored into `~/utils/file_length/_tables.py`, `[tool.ruff] exclude` and - `ruff` runs with `select = ["ALL"]` and mypy with `strict = true`. - No `# noqa`, no `# type: ignore`. A pre-commit hook rejects both outright. +- **No per-file-ignores either, including for tests.** `pyproject.toml` has no + `[tool.ruff.lint.per-file-ignores]` table at all, which is stricter than the + rest of the fleet. So a test asserts by raising: + `if x != y: msg = ...; raise AssertionError(msg)` -- never a bare `assert`, + which is `S101`. Test functions carry docstrings and full annotations like + any other code, `pytest.raises` always takes `match=`, and an expected value + gets a named constant rather than being a magic number. Decided by kuhy on + 2026-08-28 against the alternative of mirroring testsAndMisc's test block. - Notebooks: `ruff` reads and formats `.ipynb` natively (cell-aware, so an import used two cells later is not reported unused), which is why there is no `nbqa` layer. It rewrites `source` only — outputs are left byte-identical. diff --git a/Programming/ECOTE/program/translator/__init__.py b/Programming/ECOTE/program/translator/__init__.py index e69de29b..fb364d91 100644 --- a/Programming/ECOTE/program/translator/__init__.py +++ b/Programming/ECOTE/program/translator/__init__.py @@ -0,0 +1 @@ +"""LaTeX-subset to HTML translator: the ECOTE project deliverable.""" diff --git a/Programming/ECOTE/program/translator/main.py b/Programming/ECOTE/program/translator/main.py index 1f8686b2..eeb59cdd 100644 --- a/Programming/ECOTE/program/translator/main.py +++ b/Programming/ECOTE/program/translator/main.py @@ -1,68 +1,74 @@ """Program for converting Latex files into html files.""" import argparse +import logging +from pathlib import Path + +logger = logging.getLogger(__name__) -def command_name_check(latex_string, command_name): - """Checks if command name given from latex was spelled correctly.""" +def command_name_check(latex_string: str, command_name: str) -> str: + """Check if command name given from latex was spelled correctly.""" if latex_string[1 : (len(command_name) + 1)] != command_name: - print(latex_string + "Error! command misspeled!") + logger.error("%sError! command misspeled!", latex_string) return "Error!" return "" -def generic_checks(latex_string): - """Checks if string is given and whether latex parameters - ends with curly bracket. +def generic_checks(latex_string: str) -> str: + """Check that a string was given and that its parameters close. + + "Close" means the latex parameter list ends with a curly bracket. """ if latex_string == "": - print(latex_string + "Error! No input given to function") + logger.error("%sError! No input given to function", latex_string) return "Error!" if "}" not in latex_string: - print(latex_string + "Error! curly bracket not closed!") + logger.error("%sError! curly bracket not closed!", latex_string) return "Error!" return "" -def generic_checks_command(latex_string): - """In additon to generic checks it also checks if command starts with \ - """ +def generic_checks_command(latex_string: str) -> str: + r"""Run the generic checks, and also require a leading backslash.""" if generic_checks(latex_string) == "Error!": return "Error!" if latex_string[0] != "\\": - print(latex_string + "Error! has no slash at begining") + logger.error("%sError! has no slash at begining", latex_string) return "Error!" return "" -def document_class_only_checks(latex_string): - """Checks only errors connected to document class.""" +def document_class_only_checks(latex_string: str) -> str: + """Check only errors connected to document class.""" if len(latex_string) < len("\\documentclass{"): - print(latex_string + "Error! latex string to short to be document class!") + logger.error( + "%sError! latex string to short to be document class!", latex_string + ) return "Error!" if latex_string[len("\\documentclass{") - 1] != "{": - print(latex_string + "Error! curly bracket not opened!") + logger.error("%sError! curly bracket not opened!", latex_string) return "Error!" if command_name_check(latex_string, "documentclass") == "Error!": return "Error!" return "" -def document_class(latex_string): - r"""Converts LaTeX documentclass method to html +def document_class(latex_string: str) -> str: + r"""Convert LaTeX documentclass method to html. + \documentclass{article}. """ if generic_checks_command(latex_string) == "Error!": return "Error!" if document_class_only_checks(latex_string) == "Error!": return "Error!" - # document_type = latex_string[len( - # "\\documentclass{"): (len(latex_string) - 1)] return "" -def begin_document(latex_string): - r"""Converts LaTeX begin document method to html +def begin_document(latex_string: str) -> str: + r"""Convert LaTeX begin document method to html. + \begin{document}. """ if generic_checks_command(latex_string) == "Error!": @@ -72,8 +78,8 @@ def begin_document(latex_string): return "" -def begin_tabular(latex_string): - r"""Checks if LaTeX begin tabular method is correct.""" +def begin_tabular(latex_string: str) -> str: + r"""Check if LaTeX begin tabular method is correct.""" if generic_checks_command(latex_string) == "Error!": return "Error!" if command_name_check(latex_string, "begin") == "Error!": @@ -81,8 +87,8 @@ def begin_tabular(latex_string): return "" -def tabular_parameters(latex_string): - r"""Checks if LaTeX tabular environment has any parameters.""" +def tabular_parameters(latex_string: str) -> str: + r"""Check if LaTeX tabular environment has any parameters.""" if latex_string == "": return "" if latex_string == "[]": @@ -90,15 +96,18 @@ def tabular_parameters(latex_string): return "Error!" -def only_pipes_and_space(latex_string): - """Checks if latex string only contains | or " " +def only_pipes_and_space(latex_string: str) -> bool: + """Check if latex string only contains | or " ". + if yes returns True, if no returns false. """ return all(char in ("|", " ", "{", "}") for char in latex_string) -def handle_complicated_parameter(latex_string, return_array, i): - """Handles single complicated parameter from loop.""" +def handle_complicated_parameter( + latex_string: str, return_array: list[str], i: int +) -> tuple[list[str], int, str]: + """Handle single complicated parameter from loop.""" closing_bracket = latex_string.find("}", i + 1) columns_string = latex_string[i : closing_bracket + 1] result = tabular_columns_parameters(columns_string) @@ -110,9 +119,12 @@ def handle_complicated_parameter(latex_string, return_array, i): def main_tabular_parameters_loop_inside( - latex_string, i, simple_parameters_dictionary, return_array -): - """Handles single tabular parameter character.""" + latex_string: str, + i: int, + simple_parameters_dictionary: dict[str, str], + return_array: list[str], +) -> tuple[list[str], int, str]: + """Handle single tabular parameter character.""" character = latex_string[i] if character in ["l", "c", "r", "|"]: return_array.append(simple_parameters_dictionary.get(latex_string[i])) @@ -124,8 +136,10 @@ def main_tabular_parameters_loop_inside( return return_array, i, "" -def main_tabular_parameters_loop(latex_string, simple_parameters_dictionary): - """Converts simple and paragraph table parameters into html style code.""" +def main_tabular_parameters_loop( + latex_string: str, simple_parameters_dictionary: dict[str, str] +) -> list[str] | str: + """Convert simple and paragraph table parameters into html style code.""" return_array = [] i = 0 latex_string_length = len(latex_string) @@ -138,50 +152,48 @@ def main_tabular_parameters_loop(latex_string, simple_parameters_dictionary): return return_array -def tabular_required_errors(latex_string): - """Runs generic checks and +def tabular_required_errors(latex_string: str) -> str: + """Run the generic checks and. + checks if there are any parameters put to latex table. """ if generic_checks(latex_string) == "Error!": return "Error!" if latex_string == "{}": - print( - """tabular_required_parameters - required table parameters are empty!:""", + logger.error( + "tabular_required_parameters: required table parameters are empty: %s", latex_string, ) return "Error!" return "" -def simple_parameters_dictionary_return(): - """Defines html styles for simple tabular parameters.""" - simple_parameters_dictionary = { +def simple_parameters_dictionary_return() -> dict[str, str]: + """Return the html styles for the simple tabular parameters.""" + return { "l": "align='left'", "c": "align='center'", "r": "align='right'", "|": "style='border-left: 1px solid black'", } - return simple_parameters_dictionary -def tabular_required_parameters(latex_string): - """Converts simple tabular parameters to html style commands.""" +def tabular_required_parameters(latex_string: str) -> list[str] | str: + """Convert simple tabular parameters to html style commands.""" if tabular_required_errors(latex_string) == "Error!": return "Error!" simple_parameters_dictionary = simple_parameters_dictionary_return() if only_pipes_and_space(latex_string): - print( - """tabular_required_parameters, - required table parameters are only pipes and spaces!:""", + logger.error( + "tabular_required_parameters: parameters are only pipes and spaces: %s", latex_string, ) return "Error!" return main_tabular_parameters_loop(latex_string, simple_parameters_dictionary) -def length_conversions(latex_length): - """Converts latex lengths to html lengths.""" +def length_conversions(latex_length: str) -> float | None: + """Convert latex lengths to html lengths.""" length_dictionary = { "pt": [1.3, "px"], "mm": [1, "mm"], @@ -193,9 +205,11 @@ def length_conversions(latex_length): return length_dictionary.get(latex_length, "Error!") -def construct_return_string(vertical_align_type, final_length, conversed_unit): +def construct_return_string( + vertical_align_type: str, final_length: float, conversed_unit: str +) -> str: """Construsts entire html style of parameters.""" - return_string = ( + return ( "style='" + vertical_align_type + " width: " @@ -203,38 +217,40 @@ def construct_return_string(vertical_align_type, final_length, conversed_unit): + conversed_unit[1] + ";'" ) - return return_string -def return_parameter_dictionary(): - """Returns html styles for each complicated latex parameter.""" - parameter_dictionary = { +def return_parameter_dictionary() -> dict[str, str]: + """Return html styles for each complicated latex parameter.""" + return { "p": "vertical-align: top;", "m": "vertical-align: middle;", "b": "vertical-align: bottom;", } - return parameter_dictionary -def tabular_columns_parameters_errors(latex_string, vertical_align_type): - """Checks for intermediate errors +def tabular_columns_parameters_errors( + latex_string: str, vertical_align_type: str +) -> str: + """Check for intermediate errors. + Checks if parameter length starts with curly bracket. """ if vertical_align_type == "Error!": - print("tabular_columns_parameters, unknown parameter: ", latex_string[0]) + logger.error( + "tabular_columns_parameters: unknown parameter: %s", latex_string[0] + ) return "Error!" if latex_string[1] != "{": - print( - """tabular_columns_parameters - parameter length does not start with {""", + logger.error( + "tabular_columns_parameters: parameter length does not start with {: %s", latex_string, ) return "Error!" return "" -def string_length_no_whitespace(length_parameter): - """Calculates string width without whitespace.""" +def string_length_no_whitespace(length_parameter: str) -> tuple[int, str]: + """Measure the string width, ignoring whitespace.""" i = 0 length_value = "" for character in length_parameter: @@ -245,8 +261,8 @@ def string_length_no_whitespace(length_parameter): return i, length_value -def tabular_columns_parameters(latex_string): - """Handles tabular paragraph parameters and translates them to html style.""" +def tabular_columns_parameters(latex_string: str) -> str: + """Handle tabular paragraph parameters and translates them to html style.""" if generic_checks(latex_string) == "Error!": return "Error!" parameter_dictionary = return_parameter_dictionary() @@ -259,44 +275,44 @@ def tabular_columns_parameters(latex_string): length_unit = length_parameter[i:] conversed_unit = length_conversions(length_unit) if conversed_unit == "Error!": - print( - """tabular_columns_parameters, - Unit could not be conversed!""", - latex_string, + logger.error( + "tabular_columns_parameters: unit could not be converted: %s", latex_string ) return "Error!" final_length = round(float(length_value) * conversed_unit[0], 2) return construct_return_string(vertical_align_type, final_length, conversed_unit) -def split_rows(latex_string): - """Splits table row string into separate strings and puts them in array.""" +def split_rows(latex_string: str) -> list[str]: + """Split table row string into separate strings and puts them in array.""" double_backslash = "\\" - rows = latex_string.split(double_backslash) - return rows + return latex_string.split(double_backslash) -def split_columns(table_row, column_count): - """Splits table row columns into separate strings and puts them in array.""" +def split_columns(table_row: str, column_count: int) -> list[str] | str: + """Split table row columns into separate strings and puts them in array.""" columns = table_row.split("&") if len(columns) != column_count and columns != [""]: - print( - f"Error! extracted columns length: {columns} and columns_count: {column_count} is different!" + logger.error( + "Error! extracted columns length: %s and columns_count: %s is different!", + columns, + column_count, ) return "Error!" return columns -def translate_column(latex_column): +def translate_column(latex_column: str) -> str: """Translate insides of a single latex tabular column to html.""" hline_string_literal = "\\hline" replaced_hline = latex_column.replace(hline_string_literal, "
") - replaced_newline = replaced_hline.replace("\newline", "
") - return replaced_newline + return replaced_hline.replace("\newline", "
") -def handle_line_strings(line_string, column_style, column_number, return_string): - """Converts lines untill there are no more lines.""" +def handle_line_strings( + line_string: str, column_style: list[str], column_number: int, return_string: str +) -> tuple[str, int]: + """Convert lines untill there are no more lines.""" current_style = column_style[column_number] if current_style == line_string: while current_style == line_string: @@ -307,14 +323,19 @@ def handle_line_strings(line_string, column_style, column_number, return_string) def handle_single_column( - column, column_style, column_number, line_string, return_string -): - """Handles single column from columns loop.""" + column: str, + column_style: list[str], + column_number: int, + line_string: str, + return_string: str, +) -> tuple[str, int]: + """Handle single column from columns loop.""" return_string += "" columns = split_columns(row, column_amount) @@ -356,8 +385,8 @@ def go_through_latex_rows( return return_string -def translate_inside_to_html(latex_table_inside, column_style): - """Translates entire table insides to html.""" +def translate_inside_to_html(latex_table_inside: str, column_style: list[str]) -> str: + """Translate entire table insides to html.""" return_string = "
= len(column_style): - print( - f"""Error! column_number index: {column_number} - is out of length of column_style: {column_style}""" + logger.error( + "Error! column_number index %s is past the end of column_style %s", + column_number, + column_style, ) return "Error!", column_number return_string, column_number = handle_line_strings( @@ -329,9 +350,13 @@ def handle_single_column( def handle_latex_columns( - columns, column_style, column_number, line_string, return_string -): - """Goes through every column in a row and translates it to html.""" + columns: list[str], + column_style: list[str], + column_number: int, + line_string: str, + return_string: str, +) -> tuple[str, int]: + """Go through every column in a row and translates it to html.""" if columns != "Error!": for column in columns: return_string, column_number = handle_single_column( @@ -341,9 +366,13 @@ def handle_latex_columns( def go_through_latex_rows( - rows, return_string, column_amount, column_style, line_string -): - """Goes through latex table insides row by row.""" + rows: list[str], + return_string: str, + column_amount: int, + column_style: list[str], + line_string: str, +) -> str: + """Go through latex table insides row by row.""" for row in rows: return_string += "
" column_amount = 0 line_string = "style='border-left: 1px solid black'" @@ -372,21 +401,17 @@ def translate_inside_to_html(latex_table_inside, column_style): return return_string -def read_file(tex_filename): - """opens, reads and saves latex file - content into python string, then closes file. - """ - with open(tex_filename, encoding="UTF-8") as tex_file: - data = tex_file.read().replace("\n", "") - tex_file.close() - return data +def read_file(tex_filename: str) -> str: + """Read a latex file into one string, with newlines stripped.""" + with Path(tex_filename).open(encoding="UTF-8") as tex_file: + return tex_file.read().replace("\n", "") -def read_document_class(latex_full_string): - """Finds documentclass in latex file and checks function correctness.""" +def read_document_class(latex_full_string: str) -> tuple[str, str] | str: + """Find documentclass in latex file and checks function correctness.""" document_class_index = latex_full_string.find("\\documentclass") if document_class_index == -1: - print("Main function error! documentclass not found") + logger.error("Main function error! documentclass not found") return "Error!" document_class_close_bracket = latex_full_string.find("}", document_class_index) document_class_string = latex_full_string[ @@ -398,11 +423,11 @@ def read_document_class(latex_full_string): return document_class(document_class_string), latex_full_string -def read_begin_document(latex_full_string): - """Finds LaTeX \begin{document} command and returns html counterpart to it.""" +def read_begin_document(latex_full_string: str) -> tuple[str, str]: + r"""Find the LaTeX \begin{document} command and return its html counterpart.""" begin_document_index = latex_full_string.find(r"\begin{document}") if begin_document_index == -1: - print("read_begin_document error! begin{document not found") + logger.error("read_begin_document error! begin{document not found") return "Error!", latex_full_string begin_document_close_bracket = latex_full_string.find("}", begin_document_index) begin_document_string = latex_full_string[ @@ -415,8 +440,9 @@ def read_begin_document(latex_full_string): return return_string, latex_full_string -def read_start(html_string, data): - """Finds, reads latex document class +def read_start(html_string: str, data: str) -> tuple[str, str]: + """Find and read the latex document class. + and begin document and translates them to html. """ document_class_result, data = read_document_class(data) @@ -431,8 +457,11 @@ def read_start(html_string, data): return html_string, data -def handle_table_whole(html_string, data, table_start, table_end): - """Reads entire table, handles its parameters +def handle_table_whole( + html_string: str, data: str, table_start: int, table_end: int +) -> tuple[str, str] | str: + """Read entire table, handles its parameters. + and insides and translates to html. """ if table_start == -1 or table_end == -1: @@ -448,8 +477,8 @@ def handle_table_whole(html_string, data, table_start, table_end): return html_string -def handle_insides(html_string, data): - """Finds inside of table and translates them to html.""" +def handle_insides(html_string: str, data: str) -> tuple[str, str]: + """Find inside of table and translates them to html.""" tabular_index = 0 tabular_end_string = r"\end{tabular}" while tabular_index != -1: @@ -467,23 +496,25 @@ def handle_insides(html_string, data): return html_string, data -def main_function(tex_filename): - """Ran by default by program, +def main_function(tex_filename: str) -> str: + """Ran by default by program,. + gets latex file and translates to html. """ data = read_file(tex_filename) html_string = "" html_string, data = read_start(html_string, data) if html_string == "Error!": - print("""main_function error! - when reading documentclass or begin document""") + logger.error( + "main_function error!\n when reading documentclass or begin document" + ) return "Error" html_string, data = handle_insides(html_string, data) - html_string = html_string.replace("}", "") - return html_string + return html_string.replace("}", "") -def handle_arguments(): +def handle_arguments() -> str: + """Parse the command line and return the .tex filename to translate.""" parser = argparse.ArgumentParser(description="Specify tex filename and path") parser.add_argument( "--filename", @@ -499,5 +530,4 @@ def handle_arguments(): if __name__ == "__main__": TEX_FILENAME = handle_arguments() FINAL_HTML = main_function(TEX_FILENAME) - with open(TEX_FILENAME + ".html", "w", encoding="UTF-8") as f: - f.write(FINAL_HTML) + Path(TEX_FILENAME + ".html").write_text(FINAL_HTML, encoding="UTF-8") diff --git a/Programming/ECOTE/program/unit_tests/__init__.py b/Programming/ECOTE/program/unit_tests/__init__.py index e69de29b..4fed924d 100644 --- a/Programming/ECOTE/program/unit_tests/__init__.py +++ b/Programming/ECOTE/program/unit_tests/__init__.py @@ -0,0 +1 @@ +"""Unit tests for the ECOTE LaTeX-to-HTML translator.""" diff --git a/Programming/ECOTE/program/unit_tests/expect.py b/Programming/ECOTE/program/unit_tests/expect.py new file mode 100644 index 00000000..757994dd --- /dev/null +++ b/Programming/ECOTE/program/unit_tests/expect.py @@ -0,0 +1,40 @@ +"""Assertion helpers for the ECOTE test suite. + +This repo runs ruff with ``select = ["ALL"]`` and no per-file-ignores, so a +bare ``assert`` is an ``S101`` violation even inside a test. These raise +``AssertionError`` explicitly and, unlike a bare assert, always report both +values -- pytest's assertion rewriting does not apply to a helper, so the +message has to carry the detail itself. +""" + + +def expect_equal(actual: object, expected: object) -> None: + """Raise unless *actual* equals *expected*.""" + if actual != expected: + msg = f"expected {expected!r}, got {actual!r}" + raise AssertionError(msg) + + +def expect_in(member: str, container: str) -> None: + """Raise unless *member* is contained in *container*.""" + if member not in container: + msg = f"expected {member!r} to be in {container!r}" + raise AssertionError(msg) + + +def expect_true(actual: object) -> None: + """Raise unless *actual* equals ``True``. + + Separate from expect_equal so that a caller never has to pass a bare + boolean positionally, which is FBT003. + """ + if actual is not True: + msg = f"expected True, got {actual!r}" + raise AssertionError(msg) + + +def expect_false(actual: object) -> None: + """Raise unless *actual* equals ``False``.""" + if actual is not False: + msg = f"expected False, got {actual!r}" + raise AssertionError(msg) diff --git a/Programming/ECOTE/program/unit_tests/test_code/__init__.py b/Programming/ECOTE/program/unit_tests/test_code/__init__.py index e69de29b..0050609c 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/__init__.py +++ b/Programming/ECOTE/program/unit_tests/test_code/__init__.py @@ -0,0 +1 @@ +"""One module per translator function under test.""" diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_begin_document.py b/Programming/ECOTE/program/unit_tests/test_code/test_begin_document.py index a952b438..26688dbc 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_begin_document.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_begin_document.py @@ -2,57 +2,72 @@ from translator.main import begin_document +from unit_tests.expect import expect_equal + # Write python tests for a function translating LaTeX documentclass to html -def given_empty_then_error(): - """Given: "" +def given_empty_then_error() -> None: + """Given empty, then error. + + Given: "" When: N/A Then: Error message. """ - assert begin_document("") == "Error!" + expect_equal(begin_document(""), "Error!") -def given_not_closed_then_error(): - r"""Given: "\\begin\{document" +def given_not_closed_then_error() -> None: + r"""Given not closed, then error. + + Given: "\\begin\{document" When: N/A Then: Error message. """ - assert begin_document(r"\\begin{document") == "Error!" + expect_equal(begin_document(r"\\begin{document"), "Error!") -def given_no_opening_then_error(): - """Given: No opening curly bracket +def given_no_opening_then_error() -> None: + """Given no opening, then error. + + Given: No opening curly bracket When: N/A Then: Error message. """ - assert begin_document(r"\\begindocument}") == "Error!" + expect_equal(begin_document(r"\\begindocument}"), "Error!") -def given_misspeled_then_error(): - """Given: misspelled begin document +def given_misspeled_then_error() -> None: + """Given misspeled, then error. + + Given: misspelled begin document When: N/A Then: Error message. """ - assert begin_document(r"\\begim{document}") == "Error!" + expect_equal(begin_document(r"\\begim{document}"), "Error!") -def given_no_slash_then_error(): - """Given: no backslash at start +def given_no_slash_then_error() -> None: + """Given no slash, then error. + + Given: no backslash at start When: N/A Then: Error message. """ - assert begin_document(r"begin{document}") == "Error!" + expect_equal(begin_document(r"begin{document}"), "Error!") -def given_correct_then_html(): - """Given: \\documentclass{article} +def given_correct_then_html() -> None: + r"""Given correct, then html. + + Given: \\documentclass{article} When: N/A Then: . """ - assert begin_document(r"\begin{document}") == "" + expect_equal(begin_document(r"\begin{document}"), "") -def test_begin_document(): +def test_begin_document() -> None: + """Exercise begin document.""" given_correct_then_html() given_empty_then_error() given_misspeled_then_error() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_begin_tabular.py b/Programming/ECOTE/program/unit_tests/test_code/test_begin_tabular.py index f798e75d..845b452e 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_begin_tabular.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_begin_tabular.py @@ -2,65 +2,82 @@ from translator.main import begin_tabular +from unit_tests.expect import expect_equal + # Write python tests for a function translating LaTeX documentclass to html -def given_empty_then_error(): - """Given: "" +def given_empty_then_error() -> None: + """Given empty, then error. + + Given: "" When: N/A Then: Error message. """ - assert begin_tabular("") == "Error!" + expect_equal(begin_tabular(""), "Error!") -def given_not_closed_then_error(): - r"""Given: command not closed +def given_not_closed_then_error() -> None: + r"""Given not closed, then error. + + Given: command not closed When: N/A Then: Error message. """ - assert begin_tabular(r"\\begin{tabular") == "Error!" + expect_equal(begin_tabular(r"\\begin{tabular"), "Error!") -def given_no_opening_then_error(): - """Given: No opening curly bracket +def given_no_opening_then_error() -> None: + """Given no opening, then error. + + Given: No opening curly bracket When: N/A Then: Error message. """ - assert begin_tabular(r"\\begintabular}") == "Error!" + expect_equal(begin_tabular(r"\\begintabular}"), "Error!") -def given_misspeled_then_error(): - """Given: misspelled begin tabular +def given_misspeled_then_error() -> None: + """Given misspeled, then error. + + Given: misspelled begin tabular When: N/A Then: Error message. """ - assert begin_tabular(r"\\begim{tabular}") == "Error!" + expect_equal(begin_tabular(r"\\begim{tabular}"), "Error!") -def given_no_slash_then_error(): - """Given: no backslash at start +def given_no_slash_then_error() -> None: + """Given no slash, then error. + + Given: no backslash at start When: N/A Then: Error message. """ - assert begin_tabular(r"begin{tabular}") == "Error!" + expect_equal(begin_tabular(r"begin{tabular}"), "Error!") -def given_tabular_star_then_error(): - """Given: no backslash at start +def given_tabular_star_then_error() -> None: + """Given tabular star, then error. + + Given: no backslash at start When: N/A Then: Error message. """ - assert begin_tabular(r"begin{tabular*}") == "Error!" + expect_equal(begin_tabular(r"begin{tabular*}"), "Error!") -def given_correct_then_html(): - """Given: \begin{tabular} +def given_correct_then_html() -> None: + r"""Given correct, then html. + + Given: \begin{tabular} When: N/A Then: . """ - assert begin_tabular(r"\begin{tabular}") == "
" + expect_equal(begin_tabular(r"\begin{tabular}"), "
") -def test_begin_tabular(): +def test_begin_tabular() -> None: + """Exercise begin tabular.""" given_correct_then_html() given_empty_then_error() given_misspeled_then_error() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_command_name_check.py b/Programming/ECOTE/program/unit_tests/test_code/test_command_name_check.py index 57d72abd..5fd1b590 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_command_name_check.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_command_name_check.py @@ -1,23 +1,32 @@ +"""Tests the command name check.""" + from translator.main import command_name_check - -def given_valid_then_empty(): - assert command_name_check("{documentclass}", "documentclass") == "" +from unit_tests.expect import expect_equal -def given_valid_begin_then_empty(): - assert command_name_check("{begin}", "begin") == "" +def given_valid_then_empty() -> None: + """Given valid, then empty.""" + expect_equal(command_name_check("{documentclass}", "documentclass"), "") -def given_invalid_then_error(): - assert command_name_check("{documentclasS}", "documentclass") == "Error!" +def given_valid_begin_then_empty() -> None: + """Given valid begin, then empty.""" + expect_equal(command_name_check("{begin}", "begin"), "") -def given_invalid_begin_then_error(): - assert command_name_check("{begIn}", "begin") == "Error!" +def given_invalid_then_error() -> None: + """Given invalid, then error.""" + expect_equal(command_name_check("{documentclasS}", "documentclass"), "Error!") -def test_command_name_check(): +def given_invalid_begin_then_error() -> None: + """Given invalid begin, then error.""" + expect_equal(command_name_check("{begIn}", "begin"), "Error!") + + +def test_command_name_check() -> None: + """Exercise command name check.""" given_valid_then_empty() given_valid_begin_then_empty() given_invalid_then_error() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_document_class.py b/Programming/ECOTE/program/unit_tests/test_code/test_document_class.py index bf8df2dc..4bef0a88 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_document_class.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_document_class.py @@ -2,74 +2,93 @@ from translator.main import document_class +from unit_tests.expect import expect_equal + # Write python tests for a function translating LaTeX documentclass to html -def given_empty_then_error(): - """Given: "" +def given_empty_then_error() -> None: + """Given empty, then error. + + Given: "" When: N/A Then: Error message. """ - assert document_class("") == "Error!" + expect_equal(document_class(""), "Error!") -def given_not_closed_then_error(): - """Given: "\\documentclass{article" +def given_not_closed_then_error() -> None: + r"""Given not closed, then error. + + Given: "\\documentclass{article" When: N/A Then: Error message. """ - assert document_class("\\documentclass{article") == "Error!" + expect_equal(document_class("\\documentclass{article"), "Error!") -def given_parameters_then_error(): - """Given: class parameters +def given_parameters_then_error() -> None: + """Given parameters, then error. + + Given: class parameters When: N/A Then: Error message. """ - assert document_class("\\documentclass[12pt]{article}") == "Error!" + expect_equal(document_class("\\documentclass[12pt]{article}"), "Error!") -def given_no_opening_then_error(): - """Given: No opening curly bracket +def given_no_opening_then_error() -> None: + """Given no opening, then error. + + Given: No opening curly bracket When: N/A Then: Error message. """ - assert document_class("\\documentclassarticle}") == "Error!" + expect_equal(document_class("\\documentclassarticle}"), "Error!") -def given_misspeled_then_error(): - """Given: misspelled document class +def given_misspeled_then_error() -> None: + """Given misspeled, then error. + + Given: misspelled document class When: N/A Then: Error message. """ - assert document_class("\\documentclasZ{article}") == "Error!" + expect_equal(document_class("\\documentclasZ{article}"), "Error!") -def given_class_not_recognized_then_error(): - """Given: class not recognized +def given_class_not_recognized_then_error() -> None: + """Given class not recognized, then error. + + Given: class not recognized When: N/A Then: Error message. """ - assert document_class("\\documentclass{idonotexist}") == "" + expect_equal(document_class("\\documentclass{idonotexist}"), "") -def given_no_slash_then_error(): - """Given: no backslash at start +def given_no_slash_then_error() -> None: + """Given no slash, then error. + + Given: no backslash at start When: N/A Then: Error message. """ - assert document_class("documentclass{article}") == "Error!" + expect_equal(document_class("documentclass{article}"), "Error!") -def given_correct_then_html(): - """Given: \\documentclass{article} +def given_correct_then_html() -> None: + r"""Given correct, then html. + + Given: \\documentclass{article} When: N/A Then: . """ - assert document_class("\\documentclass{article}") == "" + expect_equal(document_class("\\documentclass{article}"), "") -def test_document_class(): +def test_document_class() -> None: + """Exercise document class.""" given_correct_then_html() given_class_not_recognized_then_error() given_empty_then_error() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_document_class_only_checks.py b/Programming/ECOTE/program/unit_tests/test_code/test_document_class_only_checks.py index b85f22ea..9f85a322 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_document_class_only_checks.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_document_class_only_checks.py @@ -1,19 +1,27 @@ +"""Tests the documentclass-only checks.""" + from translator.main import document_class_only_checks - -def test_missing_curly_bracket(): - assert document_class_only_checks("\\documentclass") == "Error!" +from unit_tests.expect import expect_equal -def test_missing_command_name(): - assert document_class_only_checks("\\otherclass{}") == "Error!" +def test_missing_curly_bracket() -> None: + """Exercise missing curly bracket.""" + expect_equal(document_class_only_checks("\\documentclass"), "Error!") -def test_correct_input(): - assert document_class_only_checks("\\documentclass{}") == "" +def test_missing_command_name() -> None: + """Exercise missing command name.""" + expect_equal(document_class_only_checks("\\otherclass{}"), "Error!") -def test_document_class_only_checks(): +def test_correct_input() -> None: + """Exercise correct input.""" + expect_equal(document_class_only_checks("\\documentclass{}"), "") + + +def test_document_class_only_checks() -> None: + """Exercise document class only checks.""" test_missing_curly_bracket() test_missing_command_name() test_correct_input() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_generic_checks.py b/Programming/ECOTE/program/unit_tests/test_code/test_generic_checks.py index eb0006a0..ae7802cd 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_generic_checks.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_generic_checks.py @@ -1,19 +1,27 @@ +"""Tests the generic command checks.""" + from translator.main import generic_checks - -def test_empty_string(): - assert generic_checks("") == "Error!" +from unit_tests.expect import expect_equal -def test_no_curly_bracket(): - assert generic_checks("latex_string_without_curly_bracket") == "Error!" +def test_empty_string() -> None: + """Exercise empty string.""" + expect_equal(generic_checks(""), "Error!") -def test_with_curly_bracket_at_end(): - assert generic_checks("latex_string_with_curly_bracket}") == "" +def test_no_curly_bracket() -> None: + """Exercise no curly bracket.""" + expect_equal(generic_checks("latex_string_without_curly_bracket"), "Error!") -def test_generic_checks(): +def test_with_curly_bracket_at_end() -> None: + """Exercise with curly bracket at end.""" + expect_equal(generic_checks("latex_string_with_curly_bracket}"), "") + + +def test_generic_checks() -> None: + """Exercise generic checks.""" test_empty_string() test_no_curly_bracket() test_with_curly_bracket_at_end() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_generic_checks_command.py b/Programming/ECOTE/program/unit_tests/test_code/test_generic_checks_command.py index 9cccc3de..bc6a7f7d 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_generic_checks_command.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_generic_checks_command.py @@ -1,27 +1,36 @@ +"""Tests the generic checks for a command.""" + from translator.main import generic_checks_command - -def test_empty_string(): - assert (generic_checks_command("")) == "Error!" +from unit_tests.expect import expect_equal -def test_no_curly_bracket(): - assert (generic_checks_command("latex_string_without_curly_bracket")) == "Error!" +def test_empty_string() -> None: + """Exercise empty string.""" + expect_equal(generic_checks_command(""), "Error!") -def test_no_slash_at_beginning(): - assert ( - generic_checks_command("latex_string_without_slash_at_beginning}") - ) == "Error!" +def test_no_curly_bracket() -> None: + """Exercise no curly bracket.""" + expect_equal(generic_checks_command("latex_string_without_curly_bracket"), "Error!") -def test_with_slash_and_curly_bracket(): - assert ( - generic_checks_command("\\latex_string_with_slash_and_curly_bracket}") - ) == "" +def test_no_slash_at_beginning() -> None: + """Exercise no slash at beginning.""" + expect_equal( + generic_checks_command("latex_string_without_slash_at_beginning}"), "Error!" + ) -def test_generic_checks_command(): +def test_with_slash_and_curly_bracket() -> None: + """Exercise with slash and curly bracket.""" + expect_equal( + generic_checks_command("\\latex_string_with_slash_and_curly_bracket}"), "" + ) + + +def test_generic_checks_command() -> None: + """Exercise generic checks command.""" test_empty_string() test_no_curly_bracket() test_no_slash_at_beginning() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_length_conversions.py b/Programming/ECOTE/program/unit_tests/test_code/test_length_conversions.py index 7cfcd521..32d1d565 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_length_conversions.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_length_conversions.py @@ -2,90 +2,113 @@ from translator.main import length_conversions +from unit_tests.expect import expect_equal + # Write python tests for a function translating LaTeX documentclass to html -def given_empty_then_error(): - """Given: "" +def given_empty_then_error() -> None: + """Given empty, then error. + + Given: "" When: N/A Then: Error message. """ - assert length_conversions("") == "Error!" + expect_equal(length_conversions(""), "Error!") -def given_pt_then_px(): - """Given: "" +def given_pt_then_px() -> None: + """Given pt, then px. + + Given: "" When: N/A Then: Error message. """ - assert length_conversions("pt") == [1.3, "px"] + expect_equal(length_conversions("pt"), [1.3, "px"]) -def given_mm_then_mm(): - """Given: "" +def given_mm_then_mm() -> None: + """Given mm, then mm. + + Given: "" When: N/A Then: Error message. """ - assert length_conversions("mm") == [1, "mm"] + expect_equal(length_conversions("mm"), [1, "mm"]) -def given_cm_then_cm(): - """Given: "" +def given_cm_then_cm() -> None: + """Given cm, then cm. + + Given: "" When: N/A Then: Error message. """ - assert length_conversions("cm") == [1, "cm"] + expect_equal(length_conversions("cm"), [1, "cm"]) -def given_in_then_in(): - """Given: "" +def given_in_then_in() -> None: + """Given in, then in. + + Given: "" When: N/A Then: Error message. """ - assert length_conversions("in") == [1, "in"] + expect_equal(length_conversions("in"), [1, "in"]) -def given_ex_then_ex(): - """Given: "" +def given_ex_then_ex() -> None: + """Given ex, then ex. + + Given: "" When: N/A Then: Error message. """ - assert length_conversions("ex") == [1, "ex"] + expect_equal(length_conversions("ex"), [1, "ex"]) -def given_em_then_em(): - """Given: "" +def given_em_then_em() -> None: + """Given em, then em. + + Given: "" When: N/A Then: Error message. """ - assert length_conversions("em") == [1, "em"] + expect_equal(length_conversions("em"), [1, "em"]) -def given_mu_then_error(): - """Given: "" +def given_mu_then_error() -> None: + """Given mu, then error. + + Given: "" When: N/A Then: Error message. """ - assert length_conversions("mu") == "Error!" + expect_equal(length_conversions("mu"), "Error!") -def given_sp_then_error(): - """Given: "" +def given_sp_then_error() -> None: + """Given sp, then error. + + Given: "" When: N/A Then: Error message. """ - assert length_conversions("sp") == "Error!" + expect_equal(length_conversions("sp"), "Error!") -def given_unknown_then_error(): - """Given: "" +def given_unknown_then_error() -> None: + """Given unknown, then error. + + Given: "" When: N/A Then: Error message. """ - assert length_conversions("unknown") == "Error!" + expect_equal(length_conversions("unknown"), "Error!") -def test_begin_tabular(): +def test_begin_tabular() -> None: + """Exercise begin tabular.""" given_empty_then_error() given_pt_then_px() given_mm_then_mm() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_only_pipes_and_space.py b/Programming/ECOTE/program/unit_tests/test_code/test_only_pipes_and_space.py index d6d7024f..7d7f1818 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_only_pipes_and_space.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_only_pipes_and_space.py @@ -1,27 +1,37 @@ +"""Tests the only-pipes-and-space predicate.""" + from translator.main import only_pipes_and_space - -def given_empty_then_true(): - assert only_pipes_and_space("") == True +from unit_tests.expect import expect_false, expect_true -def given_only_pipes_then_true(): - assert only_pipes_and_space("|||||") == True +def given_empty_then_true() -> None: + """Given empty, then true.""" + expect_true(only_pipes_and_space("")) -def given_only_space_then_true(): - assert only_pipes_and_space(" ") == True +def given_only_pipes_then_true() -> None: + """Given only pipes, then true.""" + expect_true(only_pipes_and_space("|||||")) -def given_space_and_pipes_then_true(): - assert only_pipes_and_space("| | ||| |") == True +def given_only_space_then_true() -> None: + """Given only space, then true.""" + expect_true(only_pipes_and_space(" ")) -def given_not_space_nor_pipes_then_false(): - assert only_pipes_and_space(" || || a") == False +def given_space_and_pipes_then_true() -> None: + """Given space and pipes, then true.""" + expect_true(only_pipes_and_space("| | ||| |")) -def test_only_pipes_and_space(): +def given_not_space_nor_pipes_then_false() -> None: + """Given not space nor pipes, then false.""" + expect_false(only_pipes_and_space(" || || a")) + + +def test_only_pipes_and_space() -> None: + """Exercise only pipes and space.""" given_empty_then_true() given_only_pipes_then_true() given_only_space_then_true() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_split_columns.py b/Programming/ECOTE/program/unit_tests/test_code/test_split_columns.py index fe734d06..f07e1b36 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_split_columns.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_split_columns.py @@ -1,27 +1,37 @@ +"""Tests splitting a tabular row into columns.""" + from translator.main import split_columns - -def given_empty_then_correct(): - assert split_columns("", 0) == [""] +from unit_tests.expect import expect_equal -def given_no_and_then_correct(): - assert split_columns("estsegsegtswegseg", 1) == ["estsegsegtswegseg"] +def given_empty_then_correct() -> None: + """Given empty, then correct.""" + expect_equal(split_columns("", 0), [""]) -def given_single_and_then_correct(): - assert split_columns("&", 2) == ["", ""] +def given_no_and_then_correct() -> None: + """Given no and, then correct.""" + expect_equal(split_columns("estsegsegtswegseg", 1), ["estsegsegtswegseg"]) -def given_too_much_columns_then_error(): - assert split_columns("test & 2 & test", 1) == "Error!" +def given_single_and_then_correct() -> None: + """Given single and, then correct.""" + expect_equal(split_columns("&", 2), ["", ""]) -def given_default_then_correct(): - assert split_columns("test & 2 & test", 3) == ["test ", " 2 ", " test"] +def given_too_much_columns_then_error() -> None: + """Given too much columns, then error.""" + expect_equal(split_columns("test & 2 & test", 1), "Error!") -def test_split_columns(): +def given_default_then_correct() -> None: + """Given default, then correct.""" + expect_equal(split_columns("test & 2 & test", 3), ["test ", " 2 ", " test"]) + + +def test_split_columns() -> None: + """Exercise split columns.""" given_empty_then_correct() given_no_and_then_correct() given_single_and_then_correct() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_split_rows.py b/Programming/ECOTE/program/unit_tests/test_code/test_split_rows.py index 8cf12c1f..6e1cf391 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_split_rows.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_split_rows.py @@ -1,24 +1,33 @@ +"""Tests splitting a tabular body into rows.""" + from translator.main import split_rows - -def given_empty_then_correct(): - assert split_rows("") == [""] +from unit_tests.expect import expect_equal -def given_no_slash_then_correct(): - assert split_rows("estsegsegtswegseg") == ["estsegsegtswegseg"] +def given_empty_then_correct() -> None: + """Given empty, then correct.""" + expect_equal(split_rows(""), [""]) -def given_double_slash_then_correct(): - assert split_rows("\\") == ["", ""] +def given_no_slash_then_correct() -> None: + """Given no slash, then correct.""" + expect_equal(split_rows("estsegsegtswegseg"), ["estsegsegtswegseg"]) -def given_actual_string_then_correct(): +def given_double_slash_then_correct() -> None: + """Given double slash, then correct.""" + expect_equal(split_rows("\\"), ["", ""]) + + +def given_actual_string_then_correct() -> None: + """Given actual string, then correct.""" actual_string = "test & 2 & test \\ 4 & 5 & 6" - assert split_rows(actual_string) == ["test & 2 & test ", " 4 & 5 & 6"] + expect_equal(split_rows(actual_string), ["test & 2 & test ", " 4 & 5 & 6"]) -def test_split_rows(): +def test_split_rows() -> None: + """Exercise split rows.""" given_empty_then_correct() given_no_slash_then_correct() given_double_slash_then_correct() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_tabular_columns_parameters.py b/Programming/ECOTE/program/unit_tests/test_code/test_tabular_columns_parameters.py index cc01ea1d..971185bc 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_tabular_columns_parameters.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_tabular_columns_parameters.py @@ -1,56 +1,71 @@ +"""Tests the tabular column parameters.""" + from translator.main import tabular_columns_parameters +from unit_tests.expect import expect_equal -def given_empty_then_error(): - """Given: + +def given_empty_then_error() -> None: + """Given empty, then error. + + Given: When: N/A Then: . """ - assert tabular_columns_parameters("") == "Error!" + expect_equal(tabular_columns_parameters(""), "Error!") -def given_unknown_then_error(): - """Given: +def given_unknown_then_error() -> None: + """Given unknown, then error. + + Given: When: N/A Then: . """ - assert tabular_columns_parameters("l{1.3cm}") == "Error!" + expect_equal(tabular_columns_parameters("l{1.3cm}"), "Error!") -def given_p_then_array(): - """Given: +def given_p_then_array() -> None: + """Given p, then array. + + Given: When: N/A Then: . """ - assert ( - tabular_columns_parameters("p{1.3pt}") - == "style='vertical-align: top; width: 1.69px;'" + expect_equal( + tabular_columns_parameters("p{1.3pt}"), + "style='vertical-align: top; width: 1.69px;'", ) -def given_m_then_array(): - """Given: +def given_m_then_array() -> None: + """Given m, then array. + + Given: When: N/A Then: . """ - assert ( - tabular_columns_parameters("m{1.3pt}") - == "style='vertical-align: middle; width: 1.69px;'" + expect_equal( + tabular_columns_parameters("m{1.3pt}"), + "style='vertical-align: middle; width: 1.69px;'", ) -def given_b_then_array(): - """Given: +def given_b_then_array() -> None: + """Given b, then array. + + Given: When: N/A Then: . """ - assert ( - tabular_columns_parameters("b{1.3pt}") - == "style='vertical-align: bottom; width: 1.69px;'" + expect_equal( + tabular_columns_parameters("b{1.3pt}"), + "style='vertical-align: bottom; width: 1.69px;'", ) -def test_tabular_columns_parameters(): +def test_tabular_columns_parameters() -> None: + """Exercise tabular columns parameters.""" given_empty_then_error() given_unknown_then_error() given_p_then_array() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_tabular_parameters.py b/Programming/ECOTE/program/unit_tests/test_code/test_tabular_parameters.py index 68d81d04..80a11022 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_tabular_parameters.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_tabular_parameters.py @@ -2,32 +2,41 @@ from translator.main import tabular_parameters +from unit_tests.expect import expect_equal -def given_empty_then_empty(): - """Given: + +def given_empty_then_empty() -> None: + """Given empty, then empty. + + Given: When: N/A Then: . """ - assert tabular_parameters("") == "" + expect_equal(tabular_parameters(""), "") -def given_empty_brackets_then_empty(): - """Given: [] +def given_empty_brackets_then_empty() -> None: + """Given empty brackets, then empty. + + Given: [] When: N/A Then: . """ - assert tabular_parameters("[]") == "" + expect_equal(tabular_parameters("[]"), "") -def given_non_empty_then_error(): - """Given: [c] +def given_non_empty_then_error() -> None: + """Given non empty, then error. + + Given: [c] When: N/A Then: . """ - assert tabular_parameters("[c]") == "Error!" + expect_equal(tabular_parameters("[c]"), "Error!") -def test_tabular_parameters(): +def test_tabular_parameters() -> None: + """Exercise tabular parameters.""" given_empty_then_empty() given_empty_brackets_then_empty() given_non_empty_then_error() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_tabular_required_parameters.py b/Programming/ECOTE/program/unit_tests/test_code/test_tabular_required_parameters.py index 17d717a4..5e987202 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_tabular_required_parameters.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_tabular_required_parameters.py @@ -2,134 +2,170 @@ from translator.main import tabular_required_parameters +from unit_tests.expect import expect_equal -def given_empty_then_error(): - """Given: + +def given_empty_then_error() -> None: + """Given empty, then error. + + Given: When: N/A Then: . """ - assert tabular_required_parameters("") == "Error!" + expect_equal(tabular_required_parameters(""), "Error!") -def given_empty_brackets_then_error(): - """Given: {} +def given_empty_brackets_then_error() -> None: + """Given empty brackets, then error. + + Given: {} When: N/A Then: . """ - assert tabular_required_parameters("{}") == "Error!" + expect_equal(tabular_required_parameters("{}"), "Error!") -def given_just_line_then_error(): - """Given: {} +def given_just_line_then_error() -> None: + """Given just line, then error. + + Given: {} When: N/A Then: . """ - assert tabular_required_parameters("{|}") == "Error!" + expect_equal(tabular_required_parameters("{|}"), "Error!") -def given_just_lines_then_error(): - """Given: {} +def given_just_lines_then_error() -> None: + """Given just lines, then error. + + Given: {} When: N/A Then: . """ - assert tabular_required_parameters("{|||||||}") == "Error!" + expect_equal(tabular_required_parameters("{|||||||}"), "Error!") -def given_single_left_then_correct(): - """Given: {l} +def given_single_left_then_correct() -> None: + """Given single left, then correct. + + Given: {l} When: N/A Then: . """ - assert tabular_required_parameters("{l}") == ["align='left'"] + expect_equal(tabular_required_parameters("{l}"), ["align='left'"]) -def given_single_center_then_correct(): - """Given: {c} +def given_single_center_then_correct() -> None: + """Given single center, then correct. + + Given: {c} When: N/A Then: . """ - assert tabular_required_parameters("{c}") == ["align='center'"] + expect_equal(tabular_required_parameters("{c}"), ["align='center'"]) -def given_single_right_then_correct(): - """Given: {r} +def given_single_right_then_correct() -> None: + """Given single right, then correct. + + Given: {r} When: N/A Then: . """ - assert tabular_required_parameters("{r}") == ["align='right'"] + expect_equal(tabular_required_parameters("{r}"), ["align='right'"]) -def given_empty_wrap_p_then_error(): - """Given: {r} +def given_empty_wrap_p_then_error() -> None: + """Given empty wrap p, then error. + + Given: {r} When: N/A Then: . """ - assert tabular_required_parameters("{p}") == "Error!" + expect_equal(tabular_required_parameters("{p}"), "Error!") -def given_empty_wrap_m_then_error(): - """Given: {r} +def given_empty_wrap_m_then_error() -> None: + """Given empty wrap m, then error. + + Given: {r} When: N/A Then: . """ - assert tabular_required_parameters("{m}") == "Error!" + expect_equal(tabular_required_parameters("{m}"), "Error!") -def given_empty_wrap_b_then_error(): - """Given: {r} +def given_empty_wrap_b_then_error() -> None: + """Given empty wrap b, then error. + + Given: {r} When: N/A Then: . """ - assert tabular_required_parameters("{b}") == "Error!" + expect_equal(tabular_required_parameters("{b}"), "Error!") -def given_empty_wrap_p_brackets_then_error(): - """Given: {r} +def given_empty_wrap_p_brackets_then_error() -> None: + """Given empty wrap p brackets, then error. + + Given: {r} When: N/A Then: . """ - assert tabular_required_parameters("{p{}}") == "Error!" + expect_equal(tabular_required_parameters("{p{}}"), "Error!") -def given_empty_wrap_m_brackets_then_error(): - """Given: {r} +def given_empty_wrap_m_brackets_then_error() -> None: + """Given empty wrap m brackets, then error. + + Given: {r} When: N/A Then: . """ - assert tabular_required_parameters("{m{}}") == "Error!" + expect_equal(tabular_required_parameters("{m{}}"), "Error!") -def given_empty_wrap_b_brackets_then_error(): - """Given: {r} +def given_empty_wrap_b_brackets_then_error() -> None: + """Given empty wrap b brackets, then error. + + Given: {r} When: N/A Then: . """ - assert tabular_required_parameters("{b{}}") == "Error!" + expect_equal(tabular_required_parameters("{b{}}"), "Error!") -def given_filled_p_brackets_then_correct(): - """Given: {r} +def given_filled_p_brackets_then_correct() -> None: + """Given filled p brackets, then correct. + + Given: {r} When: N/A Then: . """ - assert tabular_required_parameters("{p{1.3pt}}") == [ - "style='vertical-align: top; width: 1.69px;'" - ] + expect_equal( + tabular_required_parameters("{p{1.3pt}}"), + ["style='vertical-align: top; width: 1.69px;'"], + ) -def given_complicated_then_correct(): - assert tabular_required_parameters("{|lp{1.3pt}c|r|}") == [ - "style='border-left: 1px solid black'", - "align='left'", - "style='vertical-align: top; width: 1.69px;'", - "align='center'", - "style='border-left: 1px solid black'", - "align='right'", - "style='border-left: 1px solid black'", - ] +def given_complicated_then_correct() -> None: + """Given complicated, then correct.""" + expect_equal( + tabular_required_parameters("{|lp{1.3pt}c|r|}"), + [ + "style='border-left: 1px solid black'", + "align='left'", + "style='vertical-align: top; width: 1.69px;'", + "align='center'", + "style='border-left: 1px solid black'", + "align='right'", + "style='border-left: 1px solid black'", + ], + ) -def test_tabular_required_parameters(): +def test_tabular_required_parameters() -> None: + """Exercise tabular required parameters.""" given_empty_then_error() given_empty_brackets_then_error() given_just_line_then_error() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_translate_column.py b/Programming/ECOTE/program/unit_tests/test_code/test_translate_column.py index 897f31c0..9438a3d6 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_translate_column.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_translate_column.py @@ -1,30 +1,40 @@ +"""Tests translating one tabular column to HTML.""" + from translator.main import translate_column - -def given_empty_then_correct(): - assert translate_column("") == "" +from unit_tests.expect import expect_equal -def given_plain_text_then_correct(): - assert translate_column("plain text") == "plain text" +def given_empty_then_correct() -> None: + """Given empty, then correct.""" + expect_equal(translate_column(""), "") -def given_just_hline_then_correct(): - assert translate_column(r"\hline") == "
" +def given_plain_text_then_correct() -> None: + """Given plain text, then correct.""" + expect_equal(translate_column("plain text"), "plain text") -def given_just_newline_then_correct(): - assert translate_column("\newline") == "
" +def given_just_hline_then_correct() -> None: + """Given just hline, then correct.""" + expect_equal(translate_column(r"\hline"), "
") -def given_all_then_correct(): - assert ( - translate_column("\\hline \newline hline newline test") - == "

hline newline test" +def given_just_newline_then_correct() -> None: + """Given just newline, then correct.""" + expect_equal(translate_column("\newline"), "
") + + +def given_all_then_correct() -> None: + """Given all, then correct.""" + expect_equal( + translate_column("\\hline \newline hline newline test"), + "

hline newline test", ) -def test_translate_column(): +def test_translate_column() -> None: + """Exercise translate column.""" given_empty_then_correct() given_plain_text_then_correct() given_just_hline_then_correct() diff --git a/Programming/ECOTE/program/unit_tests/test_code/test_translate_inside_to_html.py b/Programming/ECOTE/program/unit_tests/test_code/test_translate_inside_to_html.py index 7b98d8f6..68962c73 100644 --- a/Programming/ECOTE/program/unit_tests/test_code/test_translate_inside_to_html.py +++ b/Programming/ECOTE/program/unit_tests/test_code/test_translate_inside_to_html.py @@ -1,15 +1,26 @@ +"""Tests translating a tabular body to HTML.""" + from translator.main import tabular_required_parameters, translate_inside_to_html +from unit_tests.expect import expect_equal -def given_correct_then_correct(): + +def given_correct_then_correct() -> None: + """Given correct, then correct.""" latex_string = "test & 2 & test \\ 4 & 5 & 6 \\" parameters_string = "{ l | c | r }" column_styles = tabular_required_parameters(parameters_string) - assert ( - translate_inside_to_html(latex_string, column_styles) - == "
test 2 test
4 5 6
" + expect_equal( + translate_inside_to_html(latex_string, column_styles), + "
test 2 test
4 5 6
", ) -def test_translate_inside_to_html(): +def test_translate_inside_to_html() -> None: + """Exercise translate inside to html.""" given_correct_then_correct() diff --git a/Programming/ECRYPT_PROJECT/__init__.py b/Programming/ECRYPT_PROJECT/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/Programming/ECRYPT_PROJECT/sieve/__init__.py b/Programming/ECRYPT_PROJECT/sieve/__init__.py index e69de29b..8ab38c77 100644 --- a/Programming/ECRYPT_PROJECT/sieve/__init__.py +++ b/Programming/ECRYPT_PROJECT/sieve/__init__.py @@ -0,0 +1,6 @@ +"""Sieve of Eratosthenes, and the pytest suite that checks it against a table. + +The lab's point is not the sieve itself but the verification: every prime and +every composite below a bound, read from the reference CSVs in this directory, +becomes its own parametrised test case. +""" diff --git a/Programming/ECRYPT_PROJECT/sieve/conftest.py b/Programming/ECRYPT_PROJECT/sieve/conftest.py index e6d18854..191f4da8 100644 --- a/Programming/ECRYPT_PROJECT/sieve/conftest.py +++ b/Programming/ECRYPT_PROJECT/sieve/conftest.py @@ -1,51 +1,61 @@ +"""Turn the reference CSVs into one test case per prime and per composite. + +``P-100000.csv`` and ``C-100000.csv`` sit beside this file and are read with a +relative path, which is why the suite has to be run from this directory (see +``../run.sh``). ``--primes`` and ``--composites`` cap how far up each table the +run goes; the defaults keep a plain ``pytest`` fast. +""" + import pandas as pd import pytest from .project import sieve_of_eratosthenes +DEFAULT_BOUND = "100" -def pytest_addoption(parser): + +def pytest_addoption(parser: pytest.Parser) -> None: + """Register the two bounds that decide how much of each table is used.""" parser.addoption( "--primes", action="store", - default="100", + default=DEFAULT_BOUND, help="Upper bound for primes to test.", ) parser.addoption( "--composites", action="store", - default="100", + default=DEFAULT_BOUND, help="Upper bound for composites to test.", ) -def pytest_configure(config): +def pytest_configure(config: pytest.Config) -> None: + """Stash the parsed bounds where pytest_generate_tests can reach them.""" pytest.primes = int(config.getoption("--primes")) pytest.composites = int(config.getoption("--composites")) -def pytest_generate_tests(metafunc): +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + """Parametrise each test over its half of the reference tables.""" df_prime = pd.read_csv("P-100000.csv", header=None) df_prime = df_prime[df_prime.iloc[:, 1] < pytest.primes] primes = set(df_prime.iloc[:, 1]) + # C-100000.csv is "n=factorisation", so the number is the first column. df_composite = pd.read_csv("C-100000.csv", header=None, delimiter="=") - # interpret first oclumns as numbers df_composite = df_composite[df_composite.iloc[:, 0] < pytest.composites] composites = set(df_composite.iloc[:, 0]) + if metafunc.function.__name__ == "test_positives": metafunc.parametrize("prime", primes) metafunc.parametrize( "primes_obtained", - [ - sieve_of_eratosthenes(max(primes) + 1), - ], + [sieve_of_eratosthenes(max(primes) + 1)], ) if metafunc.function.__name__ == "test_composites": metafunc.parametrize("composite", composites) metafunc.parametrize( "primes_obtained", - [ - sieve_of_eratosthenes(max(composites) + 1), - ], + [sieve_of_eratosthenes(max(composites) + 1)], ) diff --git a/Programming/ECRYPT_PROJECT/sieve/project.py b/Programming/ECRYPT_PROJECT/sieve/project.py index ac3ec671..a29c7b5b 100644 --- a/Programming/ECRYPT_PROJECT/sieve/project.py +++ b/Programming/ECRYPT_PROJECT/sieve/project.py @@ -1,10 +1,11 @@ -"""Python program to print all Primes Smaller than or equal to N using Sieve of Eratosthenes.""" +"""Sieve of Eratosthenes: every prime up to and including N.""" import math +import sys def sieve_of_eratosthenes(num: int) -> list[int]: - """Finds all prime number from 1 to num. + """Return every prime from 1 to num. Args: num (int): Number up to which the prime numbers should be printed @@ -14,12 +15,13 @@ def sieve_of_eratosthenes(num: int) -> list[int]: """ if num <= 0: - raise ValueError("Number should not be negative.") + msg = "Number should not be negative." + raise ValueError(msg) # boolean list to store if a number is prime or not prime = [True] * (num + 1) p = 2 # starting prime number - # we will iterate till square root of num because - # if a number is not prime then it will have a factor less than or equal to square root of that number + # Stop at sqrt(num): a composite always has a factor at or below its + # square root, so anything still marked prime past that point stays prime. while p <= math.sqrt(num): # If prime[p] is not # changed, then it is a prime @@ -34,18 +36,20 @@ def sieve_of_eratosthenes(num: int) -> list[int]: def print_sieve(num: int) -> None: - """Prints all prime number from 1 to num. + """Print every prime from 1 to num, space separated. Args: num (int): Number up to which the prime numbers should be printed """ - for number in sieve_of_eratosthenes(num): - print(number, end=" ") + sys.stdout.write(" ".join(str(n) for n in sieve_of_eratosthenes(num))) + sys.stdout.write("\n") # Driver code if __name__ == "__main__": - num = int(input("Enter a number: ")) - print(f"Following are the prime numbers smaller than or equal to {num}") - print_sieve(num) + bound = int(input("Enter a number: ")) + sys.stdout.write( + f"Following are the prime numbers smaller than or equal to {bound}\n" + ) + print_sieve(bound) diff --git a/Programming/ECRYPT_PROJECT/sieve/test_main.py b/Programming/ECRYPT_PROJECT/sieve/test_main.py index 25f456b7..37fafeae 100644 --- a/Programming/ECRYPT_PROJECT/sieve/test_main.py +++ b/Programming/ECRYPT_PROJECT/sieve/test_main.py @@ -1,16 +1,33 @@ +"""Check the sieve against the reference prime and composite tables. + +Both parametrised fixtures come from ``conftest.py``, which reads the CSVs in +this directory -- so "a prime" here means a number this course was given as +prime, not one this code decided was prime. + +The assertions raise instead of using ``assert``: this repo runs ruff with +``select = ["ALL"]`` and no per-file-ignores, and a bare assert is ``S101``. +""" + import pytest from .project import sieve_of_eratosthenes -def test_positives(prime, primes_obtained): - assert prime in primes_obtained +def test_positives(prime: int, primes_obtained: list[int]) -> None: + """Every number the reference table calls prime is found by the sieve.""" + if prime not in primes_obtained: + msg = f"{prime} is prime but the sieve did not report it" + raise AssertionError(msg) -def test_composites(composite, primes_obtained): - assert composite not in primes_obtained +def test_composites(composite: int, primes_obtained: list[int]) -> None: + """No number the reference table calls composite is reported as prime.""" + if composite in primes_obtained: + msg = f"{composite} is composite but the sieve reported it as prime" + raise AssertionError(msg) -def test_negatives(): - with pytest.raises(ValueError): +def test_negatives() -> None: + """A non-positive bound is rejected rather than silently returning [].""" + with pytest.raises(ValueError, match="should not be negative"): sieve_of_eratosthenes(-1)