IPIP: Add IP{v4,v6} over IP{v4,v6} configured tunnel support.
[vpp.git] / test / run_tests.py
1 #!/usr/bin/env python
2
3 import sys
4 import shutil
5 import os
6 import select
7 import unittest
8 import argparse
9 import time
10 from multiprocessing import Process, Pipe
11 from framework import VppTestRunner
12 from debug import spawn_gdb
13 from log import global_logger
14 from discover_tests import discover_tests
15
16
17 def test_runner_wrapper(suite, keep_alive_pipe, result_pipe, failed_pipe):
18     result = not VppTestRunner(
19         keep_alive_pipe=keep_alive_pipe,
20         failed_pipe=failed_pipe,
21         verbosity=verbose,
22         failfast=failfast).run(suite).wasSuccessful()
23     result_pipe.send(result)
24     result_pipe.close()
25     keep_alive_pipe.close()
26     failed_pipe.close()
27
28
29 class add_to_suite_callback:
30     def __init__(self, suite):
31         self.suite = suite
32
33     def __call__(self, file_name, cls, method):
34         suite.addTest(cls(method))
35
36
37 class Filter_by_class_list:
38     def __init__(self, class_list):
39         self.class_list = class_list
40
41     def __call__(self, file_name, class_name, func_name):
42         return class_name in self.class_list
43
44
45 def suite_from_failed(suite, failed):
46     filter_cb = Filter_by_class_list(failed)
47     return VppTestRunner.filter_tests(suite, filter_cb)
48
49
50 def run_forked(suite):
51     keep_alive_parent_end, keep_alive_child_end = Pipe(duplex=False)
52     result_parent_end, result_child_end = Pipe(duplex=False)
53     failed_parent_end, failed_child_end = Pipe(duplex=False)
54
55     child = Process(target=test_runner_wrapper,
56                     args=(suite, keep_alive_child_end, result_child_end,
57                           failed_child_end))
58     child.start()
59     last_test_temp_dir = None
60     last_test_vpp_binary = None
61     last_test = None
62     result = None
63     failed = set()
64     last_heard = time.time()
65     while True:
66         readable = select.select([keep_alive_parent_end.fileno(),
67                                   result_parent_end.fileno(),
68                                   failed_parent_end.fileno(),
69                                   ],
70                                  [], [], 1)[0]
71         if result_parent_end.fileno() in readable:
72             result = result_parent_end.recv()
73             break
74         if keep_alive_parent_end.fileno() in readable:
75             while keep_alive_parent_end.poll():
76                 last_test, last_test_vpp_binary,\
77                     last_test_temp_dir, vpp_pid = keep_alive_parent_end.recv()
78             last_heard = time.time()
79         if failed_parent_end.fileno() in readable:
80             while failed_parent_end.poll():
81                 failed_test = failed_parent_end.recv()
82                 failed.add(failed_test.__name__)
83             last_heard = time.time()
84         fail = False
85         if last_heard + test_timeout < time.time():
86             fail = True
87             global_logger.critical("Timeout while waiting for child test "
88                                    "runner process (last test running was "
89                                    "`%s' in `%s')!" %
90                                    (last_test, last_test_temp_dir))
91         elif not child.is_alive():
92             fail = True
93             global_logger.critical("Child process unexpectedly died (last "
94                                    "test running was `%s' in `%s')!" %
95                                    (last_test, last_test_temp_dir))
96         if fail:
97             failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
98             lttd = last_test_temp_dir.split("/")[-1]
99             link_path = '%s%s-FAILED' % (failed_dir, lttd)
100             global_logger.error("Creating a link to the failed " +
101                                 "test: %s -> %s" % (link_path, lttd))
102             try:
103                 os.symlink(last_test_temp_dir, link_path)
104             except:
105                 pass
106             api_post_mortem_path = "/tmp/api_post_mortem.%d" % vpp_pid
107             if os.path.isfile(api_post_mortem_path):
108                 global_logger.error("Copying api_post_mortem.%d to %s" %
109                                     (vpp_pid, last_test_temp_dir))
110                 shutil.copy2(api_post_mortem_path, last_test_temp_dir)
111             if last_test_temp_dir and last_test_vpp_binary:
112                 core_path = "%s/core" % last_test_temp_dir
113                 if os.path.isfile(core_path):
114                     global_logger.error("Core-file exists in test temporary "
115                                         "directory: %s!" % core_path)
116                     if d and d.lower() == "core":
117                         spawn_gdb(last_test_vpp_binary, core_path,
118                                   global_logger)
119             child.terminate()
120             result = -1
121             break
122     keep_alive_parent_end.close()
123     result_parent_end.close()
124     failed_parent_end.close()
125     return result, failed
126
127
128 if __name__ == '__main__':
129
130     try:
131         verbose = int(os.getenv("V", 0))
132     except:
133         verbose = 0
134
135     default_test_timeout = 600  # 10 minutes
136     try:
137         test_timeout = int(os.getenv("TIMEOUT", default_test_timeout))
138     except:
139         test_timeout = default_test_timeout
140
141     try:
142         debug = os.getenv("DEBUG")
143     except:
144         debug = None
145
146     parser = argparse.ArgumentParser(description="VPP unit tests")
147     parser.add_argument("-f", "--failfast", action='count',
148                         help="fast failure flag")
149     parser.add_argument("-d", "--dir", action='append', type=str,
150                         help="directory containing test files "
151                              "(may be specified multiple times)")
152     args = parser.parse_args()
153     failfast = True if args.failfast == 1 else False
154
155     suite = unittest.TestSuite()
156     cb = add_to_suite_callback(suite)
157     for d in args.dir:
158         print("Adding tests from directory tree %s" % d)
159         discover_tests(d, cb)
160
161     try:
162         retries = int(os.getenv("RETRIES"))
163     except:
164         retries = 0
165     if retries is None:
166         retries = 0
167     attempts = retries + 1
168     if attempts > 1:
169         print("Perform %s attempts to pass the suite..." % attempts)
170     if debug is None or debug.lower() not in ["gdb", "gdbserver"]:
171         while True:
172             result, failed = run_forked(suite)
173             attempts = attempts - 1
174             print("%s test(s) failed, %s attempt(s) left" %
175                   (len(failed), attempts))
176             if len(failed) > 0 and attempts > 0:
177                 suite = suite_from_failed(suite, failed)
178                 continue
179             sys.exit(result)
180
181     # don't fork if debugging..
182     sys.exit(not VppTestRunner(verbosity=verbose,
183                                failfast=failfast).run(suite).wasSuccessful())