1 package emissary.test.core.junit5;
2
3 import ch.qos.logback.classic.Level;
4 import ch.qos.logback.classic.Logger;
5 import ch.qos.logback.classic.spi.ILoggingEvent;
6 import ch.qos.logback.classic.spi.IThrowableProxy;
7 import ch.qos.logback.core.read.ListAppender;
8 import jakarta.annotation.Nullable;
9 import org.apache.commons.lang3.Validate;
10 import org.slf4j.LoggerFactory;
11
12 import java.io.Closeable;
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Objects;
17
18 import static org.junit.jupiter.api.Assertions.assertEquals;
19 import static org.junit.jupiter.api.Assertions.assertNotNull;
20 import static org.junit.jupiter.api.Assertions.assertNull;
21
22 public class LogbackTester implements Closeable {
23 public final String name;
24 public final Logger logger;
25 public final ListAppender<ILoggingEvent> appender;
26
27 public LogbackTester(final String name) {
28 Validate.notNull(name, "Required: name != null");
29
30 this.name = name;
31 logger = (Logger) LoggerFactory.getLogger(name);
32 appender = new ListAppender<>();
33
34 appender.setContext(logger.getLoggerContext());
35 appender.start();
36 logger.addAppender(appender);
37 logger.setAdditive(false);
38 }
39
40 public void checkLogList(List<SimplifiedLogEvent> events) {
41 Validate.notNull(events, "Required: events != null");
42
43 assertEquals(events.size(), appender.list.size(), "Expected event count does not match actual event count");
44
45 for (int i = 0; i < appender.list.size(); i++) {
46 final ILoggingEvent item = appender.list.get(i);
47 final SimplifiedLogEvent event = events.get(i);
48 assertEquals(event.level, item.getLevel(), "Levels not equal for element " + i);
49 assertEquals(event.message, item.getFormattedMessage(), "Messages not equal for element " + i);
50 if (event.throwableClassName == null) {
51 assertNull(item.getThrowableProxy(), "Expected no exception for element " + i);
52 } else {
53 assertNotNull(item.getThrowableProxy(), "Expected an exception for element " + i);
54 IThrowableProxy proxy = item.getThrowableProxy();
55 assertEquals(event.throwableClassName, proxy.getClassName(), "Exception class name not equal for element " + i);
56 assertEquals(event.throwableMessage, proxy.getMessage(), "Exception message not equal for element " + i);
57 }
58 }
59 }
60
61 public List<SimplifiedLogEvent> getSimplifiedLogEvents() {
62 final List<SimplifiedLogEvent> simplifiedLogEvents = new ArrayList<>();
63
64 for (int i = 0; i < appender.list.size(); i++) {
65 final ILoggingEvent event = appender.list.get(i);
66
67 if (event.getThrowableProxy() == null) {
68 simplifiedLogEvents.add(new SimplifiedLogEvent(event.getLevel(), event.getFormattedMessage(),
69 null, null));
70 } else {
71 simplifiedLogEvents.add(new SimplifiedLogEvent(event.getLevel(), event.getFormattedMessage(),
72 event.getThrowableProxy().getClassName(), event.getThrowableProxy().getMessage()));
73 }
74 }
75
76 return simplifiedLogEvents;
77 }
78
79 @Override
80 public void close() throws IOException {
81 logger.detachAndStopAllAppenders();
82 }
83
84 public static class SimplifiedLogEvent {
85 public final Level level;
86 public final String message;
87 @Nullable
88 public final String throwableClassName;
89 @Nullable
90 public final String throwableMessage;
91
92 public SimplifiedLogEvent(Level level, String message, @Nullable Throwable throwable) {
93 this(level, message,
94 throwable == null ? null : throwable.getClass().getName(),
95 throwable == null ? null : throwable.getLocalizedMessage());
96 }
97
98 public SimplifiedLogEvent(Level level, String message, @Nullable String throwableClassName, @Nullable String throwableMessage) {
99 Validate.notNull(level, "Required: level != null!");
100 Validate.notNull(message, "Required: message != null!");
101
102 this.level = level;
103 this.message = message;
104 this.throwableClassName = throwableClassName;
105 this.throwableMessage = throwableMessage;
106 }
107
108 @Override
109 public int hashCode() {
110 return Objects.hash(level, message, throwableClassName, throwableMessage);
111 }
112
113 @Override
114 public boolean equals(Object obj) {
115 if (this == obj) {
116 return true;
117 }
118
119 if (!(obj instanceof SimplifiedLogEvent)) {
120 return false;
121 }
122 SimplifiedLogEvent other = (SimplifiedLogEvent) obj;
123
124 return Objects.equals(level, other.level) &&
125 Objects.equals(message, other.message) &&
126 Objects.equals(throwableClassName, other.throwableClassName) &&
127 Objects.equals(throwableMessage, other.throwableMessage);
128 }
129
130 @Override
131 public String toString() {
132 return super.toString() + " [level=" + level + ", message=" + message + ", throwableClassName="
133 + throwableClassName + ", throwableMessage=" + throwableMessage + "]";
134 }
135 }
136 }