View Javadoc
1   package emissary.core;
2   
3   import emissary.core.channels.FillChannelFactory;
4   import emissary.core.channels.InMemoryChannelFactory;
5   import emissary.core.channels.SeekableByteChannelFactory;
6   import emissary.core.channels.SeekableByteChannelHelper;
7   import emissary.directory.DirectoryEntry;
8   import emissary.pickup.Priority;
9   import emissary.test.core.junit5.LogbackTester;
10  import emissary.test.core.junit5.UnitTest;
11  
12  import ch.qos.logback.classic.Level;
13  import com.google.common.collect.ArrayListMultimap;
14  import com.google.common.collect.ListMultimap;
15  import jakarta.annotation.Nullable;
16  import org.apache.commons.io.IOUtils;
17  import org.junit.jupiter.api.AfterEach;
18  import org.junit.jupiter.api.BeforeEach;
19  import org.junit.jupiter.api.Test;
20  import org.junit.jupiter.params.ParameterizedTest;
21  import org.junit.jupiter.params.provider.Arguments;
22  import org.junit.jupiter.params.provider.MethodSource;
23  import org.mockito.Mockito;
24  
25  import java.io.ByteArrayOutputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.lang.reflect.Field;
29  import java.nio.ByteBuffer;
30  import java.nio.channels.SeekableByteChannel;
31  import java.nio.charset.StandardCharsets;
32  import java.time.Instant;
33  import java.util.ArrayList;
34  import java.util.Arrays;
35  import java.util.Collection;
36  import java.util.Collections;
37  import java.util.HashMap;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Set;
41  import java.util.TreeSet;
42  import java.util.concurrent.ThreadLocalRandom;
43  import java.util.stream.Stream;
44  
45  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
46  import static org.junit.jupiter.api.Assertions.assertEquals;
47  import static org.junit.jupiter.api.Assertions.assertFalse;
48  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
49  import static org.junit.jupiter.api.Assertions.assertNotEquals;
50  import static org.junit.jupiter.api.Assertions.assertNotNull;
51  import static org.junit.jupiter.api.Assertions.assertNull;
52  import static org.junit.jupiter.api.Assertions.assertThrows;
53  import static org.junit.jupiter.api.Assertions.assertTrue;
54  import static org.junit.jupiter.api.Assertions.fail;
55  
56  class BaseDataObjectTest extends UnitTest {
57  
58      @Nullable
59      private BaseDataObject b = null;
60  
61      @Override
62      @BeforeEach
63      public void setUp() throws Exception {
64          this.b = new BaseDataObject("This is a test".getBytes(), "filename.txt");
65          this.b.pushCurrentForm("ONE");
66          this.b.pushCurrentForm("TWO");
67          this.b.pushCurrentForm("THREE");
68      }
69  
70      @Override
71      @AfterEach
72      public void tearDown() throws Exception {
73          super.tearDown();
74          this.b = null;
75      }
76  
77      @Test
78      void testInterface() {
79          // This should pass by compilation, but in case anyone
80          // ever thinks of taking it out, this may remind them...
81          assertInstanceOf(IBaseDataObject.class, this.b, "Implements the interface");
82      }
83  
84      @Test
85      void testConstructors() {
86          final BaseDataObject b2 = new BaseDataObject("This is a test".getBytes(), "filename.txt", "ONE");
87          assertEquals("ONE", b2.currentForm(), "Current form in ctor");
88          assertNotNull(b2.getCreationTimestamp());
89  
90          final BaseDataObject b3 = new BaseDataObject("test".getBytes(), "filename.txt", null);
91          assertEquals("", b3.currentForm(), "Current form with null in ctor");
92          assertNotNull(b3.getCreationTimestamp());
93  
94          final BaseDataObject tld = new BaseDataObject();
95          final byte[] data = "content".getBytes(StandardCharsets.UTF_8);
96          final String fileName = "aChild";
97          final String form = "UNKNOWN";
98          final BaseDataObject b4 = new BaseDataObject(data, fileName, form, tld);
99          assertEquals(fileName, b4.getFilename());
100         assertEquals(form, b4.currentForm());
101         assertNotNull(b4.getCreationTimestamp());
102         assertEquals(tld, b4.getTld());
103 
104         final String fileType = "TEXT";
105         BaseDataObject b5 = new BaseDataObject(data, fileName, form, fileType, tld);
106         assertEquals(fileName, b5.getFilename());
107         assertEquals(form, b5.currentForm());
108         assertEquals(fileType, b5.getFileType());
109         assertNotNull(b5.getCreationTimestamp());
110         assertEquals(tld, b5.getTld());
111 
112     }
113 
114     @Test
115     void testDataLength() {
116         assertEquals("This is a test".length(), this.b.dataLength(), "Data length");
117     }
118 
119     @Test
120     void testNullDataLength() {
121         this.b.setData(null);
122         assertEquals(0, this.b.dataLength(), "Null data length");
123     }
124 
125     @Test
126     void testDataLengthWithChannel() {
127         this.b.setData(null);
128         assertEquals(0, this.b.dataLength());
129         this.b.setChannelFactory(InMemoryChannelFactory.create(new byte[10]));
130         assertEquals(10, this.b.dataLength());
131         this.b.setData(new byte[10]);
132         assertEquals(10, this.b.dataLength());
133     }
134 
135 
136     @Test
137     void testLargestFile() throws IOException {
138         final BaseDataObject bdo = new BaseDataObject();
139         final long fileSize = Long.MAX_VALUE;
140         final SeekableByteChannelFactory sbcf = FillChannelFactory.create(fileSize, (byte) 0);
141         bdo.setChannelFactory(sbcf);
142         assertEquals(fileSize, bdo.getChannelSize());
143         assertEquals(BaseDataObject.MAX_BYTE_ARRAY_SIZE, bdo.dataLength());
144 
145         final SeekableByteChannel sbc = sbcf.create();
146         final long newPosition = ThreadLocalRandom.current().nextLong(Integer.MAX_VALUE, Long.MAX_VALUE);
147         sbc.position(newPosition);
148         assertEquals(newPosition, sbc.position());
149         final ByteBuffer buff = ByteBuffer.allocate(16);
150         final int bytesRead = sbc.read(buff);
151         assertEquals(16, bytesRead);
152         final byte[] zeroByteArray = new byte[16];
153         Arrays.fill(zeroByteArray, (byte) 0);
154         assertArrayEquals(zeroByteArray, buff.array());
155     }
156 
157     @Test
158     void testDataLengthWhenLargerThanMaxInt() throws IOException {
159         final Long higherLength = Integer.MAX_VALUE + 100L;
160         final String testString = "test data";
161         BaseDataObject bdo = Mockito.spy(this.b);
162         Mockito.when(bdo.getChannelSize()).thenReturn(higherLength);
163         bdo.setChannelFactory(SeekableByteChannelHelper.memory(testString.getBytes()));
164         assertEquals(higherLength.longValue(), bdo.getChannelSize());
165         assertEquals(BaseDataObject.MAX_BYTE_ARRAY_SIZE, bdo.dataLength());
166     }
167 
168     @Test
169     void testExceptionWhenGettingDataLengthWithChannel() throws IOException {
170         BaseDataObject bdo = Mockito.spy(this.b);
171         final String testString = "test data";
172         bdo.setChannelFactory(SeekableByteChannelHelper.memory(testString.getBytes()));
173         Mockito.when(bdo.getChannelSize()).thenThrow(IOException.class);
174         assertEquals(0, bdo.dataLength());
175     }
176 
177     @Test
178     void testGetDataWhenSmallerThanMaxInt() throws IOException {
179         final String testString = "test data";
180         this.b.setChannelFactory(SeekableByteChannelHelper.memory(testString.getBytes()));
181         assertEquals(testString.getBytes().length, this.b.dataLength());
182     }
183 
184     @Test
185     void testExceptionWhenGettingChannelFactory() throws IOException {
186         // Mock up a BDO with an SBCF and SBC instance that we control
187         // Create an SBCF that we can work with
188         final SeekableByteChannelFactory sbcf = Mockito.spy(SeekableByteChannelHelper.memory("Test data".getBytes()));
189         // Hook into the SBCF
190         this.b.setChannelFactory(sbcf);
191         // Hook into an SBC
192         try (SeekableByteChannel sbc = Mockito.spy(this.b.getChannelFactory().create())) {
193             // Always return this spied SBC
194             Mockito.when(sbcf.create()).thenReturn(sbc);
195             // Kick an exception when calling size()
196             Mockito.when(sbc.size()).thenThrow(IOException.class);
197             // We return an empty byte array in an IOException case
198             assertArrayEquals(new byte[0], this.b.data());
199         }
200     }
201 
202     @Test
203     void testExceptionWhenGettingChannelSize() throws IOException {
204         // Hook into the SBCF
205         final SeekableByteChannelFactory sbcf = Mockito.spy(SeekableByteChannelHelper.memory("Test data".getBytes()));
206         this.b.setChannelFactory(sbcf);
207         // Hook into an SBC
208         try (SeekableByteChannel sbc = Mockito.spy(this.b.getChannelFactory().create())) {
209             // Always return this spied SBC
210             Mockito.when(sbcf.create()).thenReturn(sbc);
211             // Kick an exception when asking for the size
212             Mockito.when(sbc.size()).thenThrow(IOException.class);
213             // Throw an exception
214             assertThrows(IOException.class, () -> this.b.getChannelSize(), "Should throw the IOException up to the caller");
215         }
216     }
217 
218     @Test
219     void testIsDataEmpty() throws IOException {
220         final IBaseDataObject nullDataIbdo = new BaseDataObject();
221         final IBaseDataObject zeroLengthDataIbdo = new BaseDataObject(new byte[0], "zeroLengthDataIbdo");
222         final IBaseDataObject nonZeroLengthDataIbdo = new BaseDataObject(new byte[1], "nonZeroLengthDataIbdo");
223 
224         assertFalse(nullDataIbdo.hasContent());
225         assertFalse(zeroLengthDataIbdo.hasContent());
226         assertTrue(nonZeroLengthDataIbdo.hasContent());
227     }
228 
229     @Test
230     void testDataLengthBothNull() {
231         BaseDataObject bdo = new BaseDataObject();
232         assertEquals(0, bdo.dataLength());
233     }
234 
235     @Test
236     void testZeroLengthDataSlice() {
237         final byte[] ary = new byte[0];
238         this.b.setData(ary, 0, 0);
239         assertEquals(0, this.b.dataLength(), "Null data length is zero");
240     }
241 
242     @Test
243     void testDataSliceLength() {
244         final byte[] ary = "abcdefghijk".getBytes();
245         this.b.setData(ary, 3, 4);
246         assertEquals(4, this.b.dataLength(), "Array slice must use length");
247     }
248 
249     @Test
250     void testDataSliceData() {
251         final byte[] ary = "abcdefghijk".getBytes();
252         this.b.setData(ary, 3, 4);
253         assertEquals("defg", new String(this.b.data()), "Array slice must use proper data");
254     }
255 
256     @Test
257     void testNullData() {
258         this.b.setData(null);
259         assertNotNull(this.b.data(), "Data array can never be null");
260     }
261 
262     @Test
263     void testNullDataSlice() {
264         this.b.setData(null, 3, 4);
265         assertNotNull(this.b.data(), "Data slice can never be null");
266     }
267 
268 
269     @Test
270     void testShortName() {
271         assertEquals("filename.txt", this.b.shortName(), "Short name");
272     }
273 
274     @Test
275     @SuppressWarnings("ByteBufferBackingArray")
276     void testByteArrays() {
277         this.b.setHeader("A fine header".getBytes());
278         this.b.setFooter("A good footer".getBytes());
279         this.b.addAlternateView("TESTVIEW", "alternate view".getBytes());
280         assertEquals("This is a test", new String(this.b.data()), "Data bytes retrieved");
281         assertEquals("A fine header", new String(this.b.header()), "Header bytes");
282         assertEquals("A good footer", new String(this.b.footer()), "Footer bytes");
283         assertEquals("alternate view", new String(this.b.getAlternateView("TESTVIEW")), "Alt view bytes");
284 
285         // remove @SuppressWarnings("ByteBufferBackingArray") when these deprecated calls are removed/updated
286         final ByteBuffer hb = this.b.headerBuffer();
287         final ByteBuffer fb = this.b.footerBuffer();
288         final ByteBuffer db = this.b.dataBuffer();
289         final ByteBuffer vb = this.b.getAlternateViewBuffer("TESTVIEW");
290         assertEquals("A fine header".length(), hb.array().length, "Byte buffer on header");
291         assertEquals("A good footer".length(), fb.array().length, "Byte buffer on footer");
292         assertEquals("This is a test".length(), db.array().length, "Byte buffer on data");
293         assertNotNull(vb, "Byte buffer on view");
294         assertEquals("alternate view".length(), vb.array().length, "Byte buffer on view");
295 
296         this.b.addAlternateView("TESTVIEW", null);
297         assertNull(this.b.getAlternateView("TESTVIEW"), "Byte buffer on removed view");
298     }
299 
300     @Test
301     void testNonExistentAltViews() {
302         assertNull(this.b.getAlternateView("NOSUCHVIEW"), "No such view");
303     }
304 
305     @Test
306     void testNonExistentAltViewBuffer() {
307         assertNull(this.b.getAlternateViewBuffer("NOSUCHVIEW"), "Byte buffer on no such view");
308     }
309 
310     @Test
311     void testAltViews() {
312         this.b.addAlternateView("TESTVIEW1", "alternate view".getBytes());
313         this.b.addAlternateView("TESTVIEW2", "alternate view".getBytes());
314         this.b.addAlternateView("TESTVIEW3", "alternate view".getBytes());
315 
316         this.b.addAlternateView("TESTVIEW2", null);
317         assertNull(this.b.getAlternateView("TESTVIEW2"), "Null view after removal");
318         assertNull(this.b.getAlternateViewBuffer("TESTVIEW2"), "Empty byte buffer after removal");
319     }
320 
321     @Test
322     void testAltViewSlice() {
323         this.b.addAlternateView("TESTVIEW1", "abcdefghij".getBytes(), 3, 4);
324         assertEquals("defg", new String(this.b.getAlternateView("TESTVIEW1")), "Alt view slice must use proper data");
325     }
326 
327     @Test
328     void testSetOfAltViewNames() {
329         this.b.addAlternateView("TESTVIEW1", "alternate view".getBytes());
330         this.b.addAlternateView("TESTVIEW2", "alternate view".getBytes());
331         this.b.addAlternateView("TESTVIEW3", "alternate view".getBytes());
332         final Set<String> vnames = this.b.getAlternateViewNames();
333         assertEquals(3, vnames.size(), "Count of view names");
334 
335         List<String> source = new ArrayList<>(vnames);
336         List<String> sorted = new ArrayList<>(vnames);
337         Collections.sort(sorted);
338         assertEquals(sorted, source, "Views are sorted");
339     }
340 
341     @Test
342     void testMapOfAltViews() {
343         this.b.addAlternateView("TESTVIEW1", "alternate view".getBytes());
344         this.b.addAlternateView("TESTVIEW2", "alternate view".getBytes());
345         this.b.addAlternateView("TESTVIEW3", "alternate view".getBytes());
346         final Map<String, byte[]> v = this.b.getAlternateViews();
347         assertEquals(3, v.size(), "Count of views");
348 
349         List<String> source = new ArrayList<>(v.keySet());
350         List<String> sorted = new ArrayList<>(v.keySet());
351         Collections.sort(sorted);
352         assertEquals(sorted, source, "Views are sorted");
353     }
354 
355     @Test
356     void testAppendAltView() {
357         this.b.addAlternateView("T1", "alternate view".getBytes());
358         this.b.appendAlternateView("T1", " more stuff".getBytes());
359         assertEquals("alternate view more stuff", new String(this.b.getAlternateView("T1")), "Appended alternate view contents");
360     }
361 
362     @Test
363     void testAppendAltViewOnEmpty() {
364         this.b.appendAlternateView("T1", "more stuff".getBytes());
365         assertEquals("more stuff", new String(this.b.getAlternateView("T1")), "Appended alternate view contents");
366     }
367 
368     @Test
369     void testAppendAltViewSlice() {
370         this.b.addAlternateView("T1", "alternate view".getBytes());
371         this.b.appendAlternateView("T1", "xx more stuff xx".getBytes(), 2, 11);
372         assertEquals("alternate view more stuff", new String(this.b.getAlternateView("T1")), "Appended alternate view contents");
373     }
374 
375     @Test
376     void testAppendAltViewSliceOnEmpty() {
377         this.b.appendAlternateView("T1", "xx more stuff xx".getBytes(), 3, 10);
378         assertEquals("more stuff", new String(this.b.getAlternateView("T1")), "Appended alternate view contents");
379     }
380 
381     @Test
382     void testWindowsShortName() {
383         this.b.setFilename("c:\\Program Files\\Windows\\filename.txt");
384         assertEquals("filename.txt", this.b.shortName(), "Short windows name");
385     }
386 
387     @Test
388     void testUnixShortName() {
389         this.b.setFilename("/usr/local/share/filename.txt");
390         assertEquals("filename.txt", this.b.shortName(), "Short unix name");
391     }
392 
393     @Test
394     void testFilename() {
395         assertEquals("filename.txt", this.b.getFilename(), "Short name");
396     }
397 
398     @Test
399     void testCurrentFormTop() {
400         assertEquals("THREE", this.b.currentForm(), "Current form push");
401     }
402 
403     @Test
404     void testCurrentFormEnqueue() {
405         final int i = this.b.enqueueCurrentForm("FOUR");
406         assertEquals(4, i, "Enqueue return value");
407         assertEquals("THREE", this.b.currentForm(), "Current form push");
408         assertEquals("ONE", this.b.currentFormAt(2), "Prev bottom form");
409         assertEquals("FOUR", this.b.currentFormAt(3), "Bottom form");
410     }
411 
412     @Test
413     void testPopCurrentForm() {
414         final String s = this.b.popCurrentForm();
415         assertEquals("THREE", s, "Pop return value");
416         assertEquals("TWO", this.b.currentForm(), "Current form after pop");
417         this.b.popCurrentForm();
418         assertEquals("ONE", this.b.currentForm(), "Current form after pop pop");
419         this.b.popCurrentForm();
420         assertEquals("", this.b.currentForm(), "No forms left");
421     }
422 
423     @Test
424     void testCurrentFormAt() {
425         assertEquals("TWO", this.b.currentFormAt(1), "Current form at");
426     }
427 
428     @Test
429     void testSearchCurrentForm() {
430         assertEquals(1, this.b.searchCurrentForm("TWO"), "Successful search");
431 
432         final List<String> l = new ArrayList<>();
433         l.add("CHOP");
434         l.add("TWO");
435         l.add("CHIP");
436         assertEquals("TWO", this.b.searchCurrentForm(l), "Successful list search");
437 
438     }
439 
440     @Test
441     void testReplaceCurrentForm() {
442         this.b.replaceCurrentForm("NEWONE");
443         assertEquals(-1, this.b.searchCurrentForm("ONE"), "Not found search");
444         assertEquals(-1, this.b.searchCurrentForm("TWO"), "Not found search");
445         assertEquals(-1, this.b.searchCurrentForm("THREE"), "Not found search");
446         assertEquals(0, this.b.searchCurrentForm("NEWONE"), "Successful search");
447         assertEquals(1, this.b.currentFormSize(), "One form left");
448 
449         this.b.replaceCurrentForm(null);
450         assertEquals(-1, this.b.searchCurrentForm("NEWONE"), "Not found search");
451         assertEquals(0, this.b.currentFormSize(), "No forms left");
452     }
453 
454 
455     @Test
456     void testBadSearchCurrentForm() {
457         assertEquals(-1, this.b.searchCurrentForm("SEVENTEEN"), "Not found search");
458 
459         final List<String> l2 = new ArrayList<>();
460         l2.add("SHIP");
461         l2.add("SHAPE");
462         assertNull(this.b.searchCurrentForm(l2), "Search but no match");
463     }
464 
465     @Test
466     void testFormSize() {
467         assertEquals(3, this.b.currentFormSize(), "Form stack size");
468     }
469 
470     @Test
471     void testDeleteFormFromBottom() {
472         this.b.deleteCurrentFormAt(0);
473         assertEquals("TWO", this.b.currentForm(), "Form remaining on bottom");
474         assertEquals(2, this.b.currentFormSize(), "Stack size after delete");
475     }
476 
477     @Test
478     void testDeleteFormFromTop() {
479         this.b.deleteCurrentFormAt(2);
480         assertEquals("THREE", this.b.currentForm(), "Form remaining on top");
481         assertEquals(2, this.b.currentFormSize(), "Stack size after delete");
482     }
483 
484     @Test
485     void testDeleteFormFromMiddle() {
486         final int i = this.b.deleteCurrentFormAt(1);
487         assertEquals(2, i, "Delete return value");
488         assertEquals("THREE", this.b.currentForm(), "Form remaining on top");
489         assertEquals("ONE", this.b.currentFormAt(1), "Form remaining on bottom");
490         assertEquals(2, this.b.currentFormSize(), "Stack size after delete");
491     }
492 
493     @Test
494     void testDeleteFormFromIllegalPosition() {
495         this.b.deleteCurrentFormAt(7);
496         assertEquals(3, this.b.currentFormSize(), "Stack size after delete");
497         this.b.deleteCurrentFormAt(-1);
498         assertEquals(3, this.b.currentFormSize(), "Stack size after delete");
499     }
500 
501     @Test
502     void testDeleteCurrentFormFromNull() {
503         while (this.b.currentFormSize() > 0) {
504             this.b.popCurrentForm();
505         }
506         assertEquals(0, this.b.deleteCurrentFormAt(0), "Delete from empty current forms failed");
507     }
508 
509     @Test
510     void testPrintMeta() {
511         this.b.putParameter("FOO", "QUUZ");
512         assertTrue(this.b.printMeta().contains("QUUZ"), "PrintMeta returnss valid params");
513     }
514 
515     @Test
516     void testSetParameters() {
517         final Map<String, Object> m = new HashMap<>();
518         m.put("A", "B");
519         this.b.putParameter("FOO", "BAR");
520         this.b.setParameters(m);
521         assertNull(this.b.getParameter("FOO"), "Set parameters must clear old data");
522     }
523 
524     @Test
525     void testStringParameterOnNonStringValue() {
526         this.b.putParameter("A", 1L);
527         assertEquals("1", this.b.getStringParameter("A"), "Non-string parameters must call toString method");
528         assertEquals("1", this.b.getParameterAsConcatString("A"), "Non-string parameters must call toString method");
529         assertEquals("1", this.b.getParameterAsString("A"), "Non-string parameters must call toString method");
530     }
531 
532     @Test
533     void testStringParameterOnNullValue() {
534         this.b.putParameter("A", null);
535         assertNull(this.b.getStringParameter("A"), "Null parameter must be returned as null");
536         assertNull(this.b.getParameterAsConcatString("A"), "Null parameter must be returned as null");
537         assertNull(this.b.getParameterAsString("A"), "Null parameter must be returned as null");
538     }
539 
540     @Test
541     void testStringParameterOnEmptyValue() {
542         this.b.putParameter("A", "");
543         assertEquals("", this.b.getStringParameter("A"), "Empty parameter must be returned as empty string");
544         assertEquals("", this.b.getParameterAsConcatString("A"), "Empty parameter must be returned as empty string");
545         assertEquals("", this.b.getParameterAsString("A"), "Empty parameter must be returned as empty string");
546     }
547 
548     @Test
549     void testNumSiblings() {
550         this.b.setNumSiblings(10);
551         assertEquals(10, this.b.getNumSiblings(), "NumSiblings simple set/get failed");
552     }
553 
554     @Test
555     void testBirthOrder() {
556         this.b.setBirthOrder(10);
557         assertEquals(10, this.b.getBirthOrder(), "BirthOrder simple set/get failed");
558     }
559 
560     @Test
561     void testFontEncoding() {
562         this.b.setFontEncoding("zhosa");
563         assertEquals("zhosa", this.b.getFontEncoding(), "FontEncoding simple set/get failed");
564     }
565 
566     @Test
567     void testFileTypeIsEmpty() {
568         this.b.setFileType(Form.UNKNOWN);
569         assertTrue(this.b.isFileTypeEmpty(), "Unknown form must count as empty");
570         this.b.setFileType("BAR");
571         final String[] fakeEmpties = {Form.UNKNOWN, "FOO", "BAR"};
572         assertTrue(this.b.setFileTypeIfEmpty("BAZ", fakeEmpties), "Unknown form must count as empty when passing in list");
573         assertEquals("BAZ", this.b.getFileType(), "Failed to use supplied list of empty forms " + Arrays.asList(fakeEmpties));
574 
575         this.b.setFileType("TEST-UNWRAPPED");
576         assertTrue(this.b.setFileTypeIfEmpty("ZAZ"), "Unknown form must count as empty when passing in list");
577         assertEquals("ZAZ", this.b.getFileType());
578     }
579 
580     @Test
581     void testAlternateViewCount() {
582         this.b.addAlternateView("FOO", "abcd".getBytes());
583         assertEquals(1, this.b.getNumAlternateViews(), "Number of alternate views failed");
584         this.b.addAlternateView("BAR", "abcd".getBytes());
585         assertEquals(2, this.b.getNumAlternateViews(), "Number of alternate views failed to increment");
586     }
587 
588     @Test
589     void testSetBroken() {
590         this.b.setBroken("This is broken");
591         assertTrue(this.b.isBroken(), "Broken indicator failed");
592         this.b.setBroken("This is still broken");
593         assertTrue(this.b.isBroken(), "Broken indicator failed after append");
594         assertTrue(this.b.getBroken().contains("still"), "Broken indicator string failed");
595     }
596 
597     @Test
598     void testDeleteFormByName() {
599         this.b.deleteCurrentForm("TWO");
600         assertEquals(2, this.b.currentFormSize(), "Remaining form count");
601         this.b.deleteCurrentForm("ONE");
602         assertEquals(1, this.b.currentFormSize(), "Remaining form count");
603         this.b.deleteCurrentForm("THREE");
604         assertEquals(0, this.b.currentFormSize(), "Remaining form count");
605 
606         this.b.pushCurrentForm("ONE");
607         this.b.deleteCurrentForm("BOGUS");
608         assertEquals(1, this.b.currentFormSize(), "Remaining form count");
609         this.b.deleteCurrentForm(null);
610         assertEquals(1, this.b.currentFormSize(), "Remaining form count");
611 
612         this.b.deleteCurrentForm("ONE");
613         assertEquals(0, this.b.currentFormSize(), "Remaining form count");
614         this.b.deleteCurrentForm("BOGUS");
615         assertEquals(0, this.b.currentFormSize(), "Remaining form count");
616         this.b.deleteCurrentForm(null);
617         assertEquals(0, this.b.currentFormSize(), "Remaining form count");
618     }
619 
620     @Test
621     void testAddCurrentFormAtBottom() {
622         this.b.addCurrentFormAt(0, "FOUR");
623         assertEquals("FOUR", this.b.currentForm(), "Form on top");
624         assertEquals("ONE", this.b.currentFormAt(3), "Form on bottom");
625         assertEquals(4, this.b.currentFormSize(), "Stack size after add");
626     }
627 
628     @Test
629     void testAddCurrentFormInMiddle() {
630         this.b.addCurrentFormAt(1, "FOUR");
631         assertEquals("THREE", this.b.currentForm(), "Form remaining on top");
632         assertEquals("ONE", this.b.currentFormAt(3), "Form on bottom");
633         assertEquals(4, this.b.currentFormSize(), "Stack size after add");
634     }
635 
636     @Test
637     void testAddCurrentFormAtTop() {
638         this.b.addCurrentFormAt(3, "FOUR");
639         assertEquals("THREE", this.b.currentForm(), "Form on top");
640         assertEquals("FOUR", this.b.currentFormAt(3), "Form on bottom");
641         assertEquals(4, this.b.currentFormSize(), "Stack size after add");
642     }
643 
644     @Test
645     void testSetCurrentForm() {
646         this.b.setCurrentForm("FOUR");
647         assertEquals("FOUR", this.b.currentForm(), "Form on top");
648         assertEquals("ONE", this.b.currentFormAt(2), "Form on bottom");
649         assertEquals(3, this.b.currentFormSize(), "Stack size after set");
650         assertTrue(this.b.toString().contains("FOUR"), "To string with current form");
651     }
652 
653     @Test
654     void testHistoryInToString() {
655         this.b.setCurrentForm("UNKNOWN");
656         this.b.appendTransformHistory("*.FOOPLACE.*.http://host:1234/fooPlace");
657         this.b.appendTransformHistory("*.BARPLACE.*.http://host:1234/barPlace");
658         final String s = this.b.toString();
659         assertTrue(s.contains("FOOPLACE"), "history elements in toString");
660         assertTrue(s.contains("BARPLACE"), "history elements in toString");
661     }
662 
663     @Test
664     void testSetCurrentFormEmpty() {
665         this.b.popCurrentForm();
666         this.b.popCurrentForm();
667         this.b.popCurrentForm();
668         this.b.setCurrentForm("FOUR");
669         assertEquals("FOUR", this.b.currentForm(), "Form on top");
670         assertEquals("FOUR", this.b.currentFormAt(0), "Form on bottom");
671         assertEquals(1, this.b.currentFormSize(), "Stack size after set");
672     }
673 
674     @Test
675     void testCurrentFormNullHandling() {
676         assertThrows(IllegalArgumentException.class, () -> this.b.pushCurrentForm(null));
677         assertThrows(IllegalArgumentException.class, () -> this.b.enqueueCurrentForm(null));
678         assertThrows(IllegalArgumentException.class, () -> this.b.setCurrentForm(null));
679         assertThrows(IllegalArgumentException.class, () -> this.b.addCurrentFormAt(0, null));
680     }
681 
682     static Stream<Arguments> arguments() {
683         return Stream.of(
684                 Arguments.of("TWO", "TWO", "ONE", "pull to top from middle"),
685                 Arguments.of("ONE", "ONE", "TWO", "pull to top from bottom"),
686                 Arguments.of("THREE", "THREE", "ONE", "pull to top from top"),
687                 Arguments.of("SEVENTEEN", "THREE", "ONE", "pull non existent to top"));
688     }
689 
690     @ParameterizedTest
691     @MethodSource("arguments")
692     void testPullToTop(String pullToTop, String expected, String expectedAt, String msg) {
693         this.b.pullFormToTop(pullToTop);
694         assertEquals(expected, this.b.currentForm(), msg);
695         assertEquals(expectedAt, this.b.currentFormAt(2), "Form on bottom");
696         assertEquals(3, this.b.currentFormSize(), "Stack size after set");
697     }
698 
699     @Test
700     void testGetAllForms() {
701         final List<String> al = this.b.getAllCurrentForms();
702         assertEquals(3, al.size(), "Forms returned");
703         assertEquals("THREE", al.get(0), "Form order");
704     }
705 
706     @Test
707     void testProcessingError() {
708         assertNull(this.b.getProcessingError(), "Empty processing error");
709         this.b.addProcessingError("ONE");
710         this.b.addProcessingError("TWO");
711         assertEquals("ONE\nTWO\n", this.b.getProcessingError(), "Catted proc error");
712     }
713 
714     @Test
715     void testBeforeStart() {
716         assertTrue(this.b.beforeStart(), "Before start on empty history");
717         this.b.appendTransformHistory("*.FOOPLACE.*.http://host:1234/fooPlace");
718         assertFalse(this.b.beforeStart(), "Before start with history");
719         this.b.clearTransformHistory();
720         assertEquals(0, this.b.transformHistory().size(), "Empty history");
721         assertTrue(this.b.beforeStart(), "Re-emptied history before start");
722 
723         this.b.appendTransformHistory("*.FOOPLACE.*.http://host:1234/fooPlace");
724         this.b.appendTransformHistory("*.<SPROUT>.*.http://host:1234/barPlace");
725         assertTrue(this.b.beforeStart(), "Before start with sprout key on end");
726         this.b.appendTransformHistory("UNKNOWN.FOOPLACE.ID.http://host:1234/bazPlace");
727         assertFalse(this.b.beforeStart(), "Not before start with sprout key on list");
728     }
729 
730     @Test
731     void testParametersMapSignature() {
732         final Map<String, Object> map = new HashMap<>();
733         map.put("ONE", "uno");
734         map.put("TWO", "dos");
735         map.put("THREE", "tres");
736 
737         this.b.putParameters(map);
738 
739         final Map<String, String> smap = new HashMap<>();
740         smap.put("FOUR", "cuatro");
741         smap.put("FIVE", "cinco");
742         smap.put("SIX", "seis");
743 
744         this.b.putParameters(smap);
745 
746         Map<String, Collection<Object>> result = this.b.getParameters();
747         assertEquals(6, result.size(), "Added all types of parameters");
748 
749         // Put in some maps
750         this.b.setParameter("SEVEN", map);
751         this.b.putParameter("EIGHT", smap);
752 
753         // Put in a map of map
754         final Map<String, Map<String, String>> combo = new HashMap<>();
755         combo.put("NINE", smap);
756         this.b.putParameters(combo);
757 
758         result = this.b.getParameters();
759         assertEquals(9, result.size(), "Added all types of parameters");
760 
761     }
762 
763     @Test
764     void testParametersMapInterfaceSignature() {
765 
766         final IBaseDataObject i = this.b;
767 
768         final Map<String, Object> map = new HashMap<>();
769         map.put("ONE", "uno");
770         map.put("TWO", "dos");
771         map.put("THREE", "tres");
772 
773         i.putParameters(map);
774 
775         final Map<String, String> smap = new HashMap<>();
776         smap.put("FOUR", "cuatro");
777         smap.put("FIVE", "cinco");
778         smap.put("SIX", "seis");
779 
780         i.putParameters(smap);
781 
782         Map<String, Collection<Object>> result = this.b.getParameters();
783         assertEquals(6, result.size(), "Added all types of parameters");
784 
785         // Put in some maps
786         i.setParameter("SEVEN", map);
787         i.putParameter("EIGHT", smap);
788 
789         // Put in a map of map
790         final Map<String, Map<String, String>> combo = new HashMap<>();
791         combo.put("NINE", smap);
792         i.putParameters(combo);
793 
794         result = i.getParameters();
795         assertEquals(9, result.size(), "Added all types of parameters");
796     }
797 
798     @Test
799     void testPutUniqueParameters() {
800 
801         this.b.putParameter("ONE", "uno");
802         this.b.putParameter("TWO", "deux");
803 
804         final Map<String, Object> map = new HashMap<>();
805         map.put("ONE", "uno");
806         map.put("TWO", "dos");
807         map.put("THREE", "tres");
808 
809         this.b.putUniqueParameters(map);
810 
811         assertEquals(1, this.b.getParameter("ONE").size(), "When putting unique parameters values must collapse");
812         assertEquals(2, this.b.getParameter("TWO").size(), "When putting unique parameters distinct values must be stored");
813         assertEquals(1, this.b.getParameter("THREE").size(), "When putting unique parameters new keys must be stored");
814     }
815 
816     @Test
817     void testMergeParameters() {
818 
819         this.b.putParameter("ONE", "uno");
820         this.b.putParameter("TWO", "deux");
821 
822         final Map<String, Object> map = new HashMap<>();
823         map.put("ONE", "uno");
824         map.put("TWO", "dos");
825         map.put("THREE", "tres");
826 
827         this.b.mergeParameters(map);
828 
829         assertEquals("uno", this.b.getStringParameter("ONE"), "When merging parameters previous values must override");
830         assertEquals("uno", this.b.getParameterAsConcatString("ONE"), "When merging parameters previous values must override");
831         assertEquals("uno", this.b.getParameterAsString("ONE"), "When merging parameters previous values must override");
832         assertEquals("deux", this.b.getStringParameter("TWO"), "When merging parameters previous values must override");
833         assertEquals("deux", this.b.getParameterAsConcatString("TWO"), "When merging parameters previous values must override");
834         assertEquals("deux", this.b.getParameterAsString("TWO"), "When merging parameters previous values must override");
835         assertEquals("tres", this.b.getStringParameter("THREE"), "When merging  parameters new keys must be stored");
836         assertEquals("tres", this.b.getParameterAsConcatString("THREE"), "When merging  parameters new keys must be stored");
837         assertEquals("tres", this.b.getParameterAsString("THREE"), "When merging  parameters new keys must be stored");
838     }
839 
840     @Test
841     void testPutParametersWithPolicy() {
842 
843         this.b.putParameter("ONE", "uno");
844         this.b.putParameter("TWO", "deux");
845 
846         final Map<String, Object> map = new HashMap<>();
847         map.put("ONE", "uno");
848         map.put("TWO", "dos");
849         map.put("THREE", "tres");
850 
851         this.b.putParameters(map, IBaseDataObject.MergePolicy.KEEP_ALL);
852 
853         assertEquals("uno;uno", this.b.getStringParameter("ONE"), "When specifying KEEP_ALL values must all stay");
854         assertEquals("uno;uno", this.b.getParameterAsConcatString("ONE"), "When specifying KEEP_ALL values must all stay");
855         assertEquals("uno;uno", this.b.getParameterAsString("ONE"), "When specifying KEEP_ALL first value should be retained");
856         assertEquals("deux;dos", this.b.getStringParameter("TWO"), "When specifying KEEP_ALL values must all stay");
857         assertEquals("deux;dos", this.b.getParameterAsConcatString("TWO"), "When specifying KEEP_ALL values must all stay");
858         assertEquals("deux;dos", this.b.getParameterAsString("TWO"), "When specifying KEEP_ALL first value should be retained");
859         assertEquals("tres", this.b.getStringParameter("THREE"), "When specifying KEEP_ALL new keys must be stored");
860         assertEquals("tres", this.b.getParameterAsConcatString("THREE"), "When specifying KEEP_ALL new keys must be stored");
861         assertEquals("tres", this.b.getParameterAsString("THREE"), "When specifying KEEP_ALL first value should be retained");
862     }
863 
864     @Test
865     void testPutParametersWithMultimapAsMap() {
866         final ListMultimap<String, String> map = ArrayListMultimap.create();
867         map.put("ONE", "uno");
868         map.put("ONE", "ein");
869         map.put("ONE", "neo");
870         map.put("TWO", "deux");
871         this.b.putParameter("TWO", "dos");
872         this.b.putParameters(map.asMap());
873         assertEquals(3, this.b.getParameter("ONE").size(), "Multimap parameters should merge");
874         assertEquals(2, this.b.getParameter("TWO").size(), "Multimap parameters should merge");
875         map.clear();
876         assertEquals(3, this.b.getParameter("ONE").size(), "Multimap parameters should be detached from callers map");
877         assertEquals(2, this.b.getParameter("TWO").size(), "Multimap parameters should be detached from callers map");
878         map.put("THREE", "tres");
879         this.b.mergeParameters(map.asMap());
880         assertEquals(1, this.b.getParameter("THREE").size(), "Multimap parameters should merge");
881         map.put("FOUR", "cuatro");
882         this.b.putUniqueParameters(map.asMap());
883         assertEquals(1, this.b.getParameter("THREE").size(), "Multimap parameters should remain unique");
884         assertEquals(1, this.b.getParameter("FOUR").size(), "Multimap params should add on unique");
885     }
886 
887     @Test
888     void testParameters() {
889         this.b.putParameter("ME", "YOU");
890         assertEquals("YOU", this.b.getStringParameter("ME"), "Gotten parameter");
891         assertEquals("YOU", this.b.getParameterAsConcatString("ME"), "Gotten parameter");
892         assertEquals("YOU", this.b.getParameterAsString("ME"), "Gotten parameter");
893         final Map<String, Object> map = new HashMap<>();
894         map.put("ONE", "uno");
895         map.put("TWO", "dos");
896         map.put("THREE", "tres");
897         this.b.putParameters(map);
898         assertEquals("uno", this.b.getStringParameter("ONE"), "Map put parameter gotten");
899         assertEquals("uno", this.b.getParameterAsConcatString("ONE"), "Map put parameter gotten");
900         assertEquals("uno", this.b.getParameterAsString("ONE"), "Map put parameter gotten");
901         assertEquals("dos", this.b.getStringParameter("TWO"), "Map put parameter gotten");
902         assertEquals("dos", this.b.getParameterAsConcatString("TWO"), "Map put parameter gotten");
903         assertEquals("dos", this.b.getParameterAsString("TWO"), "Map put parameter gotten");
904         assertEquals("tres", this.b.getStringParameter("THREE"), "Map put parameter gotten");
905         assertEquals("tres", this.b.getParameterAsConcatString("THREE"), "Map put parameter gotten");
906         assertEquals("tres", this.b.getParameterAsString("THREE"), "Map put parameter gotten");
907         assertEquals("YOU", this.b.getStringParameter("ME"), "Gotten parameter");
908         assertEquals("YOU", this.b.getParameterAsConcatString("ME"), "Gotten parameter");
909         assertEquals("YOU", this.b.getParameterAsString("ME"), "Gotten parameter");
910 
911         // Deletes
912         this.b.deleteParameter("THREE");
913         assertNull(this.b.getParameter("THREE"), "Deleted param is gone");
914 
915         // Overwrite
916         this.b.putParameter("ME", "THEM");
917         assertEquals("THEM", this.b.getStringParameter("ME"), "Gotten parameter");
918         assertEquals("THEM", this.b.getParameterAsConcatString("ME"), "Gotten parameter");
919         assertEquals("THEM", this.b.getParameterAsString("ME"), "Gotten parameter");
920 
921         // Clear
922         this.b.clearParameters();
923         assertNull(this.b.getParameter("THREE"), "Deleted param is gone");
924         assertNull(this.b.getParameter("ME"), "Deleted param is gone");
925         final Map<?, ?> m = this.b.getParameters();
926         assertNotNull(m, "Clear paramters leave empty map");
927         assertEquals(0, m.size(), "Clear parameters leaves empty map");
928 
929     }
930 
931     @Test
932     void testHasParameter() {
933         this.b.setParameter("FOO", "BAR");
934         assertTrue(this.b.hasParameter("FOO"), "Has parameter must be true when present");
935     }
936 
937     @Test
938     void testHasParameterMiss() {
939         assertFalse(this.b.hasParameter("FOO"), "Has parameter must be false when not present");
940     }
941 
942     @Test
943     void testHasParameterAfterDelete() {
944         this.b.putParameter("FOO", "BARZILLAI");
945         this.b.deleteParameter("FOO");
946         assertFalse(this.b.hasParameter("FOO"), "Has parameter must be false after parameter has been removed");
947     }
948 
949     @Test
950     void testAppendDuplicateParameters() {
951         this.b.appendParameter("YO", "GABBA");
952         this.b.appendParameter("YO", "GABBA");
953         assertEquals("GABBA;GABBA", this.b.getStringParameter("YO"), "Appended duplicate parameters should be preserved");
954         assertEquals("GABBA;GABBA", this.b.getParameterAsConcatString("YO"), "Appended duplicate parameters should be preserved");
955         assertEquals("GABBA;GABBA", this.b.getParameterAsString("YO"), "Appended duplicate parameters first value should be preserved");
956         assertTrue(this.b.hasParameter("YO"), "HasParameter should be true");
957     }
958 
959     @Test
960     void testAppendUniqueParameters() {
961         this.b.appendUniqueParameter("YO", "GABBA");
962         this.b.appendUniqueParameter("YO", "GABBA");
963         assertEquals("GABBA", this.b.getStringParameter("YO"), "Appended unique parameters should be collapsed");
964         assertEquals("GABBA", this.b.getParameterAsConcatString("YO"), "Appended unique parameters should be collapsed");
965         assertEquals("GABBA", this.b.getParameterAsString("YO"), "Appended unique parameters should be collapsed");
966         assertTrue(this.b.hasParameter("YO"), "HasParameter should be true");
967     }
968 
969     @Test
970     void testParametersWithMixtureOfSingleValuesAndLists() {
971         final Map<String, Object> p = new HashMap<>();
972         final List<String> foolist = new ArrayList<>();
973         foolist.add("FOO1");
974         foolist.add("FOO2");
975         foolist.add("FOO3");
976         p.put("FOO", foolist);
977         this.b.putParameters(p);
978         assertEquals(3, this.b.getParameter("FOO").size(), "Returned list size should match what was put in");
979         this.b.appendParameter("FOO", "FOO4");
980         assertEquals("FOO1;FOO2;FOO3;FOO4",
981                 this.b.getStringParameter("FOO"),
982                 "Returned string should be combination of initial list and added value");
983         assertEquals("FOO1;FOO2;FOO3;FOO4",
984                 this.b.getParameterAsConcatString("FOO"),
985                 "Returned string should be combination of initial list and added value");
986         assertEquals("FOO1;FOO2;FOO3;FOO4",
987                 this.b.getParameterAsString("FOO"),
988                 "Returned string should be first value from combination of initial list and added value");
989     }
990 
991     @Test
992     void testParametersWithMixtureOfSingleValuesAndSets() {
993         final Map<String, Object> p = new HashMap<>();
994         final Set<String> fooset = new TreeSet<>();
995         fooset.add("FOO1");
996         fooset.add("FOO2");
997         fooset.add("FOO3");
998         p.put("FOO", fooset);
999         this.b.putParameters(p);
1000         assertEquals(3, this.b.getParameter("FOO").size(), "Returned list size should match what was put in");
1001         this.b.appendParameter("FOO", "FOO4");
1002         assertEquals("FOO1;FOO2;FOO3;FOO4", this.b.getStringParameter("FOO"), "Returned string should be combination of initial set and added value");
1003         assertEquals("FOO1;FOO2;FOO3;FOO4", this.b.getParameterAsConcatString("FOO"),
1004                 "Returned string should be combination of initial set and added value");
1005         assertEquals("FOO1;FOO2;FOO3;FOO4", this.b.getParameterAsString("FOO"),
1006                 "Returned string should be first value from combination of initial set and added value");
1007     }
1008 
1009     @Test
1010     void testCookedParameters() {
1011         this.b.appendParameter("YO", "GABBA");
1012         this.b.appendParameter("YO", "GABBA");
1013         this.b.appendUniqueParameter("WHALE", "BLUBBER");
1014         this.b.appendUniqueParameter("WHALE", "BLUBBER");
1015         final Map<String, String> m = this.b.getCookedParameters();
1016         assertNotNull(m, "Cooked parameters cannot be null");
1017         assertEquals("GABBA;GABBA", m.get("YO"), "Cooked parameters should contains inserted value");
1018         assertEquals("BLUBBER", m.get("WHALE"), "Cooked parameters should contains inserted unique value");
1019     }
1020 
1021     @Test
1022     void testParameterKeys() {
1023         this.b.appendParameter("YO", "GABBA");
1024         this.b.appendParameter("YO", "GABBA");
1025         this.b.appendUniqueParameter("WHALE", "BLUBBER");
1026         this.b.appendUniqueParameter("WHALE", "BLUBBER");
1027         final Set<String> keys = this.b.getParameterKeys();
1028         assertNotNull(keys, "Parameter keys cannot be null");
1029         assertTrue(keys.contains("YO"), "Parameter keys should contains inserted key");
1030         assertTrue(keys.contains("WHALE"), "Parameter keys should contains inserted unique key");
1031     }
1032 
1033     @Test
1034     void testAppendParameter() {
1035         this.b.putParameter("ME", "YOU");
1036         this.b.appendParameter("ME", "FOO");
1037         assertEquals("YOU;FOO", this.b.getStringParameter("ME"), "Appended parameter value");
1038         assertEquals("YOU;FOO", this.b.getParameterAsConcatString("ME"), "Appended parameter value");
1039         assertEquals("YOU;FOO", this.b.getParameterAsString("ME"), "Appended parameter value");
1040     }
1041 
1042     @Test
1043     void testAppendParameterIterables() {
1044         this.b.putParameter("ME", "YOU");
1045         this.b.appendParameter("ME", Arrays.asList("FOO", "BAR", "BAZ"));
1046         assertEquals("YOU;FOO;BAR;BAZ", this.b.getStringParameter("ME"), "Appended parameter value");
1047         assertEquals("YOU;FOO;BAR;BAZ", this.b.getParameterAsConcatString("ME"), "Appended parameter value");
1048         assertEquals("YOU;FOO;BAR;BAZ", this.b.getParameterAsString("ME"), "First value from appended parameter value");
1049 
1050         final Set<String> s = new TreeSet<>();
1051         s.add("ZAB");
1052         s.add("RAB");
1053         s.add("OOF");
1054         this.b.appendParameter("ME", s);
1055 
1056         assertEquals("YOU;FOO;BAR;BAZ;OOF;RAB;ZAB", this.b.getStringParameter("ME"), "Appended set parameter value");
1057         assertEquals("YOU;FOO;BAR;BAZ;OOF;RAB;ZAB", this.b.getParameterAsConcatString("ME"), "Appended set parameter value");
1058         assertEquals("YOU;FOO;BAR;BAZ;OOF;RAB;ZAB", this.b.getParameterAsString("ME"), "First value from appended set parameter value");
1059     }
1060 
1061     @Test
1062     void testAppendParameterOntoEmpty() {
1063         this.b.appendParameter("ME", "FOO");
1064         assertEquals("FOO", this.b.getStringParameter("ME"), "Appended parameter value");
1065         assertEquals("FOO", this.b.getParameterAsConcatString("ME"), "Appended parameter value");
1066         assertEquals("FOO", this.b.getParameterAsString("ME"), "First value from appended parameter value");
1067     }
1068 
1069 
1070     @Test
1071     void testWhereAmI() {
1072         assertNotNull(this.b.whereAmI(), "WhereamI gets host name");
1073         assertNotEquals("FAILED", this.b.whereAmI(), "WhereamI gets host name");
1074     }
1075 
1076     @Test
1077     void testVisitHistory() {
1078         assertNull(this.b.getLastPlaceVisited(), "No last place");
1079         assertNull(this.b.getPenultimatePlaceVisited(), "No penultimate place");
1080 
1081         this.b.appendTransformHistory("UNKNOWN.FOO.ID.http://host:1234/FooPlace$1010");
1082 
1083         assertNull(this.b.getPenultimatePlaceVisited(), "Still no penultimate place");
1084 
1085         this.b.appendTransformHistory("UNKNOWN.BAR.ID.http://host:1234/BarPlace$2020");
1086         this.b.appendTransformHistory("UNKNOWN.BAZ.ID.http://host:1234/BazPlace$3030");
1087         this.b.appendTransformHistory("UNKNOWN.BAM.ID.http://host:1234/BamPlace$4040");
1088 
1089         final DirectoryEntry sde = this.b.getLastPlaceVisited();
1090         assertNotNull(sde, "Last place directory entry");
1091         assertEquals("UNKNOWN.BAM.ID.http://host:1234/BamPlace$4040", sde.getFullKey(), "Last place key");
1092         final DirectoryEntry pen = this.b.getPenultimatePlaceVisited();
1093         assertNotNull(pen, "Penultimate place");
1094         assertEquals("UNKNOWN.BAZ.ID.http://host:1234/BazPlace$3030", pen.getFullKey(), "Pen place key");
1095 
1096         assertTrue(this.b.hasVisited("*.BAM.*.*"), "Has visited last");
1097         assertTrue(this.b.hasVisited("*.BAR.*.*"), "Has visited first");
1098         assertFalse(this.b.hasVisited("*.SHAZAM.*.*"), "No such visit");
1099 
1100         this.b.clearTransformHistory();
1101         assertFalse(this.b.hasVisited("*.BAM.*.*"), "Has no visited after clear");
1102     }
1103 
1104     @Test
1105     void testVisitHistoryCoordinated() {
1106         assertNull(this.b.getLastPlaceVisited(), "Transform history should be empty");
1107         assertNull(this.b.getPenultimatePlaceVisited(), "Transform history should be empty");
1108 
1109         this.b.appendTransformHistory("UNKNOWN.FOO.ID.http://host:1234/FooPlace$1010");
1110         this.b.appendTransformHistory("UNKNOWN.BAR_COORDINATION.ID.http://host:1234/BarPlace$2020");
1111         this.b.appendTransformHistory("UNKNOWN.BAZ.ID.http://host:1234/BazPlace$3030", true);
1112         this.b.appendTransformHistory("UNKNOWN.BAM.ID.http://host:1234/BamPlace$4040", true);
1113 
1114         final DirectoryEntry sde = this.b.getLastPlaceVisited();
1115         assertNotNull(sde, "Last place directory entry should exist");
1116         assertEquals("UNKNOWN.BAR_COORDINATION.ID.http://host:1234/BarPlace$2020", sde.getFullKey(), "Last place returned the wrong key");
1117 
1118         final DirectoryEntry pen = this.b.getPenultimatePlaceVisited();
1119         assertNotNull(pen, "Penultimate place directory entry should exist");
1120         assertEquals("UNKNOWN.FOO.ID.http://host:1234/FooPlace$1010", pen.getFullKey(), "Penultimate place returned the wrong key");
1121 
1122         assertTrue(this.b.hasVisited("*.FOO.*.*"), "Has visited should have matched for pattern");
1123         assertTrue(this.b.hasVisited("*.BAR_COORDINATION.*.*"), "Has visited should have matched for pattern");
1124         assertFalse(this.b.hasVisited("*.BAZ.*.*"), "Has visited should not have matched for pattern");
1125         assertFalse(this.b.hasVisited("*.BAM.*.*"), "Has visited should not have matched for pattern");
1126 
1127         this.b.clearTransformHistory();
1128         assertFalse(this.b.hasVisited("*.FOO.*.*"), "Has visited should not have matched for pattern");
1129         assertFalse(this.b.hasVisited("*.BAR_COORDINATION.*.*"), "Has visited should not have matched for pattern");
1130     }
1131 
1132     @Test
1133     void testFiletype() {
1134         this.b.setFileType(Form.UNKNOWN);
1135         assertEquals(Form.UNKNOWN, this.b.getFileType(), "Filetype saved");
1136 
1137         this.b.setFileTypeIfEmpty("FOO");
1138         assertEquals("FOO", this.b.getFileType(), "Filetype saved on empty");
1139 
1140         this.b.setFileTypeIfEmpty("BAR");
1141         assertEquals("FOO", this.b.getFileType(), "Filetype ignored on non-empty");
1142 
1143         this.b.setFileType(null);
1144         assertNull(this.b.getFileType(), "Null filetype set");
1145         this.b.setFileTypeIfEmpty("BAZ");
1146         assertEquals("BAZ", this.b.getFileType(), "Filetype set on null as empty");
1147     }
1148 
1149     @Test
1150     void testClone() throws IOException {
1151         try {
1152             this.b.setParameter("FOOBAR", "JOEBLOGGS");
1153             final IBaseDataObject clone = this.b.clone();
1154             assertEquals(this.b.getFilename(), clone.getFilename(), "Names must match");
1155             final String savedName = this.b.getFilename();
1156             this.b.setFilename("foo.bar");
1157             assertEquals(savedName, clone.getFilename(), "Names must be detached after clone");
1158 
1159             assertEquals(this.b.currentFormSize(), clone.currentFormSize(), "Current form size must match");
1160             this.b.popCurrentForm();
1161             assertEquals(this.b.currentFormSize(), clone.currentFormSize() - 1, "Current form stack must be detached after clone");
1162             final String newData = "some new data";
1163             final SeekableByteChannelFactory sbcf = InMemoryChannelFactory.create(newData.getBytes());
1164             this.b.setChannelFactory(sbcf);
1165             final IBaseDataObject cloneSbc = this.b.clone();
1166             assertEquals(13, cloneSbc.getChannelFactory().create().read(ByteBuffer.allocate(newData.length())));
1167 
1168             assertEquals(newData, new String(cloneSbc.data()));
1169 
1170         } catch (CloneNotSupportedException ex) {
1171             fail("Clone must be supported on BaseDataObject", ex);
1172         }
1173     }
1174 
1175     @Test
1176     void testHeaderEncoding() {
1177         this.b.setHeaderEncoding("foo");
1178         assertEquals("foo", this.b.getHeaderEncoding(), "Header encoding simple string set/get failed");
1179     }
1180 
1181     @Test
1182     void testDefaultPriority() {
1183         assertEquals(Priority.DEFAULT, this.b.getPriority(), "Default priority failed");
1184     }
1185 
1186     @Test
1187     void testUpdatedPriority() {
1188         this.b.setPriority(1);
1189         assertEquals(1, this.b.getPriority(), "Updated priority failed");
1190     }
1191 
1192     @Test
1193     void testDefaultConstructor_setDateTime() {
1194         // setup
1195         final BaseDataObject ibdo = new BaseDataObject();
1196 
1197         // verify
1198         assertNotNull(ibdo.getCreationTimestamp());
1199     }
1200 
1201     @Test
1202     void testDefaultConstructor_getSetDateTime() {
1203         // setup
1204         final Instant date = Instant.now();
1205 
1206         // test
1207         this.b.setCreationTimestamp(date);
1208 
1209         // verify
1210         assertEquals(date, this.b.getCreationTimestamp());
1211     }
1212 
1213     @Test
1214     void testNullTimestampSettingThrowsException() {
1215         assertThrows(IllegalArgumentException.class, () -> this.b.setCreationTimestamp(null));
1216     }
1217 
1218     @Test
1219     void testNullDataAndChannel() {
1220         final BaseDataObject bdo = new BaseDataObject();
1221         assertNull(bdo.getChannelFactory());
1222     }
1223 
1224     @Test
1225     void testSettingChannelFactoryWhenCurrentlyNull() throws IOException {
1226         final String testData = "This is a test";
1227         final SeekableByteChannelFactory sbcf = SeekableByteChannelHelper.memory(testData.getBytes());
1228         final BaseDataObject bdo = new BaseDataObject();
1229         bdo.setChannelFactory(sbcf);
1230         assertEquals(sbcf, bdo.getChannelFactory());
1231     }
1232 
1233     @Test
1234     void testCanRetrieveChannelFactoryFromByteArray() throws IOException {
1235         final String testData = "This is a test";
1236         this.b.setData(testData.getBytes()); // nulls out channelFactory
1237         final ByteBuffer buff = ByteBuffer.allocate(testData.length());
1238         final SeekableByteChannelFactory sbcf = this.b.getChannelFactory();
1239         sbcf.create().read(buff);
1240         assertArrayEquals(testData.getBytes(), buff.array());
1241     }
1242 
1243     @Test
1244     void testBothDataFieldsHaveValue()
1245             throws IOException, NoSuchFieldException, IllegalAccessException {
1246         final BaseDataObject bdo = new BaseDataObject();
1247         final String testData = "This is a test";
1248         bdo.setChannelFactory(SeekableByteChannelHelper.memory(testData.getBytes()));
1249         Field theData = bdo.getClass().getDeclaredField("theData");
1250         theData.set(bdo, testData.getBytes());
1251 
1252         final String msg = "Should throw an error when trying to access data on a BDO where we have a byte array and a channel";
1253 
1254         assertThrows(IllegalStateException.class, () -> bdo.data(), msg);
1255         assertThrows(IllegalStateException.class, () -> bdo.getChannelFactory(), msg);
1256         assertThrows(IllegalStateException.class, () -> bdo.getChannelSize(), msg);
1257         assertThrows(IllegalStateException.class, () -> bdo.dataLength(), msg);
1258     }
1259 
1260     @Test
1261     void testBasicDataMethods() throws IOException {
1262         final String testData = "This is a test";
1263         final byte[] testArray = testData.getBytes();
1264         final int testLength = testArray.length;
1265 
1266         // data() check with byte[]
1267         byte[] existingData = this.b.data();
1268         assertEquals(testData, new String(existingData));
1269 
1270         // getSeekableByteChannelFactory() check with byte[]
1271         ByteBuffer buff = ByteBuffer.allocate(testLength);
1272         this.b.getChannelFactory().create().read(buff);
1273         assertEquals(testData, new String(buff.array()));
1274 
1275         final SeekableByteChannelFactory sbcf = InMemoryChannelFactory.create(testArray);
1276         this.b.setChannelFactory(sbcf);
1277 
1278         // data() check with sbcf
1279         existingData = this.b.data();
1280         assertEquals(testData, new String(existingData));
1281 
1282         // getSeekableByteChannelFactory() check with sbcf
1283         buff = ByteBuffer.allocate(testLength);
1284         this.b.getChannelFactory().create().read(buff);
1285         assertEquals(testData, new String(buff.array()));
1286     }
1287 
1288     @Test
1289     void testSeekableByteChannelFactoryWithInvalidData() {
1290         assertThrows(NullPointerException.class, () -> this.b.setChannelFactory(null));
1291     }
1292 
1293     @Test
1294     void testExtractedRecords() {
1295         final BaseDataObject other = new BaseDataObject();
1296         assertFalse(this.b.hasExtractedRecords(), "Expected no extracted records");
1297         assertNull(this.b.getExtractedRecords());
1298 
1299         this.b.setExtractedRecords(new ArrayList<>());
1300         assertFalse(this.b.hasExtractedRecords(), "Expected no extracted records");
1301         assertNotNull(this.b.getExtractedRecords(), "Expected non-null extracted records");
1302         assertEquals(0, this.b.getExtractedRecords().size(), "Expected empty extracted records");
1303 
1304         this.b.setExtractedRecords(Collections.<IBaseDataObject>singletonList(other));
1305         assertTrue(this.b.hasExtractedRecords(), "Expected extracted records");
1306         assertEquals(1, this.b.getExtractedRecords().size(), "Expected a single extracted record");
1307         assertEquals(1, this.b.getExtractedRecordCount(), "Expected a single extracted record");
1308 
1309         assertThrows(IllegalArgumentException.class, () -> this.b.setExtractedRecords(null));
1310 
1311         assertThrows(IllegalArgumentException.class, () -> this.b.addExtractedRecord(null));
1312 
1313         this.b.addExtractedRecord(other);
1314         assertTrue(this.b.hasExtractedRecords(), "Expected extracted records");
1315         assertEquals(2, this.b.getExtractedRecords().size(), "Expected a two extracted record");
1316         assertEquals(2, this.b.getExtractedRecordCount(), "Expected a two extracted record");
1317 
1318         this.b.addExtractedRecord(other);
1319         assertTrue(this.b.hasExtractedRecords(), "Expected extracted records");
1320         assertEquals(3, this.b.getExtractedRecords().size(), "Expected three extracted record");
1321         assertEquals(3, this.b.getExtractedRecordCount(), "Expected three extracted record");
1322 
1323         this.b.clearExtractedRecords();
1324         assertFalse(this.b.hasExtractedRecords(), "Expected no extracted records");
1325         assertNull(this.b.getExtractedRecords());
1326 
1327         final List<IBaseDataObject> list = new ArrayList<>();
1328         list.add(new BaseDataObject());
1329         list.add(null);
1330 
1331         assertThrows(IllegalArgumentException.class, () -> this.b.setExtractedRecords(list));
1332 
1333         assertThrows(IllegalArgumentException.class, () -> this.b.addExtractedRecords(list));
1334 
1335         list.remove(1);
1336 
1337         assertThrows(IllegalArgumentException.class, () -> this.b.addExtractedRecords(null));
1338 
1339         this.b.addExtractedRecords(list);
1340         assertEquals(1, this.b.getExtractedRecordCount(), "Expect one extracted record");
1341 
1342         this.b.addExtractedRecords(list);
1343         assertEquals(2, this.b.getExtractedRecordCount(), "Expect two extracted record");
1344 
1345         final List<ExtendedDataObject> elist = new ArrayList<>();
1346         elist.add(new ExtendedDataObject());
1347         elist.add(new ExtendedDataObject());
1348 
1349         this.b.addExtractedRecords(elist);
1350         assertEquals(4, this.b.getExtractedRecordCount(), "Expected extended records to be added");
1351 
1352     }
1353 
1354     @Test
1355     void testSetCurrentFormWithBoolean() {
1356         IBaseDataObject testIbdo = DataObjectFactory.getInstance(null, "dummy", "FORM-1");
1357         testIbdo.enqueueCurrentForm("FORM-2");
1358         testIbdo.enqueueCurrentForm("FORM-3");
1359 
1360         assertEquals(3, testIbdo.currentFormSize(), "Form stack should have 3 forms before test");
1361         testIbdo.setCurrentForm("FINAL-FORM", true);
1362         assertEquals(1, testIbdo.currentFormSize(), "Form stack should have been cleared except for final set form");
1363         assertEquals("FINAL-FORM", testIbdo.currentForm(), "Form should be set to FINAL-FORM");
1364 
1365         testIbdo.enqueueCurrentForm("FINAL-FORM-2");
1366         testIbdo.setCurrentForm("FINAL-FORM-3", false);
1367         assertEquals(2, testIbdo.currentFormSize(), "Form stack should be 2, since we didnt clear the entire stack");
1368         assertEquals("FINAL-FORM-3", testIbdo.currentFormAt(0), "Top of form stack should be form 3");
1369         assertEquals("FINAL-FORM-2", testIbdo.currentFormAt(1), "2nd form in stack should be form 2");
1370     }
1371 
1372     @Test
1373     void testExtractedRecordClone() {
1374 
1375         final List<IBaseDataObject> list = new ArrayList<>();
1376         list.add(new BaseDataObject());
1377         this.b.addExtractedRecords(list);
1378 
1379         try {
1380             assertEquals(this.b.getExtractedRecordCount(), this.b.clone()
1381                     .getExtractedRecordCount(), "Cloned IBDO should have same sized extracted record list");
1382         } catch (CloneNotSupportedException ex) {
1383             fail("Clone method should have been called", ex);
1384         }
1385     }
1386 
1387     @Test
1388     void testNewInputStream() throws IOException {
1389         final IBaseDataObject ibdo = new BaseDataObject();
1390 
1391         assertNull(ibdo.newInputStream());
1392 
1393         final byte[] bytes1 = new byte[] {0, 1, 2, 3};
1394 
1395         ibdo.setData(bytes1);
1396 
1397         try (InputStream bytesInputStream = ibdo.newInputStream();
1398                 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();) {
1399             IOUtils.copy(bytesInputStream, byteArrayOutputStream);
1400 
1401             assertArrayEquals(bytes1, byteArrayOutputStream.toByteArray());
1402         }
1403 
1404         final byte[] bytes2 = new byte[] {4, 5, 6, 7};
1405         final SeekableByteChannelFactory sbcf = SeekableByteChannelHelper.memory(bytes2);
1406 
1407         ibdo.setChannelFactory(sbcf);
1408 
1409         try (InputStream sbcfInputStream = ibdo.newInputStream();
1410                 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
1411             IOUtils.copy(sbcfInputStream, byteArrayOutputStream);
1412 
1413             assertArrayEquals(bytes2, byteArrayOutputStream.toByteArray());
1414         }
1415     }
1416 
1417     @Test
1418     void testGetParameterAsString() throws IOException {
1419         assertNotNull(this.b);
1420 
1421         try (LogbackTester logbackTester = new LogbackTester(BaseDataObject.class.getName())) {
1422             this.b.putParameter("A", 1L);
1423             this.b.appendParameter("A", "TWO");
1424             this.b.appendParameter("A", "THREE");
1425             assertEquals("1;TWO;THREE", this.b.getParameterAsString("A"));
1426             assertEquals("1;TWO;THREE", this.b.getParameterAsConcatString("A"));
1427             assertEquals("1;TWO;THREE", this.b.getStringParameter("A"));
1428             LogbackTester.SimplifiedLogEvent logEvent = new LogbackTester.SimplifiedLogEvent(Level.WARN,
1429                     "Multiple values for parameter, parameter:A, number of values:3", null);
1430             logbackTester.checkLogList(Collections.singletonList(logEvent));
1431         }
1432 
1433         this.b.putParameter("A", 2L);
1434         assertEquals("2", this.b.getParameterAsString("A"));
1435         assertEquals("2", this.b.getParameterAsConcatString("A"));
1436         assertEquals("2", this.b.getStringParameter("A"));
1437 
1438         this.b.putParameter("A", "THREE");
1439         assertEquals("THREE", this.b.getParameterAsString("A"));
1440         assertEquals("THREE", this.b.getParameterAsConcatString("A"));
1441         assertEquals("THREE", this.b.getStringParameter("A"));
1442 
1443         this.b.putParameter("A", null);
1444         assertNull(this.b.getParameterAsString("A"));
1445         assertNull(this.b.getParameterAsConcatString("A"));
1446         assertNull(this.b.getStringParameter("A"));
1447 
1448         this.b.putParameter("A", "");
1449         assertEquals("", this.b.getParameterAsString("A"));
1450         assertEquals("", this.b.getParameterAsConcatString("A"));
1451         assertEquals("", this.b.getStringParameter("A"));
1452 
1453         assertNull(this.b.getParameterAsString("DNE"));
1454         assertNull(this.b.getParameterAsConcatString("DNE"));
1455         assertNull(this.b.getStringParameter("DNE"));
1456 
1457         this.b.putParameter("A", null);
1458         this.b.appendParameter("A", "FOUR");
1459         this.b.appendParameter("A", "  ");
1460         assertEquals("null;FOUR;  ", this.b.getParameterAsString("A"));
1461         assertEquals("null;FOUR;  ", this.b.getParameterAsConcatString("A"));
1462         assertEquals("null;FOUR;  ", this.b.getStringParameter("A"));
1463     }
1464 
1465 }