Skip to content

Commit

Permalink
Merge pull request #2003 from jcarranzan/feature/picocli-coverage
Browse files Browse the repository at this point in the history
Add coverage for quarkus-picocli
  • Loading branch information
michalvavrik authored Sep 20, 2024
2 parents 6b71d4b + dfb7a58 commit ee2795a
Show file tree
Hide file tree
Showing 15 changed files with 324 additions and 0 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@
<module>scheduling/quartz</module>
<module>super-size/many-extensions</module>
<module>quarkus-cli</module>
<module>quarkus-picocli</module>
<module>logging/jboss</module>
<module>logging/thirdparty</module>
<module>qute/multimodule</module>
Expand Down
18 changes: 18 additions & 0 deletions quarkus-picocli/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.quarkus.ts.qe</groupId>
<artifactId>parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>quarkus-picocli</artifactId>
<name>Quarkus QE TS: Quarkus Picocli</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-picocli</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.ts.qe.command;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.logging.Log;

import picocli.CommandLine;

/**
* According picocli guide,
* Beans with @CommandLine.Command should not use proxied scopes (e.g. do not use @ApplicationScope)
* because Picocli will not be able to set field values in such beans.
* So this class creation is to test it, see testAgeCommandWithApplicationScoped.
*/
@CommandLine.Command(name = "age", mixinStandardHelpOptions = true)
@ApplicationScoped
public class AgeCommand implements Runnable {
@CommandLine.Option(names = { "-a", "--age" }, description = "your age", defaultValue = "0")
private int age;

@Override
public void run() {
Log.info("Your age is: " + age);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.quarkus.ts.qe.command;

import picocli.CommandLine;

public class CommonOptions {
@CommandLine.Option(names = { "-v", "--verbose" }, description = "Enable verbose mode")
boolean verbose;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.quarkus.ts.qe.command;

import io.quarkus.logging.Log;

import picocli.CommandLine;

@CommandLine.Command(name = "customized-command", mixinStandardHelpOptions = true, subcommands = { HelloCommand.class,
AgeCommand.class })
public class EntryCommand implements Runnable {
@CommandLine.Spec
CommandLine.Model.CommandSpec spec;

@Override
public void run() {
Log.info("Running EntryCommand with name: " + spec.name());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.quarkus.ts.qe.command;

import jakarta.inject.Inject;

import io.quarkus.logging.Log;
import io.quarkus.ts.qe.services.HelloService;

import picocli.CommandLine;

@CommandLine.Command(name = "greeting", mixinStandardHelpOptions = true)
public class HelloCommand implements Runnable {
@CommandLine.Option(names = { "-n", "--name" }, description = "Who will we greet?", defaultValue = "World")
String name;
@Inject
HelloService helloService;

public HelloCommand() {
}

public HelloCommand(HelloService helloService) {
this.helloService = helloService;
}

@Override
public void run() {
Log.info("Executing HelloCommand with name: " + name);
helloService.greet(name);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.quarkus.ts.qe.command;

import io.quarkus.logging.Log;

import picocli.CommandLine;

@CommandLine.Command(name = "start", description = "Starts the service.")
public class OtherCommand implements Runnable {

@CommandLine.Mixin
CommonOptions commonOptions;

@CommandLine.Option(names = { "-t", "--timeout" }, description = "Timeout in seconds", defaultValue = "60")
int timeout;

@Override
public void run() {
Log.infof("Service started with timeout: %d and verbosity: %s%n", timeout, commonOptions);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.quarkus.ts.qe.command;

import io.quarkus.logging.Log;

import picocli.CommandLine;

@CommandLine.Command(name = "other", mixinStandardHelpOptions = true, subcommands = { OtherCommand.class })
public class OtherEntryCommand implements Runnable {
@CommandLine.Spec
CommandLine.Model.CommandSpec spec;

@Override
public void run() {
Log.info("OtherEntryCommand with name " + spec.name());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.quarkus.ts.qe.configuration;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;

import io.quarkus.arc.profile.IfBuildProfile;
import io.quarkus.picocli.runtime.annotations.TopCommand;
import io.quarkus.ts.qe.command.EntryCommand;
import io.quarkus.ts.qe.command.OtherEntryCommand;

@ApplicationScoped
public class Config {
@Produces
@TopCommand
@IfBuildProfile("dev")
public Object devCommand() {
return EntryCommand.class;
}

@Produces
@TopCommand
@IfBuildProfile("prod")
public Object prodCommand() {
return OtherEntryCommand.class;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.ts.qe.services;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class AgeService {

public String processAge(int age) {
return "Processed age: " + age;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkus.ts.qe.services;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.logging.Log;

@ApplicationScoped
public class HelloService {

public String greet(String name) {
String message = String.format("Hello %s!", name);
Log.info(message);
return message;
}
}
102 changes: 102 additions & 0 deletions quarkus-picocli/src/test/java/PicocliDevIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import static java.util.concurrent.CompletableFuture.runAsync;

import org.junit.jupiter.api.Test;

import io.quarkus.test.bootstrap.RestService;
import io.quarkus.test.scenarios.QuarkusScenario;
import io.quarkus.test.services.QuarkusApplication;
import io.quarkus.ts.qe.command.AgeCommand;
import io.quarkus.ts.qe.command.EntryCommand;
import io.quarkus.ts.qe.command.HelloCommand;
import io.quarkus.ts.qe.configuration.Config;
import io.quarkus.ts.qe.services.AgeService;
import io.quarkus.ts.qe.services.HelloService;

@QuarkusScenario
public class PicocliDevIT {

@QuarkusApplication(classes = { EntryCommand.class, Config.class, HelloCommand.class, AgeCommand.class, AgeService.class,
HelloService.class }, properties = "dev.properties")
static final RestService greetingApp = new RestService()
.withProperty("quarkus.args", "greeting --name QE")
.setAutoStart(false);

@QuarkusApplication(classes = { EntryCommand.class, Config.class, HelloCommand.class, AgeCommand.class, AgeService.class,
HelloService.class }, properties = "dev.properties")
static final RestService ageApp = new RestService()
.withProperty("quarkus.args", "age --age 30")
.setAutoStart(false);

@QuarkusApplication(classes = { EntryCommand.class, Config.class, HelloCommand.class, AgeCommand.class, AgeService.class,
HelloService.class }, properties = "dev.properties")
static final RestService greetingBlankArgumentApp = new RestService()
.withProperty("quarkus.args", " --name QE")
.setAutoStart(false);

@QuarkusApplication(classes = { EntryCommand.class, Config.class, HelloCommand.class, AgeCommand.class, AgeService.class,
HelloService.class }, properties = "dev.properties")
static final RestService greetingInvalidArgumentApp = new RestService()
.withProperty("quarkus.args", "greeting -x QE")
.setAutoStart(false);

@QuarkusApplication(classes = { EntryCommand.class, Config.class, HelloCommand.class, AgeCommand.class, AgeService.class,
HelloService.class }, properties = "dev.properties")
static final RestService bothTopCommandApp = new RestService()
.setAutoStart(false);

@Test
public void verifyErrorForApplicationScopedBeanInPicocliCommand() {
try {
runAsync(ageApp::start);
ageApp.logs().assertContains("CDI: programmatic lookup problem detected");
} finally {
ageApp.stop();
}
}

@Test
public void verifyGreetingCommandOutputsExpectedMessage() {
try {
runAsync(greetingApp::start);
greetingApp.logs().assertContains("Hello QE!");
} finally {
greetingApp.stop();
}
}

@Test
void verifyErrorForBlankArgumentsInGreetingCommand() {
try {
runAsync(greetingBlankArgumentApp::start);
greetingBlankArgumentApp.logs().assertContains("Unmatched arguments from index 0: '', '--name', 'QE'");
} finally {
greetingBlankArgumentApp.stop();
}
}

@Test
void verifyErrorForInvalidArgumentsInGreetingCommand() {
try {
runAsync(greetingInvalidArgumentApp::start);
greetingInvalidArgumentApp.logs().assertContains("Unknown options: '-x', 'QE'");
} finally {
greetingInvalidArgumentApp.stop();
}
}

/**
* Chain Commands in a Single Execution is not possible
*/
@Test
public void verifyErrorForMultipleCommandsWithoutTopCommand() {
bothTopCommandApp
.withProperty("quarkus.args", "greeting --name EEUU age --age 247");
try {
runAsync(bothTopCommandApp::start);
bothTopCommandApp.logs().assertContains("Unmatched arguments from index 3: 'age', '--age', '247'");
} finally {
bothTopCommandApp.stop();
}
}

}
31 changes: 31 additions & 0 deletions quarkus-picocli/src/test/java/PicocliProdIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import static java.util.concurrent.CompletableFuture.runAsync;

import org.junit.jupiter.api.Test;

import io.quarkus.test.bootstrap.RestService;
import io.quarkus.test.scenarios.QuarkusScenario;
import io.quarkus.test.services.QuarkusApplication;
import io.quarkus.ts.qe.command.CommonOptions;
import io.quarkus.ts.qe.command.OtherCommand;
import io.quarkus.ts.qe.command.OtherEntryCommand;
import io.quarkus.ts.qe.configuration.Config;

@QuarkusScenario
public class PicocliProdIT {
@QuarkusApplication(classes = { OtherEntryCommand.class, Config.class, OtherCommand.class,
CommonOptions.class }, properties = "prod.properties")
static final RestService customized = new RestService()
.withProperty("quarkus.args", "start -t 60 -v")
.setAutoStart(false);

@Test
public void verifyCustomizedCommandLineBehavior() {
String expectedOutput = "Service started with timeout: 60 and verbosity";
try {
runAsync(customized::start);
customized.logs().assertContains(expectedOutput);
} finally {
customized.stop();
}
}
}
1 change: 1 addition & 0 deletions quarkus-picocli/src/test/resources/dev.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
quarkus.profile=dev
1 change: 1 addition & 0 deletions quarkus-picocli/src/test/resources/prod.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
quarkus.profile=prod

0 comments on commit ee2795a

Please sign in to comment.