3 from __future__ import print_function
19 from collections import deque
20 from threading import Thread, Event
21 from inspect import getdoc, isclass
22 from traceback import format_exception
23 from logging import FileHandler, DEBUG, Formatter
27 from scapy.packet import Raw
28 import hook as hookmodule
29 from vpp_pg_interface import VppPGInterface
30 from vpp_sub_interface import VppSubInterface
31 from vpp_lo_interface import VppLoInterface
32 from vpp_bvi_interface import VppBviInterface
33 from vpp_papi_provider import VppPapiProvider
35 from vpp_papi.vpp_stats import VPPStats
36 from vpp_papi.vpp_transport_socket import VppTransportSocketIOError
37 from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
39 from vpp_object import VppObjectRegistry
40 from util import ppp, is_core_present
41 from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
42 from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
43 from scapy.layers.inet6 import ICMPv6EchoReply
45 logger = logging.getLogger(__name__)
47 # Set up an empty logger for the testcase that can be overridden as necessary
48 null_logger = logging.getLogger('VppTestCase')
49 null_logger.addHandler(logging.NullHandler())
58 class BoolEnvironmentVariable(object):
60 def __init__(self, env_var_name, default='n', true_values=None):
61 self.name = env_var_name
62 self.default = default
63 self.true_values = true_values if true_values is not None else \
67 return os.getenv(self.name, self.default).lower() in self.true_values
69 if sys.version_info[0] == 2:
70 __nonzero__ = __bool__
73 return 'BoolEnvironmentVariable(%r, default=%r, true_values=%r)' % \
74 (self.name, self.default, self.true_values)
77 debug_framework = BoolEnvironmentVariable('TEST_DEBUG')
82 Test framework module.
84 The module provides a set of tools for constructing and running tests and
85 representing the results.
89 class VppDiedError(Exception):
90 """ exception for reporting that the subprocess has died."""
92 signals_by_value = {v: k for k, v in signal.__dict__.items() if
93 k.startswith('SIG') and not k.startswith('SIG_')}
95 def __init__(self, rv=None, testcase=None, method_name=None):
97 self.signal_name = None
98 self.testcase = testcase
99 self.method_name = method_name
102 self.signal_name = VppDiedError.signals_by_value[-rv]
103 except (KeyError, TypeError):
106 if testcase is None and method_name is None:
109 in_msg = ' while running %s.%s' % (testcase, method_name)
112 msg = "VPP subprocess died unexpectedly%s with return code: %d%s."\
113 % (in_msg, self.rv, ' [%s]' %
115 self.signal_name is not None else ''))
117 msg = "VPP subprocess died unexpectedly%s." % in_msg
119 super(VppDiedError, self).__init__(msg)
122 class _PacketInfo(object):
123 """Private class to create packet info object.
125 Help process information about the next packet.
126 Set variables to default values.
128 #: Store the index of the packet.
130 #: Store the index of the source packet generator interface of the packet.
132 #: Store the index of the destination packet generator interface
135 #: Store expected ip version
137 #: Store expected upper protocol
139 #: Store the copy of the former packet.
142 def __eq__(self, other):
143 index = self.index == other.index
144 src = self.src == other.src
145 dst = self.dst == other.dst
146 data = self.data == other.data
147 return index and src and dst and data
150 def pump_output(testclass):
151 """ pump output from vpp stdout/stderr to proper queues """
154 while not testclass.pump_thread_stop_flag.is_set():
155 readable = select.select([testclass.vpp.stdout.fileno(),
156 testclass.vpp.stderr.fileno(),
157 testclass.pump_thread_wakeup_pipe[0]],
159 if testclass.vpp.stdout.fileno() in readable:
160 read = os.read(testclass.vpp.stdout.fileno(), 102400)
162 split = read.decode('ascii',
163 errors='backslashreplace').splitlines(True)
164 if len(stdout_fragment) > 0:
165 split[0] = "%s%s" % (stdout_fragment, split[0])
166 if len(split) > 0 and split[-1].endswith("\n"):
170 stdout_fragment = split[-1]
171 testclass.vpp_stdout_deque.extend(split[:limit])
172 if not testclass.cache_vpp_output:
173 for line in split[:limit]:
174 testclass.logger.info(
175 "VPP STDOUT: %s" % line.rstrip("\n"))
176 if testclass.vpp.stderr.fileno() in readable:
177 read = os.read(testclass.vpp.stderr.fileno(), 102400)
179 split = read.decode('ascii',
180 errors='backslashreplace').splitlines(True)
181 if len(stderr_fragment) > 0:
182 split[0] = "%s%s" % (stderr_fragment, split[0])
183 if len(split) > 0 and split[-1].endswith("\n"):
187 stderr_fragment = split[-1]
189 testclass.vpp_stderr_deque.extend(split[:limit])
190 if not testclass.cache_vpp_output:
191 for line in split[:limit]:
192 testclass.logger.error(
193 "VPP STDERR: %s" % line.rstrip("\n"))
194 # ignoring the dummy pipe here intentionally - the
195 # flag will take care of properly terminating the loop
198 def _is_skip_aarch64_set():
199 return BoolEnvironmentVariable('SKIP_AARCH64')
202 is_skip_aarch64_set = _is_skip_aarch64_set()
205 def _is_platform_aarch64():
206 return platform.machine() == 'aarch64'
209 is_platform_aarch64 = _is_platform_aarch64()
212 def _running_extended_tests():
213 return BoolEnvironmentVariable("EXTENDED_TESTS")
216 running_extended_tests = _running_extended_tests()
219 def _running_gcov_tests():
220 return BoolEnvironmentVariable("GCOV_TESTS")
223 running_gcov_tests = _running_gcov_tests()
226 class KeepAliveReporter(object):
228 Singleton object which reports test start to parent process
233 self.__dict__ = self._shared_state
241 def pipe(self, pipe):
242 if self._pipe is not None:
243 raise Exception("Internal error - pipe should only be set once.")
246 def send_keep_alive(self, test, desc=None):
248 Write current test tmpdir & desc to keep-alive pipe to signal liveness
250 if self.pipe is None:
251 # if not running forked..
255 desc = '%s (%s)' % (desc, unittest.util.strclass(test))
259 self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
262 class TestCaseTag(Enum):
263 # marks the suites that must run at the end
264 # using only a single test runner
266 # marks the suites broken on VPP multi-worker
267 FIXME_VPP_WORKERS = 2
270 def create_tag_decorator(e):
273 cls.test_tags.append(e)
274 except AttributeError:
280 tag_run_solo = create_tag_decorator(TestCaseTag.RUN_SOLO)
281 tag_fixme_vpp_workers = create_tag_decorator(TestCaseTag.FIXME_VPP_WORKERS)
284 class VppTestCase(unittest.TestCase):
285 """This subclass is a base class for VPP test cases that are implemented as
286 classes. It provides methods to create and run test case.
289 extra_vpp_punt_config = []
290 extra_vpp_plugin_config = []
292 vapi_response_timeout = 5
295 def packet_infos(self):
296 """List of packet infos"""
297 return self._packet_infos
300 def get_packet_count_for_if_idx(cls, dst_if_index):
301 """Get the number of packet info for specified destination if index"""
302 if dst_if_index in cls._packet_count_for_dst_if_idx:
303 return cls._packet_count_for_dst_if_idx[dst_if_index]
308 def has_tag(cls, tag):
309 """ if the test case has a given tag - return true """
311 return tag in cls.test_tags
312 except AttributeError:
317 def is_tagged_run_solo(cls):
318 """ if the test case class is timing-sensitive - return true """
319 return cls.has_tag(TestCaseTag.RUN_SOLO)
323 """Return the instance of this testcase"""
324 return cls.test_instance
327 def set_debug_flags(cls, d):
328 cls.gdbserver_port = 7777
329 cls.debug_core = False
330 cls.debug_gdb = False
331 cls.debug_gdbserver = False
332 cls.debug_all = False
337 cls.debug_core = True
338 elif dl == "gdb" or dl == "gdb-all":
340 elif dl == "gdbserver" or dl == "gdbserver-all":
341 cls.debug_gdbserver = True
343 raise Exception("Unrecognized DEBUG option: '%s'" % d)
344 if dl == "gdb-all" or dl == "gdbserver-all":
348 def get_least_used_cpu():
349 cpu_usage_list = [set(range(psutil.cpu_count()))]
350 vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
351 if 'vpp_main' == p.info['name']]
352 for vpp_process in vpp_processes:
353 for cpu_usage_set in cpu_usage_list:
355 cpu_num = vpp_process.cpu_num()
356 if cpu_num in cpu_usage_set:
357 cpu_usage_set_index = cpu_usage_list.index(
359 if cpu_usage_set_index == len(cpu_usage_list) - 1:
360 cpu_usage_list.append({cpu_num})
362 cpu_usage_list[cpu_usage_set_index + 1].add(
364 cpu_usage_set.remove(cpu_num)
366 except psutil.NoSuchProcess:
369 for cpu_usage_set in cpu_usage_list:
370 if len(cpu_usage_set) > 0:
371 min_usage_set = cpu_usage_set
374 return random.choice(tuple(min_usage_set))
377 def setUpConstants(cls):
378 """ Set-up the test case class based on environment variables """
379 cls.step = BoolEnvironmentVariable('STEP')
380 d = os.getenv("DEBUG", None)
381 # inverted case to handle '' == True
382 c = os.getenv("CACHE_OUTPUT", "1")
383 cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
384 cls.set_debug_flags(d)
385 cls.vpp_bin = os.getenv('VPP_BIN', "vpp")
386 cls.plugin_path = os.getenv('VPP_PLUGIN_PATH')
387 cls.test_plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
388 cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
390 if cls.plugin_path is not None:
391 if cls.extern_plugin_path is not None:
392 plugin_path = "%s:%s" % (
393 cls.plugin_path, cls.extern_plugin_path)
395 plugin_path = cls.plugin_path
396 elif cls.extern_plugin_path is not None:
397 plugin_path = cls.extern_plugin_path
399 if cls.step or cls.debug_gdb or cls.debug_gdbserver:
400 debug_cli = "cli-listen localhost:5002"
402 size = os.getenv("COREDUMP_SIZE")
404 coredump_size = "coredump-size %s" % size
405 if coredump_size is None:
406 coredump_size = "coredump-size unlimited"
408 cpu_core_number = cls.get_least_used_cpu()
409 if not hasattr(cls, "vpp_worker_count"):
410 cls.vpp_worker_count = 0
411 worker_config = os.getenv("VPP_WORKER_CONFIG", "")
413 elems = worker_config.split(" ")
414 if elems[0] != "workers" or len(elems) != 2:
415 raise ValueError("Wrong VPP_WORKER_CONFIG == '%s' value." %
417 cls.vpp_worker_count = int(elems[1])
418 if cls.vpp_worker_count > 0 and\
419 cls.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
420 cls.vpp_worker_count = 0
422 default_variant = os.getenv("VARIANT")
423 if default_variant is not None:
424 default_variant = "defaults { %s 100 }" % default_variant
428 api_fuzzing = os.getenv("API_FUZZ")
429 if api_fuzzing is None:
434 "unix", "{", "nodaemon", debug_cli, "full-coredump",
435 coredump_size, "runtime-dir", cls.tempdir, "}",
436 "api-trace", "{", "on", "}",
437 "api-segment", "{", "prefix", cls.shm_prefix, "}",
438 "cpu", "{", "main-core", str(cpu_core_number), ]
439 if cls.vpp_worker_count:
440 cls.vpp_cmdline.extend(["workers", str(cls.vpp_worker_count)])
441 cls.vpp_cmdline.extend([
443 "physmem", "{", "max-size", "32m", "}",
444 "statseg", "{", "socket-name", cls.stats_sock, "}",
445 "socksvr", "{", "socket-name", cls.api_sock, "}",
446 "node { ", default_variant, "}",
447 "api-fuzz {", api_fuzzing, "}",
448 "plugins", "{", "plugin", "dpdk_plugin.so", "{", "disable", "}",
449 "plugin", "rdma_plugin.so", "{", "disable", "}",
450 "plugin", "lisp_unittest_plugin.so", "{", "enable", "}",
451 "plugin", "unittest_plugin.so", "{", "enable", "}"
452 ] + cls.extra_vpp_plugin_config + ["}", ])
454 if cls.extra_vpp_punt_config is not None:
455 cls.vpp_cmdline.extend(cls.extra_vpp_punt_config)
456 if plugin_path is not None:
457 cls.vpp_cmdline.extend(["plugin_path", plugin_path])
458 if cls.test_plugin_path is not None:
459 cls.vpp_cmdline.extend(["test_plugin_path", cls.test_plugin_path])
461 cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
462 cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
465 def wait_for_enter(cls):
466 if cls.debug_gdbserver:
467 print(double_line_delim)
468 print("Spawned GDB server with PID: %d" % cls.vpp.pid)
470 print(double_line_delim)
471 print("Spawned VPP with PID: %d" % cls.vpp.pid)
473 cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
475 print(single_line_delim)
476 print("You can debug VPP using:")
477 if cls.debug_gdbserver:
478 print("sudo gdb " + cls.vpp_bin +
479 " -ex 'target remote localhost:{port}'"
480 .format(port=cls.gdbserver_port))
481 print("Now is the time to attach gdb by running the above "
482 "command, set up breakpoints etc., then resume VPP from "
483 "within gdb by issuing the 'continue' command")
484 cls.gdbserver_port += 1
486 print("sudo gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
487 print("Now is the time to attach gdb by running the above "
488 "command and set up breakpoints etc., then resume VPP from"
489 " within gdb by issuing the 'continue' command")
490 print(single_line_delim)
491 input("Press ENTER to continue running the testcase...")
495 cmdline = cls.vpp_cmdline
497 if cls.debug_gdbserver:
498 gdbserver = '/usr/bin/gdbserver'
499 if not os.path.isfile(gdbserver) or \
500 not os.access(gdbserver, os.X_OK):
501 raise Exception("gdbserver binary '%s' does not exist or is "
502 "not executable" % gdbserver)
504 cmdline = [gdbserver, 'localhost:{port}'
505 .format(port=cls.gdbserver_port)] + cls.vpp_cmdline
506 cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
509 cls.vpp = subprocess.Popen(cmdline,
510 stdout=subprocess.PIPE,
511 stderr=subprocess.PIPE)
512 except subprocess.CalledProcessError as e:
513 cls.logger.critical("Subprocess returned with non-0 return code: ("
517 cls.logger.critical("Subprocess returned with OS error: "
518 "(%s) %s", e.errno, e.strerror)
520 except Exception as e:
521 cls.logger.exception("Subprocess returned unexpected from "
528 def wait_for_coredump(cls):
529 corefile = cls.tempdir + "/core"
530 if os.path.isfile(corefile):
531 cls.logger.error("Waiting for coredump to complete: %s", corefile)
532 curr_size = os.path.getsize(corefile)
533 deadline = time.time() + 60
535 while time.time() < deadline:
538 curr_size = os.path.getsize(corefile)
539 if size == curr_size:
543 cls.logger.error("Timed out waiting for coredump to complete:"
546 cls.logger.error("Coredump complete: %s, size %d",
552 Perform class setup before running the testcase
553 Remove shared memory files, start vpp and connect the vpp-api
555 super(VppTestCase, cls).setUpClass()
556 gc.collect() # run garbage collection first
557 cls.logger = get_logger(cls.__name__)
558 seed = os.environ["RND_SEED"]
560 if hasattr(cls, 'parallel_handler'):
561 cls.logger.addHandler(cls.parallel_handler)
562 cls.logger.propagate = False
564 cls.tempdir = tempfile.mkdtemp(
565 prefix='vpp-unittest-%s-' % cls.__name__)
566 cls.stats_sock = "%s/stats.sock" % cls.tempdir
567 cls.api_sock = "%s/api.sock" % cls.tempdir
568 cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
569 cls.file_handler.setFormatter(
570 Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
572 cls.file_handler.setLevel(DEBUG)
573 cls.logger.addHandler(cls.file_handler)
574 cls.logger.debug("--- setUpClass() for %s called ---" %
576 cls.shm_prefix = os.path.basename(cls.tempdir) # Only used for VAPI
577 os.chdir(cls.tempdir)
578 cls.logger.info("Temporary dir is %s, api socket is %s",
579 cls.tempdir, cls.api_sock)
580 cls.logger.debug("Random seed is %s" % seed)
582 cls.reset_packet_infos()
586 cls.registry = VppObjectRegistry()
587 cls.vpp_startup_failed = False
588 cls.reporter = KeepAliveReporter()
589 # need to catch exceptions here because if we raise, then the cleanup
590 # doesn't get called and we might end with a zombie vpp
593 cls.reporter.send_keep_alive(cls, 'setUpClass')
594 VppTestResult.current_test_case_info = TestCaseInfo(
595 cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
596 cls.vpp_stdout_deque = deque()
597 cls.vpp_stderr_deque = deque()
598 cls.pump_thread_stop_flag = Event()
599 cls.pump_thread_wakeup_pipe = os.pipe()
600 cls.pump_thread = Thread(target=pump_output, args=(cls,))
601 cls.pump_thread.daemon = True
602 cls.pump_thread.start()
603 if cls.debug_gdb or cls.debug_gdbserver:
604 cls.vapi_response_timeout = 0
605 cls.vapi = VppPapiProvider(cls.__name__, cls,
606 cls.vapi_response_timeout)
608 hook = hookmodule.StepHook(cls)
610 hook = hookmodule.PollHook(cls)
611 cls.vapi.register_hook(hook)
612 cls.statistics = VPPStats(socketname=cls.stats_sock)
616 cls.vpp_startup_failed = True
618 "VPP died shortly after startup, check the"
619 " output to standard error for possible cause")
623 except (vpp_papi.VPPIOError, Exception) as e:
624 cls.logger.debug("Exception connecting to vapi: %s" % e)
625 cls.vapi.disconnect()
627 if cls.debug_gdbserver:
628 print(colorize("You're running VPP inside gdbserver but "
629 "VPP-API connection failed, did you forget "
630 "to 'continue' VPP from within gdb?", RED))
632 except vpp_papi.VPPRuntimeError as e:
633 cls.logger.debug("%s" % e)
636 except Exception as e:
637 cls.logger.debug("Exception connecting to VPP: %s" % e)
642 def _debug_quit(cls):
643 if (cls.debug_gdbserver or cls.debug_gdb):
647 if cls.vpp.returncode is None:
649 print(double_line_delim)
650 print("VPP or GDB server is still running")
651 print(single_line_delim)
652 input("When done debugging, press ENTER to kill the "
653 "process and finish running the testcase...")
654 except AttributeError:
660 Disconnect vpp-api, kill vpp and cleanup shared memory files
664 # first signal that we want to stop the pump thread, then wake it up
665 if hasattr(cls, 'pump_thread_stop_flag'):
666 cls.pump_thread_stop_flag.set()
667 if hasattr(cls, 'pump_thread_wakeup_pipe'):
668 os.write(cls.pump_thread_wakeup_pipe[1], b'ding dong wake up')
669 if hasattr(cls, 'pump_thread'):
670 cls.logger.debug("Waiting for pump thread to stop")
671 cls.pump_thread.join()
672 if hasattr(cls, 'vpp_stderr_reader_thread'):
673 cls.logger.debug("Waiting for stderr pump to stop")
674 cls.vpp_stderr_reader_thread.join()
676 if hasattr(cls, 'vpp'):
677 if hasattr(cls, 'vapi'):
678 cls.logger.debug(cls.vapi.vpp.get_stats())
679 cls.logger.debug("Disconnecting class vapi client on %s",
681 cls.vapi.disconnect()
682 cls.logger.debug("Deleting class vapi attribute on %s",
686 if cls.vpp.returncode is None:
687 cls.wait_for_coredump()
688 cls.logger.debug("Sending TERM to vpp")
690 cls.logger.debug("Waiting for vpp to die")
692 outs, errs = cls.vpp.communicate(timeout=5)
693 except subprocess.TimeoutExpired:
695 outs, errs = cls.vpp.communicate()
696 cls.logger.debug("Deleting class vpp attribute on %s",
698 cls.vpp.stdout.close()
699 cls.vpp.stderr.close()
702 if cls.vpp_startup_failed:
703 stdout_log = cls.logger.info
704 stderr_log = cls.logger.critical
706 stdout_log = cls.logger.info
707 stderr_log = cls.logger.info
709 if hasattr(cls, 'vpp_stdout_deque'):
710 stdout_log(single_line_delim)
711 stdout_log('VPP output to stdout while running %s:', cls.__name__)
712 stdout_log(single_line_delim)
713 vpp_output = "".join(cls.vpp_stdout_deque)
714 with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
716 stdout_log('\n%s', vpp_output)
717 stdout_log(single_line_delim)
719 if hasattr(cls, 'vpp_stderr_deque'):
720 stderr_log(single_line_delim)
721 stderr_log('VPP output to stderr while running %s:', cls.__name__)
722 stderr_log(single_line_delim)
723 vpp_output = "".join(cls.vpp_stderr_deque)
724 with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
726 stderr_log('\n%s', vpp_output)
727 stderr_log(single_line_delim)
730 def tearDownClass(cls):
731 """ Perform final cleanup after running all tests in this test-case """
732 cls.logger.debug("--- tearDownClass() for %s called ---" %
734 cls.reporter.send_keep_alive(cls, 'tearDownClass')
736 cls.file_handler.close()
737 cls.reset_packet_infos()
739 debug_internal.on_tear_down_class(cls)
741 def show_commands_at_teardown(self):
742 """ Allow subclass specific teardown logging additions."""
743 self.logger.info("--- No test specific show commands provided. ---")
746 """ Show various debug prints after each test """
747 self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
748 (self.__class__.__name__, self._testMethodName,
749 self._testMethodDoc))
752 if not self.vpp_dead:
753 self.logger.debug(self.vapi.cli("show trace max 1000"))
754 self.logger.info(self.vapi.ppcli("show interface"))
755 self.logger.info(self.vapi.ppcli("show hardware"))
756 self.logger.info(self.statistics.set_errors_str())
757 self.logger.info(self.vapi.ppcli("show run"))
758 self.logger.info(self.vapi.ppcli("show log"))
759 self.logger.info(self.vapi.ppcli("show bihash"))
760 self.logger.info("Logging testcase specific show commands.")
761 self.show_commands_at_teardown()
762 self.registry.remove_vpp_config(self.logger)
763 # Save/Dump VPP api trace log
764 m = self._testMethodName
765 api_trace = "vpp_api_trace.%s.%d.log" % (m, self.vpp.pid)
766 tmp_api_trace = "/tmp/%s" % api_trace
767 vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
768 self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
769 self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
771 os.rename(tmp_api_trace, vpp_api_trace_log)
772 self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
774 except VppTransportSocketIOError:
775 self.logger.debug("VppTransportSocketIOError: Vpp dead. "
776 "Cannot log show commands.")
779 self.registry.unregister_all(self.logger)
782 """ Clear trace before running each test"""
783 super(VppTestCase, self).setUp()
784 self.reporter.send_keep_alive(self)
786 raise VppDiedError(rv=None, testcase=self.__class__.__name__,
787 method_name=self._testMethodName)
788 self.sleep(.1, "during setUp")
789 self.vpp_stdout_deque.append(
790 "--- test setUp() for %s.%s(%s) starts here ---\n" %
791 (self.__class__.__name__, self._testMethodName,
792 self._testMethodDoc))
793 self.vpp_stderr_deque.append(
794 "--- test setUp() for %s.%s(%s) starts here ---\n" %
795 (self.__class__.__name__, self._testMethodName,
796 self._testMethodDoc))
797 self.vapi.cli("clear trace")
798 # store the test instance inside the test class - so that objects
799 # holding the class can access instance methods (like assertEqual)
800 type(self).test_instance = self
803 def pg_enable_capture(cls, interfaces=None):
805 Enable capture on packet-generator interfaces
807 :param interfaces: iterable interface indexes (if None,
808 use self.pg_interfaces)
811 if interfaces is None:
812 interfaces = cls.pg_interfaces
817 def register_capture(cls, cap_name):
818 """ Register a capture in the testclass """
819 # add to the list of captures with current timestamp
820 cls._captures.append((time.time(), cap_name))
823 def get_vpp_time(cls):
824 # processes e.g. "Time now 2.190522, Wed, 11 Mar 2020 17:29:54 GMT"
825 # returns float("2.190522")
826 timestr = cls.vapi.cli('show clock')
827 head, sep, tail = timestr.partition(',')
828 head, sep, tail = head.partition('Time now')
832 def sleep_on_vpp_time(cls, sec):
833 """ Sleep according to time in VPP world """
834 # On a busy system with many processes
835 # we might end up with VPP time being slower than real world
836 # So take that into account when waiting for VPP to do something
837 start_time = cls.get_vpp_time()
838 while cls.get_vpp_time() - start_time < sec:
842 def pg_start(cls, trace=True):
843 """ Enable the PG, wait till it is done, then clean up """
845 cls.vapi.cli("clear trace")
846 cls.vapi.cli("trace add pg-input 1000")
847 cls.vapi.cli('packet-generator enable')
848 # PG, when starts, runs to completion -
849 # so let's avoid a race condition,
850 # and wait a little till it's done.
851 # Then clean it up - and then be gone.
852 deadline = time.time() + 300
853 while cls.vapi.cli('show packet-generator').find("Yes") != -1:
854 cls.sleep(0.01) # yield
855 if time.time() > deadline:
856 cls.logger.error("Timeout waiting for pg to stop")
858 for stamp, cap_name in cls._captures:
859 cls.vapi.cli('packet-generator delete %s' % cap_name)
863 def create_pg_interfaces(cls, interfaces, gso=0, gso_size=0):
865 Create packet-generator interfaces.
867 :param interfaces: iterable indexes of the interfaces.
868 :returns: List of created interfaces.
873 intf = VppPGInterface(cls, i, gso, gso_size)
874 setattr(cls, intf.name, intf)
876 cls.pg_interfaces = result
880 def create_loopback_interfaces(cls, count):
882 Create loopback interfaces.
884 :param count: number of interfaces created.
885 :returns: List of created interfaces.
887 result = [VppLoInterface(cls) for i in range(count)]
889 setattr(cls, intf.name, intf)
890 cls.lo_interfaces = result
894 def create_bvi_interfaces(cls, count):
896 Create BVI interfaces.
898 :param count: number of interfaces created.
899 :returns: List of created interfaces.
901 result = [VppBviInterface(cls) for i in range(count)]
903 setattr(cls, intf.name, intf)
904 cls.bvi_interfaces = result
908 def extend_packet(packet, size, padding=' '):
910 Extend packet to given size by padding with spaces or custom padding
911 NOTE: Currently works only when Raw layer is present.
913 :param packet: packet
914 :param size: target size
915 :param padding: padding used to extend the payload
918 packet_len = len(packet) + 4
919 extend = size - packet_len
921 num = (extend // len(padding)) + 1
922 packet[Raw].load += (padding * num)[:extend].encode("ascii")
925 def reset_packet_infos(cls):
926 """ Reset the list of packet info objects and packet counts to zero """
927 cls._packet_infos = {}
928 cls._packet_count_for_dst_if_idx = {}
931 def create_packet_info(cls, src_if, dst_if):
933 Create packet info object containing the source and destination indexes
934 and add it to the testcase's packet info list
936 :param VppInterface src_if: source interface
937 :param VppInterface dst_if: destination interface
939 :returns: _PacketInfo object
943 info.index = len(cls._packet_infos)
944 info.src = src_if.sw_if_index
945 info.dst = dst_if.sw_if_index
946 if isinstance(dst_if, VppSubInterface):
947 dst_idx = dst_if.parent.sw_if_index
949 dst_idx = dst_if.sw_if_index
950 if dst_idx in cls._packet_count_for_dst_if_idx:
951 cls._packet_count_for_dst_if_idx[dst_idx] += 1
953 cls._packet_count_for_dst_if_idx[dst_idx] = 1
954 cls._packet_infos[info.index] = info
958 def info_to_payload(info):
960 Convert _PacketInfo object to packet payload
962 :param info: _PacketInfo object
964 :returns: string containing serialized data from packet info
966 return "%d %d %d %d %d" % (info.index, info.src, info.dst,
970 def payload_to_info(payload, payload_field='load'):
972 Convert packet payload to _PacketInfo object
974 :param payload: packet payload
975 :type payload: <class 'scapy.packet.Raw'>
976 :param payload_field: packet fieldname of payload "load" for
977 <class 'scapy.packet.Raw'>
978 :type payload_field: str
979 :returns: _PacketInfo object containing de-serialized data from payload
982 numbers = getattr(payload, payload_field).split()
984 info.index = int(numbers[0])
985 info.src = int(numbers[1])
986 info.dst = int(numbers[2])
987 info.ip = int(numbers[3])
988 info.proto = int(numbers[4])
991 def get_next_packet_info(self, info):
993 Iterate over the packet info list stored in the testcase
994 Start iteration with first element if info is None
995 Continue based on index in info if info is specified
997 :param info: info or None
998 :returns: next info in list or None if no more infos
1003 next_index = info.index + 1
1004 if next_index == len(self._packet_infos):
1007 return self._packet_infos[next_index]
1009 def get_next_packet_info_for_interface(self, src_index, info):
1011 Search the packet info list for the next packet info with same source
1014 :param src_index: source interface index to search for
1015 :param info: packet info - where to start the search
1016 :returns: packet info or None
1020 info = self.get_next_packet_info(info)
1023 if info.src == src_index:
1026 def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
1028 Search the packet info list for the next packet info with same source
1029 and destination interface indexes
1031 :param src_index: source interface index to search for
1032 :param dst_index: destination interface index to search for
1033 :param info: packet info - where to start the search
1034 :returns: packet info or None
1038 info = self.get_next_packet_info_for_interface(src_index, info)
1041 if info.dst == dst_index:
1044 def assert_equal(self, real_value, expected_value, name_or_class=None):
1045 if name_or_class is None:
1046 self.assertEqual(real_value, expected_value)
1049 msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
1050 msg = msg % (getdoc(name_or_class).strip(),
1051 real_value, str(name_or_class(real_value)),
1052 expected_value, str(name_or_class(expected_value)))
1054 msg = "Invalid %s: %s does not match expected value %s" % (
1055 name_or_class, real_value, expected_value)
1057 self.assertEqual(real_value, expected_value, msg)
1059 def assert_in_range(self,
1067 msg = "Invalid %s: %s out of range <%s,%s>" % (
1068 name, real_value, expected_min, expected_max)
1069 self.assertTrue(expected_min <= real_value <= expected_max, msg)
1071 def assert_packet_checksums_valid(self, packet,
1072 ignore_zero_udp_checksums=True):
1073 received = packet.__class__(scapy.compat.raw(packet))
1074 udp_layers = ['UDP', 'UDPerror']
1075 checksum_fields = ['cksum', 'chksum']
1078 temp = received.__class__(scapy.compat.raw(received))
1080 layer = temp.getlayer(counter)
1082 layer = layer.copy()
1083 layer.remove_payload()
1084 for cf in checksum_fields:
1085 if hasattr(layer, cf):
1086 if ignore_zero_udp_checksums and \
1087 0 == getattr(layer, cf) and \
1088 layer.name in udp_layers:
1090 delattr(temp.getlayer(counter), cf)
1091 checksums.append((counter, cf))
1094 counter = counter + 1
1095 if 0 == len(checksums):
1097 temp = temp.__class__(scapy.compat.raw(temp))
1098 for layer, cf in checksums:
1099 calc_sum = getattr(temp[layer], cf)
1101 getattr(received[layer], cf), calc_sum,
1102 "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
1104 "Checksum field `%s` on `%s` layer has correct value `%s`" %
1105 (cf, temp[layer].name, calc_sum))
1107 def assert_checksum_valid(self, received_packet, layer,
1108 field_name='chksum',
1109 ignore_zero_checksum=False):
1110 """ Check checksum of received packet on given layer """
1111 received_packet_checksum = getattr(received_packet[layer], field_name)
1112 if ignore_zero_checksum and 0 == received_packet_checksum:
1114 recalculated = received_packet.__class__(
1115 scapy.compat.raw(received_packet))
1116 delattr(recalculated[layer], field_name)
1117 recalculated = recalculated.__class__(scapy.compat.raw(recalculated))
1118 self.assert_equal(received_packet_checksum,
1119 getattr(recalculated[layer], field_name),
1120 "packet checksum on layer: %s" % layer)
1122 def assert_ip_checksum_valid(self, received_packet,
1123 ignore_zero_checksum=False):
1124 self.assert_checksum_valid(received_packet, 'IP',
1125 ignore_zero_checksum=ignore_zero_checksum)
1127 def assert_tcp_checksum_valid(self, received_packet,
1128 ignore_zero_checksum=False):
1129 self.assert_checksum_valid(received_packet, 'TCP',
1130 ignore_zero_checksum=ignore_zero_checksum)
1132 def assert_udp_checksum_valid(self, received_packet,
1133 ignore_zero_checksum=True):
1134 self.assert_checksum_valid(received_packet, 'UDP',
1135 ignore_zero_checksum=ignore_zero_checksum)
1137 def assert_embedded_icmp_checksum_valid(self, received_packet):
1138 if received_packet.haslayer(IPerror):
1139 self.assert_checksum_valid(received_packet, 'IPerror')
1140 if received_packet.haslayer(TCPerror):
1141 self.assert_checksum_valid(received_packet, 'TCPerror')
1142 if received_packet.haslayer(UDPerror):
1143 self.assert_checksum_valid(received_packet, 'UDPerror',
1144 ignore_zero_checksum=True)
1145 if received_packet.haslayer(ICMPerror):
1146 self.assert_checksum_valid(received_packet, 'ICMPerror')
1148 def assert_icmp_checksum_valid(self, received_packet):
1149 self.assert_checksum_valid(received_packet, 'ICMP')
1150 self.assert_embedded_icmp_checksum_valid(received_packet)
1152 def assert_icmpv6_checksum_valid(self, pkt):
1153 if pkt.haslayer(ICMPv6DestUnreach):
1154 self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
1155 self.assert_embedded_icmp_checksum_valid(pkt)
1156 if pkt.haslayer(ICMPv6EchoRequest):
1157 self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
1158 if pkt.haslayer(ICMPv6EchoReply):
1159 self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
1161 def get_packet_counter(self, counter):
1162 if counter.startswith("/"):
1163 counter_value = self.statistics.get_counter(counter)
1165 counters = self.vapi.cli("sh errors").split('\n')
1167 for i in range(1, len(counters) - 1):
1168 results = counters[i].split()
1169 if results[1] == counter:
1170 counter_value = int(results[0])
1172 return counter_value
1174 def assert_packet_counter_equal(self, counter, expected_value):
1175 counter_value = self.get_packet_counter(counter)
1176 self.assert_equal(counter_value, expected_value,
1177 "packet counter `%s'" % counter)
1179 def assert_error_counter_equal(self, counter, expected_value):
1180 counter_value = self.statistics.get_err_counter(counter)
1181 self.assert_equal(counter_value, expected_value,
1182 "error counter `%s'" % counter)
1185 def sleep(cls, timeout, remark=None):
1187 # /* Allow sleep(0) to maintain win32 semantics, and as decreed
1188 # * by Guido, only the main thread can be interrupted.
1190 # https://github.com/python/cpython/blob/6673decfa0fb078f60587f5cb5e98460eea137c2/Modules/timemodule.c#L1892 # noqa
1193 if hasattr(os, 'sched_yield'):
1199 cls.logger.debug("Starting sleep for %es (%s)", timeout, remark)
1200 before = time.time()
1203 if after - before > 2 * timeout:
1204 cls.logger.error("unexpected self.sleep() result - "
1205 "slept for %es instead of ~%es!",
1206 after - before, timeout)
1209 "Finished sleep (%s) - slept %es (wanted %es)",
1210 remark, after - before, timeout)
1212 def pg_send(self, intf, pkts, worker=None, trace=True):
1213 intf.add_stream(pkts, worker=worker)
1214 self.pg_enable_capture(self.pg_interfaces)
1215 self.pg_start(trace=trace)
1217 def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
1218 self.pg_send(intf, pkts)
1221 for i in self.pg_interfaces:
1222 i.get_capture(0, timeout=timeout)
1223 i.assert_nothing_captured(remark=remark)
1226 def send_and_expect(self, intf, pkts, output, n_rx=None, worker=None,
1230 self.pg_send(intf, pkts, worker=worker, trace=trace)
1231 rx = output.get_capture(n_rx)
1234 def send_and_expect_only(self, intf, pkts, output, timeout=None):
1235 self.pg_send(intf, pkts)
1236 rx = output.get_capture(len(pkts))
1240 for i in self.pg_interfaces:
1241 if i not in outputs:
1242 i.get_capture(0, timeout=timeout)
1243 i.assert_nothing_captured()
1249 def get_testcase_doc_name(test):
1250 return getdoc(test.__class__).splitlines()[0]
1253 def get_test_description(descriptions, test):
1254 short_description = test.shortDescription()
1255 if descriptions and short_description:
1256 return short_description
1261 class TestCaseInfo(object):
1262 def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
1263 self.logger = logger
1264 self.tempdir = tempdir
1265 self.vpp_pid = vpp_pid
1266 self.vpp_bin_path = vpp_bin_path
1267 self.core_crash_test = None
1270 class VppTestResult(unittest.TestResult):
1272 @property result_string
1273 String variable to store the test case result string.
1275 List variable containing 2-tuples of TestCase instances and strings
1276 holding formatted tracebacks. Each tuple represents a test which
1277 raised an unexpected exception.
1279 List variable containing 2-tuples of TestCase instances and strings
1280 holding formatted tracebacks. Each tuple represents a test where
1281 a failure was explicitly signalled using the TestCase.assert*()
1285 failed_test_cases_info = set()
1286 core_crash_test_cases_info = set()
1287 current_test_case_info = None
1289 def __init__(self, stream=None, descriptions=None, verbosity=None,
1292 :param stream File descriptor to store where to report test results.
1293 Set to the standard error stream by default.
1294 :param descriptions Boolean variable to store information if to use
1295 test case descriptions.
1296 :param verbosity Integer variable to store required verbosity level.
1298 super(VppTestResult, self).__init__(stream, descriptions, verbosity)
1299 self.stream = stream
1300 self.descriptions = descriptions
1301 self.verbosity = verbosity
1302 self.result_string = None
1303 self.runner = runner
1305 def addSuccess(self, test):
1307 Record a test succeeded result
1312 if self.current_test_case_info:
1313 self.current_test_case_info.logger.debug(
1314 "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
1315 test._testMethodName,
1316 test._testMethodDoc))
1317 unittest.TestResult.addSuccess(self, test)
1318 self.result_string = colorize("OK", GREEN)
1320 self.send_result_through_pipe(test, PASS)
1322 def addSkip(self, test, reason):
1324 Record a test skipped.
1330 if self.current_test_case_info:
1331 self.current_test_case_info.logger.debug(
1332 "--- addSkip() %s.%s(%s) called, reason is %s" %
1333 (test.__class__.__name__, test._testMethodName,
1334 test._testMethodDoc, reason))
1335 unittest.TestResult.addSkip(self, test, reason)
1336 self.result_string = colorize("SKIP", YELLOW)
1338 self.send_result_through_pipe(test, SKIP)
1340 def symlink_failed(self):
1341 if self.current_test_case_info:
1343 failed_dir = os.getenv('FAILED_DIR')
1344 link_path = os.path.join(
1347 os.path.basename(self.current_test_case_info.tempdir))
1349 self.current_test_case_info.logger.debug(
1350 "creating a link to the failed test")
1351 self.current_test_case_info.logger.debug(
1352 "os.symlink(%s, %s)" %
1353 (self.current_test_case_info.tempdir, link_path))
1354 if os.path.exists(link_path):
1355 self.current_test_case_info.logger.debug(
1356 'symlink already exists')
1358 os.symlink(self.current_test_case_info.tempdir, link_path)
1360 except Exception as e:
1361 self.current_test_case_info.logger.error(e)
1363 def send_result_through_pipe(self, test, result):
1364 if hasattr(self, 'test_framework_result_pipe'):
1365 pipe = self.test_framework_result_pipe
1367 pipe.send((test.id(), result))
1369 def log_error(self, test, err, fn_name):
1370 if self.current_test_case_info:
1371 if isinstance(test, unittest.suite._ErrorHolder):
1372 test_name = test.description
1374 test_name = '%s.%s(%s)' % (test.__class__.__name__,
1375 test._testMethodName,
1376 test._testMethodDoc)
1377 self.current_test_case_info.logger.debug(
1378 "--- %s() %s called, err is %s" %
1379 (fn_name, test_name, err))
1380 self.current_test_case_info.logger.debug(
1381 "formatted exception is:\n%s" %
1382 "".join(format_exception(*err)))
1384 def add_error(self, test, err, unittest_fn, error_type):
1385 if error_type == FAIL:
1386 self.log_error(test, err, 'addFailure')
1387 error_type_str = colorize("FAIL", RED)
1388 elif error_type == ERROR:
1389 self.log_error(test, err, 'addError')
1390 error_type_str = colorize("ERROR", RED)
1392 raise Exception('Error type %s cannot be used to record an '
1393 'error or a failure' % error_type)
1395 unittest_fn(self, test, err)
1396 if self.current_test_case_info:
1397 self.result_string = "%s [ temp dir used by test case: %s ]" % \
1399 self.current_test_case_info.tempdir)
1400 self.symlink_failed()
1401 self.failed_test_cases_info.add(self.current_test_case_info)
1402 if is_core_present(self.current_test_case_info.tempdir):
1403 if not self.current_test_case_info.core_crash_test:
1404 if isinstance(test, unittest.suite._ErrorHolder):
1405 test_name = str(test)
1407 test_name = "'{!s}' ({!s})".format(
1408 get_testcase_doc_name(test), test.id())
1409 self.current_test_case_info.core_crash_test = test_name
1410 self.core_crash_test_cases_info.add(
1411 self.current_test_case_info)
1413 self.result_string = '%s [no temp dir]' % error_type_str
1415 self.send_result_through_pipe(test, error_type)
1417 def addFailure(self, test, err):
1419 Record a test failed result
1422 :param err: error message
1425 self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
1427 def addError(self, test, err):
1429 Record a test error result
1432 :param err: error message
1435 self.add_error(test, err, unittest.TestResult.addError, ERROR)
1437 def getDescription(self, test):
1439 Get test description
1442 :returns: test description
1445 return get_test_description(self.descriptions, test)
1447 def startTest(self, test):
1455 def print_header(test):
1456 test_doc = getdoc(test)
1458 raise Exception("No doc string for test '%s'" % test.id())
1459 test_title = test_doc.splitlines()[0]
1460 test_title_colored = colorize(test_title, GREEN)
1461 if test.is_tagged_run_solo():
1462 # long live PEP-8 and 80 char width limitation...
1464 test_title_colored = colorize("SOLO RUN: " + test_title, c)
1466 # This block may overwrite the colorized title above,
1467 # but we want this to stand out and be fixed
1468 if test.has_tag(TestCaseTag.FIXME_VPP_WORKERS):
1470 w = "FIXME with VPP workers: "
1471 test_title_colored = colorize(w + test_title, c)
1473 if not hasattr(test.__class__, '_header_printed'):
1474 print(double_line_delim)
1475 print(test_title_colored)
1476 print(double_line_delim)
1477 test.__class__._header_printed = True
1480 self.start_test = time.time()
1481 unittest.TestResult.startTest(self, test)
1482 if self.verbosity > 0:
1483 self.stream.writeln(
1484 "Starting " + self.getDescription(test) + " ...")
1485 self.stream.writeln(single_line_delim)
1487 def stopTest(self, test):
1489 Called when the given test has been run
1494 unittest.TestResult.stopTest(self, test)
1496 if self.verbosity > 0:
1497 self.stream.writeln(single_line_delim)
1498 self.stream.writeln("%-73s%s" % (self.getDescription(test),
1499 self.result_string))
1500 self.stream.writeln(single_line_delim)
1502 self.stream.writeln("%-68s %4.2f %s" %
1503 (self.getDescription(test),
1504 time.time() - self.start_test,
1505 self.result_string))
1507 self.send_result_through_pipe(test, TEST_RUN)
1509 def printErrors(self):
1511 Print errors from running the test case
1513 if len(self.errors) > 0 or len(self.failures) > 0:
1514 self.stream.writeln()
1515 self.printErrorList('ERROR', self.errors)
1516 self.printErrorList('FAIL', self.failures)
1518 # ^^ that is the last output from unittest before summary
1519 if not self.runner.print_summary:
1520 devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
1521 self.stream = devnull
1522 self.runner.stream = devnull
1524 def printErrorList(self, flavour, errors):
1526 Print error list to the output stream together with error type
1527 and test case description.
1529 :param flavour: error type
1530 :param errors: iterable errors
1533 for test, err in errors:
1534 self.stream.writeln(double_line_delim)
1535 self.stream.writeln("%s: %s" %
1536 (flavour, self.getDescription(test)))
1537 self.stream.writeln(single_line_delim)
1538 self.stream.writeln("%s" % err)
1541 class VppTestRunner(unittest.TextTestRunner):
1543 A basic test runner implementation which prints results to standard error.
1547 def resultclass(self):
1548 """Class maintaining the results of the tests"""
1549 return VppTestResult
1551 def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
1552 result_pipe=None, failfast=False, buffer=False,
1553 resultclass=None, print_summary=True, **kwargs):
1554 # ignore stream setting here, use hard-coded stdout to be in sync
1555 # with prints from VppTestCase methods ...
1556 super(VppTestRunner, self).__init__(sys.stdout, descriptions,
1557 verbosity, failfast, buffer,
1558 resultclass, **kwargs)
1559 KeepAliveReporter.pipe = keep_alive_pipe
1561 self.orig_stream = self.stream
1562 self.resultclass.test_framework_result_pipe = result_pipe
1564 self.print_summary = print_summary
1566 def _makeResult(self):
1567 return self.resultclass(self.stream,
1572 def run(self, test):
1579 faulthandler.enable() # emit stack trace to stderr if killed by signal
1581 result = super(VppTestRunner, self).run(test)
1582 if not self.print_summary:
1583 self.stream = self.orig_stream
1584 result.stream = self.orig_stream
1588 class Worker(Thread):
1589 def __init__(self, executable_args, logger, env=None, *args, **kwargs):
1590 super(Worker, self).__init__(*args, **kwargs)
1591 self.logger = logger
1592 self.args = executable_args
1593 if hasattr(self, 'testcase') and self.testcase.debug_all:
1594 if self.testcase.debug_gdbserver:
1595 self.args = ['/usr/bin/gdbserver', 'localhost:{port}'
1596 .format(port=self.testcase.gdbserver_port)] + args
1597 elif self.testcase.debug_gdb and hasattr(self, 'wait_for_gdb'):
1598 self.args.append(self.wait_for_gdb)
1599 self.app_bin = executable_args[0]
1600 self.app_name = os.path.basename(self.app_bin)
1601 if hasattr(self, 'role'):
1602 self.app_name += ' {role}'.format(role=self.role)
1605 env = {} if env is None else env
1606 self.env = copy.deepcopy(env)
1608 def wait_for_enter(self):
1609 if not hasattr(self, 'testcase'):
1611 if self.testcase.debug_all and self.testcase.debug_gdbserver:
1613 print(double_line_delim)
1614 print("Spawned GDB Server for '{app}' with PID: {pid}"
1615 .format(app=self.app_name, pid=self.process.pid))
1616 elif self.testcase.debug_all and self.testcase.debug_gdb:
1618 print(double_line_delim)
1619 print("Spawned '{app}' with PID: {pid}"
1620 .format(app=self.app_name, pid=self.process.pid))
1623 print(single_line_delim)
1624 print("You can debug '{app}' using:".format(app=self.app_name))
1625 if self.testcase.debug_gdbserver:
1626 print("sudo gdb " + self.app_bin +
1627 " -ex 'target remote localhost:{port}'"
1628 .format(port=self.testcase.gdbserver_port))
1629 print("Now is the time to attach gdb by running the above "
1630 "command, set up breakpoints etc., then resume from "
1631 "within gdb by issuing the 'continue' command")
1632 self.testcase.gdbserver_port += 1
1633 elif self.testcase.debug_gdb:
1634 print("sudo gdb " + self.app_bin +
1635 " -ex 'attach {pid}'".format(pid=self.process.pid))
1636 print("Now is the time to attach gdb by running the above "
1637 "command and set up breakpoints etc., then resume from"
1638 " within gdb by issuing the 'continue' command")
1639 print(single_line_delim)
1640 input("Press ENTER to continue running the testcase...")
1643 executable = self.args[0]
1644 if not os.path.exists(executable) or not os.access(
1645 executable, os.F_OK | os.X_OK):
1646 # Exit code that means some system file did not exist,
1647 # could not be opened, or had some other kind of error.
1648 self.result = os.EX_OSFILE
1649 raise EnvironmentError(
1650 "executable '%s' is not found or executable." % executable)
1651 self.logger.debug("Running executable: '{app}'"
1652 .format(app=' '.join(self.args)))
1653 env = os.environ.copy()
1654 env.update(self.env)
1655 env["CK_LOG_FILE_NAME"] = "-"
1656 self.process = subprocess.Popen(
1657 self.args, shell=False, env=env, preexec_fn=os.setpgrp,
1658 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
1659 self.wait_for_enter()
1660 out, err = self.process.communicate()
1661 self.logger.debug("Finished running `{app}'".format(app=self.app_name))
1662 self.logger.info("Return code is `%s'" % self.process.returncode)
1663 self.logger.info(single_line_delim)
1664 self.logger.info("Executable `{app}' wrote to stdout:"
1665 .format(app=self.app_name))
1666 self.logger.info(single_line_delim)
1667 self.logger.info(out.decode('utf-8'))
1668 self.logger.info(single_line_delim)
1669 self.logger.info("Executable `{app}' wrote to stderr:"
1670 .format(app=self.app_name))
1671 self.logger.info(single_line_delim)
1672 self.logger.info(err.decode('utf-8'))
1673 self.logger.info(single_line_delim)
1674 self.result = self.process.returncode
1677 if __name__ == '__main__':