View Javadoc
1   package emissary.test.core.junit5;
2   
3   import emissary.core.IBaseDataObjectXmlCodecs;
4   
5   import ch.qos.logback.classic.Level;
6   import ch.qos.logback.classic.Logger;
7   import ch.qos.logback.classic.spi.ILoggingEvent;
8   import ch.qos.logback.classic.spi.IThrowableProxy;
9   import ch.qos.logback.core.read.ListAppender;
10  import jakarta.annotation.Nullable;
11  import org.apache.commons.lang3.Validate;
12  import org.jdom2.Element;
13  import org.slf4j.LoggerFactory;
14  
15  import java.io.Closeable;
16  import java.io.IOException;
17  import java.util.ArrayList;
18  import java.util.List;
19  import java.util.Objects;
20  
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertNull;
24  
25  public class LogbackTester implements Closeable {
26      public final String name;
27      public final Logger logger;
28      public final ListAppender<ILoggingEvent> appender;
29  
30      public LogbackTester(final String name) {
31          Validate.notNull(name, "Required: name != null");
32  
33          this.name = name;
34          logger = (Logger) LoggerFactory.getLogger(name);
35          appender = new ListAppender<>();
36  
37          appender.setContext(logger.getLoggerContext());
38          appender.start();
39          logger.addAppender(appender);
40          logger.setAdditive(false);
41      }
42  
43      public void checkLogList(List<SimplifiedLogEvent> events) {
44          Validate.notNull(events, "Required: events != null");
45  
46          assertEquals(events.size(), appender.list.size(), "Expected event count does not match actual event count");
47  
48          for (int i = 0; i < appender.list.size(); i++) {
49              final ILoggingEvent item = appender.list.get(i);
50              final SimplifiedLogEvent event = events.get(i);
51              assertEquals(event.level, item.getLevel(), "Levels not equal for element " + i);
52              assertEquals(event.message, item.getFormattedMessage(), "Messages not equal for element " + i);
53              if (event.throwableClassName == null) {
54                  assertNull(item.getThrowableProxy(), "Expected no exception for element " + i);
55              } else {
56                  assertNotNull(item.getThrowableProxy(), "Expected an exception for element " + i);
57                  IThrowableProxy proxy = item.getThrowableProxy();
58                  assertEquals(event.throwableClassName, proxy.getClassName(), "Exception class name not equal for element " + i);
59                  assertEquals(event.throwableMessage, proxy.getMessage(), "Exception message not equal for element " + i);
60              }
61          }
62      }
63  
64      public List<SimplifiedLogEvent> getSimplifiedLogEvents() {
65          final List<SimplifiedLogEvent> simplifiedLogEvents = new ArrayList<>();
66  
67          for (int i = 0; i < appender.list.size(); i++) {
68              final ILoggingEvent event = appender.list.get(i);
69  
70              if (event.getThrowableProxy() == null) {
71                  simplifiedLogEvents.add(new SimplifiedLogEvent(event.getLevel(), event.getFormattedMessage(),
72                          null, null));
73              } else {
74                  simplifiedLogEvents.add(new SimplifiedLogEvent(event.getLevel(), event.getFormattedMessage(),
75                          event.getThrowableProxy().getClassName(), event.getThrowableProxy().getMessage()));
76              }
77          }
78  
79          return simplifiedLogEvents;
80      }
81  
82      @Override
83      public void close() throws IOException {
84          logger.detachAndStopAllAppenders();
85      }
86  
87      public static class SimplifiedLogEvent {
88  
89  
90          /**
91           * The XML Element name for the log events.
92           */
93          public static final String LOG_NAME = "log";
94          /**
95           * The XML Element name for the SimplifiedLogEvent level attribute.
96           */
97          public static final String LEVEL_NAME = "level";
98          /**
99           * the XML Element name for the SimplifiedLogEvent message attribute.
100          */
101         public static final String MESSAGE_NAME = "message";
102         /**
103          * The XML Element name for the SimplifiedLogEvent throwableClassName attribute.
104          */
105         public static final String THROWABLE_CLASS_NAME = "throwableClassName";
106         /**
107          * The XML Element name for the SimplifiedLogEvent throwableMessage attribute.
108          */
109         public static final String THROWABLE_MESSAGE_NAME = "throwableMessage";
110 
111         public final Level level;
112         public final String message;
113         @Nullable
114         public final String throwableClassName;
115         @Nullable
116         public final String throwableMessage;
117 
118         public SimplifiedLogEvent(Level level, String message, @Nullable Throwable throwable) {
119             this(level, message,
120                     throwable == null ? null : throwable.getClass().getName(),
121                     throwable == null ? null : throwable.getLocalizedMessage());
122         }
123 
124         public SimplifiedLogEvent(Level level, String message, @Nullable String throwableClassName, @Nullable String throwableMessage) {
125             Validate.notNull(level, "Required: level != null!");
126             Validate.notNull(message, "Required: message != null!");
127 
128             this.level = level;
129             this.message = message;
130             this.throwableClassName = throwableClassName;
131             this.throwableMessage = throwableMessage;
132         }
133 
134         @Override
135         public int hashCode() {
136             return Objects.hash(level, message, throwableClassName, throwableMessage);
137         }
138 
139         @Override
140         public boolean equals(Object obj) {
141             if (this == obj) {
142                 return true;
143             }
144 
145             if (!(obj instanceof SimplifiedLogEvent)) {
146                 return false;
147             }
148             SimplifiedLogEvent other = (SimplifiedLogEvent) obj;
149 
150             return Objects.equals(level, other.level) &&
151                     Objects.equals(message, other.message) &&
152                     Objects.equals(throwableClassName, other.throwableClassName) &&
153                     Objects.equals(throwableMessage, other.throwableMessage);
154         }
155 
156         @Override
157         public String toString() {
158             return super.toString() + " [level=" + level + ", message=" + message + ", throwableClassName="
159                     + throwableClassName + ", throwableMessage=" + throwableMessage + "]";
160         }
161 
162         /**
163          * This method returns any log events from the given XML element.
164          *
165          * @param answersElement the "answers" XML element that should contain any log events.
166          * @return the list of log events.
167          */
168         public static List<SimplifiedLogEvent> fromXml(final Element answersElement) {
169             final List<SimplifiedLogEvent> simplifiedLogEvents = new ArrayList<>();
170 
171             final List<Element> answerChildren = answersElement.getChildren();
172 
173             for (final Element answerChild : answerChildren) {
174                 final String childName = answerChild.getName();
175 
176                 if (childName.equals(LOG_NAME)) {
177                     final Level level = Level.valueOf(answerChild.getChild(LEVEL_NAME).getValue());
178                     final String message = answerChild.getChild(MESSAGE_NAME).getValue();
179                     final Element throwableClassNameElement = answerChild.getChild(THROWABLE_CLASS_NAME);
180                     final Element throwableMessageElement = answerChild.getChild(THROWABLE_MESSAGE_NAME);
181                     final String throwableClassName = throwableClassNameElement == null ? null : throwableClassNameElement.getValue();
182                     final String throwableMessage = throwableMessageElement == null ? null : throwableMessageElement.getValue();
183 
184                     simplifiedLogEvents.add(new SimplifiedLogEvent(level, message, throwableClassName, throwableMessage));
185                 }
186             }
187 
188             return simplifiedLogEvents;
189         }
190 
191         public static List<Element> toXml(final List<SimplifiedLogEvent> logEvents) {
192             final List<Element> logElements = new ArrayList<>();
193 
194             for (SimplifiedLogEvent e : logEvents) {
195                 final Element logElement = new Element(LOG_NAME);
196 
197                 logElement.addContent(IBaseDataObjectXmlCodecs.preserve(IBaseDataObjectXmlCodecs.protectedElement(LEVEL_NAME, e.level.toString())));
198                 logElement.addContent(IBaseDataObjectXmlCodecs.preserve(IBaseDataObjectXmlCodecs.protectedElement(MESSAGE_NAME, e.message)));
199                 if (e.throwableClassName != null) {
200                     logElement.addContent(
201                             IBaseDataObjectXmlCodecs.preserve(IBaseDataObjectXmlCodecs.protectedElement(THROWABLE_CLASS_NAME, e.throwableClassName)));
202                 }
203                 if (e.throwableMessage != null) {
204                     logElement.addContent(
205                             IBaseDataObjectXmlCodecs.preserve(IBaseDataObjectXmlCodecs.protectedElement(THROWABLE_MESSAGE_NAME, e.throwableMessage)));
206                 }
207                 logElements.add(logElement);
208             }
209 
210             return logElements;
211         }
212 
213     }
214 }