001/**
002 * Copyright (c) 2004-2013 QOS.ch, Copyright (C) 2015 Google Inc.
003 * All rights reserved.
004 *
005 * Permission is hereby granted, free  of charge, to any person obtaining
006 * a  copy  of this  software  and  associated  documentation files  (the
007 * "Software"), to  deal in  the Software without  restriction, including
008 * without limitation  the rights to  use, copy, modify,  merge, publish,
009 * distribute,  sublicense, and/or sell  copies of  the Software,  and to
010 * permit persons to whom the Software  is furnished to do so, subject to
011 * the following conditions:
012 *
013 * The  above  copyright  notice  and  this permission  notice  shall  be
014 * included in all copies or substantial portions of the Software.
015 *
016 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
017 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
018 * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
021 * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023 */
024
025package org.slf4j.helpers;
026
027import static org.junit.Assert.assertEquals;
028import static org.junit.Assert.assertFalse;
029import static org.junit.Assert.assertNull;
030import static org.junit.Assert.fail;
031
032import java.lang.Thread.UncaughtExceptionHandler;
033import java.util.Map;
034
035import org.junit.After;
036import org.junit.Test;
037import org.slf4j.spi.MDCAdapter;
038
039/**
040 * Tests for {@link BasicMDCAdapter}
041 * 
042 * @author Lukasz Cwik
043 */
044public class MDCAdapterTestBase {
045    
046    protected MDCAdapter mdc = instantiateMDC();
047
048    
049    
050    protected MDCAdapter instantiateMDC() {
051        return new BasicMDCAdapter();
052    }
053    
054    // leave MDC clean
055    @After
056    public void tearDown() throws Exception {
057        mdc.clear();
058    }
059
060    @Test
061    public void testSettingAndGettingWithMDC() {
062        assertNull(mdc.get("testKey"));
063        mdc.put("testKey", "testValue");
064        assertEquals(mdc.get("testKey"), "testValue");
065    }
066
067    @Test
068    public void testOverwritingAKeyInMDC() {
069        assertNull(mdc.get("testKey"));
070        mdc.put("testKey", "testValue");
071        mdc.put("testKey", "differentTestValue");
072        assertEquals(mdc.get("testKey"), "differentTestValue");
073    }
074
075    @Test
076    public void testGetCopyOfContextMapFromMDC() {
077        mdc.put("testKey", "testValue");
078        Map<String, String> copy = mdc.getCopyOfContextMap();
079        mdc.put("anotherTestKey", "anotherTestValue");
080        assertFalse(copy.size() == mdc.getCopyOfContextMap().size());
081    }
082
083    @Test
084    public void testMDCInheritsValuesFromParentThread() throws Exception {
085        mdc.put("parentKey", "parentValue");
086        runAndWait(() -> {
087            mdc.put("childKey", "childValue");
088            assertEquals("parentValue", mdc.get("parentKey"));
089        });
090    }
091
092    @Test
093    public void testMDCDoesntGetValuesFromChildThread() throws Exception {
094        mdc.put("parentKey", "parentValue");
095        runAndWait(() -> mdc.put("childKey", "childValue"));
096        assertEquals("parentValue", mdc.get("parentKey"));
097        assertNull(mdc.get("childKey"));
098    }
099
100    
101    @Test
102    public void testInvokingSetContextMap_WithANullMap_SLF4J_414() {
103        mdc.setContextMap(null);
104    }
105    
106    @Test
107    public void testMDCChildThreadCanOverwriteParentThread() throws Exception {
108        mdc.put("sharedKey", "parentValue");
109        runAndWait(() -> {
110            assertEquals("parentValue", mdc.get("sharedKey"));
111            mdc.put("sharedKey", "childValue");
112            assertEquals("childValue", mdc.get("sharedKey"));
113        });
114        assertEquals("parentValue", mdc.get("sharedKey"));
115    }
116
117    private void runAndWait(Runnable runnable) {
118        RecordingExceptionHandler handler = new RecordingExceptionHandler();
119        Thread thread = new Thread(runnable);
120        thread.setUncaughtExceptionHandler(handler);
121        thread.start();
122        try {
123            thread.join();
124        } catch (Throwable t) {
125            fail("Unexpected failure in child thread:" + t.getMessage());
126        }
127        assertFalse(handler.getMessage(), handler.hadException());
128    }
129
130    /** A {@link UncaughtExceptionHandler} that records whether the thread threw an exception. */
131    private static class RecordingExceptionHandler implements UncaughtExceptionHandler {
132        private Throwable exception;
133
134        @Override
135        public void uncaughtException(Thread t, Throwable e) {
136            exception = e;
137        }
138
139        boolean hadException() {
140            return exception != null;
141        }
142
143        String getMessage() {
144            return exception != null ? exception.getMessage() : "";
145        }
146    }
147}