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
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,17 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Comparator;
import java.util.Objects;
import java.util.UUID;
Expand All @@ -41,6 +48,12 @@ public class FileSystemResultsWriter implements AllureResultsWriter {

private static final Logger LOGGER = LoggerFactory.getLogger(FileSystemResultsWriter.class);

private static final String TEST_RESULT_ENTITY_NAME = "test result";

private static final String TEST_RESULT_CONTAINER_ENTITY_NAME = "test result container";

private static final String ATTACHMENT_ENTITY_NAME = "attachment";

private final Path outputDirectory;

private final ObjectMapper mapper;
Expand Down Expand Up @@ -84,13 +97,11 @@ public void write(final TestResult testResult) {
final String testResultName = Objects.isNull(testResult.getUuid())
? generateTestResultName()
: generateTestResultName(testResult.getUuid());
ensureInitialized();
final Path file = outputDirectory.resolve(testResultName);
try {
mapper.writeValue(file.toFile(), testResult);
} catch (IOException e) {
throw new AllureResultsWriteException("Could not write Allure test result", e);
}
write(file, TEST_RESULT_ENTITY_NAME, channel -> {
final DataOutput output = new DataOutputStream(Channels.newOutputStream(channel));
mapper.writeValue(output, testResult);
});
}

/**
Expand All @@ -101,26 +112,63 @@ public void write(final TestResultContainer testResultContainer) {
final String testResultContainerName = Objects.isNull(testResultContainer.getUuid())
? generateTestResultContainerName()
: generateTestResultContainerName(testResultContainer.getUuid());
ensureInitialized();
final Path file = outputDirectory.resolve(testResultContainerName);
try {
mapper.writeValue(file.toFile(), testResultContainer);
} catch (IOException e) {
throw new AllureResultsWriteException("Could not write Allure test result container", e);
}
write(file, TEST_RESULT_CONTAINER_ENTITY_NAME, channel -> {
final DataOutput output = new DataOutputStream(Channels.newOutputStream(channel));
mapper.writeValue(output, testResultContainer);
});
}

/**
* {@inheritDoc}
*/
@Override
public void write(final String source, final InputStream attachment) {
ensureInitialized();
final Path file = outputDirectory.resolve(source);
try (InputStream is = attachment) {
Files.copy(is, file);
write(file, ATTACHMENT_ENTITY_NAME, channel -> {
final OutputStream output = Channels.newOutputStream(channel);
is.transferTo(output);
});
} catch (IOException e) {
throw new AllureResultsWriteException(getErrorMessage(ATTACHMENT_ENTITY_NAME), e);
}
}

private void write(final Path file, final String entityName, final ChannelConsumer consumer) {
ensureInitialized();
Path tempFile = null;
try {
tempFile = Files.createTempFile(outputDirectory, ".allure-write-", ".tmp");
try (FileChannel channel = FileChannel.open(tempFile, StandardOpenOption.WRITE)) {
consumer.accept(channel);
channel.force(true);
}
Files.move(tempFile, file, StandardCopyOption.REPLACE_EXISTING);
tempFile = null;
} catch (IOException e) {
deleteIfExists(tempFile);
throw new AllureResultsWriteException(getErrorMessage(entityName), e);
}
}

private static String getErrorMessage(final String entityName) {
return "Could not write Allure " + entityName;
}

private interface ChannelConsumer {

void accept(FileChannel channel) throws IOException;
}

private static void deleteIfExists(final Path file) {
if (Objects.isNull(file)) {
return;
}
try {
Files.deleteIfExists(file);
} catch (IOException e) {
throw new AllureResultsWriteException("Could not write Allure attachment", e);
LOGGER.warn("Failed to delete temporary Allure result file {}", file, e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,28 @@

import io.qameta.allure.model.StatusDetails;
import io.qameta.allure.model.TestResult;
import io.qameta.allure.model.TestResultContainer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static io.qameta.allure.FileSystemResultsWriter.generateTestResultContainerName;
import static io.qameta.allure.FileSystemResultsWriter.generateTestResultName;
import static io.qameta.allure.test.ThreadLocalEnhancedRandom.current;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class FileSystemResultsWriterTest {

@Test
Expand All @@ -54,6 +64,19 @@ void shouldWriteTestResult(@TempDir final Path folder) {
.isRegularFile();
}

@Test
void shouldWriteTestResultContainer(@TempDir final Path folder) {
FileSystemResultsWriter writer = new FileSystemResultsWriter(folder);
final String uuid = UUID.randomUUID().toString();
final TestResultContainer container = current().nextObject(TestResultContainer.class).setUuid(uuid);

writer.write(container);

final String fileName = generateTestResultContainerName(uuid);
assertThat(folder.resolve(fileName))
.isRegularFile();
}

@Test
void shouldWriteTitlePath(@TempDir final Path folder) throws IOException {
FileSystemResultsWriter writer = new FileSystemResultsWriter(folder);
Expand Down Expand Up @@ -90,6 +113,35 @@ void shouldWriteStatusDetailsActualAndExpected(@TempDir final Path folder) throw
.contains("\"expected\":\"expected value\"");
}

@Test
void shouldWriteAttachmentFile(@TempDir final Path folder) throws IOException {
FileSystemResultsWriter writer = new FileSystemResultsWriter(folder);
final String source = "source-attachment.txt";
final String content = "attachment body";

writer.write(source, new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));

assertThat(Files.readString(folder.resolve(source)))
.isEqualTo(content);
}

@Test
void shouldNotCreateFinalAttachmentFileWhenStreamFails(@TempDir final Path folder) throws IOException {
FileSystemResultsWriter writer = new FileSystemResultsWriter(folder);
final String source = "broken-attachment.txt";
final byte[] content = "partial attachment body".getBytes(StandardCharsets.UTF_8);

assertThatThrownBy(() -> writer.write(source, new FailingInputStream(content)))
.isInstanceOf(AllureResultsWriteException.class)
.hasMessage("Could not write Allure attachment")
.hasCauseInstanceOf(IOException.class);

assertThat(folder.resolve(source))
.doesNotExist();
assertThat(listFiles(folder))
.isEmpty();
}

@Test
void shouldPreserveOldResultsWhenCleanIsDisabled(@TempDir final Path folder) throws IOException {
Path existingFile = folder.resolve("existing-result.json");
Expand Down Expand Up @@ -176,4 +228,30 @@ private static void writeTestResult(final FileSystemResultsWriter writer, final
writer.write(testResult);
});
}

private static List<Path> listFiles(final Path folder) throws IOException {
try (Stream<Path> files = Files.list(folder)) {
return files.collect(Collectors.toList());
}
}

private static final class FailingInputStream extends InputStream {

private final byte[] content;

private int index;

private FailingInputStream(final byte[] content) {
this.content = content;
}

@Override
public int read() throws IOException {
if (index < content.length) {
return content[index++] & 0xff;
}
throw new IOException("Simulated attachment stream failure");
}
}

}
Loading