Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add failFast option to allow displaying all errors in build #1032

Merged
3 changes: 3 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/main[co

== Unreleased

Improvements (for all modules)::

* Add failFast option to logHandler to allow displaying all errors in build (#1032)

== v3.1.1 (2024-11-14)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.asciidoctor.maven.log;

import java.io.File;

import org.asciidoctor.ast.Cursor;
import org.asciidoctor.log.LogRecord;

/**
* {@link LogRecord} proxy that allows capturing the source file being
* processed.
* Important: the {@link #sourceFile} and the actual source where an error is present
* may not be the same. For example if the source is being included.
*
* @since 3.2.0
*/
final class CapturedLogRecord extends LogRecord {

private final File sourceFile;

CapturedLogRecord(LogRecord record, File sourceFile) {
super(record.getSeverity(), record.getCursor(), record.getMessage(), record.getSourceFileName(), record.getSourceMethodName());
this.sourceFile = sourceFile;
}

public Cursor getCursor() {
if (super.getCursor() != null) {
return super.getCursor();
}
if (sourceFile != null) {
return new FileCursor(sourceFile);
}
return null;
}

public File getSourceFile() {
return sourceFile;
}

class FileCursor implements Cursor {

private final File file;

public FileCursor(File file) {
this.file = file;
}

@Override
public int getLineNumber() {
return 0;
}

@Override
public String getPath() {
return file.getName();
}

@Override
public String getDir() {
return file.getParent();
}

@Override
public String getFile() {
return file.getAbsolutePath();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@
* POJO for Maven XML mapping.
*
* @author abelsromero
* @since 1.5.7
*/
public class LogHandler {

private Boolean outputToConsole;
private FailIf failIf;
private Boolean failFast;

public LogHandler() {
outputToConsole = Boolean.TRUE;
failFast = Boolean.TRUE;
}

public Boolean getOutputToConsole() {
return outputToConsole;
Expand All @@ -37,4 +44,11 @@ public boolean isContainsTextNotBlank() {
return failIf != null && isNotBlank(failIf.getContainsText());
}

public Boolean getFailFast() {
return failFast;
}

public void setFailFast(Boolean failFast) {
this.failFast = failFast;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.io.File;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

import org.asciidoctor.log.LogRecord;
Expand All @@ -23,35 +24,27 @@ public LogRecordsProcessors(LogHandler logHandler,
}

public void processLogRecords(MemoryLogHandler memoryLogHandler) throws Exception {
if (logHandler.isSeveritySet() && logHandler.isContainsTextNotBlank()) {
final Severity severity = logHandler.getFailIf().getSeverity();
final String textToSearch = logHandler.getFailIf().getContainsText();
if (logHandler.isSeveritySet() || logHandler.isContainsTextNotBlank()) {
final Severity severity = Optional.ofNullable(logHandler.getFailIf()).map(FailIf::getSeverity).orElse(null);
final String textToSearch = Optional.ofNullable(logHandler.getFailIf()).map(FailIf::getContainsText).orElse(null);

final List<LogRecord> records = memoryLogHandler.filter(severity, textToSearch);
if (records.size() > 0) {
for (LogRecord record : records) {
errorMessageConsumer.accept(LogRecordFormatter.format(record, sourceDirectory));
}
throw new Exception(String.format("Found %s issue(s) matching severity %s or higher and text '%s'", records.size(), severity, textToSearch));
throw new Exception(getMessage(records, severity, textToSearch));
}
}
}

private String getMessage(List<LogRecord> records, Severity severity, String textToSearch) {
if (logHandler.isSeveritySet() && logHandler.isContainsTextNotBlank()) {
return String.format("Found %s issue(s) matching severity %s or higher and text '%s'", records.size(), severity, textToSearch);
} else if (logHandler.isSeveritySet()) {
final Severity severity = logHandler.getFailIf().getSeverity();
final List<LogRecord> records = memoryLogHandler.filter(severity);
if (records.size() > 0) {
for (LogRecord record : records) {
errorMessageConsumer.accept(LogRecordFormatter.format(record, sourceDirectory));
}
throw new Exception(String.format("Found %s issue(s) of severity %s or higher during conversion", records.size(), severity));
}
} else if (logHandler.isContainsTextNotBlank()) {
final String textToSearch = logHandler.getFailIf().getContainsText();
final List<LogRecord> records = memoryLogHandler.filter(textToSearch);
if (records.size() > 0) {
for (LogRecord record : records) {
errorMessageConsumer.accept(LogRecordFormatter.format(record, sourceDirectory));
}
throw new Exception(String.format("Found %s issue(s) containing '%s'", records.size(), textToSearch));
}
return String.format("Found %s issue(s) of severity %s or higher during conversion", records.size(), severity);
} else {
return String.format("Found %s issue(s) containing '%s'", records.size(), textToSearch);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.asciidoctor.maven.log;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
Expand All @@ -8,6 +9,7 @@
import org.asciidoctor.log.LogHandler;
import org.asciidoctor.log.LogRecord;
import org.asciidoctor.log.Severity;
import org.asciidoctor.maven.commons.StringUtils;


/**
Expand All @@ -23,16 +25,26 @@ public class MemoryLogHandler implements LogHandler {
private final Boolean outputToConsole;
private final Consumer<LogRecord> recordConsumer;

/**
* Provides simple way to inject the current file being processes.
* Will need re-work in concurrent scenarios.
*
* @since 3.2.0
*/
private File currentFile;

public MemoryLogHandler(Boolean outputToConsole, Consumer<LogRecord> recordConsumer) {
this.outputToConsole = outputToConsole == null ? Boolean.FALSE : outputToConsole;
this.recordConsumer = recordConsumer;
}

@Override
public void log(LogRecord logRecord) {
records.add(logRecord);
final CapturedLogRecord record = new CapturedLogRecord(logRecord, currentFile);

records.add(record);
if (outputToConsole)
recordConsumer.accept(logRecord);
recordConsumer.accept(record);
}

public void clear() {
Expand All @@ -46,9 +58,7 @@ public void clear() {
* @return list of filtered logRecords
*/
public List<LogRecord> filter(Severity severity) {
return this.records.stream()
.filter(record -> severityIsHigher(record, severity))
.collect(Collectors.toList());
return filter(severity, null);
}

/**
Expand All @@ -58,16 +68,14 @@ public List<LogRecord> filter(Severity severity) {
* @return list of filtered logRecords
*/
public List<LogRecord> filter(String text) {
return this.records.stream()
.filter(record -> messageContains(record, text))
.collect(Collectors.toList());
return filter(null, text);
}

/**
* Returns LogRecords that are equal or above the severity level and whose message contains text.
*
* @param severity Asciidoctor's severity level
* @param text text to search for in the LogRecords
* @param severity Asciidoctor's severity level (no filter applied when null)
* @param text text to search for in the LogRecords (no filter applied when null)
* @return list of filtered logRecords
*/
public List<LogRecord> filter(Severity severity, String text) {
Expand Down Expand Up @@ -96,11 +104,23 @@ public void processAll() {
}

private static boolean severityIsHigher(LogRecord record, Severity severity) {
return record.getSeverity().ordinal() >= severity.ordinal();
if (severity == null) {
return true;
} else {
return record.getSeverity().ordinal() >= severity.ordinal();
}
}

private static boolean messageContains(LogRecord record, String text) {
return record.getMessage().contains(text);
if (StringUtils.isBlank(text)) {
return true;
} else {
return record.getMessage().contains(text);
}
}

public void setCurrentFile(File currentFile) {
this.currentFile = currentFile;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,15 @@ public LogHandler deserialize(Xpp3Dom configNode) {
if (logHandlerNode == null || !logHandlerNode.getName().equals(NODE_NAME))
return logHandler;

logHandler.setOutputToConsole(deserializeOutputToConsole(logHandlerNode));
logHandler.setOutputToConsole(getBoolean(logHandlerNode, "outputToConsole"));
logHandler.setFailFast(getBoolean(logHandlerNode, "failFast"));

deserializeFailIf(logHandlerNode.getChild("failIf"))
.ifPresent(logHandler::setFailIf);
.ifPresent(logHandler::setFailIf);

return logHandler;
}

private Boolean deserializeOutputToConsole(Xpp3Dom node) {
return getBoolean(node, "outputToConsole");
}

private Optional<FailIf> deserializeFailIf(Xpp3Dom node) {
if (node == null)
return Optional.empty();
Expand All @@ -58,18 +55,18 @@ private Optional<FailIf> deserializeFailIf(Xpp3Dom node) {
return Optional.ofNullable(failIf);
}

private String sanitizeString(Xpp3Dom severity) {
String value = severity.getValue();
return value == null ? "" : value.trim();
}

private LogHandler defaultLogHandler() {
LogHandler logHandler = new LogHandler();
logHandler.setOutputToConsole(Boolean.TRUE);
return logHandler;
}

private Boolean getBoolean(Xpp3Dom node, String name) {
private static String sanitizeString(Xpp3Dom node) {
final String value = node.getValue();
return value == null ? "" : value.trim();
}

private static Boolean getBoolean(Xpp3Dom node, String name) {
final Xpp3Dom child = node.getChild(name);
if (child == null) {
return Boolean.TRUE;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.asciidoctor.maven.log;

import java.io.File;

import static org.assertj.core.api.Assertions.assertThat;

import org.asciidoctor.ast.Cursor;
import org.asciidoctor.log.LogRecord;
import org.asciidoctor.log.Severity;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

class CapturedLogRecordTest {

@Nested
class WhenLogRecordsContainsCursor {

@Test
void shouldReturnCursorFile() {
final var cursorFile = new File("uno", "dos");
final var recordFile = new File("tres", "quatre");
final var cursor = new TestCursor(cursorFile.getAbsolutePath(), 0, null, null);

final var logRecord = new CapturedLogRecord(getLogRecord(cursor), recordFile);
final var capturedLogRecord = new CapturedLogRecord(logRecord, cursorFile);

assertThat(capturedLogRecord.getCursor().getFile()).isEqualTo(cursorFile.getAbsolutePath());
}

}

@Nested
class WhenLogRecordsDoesNotContainsCursor {

@Test
void shouldReturnLogRecordFileWhenCursorFileIsSet() {
final var cursorFile = new File("uno", "dos");
final var recordFile = new File("tres", "quatre");

final var logRecord = new CapturedLogRecord(getLogRecord(null), recordFile);
final var capturedLogRecord = new CapturedLogRecord(logRecord, cursorFile);

assertThat(capturedLogRecord.getCursor().getFile()).isEqualTo(recordFile.getAbsolutePath());
}

@Test
void shouldReturnNullWhenCursorFileIsNoSet() {
final var logRecord = new CapturedLogRecord(getLogRecord(null), null);
final var capturedLogRecord = new CapturedLogRecord(logRecord, null);

assertThat(capturedLogRecord.getCursor()).isNull();
}

}

private LogRecord getLogRecord(Cursor cursor) {
return new LogRecord(Severity.INFO, cursor, "a message");
}

}
Loading