Skip to content

Commit 8d41e9d

Browse files
authored
introduce single file report mode (fixes #755, via #2072)
1 parent cb08a70 commit 8d41e9d

File tree

93 files changed

+1507
-419
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+1507
-419
lines changed

allure-commandline/src/main/java/io/qameta/allure/CommandLine.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ public ExitCode run() {
161161
generateCommand.getReportDirectory(),
162162
generateCommand.getResultsOptions().getResultsDirectories(),
163163
generateCommand.isCleanReportDirectory(),
164+
generateCommand.isSingleFileMode(),
164165
generateCommand.getConfigOptions()
165166
);
166167
case SERVE_COMMAND:

allure-commandline/src/main/java/io/qameta/allure/Commands.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class Commands {
5151

5252
private static final Logger LOGGER = LoggerFactory.getLogger(Commands.class);
5353
private static final String DIRECTORY_EXISTS_MESSAGE = "Allure: Target directory {} for the report is already"
54-
+ " in use, add a '--clean' option to overwrite";
54+
+ " in use, add a '--clean' option to overwrite";
5555

5656
private final Path allureHome;
5757

@@ -91,19 +91,26 @@ public ExitCode generate(final Path reportDirectory,
9191
final List<Path> resultsDirectories,
9292
final boolean clean,
9393
final ConfigOptions profile) {
94+
return generate(reportDirectory, resultsDirectories, clean, false, profile);
95+
}
96+
97+
public ExitCode generate(final Path reportDirectory,
98+
final List<Path> resultsDirectories,
99+
final boolean clean,
100+
final boolean singleFileMode,
101+
final ConfigOptions profile) {
94102
final boolean directoryExists = Files.exists(reportDirectory);
95103
if (clean && directoryExists) {
96104
FileUtils.deleteQuietly(reportDirectory.toFile());
97105
} else if (directoryExists && isDirectoryNotEmpty(reportDirectory)) {
98106
LOGGER.error(DIRECTORY_EXISTS_MESSAGE, reportDirectory.toAbsolutePath());
99107
return ExitCode.GENERIC_ERROR;
100108
}
101-
try {
102-
final ReportGenerator generator = new ReportGenerator(createReportConfiguration(profile));
109+
final ReportGenerator generator = new ReportGenerator(createReportConfiguration(profile));
110+
if (singleFileMode) {
111+
generator.generateSingleFile(reportDirectory, resultsDirectories);
112+
} else {
103113
generator.generate(reportDirectory, resultsDirectories);
104-
} catch (IOException e) {
105-
LOGGER.error("Could not generate report", e);
106-
return ExitCode.GENERIC_ERROR;
107114
}
108115
LOGGER.info("Report successfully generated to {}", reportDirectory);
109116
return ExitCode.NO_ERROR;
@@ -152,7 +159,7 @@ public ExitCode open(final Path reportDirectory, final String host, final int po
152159
openBrowser(server.getURI());
153160
} catch (IOException | AWTError e) {
154161
LOGGER.error(
155-
"Could not open the report in browser, try to open it manually {}: {}",
162+
"Could not open the report in browser, try to open it manually {}",
156163
server.getURI(),
157164
e
158165
);
@@ -221,11 +228,11 @@ protected void openBrowser(final URI url) throws IOException {
221228
Desktop.getDesktop().browse(url);
222229
} catch (UnsupportedOperationException e) {
223230
LOGGER.error("Browse operation is not supported on your platform."
224-
+ "You can use the link below to open the report manually.", e);
231+
+ "You can use the link below to open the report manually.", e);
225232
}
226233
} else {
227234
LOGGER.error("Can not open browser because this capability is not supported on "
228-
+ "your platform. You can use the link below to open the report manually.");
235+
+ "your platform. You can use the link below to open the report manually.");
229236
}
230237
}
231238

allure-commandline/src/main/java/io/qameta/allure/command/GenerateCommand.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ public class GenerateCommand {
4747
)
4848
private Path reportDirectory = Paths.get("allure-report");
4949

50+
@Parameter(
51+
names = {"--single-file"},
52+
description = "Generate Allure report in single file mode."
53+
)
54+
private boolean singleFileMode;
55+
5056
@ParametersDelegate
5157
private ResultsOptions resultsOptions = new ResultsOptions();
5258

@@ -68,4 +74,8 @@ public ResultsOptions getResultsOptions() {
6874
public ConfigOptions getConfigOptions() {
6975
return configOptions;
7076
}
77+
78+
public boolean isSingleFileMode() {
79+
return singleFileMode;
80+
}
7181
}

allure-commandline/src/test/java/io/qameta/allure/plugin/DirectoryPluginLoaderTest.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.io.InputStream;
2727
import java.nio.file.Files;
2828
import java.nio.file.Path;
29+
import java.util.Map;
2930
import java.util.Objects;
3031
import java.util.Optional;
3132

@@ -57,6 +58,7 @@ void shouldLoadEmptyPlugin(@TempDir final Path pluginDirectory) {
5758
.isEmpty();
5859
}
5960

61+
@SuppressWarnings("deprecation")
6062
@Test
6163
void shouldLoadPluginExtensions(@TempDir final Path pluginFolder) throws Exception {
6264
add(pluginFolder, "plugin.jar", "plugin.jar");
@@ -108,14 +110,16 @@ void shouldLoadStaticOnlyPlugin(@TempDir final Path temp) throws Exception {
108110
.isNotNull()
109111
.isEmpty();
110112

111-
final Path unpack = Files.createDirectories(temp.resolve("unpack"));
112-
plugin.unpackReportStatic(unpack);
113113

114-
assertThat(unpack.resolve("some-file"))
114+
final Map<String, Path> pluginFiles = plugin.getPluginFiles();
115+
116+
assertThat(pluginFiles).containsKey("some-file");
117+
assertThat(pluginFiles.get("some-file"))
115118
.isRegularFile()
116119
.hasContent("ho-ho-ho");
117120
}
118121

122+
@SuppressWarnings("deprecation")
119123
@Test
120124
void shouldLoadJarsInLibDirectory(@TempDir final Path pluginFolder) throws Exception {
121125
add(pluginFolder, "plugin.jar", "lib/plugin.jar");

allure-generator/src/main/java/io/qameta/allure/ConfigurationBuilder.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import io.qameta.allure.core.Configuration;
2929
import io.qameta.allure.core.MarkdownDescriptionsPlugin;
3030
import io.qameta.allure.core.Plugin;
31-
import io.qameta.allure.core.ReportWebPlugin;
3231
import io.qameta.allure.core.TestsResultsPlugin;
3332
import io.qameta.allure.duration.DurationPlugin;
3433
import io.qameta.allure.duration.DurationTrendPlugin;
@@ -110,7 +109,6 @@ public ConfigurationBuilder useDefault() {
110109
new StatusChartPlugin(),
111110
new TimelinePlugin(),
112111
new SuitesPlugin(),
113-
new ReportWebPlugin(),
114112
new TestsResultsPlugin(),
115113
new AttachmentsPlugin(),
116114
new MailPlugin(),

allure-generator/src/main/java/io/qameta/allure/DefaultConfiguration.java

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.util.Collections;
2222
import java.util.List;
2323
import java.util.Optional;
24-
import java.util.stream.Collectors;
2524

2625
/**
2726
* Default implementation of {@link Configuration}.
@@ -46,26 +45,13 @@ public List<Plugin> getPlugins() {
4645
}
4746

4847
@Override
49-
public List<Aggregator> getAggregators() {
50-
return extensions.stream()
51-
.filter(Aggregator.class::isInstance)
52-
.map(Aggregator.class::cast)
53-
.collect(Collectors.toList());
48+
public List<Extension> getExtensions() {
49+
return Collections.unmodifiableList(extensions);
5450
}
5551

5652
@Override
57-
public List<Reader> getReaders() {
58-
return extensions.stream()
59-
.filter(Reader.class::isInstance)
60-
.map(Reader.class::cast)
61-
.collect(Collectors.toList());
62-
}
63-
64-
@Override
65-
public <T> Optional<T> getContext(final Class<T> contextType) {
66-
return extensions.stream()
67-
.filter(contextType::isInstance)
68-
.map(contextType::cast)
53+
public <S, T extends Context<S>> Optional<T> getContext(final Class<T> contextType) {
54+
return getExtensions(contextType).stream()
6955
.findFirst();
7056
}
7157
}

allure-generator/src/main/java/io/qameta/allure/DefaultResultsVisitor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ private static String getExtensionByMimeType(final String type) {
120120
try {
121121
return getDefaultMimeTypes().forName(type).getExtension();
122122
} catch (Exception e) {
123-
LOGGER.warn("Can't detect extension for MIME-type {} {}", type, e);
123+
LOGGER.warn("Can't detect extension for MIME-type {}", type, e);
124124
return "";
125125
}
126126
}
@@ -129,7 +129,7 @@ public static String probeContentType(final Path path) {
129129
try (InputStream stream = newInputStream(path)) {
130130
return probeContentType(stream, Objects.toString(path.getFileName()));
131131
} catch (IOException e) {
132-
LOGGER.warn("Couldn't detect the media type of attachment {} {}", path, e);
132+
LOGGER.warn("Couldn't detect the media type of attachment {}", path, e);
133133
return WILDCARD;
134134
}
135135
}
@@ -140,7 +140,7 @@ public static String probeContentType(final InputStream is, final String name) {
140140
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, name);
141141
return getDefaultMimeTypes().detect(stream, metadata).toString();
142142
} catch (IOException e) {
143-
LOGGER.warn("Couldn't detect the media type of attachment {} {}", name, e);
143+
LOGGER.warn("Couldn't detect the media type of attachment {}", name, e);
144144
return WILDCARD;
145145
}
146146
}
@@ -149,7 +149,7 @@ private static Long getFileSizeSafe(final Path path) {
149149
try {
150150
return size(path);
151151
} catch (IOException e) {
152-
LOGGER.warn("Could not get the size of file {} {}", path, e);
152+
LOGGER.warn("Could not get the size of file {}", path, e);
153153
return null;
154154
}
155155
}

allure-generator/src/main/java/io/qameta/allure/DummyReportGenerator.java

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@
2626
import io.qameta.allure.context.ReportInfoContext;
2727
import io.qameta.allure.core.AttachmentsPlugin;
2828
import io.qameta.allure.core.Configuration;
29-
import io.qameta.allure.core.LaunchResults;
3029
import io.qameta.allure.core.MarkdownDescriptionsPlugin;
3130
import io.qameta.allure.core.Plugin;
32-
import io.qameta.allure.core.ReportWebPlugin;
3331
import io.qameta.allure.core.TestsResultsPlugin;
3432
import io.qameta.allure.duration.DurationPlugin;
3533
import io.qameta.allure.duration.DurationTrendPlugin;
@@ -80,9 +78,9 @@ public final class DummyReportGenerator {
8078
private static final Logger LOGGER = LoggerFactory.getLogger(DummyReportGenerator.class);
8179
private static final int MIN_ARGUMENTS_COUNT = 2;
8280
private static final List<Extension> EXTENSIONS = Arrays.asList(
81+
new ReportInfoContext("dev"),
8382
new JacksonContext(),
8483
new MarkdownContext(),
85-
new ReportInfoContext("dev"),
8684
new FreemarkerContext(),
8785
new RandomUidContext(),
8886
new MarkdownDescriptionsPlugin(),
@@ -111,16 +109,7 @@ public final class DummyReportGenerator {
111109
new LaunchPlugin(),
112110
new Allure1Plugin(),
113111
new Allure1EnvironmentPlugin(),
114-
new Allure2Plugin(),
115-
new ReportWebPlugin() {
116-
@Override
117-
public void aggregate(final Configuration configuration,
118-
final List<LaunchResults> launchesResults,
119-
final Path outputDirectory) throws IOException {
120-
writePluginsStatic(configuration, outputDirectory);
121-
writeIndexHtml(configuration, outputDirectory);
122-
}
123-
}
112+
new Allure2Plugin()
124113
);
125114

126115
private DummyReportGenerator() {
@@ -148,7 +137,7 @@ public static void main(final String... args) throws IOException {
148137
.fromPlugins(plugins)
149138
.build();
150139
final ReportGenerator generator = new ReportGenerator(configuration);
151-
generator.generate(files[lastIndex], Arrays.copyOf(files, lastIndex));
140+
generator.generateSingleFile(files[lastIndex], Arrays.asList(Arrays.copyOf(files, lastIndex)));
152141
}
153142

154143
public static Path[] getFiles(final String... paths) {

allure-generator/src/main/java/io/qameta/allure/ReportGenerator.java

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,25 @@
1616
package io.qameta.allure;
1717

1818
import io.qameta.allure.core.Configuration;
19+
import io.qameta.allure.core.FileSystemReportStorage;
20+
import io.qameta.allure.core.InMemoryReportStorage;
1921
import io.qameta.allure.core.LaunchResults;
22+
import io.qameta.allure.core.ReportWebGenerator;
23+
import io.qameta.allure.util.DeleteVisitor;
2024
import org.slf4j.Logger;
2125
import org.slf4j.LoggerFactory;
2226

2327
import java.io.IOException;
28+
import java.io.UncheckedIOException;
29+
import java.nio.file.FileVisitResult;
2430
import java.nio.file.Files;
2531
import java.nio.file.Path;
32+
import java.nio.file.SimpleFileVisitor;
33+
import java.nio.file.attribute.BasicFileAttributes;
34+
import java.util.Arrays;
2635
import java.util.List;
36+
import java.util.Objects;
2737
import java.util.stream.Collectors;
28-
import java.util.stream.Stream;
2938

3039
/**
3140
* @author charlie (Dmitry Baev).
@@ -40,34 +49,82 @@ public ReportGenerator(final Configuration configuration) {
4049
this.configuration = configuration;
4150
}
4251

43-
public LaunchResults readResults(final Path resultsDirectory) {
52+
private LaunchResults readResults(final Path resultsDirectory) {
4453
final DefaultResultsVisitor visitor = new DefaultResultsVisitor(configuration);
45-
configuration
46-
.getReaders()
54+
configuration.getExtensions(Reader.class)
4755
.forEach(reader -> reader.readResults(configuration, visitor, resultsDirectory));
4856
return visitor.getLaunchResults();
4957
}
5058

51-
public void aggregate(final List<LaunchResults> results, final Path outputDirectory) throws IOException {
52-
for (Aggregator aggregator : configuration.getAggregators()) {
53-
aggregator.aggregate(configuration, results, outputDirectory);
59+
private void aggregate(final List<LaunchResults> results, final ReportStorage storage) {
60+
processOldAggregators(results, storage);
61+
62+
for (final Aggregator2 aggregator : configuration.getExtensions(Aggregator2.class)) {
63+
aggregator.aggregate(configuration, results, storage);
5464
}
5565
}
5666

57-
public void generate(final Path outputDirectory, final List<Path> resultsDirectories) throws IOException {
58-
generate(outputDirectory, resultsDirectories.stream());
67+
@SuppressWarnings("deprecation")
68+
private void processOldAggregators(final List<LaunchResults> results,
69+
final ReportStorage storage) {
70+
Path tempDir = null;
71+
try {
72+
tempDir = Files.createTempDirectory("allure-");
73+
74+
for (Aggregator aggregator : configuration.getExtensions(Aggregator.class)) {
75+
aggregator.aggregate(configuration, results, tempDir);
76+
}
77+
78+
final Path finalTempDir = tempDir;
79+
Files.walkFileTree(tempDir, new SimpleFileVisitor<Path>() {
80+
@Override
81+
public FileVisitResult visitFile(final Path file,
82+
final BasicFileAttributes attrs) {
83+
final String fileId = file.relativize(finalTempDir).toString();
84+
storage.addDataFile(fileId, file);
85+
return FileVisitResult.CONTINUE;
86+
}
87+
});
88+
} catch (IOException e) {
89+
throw new UncheckedIOException(e);
90+
} finally {
91+
if (Objects.nonNull(tempDir)) {
92+
try {
93+
Files.walkFileTree(tempDir, new DeleteVisitor());
94+
} catch (IOException ignored) {
95+
// do nothing
96+
}
97+
}
98+
}
99+
59100
}
60101

61-
public void generate(final Path outputDirectory, final Path... resultsDirectories) throws IOException {
62-
generate(outputDirectory, Stream.of(resultsDirectories));
102+
public void generate(final Path outputDirectory, final List<Path> resultsDirectories) {
103+
generate(
104+
new FileSystemReportStorage(outputDirectory),
105+
outputDirectory,
106+
resultsDirectories
107+
);
63108
}
64109

65-
private void generate(final Path outputDirectory, final Stream<Path> resultsDirectories) throws IOException {
66-
final List<LaunchResults> results = resultsDirectories
110+
public void generate(final Path outputDirectory, final Path... resultsDirectories) {
111+
generate(outputDirectory, Arrays.asList(resultsDirectories));
112+
}
113+
114+
private void generate(final ReportStorage storage,
115+
final Path outputDirectory,
116+
final List<Path> resultsDirectories) {
117+
final List<LaunchResults> results = resultsDirectories.stream()
67118
.filter(this::isValidResultsDirectory)
68119
.map(this::readResults)
69120
.collect(Collectors.toList());
70-
aggregate(results, outputDirectory);
121+
aggregate(results, storage);
122+
new ReportWebGenerator().generate(configuration, storage, outputDirectory);
123+
}
124+
125+
public void generateSingleFile(final Path outputDirectory, final List<Path> resultsDirectories) {
126+
final InMemoryReportStorage storage = new InMemoryReportStorage();
127+
generate(storage, outputDirectory, resultsDirectories);
71128
}
72129

73130
private boolean isValidResultsDirectory(final Path resultsDirectory) {

0 commit comments

Comments
 (0)