File: testsuite.py

package info (click to toggle)
libreswan 5.2-2.3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 81,644 kB
  • sloc: ansic: 129,988; sh: 32,018; xml: 20,646; python: 10,303; makefile: 3,022; javascript: 1,506; sed: 574; yacc: 511; perl: 264; awk: 52
file content (392 lines) | stat: -rw-r--r-- 15,282 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# Lists the tests
#
# Copyright (C) 2016-2016 Andrew Cagney <cagney@gnu.org>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.  See <https://www.gnu.org/licenses/gpl2.txt>.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.

import os
import re
import collections

from fab import logutil
from fab import utilsdir
from fab import scripts
from fab import hosts

class Test:

    def __init__(self, test_directory, testing_directory,
                 saved_test_output_directory=None,
                 kind="kvmplutotest", status="good"):
        # basics
        self.kind = kind
        self.status = status

        # The test's name is always identical to the test directory's
        # name (aka basename).  However, since TEST_DIRECTORY could be
        # relative (for instance "."  or "./..") it first needs to be
        # made absolute before the basename can be extracted.
        test_directory = os.path.realpath(test_directory)
        # The test's name is the same as the directory's basename.
        self.name = os.path.basename(test_directory)
        self.full_name = "test " + self.name

        self.logger = logutil.getLogger(self.name)

        # Construct the test's relative directory path such that it
        # always contains the test directory name (i.e., the test
        # name) as context.  For instance: "."  gets rewritten as
        # ../<test>; and ".." gets rewritten as "../../<test>".  This
        # ensures that displayed paths always include some context.
        # For instance, given "kvmresult.py .", "../<test> passed"
        # (and not ". passed") will be displayed.
        self.directory = os.path.join(os.path.relpath(os.path.dirname(test_directory)), self.name)

        # Directory where the next test run's output should be
        # written.
        self.output_directory = os.path.join(self.directory, "OUTPUT")

        # Directory containing saved output from a previous test run.
        # If the test's output directory was explicitly specified, say
        # as a parameter to kvmrunner.py vis:
        #
        #   kvmresults.py testing/pluto/<test>/OUTPUT.OLD
        #   kvmresults.py testing/pluto/OUTPUT/<test>
        #
        # than that directory, and not the next output-directory, will
        # be passed in and saved here.  Otherwise it is None, and the
        # OUTPUT_DIRECTORY should be used.
        if saved_test_output_directory:
            self.saved_output_directory = saved_test_output_directory
        else:
            self.saved_output_directory = None

        # The testing_directory to use when performing post.mortem
        # tasks such as running the sanitizer.
        #
        # Since test.directory may be incomplete (sanitizers directory
        # may be missing), use the testing directory belonging to this
        # script.
        if testing_directory:
            # trust it
            self._testing_directory = os.path.relpath(testing_directory)
        else:
            self._testing_directory = utilsdir.relpath("..")

        # Get an ordered list of {guest_name:,command:} to run.
        self.commands = scripts.commands(self.directory, self.logger)

        # Just assume any non-empty hosts mentioned in scripts needs
        # to run.
        guests = hosts.Set()
        for command in self.commands:
            if command.guest:
                guests.add(command.guest)
        self.guests = sorted(guests)

        # remember all the platforms that are required
        platforms = hosts.Set()
        for guest in guests:
            if guest.platform:
                platforms.add(guest.platform)
        self.platforms = sorted(platforms)

    def testing_directory(self, *path):
        return os.path.relpath(os.path.join(self._testing_directory, *path))

    def __str__(self):
        return self.full_name


# Load the tetsuite defined by TESTLIST

class Testsuite:

    def __init__(self, logger, testlist,
                 testing_directory):
        self.directory = os.path.dirname(testlist)
        self.testlist = collections.OrderedDict()
        line_nr = 0
        ok = True
        with open(testlist, 'r') as testlist_file:
            for line in testlist_file:
                line_nr += 1
                # clean up the line, but save the original for logging
                orig = line.strip("\r\n")
                if "#" in line:
                    line = line[:line.find("#")]
                line = line.strip()
                # the two log messages should align
                if not line:
                    logger.debug("empty: %s", orig)
                    continue
                else:
                    logger.debug("input: %s", orig)
                # Extract the fields
                fields = line.split()
                if len(fields) < 3:
                    # This is serious
                    logger.error("****** %s:%d: line has too few fields: %s",
                                 testlist, line_nr, orig)
                    ok = False
                    continue
                if len(fields) > 4:
                    # This is serious
                    logger.error("****** %s:%d: line has too many fields: %s",
                                 testlist, line_nr, orig)
                    ok = False
                    continue
                kind = fields[0]
                name = fields[1]
                status = fields[2]
                # pr = fields[3]?
                test = Test(kind=kind, status=status,
                            test_directory=os.path.join(self.directory, name),
                            testing_directory=testing_directory)
                logger.debug("test directory: %s", test.directory)
                if not os.path.exists(test.directory):
                    # This is serious.  However, stumble on.
                    logger.error("****** %s:%d: invalid test %s: test directory not found: %s",
                                 testlist, line_nr,
                                 test.name, test.directory)
                    ok = False
                    continue
                if test.name in self.testlist:
                    # This is serious.
                    #
                    # However, after reporting continue and select the
                    # second entry.  Preserves historic behaviour, as
                    # selecting the first entry would invalidate
                    # earlier test results.
                    first = self.testlist[test.name]
                    logger.error("****** %s:%d: test %s %s %s is a duplicate of %s %s %s",
                                 testlist, line_nr,
                                 test.kind, test.name, test.status,
                                 first.kind, first.name, first.status)
                    ok = False
                # an OrderedDict which saves insertion order
                self.testlist[test.name] = test

        if not ok:
            raise Exception("TESTLIST file %s invalid" % (testlist))

    def __iter__(self):
        return self.testlist.values().__iter__()

    def __contains__(self, index):
        return index in self.testlist

    def __getitem__(self, index):
        return self.testlist[index]

    def __len__(self):
        return self.testlist.__len__()


def add_arguments(parser):

    group = parser.add_argument_group("testsuite arguments",
                                      "options for configuring the testsuite or test directories")

    group.add_argument("--testing-directory", metavar="DIRECTORY",
                       default=utilsdir.relpath(".."),
                       help="directory containing 'sanitizers/', 'default-testparams.sh' and 'pluto' along with other scripts and files used to perform test postmortem; default: '%(default)s/'")


def log_arguments(logger, args):
    logger.info("Testsuite arguments:")
    logger.info("  testing-directory: '%s'", args.testing_directory)


def is_test_directory(directory):
    """Heuristic to detect an individual test directory"""
    for h in ["description.txt", "eastinit.sh"]:
        if os.path.exists(os.path.join(directory, h)):
            return True
    return False

def is_test_output_directory(directory):
    """Heuristic to detect a test output directory"""
    for h in ["debug.log", "east.console.verbose.txt"]:
        if os.path.exists(os.path.join(directory, h)):
            return True
    return False


def load(logger, log_level, args,
         testsuite_directory=None):
    """Load the single testsuite (TESTLIST) found in DIRECTORY

    A testsutite is defined by the presence of TESTLIST in DIRECTORY,
    it returns what looks like a dictionary indexable by test name.

    """

    # Is DIRECTORY a testsuite or a testlist file?  For instance:
    # testing/pluto or testing/pluto/TESTLIST.

    if os.path.isfile(testsuite_directory):
        testlist = testsuite_directory
        testsuite_directory = os.path.dirname(testsuite_directory)
        logger.log(log_level, "'%s' is a TESTLIST file", testlist)
    else:
        head = "testing/pluto"
        path = "TESTLIST"
        while True:
            testlist = os.path.join(testsuite_directory, path)
            if os.path.isfile(testlist):
                logger.log(log_level, "'%s' is a testsuite directory", testsuite_directory)
                break
            if not head:
                logger.debug("'%s' does not appear to be a testsuite directory", testsuite_directory)
                return None
            head, tail = os.path.split(head)
            path = os.path.join(tail, path)

    return Testsuite(logger, testlist,
                     testing_directory=args.testing_directory)


def append_test(logger, log_level, tests, args, directory,
                message,
                test=None,
                test_directory=None,
                saved_test_output_directory=None):
    """If it looks like a test, append it"""

    logger.debug("does '%s' contain %s?", directory, message)

    # simple checks
    if saved_test_output_directory \
    and not is_test_output_directory(saved_test_output_directory):
        return False

    if test:
        assert not test_directory
        test_directory = os.path.join(args.testing_directory, "pluto", test)

    if not test_directory:
        return False

    if not is_test_directory(test_directory):
        return False

    test = Test(test_directory=test_directory,
                saved_test_output_directory=saved_test_output_directory,
                testing_directory=args.testing_directory)
    logger.log(log_level, "directory '%s' contains %s '%s'", directory, message, test.name)
    tests.append(test)

    return True


def load_testsuite_or_tests(logger, directories, args,
                            log_level=logutil.DEBUG):

    # Deal with each directory in turn.  It might be a test,
    # testsuite, testlist, or output.

    tests = []
    for directory in directories:

        logger.debug("is %s a test or testsuite?", directory)

        # Python's basename is close to useless - given "foo/" it
        # returns "" and not "foo" - get around this.
        if not os.path.basename(directory):
            logger.debug("chopping / off '%s'", directory)
            directory = os.path.dirname(directory)

        # perhaps directory/file is a testsuite?
        testsuite = load(logger, log_level, args, testsuite_directory=directory)
        if testsuite:
            # more efficient?
            for test in testsuite:
                tests.append(test)
            continue

        # kvmrunner.py testing/pluto/<test>
        if append_test(logger, log_level, tests, args, directory,
                       "the test",
                       test_directory=directory):
            continue

        # kvmrunner.py testing/pluto/<test>/OUTPUT
        #
        # This works when OUTPUT contains results.
        if os.path.basename(directory) == "OUTPUT" \
        and append_test(logger, log_level, tests, args, directory,
                       "output from the test",
                       test_directory=os.path.dirname(directory),
                       saved_test_output_directory=directory):
            continue

        # rm testing/pluto/<test>/OUTPUT/* ; kvmrunner.py testing/pluto/<test>/OUTPUT/
        # ???

        # kvmrunner.py BACKUP/YYYYMMDD/<test>/OUTPUT
        if os.path.basename(directory) == "OUTPUT" \
        and append_test(logger, log_level, tests, args, directory,
                       "saved output from the test",
                       test=os.path.basename(os.path.dirname(directory)),
                       saved_test_output_directory=directory):
            continue

        # kvmrunner.py BACKUP/YYYYMMDD/<test> [/OUTPUT/]
        if append_test(logger, log_level, tests, args, directory,
                       "OUTPUT/ which contains saved output from the test",
                       test=os.path.basename(directory),
                       saved_test_output_directory=os.path.join(directory, "OUTPUT")):
            continue

        # cp testing/pluto/<test>/OUTPUT <test> ; kvmrunner.py <test>
        if append_test(logger, log_level, tests, args, directory,
                       "saved output from the test",
                       test=os.path.basename(directory),
                       saved_test_output_directory=directory):
            continue

        # kvmrunner.py BACKUP/YYYYMMDD/ [<test>/OUTPUT]
        found = False
        for subdir in os.listdir(directory):
            if append_test(logger, log_level, tests, args, directory,
                           subdir + "/OUTPUT/ which contains saved output from the test",
                           test=subdir,
                           saved_test_output_directory=os.path.join(directory, subdir, "OUTPUT")):
                found = True
        if found:
            continue

        # kvmrunner.py BACKUP/YYYYMMDD/ [<test:OUTPUT>]
        found = False
        for subdir in os.listdir(directory):
            if append_test(logger, log_level, tests, args, directory,
                           subdir + "/ which contains saved output from the test",
                           test=subdir,
                           saved_test_output_directory=os.path.join(directory, subdir)):
                found = True
        if found:
            continue

        # rm testing/pluto/TESTLIST ; kvmrunner.py testing/pluto/
        found = False
        for subdir in os.listdir(directory):
            if append_test(logger, log_level, tests, args, directory,
                           subdir + "/ contains the test",
                           test_directory=os.path.join(directory, subdir)):
                found = True
        if found:
            continue

        logger.error("directory '%s' is invalid", directory)
        continue

    return tests