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[event.getMarkers().size()]))
212 .withKeyValuePairs(
213 event.getKeyValuePairs().toArray(new KeyValuePair[event.getKeyValuePairs().size()]))
214 .withThrowable(event.getThrowable().orElse(null))
215 .withLevel(event.getLevel())
216 .withMessage(event.getMessage())
217 .withArguments(event.getArguments().toArray())
218 .withMdc(event.getMdc(), mdcComparator)
219 .build();
220 }
221
222 private Optional<LoggingEvent> findEvent(Predicate<LoggingEvent> predicate) {
223 return loggingEventsSupplier.get().stream().filter(predicate).findFirst();
224 }
225
226 public static class PredicateBuilder {
227
228 private static final Predicate<LoggingEvent> IGNORE_PREDICATE = event -> true;
229
230 private Predicate<LoggingEvent> messagePredicate = IGNORE_PREDICATE;
231 private Predicate<LoggingEvent> argumentsPredicate = IGNORE_PREDICATE;
232 private Predicate<LoggingEvent> markerPredicate = IGNORE_PREDICATE;
233 private Predicate<LoggingEvent> keyValuePairsPredicate = IGNORE_PREDICATE;
234 private Predicate<LoggingEvent> mdcPredicate = IGNORE_PREDICATE;
235 private Predicate<LoggingEvent> throwablePredicate = IGNORE_PREDICATE;
236 private Predicate<LoggingEvent> levelPredicate = IGNORE_PREDICATE;
237
238 public static PredicateBuilder aLog() {
239 return new PredicateBuilder();
240 }
241
242 public PredicateBuilder withLevel(Level level) {
243 levelPredicate = event -> event.getLevel().equals(level);
244 return this;
245 }
246
247
248
249
250
251 @Deprecated
252 public PredicateBuilder withMarker(Marker marker) {
253 return withMarkers(marker == null ? new Marker[] {} : new Marker[] {marker});
254 }
255
256 public PredicateBuilder withMarkers(Marker... markers) {
257 markerPredicate = event -> event.getMarkers().equals(Arrays.asList(markers));
258 return this;
259 }
260
261 public PredicateBuilder withKeyValuePairs(KeyValuePair... keyValuePair) {
262 keyValuePairsPredicate =
263 event -> event.getKeyValuePairs().equals(Arrays.asList(keyValuePair));
264 return this;
265 }
266
267 public PredicateBuilder withMessage(String message) {
268 return withMessage(message::equals);
269 }
270
271 public PredicateBuilder withMessage(Predicate<String> predicate) {
272 this.messagePredicate = event -> predicate.test(event.getMessage());
273 return this;
274 }
275
276 public PredicateBuilder withArguments(Object... arguments) {
277 return withArguments(actualArgs -> actualArgs.equals(Arrays.asList(arguments)));
278 }
279
280 public PredicateBuilder withArguments(Predicate<Collection<Object>> predicate) {
281 this.argumentsPredicate = event -> predicate.test(event.getArguments());
282 return this;
283 }
284
285 public PredicateBuilder withThrowable(Throwable throwable) {
286 return withThrowable(t -> t.equals(Optional.ofNullable(throwable)));
287 }
288
289 public PredicateBuilder withThrowable(Predicate<Optional<Throwable>> predicate) {
290 this.throwablePredicate = event -> predicate.test(event.getThrowable());
291 return this;
292 }
293
294 public PredicateBuilder withMdc(Map<String, String> mdc, MdcComparator comparator) {
295 this.mdcPredicate = event -> comparator.compare(event.getMdc(), mdc);
296 return this;
297 }
298
299 public Predicate<LoggingEvent> build() {
300 return levelPredicate
301 .and(markerPredicate)
302 .and(keyValuePairsPredicate)
303 .and(messagePredicate)
304 .and(argumentsPredicate)
305 .and(throwablePredicate)
306 .and(mdcPredicate);
307 }
308 }
309
310 public static final class MdcComparator {
311
312
313 public static final MdcComparator IGNORING = new MdcComparator((a, b) -> true);
314
315
316
317
318 public static final MdcComparator EXACT =
319 new MdcComparator((a, b) -> a.size() == b.size() && a.entrySet().containsAll(b.entrySet()));
320
321
322
323
324
325 public static final MdcComparator CONTAINING =
326 new MdcComparator((a, b) -> a.entrySet().containsAll(b.entrySet()));
327
328 private final BiPredicate<Map<String, String>, Map<String, String>> compareFunction;
329
330 private MdcComparator(BiPredicate<Map<String, String>, Map<String, String>> compareFunction) {
331 this.compareFunction = compareFunction;
332 }
333
334 public boolean compare(Map<String, String> actual, Map<String, String> expected) {
335 return compareFunction.test(actual, expected);
336 }
337 }
338 }