1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.cli.bug;
19
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21
22 import java.io.PrintWriter;
23 import java.io.StringWriter;
24 import java.sql.ParameterMetaData;
25 import java.sql.Types;
26
27 import org.apache.commons.cli.HelpFormatter;
28 import org.apache.commons.cli.Option;
29 import org.apache.commons.cli.OptionGroup;
30 import org.apache.commons.cli.Options;
31 import org.junit.jupiter.api.BeforeEach;
32 import org.junit.jupiter.api.Test;
33
34 public class BugCLI162Test {
35
36 private static final String CR = System.lineSeparator();
37
38
39 private static final String OPT = "-";
40
41 private static final String OPT_COLUMN_NAMES = "l";
42
43 private static final String OPT_CONNECTION = "c";
44
45 private static final String OPT_DESCRIPTION = "e";
46
47 private static final String OPT_DRIVER = "d";
48
49 private static final String OPT_DRIVER_INFO = "n";
50
51 private static final String OPT_FILE_BINDING = "b";
52
53 private static final String OPT_FILE_JDBC = "j";
54
55 private static final String OPT_FILE_SFMD = "f";
56
57 private static final String OPT_HELP = "h";
58
59 private static final String OPT_HELP_LONG = "help";
60
61 private static final String OPT_INTERACTIVE = "i";
62
63 private static final String OPT_JDBC_TO_SFMD = "2";
64
65 private static final String OPT_JDBC_TO_SFMD_L = "jdbc2sfmd";
66
67 private static final String OPT_METADATA = "m";
68
69 private static final String OPT_PARAM_MODES_INT = "o";
70
71 private static final String OPT_PARAM_MODES_NAME = "O";
72
73 private static final String OPT_PARAM_NAMES = "a";
74
75 private static final String OPT_PARAM_TYPES_INT = "y";
76
77 private static final String OPT_PARAM_TYPES_NAME = "Y";
78
79 private static final String OPT_PASSWORD = "p";
80
81 private static final String OPT_PASSWORD_L = "password";
82
83 private static final String OPT_SQL = "s";
84
85 private static final String OPT_SQL_L = "sql";
86
87 private static final String OPT_STACK_TRACE = "t";
88
89 private static final String OPT_TIMING = "g";
90
91 private static final String OPT_TRIM_L = "trim";
92
93 private static final String OPT_USER = "u";
94
95 private static final String OPT_WRITE_TO_FILE = "w";
96
97 private static final String PMODE_IN = "IN";
98
99 private static final String PMODE_INOUT = "INOUT";
100
101 private static final String PMODE_OUT = "OUT";
102
103 private static final String PMODE_UNK = "Unknown";
104
105 private static final String PMODES = PMODE_IN + ", " + PMODE_INOUT + ", " + PMODE_OUT + ", " + PMODE_UNK;
106
107
108 private static final String EXPECTED = "usage: org.apache.commons.cli.bug.BugCLI162Test" + CR +
109 " -2,--jdbc2sfmd <arg> Converts the JDBC file in the first argument" + CR +
110 " to an SMFD file specified in the second" + CR +
111 " argument." + CR +
112 " -a,--paramNames <arg> Parameter XML names; default names are" + CR +
113 " param1, param2, etc. Example: -a \"pname1" + CR +
114 " pname2\"" + CR +
115 " -b,--jdbc <arg> Writes a JDBC binding node file for the given" + CR +
116 " SQL" + CR +
117 " -c,--url <arg> Connection URL" + CR +
118 " -d,--driver <arg> JDBC driver class name" + CR +
119 " -e,--description <arg> SFMD description. A default description is" + CR +
120 " used if omited. Example: -e \"Runs such and" + CR +
121 " such\"" + CR +
122 " -f,--sfmd <arg> Writes a SFMD file for the given SQL" + CR +
123 " -g,--printTiming Prints timing information" + CR +
124 " -h,--help Prints help and quits" + CR +
125 " -i,--interactive Runs in interactive mode, reading and writing" + CR +
126 " from the console, 'go' or '/' sends a" + CR +
127 " statement" + CR +
128 " -j,--node <arg> Writes a JDBC node file for the given SQL" + CR +
129 " (internal debugging)" + CR +
130 " -l,--columnNames <arg> Column XML names; default names column" + CR +
131 " labels. Example: -l \"cname1 cname2\"" + CR +
132 " -m,--printMetaData Prints metadata information" + CR +
133 " -n,--info Prints driver information and properties. If" + CR +
134 " -c is not specified, all drivers on the" + CR +
135 " classpath are displayed." + CR +
136 " -o,--paramModes <arg> Parameters modes (1=IN, 2=INOUT, 4=OUT," + CR +
137 " 0=Unknown). -o and -O are mutually exclusive." + CR +
138 " Example for 2 parameters, OUT and IN: -o \"4" + CR +
139 " 1\"" + CR +
140 " -O,--paramModeNames <arg> Parameters mode names (IN, INOUT, OUT," + CR +
141 " Unknown). -o and -O are mutually exclusive." + CR +
142 " Example for 2 parameters, OUT and IN: -O \"OUT" + CR +
143 " IN\"" + CR +
144 " -p,--password <arg> The database password for the user specified" + CR +
145 " with the -u option. You can obfuscate the" + CR +
146 " password with" + CR +
147 " org.mortbay.jetty.security.Password, see" + CR +
148 " https://docs.codehaus.org/display/JETTY/Secur" + CR +
149 " ing+Passwords" + CR +
150 " -s,--sql <arg> Runs SQL or {call stored_procedure(?, ?)} or" + CR +
151 " {?=call function(?, ?)}" + CR +
152 " -t,--printStack Prints stack traces on errors" + CR +
153 " --trim <arg> Trims leading and trailing spaces from all" + CR +
154 " column values. Column XML names can be" + CR +
155 " optionally specified to set which columns to" + CR +
156 " trim." + CR +
157 " -u,--user <arg> A database user name" + CR +
158 " -w,--outfile <arg> Writes the SQL output to the given file" + CR +
159 " -y,--paramTypes <arg> Parameter types from java.sql.Types. -y and" + CR +
160 " -Y are mutually exclusive. Example: -y \"-10" + CR +
161 " 12\"" + CR +
162 " -Y,--paramTypeNames <arg> Parameter java.sql.Types names. -y and -Y are" + CR +
163 " mutually exclusive. Example: -Y \"CURSOR" + CR +
164 " VARCHAR\"" + CR;
165
166
167 private HelpFormatter formatter;
168
169 private StringWriter sw;
170
171 @BeforeEach
172 public void setUp() {
173 formatter = new HelpFormatter();
174 sw = new StringWriter();
175 }
176
177 @Test
178 public void testInfiniteLoop() {
179 final Options options = new Options();
180 options.addOption("h", "help", false, "This is a looooong description");
181
182 formatter.printHelp(new PrintWriter(sw), 20, "app", null, options, HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null);
183
184
185 final String expected = "usage: app" + CR +
186 " -h,--help This is" + CR +
187 " a" + CR +
188 " looooon" + CR +
189 " g" + CR +
190 " descrip" + CR +
191 " tion" + CR;
192
193 assertEquals(expected, sw.toString());
194 }
195
196 @Test
197 public void testLongLineChunking() {
198 final Options options = new Options();
199
200 options.addOption("x", "extralongarg", false,
201 "This description has ReallyLongValuesThatAreLongerThanTheWidthOfTheColumns " +
202 "and also other ReallyLongValuesThatAreHugerAndBiggerThanTheWidthOfTheColumnsBob, " +
203 "yes. ");
204
205 formatter.printHelp(new PrintWriter(sw), 35, this.getClass().getName(), "Header", options, 0, 5, "Footer");
206
207 final String expected = "usage:" + CR +
208 " org.apache.commons.cli.bug.B" + CR +
209 " ugCLI162Test" + CR +
210 "Header" + CR +
211 "-x,--extralongarg This" + CR +
212 " description" + CR +
213 " has" + CR +
214 " ReallyLongVal" + CR +
215 " uesThatAreLon" + CR +
216 " gerThanTheWid" + CR +
217 " thOfTheColumn" + CR +
218 " s and also" + CR +
219 " other" + CR +
220 " ReallyLongVal" + CR +
221 " uesThatAreHug" + CR +
222 " erAndBiggerTh" + CR +
223 " anTheWidthOfT" + CR +
224 " heColumnsBob," + CR +
225 " yes." + CR +
226 "Footer" + CR;
227
228 assertEquals(expected, sw.toString(), "Long arguments did not split as expected");
229 }
230
231 @Test
232 public void testLongLineChunkingIndentIgnored() {
233 final Options options = new Options();
234 options.addOption("x", "extralongarg", false, "This description is Long.");
235 formatter.printHelp(new PrintWriter(sw), 22, this.getClass().getName(), "Header", options, 0, 5, "Footer");
236
237 final String expected = "usage:" + CR +
238 " org.apache.comm" + CR +
239 " ons.cli.bug.Bug" + CR +
240 " CLI162Test" + CR +
241 "Header" + CR +
242 "-x,--extralongarg" + CR +
243 " This description is" + CR +
244 " Long." + CR +
245 "Footer" + CR;
246
247 assertEquals(expected, sw.toString(), "Long arguments did not split as expected");
248 }
249
250 @Test
251 public void testPrintHelpLongLines() {
252
253 final Options commandLineOptions;
254 commandLineOptions = new Options();
255 commandLineOptions.addOption(OPT_HELP, OPT_HELP_LONG, false, "Prints help and quits");
256 commandLineOptions.addOption(OPT_DRIVER, "driver", true, "JDBC driver class name");
257
258 commandLineOptions.addOption(OPT_DRIVER_INFO, "info", false, "Prints driver information and properties. If "
259 + OPT
260 + OPT_CONNECTION
261 + " is not specified, all drivers on the classpath are displayed.");
262
263 commandLineOptions.addOption(OPT_CONNECTION, "url", true, "Connection URL");
264 commandLineOptions.addOption(OPT_USER, "user", true, "A database user name");
265
266 commandLineOptions
267 .addOption(
268 OPT_PASSWORD,
269 OPT_PASSWORD_L,
270 true,
271 "The database password for the user specified with the "
272 + OPT
273 + OPT_USER
274 + " option. You can obfuscate the password with org.mortbay.jetty.security.Password,"
275 + " see https://docs.codehaus.org/display/JETTY/Securing+Passwords");
276
277 commandLineOptions.addOption(OPT_SQL, OPT_SQL_L, true, "Runs SQL or {call stored_procedure(?, ?)} or {?=call function(?, ?)}");
278 commandLineOptions.addOption(OPT_FILE_SFMD, "sfmd", true, "Writes a SFMD file for the given SQL");
279 commandLineOptions.addOption(OPT_FILE_BINDING, "jdbc", true, "Writes a JDBC binding node file for the given SQL");
280 commandLineOptions.addOption(OPT_FILE_JDBC, "node", true, "Writes a JDBC node file for the given SQL (internal debugging)");
281 commandLineOptions.addOption(OPT_WRITE_TO_FILE, "outfile", true, "Writes the SQL output to the given file");
282 commandLineOptions.addOption(OPT_DESCRIPTION, "description", true,
283 "SFMD description. A default description is used if omited. Example: " + OPT + OPT_DESCRIPTION + " \"Runs such and such\"");
284 commandLineOptions.addOption(OPT_INTERACTIVE, "interactive", false,
285 "Runs in interactive mode, reading and writing from the console, 'go' or '/' sends a statement");
286 commandLineOptions.addOption(OPT_TIMING, "printTiming", false, "Prints timing information");
287 commandLineOptions.addOption(OPT_METADATA, "printMetaData", false, "Prints metadata information");
288 commandLineOptions.addOption(OPT_STACK_TRACE, "printStack", false, "Prints stack traces on errors");
289
290 Option option = new Option(OPT_COLUMN_NAMES, "columnNames", true, "Column XML names; default names column labels. Example: "
291 + OPT
292 + OPT_COLUMN_NAMES
293 + " \"cname1 cname2\"");
294
295 commandLineOptions.addOption(option);
296
297 option = new Option(OPT_PARAM_NAMES, "paramNames", true, "Parameter XML names; default names are param1, param2, etc. Example: "
298 + OPT
299 + OPT_PARAM_NAMES
300 + " \"pname1 pname2\"");
301
302 commandLineOptions.addOption(option);
303
304 final OptionGroup pOutTypesOptionGroup = new OptionGroup();
305 final String pOutTypesOptionGroupDoc = OPT + OPT_PARAM_TYPES_INT + " and " + OPT + OPT_PARAM_TYPES_NAME + " are mutually exclusive.";
306 final String typesClassName = Types.class.getName();
307
308 option = new Option(OPT_PARAM_TYPES_INT, "paramTypes", true, "Parameter types from "
309 + typesClassName
310 + ". "
311 + pOutTypesOptionGroupDoc
312 + " Example: "
313 + OPT
314 + OPT_PARAM_TYPES_INT
315 + " \"-10 12\"");
316
317 commandLineOptions.addOption(option);
318
319 option = new Option(OPT_PARAM_TYPES_NAME, "paramTypeNames", true, "Parameter "
320 + typesClassName
321 + " names. "
322 + pOutTypesOptionGroupDoc
323 + " Example: "
324 + OPT
325 + OPT_PARAM_TYPES_NAME
326 + " \"CURSOR VARCHAR\"");
327
328 commandLineOptions.addOption(option);
329 commandLineOptions.addOptionGroup(pOutTypesOptionGroup);
330
331 final OptionGroup modesOptionGroup = new OptionGroup();
332 final String modesOptionGroupDoc = OPT + OPT_PARAM_MODES_INT + " and " + OPT + OPT_PARAM_MODES_NAME + " are mutually exclusive.";
333
334 option = new Option(OPT_PARAM_MODES_INT, "paramModes", true, "Parameters modes ("
335 + ParameterMetaData.parameterModeIn
336 + "=IN, "
337 + ParameterMetaData.parameterModeInOut
338 + "=INOUT, "
339 + ParameterMetaData.parameterModeOut
340 + "=OUT, "
341 + ParameterMetaData.parameterModeUnknown
342 + "=Unknown"
343 + "). "
344 + modesOptionGroupDoc
345 + " Example for 2 parameters, OUT and IN: "
346 + OPT
347 + OPT_PARAM_MODES_INT
348 + " \""
349 + ParameterMetaData.parameterModeOut
350 + " "
351 + ParameterMetaData.parameterModeIn
352 + "\"");
353
354 modesOptionGroup.addOption(option);
355
356 option = new Option(OPT_PARAM_MODES_NAME, "paramModeNames", true, "Parameters mode names ("
357 + PMODES
358 + "). "
359 + modesOptionGroupDoc
360 + " Example for 2 parameters, OUT and IN: "
361 + OPT
362 + OPT_PARAM_MODES_NAME
363 + " \""
364 + PMODE_OUT
365 + " "
366 + PMODE_IN
367 + "\"");
368
369 modesOptionGroup.addOption(option);
370 commandLineOptions.addOptionGroup(modesOptionGroup);
371 option = new Option(null, OPT_TRIM_L, true,
372 "Trims leading and trailing spaces from all column values. Column XML names can be optionally specified to set which columns to trim.");
373 option.setOptionalArg(true);
374 commandLineOptions.addOption(option);
375 option = new Option(OPT_JDBC_TO_SFMD, OPT_JDBC_TO_SFMD_L, true,
376 "Converts the JDBC file in the first argument to an SMFD file specified in the second argument.");
377 option.setArgs(2);
378 commandLineOptions.addOption(option);
379
380 formatter.printHelp(new PrintWriter(sw), HelpFormatter.DEFAULT_WIDTH, this.getClass().getName(), null, commandLineOptions,
381 HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null);
382
383 assertEquals(EXPECTED, sw.toString());
384 }
385
386 }