extract_qmtest_metadata.py 4.53 KB
Newer Older
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python
'''
Simple script to extract metadata (dependencies, labels) from QMTest tests (.qmt
files) and suites (.qms files), and report them as declaration of CTest test
properties.
'''
__author__ = 'Marco Clemencic <marco.clemencic@cern.ch>'

import os
10
import platform
11
12
import xml.etree.ElementTree as ET
import collections
13
import re
14
import six
15

Marco Clemencic's avatar
Marco Clemencic committed
16

17
18
19
20
21
22
23
24
25
26
def qmt_filename_to_name(path):
    '''
    convert the relative path to a .qmt/.qms file to the canonical QMTest test
    name.

    For example:

    >>> qmt_filename_to_name('some_suite.qms/sub.qms/mytest.qmt')
    'some_suite.sub.mytest'
    '''
Gitlab CI's avatar
Gitlab CI committed
27
28
    return '.'.join(
        re.sub(r'\.qm[st]$', '', p) for p in path.split(os.path.sep))
29

Marco Clemencic's avatar
Marco Clemencic committed
30

31
32
33
34
35
36
37
38
39
40
41
42
def fix_test_name(name, pkg):
    '''
    Convert the QMTest test name to the name used in CTest.

    >>> fix_test_name('package.bug.123', 'Package')
    'Package.bug.123'

    >>> fix_test_name('simple', 'Package')
    'Package.simple'
    '''
    return re.sub(r'^(%s\.)?' % pkg.lower(), '%s.' % pkg, name)

Marco Clemencic's avatar
Marco Clemencic committed
43

44
45
46
47
48
49
50
51
52
53
def find_files(rootdir, ext):
    '''
    Find recursively all the files in a directory with a given extension.
    '''
    for dirpath, _dirnames, filenames in os.walk(rootdir):
        for filename in filenames:
            if os.path.splitext(filename)[1] == ext:
                yield os.path.join(dirpath, filename)


54
55
56
57
58
59
60
61
62
63
64
65
def parse_xml(path):
    '''
    Return the parsed tree, handling exceptions if needed.
    '''
    try:
        return ET.parse(path)
    except ET.ParseError as e:
        sys.stderr.write('ERROR: could not parse {}\n{}\n'.format(path, e))
        sys.stderr.flush()
        exit(1)


66
67
68
69
70
71
72
73
74
75
76
77
def analyze_deps(pkg, rootdir):
    '''
    Collect dependencies from the QMTest tests in a directory and report them
    to stdout as CMake commands.

    @param pkg: name of the package (used to fix the name of the tests to match
                the CMake ones
    @param rootdir: directory containing the QMTest tests (usually tests/qmtest)
    '''
    prereq_xpath = 'argument[@name="prerequisites"]/set/tuple/text'
    for path in find_files(rootdir, '.qmt'):
        name = qmt_filename_to_name(os.path.relpath(path, rootdir))
78
        name = fix_test_name(name, pkg)
79

80
81
        tree = parse_xml(path)

Gitlab CI's avatar
Gitlab CI committed
82
83
84
        prereqs = [
            fix_test_name(el.text, pkg) for el in tree.findall(prereq_xpath)
        ]
85
        if prereqs:
Gitlab CI's avatar
Gitlab CI committed
86
87
            print('set_property(TEST {0} APPEND PROPERTY DEPENDS {1})'.format(
                name, ' '.join(prereqs)))
88

Marco Clemencic's avatar
Marco Clemencic committed
89

90
91
92
93
94
95
96
97
98
99
100
def analyze_suites(pkg, rootdir):
    '''
    Find all the suites (.qms files) defined in a directory and use it as a
    label for the tests in it.
    '''
    labels = collections.defaultdict(list)

    tests_xpath = 'argument[@name="test_ids"]/set/text'
    suites_xpath = 'argument[@name="suite_ids"]/set/text'
    for path in find_files(rootdir, '.qms'):
        name = qmt_filename_to_name(os.path.relpath(path, rootdir))
101
        name = fix_test_name(name, pkg)
102

103
        tree = parse_xml(path)
104

Gitlab CI's avatar
Gitlab CI committed
105
106
        labels[name].extend(
            fix_test_name(el.text, pkg) for el in tree.findall(tests_xpath))
107
108
109
110
111
112
113
114

        if tree.findall(suites_xpath):
            sys.stderr.write(('WARNING: %s: suites of suites are '
                              'not supported yet\n') % path)
            sys.stderr.flush()

    # transpose the dictionary of lists
    test_labels = collections.defaultdict(set)
115
    for label, tests in six.iteritems(labels):
116
117
118
        for test in tests:
            test_labels[test].add(label)

119
    for test, labels in six.iteritems(test_labels):
Gitlab CI's avatar
Gitlab CI committed
120
121
        print('set_property(TEST {0} APPEND PROPERTY LABELS {1})'.format(
            test, ' '.join(labels)))
122

Marco Clemencic's avatar
Marco Clemencic committed
123

124
125
126
127
def analyze_disabling(pkg, rootdir):
    '''
    Set the label 'disabled' for tests that are not supported on a platform.
    '''
Gitlab CI's avatar
Gitlab CI committed
128
129
    platform_id = (os.environ.get('BINARY_TAG') or os.environ.get('CMTCONFIG')
                   or platform.platform())
130
131
132
133
134
135

    unsupp_xpath = 'argument[@name="unsupported_platforms"]/set/text'
    for path in find_files(rootdir, '.qmt'):
        name = qmt_filename_to_name(os.path.relpath(path, rootdir))
        name = fix_test_name(name, pkg)

136
        tree = parse_xml(path)
137
        # If at least one regex matches the test is disabled.
Gitlab CI's avatar
Gitlab CI committed
138
139
140
141
        skip_test = [
            None for el in tree.findall(unsupp_xpath)
            if re.search(el.text, platform_id)
        ]
142
        if skip_test:
Gitlab CI's avatar
Gitlab CI committed
143
144
            print('set_property(TEST {0} APPEND PROPERTY LABELS disabled)'.
                  format(name))
145

146
147
148
149
150

if __name__ == '__main__':
    import sys
    analyze_deps(*sys.argv[1:])
    analyze_suites(*sys.argv[1:])
151
    analyze_disabling(*sys.argv[1:])