-
Notifications
You must be signed in to change notification settings - Fork 132
/
Copy pathAnnotationExtractor.php
254 lines (225 loc) · 8.24 KB
/
AnnotationExtractor.php
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
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\FunctionalTestingFramework\Test\Util;
use Magento\FunctionalTestingFramework\Config\MftfApplicationConfig;
use Magento\FunctionalTestingFramework\Exceptions\XmlException;
use Magento\FunctionalTestingFramework\Util\Logger\LoggingUtil;
/**
* Class AnnotationExtractor
*/
class AnnotationExtractor extends BaseObjectExtractor
{
/**
* Mappings of all Test => title mappings, indexed by Story
* e.g. $storyToTitleMappings['storyAnnotation'] = ['testName' => 'titleAnnotation']
* @var array
*/
private $storyToTitleMappings = [];
/**
* @var array
*/
private $testCaseToTitleMappings = [];
const ANNOTATION_VALUE = 'value';
const MAGENTO_TO_ALLURE_SEVERITY_MAP = [
"BLOCKER" => "BLOCKER",
"CRITICAL" => "CRITICAL",
"MAJOR" => "NORMAL",
"AVERAGE" => "MINOR",
"MINOR" => "TRIVIAL"
];
const REQUIRED_ANNOTATIONS = [
"stories",
"title",
"description",
"severity"
];
/**
* AnnotationExtractor constructor.
*/
public function __construct()
{
// empty constructor
}
/**
* This method trims away irrelevant tags and returns annotations used in the array passed. The annotations
* can be found in both Tests and their child element tests.
*
* @param array $testAnnotations
* @param string $filename
* @return array
* @throws XmlException
*/
public function extractAnnotations($testAnnotations, $filename)
{
$annotationObjects = [];
$annotations = $this->stripDescriptorTags($testAnnotations, self::NODE_NAME);
// parse the Test annotations
foreach ($annotations as $annotationKey => $annotationData) {
$annotationValues = [];
// Only transform severity annotation
if ($annotationKey == "severity") {
$annotationObjects[$annotationKey] = $this->transformAllureSeverityToMagento(
$annotationData[0]['value']
);
continue;
}
if ($annotationKey == "skip") {
$annotationData = $annotationData['issueId'];
$this->validateSkippedIssues($annotationData, $filename);
}
foreach ($annotationData as $annotationValue) {
$annotationValues[] = $annotationValue[self::ANNOTATION_VALUE];
}
// TODO deprecation|deprecate MFTF 3.0.0
if ($annotationKey == "group" && in_array("skip", $annotationValues)) {
LoggingUtil::getInstance()->getLogger(AnnotationExtractor::class)->warning(
"Use of group skip will be deprecated in MFTF 3.0.0. Please update tests to use skip tags.",
["test" => $filename]
);
}
$annotationObjects[$annotationKey] = $annotationValues;
}
$this->addTestCaseIdToTitle($annotationObjects, $filename);
$this->validateMissingAnnotations($annotationObjects, $filename);
$this->addStoryTitleToMap($annotationObjects, $filename);
return $annotationObjects;
}
/**
* Adds story/title/filename combination to static map
* @param array $annotations
* @param string $filename
* @return void
*/
public function addStoryTitleToMap($annotations, $filename)
{
if (isset($annotations['stories']) && isset($annotations['title'])) {
$story = $annotations['stories'][0];
$title = $annotations['title'][0];
$this->storyToTitleMappings[$story . "/" . $title][] = $filename;
}
}
/**
* Appends TestCaseId or [NO TESTCASEID] to test titles (to prevent Allure collision).
* @param array $annotations
* @param string $filename
* @return void
*/
private function addTestCaseIdToTitle(&$annotations, $filename)
{
if (!isset($annotations['title'])) {
return;
}
$testCaseId = "[NO TESTCASEID]";
if (isset($annotations['testCaseId'])) {
$testCaseId = $annotations['testCaseId'][0];
}
$newTitle = "{$testCaseId}: " . $annotations['title'][0];
$annotations['title'][0] = $newTitle;
$this->testCaseToTitleMappings[$newTitle][] = $filename;
}
/**
* Validates given annotations against list of required annotations.
* @param array $annotationObjects
* @return void
*/
private function validateMissingAnnotations($annotationObjects, $filename)
{
$missingAnnotations = [];
foreach (self::REQUIRED_ANNOTATIONS as $REQUIRED_ANNOTATION) {
if (!array_key_exists($REQUIRED_ANNOTATION, $annotationObjects)) {
$missingAnnotations[] = $REQUIRED_ANNOTATION;
}
}
if (!empty($missingAnnotations)) {
$message = "Test {$filename} is missing required annotations.";
LoggingUtil::getInstance()->getLogger(ActionObject::class)->deprecation(
$message,
["testName" => $filename, "missingAnnotations" => implode(", ", $missingAnnotations)]
);
}
}
/**
* Validates that all Story/Title combinations are unique, builds list of violators if found.
* @throws XmlException
* @return void
*/
public function validateStoryTitleUniqueness()
{
$dupes = [];
foreach ($this->storyToTitleMappings as $storyTitle => $files) {
if (count($files) > 1) {
$dupes[$storyTitle] = "'" . implode("', '", $files) . "'";
}
}
if (!empty($dupes)) {
$message = "Story and Title annotation pairs must be unique:\n\n";
foreach ($dupes as $storyTitle => $tests) {
$storyTitleArray = explode("/", $storyTitle);
$story = $storyTitleArray[0];
$title = $storyTitleArray[1];
$message .= "Story: '{$story}' Title: '{$title}' in Tests {$tests}\n\n";
}
throw new XmlException($message);
}
}
/**
* Validates uniqueness between Test Case ID and Titles globally
* @returns void
* @throws XmlException
* @return void
*/
public function validateTestCaseIdTitleUniqueness()
{
$dupes = [];
foreach ($this->testCaseToTitleMappings as $newTitle => $files) {
if (count($files) > 1) {
$dupes[$newTitle] = "'" . implode("', '", $files) . "'";
}
}
if (!empty($dupes)) {
$message = "TestCaseId and Title pairs must be unique:\n\n";
foreach ($dupes as $newTitle => $tests) {
$testCaseTitleArray = explode(": ", $newTitle);
$testCaseId = $testCaseTitleArray[0];
$title = $testCaseTitleArray[1];
$message .= "TestCaseId: '{$testCaseId}' Title: '{$title}' in Tests {$tests}\n\n";
}
throw new XmlException($message);
}
}
/**
* Validates that all issueId tags contain a non-empty value
* @param array $issues
* @param string $filename
* @throws XmlException
* @return void
*/
public function validateSkippedIssues($issues, $filename)
{
foreach ($issues as $issueId) {
if (empty($issueId['value'])) {
$message = "issueId for skipped tests cannot be empty. Test: $filename";
throw new XmlException($message);
}
}
}
/**
* This method transforms Magento severity values from Severity annotation
* Returns Allure annotation value
*
* @param string $annotationData
* @return array
*/
public function transformAllureSeverityToMagento($annotationData)
{
$annotationValue = strtoupper($annotationData);
//Mapping Magento severity to Allure Severity
//Attempts to resolve annotationValue reference against MAGENTO_TO_ALLURE_SEVERITY_MAP -
// if not found returns without modification
$allureAnnotation[] = self::MAGENTO_TO_ALLURE_SEVERITY_MAP[$annotationValue] ?? $annotationValue;
return $allureAnnotation;
}
}