/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.types.aksql.aktypes; import com.foundationdb.server.types.aksql.aktypes.AkInterval.AkIntervalMonthsFormat; import com.foundationdb.server.types.aksql.aktypes.AkInterval.AkIntervalSecondsFormat; import com.foundationdb.server.types.aksql.aktypes.AkInterval.IntervalFormat; import com.google.common.collect.Sets; import org.junit.AfterClass; import org.junit.Test; import java.util.EnumSet; import java.util.Set; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public final class AkIntervalTest { @Test public void conversionsForLong() { long nanos = new TimeBuilder() .add(3, TimeUnit.DAYS) .add(5, TimeUnit.HOURS) .add(13, TimeUnit.MINUTES) .add(17, TimeUnit.SECONDS) .add(43, TimeUnit.MICROSECONDS) .add(666, TimeUnit.NANOSECONDS) // will be truncated, since we're working with micros .getNanos(); long raw = AkInterval.secondsRawFrom(nanos, TimeUnit.NANOSECONDS); long amount; // will go from days, to hours, etc to nanos amount = checkAndGet(raw, TimeUnit.DAYS, 3); amount = checkAndGet(raw, TimeUnit.HOURS, (amount*24) + 5); amount = checkAndGet(raw, TimeUnit.MINUTES, (amount*60) + 13); amount = checkAndGet(raw, TimeUnit.SECONDS, (amount*60) + 17); amount = checkAndGet(raw, TimeUnit.MICROSECONDS, (amount*1000000) + 43); checkAndGet(raw, TimeUnit.NANOSECONDS, amount*1000); // the 666 extra were truncated } @Test public void testDaysParser() { new TimeBuilder(3, TimeUnit.DAYS) .checkParse(AkIntervalSecondsFormat.DAY, "3") .badParse(AkIntervalSecondsFormat.DAY, "3-4"); } @Test public void testHoursParser() { new TimeBuilder(3, TimeUnit.HOURS) .checkParse(AkIntervalSecondsFormat.HOUR, "3") .badParse(AkIntervalSecondsFormat.HOUR, "3-4"); } @Test public void testMinutesParser() { new TimeBuilder(43, TimeUnit.MINUTES) .checkParse(AkIntervalSecondsFormat.MINUTE, "43") .badParse(AkIntervalSecondsFormat.MINUTE, "43-4"); } @Test public void testSecondsParser() { new TimeBuilder(11, TimeUnit.SECONDS) .checkParse(AkIntervalSecondsFormat.SECOND, "11") .badParse(AkIntervalSecondsFormat.SECOND, "11-4"); } @Test public void testSecondsFractionalParser() { new TimeBuilder(123456, TimeUnit.MICROSECONDS) .checkParse(AkIntervalSecondsFormat.SECOND, "0.123456") .checkParse(AkIntervalSecondsFormat.SECOND, "0.1234567") // 7 should get truncated .checkParse(AkIntervalSecondsFormat.SECOND, ".123456") .badParse(AkIntervalSecondsFormat.SECOND, "4 5"); } @Test public void testDaysToHoursParser() { new TimeBuilder(2, TimeUnit.DAYS).add(5, TimeUnit.HOURS) .checkParse(AkIntervalSecondsFormat.DAY_HOUR, "2 5") .badParse(AkIntervalSecondsFormat.DAY_HOUR, "2"); } @Test public void testDaysToMinutesParser() { new TimeBuilder(2, TimeUnit.DAYS).add(5, TimeUnit.HOURS).add(13, TimeUnit.MINUTES) .checkParse(AkIntervalSecondsFormat.DAY_MINUTE, "2 5:13") .badParse(AkIntervalSecondsFormat.DAY_MINUTE, "2"); } @Test public void testDaysToSecondsParser() { new TimeBuilder(2, TimeUnit.DAYS).add(5, TimeUnit.HOURS).add(13, TimeUnit.MINUTES).add(27, TimeUnit.SECONDS) .checkParse(AkIntervalSecondsFormat.DAY_SECOND, "2 5:13:27") .badParse(AkIntervalSecondsFormat.DAY_SECOND, "2"); } @Test public void testHoursToMinutesParser() { new TimeBuilder(35, TimeUnit.HOURS).add(13, TimeUnit.MINUTES) .checkParse(AkIntervalSecondsFormat.HOUR_MINUTE, "35:13") .badParse(AkIntervalSecondsFormat.HOUR_MINUTE, "2"); } @Test public void testHoursToSecondsParser() { new TimeBuilder(35, TimeUnit.HOURS).add(13, TimeUnit.MINUTES).add(52, TimeUnit.SECONDS) .checkParse(AkIntervalSecondsFormat.HOUR_SECOND, "35:13:52") .badParse(AkIntervalSecondsFormat.HOUR_SECOND, "2"); } @Test public void testMinutesToSecondsParser() { new TimeBuilder(14, TimeUnit.MINUTES).add(52, TimeUnit.SECONDS) .checkParse(AkIntervalSecondsFormat.MINUTE_SECOND, "14:52") .badParse(AkIntervalSecondsFormat.MINUTE_SECOND, "2"); new TimeBuilder(61, TimeUnit.SECONDS) .checkParse(AkIntervalSecondsFormat.MINUTE_SECOND, "1:1"); } @Test public void testYearsParser() { monthsFormatsTested.add(AkIntervalMonthsFormat.YEAR); assertEquals("years", (43*12), AkIntervalMonthsFormat.YEAR.parse("43")); badParse(AkIntervalMonthsFormat.YEAR, "123-3"); } @Test public void testMonthsParser() { monthsFormatsTested.add(AkIntervalMonthsFormat.MONTH); assertEquals("months", 43, AkIntervalMonthsFormat.MONTH.parse("43")); badParse(AkIntervalMonthsFormat.MONTH, "123-3"); } @Test public void testYearsToMonthsParser() { monthsFormatsTested.add(AkIntervalMonthsFormat.YEAR_MONTH); assertEquals("years-months", (43*12) + 7, AkIntervalMonthsFormat.YEAR_MONTH.parse("43-7")); badParse(AkIntervalMonthsFormat.YEAR_MONTH, "43-13"); badParse(AkIntervalMonthsFormat.YEAR_MONTH, "123"); } @AfterClass public static void confirmAllTested() { confirmAllTested(AkIntervalSecondsFormat.class, secondsFormatsTested); confirmAllTested(AkIntervalMonthsFormat.class, monthsFormatsTested); } private static <T extends Enum<T>> void confirmAllTested(Class<T> clazz, Set<T> tested) { Set<T> untested = Sets.difference(EnumSet.allOf(clazz), tested); if (!untested.isEmpty()) fail("untested formats for parsing: " + untested); } private static final class TimeBuilder { long nanos; public TimeBuilder() { // nothing to do } public TimeBuilder(long amount, TimeUnit unit) { add(amount, unit); } public TimeBuilder add(long amount, TimeUnit unit) { nanos += unit.toNanos(amount); return this; } public long getNanos() { return nanos; } public TimeBuilder checkParse(AkIntervalSecondsFormat format, String string) { secondsFormatsTested.add(format); long actual = format.parse(string); assertEquals("nanoseconds", nanos, AkInterval.secondsIntervalAs(actual, TimeUnit.NANOSECONDS)); return this; } public TimeBuilder badParse(AkIntervalSecondsFormat format, String string) { AkIntervalTest.badParse(format, string); return this; } } private static void badParse(IntervalFormat format, String string) { try { format.parse(string); fail("expected failure when parsing '" + string + "' as " + format); } catch (RuntimeException e) { // expected! } } /** * Checks the value and then returns the <tt>expected</tt> variable, so that it can be reused. */ private long checkAndGet(long rawAmount, TimeUnit expectedUnit, long expectedValue) { assertEquals(expectedUnit.name(), expectedValue, AkInterval.secondsIntervalAs(rawAmount, expectedUnit)); return expectedValue; } private static final Set<AkIntervalSecondsFormat> secondsFormatsTested = EnumSet.noneOf(AkIntervalSecondsFormat.class); private static final Set<AkIntervalMonthsFormat> monthsFormatsTested = EnumSet.noneOf(AkIntervalMonthsFormat.class); }