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 BasicMDCAdapterTest {
045    MDCAdapter mdc = new BasicMDCAdapter();
046
047    @After
048    public void tearDown() throws Exception {
049        mdc.clear();
050    }
051
052    @Test
053    public void testSettingAndGettingWithMDC() {
054        assertNull(mdc.get("testKey"));
055        mdc.put("testKey", "testValue");
056        assertEquals(mdc.get("testKey"), "testValue");
057    }
058
059    @Test
060    public void testOverwritingAKeyInMDC() {
061        assertNull(mdc.get("testKey"));
062        mdc.put("testKey", "testValue");
063        mdc.put("testKey", "differentTestValue");
064        assertEquals(mdc.get("testKey"), "differentTestValue");
065    }
066
067    @Test
068    public void testClearingMDC() {
069        mdc.put("testKey", "testValue");
070        assertFalse(mdc.getCopyOfContextMap().isEmpty());
071        mdc.clear();
072        assertNull(mdc.getCopyOfContextMap());
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(new Runnable() {
087            public void run() {
088                mdc.put("childKey", "childValue");
089                assertEquals("parentValue", mdc.get("parentKey"));
090            }
091        });
092    }
093
094    @Test
095    public void testMDCDoesntGetValuesFromChildThread() throws Exception {
096        mdc.put("parentKey", "parentValue");
097        runAndWait(new Runnable() {
098            public void run() {
099                mdc.put("childKey", "childValue");
100            }
101        });
102        assertEquals("parentValue", mdc.get("parentKey"));
103        assertNull(mdc.get("childKey"));
104    }
105
106    @Test
107    public void testMDCChildThreadCanOverwriteParentThread() throws Exception {
108        mdc.put("sharedKey", "parentValue");
109        runAndWait(new Runnable() {
110            public void run() {
111                assertEquals("parentValue", mdc.get("sharedKey"));
112                mdc.put("sharedKey", "childValue");
113                assertEquals("childValue", mdc.get("sharedKey"));
114            }
115        });
116        assertEquals("parentValue", mdc.get("sharedKey"));
117    }
118
119    private void runAndWait(Runnable runnable) throws Exception {
120        RecordingExceptionHandler handler = new RecordingExceptionHandler();
121        Thread thread = new Thread(runnable);
122        thread.setUncaughtExceptionHandler(handler);
123        thread.start();
124        try {
125            thread.join();
126        } catch (Throwable t) {
127            fail("Unexpected failure in child thread:" + t.getMessage());
128        }
129        assertFalse(handler.getMessage(), handler.hadException());
130    }
131
132    /** A {@link UncaughtExceptionHandler} that records whether the thread threw an exception. */
133    private static class RecordingExceptionHandler implements UncaughtExceptionHandler {
134        private Throwable exception;
135
136        public void uncaughtException(Thread t, Throwable e) {
137            exception = e;
138        }
139
140        boolean hadException() {
141            return exception != null;
142        }
143
144        String getMessage() {
145            return exception != null ? exception.getMessage() : "";
146        }
147    }
148}