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

105 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-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 

9import re 

10 

11import pytest 

12 

13import tsfpga 

14from tsfpga.system_utils import create_file, read_file 

15from tsfpga.git_utils import find_git_files 

16 

17 

18class CopyrightHeader: 

19 

20 separator_line_length = 100 

21 

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

23 self._file = file 

24 self.comment_character = self._get_comment_character() 

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

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

27 ) 

28 self.expected_copyright_header = self._get_expected_copyright_header( 

29 copyright_holder, copyright_text_lines 

30 ) 

31 

32 def check_file(self): 

33 """ 

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

35 comment. 

36 """ 

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

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

39 data = read_file(self._file) 

40 return regexp.match(data) is not None 

41 

42 def fix_file(self): 

43 if self._is_suitable_for_insertion(): 

44 self._insert_copyright_header() 

45 else: 

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

47 

48 def _get_expected_copyright_header(self, copyright_holder, copyright_text_lines): 

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

50 header += ( 

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

52 ) 

53 if copyright_text_lines: 

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

55 for copyright_text_line in copyright_text_lines: 

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

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

58 return header 

59 

60 def _get_comment_character(self): 

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

62 return "#" 

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

64 return "--" 

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

66 return "#" 

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

68 return "//" 

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

70 

71 def _is_suitable_for_insertion(self): 

72 """ 

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

74 header comment. 

75 """ 

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

77 

78 def _insert_copyright_header(self): 

79 data = read_file(self._file) 

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

81 create_file(self._file, data) 

82 

83 

84def files_to_check_for_copyright_header(): 

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

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

87 for file_ending in file_endings: 

88 for file in find_git_files( 

89 directory=tsfpga.REPO_ROOT, 

90 exclude_directories=exclude_directories, 

91 file_endings_include=file_ending, 

92 ): 

93 yield file 

94 

95 

96def test_copyright_header_of_all_checked_in_files(): 

97 test_ok = True 

98 for file in files_to_check_for_copyright_header(): 

99 copyright_lines = [ 

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

101 "FPGA development.", 

102 "https://tsfpga.com", 

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

104 ] 

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

106 

107 if not copyright_header_checker.check_file(): 

108 test_ok = False 

109 print( 

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

111 ) 

112 

113 assert test_ok 

114 

115 

116def test_check_file(tmp_path): 

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

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

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

120 

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

122 copyright_header = CopyrightHeader(file, "Apa") 

123 assert copyright_header.check_file() 

124 

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

126 copyright_header = CopyrightHeader(file, "Apa") 

127 assert not copyright_header.check_file() 

128 

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

130 copyright_header = CopyrightHeader(file, "Apa") 

131 assert copyright_header.check_file() 

132 

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

134 copyright_header = CopyrightHeader(file, "Apa") 

135 assert copyright_header.check_file() 

136 

137 

138def test_check_file_with_copyright_text(tmp_path): 

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

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

141 header += "--\n" 

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

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

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

145 

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

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

148 assert copyright_header.check_file() 

149 

150 

151def test_fix_file_comment_insertion(tmp_path): 

152 data = "Apa\n" 

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

154 

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

156 copyright_header.fix_file() 

157 

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

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

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

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

162 assert data[3] == "" 

163 assert data[4] == "Apa" 

164 

165 

166def test_fix_file_should_not_run_on_dirty_file(tmp_path): 

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

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

169 copyright_header = CopyrightHeader(file, "A") 

170 

171 with pytest.raises(ValueError) as exception_info: 

172 copyright_header.fix_file() 

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