blob: b6f4456ad2eba53555837c988cc2160d52f55fd8 [file] [log] [blame]
Patrick Williamsac13d5f2023-11-24 18:59:46 -06001# 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
29 def test_license_presence(self):
30 if not self.added:
31 self.skip('No added recipes, skipping test')
32
33 # TODO: this is a workaround so we can parse the recipe not
34 # containing the LICENSE var: add some default license instead
35 # of INVALID into auto.conf, then remove this line at the end
36 auto_conf = os.path.join(os.environ.get('BUILDDIR'), 'conf', 'auto.conf')
37 open_flag = 'w'
38 if os.path.exists(auto_conf):
39 open_flag = 'a'
40 with open(auto_conf, open_flag) as fd:
41 for pn in self.added:
42 fd.write('LICENSE ??= "%s"\n' % self.invalid_license)
43
44 no_license = False
45 for pn in self.added:
46 rd = self.tinfoil.parse_recipe(pn)
47 license = rd.getVar(self.metadata_lic)
48 if license == self.invalid_license:
49 no_license = True
50 break
51
52 # remove auto.conf line or the file itself
53 if open_flag == 'w':
54 os.remove(auto_conf)
55 else:
56 fd = open(auto_conf, 'r')
57 lines = fd.readlines()
58 fd.close()
59 with open(auto_conf, 'w') as fd:
60 fd.write(''.join(lines[:-1]))
61
62 if no_license:
63 self.fail('Recipe does not have the LICENSE field set.')
64
65 def test_lic_files_chksum_presence(self):
66 if not self.added:
67 self.skip('No added recipes, skipping test')
68
69 for pn in self.added:
70 rd = self.tinfoil.parse_recipe(pn)
71 pathname = rd.getVar('FILE')
72 # we are not interested in images
73 if '/images/' in pathname:
74 continue
75 lic_files_chksum = rd.getVar(self.metadata_chksum)
76 if rd.getVar(self.license_var) == self.closed:
77 continue
78 if not lic_files_chksum:
79 self.fail('%s is missing in newly added recipe' % self.metadata_chksum)
80
81 def test_lic_files_chksum_modified_not_mentioned(self):
82 if not self.modified:
83 self.skip('No modified recipes, skipping test')
84
85 for patch in self.patchset:
86 # for the moment, we are just interested in metadata
87 if patch.path.endswith('.patch'):
88 continue
89 payload = str(patch)
90 if (self.lic_chksum_added.search_string(payload) or self.lic_chksum_removed.search_string(payload)):
91 # if any patch on the series contain reference on the metadata, fail
92 for commit in self.commits:
93 if self.lictag_re.search_string(commit.commit_message):
94 break
95 else:
96 self.fail('LIC_FILES_CHKSUM changed without "License-Update:" tag and description in commit message')
97
98 def test_max_line_length(self):
99 for patch in self.patchset:
100 # for the moment, we are just interested in metadata
101 if patch.path.endswith('.patch'):
102 continue
103 payload = str(patch)
104 for line in payload.splitlines():
105 if self.add_mark.search_string(line):
106 current_line_length = len(line[1:])
107 if current_line_length > self.max_length:
108 self.fail('Patch line too long (current length %s, maximum is %s)' % (current_line_length, self.max_length),
109 data=[('Patch', patch.path), ('Line', '%s ...' % line[0:80])])
110
111 def pretest_src_uri_left_files(self):
112 # these tests just make sense on patches that can be merged
113 if not PatchTestInput.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['%s-%s-%s' % (self.shortid(), self.metadata_src_uri, pn)] = rd.getVar(self.metadata_src_uri)
125
126 def test_src_uri_left_files(self):
127 # these tests just make sense on patches that can be merged
128 if not PatchTestInput.repo.canbemerged:
129 self.skip('Patch cannot be merged')
130 if not self.modified:
131 self.skip('No modified recipes, skipping pretest')
132
133 # get the proper metadata values
134 for pn in self.modified:
135 # we are not interested in images
136 if 'core-image' in pn:
137 continue
138 rd = self.tinfoil.parse_recipe(pn)
139 PatchTestDataStore['%s-%s-%s' % (self.shortid(), self.metadata_src_uri, pn)] = rd.getVar(self.metadata_src_uri)
140
141 for pn in self.modified:
142 pretest_src_uri = PatchTestDataStore['pre%s-%s-%s' % (self.shortid(), self.metadata_src_uri, pn)].split()
143 test_src_uri = PatchTestDataStore['%s-%s-%s' % (self.shortid(), self.metadata_src_uri, pn)].split()
144
145 pretest_files = set([os.path.basename(patch) for patch in pretest_src_uri if patch.startswith('file://')])
146 test_files = set([os.path.basename(patch) for patch in test_src_uri if patch.startswith('file://')])
147
148 # check if files were removed
149 if len(test_files) < len(pretest_files):
150
151 # get removals from patchset
152 filesremoved_from_patchset = set()
153 for patch in self.patchset:
154 if patch.is_removed_file:
155 filesremoved_from_patchset.add(os.path.basename(patch.path))
156
157 # get the deleted files from the SRC_URI
158 filesremoved_from_usr_uri = pretest_files - test_files
159
160 # finally, get those patches removed at SRC_URI and not removed from the patchset
161 # TODO: we are not taking into account renames, so test may raise false positives
162 not_removed = filesremoved_from_usr_uri - filesremoved_from_patchset
163 if not_removed:
164 self.fail('Patches not removed from tree. Remove them and amend the submitted mbox',
165 data=[('Patch', f) for f in not_removed])
166
167 def test_summary_presence(self):
168 if not self.added:
169 self.skip('No added recipes, skipping test')
170
171 for pn in self.added:
172 # we are not interested in images
173 if 'core-image' in pn:
174 continue
175 rd = self.tinfoil.parse_recipe(pn)
176 summary = rd.getVar(self.metadata_summary)
177
178 # "${PN} version ${PN}-${PR}" is the default, so fail if default
179 if summary.startswith('%s version' % pn):
180 self.fail('%s is missing in newly added recipe' % self.metadata_summary)