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