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
22 changes: 22 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,28 @@ jobs:
run: ./bin/allure --version
- name: Generate demo report
run: ./bin/allure generate testcmd-smoke-results
- name: Verify generated tree data
shell: pwsh
run: |
$requiredFiles = @(
'allure-report/data/behaviors.json',
'allure-report/data/behaviors.csv',
'allure-report/data/packages.json',
'allure-report/widgets/behaviors.json'
)

$missingFiles = $requiredFiles | Where-Object {
-not (Test-Path -LiteralPath $_ -PathType Leaf)
}

if ($missingFiles.Count -gt 0) {
Write-Error "Missing generated report files: $($missingFiles -join ', ')"
exit 1
}

$requiredFiles | ForEach-Object {
Write-Host "Verified $_"
}
report:
needs: [build, testcmd]
name: "Build report"
Expand Down
6 changes: 0 additions & 6 deletions allure-commandline/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,6 @@ tasks.distTar {
val startScripts by tasks.existing(CreateStartScripts::class) {
applicationName = "allure"
classpath = files(tasks.jar) + configurations.runtimeClasspath.get() + files("src/lib/config")
doLast {
unixScript.writeText(unixScript.readText()
.replace(Regex("(?m)^APP_HOME="), "export APP_HOME=")
.replace("\$(uname)\" = \"Darwin", "")
)
}
}

tasks.build {
Expand Down
80 changes: 73 additions & 7 deletions allure-commandline/src/main/java/io/qameta/allure/CommandLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@

import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.ProviderNotFoundException;
import java.security.CodeSource;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
Expand All @@ -51,6 +57,10 @@ public class CommandLine {
protected static final String GENERATE_COMMAND = "generate";
protected static final String OPEN_COMMAND = "open";
protected static final String PLUGIN_COMMAND = "plugin";
private static final String CONFIG_DIRECTORY = "config";
private static final String PLUGINS_DIRECTORY = "plugins";
private static final String DEFAULT_CONFIG_FILE_NAME = "allure.yml";
private static final String LIB_DIRECTORY = "lib";

private final MainCommand mainCommand;
private final ServeCommand serveCommand;
Expand Down Expand Up @@ -80,20 +90,76 @@ public CommandLine(final Commands commands) {
}

public static void main(final String[] args) throws InterruptedException {
final String allureHome = System.getenv("APP_HOME");
final CommandLine commandLine;
if (Objects.isNull(allureHome)) {
commandLine = new CommandLine((Path) null);
LOGGER.info("APP_HOME is not set, using default configuration");
} else {
commandLine = new CommandLine(Paths.get(allureHome));
final Optional<Path> allureHome = resolveAllureHome(System.getenv("APP_HOME"));
if (!allureHome.isPresent()) {
LOGGER.info("Allure home is not set, using default configuration");
}
final CommandLine commandLine = new CommandLine(allureHome.orElse(null));
final ExitCode exitCode = commandLine
.parse(args)
.orElseGet(commandLine::run);
System.exit(exitCode.getCode());
}

static Optional<Path> resolveAllureHome(final String allureHome, final URI codeSource) {
if (Objects.nonNull(allureHome)) {
return Optional.of(Paths.get(allureHome));
}
return inferAllureHome(codeSource);
}

private static Optional<Path> resolveAllureHome(final String allureHome) {
if (Objects.nonNull(allureHome)) {
return Optional.of(Paths.get(allureHome));
}
return getCodeSource()
.flatMap(CommandLine::inferAllureHome);
}

private static Optional<URI> getCodeSource() {
try {
return Optional.ofNullable(CommandLine.class.getProtectionDomain().getCodeSource())
.map(CodeSource::getLocation)
.map(location -> {
try {
return location.toURI();
} catch (URISyntaxException e) {
LOGGER.debug("Could not resolve commandline location", e);
return null;
}
});
} catch (SecurityException e) {
LOGGER.debug("Could not access commandline location", e);
return Optional.empty();
}
}

private static Optional<Path> inferAllureHome(final URI codeSource) {
try {
final Path location = Paths.get(codeSource).toAbsolutePath().normalize();
final Path lib = Files.isDirectory(location) ? location : location.getParent();
if (Objects.isNull(lib)
|| Objects.isNull(lib.getFileName())
|| !LIB_DIRECTORY.equals(lib.getFileName().toString())) {
return Optional.empty();
}
final Path home = lib.getParent();
return isAllureHome(home)
? Optional.of(home)
: Optional.empty();
} catch (IllegalArgumentException | FileSystemNotFoundException | ProviderNotFoundException
| SecurityException e) {
LOGGER.debug("Could not infer Allure home from commandline location {}", codeSource, e);
return Optional.empty();
}
}

private static boolean isAllureHome(final Path home) {
return Objects.nonNull(home)
&& Files.isRegularFile(home.resolve(CONFIG_DIRECTORY).resolve(DEFAULT_CONFIG_FILE_NAME))
&& Files.isDirectory(home.resolve(PLUGINS_DIRECTORY));
}

public Optional<ExitCode> parse(final String... args) {
if (args.length == 0) {
printUsage(commander);
Expand Down
100 changes: 100 additions & 0 deletions allure-commandline/src/test/java/io/qameta/allure/CommandLineTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.mockito.ArgumentCaptor;

import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
Expand Down Expand Up @@ -62,6 +63,54 @@ void setUp() {
this.commandLine = new CommandLine(commands);
}

/**
* Verifies the explicit application home is used as the commandline home.
* The test checks APP_HOME has priority over the commandline location.
*/
@Description
@Test
void shouldResolveAllureHomeFromAppHome(@TempDir final Path temp) {
final Path appHome = temp.resolve("custom-home");
final URI commandlineLocation = temp.resolve("allure").resolve("lib").resolve("allure-commandline.jar")
.toUri();

final Optional<Path> result = resolveAllureHome(appHome.toString(), commandlineLocation);

assertThat(result)
.hasValue(appHome);
}

/**
* Verifies the commandline can infer an installed distribution home.
* The test checks a commandline jar under lib resolves to the parent distribution directory.
*/
@Description
@Test
void shouldInferAllureHomeFromCommandlineJar(@TempDir final Path temp) throws IOException {
final Path allureHome = createAllureHome(temp);
final Path commandlineJar = createCommandlineJar(allureHome);

final Optional<Path> result = resolveAllureHome(null, commandlineJar.toUri());

assertThat(result)
.hasValue(allureHome);
}

/**
* Verifies invalid commandline locations are ignored.
* The test checks a candidate without distribution config and plugins does not resolve.
*/
@Description
@Test
void shouldIgnoreInvalidInferredAllureHome(@TempDir final Path temp) throws IOException {
final Path commandlineJar = createCommandlineJar(temp.resolve("invalid-allure"));

final Optional<Path> result = resolveAllureHome(null, commandlineJar.toUri());

assertThat(result)
.isEmpty();
}

/**
* Verifies that invoking the parser without arguments is rejected.
* The test checks the command line reports an argument parsing error.
Expand Down Expand Up @@ -582,4 +631,55 @@ private String describeMainCommand() {
verboseOptions.isQuiet()
);
}

@Step("Create installed Allure home")
private Path createAllureHome(final Path temp) throws IOException {
final Path allureHome = temp.resolve("allure");
Files.createDirectories(allureHome.resolve("config"));
Files.write(allureHome.resolve("config").resolve("allure.yml"), Arrays.asList("plugins: []"));
Files.createDirectories(allureHome.resolve("plugins"));
Allure.addAttachment(
"allure-home-layout.txt",
"text/plain",
String.format(
"home=%s%nconfig=%s%nplugins=%s%n",
allureHome,
allureHome.resolve("config").resolve("allure.yml"),
allureHome.resolve("plugins")
),
".txt"
);
return allureHome;
}

@Step("Create commandline jar")
private Path createCommandlineJar(final Path allureHome) throws IOException {
final Path lib = Files.createDirectories(allureHome.resolve("lib"));
final Path commandlineJar = lib.resolve("allure-commandline-test.jar");
Files.write(commandlineJar, Arrays.asList("commandline jar marker"));
Allure.addAttachment(
"commandline-location.txt",
"text/plain",
String.format("commandlineJar=%s%n", commandlineJar),
".txt"
);
return commandlineJar;
}

@Step("Resolve Allure home")
private Optional<Path> resolveAllureHome(final String appHome, final URI commandlineLocation) {
final Optional<Path> result = CommandLine.resolveAllureHome(appHome, commandlineLocation);
Allure.addAttachment(
"allure-home-resolution.txt",
"text/plain",
String.format(
"appHome=%s%ncommandlineLocation=%s%nresolvedHome=%s%n",
appHome,
commandlineLocation,
result.map(Path::toString).orElse("<empty>")
),
".txt"
);
return result;
}
}
Loading