Coverage for tsfpga/test/lint/file_format_lint.py: 51%

90 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-07 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 

9# Standard libraries 

10from pathlib import Path 

11 

12# Third party libraries 

13import pytest 

14 

15# First party libraries 

16from tsfpga import DEFAULT_FILE_ENCODING 

17from tsfpga.system_utils import create_file 

18 

19 

20def open_file_with_encoding(file: Path) -> None: 

21 """ 

22 Try to open the ``file`` with ASCII encoding. 

23 If that fails, i.e. if it contains non-ASCII characters, print information and raise exception. 

24 """ 

25 try: 

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

27 file_handle.read() 

28 

29 except UnicodeDecodeError as exception: 

30 print(file) 

31 with open(file, encoding="utf8") as file_handle: 

32 lines = file_handle.readlines() 

33 

34 for line_idx, line in enumerate(lines): 

35 for character in line: 

36 try: 

37 character.encode("ascii") 

38 except UnicodeEncodeError: 

39 print(f"Character {character} on line {line_idx + 1} is not ASCII") 

40 

41 raise exception 

42 

43 

44def check_file_ends_with_newline(file: Path) -> bool: 

45 """ 

46 Return True if ``file`` ends with newline. 

47 """ 

48 test_ok = True 

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

50 file_data = file_handle.read() 

51 if len(file_data) != 0: 

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

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

54 test_ok = False 

55 

56 return test_ok 

57 

58 

59def check_file_for_tab_character(file: Path) -> bool: 

60 """ 

61 Return True of ``file`` does not contain any TAB character. 

62 """ 

63 test_ok = True 

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

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

66 if "\t" in line: 

67 test_ok = False 

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

69 return test_ok 

70 

71 

72def check_file_for_carriage_return(file: Path) -> bool: 

73 """ 

74 Return True if ``file`` does not contain any carriage return (CR). 

75 """ 

76 test_ok = True 

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

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

79 test_ok = False 

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

81 

82 return test_ok 

83 

84 

85def check_file_for_trailing_whitespace(file: Path) -> bool: 

86 """ 

87 Return True if ``file`` does not contain any trailing whitespace. 

88 """ 

89 test_ok = True 

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

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

92 if " \n" in line: 

93 test_ok = False 

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

95 

96 return test_ok 

97 

98 

99def check_file_for_line_length(file_path: Path, max_length: int = 100) -> bool: 

100 """ 

101 Return True if ``file`` does not contain any line that is longer than ``max_length`` characters. 

102 """ 

103 max_length_with_newline = max_length + 1 

104 test_ok = True 

105 

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

107 lines = file_handle.readlines() 

108 for line_number, line in enumerate(lines): 

109 line_length = len(line) 

110 if line_length > max_length_with_newline: 

111 print( 

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

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

114 ) 

115 test_ok = False 

116 

117 return test_ok 

118 

119 

120def test_open_file_with_encoding_should_raise_exception_on_bad_file(tmp_path: Path) -> None: 

121 """ 

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

123 """ 

124 file = tmp_path / "temp_file_for_test.txt" 

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

126 # Swedish word for island = non-ASCII character 

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

128 file_handle.write(data) 

129 

130 with pytest.raises(UnicodeDecodeError): 

131 open_file_with_encoding(file) 

132 

133 

134def test_check_file_for_tab_character_should_fail_on_bad_file(tmp_path: Path) -> None: 

135 """ 

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

137 """ 

138 data = "Apa\thest" 

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

140 assert not check_file_for_tab_character(file) 

141 

142 

143def test_check_file_for_carriage_return_should_fail_on_bad_file(tmp_path: Path) -> None: 

144 """ 

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

146 """ 

147 file = tmp_path / "temp_file_for_test.txt" 

148 data = b"Apa\r\nhest" 

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

150 file_handle.write(data) 

151 assert not check_file_for_carriage_return(file) 

152 

153 

154def test_check_file_for_trailing_whitespace(tmp_path: Path) -> None: 

155 """ 

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

157 """ 

158 data = "Apa \nhest \nzebra" 

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

160 assert not check_file_for_trailing_whitespace(file) 

161 

162 

163def test_check_file_for_line_length(tmp_path: Path) -> None: 

164 """ 

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

166 """ 

167 ok_data = """ 

168asdf 

169apa 

170hest 

171""" 

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

173 assert check_file_for_line_length(ok_file_path) 

174 

175 bad_data = """ 

176asdf 

177apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa apa 

178hest 

179""" 

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

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