diff --git a/tcases-cli/src/main/java/org/cornutum/tcases/HelpException.java b/tcases-cli/src/main/java/org/cornutum/tcases/HelpException.java new file mode 100644 index 000000000..b923898c3 --- /dev/null +++ b/tcases-cli/src/main/java/org/cornutum/tcases/HelpException.java @@ -0,0 +1,16 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2020, Cornutum Project +// www.cornutum.org +// +////////////////////////////////////////////////////////////////////////////// + +package org.cornutum.tcases; + +/** + * Reports a command terminated by a request for help information. + */ +public class HelpException extends RuntimeException + { + private static final long serialVersionUID = 2254436449196262169L; + } diff --git a/tcases-cli/src/main/java/org/cornutum/tcases/ReducerCommand.java b/tcases-cli/src/main/java/org/cornutum/tcases/ReducerCommand.java index 6dab40e35..9d3c0d536 100644 --- a/tcases-cli/src/main/java/org/cornutum/tcases/ReducerCommand.java +++ b/tcases-cli/src/main/java/org/cornutum/tcases/ReducerCommand.java @@ -212,12 +212,17 @@ protected int handleOption( String[] args, int i) { String arg = args[i]; - if( arg.equals( "-f")) + if( arg.equals( "-help")) + { + throwHelpException(); + } + + else if( arg.equals( "-f")) { i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setFunction( args[i]); } @@ -227,7 +232,7 @@ else if( arg.equals( "-g")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setGenDef( new File( args[i])); } @@ -237,7 +242,7 @@ else if( arg.equals( "-r")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -259,7 +264,7 @@ else if( arg.equals( "-s")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -281,7 +286,7 @@ else if( arg.equals( "-t")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setTestDef( new File( args[i])); } @@ -291,7 +296,7 @@ else if( arg.equals( "-T")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -305,7 +310,7 @@ else if( arg.equals( "-T")) else { - throwUsageException(); + throwUsageException( String.format( "Unknown option: %s", arg)); } return i + 1; @@ -319,22 +324,22 @@ protected void handleArgs( String[] args, int i) int nargs = args.length - i; if( nargs != 1) { - throwUsageException(); + throwUsageException( String.format( "Unexpected argument: %s", args[i+1])); } setInputDef( new File( args[i])); } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a missing option value */ - protected void throwUsageException() + protected void throwMissingValue( String option) { - throwUsageException( null, null); + throwUsageException( String.format( "No value given for %s option", option)); } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a command line error. */ protected void throwUsageException( String detail) { @@ -342,28 +347,78 @@ protected void throwUsageException( String detail) } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a command line error. */ protected void throwUsageException( String detail, Exception cause) { - if( detail != null) - { - cause = new RuntimeException( detail, cause); - } - throw - new RuntimeException - ( "Usage: " - + ReducerCommand.class.getSimpleName() - + " [-f function]" - + " [-g genDef]" - + " [-r resampleFactor]" - + " [-R]" - + " [-s sampleCount]" - + " [-t testDef]" - + " [-T contentType]" - + " inputDef", - cause); + new IllegalArgumentException + ( "Invalid command line argument. For all command line details, use the -help option.", + new IllegalArgumentException( detail, cause)); + } + + /** + * Throws a HelpException after printing usage information to standard error. + */ + protected void throwHelpException() + { + printUsage(); + throw new HelpException(); + } + + /** + * Prints usage information to standard error. + */ + protected void printUsage() + { + for( String line : + new String[] { + "Usage: tcases-reducer [option...] inputDef", + "", + "For a system input definition, updates the associated test case generators to reduce the number", + "of generated test cases, using the given command line options.", + "", + "Each option is one of the following:", + "", + " -f function If -f is defined, update only the test case generator for the given function.", + " Otherwise, update the test case generators for all functions.", + "", + " -g genDef If -g is defined, update the generator specified in the given genDef file.", + " Otherwise, update the default generate definition file: the corresponding", + " *-Generators.xml file in the same directory as the inputDef.", + "", + " -l logFile If -l is defined, log output is written to the given file. If omitted,", + " log output is written to a file named tcases-reducer.log in the current working", + " directory. If logFile is 'stdout', log output is written to standard output.", + "", + " -L logLevel Defines the level for Tcases log output. If omitted, the default level is INFO", + " Tcases logging uses the configuration and levels defined by the Logback system.", + "", + " -r resampleFactor If -r is defined, use the given resampleFactor to determine the number", + " of samples in the next round of reducing. Depending on the resampleFactor,", + " the next round may use more or fewer samples. If the previous round called", + " for N samples and produced a reduction, then the number of samples for the ", + " next round will be N * ( 1 + resampleFactor). To increase sample count with", + " each round, define resampleFactor > 0. To decrease sample count with each round,", + " define -1 < resampleFactor < 0. If resampleFactor is omitted, the default value is 0.", + "", + " -R If defined, ignore any random seed defined in the genDef file and search for a new seed.", + "", + " -s sampleCount Defines the number of samples for the initial round of reducing. If omitted,", + " the default sampleCount is 10.", + "", + " -t testDef If -t is defined, generate test cases based on the test definitions in the", + " specified testDef file, relative to the directory containing the inputDef.", + "", + " -T contentType Defines the default content type for the files read and produced.", + " The contentType must be one of 'json' or 'xml'. The default content type is", + " assumed for any file that is not specified explicitly or that does not have a", + " recognized extension. If omitted, the default content type is derived from the", + " inputDef name." + }) + { + System.err.println( line); + } } /** @@ -656,6 +711,10 @@ public static void main( String[] args) ReducerCommand reducer = new ReducerCommand(); reducer.run( new Options( args)); } + catch( HelpException h) + { + exitCode = 1; + } catch( Exception e) { exitCode = 1; diff --git a/tcases-cli/src/main/java/org/cornutum/tcases/TcasesCommand.java b/tcases-cli/src/main/java/org/cornutum/tcases/TcasesCommand.java index cdaf564f2..8475b6b2c 100644 --- a/tcases-cli/src/main/java/org/cornutum/tcases/TcasesCommand.java +++ b/tcases-cli/src/main/java/org/cornutum/tcases/TcasesCommand.java @@ -354,12 +354,17 @@ protected int handleOption( String[] args, int i) { String arg = args[i]; - if( arg.equals( "-o")) + if( arg.equals( "-help")) + { + throwHelpException(); + } + + else if( arg.equals( "-o")) { i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setOutDir( new File( args[i])); } @@ -369,7 +374,7 @@ else if( arg.equals( "-f")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setOutFile( new File( args[i])); } @@ -379,7 +384,7 @@ else if( arg.equals( "-t")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setTestDef( new File( args[i])); } @@ -389,7 +394,7 @@ else if( arg.equals( "-T")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -411,7 +416,7 @@ else if( arg.equals( "-g")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setGenDef( new File( args[i])); } @@ -421,7 +426,7 @@ else if( arg.equals( "-r")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -443,7 +448,7 @@ else if( arg.equals( "-c")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -489,7 +494,7 @@ else if( arg.equals( "-x")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setTransformDef( new File( args[i])); } @@ -499,7 +504,7 @@ else if( arg.equals( "-p")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } String binding = args[i]; int valuePos = binding.indexOf( '='); @@ -518,7 +523,7 @@ else if( arg.equals( "-p")) else { - throwUsageException(); + throwUsageException( String.format( "Unknown option: %s", arg)); } return i + 1; @@ -533,7 +538,7 @@ protected void handleArgs( String[] args, int i) if( nargs > 1) { - throwUsageException(); + throwUsageException( String.format( "Unexpected argument: %s", args[i+1])); } if( nargs > 0) @@ -543,15 +548,15 @@ protected void handleArgs( String[] args, int i) } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a missing option value */ - protected void throwUsageException() + protected void throwMissingValue( String option) { - throwUsageException( null, null); + throwUsageException( String.format( "No value given for %s option", option)); } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a command line error. */ protected void throwUsageException( String detail) { @@ -559,32 +564,114 @@ protected void throwUsageException( String detail) } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a command line error. */ protected void throwUsageException( String detail, Exception cause) { - if( detail != null) - { - cause = new RuntimeException( detail, cause); - } - throw new IllegalArgumentException - ( "Usage: " - + Tcases.class.getSimpleName() - + " [-v]" - + " [-c tupleSize]" - + " [-f outFile]" - + " [-g genDef]" - + " [-n]" - + " [-o outDir]" - + " [-p name=value]" - + " [-r seed] [-R]" - + " [-t testDef]" - + " [-T contentType]" - + " [-x transformDef | -J | -H]" - + " [inputDef]", - cause); + ( "Invalid command line argument. For all command line details, use the -help option.", + new IllegalArgumentException( detail, cause)); + } + + /** + * Throws a HelpException after printing usage information to standard error. + */ + protected void throwHelpException() + { + printUsage(); + throw new HelpException(); + } + + /** + * Prints usage information to standard error. + */ + protected void printUsage() + { + for( String line : + new String[] { + "Usage: tcases [option...] [inputDef]", + "", + "Generates a set of test cases from a system input definition, according to the given options.", + "", + "The system input definition is read from the given inputDef. If omitted, the system input", + "definition is read from standard input. Otherwise, the system input definition is read from", + "the first one of the following files that can be located.", + "", + " 1. inputDef", + " 2. inputDef-Input.xml", + " 3. inputDef.xml", + "", + "Each option is one of the following:", + "", + " -c tuples If -c is defined, use the given default tuple size for all generators.", + " This updates the generator definitions specified by the genDef file.", + "", + " -f outFile If -f is defined, test definition output is written to the specified", + " outFile, relative to the given outDir. If omitted, test definitions are", + " written to the file specified by the -t option. If an output path cannot", + " be derived, output is written to standard output.", + "", + " -g genDef If -g is defined, test definitions are created using the generator(s)", + " specified by the given genDef file. If omitted, the default generator", + " definition is used. The default generator definition is read from the", + " corresponding *-Generators.xml file in the same directory as the inputDef,", + " if it exists. Otherwise, the default TupleGenerator is used for all", + " functions.", + "", + " -J If -J is defined, test definition output is transformed into Java source", + " code for a JUnit test class. The resulting Java source file is written to", + " the specified outDir.", + "", + " -H If -H is defined, test definition output is transformed into an HTML report", + " that is written to the specified outDir.", + "", + " -l logFile If -l is defined, log output is written to the given file. If omitted,", + " log output is written to a file named tcases.log in the current working", + " directory. If logFile is 'stdout', log output is written to standard output.", + "", + " -L logLevel Defines the level for Tcases log output. If omitted, the default level is INFO", + " Tcases logging uses the configuration and levels defined by the Logback system.", + "", + " -n If -n is defined, any previous contents of the testDef are ignored.", + " If omitted, new test definitions are based on the previous testDef.", + "", + " -o outDir If -o is defined, test definition output is written to the specified", + " directory. If omitted, the default outDir is the directory containing", + " the inputDef or, if reading from standard input, the current working", + " directory. If an output path cannot be derived, output is written to", + " standard output.", + "", + " -p name=value Defines the value of a transform parameter. Any number of -p options", + " may be specified. This option is meaningful only if the -x or -J option is given.", + "", + " -r seed If -r is defined, use the given random number seed for all generators.", + " This updates the generator definitions specified by the genDef file.", + "", + " -R If -R is defined, choose a new random number seed for all generators.", + " This updates the generator definitions specified by the genDef file.", + "", + " -t testDef If -t is defined, test definition output is written to the specified", + " testDef path, relative to the outDir. If omitted, the default testDef", + " name is derived from the inputDef name. If an output path cannot be", + " derived, output is written to standard output.", + "", + " -T contentType Defines the default content type for the files read and produced.", + " The contentType must be one of 'json' or 'xml'. The default content type is", + " assumed for any file that is not specified explicitly or that does not have a", + " recognized extension. If omitted, the default content type is derived from the", + " inputDef name.", + "", + " -v Shows the current Tcases version. If this option is given, no other", + " action is performed.", + "", + " -x xsltDef If -x is defined, test definition output is transformed according to the", + " XSLT transform defined by the xsltDef file. If relative, the xsltDef path is", + " assumed to be relative to the directory containing the inputDef." + }) + { + System.err.println( line); + } } /** @@ -1074,6 +1161,10 @@ public static void main( String[] args) { run( new Options( args)); } + catch( HelpException h) + { + exitCode = 1; + } catch( Exception e) { exitCode = 1; diff --git a/tcases-cli/src/main/java/org/cornutum/tcases/openapi/ApiCommand.java b/tcases-cli/src/main/java/org/cornutum/tcases/openapi/ApiCommand.java index 2bb37537d..44ced8222 100644 --- a/tcases-cli/src/main/java/org/cornutum/tcases/openapi/ApiCommand.java +++ b/tcases-cli/src/main/java/org/cornutum/tcases/openapi/ApiCommand.java @@ -7,6 +7,7 @@ package org.cornutum.tcases.openapi; +import org.cornutum.tcases.HelpException; import org.cornutum.tcases.SystemInputDef; import org.cornutum.tcases.Tcases; import org.cornutum.tcases.TcasesIO; @@ -333,12 +334,17 @@ protected int handleOption( String[] args, int i) { String arg = args[i]; - if( arg.equals( "-o")) + if( arg.equals( "-help")) + { + throwHelpException(); + } + + else if( arg.equals( "-o")) { i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setOutDir( new File( args[i])); } @@ -348,7 +354,7 @@ else if( arg.equals( "-c")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setConditionNotifiers( args[i]); } @@ -358,7 +364,7 @@ else if( arg.equals( "-f")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setOutFile( new File( args[i])); } @@ -378,7 +384,7 @@ else if( arg.equals( "-T")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -444,7 +450,7 @@ else if( arg.equals( "-x")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setTransformDef( new File( args[i])); } @@ -454,7 +460,7 @@ else if( arg.equals( "-p")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } String binding = args[i]; int valuePos = binding.indexOf( '='); @@ -476,7 +482,7 @@ else if( arg.equals( "-r")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -493,7 +499,7 @@ else if( arg.equals( "-m")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -507,7 +513,7 @@ else if( arg.equals( "-m")) else { - throwUsageException(); + throwUsageException( String.format( "Unknown option: %s", arg)); } return i + 1; @@ -522,7 +528,7 @@ protected void handleArgs( String[] args, int i) if( nargs > 1) { - throwUsageException(); + throwUsageException( String.format( "Unexpected argument: %s", args[i+1])); } if( nargs > 0) @@ -532,15 +538,15 @@ protected void handleArgs( String[] args, int i) } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a missing option value */ - protected void throwUsageException() + protected void throwMissingValue( String option) { - throwUsageException( null, null); + throwUsageException( String.format( "No value given for %s option", option)); } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a command line error. */ protected void throwUsageException( String detail) { @@ -548,7 +554,7 @@ protected void throwUsageException( String detail) } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a command line error. */ protected void throwUsageException( String detail, Exception cause) { @@ -556,34 +562,105 @@ protected void throwUsageException( String detail, Exception cause) } /** - * Returns a RuntimeException reporting a command line error. + * Returns an IllegalArgumentException reporting a command line error. */ - protected RuntimeException getUsageException( String detail, Exception cause) + protected IllegalArgumentException getUsageException( String detail, Exception cause) { - if( detail != null) - { - cause = new RuntimeException( detail, cause); - } - return new IllegalArgumentException - ( "Usage: " - + ApiCommand.class.getSimpleName() - + " [-v]" - + " [-C | -S | -D]" - + " [-I]" - + " [-R]" - + " [-W]" - + " [-c {log | fail | ignore}]" - + " [-f outFile]" - + " [-o outDir]" - + " [-x transformDef | -J | -H]" - + " [-p name=value]" - + " [-r seed]" - + " [-m maxTries]" - + " [-T contentType]" - + " [apiSpec]", - cause); + ( "Invalid command line argument. For all command line details, use the -help option.", + new IllegalArgumentException( detail, cause)); + } + + /** + * Throws a HelpException after printing usage information to standard error. + */ + protected void throwHelpException() + { + printUsage(); + throw new HelpException(); + } + + /** + * Prints usage information to standard error. + */ + protected void printUsage() + { + for( String line : + new String[] { + "Usage: tcases-api [option...] [apiSpec]", + "", + "Generates input models and test models for API clients and servers, based on an OpenAPI v3 compliant API spec.", + "", + "An OpenApi v3 API spec is read from the given apiSpec file. If omitted, the API spec is read from standard input.", + "If no outFile is specified, output is written to a default file derived from the apiSpec or, if no apiSpec is", + "given, to standard output.", + "", + "Suppose that the base name of apiSpec (less any extension) is B. Then, assuming defaults for all options, output", + "will be a test definition for API requests written to a file named 'B-Requests-Test.json'. If -C is specified,", + "output will be a test definition for API responses written to a file named 'B-Responses-Test.json'. If -D is", + "specified, output will be a list of request test cases written to a file named 'B-Request-Cases.json'. If -I is", + "specified, output will be the corresponding input definition, written to either 'B-Requests-Input.json' or", + "'B-Responses-Input.json', respectively.", + "", + "Each option is one of the following:", + "", + " -C, -S, -D If -C is given, produce results to test inputs to an API client, i.e. API responses. If -S is given,", + " produce results to test inputs to an API server, i.e. API requests. If -D is given, produce request", + " test cases for an API server, i.e. API request tests. If none of these is given, the default is -S.", + "", + " -I Produce an input definition file for either an API client (-C) or an API server (-S). If omitted,", + " produce the corresponding test definition file.", + "", + " -R If specified, tests will be generated assuming that the API will strictly enforce exclusion of 'readOnly'", + " properties from request parameters. If omitted, no strict enforcement is assumed.", + "", + " -W If specified, tests will be generated assuming that the API will strictly enforce exclusion of 'writeOnly'", + " properties from responses. If omitted, no strict enforcement is assumed.", + "", + " -c M[R] Defines how input modelling and request case resolution conditions are reported. Both M (for modelling conditions)", + " and R (for resolution conditions) must be one of 'log', 'fail', or 'ignore'. If 'log' is specified, conditions are", + " reported using log messages. If 'fail' is specified, any condition will cause an exception. If 'ignore' is specified,", + " all conditions are silently ignored. If R is omitted, the default is 'log'. If -c is omitted, the default is", + " 'log,log'.", + "", + " -f outFile If -f is defined, output is written to the specified outFile, relative to the given outDir.", + " If omitted, the default outFile is derived from the apiSpec.", + "", + " -o outDir If -o is defined, output is written to the specified directory. If omitted, the default outDir is", + " the directory containing the apiSpec or, if reading from standard input, the current working directory.", + " If an output path cannot be derived, output is written to standard output.", + "", + " -J If -J is defined, test definition output is transformed into Java source", + " code for a JUnit test class. The resulting Java source file is written to", + " the specified outDir.", + "", + " -H If -H is defined, test definition output is transformed into an HTML report", + " that is written to the specified outDir.", + "", + " -x xsltDef If -x is defined, test definition output is transformed according to the", + " XSLT transform defined by the xsltDef file. If relative, the xsltDef path is", + " assumed to be relative to the directory containing the apiSpec.", + "", + " -p name=value Defines the value of a transform parameter. Any number of -p options", + " may be specified. This option is meaningful only if the -x or -J option is given.", + "", + " -r seed When -D is specified, use the given random number seed to generate request test case", + " input values. If omitted, the default random number seed is derived from the 'apiSpec' name.", + "", + " -m maxTries When -D is specified, defines the maximum attempts made to resolve a request test case input", + " value before reporting failure. If omitted, the default value is 10000.", + "", + " -T docType Defines the content type of the OpenApi specification. The 'docType' must be one of 'json', 'yaml',", + " or 'yml'. If omitted, the default content type is derived from the 'apiSpec' name. ", + " If the 'apiSpec' is read from standard input or does not have a recognized extension, the default", + " content type is 'json'.", + "", + " -v Prints the current command version identifier to standard output." + }) + { + System.err.println( line); + } } /** @@ -1227,6 +1304,10 @@ public static void main( String[] args) { run( new Options( args)); } + catch( HelpException h) + { + exitCode = 1; + } catch( Exception e) { exitCode = 1; diff --git a/tcases-cli/src/main/java/org/cornutum/tcases/openapi/ApiTestCommand.java b/tcases-cli/src/main/java/org/cornutum/tcases/openapi/ApiTestCommand.java index a5d5dd889..b0b9bb488 100644 --- a/tcases-cli/src/main/java/org/cornutum/tcases/openapi/ApiTestCommand.java +++ b/tcases-cli/src/main/java/org/cornutum/tcases/openapi/ApiTestCommand.java @@ -7,6 +7,7 @@ package org.cornutum.tcases.openapi; +import org.cornutum.tcases.HelpException; import org.cornutum.tcases.SystemInputDef; import org.cornutum.tcases.Tcases; import org.cornutum.tcases.openapi.io.TcasesOpenApiIO; @@ -338,12 +339,17 @@ protected int handleOption( String[] args, int i) { String arg = args[i]; - if( arg.equals( "-t")) + if( arg.equals( "-help")) + { + throwHelpException(); + } + + else if( arg.equals( "-t")) { i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -360,7 +366,7 @@ else if( arg.equals( "-e")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -377,7 +383,7 @@ else if( arg.equals( "-n")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setTestName( args[i]); } @@ -387,7 +393,7 @@ else if( arg.equals( "-p")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setTestPackage( args[i]); } @@ -397,7 +403,7 @@ else if( arg.equals( "-b")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setBaseClass( args[i]); } @@ -407,7 +413,7 @@ else if( arg.equals( "-f")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setOutFile( new File( args[i])); } @@ -417,7 +423,7 @@ else if( arg.equals( "-o")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setOutDir( new File( args[i])); } @@ -427,7 +433,7 @@ else if( arg.equals( "-M")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setMocoTestConfig( new File( args[i])); } @@ -437,7 +443,7 @@ else if( arg.equals( "-P")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setPaths( Arrays.asList( args[i].split( " *, *"))); } @@ -447,7 +453,7 @@ else if( arg.equals( "-O")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setOperations( Arrays.asList( args[i].split( " *, *"))); } @@ -457,7 +463,7 @@ else if( arg.equals( "-c")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } setConditionNotifiers( args[i]); } @@ -472,7 +478,7 @@ else if( arg.equals( "-r")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -489,7 +495,7 @@ else if( arg.equals( "-m")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -506,7 +512,7 @@ else if( arg.equals( "-T")) i++; if( i >= args.length) { - throwUsageException(); + throwMissingValue( arg); } try { @@ -525,7 +531,7 @@ else if( arg.equals( "-v")) else { - throwUsageException(); + throwUsageException( String.format( "Unknown option: %s", arg)); } return i + 1; @@ -540,7 +546,7 @@ protected void handleArgs( String[] args, int i) if( nargs > 1) { - throwUsageException(); + throwUsageException( String.format( "Unexpected argument: %s", args[i+1])); } if( nargs > 0) @@ -550,15 +556,15 @@ protected void handleArgs( String[] args, int i) } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a missing option value */ - protected void throwUsageException() + protected void throwMissingValue( String option) { - throwUsageException( null, null); + throwUsageException( String.format( "No value given for %s option", option)); } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a command line error. */ protected void throwUsageException( String detail) { @@ -566,7 +572,7 @@ protected void throwUsageException( String detail) } /** - * Throws a RuntimeException reporting a command line error. + * Throws a IllegalArgumentException reporting a command line error. */ protected void throwUsageException( String detail, Exception cause) { @@ -574,37 +580,107 @@ protected void throwUsageException( String detail, Exception cause) } /** - * Returns a RuntimeException reporting a command line error. + * Returns an IllegalArgumentException reporting a command line error. */ - protected RuntimeException getUsageException( String detail, Exception cause) + protected IllegalArgumentException getUsageException( String detail, Exception cause) { - if( detail != null) - { - cause = new RuntimeException( detail, cause); - } - return new IllegalArgumentException - ( "Usage: " - + ApiTestCommand.class.getSimpleName() - + " [-v]" - + " [-t testType]" - + " [-e execType]" - + " [-n testName]" - + " [-p testPackage]" - + " [-b baseClass]" - + " [-f outFile]" - + " [-o outDir]" - + " [-M mocoTestConfig]" - + " [-P paths]" - + " [-O operations]" - + " [-T contentType]" - + " [-c condRep[,condRep]]" - + " [-R]" - + " [-r seed]" - + " [-m maxTries]" - + " [apiSpec]", - cause); + ( "Invalid command line argument. For all command line details, use the -help option.", + new IllegalArgumentException( detail, cause)); + } + + /** + * Throws a HelpException after printing usage information to standard error. + */ + protected void throwHelpException() + { + printUsage(); + throw new HelpException(); + } + + /** + * Prints usage information to standard error. + */ + protected void printUsage() + { + for( String line : + new String[] { + "Usage: tcases-api-test [option...] [apiSpec]", + "", + "Generates executable test code for API servers, based on an OpenAPI v3 compliant API spec.", + "", + "An OpenAPI v3 API spec is read from the given apiSpec file. If omitted, the API spec is read from", + "standard input. If no outFile is specified, output is written to a default file derived from the", + "apiSpec or, if no apiSpec is given, to standard output.", + "", + "Each option is one of the following:", + "", + " -t testType Defines the test framework used to run API tests. Valid values are 'junit', 'testng',", + " or 'moco'. If omitted, the default is 'junit'.", + "", + " Use 'moco' to generate a JUnit test that sends requests to a Moco stub server.", + " To define the Moco server test configuration, use the '-M' option.", + "", + " -e execType Defines the request execution interface used to run API tests. Valid values are", + " 'restassured'. If omitted, the default is 'restassured'.", + "", + " -n testName Defines the name of the test class that is generated. This can be either a fully-", + " qualified class name or a simple class name. If omitted, the default is based on", + " the title of the apiSpec.", + "", + " -p testPackage Defines the package for the test class that is generated. This can be omitted if", + " the testName is a fully-qualified class name or if the package can be determined", + " from the outDir.", + "", + " -b baseClass If defined, specifies a base class for the generated test class. This can be a", + " fully-qualified class name or a simple class name, if the baseClass belongs to", + " the same package as the generated test class.", + "", + " -f outFile If defined, output is written to the specified outFile, relative to the given outDir.", + " If omitted, the default outFile is derived from the testName.", + "", + " -o outDir If -o is defined, output is written to the specified directory. If omitted, the", + " default outDir is the directory containing the apiSpec or, if reading from standard", + " input, output is written to standard output.", + "", + " -M mocoTestConfig When the testType is 'moco', specifies the Moco server test configuration file.", + "", + " -P paths If defined, tests are generated only for the specified API resource paths. The paths", + " option must be a comma-separated list of resource paths defined in the apiSpec. If", + " omitted, tests are generated for all resource paths.", + "", + " -O operations If defined, tests are generated only for the specified HTTP methods. The operations", + " option must be a comma-separated list of path operations defined in the apiSpec. If", + " omitted, tests are generated for all operations.", + "", + " -T contentType Defines the content type of the OpenApi specification. The contentType must be one", + " of 'json', 'yaml', or 'yml'. If omitted, the default content type is derived from the", + " apiSpec name. If the apiSpec is read from standard input or does not have a recognized", + " extension, the default content type is 'json'.", + "", + " -c M[,R] Defines how input modelling and request case resolution conditions are reported.", + " Both M (for modelling conditions) and R (for resolution conditions) must be one of", + " 'log', 'fail', or 'ignore'. If 'log' is specified, conditions are reported using log", + " messages. If 'fail' is specified, any condition will cause an exception. If 'ignore'", + " is specified, all conditions are silently ignored. If R is omitted, the default is", + " 'log'. If -c is omitted, the default is 'log,log'.", + "", + " -R If specified, tests will be generated assuming that the API will strictly enforce", + " exclusion of 'readOnly' properties from request parameters. If omitted, no strict", + " enforcement is assumed.", + "", + " -r seed If defined, use the given random number seed to generate request test case input", + " values. If omitted, the default random number seed is derived from the apiSpec name.", + "", + " -m maxTries Defines the maximum attempts made to resolve a request test case input value before", + " reporting failure. If omitted, the default value is 10000.", + "", + " -v Prints the current command version identifier to standard output." + }) + { + System.err.println( line); + } } /** @@ -1337,6 +1413,10 @@ public static void main( String[] args) { run( new Options( args)); } + catch( HelpException h) + { + exitCode = 1; + } catch( Exception e) { exitCode = 1; diff --git a/tcases-cli/src/test/java/org/cornutum/tcases/TestTcasesCommand.java b/tcases-cli/src/test/java/org/cornutum/tcases/TestTcasesCommand.java index 2de36268c..86927ce5d 100644 --- a/tcases-cli/src/test/java/org/cornutum/tcases/TestTcasesCommand.java +++ b/tcases-cli/src/test/java/org/cornutum/tcases/TestTcasesCommand.java @@ -27,6 +27,7 @@ import java.io.PrintStream; import java.net.URL; import java.nio.charset.Charset; +import java.util.Optional; /** * Runs tests for {@link TcasesCommand#main}. @@ -814,23 +815,7 @@ public void run_Transform_4() throws Exception inFile.getPath() }; - // When... - Exception failure = null; - try - { - TcasesCommand.run( new Options( args)); - } - catch( Exception expected) - { - failure = expected; - } - - // Then... - assertThat( "Exception thrown", failure != null, is( true)); - assertThat( "Usage exception", failure.getMessage().startsWith( "Usage: "), is( true)); - - String cause = failure.getCause()==null? null : failure.getCause().getMessage(); - assertThat( "Cause", cause, is( "Invalid -p option: parameter name undefined")); + assertUsageException( args, "Invalid -p option: parameter name undefined"); } /** @@ -910,23 +895,7 @@ public void run_Transform_6() throws Exception inFile.getPath() }; - // When... - Exception failure = null; - try - { - TcasesCommand.run( new Options( args)); - } - catch( Exception expected) - { - failure = expected; - } - - // Then... - assertThat( "Exception thrown", failure != null, is( true)); - assertThat( "Usage exception", failure.getMessage().startsWith( "Usage: "), is( true)); - - String cause = failure.getCause()==null? null : failure.getCause().getMessage(); - assertThat( "Cause", cause, is( "Invalid -p option: must be name=value")); + assertUsageException( args, "Invalid -p option: must be name=value"); } /** @@ -1011,75 +980,7 @@ public void run_Transform_8() throws Exception inFile.getPath() }; - // When... - Exception failure = null; - try - { - TcasesCommand.run( new Options( args)); - } - catch( Exception expected) - { - failure = expected; - } - - // Then... - assertThat( "Exception thrown", failure != null, is( true)); - assertThat( "Usage exception", failure.getMessage().startsWith( "Usage: "), is( true)); - - String cause = failure.getCause()==null? null : failure.getCause().getMessage(); - assertThat( "Cause", cause, is( "Can't specify multiple output transforms")); - - // Given... - args = new String[] - { - "-H", - "-x", transformFile.getPath(), - inFile.getPath() - }; - - // When... - failure = null; - try - { - TcasesCommand.run( new Options( args)); - } - catch( Exception expected) - { - failure = expected; - } - - // Then... - assertThat( "Exception thrown", failure != null, is( true)); - assertThat( "Usage exception", failure.getMessage().startsWith( "Usage: "), is( true)); - - cause = failure.getCause()==null? null : failure.getCause().getMessage(); - assertThat( "Cause", cause, is( "Can't specify multiple output transforms")); - - // Given... - args = new String[] - { - "-H", - "-J", - inFile.getPath() - }; - - // When... - failure = null; - try - { - TcasesCommand.run( new Options( args)); - } - catch( Exception expected) - { - failure = expected; - } - - // Then... - assertThat( "Exception thrown", failure != null, is( true)); - assertThat( "Usage exception", failure.getMessage().startsWith( "Usage: "), is( true)); - - cause = failure.getCause()==null? null : failure.getCause().getMessage(); - assertThat( "Cause", cause, is( "Can't specify multiple output transforms")); + assertUsageException( args, "Can't specify multiple output transforms"); } /** @@ -1519,6 +1420,21 @@ public void runWithContentType_Transformed() throws Exception assertThat( "Test def created", outFile.exists(), is( true)); } + /** + * Reports a failure if using the given command arguments does not produce the expected exception. + */ + private void assertUsageException( String[] args, String error) + { + expectFailure( IllegalArgumentException.class) + .when( () -> TcasesCommand.run( new Options( args))) + .then( failure -> { + assertThat + ( "Error", + Optional.ofNullable( failure.getCause()).map( Throwable::getMessage).orElse( null), + is( error)); + }); + } + /** * Return the file for the given resource. */ diff --git a/tcases-shell/bin/tcases b/tcases-shell/bin/tcases index a0fee233b..e4fdd0ff4 100755 --- a/tcases-shell/bin/tcases +++ b/tcases-shell/bin/tcases @@ -7,108 +7,22 @@ pgm=`basename "$0"` -usage() +runJava() { - echo Usage: $pgm "[-c tupleSize] [-f outFile] [-g genDef] [-n] [-l logFile] [-L logLevel] [-o outDir] [-p name=value] [-r seed] [-R] [-t testDef] [-T contentType] [-v] [-x xsltDef | -J | -H] [inputDef]" >&2 - echo "" >&2 - echo " Generates a set of test cases from a system input definition, according" >&2 - echo " to the given command line arguments." >&2 - echo "" >&2 - echo " inputDef The system input definition is read from the given inputDef. If omitted," >&2 - echo " the system input definition is read from standard input. Otherwise, the" >&2 - echo " system input definition is read from the first one of the following files" >&2 - echo " that can be located." >&2 - echo "" >&2 - echo " 1. inputDef" >&2 - echo " 2. inputDef-Input.xml" >&2 - echo " 3. inputDef.xml" >&2 - echo "" >&2 - echo " -c tuples If -c is defined, use the given default tuple size for all generators." >&2 - echo " This updates the generator definitions specified by the genDef file." >&2 - echo "" >&2 - echo " -f outFile If -f is defined, test definition output is written to the specified" >&2 - echo " outFile, relative to the given outDir. If omitted, test definitions are" >&2 - echo " written to the file specified by the -t option. If an output path cannot" >&2 - echo " be derived, output is written to standard output." >&2 - echo "" >&2 - echo " -g genDef If -g is defined, test definitions are created using the generator(s)" >&2 - echo " specified by the given genDef file. If omitted, the default generator" >&2 - echo " definition is used. The default generator definition is read from the" >&2 - echo " corresponding *-Generators.xml file in the same directory as the inputDef," >&2 - echo " if it exists. Otherwise, the default TupleGenerator is used for all" >&2 - echo " functions." >&2 - echo "" >&2 - echo " -J If -J is defined, test definition output is transformed into Java source" >&2 - echo " code for a JUnit test class. The resulting Java source file is written to" >&2 - echo " the specified outDir." >&2 - echo "" >&2 - echo " -H If -H is defined, test definition output is transformed into an HTML report" >&2 - echo " that is written to the specified outDir." >&2 - echo "" >&2 - echo " -l logFile If -l is defined, log output is written to the given file. If omitted," >&2 - echo " log output is written to a file named ${pgm}.log in the current working" >&2 - echo " directory. If logFile is \"stdout\", log output is written to standard output." >&2 - echo "" >&2 - echo " -L logLevel Defines the level for Tcases log output. If omitted, the default level is INFO" >&2 - echo " Tcases logging uses the configuration and levels defined by the Logback system." >&2 - echo "" >&2 - echo " -n If -n is defined, any previous contents of the testDef are ignored." >&2 - echo " If omitted, new test definitions are based on the previous testDef." >&2 - echo "" >&2 - echo " -o outDir If -o is defined, test definition output is written to the specified" >&2 - echo " directory. If omitted, the default outDir is the directory containing" >&2 - echo " the inputDef or, if reading from standard input, the current working" >&2 - echo " directory. If an output path cannot be derived, output is written to" >&2 - echo " standard output." >&2 - echo "" >&2 - echo " -p name=value Defines the value of a transform parameter. Any number of -p options" >&2 - echo " may be specified. This option is meaningful only if the -x or -J option is given." >&2 - echo "" >&2 - echo " -r seed If -r is defined, use the given random number seed for all generators." >&2 - echo " This updates the generator definitions specified by the genDef file." >&2 - echo "" >&2 - echo " -R If -R is defined, choose a new random number seed for all generators." >&2 - echo " This updates the generator definitions specified by the genDef file." >&2 - echo "" >&2 - echo " -t testDef If -t is defined, test definition output is written to the specified" >&2 - echo " testDef path, relative to the outDir. If omitted, the default testDef" >&2 - echo " name is derived from the inputDef name. If an output path cannot be" >&2 - echo " derived, output is written to standard output." >&2 - echo "" >&2 - echo " -T contentType Defines the default content type for the files read and produced." >&2 - echo " The contentType must be one of \"json\" or \"xml\". The default content type is" >&2 - echo " assumed for any file that is not specified explicitly or that does not have a" >&2 - echo " recognized extension. If omitted, the default content type is derived from the" >&2 - echo " inputDef name." >&2 - echo "" >&2 - echo " -v Shows the current Tcases version. If this option is given, no other" >&2 - echo " action is performed." >&2 - echo "" >&2 - echo " -x xsltDef If -x is defined, test definition output is transformed according to the" >&2 - echo " XSLT transform defined by the xsltDef file. If relative, the xsltDef path is" >&2 - echo " assumed to be relative to the directory containing the inputDef." >&2 + java \ + -cp "$classPath" \ + -D${logDest}="$logFile" \ + -Dtcases.log.level="$logLevel" \ + org.cornutum.tcases.TcasesCommand \ + $@ } +args= while [ $# -gt 0 ] ; do case $1 in - -c) shift; defTupleSize="$1";; - -f) shift; outFile="$1";; - -g) shift; genDef="$1";; - -H) isHtml="$1";; - -J) isJUnit="$1";; -l) shift; logFile="$1";; -L) shift; logLevel="$1";; - -n) noExtend="$1";; - -o) shift; outDir="$1";; - -p) shift; params="$params -p $1";; - -r) shift; seed="$1";; - -R) newSeed="$1";; - -t) shift; testDef="$1";; - -T) shift; contentType="$1";; - -v) showVersion="$1";; - -x) shift; xsltDef="$1";; - -*) usage; exit 1;; - *) break;; + *) args="$args $1";; esac shift done @@ -121,12 +35,6 @@ if [ $logFile = "stdout" ] ; then else logDest=tcases.log.file fi -inputDef="$1" -shift - -if [ $# -ne 0 ] ; then - usage; exit 1 -fi binDir=`dirname "$0"` releaseDir=`cd "$binDir"/..; pwd` @@ -137,38 +45,4 @@ for jar in "${libDir}"/*.jar; do classPath="${classPath}:${jar}" done -uname=`uname` -cygwin=`expr $uname : 'CYGWIN'` -if [ $cygwin -gt 0 ] ; then - classPath=`cygpath --path -m "$classPath"` - genDef=${genDef:+`cygpath -m "$genDef"`} - inputDef=${inputDef:+`cygpath -m "$inputDef"`} - logFile=`cygpath -m "$logFile"` - outDir=${outDir:+`cygpath -m "$outDir"`} - outFile=${outFile:+`cygpath -m "$outFile"`} - testDef=${testDef:+`cygpath -m "$testDef"`} - xsltDef=${xsltDef:+`cygpath -m "$xsltDef"`} -fi - - -java \ - -cp "$classPath" \ - -D${logDest}="$logFile" \ - -Dtcases.log.level="$logLevel" \ - org.cornutum.tcases.TcasesCommand \ - $showVersion \ - ${genDef:+-g "$genDef"} \ - ${seed:+-r "$seed"} \ - $newSeed \ - ${defTupleSize:+-c "$defTupleSize"} \ - $noExtend \ - $isJUnit \ - $isHtml \ - ${outDir:+-o "$outDir"} \ - ${outFile:+-f "$outFile"} \ - ${params} \ - ${testDef:+-t "$testDef"} \ - ${contentType:+-T "$contentType"} \ - ${xsltDef:+-x "$xsltDef"} \ - ${inputDef:+"$inputDef"} - +runJava $args diff --git a/tcases-shell/bin/tcases-api b/tcases-shell/bin/tcases-api index aca1a8600..b532a2397 100755 --- a/tcases-shell/bin/tcases-api +++ b/tcases-shell/bin/tcases-api @@ -7,100 +7,22 @@ pgm=`basename "$0"` -usage() +runJava() { - echo Usage: $pgm "[option...] [apiSpec]" >&2 - echo "" >&2 - echo "where each 'option' is one of the following:" >&2 - echo "" >&2 - echo " -C, -S, -D If -C is given, produce results to test inputs to an API client, i.e. API responses. If -S is given," >&2 - echo " produce results to test inputs to an API server, i.e. API requests. If -D is given, produce request" >&2 - echo " test cases for an API server, i.e. API request tests. If none of these is given, the default is -S." >&2 - echo "" >&2 - echo " -I Produce an input definition file for either an API client (-C) or an API server (-S). If omitted," >&2 - echo " produce the corresponding test definition file." >&2 - echo "" >&2 - echo " -R If specified, tests will be generated assuming that the API will strictly enforce exclusion of 'readOnly'" >&2 - echo " properties from request parameters. If omitted, no strict enforcement is assumed." >&2 - echo "" >&2 - echo " -W If specified, tests will be generated assuming that the API will strictly enforce exclusion of 'writeOnly'" >&2 - echo " properties from responses. If omitted, no strict enforcement is assumed." >&2 - echo "" >&2 - echo " -c M[R] Defines how input modelling and request case resolution conditions are reported. Both M (for modelling conditions)" >&2 - echo " and R (for resolution conditions) must be one of 'log', 'fail', or 'ignore'. If 'log' is specified, conditions are" >&2 - echo " reported using log messages. If 'fail' is specified, any condition will cause an exception. If 'ignore' is specified," >&2 - echo " all conditions are silently ignored. If R is omitted, the default is 'log'. If -c is omitted, the default is" >&2 - echo " 'log,log'." >&2 - echo "" >&2 - echo " -f outFile If -f is defined, output is written to the specified outFile, relative to the given outDir." >&2 - echo " If omitted, the default outFile is derived from the apiSpec." >&2 - echo "" >&2 - echo " -o outDir If -o is defined, output is written to the specified directory. If omitted, the default outDir is" >&2 - echo " the directory containing the apiSpec or, if reading from standard input, the current working directory." >&2 - echo " If an output path cannot be derived, output is written to standard output." >&2 - echo "" >&2 - echo " -J If -J is defined, test definition output is transformed into Java source" >&2 - echo " code for a JUnit test class. The resulting Java source file is written to" >&2 - echo " the specified outDir." >&2 - echo "" >&2 - echo " -H If -H is defined, test definition output is transformed into an HTML report" >&2 - echo " that is written to the specified outDir." >&2 - echo "" >&2 - echo " -x xsltDef If -x is defined, test definition output is transformed according to the" >&2 - echo " XSLT transform defined by the xsltDef file. If relative, the xsltDef path is" >&2 - echo " assumed to be relative to the directory containing the apiSpec." >&2 - echo "" >&2 - echo " -p name=value Defines the value of a transform parameter. Any number of -p options" >&2 - echo " may be specified. This option is meaningful only if the -x or -J option is given." >&2 - echo "" >&2 - echo " -r seed When -D is specified, use the given random number seed to generate request test case" >&2 - echo " input values. If omitted, the default random number seed is derived from the 'apiSpec' name." >&2 - echo "" >&2 - echo " -m maxTries When -D is specified, defines the maximum attempts made to resolve a request test case input" >&2 - echo " value before reporting failure. If omitted, the default value is 10000." >&2 - echo "" >&2 - echo " -T docType Defines the content type of the OpenApi specification. The 'docType' must be one of 'json', 'yaml'," >&2 - echo " or 'yml'. If omitted, the default content type is derived from the 'apiSpec' name. " >&2 - echo " If the 'apiSpec' is read from standard input or does not have a recognized extension, the default" >&2 - echo " content type is 'json'." >&2 - echo "" >&2 - echo " -v Prints the current command version identifier to standard output." >&2 - echo "" >&2 - echo " apiSpec An OpenApi v3 API spec is read from the given apiSpec file. If omitted, the API spec is read from" >&2 - echo " standard input. If no outFile is specified, output is written to a default file derived from the" >&2 - echo " apiSpec or, if no apiSpec is given, to standard output." >&2 - echo "" >&2 - echo " Suppose that the base name of apiSpec (less any extension) is B. Then, assuming defaults for all" >&2 - echo " options, output will be a test definition for API requests written to a file named 'B-Requests-Test.json'." >&2 - echo " If -C is specified, output will be a test definition for API responses written to a file named" >&2 - echo " 'B-Responses-Test.json'. If -D is specified, output will be a list of request test cases written to a file" >&2 - echo " named 'B-Request-Cases.json'. If -I is specified, output will be the corresponding input definition, written" >&2 - echo " to either 'B-Requests-Input.json' or 'B-Responses-Input.json', respectively." >&2 + java \ + -cp "$classPath" \ + -D${logDest}="$logFile" \ + -Dtcases.log.level="$logLevel" \ + org.cornutum.tcases.openapi.ApiCommand \ + $@ } +args= while [ $# -gt 0 ] ; do case $1 in - -C) isClient="$1";; - -S) isServer="$1";; - -D) isRequestCases="$1";; - -I) isInput="$1";; - -R) enforceRO="$1";; - -W) enforceWO="$1";; -l) shift; logFile="$1";; -L) shift; logLevel="$1";; - -f) shift; outFile="$1";; - -o) shift; outDir="$1";; - -c) shift; notifier="$1";; - -H) isHtml="$1";; - -J) isJUnit="$1";; - -x) shift; xsltDef="$1";; - -p) shift; params="$params -p $1";; - -r) shift; random="$1";; - -m) shift; maxTries="$1";; - -T) shift; contentType="$1";; - -v) showVersion="$1";; - -*) usage; exit 1;; - *) break;; + *) args="$args $1";; esac shift done @@ -113,12 +35,6 @@ if [ $logFile = "stdout" ] ; then else logDest=tcases.log.file fi -apiSpec="$1" -shift - -if [ $# -ne 0 ] ; then - usage; exit 1 -fi binDir=`dirname "$0"` releaseDir=`cd "$binDir"/..; pwd` @@ -129,39 +45,4 @@ for jar in "${libDir}"/*.jar; do classPath="${classPath}:${jar}" done -uname=`uname` -cygwin=`expr $uname : 'CYGWIN'` -if [ $cygwin -gt 0 ] ; then - classPath=`cygpath --path -m "$classPath"` - apiSpec=${apiSpec:+`cygpath -m "$apiSpec"`} - logFile=`cygpath -m "$logFile"` - outDir=${outDir:+`cygpath -m "$outDir"`} - outFile=${outFile:+`cygpath -m "$outFile"`} - xsltDef=${xsltDef:+`cygpath -m "$xsltDef"`} -fi - - -java \ - -cp "$classPath" \ - -D${logDest}="$logFile" \ - -Dtcases.log.level="$logLevel" \ - org.cornutum.tcases.openapi.ApiCommand \ - $showVersion \ - $isClient \ - $isServer \ - $isRequestCases \ - $isInput \ - $enforceRO \ - $enforceWO \ - ${notifier:+-c "$notifier"} \ - ${outDir:+-o "$outDir"} \ - ${outFile:+-f "$outFile"} \ - $isJUnit \ - $isHtml \ - ${xsltDef:+-x "$xsltDef"} \ - ${params} \ - ${random:+-r "$random"} \ - ${maxTries:+-m "$maxTries"} \ - ${contentType:+-T "$contentType"} \ - ${apiSpec:+"$apiSpec"} - +runJava $args diff --git a/tcases-shell/bin/tcases-api-test b/tcases-shell/bin/tcases-api-test index 33105e7b6..b897c776e 100644 --- a/tcases-shell/bin/tcases-api-test +++ b/tcases-shell/bin/tcases-api-test @@ -7,112 +7,22 @@ pgm=`basename "$0"` -usage() +runJava() { - echo Usage: $pgm "[option...] [apiSpec]" >&2 - echo "" >&2 - echo "where each 'option' is one of the following:" >&2 - echo "" >&2 - echo "-t testType " >&2 - echo " Defines the test framework used to run API tests. Valid values are 'junit', 'testng', or 'moco'." >&2 - echo " If omitted, the default is 'junit'." >&2 - echo "" >&2 - echo " Use 'moco' to generate a JUnit test that sends requests to a Moco stub server. To define " >&2 - echo " the Moco server test configuration, use the '-M' option." >&2 - echo "" >&2 - echo "-e execType " >&2 - echo " Defines the request execution interface used to run API tests. Valid values are 'restassured'." >&2 - echo " If omitted, the default is 'restassured'." >&2 - echo "" >&2 - echo "-n testName " >&2 - echo " Defines the name of the test class that is generated. This can be either a fully-qualified class name" >&2 - echo " or a simple class name. If omitted, the default is based on the title of the 'apiSpec'." >&2 - echo "" >&2 - echo "-p testPackage " >&2 - echo " Defines the package for the test class that is generated. This can be omitted if the 'testName'" >&2 - echo " is a fully-qualified class name or if the package can be determined from the 'outDir'." >&2 - echo "" >&2 - echo "-b baseClass " >&2 - echo " If defined, specifies a base class for the generated test class. This can be a fully-qualified class" >&2 - echo " name or a simple class name, if the 'baseClass' belongs to the same package as the generated test class." >&2 - echo "" >&2 - echo "-f outFile " >&2 - echo " If defined, output is written to the specified 'outFile', relative to the given 'outDir'." >&2 - echo " If omitted, the default 'outFile' is derived from the 'testName'." >&2 - echo "" >&2 - echo "-o outDir " >&2 - echo " If '-o' is defined, output is written to the specified directory. If omitted, the default " >&2 - echo " 'outDir' is the directory containing the 'apiSpec' or, if reading from standard input," >&2 - echo " output is written to standard output." >&2 - echo "" >&2 - echo "-M mocoTestConfig " >&2 - echo " When the 'testType' is 'moco', specifies the Moco server test configuration file." >&2 - echo "" >&2 - echo "-P paths " >&2 - echo " If defined, tests are generated only for the specified API resource paths. 'paths' must be a" >&2 - echo " comma-separated list of resource paths defined in the 'apiSpec'. If omitted, tests are generated " >&2 - echo " for all resource paths." >&2 - echo "" >&2 - echo "-O operations " >&2 - echo " If defined, tests are generated only for the specified HTTP methods. 'operations' must be a" >&2 - echo " comma-separated list of path operations defined in the 'apiSpec'. If omitted, tests are generated " >&2 - echo " for all operations." >&2 - echo "" >&2 - echo "-T contentType " >&2 - echo " Defines the content type of the OpenApi specification. The 'contentType' must be one of 'json', 'yaml'," >&2 - echo " or 'yml'. If omitted, the default content type is derived from the 'apiSpec' name. If the 'apiSpec'" >&2 - echo " is read from standard input or does not have a recognized extension, the default content type is 'json'." >&2 - echo "" >&2 - echo "-c M[,R] " >&2 - echo " Defines how input modelling and request case resolution conditions are reported. Both 'M' (for modelling conditions)" >&2 - echo " and 'R' (for resolution conditions) must be one of 'log', 'fail', or 'ignore'. If 'log' is specified, conditions" >&2 - echo " are reported using log messages. If 'fail' is specified, any condition will cause an exception." >&2 - echo " If 'ignore' is specified, all conditions are silently ignored. If 'R' is omitted, the default is 'log'." >&2 - echo " If '-c' is omitted, the default is 'log,log'." >&2 - echo "" >&2 - echo "-R " >&2 - echo " If specified, tests will be generated assuming that the API will strictly enforce exclusion of 'readOnly'" >&2 - echo " properties from request parameters. If omitted, no strict enforcement is assumed." >&2 - echo "" >&2 - echo "-r seed " >&2 - echo " If defined, use the given random number seed to generate request test case input values. " >&2 - echo " If omitted, the default random number seed is derived from the 'apiSpec' name." >&2 - echo "" >&2 - echo "-m maxTries " >&2 - echo " Defines the maximum attempts made to resolve a request test case input value before reporting failure." >&2 - echo " If omitted, the default value is 10000." >&2 - echo "" >&2 - echo "-v " >&2 - echo " Prints the current command version identifier to standard output." >&2 - echo "" >&2 - echo "apiSpec " >&2 - echo " An OpenAPI v3 API spec is read from the given 'apiSpec' file. If omitted, the API spec is" >&2 - echo " read from standard input. If no 'outFile' is specified, output is written to a default file" >&2 - echo " derived from the 'apiSpec' or, if no 'apiSpec' is given, to standard output." >&2 + java \ + -cp "$classPath" \ + -D${logDest}="$logFile" \ + -Dtcases.log.level="$logLevel" \ + org.cornutum.tcases.openapi.ApiTestCommand \ + $@ } +args= while [ $# -gt 0 ] ; do case $1 in - -t) shift; testType="$1";; - -e) shift; execType="$1";; - -n) shift; testName="$1";; - -p) shift; testPackage="$1";; - -b) shift; baseClass="$1";; - -f) shift; outFile="$1";; - -o) shift; outDir="$1";; - -M) shift; mocoTestConfig="$1";; - -P) shift; paths="$1";; - -O) shift; operations="$1";; - -T) shift; contentType="$1";; - -c) shift; notifier="$1";; - -R) enforceRO="$1";; - -r) shift; seed="$1";; - -m) shift; maxTries="$1";; - -v) showVersion="$1";; -l) shift; logFile="$1";; -L) shift; logLevel="$1";; - -*) usage; exit 1;; - *) break;; + *) args="$args $1";; esac shift done @@ -125,12 +35,6 @@ if [ $logFile = "stdout" ] ; then else logDest=tcases.log.file fi -apiSpec="$1" -shift - -if [ $# -ne 0 ] ; then - usage; exit 1 -fi binDir=`dirname "$0"` releaseDir=`cd "$binDir"/..; pwd` @@ -141,35 +45,4 @@ for jar in "${libDir}"/*.jar; do classPath="${classPath}:${jar}" done -uname=`uname` -cygwin=`expr $uname : 'CYGWIN'` -if [ $cygwin -gt 0 ] ; then - classPath=`cygpath --path -m "$classPath"` - apiSpec=${apiSpec:+`cygpath -m "$apiSpec"`} - logFile=`cygpath -m "$logFile"` - outDir=${outDir:+`cygpath -m "$outDir"`} - outFile=${outFile:+`cygpath -m "$outFile"`} -fi - -java \ - -cp "$classPath" \ - -D${logDest}="$logFile" \ - -Dtcases.log.level="$logLevel" \ - org.cornutum.tcases.openapi.ApiTestCommand \ - ${testType:+-t "$testType"} \ - ${execType:+-e "$execType"} \ - ${testName:+-n "$testName"} \ - ${testPackage:+-p "$testPackage"} \ - ${baseClass:+-b "$baseClass"} \ - ${outFile:+-f "$outFile"} \ - ${outDir:+-o "$outDir"} \ - ${mocoTestConfig:+-M "$mocoTestConfig"} \ - ${paths:+-P "$paths"} \ - ${operations:+-O "$operations"} \ - ${contentType:+-T "$contentType"} \ - ${notifier:+-c "$notifier"} \ - ${enforceRO} \ - ${seed:+-r "$seed"} \ - ${maxTries:+-m "$maxTries"} \ - ${showVersion} \ - ${apiSpec:+"$apiSpec"} +runJava $args diff --git a/tcases-shell/bin/tcases-reducer b/tcases-shell/bin/tcases-reducer index bc94aa046..2458279b3 100755 --- a/tcases-shell/bin/tcases-reducer +++ b/tcases-shell/bin/tcases-reducer @@ -7,65 +7,22 @@ pgm=`basename "$0"` -usage() +runJava() { - echo Usage: $pgm "[-f function] [-g genDef] [-r resampleFactor] [-R] [-s sampleCount] [-t testDef] [-T contentType] inputDef" >&2 - echo "" >&2 - echo " For a system input definition, updates the associated test case generators to reduce the number" >&2 - echo " of generated test cases, using the given command line options." >&2 - echo "" >&2 - echo " inputDef The system input definition is read from the given inputDef." >&2 - echo "" >&2 - echo " -f function If -f is defined, update only the test case generator for the given function." >&2 - echo " Otherwise, update the test case generators for all functions." >&2 - echo "" >&2 - echo " -g genDef If -g is defined, update the generator specified in the given genDef file." >&2 - echo " Otherwise, update the default generate definition file: the corresponding" >&2 - echo " *-Generators.xml file in the same directory as the inputDef." >&2 - echo "" >&2 - echo " -l logFile If -l is defined, log output is written to the given file. If omitted," >&2 - echo " log output is written to a file named ${pgm}.log in the current working" >&2 - echo " directory. If logFile is \"stdout\", log output is written to standard output." >&2 - echo "" >&2 - echo " -L logLevel Defines the level for Tcases log output. If omitted, the default level is INFO" >&2 - echo " Tcases logging uses the configuration and levels defined by the Logback system." >&2 - echo "" >&2 - echo " -r resampleFactor If -r is defined, use the given resampleFactor to determine the number" >&2 - echo " of samples in the next round of reducing. Depending on the resampleFactor," >&2 - echo " the next round may use more or fewer samples. If the previous round called" >&2 - echo " for N samples and produced a reduction, then the number of samples for the " >&2 - echo " next round will be N * ( 1 + resampleFactor). To increase sample count with" >&2 - echo " each round, define resampleFactor > 0. To decrease sample count with each round," >&2 - echo " define -1 < resampleFactor < 0. If resampleFactor is omitted, the default value is 0." >&2 - echo "" >&2 - echo " -R If defined, ignore any random seed defined in the genDef file and search for a new seed." >&2 - echo "" >&2 - echo " -s sampleCount Defines the number of samples for the initial round of reducing. If omitted," >&2 - echo " the default sampleCount is 10." >&2 - echo "" >&2 - echo " -t testDef If -t is defined, generate test cases based on the test definitions in the" >&2 - echo " specified testDef file, relative to the directory containing the inputDef." >&2 - echo "" >&2 - echo " -T contentType Defines the default content type for the files read and produced." >&2 - echo " The contentType must be one of \"json\" or \"xml\". The default content type is" >&2 - echo " assumed for any file that is not specified explicitly or that does not have a" >&2 - echo " recognized extension. If omitted, the default content type is derived from the" >&2 - echo " inputDef name." >&2 + java \ + -cp "$classPath" \ + -D${logDest}="$logFile" \ + -Dtcases.log.level="$logLevel" \ + org.cornutum.tcases.ReducerCommand \ + $@ } +args= while [ $# -gt 0 ] ; do case $1 in - -f) shift; function="$1";; - -g) shift; genDef="$1";; -l) shift; logFile="$1";; -L) shift; logLevel="$1";; - -r) shift; resmpf="$1";; - -R) newSeed="$1";; - -s) shift; smpc="$1";; - -t) shift; testDef="$1";; - -T) shift; contentType="$1";; - -*) usage; exit 1;; - *) break;; + *) args="$args $1";; esac shift done @@ -78,12 +35,6 @@ if [ $logFile = "stdout" ] ; then else logDest=tcases.log.file fi -inputDef="$1" -shift - -if [ $# -ne 0 ] ; then - usage; exit 1 -fi binDir=`dirname "$0"` releaseDir=`cd "$binDir"/..; pwd` @@ -94,28 +45,4 @@ for jar in "${libDir}"/*.jar; do classPath="${classPath}:${jar}" done -uname=`uname` -cygwin=`expr $uname : 'CYGWIN'` -if [ $cygwin -gt 0 ] ; then - classPath=`cygpath --path -m "$classPath"` - genDef=${genDef:+`cygpath -m "$genDef"`} - inputDef=${inputDef:+`cygpath -m "$inputDef"`} - logFile=`cygpath -m "$logFile"` - testDef=${testDef:+`cygpath -m "$testDef"`} -fi - - -java \ - -cp "$classPath" \ - -D${logDest}="$logFile" \ - -Dtcases.log.level="$logLevel" \ - org.cornutum.tcases.ReducerCommand \ - ${genDef:+-g "$genDef"} \ - ${resmpf:+-r "$resmpf"} \ - $newSeed \ - ${smpc:+-s "$smpc"} \ - ${function:+-f "$function"} \ - ${testDef:+-t "$testDef"} \ - ${contentType:+-T "$contentType"} \ - $inputDef - +runJava $args