1 package emissary.test.core.junit5;
2
3 import emissary.core.BaseDataObject;
4 import emissary.core.IBaseDataObject;
5 import emissary.core.IBaseDataObjectHelper;
6 import emissary.core.IBaseDataObjectXmlCodecs.ElementDecoders;
7 import emissary.core.IBaseDataObjectXmlCodecs.ElementEncoders;
8 import emissary.place.IServiceProviderPlace;
9 import emissary.test.core.junit5.LogbackTester.SimplifiedLogEvent;
10 import emissary.util.ByteUtil;
11 import emissary.util.DisposeHelper;
12
13 import com.google.errorprone.annotations.ForOverride;
14 import org.apache.commons.lang3.ArrayUtils;
15 import org.jdom2.Document;
16 import org.junit.jupiter.params.ParameterizedTest;
17 import org.junit.jupiter.params.provider.MethodSource;
18
19 import java.nio.charset.StandardCharsets;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Map.Entry;
23 import java.util.Optional;
24 import java.util.TreeMap;
25
26 import static emissary.core.IBaseDataObjectXmlCodecs.ALWAYS_SHA256_ELEMENT_ENCODERS;
27 import static emissary.core.IBaseDataObjectXmlCodecs.DEFAULT_ELEMENT_DECODERS;
28 import static emissary.core.IBaseDataObjectXmlCodecs.SHA256_ELEMENT_ENCODERS;
29 import static org.junit.jupiter.api.Assertions.fail;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public abstract class RegressionTest extends ExtractionTest {
54
55
56
57
58
59
60
61 @ForOverride
62 protected boolean generateAnswers() {
63 return Boolean.getBoolean("generateAnswers");
64 }
65
66
67
68
69
70
71
72
73
74 @ForOverride
75 protected IBaseDataObject getInitialIbdo(final String resource) {
76 return RegressionTestUtil.getInitialIbdoWithFormInFilename(new ClearDataBaseDataObject(), resource, kff);
77 }
78
79
80
81
82
83
84
85
86
87
88 @ForOverride
89 protected void tweakInitialIbdoBeforeSerialisation(final String resource, final IBaseDataObject initialIbdo) {
90 if (initialIbdo instanceof ClearDataBaseDataObject) {
91 ((ClearDataBaseDataObject) initialIbdo).clearData();
92 } else {
93 fail("Didn't get an expected type of IBaseDataObject");
94 }
95 }
96
97
98
99
100
101
102
103
104
105
106
107 @ForOverride
108 protected void tweakFinalIbdoBeforeSerialisation(final String resource, final IBaseDataObject finalIbdo) {
109 RegressionTestUtil.tweakFinalIbdoWithFormInFilename(resource, finalIbdo);
110
111 fixDisposeRunnables(finalIbdo);
112 }
113
114
115
116
117
118
119
120
121
122 @ForOverride
123 protected void tweakFinalResultsBeforeSerialisation(final String resource, final List<IBaseDataObject> children) {
124
125 }
126
127
128
129
130
131
132
133
134
135 @ForOverride
136 protected void tweakFinalLogEventsBeforeSerialisation(final String resource, final List<SimplifiedLogEvent> simplifiedLogEvents) {
137
138 }
139
140 @Override
141 @ForOverride
142 protected String getInitialForm(final String resource) {
143 return RegressionTestUtil.getInitialFormFromFilename(resource);
144 }
145
146
147
148
149
150
151 @Deprecated
152 protected ElementDecoders getDecoders() {
153 return DEFAULT_ELEMENT_DECODERS;
154 }
155
156
157
158
159
160
161
162 protected ElementDecoders getDecoders(final String resource) {
163 return getDecoders();
164 }
165
166
167
168
169
170
171 @Deprecated
172 protected ElementEncoders getEncoders() {
173 return SHA256_ELEMENT_ENCODERS;
174 }
175
176
177
178
179
180
181
182 protected ElementEncoders getEncoders(final String resource) {
183 return getEncoders();
184 }
185
186
187
188
189
190
191
192
193 @Override
194 protected void checkAnswersPreHook(final Document answers, final IBaseDataObject payload, final List<IBaseDataObject> attachments,
195 final String tname) {
196
197 if (getLogbackLoggerName() != null) {
198 checkAnswersPreHookLogEvents(actualSimplifiedLogEvents);
199 }
200
201 final boolean alwaysHash;
202
203 if (SHA256_ELEMENT_ENCODERS.equals(getEncoders(tname))) {
204 alwaysHash = false;
205 } else if (ALWAYS_SHA256_ELEMENT_ENCODERS.equals(getEncoders(tname))) {
206 alwaysHash = true;
207 } else {
208 return;
209 }
210
211
212 for (Entry<String, byte[]> entry : new TreeMap<>(payload.getAlternateViews()).entrySet()) {
213 Optional<String> viewSha256 = hashBytesIfNonPrintable(entry.getValue(), alwaysHash);
214 viewSha256.ifPresent(s -> payload.addAlternateView(entry.getKey(), s.getBytes(StandardCharsets.UTF_8)));
215 }
216
217
218 Optional<String> payloadSha256 = hashBytesIfNonPrintable(payload.data(), alwaysHash);
219 payloadSha256.ifPresent(s -> payload.setData(s.getBytes(StandardCharsets.UTF_8)));
220
221 if (payload.getExtractedRecords() != null) {
222 for (final IBaseDataObject extractedRecord : payload.getExtractedRecords()) {
223 Optional<String> recordSha256 = hashBytesIfNonPrintable(extractedRecord.data(), alwaysHash);
224 recordSha256.ifPresent(s -> extractedRecord.setData(s.getBytes(StandardCharsets.UTF_8)));
225 }
226 }
227
228 if (attachments != null) {
229 for (final IBaseDataObject attachment : attachments) {
230 if (ByteUtil.hasNonPrintableValues(attachment.data())) {
231 Optional<String> attachmentSha256 = hashBytesIfNonPrintable(attachment.data(), alwaysHash);
232 attachmentSha256.ifPresent(s -> attachment.setData(s.getBytes(StandardCharsets.UTF_8)));
233 }
234 }
235 }
236
237 fixDisposeRunnables(payload);
238 }
239
240
241
242
243
244
245
246
247 protected Optional<String> hashBytesIfNonPrintable(byte[] bytes, final boolean alwaysHash) {
248 if (ArrayUtils.isNotEmpty(bytes) && (alwaysHash || ByteUtil.containsNonIndexableBytes(bytes))) {
249 return Optional.ofNullable(ByteUtil.sha256Bytes(bytes));
250 }
251
252 return Optional.empty();
253 }
254
255 protected static class ClearDataBaseDataObject extends BaseDataObject {
256 private static final long serialVersionUID = -8728006876784881020L;
257
258 protected void clearData() {
259 theData = null;
260 seekableByteChannelFactory = null;
261 }
262 }
263
264 @ParameterizedTest
265 @MethodSource("data")
266 @Override
267 public void testExtractionPlace(final String resource) {
268 logger.debug("Running {} test on resource {}", place.getClass().getName(), resource);
269
270 if (generateAnswers()) {
271 try {
272 generateAnswerFiles(resource);
273 } catch (final Exception e) {
274 logger.error("Error running test {}", resource, e);
275 fail("Unable to generate answer file", e);
276 }
277 }
278
279
280 super.testExtractionPlace(resource);
281 }
282
283
284
285
286
287
288
289
290
291 protected void generateAnswerFiles(final String resource) throws Exception {
292
293 final IBaseDataObject initialIbdo = getInitialIbdo(resource);
294
295 final IBaseDataObject finalIbdo = IBaseDataObjectHelper.clone(initialIbdo);
296
297 final List<IBaseDataObject> finalResults;
298 final List<SimplifiedLogEvent> finalLogEvents;
299 if (getLogbackLoggerName() == null) {
300 finalResults = place.agentProcessHeavyDuty(finalIbdo);
301 finalLogEvents = new ArrayList<>();
302 } else {
303 try (LogbackTester logbackTester = new LogbackTester(getLogbackLoggerName())) {
304 finalResults = place.agentProcessHeavyDuty(finalIbdo);
305 finalLogEvents = logbackTester.getSimplifiedLogEvents();
306 }
307 }
308
309
310 tweakInitialIbdoBeforeSerialisation(resource, initialIbdo);
311 tweakFinalIbdoBeforeSerialisation(resource, finalIbdo);
312 tweakFinalResultsBeforeSerialisation(resource, finalResults);
313 tweakFinalLogEventsBeforeSerialisation(resource, finalLogEvents);
314
315
316 RegressionTestUtil.writeAnswerXml(resource, initialIbdo, finalIbdo, finalResults, finalLogEvents, getEncoders(resource),
317 super.answerFileClassRef);
318 }
319
320 @Override
321 protected List<IBaseDataObject> processHeavyDutyHook(IServiceProviderPlace place, IBaseDataObject payload)
322 throws Exception {
323 if (getLogbackLoggerName() == null) {
324 actualSimplifiedLogEvents = new ArrayList<>();
325
326 return super.processHeavyDutyHook(place, payload);
327 } else {
328 try (LogbackTester logbackTester = new LogbackTester(getLogbackLoggerName())) {
329 final List<IBaseDataObject> attachments = super.processHeavyDutyHook(place, payload);
330
331 actualSimplifiedLogEvents = logbackTester.getSimplifiedLogEvents();
332
333 return attachments;
334 }
335 }
336 }
337
338 @Override
339 protected Document getAnswerDocumentFor(final String resource) {
340
341 return generateAnswers() ? RegressionTestUtil.getAnswerDocumentFor(resource, super.answerFileClassRef) : super.getAnswerDocumentFor(resource);
342 }
343
344 @Override
345 protected void setupPayload(final IBaseDataObject payload, final Document answers) {
346 RegressionTestUtil.setupPayload(payload, answers, getDecoders(payload.getFilename()));
347 }
348
349 @Override
350 protected void checkAnswers(final Document answers, final IBaseDataObject payload,
351 final List<IBaseDataObject> attachments, final String tname) {
352 RegressionTestUtil.checkAnswers(answers, payload, actualSimplifiedLogEvents, attachments, place.getClass().getName(), getDecoders(tname),
353 generateAnswers());
354 }
355
356
357
358
359
360
361 protected void fixDisposeRunnables(final IBaseDataObject ibdo) {
362 if (ibdo.hasParameter(DisposeHelper.KEY)) {
363 final List<Object> values = ibdo.getParameter(DisposeHelper.KEY);
364 final List<String> newValues = new ArrayList<>();
365
366 for (Object o : values) {
367 newValues.add(o.getClass().getName());
368 }
369
370 ibdo.putParameter(DisposeHelper.KEY, newValues);
371 }
372 }
373 }