]> Git Repo - qemu.git/blob - scripts/device-crash-test
hw/display/bcm2835_fb: Silence Coverity warning about multiply overflow
[qemu.git] / scripts / device-crash-test
1 #!/usr/bin/env python
2 #
3 #  Copyright (c) 2017 Red Hat Inc
4 #
5 # Author:
6 #  Eduardo Habkost <[email protected]>
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License along
19 # with this program; if not, write to the Free Software Foundation, Inc.,
20 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22 """
23 Run QEMU with all combinations of -machine and -device types,
24 check for crashes and unexpected errors.
25 """
26 from __future__ import print_function
27
28 import sys
29 import os
30 import glob
31 import logging
32 import traceback
33 import re
34 import random
35 import argparse
36 from itertools import chain
37
38 from qemu import QEMUMachine
39
40 logger = logging.getLogger('device-crash-test')
41 dbg = logger.debug
42
43
44 # Purposes of the following whitelist:
45 # * Avoiding verbose log messages when we find known non-fatal
46 #   (exitcode=1) errors
47 # * Avoiding fatal errors when we find known crashes
48 # * Skipping machines/devices that are known not to work out of
49 #   the box, when running in --quick mode
50 #
51 # Keeping the whitelist updated is desirable, but not required,
52 # because unexpected cases where QEMU exits with exitcode=1 will
53 # just trigger a INFO message.
54
55 # Valid whitelist entry keys:
56 # * accel: regexp, full match only
57 # * machine: regexp, full match only
58 # * device: regexp, full match only
59 # * log: regexp, partial match allowed
60 # * exitcode: if not present, defaults to 1. If None, matches any exitcode
61 # * warn: if True, matching failures will be logged as warnings
62 # * expected: if True, QEMU is expected to always fail every time
63 #   when testing the corresponding test case
64 # * loglevel: log level of log output when there's a match.
65 ERROR_WHITELIST = [
66     # Machines that won't work out of the box:
67     #             MACHINE                         | ERROR MESSAGE
68     {'machine':'niagara', 'expected':True},       # Unable to load a firmware for -M niagara
69     {'machine':'boston', 'expected':True},        # Please provide either a -kernel or -bios argument
70     {'machine':'leon3_generic', 'expected':True}, # Can't read bios image (null)
71
72     # devices that don't work out of the box because they require extra options to "-device DEV":
73     #            DEVICE                                    | ERROR MESSAGE
74     {'device':'.*-(i386|x86_64)-cpu', 'expected':True},    # CPU socket-id is not set
75     {'device':'ARM,bitband-memory', 'expected':True},      # source-memory property not set
76     {'device':'arm.cortex-a9-global-timer', 'expected':True}, # a9_gtimer_realize: num-cpu must be between 1 and 4
77     {'device':'arm_mptimer', 'expected':True},             # num-cpu must be between 1 and 4
78     {'device':'armv7m', 'expected':True},                  # memory property was not set
79     {'device':'aspeed.scu', 'expected':True},              # Unknown silicon revision: 0x0
80     {'device':'aspeed.sdmc', 'expected':True},             # Unknown silicon revision: 0x0
81     {'device':'bcm2835-dma', 'expected':True},             # bcm2835_dma_realize: required dma-mr link not found: Property '.dma-mr' not found
82     {'device':'bcm2835-fb', 'expected':True},              # bcm2835_fb_realize: required vcram-base property not set
83     {'device':'bcm2835-mbox', 'expected':True},            # bcm2835_mbox_realize: required mbox-mr link not found: Property '.mbox-mr' not found
84     {'device':'bcm2835-peripherals', 'expected':True},     # bcm2835_peripherals_realize: required ram link not found: Property '.ram' not found
85     {'device':'bcm2835-property', 'expected':True},        # bcm2835_property_realize: required fb link not found: Property '.fb' not found
86     {'device':'bcm2835_gpio', 'expected':True},            # bcm2835_gpio_realize: required sdhci link not found: Property '.sdbus-sdhci' not found
87     {'device':'bcm2836', 'expected':True},                 # bcm2836_realize: required ram link not found: Property '.ram' not found
88     {'device':'cfi.pflash01', 'expected':True},            # attribute "sector-length" not specified or zero.
89     {'device':'cfi.pflash02', 'expected':True},            # attribute "sector-length" not specified or zero.
90     {'device':'icp', 'expected':True},                     # icp_realize: required link 'xics' not found: Property '.xics' not found
91     {'device':'ics', 'expected':True},                     # ics_base_realize: required link 'xics' not found: Property '.xics' not found
92     # "-device ide-cd" does work on more recent QEMU versions, so it doesn't have expected=True
93     {'device':'ide-cd'},                                 # No drive specified
94     {'device':'ide-drive', 'expected':True},               # No drive specified
95     {'device':'ide-hd', 'expected':True},                  # No drive specified
96     {'device':'ipmi-bmc-extern', 'expected':True},         # IPMI external bmc requires chardev attribute
97     {'device':'isa-debugcon', 'expected':True},            # Can't create serial device, empty char device
98     {'device':'isa-ipmi-bt', 'expected':True},             # IPMI device requires a bmc attribute to be set
99     {'device':'isa-ipmi-kcs', 'expected':True},            # IPMI device requires a bmc attribute to be set
100     {'device':'isa-parallel', 'expected':True},            # Can't create serial device, empty char device
101     {'device':'ivshmem', 'expected':True},                 # You must specify either 'shm' or 'chardev'
102     {'device':'ivshmem-doorbell', 'expected':True},        # You must specify a 'chardev'
103     {'device':'ivshmem-plain', 'expected':True},           # You must specify a 'memdev'
104     {'device':'loader', 'expected':True},                  # please include valid arguments
105     {'device':'nand', 'expected':True},                    # Unsupported NAND block size 0x1
106     {'device':'nvdimm', 'expected':True},                  # 'memdev' property is not set
107     {'device':'nvme', 'expected':True},                    # Device initialization failed
108     {'device':'pc-dimm', 'expected':True},                 # 'memdev' property is not set
109     {'device':'pci-bridge', 'expected':True},              # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
110     {'device':'pci-bridge-seat', 'expected':True},         # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
111     {'device':'pxa2xx-dma', 'expected':True},              # channels value invalid
112     {'device':'pxb', 'expected':True},                     # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0.
113     {'device':'scsi-block', 'expected':True},              # drive property not set
114     {'device':'scsi-disk', 'expected':True},               # drive property not set
115     {'device':'scsi-generic', 'expected':True},            # drive property not set
116     {'device':'scsi-hd', 'expected':True},                 # drive property not set
117     {'device':'spapr-pci-host-bridge', 'expected':True},   # BUID not specified for PHB
118     {'device':'spapr-rng', 'expected':True},               # spapr-rng needs an RNG backend!
119     {'device':'spapr-vty', 'expected':True},               # chardev property not set
120     {'device':'tpm-tis', 'expected':True},                 # tpm_tis: backend driver with id (null) could not be found
121     {'device':'unimplemented-device', 'expected':True},    # property 'size' not specified or zero
122     {'device':'usb-braille', 'expected':True},             # Property chardev is required
123     {'device':'usb-mtp', 'expected':True},                 # rootdir property must be configured
124     {'device':'usb-redir', 'expected':True},               # Parameter 'chardev' is missing
125     {'device':'usb-serial', 'expected':True},              # Property chardev is required
126     {'device':'usb-storage', 'expected':True},             # drive property not set
127     {'device':'vfio-amd-xgbe', 'expected':True},           # -device vfio-amd-xgbe: vfio error: wrong host device name
128     {'device':'vfio-calxeda-xgmac', 'expected':True},      # -device vfio-calxeda-xgmac: vfio error: wrong host device name
129     {'device':'vfio-pci', 'expected':True},                # No provided host device
130     {'device':'vfio-pci-igd-lpc-bridge', 'expected':True}, # VFIO dummy ISA/LPC bridge must have address 1f.0
131     {'device':'vhost-scsi.*', 'expected':True},            # vhost-scsi: missing wwpn
132     {'device':'vhost-vsock-device', 'expected':True},      # guest-cid property must be greater than 2
133     {'device':'vhost-vsock-pci', 'expected':True},         # guest-cid property must be greater than 2
134     {'device':'virtio-9p-ccw', 'expected':True},           # 9pfs device couldn't find fsdev with the id = NULL
135     {'device':'virtio-9p-device', 'expected':True},        # 9pfs device couldn't find fsdev with the id = NULL
136     {'device':'virtio-9p-pci', 'expected':True},           # 9pfs device couldn't find fsdev with the id = NULL
137     {'device':'virtio-blk-ccw', 'expected':True},          # drive property not set
138     {'device':'virtio-blk-device', 'expected':True},       # drive property not set
139     {'device':'virtio-blk-device', 'expected':True},       # drive property not set
140     {'device':'virtio-blk-pci', 'expected':True},          # drive property not set
141     {'device':'virtio-crypto-ccw', 'expected':True},       # 'cryptodev' parameter expects a valid object
142     {'device':'virtio-crypto-device', 'expected':True},    # 'cryptodev' parameter expects a valid object
143     {'device':'virtio-crypto-pci', 'expected':True},       # 'cryptodev' parameter expects a valid object
144     {'device':'virtio-input-host-device', 'expected':True}, # evdev property is required
145     {'device':'virtio-input-host-pci', 'expected':True},   # evdev property is required
146     {'device':'xen-pvdevice', 'expected':True},            # Device ID invalid, it must always be supplied
147     {'device':'vhost-vsock-ccw', 'expected':True},         # guest-cid property must be greater than 2
148     {'device':'ALTR.timer', 'expected':True},              # "clock-frequency" property must be provided
149     {'device':'zpci', 'expected':True},                    # target must be defined
150     {'device':'pnv-(occ|icp|lpc)', 'expected':True},       # required link 'xics' not found: Property '.xics' not found
151     {'device':'powernv-cpu-.*', 'expected':True},          # pnv_core_realize: required link 'xics' not found: Property '.xics' not found
152
153     # ioapic devices are already created by pc and will fail:
154     {'machine':'q35|pc.*', 'device':'kvm-ioapic', 'expected':True}, # Only 1 ioapics allowed
155     {'machine':'q35|pc.*', 'device':'ioapic', 'expected':True},     # Only 1 ioapics allowed
156
157     # "spapr-cpu-core needs a pseries machine"
158     {'machine':'(?!pseries).*', 'device':'.*-spapr-cpu-core', 'expected':True},
159
160     # KVM-specific devices shouldn't be tried without accel=kvm:
161     {'accel':'(?!kvm).*', 'device':'kvmclock', 'expected':True},
162
163     # xen-specific machines and devices:
164     {'accel':'(?!xen).*', 'machine':'xen.*', 'expected':True},
165     {'accel':'(?!xen).*', 'device':'xen-.*', 'expected':True},
166
167     # this fails on some machine-types, but not all, so they don't have expected=True:
168     {'device':'vmgenid'}, # vmgenid requires DMA write support in fw_cfg, which this machine type does not provide
169
170     # Silence INFO messages for errors that are common on multiple
171     # devices/machines:
172     {'log':r"No '[\w-]+' bus found for device '[\w-]+'"},
173     {'log':r"images* must be given with the 'pflash' parameter"},
174     {'log':r"(Guest|ROM|Flash|Kernel) image must be specified"},
175     {'log':r"[cC]ould not load [\w ]+ (BIOS|bios) '[\w-]+\.bin'"},
176     {'log':r"Couldn't find rom image '[\w-]+\.bin'"},
177     {'log':r"speed mismatch trying to attach usb device"},
178     {'log':r"Can't create a second ISA bus"},
179     {'log':r"duplicate fw_cfg file name"},
180     # sysbus-related error messages: most machines reject most dynamic sysbus devices:
181     {'log':r"Option '-device [\w.,-]+' cannot be handled by this machine"},
182     {'log':r"Device [\w.,-]+ is not supported by this machine yet"},
183     {'log':r"Device [\w.,-]+ can not be dynamically instantiated"},
184     {'log':r"Platform Bus: Can not fit MMIO region of size "},
185     # other more specific errors we will ignore:
186     {'device':'.*-spapr-cpu-core', 'log':r"CPU core type should be"},
187     {'log':r"MSI(-X)? is not supported by interrupt controller"},
188     {'log':r"pxb-pcie? devices cannot reside on a PCIe? bus"},
189     {'log':r"Ignoring smp_cpus value"},
190     {'log':r"sd_init failed: Drive 'sd0' is already in use because it has been automatically connected to another device"},
191     {'log':r"This CPU requires a smaller page size than the system is using"},
192     {'log':r"MSI-X support is mandatory in the S390 architecture"},
193     {'log':r"rom check and register reset failed"},
194     {'log':r"Unable to initialize GIC, CPUState for CPU#0 not valid"},
195     {'log':r"Multiple VT220 operator consoles are not supported"},
196     {'log':r"core 0 already populated"},
197     {'log':r"could not find stage1 bootloader"},
198
199     # other exitcode=1 failures not listed above will just generate INFO messages:
200     {'exitcode':1, 'loglevel':logging.INFO},
201
202     # KNOWN CRASHES:
203     # Known crashes will generate error messages, but won't be fatal.
204     # Those entries must be removed once we fix the crashes.
205     {'exitcode':-6, 'log':r"Device 'serial0' is in use", 'loglevel':logging.ERROR},
206     {'exitcode':-6, 'log':r"qemu_net_client_setup: Assertion `!peer->peer' failed", 'loglevel':logging.ERROR},
207     {'exitcode':-6, 'log':r'RAMBlock "[\w.-]+" already registered', 'loglevel':logging.ERROR},
208     {'exitcode':-6, 'log':r"find_ram_offset: Assertion `size != 0' failed.", 'loglevel':logging.ERROR},
209     {'exitcode':-6, 'log':r"add_cpreg_to_hashtable: code should not be reached", 'loglevel':logging.ERROR},
210     {'exitcode':-6, 'log':r"qemu_alloc_display: Assertion `surface->image != NULL' failed", 'loglevel':logging.ERROR},
211     {'exitcode':-6, 'log':r"Unexpected error in error_set_from_qdev_prop_error", 'loglevel':logging.ERROR},
212     {'exitcode':-6, 'log':r"Object .* is not an instance of type spapr-machine", 'loglevel':logging.ERROR},
213     {'exitcode':-6, 'log':r"Object .* is not an instance of type generic-pc-machine", 'loglevel':logging.ERROR},
214     {'exitcode':-6, 'log':r"Object .* is not an instance of type e500-ccsr", 'loglevel':logging.ERROR},
215     {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR},
216
217     # everything else (including SIGABRT and SIGSEGV) will be a fatal error:
218     {'exitcode':None, 'fatal':True, 'loglevel':logging.FATAL},
219 ]
220
221
222 def whitelistTestCaseMatch(wl, t):
223     """Check if a test case specification can match a whitelist entry
224
225     This only checks if a whitelist entry is a candidate match
226     for a given test case, it won't check if the test case
227     results/output match the entry.  See whitelistResultMatch().
228     """
229     return (('machine' not in wl or
230              'machine' not in t or
231              re.match(wl['machine'] + '$', t['machine'])) and
232             ('accel' not in wl or
233              'accel' not in t or
234              re.match(wl['accel'] + '$', t['accel'])) and
235             ('device' not in wl or
236              'device' not in t or
237              re.match(wl['device'] + '$', t['device'])))
238
239
240 def whitelistCandidates(t):
241     """Generate the list of candidates that can match a test case"""
242     for i, wl in enumerate(ERROR_WHITELIST):
243         if whitelistTestCaseMatch(wl, t):
244             yield (i, wl)
245
246
247 def findExpectedResult(t):
248     """Check if there's an expected=True whitelist entry for a test case
249
250     Returns (i, wl) tuple, where i is the index in
251     ERROR_WHITELIST and wl is the whitelist entry itself.
252     """
253     for i, wl in whitelistCandidates(t):
254         if wl.get('expected'):
255             return (i, wl)
256
257
258 def whitelistResultMatch(wl, r):
259     """Check if test case results/output match a whitelist entry
260
261     It is valid to call this function only if
262     whitelistTestCaseMatch() is True for the entry (e.g. on
263     entries returned by whitelistCandidates())
264     """
265     assert whitelistTestCaseMatch(wl, r['testcase'])
266     return ((wl.get('exitcode', 1) is None or
267              r['exitcode'] == wl.get('exitcode', 1)) and
268             ('log' not in wl or
269              re.search(wl['log'], r['log'], re.MULTILINE)))
270
271
272 def checkResultWhitelist(r):
273     """Look up whitelist entry for a given test case result
274
275     Returns (i, wl) tuple, where i is the index in
276     ERROR_WHITELIST and wl is the whitelist entry itself.
277     """
278     for i, wl in whitelistCandidates(r['testcase']):
279         if whitelistResultMatch(wl, r):
280             return i, wl
281
282     raise Exception("this should never happen")
283
284
285 def qemuOptsEscape(s):
286     """Escape option value QemuOpts"""
287     return s.replace(",", ",,")
288
289
290 def formatTestCase(t):
291     """Format test case info as "key=value key=value" for prettier logging output"""
292     return ' '.join('%s=%s' % (k, v) for k, v in t.items())
293
294
295 def qomListTypeNames(vm, **kwargs):
296     """Run qom-list-types QMP command, return type names"""
297     types = vm.command('qom-list-types', **kwargs)
298     return [t['name'] for t in types]
299
300
301 def infoQDM(vm):
302     """Parse 'info qdm' output"""
303     args = {'command-line': 'info qdm'}
304     devhelp = vm.command('human-monitor-command', **args)
305     for l in devhelp.split('\n'):
306         l = l.strip()
307         if l == '' or l.endswith(':'):
308             continue
309         d = {'name': re.search(r'name "([^"]+)"', l).group(1),
310              'no-user': (re.search(', no-user', l) is not None)}
311         yield d
312
313
314 class QemuBinaryInfo(object):
315     def __init__(self, binary, devtype):
316         if devtype is None:
317             devtype = 'device'
318
319         self.binary = binary
320         self._machine_info = {}
321
322         dbg("devtype: %r", devtype)
323         args = ['-S', '-machine', 'none,accel=kvm:tcg']
324         dbg("querying info for QEMU binary: %s", binary)
325         vm = QEMUMachine(binary=binary, args=args)
326         vm.launch()
327         try:
328             self.alldevs = set(qomListTypeNames(vm, implements=devtype, abstract=False))
329             # there's no way to query DeviceClass::user_creatable using QMP,
330             # so use 'info qdm':
331             self.no_user_devs = set([d['name'] for d in infoQDM(vm, ) if d['no-user']])
332             self.machines = list(m['name'] for m in vm.command('query-machines'))
333             self.user_devs = self.alldevs.difference(self.no_user_devs)
334             self.kvm_available = vm.command('query-kvm')['enabled']
335         finally:
336             vm.shutdown()
337
338     def machineInfo(self, machine):
339         """Query for information on a specific machine-type
340
341         Results are cached internally, in case the same machine-
342         type is queried multiple times.
343         """
344         if machine in self._machine_info:
345             return self._machine_info[machine]
346
347         mi = {}
348         args = ['-S', '-machine', '%s' % (machine)]
349         dbg("querying machine info for binary=%s machine=%s", self.binary, machine)
350         vm = QEMUMachine(binary=self.binary, args=args)
351         try:
352             vm.launch()
353             mi['runnable'] = True
354         except KeyboardInterrupt:
355             raise
356         except:
357             dbg("exception trying to run binary=%s machine=%s", self.binary, machine, exc_info=sys.exc_info())
358             dbg("log: %r", vm.get_log())
359             mi['runnable'] = False
360
361         vm.shutdown()
362         self._machine_info[machine] = mi
363         return mi
364
365
366 BINARY_INFO = {}
367
368
369 def getBinaryInfo(args, binary):
370     if binary not in BINARY_INFO:
371         BINARY_INFO[binary] = QemuBinaryInfo(binary, args.devtype)
372     return BINARY_INFO[binary]
373
374
375 def checkOneCase(args, testcase):
376     """Check one specific case
377
378     Returns a dictionary containing failure information on error,
379     or None on success
380     """
381     binary = testcase['binary']
382     accel = testcase['accel']
383     machine = testcase['machine']
384     device = testcase['device']
385
386     dbg("will test: %r", testcase)
387
388     args = ['-S', '-machine', '%s,accel=%s' % (machine, accel),
389             '-device', qemuOptsEscape(device)]
390     cmdline = ' '.join([binary] + args)
391     dbg("will launch QEMU: %s", cmdline)
392     vm = QEMUMachine(binary=binary, args=args)
393
394     exc_traceback = None
395     try:
396         vm.launch()
397     except KeyboardInterrupt:
398         raise
399     except:
400         exc_traceback = traceback.format_exc()
401         dbg("Exception while running test case")
402     finally:
403         vm.shutdown()
404         ec = vm.exitcode()
405         log = vm.get_log()
406
407     if exc_traceback is not None or ec != 0:
408         return {'exc_traceback':exc_traceback,
409                 'exitcode':ec,
410                 'log':log,
411                 'testcase':testcase,
412                 'cmdline':cmdline}
413
414
415 def binariesToTest(args, testcase):
416     if args.qemu:
417         r = args.qemu
418     else:
419         r = glob.glob('./*-softmmu/qemu-system-*')
420     return r
421
422
423 def accelsToTest(args, testcase):
424     if getBinaryInfo(args, testcase['binary']).kvm_available:
425         yield 'kvm'
426     yield 'tcg'
427
428
429 def machinesToTest(args, testcase):
430     return getBinaryInfo(args, testcase['binary']).machines
431
432
433 def devicesToTest(args, testcase):
434     return getBinaryInfo(args, testcase['binary']).user_devs
435
436
437 TESTCASE_VARIABLES = [
438     ('binary', binariesToTest),
439     ('accel', accelsToTest),
440     ('machine', machinesToTest),
441     ('device', devicesToTest),
442 ]
443
444
445 def genCases1(args, testcases, var, fn):
446     """Generate new testcases for one variable
447
448     If an existing item already has a variable set, don't
449     generate new items and just return it directly. This
450     allows the "-t" command-line option to be used to choose
451     a specific test case.
452     """
453     for testcase in testcases:
454         if var in testcase:
455             yield testcase.copy()
456         else:
457             for i in fn(args, testcase):
458                 t = testcase.copy()
459                 t[var] = i
460                 yield t
461
462
463 def genCases(args, testcase):
464     """Generate test cases for all variables
465     """
466     cases = [testcase.copy()]
467     for var, fn in TESTCASE_VARIABLES:
468         dbg("var: %r, fn: %r", var, fn)
469         cases = genCases1(args, cases, var, fn)
470     return cases
471
472
473 def casesToTest(args, testcase):
474     cases = genCases(args, testcase)
475     if args.random:
476         cases = list(cases)
477         cases = random.sample(cases, min(args.random, len(cases)))
478     if args.debug:
479         cases = list(cases)
480         dbg("%d test cases to test", len(cases))
481     if args.shuffle:
482         cases = list(cases)
483         random.shuffle(cases)
484     return cases
485
486
487 def logFailure(f, level):
488     t = f['testcase']
489     logger.log(level, "failed: %s", formatTestCase(t))
490     logger.log(level, "cmdline: %s", f['cmdline'])
491     for l in f['log'].strip().split('\n'):
492         logger.log(level, "log: %s", l)
493     logger.log(level, "exit code: %r", f['exitcode'])
494     if f['exc_traceback']:
495         logger.log(level, "exception:")
496         for l in f['exc_traceback'].split('\n'):
497             logger.log(level, "  %s", l.rstrip('\n'))
498
499
500 def main():
501     parser = argparse.ArgumentParser(description="QEMU -device crash test")
502     parser.add_argument('-t', metavar='KEY=VALUE', nargs='*',
503                         help="Limit test cases to KEY=VALUE",
504                         action='append', dest='testcases', default=[])
505     parser.add_argument('-d', '--debug', action='store_true',
506                         help='debug output')
507     parser.add_argument('-v', '--verbose', action='store_true', default=True,
508                         help='verbose output')
509     parser.add_argument('-q', '--quiet', dest='verbose', action='store_false',
510                         help='non-verbose output')
511     parser.add_argument('-r', '--random', type=int, metavar='COUNT',
512                         help='run a random sample of COUNT test cases',
513                         default=0)
514     parser.add_argument('--shuffle', action='store_true',
515                         help='Run test cases in random order')
516     parser.add_argument('--dry-run', action='store_true',
517                         help="Don't run any tests, just generate list")
518     parser.add_argument('-D', '--devtype', metavar='TYPE',
519                         help="Test only device types that implement TYPE")
520     parser.add_argument('-Q', '--quick', action='store_true', default=True,
521                         help="Quick mode: skip test cases that are expected to fail")
522     parser.add_argument('-F', '--full', action='store_false', dest='quick',
523                         help="Full mode: test cases that are expected to fail")
524     parser.add_argument('--strict', action='store_true', dest='strict',
525                         help="Treat all warnings as fatal")
526     parser.add_argument('qemu', nargs='*', metavar='QEMU',
527                         help='QEMU binary to run')
528     args = parser.parse_args()
529
530     if args.debug:
531         lvl = logging.DEBUG
532     elif args.verbose:
533         lvl = logging.INFO
534     else:
535         lvl = logging.WARN
536     logging.basicConfig(stream=sys.stdout, level=lvl, format='%(levelname)s: %(message)s')
537
538     fatal_failures = []
539     wl_stats = {}
540     skipped = 0
541     total = 0
542
543     tc = {}
544     dbg("testcases: %r", args.testcases)
545     if args.testcases:
546         for t in chain(*args.testcases):
547             for kv in t.split():
548                 k, v = kv.split('=', 1)
549                 tc[k] = v
550
551     if len(binariesToTest(args, tc)) == 0:
552         print("No QEMU binary found", file=sys.stderr)
553         parser.print_usage(sys.stderr)
554         return 1
555
556     for t in casesToTest(args, tc):
557         logger.info("running test case: %s", formatTestCase(t))
558         total += 1
559
560         expected_match = findExpectedResult(t)
561         if (args.quick and
562                 (expected_match or
563                  not getBinaryInfo(args, t['binary']).machineInfo(t['machine'])['runnable'])):
564             dbg("skipped: %s", formatTestCase(t))
565             skipped += 1
566             continue
567
568         if args.dry_run:
569             continue
570
571         try:
572             f = checkOneCase(args, t)
573         except KeyboardInterrupt:
574             break
575
576         if f:
577             i, wl = checkResultWhitelist(f)
578             dbg("testcase: %r, whitelist match: %r", t, wl)
579             wl_stats.setdefault(i, []).append(f)
580             level = wl.get('loglevel', logging.DEBUG)
581             logFailure(f, level)
582             if wl.get('fatal') or (args.strict and level >= logging.WARN):
583                 fatal_failures.append(f)
584         else:
585             dbg("success: %s", formatTestCase(t))
586             if expected_match:
587                 logger.warn("Didn't fail as expected: %s", formatTestCase(t))
588
589     logger.info("Total: %d test cases", total)
590     if skipped:
591         logger.info("Skipped %d test cases", skipped)
592
593     if args.debug:
594         stats = sorted([(len(wl_stats.get(i, [])), wl) for i, wl in enumerate(ERROR_WHITELIST)])
595         for count, wl in stats:
596             dbg("whitelist entry stats: %d: %r", count, wl)
597
598     if fatal_failures:
599         for f in fatal_failures:
600             t = f['testcase']
601             logger.error("Fatal failure: %s", formatTestCase(t))
602         logger.error("Fatal failures on some machine/device combinations")
603         return 1
604
605 if __name__ == '__main__':
606     sys.exit(main())
This page took 0.059366 seconds and 4 git commands to generate.