1 package com.github.valfirst.slf4jtest;
2
3 import java.util.*;
4 import java.util.function.BiPredicate;
5 import java.util.function.Predicate;
6 import org.slf4j.Marker;
7 import org.slf4j.event.KeyValuePair;
8 import org.slf4j.event.Level;
9
10
11
12
13
14
15
16
17 public class TestLoggerAssert extends AbstractTestLoggerAssert<TestLoggerAssert> {
18
19 private MdcComparator mdcComparator = MdcComparator.EXACT;
20
21 protected TestLoggerAssert(TestLogger testLogger) {
22 super(testLogger, TestLoggerAssert.class);
23 }
24
25
26
27
28
29
30
31 public TestLoggerAssert anyThread() {
32 loggingEventsSupplier = actual::getAllLoggingEvents;
33 return this;
34 }
35
36
37
38
39
40
41
42
43
44
45
46
47 public TestLoggerAssert usingMdcComparator(MdcComparator mdcComparator) {
48 this.mdcComparator = mdcComparator;
49 return this;
50 }
51
52
53
54
55
56
57
58
59
60 public TestLoggerAssert hasLogged(Level level, String message, Object... arguments) {
61 return hasLogged(event(level, message, arguments));
62 }
63
64
65
66
67
68
69
70
71
72
73
74 public TestLoggerAssert hasLogged(
75 Throwable throwable, Level level, String message, Object... arguments) {
76 return hasLogged(event(throwable, level, message, arguments));
77 }
78
79
80
81
82
83
84
85 public TestLoggerAssert hasLogged(LoggingEvent event) {
86 return hasLogged(buildPredicate(event), "Failed to find event:%n %s", event);
87 }
88
89
90
91
92
93
94
95
96 public TestLoggerAssert hasLogged(Predicate<LoggingEvent> predicate) {
97 return hasLogged(predicate, "Failed to find log matching predicate");
98 }
99
100
101
102
103
104
105
106
107 public TestLoggerAssert hasLogged(PredicateBuilder predicate) {
108 return hasLogged(predicate.build());
109 }
110
111
112
113
114
115
116
117
118
119 public TestLoggerAssert hasNotLogged(Level level, String message, Object... arguments) {
120 return hasNotLogged(event(level, message, arguments));
121 }
122
123
124
125
126
127
128
129
130
131
132
133 public TestLoggerAssert hasNotLogged(
134 Throwable throwable, Level level, String message, Object... arguments) {
135 return hasNotLogged(event(throwable, level, message, arguments));
136 }
137
138
139
140
141
142
143
144 public TestLoggerAssert hasNotLogged(LoggingEvent event) {
145 return hasNotLogged(buildPredicate(event));
146 }
147
148
149
150
151
152
153
154
155 public TestLoggerAssert hasNotLogged(Predicate<LoggingEvent> predicate) {
156 findEvent(predicate)
157 .ifPresent(
158 loggingEvent ->
159 failWithMessage("Found %s, even though we expected not to", loggingEvent));
160
161 return this;
162 }
163
164
165
166
167
168
169
170
171 public TestLoggerAssert hasNotLogged(PredicateBuilder predicate) {
172 findEvent(predicate.build())
173 .ifPresent(
174 loggingEvent ->
175 failWithMessage("Found %s, even though we expected not to", loggingEvent));
176
177 return this;
178 }
179
180
181
182
183
184
185
186 public LevelAssert hasLevel(Level level) {
187 LevelAssert levelAssert = new LevelAssert(actual, level);
188 levelAssert.loggingEventsSupplier = loggingEventsSupplier;
189 return levelAssert;
190 }
191
192 private TestLoggerAssert hasLogged(
193 Predicate<LoggingEvent> predicate, String failureMessage, Object... arguments) {
194 if (!findEvent(predicate).isPresent()) {
195 String allEvents =
196 loggingEventsSupplier.get().stream()
197 .map(Objects::toString)
198 .reduce((first, second) -> first + "\n - " + second)
199 .map(output -> " - " + output)
200 .orElse(" <none>");
201 Object[] newArguments = Arrays.copyOf(arguments, arguments.length + 1);
202 newArguments[arguments.length] = allEvents;
203 failWithMessage(
204 failureMessage + "%n%nThe logger contained the following events:%n%s", newArguments);
205 }
206 return this;
207 }
208
209 private Predicate<LoggingEvent> buildPredicate(LoggingEvent event) {
210 return new PredicateBuilder()
211 .withMarkers(event.getMarkers().toArray(new Marker[0]))
212 .withKeyValuePairs(event.getKeyValuePairs().toArray(new KeyValuePair[0]))
213 .withThrowable(event.getThrowable().orElse(null))
214 .withLevel(event.getLevel())
215 .withMessage(event.getMessage())
216 .withArguments(event.getArguments().toArray())
217 .withMdc(event.getMdc(), mdcComparator)
218 .build();
219 }
220
221 private Optional<LoggingEvent> findEvent(Predicate<LoggingEvent> predicate) {
222 return loggingEventsSupplier.get().stream().filter(predicate).findFirst();
223 }
224
225 public static class PredicateBuilder {
226
227 private static final Predicate<LoggingEvent> IGNORE_PREDICATE = event -> true;
228
229 private Predicate<LoggingEvent> messagePredicate = IGNORE_PREDICATE;
230 private Predicate<LoggingEvent> argumentsPredicate = IGNORE_PREDICATE;
231 private Predicate<LoggingEvent> markerPredicate = IGNORE_PREDICATE;
232 private Predicate<LoggingEvent> keyValuePairsPredicate = IGNORE_PREDICATE;
233 private Predicate<LoggingEvent> mdcPredicate = IGNORE_PREDICATE;
234 private Predicate<LoggingEvent> throwablePredicate = IGNORE_PREDICATE;
235 private Predicate<LoggingEvent> levelPredicate = IGNORE_PREDICATE;
236
237 public static PredicateBuilder aLog() {
238 return new PredicateBuilder();
239 }
240
241 public PredicateBuilder withLevel(Level level) {
242 levelPredicate = event -> event.getLevel().equals(level);
243 return this;
244 }
245
246
247
248
249
250 @Deprecated
251 public PredicateBuilder withMarker(Marker marker) {
252 return withMarkers(marker == null ? new Marker[] {} : new Marker[] {marker});
253 }
254
255 public PredicateBuilder withMarkers(Marker... markers) {
256 markerPredicate = event -> event.getMarkers().equals(Arrays.asList(markers));
257 return this;
258 }
259
260 public PredicateBuilder withKeyValuePairs(KeyValuePair... keyValuePair) {
261 keyValuePairsPredicate =
262 event -> event.getKeyValuePairs().equals(Arrays.asList(keyValuePair));
263 return this;
264 }
265
266 public PredicateBuilder withMessage(String message) {
267 return withMessage(message::equals);
268 }
269
270 public PredicateBuilder withMessage(Predicate<String> predicate) {
271 this.messagePredicate = event -> predicate.test(event.getMessage());
272 return this;
273 }
274
275 public PredicateBuilder withArguments(Object... arguments) {
276 return withArguments(actualArgs -> actualArgs.equals(Arrays.asList(arguments)));
277 }
278
279 public PredicateBuilder withArguments(Predicate<Collection<Object>> predicate) {
280 this.argumentsPredicate = event -> predicate.test(event.getArguments());
281 return this;
282 }
283
284 public PredicateBuilder withThrowable(Throwable throwable) {
285 return withThrowable(t -> t.equals(Optional.ofNullable(throwable)));
286 }
287
288 public PredicateBuilder withThrowable(Predicate<Optional<Throwable>> predicate) {
289 this.throwablePredicate = event -> predicate.test(event.getThrowable());
290 return this;
291 }
292
293 public PredicateBuilder withMdc(Map<String, String> mdc, MdcComparator comparator) {
294 this.mdcPredicate = event -> comparator.compare(event.getMdc(), mdc);
295 return this;
296 }
297
298 public Predicate<LoggingEvent> build() {
299 return levelPredicate
300 .and(markerPredicate)
301 .and(keyValuePairsPredicate)
302 .and(messagePredicate)
303 .and(argumentsPredicate)
304 .and(throwablePredicate)
305 .and(mdcPredicate);
306 }
307 }
308
309 public static final class MdcComparator {
310
311
312 public static final MdcComparator IGNORING = new MdcComparator((a, b) -> true);
313
314
315
316
317 public static final MdcComparator EXACT =
318 new MdcComparator((a, b) -> a.size() == b.size() && a.entrySet().containsAll(b.entrySet()));
319
320
321
322
323
324 public static final MdcComparator CONTAINING =
325 new MdcComparator((a, b) -> a.entrySet().containsAll(b.entrySet()));
326
327 private final BiPredicate<Map<String, String>, Map<String, String>> compareFunction;
328
329 private MdcComparator(BiPredicate<Map<String, String>, Map<String, String>> compareFunction) {
330 this.compareFunction = compareFunction;
331 }
332
333 public boolean compare(Map<String, String> actual, Map<String, String> expected) {
334 return compareFunction.test(actual, expected);
335 }
336 }
337 }