~widelands-dev/widelands/cricket_frog_sounds

6689.1.1 by Holger Rapp
First unfinished version of a regression test suite.
1
#!/usr/bin/env python
2
# encoding: utf-8
3
6689.1.4 by Holger Rapp
All tests now run and pass on my system.
4
from glob import glob
5
import argparse
6
import os
7
import re
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
8
import shutil
9
import subprocess
6803 by Holger Rapp
regression_test.py returns an error to the system on failure.
10
import sys
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
11
import tempfile
6689.1.2 by Holger Rapp
Brought back original content of regression_test.py.
12
import unittest
6849 by Tino
allow regression test on windows
13
import platform
6689.1.4 by Holger Rapp
All tests now run and pass on my system.
14
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
15
def out(string):
16
    sys.stdout.write(string)
17
    sys.stdout.flush()
18
19
class WidelandsTestCase(unittest.TestCase):
20
    do_use_random_directory = True
21
    path_to_widelands_binary = None
22
    keep_output_around = False
23
24
    def __init__(self, test_script, **wlargs):
25
        unittest.TestCase.__init__(self)
26
        self._test_script = test_script
27
        self._wlargs = wlargs
28
29
    def __str__(self):
30
        return self._test_script
31
32
    def setUp(self):
33
        if self.do_use_random_directory:
34
            self.run_dir = tempfile.mkdtemp(prefix="widelands_regression_test")
35
        else:
36
            self.run_dir = os.path.join(tempfile.gettempdir(), "widelands_regression_test", self.__class__.__name__)
37
            if os.path.exists(self.run_dir):
38
                if not self.keep_output_around:
39
                    shutil.rmtree(self.run_dir)
6812.1.18 by Holger Rapp
No more multiple inheritance in scripting layer. All clearer now.
40
                    os.makedirs(self.run_dir)
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
41
            else:
42
                os.makedirs(self.run_dir)
43
        self.widelands_returncode = 0
44
45
    def run(self, result=None):
46
        self.currentResult = result # remember result for use in tearDown
47
        unittest.TestCase.run(self, result)
48
49
    def tearDown(self):
50
        if self.currentResult.wasSuccessful() and not self.keep_output_around:
51
            shutil.rmtree(self.run_dir)
52
6820.2.10 by Holger Rapp
Fixed a bug in the test runner and added a new test case.
53
    def run_widelands(self, wlargs, which_time):
54
        """Run Widelands with the given 'wlargs'. 'which_time' is an integer
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
55
        defining the number of times Widelands has been run this test case
56
        (i.e. because we might load a saved game from an earlier run. This will
57
        impact the filenames for stdout.txt.
58
59
        Returns the stdout filename."""
60
        stdout_filename = os.path.join(self.run_dir, "stdout_%02i.txt" % which_time)
61
        if (os.path.exists(stdout_filename)):
62
            os.unlink(stdout_filename)
63
64
        with open(stdout_filename, 'a') as stdout_file:
65
            args = [self.path_to_widelands_binary, '--verbose=true',
7000 by Jens Beyer (Qcumber-some)
force standard english as language for test; use predictable datadir
66
                    '--datadir=%s' % os.path.dirname(__file__), '--homedir=%s' % self.run_dir,
67
                    '--disable_fx=true', '--disable_music=true', '--language=en_US' ]
6820.2.10 by Holger Rapp
Fixed a bug in the test runner and added a new test case.
68
            args += [ "--%s=%s" % (key, value) for key, value in wlargs.iteritems() ]
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
69
70
            widelands = subprocess.Popen(
71
                    args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
72
            while 1:
73
                line = widelands.stdout.readline()
74
                if not line:
75
                    break
76
                stdout_file.write(line)
77
                stdout_file.flush()
78
            widelands.communicate()
6849 by Tino
allow regression test on windows
79
            if platform.system() == "Windows":
80
                 win_stdout = self.path_to_widelands_binary.replace("widelands.exe","stdout.txt")
81
                 win_stderr = self.path_to_widelands_binary.replace("widelands.exe","stderr.txt")
82
                 with open(win_stdout,"r") as f:
83
                     for line in f:
84
                         stdout_file.write(line)
85
                     stdout_file.flush()
86
                 if (os.path.exists(win_stderr)):
87
                     with open(win_stderr,"r") as f:
88
                         for line in f:
89
                             stdout_file.write(line)
90
                         stdout_file.flush()
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
91
92
            self.widelands_returncode = widelands.returncode
93
        return stdout_filename
94
95
    def runTest(self):
96
        out("\n  Running Widelands ... ")
97
        stdout_filename = self.run_widelands(self._wlargs, 0)
98
        stdout = open(stdout_filename, "r").read()
99
        self.verify_success(stdout, stdout_filename)
100
101
        find_saves = lambda stdout: re.findall("Script requests save to: (\w+)$", stdout, re.M)
102
        savegame_done = { fn: False for fn in find_saves(stdout) }
103
        which_time = 1
104
        while not all(savegame_done.values()):
6820.2.11 by Holger Rapp
Added two more test that remove the ship when a ware should be transported.
105
            for savegame in sorted(savegame_done):
106
                if not savegame_done[savegame]: break
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
107
            out("  Loading savegame: %s ... " % savegame)
108
            stdout_filename = self.run_widelands({ "loadgame": os.path.join(
109
                self.run_dir, "save", "%s.wgf" % savegame) }, which_time)
110
            which_time += 1
111
            stdout = open(stdout_filename, "r").read()
112
            for new_save in find_saves(stdout):
113
                if new_save not in savegame_done:
114
                    savegame_done[new_save] = False
115
            savegame_done[savegame] = True
116
            self.verify_success(stdout, stdout_filename)
117
118
    def verify_success(self, stdout, stdout_filename):
119
        common_msg = "Analyze the files in %s to see why this test case failed. Stdout is\n  %s\n" % (
120
                self.run_dir, stdout_filename)
121
        self.assertTrue(self.widelands_returncode == 0,
122
            "Widelands exited abnormally. %s" % common_msg
123
        )
124
        self.assertTrue("All Tests passed" in stdout,
125
            "Not all tests pass. %s." % common_msg
126
        )
127
        out("done.\n")
128
        if self.keep_output_around:
129
            out("    stdout: %s\n" % stdout_filename)
6689.1.4 by Holger Rapp
All tests now run and pass on my system.
130
131
def parse_args():
132
    p = argparse.ArgumentParser(description=
133
        "Run the regression tests suite."
134
    )
135
136
    p.add_argument("-r", "--regexp", type=str,
137
        help = "Run only the tests from the files which filename matches."
138
    )
139
    p.add_argument("-n", "--nonrandom", action="store_true", default = False,
140
        help = "Do not randomize the directories for the tests. This is useful "
141
        "if you want to run a test more often than once and not reopen stdout.txt "
142
        "in your editor."
143
    )
6801 by Holger Rapp
Fix more bugs around seafaring, especially related to persistence.
144
    p.add_argument("-k", "--keep-around", action="store_true", default = False,
145
        help = "Keep the output files around even when a test terminates successfully."
146
    )
6689.1.6 by Holger Rapp
Let the caller specify the widelands binary to use for regression testing.
147
    p.add_argument("-b", "--binary", type=str,
148
        help = "Run this binary as Widelands. Otherwise some default paths are searched."
149
    )
150
151
    args = p.parse_args()
152
153
    if args.binary is None:
154
        potential_binaries = (
155
            glob("widelands") +
156
            glob("src/widelands") +
157
            glob("../*/src/widelands")
158
        )
159
        if not potential_binaries:
160
            p.error("No widelands binary found. Please specify with -b.")
161
        args.binary = potential_binaries[0]
162
    return args
163
6689.1.2 by Holger Rapp
Brought back original content of regression_test.py.
164
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
165
def discover_loadgame_tests(regexp, suite):
166
    """Add all tests using --loadgame to the 'suite'."""
167
    for fixture in glob(os.path.join("test", "save", "*")):
168
        if not os.path.isdir(fixture):
169
            continue
170
        savegame = glob(os.path.join(fixture, "*.wgf"))[0]
171
        for test_script in glob(os.path.join(fixture, "test*.lua")):
172
            if regexp is not None and not re.search(regexp, test_script):
173
                continue
174
            suite.addTest(
175
                    WidelandsTestCase(test_script,
176
                        loadgame=savegame, script=test_script))
177
178
def discover_scenario_tests(regexp, suite):
179
    """Add all tests using --scenario to the 'suite'."""
180
    for wlmap in glob(os.path.join("test", "maps", "*")):
181
        if not os.path.isdir(wlmap):
182
            continue
183
        for test_script in glob(os.path.join(wlmap, "scripting", "test*.lua")):
184
            if regexp is not None and not re.search(regexp, test_script):
185
                continue
186
            suite.addTest(
187
                    WidelandsTestCase(test_script,
188
                        scenario=wlmap, script=test_script))
189
190
def discover_editor_tests(regexp, suite):
191
    """Add all tests needing --editor to the 'suite'."""
192
    for wlmap in glob(os.path.join("test", "maps", "*")):
193
        if not os.path.isdir(wlmap):
194
            continue
195
        for test_script in glob(os.path.join(wlmap, "scripting", "editor_test*.lua")):
196
            if regexp is not None and not re.search(regexp, test_script):
197
                continue
198
            suite.addTest(
199
                    WidelandsTestCase(test_script,
200
                        editor=wlmap, script=test_script))
201
6689.1.2 by Holger Rapp
Brought back original content of regression_test.py.
202
def main():
6689.1.4 by Holger Rapp
All tests now run and pass on my system.
203
    args = parse_args()
204
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
205
    WidelandsTestCase.path_to_widelands_binary = args.binary
6689.1.6 by Holger Rapp
Let the caller specify the widelands binary to use for regression testing.
206
    print "Using '%s' binary." % args.binary
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
207
    WidelandsTestCase.do_use_random_directory = not args.nonrandom
208
    WidelandsTestCase.keep_output_around = args.keep_around
6689.1.4 by Holger Rapp
All tests now run and pass on my system.
209
210
    all_files = [os.path.basename(filename) for filename in glob("test/test_*.py") ]
211
    if args.regexp:
212
        all_files = [filename for filename in all_files if re.search(args.regexp, filename) ]
213
214
    all_modules = [ "test.%s" % filename[:-3] for filename in all_files ]
215
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
216
    suite = unittest.TestSuite()
217
    discover_loadgame_tests(args.regexp, suite)
218
    discover_scenario_tests(args.regexp, suite)
219
    discover_editor_tests(args.regexp, suite)
6689.1.2 by Holger Rapp
Brought back original content of regression_test.py.
220
6820.2.7 by Holger Rapp
Finally found a better way to define tests that do not need all the redundant python files.
221
    return unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
6689.1.2 by Holger Rapp
Brought back original content of regression_test.py.
222
223
if __name__ == '__main__':
6803 by Holger Rapp
regression_test.py returns an error to the system on failure.
224
    sys.exit(0 if main() else 1)