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