Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions broken.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"matchers": [
{
"messageRegexp": "Test exceeded execution time allowance of.*"
}
]
}
17 changes: 17 additions & 0 deletions src/main/java/io/eroshenkoam/xcresults/broken/BrokenConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.eroshenkoam.xcresults.broken;

import java.io.Serializable;
import java.util.List;

public class BrokenConfig implements Serializable {

private List<BrokenStatusMatcher> matchers;

public List<BrokenStatusMatcher> getMatchers() {
return matchers;
}

public void setMatchers(final List<BrokenStatusMatcher> matchers) {
this.matchers = matchers;
}
}
Original file line number Diff line number Diff line change
@@ -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<Path, TestResult> 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<BrokenConfig> 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<BrokenConfig> 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();
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ public class ExportCommand implements Runnable {
)
protected List<Path> inputPath;

@CommandLine.Option(
names = {"--broken-config-path"},
description = "Broken config path"
)
protected String brokenConfigPath;

@CommandLine.Option(
names = {"-o", "--output"},
description = "Export output directory"
Expand Down Expand Up @@ -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();
}
Expand Down
25 changes: 18 additions & 7 deletions src/main/java/io/eroshenkoam/xcresults/export/ExportProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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<String, String> 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<String, String> attachment : attachmentsRefs.entrySet()) {
// final String attachmentRef = attachment.getValue();
// final Path attachmentPath = outputPath.resolve(attachment.getKey());
// exportReference(attachmentRef, attachmentPath);
// }
//
final List<ExportPostProcessor> 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) {
Expand Down
Loading