1#!/usr/bin/env python 2# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3# -*- coding: utf-8 -*- 4# -*- Mode: Python 5# 6# Author: Dodji Seketeli 7# 8# Based on some preliminary work from Chenxiong Qi, posted to 9# https://sourceware.org/ml/libabigail/2016-q3/msg00031.html. 10 11 12'''A local-only version of the fedabipkgdiff program 13 14This program behaves exactly like the fedabipkgdiff tool, except 15that it doesn't use the network (via the Koji client) to get Fedora 16packages. The Koji client is the interface that is used to access 17the Fedora build system to get Fedora binary packages. 18 19This program thus loads the fedabipkgdiff tool and overloads 20(replaces) its Koji Client session with a fake (also known as a 21mock) Koji client named MockClientSession that gets the packages 22locally. Packages are stored under the directory 23tests/data/test-fedabipkgdiff/packages. This program then invokes 24the fedabipkgdiff program just as if the user invoked it from the 25command line. 26 27This program is useful for tests that can be called from "make 28check" because those must be able to perform in environments where 29no network is avaible. 30 31Please note that the descriptions of the builds of each package are 32stored in at least three global variables below: packages, builds 33and rpms. 34 35Whenever a user wants to add a new build to the local set of builds 36locally available via MockClientSession, she must update these three 37variables. 38''' 39 40import os 41import tempfile 42import imp 43import six 44 45try: 46 from mock import patch 47except ImportError: 48 import sys 49 six.print_('mock is required to run tests. Please install before running' 50 ' tests.', file=sys.stderr) 51 sys.exit(1) 52 53ABIPKGDIFF = '@abs_top_builddir@/tools/abipkgdiff' 54FEDABIPKGDIFF = '@abs_top_srcdir@/tools/fedabipkgdiff' 55INPUT_DIR = '@abs_top_srcdir@/tests/data/test-fedabipkgdiff' 56OUTPUT_DIR = '@abs_top_builddir@/tests/output/test-fedabipkgdiff' 57TEST_TOPDIR = 'file://{0}'.format(INPUT_DIR) 58 59DOWNLOAD_CACHE_DIR_PREFIX=os.path.join(OUTPUT_DIR, 'download-cache') 60if not os.path.exists(DOWNLOAD_CACHE_DIR_PREFIX): 61 os.makedirs(DOWNLOAD_CACHE_DIR_PREFIX) 62DOWNLOAD_CACHE_DIR=tempfile.mkdtemp(dir=DOWNLOAD_CACHE_DIR_PREFIX) 63 64 65def get_download_dir(): 66 """Mock get_download_dir""" 67 if not os.path.exists(DOWNLOAD_CACHE_DIR): 68 os.makedirs(DOWNLOAD_CACHE_DIR) 69 return DOWNLOAD_CACHE_DIR 70 71 72# Import the fedabipkgdiff program file from the source directory. 73fedabipkgdiff_mod = imp.load_source('fedabipkgdiff', FEDABIPKGDIFF) 74 75 76# ----------------- Koji resource storage begins ------------------ 77# 78# List all necessary Koji resources for running tests within this test 79# module. Currently, packages, builds, and rpms are listed here, and their 80# relationship is maintained well. At the same time, all information including 81# the ID for each package, build and rpm is real and can be queried from Koji, 82# that is for convenience once developers need this. 83# 84# When additional packages, builds and rpms are required for test cases, here 85# is the right place to add them. Just think them as a super simple in-memory 86# database, and methods of MockClientSession knows well how to read them. 87 88packages = [ 89 {'id': 286, 'name': 'gnutls'}, 90 {'id': 612, 'name': 'dbus-glib'}, 91 {'id': 9041, 'name': 'nss-util'}, 92 {'id': 18494, 'name': 'vte291'}, 93 ] 94 95builds = [ 96 {'build_id': 428835, 'nvr': 'dbus-glib-0.100-4.fc20', 97 'name': 'dbus-glib', 'release': '4.fc20', 'version': '0.100', 98 'package_id': 612, 'package_name': 'dbus-glib', 'state': 1, 99 }, 100 {'build_id': 430720, 'nvr': 'dbus-glib-0.100.2-1.fc20', 101 'name': 'dbus-glib', 'release': '1.fc20', 'version': '0.100.2', 102 'package_id': 612, 'package_name': 'dbus-glib', 'state': 1, 103 }, 104 {'build_id': 442366, 'nvr': 'dbus-glib-0.100.2-2.fc20', 105 'name': 'dbus-glib', 'release': '2.fc20', 'version': '0.100.2', 106 'package_id': 612, 'package_name': 'dbus-glib', 'state': 1, 107 }, 108 {'build_id': 715478, 'nvr': 'dbus-glib-0.106-1.fc23', 109 'name': 'dbus-glib', 'release': '1.fc23', 'version': '0.106', 110 'package_id': 612, 'package_name': 'dbus-glib', 'state': 1, 111 }, 112 {'build_id': 648058, 'nvr': 'dbus-glib-0.104-3.fc23', 113 'name': 'dbus-glib', 'release': '3.fc23', 'version': '0.104', 114 'package_id': 612, 'package_name': 'dbus-glib', 'state': 1, 115 }, 116 {'build_id': 613769, 'nvr': 'dbus-glib-0.104-2.fc23', 117 'name': 'dbus-glib', 'release': '2.fc23', 'version': '0.104', 118 'package_id': 612, 'package_name': 'dbus-glib', 'state': 1, 119 }, 120 121 {'build_id': 160295, 'nvr': 'nss-util-3.12.6-1.fc14', 122 'name': 'nss-util', 'version': '3.12.6', 'release': '1.fc14', 123 'package_id': 9041, 'package_name': 'nss-util', 'state': 1, 124 }, 125 {'build_id': 767978, 'nvr': 'nss-util-3.24.0-2.0.fc25', 126 'name': 'nss-util', 'version': '3.24.0', 'release': '2.0.fc25', 127 'package_id': 9041, 'package_name': 'nss-util', 'state': 1, 128 }, 129 130 # builds of package gnutls 131 {'build_id': 767306, 'nvr': 'gnutls-3.4.12-1.fc23', 132 'name': 'gnutls', 'release': '1.fc23', 'version': '3.4.12', 133 'package_id': 286, 'package_name': 'gnutls', 'state': 1, 134 }, 135 {'build_id': 770965, 'nvr': 'gnutls-3.4.13-1.fc23', 136 'name': 'gnutls', 'release': '1.fc23', 'version': '3.4.13', 137 'package_id': 286, 'package_name': 'gnutls', 'state': 1, 138 }, 139 {'build_id': 649701, 'nvr': 'gnutls-3.4.2-1.fc23', 140 'name': 'gnutls', 'release': '1.fc23', 'version': '3.4.2', 141 'package_id': 286, 'package_name': 'gnutls', 'state': 1, 142 }, 143 144 # builds of package vte291 145 {'build_id': 600011, 'nvr': 'vte291-0.39.1-1.fc22', 146 'name': 'vte291', 'version': '0.39.1', 'release': '1.fc22', 147 'package_id': 18494, 'package_name': 'vte291', 'state': 1, 148 }, 149 {'build_id': 612610, 'nvr': 'vte291-0.39.90-1.fc22', 150 'name': 'vte291', 'version': '0.39.90', 'release': '1.fc22', 151 'package_id': 18494, 'package_name': 'vte291', 'state': 1, 152 }, 153 ] 154 155rpms = [ 156 {'build_id': 442366, 157 'name': 'dbus-glib', 'release': '2.fc20', 'version': '0.100.2', 158 'arch': 'x86_64', 'nvr': 'dbus-glib-0.100.2-2.fc20', 159 }, 160 {'build_id': 442366, 161 'name': 'dbus-glib-devel', 'release': '2.fc20', 'version': '0.100.2', 162 'arch': 'x86_64', 'nvr': 'dbus-glib-devel-0.100.2-2.fc20', 163 }, 164 {'build_id': 442366, 165 'name': 'dbus-glib-debuginfo', 'release': '2.fc20', 'version': '0.100.2', 166 'arch': 'x86_64', 'nvr': 'dbus-glib-debuginfo-0.100.2-2.fc20', 167 }, 168 {'build_id': 442366, 169 'name': 'dbus-glib-devel', 'release': '2.fc20', 'version': '0.100.2', 170 'arch': 'i686', 'nvr': 'dbus-glib-devel-0.100.2-2.fc20', 171 }, 172 {'build_id': 442366, 173 'name': 'dbus-glib-debuginfo', 'release': '2.fc20', 'version': '0.100.2', 174 'arch': 'i686', 'nvr': 'dbus-glib-debuginfo-0.100.2-2.fc20', 175 }, 176 {'build_id': 442366, 177 'name': 'dbus-glib', 'version': '0.100.2', 'release': '2.fc20', 178 'arch': 'i686', 'nvr': 'dbus-glib-0.100.2-2.fc20', 179 }, 180 181 {'build_id': 715478, 182 'name': 'dbus-glib-debuginfo', 'version': '0.106', 'release': '1.fc23', 183 'arch': 'i686', 'nvr': 'dbus-glib-debuginfo-0.106-1.fc23', 184 }, 185 {'build_id': 715478, 186 'name': 'dbus-glib', 'version': '0.106', 'release': '1.fc23', 187 'arch': 'i686', 'nvr': 'dbus-glib-0.106-1.fc23', 188 }, 189 {'build_id': 715478, 190 'name': 'dbus-glib-devel', 'version': '0.106', 'release': '1.fc23', 191 'arch': 'i686', 'nvr': 'dbus-glib-devel-0.106-1.fc23', 192 }, 193 {'build_id': 715478, 194 'name': 'dbus-glib', 'version': '0.106', 'release': '1.fc23', 195 'arch': 'x86_64', 'nvr': 'dbus-glib-0.106-1.fc23', 196 }, 197 {'build_id': 715478, 198 'name': 'dbus-glib-debuginfo', 'version': '0.106', 'release': '1.fc23', 199 'arch': 'x86_64', 'nvr': 'dbus-glib-debuginfo-0.106-1.fc23', 200 }, 201 {'build_id': 715478, 202 'name': 'dbus-glib-devel', 'release': '1.fc23', 'version': '0.106', 203 'arch': 'x86_64', 'nvr': 'dbus-glib-devel-0.106-1.fc23', 204 }, 205 206 # RPMs of build nss-util-3.12.6-1.fc14 207 {'build_id': 160295, 208 'name': 'nss-util', 'release': '1.fc14', 'version': '3.12.6', 209 'arch': 'x86_64', 'nvr': 'nss-util-3.12.6-1.fc14', 210 }, 211 {'build_id': 160295, 212 'name': 'nss-util-devel', 'release': '1.fc14', 'version': '3.12.6', 213 'arch': 'x86_64', 'nvr': 'nss-util-devel-3.12.6-1.fc14', 214 }, 215 {'build_id': 160295, 216 'name': 'nss-util-debuginfo', 'release': '1.fc14', 'version': '3.12.6', 217 'arch': 'x86_64', 'nvr': 'nss-util-debuginfo-3.12.6-1.fc14', 218 }, 219 220 # RPMs of build nss-util-3.24.0-2.0.fc25 221 {'build_id': 767978, 222 'name': 'nss-util-debuginfo', 'release': '2.0.fc25', 'version': '3.24.0', 223 'arch': 'x86_64', 'nvr': 'nss-util-debuginfo-3.24.0-2.0.fc25', 224 }, 225 {'build_id': 767978, 226 'name': 'nss-util', 'release': '2.0.fc25', 'version': '3.24.0', 227 'arch': 'x86_64', 'nvr': 'nss-util-3.24.0-2.0.fc25', 228 }, 229 {'build_id': 767978, 230 'name': 'nss-util-devel', 'release': '2.0.fc25', 'version': '3.24.0', 231 'arch': 'x86_64', 'nvr': 'nss-util-devel-3.24.0-2.0.fc25', 232 }, 233 # RPMs of build vte291-0.39.1-1.fc22 234 {'build_id': 600011, 235 'name': 'vte291', 'version': '0.39.1', 'release': '1.fc22', 236 'arch': 'x86_64', 'nvr': 'vte291-0.39.1-1.fc22', 237 }, 238 {'build_id': 600011, 239 'name': 'vte291-devel', 'version': '0.39.1', 'release': '1.fc22', 240 'arch': 'x86_64', 'nvr': 'vte291-0.39.1-1.fc22', 241 }, 242 {'build_id': 600011, 243 'name': 'vte291-debuginfo', 'version': '0.39.1', 'release': '1.fc22', 244 'arch': 'x86_64', 'nvr': 'vte291-0.39.1-1.fc22', 245 }, 246 247 # RPMs of build vte291-0.39.90-1.fc22 248 {'build_id': 612610, 249 'name': 'vte291', 'version': '0.39.90', 'release': '1.fc22', 250 'arch': 'x86_64', 'nvr': 'vte291-0.39.90-1.fc22', 251 }, 252 {'build_id': 612610, 253 'name': 'vte291-devel', 'version': '0.39.90', 'release': '1.fc22', 254 'arch': 'x86_64', 'nvr': 'vte291-0.39.90-1.fc22', 255 }, 256 {'build_id': 612610, 257 'name': 'vte291-debuginfo', 'version': '0.39.90', 'release': '1.fc22', 258 'arch': 'x86_64', 'nvr': 'vte291-0.39.90-1.fc22', 259 }, 260 ] 261 262# ----------------- End of Koji resource storage ------------------ 263 264 265class MockClientSession(object): 266 """Mock koji.ClientSession 267 268 This mock ClientSession aims to avoid touching a real Koji instance to 269 interact with XMLRPC APIs required by fedabipkgdiff. 270 271 For the tests within this module, methods do not necessarily to return 272 complete RPM and build information. So, if you need more additional 273 information, here is the right place to add them. 274 """ 275 276 def __init__(self, baseurl): 277 """Initialize a mock ClientSession 278 279 :param str baseurl: the URL to remote kojihub service. As of writing 280 this mock class, `baseurl` is not used at all, just keep it here if 281 it's useful in the future. 282 283 All mock methods have same signature as corresponding kojihub.* 284 methods, and type of parameters may be different and only satify 285 fedabipkgdiff requirement. 286 """ 287 self.baseurl = baseurl 288 289 def getPackage(self, name): 290 """Mock kojihub.getPackage 291 292 :param str name: name of package to find and return 293 :return: the found package 294 :rtype: dict 295 """ 296 assert isinstance(name, six.string_types) 297 298 def selector(package): 299 return package['name'] == name 300 301 return [p for p in packages if selector(p)][0] 302 303 def getBuild(self, build_id): 304 """Mock kojihub.getBuild 305 306 :param int build_id: ID of build to find and return 307 :return: the found build 308 :rtype: dict 309 """ 310 assert isinstance(build_id, int) 311 312 def selector(build): 313 return build['build_id'] == build_id 314 315 return [b for b in builds if selector(b)][0] 316 317 def listBuilds(self, packageID, state=None): 318 """Mock kojihub.listBuilds 319 320 :param int packageID: ID of package whose builds is found and returned 321 :param state: build state. If state is omitted, all builds of a package 322 are returned 323 :type state: int or None 324 """ 325 assert isinstance(packageID, int) 326 if state is not None: 327 assert isinstance(state, int) 328 329 def selector(build): 330 selected = build['package_id'] == packageID 331 if state is not None: 332 selected = selected and build['state'] == state 333 return selected 334 335 return filter(selector, builds) 336 337 def getRPM(self, rpminfo): 338 """Mock kojihub.getRPM 339 340 :param dict rpminfo: a mapping containing rpm information, at least, 341 it contains name, version, release, and arch. 342 """ 343 assert isinstance(rpminfo, dict) 344 345 def selector(rpm): 346 return rpm['name'] == rpminfo['name'] and \ 347 rpm['version'] == rpminfo['version'] and \ 348 rpm['release'] == rpminfo['release'] and \ 349 rpm['arch'] == rpminfo['arch'] 350 351 return [rpm for rpm in rpms if selector(rpm)][0] 352 353 def listRPMs(self, buildID, arches=None): 354 """Mock kojihub.listRPMs 355 356 :param int buildID: ID of build from which to list rpms 357 :param arches: to list rpms built for specific arches. If arches is 358 omitted, rpms of all arches will be listed. 359 :type arches: list, tuple, str, or None 360 :return: list of rpms 361 :rtype: list 362 """ 363 assert isinstance(buildID, int) 364 if arches is not None: 365 assert isinstance(arches, (tuple, list, six.string_types)) 366 367 if arches is not None and isinstance(arches, six.string_types): 368 arches = [arches] 369 370 def selector(rpm): 371 selected = rpm['build_id'] == buildID 372 if arches is not None: 373 selected = selected and rpm['arch'] in arches 374 return selected 375 376 return [rpm for rpm in rpms if selector(rpm)] 377 378 379@patch('koji.ClientSession', new=MockClientSession) 380@patch('fedabipkgdiff.DEFAULT_KOJI_TOPURL', new=TEST_TOPDIR) 381@patch('fedabipkgdiff.DEFAULT_ABIPKGDIFF', new=ABIPKGDIFF) 382@patch('fedabipkgdiff.get_download_dir', new=get_download_dir) 383def run_fedabipkgdiff(): 384 return fedabipkgdiff_mod.main() 385 386 387def do_main(): 388 run_fedabipkgdiff() 389 390 391if __name__ == '__main__': 392 do_main() 393