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
« 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# --------------------------------------------------------------------------------------------------
9# Standard libraries
10import re
12# Third party libraries
13import pytest
15# First party libraries
16import tsfpga
17from tsfpga.git_utils import find_git_files
18from tsfpga.system_utils import create_file, read_file
21class CopyrightHeader:
23 separator_line_length = 100
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 )
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
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}")
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
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}")
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)
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)
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
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)
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 )
116 assert test_ok
119def test_check_file(tmp_path):
120 header = "-- " + "-" * 97 + "\n"
121 header += "-- Copyright (c) Apa. All rights reserved.\n"
122 header += "-- " + "-" * 97 + "\n"
124 file = create_file(tmp_path / "header.vhd", header)
125 copyright_header = CopyrightHeader(file, "Apa")
126 assert copyright_header.check_file()
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()
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()
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()
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"
149 file = create_file(tmp_path / "header.vhd", header)
150 copyright_header = CopyrightHeader(file, "Apa", ["Some more", "text."])
151 assert copyright_header.check_file()
154def test_fix_file_comment_insertion(tmp_path):
155 data = "Apa\n"
156 file = create_file(tmp_path / "file_for_test.vhd", data)
158 copyright_header = CopyrightHeader(file, "Hest Hestsson")
159 copyright_header.fix_file()
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"
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")
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}"