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

110 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-05-28 04:01 +0000

1# -------------------------------------------------------------------------------------------------- 

2# Copyright (c) Lukas Vik. All rights reserved. 

3# 

4# This file is part of the tsfpga project. 

5# https://tsfpga.com 

6# https://gitlab.com/tsfpga/tsfpga 

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

8 

9import pytest 

10 

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

12from tsfpga.git_utils import find_git_files 

13from tsfpga.system_utils import create_file 

14 

15 

16def open_file_with_encoding(file): 

17 print(file) 

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

19 file_handle.read() 

20 

21 

22def test_all_checked_in_files_are_properly_encoded(): 

23 """ 

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

25 should contain only ASCII characters. 

26 

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

28 """ 

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

30 open_file_with_encoding(file) 

31 

32 

33def check_file_ends_with_newline(file): 

34 test_ok = True 

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

36 file_data = file_handle.read() 

37 if len(file_data) != 0: 

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

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

40 test_ok = False 

41 return test_ok 

42 

43 

44def test_all_checked_in_files_end_with_newline(): 

45 """ 

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

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

48 """ 

49 test_ok = True 

50 for file in files_to_test(): 

51 test_ok &= check_file_ends_with_newline(file) 

52 assert test_ok 

53 

54 

55def check_file_for_tab_character(file): 

56 test_ok = True 

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

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

59 if "\t" in line: 

60 test_ok = False 

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

62 return test_ok 

63 

64 

65def test_no_checked_in_files_contain_tabs(): 

66 """ 

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

68 contain TAB characters. 

69 """ 

70 test_ok = True 

71 for file in files_to_test(): 

72 test_ok &= check_file_for_tab_character(file) 

73 assert test_ok 

74 

75 

76def check_file_for_carriage_return(file): 

77 test_ok = True 

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

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

80 test_ok = False 

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

82 return test_ok 

83 

84 

85def test_no_checked_in_files_contain_carriage_return(): 

86 """ 

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

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

89 break. 

90 """ 

91 test_ok = True 

92 for file in files_to_test(): 

93 test_ok &= check_file_for_carriage_return(file) 

94 assert test_ok 

95 

96 

97def check_file_for_trailing_whitespace(file): 

98 test_ok = True 

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

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

101 if " \n" in line: 

102 test_ok = False 

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

104 return test_ok 

105 

106 

107def test_no_checked_in_files_contain_trailing_whitespace(): 

108 """ 

109 Trailing whitespace is not allowed. Some motivation here: 

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

111 """ 

112 test_ok = True 

113 for file in files_to_test(): 

114 test_ok &= check_file_for_trailing_whitespace(file) 

115 assert test_ok 

116 

117 

118def check_file_for_line_length(file_path, max_length=100): 

119 max_length_with_newline = max_length + 1 

120 test_ok = True 

121 

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

123 lines = file_handle.readlines() 

124 for line_number, line in enumerate(lines): 

125 line_length = len(line) 

126 if line_length > max_length_with_newline: 

127 print( 

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

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

130 ) 

131 test_ok = False 

132 

133 return test_ok 

134 

135 

136def test_no_checked_in_files_have_too_long_lines(): 

137 test_ok = True 

138 excludes = [ 

139 # YAML format seems hard to break lines in 

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

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

142 REPO_ROOT / "license.rst", 

143 # Impossible RST syntax to break 

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

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

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

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

148 # Impossible dockerfile syntax to break 

149 REPO_ROOT / "docker" / "ci" / "Dockerfile", 

150 # Impossible TCL syntax to break 

151 TSFPGA_TCL / "check_timing.tcl", 

152 # From Vivado, not modified by us 

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

154 ] 

155 for file_path in files_to_test(exclude_directories=excludes): 

156 test_ok &= check_file_for_line_length(file_path=file_path) 

157 

158 assert test_ok 

159 

160 

161def files_to_test(exclude_directories=None): 

162 # Do not test binary image files 

163 return find_git_files( 

164 directory=REPO_ROOT, 

165 exclude_directories=exclude_directories, 

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

167 ) 

168 

169 

170def test_open_file_with_encoding_should_raise_exception_on_bad_file(tmp_path): 

171 """ 

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

173 """ 

174 file = tmp_path / "temp_file_for_test.txt" 

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

176 # Swedish word for island = non-ASCII character 

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

178 file_handle.write(data) 

179 

180 with pytest.raises(UnicodeDecodeError): 

181 open_file_with_encoding(file) 

182 

183 

184def test_check_file_for_tab_character_should_fail_on_bad_file(tmp_path): 

185 """ 

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

187 """ 

188 data = "Apa\thest" 

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

190 assert not check_file_for_tab_character(file) 

191 

192 

193def test_check_file_for_carriage_return_should_fail_on_bad_file(tmp_path): 

194 """ 

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

196 """ 

197 file = tmp_path / "temp_file_for_test.txt" 

198 data = b"Apa\r\nhest" 

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

200 file_handle.write(data) 

201 assert not check_file_for_carriage_return(file) 

202 

203 

204def test_check_file_for_trailing_whitespace(tmp_path): 

205 """ 

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

207 """ 

208 data = "Apa \nhest \nzebra" 

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

210 assert not check_file_for_trailing_whitespace(file) 

211 

212 

213def test_check_file_for_line_length(tmp_path): 

214 """ 

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

216 """ 

217 ok_data = """ 

218asdf 

219apa 

220hest 

221""" 

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

223 assert check_file_for_line_length(ok_file_path) 

224 

225 bad_data = """ 

226asdf 

227apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa 

228hest 

229""" 

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

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