Coverage for tsfpga/test/lint/test_copyright.py: 95%

105 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-01-31 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# Standard libraries 

10import re 

11 

12# Third party libraries 

13import pytest 

14 

15# First party libraries 

16import tsfpga 

17from tsfpga.git_utils import find_git_files 

18from tsfpga.system_utils import create_file, read_file 

19 

20 

21class CopyrightHeader: 

22 

23 separator_line_length = 100 

24 

25 def __init__(self, file, copyright_holder, copyright_text_lines=None): 

26 self._file = file 

27 self.comment_character = self._get_comment_character() 

28 self.separator_line = f"{self.comment_character} " + "-" * ( 

29 self.separator_line_length - 1 - len(self.comment_character) 

30 ) 

31 self.expected_copyright_header = self._get_expected_copyright_header( 

32 copyright_holder, copyright_text_lines 

33 ) 

34 

35 def check_file(self): 

36 """ 

37 Copyright comments should be correct. It should be followed by a blank line or another 

38 comment. 

39 """ 

40 copyright_header_re = self.expected_copyright_header.replace("(", "\\(").replace(")", "\\)") 

41 regexp = re.compile(copyright_header_re + rf"($|\n|{self.comment_character})") 

42 data = read_file(self._file) 

43 return regexp.match(data) is not None 

44 

45 def fix_file(self): 

46 if self._is_suitable_for_insertion(): 

47 self._insert_copyright_header() 

48 else: 

49 raise ValueError(f"Can not fix copyright header in file {self._file}") 

50 

51 def _get_expected_copyright_header(self, copyright_holder, copyright_text_lines): 

52 header = f"{self.separator_line}\n" 

53 header += ( 

54 f"{self.comment_character} Copyright (c) {copyright_holder}. All rights reserved.\n" 

55 ) 

56 if copyright_text_lines: 

57 header += f"{self.comment_character}\n" 

58 for copyright_text_line in copyright_text_lines: 

59 header += f"{self.comment_character} {copyright_text_line}\n" 

60 header += f"{self.separator_line}\n" 

61 return header 

62 

63 def _get_comment_character(self): 

64 if self._file.name.endswith(".py"): 

65 return "#" 

66 if self._file.name.endswith(".vhd"): 

67 return "--" 

68 if self._file.name.endswith((".xdc", ".tcl")): 

69 return "#" 

70 if self._file.name.endswith((".c", ".cpp", ".h", ".v")): 

71 return "//" 

72 raise RuntimeError(f"Could not decide file ending of {self._file}") 

73 

74 def _is_suitable_for_insertion(self): 

75 """ 

76 If the file does not begin with a comment, we consired it suitable to insert a new copyright 

77 header comment. 

78 """ 

79 return not read_file(self._file).startswith(self.comment_character) 

80 

81 def _insert_copyright_header(self): 

82 data = read_file(self._file) 

83 data = f"{self.expected_copyright_header}\n{data}" 

84 create_file(self._file, data) 

85 

86 

87def files_to_check_for_copyright_header(): 

88 file_endings = (".py", ".vhd", ".tcl") 

89 exclude_directories = [tsfpga.TSFPGA_EXAMPLE_MODULES / "artyz7" / "tcl"] 

90 for file_ending in file_endings: 

91 for file in find_git_files( 

92 directory=tsfpga.REPO_ROOT, 

93 exclude_directories=exclude_directories, 

94 file_endings_include=file_ending, 

95 ): 

96 yield file 

97 

98 

99def test_copyright_header_of_all_checked_in_files(): 

100 test_ok = True 

101 for file in files_to_check_for_copyright_header(): 

102 copyright_lines = [ 

103 "This file is part of the tsfpga project, a project platform for modern " 

104 "FPGA development.", 

105 "https://tsfpga.com", 

106 "https://gitlab.com/tsfpga/tsfpga", 

107 ] 

108 copyright_header_checker = CopyrightHeader(file, "Lukas Vik", copyright_lines) 

109 

110 if not copyright_header_checker.check_file(): 

111 test_ok = False 

112 print( 

113 f"Fail for {file}\nExpected:\n{copyright_header_checker.expected_copyright_header}" 

114 ) 

115 

116 assert test_ok 

117 

118 

119def test_check_file(tmp_path): 

120 header = "-- " + "-" * 97 + "\n" 

121 header += "-- Copyright (c) Apa. All rights reserved.\n" 

122 header += "-- " + "-" * 97 + "\n" 

123 

124 file = create_file(tmp_path / "header.vhd", header) 

125 copyright_header = CopyrightHeader(file, "Apa") 

126 assert copyright_header.check_file() 

127 

128 file = create_file(tmp_path / "non_comment.vhd", header + "non-comment on line after") 

129 copyright_header = CopyrightHeader(file, "Apa") 

130 assert not copyright_header.check_file() 

131 

132 file = create_file(tmp_path / "empty_line.vhd", header + "\nEmpty line and then non-comment") 

133 copyright_header = CopyrightHeader(file, "Apa") 

134 assert copyright_header.check_file() 

135 

136 file = create_file(tmp_path / "further_comment.vhd", header + "-- Further comment\n") 

137 copyright_header = CopyrightHeader(file, "Apa") 

138 assert copyright_header.check_file() 

139 

140 

141def test_check_file_with_copyright_text(tmp_path): 

142 header = "-- " + "-" * 97 + "\n" 

143 header += "-- Copyright (c) Apa. All rights reserved.\n" 

144 header += "--\n" 

145 header += "-- Some more\n" 

146 header += "-- text.\n" 

147 header += "-- " + "-" * 97 + "\n" 

148 

149 file = create_file(tmp_path / "header.vhd", header) 

150 copyright_header = CopyrightHeader(file, "Apa", ["Some more", "text."]) 

151 assert copyright_header.check_file() 

152 

153 

154def test_fix_file_comment_insertion(tmp_path): 

155 data = "Apa\n" 

156 file = create_file(tmp_path / "file_for_test.vhd", data) 

157 

158 copyright_header = CopyrightHeader(file, "Hest Hestsson") 

159 copyright_header.fix_file() 

160 

161 data = read_file(file).split("\n") 

162 assert data[0] == "-- " + "-" * 97 

163 assert data[1] == "-- Copyright (c) Hest Hestsson. All rights reserved." 

164 assert data[2] == "-- " + "-" * 97 

165 assert data[3] == "" 

166 assert data[4] == "Apa" 

167 

168 

169def test_fix_file_should_not_run_on_dirty_file(tmp_path): 

170 data = "-- Custom comment line\n\nApa\n" 

171 file = create_file(tmp_path / "file_for_test.vhd", data) 

172 copyright_header = CopyrightHeader(file, "A") 

173 

174 with pytest.raises(ValueError) as exception_info: 

175 copyright_header.fix_file() 

176 assert str(exception_info.value) == f"Can not fix copyright header in file {file}"