View Javadoc
1   package emissary.util.shell;
2   
3   import emissary.test.core.junit5.UnitTest;
4   
5   import ch.qos.logback.classic.Level;
6   import ch.qos.logback.classic.Logger;
7   import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
8   import ch.qos.logback.classic.spi.ILoggingEvent;
9   import ch.qos.logback.core.OutputStreamAppender;
10  import org.junit.jupiter.api.AfterEach;
11  import org.junit.jupiter.api.BeforeEach;
12  import org.junit.jupiter.api.Test;
13  import org.slf4j.LoggerFactory;
14  import org.slf4j.MDC;
15  
16  import java.io.ByteArrayOutputStream;
17  import java.io.IOException;
18  import javax.annotation.Nullable;
19  
20  import static emissary.log.MDCConstants.SERVICE_LOCATION;
21  import static emissary.log.MDCConstants.SHORT_NAME;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  
24  class ProcessReaderTest extends UnitTest {
25  
26      static final String SOME_NAME = "some name";
27      static final String SOME_LOCATION = "some location";
28      static final String SOME_MESSAGE = "some message";
29      static final String NOT_SET = "[not set]";
30      static final String MSG_PATTERN = "SHORT_NAME: '%X{" + SHORT_NAME + "}' SERVICE_LOCATION: '%X{" + SERVICE_LOCATION + "}' - %m";
31      // pattern used to construct expected values using String.format(...)
32      static final String FORMAT_PATTERN = "SHORT_NAME: '%s' SERVICE_LOCATION: '%s' - %s";
33  
34      static final Logger logger = (Logger) LoggerFactory.getLogger(DummyProcessReader.class);
35      @Nullable
36      OutputStreamAppender<ILoggingEvent> appender;
37      @Nullable
38      PatternLayoutEncoder encoder;
39  
40      @BeforeEach
41      @Override
42      public void setUp() throws Exception {
43          super.setUp();
44  
45          encoder = new PatternLayoutEncoder();
46          encoder.setPattern(MSG_PATTERN);
47          encoder.setContext(logger.getLoggerContext());
48          encoder.start();
49  
50          appender = new OutputStreamAppender<>();
51          appender.setEncoder(encoder);
52          appender.setContext(logger.getLoggerContext());
53  
54          logger.addAppender(appender);
55          logger.setLevel(Level.ALL);
56  
57          MDC.put(SERVICE_LOCATION, SOME_LOCATION);
58          MDC.put(SHORT_NAME, SOME_NAME);
59      }
60  
61      @AfterEach
62      @Override
63      public void tearDown() throws Exception {
64          super.tearDown();
65          MDC.remove(SERVICE_LOCATION);
66          MDC.remove(SHORT_NAME);
67  
68          if (encoder != null) {
69              encoder.stop();
70          }
71          if (appender != null) {
72              appender.stop();
73          }
74          logger.detachAndStopAllAppenders();
75      }
76  
77      /**
78       * Used to validate that {@link MDC} values are properly passed down to child threads via the
79       * {@link ProcessReader#applyLogContextMap()}
80       */
81      @Test
82      void testFormattedLogMessageWithMDCTransfer() throws IOException, InterruptedException {
83  
84          try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
85              appender.setOutputStream(outputStream);
86              appender.start();
87  
88              DummyProcessReader objectUnderTest = new DummyProcessReader();
89              objectUnderTest.setContextMap(MDC.getCopyOfContextMap());
90              objectUnderTest.start();
91              objectUnderTest.join();
92  
93              String expected = String.format(FORMAT_PATTERN, SOME_NAME, SOME_LOCATION, SOME_MESSAGE);
94              assertEquals(expected, outputStream.toString());
95          }
96      }
97  
98      @Test
99      void testFormattedLogMessageWithoutMDCTransfer() throws IOException, InterruptedException {
100 
101         try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
102             appender.setOutputStream(outputStream);
103             appender.start();
104 
105             DummyProcessReader objectUnderTest = new DummyProcessReader();
106             // Skip the call to setContextMap()
107             // objectUnderTest.setContextMap(MDC.getCopyOfContextMap());
108             objectUnderTest.start();
109             objectUnderTest.join();
110 
111             String expected = String.format(FORMAT_PATTERN, NOT_SET, NOT_SET, SOME_MESSAGE);
112             assertEquals(expected, outputStream.toString());
113         }
114     }
115 
116     static class DummyProcessReader extends ProcessReader {
117         final Logger logger = (Logger) LoggerFactory.getLogger(DummyProcessReader.class);
118 
119         @Override
120         public void finish() {
121             // nothing to do
122         }
123 
124         @Override
125         void runImpl() {
126             // put default values in MDC map
127             MDC.put(SERVICE_LOCATION, NOT_SET);
128             MDC.put(SHORT_NAME, NOT_SET);
129 
130             // override MDC values if info was passed from parent thread
131             applyLogContextMap();
132             logger.info(SOME_MESSAGE);
133         }
134     }
135 }