2 * Copyright © 2016-2017 European Support Limited
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.openecomp.sdc.logging.slf4j;
19 import static org.junit.Assert.assertTrue;
20 import static org.junit.Assume.assumeTrue;
21 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_EMPTY;
22 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_EXCEPTION_FROM_INNER;
23 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_INNER_RUN;
24 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_NOT_COPIED;
25 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_OUTER_RUN;
26 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_POPULATED;
27 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_PROPAGATED_TO_CHILD;
28 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REMAIN_EMPTY;
29 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REPLACED_WITH_STORED;
30 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_RETAINED_IN_CURRENT;
31 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_RETAINED_IN_PARENT;
32 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REVERTED_ON_EXCEPTION;
33 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.IS_SUITABLE_LOGBACK_VERSION;
34 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.assertContextEmpty;
35 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.assertContextFields;
36 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.putUniqueValues;
39 import java.util.concurrent.atomic.AtomicBoolean;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.openecomp.sdc.logging.spi.LoggingContextService;
45 * Unit-testing logging context propagation to Runnable.
50 @SuppressWarnings("DefaultAnnotationParam")
51 public class RunnableContextPropagationTest {
53 private final LoggingContextService ctxService = new SLF4JLoggingServiceProvider();
56 public void checkSuitableLogbackVersion() {
57 assumeTrue(IS_SUITABLE_LOGBACK_VERSION);
61 public void contextNotCopiedToChildThreadByDefault() throws InterruptedException {
63 Map<ContextField, String> values = putUniqueValues();
64 AtomicBoolean complete = new AtomicBoolean(false);
66 // create thread right away without copying context
67 Thread thread = new Thread(() -> {
68 assertContextEmpty("Data unexpectedly copied to a child thread. "
69 + "Are you using an old version of SLF4J diagnostic context implementation (e.g. logback)?");
76 assertContextFields(EXPECT_RETAINED_IN_CURRENT, values);
77 assertTrue(EXPECT_INNER_RUN, complete.get());
81 public void contextCopiedWhenToRunnableCalled() throws InterruptedException {
83 Map<ContextField, String> values = putUniqueValues();
84 AtomicBoolean complete = new AtomicBoolean(false);
86 // pass the runnable to the context service first
87 Thread thread = new Thread(ctxService.copyToRunnable(() -> {
88 assertContextFields(EXPECT_PROPAGATED_TO_CHILD, values);
95 assertContextFields(EXPECT_RETAINED_IN_CURRENT, values);
96 assertTrue(EXPECT_INNER_RUN, complete.get());
100 public void copiedContextRetainedEvenWhenAnotherPushed() throws InterruptedException {
102 Map<ContextField, String> innerValues = putUniqueValues();
103 AtomicBoolean innerComplete = new AtomicBoolean(false);
105 // should run with the context of main thread
106 Runnable inner = ctxService.copyToRunnable(() -> {
107 assertContextFields(EXPECT_PROPAGATED_TO_CHILD, innerValues);
108 innerComplete.set(true);
111 // pushes its context, but the inner must run with its own context
112 AtomicBoolean outerComplete = new AtomicBoolean(false);
113 Thread outer = new Thread(() -> {
114 Map<ContextField, String> outerValues = putUniqueValues();
116 assertContextFields(EXPECT_REPLACED_WITH_STORED, outerValues);
117 outerComplete.set(true);
123 assertContextFields(EXPECT_RETAINED_IN_CURRENT, innerValues);
124 assertTrue(EXPECT_OUTER_RUN, outerComplete.get());
125 assertTrue(EXPECT_INNER_RUN, innerComplete.get());
129 public void contextRemainsEmptyWhenParentWasEmpty() throws InterruptedException {
132 assertContextEmpty(EXPECT_EMPTY);
134 final AtomicBoolean complete = new AtomicBoolean(false);
135 Runnable runnable = ctxService.copyToRunnable(() -> {
136 assertContextEmpty(EXPECT_EMPTY);
140 Thread thread = new Thread(runnable);
144 assertContextEmpty(EXPECT_EMPTY);
145 assertTrue(EXPECT_INNER_RUN, complete.get());
149 public void childThreadCleanedUpAfterRunnableRuns() throws Exception {
151 Map<ContextField, String> innerValues = putUniqueValues();
152 AtomicBoolean innerComplete = new AtomicBoolean(false);
153 // should run with the context of main thread
154 Runnable inner = ctxService.copyToRunnable(() -> {
155 assertContextFields(EXPECT_PROPAGATED_TO_CHILD, innerValues);
156 innerComplete.set(true);
159 // pushes its own context, but runs the inner
160 AtomicBoolean outerComplete = new AtomicBoolean(false);
161 Thread outer = new Thread(() -> {
162 assertContextEmpty(EXPECT_NOT_COPIED);
164 assertContextEmpty(EXPECT_REMAIN_EMPTY);
165 outerComplete.set(true);
171 assertContextFields(EXPECT_RETAINED_IN_PARENT, innerValues);
172 assertTrue(EXPECT_OUTER_RUN, outerComplete.get());
173 assertTrue(EXPECT_INNER_RUN, innerComplete.get());
177 public void childThreadCleanedUpAfterException() throws Exception {
179 Map<ContextField, String> innerValues = putUniqueValues();
181 // should run with the context of main thread
182 AtomicBoolean innerComplete = new AtomicBoolean(false);
183 Runnable inner = ctxService.copyToRunnable(() -> {
184 assertContextFields(EXPECT_PROPAGATED_TO_CHILD, innerValues);
185 innerComplete.set(true);
186 throw new IllegalArgumentException();
189 // pushes its own context, but runs the inner runnable
190 AtomicBoolean outerComplete = new AtomicBoolean(false);
191 AtomicBoolean exceptionThrown = new AtomicBoolean(false);
192 Thread outer = new Thread(() -> {
194 Map<ContextField, String> outerValues = putUniqueValues();
195 assertContextFields(EXPECT_POPULATED, outerValues);
199 } catch (IllegalArgumentException e) {
200 exceptionThrown.set(true);
202 assertContextFields(EXPECT_REVERTED_ON_EXCEPTION, outerValues);
203 outerComplete.set(true);
210 assertContextFields(EXPECT_RETAINED_IN_PARENT, innerValues);
211 assertTrue(EXPECT_OUTER_RUN, outerComplete.get());
212 assertTrue(EXPECT_INNER_RUN, innerComplete.get());
213 assertTrue(EXPECT_EXCEPTION_FROM_INNER, exceptionThrown.get());