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.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_EMPTY;
20 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_EXCEPTION_FROM_INNER;
21 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_INNER_RUN;
22 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_NOT_COPIED;
23 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_OUTER_RUN;
24 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_POPULATED;
25 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_PROPAGATED_TO_CHILD;
26 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REMAIN_EMPTY;
27 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REPLACED_WITH_STORED;
28 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_RETAINED_IN_CURRENT;
29 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_RETAINED_IN_PARENT;
30 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.EXPECT_REVERTED_ON_EXCEPTION;
31 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.IS_SUITABLE_LOGBACK_VERSION;
32 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.assertContextEmpty;
33 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.assertContextFields;
34 import static org.openecomp.sdc.logging.slf4j.ContextPropagationTestHelper.putUniqueValues;
35 import static org.testng.Assert.assertTrue;
38 import java.util.concurrent.atomic.AtomicBoolean;
39 import org.openecomp.sdc.logging.spi.LoggingContextService;
40 import org.testng.annotations.Test;
43 * Unit-testing logging context propagation to Runnable.
48 @SuppressWarnings("DefaultAnnotationParam")
49 public class RunnableContextPropagationTest {
51 private final LoggingContextService ctxService = new SLF4JLoggingServiceProvider();
53 @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
54 public void contextNotCopiedToChildThreadByDefault() throws InterruptedException {
56 Map<ContextField, String> values = putUniqueValues();
57 AtomicBoolean complete = new AtomicBoolean(false);
59 // create thread right away without copying context
60 Thread thread = new Thread(() -> {
61 assertContextEmpty("Data unexpectedly copied to a child thread. "
62 + "Are you using an old version of SLF4J diagnostic context implementation (e.g. logback)?");
69 assertContextFields(values, EXPECT_RETAINED_IN_CURRENT);
70 assertTrue(complete.get(), EXPECT_INNER_RUN);
73 @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
74 public void contextCopiedWhenToRunnableCalled() throws InterruptedException {
76 Map<ContextField, String> values = putUniqueValues();
77 AtomicBoolean complete = new AtomicBoolean(false);
79 // pass the runnable to the context service first
80 Thread thread = new Thread(ctxService.copyToRunnable(() -> {
81 assertContextFields(values, EXPECT_PROPAGATED_TO_CHILD);
88 assertContextFields(values, EXPECT_RETAINED_IN_CURRENT);
89 assertTrue(complete.get(), EXPECT_INNER_RUN);
92 @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
93 public void copiedContextRetainedEvenWhenAnotherPushed() throws InterruptedException {
95 Map<ContextField, String> innerValues = putUniqueValues();
96 AtomicBoolean innerComplete = new AtomicBoolean(false);
98 // should run with the context of main thread
99 Runnable inner = ctxService.copyToRunnable(() -> {
100 assertContextFields(innerValues, EXPECT_PROPAGATED_TO_CHILD);
101 innerComplete.set(true);
104 // pushes its context, but the inner must run with its own context
105 AtomicBoolean outerComplete = new AtomicBoolean(false);
106 Thread outer = new Thread(() -> {
107 Map<ContextField, String> outerValues = putUniqueValues();
109 assertContextFields(outerValues, EXPECT_REPLACED_WITH_STORED);
110 outerComplete.set(true);
116 assertContextFields(innerValues, EXPECT_RETAINED_IN_CURRENT);
117 assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
118 assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
121 @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
122 public void contextRemainsEmptyWhenParentWasEmpty() throws InterruptedException {
125 assertContextEmpty(EXPECT_EMPTY);
127 final AtomicBoolean complete = new AtomicBoolean(false);
128 Runnable runnable = ctxService.copyToRunnable(() -> {
129 assertContextEmpty(EXPECT_EMPTY);
133 Thread thread = new Thread(runnable);
137 assertContextEmpty(EXPECT_EMPTY);
138 assertTrue(complete.get(), EXPECT_INNER_RUN);
141 @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
142 public void childThreadCleanedUpAfterRunnableRuns() throws Exception {
144 Map<ContextField, String> innerValues = putUniqueValues();
145 AtomicBoolean innerComplete = new AtomicBoolean(false);
146 // should run with the context of main thread
147 Runnable inner = ctxService.copyToRunnable(() -> {
148 assertContextFields(innerValues, EXPECT_PROPAGATED_TO_CHILD);
149 innerComplete.set(true);
152 // pushes its own context, but runs the inner
153 AtomicBoolean outerComplete = new AtomicBoolean(false);
154 Thread outer = new Thread(() -> {
155 assertContextEmpty(EXPECT_NOT_COPIED);
157 assertContextEmpty(EXPECT_REMAIN_EMPTY);
158 outerComplete.set(true);
164 assertContextFields(innerValues, EXPECT_RETAINED_IN_PARENT);
165 assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
166 assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
169 @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
170 public void childThreadCleanedUpAfterException() throws Exception {
172 Map<ContextField, String> innerValues = putUniqueValues();
174 // should run with the context of main thread
175 AtomicBoolean innerComplete = new AtomicBoolean(false);
176 Runnable inner = ctxService.copyToRunnable(() -> {
177 assertContextFields(innerValues, EXPECT_PROPAGATED_TO_CHILD);
178 innerComplete.set(true);
179 throw new IllegalArgumentException();
182 // pushes its own context, but runs the inner runnable
183 AtomicBoolean outerComplete = new AtomicBoolean(false);
184 AtomicBoolean exceptionThrown = new AtomicBoolean(false);
185 Thread outer = new Thread(() -> {
187 Map<ContextField, String> outerValues = putUniqueValues();
188 assertContextFields(outerValues, EXPECT_POPULATED);
192 } catch (IllegalArgumentException e) {
193 exceptionThrown.set(true);
195 assertContextFields(outerValues, EXPECT_REVERTED_ON_EXCEPTION);
196 outerComplete.set(true);
203 assertContextFields(innerValues, EXPECT_RETAINED_IN_PARENT);
204 assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
205 assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
206 assertTrue(exceptionThrown.get(), EXPECT_EXCEPTION_FROM_INNER);