/* The MIT License (MIT)
*
* Copyright (c) 2015 Reinventing Geospatial, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.rgi.geopackage.features;
import com.rgi.geopackage.features.geometry.Geometry;
import com.rgi.geopackage.features.geometry.xy.Envelope;
import com.rgi.geopackage.features.geometry.xy.WkbPoint;
import org.junit.Test;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Objects;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
/**
* @author Luke Lambert
*/
@SuppressWarnings("JavaDoc")
public class BinaryHeaderTest
{
/**
* Constructor should fail on a null byte array
*/
@Test(expected = IllegalArgumentException.class)
public void constructorNullByteArray()
{
new BinaryHeader(null);
}
/**
* Constructor should fail on a byte array that's too short
*/
@Test(expected = IllegalArgumentException.class)
public void constructorArrayTooShort()
{
final byte[] bytes = {(byte)71, (byte)80};
new BinaryHeader(bytes);
}
/**
* Constructor should fail on a byte array that doesn't begin with the bytes 'G' and 'P'
*/
@Test(expected = IllegalArgumentException.class)
public void constructorArrayDoesntStartWithMagicBytes()
{
final byte[] bytes = {(byte)0, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0};
new BinaryHeader(bytes);
}
/**
* Constructor should fail when the header specifies more bytes exist in
* the envelope than there are left in the array
*/
@Test(expected = IllegalArgumentException.class)
public void constructorArrayTooShortForEnvelopeIndicator()
{
final byte[] bytes = {(byte)71, (byte)80, (byte)0, (byte)0b00001000, (byte)0, (byte)0, (byte)0, (byte)0};
new BinaryHeader(bytes);
}
/**
* Tests that the bytes going in to the constructor are being correctly
* parsed
*/
@Test
public void constructor()
{
final EnvelopeContentsIndicator envelopeContentsIndicator = EnvelopeContentsIndicator.Xyzm;
final int byteSize = 2 + // 2 bytes for the 'magic' header
1 + // 1 byte for version
1 + // 1 byte for flags
4 + // 4 bytes (int32) for the srs id
(8 * envelopeContentsIndicator.getArraySize()); // 8 bytes per double, array size number of doubles
final ByteBuffer byteBuffer = ByteBuffer.allocate(byteSize);
// magic header
byteBuffer.put((byte)71); // 'G'
byteBuffer.put((byte)80); // 'P'
final byte version = (byte)0; // Version "1"
byteBuffer.put(version);
final BinaryType binaryType = BinaryType.Extended;
final Contents contents = Contents.Empty;
@SuppressWarnings("NumericCastThatLosesPrecision")
final byte flags = (byte)(// skip the first two reserved bits
binaryType.getBitMask() |
contents.getBitMask() | // 1 = empty geometry
envelopeContentsIndicator.getCode() << 1 | // 4 component envelope
(byte)(Objects.equals(byteBuffer.order(), ByteOrder.BIG_ENDIAN) ? 0b00000000 : 0b00000001));
byteBuffer.put(flags); // extended empty geometry with a 4 component envelope
final int srsId = 4326;
byteBuffer.putInt(srsId); // srs id
final double[] envelope = {Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN}; // min x, max x, min y, max y, min z, max z, min m, max m
byteBuffer.putDouble(envelope[0]); // min x
byteBuffer.putDouble(envelope[1]); // max x
byteBuffer.putDouble(envelope[2]); // min y
byteBuffer.putDouble(envelope[3]); // max y
byteBuffer.putDouble(envelope[4]); // min z
byteBuffer.putDouble(envelope[5]); // max z
byteBuffer.putDouble(envelope[6]); // min m
byteBuffer.putDouble(envelope[7]); // max m
final BinaryHeader binaryHeader = new BinaryHeader(byteBuffer.array());
assertEquals("Version incorrectly set by the constructor",
version,
binaryHeader.getVersion());
assertEquals("Binary type incorrectly set by the constructor",
binaryType,
binaryHeader.getBinaryType());
assertEquals("Contents incorrectly set by the constructor",
contents,
binaryHeader.getContents());
assertSame("Byte order incorrectly set by the constructor",
byteBuffer.order(),
binaryHeader.getByteOrder());
assertEquals("Spatial reference system identifier incorrectly set by the constructor",
srsId,
binaryHeader.getSpatialReferenceSystemIdentifier());
assertSame("Envelope contents indicator incorrectly set by the constructor",
envelopeContentsIndicator,
binaryHeader.getEnvelopeContentsIndicator());
assertArrayEquals("Envelope array incorrectly set by the constructor",
envelope,
binaryHeader.getEnvelopeArray(),
0.0);
assertEquals("Flags incorrectly set by the constructor",
flags,
binaryHeader.getFlags());
assertEquals("Constructor has incorrectly calculated the binary header's total byte size",
byteSize,
binaryHeader.getByteSize());
}
/**
* Constructor should fail when binary type is null
*/
@Test(expected = IllegalArgumentException.class)
public void binaryTypeIsNull()
{
new BinaryHeader((byte)1,
null,
Contents.NotEmpty,
ByteOrder.BIG_ENDIAN,
4326,
EnvelopeContentsIndicator.NoEnvelope,
emptyEnvelope);
}
/**
* Constructor should fail when contents is null
*/
@Test(expected = IllegalArgumentException.class)
public void contentsIsNull()
{
new BinaryHeader((byte)1,
BinaryType.Standard,
null,
ByteOrder.BIG_ENDIAN,
4326,
EnvelopeContentsIndicator.NoEnvelope,
emptyEnvelope);
}
/**
* Constructor should fail when byte order is null
*/
@Test(expected = IllegalArgumentException.class)
public void byteOrderIsNull()
{
new BinaryHeader((byte)1,
BinaryType.Standard,
Contents.NotEmpty,
null,
4326,
EnvelopeContentsIndicator.NoEnvelope,
emptyEnvelope);
}
/**
* Constructor should fail when envelope contents indicator is null
*/
@Test(expected = IllegalArgumentException.class)
public void envelopeContentsIndicatorIsNull()
{
new BinaryHeader((byte)1,
BinaryType.Standard,
Contents.NotEmpty,
ByteOrder.BIG_ENDIAN,
4326,
null,
emptyEnvelope);
}
/**
* Constructor should fail when envelope is null
*/
@Test(expected = IllegalArgumentException.class)
public void envelopeIsNull()
{
new BinaryHeader((byte)1,
BinaryType.Standard,
Contents.NotEmpty,
ByteOrder.BIG_ENDIAN,
4326,
EnvelopeContentsIndicator.NoEnvelope,
null);
}
/**
* Constructor should fail when the envelope size doesn't match the contents indicator
*/
@Test(expected = IllegalArgumentException.class)
public void envelopeSizeDoesntMatchContentsIndicator()
{
new BinaryHeader((byte)1,
BinaryType.Standard,
Contents.NotEmpty,
ByteOrder.BIG_ENDIAN,
4326,
EnvelopeContentsIndicator.Xyzm,
emptyEnvelope);
}
/**
* Constructor should fail when the envelope size doesn't match the contents indicator
*/
@Test
public void goodConstructorCall()
{
final byte version = (byte)1;
final BinaryType binaryType = BinaryType.Standard;
final Contents contents = Contents.NotEmpty;
final ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
final int srsId = 4326;
final EnvelopeContentsIndicator envelopeIndicator = EnvelopeContentsIndicator.NoEnvelope;
final BinaryHeader header = new BinaryHeader(version,
binaryType,
contents,
byteOrder,
srsId,
envelopeIndicator,
emptyEnvelope);
assertEquals("Version incorrectly set by the constructor",
version,
header.getVersion());
assertEquals("Binary type incorrectly set by the constructor",
binaryType,
header.getBinaryType());
assertEquals("Contents incorrectly set by the constructor",
contents,
header.getContents());
assertSame("Byte order incorrectly set by the constructor",
byteOrder,
header.getByteOrder());
assertEquals("Spatial reference system identifier incorrectly set by the constructor",
srsId,
header.getSpatialReferenceSystemIdentifier());
assertSame("Envelope contents indicator incorrectly set by the constructor",
envelopeIndicator,
header.getEnvelopeContentsIndicator());
assertArrayEquals("Envelope incorrectly set by the constructor",
emptyEnvelope,
header.getEnvelopeArray(),
0.0);
}
/**
* writeBytes() should fail on null ByteBuffer
*/
@Test(expected = IllegalArgumentException.class)
public void writeBytesFailsOnNullByteBuffer() throws IOException
{
new BinaryHeader((byte)1,
BinaryType.Standard,
Contents.NotEmpty,
ByteOrder.BIG_ENDIAN,
4326,
EnvelopeContentsIndicator.NoEnvelope,
emptyEnvelope).writeBytes(null);
}
/**
* writeBytes() should produce a byte buffer that can be re-read to produce an identical header
*/
@Test
public void writeBytesRoundTrip() throws IOException
{
final double[] envelope = {0.0, 0.0, 0.0, 0.0};
final BinaryHeader expected = new BinaryHeader((byte)1,
BinaryType.Standard,
Contents.NotEmpty,
ByteOrder.BIG_ENDIAN,
4326,
EnvelopeContentsIndicator.Xy,
envelope);
final ByteOutputStream byteOutputStream = new ByteOutputStream(expected.getByteSize());
expected.writeBytes(byteOutputStream);
final BinaryHeader actual = new BinaryHeader(byteOutputStream.array());
assertEquals("writeBytes() did not produce a correct BinaryHeader",
expected,
actual);
}
/**
* Test that calling .equals on the same object returns true
*/
@Test
public void equalSameObject()
{
final BinaryHeader header = new BinaryHeader((byte)1,
BinaryType.Standard,
Contents.NotEmpty,
ByteOrder.BIG_ENDIAN,
4326,
EnvelopeContentsIndicator.NoEnvelope,
emptyEnvelope);
assertEquals("Equals should have returned true for testing with the same object",
header,
header);
}
/**
* Test that calling .equals on an object of a different type, fails
*/
@Test
public void equalDifferentType()
{
final BinaryHeader header = new BinaryHeader((byte)1,
BinaryType.Standard,
Contents.NotEmpty,
ByteOrder.BIG_ENDIAN,
4326,
EnvelopeContentsIndicator.NoEnvelope,
emptyEnvelope);
//noinspection MisorderedAssertEqualsArguments
assertNotEquals("Equals should have returned false for testing with a BinaryHeader with an object of a different type",
header,
new Object());
}
/**
* Test that hashCode() for different BinaryHeaders with the same parameters produce identical results
*/
@Test
public void testHashCode()
{
final BinaryHeader header1 = new BinaryHeader((byte)1,
BinaryType.Standard,
Contents.NotEmpty,
ByteOrder.BIG_ENDIAN,
4326,
EnvelopeContentsIndicator.NoEnvelope,
emptyEnvelope);
final BinaryHeader header2 = new BinaryHeader((byte)1,
BinaryType.Standard,
Contents.NotEmpty,
ByteOrder.BIG_ENDIAN,
4326,
EnvelopeContentsIndicator.NoEnvelope,
emptyEnvelope);
assertEquals("Equals should have returned true for testing with the same object",
header1.hashCode(),
header2.hashCode());
}
/**
* Static writeBytes() should fail on a null geometry
*/
@Test(expected = IllegalArgumentException.class)
public void staticWriteBytes() throws IOException
{
BinaryHeader.writeBytes(new ByteOutputStream(),
null,
4326);
}
/**
* Static writeBytes() produce a BinaryHeader (round trip) with BinaryType,
* Contents, and envelope that corresponds to the input geometry
*/
@Test
public void staticWriteBytesRoundTrip() throws IOException
{
try(final ByteOutputStream byteOutputStream = new ByteOutputStream())
{
final Geometry geometry = new WkbPoint(0.0, 0.0);
final Envelope envelope = geometry.createEnvelope();
final int srsId = 4326;
BinaryHeader.writeBytes(byteOutputStream,
geometry,
srsId);
final BinaryHeader header = new BinaryHeader(byteOutputStream.array());
assertEquals("Binary type incorrectly set by the constructor",
BinaryType.fromGeometryTypeName(geometry.getGeometryTypeName()),
header.getBinaryType());
assertEquals("Contents incorrectly set by the constructor",
geometry.getContents(),
header.getContents());
assertEquals("Spatial reference system identifier incorrectly set by the constructor",
srsId,
header.getSpatialReferenceSystemIdentifier());
assertSame("Envelope contents indicator incorrectly set by the constructor",
envelope.getContentsIndicator(),
header.getEnvelopeContentsIndicator());
assertArrayEquals("Envelope incorrectly set by the constructor",
envelope.toArray(),
header.getEnvelopeArray(),
0.0);
}
}
private static final double[] emptyEnvelope = {};
}