From 2f07fafd42c85598980f7a40846c92046ad71f31 Mon Sep 17 00:00:00 2001 From: John DeRegnaucourt Date: Sat, 23 Mar 2024 10:37:53 -0400 Subject: [PATCH] Added more negative testing for Converter. --- .../cedarsoftware/util/ArrayUtilities.java | 2 - .../com/cedarsoftware/util/DateUtilities.java | 4 +- .../util/convert/CharArrayConversions.java | 3 - .../util/convert/CharBufferConversions.java | 4 +- .../util/convert/NumberConversions.java | 13 -- .../util/convert/StringConversions.java | 192 ++++++++---------- .../util/convert/ConverterEverythingTest.java | 141 ++++++++----- .../util/convert/ConverterTest.java | 32 ++- .../util/convert/StringConversionsTests.java | 6 +- 9 files changed, 213 insertions(+), 184 deletions(-) diff --git a/src/main/java/com/cedarsoftware/util/ArrayUtilities.java b/src/main/java/com/cedarsoftware/util/ArrayUtilities.java index ffcc2871..02f89842 100644 --- a/src/main/java/com/cedarsoftware/util/ArrayUtilities.java +++ b/src/main/java/com/cedarsoftware/util/ArrayUtilities.java @@ -31,11 +31,9 @@ public final class ArrayUtilities * Immutable common arrays. */ public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; - public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; public static final char[] EMPTY_CHAR_ARRAY = new char[0]; public static final Character[] EMPTY_CHARACTER_ARRAY = new Character[0]; - public static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; /** diff --git a/src/main/java/com/cedarsoftware/util/DateUtilities.java b/src/main/java/com/cedarsoftware/util/DateUtilities.java index c663e2f3..b5016ea3 100644 --- a/src/main/java/com/cedarsoftware/util/DateUtilities.java +++ b/src/main/java/com/cedarsoftware/util/DateUtilities.java @@ -179,11 +179,11 @@ public static Date parseDate(String dateStr) { * passed in, null will be returned. */ public static ZonedDateTime parseDate(String dateStr, ZoneId defaultZoneId, boolean ensureDateTimeAlone) { - if (StringUtilities.isEmpty(dateStr)) { + dateStr = StringUtilities.trimToNull(dateStr); + if (dateStr == null) { return null; } Convention.throwIfNull(defaultZoneId, "ZoneId cannot be null. Use ZoneId.of(\"America/New_York\"), ZoneId.systemDefault(), etc."); - dateStr = dateStr.trim(); if (allDigits.matcher(dateStr).matches()) { return Instant.ofEpochMilli(Long.parseLong(dateStr)).atZone(defaultZoneId); diff --git a/src/main/java/com/cedarsoftware/util/convert/CharArrayConversions.java b/src/main/java/com/cedarsoftware/util/convert/CharArrayConversions.java index 7eed9bf3..4a4becd8 100644 --- a/src/main/java/com/cedarsoftware/util/convert/CharArrayConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/CharArrayConversions.java @@ -56,9 +56,6 @@ static byte[] toByteArray(Object from, Converter converter) { static char[] toCharArray(Object from, Converter converter) { char[] chars = (char[])from; - if (chars == null) { - return null; - } return Arrays.copyOf(chars, chars.length); } } diff --git a/src/main/java/com/cedarsoftware/util/convert/CharBufferConversions.java b/src/main/java/com/cedarsoftware/util/convert/CharBufferConversions.java index f9d2264c..3420a102 100644 --- a/src/main/java/com/cedarsoftware/util/convert/CharBufferConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/CharBufferConversions.java @@ -27,7 +27,7 @@ final class CharBufferConversions { private CharBufferConversions() {} static CharBuffer toCharBuffer(Object from, Converter converter) { - // Create a readonly buffer so we aren't changing + // Create a readonly buffer, so we aren't changing // the original buffers mark and position when // working with this buffer. This could be inefficient // if constantly fed with writeable buffers so should be documented @@ -49,7 +49,7 @@ static String toString(Object from, Converter converter) { static char[] toCharArray(Object from, Converter converter) { CharBuffer buffer = toCharBuffer(from, converter); - if (buffer == null || !buffer.hasRemaining()) { + if (!buffer.hasRemaining()) { return EMPTY_CHAR_ARRAY; } diff --git a/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java b/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java index d2e447ed..9c1f7b68 100644 --- a/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/NumberConversions.java @@ -16,8 +16,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import com.cedarsoftware.util.StringUtilities; - /** * @author Kenny Partlow (kpartlow@gmail.com) *
@@ -118,10 +116,6 @@ static AtomicInteger toAtomicInteger(Object from, Converter converter) { return new AtomicInteger(toInt(from, converter)); } - static BigDecimal toBigDecimal(Object from, Converter converter) { - return new BigDecimal(StringUtilities.trimToEmpty(from.toString())); - } - static AtomicBoolean toAtomicBoolean(Object from, Converter converter) { return new AtomicBoolean(toLong(from, converter) != 0); } @@ -152,10 +146,6 @@ static boolean isBigDecimalNotZero(Object from, Converter converter) { return ((BigDecimal)from).compareTo(BigDecimal.ZERO) != 0; } - static BigInteger toBigInteger(Object from, Converter converter) { - return new BigInteger(StringUtilities.trimToEmpty(from.toString())); - } - /** * @param from - object that is a number to be converted to char * @param converter - instance of converter mappings to use. @@ -212,9 +202,6 @@ static OffsetDateTime toOffsetDateTime(Object from, Converter converter) { } static Year toYear(Object from, Converter converter) { - if (from instanceof Byte) { - throw new IllegalArgumentException("Cannot convert Byte to Year, not enough precision."); - } Number number = (Number) from; return Year.of(number.shortValue()); } diff --git a/src/main/java/com/cedarsoftware/util/convert/StringConversions.java b/src/main/java/com/cedarsoftware/util/convert/StringConversions.java index 19c845d8..4a9957d6 100644 --- a/src/main/java/com/cedarsoftware/util/convert/StringConversions.java +++ b/src/main/java/com/cedarsoftware/util/convert/StringConversions.java @@ -27,7 +27,6 @@ import java.util.Calendar; import java.util.Date; import java.util.Locale; -import java.util.Optional; import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; @@ -41,7 +40,6 @@ import com.cedarsoftware.util.StringUtilities; import static com.cedarsoftware.util.ArrayUtilities.EMPTY_BYTE_ARRAY; -import static com.cedarsoftware.util.ArrayUtilities.EMPTY_CHARACTER_ARRAY; import static com.cedarsoftware.util.ArrayUtilities.EMPTY_CHAR_ARRAY; /** @@ -79,9 +77,9 @@ static String asString(Object from) { } static Byte toByte(Object from, Converter converter) { - String str = StringUtilities.trimToEmpty((String) from); - if (str.isEmpty()) { - return CommonValues.BYTE_ZERO; + String str = (String) from; + if (StringUtilities.isEmpty(str)) { + return (byte)0; } try { return Byte.valueOf(str); @@ -95,9 +93,9 @@ static Byte toByte(Object from, Converter converter) { } static Short toShort(Object from, Converter converter) { - String str = StringUtilities.trimToEmpty((String) from); - if (str.isEmpty()) { - return CommonValues.SHORT_ZERO; + String str = (String) from; + if (StringUtilities.isEmpty(str)) { + return (short)0; } try { return Short.valueOf(str); @@ -111,9 +109,9 @@ static Short toShort(Object from, Converter converter) { } static Integer toInt(Object from, Converter converter) { - String str = StringUtilities.trimToEmpty(asString(from)); - if (str.isEmpty()) { - return CommonValues.INTEGER_ZERO; + String str = (String) from; + if (StringUtilities.isEmpty(str)) { + return 0; } try { return Integer.valueOf(str); @@ -127,9 +125,9 @@ static Integer toInt(Object from, Converter converter) { } static Long toLong(Object from, Converter converter) { - String str = StringUtilities.trimToEmpty(asString(from)); - if (str.isEmpty()) { - return CommonValues.LONG_ZERO; + String str = (String) from; + if (StringUtilities.isEmpty(str)) { + return 0L; } try { @@ -157,9 +155,9 @@ private static Long toLong(String s, BigDecimal low, BigDecimal high) { } static Float toFloat(Object from, Converter converter) { - String str = StringUtilities.trimToEmpty(asString(from)); - if (str.isEmpty()) { - return CommonValues.FLOAT_ZERO; + String str = (String) from; + if (StringUtilities.isEmpty(str)) { + return 0f; } try { return Float.valueOf(str); @@ -169,9 +167,9 @@ static Float toFloat(Object from, Converter converter) { } static Double toDouble(Object from, Converter converter) { - String str = StringUtilities.trimToEmpty(asString(from)); - if (str.isEmpty()) { - return CommonValues.DOUBLE_ZERO; + String str = (String) from; + if (StringUtilities.isEmpty(str)) { + return 0.0; } try { return Double.valueOf(str); @@ -181,7 +179,7 @@ static Double toDouble(Object from, Converter converter) { } static AtomicBoolean toAtomicBoolean(Object from, Converter converter) { - return new AtomicBoolean(toBoolean(asString(from), converter)); + return new AtomicBoolean(toBoolean(from, converter)); } static AtomicInteger toAtomicInteger(Object from, Converter converter) { @@ -193,11 +191,7 @@ static AtomicLong toAtomicLong(Object from, Converter converter) { } static Boolean toBoolean(Object from, Converter converter) { - String from1 = asString(from); - String str = StringUtilities.trimToEmpty(from1); - if (str.isEmpty()) { - return false; - } + String str = (String) from; // faster equals check "true" and "false" if ("true".equals(str)) { return true; @@ -209,8 +203,8 @@ static Boolean toBoolean(Object from, Converter converter) { static char toCharacter(Object from, Converter converter) { String str = (String)from; - if (str == null || str.isEmpty()) { - return CommonValues.CHARACTER_ZERO; + if (str.isEmpty()) { + return (char)0; } if (str.length() == 1) { return str.charAt(0); @@ -224,8 +218,8 @@ static char toCharacter(Object from, Converter converter) { } static BigInteger toBigInteger(Object from, Converter converter) { - String str = StringUtilities.trimToNull(asString(from)); - if (str == null) { + String str = (String) from; + if (StringUtilities.isEmpty(str)) { return BigInteger.ZERO; } try { @@ -237,8 +231,8 @@ static BigInteger toBigInteger(Object from, Converter converter) { } static BigDecimal toBigDecimal(Object from, Converter converter) { - String str = StringUtilities.trimToEmpty(asString(from)); - if (str.isEmpty()) { + String str = (String) from; + if (StringUtilities.isEmpty(str)) { return BigDecimal.ZERO; } try { @@ -249,12 +243,12 @@ static BigDecimal toBigDecimal(Object from, Converter converter) { } static URL toURL(Object from, Converter converter) { - String str = StringUtilities.trimToNull(asString(from)); - if (str == null) { + String str = (String) from; + if (StringUtilities.isEmpty(str)) { return null; } try { - URI uri = URI.create((String) from); + URI uri = URI.create(str); return uri.toURL(); } catch (Exception e) { throw new IllegalArgumentException("Cannot convert String '" + str + "' to URL", e); @@ -262,8 +256,8 @@ static URL toURL(Object from, Converter converter) { } static URI toURI(Object from, Converter converter) { - String str = StringUtilities.trimToNull(asString(from)); - if (str == null) { + String str = (String) from; + if (StringUtilities.isEmpty(str)) { return null; } return URI.create((String) from); @@ -353,8 +347,7 @@ static Period toPeriod(Object from, Converter converter) { } static Date toDate(Object from, Converter converter) { - String strDate = (String) from; - ZonedDateTime zdt = DateUtilities.parseDate(strDate, converter.getOptions().getZoneId(), true); + ZonedDateTime zdt = toZonedDateTime(from, converter); if (zdt == null) { return null; } @@ -382,104 +375,93 @@ static TimeZone toTimeZone(Object from, Converter converter) { static Calendar toCalendar(Object from, Converter converter) { String calStr = (String) from; - if (StringUtilities.isEmpty(calStr)) { + ZonedDateTime zdt = toZonedDateTime(from, converter); + if (zdt == null) { return null; } - ZonedDateTime zdt = DateUtilities.parseDate(calStr, converter.getOptions().getZoneId(), true); ZonedDateTime zdtUser = zdt.withZoneSameInstant(converter.getOptions().getZoneId()); - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(zdtUser.getZone())); cal.setTimeInMillis(zdtUser.toInstant().toEpochMilli()); return cal; } static LocalDate toLocalDate(Object from, Converter converter) { - return parseDate(from, converter).map(ZonedDateTime::toLocalDate).orElse(null); + ZonedDateTime zdt = toZonedDateTime(from, converter); + if (zdt == null) { + return null; + } + return zdt.toLocalDate(); } static LocalDateTime toLocalDateTime(Object from, Converter converter) { - return parseDate(from, converter).map(ZonedDateTime::toLocalDateTime).orElse(null); - } - - static LocalTime toLocalTime(Object from, Converter converter) { - String str = StringUtilities.trimToNull(asString(from)); - if (str == null) { + ZonedDateTime zdt = toZonedDateTime(from, converter); + if (zdt == null) { return null; } + return zdt.toLocalDateTime(); + } + static LocalTime toLocalTime(Object from, Converter converter) { + String str = (String) from; try { return LocalTime.parse(str); } catch (Exception e) { - return parseDate(str, converter).map(ZonedDateTime::toLocalTime).orElse(null); + ZonedDateTime zdt = toZonedDateTime(str, converter); + if (zdt == null) { + return null; + } + return zdt.toLocalTime(); } } static Locale toLocale(Object from, Converter converter) { - String str = StringUtilities.trimToNull(asString(from)); - if (str == null) { + String str = (String)from; + if (StringUtilities.isEmpty(str)) { return null; } - return Locale.forLanguageTag(str); } - private static Optional parseDate(Object from, Converter converter) { - String str = StringUtilities.trimToNull(asString(from)); - - if (str == null) { - return Optional.empty(); - } - - ZonedDateTime zonedDateTime = DateUtilities.parseDate(str, converter.getOptions().getZoneId(), true); - - if (zonedDateTime == null) { - return Optional.empty(); - } - - return Optional.of(zonedDateTime); - } - - static ZonedDateTime toZonedDateTime(Object from, Converter converter) { - return parseDate(from, converter).orElse(null); + return DateUtilities.parseDate((String)from, converter.getOptions().getZoneId(), true); } static ZoneId toZoneId(Object from, Converter converter) { - String s = StringUtilities.trimToNull(asString(from)); - if (s == null) { + String str = (String) from; + if (StringUtilities.isEmpty(str)) { return null; } try { - return ZoneId.of(s); + return ZoneId.of(str); } catch (Exception e) { - throw new IllegalArgumentException("Unknown time-zone ID: '" + s + "'", e); + throw new IllegalArgumentException("Unknown time-zone ID: '" + str + "'", e); } } static ZoneOffset toZoneOffset(Object from, Converter converter) { - String s = StringUtilities.trimToNull(asString(from)); - if (s == null) { + String str = (String)from; + if (StringUtilities.isEmpty(str)) { return null; } try { - return ZoneOffset.of(s); + return ZoneOffset.of(str); } catch (Exception e) { - throw new IllegalArgumentException("Unknown time-zone offset: '" + s + "'"); + throw new IllegalArgumentException("Unknown time-zone offset: '" + str + "'"); } } static OffsetDateTime toOffsetDateTime(Object from, Converter converter) { - return parseDate(from, converter).map(ZonedDateTime::toOffsetDateTime).orElse(null); - } - - static OffsetTime toOffsetTime(Object from, Converter converter) { - String s = StringUtilities.trimToNull(asString(from)); - if (s == null) { + ZonedDateTime zdt = toZonedDateTime(from, converter); + if (zdt == null) { return null; } + return zdt.toOffsetDateTime(); + } + static OffsetTime toOffsetTime(Object from, Converter converter) { + String str = (String) from; try { - return OffsetTime.parse(s, DateTimeFormatter.ISO_OFFSET_TIME); + return OffsetTime.parse(str, DateTimeFormatter.ISO_OFFSET_TIME); } catch (Exception e) { try { OffsetDateTime dateTime = toOffsetDateTime(from, converter); @@ -488,39 +470,31 @@ static OffsetTime toOffsetTime(Object from, Converter converter) { } return dateTime.toOffsetTime(); } catch (Exception ex) { - throw new IllegalArgumentException("Unable to parse '" + s + "' as an OffsetTime", e); + throw new IllegalArgumentException("Unable to parse '" + str + "' as an OffsetTime", e); } } } static Instant toInstant(Object from, Converter converter) { - String s = (String)from; - if (StringUtilities.isEmpty(s)) { + ZonedDateTime zdt = toZonedDateTime(from, converter); + if (zdt == null) { return null; } - try { - return Instant.parse(s); - } catch (Exception e) { - return parseDate(s, converter).map(ZonedDateTime::toInstant).orElse(null); - } + return zdt.toInstant(); } static char[] toCharArray(Object from, Converter converter) { - String s = asString(from); + String str = from.toString(); - if (s == null || s.isEmpty()) { + if (StringUtilities.isEmpty(str)) { return EMPTY_CHAR_ARRAY; } - return s.toCharArray(); + return str.toCharArray(); } static Character[] toCharacterArray(Object from, Converter converter) { CharSequence s = (CharSequence) from; - - if (s == null) { - return EMPTY_CHARACTER_ARRAY; - } int len = s.length(); Character[] ca = new Character[len]; for (int i=0; i < len; i++) { @@ -560,19 +534,19 @@ static StringBuilder toStringBuilder(Object from, Converter converter) { } static Year toYear(Object from, Converter converter) { - String s = StringUtilities.trimToNull(asString(from)); - if (s == null) { - return null; - } - + String str = (String) from; try { - return Year.of(Integer.parseInt(s)); + str = StringUtilities.trimToNull(str); + return Year.of(Integer.parseInt(str)); } catch (Exception e) { try { - ZonedDateTime zdt = DateUtilities.parseDate(s, converter.getOptions().getZoneId(), true); + ZonedDateTime zdt = toZonedDateTime(from, converter); + if (zdt == null) { + return null; + } return Year.of(zdt.getYear()); } catch (Exception ex) { - throw new IllegalArgumentException("Unable to parse 4-digit year from '" + s + "'", e); + throw new IllegalArgumentException("Unable to parse 4-digit year from '" + str + "'", e); } } } diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java index de7b9737..03959321 100644 --- a/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java +++ b/src/test/java/com/cedarsoftware/util/convert/ConverterEverythingTest.java @@ -58,9 +58,12 @@ import static com.cedarsoftware.util.convert.MapConversions.DATE; import static com.cedarsoftware.util.convert.MapConversions.DATE_TIME; import static com.cedarsoftware.util.convert.MapConversions.EPOCH_MILLIS; +import static com.cedarsoftware.util.convert.MapConversions.HOURS; import static com.cedarsoftware.util.convert.MapConversions.LANGUAGE; +import static com.cedarsoftware.util.convert.MapConversions.MINUTES; import static com.cedarsoftware.util.convert.MapConversions.NANOS; import static com.cedarsoftware.util.convert.MapConversions.SCRIPT; +import static com.cedarsoftware.util.convert.MapConversions.SECONDS; import static com.cedarsoftware.util.convert.MapConversions.TIME; import static com.cedarsoftware.util.convert.MapConversions.URI_KEY; import static com.cedarsoftware.util.convert.MapConversions.URL_KEY; @@ -237,6 +240,7 @@ private static void loadUuidTests() { {new BigInteger("18446744073709551617"), new UUID(1L, 1L), true}, {new BigInteger("170141183460469231722463931679029329919"), new UUID(Long.MAX_VALUE, Long.MAX_VALUE), true}, {BigInteger.ZERO, UUID.fromString("00000000-0000-0000-0000-000000000000"), true}, + {BigInteger.valueOf(-1), new IllegalArgumentException("Cannot convert a negative number [-1] to a UUID")}, {BigInteger.valueOf(1), UUID.fromString("00000000-0000-0000-0000-000000000001"), true}, {new BigInteger("18446744073709551617"), UUID.fromString("00000000-0000-0001-0000-000000000001"), true}, {new BigInteger("340282366920938463463374607431768211455"), UUID.fromString("ffffffff-ffff-ffff-ffff-ffffffffffff"), true}, @@ -260,6 +264,7 @@ private static void loadUrlTests() { {toURL("https://chat.openai.com"), toURL("https://chat.openai.com")}, }); TEST_DB.put(pair(String.class, URL.class), new Object[][]{ + {"", null}, {"https://domain.com", toURL("https://domain.com"), true}, {"http://localhost", toURL("http://localhost"), true}, {"http://localhost:8080", toURL("http://localhost:8080"), true}, @@ -283,6 +288,9 @@ private static void loadUrlTests() { { mapOf(URL_KEY, "https://domain.com"), toURL("https://domain.com"), true}, { mapOf(URL_KEY, "bad earl"), new IllegalArgumentException("Cannot convert Map to URL. Malformed URL: 'bad earl'")}, }); + TEST_DB.put(pair(URI.class, URL.class), new Object[][]{ + {toURI("urn:isbn:0451450523"), new IllegalArgumentException("Unable to convert URI to URL")}, + }); } /** @@ -293,9 +301,10 @@ private static void loadUriTests() { {null, null} }); TEST_DB.put(pair(URI.class, URI.class), new Object[][]{ - {toURI("https://chat.openai.com"), toURI("https://chat.openai.com")}, + {toURI("https://chat.openai.com"), toURI("https://chat.openai.com"), true}, }); TEST_DB.put(pair(String.class, URI.class), new Object[][]{ + {"", null}, {"https://domain.com", toURI("https://domain.com"), true}, {"http://localhost", toURI("http://localhost"), true}, {"http://localhost:8080", toURI("http://localhost:8080"), true}, @@ -323,6 +332,9 @@ private static void loadUriTests() { { (Supplier) () -> { try {return new URL("https://domain.com");} catch(Exception e){return null;} }, toURI("https://domain.com"), true}, + { (Supplier) () -> { + try {return new URL("http://example.com/query?param=value with spaces");} catch(Exception e){return null;} + }, new IllegalArgumentException("Unable to convert URL to URI")}, }); } @@ -337,6 +349,7 @@ private static void loadTimeZoneTests() { {TimeZone.getTimeZone("GMT"), TimeZone.getTimeZone("GMT")}, }); TEST_DB.put(pair(String.class, TimeZone.class), new Object[][]{ + {"", null}, {"America/New_York", TimeZone.getTimeZone("America/New_York"), true}, {"EST", TimeZone.getTimeZone("EST"), true}, {"GMT+05:00", TimeZone.getTimeZone(ZoneId.of("+05:00")), true}, @@ -361,6 +374,8 @@ private static void loadOffsetTimeTests() { {OffsetTime.parse("00:00+09:00"), OffsetTime.parse("00:00:00+09:00")}, }); TEST_DB.put(pair(String.class, OffsetTime.class), new Object[][]{ + {"", null}, + {"2024-03-23T03:51", OffsetTime.parse("03:51+09:00")}, {"10:15:30+01:00", OffsetTime.parse("10:15:30+01:00"), true}, {"10:15:30+01:00:59", OffsetTime.parse("10:15:30+01:00:59"), true}, {"10:15:30+01:00.001", new IllegalArgumentException("Unable to parse '10:15:30+01:00.001' as an OffsetTime")}, @@ -372,6 +387,7 @@ private static void loadOffsetTimeTests() { {mapOf(TIME, "00:00-09:00"), OffsetTime.parse("00:00-09:00"), true}, {mapOf(TIME, "00:00:00+09:00"), OffsetTime.parse("00:00+09:00")}, // no reverse {mapOf(TIME, "00:00:00+09:00:00"), OffsetTime.parse("00:00+09:00")}, // no reverse + {mapOf(TIME, "garbage"), new IllegalArgumentException("Unable to parse OffsetTime: garbage")}, // no reverse }); TEST_DB.put(pair(OffsetDateTime.class, OffsetTime.class), new Object[][]{ {odt("1969-12-31T23:59:59.999999999Z"), OffsetTime.parse("08:59:59.999999999+09:00")}, @@ -390,6 +406,13 @@ private static void loadLocaleTests() { TEST_DB.put(pair(Locale.class, Locale.class), new Object[][]{ {new Locale.Builder().setLanguage("en").setRegion("US").build(), new Locale.Builder().setLanguage("en").setRegion("US").build()}, }); + TEST_DB.put(pair(String.class, Locale.class), new Object[][]{ + { "", null}, + { "en-Latn-US-POSIX", new Locale.Builder().setLanguage("en").setRegion("US").setScript("Latn").setVariant("POSIX").build(), true}, + { "en-Latn-US", new Locale.Builder().setLanguage("en").setRegion("US").setScript("Latn").build(), true}, + { "en-US", new Locale.Builder().setLanguage("en").setRegion("US").build(), true}, + { "en", new Locale.Builder().setLanguage("en").build(), true}, + }); TEST_DB.put(pair(Map.class, Locale.class), new Object[][]{ {mapOf(LANGUAGE, "joker 75", COUNTRY, "US", SCRIPT, "Latn", VARIANT, "POSIX"), new IllegalArgumentException("joker")}, {mapOf(LANGUAGE, "en", COUNTRY, "Amerika", SCRIPT, "Latn", VARIANT, "POSIX"), new IllegalArgumentException("Amerika")}, @@ -462,12 +485,6 @@ private static void loadMapTests() { return map; }, true}, }); - TEST_DB.put(pair(Date.class, Map.class), new Object[][] { - { new Date(-1L), mapOf(EPOCH_MILLIS, -1L, DATE, "1970-01-01", TIME, "08:59:59.999", ZONE, TOKYO_Z.toString()), true}, - { new Date(0L), mapOf(EPOCH_MILLIS, 0L, DATE, "1970-01-01", TIME, "09:00", ZONE, TOKYO_Z.toString()), true}, - { new Date(1L), mapOf(EPOCH_MILLIS, 1L, DATE, "1970-01-01", TIME, "09:00:00.001", ZONE, TOKYO_Z.toString()), true}, - { new Date(1710714535152L), mapOf(EPOCH_MILLIS, 1710714535152L, DATE, "2024-03-18", TIME, "07:28:55.152", ZONE, TOKYO_Z.toString()), true}, - }); TEST_DB.put(pair(java.sql.Date.class, Map.class), new Object[][] { { new java.sql.Date(-1L), mapOf(EPOCH_MILLIS, -1L, DATE, "1970-01-01", TIME, "08:59:59.999", ZONE, TOKYO_Z.toString()), true}, { new java.sql.Date(0L), mapOf(EPOCH_MILLIS, 0L, DATE, "1970-01-01", TIME, "09:00", ZONE, TOKYO_Z.toString()), true}, @@ -772,20 +789,12 @@ private static void loadStringTests() { {new byte[]{(byte) 0xf0, (byte) 0x9f, (byte) 0x8d, (byte) 0xba}, "\uD83C\uDF7A", true}, // beer mug, byte[] treated as UTF-8. {new byte[]{(byte) 65, (byte) 66, (byte) 67, (byte) 68}, "ABCD", true} }); - TEST_DB.put(pair(char[].class, String.class), new Object[][]{ - {new char[]{'A', 'B', 'C', 'D'}, "ABCD", true} - }); TEST_DB.put(pair(Character[].class, String.class), new Object[][]{ {new Character[]{'A', 'B', 'C', 'D'}, "ABCD", true} }); TEST_DB.put(pair(ByteBuffer.class, String.class), new Object[][]{ {ByteBuffer.wrap(new byte[]{(byte) 0x30, (byte) 0x31, (byte) 0x32, (byte) 0x33}), "0123", true} }); - TEST_DB.put(pair(Date.class, String.class), new Object[][]{ - {new Date(-1), "1970-01-01T08:59:59.999+09:00", true}, // Tokyo (set in options - defaults to system when not set explicitly) - {new Date(0), "1970-01-01T09:00:00.000+09:00", true}, - {new Date(1), "1970-01-01T09:00:00.001+09:00", true}, - }); TEST_DB.put(pair(java.sql.Date.class, String.class), new Object[][]{ {new java.sql.Date(-1), "1970-01-01T08:59:59.999+09:00", true}, // Tokyo (set in options - defaults to system when not set explicitly) {new java.sql.Date(0), "1970-01-01T09:00:00.000+09:00", true}, @@ -796,30 +805,11 @@ private static void loadStringTests() { {new Timestamp(0), "1970-01-01T09:00:00.000+09:00", true}, {new Timestamp(1), "1970-01-01T09:00:00.001+09:00", true}, }); - TEST_DB.put(pair(LocalDate.class, String.class), new Object[][]{ - {LocalDate.parse("1969-12-31"), "1969-12-31", true}, - {LocalDate.parse("1970-01-01"), "1970-01-01", true}, - {LocalDate.parse("2024-03-20"), "2024-03-20", true}, - }); - TEST_DB.put(pair(LocalTime.class, String.class), new Object[][]{ - {LocalTime.parse("16:20:00"), "16:20:00", true}, - {LocalTime.of(9, 26), "09:26:00", true}, - {LocalTime.of(9, 26, 17), "09:26:17", true}, - {LocalTime.of(9, 26, 17, 1), "09:26:17.000000001", true}, - }); - TEST_DB.put(pair(LocalDateTime.class, String.class), new Object[][]{ - {ldt("1965-12-31T16:20:00"), "1965-12-31T16:20:00", true}, - }); TEST_DB.put(pair(ZonedDateTime.class, String.class), new Object[][]{ {ZonedDateTime.parse("1969-12-31T23:59:59.999999999Z"), "1969-12-31T23:59:59.999999999Z", true}, {ZonedDateTime.parse("1970-01-01T00:00:00Z"), "1970-01-01T00:00:00Z", true}, {ZonedDateTime.parse("1970-01-01T00:00:00.000000001Z"), "1970-01-01T00:00:00.000000001Z", true}, }); - TEST_DB.put(pair(Calendar.class, String.class), new Object[][]{ - {cal(-1), "1970-01-01T08:59:59.999+09:00", true}, - {cal(0), "1970-01-01T09:00:00.000+09:00", true}, - {cal(1), "1970-01-01T09:00:00.001+09:00", true}, - }); TEST_DB.put(pair(Number.class, String.class), new Object[][]{ {(byte) 1, "1"}, {(short) 2, "2"}, @@ -843,18 +833,9 @@ private static void loadStringTests() { TEST_DB.put(pair(String.class, String.class), new Object[][]{ {"same", "same"}, }); - TEST_DB.put(pair(OffsetDateTime.class, String.class), new Object[][]{ - {OffsetDateTime.parse("2024-02-10T10:15:07+01:00"), "2024-02-10T10:15:07+01:00", true}, - }); TEST_DB.put(pair(String.class, StringBuffer.class), new Object[][]{ {"same", new StringBuffer("same")}, }); - TEST_DB.put(pair(Locale.class, String.class), new Object[][]{ - { new Locale.Builder().setLanguage("en").setRegion("US").setScript("Latn").setVariant("POSIX").build(), "en-Latn-US-POSIX", true}, - { new Locale.Builder().setLanguage("en").setRegion("US").setScript("Latn").build(), "en-Latn-US", true}, - { new Locale.Builder().setLanguage("en").setRegion("US").build(), "en-US", true}, - { new Locale.Builder().setLanguage("en").build(), "en", true}, - }); } /** @@ -869,21 +850,25 @@ private static void loadZoneOffsetTests() { {ZoneOffset.of("+5"), ZoneOffset.of("+05:00")}, }); TEST_DB.put(pair(String.class, ZoneOffset.class), new Object[][]{ + {"", null}, {"-00:00", ZoneOffset.of("+00:00")}, {"-05:00", ZoneOffset.of("-05:00"), true}, {"+5", ZoneOffset.of("+05:00")}, {"+05:00:01", ZoneOffset.of("+05:00:01"), true}, + {"05:00:01", new IllegalArgumentException("Unknown time-zone offset: '05:00:01'")}, {"America/New_York", new IllegalArgumentException("Unknown time-zone offset: 'America/New_York'")}, }); TEST_DB.put(pair(Map.class, ZoneOffset.class), new Object[][]{ + {mapOf(HOURS, 5, MINUTES, 30, SECONDS, 16), ZoneOffset.of("+05:30:16"), true}, + {mapOf(HOURS, 5, MINUTES, 30, SECONDS, 16), ZoneOffset.of("+05:30:16"), true}, {mapOf("_v", "-10"), ZoneOffset.of("-10:00")}, - {mapOf("hours", -10L), ZoneOffset.of("-10:00")}, - {mapOf("hours", -10, "minutes", 0), ZoneOffset.of("-10:00"), true}, + {mapOf(HOURS, -10L), ZoneOffset.of("-10:00")}, + {mapOf(HOURS, -10, MINUTES, 0), ZoneOffset.of("-10:00"), true}, {mapOf("hrs", -10L, "mins", "0"), new IllegalArgumentException("Map to ZoneOffset the map must include one of the following: [hours, minutes, seconds], [_v], or [value]")}, - {mapOf("hours", -10L, "minutes", "0", "seconds", 0), ZoneOffset.of("-10:00")}, - {mapOf("hours", "-10", "minutes", (byte) -15, "seconds", "-1"), ZoneOffset.of("-10:15:01")}, - {mapOf("hours", "10", "minutes", (byte) 15, "seconds", true), ZoneOffset.of("+10:15:01")}, - {mapOf("hours", mapOf("_v", "10"), "minutes", mapOf("_v", (byte) 15), "seconds", mapOf("_v", true)), ZoneOffset.of("+10:15:01")}, // full recursion + {mapOf(HOURS, -10L, MINUTES, "0", SECONDS, 0), ZoneOffset.of("-10:00")}, + {mapOf(HOURS, "-10", MINUTES, (byte) -15, SECONDS, "-1"), ZoneOffset.of("-10:15:01")}, + {mapOf(HOURS, "10", MINUTES, (byte) 15, SECONDS, true), ZoneOffset.of("+10:15:01")}, + {mapOf(HOURS, mapOf("_v", "10"), MINUTES, mapOf("_v", (byte) 15), SECONDS, mapOf("_v", true)), ZoneOffset.of("+10:15:01")}, // full recursion }); } @@ -1003,6 +988,10 @@ private static void loadLocalDateTimeTests() { {LocalDate.parse("1970-01-01"), ldt("1970-01-01T00:00:00"), true}, {LocalDate.parse("1970-01-02"), ldt("1970-01-02T00:00:00"), true}, }); + TEST_DB.put(pair(String.class, LocalDateTime.class), new Object[][]{ + {"", null}, + {"1965-12-31T16:20:00", ldt("1965-12-31T16:20:00"), true}, + }); } /** @@ -1119,6 +1108,14 @@ private static void loadLocalTimeTests() { {zdt("1970-01-01T00:00Z"), LocalTime.parse("09:00")}, {zdt("1970-01-01T00:00:00.000000001Z"), LocalTime.parse("09:00:00.000000001")}, }); + TEST_DB.put(pair(String.class, LocalTime.class), new Object[][]{ + {"", null}, + {"2024-03-23T03:35", LocalTime.parse("03:35")}, + {"16:20:00", LocalTime.parse("16:20:00"), true}, + {"09:26:00", LocalTime.of(9, 26), true}, + {"09:26:17", LocalTime.of(9, 26, 17), true}, + {"09:26:17.000000001", LocalTime.of(9, 26, 17, 1), true}, + }); TEST_DB.put(pair(Map.class, LocalTime.class), new Object[][] { {mapOf(TIME, "00:00"), LocalTime.parse("00:00:00.000000000"), true}, {mapOf(TIME, "00:00:00.000000001"), LocalTime.parse("00:00:00.000000001"), true}, @@ -1196,6 +1193,12 @@ private static void loadLocalDateTests() { {OffsetDateTime.parse("1970-01-01T00:00:00+09:00"), LocalDate.parse("1970-01-01"), true }, {OffsetDateTime.parse("1970-01-02T00:00:00+09:00"), LocalDate.parse("1970-01-02"), true }, }); + TEST_DB.put(pair(String.class, LocalDate.class), new Object[][]{ + { "", null}, + {"1969-12-31", LocalDate.parse("1969-12-31"), true}, + {"1970-01-01", LocalDate.parse("1970-01-01"), true}, + {"2024-03-20", LocalDate.parse("2024-03-20"), true}, + }); TEST_DB.put(pair(Map.class, LocalDate.class), new Object[][] { {mapOf(DATE, "1969-12-31"), LocalDate.parse("1969-12-31"), true}, {mapOf(DATE, "1970-01-01"), LocalDate.parse("1970-01-01"), true}, @@ -1326,6 +1329,7 @@ private static void loadZoneIdTests() { {TOKYO_Z, TOKYO_Z}, }); TEST_DB.put(pair(String.class, ZoneId.class), new Object[][]{ + {"", null}, {"America/New_York", NY_Z, true}, {"Asia/Tokyo", TOKYO_Z, true}, {"America/Cincinnati", new IllegalArgumentException("Unknown time-zone ID: 'America/Cincinnati'")}, @@ -1361,6 +1365,8 @@ private static void loadYearTests() { {Year.of(1970), Year.of(1970), true}, }); TEST_DB.put(pair(String.class, Year.class), new Object[][]{ + {"", null}, + {"2024-03-23T04:10", Year.of(2024)}, {"1970", Year.of(1970), true}, {"1999", Year.of(1999), true}, {"2000", Year.of(2000), true}, @@ -1453,6 +1459,7 @@ private static void loadYearMonthTests() { {YearMonth.of(1999, 6), YearMonth.of(1999, 6), true}, }); TEST_DB.put(pair(String.class, YearMonth.class), new Object[][]{ + {"", null}, {"2024-01", YearMonth.of(2024, 1), true}, {"2024-1", new IllegalArgumentException("Unable to extract Year-Month from string: 2024-1")}, {"2024-1-1", YearMonth.of(2024, 1)}, @@ -1487,6 +1494,7 @@ private static void loadMonthDayTests() { {MonthDay.of(6, 30), MonthDay.of(6, 30)}, }); TEST_DB.put(pair(String.class, MonthDay.class), new Object[][]{ + {"", null}, {"1-1", MonthDay.of(1, 1)}, {"01-01", MonthDay.of(1, 1)}, {"--01-01", MonthDay.of(1, 1), true}, @@ -1496,6 +1504,7 @@ private static void loadMonthDayTests() { {"-12-31", new IllegalArgumentException("Unable to extract Month-Day from string: -12-31")}, {"6-30", MonthDay.of(6, 30)}, {"06-30", MonthDay.of(6, 30)}, + {"2024-06-30", MonthDay.of(6, 30)}, {"--06-30", MonthDay.of(6, 30), true}, {"--6-30", new IllegalArgumentException("Unable to extract Month-Day from string: --6-30")}, }); @@ -1565,6 +1574,10 @@ private static void loadOffsetDateTimeTests() { {zdt("1970-01-01T00:00:00.000000001Z"), odt("1970-01-01T00:00:00.000000001Z"), true}, {zdt("2024-03-20T21:18:05.123456Z"), odt("2024-03-20T21:18:05.123456Z"), true}, }); + TEST_DB.put(pair(String.class, OffsetDateTime.class), new Object[][]{ + {"", null}, + {"2024-02-10T10:15:07+01:00", OffsetDateTime.parse("2024-02-10T10:15:07+01:00"), true}, + }); } /** @@ -1778,6 +1791,23 @@ private static void loadDateTests() { {odt("1970-01-01T00:00:00.001Z"), new Date(1), true}, {odt("1970-01-01T00:00:00.999Z"), new Date(999), true}, }); + TEST_DB.put(pair(String.class, Date.class), new Object[][]{ + {"", null}, + {"1970-01-01T08:59:59.999+09:00", new Date(-1), true}, // Tokyo (set in options - defaults to system when not set explicitly) + {"1970-01-01T09:00:00.000+09:00", new Date(0), true}, + {"1970-01-01T09:00:00.001+09:00", new Date(1), true}, + }); + TEST_DB.put(pair(Map.class, Date.class), new Object[][] { + { mapOf(EPOCH_MILLIS, -1L, DATE, "1970-01-01", TIME, "08:59:59.999", ZONE, TOKYO_Z.toString()), new Date(-1L), true}, + { mapOf(EPOCH_MILLIS, 0L, DATE, "1970-01-01", TIME, "09:00", ZONE, TOKYO_Z.toString()), new Date(0L), true}, + { mapOf(EPOCH_MILLIS, 1L, DATE, "1970-01-01", TIME, "09:00:00.001", ZONE, TOKYO_Z.toString()), new Date(1L), true}, + { mapOf(EPOCH_MILLIS, 1710714535152L, DATE, "2024-03-18", TIME, "07:28:55.152", ZONE, TOKYO_Z.toString()), new Date(1710714535152L), true}, + { mapOf(EPOCH_MILLIS, "bad date", DATE, "2024-03-18", TIME, "07:28:55.152", ZONE, TOKYO_Z.toString()), new IllegalArgumentException("Unable to parse: bad date")}, + { mapOf(DATE, "1970-01-01", TIME, "09:00:00", ZONE, TOKYO_Z.toString()), new Date(0)}, + { mapOf(DATE, "bad date", TIME, "09:00:00", ZONE, TOKYO_Z.toString()), new IllegalArgumentException("Unable to parse: bad date")}, + { mapOf(DATE, "1970-01-01", TIME, "bad time", ZONE, TOKYO_Z.toString()), new IllegalArgumentException("Unable to parse: bad time")}, + { mapOf(DATE, "1970-01-01", TIME, "09:00:00", ZONE, "bad zone"), new IllegalArgumentException("Unknown time-zone ID: 'bad zone'")}, + }); } /** @@ -1859,6 +1889,12 @@ private static void loadCalendarTests() { {odt("1970-01-01T00:00Z"), cal(0), true}, {odt("1970-01-01T00:00:00.001Z"), cal(1), true}, }); + TEST_DB.put(pair(String.class, Calendar.class), new Object[][]{ + { "", null}, + {"1970-01-01T08:59:59.999+09:00", cal(-1), true}, + {"1970-01-01T09:00:00.000+09:00", cal(0), true}, + {"1970-01-01T09:00:00.001+09:00", cal(1), true}, + }); } /** @@ -2001,6 +2037,7 @@ private static void loadBigDecimalTests() { {Instant.parse("1970-01-02T00:00:00.000000001Z"), new BigDecimal("86400.000000001"), true}, }); TEST_DB.put(pair(String.class, BigDecimal.class), new Object[][]{ + {"", BigDecimal.ZERO}, {"-1", new BigDecimal("-1"), true}, {"-1", new BigDecimal("-1.0"), true}, {"0", BigDecimal.ZERO, true}, @@ -2261,6 +2298,7 @@ private static void loadCharacterTests() { {mapOf("_v", 65536), new IllegalArgumentException("Value '65536' out of range to be converted to character")}, }); TEST_DB.put(pair(String.class, Character.class), new Object[][]{ + {"", (char) 0}, {" ", (char) 32, true}, {"0", '0', true}, {"1", '1', true}, @@ -3464,6 +3502,7 @@ private static void loadCharArrayTest() { {ByteBuffer.wrap(new byte[] {'h', 'i'}), new char[] {'h', 'i'}, true}, }); TEST_DB.put(pair(CharBuffer.class, char[].class), new Object[][]{ + {CharBuffer.wrap(new char[] {}), new char[] {}, true}, {CharBuffer.wrap(new char[] {'h', 'i'}), new char[] {'h', 'i'}, true}, }); TEST_DB.put(pair(StringBuffer.class, char[].class), new Object[][]{ @@ -3472,6 +3511,10 @@ private static void loadCharArrayTest() { TEST_DB.put(pair(StringBuilder.class, char[].class), new Object[][]{ {new StringBuilder("hi"), new char[] {'h', 'i'}, true}, }); + TEST_DB.put(pair(String.class, char[].class), new Object[][]{ + {"", new char[]{}, true}, + {"ABCD", new char[]{'A', 'B', 'C', 'D'}, true}, + }); } /** diff --git a/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java b/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java index c56e5cb7..c67f9db2 100644 --- a/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java +++ b/src/test/java/com/cedarsoftware/util/convert/ConverterTest.java @@ -4139,7 +4139,7 @@ private static Stream emptyStringTypes_withSameAsReturns() { void testEmptyStringToType_whereTypeReturnsSpecificObject(Object value, Class type, Object expected) { Object converted = this.converter.convert(value, type); - assertThat(converted).isSameAs(expected); + assertEquals(converted, expected); } private static Stream emptyStringTypes_notSameObject() { @@ -4298,6 +4298,36 @@ void testForExceptionsThatAreNotIllegalArgument() { } + @Test + void testNullCharArray() + { + char[] x = converter.convert(null, char[].class); + assertNull(x); + } + + @Test + void testAPIsAreEqual() + { + assertEquals(converter.allSupportedConversions().size(), converter.getSupportedConversions().size()); + } + + @Test + void testIsConversionSupportedFor() + { + assert converter.isConversionSupportedFor(byte.class, Byte.class); + assert converter.isConversionSupportedFor(Date.class, long.class); + assert converter.isConversionSupportedFor(long.class, Date.class); + assert converter.isConversionSupportedFor(GregorianCalendar.class, ZonedDateTime.class); + } + + @Test + void testNullTypeInput() + { + assertThatThrownBy(() -> converter.convert("foo", null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("toType cannot be null"); + } + private ConverterOptions createCharsetOptions(final Charset charset) { return new ConverterOptions() { @Override diff --git a/src/test/java/com/cedarsoftware/util/convert/StringConversionsTests.java b/src/test/java/com/cedarsoftware/util/convert/StringConversionsTests.java index 5631f4c6..32ebc62c 100644 --- a/src/test/java/com/cedarsoftware/util/convert/StringConversionsTests.java +++ b/src/test/java/com/cedarsoftware/util/convert/StringConversionsTests.java @@ -41,9 +41,9 @@ void testClassCompliance() throws Exception { private static Stream toYear_withParseableParams() { return Stream.of( - Arguments.of("1999"), - Arguments.of("\t1999\r\n"), - Arguments.of(" 1999 ") +// Arguments.of("1999"), + Arguments.of("\t1999\r\n") +// Arguments.of(" 1999 ") ); }