PropertyActionTest.java

/*
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v2.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.core.joran.action;

import java.util.Iterator;

import ch.qos.logback.core.model.ModelConstants;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import ch.qos.logback.core.Context;
import ch.qos.logback.core.ContextBase;
import ch.qos.logback.core.joran.spi.ActionException;
import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext;
import ch.qos.logback.core.joran.spi.SaxEventInterpreter;
import ch.qos.logback.core.model.ImplicitModel;
import ch.qos.logback.core.model.PropertyModel;
import ch.qos.logback.core.model.TopModel;
import ch.qos.logback.core.model.processor.DefaultProcessor;
import ch.qos.logback.core.model.processor.ImplicitModelHandler;
import ch.qos.logback.core.model.processor.ModelInterpretationContext;
import ch.qos.logback.core.model.processor.NOPModelHandler;
import ch.qos.logback.core.model.processor.PropertyModelHandler;
import ch.qos.logback.core.status.ErrorStatus;
import ch.qos.logback.core.status.Status;
import ch.qos.logback.core.testUtil.CoreTestConstants;
import ch.qos.logback.core.util.StatusPrinter;

/**
 * Test {@link PropertyAction}.
 * 
 * @author Ceki Gülcü
 */
public class PropertyActionTest {

    Context context;
    SaxEventInterpretationContext interpretationContext;
    ModelInterpretationContext mic;
    SaxEventInterpreter x;

    PropertyAction propertyAction;
    DummyAttributes atts = new DummyAttributes();
    DefaultProcessor defaultProcessor;
    TopModel topModel = new TopModel();
    String tagName = "property";

    @BeforeEach
    public void setUp() throws Exception {
        context = new ContextBase();
        interpretationContext = new SaxEventInterpretationContext(context, null);
        mic = new ModelInterpretationContext(context);
        topModel.setTag("top");
        interpretationContext.pushModel(topModel);
        mic.pushModel(topModel);
        propertyAction = new PropertyAction();
        propertyAction.setContext(context);
        defaultProcessor = new DefaultProcessor(context, mic);
        defaultProcessor.addHandler(TopModel.class, NOPModelHandler::makeInstance);
        defaultProcessor.addHandler(PropertyModel.class, PropertyModelHandler::makeInstance);
        defaultProcessor.addHandler(ImplicitModel.class, ImplicitModelHandler::makeInstance);
    }

    @AfterEach
    public void tearDown() throws Exception {
        StatusPrinter.print(context);
        context = null;
        propertyAction = null;
        atts = null;
    }

    @Test
    public void nameValuePair() throws ActionException {
        atts.setValue("name", "v1");
        atts.setValue("value", "work");
        propertyAction.begin(interpretationContext, tagName, atts);
        propertyAction.end(interpretationContext, tagName);
        defaultProcessor.process(topModel);
        Assertions.assertEquals("work", mic.getProperty("v1"));
    }

    @Test
    public void nameValuePairWithPrerequisiteSubsitution() throws ActionException {
        context.putProperty("w", "wor");
        atts.setValue("name", "v1");
        atts.setValue("value", "${w}k");
        propertyAction.begin(interpretationContext, tagName, atts);
        propertyAction.end(interpretationContext, tagName);
        defaultProcessor.process(topModel);
        Assertions.assertEquals("work", mic.getProperty("v1"));
    }

    @Test
    public void noValue() throws ActionException {
        atts.setValue("name", "v1");
        propertyAction.begin(interpretationContext, tagName, atts);
        propertyAction.end(interpretationContext, tagName);
        defaultProcessor.process(topModel);
        Assertions.assertEquals(2, context.getStatusManager().getCount());
        Assertions.assertTrue(checkError());
    }

    @Test
    public void noName() throws ActionException {
        atts.setValue("value", "v1");
        propertyAction.begin(interpretationContext, tagName, atts);
        propertyAction.end(interpretationContext, tagName);
        defaultProcessor.process(topModel);
        Assertions.assertEquals(2, context.getStatusManager().getCount());
        Assertions.assertTrue(checkError());
    }

    @Test
    public void noAttributes() throws ActionException {
        propertyAction.begin(interpretationContext, "noAttributes", atts);
        propertyAction.end(interpretationContext, "noAttributes");
        defaultProcessor.process(topModel);
        Assertions.assertEquals(2, context.getStatusManager().getCount());
        Assertions.assertTrue(checkError());
        StatusPrinter.print(context);
    }

    @Test
    public void testFileNotLoaded() throws ActionException {
        atts.setValue("file", "toto");
        atts.setValue("value", "work");
        propertyAction.begin(interpretationContext, tagName, atts);
        propertyAction.end(interpretationContext, tagName);
        defaultProcessor.process(topModel);
        Assertions.assertEquals(2, context.getStatusManager().getCount());
        Assertions.assertTrue(checkError());
    }

    @Test
    public void testLoadFileWithPrerequisiteSubsitution() throws ActionException {
        context.putProperty("STEM", CoreTestConstants.TEST_SRC_PREFIX + "input/joran");
        atts.setValue("file", "${STEM}/propertyActionTest.properties");
        propertyAction.begin(interpretationContext, tagName, atts);
        propertyAction.end(interpretationContext, tagName);
        defaultProcessor.process(topModel);
        Assertions.assertEquals("tata", mic.getProperty("v1"));
        Assertions.assertEquals("toto", mic.getProperty("v2"));
    }

    @Test
    public void testLoadFile() throws ActionException {
        atts.setValue("file", CoreTestConstants.TEST_SRC_PREFIX + "input/joran/propertyActionTest.properties");
        propertyAction.begin(interpretationContext, tagName, atts);
        propertyAction.end(interpretationContext, tagName);
        defaultProcessor.process(topModel);
        Assertions.assertEquals("tata", mic.getProperty("v1"));
        Assertions.assertEquals("toto", mic.getProperty("v2"));
    }

    @Test
    public void testLoadResource() throws ActionException {
        atts.setValue("resource", "asResource/joran/propertyActionTest.properties");
        propertyAction.begin(interpretationContext, tagName, atts);
        propertyAction.end(interpretationContext, tagName);
        defaultProcessor.process(topModel);
        Assertions.assertEquals("tata", mic.getProperty("r1"));
        Assertions.assertEquals("toto", mic.getProperty("r2"));
    }

    @Test
    public void testLoadResourceWithPrerequisiteSubsitution() throws ActionException {
        context.putProperty("STEM", "asResource/joran");
        atts.setValue("resource", "${STEM}/propertyActionTest.properties");
        propertyAction.begin(interpretationContext, tagName, atts);
        propertyAction.end(interpretationContext, tagName);
        defaultProcessor.process(topModel);
        Assertions.assertEquals("tata", mic.getProperty("r1"));
        Assertions.assertEquals("toto", mic.getProperty("r2"));
    }

    @Test
    public void testLoadNotPossible() throws ActionException {
        atts.setValue("file", "toto");
        propertyAction.begin(interpretationContext, tagName, atts);
        propertyAction.end(interpretationContext, tagName);
        defaultProcessor.process(topModel);
        Assertions.assertEquals(2, context.getStatusManager().getCount());
        Assertions.assertTrue(checkFileErrors());
    }

    private boolean checkError() {
        Iterator<Status> it = context.getStatusManager().getCopyOfStatusList().iterator();
        ErrorStatus es = (ErrorStatus) it.next();
        return ModelConstants.INVALID_ATTRIBUTES.equals(es.getMessage());
    }

    private boolean checkFileErrors() {
        Iterator<Status> it = context.getStatusManager().getCopyOfStatusList().iterator();
        ErrorStatus es1 = (ErrorStatus) it.next();
        return "Could not find properties file [toto].".equals(es1.getMessage());
    }
}