    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}