/* * 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 "../logunit.h" #include #include #include #include #include "../util/transformer.h" #include "../util/compare.h" #include "../util/xmltimestampfilter.h" #include "../util/xmllineattributefilter.h" #include "../util/xmlthreadfilter.h" #include "../util/filenamefilter.h" #include #include #include "../testchar.h" #include #include #include #include #include "../xml/xlevel.h" #include #include using namespace log4cxx; using namespace log4cxx::helpers; using namespace log4cxx::xml; using namespace log4cxx::spi; #if defined(__LOG4CXX_FUNC__) #undef __LOG4CXX_FUNC__ #define __LOG4CXX_FUNC__ "X::X()" #else #error __LOG4CXX_FUNC__ expected to be defined #endif /** * Test for XMLLayout. * */ LOGUNIT_CLASS(XMLLayoutTest) { LOGUNIT_TEST_SUITE(XMLLayoutTest); LOGUNIT_TEST(testGetContentType); LOGUNIT_TEST(testIgnoresThrowable); LOGUNIT_TEST(testGetHeader); LOGUNIT_TEST(testGetFooter); LOGUNIT_TEST(testFormat); LOGUNIT_TEST(testFormatWithNDC); LOGUNIT_TEST(testGetSetLocationInfo); LOGUNIT_TEST(testActivateOptions); LOGUNIT_TEST(testProblemCharacters); LOGUNIT_TEST(testNDCWithCDATA); LOGUNIT_TEST_SUITE_END(); public: /** * Clear MDC and NDC before test. */ void setUp() { NDC::clear(); MDC::clear(); } /** * Clear MDC and NDC after test. */ void tearDown() { setUp(); } public: /** * Tests getContentType. */ void testGetContentType() { LogString expected(LOG4CXX_STR("text/plain")); LogString actual(XMLLayout().getContentType()); LOGUNIT_ASSERT(expected == actual); } /** * Tests ignoresThrowable. */ void testIgnoresThrowable() { LOGUNIT_ASSERT_EQUAL(false, XMLLayout().ignoresThrowable()); } /** * Tests getHeader. */ void testGetHeader() { Pool p; LogString header; XMLLayout().appendHeader(header, p); LOGUNIT_ASSERT_EQUAL((size_t) 0, header.size()); } /** * Tests getFooter. */ void testGetFooter() { Pool p; LogString footer; XMLLayout().appendFooter(footer, p); LOGUNIT_ASSERT_EQUAL((size_t) 0, footer.size()); } private: /** * Parses the string as the body of an XML document and returns the document element. * @param source source string. * @return document element. * @throws Exception if parser can not be constructed or source is not a valid XML document. */ static apr_xml_elem* parse(const LogString& source, Pool& p) { char backing[3000]; ByteBuffer buf(backing, sizeof(backing)); CharsetEncoderPtr encoder(CharsetEncoder::getUTF8Encoder()); LogString header(LOG4CXX_STR("")); LogString::const_iterator iter(header.begin()); encoder->encode(header, iter, buf); LOGUNIT_ASSERT(iter == header.end()); iter = source.begin(); encoder->encode(source, iter, buf); LOGUNIT_ASSERT(iter == source.end()); LogString footer(LOG4CXX_STR("")); iter = footer.begin(); encoder->encode(footer, iter, buf); buf.flip(); apr_pool_t* apr_pool = p.getAPRPool(); apr_xml_parser* parser = apr_xml_parser_create(apr_pool); LOGUNIT_ASSERT(parser != 0); apr_status_t stat = apr_xml_parser_feed(parser, buf.data(), buf.remaining()); LOGUNIT_ASSERT(stat == APR_SUCCESS); apr_xml_doc* doc = 0; stat = apr_xml_parser_done(parser, &doc); LOGUNIT_ASSERT(doc != 0); apr_xml_elem* eventSet = doc->root; LOGUNIT_ASSERT(eventSet != 0); apr_xml_elem* event = eventSet->first_child; LOGUNIT_ASSERT(event != 0); return event; } std::string getAttribute(apr_xml_elem* elem, const char* attrName) { for(apr_xml_attr* attr = elem->attr; attr != NULL; attr = attr->next) { if (strcmp(attr->name, attrName) == 0) { return attr->value; } } return ""; } std::string getText(apr_xml_elem* elem) { std::string dMessage; for(apr_text* t = elem->first_cdata.first; t != NULL; t = t->next) { dMessage.append(t->text); } return dMessage; } /** * Checks a log4j:event element against expectations. * @param element element, may not be null. * @param event event, may not be null. */ void checkEventElement( apr_xml_elem* element, LoggingEventPtr& event) { std::string tagName("event"); LOGUNIT_ASSERT_EQUAL(tagName, (std::string) element->name); LOG4CXX_ENCODE_CHAR(cLoggerName, event->getLoggerName()); LOGUNIT_ASSERT_EQUAL(cLoggerName, getAttribute(element, "logger")); LOG4CXX_ENCODE_CHAR(cLevelName, event->getLevel()->toString()); LOGUNIT_ASSERT_EQUAL(cLevelName, getAttribute(element, "level")); } /** * Checks a log4j:message element against expectations. * @param element element, may not be null. * @param message expected message. */ void checkMessageElement( apr_xml_elem* element, std::string message) { std::string tagName = "message"; LOGUNIT_ASSERT_EQUAL(tagName, (std::string) element->name); LOGUNIT_ASSERT_EQUAL(message, getText(element)); } /** * Checks a log4j:message element against expectations. * @param element element, may not be null. * @param message expected message. */ void checkNDCElement(apr_xml_elem* element, std::string message) { std::string tagName = "NDC"; LOGUNIT_ASSERT_EQUAL(tagName, (std::string) element->name); std::string dMessage = getText(element); LOGUNIT_ASSERT_EQUAL(message, dMessage); } /** * Checks a log4j:properties element against expectations. * @param element element, may not be null. * @param key key. * @param value value. */ void checkPropertiesElement( apr_xml_elem* element, std::string key, std::string value) { std::string tagName = "properties"; std::string dataTag = "data"; int childNodeCount = 0; LOGUNIT_ASSERT_EQUAL(tagName, (std::string) element->name); for(apr_xml_elem* child = element->first_child; child != NULL; child = child->next) { LOGUNIT_ASSERT_EQUAL(dataTag, (std::string) child->name); LOGUNIT_ASSERT_EQUAL(key, getAttribute(child, "name")); LOGUNIT_ASSERT_EQUAL(value, getAttribute(child, "value")); childNodeCount++; } LOGUNIT_ASSERT_EQUAL(1, childNodeCount); } public: /** * Tests formatted results. * @throws Exception if parser can not be constructed or source is not a valid XML document. */ void testFormat() { LogString logger = LOG4CXX_STR("org.apache.log4j.xml.XMLLayoutTest"); LoggingEventPtr event = new LoggingEvent( logger, Level::getInfo(), LOG4CXX_STR("Hello, World"), LOG4CXX_LOCATION); Pool p; XMLLayout layout; LogString result; layout.format(result, event, p); apr_xml_elem* parsedResult = parse(result, p); checkEventElement(parsedResult, event); int childElementCount = 0; for ( apr_xml_elem* node = parsedResult->first_child; node != NULL; node = node->next) { childElementCount++; checkMessageElement(node, "Hello, World"); } LOGUNIT_ASSERT_EQUAL(1, childElementCount); } /** * Tests formatted results with an exception. * @throws Exception if parser can not be constructed or source is not a valid XML document. */ void testFormatWithNDC() { LogString logger = LOG4CXX_STR("org.apache.log4j.xml.XMLLayoutTest"); NDC::push("NDC goes here"); LoggingEventPtr event = new LoggingEvent( logger, Level::getInfo(), LOG4CXX_STR("Hello, World"), LOG4CXX_LOCATION); Pool p; XMLLayout layout; LogString result; layout.format(result, event, p); NDC::pop(); apr_xml_elem* parsedResult = parse(result, p); checkEventElement(parsedResult, event); int childElementCount = 0; for ( apr_xml_elem* node = parsedResult->first_child; node != NULL; node = node->next) { childElementCount++; if (childElementCount == 1) { checkMessageElement(node, "Hello, World"); } else { checkNDCElement(node, "NDC goes here"); } } LOGUNIT_ASSERT_EQUAL(2, childElementCount); } /** * Tests getLocationInfo and setLocationInfo. */ void testGetSetLocationInfo() { XMLLayout layout; LOGUNIT_ASSERT_EQUAL(false, layout.getLocationInfo()); layout.setLocationInfo(true); LOGUNIT_ASSERT_EQUAL(true, layout.getLocationInfo()); layout.setLocationInfo(false); LOGUNIT_ASSERT_EQUAL(false, layout.getLocationInfo()); } /** * Tests activateOptions(). */ void testActivateOptions() { Pool p; XMLLayout layout; layout.activateOptions(p); } /** * Tests problematic characters in multiple fields. * @throws Exception if parser can not be constructed or source is not a valid XML document. */ void testProblemCharacters() { std::string problemName = "com.example.bar<>&\"'"; LogString problemNameLS = LOG4CXX_STR("com.example.bar<>&\"'"); LevelPtr level = new XLevel(6000, problemNameLS, 7); NDC::push(problemName); MDC::clear(); MDC::put(problemName, problemName); LoggingEventPtr event = new LoggingEvent(problemNameLS, level, problemNameLS, LOG4CXX_LOCATION); XMLLayout layout; layout.setProperties(true); Pool p; LogString result; layout.format(result, event, p); MDC::clear(); apr_xml_elem* parsedResult = parse(result, p); checkEventElement(parsedResult, event); int childElementCount = 0; for ( apr_xml_elem* node = parsedResult->first_child; node != NULL; node = node->next) { childElementCount++; switch(childElementCount) { case 1: checkMessageElement(node, problemName); break; case 2: checkNDCElement(node, problemName); break; case 3: checkPropertiesElement(node, problemName.c_str(), problemName.c_str()); break; default: break; } } LOGUNIT_ASSERT_EQUAL(3, childElementCount); } /** * Tests CDATA element within NDC content. See bug 37560. */ void testNDCWithCDATA() { LogString logger = LOG4CXX_STR("com.example.bar"); LevelPtr level = Level::getInfo(); std::string ndcMessage =""; NDC::push(ndcMessage); LoggingEventPtr event = new LoggingEvent( logger, level, LOG4CXX_STR("Hello, World"), LOG4CXX_LOCATION); XMLLayout layout; Pool p; LogString result; layout.format(result, event, p); NDC::clear(); apr_xml_elem* parsedResult = parse(result, p); int ndcCount = 0; for(apr_xml_elem* node = parsedResult->first_child; node != NULL; node = node->next) { if (strcmp(node->name, "NDC") == 0) { ndcCount++; LOGUNIT_ASSERT_EQUAL(ndcMessage, getText(node)); } } LOGUNIT_ASSERT_EQUAL(1, ndcCount); } }; LOGUNIT_TEST_SUITE_REGISTRATION(XMLLayoutTest);