1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# ####################################################################
5#  Copyright (C) 2005-2019 by the FIFE team
6#  http://www.fifengine.net
7#  This file is part of FIFE.
8#
9#  FIFE is free software; you can redistribute it and/or
10#  modify it under the terms of the GNU Lesser General Public
11#  License as published by the Free Software Foundation; either
12#  version 2.1 of the License, or (at your option) any later version.
13#
14#  This library is distributed in the hope that it will be useful,
15#  but WITHOUT ANY WARRANTY; without even the implied warranty of
16#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17#  Lesser General Public License for more details.
18#
19#  You should have received a copy of the GNU Lesser General Public
20#  License along with this library; if not, write to the
21#  Free Software Foundation, Inc.,
22#  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23# ####################################################################
24
25from __future__ import print_function
26from builtins import input
27import os, re, sys, optparse, unittest
28
29def genpath(somepath):
30	return os.path.sep.join(somepath.split('/'))
31
32def print_header(text):
33	print('\n')
34	print(80 * '=')
35	print(text)
36	print(80 * '-')
37
38def resolve_test_progs(sconscript_filename):
39	""" Get the names of all test programs by evaluating the SConscript file """
40	reprg = re.compile(r"""^env.Program\(["'](.*?)['"]""")
41	progs = []
42	for line in open(sconscript_filename):
43		m = reprg.match(line.strip())
44		if m:
45			progs.append(m.group(1))
46	return progs
47
48def resolve_test_modules(directory):
49	pythonfilenames = [p for p in os.listdir(directory) if len(p) > 3 and p[-3:] == '.py']
50	modname = directory.replace(os.path.sep, '.') + '.'
51	modules = []
52	skipped_filenames = ('test_all.py',)
53	for p in pythonfilenames:
54		skip = False
55		for s in skipped_filenames:
56			if p.find(s) != -1:
57				skip = True
58		if p[0] == '_':
59			skip = True
60		if not skip:
61			modules.append(modname + p[:-3])
62	return modules
63
64def run_core_tests(progs):
65	prevdir = os.getcwd()
66
67	errors, failures = [], []
68	for prog in progs:
69		print('\n===== Running %s =====' % prog)
70		if os.system(os.sep.join(('build','tests','debug', prog))):
71			errors.append(prog)
72	os.chdir(prevdir)
73	return errors, failures
74
75def get_dynamic_imports(modules):
76	imported = []
77	for module in modules:
78		m = __import__(module)
79		for part in module.split('.')[1:]:
80			m = getattr(m, part)
81		imported.append(m)
82	return imported
83
84def run_test_modules(modules):
85	imported = get_dynamic_imports(modules)
86	suites = []
87	for m in imported:
88		try:
89			for c in m.__dict__['TEST_CLASSES']:
90				suites.append(unittest.TestLoader().loadTestsFromTestCase(c))
91		except (AttributeError, KeyError):
92			pass
93	mastersuite = unittest.TestSuite(suites)
94	runner = unittest.TextTestRunner(verbosity=2)
95	result = runner.run(mastersuite)
96	return [e[1] for e in result.errors], [f[1] for f in result.failures]
97
98def run_all(tests):
99	def print_errors(txt, errs):
100		if errs:
101			print(txt + ':')
102			for msg in errs:
103				print('  ' + msg)
104
105	core_errors, core_failures = run_core_tests(tests['core'])
106	swig_errors, swig_failures = run_test_modules(tests['swig'])
107	ext_errors, ext_failures = run_test_modules(tests['ext'])
108
109	print(80 * '=')
110	errorsfound = False
111
112	if core_errors or core_failures:
113		print_errors('Errors in core tests', core_errors)
114		print_errors('Failures in core tests', core_failures)
115		errorsfound = True
116	else:
117		print('No Core errors found')
118
119	if swig_errors or swig_failures:
120		print_errors('Errors in SWIG tests', swig_errors)
121		print_errors('Failures in SWIG tests', swig_failures)
122		errorsfound = True
123	else:
124		print('No SWIG errors found')
125
126	if swig_errors or swig_failures:
127		print_errors('Errors in extensions tests', ext_errors)
128		print_errors('Failures in extensions tests', ext_failures)
129		errorsfound = True
130	else:
131		print('No Extensions errors found')
132
133	print(80 * '=')
134	if errorsfound:
135		print('ERROR. One or more tests failed!')
136	else:
137		print('OK. All tests ran succesfully!')
138	print('')
139
140def quit(dummy):
141	sys.exit(0)
142
143def run(automatic, selected_cases):
144	index = 0
145	tests = {}
146
147	core_tests = resolve_test_progs(genpath('tests/core_tests/SConscript'))
148	for t in core_tests:
149		tests[index] = ('Core tests', t, [t], run_core_tests)
150		index += 1
151	tests[index] = ('Core tests', 'all', core_tests, run_core_tests)
152	index += 1
153
154	swig_tests = resolve_test_modules(genpath('tests/swig_tests'))
155	for t in swig_tests:
156		tests[index] = ('SWIG tests', t, [t], run_test_modules)
157		index += 1
158	tests[index] = ('SWIG tests', 'all', swig_tests, run_test_modules)
159	index += 1
160
161	extension_tests = resolve_test_modules(genpath('tests/extension_tests'))
162	for t in extension_tests:
163		tests[index] = ('Extension tests', t, [t], run_test_modules)
164		index += 1
165	tests[index] = ('Extension tests', 'all', extension_tests, run_test_modules)
166	index += 1
167
168	alltests = {'core': core_tests, 'swig': swig_tests, 'ext': extension_tests}
169	tests[index] = ('Other', 'Run all tests', alltests, run_all)
170	tests[index+1] = ('Other', 'Cancel and quit', None, quit)
171
172	if (not automatic) and (not selected_cases):
173		selection = None
174		while True:
175			print('Select test module to run:')
176			prevheader = ''
177			for ind in sorted(tests.keys()):
178				header, name, params, fn = tests[ind]
179				if header != prevheader:
180					print(header)
181					prevheader = header
182				print('  %d) %s' % (ind, name))
183			selection = input('-> : ')
184
185			try:
186				selection = int(selection)
187				if (selection < 0) or (selection > max(tests.keys())):
188					raise ValueError
189				break
190			except ValueError:
191				print('Please enter number between 0-%d\n' % max(tests.keys()))
192				continue
193		header, name, params, fn = tests[selection]
194		fn(params)
195	elif (selected_cases):
196		for case in selected_cases:
197			try:
198				caseid = int(case)
199				if (caseid < 0) or (caseid > max(tests.keys())):
200					raise ValueError
201				header, name, params, fn = tests[caseid]
202				fn(params)
203			except ValueError:
204				print('No test case with value %s found' % case)
205	else:
206		run_all(alltests)
207
208def main():
209	usage = 'usage: %prog [options] [args]\n' + \
210		'This is a test runner.\n' + \
211		'It enables you to test functionalities of fifengine core, extensions and the generated swig interface.\n' + \
212		'You can give a list of test ids as arguments to the script.\n' + \
213		'This is useful when running a test over and over again with little changes.\n' + \
214		'Available test ids can be seen from interactive menu (run script without any parameters).\n' + \
215		'You can also use "-a" to run all tests from the CLI.'
216	parser = optparse.OptionParser(usage)
217	parser.add_option("-a", "--automatic",
218			action="store_true", dest="automatic", default=False,
219			help="In case selected, runs all the tests automatically")
220	options, args = parser.parse_args()
221	run(options.automatic, args)
222
223
224
225
226if __name__ == '__main__':
227	main()
228