6f988c22d5849c754ea6bea1241d5cc0acd599eb
[sdc.git] /
1 /*
2  * Copyright © 2016-2017 European Support Limited
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package org.openecomp.sdc.logging.slf4j;
18
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;
36
37 import java.util.Map;
38 import java.util.concurrent.atomic.AtomicBoolean;
39 import org.openecomp.sdc.logging.spi.LoggingContextService;
40 import org.testng.annotations.Test;
41
42 /**
43  * Unit-testing logging context propagation to Runnable.
44  *
45  * @author evitaliy
46  * @since 08 Jan 18
47  */
48 @SuppressWarnings("DefaultAnnotationParam")
49 public class RunnableContextPropagationTest {
50
51     private final LoggingContextService ctxService = new SLF4JLoggingServiceProvider();
52
53     @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
54     public void contextNotCopiedToChildThreadByDefault() throws InterruptedException {
55
56         Map<ContextField, String> values = putUniqueValues();
57         AtomicBoolean complete = new AtomicBoolean(false);
58
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)?");
63             complete.set(true);
64         });
65
66         thread.start();
67         thread.join();
68
69         assertContextFields(values, EXPECT_RETAINED_IN_CURRENT);
70         assertTrue(complete.get(), EXPECT_INNER_RUN);
71     }
72
73     @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
74     public void contextCopiedWhenToRunnableCalled() throws InterruptedException {
75
76         Map<ContextField, String> values = putUniqueValues();
77         AtomicBoolean complete = new AtomicBoolean(false);
78
79         // pass the runnable to the context service first
80         Thread thread = new Thread(ctxService.copyToRunnable(() -> {
81             assertContextFields(values, EXPECT_PROPAGATED_TO_CHILD);
82             complete.set(true);
83         }));
84
85         thread.start();
86         thread.join();
87
88         assertContextFields(values, EXPECT_RETAINED_IN_CURRENT);
89         assertTrue(complete.get(), EXPECT_INNER_RUN);
90     }
91
92     @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
93     public void copiedContextRetainedEvenWhenAnotherPushed() throws InterruptedException {
94
95         Map<ContextField, String> innerValues = putUniqueValues();
96         AtomicBoolean innerComplete = new AtomicBoolean(false);
97
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);
102         });
103
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();
108             inner.run();
109             assertContextFields(outerValues, EXPECT_REPLACED_WITH_STORED);
110             outerComplete.set(true);
111         });
112
113         outer.start();
114         outer.join();
115
116         assertContextFields(innerValues, EXPECT_RETAINED_IN_CURRENT);
117         assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
118         assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
119     }
120
121     @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
122     public void contextRemainsEmptyWhenParentWasEmpty() throws InterruptedException {
123
124         ctxService.clear();
125         assertContextEmpty(EXPECT_EMPTY);
126
127         final AtomicBoolean complete = new AtomicBoolean(false);
128         Runnable runnable = ctxService.copyToRunnable(() -> {
129             assertContextEmpty(EXPECT_EMPTY);
130             complete.set(true);
131         });
132
133         Thread thread = new Thread(runnable);
134         thread.start();
135         thread.join();
136
137         assertContextEmpty(EXPECT_EMPTY);
138         assertTrue(complete.get(), EXPECT_INNER_RUN);
139     }
140
141     @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
142     public void childThreadCleanedUpAfterRunnableRuns() throws Exception {
143
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);
150         });
151
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);
156             inner.run();
157             assertContextEmpty(EXPECT_REMAIN_EMPTY);
158             outerComplete.set(true);
159         });
160
161         outer.start();
162         outer.join();
163
164         assertContextFields(innerValues, EXPECT_RETAINED_IN_PARENT);
165         assertTrue(outerComplete.get(), EXPECT_OUTER_RUN);
166         assertTrue(innerComplete.get(), EXPECT_INNER_RUN);
167     }
168
169     @Test(enabled = IS_SUITABLE_LOGBACK_VERSION)
170     public void childThreadCleanedUpAfterException() throws Exception {
171
172         Map<ContextField, String> innerValues = putUniqueValues();
173
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();
180         });
181
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(() -> {
186
187             Map<ContextField, String> outerValues = putUniqueValues();
188             assertContextFields(outerValues, EXPECT_POPULATED);
189
190             try {
191                 inner.run();
192             } catch (IllegalArgumentException e) {
193                 exceptionThrown.set(true);
194             } finally {
195                 assertContextFields(outerValues, EXPECT_REVERTED_ON_EXCEPTION);
196                 outerComplete.set(true);
197             }
198         });
199
200         outer.start();
201         outer.join();
202
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);
207     }
208 }