View Javadoc
1   package nom.tam.fits.test;
2   
3   import java.io.ByteArrayOutputStream;
4   import java.io.File;
5   import java.io.FileInputStream;
6   import java.io.RandomAccessFile;
7   import java.nio.file.Files;
8   import java.nio.file.StandardCopyOption;
9   import java.util.Arrays;
10  
11  import org.junit.jupiter.api.Assertions;
12  import org.junit.jupiter.api.Disabled;
13  import org.junit.jupiter.api.Test;
14  
15  import nom.tam.fits.BasicHDU;
16  import nom.tam.fits.Fits;
17  import nom.tam.fits.FitsException;
18  import nom.tam.fits.FitsFactory;
19  import nom.tam.fits.FitsIntegrityException;
20  import nom.tam.fits.Header;
21  import nom.tam.fits.ImageData;
22  import nom.tam.fits.ImageHDU;
23  import nom.tam.fits.header.Bitpix;
24  import nom.tam.fits.header.Standard;
25  import nom.tam.fits.utilities.FitsCheckSum;
26  import nom.tam.util.ArrayDataOutput;
27  import nom.tam.util.FitsFile;
28  import nom.tam.util.FitsIO;
29  import nom.tam.util.FitsInputStream;
30  import nom.tam.util.FitsOutputStream;
31  import nom.tam.util.RandomAccess;
32  import nom.tam.util.test.ThrowAnyException;
33  
34  /*
35   * #%L
36   * nom.tam FITS library
37   * %%
38   * Copyright (C) 2004 - 2024 nom-tam-fits
39   * %%
40   * This is free and unencumbered software released into the public domain.
41   *
42   * Anyone is free to copy, modify, publish, use, compile, sell, or
43   * distribute this software, either in source code form or as a compiled
44   * binary, for any purpose, commercial or non-commercial, and by any
45   * means.
46   *
47   * In jurisdictions that recognize copyright laws, the author or authors
48   * of this software dedicate any and all copyright interest in the
49   * software to the public domain. We make this dedication for the benefit
50   * of the public at large and to the detriment of our heirs and
51   * successors. We intend this dedication to be an overt act of
52   * relinquishment in perpetuity of all present and future rights to this
53   * software under copyright law.
54   *
55   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
56   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
57   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
58   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
59   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
60   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
61   * OTHER DEALINGS IN THE SOFTWARE.
62   * #L%
63   */
64  
65  import static nom.tam.fits.header.Checksum.CHECKSUM;
66  import static nom.tam.fits.header.Checksum.DATASUM;
67  
68  @SuppressWarnings({"javadoc", "deprecation"})
69  public class ChecksumTest {
70  
71      @Test
72      public void testChecksumDataFail() throws Exception {
73          Assertions.assertThrows(IllegalArgumentException.class, () -> FitsCheckSum.checksum(new byte[999]));
74      }
75  
76      @Test
77      public void testChecksumDataFailException() throws Exception {
78          int[][] data = new int[][] {{1, 2}, {3, 4}, {5, 6}};
79          ImageData d = ImageHDU.encapsulate(data);
80          Header h = ImageHDU.manufactureHeader(d);
81          BasicHDU<?> bhdu = new ImageHDU(h, d) {
82  
83              @Override
84              public ImageData getData() {
85                  ThrowAnyException.throwIOException("fake");
86                  return null;
87              }
88          };
89  
90          Assertions.assertThrows(FitsException.class, () -> Fits.setChecksum(bhdu));
91      }
92  
93      @Test
94      public void testChecksum() throws Exception {
95          // AK: This test requires long strings to be disabled.
96          FitsFactory.setLongStringsEnabled(false);
97  
98          try (Fits f = new Fits()) {
99              int[][] data = new int[][] {{1, 2}, {3, 4}, {5, 6}};
100             BasicHDU<?> bhdu1 = FitsFactory.hduFactory(data);
101 
102             BasicHDU<?> bhdu = bhdu1;
103             f.addHDU(bhdu);
104 
105             Fits.setChecksum(bhdu);
106             ByteArrayOutputStream bs = new ByteArrayOutputStream();
107 
108             try (FitsOutputStream bdos = new FitsOutputStream(bs)) {
109                 f.write(bdos);
110             }
111 
112             byte[] stream = bs.toByteArray();
113             long chk = Fits.checksum(stream);
114             int val = (int) chk;
115 
116             Assertions.assertEquals(-1, val);
117         }
118     }
119 
120     @Test
121     public void testCheckSumBasic() throws Exception {
122         try (FileInputStream in = new FileInputStream("src/test/resources/nom/tam/fits/test/test.fits")) {
123 
124             try (Fits fits = new Fits()) {
125                 try (FitsInputStream fin = new FitsInputStream(in)) {
126                     fits.setStream(fin);
127                     fits.read();
128                 }
129                 fits.setChecksum();
130             }
131         }
132     }
133 
134     @Test
135     public void testCheckSum2() throws Exception {
136         // AK: This test requires long strings to be disabled.
137         FitsFactory.setLongStringsEnabled(false);
138 
139         try (FileInputStream in = new FileInputStream("src/test/resources/nom/tam/fits/test/test.fits");
140                 Fits fits = new Fits()) {
141             try (FitsInputStream fin = new FitsInputStream(in)) {
142                 fits.setStream(fin);
143                 fits.read();
144             }
145             fits.setChecksum();
146             Assertions.assertEquals(FitsIO.INTEGER_MASK, fits.getHDU(0).calcChecksum());
147         }
148     }
149 
150     // TODO This test fails in the CI for some reason, but not locally.
151     @Disabled
152     @Test
153     public void testIntegerOverflowChecksum() throws Exception {
154         byte[][] data = new byte[2][1440];
155         Arrays.fill(data[0], (byte) 17); // this generates a high checksum.
156         Arrays.fill(data[1], (byte) 17); // this generates a high checksum.
157         ImageData imageData = ImageHDU.encapsulate(data);
158         ImageHDU imageHdu = new ImageHDU(ImageHDU.manufactureHeader(imageData), imageData);
159         // now force no now date in the header (will change the checksum)
160         imageHdu.card(Standard.SIMPLE).comment("XXX").value(true);
161         imageHdu.setChecksum();
162         Assertions.assertEquals("CVfXFTeVCTeVCTeV", imageHdu.getHeader().card(CHECKSUM).card().getValue());
163     }
164 
165     @Test
166     public void testCheckSumDeferred() throws Exception {
167         try (Fits fits = new Fits("src/test/resources/nom/tam/fits/test/test.fits")) {
168             ImageHDU im = (ImageHDU) fits.readHDU();
169 
170             Assertions.assertTrue(im.getData().isDeferred());
171             fits.setChecksum();
172             Assertions.assertTrue(im.getData().isDeferred());
173             Assertions.assertEquals(FitsIO.INTEGER_MASK, im.calcChecksum());
174 
175             // Now load the data in RAM and repeat.
176             im.getData().getData();
177             Assertions.assertFalse(im.getData().isDeferred());
178             fits.setChecksum();
179             Assertions.assertEquals(FitsIO.INTEGER_MASK, im.calcChecksum());
180         }
181     }
182 
183     @Test
184     public void testCheckSumVerifyOfficial() throws Exception {
185         try (Fits fits = new Fits("src/test/resources/nom/tam/fits/test/checksum.fits")) {
186             fits.verifyIntegrity();
187             // No exception...
188 
189             int n = fits.getNumberOfHDUs();
190 
191             for (int i = 0; i < n; i++) {
192                 Assertions.assertTrue(fits.getHDU(i).verifyDataIntegrity());
193                 Assertions.assertTrue(fits.getHDU(i).verifyIntegrity());
194             }
195         }
196     }
197 
198     @Test
199     public void testCheckSumVerifyModifiedHeaderFail() throws Exception {
200         File copy = new File("target/checksum-modhead.fits");
201 
202         Files.copy(new File("src/test/resources/nom/tam/fits/test/checksum.fits").toPath(), copy.toPath(),
203                 StandardCopyOption.REPLACE_EXISTING);
204 
205         try (RandomAccessFile rf = new RandomAccessFile(copy, "rw")) {
206             rf.seek(FitsFactory.FITS_BLOCK_SIZE - 1); // Guaranteed to be inside header.
207             rf.write('~');
208         }
209 
210         try (Fits fits = new Fits(copy)) {
211             Assertions.assertThrows(FitsIntegrityException.class, () -> fits.verifyIntegrity());
212         }
213     }
214 
215     @Test
216     public void testCheckSumVerifyModifiedDatasumFail() throws Exception {
217         File copy = new File("target/checksum-moddata.fits");
218 
219         Files.copy(new File("src/test/resources/nom/tam/fits/test/checksum.fits").toPath(), copy.toPath(),
220                 StandardCopyOption.REPLACE_EXISTING);
221 
222         try (RandomAccessFile rf = new RandomAccessFile(copy, "rw")) {
223             rf.seek(rf.length() - 1);
224             rf.write('~');
225         }
226 
227         try (Fits fits = new Fits(copy)) {
228             Assertions.assertThrows(FitsIntegrityException.class, () -> fits.verifyIntegrity());
229         }
230     }
231 
232     @Test
233     public void testCheckSumVerifyNoSum() throws Exception {
234         try (Fits fits = new Fits("src/test/resources/nom/tam/fits/test/test.fits")) {
235             fits.verifyIntegrity();
236             Assertions.assertFalse(fits.getHDU(0).verifyIntegrity());
237             Assertions.assertFalse(fits.getHDU(0).verifyDataIntegrity());
238         }
239         // No exception...
240     }
241 
242     @Test
243     public void testCheckSumVerifyStream() throws Exception {
244         try (Fits fits = new Fits(new FileInputStream(new File("src/test/resources/nom/tam/fits/test/checksum.fits")))) {
245             fits.verifyIntegrity();
246         }
247         /* No exception */
248     }
249 
250     @Test
251     public void testDatasumVerifyStream() throws Exception {
252         try (Fits fits = new Fits(new FileInputStream(new File("src/test/resources/nom/tam/fits/test/checksum.fits")))) {
253             Assertions.assertTrue(fits.getHDU(0).verifyDataIntegrity());
254         }
255     }
256 
257     @Test
258     public void testCheckSumReadHDUStream() throws Exception {
259         try (FitsInputStream in = new FitsInputStream(
260                 new FileInputStream(new File("src/test/resources/nom/tam/fits/test/checksum.fits")))) {
261             ImageHDU hdu = new ImageHDU(null, null);
262             hdu.read(in);
263             hdu.verifyDataIntegrity();
264             hdu.verifyIntegrity();
265         }
266         /* No exception */
267     }
268 
269     @Test
270     public void testCheckSumReadHDUFile() throws Exception {
271         try (FitsFile in = new FitsFile("src/test/resources/nom/tam/fits/test/checksum.fits", "r")) {
272             ImageHDU hdu = new ImageHDU(null, null);
273             hdu.read(in);
274             hdu.verifyDataIntegrity();
275             hdu.verifyIntegrity();
276         }
277         /* No exception */
278     }
279 
280     @Test
281     public void testCStreamheckSumReads() throws Exception {
282         try (FitsInputStream in = new FitsInputStream(
283                 new FileInputStream(new File("src/test/resources/nom/tam/fits/test/checksum.fits")))) {
284             Assertions.assertEquals(0, in.nextChecksum());
285             for (int i = 0; i < FitsFactory.FITS_BLOCK_SIZE; i++) {
286                 in.read();
287             }
288             Assertions.assertNotEquals(0, in.nextChecksum());
289         }
290         /* No exception */
291     }
292 
293     @Test
294     public void testCheckSumVerifyFail() throws Exception {
295         try (Fits fits = new Fits("src/test/resources/nom/tam/fits/test/test.fits")) {
296             fits.read();
297             fits.setChecksum();
298 
299             ImageHDU im = (ImageHDU) fits.getHDU(0);
300 
301             short[][] data = (short[][]) im.getData().getData();
302             data[0][0]++;
303 
304             // Deferree read
305             Assertions.assertNotEquals(FitsCheckSum.checksum(im.getData()), im.getStoredDatasum());
306             Assertions.assertNotEquals(FitsCheckSum.checksum(im), im.getStoredChecksum());
307             Assertions.assertNotEquals(fits.calcChecksum(0), im.getStoredChecksum());
308 
309             // in-memory
310             Assertions.assertNotEquals(im.getData().calcChecksum(), im.getStoredDatasum());
311             Assertions.assertNotEquals(im.calcChecksum(), im.getStoredChecksum());
312         }
313     }
314 
315     @Test
316     public void testCheckSumIncrement() throws Exception {
317         try (Fits fits = new Fits("src/test/resources/nom/tam/fits/test/test.fits")) {
318             fits.read();
319             fits.setChecksum();
320 
321             ImageHDU im = (ImageHDU) fits.getHDU(0);
322             Header h = im.getHeader();
323 
324             short[][] data = (short[][]) im.getData().getData();
325 
326             data[0][0]++;
327 
328             FitsCheckSum.setDatasum(h, FitsCheckSum.checksum(im.getData()));
329 
330             // Deferred read
331             Assertions.assertEquals(FitsCheckSum.checksum(im.getData()), im.getStoredDatasum());
332             Assertions.assertEquals(FitsIO.INTEGER_MASK, im.calcChecksum());
333 
334             // in-memory
335             im.setChecksum();
336             Assertions.assertEquals(im.getData().calcChecksum(), im.getStoredDatasum());
337             Assertions.assertEquals(FitsIO.INTEGER_MASK, im.calcChecksum());
338         }
339     }
340 
341     @Test
342     public void testCheckSumDecode() throws Exception {
343         long sum = 20220829;
344         Assertions.assertEquals(sum, FitsCheckSum.decode(FitsCheckSum.encode(sum)));
345         Assertions.assertEquals(sum, FitsCheckSum.decode(FitsCheckSum.encode(sum, false), false));
346         Assertions.assertEquals(sum, FitsCheckSum.decode(FitsCheckSum.encode(sum, true), true));
347         Assertions.assertEquals(sum, FitsCheckSum.decode(FitsCheckSum.checksumEnc(sum, false), false));
348         Assertions.assertEquals(sum, FitsCheckSum.decode(FitsCheckSum.checksumEnc(sum, true), true));
349     }
350 
351     @Test
352     public void testCheckSumDecodeInvalidLength() throws Exception {
353         Assertions.assertThrows(IllegalArgumentException.class, () -> FitsCheckSum.decode(""));
354     }
355 
356     @Test
357     public void testCheckSumDecodeInvalidChars() throws Exception {
358         byte[] b = new byte[16];
359         Arrays.fill(b, (byte) 0x2f);
360         Assertions.assertThrows(IllegalArgumentException.class, () -> FitsCheckSum.decode(new String(b)));
361     }
362 
363     @Test
364     public void testCheckSumNoDatasum() throws Exception {
365         Assertions.assertThrows(FitsException.class, () -> FitsCheckSum.getStoredDatasum(new Header()));
366     }
367 
368     @Test
369     public void testCheckSumNoChecksum() throws Exception {
370         Assertions.assertThrows(FitsException.class, () -> FitsCheckSum.getStoredChecksum(new Header()));
371     }
372 
373     @Test
374     public void testCheckSumwWrap() throws Exception {
375         Assertions.assertEquals(0, FitsCheckSum.sumOf(Integer.MAX_VALUE, Integer.MAX_VALUE) & ~FitsIO.INTEGER_MASK);
376     }
377 
378     @Test
379     public void testCheckSumAutoAdd() throws Exception {
380         Header h = new Header();
381         h.setSimple(true);
382         h.setBitpix(Bitpix.INTEGER);
383         h.setNaxes(0);
384         FitsCheckSum.checksum(h);
385         Assertions.assertFalse(h.containsKey(CHECKSUM));
386     }
387 
388     @Test
389     public void testCheckSumKeep() throws Exception {
390         Header h = new Header();
391         h.setSimple(true);
392         h.setBitpix(Bitpix.INTEGER);
393         h.setNaxes(0);
394         h.addValue(DATASUM, "0");
395         h.addValue(CHECKSUM, "blah");
396         FitsCheckSum.checksum(h);
397         Assertions.assertEquals("blah", h.getStringValue(CHECKSUM));
398     }
399 
400     @Test
401     public void testCheckSumMissingDatasum() throws Exception {
402         Header h = new Header();
403         h.setSimple(true);
404         h.setBitpix(Bitpix.INTEGER);
405         h.setNaxes(0);
406         h.addValue(CHECKSUM, "blah");
407         h.validate(true);
408         Assertions.assertFalse(h.containsKey(CHECKSUM));
409     }
410 
411     @Test
412     public void testCheckSumSubtract() throws Exception {
413         long a = 20220829;
414         long b = 19740131;
415 
416         long sum = FitsCheckSum.sumOf(a, b);
417 
418         Assertions.assertEquals(b, FitsCheckSum.differenceOf(sum, a));
419         Assertions.assertEquals(a, FitsCheckSum.differenceOf(sum, b));
420     }
421 
422     @Test
423     public void testSetChecksumFitsException() throws Exception {
424         ImageData data = new ImageData() {
425             @Override
426             public void write(ArrayDataOutput bdos) throws FitsException {
427                 throw new FitsException("not implemented");
428             }
429         };
430 
431         Header h = new Header();
432         h.setSimple(true);
433         h.setBitpix(Bitpix.INTEGER);
434         h.setNaxes(0);
435 
436         ImageHDU im = new ImageHDU(h, data);
437 
438         Assertions.assertThrows(FitsException.class, () -> FitsCheckSum.setChecksum(im));
439     }
440 
441     @Test
442     public void testChecksumFitsLoaded() throws Exception {
443         try (Fits fits = new Fits(new File("src/test/resources/nom/tam/fits/test/test.fits"))) {
444             fits.read();
445             fits.setChecksum();
446             BasicHDU<?> hdu = fits.getHDU(0);
447             Header h = hdu.getHeader();
448             Assertions.assertTrue(h.containsKey(CHECKSUM));
449             Assertions.assertTrue(h.containsKey(DATASUM));
450             Assertions.assertNotEquals(0, hdu.getStoredChecksum());
451             Assertions.assertNotEquals(0, hdu.getStoredDatasum());
452         }
453     }
454 
455     @Test
456     public void testChecksumFitsUnloaded() throws Exception {
457         try (Fits fits = new Fits(new File("src/test/resources/nom/tam/fits/test/test.fits"))) {
458             fits.setChecksum();
459             BasicHDU<?> hdu = fits.getHDU(0);
460             Header h = hdu.getHeader();
461             Assertions.assertTrue(h.containsKey(CHECKSUM));
462             Assertions.assertTrue(h.containsKey(DATASUM));
463             Assertions.assertNotEquals(0, hdu.getStoredChecksum());
464             Assertions.assertNotEquals(0, hdu.getStoredDatasum());
465         }
466     }
467 
468     @Test
469     public void testChecksumFitsCreated() throws Exception {
470         int[][] data = new int[5][5];
471         data[0][0] = 1;
472 
473         try (Fits fits = new Fits()) {
474             fits.addHDU(FitsFactory.hduFactory(data));
475             fits.setChecksum();
476             BasicHDU<?> hdu = fits.getHDU(0);
477             Header h = hdu.getHeader();
478             Assertions.assertTrue(h.containsKey(CHECKSUM));
479             Assertions.assertTrue(h.containsKey(DATASUM));
480             Assertions.assertNotEquals(0, hdu.getStoredChecksum());
481             Assertions.assertNotEquals(0, hdu.getStoredDatasum());
482         }
483     }
484 
485     @Test
486     public void testChecksumNullFile() throws Exception {
487         Assertions.assertEquals(0, FitsCheckSum.checksum((RandomAccess) null, 0, 1000));
488     }
489 
490     @Test
491     public void testDeferredChecksumRange() throws Exception {
492         int[][] im = new int[10][10];
493 
494         for (int i = 0; i < 10; i++) {
495             for (int j = 0; j < 10; j++) {
496                 im[i][j] = i + j;
497             }
498         }
499 
500         ImageHDU hdu = (ImageHDU) FitsFactory.hduFactory(im);
501         long sum = hdu.getData().calcChecksum();
502 
503         try (Fits fits = new Fits()) {
504             fits.addHDU(hdu);
505             fits.addHDU(hdu);
506             fits.write("target/checksumRangeTest.fits");
507         }
508 
509         try (Fits fits = new Fits("target/checksumRangeTest.fits")) {
510             Assertions.assertEquals(sum, fits.calcDatasum(0));
511             fits.close();
512         }
513     }
514 
515     @Test
516     public void testChecksumEncode() throws Exception {
517         Assertions.assertEquals("hcHjjc9ghcEghc9g", FitsCheckSum.encode(868229149L));
518     }
519 
520     @Test
521     public void testChecksumDecode() throws Exception {
522         Assertions.assertEquals(868229149L, FitsCheckSum.decode("hcHjjc9ghcEghc9g"));
523     }
524 }