blob: 03b0c36c99432073f84da8753d469a05011f1277 [file] [log] [blame]
Roger Meier41ad4342015-03-24 22:30:40 +01001#
2# Licensed to the Apache Software Foundation (ASF) under one
3# or more contributor license agreements. See the NOTICE file
4# distributed with this work for additional information
5# regarding copyright ownership. The ASF licenses this file
6# to you under the Apache License, Version 2.0 (the
7# "License"); you may not use this file except in compliance
8# with the License. You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing,
13# software distributed under the License is distributed on an
14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15# KIND, either express or implied. See the License for the
16# specific language governing permissions and limitations
17# under the License.
18#
19
20import platform
Nobuaki Sukegawa378b7272016-01-03 17:04:50 +090021import re
Roger Meier41ad4342015-03-24 22:30:40 +010022from itertools import product
23
Nobuaki Sukegawaa6ab1f52015-11-28 15:04:39 +090024from .util import merge_dict
Nobuaki Sukegawa144bbef2016-02-11 13:15:40 +090025from .test import TestEntry
Roger Meier41ad4342015-03-24 22:30:40 +010026
27# Those keys are passed to execution as is.
28# Note that there are keys other than these, namely:
29# delay: After server is started, client start is delayed for the value
30# (seconds).
31# timeout: Test timeout after client is started (seconds).
32# platforms: Supported platforms. Should match platform.system() value.
33# protocols: list of supported protocols
34# transports: list of supported transports
35# sockets: list of supported sockets
Nobuaki Sukegawaf5b795d2015-03-29 14:48:48 +090036#
37# protocols and transports entries can be colon separated "spec:impl" pair
38# (e.g. binary:accel) where test is run for any matching "spec" while actual
39# argument passed to test executable is "impl".
40# Otherwise "spec" is equivalent to "spec:spec" pair.
Konrad Grochowski1f6e3802015-05-18 18:10:06 +020041# (e.g. "binary" is equivalent to "binary:binary" in tests.json)
Nobuaki Sukegawaf5b795d2015-03-29 14:48:48 +090042#
Roger Meier41ad4342015-03-24 22:30:40 +010043VALID_JSON_KEYS = [
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090044 'name', # name of the library, typically a language name
45 'workdir', # work directory where command is executed
46 'command', # test command
47 'extra_args', # args appended to command after other args are appended
48 'remote_args', # args added to the other side of the program
49 'join_args', # whether args should be passed as single concatenated string
50 'env', # additional environmental variable
Roger Meier41ad4342015-03-24 22:30:40 +010051]
52
Nobuaki Sukegawa59310f52016-02-18 01:41:46 +090053DEFAULT_MAX_DELAY = 5
Roger Meier41ad4342015-03-24 22:30:40 +010054DEFAULT_TIMEOUT = 5
55
56
Nobuaki Sukegawa378b7272016-01-03 17:04:50 +090057def _collect_testlibs(config, server_match, client_match=[None]):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090058 """Collects server/client configurations from library configurations"""
59 def expand_libs(config):
60 for lib in config:
61 sv = lib.pop('server', None)
62 cl = lib.pop('client', None)
63 yield lib, sv, cl
Roger Meier41ad4342015-03-24 22:30:40 +010064
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090065 def yield_testlibs(base_configs, configs, match):
66 for base, conf in zip(base_configs, configs):
67 if conf:
68 if not match or base['name'] in match:
69 platforms = conf.get('platforms') or base.get('platforms')
70 if not platforms or platform.system() in platforms:
71 yield merge_dict(base, conf)
Roger Meier41ad4342015-03-24 22:30:40 +010072
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090073 libs, svs, cls = zip(*expand_libs(config))
74 servers = list(yield_testlibs(libs, svs, server_match))
75 clients = list(yield_testlibs(libs, cls, client_match))
76 return servers, clients
Roger Meier41ad4342015-03-24 22:30:40 +010077
78
Nobuaki Sukegawa378b7272016-01-03 17:04:50 +090079def collect_features(config, match):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090080 res = list(map(re.compile, match))
81 return list(filter(lambda c: any(map(lambda r: r.search(c['name']), res)), config))
Nobuaki Sukegawa378b7272016-01-03 17:04:50 +090082
83
84def _do_collect_tests(servers, clients):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090085 def intersection(key, o1, o2):
86 """intersection of two collections.
87 collections are replaced with sets the first time"""
88 def cached_set(o, key):
89 v = o[key]
90 if not isinstance(v, set):
91 v = set(v)
92 o[key] = v
93 return v
94 return cached_set(o1, key) & cached_set(o2, key)
Roger Meier41ad4342015-03-24 22:30:40 +010095
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090096 def intersect_with_spec(key, o1, o2):
97 # store as set of (spec, impl) tuple
98 def cached_set(o):
99 def to_spec_impl_tuples(values):
100 for v in values:
101 spec, _, impl = v.partition(':')
102 yield spec, impl or spec
103 v = o[key]
104 if not isinstance(v, set):
105 v = set(to_spec_impl_tuples(set(v)))
106 o[key] = v
107 return v
108 for spec1, impl1 in cached_set(o1):
109 for spec2, impl2 in cached_set(o2):
110 if spec1 == spec2:
111 name = impl1 if impl1 == impl2 else '%s-%s' % (impl1, impl2)
112 yield name, impl1, impl2
Roger Meier41ad4342015-03-24 22:30:40 +0100113
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900114 def maybe_max(key, o1, o2, default):
115 """maximum of two if present, otherwise defult value"""
116 v1 = o1.get(key)
117 v2 = o2.get(key)
118 return max(v1, v2) if v1 and v2 else v1 or v2 or default
Roger Meier41ad4342015-03-24 22:30:40 +0100119
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900120 def filter_with_validkeys(o):
121 ret = {}
122 for key in VALID_JSON_KEYS:
123 if key in o:
124 ret[key] = o[key]
125 return ret
Roger Meier41ad4342015-03-24 22:30:40 +0100126
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900127 def merge_metadata(o, **ret):
128 for key in VALID_JSON_KEYS:
129 if key in o:
130 ret[key] = o[key]
131 return ret
Roger Meier41ad4342015-03-24 22:30:40 +0100132
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900133 for sv, cl in product(servers, clients):
134 for proto, proto1, proto2 in intersect_with_spec('protocols', sv, cl):
135 for trans, trans1, trans2 in intersect_with_spec('transports', sv, cl):
136 for sock in intersection('sockets', sv, cl):
137 yield {
138 'server': merge_metadata(sv, **{'protocol': proto1, 'transport': trans1}),
139 'client': merge_metadata(cl, **{'protocol': proto2, 'transport': trans2}),
Nobuaki Sukegawa59310f52016-02-18 01:41:46 +0900140 'delay': maybe_max('delay', sv, cl, DEFAULT_MAX_DELAY),
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900141 'timeout': maybe_max('timeout', sv, cl, DEFAULT_TIMEOUT),
142 'protocol': proto,
143 'transport': trans,
144 'socket': sock
145 }
Roger Meier41ad4342015-03-24 22:30:40 +0100146
147
Nobuaki Sukegawa144bbef2016-02-11 13:15:40 +0900148def _filter_entries(tests, regex):
149 if regex:
150 return filter(lambda t: re.search(regex, TestEntry.get_name(**t)), tests)
151 return tests
152
153
154def collect_cross_tests(tests_dict, server_match, client_match, regex):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900155 sv, cl = _collect_testlibs(tests_dict, server_match, client_match)
Nobuaki Sukegawa144bbef2016-02-11 13:15:40 +0900156 return list(_filter_entries(_do_collect_tests(sv, cl), regex))
Nobuaki Sukegawa378b7272016-01-03 17:04:50 +0900157
158
Nobuaki Sukegawa144bbef2016-02-11 13:15:40 +0900159def collect_feature_tests(tests_dict, features_dict, server_match, feature_match, regex):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900160 sv, _ = _collect_testlibs(tests_dict, server_match)
161 ft = collect_features(features_dict, feature_match)
Nobuaki Sukegawa144bbef2016-02-11 13:15:40 +0900162 return list(_filter_entries(_do_collect_tests(sv, ft), regex))