/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hive.serde2.io; import com.google.code.tempusfugit.concurrency.annotations.*; import com.google.code.tempusfugit.concurrency.*; import org.junit.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.junit.Assert.*; import java.io.*; import java.sql.Date; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.LinkedList; import java.util.TimeZone; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class TestDateWritable { private static final Logger LOG = LoggerFactory.getLogger(TestDateWritable.class); @Rule public ConcurrentRule concurrentRule = new ConcurrentRule(); @Rule public RepeatingRule repeatingRule = new RepeatingRule(); @Test @Concurrent(count=4) @Repeating(repetition=100) public void testConstructor() { Date date = Date.valueOf(getRandomDateString()); DateWritable dw1 = new DateWritable(date); DateWritable dw2 = new DateWritable(dw1); DateWritable dw3 = new DateWritable(dw1.getDays()); assertEquals(dw1, dw1); assertEquals(dw1, dw2); assertEquals(dw2, dw3); assertEquals(date, dw1.get()); assertEquals(date, dw2.get()); assertEquals(date, dw3.get()); } @Test @Concurrent(count=4) @Repeating(repetition=100) public void testComparison() { // Get 2 different dates Date date1 = Date.valueOf(getRandomDateString()); Date date2 = Date.valueOf(getRandomDateString()); while (date1.equals(date2)) { date2 = Date.valueOf(getRandomDateString()); } DateWritable dw1 = new DateWritable(date1); DateWritable dw2 = new DateWritable(date2); DateWritable dw3 = new DateWritable(date1); assertTrue("Dates should be equal", dw1.equals(dw1)); assertTrue("Dates should be equal", dw1.equals(dw3)); assertTrue("Dates should be equal", dw3.equals(dw1)); assertEquals("Dates should be equal", 0, dw1.compareTo(dw1)); assertEquals("Dates should be equal", 0, dw1.compareTo(dw3)); assertEquals("Dates should be equal", 0, dw3.compareTo(dw1)); assertFalse("Dates not should be equal", dw1.equals(dw2)); assertFalse("Dates not should be equal", dw2.equals(dw1)); assertTrue("Dates not should be equal", 0 != dw1.compareTo(dw2)); assertTrue("Dates not should be equal", 0 != dw2.compareTo(dw1)); } @Test @Concurrent(count=4) @Repeating(repetition=100) public void testGettersSetters() { Date date1 = Date.valueOf(getRandomDateString()); Date date2 = Date.valueOf(getRandomDateString()); Date date3 = Date.valueOf(getRandomDateString()); DateWritable dw1 = new DateWritable(date1); DateWritable dw2 = new DateWritable(date2); DateWritable dw3 = new DateWritable(date3); DateWritable dw4 = new DateWritable(); // Getters assertEquals(date1, dw1.get()); assertEquals(date1.getTime() / 1000, dw1.getTimeInSeconds()); dw4.set(Date.valueOf("1970-01-02")); assertEquals(1, dw4.getDays()); dw4.set(Date.valueOf("1971-01-01")); assertEquals(365, dw4.getDays()); // Setters dw4.set(dw1.getDays()); assertEquals(dw1, dw4); dw4.set(dw2.get()); assertEquals(dw2, dw4); dw4.set(dw3); assertEquals(dw3, dw4); } @Test @Concurrent(count=4) @Repeating(repetition=100) public void testWritableMethods() throws Throwable { DateWritable dw1 = new DateWritable(Date.valueOf(getRandomDateString())); DateWritable dw2 = new DateWritable(); ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutput out = new DataOutputStream(byteStream); dw1.write(out); dw2.readFields(new DataInputStream(new ByteArrayInputStream(byteStream.toByteArray()))); assertEquals("Dates should be equal", dw1, dw2); } @Test @Concurrent(count=4) @Repeating(repetition=100) public void testDateValueOf() { // Just making sure Date.valueOf() works ok String dateStr = getRandomDateString(); Date date = Date.valueOf(dateStr); assertEquals(dateStr, date.toString()); } private static String[] dateStrings = new String[365]; @BeforeClass public static void setupDateStrings() { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date initialDate = Date.valueOf("2014-01-01"); Calendar cal = Calendar.getInstance(); cal.setTime(initialDate); for (int idx = 0; idx < 365; ++idx) { dateStrings[idx] = format.format(cal.getTime()); cal.add(1, Calendar.DAY_OF_YEAR); } } private static String getRandomDateString() { return dateStrings[(int) (Math.random() * 365)]; } public static class DateTestCallable implements Callable<Void> { private LinkedList<DtMismatch> bad; private String tz; public DateTestCallable(LinkedList<DtMismatch> bad, String tz) { this.bad = bad; this.tz = tz; } @Override public Void call() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss"); // Iterate through each day of the year, make sure Date/DateWritable match Date originalDate = Date.valueOf("1900-01-01"); Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(originalDate.getTime()); for (int idx = 0; idx < 365*200; ++idx) { originalDate = new Date(cal.getTimeInMillis()); // Make sure originalDate is at midnight in the local time zone, // since DateWritable will generate dates at that time. originalDate = Date.valueOf(originalDate.toString()); DateWritable dateWritable = new DateWritable(originalDate); Date actual = dateWritable.get(false); if (!originalDate.equals(actual)) { String originalStr = sdf.format(originalDate); String actualStr = sdf.format(actual); if (originalStr.substring(0, 10).equals(actualStr.substring(0, 10))) continue; bad.add(new DtMismatch(originalStr, actualStr, tz)); } cal.add(Calendar.DAY_OF_YEAR, 1); } // Success! return null; } } private static class DtMismatch { String expected, found, tz; public DtMismatch(String originalStr, String actualStr, String tz) { this.expected = originalStr; this.found = actualStr; this.tz = tz; } } @Test public void testDaylightSavingsTime() throws Exception { LinkedList<DtMismatch> bad = new LinkedList<>(); for (String timeZone: TimeZone.getAvailableIDs()) { TimeZone previousDefault = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone(timeZone)); assertEquals("Default timezone should now be " + timeZone, timeZone, TimeZone.getDefault().getID()); ExecutorService threadPool = Executors.newFixedThreadPool(1); try { // TODO: pointless threadPool.submit(new DateTestCallable(bad, timeZone)).get(); } finally { threadPool.shutdown(); TimeZone.setDefault(previousDefault); } } StringBuilder errors = new StringBuilder("\nDATE MISMATCH:\n"); for (DtMismatch dm : bad) { errors.append("E ").append(dm.tz).append(": ").append(dm.expected).append(" != ").append(dm.found).append("\n"); } LOG.error(errors.toString()); if (!bad.isEmpty()) throw new Exception(bad.size() + " mismatches, see logs"); } }