Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion script_umdp3_checker/checker_dispatch_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ def get_diff_dispatch_table_fortran(self) -> list[Callable]:
"""Get dispatch table for Fortran diff tests"""
return [
self.umdp3_checker.openmp_sentinels_in_column_one,
self.umdp3_checker.unseparated_keywords,
self.umdp3_checker.go_to_other_than_9999,
self.umdp3_checker.write_using_default_format,
self.umdp3_checker.dimension_forbidden,
Expand Down
33 changes: 0 additions & 33 deletions script_umdp3_checker/tests/test_fortran_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,39 +77,6 @@ def test_openmp_sentinels_in_column_one(lines, expected_result):
assert result.failure_count == expected_result


unseparated_keywords_parameters = [
(["ELSEIF", "ENDDO", "ENDSUBROUTINE"], 3, "All keywords unseparated"),
(["ELSE IF", "ENDMODULE", "ENDSUBROUTINE"], 2, "One keyword separated"),
(["ELSE IF", "END PARRALEL DO", "END IF"], 0, "All keywords separated"),
(["i=0", "i=i+1", "PRINT*,i"], 0, "No keywords"),
(["PROGRAM test", "i=0", "ENDIF"], 1, "One keyword unseparated"),
(["i=0", "ENDPARALLELDO", "END DO"], 1, "One keyword unseparated in middle"),
(
[
"REAL, INTENT(IN OUT) :: lambda_pole !INOUT Longitude of pole",
"REAL, INTENT(OUT) :: Dave",
"DO",
"nothing",
"END DO",
],
0,
"unseparated keyword in comment",
),
(["#endif", "i=i+1", "PRINT*,i"], 0, "Safely ignore cpp commands"),
]


@pytest.mark.parametrize(
"lines, expected_result",
[data[:2] for data in unseparated_keywords_parameters],
ids=[data[2] for data in unseparated_keywords_parameters],
)
def test_unseparated_keywords(lines, expected_result):
checker = UMDP3Checker()
result = checker.unseparated_keywords(lines)
assert result.failure_count == expected_result


go_to_other_than_9999_parameters = [
(
[" GO TO 1000", " GO TO 2000"],
Expand Down
116 changes: 113 additions & 3 deletions script_umdp3_checker/tests/test_umdp3_rules_S3.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
r3_1_1_there_can_be_only_one,
r3_2_1_check_crown_copyright,
r3_3_2_line_too_long,
r3_3_3_one_statement_per_line,
r3_4_1_capitalised_keywords,
r3_4_2_no_full_uppercase_variable_names,
r3_4_3_no_unseparated_keywords,
)


Expand Down Expand Up @@ -306,6 +308,64 @@ def test_r3_3_2_line_too_long(
# =================================================================


@pytest.mark.parametrize(
"changes_list, expected_result, expected_errors",
[
(
[["add", 74, ["icode=0 ; icode= icode + 1"]]],
1,
{"multiple statements on one line": [74]},
),
(
[
[
"add",
83,
[
"IF ( l_unscale ) THEN ; icode=-300 ; ALLOCATE(field( 1,1 ) ) ;"
+ " END IF"
],
],
[
"replace",
116,
["IF ( l_unscale ) THEN ; DEALLOCATE(field) ; END IF"],
],
],
2,
{"multiple statements on one line": [83, 117]},
),
(
[],
0,
{},
), # No changes, expect no errors
],
ids=[
"single multi-statements failure",
"multiple multi-statments failure",
"No error check",
],
)
def test_3_3_3_one_statement_per_line(
example_fortran_lines, changes_list, expected_result, expected_errors
):
modified_fortran_lines = modify_fortran_lines(example_fortran_lines, changes_list)
result = r3_3_3_one_statement_per_line(modified_fortran_lines)
failure_count = result.failure_count
errors = result.errors
for error, lines_list in errors.items():
assert error in expected_errors
for line_no in lines_list:
assert line_no in expected_errors[error]
assert len(lines_list) == len(expected_errors[error])
assert len(errors) == len(expected_errors)
assert failure_count == expected_result


# =================================================================


@pytest.mark.parametrize(
"changes_list, expected_result, expected_errors",
[
Expand Down Expand Up @@ -566,7 +626,57 @@ def test_r3_4_2_no_full_uppercase_variable_names(
assert len(errors) == len(expected_errors)
assert failure_count == expected_result

# =================================================================

"""TODO: When testing for 'unseparated keywords' remember to test/discard false +ves on
"#endif" or similar. Current (old) test flags these in the wild, but the example file
has no cpp directives..."""

@pytest.mark.parametrize(
"changes_list, expected_result, expected_errors",
[
(
[
["replace", 71, ["ENDIF"]],
["replace", 101, [" ENDDO"]],
["replace", 102, ["ENDIF"]],
],
3,
{
"unseparated keyword at line: 71: ENDIF": [71],
"unseparated keyword at line: 101: ENDDO": [101],
"unseparated keyword at line: 102: ENDIF": [102],
},
),
(
[
["replace", 119, ["ENDSUBROUTINE"]],
["replace", 120, ["ENDMODULE"]],
],
2,
{
"unseparated keyword at line: 119: ENDSUBROUTINE": [119],
"unseparated keyword at line: 120: ENDMODULE": [120],
},
),
([], 0, {}), # No changes, expect no errors
],
ids=[
"3 Unseparated Keywords Errors, ENDIF & ENDDO",
"2 Unseparated Keywords Errors, ENDSUBROUTINE & ENDMODULE",
"No Unseparated Keywords Errors",
],
)
def test_r3_4_3_no_unseparated_keywords(
example_fortran_lines, changes_list, expected_result, expected_errors
):
modified_fortran_lines = modify_fortran_lines(example_fortran_lines, changes_list)
result = r3_4_3_no_unseparated_keywords(modified_fortran_lines)
failure_count = result.failure_count
assert failure_count == expected_result
errors = result.errors
assert len(errors) == len(expected_errors)
for error, lines_list in errors.items():
assert error in expected_errors
for line_no in lines_list:
assert line_no in expected_errors[error]
assert len(lines_list) == len(expected_errors[error])
assert len(errors) == len(expected_errors)
assert failure_count == expected_result
29 changes: 0 additions & 29 deletions script_umdp3_checker/umdp3_checker_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
from typing import List, Dict
from search_lists import (
obsolescent_intrinsics,
unseparated_keywords_list,
retired_ifdefs,
deprecated_c_identifiers,
)
Expand Down Expand Up @@ -125,34 +124,6 @@ def openmp_sentinels_in_column_one(self, lines: List[str]) -> TestResult:
errors=error_log,
)

def unseparated_keywords(self, lines: List[str]) -> TestResult:
"""Check for omitted optional spaces in keywords"""
failures = 0
error_log = {}
count = -1
for count, line in enumerate(lines):
if line.lstrip(" ").startswith("!"):
continue
clean_line = self.remove_quoted(line)
clean_line = self.comment_line.sub("", clean_line).strip()
# The [^#] is to avoid false positives on #endif and similar.
for pattern in [f"([^#]|^)\\b{kw}\\b" for kw in unseparated_keywords_list]:
if re.search(pattern, clean_line, re.IGNORECASE):
failures += 1
error_log = self.add_error_log(
error_log,
f"unseparated keyword in line: {line.strip()}",
count + 1,
)
output = f"Checked {count + 1} lines, found {failures} failures."
return TestResult(
checker_name="Unseparated Keywords",
failure_count=failures,
passed=(failures == 0),
output=output,
errors=error_log,
)

def go_to_other_than_9999(self, lines: List[str]) -> TestResult:
"""Check for GO TO statements other than 9999"""
failures = 0
Expand Down
64 changes: 61 additions & 3 deletions script_umdp3_checker/umdp3_rules_S3.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@

"""ToDo: This lot will need putting back as and when the tests that use them get imported/re-created"""
# from fortran_keywords import fortran_keywords
# from search_lists import (
from search_lists import (
# obsolescent_intrinsics,
# unseparated_keywords_list,
unseparated_keywords_list,
# retired_ifdefs,
# deprecated_c_identifiers,
# )
)
# from dataclasses import dataclass, field
# from script_umdp3_checker import fortran_keywords

Expand Down Expand Up @@ -278,6 +278,35 @@ def r3_3_2_line_too_long(lines: List[str]) -> TestResult:

"""
* TODO: Never put more than one statement on a line.
"""


def r3_3_3_one_statement_per_line(lines: List[str]) -> TestResult:
"""Check for more than one statement on a line, by looking for the presence of a ";" character outside of quoted strings and comments."""
failures = 0
error_log = {}
count = -1
for count, line in enumerate(lines, 1):
# Remove quoted strings and comments
if line.lstrip(" ").startswith("!"):
continue
clean_line = remove_quoted(line)
clean_line = remove_comments(clean_line)
if ";" in clean_line:
failures += 1
error_log = add_error_log(
error_log, "multiple statements on one line", count
)
return TestResult(
checker_name="Test 3.3 One Statement Per Line",
failure_count=failures,
passed=(failures == 0),
output=f"Checked {count + 1} lines, found {failures} failures.",
errors=error_log,
)


"""
* TODO: Write your program in UK English, unless you have a very good reason for not doing so."""

"""3.4 Fortran style
Expand Down Expand Up @@ -378,6 +407,33 @@ def r3_4_2_no_full_uppercase_variable_names(lines: List[str]) -> TestResult:
* TODO: To improve readability, you should always use the optional space to separate
the Fortran keywords.
This rule also applies to OpenMP keywords. (See: 3.15)
"""
def r3_4_3_no_unseparated_keywords(lines: List[str]) -> TestResult:
"""Check for omitted optional spaces in keywords"""
failures = 0
error_log = {}
count = -1
for count, line in enumerate(lines, 1):
clean_line = remove_quoted(line)
clean_line = remove_comments(clean_line)
for kw in unseparated_keywords_list:
pattern = f"\\b{kw}\\b"
if re.search(pattern, clean_line, re.IGNORECASE):
failures += 1
error_log = add_error_log(
error_log,
f"unseparated keyword at line: {count}: {kw}",
count,
)
return TestResult(
checker_name="No Unseparated Keywords",
failure_count=failures,
passed=(failures == 0),
output=f"Checked {count} lines, found {failures} UPPERCASE variables.",
errors=error_log,
)

"""
* TODO: The full version of END should be used at all times,
eg END SUBROUTINE <name> and END FUNCTION <name>
* TODO: New code should be written using Fortran 95/2003 features. Avoid non-portable
Expand Down Expand Up @@ -455,6 +511,8 @@ def r3_4_2_no_full_uppercase_variable_names(lines: List[str]) -> TestResult:
r3_1_1_there_can_be_only_one,
r3_2_1_check_crown_copyright,
r3_3_2_line_too_long,
r3_3_3_one_statement_per_line,
r3_4_1_capitalised_keywords,
r3_4_2_no_full_uppercase_variable_names,
r3_4_3_no_unseparated_keywords,
]
Loading