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

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# -------------------------------------------------------------------------------------------------- 

8 

9from pathlib import Path 

10 

11import pytest 

12 

13from tsfpga import DEFAULT_FILE_ENCODING 

14from tsfpga.system_utils import create_file 

15 

16 

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() 

25 

26 except UnicodeDecodeError: 

27 print(file) 

28 with file.open(encoding="utf8") as file_handle: 

29 lines = file_handle.readlines() 

30 

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") 

37 

38 raise 

39 

40 

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 

51 

52 return test_ok 

53 

54 

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 

66 

67 

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}") 

77 

78 return test_ok 

79 

80 

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}") 

91 

92 return test_ok 

93 

94 

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 

101 

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 

112 

113 return test_ok 

114 

115 

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) 

125 

126 with pytest.raises(UnicodeDecodeError): 

127 open_file_with_encoding(file) 

128 

129 

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) 

137 

138 

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) 

148 

149 

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) 

157 

158 

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) 

170 

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)