blob: 0c7f80db0a6626d6723c9315ba3a294496e41df2 [file]
/*
* 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/logger.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/mdc.h>
#include <log4cxx/patternlayout.h>
#include <log4cxx/fileappender.h>
#include "util/compare.h"
#include "util/transformer.h"
#include "util/absolutedateandtimefilter.h"
#include "util/iso8601filter.h"
#include "util/absolutetimefilter.h"
#include "util/relativetimefilter.h"
#include "util/controlfilter.h"
#include "util/threadfilter.h"
#include "util/linenumberfilter.h"
#include "util/filenamefilter.h"
#include <iostream>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/pool.h>
#include <apr_strings.h>
#include <log4cxx/helpers/loglog.h>
#include "testchar.h"
#include "logunit.h"
#include <log4cxx/spi/loggerrepository.h>
#include <log4cxx/spi/loggingevent.h>
#include <log4cxx/helpers/stringhelper.h>
#define REGEX_STR(x) x
#define PAT0 REGEX_STR("\\[[0-9A-FXx]*]\\ (DEBUG|INFO|WARN|ERROR|FATAL) .* - Message [0-9]\\{1,2\\}")
#define PAT1 ISO8601_PAT REGEX_STR(" ") PAT0
#define PAT2 ABSOLUTE_DATE_AND_TIME_PAT REGEX_STR(" ") PAT0
#define PAT3 ABSOLUTE_TIME_PAT REGEX_STR(" ") PAT0
#define PAT4 RELATIVE_TIME_PAT REGEX_STR(" ") PAT0
#define PAT5 REGEX_STR("\\[[0-9A-FXx]*]\\ (DEBUG|INFO|WARN|ERROR|FATAL) .* : Message [0-9]\\{1,2\\}")
#define PAT6 REGEX_STR("\\[[0-9A-FXx]*]\\ (DEBUG|INFO |WARN |ERROR|FATAL) .*patternlayouttest.cpp\\([0-9]\\{1,4\\}\\): Message [0-9]\\{1,3\\}")
#define PAT11a REGEX_STR("^(DEBUG|INFO |WARN |ERROR|FATAL) \\[[0-9A-FXx]*]\\ log4j.PatternLayoutTest: Message [0-9]\\{1,2\\}")
#define PAT11b REGEX_STR("^(DEBUG|INFO |WARN |ERROR|FATAL) \\[[0-9A-FXx]*]\\ root: Message [0-9]\\{1,2\\}")
#define PAT12 REGEX_STR("^\\[[0-9A-FXx]*]\\ (DEBUG|INFO |WARN |ERROR|FATAL) ")\
REGEX_STR(".*patternlayouttest.cpp([0-9]\\{1,4\\}): ")\
REGEX_STR("Message [0-9]\\{1,2\\}")
#define PAT_MDC_1 REGEX_STR("")
using namespace log4cxx;
using namespace log4cxx::helpers;
LOGUNIT_CLASS(PatternLayoutTest)
{
LOGUNIT_TEST_SUITE(PatternLayoutTest);
#if ENABLE_2GB_STRING_TESTING
LOGUNIT_TEST(test2GbMessageFormatting);
#endif
LOGUNIT_TEST(test1);
LOGUNIT_TEST(test2);
LOGUNIT_TEST(test3);
LOGUNIT_TEST(test4);
LOGUNIT_TEST(test5);
LOGUNIT_TEST(test6);
LOGUNIT_TEST(test7);
LOGUNIT_TEST(test8);
LOGUNIT_TEST(test9);
LOGUNIT_TEST(test10);
LOGUNIT_TEST(test11);
LOGUNIT_TEST(test12);
LOGUNIT_TEST(test13);
LOGUNIT_TEST(test14);
LOGUNIT_TEST(testMDC1);
LOGUNIT_TEST(testMDC2);
LOGUNIT_TEST(testAbbreviateNoDots);
LOGUNIT_TEST(testAbbreviatePrecisionExceedsSegments);
LOGUNIT_TEST(testAbbreviateKeepLastTwoSegments);
LOGUNIT_TEST(testAbbreviateKeepLastSegmentOnly);
LOGUNIT_TEST(testAbbreviatePrecisionZero);
LOGUNIT_TEST(testAbbreviateEachSegmentToOneChar);
LOGUNIT_TEST(testAbbreviateEachSegmentToTwoChars);
LOGUNIT_TEST(testAbbreviateShortSegmentUnchanged);
LOGUNIT_TEST(testAbbreviateConsecutiveDots);
LOGUNIT_TEST(testAbbreviateLeadingDot);
LOGUNIT_TEST(testAbbreviateTrailingDot);
LOGUNIT_TEST(testAbbreviateEmptyName);
LOGUNIT_TEST(testAbbreviateDegenerateName);
LOGUNIT_TEST(testAbbreviateSegmentEqualToCharCountBoundary);
LOGUNIT_TEST(testAbbreviateSegmentOneCharOverBoundary);
LOGUNIT_TEST(testAbbreviateManySegmentsDeepHierarchy);
LOGUNIT_TEST(testAbbreviateLongFinalSegmentPreserved);
LOGUNIT_TEST(testAbbreviateIdempotentOnAlreadyAbbreviated);
LOGUNIT_TEST_SUITE_END();
LoggerPtr root;
LoggerPtr logger;
public:
void setUp()
{
root = Logger::getRootLogger();
MDC::clear();
logger = Logger::getLogger(LOG4CXX_TEST_STR("java.org.apache.log4j.PatternLayoutTest"));
}
void tearDown()
{
MDC::clear();
auto rep = root->getLoggerRepository();
if (rep)
{
rep->resetConfiguration();
}
}
void test2GbMessageFormatting()
{
LogString msg;
try
{
msg.append(size_t(INT_MAX) + 1000, 'E');
}
catch (const std::exception& ex)
{
LOGLOG_DEBUG(logger, ex.what() << " - skipping 2Gb message formatting test");
return;
}
msg[msg.length() - 1] = 'X';
LogString output1;
PatternLayout l1{ LOG4CXX_STR("%-5p %c{2} - %.30m") };
l1.format(output1, std::make_shared<spi::LoggingEvent>(logger->getName(), Level::getInfo(), msg, spi::LocationInfo::getLocationUnavailable()));
LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("INFO log4j.PatternLayoutTest - EEEEEEEEEEEEEEEEEEEEEEEEEEEEEX"), output1);
PatternLayout l2{ LOG4CXX_STR("%p %.30m %p") };
LogString output2;
l2.format(output2, std::make_shared<spi::LoggingEvent>(logger->getName(), Level::getDebug(), msg, spi::LocationInfo::getLocationUnavailable()));
LOGUNIT_ASSERT_EQUAL(LOG4CXX_STR("DEBUG EEEEEEEEEEEEEEEEEEEEEEEEEEEEEX DEBUG"), output2);
}
void test1()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout1.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/patternLayout.1")));
}
void test2()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout2.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT1;
ISO8601Filter filter2;
ThreadFilter filter3;
std::vector<Filter*> filters;
filters.push_back(&filter1);
filters.push_back(&filter2);
filters.push_back(&filter3);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.2")));
}
void test3()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout3.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT1;
ISO8601Filter filter2;
ThreadFilter filter3;
std::vector<Filter*> filters;
filters.push_back(&filter1);
filters.push_back(&filter2);
filters.push_back(&filter3);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.3")));
}
// Output format:
// 06 avr. 2002 18:30:58,937 [12345] DEBUG atternLayoutTest - Message 0
void test4()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout4.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT2;
AbsoluteDateAndTimeFilter filter2;
ThreadFilter filter3;
std::vector<Filter*> filters;
filters.push_back(&filter1);
filters.push_back(&filter2);
filters.push_back(&filter3);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.4")));
}
void test5()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout5.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT2;
AbsoluteDateAndTimeFilter filter2;
ThreadFilter filter3;
std::vector<Filter*> filters;
filters.push_back(&filter1);
filters.push_back(&filter2);
filters.push_back(&filter3);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.5")));
}
void test6()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout6.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT3;
AbsoluteTimeFilter filter2;
ThreadFilter filter3;
std::vector<Filter*> filters;
filters.push_back(&filter1);
filters.push_back(&filter2);
filters.push_back(&filter3);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.6")));
}
void test7()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout7.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT3;
AbsoluteTimeFilter filter2;
ThreadFilter filter3;
std::vector<Filter*> filters;
filters.push_back(&filter1);
filters.push_back(&filter2);
filters.push_back(&filter3);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.7")));
}
void test8()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout8.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT4;
//
// combo of relative time and thread identifier
// (the \\\\1 preserve a leading space)
Filter filter2(".*]", "[main]");
std::vector<Filter*> filters;
filters.push_back(&filter1);
filters.push_back(&filter2);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.8")));
}
void test9()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout9.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT5;
ThreadFilter filter2;
std::vector<Filter*> filters;
filters.push_back(&filter1);
filters.push_back(&filter2);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.9")));
}
void test10()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout10.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT6;
ThreadFilter filter2;
LineNumberFilter filter3;
FilenameFilter filenameFilter(__FILE__, "patternlayouttest.cpp");
std::vector<Filter*> filters;
filters.push_back(&filenameFilter);
filters.push_back(&filter1);
filters.push_back(&filter2);
filters.push_back(&filter3);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.10")));
}
void test11()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout11.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT11a << PAT11b;
ThreadFilter filter2;
std::vector<Filter*> filters;
filters.push_back(&filter1);
filters.push_back(&filter2);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.11")));
}
void test12()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout12.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
ControlFilter filter1;
filter1 << PAT12;
ThreadFilter filter2;
LineNumberFilter filter3;
FilenameFilter filenameFilter(__FILE__, "patternlayouttest.cpp");
std::vector<Filter*> filters;
filters.push_back(&filenameFilter);
filters.push_back(&filter1);
filters.push_back(&filter2);
filters.push_back(&filter3);
try
{
Transformer::transform(TEMP, FILTERED, filters);
}
catch (UnexpectedFormatException& e)
{
std::cout << "UnexpectedFormatException :" << e.what() << std::endl;
throw;
}
LOGUNIT_ASSERT(Compare::compare(FILTERED, LOG4CXX_FILE("witness/patternLayout.12")));
}
void test13()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout13.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/patternLayout.13")));
}
void test14()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout14.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
common();
LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/patternLayout.14")));
}
void testMDC1()
{
auto status = PropertyConfigurator::configure(LOG4CXX_FILE("input/patternLayout.mdc.1.properties"));
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
MDC::put(LOG4CXX_TEST_STR("key1"), LOG4CXX_TEST_STR("va11"));
MDC::put(LOG4CXX_TEST_STR("key2"), LOG4CXX_TEST_STR("va12"));
logger->debug(LOG4CXX_TEST_STR("Hello World"));
MDC::clear();
LOGUNIT_ASSERT(Compare::compare(TEMP, LOG4CXX_FILE("witness/patternLayout.mdc.1")));
}
void testMDC2()
{
LogString OUTPUT_FILE = LOG4CXX_STR("output/patternLayout.mdc.2");
File WITNESS_FILE = LOG4CXX_FILE("witness/patternLayout.mdc.2");
LogString mdcMsgPattern1 = LOG4CXX_STR("%m : %X%n");
LogString mdcMsgPattern2 = LOG4CXX_STR("%m : %X{key1}%n");
LogString mdcMsgPattern3 = LOG4CXX_STR("%m : %X{key2}%n");
LogString mdcMsgPattern4 = LOG4CXX_STR("%m : %X{key3}%n");
LogString mdcMsgPattern5 = LOG4CXX_STR("%m : %X{key1},%X{key2},%X{key3}%n");
// set up appender
PatternLayoutPtr layout = PatternLayoutPtr(new PatternLayout(LOG4CXX_STR("%m%n")));
AppenderPtr appender = FileAppenderPtr(new FileAppender(layout, OUTPUT_FILE, false));
// set appender on root and set level to debug
root->addAppender(appender);
root->setLevel(Level::getDebug());
// output starting message
root->debug(LOG4CXX_TEST_STR("starting mdc pattern test"));
layout->setConversionPattern(mdcMsgPattern1);
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("empty mdc, no key specified in pattern"));
layout->setConversionPattern(mdcMsgPattern2);
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("empty mdc, key1 in pattern"));
layout->setConversionPattern(mdcMsgPattern3);
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("empty mdc, key2 in pattern"));
layout->setConversionPattern(mdcMsgPattern4);
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("empty mdc, key3 in pattern"));
layout->setConversionPattern(mdcMsgPattern5);
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("empty mdc, key1, key2, and key3 in pattern"));
MDC::put(LOG4CXX_TEST_STR("key1"), LOG4CXX_TEST_STR("value1"));
MDC::put(LOG4CXX_TEST_STR("key2"), LOG4CXX_TEST_STR("value2"));
layout->setConversionPattern(mdcMsgPattern1);
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("filled mdc, no key specified in pattern"));
layout->setConversionPattern(mdcMsgPattern2);
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("filled mdc, key1 in pattern"));
layout->setConversionPattern(mdcMsgPattern3);
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("filled mdc, key2 in pattern"));
layout->setConversionPattern(mdcMsgPattern4);
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("filled mdc, key3 in pattern"));
layout->setConversionPattern(mdcMsgPattern5);
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("filled mdc, key1, key2, and key3 in pattern"));
MDC::remove(LOG4CXX_TEST_STR("key1"));
MDC::remove(LOG4CXX_TEST_STR("key2"));
layout->setConversionPattern(LOG4CXX_STR("%m%n"));
layout->activateOptions();
root->debug(LOG4CXX_TEST_STR("finished mdc pattern test"));
LOGUNIT_ASSERT(Compare::compare(OUTPUT_FILE, WITNESS_FILE));
}
/*
* Tests for logger-name abbreviation, exercised via %c{precision} in PatternLayout.
*/
/**
* No dots at all: name shorter than or equal to any precision should be
* returned unchanged (nextDot == npos path, no truncation possible).
*/
void testAbbreviateNoDots()
{
PatternLayout layout(LOG4CXX_STR("%c{2}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("NoDotsHere")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("NoDotsHere")), result);
}
/**
* Precision larger than number of segments: entire name is preserved,
* exercising the "keep trailing/last element(s) whole" path.
*/
void testAbbreviatePrecisionExceedsSegments()
{
PatternLayout layout(LOG4CXX_STR("%c{5}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("org.apache.log4cxx")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("org.apache.log4cxx")), result);
}
/**
* Standard case: precision 2 keeps the last two dotted segments and
* drops the leading ones entirely (no ellipsis involved at this level).
*/
void testAbbreviateKeepLastTwoSegments()
{
PatternLayout layout(LOG4CXX_STR("%c{2}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("org.apache.log4cxx.PatternLayout")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("log4cxx.PatternLayout")), result);
}
/**
* Precision 1 keeps only the final segment; every prior segment,
* however long, must be dropped by abbreviate()'s erase/rewrite step.
*/
void testAbbreviateKeepLastSegmentOnly()
{
PatternLayout layout(LOG4CXX_STR("%c{1}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("com.example.verylongcompany.module.ClassName")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("ClassName")), result);
}
/**
* Precision 0: degenerate case, only the final element remains
* (mirrors log4j semantics where 0 behaves like 1 for the last element).
*/
void testAbbreviatePrecisionZero()
{
PatternLayout layout(LOG4CXX_STR("%c{0}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("a.b.c.d.Ending")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("Ending")), result);
}
/**
* Fractional / dotted-count abbreviator ("%c{1.}" family): each retained
* leading segment is itself truncated to 1 character plus a trailing dot,
* directly exercising the charCount/erase/insert logic inside abbreviate().
*/
void testAbbreviateEachSegmentToOneChar()
{
PatternLayout layout(LOG4CXX_STR("%c{1.}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("org.apache.log4cxx.PatternLayout")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("o.a.l.PatternLayout")), result);
}
/**
* Segment truncated to 2 characters ("%c{2.}"): checks charCount > 1
* truncation path (nextDot - startPos > charCount) rather than the
* charCount == 1 special case.
*/
void testAbbreviateEachSegmentToTwoChars()
{
PatternLayout layout(LOG4CXX_STR("%c{2.}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("org.apache.log4cxx.PatternLayout")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("or.ap.lo.PatternLayout")), result);
}
/**
* Segment shorter than or equal to charCount is left untouched
* (the "(nextDot - startPos) > charCount" guard should skip erase/insert).
*/
void testAbbreviateShortSegmentUnchanged()
{
PatternLayout layout(LOG4CXX_STR("%c{3.}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("a.bb.ccc.LongFinalSegment")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
// "a" (1<=3), "bb" (2<=3), "ccc" (3<=3) all pass through unabbreviated
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("a.bb.ccc.LongFinalSegment")), result);
}
/**
* Consecutive dots produce a zero-length segment; abbreviate() must
* not throw or corrupt the buffer, and should leave the empty
* segment as-is (nextDot - startPos == 0, not > charCount).
*/
void testAbbreviateConsecutiveDots()
{
PatternLayout layout(LOG4CXX_STR("%c{1.}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("org..apache.Logger")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("o..a.Logger")), result);
}
/**
* Leading dot creates an empty first segment; verifies startPos handling
* at position 0 doesn't underflow or mis-index into the buffer.
*/
void testAbbreviateLeadingDot()
{
PatternLayout layout(LOG4CXX_STR("%c{1.}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR(".apache.Logger")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR(".a.Logger")), result);
}
/**
* Trailing dot with nothing after it: the final "segment" after the
* last dot is empty, and nextDot == npos is never reached for that
* position, so the loop must still terminate correctly.
*/
void testAbbreviateTrailingDot()
{
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("org.apache.")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString maxElementCharCountResult;
PatternLayout(LOG4CXX_STR("%c{1.}")).format(maxElementCharCountResult, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("o.a.")), maxElementCharCountResult);
LogString maxElementCountResult;
PatternLayout(LOG4CXX_STR("%c{2}")).format(maxElementCountResult, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("apache.")), maxElementCountResult);
}
/**
* Empty logger name: startPos == buf.size() from the start, find()
* immediately returns npos, function must be a no-op.
*/
void testAbbreviateEmptyName()
{
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString maxElementCountResult;
PatternLayout(LOG4CXX_STR("%c{2}")).format(maxElementCountResult, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("")), maxElementCountResult);
LogString maxElementCharCountResult;
PatternLayout(LOG4CXX_STR("%c{1.}")).format(maxElementCharCountResult, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("")), maxElementCharCountResult);
}
/**
* Degenerate logger name.
*/
void testAbbreviateDegenerateName()
{
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR(".")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString maxElementCountResult;
PatternLayout(LOG4CXX_STR("%c{1}")).format(maxElementCountResult, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR(".")), maxElementCountResult);
LogString maxElementCharCountResult;
PatternLayout(LOG4CXX_STR("%c{1.}")).format(maxElementCharCountResult, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR(".")), maxElementCharCountResult);
}
/**
* Single-character segments equal to charCount exactly: boundary check
* on "> charCount" vs ">= charCount" (should NOT truncate when equal,
* since the guard is strictly "(nextDot - startPos) > charCount").
*/
void testAbbreviateSegmentEqualToCharCountBoundary()
{
PatternLayout layout(LOG4CXX_STR("%c{2.}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("ab.cd.RestOfName")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
// "ab" (len 2 == charCount 2) and "cd" (len 2 == charCount 2) both
// pass through untouched; only the final segment is never abbreviated.
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("ab.cd.RestOfName")), result);
}
/**
* Segment exactly one character longer than charCount: smallest case
* that must actually trigger the erase/insert (or rewrite) path.
*/
void testAbbreviateSegmentOneCharOverBoundary()
{
PatternLayout layout(LOG4CXX_STR("%c{2.}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("abc.de.RestOfName")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
// "abc" (len 3 > 2) truncates to "ab"; "de" (len 2 == 2) stays whole.
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("ab.de.RestOfName")), result);
}
/**
* Many short segments (deep package hierarchy): the classic case that
* exposes O(n*k) blowup in an erase/insert-per-segment implementation.
*/
void testAbbreviateManySegmentsDeepHierarchy()
{
PatternLayout layout(LOG4CXX_STR("%c{1.}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.FinalClass")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL
( LogString(LOG4CXX_STR("a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.FinalClass"))
, result
);
}
/**
* Very long single final segment combined with many short leading ones:
* ensures the tail (unabbreviated, no trailing dot) is copied intact
* even when it is far longer than charCount, since the last element is
* never subject to truncation regardless of length.
*/
void testAbbreviateLongFinalSegmentPreserved()
{
PatternLayout layout(LOG4CXX_STR("%c{1.}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("x.y.z.ThisIsAVeryLongFinalClassNameThatShouldNotBeTruncatedAtAll")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL
( LogString(LOG4CXX_STR("x.y.z.ThisIsAVeryLongFinalClassNameThatShouldNotBeTruncatedAtAll"))
, result
);
}
/**
* Idempotency / stability check: running the same abbreviation pattern
* against an already-abbreviated name should be a no-op, since every
* remaining segment is already <= charCount.
*/
void testAbbreviateIdempotentOnAlreadyAbbreviated()
{
PatternLayout layout(LOG4CXX_STR("%c{1.}"));
auto event = std::make_shared<spi::LoggingEvent>
( LOG4CXX_STR("o.a.l.PatternLayout")
, Level::getInfo()
, LOG4CXX_STR("msg")
, LOG4CXX_LOCATION
);
LogString result;
layout.format(result, event);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("o.a.l.PatternLayout")), result);
}
std::string createMessage(Pool & pool, int i)
{
std::string msg("Message ");
msg.append(pool.itoa(i));
return msg;
}
void common()
{
int i = -1;
Pool pool;
LOG4CXX_DEBUG(logger, createMessage(pool, ++i));
LOG4CXX_DEBUG(root, createMessage(pool, i));
LOG4CXX_INFO(logger, createMessage(pool, ++i));
LOG4CXX_INFO(root, createMessage(pool, i));
LOG4CXX_WARN(logger, createMessage(pool, ++i));
LOG4CXX_WARN(root, createMessage(pool, i));
LOG4CXX_ERROR(logger, createMessage(pool, ++i));
LOG4CXX_ERROR(root, createMessage(pool, i));
LOG4CXX_FATAL(logger, createMessage(pool, ++i));
LOG4CXX_FATAL(root, createMessage(pool, i));
}
private:
static const LogString FILTERED;
static const LogString TEMP;
};
const LogString PatternLayoutTest::TEMP(LOG4CXX_STR("output/patternlayout"));
const LogString PatternLayoutTest::FILTERED(LOG4CXX_STR("output/patternlayoutfiltered"));
LOGUNIT_TEST_SUITE_REGISTRATION(PatternLayoutTest);