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