Coverage for tsfpga/test/lint/test_file_format.py: 98%

110 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-29 20:01 +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://gitlab.com/tsfpga/tsfpga 

7# -------------------------------------------------------------------------------------------------- 

8 

9# Third party libraries 

10import pytest 

11 

12# First party libraries 

13from tsfpga import DEFAULT_FILE_ENCODING, REPO_ROOT, TSFPGA_DOC, TSFPGA_EXAMPLE_MODULES, TSFPGA_TCL 

14from tsfpga.git_utils import find_git_files 

15from tsfpga.system_utils import create_file 

16 

17 

18def open_file_with_encoding(file): 

19 print(file) 

20 with open(file, encoding="ascii") as file_handle: 

21 file_handle.read() 

22 

23 

24def test_all_checked_in_files_are_properly_encoded(): 

25 """ 

26 To avoid problems with different editors and different file encodings, all checked in files 

27 should contain only ASCII characters. 

28 

29 Avoid one of the documentation files that uses wonky characters to illustrate a directory tree. 

30 """ 

31 for file in files_to_test(exclude_directories=[TSFPGA_DOC / "sphinx" / "module_structure.rst"]): 

32 open_file_with_encoding(file) 

33 

34 

35def check_file_ends_with_newline(file): 

36 test_ok = True 

37 with open(file, encoding=DEFAULT_FILE_ENCODING) as file_handle: 

38 file_data = file_handle.read() 

39 if len(file_data) != 0: 

40 if file_data[-1] != "\n": 

41 print(f"File {file} didn't end with newline") 

42 test_ok = False 

43 return test_ok 

44 

45 

46def test_all_checked_in_files_end_with_newline(): 

47 """ 

48 All checked in non-empty files should end with a UNIX style line break (\n). 

49 Otherwise UNIX doesn't consider them actual text files. 

50 """ 

51 test_ok = True 

52 for file in files_to_test(): 

53 test_ok &= check_file_ends_with_newline(file) 

54 assert test_ok 

55 

56 

57def check_file_for_tab_character(file): 

58 test_ok = True 

59 with open(file, encoding=DEFAULT_FILE_ENCODING) as file_handle: 

60 for idx, line in enumerate(file_handle.readlines()): 

61 if "\t" in line: 

62 test_ok = False 

63 print(f"TAB charatcher (\\t) on line {idx + 1} in {file}") 

64 return test_ok 

65 

66 

67def test_no_checked_in_files_contain_tabs(): 

68 """ 

69 To avoid problems with files looking different in different editors, no checked in files may 

70 contain TAB characters. 

71 """ 

72 test_ok = True 

73 for file in files_to_test(): 

74 test_ok &= check_file_for_tab_character(file) 

75 assert test_ok 

76 

77 

78def check_file_for_carriage_return(file): 

79 test_ok = True 

80 with open(file, encoding=DEFAULT_FILE_ENCODING, newline="") as file_handle: 

81 if "\r" in file_handle.read(): 

82 test_ok = False 

83 print(f"Windows style line breaks (\\r\\n aka CR/LF) in {file}") 

84 return test_ok 

85 

86 

87def test_no_checked_in_files_contain_carriage_return(): 

88 """ 

89 All checked in files should use UNIX style line breaks (\n not \r\n). 

90 Some Linux editors and tools will display or interpret the \r as something other than a line 

91 break. 

92 """ 

93 test_ok = True 

94 for file in files_to_test(): 

95 test_ok &= check_file_for_carriage_return(file) 

96 assert test_ok 

97 

98 

99def check_file_for_trailing_whitespace(file): 

100 test_ok = True 

101 with open(file, encoding=DEFAULT_FILE_ENCODING) as file_handle: 

102 for idx, line in enumerate(file_handle.readlines()): 

103 if " \n" in line: 

104 test_ok = False 

105 print(f"Trailing whitespace on line {idx + 1} in {file}") 

106 return test_ok 

107 

108 

109def test_no_checked_in_files_contain_trailing_whitespace(): 

110 """ 

111 Trailing whitespace is not allowed. Some motivation here: 

112 https://softwareengineering.stackexchange.com/questions/121555/ 

113 """ 

114 test_ok = True 

115 for file in files_to_test(): 

116 test_ok &= check_file_for_trailing_whitespace(file) 

117 assert test_ok 

118 

119 

120def check_file_for_line_length(file_path, max_length=100): 

121 max_length_with_newline = max_length + 1 

122 test_ok = True 

123 

124 with open(file_path, encoding=DEFAULT_FILE_ENCODING) as file_handle: 

125 lines = file_handle.readlines() 

126 for line_number, line in enumerate(lines): 

127 line_length = len(line) 

128 if line_length > max_length_with_newline: 

129 print( 

130 f"Line {file_path}:{line_number + 1} is too long " 

131 f"({line_length - 1} > {max_length_with_newline - 1})" 

132 ) 

133 test_ok = False 

134 

135 return test_ok 

136 

137 

138def test_no_checked_in_files_have_too_long_lines(): 

139 test_ok = True 

140 excludes = [ 

141 # YAML format seems hard to break lines in 

142 REPO_ROOT / ".gitlab-ci.yml", 

143 # We list the license text exactly as the original, with no line breaks 

144 REPO_ROOT / "license.rst", 

145 # Impossible RST syntax to break 

146 TSFPGA_DOC / "release_notes" / "5.0.0.rst", 

147 TSFPGA_DOC / "sphinx" / "hdl_modules.rst", 

148 TSFPGA_DOC / "sphinx" / "hdl_registers.rst", 

149 TSFPGA_DOC / "sphinx" / "simulation.rst", 

150 # Impossible dockerfile syntax to break 

151 REPO_ROOT / "docker" / "ci" / "Dockerfile", 

152 # Impossible TCL syntax to break 

153 TSFPGA_TCL / "check_timing.tcl", 

154 # From Vivado, not modified by us 

155 TSFPGA_EXAMPLE_MODULES / "artyz7" / "tcl" / "block_design.tcl", 

156 ] 

157 for file_path in files_to_test(exclude_directories=excludes): 

158 test_ok &= check_file_for_line_length(file_path=file_path) 

159 

160 assert test_ok 

161 

162 

163def files_to_test(exclude_directories=None): 

164 # Do not test binary image files 

165 return find_git_files( 

166 directory=REPO_ROOT, 

167 exclude_directories=exclude_directories, 

168 file_endings_avoid=("png", "svg"), 

169 ) 

170 

171 

172def test_open_file_with_encoding_should_raise_exception_on_bad_file(tmp_path): 

173 """ 

174 Sanity check that the function we use actually triggers on bad files. 

175 """ 

176 file = tmp_path / "temp_file_for_test.txt" 

177 with open(file, "w", encoding="utf-8") as file_handle: 

178 # Swedish word for island = non-ASCII character 

179 data = "\N{LATIN CAPITAL LETTER O WITH DIAERESIS}" 

180 file_handle.write(data) 

181 

182 with pytest.raises(UnicodeDecodeError): 

183 open_file_with_encoding(file) 

184 

185 

186def test_check_file_for_tab_character_should_fail_on_bad_file(tmp_path): 

187 """ 

188 Sanity check that the function we use actually triggers on bad files. 

189 """ 

190 data = "Apa\thest" 

191 file = create_file(tmp_path / "temp_file_for_test.txt", data) 

192 assert not check_file_for_tab_character(file) 

193 

194 

195def test_check_file_for_carriage_return_should_fail_on_bad_file(tmp_path): 

196 """ 

197 Sanity check that the function we use actually triggers on bad files. 

198 """ 

199 file = tmp_path / "temp_file_for_test.txt" 

200 data = b"Apa\r\nhest" 

201 with file.open("wb") as file_handle: 

202 file_handle.write(data) 

203 assert not check_file_for_carriage_return(file) 

204 

205 

206def test_check_file_for_trailing_whitespace(tmp_path): 

207 """ 

208 Sanity check that the function we use actually triggers on bad files. 

209 """ 

210 data = "Apa \nhest \nzebra" 

211 file = create_file(tmp_path / "temp_file_for_test.txt", data) 

212 assert not check_file_for_trailing_whitespace(file) 

213 

214 

215def test_check_file_for_line_length(tmp_path): 

216 """ 

217 Sanity check that the function we use actually triggers on bad files. 

218 """ 

219 ok_data = """ 

220asdf 

221apa 

222hest 

223""" 

224 ok_file_path = create_file(tmp_path / "ok_data.txt", contents=ok_data) 

225 assert check_file_for_line_length(ok_file_path) 

226 

227 bad_data = """ 

228asdf 

229apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa 

230hest 

231""" 

232 bad_file_path = create_file(tmp_path / "bad_data.txt", contents=bad_data) 

233 assert not check_file_for_line_length(file_path=bad_file_path, max_length=80)