View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.cli;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertNotEquals;
23  import static org.junit.jupiter.api.Assertions.assertNotSame;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  
28  import java.io.ByteArrayInputStream;
29  import java.io.ByteArrayOutputStream;
30  import java.io.IOException;
31  import java.io.ObjectInputStream;
32  import java.io.ObjectOutputStream;
33  
34  import org.junit.jupiter.api.Test;
35  
36  public class OptionTest {
37  
38      private static final class DefaultOption extends Option {
39          private static final long serialVersionUID = 1L;
40  
41          private final String defaultValue;
42  
43          DefaultOption(final String opt, final String description, final String defaultValue) throws IllegalArgumentException {
44              super(opt, true, description);
45              this.defaultValue = defaultValue;
46          }
47  
48          @Override
49          public String getValue() {
50              return super.getValue() != null ? super.getValue() : defaultValue;
51          }
52      }
53  
54      private static final class TestOption extends Option {
55          private static final long serialVersionUID = 1L;
56  
57          TestOption(final String opt, final boolean hasArg, final String description) throws IllegalArgumentException {
58              super(opt, hasArg, description);
59          }
60  
61          @Override
62          public boolean addValue(final String value) {
63              processValue(value);
64              return true;
65          }
66      }
67  
68      private static void checkOption(final Option option, final String opt, final String description, final String longOpt, final int numArgs,
69              final String argName, final boolean required, final boolean optionalArg, final char valueSeparator, final Class<?> cls, final String deprecatedDesc,
70              final Boolean deprecatedForRemoval, final String deprecatedSince) {
71          assertEquals(opt, option.getOpt());
72          assertEquals(description, option.getDescription());
73          assertEquals(longOpt, option.getLongOpt());
74          assertEquals(numArgs, option.getArgs());
75          assertEquals(argName, option.getArgName());
76          assertEquals(required, option.isRequired());
77  
78          assertEquals(optionalArg, option.hasOptionalArg());
79          assertEquals(numArgs > 0, option.hasArg());
80          assertEquals(numArgs > 0, option.acceptsArg());
81          assertEquals(valueSeparator, option.getValueSeparator());
82          assertEquals(cls, option.getType());
83          if (deprecatedDesc != null) {
84              assertEquals(deprecatedDesc, option.getDeprecated().getDescription());
85          }
86          if (deprecatedForRemoval != null) {
87              assertEquals(deprecatedForRemoval, option.getDeprecated().isForRemoval());
88          }
89          if (deprecatedSince != null) {
90              assertEquals(deprecatedSince, option.getDeprecated().getSince());
91          }
92      }
93  
94      private Option roundTrip(final Option o) throws IOException, ClassNotFoundException {
95          final ByteArrayOutputStream baos = new ByteArrayOutputStream();
96          final ObjectOutputStream oos = new ObjectOutputStream(baos);
97          oos.writeObject(o);
98          final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
99          final ObjectInputStream ois = new ObjectInputStream(bais);
100         return (Option) ois.readObject();
101     }
102 
103     @Test
104     public void testAddValue() {
105         final Option option = new Option("f", null);
106         assertThrows(UnsupportedOperationException.class, () -> option.addValue(""));
107         assertThrows(IllegalArgumentException.class, () -> option.processValue(""));
108     }
109 
110     @Test
111     public void testBuilderEmpty() {
112         assertThrows(IllegalArgumentException.class, () -> Option.builder().build());
113     }
114 
115     @Test
116     public void testBuilderInsufficientParams1() {
117         assertThrows(IllegalArgumentException.class, () -> Option.builder().desc("desc").build());
118     }
119 
120     @Test
121     public void testBuilderInsufficientParams2() {
122         assertThrows(IllegalArgumentException.class, () -> Option.builder(null).desc("desc").build());
123     }
124 
125     @Test
126     public void testBuilderInvalidOptionName0() {
127         assertThrows(IllegalArgumentException.class, () -> Option.builder().option(null).build());
128         assertThrows(IllegalArgumentException.class, () -> Option.builder().option(""));
129         assertThrows(IllegalArgumentException.class, () -> Option.builder().option(" "));
130     }
131 
132     @Test
133     public void testBuilderInvalidOptionName1() {
134         assertThrows(IllegalArgumentException.class, () -> Option.builder().option("invalid?"));
135     }
136 
137     @Test
138     public void testBuilderInvalidOptionName2() {
139         assertThrows(IllegalArgumentException.class, () -> Option.builder().option("invalid@"));
140     }
141 
142     @Test
143     public void testBuilderInvalidOptionName3() {
144         assertThrows(IllegalArgumentException.class, () -> Option.builder("invalid?"));
145     }
146 
147     @Test
148     public void testBuilderInvalidOptionName4() {
149         assertThrows(IllegalArgumentException.class, () -> Option.builder("invalid@"));
150     }
151 
152     @Test
153     public void testBuilderMethods() {
154         final char defaultSeparator = (char) 0;
155 
156         checkOption(Option.builder("a").desc("desc").build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class, null,
157                 null, null);
158         checkOption(Option.builder("a").desc("desc").build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class, null,
159                 null, null);
160         checkOption(Option.builder("a").desc("desc").longOpt("aaa").build(), "a", "desc", "aaa", Option.UNINITIALIZED, null, false, false, defaultSeparator,
161                 String.class, null, null, null);
162         checkOption(Option.builder("a").desc("desc").hasArg(true).build(), "a", "desc", null, 1, null, false, false, defaultSeparator, String.class, null, null,
163                 null);
164         checkOption(Option.builder("a").desc("desc").hasArg(false).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
165                 String.class, null, null, null);
166         checkOption(Option.builder("a").desc("desc").hasArg(true).build(), "a", "desc", null, 1, null, false, false, defaultSeparator, String.class, null, null,
167                 null);
168         checkOption(Option.builder("a").desc("desc").numberOfArgs(3).build(), "a", "desc", null, 3, null, false, false, defaultSeparator, String.class, null,
169                 null, null);
170         checkOption(Option.builder("a").desc("desc").required(true).build(), "a", "desc", null, Option.UNINITIALIZED, null, true, false, defaultSeparator,
171                 String.class, null, null, null);
172         checkOption(Option.builder("a").desc("desc").required(false).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
173                 String.class, null, null, null);
174 
175         checkOption(Option.builder("a").desc("desc").argName("arg1").build(), "a", "desc", null, Option.UNINITIALIZED, "arg1", false, false, defaultSeparator,
176                 String.class, null, null, null);
177         checkOption(Option.builder("a").desc("desc").optionalArg(false).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
178                 String.class, null, null, null);
179         checkOption(Option.builder("a").desc("desc").optionalArg(true).build(), "a", "desc", null, 1, null, false, true, defaultSeparator, String.class, null,
180                 null, null);
181         checkOption(Option.builder("a").desc("desc").valueSeparator(':').build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, ':',
182                 String.class, null, null, null);
183         checkOption(Option.builder("a").desc("desc").type(Integer.class).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
184                 Integer.class, null, null, null);
185         checkOption(Option.builder("a").desc("desc").type(null).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator,
186                 String.class, null, null, null);
187         checkOption(Option.builder().option("a").desc("desc").type(Integer.class).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false,
188                 defaultSeparator, Integer.class, null, null, null);
189         // Deprecated
190         checkOption(Option.builder().option("a").desc("desc").type(Integer.class).deprecated().build(), "a", "desc", null, Option.UNINITIALIZED, null, false,
191                 false, defaultSeparator, Integer.class, "", false, "");
192         checkOption(Option.builder().option("a").desc("desc").type(Integer.class).deprecated(DeprecatedAttributes.builder().get()).build(), "a", "desc", null,
193                 Option.UNINITIALIZED, null, false, false, defaultSeparator, Integer.class, "", false, "");
194         checkOption(Option.builder().option("a").desc("desc").type(Integer.class).deprecated(DeprecatedAttributes.builder().setDescription("X").get()).build(),
195                 "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, Integer.class, "X", false, "");
196         checkOption(
197                 Option.builder().option("a").desc("desc").type(Integer.class)
198                         .deprecated(DeprecatedAttributes.builder().setDescription("X").setForRemoval(true).get()).build(),
199                 "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, Integer.class, "X", true, "");
200         checkOption(
201                 Option.builder().option("a").desc("desc").type(Integer.class)
202                         .deprecated(DeprecatedAttributes.builder().setDescription("X").setForRemoval(true).setSince("2.0").get()).build(),
203                 "a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, Integer.class, "X", true, "2.0");
204     }
205 
206     @Test
207     public void testClear() {
208         final TestOption option = new TestOption("x", true, "");
209         assertEquals(0, option.getValuesList().size());
210         option.addValue("a");
211         assertEquals(1, option.getValuesList().size());
212         option.clearValues();
213         assertEquals(0, option.getValuesList().size());
214     }
215 
216     // See https://issues.apache.org/jira/browse/CLI-21
217     @Test
218     public void testClone() {
219         final TestOption a = new TestOption("a", true, "");
220         final TestOption b = (TestOption) a.clone();
221         assertEquals(a, b);
222         assertNotSame(a, b);
223         a.setDescription("a");
224         assertEquals("", b.getDescription());
225         b.setArgs(2);
226         b.addValue("b1");
227         b.addValue("b2");
228         assertEquals(1, a.getArgs());
229         assertEquals(0, a.getValuesList().size());
230         assertEquals(2, b.getValues().length);
231     }
232 
233     @Test
234     public void testEquals() {
235         final Option option1a = new Option("1", null);
236         final Option option1b = new Option("1", null);
237         final Option option2 = new Option("2", null);
238         assertEquals(option1a, option1a);
239         assertEquals(option1a, option1b);
240         assertEquals(option1b, option1a);
241         assertNotEquals(option1a, option2);
242         assertNotEquals(option1b, option2);
243         assertNotEquals(option2, option1a);
244         assertNotEquals(option2, "");
245     }
246 
247     @Test
248     public void testGetValue() {
249         final Option option = new Option("f", null);
250         option.setArgs(Option.UNLIMITED_VALUES);
251 
252         assertEquals("default", option.getValue("default"));
253         assertNull(option.getValue(0));
254 
255         option.processValue("foo");
256 
257         assertEquals("foo", option.getValue());
258         assertEquals("foo", option.getValue(0));
259         assertEquals("foo", option.getValue("default"));
260     }
261 
262     @Test
263     public void testHasArgName() {
264         final Option option = new Option("f", null);
265 
266         option.setArgName(null);
267         assertFalse(option.hasArgName());
268 
269         option.setArgName("");
270         assertFalse(option.hasArgName());
271 
272         option.setArgName("file");
273         assertTrue(option.hasArgName());
274     }
275 
276     @Test
277     public void testHasArgs() {
278         final Option option = new Option("f", null);
279 
280         option.setArgs(0);
281         assertFalse(option.hasArgs());
282 
283         option.setArgs(1);
284         assertFalse(option.hasArgs());
285 
286         option.setArgs(10);
287         assertTrue(option.hasArgs());
288 
289         option.setArgs(Option.UNLIMITED_VALUES);
290         assertTrue(option.hasArgs());
291 
292         option.setArgs(Option.UNINITIALIZED);
293         assertFalse(option.hasArgs());
294     }
295 
296     @Test
297     public void testHashCode() {
298         assertNotEquals(Option.builder("test").build().hashCode(), Option.builder("test2").build().hashCode());
299         assertNotEquals(Option.builder("test").build().hashCode(), Option.builder().longOpt("test").build().hashCode());
300         assertNotEquals(Option.builder("test").build().hashCode(), Option.builder("test").longOpt("long test").build().hashCode());
301     }
302 
303     @Test
304     public void testSerialization() throws IOException, ClassNotFoundException {
305         final Option option = Option.builder("o").type(TypeHandlerTest.Instantiable.class).build();
306         assertEquals(Converter.DEFAULT, option.getConverter());
307         Option roundtrip = roundTrip(option);
308         assertEquals(Converter.DEFAULT, roundtrip.getConverter());
309         // verify unregistered class converters and verifiers get reset to default.
310         // converters are NOT Serializable, use a serialization proxy if you want that.
311         option.setConverter(Converter.DATE);
312         roundtrip = roundTrip(option);
313         assertEquals(Converter.DEFAULT, roundtrip.getConverter());
314         // verify registered class converters and verifiers do not get reset to default.
315         // converters are NOT Serializable, use a serialization proxy if you want that.
316         // verify earlier values still set.
317         assertEquals(Converter.DATE, option.getConverter());
318         roundtrip = roundTrip(option);
319         assertEquals(Converter.DEFAULT, roundtrip.getConverter());
320     }
321 
322     @Test
323     public void testSubclass() {
324         final Option option = new DefaultOption("f", "file", "myfile.txt");
325         final Option clone = (Option) option.clone();
326         assertEquals("myfile.txt", clone.getValue());
327         assertEquals(DefaultOption.class, clone.getClass());
328     }
329 
330     @Test
331     public void testTypeClass() {
332         final Option option = new Option("f", null);
333         assertEquals(String.class, option.getType());
334         option.setType(CharSequence.class);
335         assertEquals(CharSequence.class, option.getType());
336     }
337 
338     @Test
339     public void testTypeObject() {
340         final Option option = new Option("f", null);
341         assertEquals(String.class, option.getType());
342         @SuppressWarnings("cast")
343         final Object type = CharSequence.class; // Do NOT remove cast
344         option.setType(type);
345         assertEquals(CharSequence.class, option.getType());
346     }
347 }