diff --git a/broken.json b/broken.json new file mode 100644 index 0000000..b15b046 --- /dev/null +++ b/broken.json @@ -0,0 +1,7 @@ +{ + "matchers": [ + { + "messageRegexp": "Test exceeded execution time allowance of.*" + } + ] +} \ No newline at end of file diff --git a/src/main/java/io/eroshenkoam/xcresults/broken/BrokenConfig.java b/src/main/java/io/eroshenkoam/xcresults/broken/BrokenConfig.java new file mode 100644 index 0000000..65594d7 --- /dev/null +++ b/src/main/java/io/eroshenkoam/xcresults/broken/BrokenConfig.java @@ -0,0 +1,17 @@ +package io.eroshenkoam.xcresults.broken; + +import java.io.Serializable; +import java.util.List; + +public class BrokenConfig implements Serializable { + + private List matchers; + + public List getMatchers() { + return matchers; + } + + public void setMatchers(final List matchers) { + this.matchers = matchers; + } +} diff --git a/src/main/java/io/eroshenkoam/xcresults/broken/BrokenPostProcessor.java b/src/main/java/io/eroshenkoam/xcresults/broken/BrokenPostProcessor.java new file mode 100644 index 0000000..18b5eb5 --- /dev/null +++ b/src/main/java/io/eroshenkoam/xcresults/broken/BrokenPostProcessor.java @@ -0,0 +1,84 @@ +package io.eroshenkoam.xcresults.broken; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import io.eroshenkoam.xcresults.export.ExportPostProcessor; +import io.qameta.allure.model.Status; +import io.qameta.allure.model.StatusDetails; +import io.qameta.allure.model.TestResult; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class BrokenPostProcessor implements ExportPostProcessor { + + private static final ObjectMapper MAPPER = new ObjectMapper() + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + + private final Path configPath; + + public BrokenPostProcessor(final String configPath) { + this.configPath = Path.of(configPath).toAbsolutePath(); + } + + @Override + public void processTestResults(final Path outputPath, final Map testResults) { + System.out.println("Broken test processor enabled"); + if (Files.notExists(configPath)) { + System.out.println("Broken test processor config file not found: " + configPath); + return; + } + final Optional config = readConfig(configPath); + if (config.isEmpty()) { + System.out.println("Broken test processor config file is invalid: " + configPath); + return; + } + testResults.forEach((path, testResult) -> updateStatus(path, testResult, config.get())); + } + + private void updateStatus(final Path path, final TestResult testResult, final BrokenConfig config) { + if (Objects.nonNull(testResult.getStatus()) && testResult.getStatus().equals(Status.FAILED)) { + final StatusDetails statusDetails = testResult.getStatusDetails(); + + final String message = Optional.ofNullable(statusDetails.getMessage()).orElse(""); + final String trace = Optional.ofNullable(statusDetails.getTrace()).orElse(""); + + if (isBroken(message, trace, config)) { + testResult.setStatus(Status.BROKEN); + try { + MAPPER.writeValue(path.toFile(), testResult); + } catch (IOException e) { + System.out.println("Can not update broken test status: " + e.getMessage()); + } + } + } + } + + private static boolean isBroken(final String message, final String trace, final BrokenConfig config) { + for (final BrokenStatusMatcher matcher: config.getMatchers()) { + final boolean messageMatched = Optional.ofNullable(matcher.getMessagePattern()) + .map(pattern -> pattern.matcher(message).matches()) + .orElse(false); + final boolean traceMatched = Optional.ofNullable(matcher.getTracePattern()) + .map(pattern -> pattern.matcher(message).matches()) + .orElse(false); + if (messageMatched || traceMatched) { + return true; + } + } + return false; + } + + private static Optional readConfig(final Path config) { + try { + return Optional.of(MAPPER.readValue(config.toFile(), BrokenConfig.class)); + } catch (IOException e) { + System.out.println("Can not read broken test processor config file:" + e.getMessage()); + return Optional.empty(); + } + } +} diff --git a/src/main/java/io/eroshenkoam/xcresults/broken/BrokenStatusMatcher.java b/src/main/java/io/eroshenkoam/xcresults/broken/BrokenStatusMatcher.java new file mode 100644 index 0000000..c3a383a --- /dev/null +++ b/src/main/java/io/eroshenkoam/xcresults/broken/BrokenStatusMatcher.java @@ -0,0 +1,46 @@ +package io.eroshenkoam.xcresults.broken; + +import java.io.Serializable; +import java.util.Objects; +import java.util.regex.Pattern; + +public class BrokenStatusMatcher implements Serializable { + + private String messageRegexp; + private Pattern messagePattern; + + private String traceRegexp; + private Pattern tracePattern; + + public Pattern getMessagePattern() { + return messagePattern; + } + + public String getMessageRegexp() { + return messageRegexp; + } + + public BrokenStatusMatcher setMessageRegexp(String messageRegexp) { + this.messageRegexp = messageRegexp; + if (Objects.nonNull(messageRegexp)) { + this.messagePattern = Pattern.compile(messageRegexp); + } + return this; + } + + public Pattern getTracePattern() { + return tracePattern; + } + + public String getTraceRegexp() { + return traceRegexp; + } + + public BrokenStatusMatcher setTraceRegexp(String traceRegexp) { + this.traceRegexp = traceRegexp; + if (Objects.nonNull(traceRegexp)) { + this.tracePattern = Pattern.compile(traceRegexp); + } + return this; + } +} diff --git a/src/main/java/io/eroshenkoam/xcresults/export/ExportCommand.java b/src/main/java/io/eroshenkoam/xcresults/export/ExportCommand.java index 912088d..48d3470 100644 --- a/src/main/java/io/eroshenkoam/xcresults/export/ExportCommand.java +++ b/src/main/java/io/eroshenkoam/xcresults/export/ExportCommand.java @@ -38,6 +38,12 @@ public class ExportCommand implements Runnable { ) protected List inputPath; + @CommandLine.Option( + names = {"--broken-config-path"}, + description = "Broken config path" + ) + protected String brokenConfigPath; + @CommandLine.Option( names = {"-o", "--output"}, description = "Export output directory" @@ -69,7 +75,7 @@ public void run() { private void runUnsafe(final Path input, final Path output) throws Exception { System.out.printf("Export xcresults from [%s] to [%s]\n", input, output); final ExportProcessor processor = new ExportProcessor( - input, output, addCarouselAttachment, carouselTemplatePath + input, output, brokenConfigPath, addCarouselAttachment, carouselTemplatePath ); processor.export(); } diff --git a/src/main/java/io/eroshenkoam/xcresults/export/ExportProcessor.java b/src/main/java/io/eroshenkoam/xcresults/export/ExportProcessor.java index 3ec2f9e..b223fa9 100644 --- a/src/main/java/io/eroshenkoam/xcresults/export/ExportProcessor.java +++ b/src/main/java/io/eroshenkoam/xcresults/export/ExportProcessor.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import freemarker.template.Version; +import io.eroshenkoam.xcresults.broken.BrokenPostProcessor; import io.eroshenkoam.xcresults.carousel.CarouselPostProcessor; import io.qameta.allure.model.ExecutableItem; import io.qameta.allure.model.TestResult; @@ -67,16 +68,20 @@ public class ExportProcessor { private final Path inputPath; private final Path outputPath; + private String brokenConfigPath; + private Boolean addCarouselAttachment; private String carouselTemplatePath; public ExportProcessor(final Path inputPath, final Path outputPath, + final String brokenConfigPath, final Boolean addCarouselAttachment, final String carouselTemplatePath) { this.inputPath = inputPath; this.outputPath = outputPath; + this.brokenConfigPath = brokenConfigPath; this.addCarouselAttachment = addCarouselAttachment; this.carouselTemplatePath = carouselTemplatePath; } @@ -142,17 +147,23 @@ public void export() throws Exception { }); testResults.put(testSummaryPath, testResult); } - System.out.printf("Export information about %s attachments...%n", attachmentsRefs.size()); - for (Map.Entry attachment : attachmentsRefs.entrySet()) { - final String attachmentRef = attachment.getValue(); - final Path attachmentPath = outputPath.resolve(attachment.getKey()); - exportReference(attachmentRef, attachmentPath); - } +// System.out.printf("Export information about %s attachments...%n", attachmentsRefs.size()); +// for (Map.Entry attachment : attachmentsRefs.entrySet()) { +// final String attachmentRef = attachment.getValue(); +// final Path attachmentPath = outputPath.resolve(attachment.getKey()); +// exportReference(attachmentRef, attachmentPath); +// } +// final List postProcessors = new ArrayList<>(); if (Objects.nonNull(addCarouselAttachment)) { postProcessors.add(new CarouselPostProcessor(carouselTemplatePath)); } - postProcessors.forEach(postProcessor -> postProcessor.processTestResults(outputPath, testResults)); + if (Objects.nonNull(brokenConfigPath)) { + postProcessors.add(new BrokenPostProcessor(brokenConfigPath)); + } + postProcessors.forEach( + postProcessor -> postProcessor.processTestResults(outputPath, testResults) + ); } private ExportMeta getTestMeta(final ExportMeta meta, final JsonNode testableSummary) {