1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.tap4j.parser;
25
26 import java.io.File;
27 import java.util.Scanner;
28 import java.util.regex.Matcher;
29
30 import org.apache.commons.lang.StringUtils;
31 import org.tap4j.model.BailOut;
32 import org.tap4j.model.Comment;
33 import org.tap4j.model.Directive;
34 import org.tap4j.model.Footer;
35 import org.tap4j.model.Header;
36 import org.tap4j.model.Plan;
37 import org.tap4j.model.SkipPlan;
38 import org.tap4j.model.TestResult;
39 import org.tap4j.model.TestSet;
40 import org.tap4j.model.Text;
41 import org.tap4j.util.DirectiveValues;
42 import org.tap4j.util.StatusValues;
43
44
45
46
47
48
49 public class Tap13Parser implements Parser {
50
51 private boolean isFirstLine = true;
52
53 private boolean planBeforeTestResult = false;
54
55
56 private String lastLine = null;
57
58
59
60
61 private TestSet testSet;
62
63
64
65
66 public Tap13Parser() {
67 super();
68 this.init();
69 }
70
71
72
73
74
75 public final void init() {
76 this.isFirstLine = true;
77 this.planBeforeTestResult = false;
78 this.lastLine = null;
79 this.testSet = new TestSet();
80 }
81
82
83
84
85
86 public TestSet getTestSet() {
87 return this.testSet;
88 }
89
90
91
92
93
94 public void parseLine(String tapLine) {
95 if (StringUtils.isEmpty(tapLine)) {
96 return;
97 }
98
99 Matcher matcher = null;
100
101
102 matcher = COMMENT_PATTERN.matcher(tapLine);
103 if (matcher.matches()) {
104 this.extractComment(matcher);
105 return;
106 }
107
108
109 lastLine = tapLine;
110
111
112 matcher = HEADER_PATTERN.matcher(tapLine);
113 if (matcher.matches()) {
114
115 this.checkTAPHeaderParsingLocationAndDuplicity();
116
117 this.extractHeader(matcher);
118 this.isFirstLine = false;
119 return;
120 }
121
122
123 matcher = PLAN_PATTERN.matcher(tapLine);
124 if (matcher.matches()) {
125
126 this.checkTAPPlanDuplicity();
127
128 this.checkIfTAPPlanIsSetBeforeTestResultsOrBailOut();
129
130 this.extractPlan(matcher);
131 this.isFirstLine = false;
132 return;
133 }
134
135
136 matcher = TEST_RESULT_PATTERN.matcher(tapLine);
137 if (matcher.matches()) {
138 this.extractTestResult(matcher);
139 return;
140 }
141
142
143 matcher = BAIL_OUT_PATTERN.matcher(tapLine);
144 if (matcher.matches()) {
145 this.extractBailOut(matcher);
146 return;
147 }
148
149
150 matcher = FOOTER_PATTERN.matcher(tapLine);
151 if (matcher.matches()) {
152 this.extractFooter(matcher);
153 return;
154 }
155
156
157 final Text text = new Text(tapLine);
158 this.testSet.getTapLines().add(text);
159 }
160
161
162
163
164 protected void checkIfTAPPlanIsSetBeforeTestResultsOrBailOut() {
165 if (this.testSet.getTestResults().size() <= 0 &&
166 this.testSet.getBailOuts().size() <= 0) {
167 this.planBeforeTestResult = true;
168 }
169 }
170
171
172
173
174
175
176 protected void checkTAPHeaderParsingLocationAndDuplicity() {
177 if (this.testSet.getHeader() != null) {
178 throw new ParserException("Duplicated TAP Header found.");
179 }
180 if (!isFirstLine) {
181 throw new ParserException(
182 "Invalid position of TAP Header. It must be the first element (apart of Comments) in the TAP Stream.");
183 }
184 }
185
186
187
188
189 protected void checkTAPPlanDuplicity() {
190 if (this.testSet.getPlan() != null) {
191 throw new ParserException("Duplicated TAP Plan found.");
192 }
193 }
194
195
196
197
198
199
200
201
202
203 protected void checkTAPPlanPosition() {
204 if (!this.planBeforeTestResult) {
205 Matcher matcher = PLAN_PATTERN.matcher(lastLine);
206
207 if (matcher.matches()) {
208 return;
209 }
210
211 throw new ParserException("Invalid position of TAP Plan.");
212 }
213 }
214
215
216
217
218
219
220 protected void checkTAPPlanIsSet() {
221 if (this.testSet.getPlan() == null) {
222 throw new ParserException("Missing TAP Plan.");
223 }
224 }
225
226
227
228
229
230
231 protected void extractHeader(Matcher matcher) {
232 final Integer version = Integer.parseInt(matcher.group(1));
233
234 final Header header = new Header(version);
235
236 final String commentToken = matcher.group(2);
237
238 if (commentToken != null) {
239 String text = matcher.group(3);
240 final Comment comment = new Comment(text);
241 header.setComment(comment);
242 }
243
244 this.testSet.setHeader(header);
245 }
246
247
248
249
250 protected void extractPlan(Matcher matcher) {
251 Integer initialTest = Integer.parseInt(matcher.group(1));
252 Integer lastTest = Integer.parseInt(matcher.group(3));
253
254 Plan plan = null;
255 plan = new Plan(initialTest, lastTest);
256
257 String skipToken = matcher.group(4);
258 if (skipToken != null) {
259 String reason = matcher.group(5);
260 final SkipPlan skip = new SkipPlan(reason);
261 plan.setSkip(skip);
262 }
263
264 String commentToken = matcher.group(6);
265 if (commentToken != null) {
266 String text = matcher.group(7);
267 final Comment comment = new Comment(text);
268 plan.setComment(comment);
269 }
270
271 this.testSet.setPlan(plan);
272 }
273
274
275
276
277 protected void extractTestResult(Matcher matcher) {
278 TestResult testResult = null;
279
280 final String okOrNotOk = matcher.group(1);
281 StatusValues status = null;
282 if (okOrNotOk.trim().equals("ok")) {
283 status = StatusValues.OK;
284 } else
285 {
286 status = StatusValues.NOT_OK;
287 }
288
289 Integer testNumber = this.getTestNumber(matcher.group(2));
290
291 testResult = new TestResult(status, testNumber);
292
293 testResult.setDescription(matcher.group(3));
294
295 String directiveToken = matcher.group(4);
296 if (directiveToken != null) {
297 String directiveText = matcher.group(5);
298 DirectiveValues directiveValue = null;
299 if (directiveText.trim().equalsIgnoreCase("todo")) {
300 directiveValue = DirectiveValues.TODO;
301 } else {
302 directiveValue = DirectiveValues.SKIP;
303 }
304 String reason = matcher.group(6);
305 Directive directive = new Directive(directiveValue, reason);
306 testResult.setDirective(directive);
307 }
308
309 String commentToken = matcher.group(7);
310 if (commentToken != null) {
311 String text = matcher.group(8);
312 final Comment comment = new Comment(text);
313 comment.setInline(Boolean.TRUE);
314 testResult.addComment(comment);
315 }
316
317 this.testSet.addTestResult(testResult);
318 this.testSet.addTapLine(testResult);
319 }
320
321
322
323
324
325
326
327
328
329 private Integer getTestNumber(String testNumber) {
330 Integer integerTestNumber = null;
331 if (StringUtils.isEmpty(testNumber)) {
332 integerTestNumber = (this.testSet.getTestResults().size() + 1);
333 } else {
334 integerTestNumber = Integer.parseInt(testNumber);
335 }
336 return integerTestNumber;
337 }
338
339
340
341
342 protected void extractBailOut(Matcher matcher) {
343 String reason = matcher.group(1);
344
345 BailOut bailOut = new BailOut(reason);
346
347 String commentToken = matcher.group(2);
348
349 if (commentToken != null) {
350 String text = matcher.group(3);
351 Comment comment = new Comment(text);
352 bailOut.setComment(comment);
353 }
354
355 this.testSet.addBailOut(bailOut);
356 this.testSet.addTapLine(bailOut);
357 }
358
359
360
361
362 protected void extractComment(Matcher matcher) {
363 String text = matcher.group(1);
364 Comment comment = new Comment(text);
365
366 this.testSet.addComment(comment);
367 this.testSet.addTapLine(comment);
368 }
369
370
371
372
373
374
375 protected void extractFooter(Matcher matcher) {
376 String text = matcher.group(1);
377 Footer footer = new Footer(text);
378
379 final String commentToken = matcher.group(2);
380
381 if (commentToken != null) {
382 String commentText = matcher.group(3);
383 final Comment comment = new Comment(commentText);
384 footer.setComment(comment);
385 }
386
387 this.testSet.setFooter(footer);
388 }
389
390
391
392
393
394 public TestSet parseTapStream(String tapStream) {
395
396 this.init();
397
398 Scanner scanner = null;
399
400 try {
401 scanner = new Scanner(tapStream);
402 String line = null;
403
404 while (scanner.hasNextLine()) {
405 line = scanner.nextLine();
406 if (StringUtils.isNotEmpty(line)) {
407 this.parseLine(line);
408 }
409 }
410 this.postProcess();
411 } catch (Exception e) {
412 throw new ParserException("Error parsing TAP Stream: " +
413 e.getMessage(), e);
414 } finally {
415 if (scanner != null) {
416 scanner.close();
417 }
418 }
419
420 return this.getTestSet();
421
422 }
423
424
425
426
427
428 public TestSet parseFile(File tapFile) {
429
430 this.init();
431
432 Scanner scanner = null;
433
434 try {
435 scanner = new Scanner(tapFile);
436 String line = null;
437
438 while (scanner.hasNextLine()) {
439 line = scanner.nextLine();
440 if (StringUtils.isNotBlank(line)) {
441 this.parseLine(line);
442 }
443 }
444 this.postProcess();
445 } catch (Exception e) {
446 throw new ParserException("Error parsing TAP Stream: " +
447 e.getMessage(), e);
448 } finally {
449 if (scanner != null) {
450 scanner.close();
451 }
452 }
453
454 return this.getTestSet();
455 }
456
457
458
459
460 protected void postProcess() {
461
462
463 this.checkTAPPlanIsSet();
464 }
465
466 }