Coverage for tsfpga/test/lint/file_format_lint.py: 51%
89 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-03-24 20:51 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-03-24 20:51 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the tsfpga project, a project platform for modern FPGA development.
5# https://tsfpga.com
6# https://github.com/tsfpga/tsfpga
7# --------------------------------------------------------------------------------------------------
9from pathlib import Path
11import pytest
13from tsfpga import DEFAULT_FILE_ENCODING
14from tsfpga.system_utils import create_file
17def open_file_with_encoding(file: Path) -> None:
18 """
19 Try to open the ``file`` with ASCII encoding.
20 If that fails, i.e. if it contains non-ASCII characters, print information and raise exception.
21 """
22 try:
23 with file.open(encoding="ascii") as file_handle:
24 file_handle.read()
26 except UnicodeDecodeError:
27 print(file)
28 with file.open(encoding="utf8") as file_handle:
29 lines = file_handle.readlines()
31 for line_idx, line in enumerate(lines):
32 for character in line:
33 try:
34 character.encode("ascii")
35 except UnicodeEncodeError: # noqa: PERF203
36 print(f"Character {character} on line {line_idx + 1} is not ASCII")
38 raise
41def check_file_ends_with_newline(file: Path) -> bool:
42 """
43 Return True if ``file`` ends with newline.
44 """
45 test_ok = True
46 with file.open(encoding=DEFAULT_FILE_ENCODING) as file_handle:
47 file_data = file_handle.read()
48 if len(file_data) != 0 and file_data[-1] != "\n":
49 print(f"File {file} didn't end with newline")
50 test_ok = False
52 return test_ok
55def check_file_for_tab_character(file: Path) -> bool:
56 """
57 Return True of ``file`` does not contain any TAB character.
58 """
59 test_ok = True
60 with file.open(encoding=DEFAULT_FILE_ENCODING) as file_handle:
61 for idx, line in enumerate(file_handle.readlines()):
62 if "\t" in line:
63 test_ok = False
64 print(f"TAB character (\\t) on line {idx + 1} in {file}")
65 return test_ok
68def check_file_for_carriage_return(file: Path) -> bool:
69 """
70 Return True if ``file`` does not contain any carriage return (CR).
71 """
72 test_ok = True
73 with file.open(encoding=DEFAULT_FILE_ENCODING, newline="") as file_handle:
74 if "\r" in file_handle.read():
75 test_ok = False
76 print(f"Windows style line breaks (\\r\\n aka CR/LF) in {file}")
78 return test_ok
81def check_file_for_trailing_whitespace(file: Path) -> bool:
82 """
83 Return True if ``file`` does not contain any trailing whitespace.
84 """
85 test_ok = True
86 with file.open(encoding=DEFAULT_FILE_ENCODING) as file_handle:
87 for idx, line in enumerate(file_handle.readlines()):
88 if " \n" in line:
89 test_ok = False
90 print(f"Trailing whitespace on line {idx + 1} in {file}")
92 return test_ok
95def check_file_for_line_length(file_path: Path, max_length: int = 100) -> bool:
96 """
97 Return True if ``file`` does not contain any line that is longer than ``max_length`` characters.
98 """
99 max_length_with_newline = max_length + 1
100 test_ok = True
102 with file_path.open(encoding=DEFAULT_FILE_ENCODING) as file_handle:
103 lines = file_handle.readlines()
104 for line_number, line in enumerate(lines):
105 line_length = len(line)
106 if line_length > max_length_with_newline:
107 print(
108 f"Line {file_path}:{line_number + 1} is too long "
109 f"({line_length - 1} > {max_length_with_newline - 1})"
110 )
111 test_ok = False
113 return test_ok
116def test_open_file_with_encoding_should_raise_exception_on_bad_file(tmp_path: Path) -> None:
117 """
118 Sanity check that the function we use actually triggers on bad files.
119 """
120 file = tmp_path / "temp_file_for_test.txt"
121 with file.open("w", encoding="utf-8") as file_handle:
122 # Swedish word for island = non-ASCII character
123 data = "\N{LATIN CAPITAL LETTER O WITH DIAERESIS}"
124 file_handle.write(data)
126 with pytest.raises(UnicodeDecodeError):
127 open_file_with_encoding(file)
130def test_check_file_for_tab_character_should_fail_on_bad_file(tmp_path: Path) -> None:
131 """
132 Sanity check that the function we use actually triggers on bad files.
133 """
134 data = "Apa\thest"
135 file = create_file(tmp_path / "temp_file_for_test.txt", data)
136 assert not check_file_for_tab_character(file)
139def test_check_file_for_carriage_return_should_fail_on_bad_file(tmp_path: Path) -> None:
140 """
141 Sanity check that the function we use actually triggers on bad files.
142 """
143 file = tmp_path / "temp_file_for_test.txt"
144 data = b"Apa\r\nhest"
145 with file.open("wb") as file_handle:
146 file_handle.write(data)
147 assert not check_file_for_carriage_return(file)
150def test_check_file_for_trailing_whitespace(tmp_path: Path) -> None:
151 """
152 Sanity check that the function we use actually triggers on bad files.
153 """
154 data = "Apa \nhest \nzebra"
155 file = create_file(tmp_path / "temp_file_for_test.txt", data)
156 assert not check_file_for_trailing_whitespace(file)
159def test_check_file_for_line_length(tmp_path: Path) -> None:
160 """
161 Sanity check that the function we use actually triggers on bad files.
162 """
163 ok_data = """
164asdf
165apa
166hest
167"""
168 ok_file_path = create_file(tmp_path / "ok_data.txt", contents=ok_data)
169 assert check_file_for_line_length(ok_file_path)
171 bad_data = """
172asdf
173apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa
174hest
175"""
176 bad_file_path = create_file(tmp_path / "bad_data.txt", contents=bad_data)
177 assert not check_file_for_line_length(file_path=bad_file_path, max_length=80)