• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

testit-tms / adapters-python / 17790047561

17 Sep 2025 07:24AM UTC coverage: 43.237% (-0.2%) from 43.443%
17790047561

Pull #202

github

web-flow
Merge a14ece727 into b59427724
Pull Request #202: fix: TMS-35114: add threads for creating and updating autotests with …

61 of 177 new or added lines in 5 files covered. (34.46%)

117 existing lines in 3 files now uncovered.

1285 of 2972 relevant lines covered (43.24%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/testit-adapter-pytest/src/testit_adapter_pytest/utils.py
1
import hashlib
×
2
import logging
×
3
import re
×
4
import traceback
×
5
import typing
×
6
import pytest
×
7

UNCOV
8
from traceback import format_exception_only
×
9

UNCOV
10
from testit_python_commons.models.link import Link
×
11
from testit_python_commons.models.test_result import TestResult
×
12
from testit_python_commons.models.test_result_with_all_fixture_step_results_model import TestResultWithAllFixtureStepResults
×
13

UNCOV
14
from testit_adapter_pytest.models.executable_test import ExecutableTest
×
15

16

UNCOV
17
__ARRAY_TYPES = (frozenset, list, set, tuple,)
×
18

19

UNCOV
20
def form_test(item) -> ExecutableTest:
×
21
    executable_test = ExecutableTest(
×
22
        external_id=__get_external_id_from(item),
23
        name=__get_display_name_from(item),
24
        duration=0,
25
        parameters=__get_parameters_from(item),
26
        properties=__get_properties_from(item),
27
        namespace=__get_namespace_from(item),
28
        classname=__get_class_name_from(item),
29
        title=__get_title_from(item),
30
        description=__get_description_from(item),
31
        links=__get_links_from(item),
32
        labels=__get_labels_from(item),
33
        work_item_ids=__get_work_item_ids_from(item),
34
        node_id=item.nodeid
35
    )
36

UNCOV
37
    if item.own_markers:
×
38
        for mark in item.own_markers:
×
39
            if mark.name == 'skip' or mark.name == 'skipif':
×
40
                executable_test.outcome = 'Skipped'
×
41
                if mark.args:
×
42
                    executable_test.message = mark.args[0]
×
43
                if mark.kwargs and 'reason' in mark.kwargs:
×
44
                    executable_test.message = mark.kwargs['reason']
×
45
            if mark.name == 'xfail':
×
46
                if mark.args:
×
47
                    executable_test.message = mark.args[0]
×
48
                if mark.kwargs and 'reason' in mark.kwargs:
×
49
                    executable_test.message = mark.kwargs['reason']
×
50

UNCOV
51
    return executable_test
×
52

53

UNCOV
54
def __get_display_name_from(item):
×
55
    display_name = __search_attribute(item, 'test_displayname')
×
56

UNCOV
57
    if not display_name:
×
58
        return item.function.__doc__ if \
×
59
            item.function.__doc__ else item.function.__name__
60

UNCOV
61
    return collect_parameters_in_string_attribute(display_name, get_all_parameters(item))
×
62

63

UNCOV
64
def __get_external_id_from(item):
×
65
    external_id = __search_attribute(item, 'test_external_id')
×
66

UNCOV
67
    if not external_id:
×
68
        return get_hash(item.parent.nodeid + item.function.__name__)
×
69

UNCOV
70
    return collect_parameters_in_string_attribute(external_id, get_all_parameters(item))
×
71

72

UNCOV
73
def __get_title_from(item):
×
74
    title = __search_attribute(item, 'test_title')
×
75

UNCOV
76
    if not title:
×
77
        return None
×
78

UNCOV
79
    return collect_parameters_in_string_attribute(title, get_all_parameters(item))
×
80

81

UNCOV
82
def __get_description_from(item):
×
83
    description = __search_attribute(item, 'test_description')
×
84

UNCOV
85
    if not description:
×
86
        return None
×
87

UNCOV
88
    return collect_parameters_in_string_attribute(description, get_all_parameters(item))
×
89

90

UNCOV
91
def __get_namespace_from(item):
×
92
    namespace = __search_attribute(item, 'test_namespace')
×
93

UNCOV
94
    if not namespace:
×
95
        return item.function.__module__
×
96

UNCOV
97
    return collect_parameters_in_string_attribute(namespace, get_all_parameters(item))
×
98

99

UNCOV
100
def __get_class_name_from(item):
×
101
    class_name = __search_attribute(item, 'test_classname')
×
102

UNCOV
103
    if not class_name:
×
104
        i = item.function.__qualname__.find('.')
×
105

UNCOV
106
        if i != -1:
×
107
            return item.function.__qualname__[:i]
×
108

UNCOV
109
        return None
×
110

UNCOV
111
    return collect_parameters_in_string_attribute(class_name, get_all_parameters(item))
×
112

113

UNCOV
114
def __get_links_from(item):
×
115
    links = __search_attribute(item, 'test_links')
×
116

UNCOV
117
    if not links:
×
118
        return []
×
119

UNCOV
120
    return __set_parameters_to_links(links, get_all_parameters(item))
×
121

122

UNCOV
123
def __get_parameters_from(item):
×
124
    test_parameters = {}
×
125

UNCOV
126
    if hasattr(item, 'callspec'):
×
127
        for key, parameter in item.callspec.params.items():
×
128
            test_parameters[key] = str(parameter)
×
129

UNCOV
130
    return test_parameters
×
131

132

UNCOV
133
def __get_properties_from(item):
×
134
    if hasattr(item, 'test_properties'):
×
135
        return item.test_properties
×
136
    return None
×
137

138

UNCOV
139
def __set_parameters_to_links(links, all_parameters):
×
140
    if not all_parameters:
×
141
        return links
×
142

UNCOV
143
    links_with_parameters = []
×
144

UNCOV
145
    for link in links:
×
146
        links_with_parameters.append(
×
147
            Link()
148
            .set_url(
149
                collect_parameters_in_string_attribute(
150
                    link.get_url(),
151
                    all_parameters))
152
            .set_title(
153
                collect_parameters_in_string_attribute(
154
                    link.get_title(),
155
                    all_parameters) if link.get_title() else None)
156
            .set_link_type(
157
                collect_parameters_in_string_attribute(
158
                    link.get_link_type(),
159
                    all_parameters) if link.get_link_type() else None)
160
            .set_description(
161
                collect_parameters_in_string_attribute(
162
                    link.get_description(),
163
                    all_parameters) if link.get_description() else None))
164

UNCOV
165
    return links_with_parameters
×
166

167

UNCOV
168
def __get_labels_from(item):
×
169
    test_labels = __search_attribute(item, 'test_labels')
×
170

UNCOV
171
    if not test_labels:
×
172
        return []
×
173

UNCOV
174
    labels = []
×
175

UNCOV
176
    for label in test_labels:
×
177
        result = collect_parameters_in_mass_attribute(
×
178
            label,
179
            get_all_parameters(item))
180

UNCOV
181
        if isinstance(result, __ARRAY_TYPES):
×
182
            for l in result:
×
183
                labels.append({
×
184
                    'name': str(l)
185
                })
186
        else:
UNCOV
187
            labels.append({
×
188
                'name': str(result)
189
            })
190

UNCOV
191
    return labels
×
192

193

UNCOV
194
def __get_work_item_ids_from(item):
×
195
    test_workitem_ids = __search_attribute(item, 'test_workitems_id')
×
196

UNCOV
197
    if not test_workitem_ids:
×
198
        return []
×
199

UNCOV
200
    workitem_ids = []
×
201

UNCOV
202
    for workitem_id in test_workitem_ids:
×
203
        result = collect_parameters_in_mass_attribute(
×
204
            workitem_id,
205
            get_all_parameters(item))
206

UNCOV
207
        if isinstance(result, __ARRAY_TYPES):
×
208
            workitem_ids += list(map(str, result))
×
209
        else:
UNCOV
210
            workitem_ids.append(str(result))
×
211

UNCOV
212
    return workitem_ids
×
213

214

UNCOV
215
def collect_parameters_in_string_attribute(attribute, all_parameters):
×
216
    param_keys = re.findall(r"\{(.*?)\}", attribute)
×
217

UNCOV
218
    if len(param_keys) > 0:
×
219
        for param_key in param_keys:
×
220
            parameter = get_parameter(param_key, all_parameters)
×
221

UNCOV
222
            if parameter is not None:
×
223
                attribute = attribute.replace("{" + param_key + "}", str(parameter))
×
224

UNCOV
225
    return attribute
×
226

227

UNCOV
228
def collect_parameters_in_mass_attribute(attribute, all_parameters):
×
229
    param_keys = re.findall(r"\{(.*?)\}", attribute)
×
230

UNCOV
231
    if len(param_keys) == 1:
×
232
        parameter = get_parameter(param_keys[0], all_parameters)
×
233

UNCOV
234
        if parameter is not None:
×
235
            return parameter
×
236

UNCOV
237
    if len(param_keys) > 1:
×
238
        logging.error(f'(For type tuple, list, set) support only one key!')
×
239

UNCOV
240
    return attribute
×
241

242

UNCOV
243
def get_parameter(key_for_parameter, all_parameters):
×
244
    id_keys_in_parameter = re.findall(r'\[(.*?)\]', key_for_parameter)
×
245

UNCOV
246
    if len(id_keys_in_parameter) > 1:
×
247
        logging.error("(For type tuple, list, set, dict) support only one level!")
×
248

UNCOV
249
        return
×
250

UNCOV
251
    if len(id_keys_in_parameter) == 0:
×
252
        if key_for_parameter not in all_parameters:
×
253
            logging.error(f"Key for parameter {key_for_parameter} not found")
×
254

UNCOV
255
            return
×
256

UNCOV
257
        return all_parameters[key_for_parameter]
×
258

UNCOV
259
    parameter_key = key_for_parameter.replace("[" + id_keys_in_parameter[0] + "]", "")
×
260
    id_key_in_parameter = id_keys_in_parameter[0].strip("\'\"")
×
261

UNCOV
262
    if id_key_in_parameter.isdigit() and int(id_key_in_parameter) in range(len(all_parameters[parameter_key])):
×
263
        return all_parameters[parameter_key][int(id_key_in_parameter)]
×
264

UNCOV
265
    if id_key_in_parameter.isalnum() and id_key_in_parameter in all_parameters[parameter_key].keys():
×
266
        return all_parameters[parameter_key][id_key_in_parameter]
×
267

UNCOV
268
    logging.error(f"Not key: {key_for_parameter} in run parameters or other keys problem")
×
269

270

UNCOV
271
def get_all_parameters(item):
×
272
    params = {}
×
273

UNCOV
274
    if hasattr(item, 'test_properties'):
×
275
        params.update(item.test_properties)
×
276

UNCOV
277
    if hasattr(item, 'callspec'):
×
278
        params.update(item.callspec.params)
×
279

UNCOV
280
    return params
×
281

282

UNCOV
283
def __search_attribute(item, attribute):
×
284
    if hasattr(item.function, attribute):
×
285
        return getattr(item.function, attribute)
×
286

UNCOV
287
    if hasattr(item.cls, attribute):
×
288
        return getattr(item.cls, attribute)
×
289

UNCOV
290
    return
×
291

292

UNCOV
293
def get_hash(value: str):
×
294
    md = hashlib.sha256(bytes(value, encoding='utf-8'))
×
295
    return md.hexdigest()
×
296

297

UNCOV
298
def convert_executable_test_to_test_result_model(executable_test: ExecutableTest) -> TestResult:
×
299
    pytest_autotest_keys = convert_node_id_to_pytest_autotest_keys(executable_test.node_id)
×
300

UNCOV
301
    return TestResult()\
×
302
        .set_external_id(executable_test.external_id)\
303
        .set_autotest_name(executable_test.name)\
304
        .set_step_results(executable_test.step_results)\
305
        .set_setup_results(executable_test.setup_step_results)\
306
        .set_teardown_results(executable_test.teardown_step_results)\
307
        .set_duration(executable_test.duration)\
308
        .set_outcome(executable_test.outcome)\
309
        .set_traces(executable_test.traces)\
310
        .set_attachments(executable_test.attachments)\
311
        .set_parameters(executable_test.parameters)\
312
        .set_properties(executable_test.properties)\
313
        .set_namespace(executable_test.namespace)\
314
        .set_classname(executable_test.classname)\
315
        .set_title(executable_test.title)\
316
        .set_description(executable_test.description)\
317
        .set_links(executable_test.links)\
318
        .set_result_links(executable_test.result_links)\
319
        .set_labels(executable_test.labels)\
320
        .set_work_item_ids(executable_test.work_item_ids) \
321
        .set_message(executable_test.message) \
322
        .set_external_key(pytest_autotest_keys)
323

324

UNCOV
325
def convert_node_id_to_pytest_autotest_keys(node_id: str) -> str:
×
326
    test_path_parts = __get_test_path_parts_by_node_id(node_id)
×
327
    directories_in_project = __get_directories_in_project_by_test_path_parts(test_path_parts)
×
328
    test_node_parts_from_module = __get_test_node_parts_from_module_by_test_path_parts(test_path_parts)
×
329

UNCOV
330
    return __join_test_node_parts_to_pytest_autotest_keys(directories_in_project + test_node_parts_from_module)
×
331

332

UNCOV
333
def __get_test_path_parts_by_node_id(node_id: str):
×
334
    return node_id.split('/')
×
335

336

UNCOV
337
def __get_directories_in_project_by_test_path_parts(test_path_parts: typing.List[str]):
×
338
    return test_path_parts[:-1]
×
339

340

UNCOV
341
def __get_test_node_parts_from_module_by_test_path_parts(test_path_parts: typing.List[str]):
×
342
    test_node_from_module = test_path_parts[-1]
×
343

UNCOV
344
    return test_node_from_module.split('::')
×
345

346

UNCOV
347
def __join_test_node_parts_to_pytest_autotest_keys(test_node_parts: typing.List[str]):
×
348
    return ' and '.join(test_node_parts)
×
349

350

UNCOV
351
def fixtures_containers_to_test_results_with_all_fixture_step_results(
×
352
        fixtures_containers: dict,
353
        test_result_ids: dict) -> typing.List[TestResultWithAllFixtureStepResults]:
354
    test_results_with_all_fixture_step_results = []
×
355

UNCOV
356
    for node_id, test_result_id in test_result_ids.items():
×
357
        test_result_with_all_fixture_step_results = TestResultWithAllFixtureStepResults(test_result_id)
×
358

UNCOV
359
        for uuid, fixtures_container in fixtures_containers.items():
×
360
            if node_id in fixtures_container.node_ids:
×
361
                if fixtures_container.befores:
×
362
                    test_result_with_all_fixture_step_results.set_setup_results(fixtures_container.befores[0].steps)
×
363

UNCOV
364
                if fixtures_container.afters:
×
365
                    test_result_with_all_fixture_step_results.set_teardown_results(fixtures_container.afters[0].steps)
×
366

UNCOV
367
        test_results_with_all_fixture_step_results.append(test_result_with_all_fixture_step_results)
×
368

UNCOV
369
    return test_results_with_all_fixture_step_results
×
370

371

UNCOV
372
def get_status(exception):
×
373
    if exception:
×
374
        if isinstance(exception, pytest.skip.Exception):
×
375
            return "Skipped"
×
376
        return "Failed"
×
377
    else:
UNCOV
378
        return "Passed"
×
379

380

UNCOV
381
def get_outcome_status(outcome):
×
382
    _, exception, _ = outcome.excinfo or (None, None, None)
×
383
    return get_status(exception)
×
384

385

UNCOV
386
def get_traceback(exc_traceback):
×
387
    return ''.join(traceback.format_tb(exc_traceback)) if exc_traceback else None
×
388

389

UNCOV
390
def get_message(etype, value):
×
391
    return '\n'.join(format_exception_only(etype, value)) if etype or value else None
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc