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
92
93 public static final String LOG_NAME = "log";
94
95
96
97 public static final String LEVEL_NAME = "level";
98
99
100
101 public static final String MESSAGE_NAME = "message";
102
103
104
105 public static final String THROWABLE_CLASS_NAME = "throwableClassName";
106
107
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
164
165
166
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 }