This is an automated email from the ASF dual-hosted git repository. swebb2066 pushed a commit to branch improve_name_abreviation_tests in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
commit 7eb5136a096294bcec4321160e8172f847e1a0c3 Author: Stephen Webb <[email protected]> AuthorDate: Sat Aug 29 13:33:47 2026 +1000 Add some name abbreviation test cases --- src/main/cpp/nameabbreviator.cpp | 2 +- src/test/cpp/patternlayouttest.cpp | 346 +++++++++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+), 1 deletion(-) diff --git a/src/main/cpp/nameabbreviator.cpp b/src/main/cpp/nameabbreviator.cpp index fca76985..43d26062 100644 --- a/src/main/cpp/nameabbreviator.cpp +++ b/src/main/cpp/nameabbreviator.cpp @@ -89,7 +89,7 @@ class MaxElementAbbreviator : public NameAbbreviator * Create new instance. * @param count maximum number of path elements to output. */ - MaxElementAbbreviator(const int count1) : count(count1) + MaxElementAbbreviator(const int count1) : count(count1 < 1 ? 1 : count1) { } diff --git a/src/test/cpp/patternlayouttest.cpp b/src/test/cpp/patternlayouttest.cpp index d07d8887..0602660b 100644 --- a/src/test/cpp/patternlayouttest.cpp +++ b/src/test/cpp/patternlayouttest.cpp @@ -39,6 +39,7 @@ #include "testchar.h" #include "logunit.h" #include <log4cxx/spi/loggerrepository.h> +#include <log4cxx/spi/loggingevent.h> #include <log4cxx/helpers/stringhelper.h> @@ -82,6 +83,23 @@ LOGUNIT_CLASS(PatternLayoutTest) 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(testAbbreviateSegmentEqualToCharCountBoundary); + LOGUNIT_TEST(testAbbreviateSegmentOneCharOverBoundary); + LOGUNIT_TEST(testAbbreviateManySegmentsDeepHierarchy); + LOGUNIT_TEST(testAbbreviateLongFinalSegmentPreserved); + LOGUNIT_TEST(testAbbreviateIdempotentOnAlreadyAbbreviated); LOGUNIT_TEST_SUITE_END(); LoggerPtr root; @@ -567,6 +585,334 @@ public: 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() + { + PatternLayout layout(LOG4CXX_STR("%c{1.}")); + auto event = std::make_shared<spi::LoggingEvent> + ( LOG4CXX_STR("org.apache.") + , Level::getInfo() + , LOG4CXX_STR("msg") + , LOG4CXX_LOCATION + ); + LogString result; + layout.format(result, event); + LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("o.a.")), result); + } + + /** + * Empty logger name: startPos == buf.size() from the start, find() + * immediately returns npos, function must be a no-op. + */ + void testAbbreviateEmptyName() + { + PatternLayout layout(LOG4CXX_STR("%c{2}")); + auto event = std::make_shared<spi::LoggingEvent> + ( LOG4CXX_STR("") + , Level::getInfo() + , LOG4CXX_STR("msg") + , LOG4CXX_LOCATION + ); + LogString result; + layout.format(result, event); + LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("")), result); + } + + /** + * 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 ");
