This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-25718 in repository https://gitbox.apache.org/repos/asf/camel.git
commit b4736de5b5594afb82cf5a04e1ff178d65c66ce8 Author: Claus Ibsen <[email protected]> AuthorDate: Sat Oct 17 10:49:20 2026 +0200 CAMEL-25718: camel-jbang - Document the commands of camel test that come from Citrus The test plugin registers commands of Citrus as they are (inspect, run, ps, agent and its start, run and stop), and PrepareCamelJBangCommandsMojo only documented a command whose class has a source in the module, so 7 of the 9 commands of camel test had no page. A command class that the source registering it imports by name is now read from the classpath of the project: its @Command (description, hidden) and its options, by reflection, as the options of a library class (CAMEL-25717). T [...] Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../jbang-commands/camel-jbang-test-agent-run.adoc | 43 +++++++ .../camel-jbang-test-agent-start.adoc | 44 +++++++ ...-test.adoc => camel-jbang-test-agent-stop.adoc} | 15 +-- ...jbang-test.adoc => camel-jbang-test-agent.adoc} | 10 +- ...ang-test.adoc => camel-jbang-test-inspect.adoc} | 15 +-- ...el-jbang-test.adoc => camel-jbang-test-ps.adoc} | 17 +-- .../pages/jbang-commands/camel-jbang-test-run.adoc | 41 +++++++ .../pages/jbang-commands/camel-jbang-test.adoc | 4 + .../camel-jbang-plugin-commands-metadata.json | 2 +- .../packaging/PrepareCamelJBangCommandsMojo.java | 85 ++++++++++++-- .../PrepareCamelJBangCommandsMojoTest.java | 130 ++++++++++++++++++--- 11 files changed, 343 insertions(+), 63 deletions(-) diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent-run.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent-run.adoc new file mode 100644 index 000000000000..ede01d282bf7 --- /dev/null +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent-run.adoc @@ -0,0 +1,43 @@ + +// AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE += camel test agent run + +Runs tests on the agent server + +NOTE: This command is provided by the `test` plugin. Install it with `camel plugin add test`. + + +== Usage + +[source,bash] +---- +camel test agent run [options] +---- + + + +== Options + +[cols="2,5,1,2",options="header"] +|=== +| Option | Description | Default | Type +| `--async` | Should the test engine print verbose test summary information. | | boolean +| `--background` | When enabled the command is not blocking for the test result response. | | boolean +| `--dep` | Set of additional Maven dependencies that should be loaded with the agent. | | String[] +| `--engine` | Name of the test engine that is used ti run tests. One of junit, junit-jupiter, junit4, testng, cucumber | | String +| `--includes` | Includes test name pattern. | | String[] +| `--modules` | Comma delimited list of additional Citrus modules that should be loaded with the agent. | | String +| `--packages` | Test package name to include in the test run. | | String[] +| `--polling-interval` | Interval used to poll for test results. Only used in asynchronous test execution mode. | 2000 | String +| `--port` | Server port to connect to. | | String +| `--property` | Default System property to set before the test run. | | String[] +| `--reset` | Should the test engine reset the suite state for each run. | true | String +| `--test-jar` | Path to a Java archive that holds tests to run. | | String +| `--timeout` | Http request timeout. | 60000 | String +| `--url` | Server endpoint URL to connect to. | | String +| `--verbose` | Should the test engine print verbose test summary information. | true | String +| `--work-directory` | The working directory used by the file based test engines to load file resources from. | | String +| `-h,--help` | Display the help and sub-commands | | boolean +|=== + + diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent-start.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent-start.adoc new file mode 100644 index 000000000000..6e3a9a3bb86a --- /dev/null +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent-start.adoc @@ -0,0 +1,44 @@ + +// AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE += camel test agent start + +Starts the Citrus agent as a server + +NOTE: This command is provided by the `test` plugin. Install it with `camel plugin add test`. + + +== Usage + +[source,bash] +---- +camel test agent start [options] +---- + + + +== Options + +[cols="2,5,1,2",options="header"] +|=== +| Option | Description | Default | Type +| `--config-class` | Configuration class name. | | String +| `--dep` | Set of additional Maven dependencies that should be loaded with the agent. | | String[] +| `--engine` | Name of the test engine that is used ti run tests. One of junit, junit-jupiter, junit4, testng, cucumber | | String +| `--includes` | Includes test name pattern. | | String[] +| `--inspect-code` | When enabled the source code gets analyzed for required modules and dependencies that are added to the classpath. | true | String +| `--modules` | Comma delimited list of additional Citrus modules that should be loaded with the agent. | | String +| `--offline` | When enabled there will be no attempts to resolve Maven artifacts via internet connection. | | String +| `--packages` | Test package name to include in the test run. | | String[] +| `--port` | Server port. | | String +| `--property` | Default System property to set before the test run. | | String[] +| `--reset` | Should the test engine reset the suite state for each run. | true | String +| `--skip-tests` | Should the server skip the test run at startup. | | String +| `--system-exit` | Should the server exit based on success or failure of the test run. | | String +| `--test-jar` | Path to a Java archive that holds tests to run. | | String +| `--time-to-live` | If this time is set the server automatically terminates after the given time. | | String +| `--verbose` | Should the test engine print verbose test summary information. | true | String +| `--work-directory` | The working directory used by the file based test engines to load file resources from. | | String +| `-h,--help` | Display the help and sub-commands | | boolean +|=== + + diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent-stop.adoc similarity index 60% copy from docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc copy to docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent-stop.adoc index 13a7c0844597..b0db61a3798b 100644 --- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent-stop.adoc @@ -1,8 +1,8 @@ // AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE -= camel test += camel test agent stop -Manage tests for Camel applications +Stop the Citrus agent service NOTE: This command is provided by the `test` plugin. Install it with `camel plugin add test`. @@ -11,19 +11,10 @@ NOTE: This command is provided by the `test` plugin. Install it with `camel plug [source,bash] ---- -camel test [options] +camel test agent stop [options] ---- -== Subcommands - -[cols="2,5",options="header"] -|=== -| Subcommand | Description -| xref:jbang-commands/camel-jbang-test-init.adoc[init] | Creates a new Citrus test -|=== - - == Options diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent.adoc similarity index 56% copy from docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc copy to docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent.adoc index 13a7c0844597..ffc5c5a0874e 100644 --- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-agent.adoc @@ -1,8 +1,8 @@ // AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE -= camel test += camel test agent -Manage tests for Camel applications +Manage Citrus agents NOTE: This command is provided by the `test` plugin. Install it with `camel plugin add test`. @@ -11,7 +11,7 @@ NOTE: This command is provided by the `test` plugin. Install it with `camel plug [source,bash] ---- -camel test [options] +camel test agent [options] ---- @@ -20,7 +20,9 @@ camel test [options] [cols="2,5",options="header"] |=== | Subcommand | Description -| xref:jbang-commands/camel-jbang-test-init.adoc[init] | Creates a new Citrus test +| xref:jbang-commands/camel-jbang-test-agent-run.adoc[run] | Runs tests on the agent server +| xref:jbang-commands/camel-jbang-test-agent-start.adoc[start] | Starts the Citrus agent as a server +| xref:jbang-commands/camel-jbang-test-agent-stop.adoc[stop] | Stop the Citrus agent service |=== diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-inspect.adoc similarity index 60% copy from docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc copy to docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-inspect.adoc index 13a7c0844597..68529de396be 100644 --- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-inspect.adoc @@ -1,8 +1,8 @@ // AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE -= camel test += camel test inspect -Manage tests for Camel applications +Inspect a Citrus test and its source code in order to provide detailed information such as used endpoints as well as required modules and dependencies. NOTE: This command is provided by the `test` plugin. Install it with `camel plugin add test`. @@ -11,19 +11,10 @@ NOTE: This command is provided by the `test` plugin. Install it with `camel plug [source,bash] ---- -camel test [options] +camel test inspect [options] ---- -== Subcommands - -[cols="2,5",options="header"] -|=== -| Subcommand | Description -| xref:jbang-commands/camel-jbang-test-init.adoc[init] | Creates a new Citrus test -|=== - - == Options diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-ps.adoc similarity index 60% copy from docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc copy to docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-ps.adoc index 13a7c0844597..3f62893c0cd3 100644 --- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-ps.adoc @@ -1,8 +1,8 @@ // AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE -= camel test += camel test ps -Manage tests for Camel applications +List running Citrus tests NOTE: This command is provided by the `test` plugin. Install it with `camel plugin add test`. @@ -11,25 +11,18 @@ NOTE: This command is provided by the `test` plugin. Install it with `camel plug [source,bash] ---- -camel test [options] +camel test ps [options] ---- -== Subcommands - -[cols="2,5",options="header"] -|=== -| Subcommand | Description -| xref:jbang-commands/camel-jbang-test-init.adoc[init] | Creates a new Citrus test -|=== - - == Options [cols="2,5,1,2",options="header"] |=== | Option | Description | Default | Type +| `--pid` | List only pid in the output | | boolean +| `--sort` | Sort by pid, name or age | pid | String | `-h,--help` | Display the help and sub-commands | | boolean |=== diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-run.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-run.adoc new file mode 100644 index 000000000000..9151e00295a3 --- /dev/null +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test-run.adoc @@ -0,0 +1,41 @@ + +// AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE += camel test run + +Run as local Citrus test + +NOTE: This command is provided by the `test` plugin. Install it with `camel plugin add test`. + + +== Usage + +[source,bash] +---- +camel test run [options] +---- + + + +== Options + +[cols="2,5,1,2",options="header"] +|=== +| Option | Description | Default | Type +| `--dep,--dependency` | Comma delimited list of additional Maven GAV dependencies that must be loaded to run the test. | | List +| `--engine` | Name of the test engine that is used to run tests. One of junit, junit-jupiter, junit4, testng, cucumber | | String +| `--includes` | Includes test name pattern. | | List +| `--inspect-code` | When enabled the source code gets analyzed for required modules and dependencies that are added to the classpath. | true | boolean +| `--logging` | Can be used to turn off logging | true | boolean +| `--logging-color` | Use colored logging | true | boolean +| `--logging-level` | Logging level | info | String +| `--modules` | Comma delimited list of additional Citrus modules that must be loaded to run the test. | | String +| `--offline` | When enabled there will be no attempts to resolve Maven artifacts via internet connection. | false | boolean +| `--property,--properties` | Default System property to set before the test run. | | List +| `--repository,--repositories` | Set of Maven repositories that should be used to resolve dependencies. | | List +| `--reset` | Should the test engine reset the suite state for this run. | true | String +| `--verbose` | Should the test engine print verbose test summary information. | true | String +| `--work-directory` | The working directory used by the file based test engines to load file resources from. | | String +| `-h,--help` | Display the help and sub-commands | | boolean +|=== + + diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc index 13a7c0844597..e7afbee9facd 100644 --- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-test.adoc @@ -20,7 +20,11 @@ camel test [options] [cols="2,5",options="header"] |=== | Subcommand | Description +| xref:jbang-commands/camel-jbang-test-agent.adoc[agent] | Manage Citrus agents | xref:jbang-commands/camel-jbang-test-init.adoc[init] | Creates a new Citrus test +| xref:jbang-commands/camel-jbang-test-inspect.adoc[inspect] | Inspect a Citrus test and its source code in order to provide detailed information such as used endpoints as well as required modules and dependencies. +| xref:jbang-commands/camel-jbang-test-ps.adoc[ps] | List running Citrus tests +| xref:jbang-commands/camel-jbang-test-run.adoc[run] | Run as local Citrus test |=== diff --git a/dsl/camel-jbang/camel-jbang-plugin-test/src/generated/resources/META-INF/camel-jbang-plugin-commands-metadata.json b/dsl/camel-jbang/camel-jbang-plugin-test/src/generated/resources/META-INF/camel-jbang-plugin-commands-metadata.json index 173749a94911..c48595f91fc2 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-test/src/generated/resources/META-INF/camel-jbang-plugin-commands-metadata.json +++ b/dsl/camel-jbang/camel-jbang-plugin-test/src/generated/resources/META-INF/camel-jbang-plugin-commands-metadata.json @@ -1,5 +1,5 @@ { "commands": [ - { "name": "test", "fullName": "test", "description": "Manage tests for Camel applications", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.test.TestCommand", "plugin": "test", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "init", "fullName": "test init", "description": "Creates a new Citrus test", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.test.T [...] + { "name": "test", "fullName": "test", "description": "Manage tests for Camel applications", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.test.TestCommand", "plugin": "test", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "agent", "fullName": "test agent", "description": "Manage Citrus agents", "sourceClass": "org.citrusframework.jbang.cli.commands.Agent", "pl [...] ] } diff --git a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojo.java b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojo.java index 9e46cd905846..27b6cc42ea3c 100644 --- a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojo.java +++ b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojo.java @@ -51,6 +51,7 @@ import org.jboss.forge.roaster.model.JavaType; import org.jboss.forge.roaster.model.source.AnnotationSource; import org.jboss.forge.roaster.model.source.FieldSource; import org.jboss.forge.roaster.model.source.Import; +import org.jboss.forge.roaster.model.source.Importer; import org.jboss.forge.roaster.model.source.JavaClassSource; import org.jboss.forge.roaster.model.source.JavaSource; @@ -200,6 +201,7 @@ public class PrepareCamelJBangCommandsMojo extends AbstractGeneratorMojo { throws IOException { List<String> lines = Files.readAllLines(mainFile.toPath()); + JavaSource<?> registering = parseSource(mainFile); // Find the start of the commandLine builder chain int startLine = -1; @@ -226,7 +228,7 @@ public class PrepareCamelJBangCommandsMojo extends AbstractGeneratorMojo { break; } - processSubcommandLine(lines.get(i), parentStack, commandsByClassName, model, null); + processSubcommandLine(lines.get(i), parentStack, commandsByClassName, model, null, registering); } } @@ -244,6 +246,7 @@ public class PrepareCamelJBangCommandsMojo extends AbstractGeneratorMojo { String plugin = nameMatcher.group(1); List<String> lines = Files.readAllLines(pluginFile.toPath()); + JavaSource<?> registering = parseSource(pluginFile); for (String line : lines) { Matcher matcher = PLUGIN_ROOT_PATTERN.matcher(line.trim()); if (!matcher.find()) { @@ -255,7 +258,7 @@ public class PrepareCamelJBangCommandsMojo extends AbstractGeneratorMojo { Matcher newMatcher = NEW_COMMAND_PATTERN.matcher(target); if (newMatcher.find()) { // a command without subcommands: commandLine.addSubcommand("name", new Command(main)) - CommandInfo info = commandsByClassName.get(newMatcher.group(1)); + CommandInfo info = commandInfo(newMatcher.group(1), commandsByClassName, registering); if (info != null && !info.hidden) { JBangCommand cmd = createCommand(info, cmdName, ""); cmd.setPlugin(plugin); @@ -273,7 +276,7 @@ public class PrepareCamelJBangCommandsMojo extends AbstractGeneratorMojo { if (!declMatcher.find()) { continue; } - CommandInfo info = commandsByClassName.get(declMatcher.group(1)); + CommandInfo info = commandInfo(declMatcher.group(1), commandsByClassName, registering); if (info == null || info.hidden) { break; } @@ -285,7 +288,7 @@ public class PrepareCamelJBangCommandsMojo extends AbstractGeneratorMojo { Stack<ParentInfo> parentStack = new Stack<>(); parentStack.push(new ParentInfo(root, -1)); for (int j = i + 1; j < lines.size(); j++) { - processSubcommandLine(lines.get(j), parentStack, commandsByClassName, model, plugin); + processSubcommandLine(lines.get(j), parentStack, commandsByClassName, model, plugin, registering); if (lines.get(j).trim().endsWith(";")) { break; } @@ -297,7 +300,7 @@ public class PrepareCamelJBangCommandsMojo extends AbstractGeneratorMojo { private void processSubcommandLine( String line, Stack<ParentInfo> parentStack, Map<String, CommandInfo> commandsByClassName, - JBangCommandModel model, String plugin) { + JBangCommandModel model, String plugin, JavaSource<?> registering) { String trimmedLine = line.trim(); Matcher matcher = SUBCOMMAND_PATTERN.matcher(trimmedLine); @@ -340,7 +343,7 @@ public class PrepareCamelJBangCommandsMojo extends AbstractGeneratorMojo { // - Ends with 3 parens ())) = leaf command (closes CommandLine + addSubcommand) boolean hasSubcommands = closingParens == 2; - CommandInfo info = commandsByClassName.get(className); + CommandInfo info = commandInfo(className, commandsByClassName, registering); boolean parentSkipped = !parentStack.isEmpty() && parentStack.peek().command == null; if (info == null || info.hidden || parentSkipped) { if (info == null) { @@ -375,6 +378,70 @@ public class PrepareCamelJBangCommandsMojo extends AbstractGeneratorMojo { } } + /** + * The source that registers the commands, for the imports of their classes, or null when it cannot be parsed. + */ + private JavaSource<?> parseSource(File file) { + try { + return Roaster.parse(file) instanceof JavaSource<?> source ? source : null; + } catch (Exception e) { + getLog().debug("Could not parse: " + file + " - " + e.getMessage()); + return null; + } + } + + /** + * The command of a class: from its source when it is one of the command classes, or else from its class on the + * classpath of the project when the source that registers it imports it by name, such as a command of a library + * that a plugin adds as it is (camel test run is the Run of Citrus). + */ + private CommandInfo commandInfo( + String className, Map<String, CommandInfo> commandsByClassName, JavaSource<?> registering) { + CommandInfo info = commandsByClassName.get(className); + if (info == null && registering != null) { + String imported = importedByName(registering, className); + if (imported != null) { + info = parseCommandFromClass(imported); + } + } + return info; + } + + /** + * Parses a command from the picocli annotations of its class on the classpath of the project, read by reflection, + * as picocli is not a dependency of this plugin. + */ + @SuppressWarnings("unchecked") + private CommandInfo parseCommandFromClass(String className) { + try { + ClassLoader loader = getProjectClassLoader(); + Class<?> clazz = loader.loadClass(className); + Annotation command + = clazz.getAnnotation((Class<? extends Annotation>) loader.loadClass("picocli.CommandLine$Command")); + if (command == null) { + return null; + } + CommandInfo info = new CommandInfo(); + info.className = clazz.getSimpleName(); + info.qualifiedName = clazz.getName(); + info.name = (String) attribute(command, "name"); + String description = String.join(" ", (String[]) attribute(command, "description")); + // without the hint to the help, as for a command of the sources + if (description.contains("(use")) { + description = description.substring(0, description.indexOf("(use")).trim(); + } + info.description = description.isEmpty() ? null : description; + info.hidden = (Boolean) attribute(command, "hidden"); + info.deprecated = clazz.isAnnotationPresent(Deprecated.class); + info.options = new ArrayList<>(); + parseOptionsFromClass(className, info.options); + return info; + } catch (Exception | LinkageError e) { + getLog().debug("Could not read the command of class: " + className + " - " + e.getMessage()); + return null; + } + } + private File findPluginFile(File dir) throws IOException { try (Stream<Path> paths = Files.walk(dir.toPath())) { return paths.filter(p -> p.toString().endsWith(".java")) @@ -559,10 +626,10 @@ public class PrepareCamelJBangCommandsMojo extends AbstractGeneratorMojo { } /** - * The qualified name of the type with the simple name that the class imports by name, or null when it does not. + * The qualified name of the type with the simple name that the source imports by name, or null when it does not. */ - private static String importedByName(JavaClassSource clazz, String simpleName) { - for (Import imported : clazz.getImports()) { + private static String importedByName(Importer<?> source, String simpleName) { + for (Import imported : source.getImports()) { if (!imported.isWildcard() && !imported.isStatic() && imported.getSimpleName().equals(simpleName)) { return imported.getQualifiedName(); } diff --git a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojoTest.java b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojoTest.java index 553683627b87..e6a31707b6c5 100644 --- a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojoTest.java +++ b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/PrepareCamelJBangCommandsMojoTest.java @@ -61,6 +61,17 @@ class PrepareCamelJBangCommandsMojoTest { } """; + // the command test of the plugin + private static final String TEST_COMMAND = """ + package org.apache.camel.dsl.jbang.core.commands.test; + + import picocli.CommandLine.Command; + + @Command(name = "test", description = "Manage tests") + public class TestCommand { + } + """; + @TempDir Path tempDir; @@ -270,6 +281,56 @@ class PrepareCamelJBangCommandsMojoTest { assertThat(init.getOptions()).extracting(JBangCommandOption::getNames).containsExactly("--directory", "-h,--help"); } + /** + * CAMEL-25718: the commands of a library that a plugin adds as they are (as camel test run is the Run of Citrus) + * are documented from their classes on the classpath of the project, with their subcommands. + */ + @Test + void testPluginCommandsOfALibraryAreDocumented() throws Exception { + compileLibrary(); + Path plugin = tempDir.resolve("plugin/commands"); + write(plugin.resolve("test/TestPlugin.java"), """ + package org.apache.camel.dsl.jbang.core.commands.test; + + import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain; + import org.apache.camel.dsl.jbang.core.common.CamelJBangPlugin; + import org.apache.camel.dsl.jbang.core.common.Plugin; + import org.example.cli.Agent; + import org.example.cli.AgentStart; + import org.example.cli.Run; + import picocli.CommandLine; + + @CamelJBangPlugin(name = "camel-jbang-plugin-test", firstVersion = "4.14.0") + public class TestPlugin implements Plugin { + + @Override + public void customize(CommandLine commandLine, CamelJBangMain main) { + var cmd = new CommandLine(new TestCommand(main)) + .addSubcommand("run", new CommandLine(new Run(main))) + .addSubcommand("agent", new CommandLine(new Agent(main)) + .addSubcommand("start", new CommandLine(new AgentStart(main)))); + + commandLine.addSubcommand("test", cmd); + } + } + """); + write(plugin.resolve("test/TestCommand.java"), TEST_COMMAND); + + JBangCommand test = generatePlugin(plugin, null); + + assertThat(test.getSubcommands()).extracting(JBangCommand::getFullName).containsExactly("test agent", "test run"); + JBangCommand agent = test.getSubcommands().get(0); + // without the hint to the help of the subcommands + assertThat(agent.getDescription()).isEqualTo("Manage agents"); + assertThat(agent.getSubcommands()).extracting(JBangCommand::getFullName).containsExactly("test agent start"); + assertThat(agent.getSubcommands().get(0).getOptions()).extracting(JBangCommandOption::getNames) + .containsExactly("--port", "-h,--help"); + JBangCommand run = test.getSubcommands().get(1); + assertThat(run.getDescription()).isEqualTo("Run as local test"); + assertThat(run.getSourceClass()).isEqualTo("org.example.cli.Run"); + assertThat(run.getOptions()).extracting(JBangCommandOption::getNames).containsExactly("--engine", "-h,--help"); + } + /** * A parent class of the same package is found when the class has a static import on demand, for which Roaster * resolves the parent class as a nested class of the imported one (as camel export, whose ExportBaseCommand has @@ -361,15 +422,7 @@ class PrepareCamelJBangCommandsMojoTest { } } """); - write(plugin.resolve("test/TestCommand.java"), """ - package org.apache.camel.dsl.jbang.core.commands.test; - - import picocli.CommandLine.Command; - - @Command(name = "test", description = "Manage tests") - public class TestCommand { - } - """); + write(plugin.resolve("test/TestCommand.java"), TEST_COMMAND); write(plugin.resolve("test/TestInit.java"), """ package org.apache.camel.dsl.jbang.core.commands.test; @@ -381,9 +434,19 @@ class PrepareCamelJBangCommandsMojoTest { } """); + JBangCommand test = generatePlugin(plugin, core); + assertThat(test.getSubcommands()).extracting(JBangCommand::getFullName).containsExactly("test init"); + return test.getSubcommands().get(0); + } + + /** + * Generates the metadata of the plugin with the commands in the given directory (and the commands of + * camel-jbang-core in the other one, when given). Returns the root command of the plugin. + */ + private JBangCommand generatePlugin(Path plugin, Path core) throws Exception { PrepareCamelJBangCommandsMojo mojo = new PrepareCamelJBangCommandsMojo(null, null); mojo.commandsDir = plugin.toFile(); - mojo.coreCommandsDir = core.toFile(); + mojo.coreCommandsDir = core != null ? core.toFile() : null; mojo.outFolder = tempDir.resolve("generated-resources").toFile(); mojo.project = projectWithOutputDir(tempDir.resolve("classes").toFile()); @@ -391,9 +454,7 @@ class PrepareCamelJBangCommandsMojoTest { String json = Files.readString( tempDir.resolve("generated-resources").resolve(PrepareCamelJBangCommandsMojo.PLUGIN_METADATA_FILE)); - JBangCommand test = JsonMapper.generateJBangCommandModel(json).getCommands().get(0); - assertThat(test.getSubcommands()).extracting(JBangCommand::getFullName).containsExactly("test init"); - return test.getSubcommands().get(0); + return JsonMapper.generateJBangCommandModel(json).getCommands().get(0); } /** @@ -432,6 +493,16 @@ class PrepareCamelJBangCommandsMojoTest { @Target(ElementType.FIELD) public @interface Mixin { } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface Command { + String name() default "<main class>"; + + String[] description() default {}; + + boolean hidden() default false; + } } """); write(sources.resolve("org/example/cli/LibraryCommand.java"), """ @@ -457,6 +528,39 @@ class PrepareCamelJBangCommandsMojoTest { String directory; } """); + write(sources.resolve("org/example/cli/Run.java"), """ + package org.example.cli; + + import picocli.CommandLine; + + @CommandLine.Command(name = "run", description = "Run as local test") + public class Run extends LibraryCommand { + + @CommandLine.Option(names = "--engine", description = "The test engine") + String engine; + } + """); + write(sources.resolve("org/example/cli/Agent.java"), """ + package org.example.cli; + + import picocli.CommandLine; + + @CommandLine.Command(name = "agent", description = "Manage agents (use agent --help to see sub commands)") + public class Agent extends LibraryCommand { + } + """); + write(sources.resolve("org/example/cli/AgentStart.java"), """ + package org.example.cli; + + import picocli.CommandLine; + + @CommandLine.Command(name = "start", description = "Starts the agent as a server") + public class AgentStart extends LibraryCommand { + + @CommandLine.Option(names = "--port", description = "The port of the agent", defaultValue = "4567") + int port; + } + """); Path classes = tempDir.resolve("classes"); Files.createDirectories(classes); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
