diff --git a/apl-db-updater/pom.xml b/apl-db-updater/pom.xml index 0f9449eefa..5207639d14 100644 --- a/apl-db-updater/pom.xml +++ b/apl-db-updater/pom.xml @@ -11,6 +11,18 @@ apl-db-updater + + + + com.apollocurrency + apl-bom-ext + 2.0.1 + pom + import + + + + diff --git a/apl-dex/pom.xml b/apl-dex/pom.xml index 4e983dae00..83ce19d1c1 100644 --- a/apl-dex/pom.xml +++ b/apl-dex/pom.xml @@ -150,4 +150,12 @@ + + + + maven-surefire-plugin + + + + \ No newline at end of file diff --git a/apl-exec/pom.xml b/apl-exec/pom.xml index e332ddfe76..73fbb9dde0 100644 --- a/apl-exec/pom.xml +++ b/apl-exec/pom.xml @@ -46,6 +46,16 @@ com.beust jcommander + + info.picocli + picocli + + + org.projectlombok + lombok + provided + + org.junit.platform @@ -82,6 +92,11 @@ mockito-junit-jupiter test + + org.junit.jupiter + junit-jupiter-params + test + diff --git a/apl-exec/src/main/java/com/apollocurrency/aplwallet/apl/exec/Apollo.java b/apl-exec/src/main/java/com/apollocurrency/aplwallet/apl/exec/Apollo.java index 9645f1cfec..4fda55b534 100644 --- a/apl-exec/src/main/java/com/apollocurrency/aplwallet/apl/exec/Apollo.java +++ b/apl-exec/src/main/java/com/apollocurrency/aplwallet/apl/exec/Apollo.java @@ -33,7 +33,6 @@ import com.apollocurrency.aplwallet.apl.util.env.dirprovider.DirProviderFactory; import com.apollocurrency.aplwallet.apl.util.env.dirprovider.PredefinedDirLocations; import com.apollocurrency.aplwallet.apl.util.injectable.PropertiesHolder; -import com.beust.jcommander.JCommander; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +48,8 @@ import java.util.Properties; import java.util.UUID; +import picocli.CommandLine; + /** * Main Apollo startup class * @@ -178,26 +179,23 @@ public static void main(String[] argv) { //parse command line first CmdLineArgs args = new CmdLineArgs(); - JCommander jc = JCommander.newBuilder() - .addObject(args) - .build(); - jc.setProgramName(Constants.APPLICATION); + CommandLine pc = new CommandLine(args); try { - jc.parse(argv); + CommandLine.ParseResult parseResult = pc.parseArgs(argv); + if (args.help) { + pc.usage(System.err); + System.exit(PosixExitCodes.OK.exitCode()); + } } catch (RuntimeException ex) { System.err.println("Error parsing command line arguments."); System.err.println(ex.getMessage()); - jc.usage(); + pc.usage(System.err); System.exit(PosixExitCodes.EX_USAGE.exitCode()); } if (args.getNetIdx() >= 0 && !args.chainId.isEmpty()) { System.err.println("--chainId, --testnet and --net parameters are incompatible, please specify only one"); System.exit(PosixExitCodes.EX_USAGE.exitCode()); } - if (args.help) { - jc.usage(); - System.exit(PosixExitCodes.OK.exitCode()); - } //set main application class to runtime diff --git a/apl-exec/src/main/java/com/apollocurrency/aplwallet/apl/exec/CmdLineArgs.java b/apl-exec/src/main/java/com/apollocurrency/aplwallet/apl/exec/CmdLineArgs.java index abf14d5dde..a969dbd2e5 100644 --- a/apl-exec/src/main/java/com/apollocurrency/aplwallet/apl/exec/CmdLineArgs.java +++ b/apl-exec/src/main/java/com/apollocurrency/aplwallet/apl/exec/CmdLineArgs.java @@ -1,64 +1,76 @@ -package com.apollocurrency.aplwallet.apl.exec; - -import com.beust.jcommander.Parameter; - -/** +/* + * Copyright © 2018-2022 Apollo Foundation * Command line parameters * * @author alukin@gmail.com + * @author tx_hv@ukr.net */ + +package com.apollocurrency.aplwallet.apl.exec; + +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +@Command(name = "apollo-blockchain") public class CmdLineArgs { public static int DEFAULT_DEBUG_LEVEL = 2; - @Parameter(names = {"--debug", "-d"}, description = "Debug level [0-4] from ERROR to TRACE") + @Option(names = {"--debug", "-d"}, description = "Common debug level [0-4] from ERROR=0, WARN=1, INFO=2, DEBUG=3, TRACE=4") public int debug = DEFAULT_DEBUG_LEVEL; - @Parameter(names = {"--debug-updater", "-du"}, description = "Force updater to use debug certificates for verifying update transactions") + @Option(names = {"--debug-updater", "-du"}, description = "Force updater to use debug certificates for verifying update transactions") public boolean debugUpdater; - @Parameter(names = {"--help", "-h"}, help = true, description = "Print help message") + @Option(names = {"--help", "-h"}, usageHelp = true, help = true, description = "Print help message") public boolean help; - @Parameter(names = {"--service-mode", "-s"}, help = true, description = "Run in service mode with current system user") + @Option(names = {"--service-mode", "-s"}, arity = "0..1", defaultValue = "false", fallbackValue = "true", description = "Run in service mode with current system user") public boolean serviceMode; - @Parameter(names = {"--ignore-resources"}, description = "Ignore resources bundled with application jar. Default is false") - public boolean ingnoreResources = false; - @Parameter(names = {"--config-dir", "-c"}, description = "Load all configuration and resources from specified path. System resources not ignored, standard config search is ignored.") + @Option(names = {"--ignore-resources"}, description = "Ignore resources bundled with application jar. Default is false") + public boolean ignoreResources = false; + @Option(names = {"--config-dir", "-c"}, description = "Load all configuration and resources from specified path. System resources not ignored, standard config search is ignored.") public String configDir = ""; - @Parameter(names = {"--log-dir", "-l"}, description = "Save log files to from specified directory.") + @Option(names = {"--log-dir", "-l"}, description = "Save log files to from specified directory.") public String logDir = ""; - @Parameter(names = {"--db-dir"}, description = "Load/Save DB files to from specified directory. Ignored if DB URL is remote.") + @Option(names = {"--db-dir"}, description = "Load/Save DB files to from specified directory. Ignored if DB URL is remote.") public String dbDir = ""; - @Parameter(names = {"--vault-key-dir"}, description = "Load/Save vault wallets keys to/form specified keystore directory.") + @Option(names = {"--vault-key-dir"}, description = "Load/Save vault wallets keys to/form specified keystore directory.") public String vaultKeystoreDir = ""; - @Parameter(names = {"--dex-key-dir"}, description = "Load/Save dex keys to/form specified keystore directory.") + @Option(names = {"--dex-key-dir"}, description = "Load/Save dex keys to/form specified keystore directory.") public String dexKeystoreDir = ""; - @Parameter(names = {"--no-shard-import"}, description = "Start from Genesis block, do not try to import last shard") + @Option(names = {"--no-shard-import"}, arity = "0..1", defaultValue = "false", fallbackValue = "true", + description = "Empty node starts downloading blocks from Genesis block, does not try to import last shard downloaded from another sharded nodes." + + " Default: ${DEFAULT-VALUE}, if specified without parameter value is: ${FALLBACK-VALUE}." + ) public boolean noShardImport = false; - @Parameter(names = {"--no-shard-create"}, description = "Do not create shards even if it configured to do so. Shards require much more resources") + @Option(names = {"--no-shard-create"}, arity = "0..1", defaultValue = "false", fallbackValue = "true", + description = "Do not create shards even if it configured to do so, default: ${DEFAULT-VALUE}, " + + "if specified without parameter: ${FALLBACK-VALUE}. Shards are require more OS resources." + ) public boolean noShardCreate = false; - @Parameter(names = {"--update-attachment-file", "-u"}, description = "Full path to file which represent json of UpdateAttachment for local updates debug") + @Option(names = {"--update-attachment-file", "-u"}, + description = "Full path to file which represent json of UpdateAttachment for local updates with debugging purpose.") public String updateAttachmentFile = ""; // TODO cleanup apl-default.properties - @Parameter(names = {"--2fa-dir"}, description = "Load/Save 2FA keys to/form specified directory. Note that this parameter will not work when you do not set apl.store2FAInFileSystem=true in apl-default.properties") + @Option(names = {"--2fa-dir"}, description = "Load/Save 2FA keys to/form specified directory. Note that this Option will not work when you do not set apl.store2FAInFileSystem=true in apl-default.properties") public String twoFactorAuthDir = ""; - @Parameter(names = {"--dexp-dir"}, description = "Export/Import CSV data to/form specified directory.") + @Option(names = {"--dexp-dir"}, description = "Export/Import CSV data to/form specified directory.") public String dataExportDir = ""; - @Parameter(names = {"--pid-file"}, description = "Save PID to specified file.") + @Option(names = {"--pid-file"}, description = "Save PID to specified file.") public String pidFile = ""; - @Parameter(names = {"--net", "-n"}, help = true, description = "Connect to net [0-4]. 0 means mainnet, 1 - 1st testnet and so on") + @Option(names = {"--net", "-n"}, help = true, description = "Connect to net [0-4]. 0 means mainnet, 1 - 1st testnet and so on") public int netIdx = -1; - @Parameter(names = {"--chain", "-C"}, help = true, description = "Connect to net with given chainID. UUID of chain id may be specified partially, 6 symbos min. Configs must be present.") + @Option(names = {"--chain", "-C"}, help = true, description = "Connect to net with given chainID. UUID of chain id may be specified partially, 6 symbols min. Config file must be present.") public String chainId = ""; - @Parameter(names = {"--testnet"}, help = true, description = "Connect to testent 1. Has higher priority then --net") + @Option(names = {"--testnet"}, help = true, description = "Connect to testent 1. Has higher priority then --net") public boolean isTestnet = false; //--- - @Parameter(names = {"--disable-weld-concurrent-deployment"}, - description = "If use it, Weld doesn't use ConcurrentDeployer and ConcurrentValidator to build the container. Default value is true.") + @Option(names = {"--disable-weld-concurrent-deployment"}, defaultValue = "false", + description = "If use it, Weld doesn't use ConcurrentDeployer and ConcurrentValidator to build the container. Default value is ${DEFAULT-VALUE}.") public boolean disableWeldConcurrentDeployment = false; public boolean isResourceIgnored() { - return ingnoreResources; + return ignoreResources; } public int getNetIdx() { diff --git a/apl-exec/src/test/java/com/apollocurrency/aplwallet/apl/exec/CmdLineArgsTest.java b/apl-exec/src/test/java/com/apollocurrency/aplwallet/apl/exec/CmdLineArgsTest.java new file mode 100644 index 0000000000..7cd6a6ff02 --- /dev/null +++ b/apl-exec/src/test/java/com/apollocurrency/aplwallet/apl/exec/CmdLineArgsTest.java @@ -0,0 +1,200 @@ +package com.apollocurrency.aplwallet.apl.exec; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import picocli.CommandLine; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.*; + +@Slf4j +class CmdLineArgsTest { + + String[] allArgStringArray = { + "--debug", "4", + "--debug-updater", "true", + "--help", "true", + "--service-mode", "true", + "--ignore-resources", "true", + "--config-dir", "/tmp", + "--log-dir", "/tmp", + "--db-dir", "/tmp", + "--vault-key-dir", "/tmp", + "--dex-key-dir", "/tmp", + "--no-shard-import", "true", + "--no-shard-create", "true", + "--update-attachment-file", "attach-file.bin", + "--2fa-dir", "/tmp", + "--dexp-dir", "/tmp", + "--pid-file", "/tmp/1.pid", + "--net", "3", + "--chain", "chains-1.json", + "--testnet", "false", + "--disable-weld-concurrent-deployment", "false" + }; + + String[] allArgShortArray = { + "-d", "1", + "-du", "true", + "-h", "true", + "-s", "true", + "--ignore-resources", "true", + "-c", "/tmp", + "-l", "/tmp", + "--db-dir", "/tmp", + "--vault-key-dir", "/tmp", + "--dex-key-dir", "/tmp", + "--no-shard-import", "false", + "--no-shard-create", "false", + "-u", "attach-file.bin", + "--2fa-dir", "/tmp", + "--dexp-dir", "/tmp", + "--pid-file", "/tmp/1.pid", + "-n", "3", + "-C", "chains-1.json", + "--testnet", "true", + "--disable-weld-concurrent-deployment", "false" + }; + + @Test + void testAllArgs_longStringValues() { + CmdLineArgs lineArgs = new CmdLineArgs(); + CommandLine.ParseResult result = new CommandLine(lineArgs).parseArgs(allArgStringArray); + log.debug("args = {}, result = {}", allArgStringArray, result); + assertEquals(0, result.errors().size()); + } + + @Test + void testAllArgs_emptyValues() { + String[] emptyArgs = {}; + CommandLine.ParseResult result = new CommandLine(new CmdLineArgs()).parseArgs(emptyArgs); + log.debug("args = {}, result = {}", allArgStringArray, result); + assertEquals(0, result.errors().size()); + } + + @Test + void testAllArgs_shortStringValues() { + CommandLine.ParseResult result = new CommandLine(new CmdLineArgs()).parseArgs(allArgShortArray); + log.debug("args = {}, result = {}", allArgStringArray, result); + assertEquals(0, result.errors().size()); + } + + @Test + void testAllArgs_unknownStringValues() { + String[] unknownArgsArray = {"--unknown-param", "-UP"}; + Exception error = assertThrows(CommandLine.UnmatchedArgumentException.class, + () -> new CommandLine(new CmdLineArgs()).parseArgs(unknownArgsArray) + ); + log.debug("args = {}, error = {}", Arrays.toString(unknownArgsArray), error.getMessage()); + assertEquals("Unknown options: " + + Arrays.stream(unknownArgsArray).map(String::valueOf).reduce((a, b) -> "'" + a + "', '" + b).get() + "'", + error.getMessage()); + } + + @ParameterizedTest + @CsvSource(value = {"--debug-updater", "--help", "--service-mode", "--ignore-resources", "--no-shard-import", + "--no-shard-create", "--testnet", "--disable-weld-concurrent-deployment", + "-du", "-h", "-s" + }) + void test_emptyBooleansEvaluatedToTrue(String paramName) { + CommandLine.ParseResult result = new CommandLine(new CmdLineArgs()).parseArgs(paramName); + log.debug("Parameter = {}, result = {}", paramName, result.errors()); + assertEquals(0, result.errors().size()); + assertTrue((Boolean) result.matchedArgs().get(0).typedValues().get(0)); + } + + @ParameterizedTest + @CsvSource(value = {"--debug-updater=false,false", "--help=false,false", "--service-mode=false,false", "--ignore-resources=false,false", + "--no-shard-import=false,false", "--no-shard-create=false,false", "--testnet=false,false", "--disable-weld-concurrent-deployment=false,false", + "-du,true", "-h,true", "-s,true" + }) + void test_specifiedFalseBooleans(String paramName, String value) { + CommandLine.ParseResult result = new CommandLine(new CmdLineArgs()).parseArgs(paramName); + log.debug("Parameter = {}, result = {}", paramName, result.errors()); + assertEquals(0, result.errors().size()); + assertEquals(Boolean.valueOf(value), result.matchedArgs().get(0).typedValues().get(0)); + } + + @ParameterizedTest + @CsvSource(value = {"--debug-updater=true,true", "--help,true", "--service-mode=true,true", "--ignore-resources=true,true", + "--no-shard-import=true,true", "--no-shard-create=true,true", "--testnet=true,true", "--disable-weld-concurrent-deployment=true,true", + "-du,true", "-h,true", "-s,true" + }) + void test_specifiedTrueBooleans(String paramName, String value) { + CommandLine.ParseResult result = new CommandLine(new CmdLineArgs()).parseArgs(paramName); + log.debug("Parameter = {}, result = {}", paramName, result.errors()); + assertEquals(0, result.errors().size()); + assertEquals(Boolean.valueOf(value), result.matchedArgs().get(0).typedValues().get(0)); + } + + @ParameterizedTest + @CsvSource(value = {"--debug-updater,true", "--help,true", "--service-mode,true", "--ignore-resources,true", + "--no-shard-import,true", "--no-shard-create,true", "--testnet,true", "--disable-weld-concurrent-deployment,true", + "-du,true", "-h,true", "-s,true" + }) + void test_specifiedDefaults(String paramName, String value) { + CommandLine.ParseResult result = new CommandLine(new CmdLineArgs()).parseArgs(paramName); + log.debug("Parameter = {}, result = {}", paramName, result.errors()); + assertEquals(0, result.errors().size()); + assertEquals(Boolean.valueOf(value), result.matchedArgs().get(0).typedValues().get(0)); + } + + @ParameterizedTest + @CsvSource(value = { + "--config-dir=/tmp,/tmp", + "--log-dir=/tmp,/tmp", + "--db-dir=/tmp,/tmp", + "--vault-key-dir=/tmp,/tmp", + "--dex-key-dir=/tmp,/tmp", + "--update-attachment-file=attach-file.bin,attach-file.bin", + "--2fa-dir=/tmp,/tmp", + "--dexp-dir=/tmp,/tmp", + "--pid-file=/tmp/1.pid,/tmp/1.pid", + "--chain=chains-1.json,chains-1.json", + }) + void test_longStringParamsSpecified(String paramName, String value) { + CommandLine.ParseResult result = new CommandLine(new CmdLineArgs()).parseArgs(paramName); + log.debug("Parameter = {}, result = {}", paramName, result.errors()); + assertEquals(0, result.errors().size()); + assertEquals(value, result.matchedArgs().get(0).typedValues().get(0)); + } + + @ParameterizedTest + @CsvSource(value = { + "-c /tmp,/tmp", + "-l /tmp,/tmp", + "-u attach-file.bin,attach-file.bin", + "-C chains-1.json,chains-1.json" + }) + void test_shortStringParamsSpecified(String paramName, String value) { + CommandLine.ParseResult result = new CommandLine(new CmdLineArgs()).parseArgs(paramName); + log.debug("Parameter = {}, result = {}", paramName, result.errors()); + assertEquals(0, result.errors().size()); + assertEquals(value, result.matchedArgs().get(0).typedValues().get(0).toString().trim()); + } + + @ParameterizedTest + @CsvSource(value = { + "--config-dir", + "--log-dir", + "--db-dir", + "--vault-key-dir", + "--dex-key-dir", + "--update-attachment-file", + "--2fa-dir", + "--dexp-dir", + "--pid-file", + "--chain", + }) + void test_errorWhenStringParamsIsNotSpecified(String paramName) { + Exception error = assertThrows(CommandLine.MissingParameterException.class, + () -> new CommandLine(new CmdLineArgs()).parseArgs(paramName) + ); + log.debug("Parameter = {}, error = {}", paramName, error.getMessage()); + assertTrue(error.getMessage().startsWith("Missing required parameter for option '" + paramName + "'")); + } + +} \ No newline at end of file diff --git a/apl-smc/pom.xml b/apl-smc/pom.xml index 6b1ca63caa..e20bcc6ad1 100644 --- a/apl-smc/pom.xml +++ b/apl-smc/pom.xml @@ -140,4 +140,12 @@ + + + + maven-surefire-plugin + + + + \ No newline at end of file diff --git a/apl-updater/pom.xml b/apl-updater/pom.xml index 01a7a7ac8a..316d0b0c29 100644 --- a/apl-updater/pom.xml +++ b/apl-updater/pom.xml @@ -105,7 +105,7 @@ maven-surefire-plugin - + diff --git a/apl-vault-wallet/pom.xml b/apl-vault-wallet/pom.xml index 8fdbdd6dbb..c76426fae8 100644 --- a/apl-vault-wallet/pom.xml +++ b/apl-vault-wallet/pom.xml @@ -144,8 +144,14 @@ janino test - - + + + + maven-surefire-plugin + + + + \ No newline at end of file