summaryrefslogtreecommitdiffstats
path: root/meta/lib/patchtest/tests/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/patchtest/tests/base.py')
-rw-r--r--meta/lib/patchtest/tests/base.py252
1 files changed, 0 insertions, 252 deletions
diff --git a/meta/lib/patchtest/tests/base.py b/meta/lib/patchtest/tests/base.py
deleted file mode 100644
index 919ca136bb..0000000000
--- a/meta/lib/patchtest/tests/base.py
+++ /dev/null
@@ -1,252 +0,0 @@
1# Base class to be used by all test cases defined in the suite
2#
3# Copyright (C) 2016 Intel Corporation
4#
5# SPDX-License-Identifier: GPL-2.0-only
6
7import unittest
8import logging
9import json
10import unidiff
11from patchtest_parser import PatchtestParser
12import mailbox
13import patchtest_patterns
14import collections
15import sys
16import os
17import re
18
19logger = logging.getLogger("patchtest")
20debug = logger.debug
21info = logger.info
22warn = logger.warn
23error = logger.error
24
25Commit = collections.namedtuple(
26 "Commit", ["author", "subject", "commit_message", "shortlog", "payload"]
27)
28
29Commit = collections.namedtuple('Commit', ['author', 'subject', 'commit_message', 'shortlog', 'payload'])
30
31class PatchtestOEError(Exception):
32 """Exception for handling patchtest-oe errors"""
33 def __init__(self, message, exitcode=1):
34 super().__init__(message)
35 self.exitcode = exitcode
36
37class Base(unittest.TestCase):
38 # if unit test fails, fail message will throw at least the following JSON: {"id": <testid>}
39
40 @staticmethod
41 def msg_to_commit(msg):
42 payload = msg.get_payload()
43 return Commit(subject=msg['subject'].replace('\n', ' ').replace(' ', ' '),
44 author=msg.get('From'),
45 shortlog=Base.shortlog(msg['subject']),
46 commit_message=Base.commit_message(payload),
47 payload=payload)
48
49 @staticmethod
50 def commit_message(payload):
51 commit_message = payload.__str__()
52 match = patchtest_patterns.endcommit_messages_regex.search(payload)
53 if match:
54 commit_message = payload[:match.start()]
55 return commit_message
56
57 @staticmethod
58 def shortlog(shlog):
59 # remove possible prefix (between brackets) before colon
60 start = shlog.find(']', 0, shlog.find(':'))
61 # remove also newlines and spaces at both sides
62 return shlog[start + 1:].replace('\n', '').strip()
63
64 @classmethod
65 def setUpClass(cls):
66
67 # General objects: mailbox.mbox and patchset
68 cls.mbox = mailbox.mbox(PatchtestParser.repo.patch.path)
69
70 # Patch may be malformed, so try parsing it
71 cls.unidiff_parse_error = ''
72 cls.patchset = None
73 try:
74 cls.patchset = unidiff.PatchSet.from_filename(
75 PatchtestParser.repo.patch.path, encoding="UTF-8"
76 )
77 except unidiff.UnidiffParseError as upe:
78 cls.patchset = []
79 cls.unidiff_parse_error = str(upe)
80
81 # Easy to iterate list of commits
82 cls.commits = []
83 for msg in cls.mbox:
84 if msg['subject'] and msg.get_payload():
85 cls.commits.append(Base.msg_to_commit(msg))
86
87 cls.setUpClassLocal()
88
89 @classmethod
90 def tearDownClass(cls):
91 cls.tearDownClassLocal()
92
93 @classmethod
94 def setUpClassLocal(cls):
95 pass
96
97 @classmethod
98 def tearDownClassLocal(cls):
99 pass
100
101 def fail(self, issue, fix=None, commit=None, data=None):
102 """ Convert to a JSON string failure data"""
103 value = {'id': self.id(),
104 'issue': issue}
105
106 if fix:
107 value['fix'] = fix
108 if commit:
109 value['commit'] = {'subject': commit.subject,
110 'shortlog': commit.shortlog}
111
112 # extend return value with other useful info
113 if data:
114 value['data'] = data
115
116 return super(Base, self).fail(json.dumps(value))
117
118 def skip(self, issue, data=None):
119 """ Convert the skip string to JSON"""
120 value = {'id': self.id(),
121 'issue': issue}
122
123 # extend return value with other useful info
124 if data:
125 value['data'] = data
126
127 return super(Base, self).skipTest(json.dumps(value))
128
129 def shortid(self):
130 return self.id().split('.')[-1]
131
132 def __str__(self):
133 return json.dumps({'id': self.id()})
134
135class Metadata(Base):
136 @classmethod
137 def setUpClassLocal(cls):
138 cls.tinfoil = cls.setup_tinfoil()
139
140 # get info about added/modified/remove recipes
141 cls.added, cls.modified, cls.removed = cls.get_metadata_stats(cls.patchset)
142
143 @classmethod
144 def tearDownClassLocal(cls):
145 cls.tinfoil.shutdown()
146
147 @classmethod
148 def setup_tinfoil(cls, config_only=False):
149 """Initialize tinfoil api from bitbake"""
150
151 # import relevant libraries
152 try:
153 scripts_path = os.path.join(PatchtestParser.repodir, "scripts", "lib")
154 if scripts_path not in sys.path:
155 sys.path.insert(0, scripts_path)
156 import scriptpath
157 scriptpath.add_bitbake_lib_path()
158 import bb.tinfoil
159 except ImportError:
160 raise PatchtestOEError('Could not import tinfoil module')
161
162 orig_cwd = os.path.abspath(os.curdir)
163
164 # Load tinfoil
165 tinfoil = None
166 try:
167 builddir = os.environ.get('BUILDDIR')
168 if not builddir:
169 logger.warn('Bitbake environment not loaded?')
170 return tinfoil
171 os.chdir(builddir)
172 tinfoil = bb.tinfoil.Tinfoil()
173 tinfoil.prepare(config_only=config_only)
174 except bb.tinfoil.TinfoilUIException as te:
175 if tinfoil:
176 tinfoil.shutdown()
177 raise PatchtestOEError('Could not prepare properly tinfoil (TinfoilUIException)')
178 except Exception as e:
179 if tinfoil:
180 tinfoil.shutdown()
181 raise e
182 finally:
183 os.chdir(orig_cwd)
184
185 return tinfoil
186
187 @classmethod
188 def get_metadata_stats(cls, patchset):
189 """Get lists of added, modified and removed metadata files"""
190
191 def find_pn(data, path):
192 """Find the PN from data"""
193 pn = None
194 pn_native = None
195 for _path, _pn in data:
196 if path in _path:
197 if 'native' in _pn:
198 # store the native PN but look for the non-native one first
199 pn_native = _pn
200 else:
201 pn = _pn
202 break
203 else:
204 # sent the native PN if found previously
205 if pn_native:
206 return pn_native
207
208 # on renames (usually upgrades), we need to check (FILE) base names
209 # because the unidiff library does not provided the new filename, just the modified one
210 # and tinfoil datastore, once the patch is merged, will contain the new filename
211 path_basename = path.split('_')[0]
212 for _path, _pn in data:
213 _path_basename = _path.split('_')[0]
214 if path_basename == _path_basename:
215 pn = _pn
216 return pn
217
218 if not cls.tinfoil:
219 cls.tinfoil = cls.setup_tinfoil()
220
221 added_paths, modified_paths, removed_paths = [], [], []
222 added, modified, removed = [], [], []
223
224 # get metadata filename additions, modification and removals
225 for patch in patchset:
226 if patch.path.endswith('.bb') or patch.path.endswith('.bbappend') or patch.path.endswith('.inc'):
227 if patch.is_added_file:
228 added_paths.append(
229 os.path.join(
230 os.path.abspath(PatchtestParser.repodir), patch.path
231 )
232 )
233 elif patch.is_modified_file:
234 modified_paths.append(
235 os.path.join(
236 os.path.abspath(PatchtestParser.repodir), patch.path
237 )
238 )
239 elif patch.is_removed_file:
240 removed_paths.append(
241 os.path.join(
242 os.path.abspath(PatchtestParser.repodir), patch.path
243 )
244 )
245
246 data = cls.tinfoil.cooker.recipecaches[''].pkg_fn.items()
247
248 added = [find_pn(data,path) for path in added_paths]
249 modified = [find_pn(data,path) for path in modified_paths]
250 removed = [find_pn(data,path) for path in removed_paths]
251
252 return [a for a in added if a], [m for m in modified if m], [r for r in removed if r]