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         https://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.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.IOException;
28  import java.math.BigDecimal;
29  import java.math.BigInteger;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.nio.file.InvalidPathException;
33  import java.nio.file.Path;
34  import java.nio.file.Paths;
35  import java.text.DateFormat;
36  import java.text.SimpleDateFormat;
37  import java.time.Instant;
38  import java.util.ArrayList;
39  import java.util.Date;
40  import java.util.List;
41  import java.util.Map;
42  import java.util.stream.Stream;
43  
44  import org.apache.commons.io.FileUtils;
45  import org.apache.commons.io.IOUtils;
46  import org.junit.jupiter.api.Test;
47  import org.junit.jupiter.params.ParameterizedTest;
48  import org.junit.jupiter.params.provider.Arguments;
49  import org.junit.jupiter.params.provider.MethodSource;
50  import org.junitpioneer.jupiter.DefaultLocale;
51  
52  class TypeHandlerTest {
53  
54      /** Used for Class and Object creation tests. */
55      public static class Instantiable {
56  
57          @Override
58          public boolean equals(final Object arg0) {
59              return arg0 instanceof Instantiable;
60          }
61  
62          @Override
63          public int hashCode() {
64              return 1;
65          }
66      }
67  
68      /** Used for Class and Object negative creation tests */
69      public static final class NotInstantiable {
70          private NotInstantiable() {
71          }
72  
73      }
74  
75      /** Always returns the same Path. */
76      private static final Converter<Path, InvalidPathException> PATH_CONVERTER = s -> Paths.get("foo");
77  
78      private static Stream<Date> createDateFixtures() {
79          return Stream.of(Date.from(Instant.EPOCH), Date.from(Instant.ofEpochSecond(0)), Date.from(Instant.ofEpochSecond(40_000)));
80  
81      }
82  
83      private static Stream<Arguments> createValueTestParameters() throws MalformedURLException {
84          // force the PatternOptionBuilder to load / modify the TypeHandler table.
85          @SuppressWarnings("unused")
86          final Class<?> loadStatic = PatternOptionBuilder.FILES_VALUE;
87          // reset the type handler table.
88          // TypeHandler.resetConverters();
89          final List<Arguments> list = new ArrayList<>();
90  
91          /*
92           * Dates calculated from strings are dependent upon configuration and environment settings for the machine on which the test is running. To avoid this
93           * problem, convert the time into a string and then unparse that using the converter. This produces strings that always match the correct time zone.
94           */
95          final Date date = new Date(1023400137000L);
96          final DateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
97  
98          list.add(Arguments.of(Instantiable.class.getName(), PatternOptionBuilder.CLASS_VALUE, Instantiable.class));
99          list.add(Arguments.of("what ever", PatternOptionBuilder.CLASS_VALUE, ParseException.class));
100 
101         list.add(Arguments.of("what ever", PatternOptionBuilder.DATE_VALUE, ParseException.class));
102         list.add(Arguments.of(dateFormat.format(date), PatternOptionBuilder.DATE_VALUE, date));
103         list.add(Arguments.of("Jun 06 17:48:57 EDT 2002", PatternOptionBuilder.DATE_VALUE, ParseException.class));
104 
105         list.add(Arguments.of("non-existing.file", PatternOptionBuilder.EXISTING_FILE_VALUE, ParseException.class));
106 
107         list.add(Arguments.of("some-file.txt", PatternOptionBuilder.FILE_VALUE, new File("some-file.txt")));
108 
109         list.add(Arguments.of("some-path.txt", Path.class, new File("some-path.txt").toPath()));
110 
111         // the PatternOptionBuilder.FILES_VALUE is not registered so it should just return the string
112         list.add(Arguments.of("some.files", PatternOptionBuilder.FILES_VALUE, "some.files"));
113 
114         list.add(Arguments.of("just-a-string", Integer.class, ParseException.class));
115         list.add(Arguments.of("5", Integer.class, 5));
116         list.add(Arguments.of("5.5", Integer.class, ParseException.class));
117         list.add(Arguments.of(Long.toString(Long.MAX_VALUE), Integer.class, ParseException.class));
118 
119         list.add(Arguments.of("just-a-string", Long.class, ParseException.class));
120         list.add(Arguments.of("5", Long.class, 5L));
121         list.add(Arguments.of("5.5", Long.class, ParseException.class));
122 
123         list.add(Arguments.of("just-a-string", Short.class, ParseException.class));
124         list.add(Arguments.of("5", Short.class, (short) 5));
125         list.add(Arguments.of("5.5", Short.class, ParseException.class));
126         list.add(Arguments.of(Integer.toString(Integer.MAX_VALUE), Short.class, ParseException.class));
127 
128         list.add(Arguments.of("just-a-string", Byte.class, ParseException.class));
129         list.add(Arguments.of("5", Byte.class, (byte) 5));
130         list.add(Arguments.of("5.5", Byte.class, ParseException.class));
131         list.add(Arguments.of(Short.toString(Short.MAX_VALUE), Byte.class, ParseException.class));
132 
133         list.add(Arguments.of("just-a-string", Character.class, 'j'));
134         list.add(Arguments.of("5", Character.class, '5'));
135         list.add(Arguments.of("5.5", Character.class, '5'));
136         list.add(Arguments.of("\\u0124", Character.class, Character.toChars(0x0124)[0]));
137 
138         list.add(Arguments.of("just-a-string", Double.class, ParseException.class));
139         list.add(Arguments.of("5", Double.class, 5d));
140         list.add(Arguments.of("5.5", Double.class, 5.5));
141 
142         list.add(Arguments.of("just-a-string", Float.class, ParseException.class));
143         list.add(Arguments.of("5", Float.class, 5f));
144         list.add(Arguments.of("5.5", Float.class, 5.5f));
145         list.add(Arguments.of(Double.toString(Double.MAX_VALUE), Float.class, Float.POSITIVE_INFINITY));
146 
147         list.add(Arguments.of("just-a-string", BigInteger.class, ParseException.class));
148         list.add(Arguments.of("5", BigInteger.class, new BigInteger("5")));
149         list.add(Arguments.of("5.5", BigInteger.class, ParseException.class));
150 
151         list.add(Arguments.of("just-a-string", BigDecimal.class, ParseException.class));
152         list.add(Arguments.of("5", BigDecimal.class, new BigDecimal("5")));
153         list.add(Arguments.of("5.5", BigDecimal.class, new BigDecimal(5.5)));
154 
155         list.add(Arguments.of("1.5", PatternOptionBuilder.NUMBER_VALUE, Double.valueOf(1.5)));
156         list.add(Arguments.of("15", PatternOptionBuilder.NUMBER_VALUE, Long.valueOf(15)));
157         list.add(Arguments.of("not a number", PatternOptionBuilder.NUMBER_VALUE, ParseException.class));
158 
159         list.add(Arguments.of(Instantiable.class.getName(), PatternOptionBuilder.OBJECT_VALUE, new Instantiable()));
160         list.add(Arguments.of(NotInstantiable.class.getName(), PatternOptionBuilder.OBJECT_VALUE, ParseException.class));
161         list.add(Arguments.of("unknown", PatternOptionBuilder.OBJECT_VALUE, ParseException.class));
162 
163         list.add(Arguments.of("String", PatternOptionBuilder.STRING_VALUE, "String"));
164 
165         final String urlString = "https://commons.apache.org";
166         list.add(Arguments.of(urlString, PatternOptionBuilder.URL_VALUE, new URL(urlString)));
167         list.add(Arguments.of("Malformed-url", PatternOptionBuilder.URL_VALUE, ParseException.class));
168 
169         return list.stream();
170 
171     }
172 
173     @Test
174     void testCreateClass() throws ParseException {
175         final Class<?> cls = getClass();
176         assertEquals(cls, TypeHandler.createClass(cls.getName()));
177     }
178 
179     @ParameterizedTest
180     @MethodSource("createDateFixtures")
181     @DefaultLocale(language = "en", country = "US")
182     void testCreateDate(final Date date) {
183         assertEquals(date, TypeHandler.createDate(date.toString()));
184     }
185 
186     @Test
187     void testCreateFile() {
188         final File file = FileUtils.current().getAbsoluteFile();
189         assertEquals(file, TypeHandler.createFile(file.toString()));
190     }
191 
192     @Test
193     void testCreateFiles() {
194         assertThrows(UnsupportedOperationException.class, () -> TypeHandler.createFiles(null));
195     }
196 
197     @Test
198     void testCreateNumber() throws ParseException {
199         assertEquals(0L, TypeHandler.createNumber("0"));
200         assertEquals(0d, TypeHandler.createNumber("0.0"));
201     }
202 
203     @Test
204     void testCreateObject() throws ParseException {
205         assertTrue(TypeHandler.createObject(Date.class.getName()) instanceof Date);
206     }
207 
208     @Test
209     void testCreateURL() throws ParseException, MalformedURLException {
210         final URL file = Paths.get("").toAbsolutePath().toUri().toURL();
211         assertEquals(file, TypeHandler.createURL(file.toString()));
212     }
213 
214     @SuppressWarnings("unchecked")
215     @ParameterizedTest(name = "{0} as {1}")
216     @MethodSource("createValueTestParameters")
217     void testCreateValue(final String str, final Class<?> type, final Object expected) throws Exception {
218         @SuppressWarnings("cast")
219         final Object objectApiTest = type; // KEEP this cast
220         if (expected instanceof Class<?> && Throwable.class.isAssignableFrom((Class<?>) expected)) {
221             assertThrows((Class<Throwable>) expected, () -> TypeHandler.createValue(str, type));
222             assertThrows((Class<Throwable>) expected, () -> TypeHandler.createValue(str, objectApiTest));
223         } else {
224             assertEquals(expected, TypeHandler.createValue(str, type));
225             assertEquals(expected, TypeHandler.createValue(str, objectApiTest));
226         }
227     }
228 
229     @Test
230     void testCreateValueExistingFile() throws Exception {
231         try (FileInputStream result = TypeHandler.createValue("src/test/resources/org/apache/commons/cli/existing-readable.file",
232                 PatternOptionBuilder.EXISTING_FILE_VALUE)) {
233             assertNotNull(result);
234         }
235     }
236 
237     /* proof of equality for later tests */
238     @Test
239     void testnstantiableEquals() {
240         assertEquals(new Instantiable(), new Instantiable());
241     }
242 
243     @Test
244     void testOpenFile() throws ParseException, IOException {
245         try (FileInputStream fis = TypeHandler.openFile("src/test/resources/org/apache/commons/cli/existing-readable.file")) {
246             IOUtils.consume(fis);
247         }
248     }
249 
250     @Test
251     void testRegister() {
252         final Map<Class<?>, Converter<?, ? extends Throwable>> map = TypeHandler.createDefaultMap();
253         final TypeHandler typeHandler = new TypeHandler(map);
254         assertEquals(Converter.PATH, typeHandler.getConverter(Path.class));
255         try {
256             map.put(Path.class, PATH_CONVERTER);
257             assertEquals(PATH_CONVERTER, typeHandler.getConverter(Path.class));
258         } finally {
259             map.remove(Path.class);
260             assertEquals(Converter.DEFAULT, typeHandler.getConverter(Path.class));
261         }
262     }
263 
264 }