/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2013, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package ch.qos.logback.classic.joran.action; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.util.Arrays; import java.util.HashMap; import java.util.Stack; import org.junit.Before; import org.junit.Test; import org.xml.sax.SAXParseException; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.joran.TrivialConfigurator; import ch.qos.logback.core.joran.action.Action; import ch.qos.logback.core.joran.action.IncludeAction; import ch.qos.logback.core.joran.action.NOPAction; import ch.qos.logback.core.joran.action.ext.StackAction; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.joran.spi.ElementSelector; import ch.qos.logback.core.status.Status; import ch.qos.logback.core.status.StatusChecker; import ch.qos.logback.core.testUtil.FileTestUtil; import ch.qos.logback.core.util.CoreTestConstants; public class ConditionalIncludeActionTest { Context context = new ContextBase(); StatusChecker statusChecker = new StatusChecker(context); TrivialConfigurator tc; StackAction stackAction; static private final String INCLUSION_DIR_PREFIX = CoreTestConstants.JORAN_INPUT_PREFIX + "inclusion/"; static private final String SECOND_FILE = INCLUSION_DIR_PREFIX + "second.xml"; static private final String INCLUDED_FILE = INCLUSION_DIR_PREFIX + "included.xml"; static private final String URL_TO_INCLUDE = "file:./" + INCLUDED_FILE; static private final String INVALID = INCLUSION_DIR_PREFIX + "invalid.xml"; static private final String INCLUDED_AS_RESOURCE = "asResource/joran/inclusion/includedAsResource.xml"; static private final String ANDROID_XML_RESOURCE = "asResource/AndroidManifest.xml"; @Before public void setUp() throws Exception { FileTestUtil.makeTestOutputDir(); HashMap<ElementSelector, Action> rulesMap = new HashMap<ElementSelector, Action>(); rulesMap.put(new ElementSelector("x"), new NOPAction()); rulesMap.put(new ElementSelector("x/include"), new IncludeAction()); rulesMap.put(new ElementSelector("x/findInclude"), new FindIncludeAction()); rulesMap.put(new ElementSelector("x/findInclude/include"), new ConditionalIncludeAction()); stackAction = new StackAction(); rulesMap.put(new ElementSelector("x/stack"), stackAction); tc = new TrivialConfigurator(rulesMap); tc.setContext(context); } @Test public void findsIncludeWithRegularInclude() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include file='"+ SECOND_FILE +"'/>" + "<include file='nonexistent.txt'/>" + "</findInclude>" + "<stack name='A'/>" + "<stack name='B'/>" + "<include file='"+ SECOND_FILE +"'/>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("SECOND", "A", "B", "SECOND", "C"); } @Test public void findsIncludeFromBeginningOfPathList() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include file='"+ SECOND_FILE +"'/>" + "<include file='nonexistent.txt'/>" + "<include file='"+ INVALID +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("SECOND", "C"); } @Test public void findsIncludeFromMiddleOfPathList() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include file='nonexistent.txt'/>" + "<include file='"+ SECOND_FILE +"'/>" + "<include file='nonexistent.txt'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("SECOND", "C"); } @Test public void findsIncludeFromEndOfPathList() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include file='nonexistent.txt'/>" + "<include file='nonexistent.txt'/>" + "<include file='"+ SECOND_FILE +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("SECOND", "C"); } @Test public void errorsOutForInvalidXmlAtFoundPath() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include file='"+ INVALID +"'/>" + "<include file='nonexistent.txt'/>" + "<include file='"+ SECOND_FILE +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); assertEquals(Status.ERROR, statusChecker.getHighestLevel(0)); assertTrue(statusChecker.containsException(SAXParseException.class)); verifyConfig("C"); } @Test public void configHandlesMultipleFindIncludeElements() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include file='nonexistent.txt'/>" + "<include file='"+ SECOND_FILE +"'/>" + "</findInclude>" + "<findInclude>" + "<include file='nonexistent.txt'/>" + "<include file='"+ SECOND_FILE +"'/>" + "</findInclude>" + "<stack name='B'/>" + "<findInclude>" + "<include file='nonexistent.txt'/>" + "<include file='"+ SECOND_FILE +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("SECOND", "SECOND", "B", "SECOND", "C"); } @Test public void findsIncludeFromSinglePath() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include file='"+ SECOND_FILE +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("SECOND", "C"); } @Test public void findsIncludeFromManyPaths() throws JoranException { final String NONEXIST_INCLUDES = new String(new char[1000]).replace("\0", "<include file='nonexistent.txt'/>"); final String xml = "<x>" + "<findInclude>" + NONEXIST_INCLUDES + "<include file='"+ SECOND_FILE +"'/>" + NONEXIST_INCLUDES + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("SECOND", "C"); } @Test public void findsIncludeFromFewPaths() throws JoranException { final String NONEXIST_INCLUDES = new String(new char[5]).replace("\0", "<include file='nonexistent.txt'/>"); final String xml = "<x>" + "<findInclude>" + NONEXIST_INCLUDES + "<include file='"+ SECOND_FILE +"'/>" + NONEXIST_INCLUDES + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("SECOND", "C"); } @Test public void includesResource() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include resource='"+ INCLUDED_AS_RESOURCE +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("AR_A", "AR_B", "C"); } @Test public void ignoresNonexistentResource() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include resource='nonexistent.txt'/>" + "<include resource='"+ INCLUDED_AS_RESOURCE +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("AR_A", "AR_B", "C"); } @Test public void includesUrl() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include url='"+ URL_TO_INCLUDE +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("IA", "IB", "C"); } @Test public void ignoresUnknownUrl() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include url='http://nonexistent.html'/>" + "<include url='"+ URL_TO_INCLUDE +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("IA", "IB", "C"); } @Test public void ignoresMalformedUrl() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include url='htp://nonexistent.html'/>" + "<include url='"+ URL_TO_INCLUDE +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); verifyConfig("IA", "IB", "C"); } @Test public void includesAndroidManifest() throws JoranException { final String xml = "<x>" + "<findInclude>" + "<include resource='nonexistent.txt'/>" + "<include resource='"+ ANDROID_XML_RESOURCE +"'/>" + "</findInclude>" + "<stack name='C'/>" + "</x>"; final ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes()); tc.doConfigure(stream); } void verifyConfig(String... expected) { Stack<String> witness = new Stack<String>(); witness.addAll(Arrays.asList(expected)); assertEquals(witness, stackAction.getStack()); } }