blob: d29ff51ad7d5f4ff9614c22067bb6305069e1e8d [file] [log] [blame]
/*
* 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.
*/
#include <log4cxx/helpers/cacheddateformat.h>
#include "../logunit.h"
#include <log4cxx/helpers/absolutetimedateformat.h>
#include <log4cxx/helpers/iso8601dateformat.h>
#include <log4cxx/helpers/relativetimedateformat.h>
#include <log4cxx/helpers/pool.h>
#include <locale>
#include "../insertwide.h"
#include <apr.h>
#include <apr_time.h>
#include "localechanger.h"
using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace log4cxx::pattern;
#define LOCALE_US "C"
#if defined(_WIN32)
#define LOCALE_JP "Japanese_japan"
#else
#define LOCALE_JP "ja_JP"
#endif
//Define INT64_C for compilers that don't have it
#if (!defined(INT64_C))
#define INT64_C(value) value ##LL
#endif
#define LOG4CXX_TEST 1
#include <log4cxx/private/log4cxx_private.h>
/**
* Unit test {@link CachedDateFormat}.
*/
LOGUNIT_CLASS(CachedDateFormatTestCase)
{
LOGUNIT_TEST_SUITE( CachedDateFormatTestCase );
LOGUNIT_TEST(test1);
LOGUNIT_TEST(test2);
LOGUNIT_TEST(test3);
LOGUNIT_TEST(test4);
#if LOG4CXX_HAS_STD_LOCALE
LOGUNIT_TEST(test5);
#endif
LOGUNIT_TEST(test6);
LOGUNIT_TEST(test8);
// Gump doesn't like this test
// LOGUNIT_TEST(test9);
LOGUNIT_TEST(test10);
LOGUNIT_TEST(test11);
LOGUNIT_TEST(test12);
LOGUNIT_TEST(test13);
LOGUNIT_TEST(test14);
LOGUNIT_TEST(test15);
LOGUNIT_TEST(test16);
LOGUNIT_TEST(test17);
LOGUNIT_TEST(test18);
LOGUNIT_TEST(test19);
LOGUNIT_TEST(test20);
LOGUNIT_TEST(test21);
LOGUNIT_TEST(test22);
LOGUNIT_TEST_SUITE_END();
#define MICROSECONDS_PER_DAY APR_INT64_C(86400000000)
public:
/**
* Test multiple calls in close intervals.
*/
void test1()
{
// subsequent calls within one minute
// are optimized to reuse previous formatted value
// make a couple of nearly spaced calls
DateFormatPtr baseFormatter(new AbsoluteTimeDateFormat());
CachedDateFormat gmtFormat(baseFormatter, 1000000);
gmtFormat.setTimeZone(TimeZone::getGMT());
apr_time_t jul1 = MICROSECONDS_PER_DAY * 12601L;
Pool p;
LogString actual;
gmtFormat.format(actual, jul1, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,000"), actual);
actual.erase(actual.begin(), actual.end());
gmtFormat.format(actual, jul1 + 8000, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,008"), actual);
actual.erase(actual.begin(), actual.end());
gmtFormat.format(actual, jul1 + 17000, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,017"), actual);
actual.erase(actual.begin(), actual.end());
gmtFormat.format(actual, jul1 + 237000, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,237"), actual);
actual.erase(actual.begin(), actual.end());
gmtFormat.format(actual, jul1 + 1415000, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:01,415"), actual);
actual.erase(actual.begin(), actual.end());
}
/**
* Check for interaction between caches.
*/
void test2()
{
apr_time_t jul2 = MICROSECONDS_PER_DAY * 12602;
DateFormatPtr baseFormatter(new AbsoluteTimeDateFormat());
CachedDateFormat gmtFormat(baseFormatter, 1000000);
gmtFormat.setTimeZone(TimeZone::getGMT());
DateFormatPtr chicagoBase(new AbsoluteTimeDateFormat());
CachedDateFormat chicagoFormat(chicagoBase, 1000000);
chicagoFormat.setTimeZone(TimeZone::getTimeZone(LOG4CXX_STR("GMT-5")));
Pool p;
LogString actual;
gmtFormat.format(actual, jul2, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,000"), actual);
actual.erase(actual.begin(), actual.end());
chicagoFormat.format(actual, jul2, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("19:00:00,000"), actual);
actual.erase(actual.begin(), actual.end());
gmtFormat.format(actual, jul2, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,000"), actual);
}
/**
* Test multiple calls in close intervals prior to 1 Jan 1970.
*/
void test3()
{
// subsequent calls within one minute
// are optimized to reuse previous formatted value
// make a couple of nearly spaced calls
DateFormatPtr baseFormatter(new AbsoluteTimeDateFormat());
CachedDateFormat gmtFormat(baseFormatter, 1000000);
gmtFormat.setTimeZone(TimeZone::getGMT());
apr_time_t ticks = MICROSECONDS_PER_DAY * -7;
Pool p;
LogString actual;
gmtFormat.format(actual, ticks, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,000"), actual);
actual.erase(actual.begin(), actual.end());
//
// APR's explode_time method does not properly calculate tm_usec
// prior to 1 Jan 1970 on Unix
gmtFormat.format(actual, ticks + 8000, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,008"), actual);
actual.erase(actual.begin(), actual.end());
gmtFormat.format(actual, ticks + 17000, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,017"), actual);
actual.erase(actual.begin(), actual.end());
gmtFormat.format(actual, ticks + 237000, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,237"), actual);
actual.erase(actual.begin(), actual.end());
gmtFormat.format(actual, ticks + 1423000, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:01,423"), actual);
}
void assertFormattedEquals(
const DateFormatPtr & baseFormat,
const CachedDateFormat & cachedFormat,
apr_time_t date,
Pool & p)
{
LogString expected;
LogString actual;
baseFormat->format(expected, date, p);
cachedFormat.format(actual, date, p);
LOGUNIT_ASSERT_EQUAL(expected, actual);
}
void test4()
{
// subsequent calls within one minute
// are optimized to reuse previous formatted value
// make a couple of nearly spaced calls
#if LOG4CXX_HAS_STD_LOCALE
std::locale loco(LOCALE_US);
std::locale* localeEN = &loco;
#else
std::locale* localeEN = NULL;
#endif
DateFormatPtr baseFormat(
new SimpleDateFormat(LOG4CXX_STR("EEE, MMM dd, HH:mm:ss.SSS Z"), localeEN));
CachedDateFormat cachedFormat(baseFormat, 1000000);
//
// use a date in 2000 to attempt to confuse the millisecond locator
apr_time_t ticks = MICROSECONDS_PER_DAY * 11141;
Pool p;
assertFormattedEquals(baseFormat, cachedFormat, ticks, p);
assertFormattedEquals(baseFormat, cachedFormat, ticks + 8000, p);
assertFormattedEquals(baseFormat, cachedFormat, ticks + 17000, p);
assertFormattedEquals(baseFormat, cachedFormat, ticks + 237000, p);
assertFormattedEquals(baseFormat, cachedFormat, ticks + 1415000, p);
}
#if LOG4CXX_HAS_STD_LOCALE
void test5()
{
// subsequent calls within one minute
// are optimized to reuse previous formatted value
// make a couple of nearly spaced calls
LocaleChanger localeChange(LOCALE_JP);
if (localeChange.isEffective())
{
DateFormatPtr baseFormat(new SimpleDateFormat(
LOG4CXX_STR("EEE, MMM dd, HH:mm:ss.SSS Z")));
CachedDateFormat cachedFormat(baseFormat, 1000000);
//
// use a date in 2000 to attempt to confuse the millisecond locator
apr_time_t ticks = MICROSECONDS_PER_DAY * 11141;
Pool p;
assertFormattedEquals(baseFormat, cachedFormat, ticks, p);
assertFormattedEquals(baseFormat, cachedFormat, ticks + 8000, p);
assertFormattedEquals(baseFormat, cachedFormat, ticks + 17000, p);
assertFormattedEquals(baseFormat, cachedFormat, ticks + 237000, p);
assertFormattedEquals(baseFormat, cachedFormat, ticks + 1415000, p);
}
}
#endif
/**
* Checks that numberFormat works as expected.
*/
void test6()
{
LogString numb;
Pool p;
AbsoluteTimeDateFormat formatter;
formatter.numberFormat(numb, 87, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("87"), numb);
}
/**
* Set time zone on cached and check that it is effective.
*/
void test8()
{
DateFormatPtr baseFormat(new SimpleDateFormat(LOG4CXX_STR("yyyy-MM-dd HH:mm:ss,SSS")));
baseFormat->setTimeZone(TimeZone::getGMT());
CachedDateFormat cachedFormat(baseFormat, 1000000);
apr_time_t jul4 = MICROSECONDS_PER_DAY * 12603;
Pool p;
LogString actual;
cachedFormat.format(actual, jul4, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("2004-07-04 00:00:00,000"), actual);
cachedFormat.setTimeZone(TimeZone::getTimeZone(LOG4CXX_STR("GMT-6")));
actual.erase(actual.begin(), actual.end());
cachedFormat.format(actual, jul4, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("2004-07-03 18:00:00,000"), actual);
}
/**
* Test of caching when less than three millisecond digits are specified.
*/
void test9()
{
std::locale localeUS(LOCALE_US);
DateFormatPtr baseFormat = new SimpleDateFormat(
LOG4CXX_STR("yyyy-MMMM-dd HH:mm:ss,SS Z"), &localeUS);
DateFormatPtr cachedFormat = new CachedDateFormat(baseFormat, 1000000);
TimeZonePtr cet = TimeZone::getTimeZone(LOG4CXX_STR("GMT+1"));
cachedFormat->setTimeZone(cet);
apr_time_exp_t c;
memset(&c, 0, sizeof(c));
c.tm_year = 104;
c.tm_mon = 11;
c.tm_mday = 12;
c.tm_hour = 19;
c.tm_sec = 37;
c.tm_usec = 23000;
apr_time_t dec12;
apr_status_t stat = apr_time_exp_gmt_get(&dec12, &c);
const apr_status_t statOK = 0;
LOGUNIT_ASSERT_EQUAL(statOK, stat);
Pool p;
LogString s;
cachedFormat->format(s, dec12, p);
LOGUNIT_ASSERT_EQUAL(
(LogString) LOG4CXX_STR("2004-December-12 20:00:37,23 +0100"), s);
memset(&c, 0, sizeof(c));
c.tm_year = 104;
c.tm_mon = 11;
c.tm_mday = 31;
c.tm_hour = 23;
c.tm_sec = 13;
c.tm_usec = 905000;
apr_time_t jan1;
stat = apr_time_exp_gmt_get(&jan1, &c);
LOGUNIT_ASSERT_EQUAL(statOK, stat);
s.erase(s.begin(), s.end());
cachedFormat->format(s, jan1, p);
LOGUNIT_ASSERT_EQUAL(
(LogString) LOG4CXX_STR("2005-January-01 00:00:13,905 +0100"), s);
}
/**
* Test when millisecond position moves but length remains constant.
*/
void test10()
{
#if LOG4CXX_HAS_STD_LOCALE
std::locale loco("C");
std::locale* localeUS = &loco;
#else
std::locale* localeUS = NULL;
#endif
DateFormatPtr baseFormat = new SimpleDateFormat(
LOG4CXX_STR("MMMM SSS EEEEEE"), localeUS);
DateFormatPtr cachedFormat = new CachedDateFormat(baseFormat, 1000000);
TimeZonePtr cet = TimeZone::getTimeZone(LOG4CXX_STR("GMT+1"));
cachedFormat->setTimeZone(cet);
apr_time_exp_t c;
memset(&c, 0, sizeof(c));
c.tm_year = 104;
c.tm_mon = 9;
c.tm_mday = 5;
c.tm_hour = 21;
c.tm_sec = 37;
c.tm_usec = 23000;
apr_time_t oct5;
apr_status_t stat = apr_time_exp_gmt_get(&oct5, &c);
const apr_status_t statOK = 0;
LOGUNIT_ASSERT_EQUAL(statOK, stat);
Pool p;
LogString s;
cachedFormat->format(s, oct5, p);
LOGUNIT_ASSERT_EQUAL(
(LogString) LOG4CXX_STR("October 023 Tuesday"), s);
memset(&c, 0, sizeof(c));
c.tm_year = 104;
c.tm_mon = 10;
c.tm_mday = 1;
c.tm_usec = 23000;
apr_time_t nov1;
stat = apr_time_exp_gmt_get(&nov1, &c);
LOGUNIT_ASSERT_EQUAL(statOK, stat);
s.erase(s.begin(), s.end());
cachedFormat->format(s, nov1, p);
LOGUNIT_ASSERT_EQUAL(
(LogString) LOG4CXX_STR("November 023 Monday"), s);
nov1 += 961000;
s.erase(s.begin(), s.end());
cachedFormat->format(s, nov1, p);
LOGUNIT_ASSERT_EQUAL(
(LogString) LOG4CXX_STR("November 984 Monday"), s);
}
/**
* Test that tests if caching is skipped if only "SS"
* is specified.
*/
void test11()
{
//
// Earlier versions could be tricked by "SS0" patterns.
//
LogString badPattern(LOG4CXX_STR("ss,SS0"));
DateFormatPtr simpleFormat = new SimpleDateFormat(badPattern);
DateFormatPtr gmtFormat = new CachedDateFormat(simpleFormat, 1000000);
gmtFormat->setTimeZone(TimeZone::getGMT());
//
// The first request has to 100 ms after an ordinal second
// to push the literal zero out of the pattern check
apr_time_t ticks = MICROSECONDS_PER_DAY * 11142L;
apr_time_t jul2 = ticks + 120000;
Pool p;
LogString s;
gmtFormat->format(s, jul2, p);
LOGUNIT_ASSERT_EQUAL(
(LogString) LOG4CXX_STR("00,1200"), s);
jul2 = ticks + 87000;
s.erase(s.begin(), s.end());
gmtFormat->format(s, jul2, p);
LOGUNIT_ASSERT_EQUAL(
(LogString) LOG4CXX_STR("00,870"), s);
}
/**
* Check pattern location for ISO8601
*/
void test12()
{
DateFormatPtr df = new SimpleDateFormat(LOG4CXX_STR("yyyy-MM-dd HH:mm:ss,SSS"));
apr_time_t ticks = 11142L * MICROSECONDS_PER_DAY;
Pool p;
LogString formatted;
df->format(formatted, ticks, p);
int msStart = CachedDateFormat::findMillisecondStart(ticks, formatted, df, p);
LOGUNIT_ASSERT_EQUAL(20, msStart);
// Test for for milliseconds overlapping with the magic ones as per LOGCXX-420.
apr_time_exp_t c;
memset(&c, 0, sizeof(c));
c.tm_year = 110;
c.tm_mon = 7;
c.tm_mday = 12;
c.tm_hour = 9;
c.tm_min = 4;
c.tm_sec = 50;
c.tm_usec = 406000;
LOGUNIT_ASSERT_EQUAL(0, apr_time_exp_gmt_get(&ticks, &c));
formatted.clear();
df->format(formatted, ticks, p);
msStart = CachedDateFormat::findMillisecondStart(ticks, formatted, df, p);
LOGUNIT_ASSERT_EQUAL(20, msStart);
c.tm_usec = 609000;
LOGUNIT_ASSERT_EQUAL(0, apr_time_exp_gmt_get(&ticks, &c));
formatted.clear();
df->format(formatted, ticks, p);
msStart = CachedDateFormat::findMillisecondStart(ticks, formatted, df, p);
LOGUNIT_ASSERT_EQUAL(20, msStart);
}
/**
* Check pattern location for DATE
*/
void test13()
{
DateFormatPtr df = new SimpleDateFormat(LOG4CXX_STR("yyyy-MM-dd"));
apr_time_t ticks = 11142L * MICROSECONDS_PER_DAY;
Pool p;
LogString formatted;
df->format(formatted, ticks, p);
int millisecondStart = CachedDateFormat::findMillisecondStart(ticks,
formatted, df, p);
LOGUNIT_ASSERT_EQUAL((int) CachedDateFormat::NO_MILLISECONDS, millisecondStart);
}
/**
* Check pattern location for ABSOLUTE
*/
void test14()
{
DateFormatPtr df = new SimpleDateFormat(LOG4CXX_STR("HH:mm:ss,SSS"));
apr_time_t ticks = 11142L * MICROSECONDS_PER_DAY;
Pool p;
LogString formatted;
df->format(formatted, ticks, p);
int millisecondStart = CachedDateFormat::findMillisecondStart(ticks,
formatted, df, p);
LOGUNIT_ASSERT_EQUAL(9, millisecondStart);
}
/**
* Check pattern location for single S
*/
void test15()
{
DateFormatPtr df = new SimpleDateFormat(LOG4CXX_STR("HH:mm:ss,S"));
apr_time_t ticks = 11142L * MICROSECONDS_PER_DAY;
Pool p;
LogString formatted;
df->format(formatted, ticks, p);
int millisecondStart = CachedDateFormat::findMillisecondStart(ticks,
formatted, df, p);
LOGUNIT_ASSERT_EQUAL((int) CachedDateFormat::UNRECOGNIZED_MILLISECONDS, millisecondStart);
}
/**
* Check pattern location for single SS
*/
void test16()
{
DateFormatPtr df = new SimpleDateFormat(LOG4CXX_STR("HH:mm:ss,SS"));
apr_time_t ticks = 11142L * MICROSECONDS_PER_DAY;
Pool p;
LogString formatted;
df->format(formatted, ticks, p);
int millisecondStart =
CachedDateFormat::findMillisecondStart(ticks, formatted, df, p);
LOGUNIT_ASSERT_EQUAL((int) CachedDateFormat::UNRECOGNIZED_MILLISECONDS, millisecondStart);
}
/**
* Check caching when multiple SSS appear in pattern
*/
void test17()
{
apr_time_t jul2 = 12602L * MICROSECONDS_PER_DAY;
LogString badPattern(LOG4CXX_STR("HH:mm:ss,SSS HH:mm:ss,SSS"));
DateFormatPtr simpleFormat = new SimpleDateFormat(badPattern);
simpleFormat->setTimeZone(TimeZone::getGMT());
DateFormatPtr cachedFormat = new CachedDateFormat(simpleFormat, 1000000);
Pool p;
LogString s;
cachedFormat->format(s, jul2, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,000 00:00:00,000"), s);
jul2 += 120000;
s.erase(s.begin(), s.end());
simpleFormat->format(s, jul2, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,120 00:00:00,120"), s);
s.erase(s.begin(), s.end());
int msStart = CachedDateFormat::findMillisecondStart(jul2, s, simpleFormat, p);
LOGUNIT_ASSERT_EQUAL((int) CachedDateFormat::UNRECOGNIZED_MILLISECONDS, msStart);
cachedFormat->format(s, jul2, p);
LOGUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,120 00:00:00,120"), s) ;
int maxValid = CachedDateFormat::getMaximumCacheValidity(badPattern);
LOGUNIT_ASSERT_EQUAL(1000, maxValid);
// Test overlapping millis with a magic string from CachedDateFormat for LOGCXX-420.
s.clear();
jul2 += 286000;
cachedFormat->format(s, jul2, p);
msStart = CachedDateFormat::findMillisecondStart(jul2, s, simpleFormat, p);
LOGUNIT_ASSERT_EQUAL((int) CachedDateFormat::UNRECOGNIZED_MILLISECONDS, msStart);
s.clear();
jul2 += 203000;
cachedFormat->format(s, jul2, p);
msStart = CachedDateFormat::findMillisecondStart(jul2, s, simpleFormat, p);
LOGUNIT_ASSERT_EQUAL((int) CachedDateFormat::UNRECOGNIZED_MILLISECONDS, msStart);
}
/**
* Check that patterns not containing microseconds
* are reported as being able to be cached for a full second.
*/
void test18()
{
int maxValid =
CachedDateFormat::getMaximumCacheValidity(
LOG4CXX_STR("yyyy-MM-dd"));
LOGUNIT_ASSERT_EQUAL(1000000, maxValid);
}
/**
* Check that patterns not containing 3 microseconds
* are reported as being able to be cached for a full second.
*/
void test19()
{
int maxValid =
CachedDateFormat::getMaximumCacheValidity(
LOG4CXX_STR("yyyy-MM-dd SSS"));
LOGUNIT_ASSERT_EQUAL(1000000, maxValid);
}
/**
* Check that patterns not containing 2 S's
* are reported as being able to be cached for only a millisecond.
*/
void test20()
{
int maxValid =
CachedDateFormat::getMaximumCacheValidity(
LOG4CXX_STR("yyyy-MM-dd SS"));
LOGUNIT_ASSERT_EQUAL(1000, maxValid);
}
/**
* Check that patterns not containing multi S groups
* are reported as being able to be cached for only a millisecond.
*/
void test21()
{
int maxValid =
CachedDateFormat::getMaximumCacheValidity(
LOG4CXX_STR("yyyy-MM-dd SSS SSS"));
LOGUNIT_ASSERT_EQUAL(1000, maxValid);
}
/**
* Check that findMillisecondStart correctly handles timestamps
* which use the magic millisecond value 654.
*/
void test22()
{
DateFormatPtr baseFormatter(new ISO8601DateFormat());
CachedDateFormat isoFormat(baseFormatter, 1000000);
isoFormat.setTimeZone(TimeZone::getGMT());
Pool p;
LogString formatted;
isoFormat.format(formatted, 654000, p);
LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("1970-01-01 00:00:00,654"), formatted);
formatted.clear();
isoFormat.format(formatted, 999000, p);
LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("1970-01-01 00:00:00,999"), formatted);
formatted.clear();
isoFormat.format(formatted, 1654010, p);
LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("1970-01-01 00:00:01,654"), formatted);
formatted.clear();
isoFormat.format(formatted, 1999010, p);
LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("1970-01-01 00:00:01,999"), formatted);
}
};
LOGUNIT_TEST_SUITE_REGISTRATION(CachedDateFormatTestCase);