// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html#License /* ******************************************************************************* * Copyright (C) 2007-2015, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* */ package com.ibm.icu.dev.test.duration; import java.math.BigDecimal; import java.text.FieldPosition; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.MissingResourceException; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.DatatypeConstants.Field; import javax.xml.datatype.Duration; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import com.ibm.icu.dev.test.TestFmwk; import com.ibm.icu.text.DurationFormat; import com.ibm.icu.util.Calendar; import com.ibm.icu.util.ULocale; /** * @author srl * */ @RunWith(JUnit4.class) public class ICUDurationTest extends TestFmwk { /** * Allows us to not depend on javax.xml.datatype.DatatypeFactory. * We need just a tiny subset of the Duration API: * The ICU DurationFormat just extracts the field values, * to convert the Duration into an internal Period type. */ private static final class ICUTestDuration extends javax.xml.datatype.Duration { private final int sign; // Duration docs say BigInteger/BigDecimal but // ICU only cares about intValue() and floatValue(). private final Map fields; ICUTestDuration(long millis) { fields = new HashMap(); if (millis > 0) { sign = 1; } else if (millis == 0) { sign = 0; return; } else { sign = -1; millis = -millis; } long d = millis / 86400000L; millis %= 86400000L; if (d > 0) { fields.put(DatatypeConstants.DAYS, d); } long h = millis / 3600000L; millis %= 3600000L; if (h > 0) { fields.put(DatatypeConstants.HOURS, h); } long m = millis / 60000L; millis %= 60000L; if (m > 0) { fields.put(DatatypeConstants.MINUTES, m); } fields.put(DatatypeConstants.SECONDS, (float)millis / 1000); } /** * Pass in negative values for fields not to be set. */ ICUTestDuration(int sgn, int y, int months, int d, int h, int m, float s) { sign = sgn; fields = new HashMap(); if (y >= 0) { fields.put(DatatypeConstants.YEARS, y); } if (months >= 0) { fields.put(DatatypeConstants.MONTHS, months); } if (d >= 0) { fields.put(DatatypeConstants.DAYS, d); } if (h >= 0) { fields.put(DatatypeConstants.HOURS, h); } if (m >= 0) { fields.put(DatatypeConstants.MINUTES, m); } if (s >= 0) { fields.put(DatatypeConstants.SECONDS, s); } } private ICUTestDuration(int sgn, Map f) { sign = sgn; fields = f; } @Override public Duration add(Duration rhs) { throw new UnsupportedOperationException(); } @Override public void addTo(java.util.Calendar calendar) { throw new UnsupportedOperationException(); } @Override public int compare(Duration duration) { throw new UnsupportedOperationException(); } @Override public Number getField(Field field) { return fields.get(field); } @Override public int getSign() { return sign; } @Override public int hashCode() { throw new UnsupportedOperationException(); } @Override public boolean isSet(Field field) { return fields.containsKey(field); } @Override public Duration multiply(BigDecimal factor) { throw new UnsupportedOperationException(); } @Override public Duration negate() { return new ICUTestDuration(-sign, fields); } @Override public Duration normalizeWith(java.util.Calendar startTimeInstant) { throw new UnsupportedOperationException(); } @Override public String toString() { String signString = sign > 0 ? "positive" : sign == 0 ? "zero" : "negative"; return signString + " fields=" + fields; } } private static final ICUTestDuration newDuration(long millis) { return new ICUTestDuration(millis); } private static final ICUTestDuration newDuration(int sgn, int d, int h, int m, float s) { return new ICUTestDuration(sgn, -1, -1, d, h, m, s); } private static final ICUTestDuration newDuration(int sgn, int h, int m, float s) { return new ICUTestDuration(sgn, -1, -1, -1, h, m, s); } private static final ICUTestDuration newDuration(int sgn, float s) { return new ICUTestDuration(sgn, -1, -1, -1, -1, -1, s); } /** * */ public ICUDurationTest() { } /** * Basic test */ @Test public void TestBasics() { DurationFormat df; String expect; String formatted; df = DurationFormat.getInstance(new ULocale("it")); formatted = df.formatDurationFromNow(4096); expect = "fra quattro secondi"; if(!expect.equals(formatted)) { errln("Expected " + expect + " but got " + formatted); } else { logln("format duration -> " + formatted); } formatted = df.formatDurationFromNowTo(new Date(0)); Calendar cal = Calendar.getInstance(); int years = cal.get(Calendar.YEAR) - 1970; // year of Date(0) expect = years + " anni fa"; if(!expect.equals(formatted)) { errln("Expected " + expect + " but got " + formatted); } else { logln("format date -> " + formatted); } formatted = df.formatDurationFrom(1000*3600*24, new Date(0).getTime()); expect = "fra un giorno"; if(!expect.equals(formatted)) { errln("Expected " + expect + " but got " + formatted); } else { logln("format date from -> " + formatted); } formatted = df.format(new Long(1000*3600*24*2)); expect = "fra due giorni"; if(!expect.equals(formatted)) { errln("Expected " + expect + " but got " + formatted); } else { logln("format long obj -> " + formatted); } } @Test public void TestSimpleXMLDuration() { Duration d; DurationFormat df; String out; String expected; String expected2; // test 1 d = newDuration(1, 2, 46, 40); // "PT2H46M40S" df = DurationFormat.getInstance(new ULocale("en")); expected = "2 hours, 46 minutes, and 40 seconds"; out = df.format(d); if(out.equals(expected)) { logln("out=expected: " + expected + " from " + d); } else { errln("FAIL: got " + out + " wanted " + expected + " from " + d); } // test 2 d = newDuration(10000); df = DurationFormat.getInstance(new ULocale("en")); expected = "10 seconds"; out = df.format(d); if(out.equals(expected)) { logln("out=expected: " + expected + " from " + d); } else { errln("FAIL: got " + out + " wanted " + expected + " from " + d); } // test 3 d = newDuration(1, 0, 0, 0, 10); // "P0DT0H0M10.0S" df = DurationFormat.getInstance(new ULocale("en")); expected = "10 seconds"; out = df.format(d); if(out.equals(expected)) { logln("out=expected: " + expected + " from " + d); } else { errln("FAIL: got " + out + " wanted " + expected + " from " + d); } // test 4 d = newDuration(86400000); df = DurationFormat.getInstance(new ULocale("en")); expected = "1 day, 0 hours, 0 minutes, and 0 seconds"; expected2 = "1 day and 0 seconds"; // This is the expected result for Windows with IBM JRE6 out = df.format(d); if(out.equals(expected)) { logln("out=expected: " + expected + " from " + d); } else { if(out.equals(expected2)){ logln("WARNING: got " + out + " wanted " + expected + " from " + d); } else{ errln("FAIL: got " + out + " wanted " + expected + " from " + d); } } } @Test public void TestXMLDuration() { final class TestCase { final String localeString; final ULocale locale; final String durationString; final Duration duration; final String expected; TestCase(String loc, String ds, Duration d, String exp) { localeString = loc; locale = new ULocale(loc); durationString = ds; duration = d; expected = exp; } } TestCase cases[] = { new TestCase("en", "PT10.00099S", newDuration(1, 10.00099F), "10 seconds"), new TestCase("en", "#10000", newDuration(10000), "10 seconds"), new TestCase("en", "-PT10.00099S", newDuration(-1, 10.00099F), "10 seconds"), new TestCase("en", "#-10000", newDuration(-10000), "10 seconds"), // from BD req's new TestCase("en", "PT2H46M40S", newDuration(1, 2, 46, 40), "2 hours, 46 minutes, and 40 seconds"), new TestCase("it", "PT2H46M40S", newDuration(1, 2, 46, 40), "due ore, 46 minuti e 40 secondi"), // more cases new TestCase("en", "PT10S", newDuration(1, 10), "10 seconds"), new TestCase("en", "PT88M70S", newDuration(1, -1, 88, 70), "88 minutes and 70 seconds"), new TestCase("en", "PT10.100S", newDuration(1, 10.100F), "10 seconds and 100 milliseconds"), new TestCase("en", "-PT10S", newDuration(-1, 10), "10 seconds"), new TestCase("en", "PT0H5M0S", newDuration(1, 0, 5, 0), "5 minutes and 0 seconds") }; for (TestCase tc : cases) { String loc = tc.localeString; String from = tc.durationString; String to = tc.expected; ULocale locale = tc.locale; Duration d = tc.duration; DurationFormat df = DurationFormat.getInstance(locale); String output = df.format(d); if(output.equals(to)) { logln("SUCCESS: locale: " + loc + ", from " + from + " ["+d.toString()+"] " +" to " + to + "= " + output); } else { logln("FAIL: locale: " + loc + ", from " + from + " ["+d.toString()+"] " +": expected " + to + " got " + output); } } } @Test public void TestBadObjectError() { Runtime r = Runtime.getRuntime(); DurationFormat df = DurationFormat.getInstance(new ULocale("en")); String output = null; try { output = df.format(r); errln("FAIL: did NOT get IllegalArgumentException! Should have. Formatted Runtime as " + output + " ???"); } catch (IllegalArgumentException iae) { logln("PASS: expected: Caught iae: " + iae.toString() ); } // try a second time, because it is a different code path for java < 1.5 try { output = df.format(r); errln("FAIL: [#2] did NOT get IllegalArgumentException! Should have. Formatted Runtime as " + output + " ???"); } catch (IllegalArgumentException iae) { logln("PASS: [#2] expected: Caught iae: " + iae.toString() ); } } @Test public void TestBadLocaleError() { try { DurationFormat df = DurationFormat.getInstance(new ULocale("und")); df.format(new Date()); logln("Should have thrown err."); errln("failed, should have thrown err."); } catch(MissingResourceException mre) { logln("PASS: caught missing resource exception on locale 'und'"); logln(mre.toString()); } } @Test public void TestResourceWithCalendar() { DurationFormat df = DurationFormat.getInstance(new ULocale("th@calendar=buddhist")); // should pass, but return a default formatter for th. if (df == null) { errln("FAIL: null DurationFormat returned."); } } /* Tests the class * DurationFormat */ @Test public void TestDurationFormat(){ @SuppressWarnings("serial") class TestDurationFormat extends DurationFormat { @Override public StringBuffer format(Object object, StringBuffer toAppend, FieldPosition pos) {return null;} @Override public String formatDurationFrom(long duration, long referenceDate) {return null;} @Override public String formatDurationFromNow(long duration) {return null;} @Override public String formatDurationFromNowTo(Date targetDate) {return null;} public TestDurationFormat() {super();} } // Tests the constructor and the following method // public Object parseObject(String source, ParsePosition pos) try{ TestDurationFormat tdf = new TestDurationFormat(); tdf.parseObject("",null); errln("DurationFormat.parseObjet(String,ParsePosition) was " + "to return an exception for an unsupported operation."); } catch(Exception e){} } @Test public void TestFromNowTo() { class TestCase { ULocale locale; int diffInSeconds; String expected; TestCase(ULocale locale, int diffInSeconds, String expected) { this.locale = locale; this.diffInSeconds = diffInSeconds; this.expected = expected; } } TestCase[] testCases = { new TestCase(ULocale.US, 10, "10 seconds from now"), new TestCase(ULocale.US, -10, "10 seconds ago"), new TestCase(ULocale.US, -1800, "30 minutes ago"), new TestCase(ULocale.US, 3600, "1 hour from now"), new TestCase(ULocale.US, 10000, "2 hours from now"), new TestCase(ULocale.US, -20000, "5 hours ago"), new TestCase(ULocale.FRANCE, -1800, "il y a 30 minutes"), new TestCase(ULocale.ITALY, 10000, "fra due ore"), }; final long delayMS = 10; // Safe margin - 10 milliseconds // See the comments below for (TestCase test : testCases) { DurationFormat df = DurationFormat.getInstance(test.locale); long target = System.currentTimeMillis() + test.diffInSeconds * 1000; // Need some adjustment because time difference is recalculated in // formatDurationFromNowTo method. target = test.diffInSeconds > 0 ? target + delayMS : target - delayMS; Date d = new Date(target); String result = df.formatDurationFromNowTo(d); assertEquals("TestFromNowTo (" + test.locale + ", " + test.diffInSeconds + "sec)", test.expected, result); } } }