summaryrefslogtreecommitdiffstats
path: root/meta/lib/patchtest/tests/test_metadata.py
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/patchtest/tests/test_metadata.py')
-rw-r--r--meta/lib/patchtest/tests/test_metadata.py212
1 files changed, 212 insertions, 0 deletions
diff --git a/meta/lib/patchtest/tests/test_metadata.py b/meta/lib/patchtest/tests/test_metadata.py
new file mode 100644
index 0000000000..2dee80b002
--- /dev/null
+++ b/meta/lib/patchtest/tests/test_metadata.py
@@ -0,0 +1,212 @@
1# Checks related to the patch's LIC_FILES_CHKSUM metadata variable
2#
3# Copyright (C) 2016 Intel Corporation
4#
5# SPDX-License-Identifier: GPL-2.0-only
6
7import base
8import collections
9import os
10import patchtest_patterns
11import pyparsing
12from patchtest_parser import PatchtestParser
13
14# Data store commonly used to share values between pre and post-merge tests
15PatchTestDataStore = collections.defaultdict(str)
16
17class TestMetadata(base.Metadata):
18
19 def test_license_presence(self):
20 if not self.added:
21 self.skip('No added recipes, skipping test')
22
23 # TODO: this is a workaround so we can parse the recipe not
24 # containing the LICENSE var: add some default license instead
25 # of INVALID into auto.conf, then remove this line at the end
26 auto_conf = os.path.join(os.environ.get('BUILDDIR'), 'conf', 'auto.conf')
27 open_flag = 'w'
28 if os.path.exists(auto_conf):
29 open_flag = 'a'
30 with open(auto_conf, open_flag) as fd:
31 for pn in self.added:
32 fd.write('LICENSE ??= "%s"\n' % patchtest_patterns.invalid_license)
33
34 no_license = False
35 for pn in self.added:
36 rd = self.tinfoil.parse_recipe(pn)
37 license = rd.getVar(patchtest_patterns.metadata_lic)
38 if license == patchtest_patterns.invalid_license:
39 no_license = True
40 break
41
42 # remove auto.conf line or the file itself
43 if open_flag == 'w':
44 os.remove(auto_conf)
45 else:
46 fd = open(auto_conf, 'r')
47 lines = fd.readlines()
48 fd.close()
49 with open(auto_conf, 'w') as fd:
50 fd.write(''.join(lines[:-1]))
51
52 if no_license:
53 self.fail('Recipe does not have the LICENSE field set.')
54
55 def test_lic_files_chksum_presence(self):
56 if not self.added:
57 self.skip('No added recipes, skipping test')
58
59 for pn in self.added:
60 rd = self.tinfoil.parse_recipe(pn)
61 pathname = rd.getVar('FILE')
62 # we are not interested in images
63 if '/images/' in pathname:
64 continue
65 lic_files_chksum = rd.getVar(patchtest_patterns.metadata_chksum)
66 if rd.getVar(patchtest_patterns.license_var) == patchtest_patterns.closed:
67 continue
68 if not lic_files_chksum:
69 self.fail(
70 "%s is missing in newly added recipe" % patchtest_patterns.metadata_chksum
71 )
72
73 def test_lic_files_chksum_modified_not_mentioned(self):
74 if not self.modified:
75 self.skip('No modified recipes, skipping test')
76
77 for patch in self.patchset:
78 # for the moment, we are just interested in metadata
79 if patch.path.endswith('.patch'):
80 continue
81 payload = str(patch)
82 if patchtest_patterns.lic_chksum_added.search_string(
83 payload
84 ) or patchtest_patterns.lic_chksum_removed.search_string(payload):
85 # if any patch on the series contain reference on the metadata, fail
86 for commit in self.commits:
87 if patchtest_patterns.lictag_re.search_string(commit.commit_message):
88 break
89 else:
90 self.fail('LIC_FILES_CHKSUM changed without "License-Update:" tag and description in commit message')
91
92 def test_max_line_length(self):
93 for patch in self.patchset:
94 # for the moment, we are just interested in metadata
95 if patch.path.endswith('.patch'):
96 continue
97 payload = str(patch)
98 for line in payload.splitlines():
99 if patchtest_patterns.add_mark.search_string(line):
100 current_line_length = len(line[1:])
101 if current_line_length > patchtest_patterns.patch_max_line_length:
102 self.fail(
103 "Patch line too long (current length %s, maximum is %s)"
104 % (current_line_length, patchtest_patterns.patch_max_line_length),
105 data=[
106 ("Patch", patch.path),
107 ("Line", "%s ..." % line[0:80]),
108 ],
109 )
110
111 def pretest_src_uri_left_files(self):
112 # these tests just make sense on patches that can be merged
113 if not PatchtestParser.repo.canbemerged:
114 self.skip("Patch cannot be merged")
115 if not self.modified:
116 self.skip('No modified recipes, skipping pretest')
117
118 # get the proper metadata values
119 for pn in self.modified:
120 # we are not interested in images
121 if 'core-image' in pn:
122 continue
123 rd = self.tinfoil.parse_recipe(pn)
124 PatchTestDataStore[
125 "%s-%s-%s" % (self.shortid(), patchtest_patterns.metadata_src_uri, pn)
126 ] = rd.getVar(patchtest_patterns.metadata_src_uri)
127
128 def test_src_uri_left_files(self):
129 # these tests just make sense on patches that can be merged
130 if not PatchtestParser.repo.canbemerged:
131 self.skip("Patch cannot be merged")
132 if not self.modified:
133 self.skip('No modified recipes, skipping pretest')
134
135 # get the proper metadata values
136 for pn in self.modified:
137 # we are not interested in images
138 if 'core-image' in pn:
139 continue
140 rd = self.tinfoil.parse_recipe(pn)
141 PatchTestDataStore[
142 "%s-%s-%s" % (self.shortid(), patchtest_patterns.metadata_src_uri, pn)
143 ] = rd.getVar(patchtest_patterns.metadata_src_uri)
144
145 for pn in self.modified:
146 pretest_src_uri = PatchTestDataStore[
147 "pre%s-%s-%s" % (self.shortid(), patchtest_patterns.metadata_src_uri, pn)
148 ].split()
149 test_src_uri = PatchTestDataStore[
150 "%s-%s-%s" % (self.shortid(), patchtest_patterns.metadata_src_uri, pn)
151 ].split()
152
153 pretest_files = set([os.path.basename(patch) for patch in pretest_src_uri if patch.startswith('file://')])
154 test_files = set([os.path.basename(patch) for patch in test_src_uri if patch.startswith('file://')])
155
156 # check if files were removed
157 if len(test_files) < len(pretest_files):
158
159 # get removals from patchset
160 filesremoved_from_patchset = set()
161 for patch in self.patchset:
162 if patch.is_removed_file:
163 filesremoved_from_patchset.add(os.path.basename(patch.path))
164
165 # get the deleted files from the SRC_URI
166 filesremoved_from_usr_uri = pretest_files - test_files
167
168 # finally, get those patches removed at SRC_URI and not removed from the patchset
169 # TODO: we are not taking into account renames, so test may raise false positives
170 not_removed = filesremoved_from_usr_uri - filesremoved_from_patchset
171 if not_removed:
172 self.fail('Patches not removed from tree. Remove them and amend the submitted mbox',
173 data=[('Patch', f) for f in not_removed])
174
175 def test_summary_presence(self):
176 if not self.added:
177 self.skip('No added recipes, skipping test')
178
179 for pn in self.added:
180 # we are not interested in images
181 if 'core-image' in pn:
182 continue
183 rd = self.tinfoil.parse_recipe(pn)
184 summary = rd.getVar(patchtest_patterns.metadata_summary)
185
186 # "${PN} version ${PN}-${PR}" is the default, so fail if default
187 if summary.startswith("%s version" % pn):
188 self.fail(
189 "%s is missing in newly added recipe" % patchtest_patterns.metadata_summary
190 )
191
192 def test_cve_check_ignore(self):
193 # Skip if we neither modified a recipe or target branches are not
194 # Nanbield and newer. CVE_CHECK_IGNORE was first deprecated in Nanbield.
195 if (
196 not self.modified
197 or PatchtestParser.repo.patch.branch == "kirkstone"
198 or PatchtestParser.repo.patch.branch == "dunfell"
199 ):
200 self.skip("No modified recipes or older target branch, skipping test")
201 for pn in self.modified:
202 # we are not interested in images
203 if 'core-image' in pn:
204 continue
205 rd = self.tinfoil.parse_recipe(pn)
206 cve_check_ignore = rd.getVar(patchtest_patterns.cve_check_ignore_var)
207
208 if cve_check_ignore is not None:
209 self.fail(
210 "%s is deprecated and should be replaced by %s"
211 % (patchtest_patterns.cve_check_ignore_var, patchtest_patterns.cve_status_var)
212 )