diff --git a/README.md b/README.md index 2a6571ad3..1a7c1f439 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Here is a list of sbt commands for daily development: > ~test:compile # Compile both source and test codes > ~test # Run tests upon source code change > ~testOnly *MessagePackTest # Run tests in the specified class -> ~testOnly *MessagePackTest -- -n prim # Run the test tagged as "prim" +> ~testOnly *MessagePackTest -- (pattern) # Run tests matching the pattern > project msgpack-core # Focus on a specific project > package # Create a jar file in the target folder of each project > findbugs # Produce findbugs report in target/findbugs diff --git a/build.sbt b/build.sbt index a97c20f7b..eae3cd51a 100644 --- a/build.sbt +++ b/build.sbt @@ -5,6 +5,8 @@ Global / concurrentRestrictions := Seq( Tags.limit(Tags.Test, 1) ) +val AIRFRAME_VERSION = "20.4.1" + // Use dynamic snapshot version strings for non tagged versions ThisBuild / dynverSonatypeSnapshots := true // Use coursier friendly version separator @@ -71,15 +73,19 @@ lazy val msgpackCore = Project(id = "msgpack-core", base = file("msgpack-core")) "org.msgpack.value", "org.msgpack.value.impl" ), + testFrameworks += new TestFramework("wvlet.airspec.Framework"), libraryDependencies ++= Seq( // msgpack-core should have no external dependencies junitInterface, - "org.scalatest" %% "scalatest" % "3.2.8" % "test", - "org.scalacheck" %% "scalacheck" % "1.15.4" % "test", - "org.xerial" %% "xerial-core" % "3.6.0" % "test", - "org.msgpack" % "msgpack" % "0.6.12" % "test", - "commons-codec" % "commons-codec" % "1.12" % "test", - "com.typesafe.akka" %% "akka-actor" % "2.5.23" % "test" + "org.wvlet.airframe" %% "airframe-json" % AIRFRAME_VERSION % "test", + "org.wvlet.airframe" %% "airspec" % AIRFRAME_VERSION % "test", + // Add property testing support with forAll methods + "org.scalacheck" %% "scalacheck" % "1.15.4" % "test", + // For performance comparison with msgpack v6 + "org.msgpack" % "msgpack" % "0.6.12" % "test", + // For integration test with Akka + "com.typesafe.akka" %% "akka-actor" % "2.5.23" % "test", + "org.scala-lang.modules" %% "scala-collection-compat" % "2.4.3" % "test" ) ) diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessagePack.java b/msgpack-core/src/main/java/org/msgpack/core/MessagePack.java index ed8b1e405..edd449b34 100644 --- a/msgpack-core/src/main/java/org/msgpack/core/MessagePack.java +++ b/msgpack-core/src/main/java/org/msgpack/core/MessagePack.java @@ -165,6 +165,8 @@ public static final boolean isFixedRaw(byte b) public static final byte MAP32 = (byte) 0xdf; public static final byte NEGFIXINT_PREFIX = (byte) 0xe0; + + public static final byte EXT_TIMESTAMP = (byte) -1; } private MessagePack() diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessagePacker.java b/msgpack-core/src/main/java/org/msgpack/core/MessagePacker.java index 6837f9f72..4cf789d9f 100644 --- a/msgpack-core/src/main/java/org/msgpack/core/MessagePacker.java +++ b/msgpack-core/src/main/java/org/msgpack/core/MessagePacker.java @@ -32,6 +32,7 @@ import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; +import java.time.Instant; import static org.msgpack.core.MessagePack.Code.ARRAY16; import static org.msgpack.core.MessagePack.Code.ARRAY32; @@ -41,6 +42,7 @@ import static org.msgpack.core.MessagePack.Code.EXT16; import static org.msgpack.core.MessagePack.Code.EXT32; import static org.msgpack.core.MessagePack.Code.EXT8; +import static org.msgpack.core.MessagePack.Code.EXT_TIMESTAMP; import static org.msgpack.core.MessagePack.Code.FALSE; import static org.msgpack.core.MessagePack.Code.FIXARRAY_PREFIX; import static org.msgpack.core.MessagePack.Code.FIXEXT1; @@ -798,6 +800,111 @@ else if (s.length() < (1 << 16)) { return this; } + /** + * Writes a Timestamp value. + * + *
+ * This method writes a timestamp value using timestamp format family. + * + * @param instant the timestamp to be written + * @return this packer + * @throws IOException when underlying output throws IOException + */ + public MessagePacker packTimestamp(Instant instant) + throws IOException + { + return packTimestamp(instant.getEpochSecond(), instant.getNano()); + } + + /** + * Writes a Timesamp value using a millisecond value (e.g., System.currentTimeMillis()) + * @param millis the millisecond value + * @return this packer + * @throws IOException when underlying output throws IOException + */ + public MessagePacker packTimestamp(long millis) + throws IOException + { + return packTimestamp(Instant.ofEpochMilli(millis)); + } + + private static final long NANOS_PER_SECOND = 1000000000L; + + /** + * Writes a Timestamp value. + * + *
+ * This method writes a timestamp value using timestamp format family. + * + * @param epochSecond the number of seconds from 1970-01-01T00:00:00Z + * @param nanoAdjustment the nanosecond adjustment to the number of seconds, positive or negative + * @return this + * @throws IOException when underlying output throws IOException + * @throws ArithmeticException when epochSecond plus nanoAdjustment in seconds exceeds the range of long + */ + public MessagePacker packTimestamp(long epochSecond, int nanoAdjustment) + throws IOException, ArithmeticException + { + long sec = Math.addExact(epochSecond, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND)); + long nsec = Math.floorMod((long) nanoAdjustment, NANOS_PER_SECOND); + + if (sec >>> 34 == 0) { + // sec can be serialized in 34 bits. + long data64 = (nsec << 34) | sec; + if ((data64 & 0xffffffff00000000L) == 0L) { + // sec can be serialized in 32 bits and nsec is 0. + // use timestamp 32 + writeTimestamp32((int) sec); + } + else { + // sec exceeded 32 bits or nsec is not 0. + // use timestamp 64 + writeTimestamp64(data64); + } + } + else { + // use timestamp 96 format + writeTimestamp96(sec, (int) nsec); + } + return this; + } + + private void writeTimestamp32(int sec) + throws IOException + { + // timestamp 32 in fixext 4 + ensureCapacity(6); + buffer.putByte(position++, FIXEXT4); + buffer.putByte(position++, EXT_TIMESTAMP); + buffer.putInt(position, sec); + position += 4; + } + + private void writeTimestamp64(long data64) + throws IOException + { + // timestamp 64 in fixext 8 + ensureCapacity(10); + buffer.putByte(position++, FIXEXT8); + buffer.putByte(position++, EXT_TIMESTAMP); + buffer.putLong(position, data64); + position += 8; + } + + private void writeTimestamp96(long sec, int nsec) + throws IOException + { + // timestamp 96 in ext 8 + ensureCapacity(15); + buffer.putByte(position++, EXT8); + buffer.putByte(position++, (byte) 12); // length of nsec and sec + buffer.putByte(position++, EXT_TIMESTAMP); + buffer.putInt(position, nsec); + position += 4; + buffer.putLong(position, sec); + position += 8; + } + /** * Writes header of an Array value. *
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageUnpacker.java b/msgpack-core/src/main/java/org/msgpack/core/MessageUnpacker.java
index 6d0d57ab3..b43204beb 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessageUnpacker.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageUnpacker.java
@@ -32,7 +32,9 @@
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
+import java.time.Instant;
+import static org.msgpack.core.MessagePack.Code.EXT_TIMESTAMP;
import static org.msgpack.core.Preconditions.checkNotNull;
/**
@@ -595,6 +597,12 @@ private static MessagePackException unexpected(String expected, byte b)
}
}
+ private static MessagePackException unexpectedExtension(String expected, int expectedType, int actualType)
+ {
+ return new MessageTypeException(String.format("Expected extension type %s (%d), but got extension type %d",
+ expected, expectedType, actualType));
+ }
+
public ImmutableValue unpackValue()
throws IOException
{
@@ -643,7 +651,12 @@ public ImmutableValue unpackValue()
}
case EXTENSION: {
ExtensionTypeHeader extHeader = unpackExtensionTypeHeader();
- return ValueFactory.newExtension(extHeader.getType(), readPayload(extHeader.getLength()));
+ switch (extHeader.getType()) {
+ case EXT_TIMESTAMP:
+ return ValueFactory.newTimestamp(unpackTimestamp(extHeader));
+ default:
+ return ValueFactory.newExtension(extHeader.getType(), readPayload(extHeader.getLength()));
+ }
}
default:
throw new MessageNeverUsedFormatException("Unknown value type");
@@ -707,7 +720,13 @@ public Variable unpackValue(Variable var)
}
case EXTENSION: {
ExtensionTypeHeader extHeader = unpackExtensionTypeHeader();
- var.setExtensionValue(extHeader.getType(), readPayload(extHeader.getLength()));
+ switch (extHeader.getType()) {
+ case EXT_TIMESTAMP:
+ var.setTimestampValue(unpackTimestamp(extHeader));
+ break;
+ default:
+ var.setExtensionValue(extHeader.getType(), readPayload(extHeader.getLength()));
+ }
return var;
}
default:
@@ -1257,6 +1276,45 @@ private String decodeStringFastPath(int length)
}
}
+ public Instant unpackTimestamp()
+ throws IOException
+ {
+ ExtensionTypeHeader ext = unpackExtensionTypeHeader();
+ return unpackTimestamp(ext);
+ }
+
+ /**
+ * Internal method that can be used only when the extension type header is already read.
+ */
+ private Instant unpackTimestamp(ExtensionTypeHeader ext) throws IOException
+ {
+ if (ext.getType() != EXT_TIMESTAMP) {
+ throw unexpectedExtension("Timestamp", EXT_TIMESTAMP, ext.getType());
+ }
+ switch (ext.getLength()) {
+ case 4: {
+ // Need to convert Java's int (int32) to uint32
+ long u32 = readInt() & 0xffffffffL;
+ return Instant.ofEpochSecond(u32);
+ }
+ case 8: {
+ long data64 = readLong();
+ int nsec = (int) (data64 >>> 34);
+ long sec = data64 & 0x00000003ffffffffL;
+ return Instant.ofEpochSecond(sec, nsec);
+ }
+ case 12: {
+ // Need to convert Java's int (int32) to uint32
+ long nsecU32 = readInt() & 0xffffffffL;
+ long sec = readLong();
+ return Instant.ofEpochSecond(sec, nsecU32);
+ }
+ default:
+ throw new MessageFormatException(String.format("Timestamp extension type (%d) expects 4, 8, or 12 bytes of payload but got %d bytes",
+ EXT_TIMESTAMP, ext.getLength()));
+ }
+ }
+
/**
* Reads header of an array.
*
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableTimestampValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableTimestampValue.java
new file mode 100644
index 000000000..bd4a901bb
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableTimestampValue.java
@@ -0,0 +1,26 @@
+//
+// MessagePack for Java
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+package org.msgpack.value;
+
+/**
+ * Immutable representation of MessagePack's Timestamp type.
+ *
+ * @see org.msgpack.value.TimestampValue
+ */
+public interface ImmutableTimestampValue
+ extends TimestampValue, ImmutableValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableValue.java
index 88798a13d..f85c69bac 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/ImmutableValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableValue.java
@@ -47,4 +47,7 @@ public interface ImmutableValue
@Override
public ImmutableStringValue asStringValue();
+
+ @Override
+ public ImmutableTimestampValue asTimestampValue();
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/TimestampValue.java b/msgpack-core/src/main/java/org/msgpack/value/TimestampValue.java
new file mode 100644
index 000000000..465579b01
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/TimestampValue.java
@@ -0,0 +1,33 @@
+//
+// MessagePack for Java
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+package org.msgpack.value;
+
+import java.time.Instant;
+
+/**
+ * Value representation of MessagePack's Timestamp type.
+ */
+public interface TimestampValue
+ extends ExtensionValue
+{
+ long getEpochSecond();
+
+ int getNano();
+
+ long toEpochMillis();
+
+ Instant toInstant();
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/Value.java b/msgpack-core/src/main/java/org/msgpack/value/Value.java
index 546dfbbf4..a3d1ac365 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/Value.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/Value.java
@@ -16,6 +16,7 @@
package org.msgpack.value;
import org.msgpack.core.MessagePacker;
+import org.msgpack.core.MessageTypeCastException;
import java.io.IOException;
@@ -180,6 +181,14 @@ public interface Value
*/
boolean isExtensionValue();
+ /**
+ * Returns true if the type of this value is Timestamp.
+ *
+ * If this method returns true, {@code asTimestamp} never throws exceptions.
+ * Note that you can't use instanceof
or cast ((MapValue) thisValue)
to check type of a value because type of a mutable value is variable.
+ */
+ boolean isTimestampValue();
+
/**
* Returns the value as {@code NilValue}. Otherwise throws {@code MessageTypeCastException}.
*
@@ -280,6 +289,15 @@ public interface Value
*/
ExtensionValue asExtensionValue();
+ /**
+ * Returns the value as {@code TimestampValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof
or cast ((TimestampValue) thisValue)
to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not Map.
+ */
+ TimestampValue asTimestampValue();
+
/**
* Serializes the value using the specified {@code MessagePacker}
*
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ValueFactory.java b/msgpack-core/src/main/java/org/msgpack/value/ValueFactory.java
index 21a4f85dd..dc1e28da8 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/ValueFactory.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/ValueFactory.java
@@ -25,8 +25,10 @@
import org.msgpack.value.impl.ImmutableMapValueImpl;
import org.msgpack.value.impl.ImmutableNilValueImpl;
import org.msgpack.value.impl.ImmutableStringValueImpl;
+import org.msgpack.value.impl.ImmutableTimestampValueImpl;
import java.math.BigInteger;
+import java.time.Instant;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.LinkedHashMap;
@@ -295,4 +297,19 @@ public static ImmutableExtensionValue newExtension(byte type, byte[] data)
{
return new ImmutableExtensionValueImpl(type, data);
}
+
+ public static ImmutableTimestampValue newTimestamp(Instant timestamp)
+ {
+ return new ImmutableTimestampValueImpl(timestamp);
+ }
+
+ public static ImmutableTimestampValue newTimestamp(long millis)
+ {
+ return newTimestamp(Instant.ofEpochMilli(millis));
+ }
+
+ public static ImmutableTimestampValue newTimestamp(long epochSecond, int nanoAdjustment)
+ {
+ return newTimestamp(Instant.ofEpochSecond(epochSecond, nanoAdjustment));
+ }
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ValueType.java b/msgpack-core/src/main/java/org/msgpack/value/ValueType.java
index b06478402..8eeb957b3 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/ValueType.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/ValueType.java
@@ -36,6 +36,12 @@ public enum ValueType
MAP(false, false),
EXTENSION(false, false);
+ /**
+ * Design note: We do not add Timestamp as a ValueType here because
+ * detecting Timestamp values requires reading 1-3 bytes ahead while the other
+ * value types can be determined just by reading the first one byte.
+ */
+
private final boolean numberType;
private final boolean rawType;
diff --git a/msgpack-core/src/main/java/org/msgpack/value/Variable.java b/msgpack-core/src/main/java/org/msgpack/value/Variable.java
index 28295fe1c..ae88170c7 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/Variable.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/Variable.java
@@ -30,6 +30,7 @@
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
+import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
@@ -109,6 +110,12 @@ public boolean isExtensionValue()
return getValueType().isExtensionType();
}
+ @Override
+ public boolean isTimestampValue()
+ {
+ return false;
+ }
+
@Override
public NilValue asNilValue()
{
@@ -175,6 +182,12 @@ public ExtensionValue asExtensionValue()
throw new MessageTypeCastException();
}
+ @Override
+ public TimestampValue asTimestampValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
@Override
public boolean equals(Object obj)
{
@@ -211,7 +224,8 @@ public static enum Type
RAW_STRING(ValueType.STRING),
LIST(ValueType.ARRAY),
MAP(ValueType.MAP),
- EXTENSION(ValueType.EXTENSION);
+ EXTENSION(ValueType.EXTENSION),
+ TIMESTAMP(ValueType.EXTENSION);
private final ValueType valueType;
@@ -235,6 +249,7 @@ public ValueType getValueType()
private final ArrayValueAccessor arrayAccessor = new ArrayValueAccessor();
private final MapValueAccessor mapAccessor = new MapValueAccessor();
private final ExtensionValueAccessor extensionAccessor = new ExtensionValueAccessor();
+ private final TimestampValueAccessor timestampAccessor = new TimestampValueAccessor();
private Type type;
@@ -1031,6 +1046,86 @@ public void writeTo(MessagePacker pk)
}
}
+ public Variable setTimestampValue(Instant timestamp)
+ {
+ this.type = Type.TIMESTAMP;
+ this.accessor = timestampAccessor;
+ this.objectValue = ValueFactory.newTimestamp(timestamp);
+ return this;
+ }
+
+ private class TimestampValueAccessor
+ extends AbstractValueAccessor
+ implements TimestampValue
+ {
+ @Override
+ public boolean isTimestampValue()
+ {
+ return true;
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.EXTENSION;
+ }
+
+ @Override
+ public TimestampValue asTimestampValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableTimestampValue immutableValue()
+ {
+ return (ImmutableTimestampValue) objectValue;
+ }
+
+ @Override
+ public byte getType()
+ {
+ return ((ImmutableTimestampValue) objectValue).getType();
+ }
+
+ @Override
+ public byte[] getData()
+ {
+ return ((ImmutableTimestampValue) objectValue).getData();
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ ((ImmutableTimestampValue) objectValue).writeTo(pk);
+ }
+
+ @Override
+ public long getEpochSecond()
+ {
+ return ((ImmutableTimestampValue) objectValue).getEpochSecond();
+ }
+
+ @Override
+ public int getNano()
+ {
+ return ((ImmutableTimestampValue) objectValue).getNano();
+ }
+
+ @Override
+ public long toEpochMillis()
+ {
+ return ((ImmutableTimestampValue) objectValue).toEpochMillis();
+ }
+
+ @Override
+ public Instant toInstant()
+ {
+ return ((ImmutableTimestampValue) objectValue).toInstant();
+ }
+ }
+
////
// Value
//
@@ -1144,6 +1239,12 @@ public boolean isExtensionValue()
return getValueType().isExtensionType();
}
+ @Override
+ public boolean isTimestampValue()
+ {
+ return this.type == Type.TIMESTAMP;
+ }
+
@Override
public NilValue asNilValue()
{
@@ -1242,4 +1343,13 @@ public ExtensionValue asExtensionValue()
}
return (ExtensionValue) accessor;
}
+
+ @Override
+ public TimestampValue asTimestampValue()
+ {
+ if (!isTimestampValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (TimestampValue) accessor;
+ }
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractImmutableValue.java b/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractImmutableValue.java
index 1dae99cf2..18fcd2753 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractImmutableValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractImmutableValue.java
@@ -27,6 +27,7 @@
import org.msgpack.value.ImmutableNumberValue;
import org.msgpack.value.ImmutableRawValue;
import org.msgpack.value.ImmutableStringValue;
+import org.msgpack.value.ImmutableTimestampValue;
import org.msgpack.value.ImmutableValue;
abstract class AbstractImmutableValue
@@ -98,6 +99,12 @@ public boolean isExtensionValue()
return getValueType().isExtensionType();
}
+ @Override
+ public boolean isTimestampValue()
+ {
+ return false;
+ }
+
@Override
public ImmutableNilValue asNilValue()
{
@@ -163,4 +170,10 @@ public ImmutableExtensionValue asExtensionValue()
{
throw new MessageTypeCastException();
}
+
+ @Override
+ public ImmutableTimestampValue asTimestampValue()
+ {
+ throw new MessageTypeCastException();
+ }
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableTimestampValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableTimestampValueImpl.java
new file mode 100644
index 000000000..227c85d0b
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableTimestampValueImpl.java
@@ -0,0 +1,198 @@
+//
+// MessagePack for Java
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+package org.msgpack.value.impl;
+
+import org.msgpack.core.MessagePacker;
+import org.msgpack.core.buffer.MessageBuffer;
+import org.msgpack.value.ExtensionValue;
+import org.msgpack.value.ImmutableExtensionValue;
+import org.msgpack.value.ImmutableTimestampValue;
+import org.msgpack.value.TimestampValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueType;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Arrays;
+
+import static org.msgpack.core.MessagePack.Code.EXT_TIMESTAMP;
+
+/**
+ * {@code ImmutableTimestampValueImpl} Implements {@code ImmutableTimestampValue} using a {@code byte} and a {@code byte[]} fields.
+ *
+ * @see TimestampValue
+ */
+public class ImmutableTimestampValueImpl
+ extends AbstractImmutableValue
+ implements ImmutableExtensionValue, ImmutableTimestampValue
+{
+ private final Instant instant;
+ private byte[] data;
+
+ public ImmutableTimestampValueImpl(Instant timestamp)
+ {
+ this.instant = timestamp;
+ }
+
+ @Override
+ public boolean isTimestampValue()
+ {
+ return true;
+ }
+
+ @Override
+ public byte getType()
+ {
+ return EXT_TIMESTAMP;
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ // Note: Future version should return ValueType.TIMESTAMP instead.
+ return ValueType.EXTENSION;
+ }
+
+ @Override
+ public ImmutableTimestampValue immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableExtensionValue asExtensionValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableTimestampValue asTimestampValue()
+ {
+ return this;
+ }
+
+ @Override
+ public byte[] getData()
+ {
+ if (data == null) {
+ // See MessagePacker.packTimestampImpl
+ byte[] bytes;
+ long sec = getEpochSecond();
+ int nsec = getNano();
+ if (sec >>> 34 == 0) {
+ long data64 = (nsec << 34) | sec;
+ if ((data64 & 0xffffffff00000000L) == 0L) {
+ bytes = new byte[4];
+ MessageBuffer.wrap(bytes).putInt(0, (int) sec);
+ }
+ else {
+ bytes = new byte[8];
+ MessageBuffer.wrap(bytes).putLong(0, data64);
+ }
+ }
+ else {
+ bytes = new byte[12];
+ MessageBuffer buffer = MessageBuffer.wrap(bytes);
+ buffer.putInt(0, nsec);
+ buffer.putLong(4, sec);
+ }
+ data = bytes;
+ }
+ return data;
+ }
+
+ @Override
+ public long getEpochSecond()
+ {
+ return instant.getEpochSecond();
+ }
+
+ @Override
+ public int getNano()
+ {
+ return instant.getNano();
+ }
+
+ @Override
+ public long toEpochMillis()
+ {
+ return instant.toEpochMilli();
+ }
+
+ @Override
+ public Instant toInstant()
+ {
+ return instant;
+ }
+
+ @Override
+ public void writeTo(MessagePacker packer)
+ throws IOException
+ {
+ packer.packTimestamp(instant);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ // Implements same behavior with ImmutableExtensionValueImpl.
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Value)) {
+ return false;
+ }
+ Value v = (Value) o;
+
+ if (!v.isExtensionValue()) {
+ return false;
+ }
+ ExtensionValue ev = v.asExtensionValue();
+
+ // Here should use isTimestampValue and asTimestampValue instead. However, because
+ // adding these methods to Value interface can't keep backward compatibility without
+ // using "default" keyword since Java 7, here uses instanceof of and cast instead.
+ if (ev instanceof TimestampValue) {
+ TimestampValue tv = (TimestampValue) ev;
+ return instant.equals(tv.toInstant());
+ }
+ else {
+ return EXT_TIMESTAMP == ev.getType() && Arrays.equals(getData(), ev.getData());
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ // Implements same behavior with ImmutableExtensionValueImpl.
+ int hash = EXT_TIMESTAMP;
+ hash *= 31;
+ hash = instant.hashCode();
+ return hash;
+ }
+
+ @Override
+ public String toJson()
+ {
+ return "\"" + toInstant().toString() + "\"";
+ }
+
+ @Override
+ public String toString()
+ {
+ return toInstant().toString();
+ }
+}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/InvalidDataReadTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/InvalidDataReadTest.scala
index 4950da82a..4e39ff85c 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/InvalidDataReadTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/InvalidDataReadTest.scala
@@ -1,23 +1,25 @@
package org.msgpack.core
+import org.msgpack.core.MessagePackSpec.createMessagePackData
+import wvlet.airspec.AirSpec
+
/**
- *
- */
-class InvalidDataReadTest extends MessagePackSpec {
+ *
+ */
+class InvalidDataReadTest extends AirSpec {
- "Reading long EXT32" in {
- // Prepare an EXT32 data with 2GB (Int.MaxValue size) payload for testing the behavior of MessageUnpacker.skipValue()
- // Actually preparing 2GB of data, however, is too much for CI, so we create only the header part.
- val msgpack = createMessagePackData(p => p.packExtensionTypeHeader(MessagePack.Code.EXT32, Int.MaxValue))
- val u = MessagePack.newDefaultUnpacker(msgpack)
- try {
- // This error will be thrown after reading the header as the input has no EXT32 body
- intercept[MessageInsufficientBufferException] {
- u.skipValue()
- }
- }
- finally {
- u.close()
+ test("Reading long EXT32") {
+ // Prepare an EXT32 data with 2GB (Int.MaxValue size) payload for testing the behavior of MessageUnpacker.skipValue()
+ // Actually preparing 2GB of data, however, is too much for CI, so we create only the header part.
+ val msgpack = createMessagePackData(p => p.packExtensionTypeHeader(MessagePack.Code.EXT32, Int.MaxValue))
+ val u = MessagePack.newDefaultUnpacker(msgpack)
+ try {
+ // This error will be thrown after reading the header as the input has no EXT32 body
+ intercept[MessageInsufficientBufferException] {
+ u.skipValue()
}
+ } finally {
+ u.close()
+ }
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessageBufferPackerTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessageBufferPackerTest.scala
index 2194e42ea..58b29f435 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessageBufferPackerTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessageBufferPackerTest.scala
@@ -17,12 +17,12 @@ package org.msgpack.core
import java.io.ByteArrayOutputStream
import java.util.Arrays
-
import org.msgpack.value.ValueFactory._
+import wvlet.airspec.AirSpec
-class MessageBufferPackerTest extends MessagePackSpec {
- "MessageBufferPacker" should {
- "be equivalent to ByteArrayOutputStream" in {
+class MessageBufferPackerTest extends AirSpec {
+ test("MessageBufferPacker") {
+ test("be equivalent to ByteArrayOutputStream") {
val packer1 = MessagePack.newDefaultBufferPacker
packer1.packValue(newMap(newString("a"), newInteger(1), newString("b"), newString("s")))
@@ -34,17 +34,17 @@ class MessageBufferPackerTest extends MessagePackSpec {
packer1.toByteArray shouldBe stream.toByteArray
}
- "clear unflushed" in {
+ test("clear unflushed") {
val packer = MessagePack.newDefaultBufferPacker
- packer.packInt(1);
- packer.clear();
- packer.packInt(2);
+ packer.packInt(1)
+ packer.clear()
+ packer.packInt(2)
- packer.toByteArray shouldBe Array(2)
+ packer.toByteArray shouldBe Array[Byte](2)
val buffer = packer.toBufferList().get(0)
- buffer.toByteArray() shouldBe Array(2)
+ buffer.toByteArray() shouldBe Array[Byte](2)
val array = Arrays.copyOf(buffer.sliceAsByteBuffer().array(), buffer.size())
- array shouldBe Array(2)
+ array shouldBe Array[Byte](2)
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessageFormatTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessageFormatTest.scala
index e7e9a4c36..5f3447617 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessageFormatTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessageFormatTest.scala
@@ -17,20 +17,21 @@ package org.msgpack.core
import org.msgpack.core.MessagePack.Code
import org.msgpack.value.ValueType
-import org.scalatest.exceptions.TestFailedException
+import wvlet.airspec.AirSpec
+import wvlet.airspec.spi.AirSpecException
import scala.util.Random
/**
* Created on 2014/05/07.
*/
-class MessageFormatTest extends MessagePackSpec {
- "MessageFormat" should {
- "cover all byte codes" in {
+class MessageFormatTest extends AirSpec with Benchmark {
+ test("MessageFormat") {
+ test("cover all byte codes") {
def checkV(b: Byte, tpe: ValueType) {
try MessageFormat.valueOf(b).getValueType shouldBe tpe
catch {
- case e: TestFailedException =>
+ case e: AirSpecException =>
error(f"Failure when looking at byte ${b}%02x")
throw e
}
@@ -102,7 +103,7 @@ class MessageFormatTest extends MessagePackSpec {
}
}
- "improve the valueOf performance" in {
+ test("improve the valueOf performance") {
val N = 1000000
val idx = (0 until N).map(x => Random.nextInt(256).toByte).toArray[Byte]
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackSpec.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackSpec.scala
index ae1f5ae45..dee315cd9 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackSpec.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackSpec.scala
@@ -15,22 +15,13 @@
//
package org.msgpack.core
-import java.io.ByteArrayOutputStream
-import org.scalatest._
-import org.scalatest.matchers.should.Matchers
-import org.scalatest.prop.TableDrivenPropertyChecks
-import org.scalatest.wordspec.AnyWordSpec
-import xerial.core.log.{LogLevel, Logger}
-import xerial.core.util.{TimeReport, Timer}
-
-import scala.language.implicitConversions
-
-trait MessagePackSpec extends AnyWordSpec with Matchers with GivenWhenThen with OptionValues with BeforeAndAfter with Benchmark with Logger {
+import wvlet.log.LogLevel
+import wvlet.log.io.{TimeReport, Timer}
- implicit def toTag(s: String): Tag = Tag(s)
+import java.io.ByteArrayOutputStream
+object MessagePackSpec {
def toHex(arr: Array[Byte]) = arr.map(x => f"$x%02x").mkString(" ")
-
def createMessagePackData(f: MessagePacker => Unit): Array[Byte] = {
val b = new ByteArrayOutputStream()
val packer = MessagePack.newDefaultPacker(b)
@@ -41,20 +32,19 @@ trait MessagePackSpec extends AnyWordSpec with Matchers with GivenWhenThen with
}
trait Benchmark extends Timer {
+ private val numWarmUpRuns = 10
- val numWarmUpRuns = 10
-
- override protected def time[A](blockName: String, logLevel: LogLevel, repeat: Int)(f: => A): TimeReport = {
+ override protected def time[A](blockName: String, logLevel: LogLevel = LogLevel.INFO, repeat: Int = 1, blockRepeat: Int = 1)(f: => A): TimeReport = {
super.time(blockName, logLevel = LogLevel.INFO, repeat)(f)
}
- override protected def block[A](name: String, repeat: Int)(f: => A): TimeReport = {
+ override protected def block[A](name: String)(f: => A): TimeReport = {
var i = 0
while (i < numWarmUpRuns) {
f
i += 1
}
- super.block(name, repeat)(f)
+ super.block(name)(f)
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackTest.scala
index d2a209bb0..f60702ed8 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackTest.scala
@@ -15,28 +15,31 @@
//
package org.msgpack.core
+import org.msgpack.core.MessagePack.{Code, PackerConfig, UnpackerConfig}
+import org.msgpack.core.MessagePackSpec.toHex
+import org.msgpack.value.{Value, Variable}
+import org.scalacheck.Prop.propBoolean
+import org.scalacheck.{Arbitrary, Gen}
+import wvlet.airspec.AirSpec
+import wvlet.airspec.spi.PropertyCheck
+
import java.io.ByteArrayOutputStream
import java.math.BigInteger
import java.nio.CharBuffer
import java.nio.charset.{CodingErrorAction, UnmappableCharacterException}
-import org.msgpack.core.MessagePack.Code
-import org.msgpack.core.MessagePack.{PackerConfig, UnpackerConfig}
-import org.msgpack.value.{Value, Variable}
-import org.scalacheck.Arbitrary
-import org.scalacheck.Prop.{forAll, propBoolean}
-
+import java.time.Instant
import scala.util.Random
/**
* Created on 2014/05/07.
*/
-class MessagePackTest extends MessagePackSpec {
+class MessagePackTest extends AirSpec with PropertyCheck with Benchmark {
- def isValidUTF8(s: String) = {
+ private def isValidUTF8(s: String) = {
MessagePack.UTF8.newEncoder().canEncode(s)
}
- def containsUnmappableCharacter(s: String): Boolean = {
+ private def containsUnmappableCharacter(s: String): Boolean = {
try {
MessagePack.UTF8
.newEncoder()
@@ -50,549 +53,585 @@ class MessagePackTest extends MessagePackSpec {
}
}
- "MessagePack" should {
+ test("clone packer config") {
+ val config = new PackerConfig()
+ .withBufferSize(10)
+ .withBufferFlushThreshold(32 * 1024)
+ .withSmallStringOptimizationThreshold(142)
+ val copy = config.clone()
- "clone packer config" in {
- val config = new PackerConfig()
- .withBufferSize(10)
- .withBufferFlushThreshold(32 * 1024)
- .withSmallStringOptimizationThreshold(142)
- val copy = config.clone()
+ copy shouldBe config
+ }
- copy shouldBe config
- }
+ test("clone unpacker config") {
+ val config = new UnpackerConfig()
+ .withBufferSize(1)
+ .withActionOnMalformedString(CodingErrorAction.IGNORE)
+ .withActionOnUnmappableString(CodingErrorAction.REPORT)
+ .withAllowReadingBinaryAsString(false)
+ .withStringDecoderBufferSize(34)
+ .withStringSizeLimit(4324)
+
+ val copy = config.clone()
+ copy shouldBe config
+ }
- "clone unpacker config" in {
- val config = new UnpackerConfig()
- .withBufferSize(1)
- .withActionOnMalformedString(CodingErrorAction.IGNORE)
- .withActionOnUnmappableString(CodingErrorAction.REPORT)
- .withAllowReadingBinaryAsString(false)
- .withStringDecoderBufferSize(34)
- .withStringSizeLimit(4324)
+ test("detect fixint values") {
- val copy = config.clone()
- copy shouldBe config
+ for (i <- 0 until 0x79) {
+ Code.isPosFixInt(i.toByte) shouldBe true
}
- "detect fixint values" in {
-
- for (i <- 0 until 0x79) {
- Code.isPosFixInt(i.toByte) shouldBe true
- }
-
- for (i <- 0x80 until 0xFF) {
- Code.isPosFixInt(i.toByte) shouldBe false
- }
+ for (i <- 0x80 until 0xFF) {
+ Code.isPosFixInt(i.toByte) shouldBe false
}
+ }
- "detect fixarray values" in {
- val packer = MessagePack.newDefaultBufferPacker()
- packer.packArrayHeader(0)
- packer.close
- val bytes = packer.toByteArray
- MessagePack.newDefaultUnpacker(bytes).unpackArrayHeader() shouldBe 0
- try {
- MessagePack.newDefaultUnpacker(bytes).unpackMapHeader()
- fail("Shouldn't reach here")
- } catch {
- case e: MessageTypeException => // OK
- }
+ test("detect fixarray values") {
+ val packer = MessagePack.newDefaultBufferPacker()
+ packer.packArrayHeader(0)
+ packer.close
+ val bytes = packer.toByteArray
+ MessagePack.newDefaultUnpacker(bytes).unpackArrayHeader() shouldBe 0
+ try {
+ MessagePack.newDefaultUnpacker(bytes).unpackMapHeader()
+ fail("Shouldn't reach here")
+ } catch {
+ case e: MessageTypeException => // OK
}
+ }
- "detect fixmap values" in {
- val packer = MessagePack.newDefaultBufferPacker()
- packer.packMapHeader(0)
- packer.close
- val bytes = packer.toByteArray
- MessagePack.newDefaultUnpacker(bytes).unpackMapHeader() shouldBe 0
- try {
- MessagePack.newDefaultUnpacker(bytes).unpackArrayHeader()
- fail("Shouldn't reach here")
- } catch {
- case e: MessageTypeException => // OK
- }
+ test("detect fixmap values") {
+ val packer = MessagePack.newDefaultBufferPacker()
+ packer.packMapHeader(0)
+ packer.close
+ val bytes = packer.toByteArray
+ MessagePack.newDefaultUnpacker(bytes).unpackMapHeader() shouldBe 0
+ try {
+ MessagePack.newDefaultUnpacker(bytes).unpackArrayHeader()
+ fail("Shouldn't reach here")
+ } catch {
+ case e: MessageTypeException => // OK
}
+ }
- "detect fixint quickly" in {
+ test("detect fixint quickly") {
- val N = 100000
- val idx = (0 until N).map(x => Random.nextInt(256).toByte).toArray[Byte]
+ val N = 100000
+ val idx = (0 until N).map(x => Random.nextInt(256).toByte).toArray[Byte]
- time("check fixint", repeat = 100) {
+ time("check fixint", repeat = 100) {
- block("mask") {
- var i = 0
- var count = 0
- while (i < N) {
- if ((idx(i) & Code.POSFIXINT_MASK) == 0) {
- count += 1
- }
- i += 1
+ block("mask") {
+ var i = 0
+ var count = 0
+ while (i < N) {
+ if ((idx(i) & Code.POSFIXINT_MASK) == 0) {
+ count += 1
}
+ i += 1
}
+ }
- block("mask in func") {
- var i = 0
- var count = 0
- while (i < N) {
- if (Code.isPosFixInt(idx(i))) {
- count += 1
- }
- i += 1
+ block("mask in func") {
+ var i = 0
+ var count = 0
+ while (i < N) {
+ if (Code.isPosFixInt(idx(i))) {
+ count += 1
}
+ i += 1
}
+ }
- block("shift cmp") {
- var i = 0
- var count = 0
- while (i < N) {
- if ((idx(i) >>> 7) == 0) {
- count += 1
- }
- i += 1
+ block("shift cmp") {
+ var i = 0
+ var count = 0
+ while (i < N) {
+ if ((idx(i) >>> 7) == 0) {
+ count += 1
}
-
+ i += 1
}
}
}
- "detect neg fix int values" in {
-
- for (i <- 0 until 0xe0) {
- Code.isNegFixInt(i.toByte) shouldBe false
- }
+ }
- for (i <- 0xe0 until 0xFF) {
- Code.isNegFixInt(i.toByte) shouldBe true
- }
+ test("detect neg fix int values") {
+ for (i <- 0 until 0xe0) {
+ Code.isNegFixInt(i.toByte) shouldBe false
}
- def check[A](
- v: A,
- pack: MessagePacker => Unit,
- unpack: MessageUnpacker => A,
- packerConfig: PackerConfig = new PackerConfig(),
- unpackerConfig: UnpackerConfig = new UnpackerConfig()
- ): Boolean = {
- var b: Array[Byte] = null
- try {
- val bs = new ByteArrayOutputStream()
- val packer = packerConfig.newPacker(bs)
- pack(packer)
- packer.close()
-
- b = bs.toByteArray
-
- val unpacker = unpackerConfig.newUnpacker(b)
- val ret = unpack(unpacker)
- ret shouldBe v
- true
- } catch {
- case e: Exception =>
- warn(e.getMessage)
- if (b != null) {
- warn(s"packed data (size:${b.length}): ${toHex(b)}")
- }
- throw e
- }
+ for (i <- 0xe0 until 0xFF) {
+ Code.isNegFixInt(i.toByte) shouldBe true
}
- def checkException[A](
- v: A,
- pack: MessagePacker => Unit,
- unpack: MessageUnpacker => A,
- packerConfig: PackerConfig = new PackerConfig(),
- unpaackerConfig: UnpackerConfig = new UnpackerConfig()
- ): Unit = {
- var b: Array[Byte] = null
- val bs = new ByteArrayOutputStream()
- val packer = packerConfig.newPacker(bs)
+ }
+
+ private def check[A](
+ v: A,
+ pack: MessagePacker => Unit,
+ unpack: MessageUnpacker => A,
+ packerConfig: PackerConfig = new PackerConfig(),
+ unpackerConfig: UnpackerConfig = new UnpackerConfig()
+ ): Boolean = {
+ var b: Array[Byte] = null
+ try {
+ val bs = new ByteArrayOutputStream()
+ val packer = packerConfig.newPacker(bs)
pack(packer)
packer.close()
b = bs.toByteArray
- val unpacker = unpaackerConfig.newUnpacker(b)
+ val unpacker = unpackerConfig.newUnpacker(b)
val ret = unpack(unpacker)
-
- fail("cannot not reach here")
+ ret shouldBe v
+ true
+ } catch {
+ case e: Exception =>
+ warn(e.getMessage)
+ if (b != null) {
+ warn(s"packed data (size:${b.length}): ${toHex(b)}")
+ }
+ throw e
}
+ }
- def checkOverflow[A](v: A, pack: MessagePacker => Unit, unpack: MessageUnpacker => A) {
- try {
- checkException[A](v, pack, unpack)
- } catch {
- case e: MessageIntegerOverflowException => // OK
- }
+ private def checkException[A](
+ v: A,
+ pack: MessagePacker => Unit,
+ unpack: MessageUnpacker => A,
+ packerConfig: PackerConfig = new PackerConfig(),
+ unpaackerConfig: UnpackerConfig = new UnpackerConfig()
+ ): Unit = {
+ var b: Array[Byte] = null
+ val bs = new ByteArrayOutputStream()
+ val packer = packerConfig.newPacker(bs)
+ pack(packer)
+ packer.close()
+
+ b = bs.toByteArray
+
+ val unpacker = unpaackerConfig.newUnpacker(b)
+ val ret = unpack(unpacker)
+
+ fail("cannot not reach here")
+ }
+
+ private def checkOverflow[A](v: A, pack: MessagePacker => Unit, unpack: MessageUnpacker => A) {
+ try {
+ checkException[A](v, pack, unpack)
+ } catch {
+ case e: MessageIntegerOverflowException => // OK
}
+ }
- "pack/unpack primitive values" taggedAs ("prim") in {
- forAll { (v: Boolean) =>
- check(v, _.packBoolean(v), _.unpackBoolean)
- }
- forAll { (v: Byte) =>
- check(v, _.packByte(v), _.unpackByte)
- }
- forAll { (v: Short) =>
- check(v, _.packShort(v), _.unpackShort)
- }
- forAll { (v: Int) =>
- check(v, _.packInt(v), _.unpackInt)
- }
- forAll { (v: Float) =>
- check(v, _.packFloat(v), _.unpackFloat)
- }
- forAll { (v: Long) =>
- check(v, _.packLong(v), _.unpackLong)
- }
- forAll { (v: Double) =>
- check(v, _.packDouble(v), _.unpackDouble)
- }
- check(null, _.packNil, { unpacker =>
- unpacker.unpackNil(); null
- })
+ test("pack/unpack primitive values") {
+ forAll { (v: Boolean) =>
+ check(v, _.packBoolean(v), _.unpackBoolean)
+ }
+ forAll { (v: Byte) =>
+ check(v, _.packByte(v), _.unpackByte)
+ }
+ forAll { (v: Short) =>
+ check(v, _.packShort(v), _.unpackShort)
+ }
+ forAll { (v: Int) =>
+ check(v, _.packInt(v), _.unpackInt)
}
+ forAll { (v: Float) =>
+ check(v, _.packFloat(v), _.unpackFloat)
+ }
+ forAll { (v: Long) =>
+ check(v, _.packLong(v), _.unpackLong)
+ }
+ forAll { (v: Double) =>
+ check(v, _.packDouble(v), _.unpackDouble)
+ }
+ check(null, _.packNil, { unpacker =>
+ unpacker.unpackNil(); null
+ })
+ }
- "skipping a nil value" taggedAs ("try") in {
- check(true, _.packNil, _.tryUnpackNil)
- check(false, { packer =>
- packer.packString("val")
- }, { unpacker =>
- unpacker.tryUnpackNil()
- })
- check("val", { packer =>
- packer.packString("val")
- }, { unpacker =>
- unpacker.tryUnpackNil(); unpacker.unpackString()
- })
- check("val", { packer =>
- packer.packNil(); packer.packString("val")
- }, { unpacker =>
- unpacker.tryUnpackNil(); unpacker.unpackString()
- })
- try {
- checkException(null, { _ =>
- }, _.tryUnpackNil)
- } catch {
- case e: MessageInsufficientBufferException => // OK
- }
+ test("skipping a nil value") {
+ check(true, _.packNil, _.tryUnpackNil)
+ check(false, { packer =>
+ packer.packString("val")
+ }, { unpacker =>
+ unpacker.tryUnpackNil()
+ })
+ check("val", { packer =>
+ packer.packString("val")
+ }, { unpacker =>
+ unpacker.tryUnpackNil(); unpacker.unpackString()
+ })
+ check("val", { packer =>
+ packer.packNil(); packer.packString("val")
+ }, { unpacker =>
+ unpacker.tryUnpackNil(); unpacker.unpackString()
+ })
+ try {
+ checkException(null, { _ =>
+ }, _.tryUnpackNil)
+ } catch {
+ case e: MessageInsufficientBufferException => // OK
}
+ }
- "pack/unpack integer values" taggedAs ("int") in {
- val sampleData = Seq[Long](Int.MinValue.toLong -
- 10,
- -65535,
- -8191,
- -1024,
- -255,
- -127,
- -63,
- -31,
- -15,
- -7,
- -3,
- -1,
- 0,
- 2,
- 4,
- 8,
- 16,
- 32,
- 64,
- 128,
- 256,
- 1024,
- 8192,
- 65536,
- Int.MaxValue.toLong + 10)
- for (v <- sampleData) {
- check(v, _.packLong(v), _.unpackLong)
-
- if (v.isValidInt) {
- val vi = v.toInt
- check(vi, _.packInt(vi), _.unpackInt)
- } else {
- checkOverflow(v, _.packLong(v), _.unpackInt)
- }
+ test("pack/unpack integer values") {
+ val sampleData = Seq[Long](Int.MinValue.toLong -
+ 10,
+ -65535,
+ -8191,
+ -1024,
+ -255,
+ -127,
+ -63,
+ -31,
+ -15,
+ -7,
+ -3,
+ -1,
+ 0,
+ 2,
+ 4,
+ 8,
+ 16,
+ 32,
+ 64,
+ 128,
+ 256,
+ 1024,
+ 8192,
+ 65536,
+ Int.MaxValue.toLong + 10)
+ for (v <- sampleData) {
+ check(v, _.packLong(v), _.unpackLong)
+
+ if (v.isValidInt) {
+ val vi = v.toInt
+ check(vi, _.packInt(vi), _.unpackInt)
+ } else {
+ checkOverflow(v, _.packLong(v), _.unpackInt)
+ }
+
+ if (v.isValidShort) {
+ val vi = v.toShort
+ check(vi, _.packShort(vi), _.unpackShort)
+ } else {
+ checkOverflow(v, _.packLong(v), _.unpackShort)
+ }
+
+ if (v.isValidByte) {
+ val vi = v.toByte
+ check(vi, _.packByte(vi), _.unpackByte)
+ } else {
+ checkOverflow(v, _.packLong(v), _.unpackByte)
+ }
- if (v.isValidShort) {
- val vi = v.toShort
- check(vi, _.packShort(vi), _.unpackShort)
- } else {
- checkOverflow(v, _.packLong(v), _.unpackShort)
- }
+ }
- if (v.isValidByte) {
- val vi = v.toByte
- check(vi, _.packByte(vi), _.unpackByte)
- } else {
- checkOverflow(v, _.packLong(v), _.unpackByte)
- }
+ }
- }
+ test("pack/unpack BigInteger") {
+ forAll { (a: Long) =>
+ val v = BigInteger.valueOf(a)
+ check(v, _.packBigInteger(v), _.unpackBigInteger)
+ }
+ for (bi <- Seq(BigInteger.valueOf(Long.MaxValue).add(BigInteger.valueOf(1)))) {
+ check(bi, _.packBigInteger(bi), _.unpackBigInteger())
}
- "pack/unpack BigInteger" taggedAs ("bi") in {
- forAll { (a: Long) =>
- val v = BigInteger.valueOf(a)
- check(v, _.packBigInteger(v), _.unpackBigInteger)
+ for (bi <- Seq(BigInteger.valueOf(Long.MaxValue).shiftLeft(10))) {
+ try {
+ checkException(bi, _.packBigInteger(bi), _.unpackBigInteger())
+ fail("cannot reach here")
+ } catch {
+ case e: IllegalArgumentException => // OK
}
+ }
- for (bi <- Seq(BigInteger.valueOf(Long.MaxValue).add(BigInteger.valueOf(1)))) {
- check(bi, _.packBigInteger(bi), _.unpackBigInteger())
- }
+ }
- for (bi <- Seq(BigInteger.valueOf(Long.MaxValue).shiftLeft(10))) {
- try {
- checkException(bi, _.packBigInteger(bi), _.unpackBigInteger())
- fail("cannot reach here")
- } catch {
- case e: IllegalArgumentException => // OK
- }
- }
+ test("pack/unpack strings") {
+ val utf8Strings = Arbitrary.arbitrary[String].suchThat(isValidUTF8 _)
+ utf8Strings.map { v =>
+ check(v, _.packString(v), _.unpackString)
+ }
+ }
+ test("pack/unpack large strings") {
+ // Large string
+ val strLen = Seq(1000, 2000, 10000, 50000, 100000, 500000)
+ for (l <- strLen) {
+ val v: String =
+ Iterator.continually(Random.nextString(l * 10)).find(isValidUTF8).get
+ check(v, _.packString(v), _.unpackString)
}
+ }
- "pack/unpack strings" taggedAs ("string") in {
- val utf8Strings = Arbitrary.arbitrary[String].suchThat(isValidUTF8 _)
- utf8Strings.map { v =>
- check(v, _.packString(v), _.unpackString)
+ test("report errors when packing/unpacking malformed strings") {
+ // TODO produce malformed utf-8 strings in Java8"
+ pending
+ // Create 100 malformed UTF8 Strings
+ val r = new Random(0)
+ val malformedStrings = Iterator
+ .continually {
+ val b = new Array[Byte](10)
+ r.nextBytes(b)
+ b
+ }
+ .filter(b => !isValidUTF8(new String(b)))
+ .take(100)
+
+ for (malformedBytes <- malformedStrings) {
+ // Pack tests
+ val malformed = new String(malformedBytes)
+ try {
+ checkException(malformed, _.packString(malformed), _.unpackString())
+ } catch {
+ case e: MessageStringCodingException => // OK
}
- }
- "pack/unpack large strings" taggedAs ("large-string") in {
- // Large string
- val strLen = Seq(1000, 2000, 10000, 50000, 100000, 500000)
- for (l <- strLen) {
- val v: String =
- Iterator.continually(Random.nextString(l * 10)).find(isValidUTF8).get
- check(v, _.packString(v), _.unpackString)
+ try {
+ checkException(malformed, { packer =>
+ packer.packRawStringHeader(malformedBytes.length)
+ packer.writePayload(malformedBytes)
+ }, _.unpackString())
+ } catch {
+ case e: MessageStringCodingException => // OK
}
}
+ }
- "report errors when packing/unpacking malformed strings" taggedAs ("malformed") in {
- // TODO produce malformed utf-8 strings in Java8"
- pending
- // Create 100 malformed UTF8 Strings
- val r = new Random(0)
- val malformedStrings = Iterator
- .continually {
- val b = new Array[Byte](10)
- r.nextBytes(b)
- b
- }
- .filter(b => !isValidUTF8(new String(b)))
- .take(100)
-
- for (malformedBytes <- malformedStrings) {
- // Pack tests
- val malformed = new String(malformedBytes)
- try {
- checkException(malformed, _.packString(malformed), _.unpackString())
- } catch {
- case e: MessageStringCodingException => // OK
- }
+ test("report errors when packing/unpacking strings that contain unmappable characters") {
- try {
- checkException(malformed, { packer =>
- packer.packRawStringHeader(malformedBytes.length)
- packer.writePayload(malformedBytes)
- }, _.unpackString())
- } catch {
- case e: MessageStringCodingException => // OK
- }
- }
- }
+ val unmappable = Array[Byte](0xfc.toByte, 0x0a.toByte)
+ //val unmappableChar = Array[Char](new Character(0xfc0a).toChar)
- "report errors when packing/unpacking strings that contain unmappable characters" taggedAs ("unmap") in {
+ // Report error on unmappable character
+ val unpackerConfig = new UnpackerConfig()
+ .withActionOnMalformedString(CodingErrorAction.REPORT)
+ .withActionOnUnmappableString(CodingErrorAction.REPORT)
- val unmappable = Array[Byte](0xfc.toByte, 0x0a.toByte)
- //val unmappableChar = Array[Char](new Character(0xfc0a).toChar)
+ for (bytes <- Seq(unmappable)) {
+ try {
+ checkException(bytes, { packer =>
+ packer.packRawStringHeader(bytes.length)
+ packer.writePayload(bytes)
+ }, _.unpackString(), new PackerConfig(), unpackerConfig)
+ } catch {
+ case e: MessageStringCodingException => // OK
+ }
+ }
+ }
- // Report error on unmappable character
- val unpackerConfig = new UnpackerConfig()
- .withActionOnMalformedString(CodingErrorAction.REPORT)
- .withActionOnUnmappableString(CodingErrorAction.REPORT)
+ test("pack/unpack binary") {
+ forAll { (v: Array[Byte]) =>
+ check(
+ v, { packer =>
+ packer.packBinaryHeader(v.length); packer.writePayload(v)
+ }, { unpacker =>
+ val len = unpacker.unpackBinaryHeader()
+ val out = new Array[Byte](len)
+ unpacker.readPayload(out, 0, len)
+ out
+ }
+ )
+ }
- for (bytes <- Seq(unmappable)) {
- When("unpacking")
- try {
- checkException(bytes, { packer =>
- packer.packRawStringHeader(bytes.length)
- packer.writePayload(bytes)
- }, _.unpackString(), new PackerConfig(), unpackerConfig)
- } catch {
- case e: MessageStringCodingException => // OK
+ val len = Seq(1000, 2000, 10000, 50000, 100000, 500000)
+ for (l <- len) {
+ val v = new Array[Byte](l)
+ Random.nextBytes(v)
+ check(
+ v, { packer =>
+ packer.packBinaryHeader(v.length); packer.writePayload(v)
+ }, { unpacker =>
+ val len = unpacker.unpackBinaryHeader()
+ val out = new Array[Byte](len)
+ unpacker.readPayload(out, 0, len)
+ out
}
- }
+ )
}
+ }
- "pack/unpack binary" taggedAs ("binary") in {
- forAll { (v: Array[Byte]) =>
- check(
- v, { packer =>
- packer.packBinaryHeader(v.length); packer.writePayload(v)
- }, { unpacker =>
- val len = unpacker.unpackBinaryHeader()
- val out = new Array[Byte](len)
- unpacker.readPayload(out, 0, len)
- out
- }
- )
- }
+ val testHeaderLength = Seq(1, 2, 4, 8, 16, 17, 32, 64, 255, 256, 1000, 2000, 10000, 50000, 100000, 500000)
- val len = Seq(1000, 2000, 10000, 50000, 100000, 500000)
- for (l <- len) {
- val v = new Array[Byte](l)
- Random.nextBytes(v)
- check(
- v, { packer =>
- packer.packBinaryHeader(v.length); packer.writePayload(v)
- }, { unpacker =>
- val len = unpacker.unpackBinaryHeader()
- val out = new Array[Byte](len)
- unpacker.readPayload(out, 0, len)
- out
+ test("pack/unpack arrays") {
+ forAll { (v: Array[Int]) =>
+ check(
+ v, { packer =>
+ packer.packArrayHeader(v.length)
+ v.map(packer.packInt(_))
+ }, { unpacker =>
+ val len = unpacker.unpackArrayHeader()
+ val out = new Array[Int](len)
+ for (i <- 0 until v.length) {
+ out(i) = unpacker.unpackInt
}
- )
- }
+ out
+ }
+ )
}
- val testHeaderLength = Seq(1, 2, 4, 8, 16, 17, 32, 64, 255, 256, 1000, 2000, 10000, 50000, 100000, 500000)
-
- "pack/unpack arrays" taggedAs ("array") in {
- forAll { (v: Array[Int]) =>
- check(
- v, { packer =>
- packer.packArrayHeader(v.length)
- v.map(packer.packInt(_))
- }, { unpacker =>
- val len = unpacker.unpackArrayHeader()
- val out = new Array[Int](len)
- for (i <- 0 until v.length) {
- out(i) = unpacker.unpackInt
- }
- out
- }
- )
- }
+ for (l <- testHeaderLength) {
+ check(l, _.packArrayHeader(l), _.unpackArrayHeader())
+ }
- for (l <- testHeaderLength) {
- check(l, _.packArrayHeader(l), _.unpackArrayHeader())
- }
+ try {
+ checkException(0, _.packArrayHeader(-1), _.unpackArrayHeader)
+ } catch {
+ case e: IllegalArgumentException => // OK
+ }
- try {
- checkException(0, _.packArrayHeader(-1), _.unpackArrayHeader)
- } catch {
- case e: IllegalArgumentException => // OK
- }
+ }
- }
+ test("pack/unpack maps") {
+ forAll { (v: Array[Int]) =>
+ val m = v.map(i => (i, i.toString)).toSeq
- "pack/unpack maps" taggedAs ("map") in {
- forAll { (v: Array[Int]) =>
- val m = v.map(i => (i, i.toString))
-
- check(
- m, { packer =>
- packer.packMapHeader(v.length)
- m.map {
- case (k: Int, v: String) =>
- packer.packInt(k)
- packer.packString(v)
- }
- }, { unpacker =>
- val len = unpacker.unpackMapHeader()
- val b = Seq.newBuilder[(Int, String)]
- for (i <- 0 until len) {
- b += ((unpacker.unpackInt, unpacker.unpackString))
- }
- b.result
+ check(
+ m, { packer =>
+ packer.packMapHeader(v.length)
+ m.map {
+ case (k: Int, v: String) =>
+ packer.packInt(k)
+ packer.packString(v)
}
- )
- }
-
- for (l <- testHeaderLength) {
- check(l, _.packMapHeader(l), _.unpackMapHeader())
- }
+ }, { unpacker =>
+ val len = unpacker.unpackMapHeader()
+ val b = Seq.newBuilder[(Int, String)]
+ for (i <- 0 until len) {
+ b += ((unpacker.unpackInt, unpacker.unpackString))
+ }
+ b.result
+ }
+ )
+ }
- try {
- checkException(0, _.packMapHeader(-1), _.unpackMapHeader)
- } catch {
- case e: IllegalArgumentException => // OK
- }
+ for (l <- testHeaderLength) {
+ check(l, _.packMapHeader(l), _.unpackMapHeader())
+ }
+ try {
+ checkException(0, _.packMapHeader(-1), _.unpackMapHeader)
+ } catch {
+ case e: IllegalArgumentException => // OK
}
- "pack/unpack extension types" taggedAs ("ext") in {
- forAll { (dataLen: Int, tpe: Byte) =>
- val l = Math.abs(dataLen)
- l >= 0 ==> {
- val ext =
- new ExtensionTypeHeader(ExtensionTypeHeader.checkedCastToByte(tpe), l)
- check(ext, _.packExtensionTypeHeader(ext.getType, ext.getLength), _.unpackExtensionTypeHeader())
- }
- }
+ }
- for (l <- testHeaderLength) {
- val ext = new ExtensionTypeHeader(ExtensionTypeHeader.checkedCastToByte(Random.nextInt(128)), l)
+ test("pack/unpack extension types") {
+ forAll { (dataLen: Int, tpe: Byte) =>
+ val l = Math.abs(dataLen)
+ l >= 0 ==> {
+ val ext =
+ new ExtensionTypeHeader(ExtensionTypeHeader.checkedCastToByte(tpe), l)
check(ext, _.packExtensionTypeHeader(ext.getType, ext.getLength), _.unpackExtensionTypeHeader())
}
+ }
+ for (l <- testHeaderLength) {
+ val ext = new ExtensionTypeHeader(ExtensionTypeHeader.checkedCastToByte(Random.nextInt(128)), l)
+ check(ext, _.packExtensionTypeHeader(ext.getType, ext.getLength), _.unpackExtensionTypeHeader())
}
- "pack/unpack maps in lists" in {
- val aMap = List(Map("f" -> "x"))
+ }
- check(
- aMap, { packer =>
- packer.packArrayHeader(aMap.size)
- for (m <- aMap) {
- packer.packMapHeader(m.size)
- for ((k, v) <- m) {
- packer.packString(k)
- packer.packString(v)
- }
+ test("pack/unpack maps in lists") {
+ val aMap = List(Map("f" -> "x"))
+
+ check(
+ aMap, { packer =>
+ packer.packArrayHeader(aMap.size)
+ for (m <- aMap) {
+ packer.packMapHeader(m.size)
+ for ((k, v) <- m) {
+ packer.packString(k)
+ packer.packString(v)
}
- }, { unpacker =>
- val v = new Variable()
- unpacker.unpackValue(v)
- import scala.collection.JavaConverters._
- v.asArrayValue().asScala
- .map { m =>
- val mv = m.asMapValue()
- val kvs = mv.getKeyValueArray
-
- kvs
- .grouped(2)
- .map({ kvp: Array[Value] =>
- val k = kvp(0)
- val v = kvp(1)
-
- (k.asStringValue().asString, v.asStringValue().asString)
- })
- .toMap
- }
- .toList
}
- )
+ }, { unpacker =>
+ val v = new Variable()
+ unpacker.unpackValue(v)
+ import scala.collection.JavaConverters._
+ v.asArrayValue().asScala
+ .map { m =>
+ val mv = m.asMapValue()
+ val kvs = mv.getKeyValueArray
+
+ kvs
+ .grouped(2)
+ .map({ kvp: Array[Value] =>
+ val k = kvp(0)
+ val v = kvp(1)
+
+ (k.asStringValue().asString, v.asStringValue().asString)
+ })
+ .toMap
+ }
+ .toList
+ }
+ )
+ }
+
+ test("pack/unpack timestamp values") {
+ val posLong = Gen.chooseNum[Long](-31557014167219200L, 31556889864403199L)
+ val posInt = Gen.chooseNum(0, 1000000000 - 1) // NANOS_PER_SECOND
+ forAll(posLong, posInt) { (second: Long, nano: Int) =>
+ val v = Instant.ofEpochSecond(second, nano)
+ check(v, { _.packTimestamp(v) }, { _.unpackTimestamp() })
+ }
+ // Using different insterfaces
+ forAll(posLong, posInt) { (second: Long, nano: Int) =>
+ val v = Instant.ofEpochSecond(second, nano)
+ check(v, { _.packTimestamp(second, nano) }, { _.unpackTimestamp() })
+ }
+ val secLessThan34bits = Gen.chooseNum[Long](0, 1L << 34)
+ forAll(secLessThan34bits, posInt) { (second: Long, nano: Int) =>
+ val v = Instant.ofEpochSecond(second, nano)
+ check(v, _.packTimestamp(v), _.unpackTimestamp())
+ }
+ forAll(secLessThan34bits, posInt) { (second: Long, nano: Int) =>
+ val v = Instant.ofEpochSecond(second, nano)
+ check(v, _.packTimestamp(second, nano), _.unpackTimestamp())
}
+ // Corner-cases around uint32 boundaries
+ for (v <- Seq(
+ Instant.ofEpochSecond(Instant.now().getEpochSecond, 123456789L), // uint32 nanoseq (out of int32 range)
+ Instant.ofEpochSecond(-1302749144L, 0), // 1928-09-19T21:14:16Z
+ Instant.ofEpochSecond(-747359729L, 0), // 1946-04-27T00:04:31Z
+ Instant.ofEpochSecond(4257387427L, 0) // 2104-11-29T07:37:07Z
+ )) {
+ check(v, _.packTimestamp(v), _.unpackTimestamp())
+ }
+ }
+
+ test("pack/unpack timestamp in millis") {
+ val posLong = Gen.chooseNum[Long](-31557014167219200L, 31556889864403199L)
+ forAll(posLong) { (millis: Long) =>
+ val v = Instant.ofEpochMilli(millis)
+ check(v, { _.packTimestamp(millis) }, { _.unpackTimestamp() })
+ }
}
- "MessagePack.PackerConfig" should {
- "be immutable" in {
+ test("MessagePack.PackerConfig") {
+ test("should be immutable") {
val a = new MessagePack.PackerConfig()
val b = a.withBufferSize(64 * 1024)
a.equals(b) shouldBe false
}
- "implement equals" in {
+ test("should implement equals") {
val a = new MessagePack.PackerConfig()
val b = new MessagePack.PackerConfig()
a.equals(b) shouldBe true
@@ -602,14 +641,14 @@ class MessagePackTest extends MessagePackSpec {
}
}
- "MessagePack.UnpackerConfig" should {
- "be immutable" in {
+ test("MessagePack.UnpackerConfig") {
+ test("should be immutable") {
val a = new MessagePack.UnpackerConfig()
val b = a.withBufferSize(64 * 1024)
a.equals(b) shouldBe false
}
- "implement equals" in {
+ test("implement equals") {
val a = new MessagePack.UnpackerConfig()
val b = new MessagePack.UnpackerConfig()
a.equals(b) shouldBe true
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackerTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackerTest.scala
index 5024963fd..096a811b4 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackerTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackerTest.scala
@@ -15,21 +15,21 @@
//
package org.msgpack.core
-import java.io.{ByteArrayOutputStream, File, FileInputStream, FileOutputStream}
-
-import org.msgpack.core.MessagePack.{UnpackerConfig, PackerConfig}
+import org.msgpack.core.MessagePack.PackerConfig
import org.msgpack.core.buffer.{ChannelBufferOutput, OutputStreamBufferOutput}
import org.msgpack.value.ValueFactory
-import xerial.core.io.IOUtil
+import wvlet.airspec.AirSpec
+import wvlet.log.io.IOUtil.withResource
+import java.io.{ByteArrayOutputStream, File, FileInputStream, FileOutputStream}
import scala.util.Random
/**
*
*/
-class MessagePackerTest extends MessagePackSpec {
+class MessagePackerTest extends AirSpec with Benchmark {
- def verifyIntSeq(answer: Array[Int], packed: Array[Byte]) {
+ private def verifyIntSeq(answer: Array[Int], packed: Array[Byte]) {
val unpacker = MessagePack.newDefaultUnpacker(packed)
val b = Array.newBuilder[Int]
while (unpacker.hasNext) {
@@ -40,27 +40,27 @@ class MessagePackerTest extends MessagePackSpec {
result shouldBe answer
}
- def createTempFile = {
+ private def createTempFile = {
val f = File.createTempFile("msgpackTest", "msgpack")
f.deleteOnExit
f
}
- def createTempFileWithOutputStream = {
+ private def createTempFileWithOutputStream = {
val f = createTempFile
val out = new FileOutputStream(f)
(f, out)
}
- def createTempFileWithChannel = {
+ private def createTempFileWithChannel = {
val (f, out) = createTempFileWithOutputStream
val ch = out.getChannel
(f, ch)
}
- "MessagePacker" should {
+ test("MessagePacker") {
- "reset the internal states" in {
+ test("reset the internal states") {
val intSeq = (0 until 100).map(i => Random.nextInt).toArray
val b = new ByteArrayOutputStream
@@ -86,13 +86,12 @@ class MessagePackerTest extends MessagePackSpec {
verifyIntSeq(intSeq3, b3.toByteArray)
}
- "improve the performance via reset method" taggedAs ("reset") in {
-
+ test("improve the performance via reset method") {
val N = 1000
val t = time("packer", repeat = 10) {
block("no-buffer-reset") {
val out = new ByteArrayOutputStream
- IOUtil.withResource(MessagePack.newDefaultPacker(out)) { packer =>
+ withResource(MessagePack.newDefaultPacker(out)) { packer =>
for (i <- 0 until N) {
val outputStream = new ByteArrayOutputStream()
packer
@@ -105,7 +104,7 @@ class MessagePackerTest extends MessagePackSpec {
block("buffer-reset") {
val out = new ByteArrayOutputStream
- IOUtil.withResource(MessagePack.newDefaultPacker(out)) { packer =>
+ withResource(MessagePack.newDefaultPacker(out)) { packer =>
val bufferOut =
new OutputStreamBufferOutput(new ByteArrayOutputStream())
for (i <- 0 until N) {
@@ -119,10 +118,10 @@ class MessagePackerTest extends MessagePackSpec {
}
}
- t("buffer-reset").averageWithoutMinMax should be <= t("no-buffer-reset").averageWithoutMinMax
+ t("buffer-reset").averageWithoutMinMax <= t("no-buffer-reset").averageWithoutMinMax shouldBe true
}
- "pack larger string array than byte buf" taggedAs ("larger-string-array-than-byte-buf") in {
+ test("pack larger string array than byte buf") {
// Based on https://github.com/msgpack/msgpack-java/issues/154
def test(bufferSize: Int, stringSize: Int): Boolean = {
@@ -148,7 +147,7 @@ class MessagePackerTest extends MessagePackSpec {
}
}
- "reset OutputStreamBufferOutput" in {
+ test("reset OutputStreamBufferOutput") {
val (f0, out0) = createTempFileWithOutputStream
val packer = MessagePack.newDefaultPacker(out0)
packer.packInt(99)
@@ -178,7 +177,7 @@ class MessagePackerTest extends MessagePackSpec {
up1.close
}
- "reset ChannelBufferOutput" in {
+ test("reset ChannelBufferOutput") {
val (f0, out0) = createTempFileWithChannel
val packer = MessagePack.newDefaultPacker(out0)
packer.packInt(99)
@@ -208,7 +207,7 @@ class MessagePackerTest extends MessagePackSpec {
up1.close
}
- "pack a lot of String within expected time" in {
+ test("pack a lot of String within expected time") {
val count = 20000
def measureDuration(outputStream: java.io.OutputStream) = {
@@ -231,14 +230,14 @@ class MessagePackerTest extends MessagePackSpec {
measureDuration(fileOutput)
}
}
- t("file-output-stream").averageWithoutMinMax shouldBe <(t("byte-array-output-stream").averageWithoutMinMax * 5)
+ t("file-output-stream").averageWithoutMinMax < (t("byte-array-output-stream").averageWithoutMinMax * 5) shouldBe true
}
}
- "compute totalWrittenBytes" in {
+ test("compute totalWrittenBytes") {
val out = new ByteArrayOutputStream
val packerTotalWrittenBytes =
- IOUtil.withResource(MessagePack.newDefaultPacker(out)) { packer =>
+ withResource(MessagePack.newDefaultPacker(out)) { packer =>
packer
.packByte(0) // 1
.packBoolean(true) // 1
@@ -254,7 +253,7 @@ class MessagePackerTest extends MessagePackSpec {
out.toByteArray.length shouldBe packerTotalWrittenBytes
}
- "support read-only buffer" taggedAs ("read-only") in {
+ test("support read-only buffer") {
val payload = Array[Byte](1)
val out = new ByteArrayOutputStream()
val packer = MessagePack
@@ -264,7 +263,7 @@ class MessagePackerTest extends MessagePackSpec {
.close()
}
- "pack small string with STR8" in {
+ test("pack small string with STR8") {
val packer = new PackerConfig().newBufferPacker()
packer.packString("Hello. This is a string longer than 32 characters!")
val b = packer.toByteArray
@@ -274,7 +273,7 @@ class MessagePackerTest extends MessagePackSpec {
f shouldBe MessageFormat.STR8
}
- "be able to disable STR8 for backward compatibility" in {
+ test("be able to disable STR8 for backward compatibility") {
val config = new PackerConfig()
.withStr8FormatSupport(false)
@@ -285,7 +284,7 @@ class MessagePackerTest extends MessagePackSpec {
f shouldBe MessageFormat.STR16
}
- "be able to disable STR8 when using CharsetEncoder" in {
+ test("be able to disable STR8 when using CharsetEncoder") {
val config = new PackerConfig()
.withStr8FormatSupport(false)
.withSmallStringOptimizationThreshold(0) // Disable small string optimization
@@ -294,19 +293,19 @@ class MessagePackerTest extends MessagePackSpec {
packer.packString("small string")
val unpacker = MessagePack.newDefaultUnpacker(packer.toByteArray)
val f = unpacker.getNextFormat
- f shouldNot be(MessageFormat.STR8)
+ f shouldNotBe MessageFormat.STR8
val s = unpacker.unpackString()
s shouldBe "small string"
}
- "write raw binary" taggedAs ("raw-binary") in {
+ test("write raw binary") {
val packer = new MessagePack.PackerConfig().newBufferPacker()
val msg =
Array[Byte](-127, -92, 116, 121, 112, 101, -92, 112, 105, 110, 103)
packer.writePayload(msg)
}
- "append raw binary" taggedAs ("append-raw-binary") in {
+ test("append raw binary") {
val packer = new MessagePack.PackerConfig().newBufferPacker()
val msg =
Array[Byte](-127, -92, 116, 121, 112, 101, -92, 112, 105, 110, 103)
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessageUnpackerTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessageUnpackerTest.scala
index c2c738515..5ae597f27 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessageUnpackerTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessageUnpackerTest.scala
@@ -15,14 +15,16 @@
//
package org.msgpack.core
-import java.io._
-import java.nio.ByteBuffer
-import java.util.Collections
-
+import org.msgpack.core.MessagePackSpec.{createMessagePackData, toHex}
import org.msgpack.core.buffer._
import org.msgpack.value.ValueType
-import xerial.core.io.IOUtil._
+import wvlet.airspec.AirSpec
+import wvlet.log.LogSupport
+import wvlet.log.io.IOUtil.withResource
+import java.io._
+import java.nio.ByteBuffer
+import java.util.Collections
import scala.collection.JavaConverters._
import scala.util.Random
@@ -43,12 +45,12 @@ object MessageUnpackerTest {
}
}
-import MessageUnpackerTest._
+import org.msgpack.core.MessageUnpackerTest._
-class MessageUnpackerTest extends MessagePackSpec {
+class MessageUnpackerTest extends AirSpec with Benchmark {
- val universal = MessageBuffer.allocate(0).isInstanceOf[MessageBufferU]
- def testData: Array[Byte] = {
+ private val universal = MessageBuffer.allocate(0).isInstanceOf[MessageBufferU]
+ private def testData: Array[Byte] = {
val out = new ByteArrayOutputStream()
val packer = MessagePack.newDefaultPacker(out)
@@ -68,9 +70,9 @@ class MessageUnpackerTest extends MessagePackSpec {
arr
}
- val intSeq = (for (i <- 0 until 100) yield Random.nextInt()).toArray[Int]
+ private val intSeq = (for (i <- 0 until 100) yield Random.nextInt()).toArray[Int]
- def testData2: Array[Byte] = {
+ private def testData2: Array[Byte] = {
val out = new ByteArrayOutputStream()
val packer = MessagePack.newDefaultPacker(out);
@@ -86,7 +88,7 @@ class MessageUnpackerTest extends MessagePackSpec {
arr
}
- def write(packer: MessagePacker, r: Random) {
+ private def write(packer: MessagePacker, r: Random) {
val tpeIndex = Iterator
.continually(r.nextInt(MessageFormat.values().length))
.find(_ != MessageFormat.NEVER_USED.ordinal())
@@ -142,7 +144,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- def testData3(N: Int): Array[Byte] = {
+ private def testData3(N: Int): Array[Byte] = {
val out = new ByteArrayOutputStream()
val packer = MessagePack.newDefaultPacker(out)
@@ -160,7 +162,7 @@ class MessageUnpackerTest extends MessagePackSpec {
arr
}
- def readValue(unpacker: MessageUnpacker) {
+ private def readValue(unpacker: MessageUnpacker) {
val f = unpacker.getNextFormat()
f.getValueType match {
case ValueType.ARRAY =>
@@ -181,7 +183,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- def createTempFile = {
+ private def createTempFile = {
val f = File.createTempFile("msgpackTest", "msgpack")
f.deleteOnExit
val p = MessagePack.newDefaultPacker(new FileOutputStream(f))
@@ -190,12 +192,12 @@ class MessageUnpackerTest extends MessagePackSpec {
f
}
- def checkFile(u: MessageUnpacker) = {
+ private def checkFile(u: MessageUnpacker) = {
u.unpackInt shouldBe 99
u.hasNext shouldBe false
}
- def unpackers(data: Array[Byte]): Seq[MessageUnpacker] = {
+ private def unpackers(data: Array[Byte]): Seq[MessageUnpacker] = {
val bb = ByteBuffer.allocate(data.length)
val db = ByteBuffer.allocateDirect(data.length)
bb.put(data).flip()
@@ -210,7 +212,7 @@ class MessageUnpackerTest extends MessagePackSpec {
builder.result()
}
- def unpackerCollectionWithVariousBuffers(data: Array[Byte], chunkSize: Int): Seq[MessageUnpacker] = {
+ private def unpackerCollectionWithVariousBuffers(data: Array[Byte], chunkSize: Int): Seq[MessageUnpacker] = {
val seqBytes = Seq.newBuilder[MessageBufferInput]
val seqByteBuffers = Seq.newBuilder[MessageBufferInput]
val seqDirectBuffers = Seq.newBuilder[MessageBufferInput]
@@ -238,9 +240,9 @@ class MessageUnpackerTest extends MessagePackSpec {
builder.result()
}
- "MessageUnpacker" should {
+ test("MessageUnpacker") {
- "parse message packed data" taggedAs ("unpack") in {
+ test("parse message packed data") {
val arr = testData
for (unpacker <- unpackers(arr)) {
@@ -255,7 +257,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- "skip reading values" in {
+ test("skip reading values") {
for (unpacker <- unpackers(testData)) {
var skipCount = 0
@@ -269,7 +271,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- "compare skip performance" taggedAs ("skip") in {
+ test("compare skip performance") {
val N = 10000
val data = testData3(N)
@@ -297,8 +299,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
- "parse int data" in {
-
+ test("parse int data") {
debug(intSeq.mkString(", "))
for (unpacker <- unpackers(testData2)) {
@@ -319,15 +320,13 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- ib.result shouldBe intSeq
+ ib.result shouldBe intSeq.toSeq
unpacker.getTotalReadBytes shouldBe testData2.length
}
-
}
- "read data at the buffer boundary" taggedAs ("boundary") in {
-
- trait SplitTest {
+ test("read data at the buffer boundary") {
+ trait SplitTest extends LogSupport {
val data: Array[Byte]
def run {
for (unpacker <- unpackers(data)) {
@@ -362,7 +361,7 @@ class MessageUnpackerTest extends MessagePackSpec {
new SplitTest { val data = testData3(30) }.run
}
- "read integer at MessageBuffer boundaries" taggedAs ("integer-buffer-boundary") in {
+ test("read integer at MessageBuffer boundaries") {
val packer = MessagePack.newDefaultBufferPacker()
(0 until 1170).foreach { i =>
packer.packLong(0x0011223344556677L)
@@ -385,7 +384,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- "read string at MessageBuffer boundaries" taggedAs ("string-buffer-boundary") in {
+ test("read string at MessageBuffer boundaries") {
val packer = MessagePack.newDefaultBufferPacker()
(0 until 1170).foreach { i =>
packer.packString("hello world")
@@ -408,7 +407,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- "be faster than msgpack-v6 skip" taggedAs ("cmp-skip") in {
+ test("be faster than msgpack-v6 skip") {
trait Fixture {
val unpacker: MessageUnpacker
@@ -434,7 +433,6 @@ class MessageUnpackerTest extends MessagePackSpec {
val t = time("skip performance", repeat = N) {
block("v6") {
- import org.msgpack.`type`.{ValueType => ValueTypeV6}
val v6 = new org.msgpack.MessagePack()
val unpacker = new org.msgpack.unpacker.MessagePackUnpacker(v6, new ByteArrayInputStream(data))
var count = 0
@@ -466,15 +464,16 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- t("v7-array").averageWithoutMinMax should be <= t("v6").averageWithoutMinMax
- t("v7-array-buffer").averageWithoutMinMax should be <= t("v6").averageWithoutMinMax
- if (!universal)
- t("v7-direct-buffer").averageWithoutMinMax should be <= t("v6").averageWithoutMinMax
+ t("v7-array").averageWithoutMinMax <= t("v6").averageWithoutMinMax shouldBe true
+ t("v7-array-buffer").averageWithoutMinMax <= t("v6").averageWithoutMinMax shouldBe true
+ if (!universal) {
+ t("v7-direct-buffer").averageWithoutMinMax <= t("v6").averageWithoutMinMax shouldBe true
+ }
}
import org.msgpack.`type`.{ValueType => ValueTypeV6}
- "be faster than msgpack-v6 read value" taggedAs ("cmp-unpack") in {
+ test("be faster than msgpack-v6 read value") {
def readValueV6(unpacker: org.msgpack.unpacker.MessagePackUnpacker) {
val vt = unpacker.getNextType()
@@ -598,12 +597,12 @@ class MessageUnpackerTest extends MessagePackSpec {
if (t("v7-array-buffer").averageWithoutMinMax > t("v6").averageWithoutMinMax) {
warn(s"v7-array-buffer ${t("v7-array-buffer").averageWithoutMinMax} is slower than v6 ${t("v6").averageWithoutMinMax}")
}
- if (!universal)
- t("v7-direct-buffer").averageWithoutMinMax should be <= t("v6").averageWithoutMinMax
-
+ if (!universal) {
+ t("v7-direct-buffer").averageWithoutMinMax <= t("v6").averageWithoutMinMax shouldBe true
+ }
}
- "be faster for reading binary than v6" taggedAs ("cmp-binary") in {
+ test("be faster for reading binary than v6") {
val bos = new ByteArrayOutputStream()
val packer = MessagePack.newDefaultPacker(bos)
@@ -702,37 +701,37 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- "read payload as a reference" taggedAs ("ref") in {
+ test("read payload as a reference") {
val dataSizes =
Seq(0, 1, 5, 8, 16, 32, 128, 256, 1024, 2000, 10000, 100000)
for (s <- dataSizes) {
- When(f"data size is $s%,d")
- val data = new Array[Byte](s)
- Random.nextBytes(data)
- val b = new ByteArrayOutputStream()
- val packer = MessagePack.newDefaultPacker(b)
- packer.packBinaryHeader(s)
- packer.writePayload(data)
- packer.close()
-
- for (unpacker <- unpackers(b.toByteArray)) {
- val len = unpacker.unpackBinaryHeader()
- len shouldBe s
- val ref = unpacker.readPayloadAsReference(len)
- unpacker.close()
- ref.size() shouldBe s
- val stored = new Array[Byte](len)
- ref.getBytes(0, stored, 0, len)
+ test(f"data size is $s%,d") {
+ val data = new Array[Byte](s)
+ Random.nextBytes(data)
+ val b = new ByteArrayOutputStream()
+ val packer = MessagePack.newDefaultPacker(b)
+ packer.packBinaryHeader(s)
+ packer.writePayload(data)
+ packer.close()
+
+ for (unpacker <- unpackers(b.toByteArray)) {
+ val len = unpacker.unpackBinaryHeader()
+ len shouldBe s
+ val ref = unpacker.readPayloadAsReference(len)
+ unpacker.close()
+ ref.size() shouldBe s
+ val stored = new Array[Byte](len)
+ ref.getBytes(0, stored, 0, len)
- stored shouldBe data
+ stored shouldBe data
+ }
}
}
-
}
- "reset the internal states" taggedAs ("reset") in {
+ test("reset the internal states") {
val data = intSeq
val b = createMessagePackData(packer => data foreach packer.packInt)
@@ -769,7 +768,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
- "improve the performance via reset method" taggedAs ("reset-arr") in {
+ test("improve the performance via reset method") {
val out = new ByteArrayOutputStream
val packer = MessagePack.newDefaultPacker(out)
@@ -821,7 +820,7 @@ class MessageUnpackerTest extends MessagePackSpec {
// t("reuse-array-input").averageWithoutMinMax should be <= t("no-buffer-reset").averageWithoutMinMax
}
- "reset ChannelBufferInput" in {
+ test("reset ChannelBufferInput") {
val f0 = createTempFile
val u = MessagePack.newDefaultUnpacker(new FileInputStream(f0).getChannel)
checkFile(u)
@@ -833,7 +832,7 @@ class MessageUnpackerTest extends MessagePackSpec {
u.close
}
- "reset InputStreamBufferInput" in {
+ test("reset InputStreamBufferInput") {
val f0 = createTempFile
val u = MessagePack.newDefaultUnpacker(new FileInputStream(f0))
checkFile(u)
@@ -845,7 +844,7 @@ class MessageUnpackerTest extends MessagePackSpec {
u.close
}
- "unpack large string data" taggedAs ("large-string") in {
+ test("unpack large string data") {
def createLargeData(stringLength: Int): Array[Byte] = {
val out = new ByteArrayOutputStream()
val packer = MessagePack.newDefaultPacker(out)
@@ -874,7 +873,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- "unpack string crossing end of buffer" in {
+ test("unpack string crossing end of buffer") {
def check(expected: String, strLen: Int) = {
val bytes = new Array[Byte](strLen)
val out = new ByteArrayOutputStream
@@ -910,7 +909,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- "read value length at buffer boundary" taggedAs ("number-boundary") in {
+ test("read value length at buffer boundary") {
val input = new SplitMessageBufferInput(
Array(Array[Byte](MessagePack.Code.STR16),
Array[Byte](0x00),
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/buffer/ByteStringTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/buffer/ByteStringTest.scala
index ed79ef6ab..42872fc44 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/buffer/ByteStringTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/buffer/ByteStringTest.scala
@@ -16,14 +16,16 @@
package org.msgpack.core.buffer
import akka.util.ByteString
-import org.msgpack.core.{MessagePack, MessagePackSpec, MessageUnpacker}
+import org.msgpack.core.MessagePack
+import org.msgpack.core.MessagePackSpec.createMessagePackData
+import wvlet.airspec.AirSpec
-class ByteStringTest extends MessagePackSpec {
+class ByteStringTest extends AirSpec {
- val unpackedString = "foo"
- val byteString = ByteString(createMessagePackData(_.packString(unpackedString)))
+ private val unpackedString = "foo"
+ private val byteString = ByteString(createMessagePackData(_.packString(unpackedString)))
- def unpackString(messageBuffer: MessageBuffer) = {
+ private def unpackString(messageBuffer: MessageBuffer) = {
val input = new MessageBufferInput {
private var isRead = false
@@ -41,12 +43,14 @@ class ByteStringTest extends MessagePackSpec {
MessagePack.newDefaultUnpacker(input).unpackString()
}
- "Unpacking a ByteString's ByteBuffer" should {
- "fail with a regular MessageBuffer" in {
+ test("Unpacking a ByteString's ByteBuffer") {
+ test("fail with a regular MessageBuffer") {
// can't demonstrate with new ByteBufferInput(byteString.asByteBuffer)
// as Travis tests run with JDK6 that picks up MessageBufferU
- a[RuntimeException] shouldBe thrownBy(unpackString(new MessageBuffer(byteString.asByteBuffer)))
+ intercept[RuntimeException] {
+ unpackString(new MessageBuffer(byteString.asByteBuffer))
+ }
}
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala
index 060e436a1..4cc4a98a7 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala
@@ -15,35 +15,35 @@
//
package org.msgpack.core.buffer
+import org.msgpack.core.MessagePack
+import wvlet.airspec.AirSpec
+import wvlet.log.io.IOUtil.withResource
+
import java.io._
-import java.net.{InetSocketAddress}
+import java.net.InetSocketAddress
import java.nio.ByteBuffer
import java.nio.channels.{ServerSocketChannel, SocketChannel}
import java.util.concurrent.{Callable, Executors, TimeUnit}
import java.util.zip.{GZIPInputStream, GZIPOutputStream}
-
-import org.msgpack.core.{MessagePack, MessagePackSpec}
-import xerial.core.io.IOUtil._
-
import scala.util.Random
-class MessageBufferInputTest extends MessagePackSpec {
+class MessageBufferInputTest extends AirSpec {
- val targetInputSize =
+ private val targetInputSize =
Seq(0, 10, 500, 1000, 2000, 4000, 8000, 10000, 30000, 50000, 100000)
- def testData(size: Int) = {
+ private def testData(size: Int) = {
//debug(s"test data size: ${size}")
val b = new Array[Byte](size)
Random.nextBytes(b)
b
}
- def testDataSet = {
+ private def testDataSet = {
targetInputSize.map(testData)
}
- def runTest(factory: Array[Byte] => MessageBufferInput) {
+ private def runTest(factory: Array[Byte] => MessageBufferInput) {
for (b <- testDataSet) {
checkInputData(b, factory(b))
}
@@ -74,30 +74,31 @@ class MessageBufferInputTest extends MessagePackSpec {
}
}
- def checkInputData(inputData: Array[Byte], in: MessageBufferInput) {
- When(s"input data size = ${inputData.length}")
- var cursor = 0
- for (m <- Iterator.continually(in.next).takeWhile(_ != null)) {
- m.toByteArray() shouldBe inputData.slice(cursor, cursor + m.size())
- cursor += m.size()
+ private def checkInputData(inputData: Array[Byte], in: MessageBufferInput) {
+ test(s"When input data size = ${inputData.length}") {
+ var cursor = 0
+ for (m <- Iterator.continually(in.next).takeWhile(_ != null)) {
+ m.toByteArray() shouldBe inputData.slice(cursor, cursor + m.size())
+ cursor += m.size()
+ }
+ cursor shouldBe inputData.length
}
- cursor shouldBe inputData.length
}
- "MessageBufferInput" should {
- "support byte arrays" in {
+ test("MessageBufferInput") {
+ test("support byte arrays") {
runTest(new ArrayBufferInput(_))
}
- "support ByteBuffers" in {
+ test("support ByteBuffers") {
runTest(b => new ByteBufferInput(b.toByteBuffer))
}
- "support InputStreams" taggedAs ("is") in {
+ test("support InputStreams") {
runTest(b => new InputStreamBufferInput(new GZIPInputStream(new ByteArrayInputStream(b.compress))))
}
- "support file input channel" taggedAs ("fc") in {
+ test("support file input channel") {
runTest { b =>
val tmp = b.saveToTmpFile
try {
@@ -110,13 +111,13 @@ class MessageBufferInputTest extends MessagePackSpec {
}
}
- def createTempFile = {
+ private def createTempFile = {
val f = File.createTempFile("msgpackTest", "msgpack")
f.deleteOnExit
f
}
- def createTempFileWithInputStream = {
+ private def createTempFileWithInputStream = {
val f = createTempFile
val out = new FileOutputStream(f)
MessagePack.newDefaultPacker(out).packInt(42).close
@@ -124,19 +125,19 @@ class MessageBufferInputTest extends MessagePackSpec {
(f, in)
}
- def createTempFileWithChannel = {
+ private def createTempFileWithChannel = {
val (f, in) = createTempFileWithInputStream
val ch = in.getChannel
(f, ch)
}
- def readInt(buf: MessageBufferInput): Int = {
+ private def readInt(buf: MessageBufferInput): Int = {
val unpacker = MessagePack.newDefaultUnpacker(buf)
unpacker.unpackInt
}
- "InputStreamBufferInput" should {
- "reset buffer" in {
+ test("InputStreamBufferInput") {
+ test("reset buffer") {
val (f0, in0) = createTempFileWithInputStream
val buf = new InputStreamBufferInput(in0)
readInt(buf) shouldBe 42
@@ -146,7 +147,7 @@ class MessageBufferInputTest extends MessagePackSpec {
readInt(buf) shouldBe 42
}
- "be non-blocking" taggedAs ("non-blocking") in {
+ test("be non-blocking") {
withResource(new PipedOutputStream()) { pipedOutputStream =>
withResource(new PipedInputStream()) { pipedInputStream =>
@@ -173,8 +174,8 @@ class MessageBufferInputTest extends MessagePackSpec {
}
}
- "ChannelBufferInput" should {
- "reset buffer" in {
+ test("ChannelBufferInput") {
+ test("reset buffer") {
val (f0, in0) = createTempFileWithChannel
val buf = new ChannelBufferInput(in0)
readInt(buf) shouldBe 42
@@ -184,7 +185,7 @@ class MessageBufferInputTest extends MessagePackSpec {
readInt(buf) shouldBe 42
}
- "unpack without blocking" in {
+ test("unpack without blocking") {
val server =
ServerSocketChannel.open.bind(new InetSocketAddress("localhost", 0))
val executorService = Executors.newCachedThreadPool
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferOutputTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferOutputTest.scala
index e048e1ba1..ea9cde57e 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferOutputTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferOutputTest.scala
@@ -15,62 +15,62 @@
//
package org.msgpack.core.buffer
-import java.io._
+import wvlet.airspec.AirSpec
-import org.msgpack.core.MessagePackSpec
+import java.io._
-class MessageBufferOutputTest extends MessagePackSpec {
+class MessageBufferOutputTest extends AirSpec {
- def createTempFile = {
+ private def createTempFile = {
val f = File.createTempFile("msgpackTest", "msgpack")
f.deleteOnExit
f
}
- def createTempFileWithOutputStream = {
+ private def createTempFileWithOutputStream = {
val f = createTempFile
val out = new FileOutputStream(f)
(f, out)
}
- def createTempFileWithChannel = {
+ private def createTempFileWithChannel = {
val (f, out) = createTempFileWithOutputStream
val ch = out.getChannel
(f, ch)
}
- def writeIntToBuf(buf: MessageBufferOutput) = {
+ private def writeIntToBuf(buf: MessageBufferOutput) = {
val mb0 = buf.next(8)
mb0.putInt(0, 42)
buf.writeBuffer(4)
buf.close
}
- "OutputStreamBufferOutput" should {
- "reset buffer" in {
+ test("OutputStreamBufferOutput") {
+ test("reset buffer") {
val (f0, out0) = createTempFileWithOutputStream
val buf = new OutputStreamBufferOutput(out0)
writeIntToBuf(buf)
- f0.length.toInt should be > 0
+ f0.length.toInt > 0 shouldBe true
val (f1, out1) = createTempFileWithOutputStream
buf.reset(out1)
writeIntToBuf(buf)
- f1.length.toInt should be > 0
+ f1.length.toInt > 0 shouldBe true
}
}
- "ChannelBufferOutput" should {
- "reset buffer" in {
+ test("ChannelBufferOutput") {
+ test("reset buffer") {
val (f0, ch0) = createTempFileWithChannel
val buf = new ChannelBufferOutput(ch0)
writeIntToBuf(buf)
- f0.length.toInt should be > 0
+ f0.length.toInt >= 0 shouldBe true
val (f1, ch1) = createTempFileWithChannel
buf.reset(ch1)
writeIntToBuf(buf)
- f1.length.toInt should be > 0
+ f1.length.toInt > 0 shouldBe true
}
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferTest.scala
index f0f66b4af..4bb9c69da 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferTest.scala
@@ -15,246 +15,245 @@
//
package org.msgpack.core.buffer
-import java.nio.ByteBuffer
-
-import org.msgpack.core.MessagePackSpec
+import org.msgpack.core.Benchmark
+import wvlet.airspec.AirSpec
+import java.nio.ByteBuffer
import scala.util.Random
/**
* Created on 2014/05/01.
*/
-class MessageBufferTest extends MessagePackSpec {
+class MessageBufferTest extends AirSpec with Benchmark {
- "MessageBuffer" should {
+ private val universal = MessageBuffer.allocate(0).isInstanceOf[MessageBufferU]
- val universal = MessageBuffer.allocate(0).isInstanceOf[MessageBufferU]
- "check buffer type" in {
- val b = MessageBuffer.allocate(0)
- info(s"MessageBuffer type: ${b.getClass.getName}")
- }
+ test("check buffer type") {
+ val b = MessageBuffer.allocate(0)
+ info(s"MessageBuffer type: ${b.getClass.getName}")
+ }
- "wrap byte array considering position and remaining values" taggedAs ("wrap-ba") in {
- val d = Array[Byte](10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
- val mb = MessageBuffer.wrap(d, 2, 2)
- mb.getByte(0) shouldBe 12
- mb.size() shouldBe 2
- }
+ test("wrap byte array considering position and remaining values") {
+ val d = Array[Byte](10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
+ val mb = MessageBuffer.wrap(d, 2, 2)
+ mb.getByte(0) shouldBe 12
+ mb.size() shouldBe 2
+ }
- "wrap ByteBuffer considering position and remaining values" taggedAs ("wrap-bb") in {
- val d = Array[Byte](10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
- val subset = ByteBuffer.wrap(d, 2, 2)
- val mb = MessageBuffer.wrap(subset)
- mb.getByte(0) shouldBe 12
- mb.size() shouldBe 2
- }
+ test("wrap ByteBuffer considering position and remaining values") {
+ val d = Array[Byte](10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
+ val subset = ByteBuffer.wrap(d, 2, 2)
+ val mb = MessageBuffer.wrap(subset)
+ mb.getByte(0) shouldBe 12
+ mb.size() shouldBe 2
+ }
+
+ test("have better performance than ByteBuffer") {
- "have better performance than ByteBuffer" in {
+ val N = 1000000
+ val M = 64 * 1024 * 1024
- val N = 1000000
- val M = 64 * 1024 * 1024
+ val ub = MessageBuffer.allocate(M)
+ val ud =
+ if (universal) MessageBuffer.wrap(ByteBuffer.allocate(M))
+ else MessageBuffer.wrap(ByteBuffer.allocateDirect(M))
+ val hb = ByteBuffer.allocate(M)
+ val db = ByteBuffer.allocateDirect(M)
- val ub = MessageBuffer.allocate(M)
- val ud =
- if (universal) MessageBuffer.wrap(ByteBuffer.allocate(M))
- else MessageBuffer.wrap(ByteBuffer.allocateDirect(M))
- val hb = ByteBuffer.allocate(M)
- val db = ByteBuffer.allocateDirect(M)
+ def bench(f: Int => Unit) {
+ var i = 0
+ while (i < N) {
+ f((i * 4) % M)
+ i += 1
+ }
+ }
+
+ val r = new Random(0)
+ val rs = new Array[Int](N)
+ (0 until N).map(i => rs(i) = r.nextInt(N))
+ def randomBench(f: Int => Unit) {
+ var i = 0
+ while (i < N) {
+ f((rs(i) * 4) % M)
+ i += 1
+ }
+ }
- def bench(f: Int => Unit) {
+ val rep = 3
+ info(f"Reading buffers (of size:${M}%,d) ${N}%,d x $rep times")
+ time("sequential getInt", repeat = rep) {
+ block("unsafe array") {
var i = 0
while (i < N) {
- f((i * 4) % M)
+ ub.getInt((i * 4) % M)
i += 1
}
}
- val r = new Random(0)
- val rs = new Array[Int](N)
- (0 until N).map(i => rs(i) = r.nextInt(N))
- def randomBench(f: Int => Unit) {
+ block("unsafe direct") {
var i = 0
while (i < N) {
- f((rs(i) * 4) % M)
+ ud.getInt((i * 4) % M)
i += 1
}
}
- val rep = 3
- info(f"Reading buffers (of size:${M}%,d) ${N}%,d x $rep times")
- time("sequential getInt", repeat = rep) {
- block("unsafe array") {
- var i = 0
- while (i < N) {
- ub.getInt((i * 4) % M)
- i += 1
- }
- }
-
- block("unsafe direct") {
- var i = 0
- while (i < N) {
- ud.getInt((i * 4) % M)
- i += 1
- }
- }
-
- block("allocate") {
- var i = 0
- while (i < N) {
- hb.getInt((i * 4) % M)
- i += 1
- }
+ block("allocate") {
+ var i = 0
+ while (i < N) {
+ hb.getInt((i * 4) % M)
+ i += 1
}
+ }
- block("allocateDirect") {
- var i = 0
- while (i < N) {
- db.getInt((i * 4) % M)
- i += 1
- }
+ block("allocateDirect") {
+ var i = 0
+ while (i < N) {
+ db.getInt((i * 4) % M)
+ i += 1
}
}
+ }
- time("random getInt", repeat = rep) {
- block("unsafe array") {
- var i = 0
- while (i < N) {
- ub.getInt((rs(i) * 4) % M)
- i += 1
- }
+ time("random getInt", repeat = rep) {
+ block("unsafe array") {
+ var i = 0
+ while (i < N) {
+ ub.getInt((rs(i) * 4) % M)
+ i += 1
}
+ }
- block("unsafe direct") {
- var i = 0
- while (i < N) {
- ud.getInt((rs(i) * 4) % M)
- i += 1
- }
+ block("unsafe direct") {
+ var i = 0
+ while (i < N) {
+ ud.getInt((rs(i) * 4) % M)
+ i += 1
}
+ }
- block("allocate") {
- var i = 0
- while (i < N) {
- hb.getInt((rs(i) * 4) % M)
- i += 1
- }
+ block("allocate") {
+ var i = 0
+ while (i < N) {
+ hb.getInt((rs(i) * 4) % M)
+ i += 1
}
+ }
- block("allocateDirect") {
- var i = 0
- while (i < N) {
- db.getInt((rs(i) * 4) % M)
- i += 1
- }
+ block("allocateDirect") {
+ var i = 0
+ while (i < N) {
+ db.getInt((rs(i) * 4) % M)
+ i += 1
}
}
}
- val builder = Seq.newBuilder[MessageBuffer]
- builder += MessageBuffer.allocate(10)
- builder += MessageBuffer.wrap(ByteBuffer.allocate(10))
- if (!universal) builder += MessageBuffer.wrap(ByteBuffer.allocateDirect(10))
- val buffers = builder.result()
-
- "convert to ByteBuffer" in {
- for (t <- buffers) {
- val bb = t.sliceAsByteBuffer
- bb.position() shouldBe 0
- bb.limit() shouldBe 10
- bb.capacity shouldBe 10
- }
+ }
+
+ private val builder = Seq.newBuilder[MessageBuffer]
+ builder += MessageBuffer.allocate(10)
+ builder += MessageBuffer.wrap(ByteBuffer.allocate(10))
+ if (!universal) builder += MessageBuffer.wrap(ByteBuffer.allocateDirect(10))
+ private val buffers = builder.result()
+
+ test("convert to ByteBuffer") {
+ for (t <- buffers) {
+ val bb = t.sliceAsByteBuffer
+ bb.position() shouldBe 0
+ bb.limit() shouldBe 10
+ bb.capacity shouldBe 10
}
+ }
- "put ByteBuffer on itself" in {
- for (t <- buffers) {
- val b = Array[Byte](0x02, 0x03)
- val srcArray = ByteBuffer.wrap(b)
- val srcHeap = ByteBuffer.allocate(b.length)
- srcHeap.put(b).flip
- val srcOffHeap = ByteBuffer.allocateDirect(b.length)
- srcOffHeap.put(b).flip
-
- for (src <- Seq(srcArray, srcHeap, srcOffHeap)) {
- // Write header bytes
- val header = Array[Byte](0x00, 0x01)
- t.putBytes(0, header, 0, header.length)
- // Write src after the header
- t.putByteBuffer(header.length, src, header.length)
-
- t.getByte(0) shouldBe 0x00
- t.getByte(1) shouldBe 0x01
- t.getByte(2) shouldBe 0x02
- t.getByte(3) shouldBe 0x03
- }
+ test("put ByteBuffer on itself") {
+ for (t <- buffers) {
+ val b = Array[Byte](0x02, 0x03)
+ val srcArray = ByteBuffer.wrap(b)
+ val srcHeap = ByteBuffer.allocate(b.length)
+ srcHeap.put(b).flip
+ val srcOffHeap = ByteBuffer.allocateDirect(b.length)
+ srcOffHeap.put(b).flip
+
+ for (src <- Seq(srcArray, srcHeap, srcOffHeap)) {
+ // Write header bytes
+ val header = Array[Byte](0x00, 0x01)
+ t.putBytes(0, header, 0, header.length)
+ // Write src after the header
+ t.putByteBuffer(header.length, src, header.length)
+
+ t.getByte(0) shouldBe 0x00
+ t.getByte(1) shouldBe 0x01
+ t.getByte(2) shouldBe 0x02
+ t.getByte(3) shouldBe 0x03
}
}
+ }
- "put MessageBuffer on itself" in {
- for (t <- buffers) {
- val b = Array[Byte](0x02, 0x03)
- val srcArray = ByteBuffer.wrap(b)
- val srcHeap = ByteBuffer.allocate(b.length)
- srcHeap.put(b).flip
- val srcOffHeap = ByteBuffer.allocateDirect(b.length)
- srcOffHeap.put(b).flip
- val builder = Seq.newBuilder[ByteBuffer]
- builder ++= Seq(srcArray, srcHeap)
- if (!universal) builder += srcOffHeap
-
- for (src <- builder.result().map(d => MessageBuffer.wrap(d))) {
- // Write header bytes
- val header = Array[Byte](0x00, 0x01)
- t.putBytes(0, header, 0, header.length)
- // Write src after the header
- t.putMessageBuffer(header.length, src, 0, header.length)
-
- t.getByte(0) shouldBe 0x00
- t.getByte(1) shouldBe 0x01
- t.getByte(2) shouldBe 0x02
- t.getByte(3) shouldBe 0x03
- }
+ test("put MessageBuffer on itself") {
+ for (t <- buffers) {
+ val b = Array[Byte](0x02, 0x03)
+ val srcArray = ByteBuffer.wrap(b)
+ val srcHeap = ByteBuffer.allocate(b.length)
+ srcHeap.put(b).flip
+ val srcOffHeap = ByteBuffer.allocateDirect(b.length)
+ srcOffHeap.put(b).flip
+ val builder = Seq.newBuilder[ByteBuffer]
+ builder ++= Seq(srcArray, srcHeap)
+ if (!universal) builder += srcOffHeap
+
+ for (src <- builder.result().map(d => MessageBuffer.wrap(d))) {
+ // Write header bytes
+ val header = Array[Byte](0x00, 0x01)
+ t.putBytes(0, header, 0, header.length)
+ // Write src after the header
+ t.putMessageBuffer(header.length, src, 0, header.length)
+
+ t.getByte(0) shouldBe 0x00
+ t.getByte(1) shouldBe 0x01
+ t.getByte(2) shouldBe 0x02
+ t.getByte(3) shouldBe 0x03
}
}
+ }
- "copy sliced buffer" in {
- def prepareBytes: Array[Byte] = {
- Array[Byte](0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07)
- }
+ test("copy sliced buffer") {
+ def prepareBytes: Array[Byte] = {
+ Array[Byte](0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07)
+ }
- def prepareDirectBuffer: ByteBuffer = {
- val directBuffer = ByteBuffer.allocateDirect(prepareBytes.length)
- directBuffer.put(prepareBytes)
- directBuffer.flip
- directBuffer
- }
+ def prepareDirectBuffer: ByteBuffer = {
+ val directBuffer = ByteBuffer.allocateDirect(prepareBytes.length)
+ directBuffer.put(prepareBytes)
+ directBuffer.flip
+ directBuffer
+ }
- def checkSliceAndCopyTo(srcBuffer: MessageBuffer, dstBuffer: MessageBuffer) = {
- val sliced = srcBuffer.slice(2, 5)
-
- sliced.size() shouldBe 5
- sliced.getByte(0) shouldBe 0x02
- sliced.getByte(1) shouldBe 0x03
- sliced.getByte(2) shouldBe 0x04
- sliced.getByte(3) shouldBe 0x05
- sliced.getByte(4) shouldBe 0x06
-
- sliced.copyTo(3, dstBuffer, 1, 2) // copy 0x05 and 0x06 to dstBuffer[1] and [2]
-
- dstBuffer.getByte(0) shouldBe 0x00
- dstBuffer.getByte(1) shouldBe 0x05 // copied by sliced.getByte(3)
- dstBuffer.getByte(2) shouldBe 0x06 // copied by sliced.getByte(4)
- dstBuffer.getByte(3) shouldBe 0x03
- dstBuffer.getByte(4) shouldBe 0x04
- dstBuffer.getByte(5) shouldBe 0x05
- dstBuffer.getByte(6) shouldBe 0x06
- dstBuffer.getByte(7) shouldBe 0x07
- }
+ def checkSliceAndCopyTo(srcBuffer: MessageBuffer, dstBuffer: MessageBuffer) = {
+ val sliced = srcBuffer.slice(2, 5)
+
+ sliced.size() shouldBe 5
+ sliced.getByte(0) shouldBe 0x02
+ sliced.getByte(1) shouldBe 0x03
+ sliced.getByte(2) shouldBe 0x04
+ sliced.getByte(3) shouldBe 0x05
+ sliced.getByte(4) shouldBe 0x06
+
+ sliced.copyTo(3, dstBuffer, 1, 2) // copy 0x05 and 0x06 to dstBuffer[1] and [2]
+
+ dstBuffer.getByte(0) shouldBe 0x00
+ dstBuffer.getByte(1) shouldBe 0x05 // copied by sliced.getByte(3)
+ dstBuffer.getByte(2) shouldBe 0x06 // copied by sliced.getByte(4)
+ dstBuffer.getByte(3) shouldBe 0x03
+ dstBuffer.getByte(4) shouldBe 0x04
+ dstBuffer.getByte(5) shouldBe 0x05
+ dstBuffer.getByte(6) shouldBe 0x06
+ dstBuffer.getByte(7) shouldBe 0x07
+ }
- checkSliceAndCopyTo(MessageBuffer.wrap(prepareBytes), MessageBuffer.wrap(prepareBytes))
- checkSliceAndCopyTo(MessageBuffer.wrap(ByteBuffer.wrap(prepareBytes)), MessageBuffer.wrap(ByteBuffer.wrap(prepareBytes)))
- if (!universal) {
- checkSliceAndCopyTo(MessageBuffer.wrap(prepareDirectBuffer), MessageBuffer.wrap(prepareDirectBuffer))
- }
+ checkSliceAndCopyTo(MessageBuffer.wrap(prepareBytes), MessageBuffer.wrap(prepareBytes))
+ checkSliceAndCopyTo(MessageBuffer.wrap(ByteBuffer.wrap(prepareBytes)), MessageBuffer.wrap(ByteBuffer.wrap(prepareBytes)))
+ if (!universal) {
+ checkSliceAndCopyTo(MessageBuffer.wrap(prepareDirectBuffer), MessageBuffer.wrap(prepareDirectBuffer))
}
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/example/MessagePackExampleTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/example/MessagePackExampleTest.scala
index cbbfd8751..05dfa6e65 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/example/MessagePackExampleTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/example/MessagePackExampleTest.scala
@@ -15,28 +15,28 @@
//
package org.msgpack.core.example
-import org.msgpack.core.MessagePackSpec
+import wvlet.airspec.AirSpec
/**
*
*/
-class MessagePackExampleTest extends MessagePackSpec {
+class MessagePackExampleTest extends AirSpec {
- "example" should {
+ test("example") {
- "have basic usage" in {
+ test("have basic usage") {
MessagePackExample.basicUsage()
}
- "have packer usage" in {
+ test("have packer usage") {
MessagePackExample.packer()
}
- "have file read/write example" in {
+ test("have file read/write example") {
MessagePackExample.readAndWriteFile();
}
- "have configuration example" in {
+ test("have configuration example") {
MessagePackExample.configuration();
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/RawStringValueImplTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/RawStringValueImplTest.scala
index 7de9d6c6f..fb340553c 100644
--- a/msgpack-core/src/test/scala/org/msgpack/value/RawStringValueImplTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/value/RawStringValueImplTest.scala
@@ -15,20 +15,18 @@
//
package org.msgpack.value
-import org.msgpack.core.MessagePackSpec
+import wvlet.airspec.AirSpec
-class RawStringValueImplTest extends MessagePackSpec {
+class RawStringValueImplTest extends AirSpec {
- "StringValue" should {
- "return the same hash code if they are equal" in {
- val str = "a"
- val a1 = ValueFactory.newString(str.getBytes("UTF-8"))
- val a2 = ValueFactory.newString(str)
+ test("return the same hash code if they are equal") {
+ val str = "a"
+ val a1 = ValueFactory.newString(str.getBytes("UTF-8"))
+ val a2 = ValueFactory.newString(str)
- a1.shouldEqual(a2)
- a1.hashCode.shouldEqual(a2.hashCode)
- a2.shouldEqual(a1)
- a2.hashCode.shouldEqual(a1.hashCode)
- }
+ a1 shouldBe a2
+ a1.hashCode shouldBe a2.hashCode
+ a2 shouldBe a1
+ a2.hashCode shouldBe a1.hashCode
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/ValueFactoryTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/ValueFactoryTest.scala
index c9bc8f8f0..b667ddfcf 100644
--- a/msgpack-core/src/test/scala/org/msgpack/value/ValueFactoryTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/value/ValueFactoryTest.scala
@@ -15,27 +15,29 @@
//
package org.msgpack.value
-import org.msgpack.core.MessagePackSpec
-import org.scalacheck.Prop.forAll
+import org.scalacheck.Gen
+import wvlet.airspec.AirSpec
+import wvlet.airspec.spi.PropertyCheck
/**
*
*/
-class ValueFactoryTest extends MessagePackSpec {
+class ValueFactoryTest extends AirSpec with PropertyCheck {
- def isValid(v: Value,
- expected: ValueType,
- isNil: Boolean = false,
- isBoolean: Boolean = false,
- isInteger: Boolean = false,
- isString: Boolean = false,
- isFloat: Boolean = false,
- isBinary: Boolean = false,
- isArray: Boolean = false,
- isMap: Boolean = false,
- isExtension: Boolean = false,
- isRaw: Boolean = false,
- isNumber: Boolean = false): Boolean = {
+ private def isValid(v: Value,
+ expected: ValueType,
+ isNil: Boolean = false,
+ isBoolean: Boolean = false,
+ isInteger: Boolean = false,
+ isString: Boolean = false,
+ isFloat: Boolean = false,
+ isBinary: Boolean = false,
+ isArray: Boolean = false,
+ isMap: Boolean = false,
+ isExtension: Boolean = false,
+ isRaw: Boolean = false,
+ isNumber: Boolean = false,
+ isTimestamp: Boolean = false): Boolean = {
v.isNilValue shouldBe isNil
v.isBooleanValue shouldBe isBoolean
v.isIntegerValue shouldBe isInteger
@@ -47,33 +49,70 @@ class ValueFactoryTest extends MessagePackSpec {
v.isExtensionValue shouldBe isExtension
v.isRawValue shouldBe isRaw
v.isNumberValue shouldBe isNumber
+ v.isTimestampValue shouldBe isTimestamp
true
}
- "ValueFactory" should {
-
- "create valid type values" in {
+ test("ValueFactory") {
+ test("nil") {
isValid(ValueFactory.newNil(), expected = ValueType.NIL, isNil = true)
+ }
+
+ test("boolean") {
forAll { (v: Boolean) =>
isValid(ValueFactory.newBoolean(v), expected = ValueType.BOOLEAN, isBoolean = true)
}
+ }
+
+ test("int") {
forAll { (v: Int) =>
isValid(ValueFactory.newInteger(v), expected = ValueType.INTEGER, isInteger = true, isNumber = true)
}
+ }
+
+ test("float") {
forAll { (v: Float) =>
isValid(ValueFactory.newFloat(v), expected = ValueType.FLOAT, isFloat = true, isNumber = true)
}
+ }
+ test("string") {
forAll { (v: String) =>
isValid(ValueFactory.newString(v), expected = ValueType.STRING, isString = true, isRaw = true)
}
+ }
+
+ test("array") {
forAll { (v: Array[Byte]) =>
isValid(ValueFactory.newBinary(v), expected = ValueType.BINARY, isBinary = true, isRaw = true)
}
+ }
+
+ test("empty array") {
isValid(ValueFactory.emptyArray(), expected = ValueType.ARRAY, isArray = true)
+ }
+
+ test("empty map") {
isValid(ValueFactory.emptyMap(), expected = ValueType.MAP, isMap = true)
+ }
+
+ test("ext") {
forAll { (v: Array[Byte]) =>
isValid(ValueFactory.newExtension(0, v), expected = ValueType.EXTENSION, isExtension = true, isRaw = false)
}
}
+
+ test("timestamp") {
+ forAll { (millis: Long) =>
+ isValid(ValueFactory.newTimestamp(millis), expected = ValueType.EXTENSION, isExtension = true, isTimestamp = true)
+ }
+ }
+
+ test("timestamp sec/nano") {
+ val posLong = Gen.chooseNum[Long](-31557014167219200L, 31556889864403199L)
+ val posInt = Gen.chooseNum(0, 1000000000 - 1) // NANOS_PER_SECOND
+ forAll(posLong, posInt) { (sec: Long, nano: Int) =>
+ isValid(ValueFactory.newTimestamp(sec, nano), expected = ValueType.EXTENSION, isExtension = true, isTimestamp = true)
+ }
+ }
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/ValueTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/ValueTest.scala
index eeb663bc6..83cbde6bf 100644
--- a/msgpack-core/src/test/scala/org/msgpack/value/ValueTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/value/ValueTest.scala
@@ -15,31 +15,34 @@
//
package org.msgpack.value
+import org.msgpack.core.MessagePackSpec.createMessagePackData
+
import java.math.BigInteger
import org.msgpack.core._
-import org.scalacheck.Prop.{forAll, propBoolean}
-
-import scala.util.parsing.json.JSON
+import org.scalacheck.Prop.propBoolean
+import wvlet.airframe.json.JSON
+import wvlet.airspec.AirSpec
+import wvlet.airspec.spi.PropertyCheck
-class ValueTest extends MessagePackSpec {
- def checkSuccinctType(pack: MessagePacker => Unit, expectedAtMost: MessageFormat): Boolean = {
+class ValueTest extends AirSpec with PropertyCheck {
+ private def checkSuccinctType(pack: MessagePacker => Unit, expectedAtMost: MessageFormat): Boolean = {
val b = createMessagePackData(pack)
val v1 = MessagePack.newDefaultUnpacker(b).unpackValue()
val mf = v1.asIntegerValue().mostSuccinctMessageFormat()
mf.getValueType shouldBe ValueType.INTEGER
- mf.ordinal() shouldBe <=(expectedAtMost.ordinal())
+ mf.ordinal() <= expectedAtMost.ordinal() shouldBe true
val v2 = new Variable
MessagePack.newDefaultUnpacker(b).unpackValue(v2)
val mf2 = v2.asIntegerValue().mostSuccinctMessageFormat()
mf2.getValueType shouldBe ValueType.INTEGER
- mf2.ordinal() shouldBe <=(expectedAtMost.ordinal())
+ mf2.ordinal() <= expectedAtMost.ordinal() shouldBe true
true
}
- "Value" should {
- "tell most succinct integer type" in {
+ test("Value") {
+ test("tell most succinct integer type") {
forAll { (v: Byte) =>
checkSuccinctType(_.packByte(v), MessageFormat.INT8)
}
@@ -63,7 +66,7 @@ class ValueTest extends MessagePackSpec {
}
}
- "produce json strings" in {
+ test("produce json strings") {
import ValueFactory._
@@ -94,9 +97,9 @@ class ValueTest extends MessagePackSpec {
.put(newString("address"), newArray(newString("xxx-xxxx"), newString("yyy-yyyy")))
.put(newString("name"), newString("mitsu"))
.build()
- val i1 = JSON.parseFull(m.toJson)
- val i2 = JSON.parseFull(m.toString) // expect json value
- val a1 = JSON.parseFull("""{"id":1001,"name":"mitsu","address":["xxx-xxxx","yyy-yyyy"]}""")
+ val i1 = JSON.parse(m.toJson)
+ val i2 = JSON.parse(m.toString) // expect json value
+ val a1 = JSON.parse("""{"id":1001,"name":"mitsu","address":["xxx-xxxx","yyy-yyyy"]}""")
// Equals as JSON map
i1 shouldBe a1
i2 shouldBe a1
@@ -108,7 +111,7 @@ class ValueTest extends MessagePackSpec {
}
- "check appropriate range for integers" in {
+ test("check appropriate range for integers") {
import ValueFactory._
import java.lang.Byte
import java.lang.Short
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/ValueTypeTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/ValueTypeTest.scala
index 445eda32c..4627faba4 100644
--- a/msgpack-core/src/test/scala/org/msgpack/value/ValueTypeTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/value/ValueTypeTest.scala
@@ -16,73 +16,70 @@
package org.msgpack.value
import org.msgpack.core.MessagePack.Code._
-import org.msgpack.core.{MessageFormat, MessageFormatException, MessagePackSpec}
+import org.msgpack.core.{MessageFormat, MessageFormatException}
+import wvlet.airspec.AirSpec
/**
* Created on 2014/05/06.
*/
-class ValueTypeTest extends MessagePackSpec {
+class ValueTypeTest extends AirSpec {
- "ValueType" should {
-
- "lookup ValueType from a byte value" taggedAs ("code") in {
-
- def check(b: Byte, tpe: ValueType) {
- MessageFormat.valueOf(b).getValueType shouldBe tpe
- }
+ test("lookup ValueType from a byte value") {
+ def check(b: Byte, tpe: ValueType) {
+ MessageFormat.valueOf(b).getValueType shouldBe tpe
+ }
- for (i <- 0 until 0x7f) {
- check(i.toByte, ValueType.INTEGER)
- }
+ for (i <- 0 until 0x7f) {
+ check(i.toByte, ValueType.INTEGER)
+ }
- for (i <- 0x80 until 0x8f) {
- check(i.toByte, ValueType.MAP)
- }
+ for (i <- 0x80 until 0x8f) {
+ check(i.toByte, ValueType.MAP)
+ }
- for (i <- 0x90 until 0x9f) {
- check(i.toByte, ValueType.ARRAY)
- }
+ for (i <- 0x90 until 0x9f) {
+ check(i.toByte, ValueType.ARRAY)
+ }
- check(NIL, ValueType.NIL)
+ check(NIL, ValueType.NIL)
- try {
- MessageFormat.valueOf(NEVER_USED).getValueType
- fail("NEVER_USED type should not have ValueType")
- } catch {
- case e: MessageFormatException =>
- // OK
- }
+ try {
+ MessageFormat.valueOf(NEVER_USED).getValueType
+ fail("NEVER_USED type should not have ValueType")
+ } catch {
+ case e: MessageFormatException =>
+ // OK
+ }
- check(TRUE, ValueType.BOOLEAN)
- check(FALSE, ValueType.BOOLEAN)
+ check(TRUE, ValueType.BOOLEAN)
+ check(FALSE, ValueType.BOOLEAN)
- for (t <- Seq(BIN8, BIN16, BIN32)) {
- check(t, ValueType.BINARY)
- }
+ for (t <- Seq(BIN8, BIN16, BIN32)) {
+ check(t, ValueType.BINARY)
+ }
- for (t <- Seq(FIXEXT1, FIXEXT2, FIXEXT4, FIXEXT8, FIXEXT16, EXT8, EXT16, EXT32)) {
- check(t, ValueType.EXTENSION)
- }
+ for (t <- Seq(FIXEXT1, FIXEXT2, FIXEXT4, FIXEXT8, FIXEXT16, EXT8, EXT16, EXT32)) {
+ check(t, ValueType.EXTENSION)
+ }
- for (t <- Seq(INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64)) {
- check(t, ValueType.INTEGER)
- }
+ for (t <- Seq(INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64)) {
+ check(t, ValueType.INTEGER)
+ }
- for (t <- Seq(STR8, STR16, STR32)) {
- check(t, ValueType.STRING)
- }
+ for (t <- Seq(STR8, STR16, STR32)) {
+ check(t, ValueType.STRING)
+ }
- for (t <- Seq(FLOAT32, FLOAT64)) {
- check(t, ValueType.FLOAT)
- }
+ for (t <- Seq(FLOAT32, FLOAT64)) {
+ check(t, ValueType.FLOAT)
+ }
- for (t <- Seq(ARRAY16, ARRAY32)) {
- check(t, ValueType.ARRAY)
- }
+ for (t <- Seq(ARRAY16, ARRAY32)) {
+ check(t, ValueType.ARRAY)
+ }
- for (i <- 0xe0 until 0xff) {
- check(i.toByte, ValueType.INTEGER)
- }
+ for (i <- 0xe0 until 0xff) {
+ check(i.toByte, ValueType.INTEGER)
}
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/VariableTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/VariableTest.scala
new file mode 100644
index 000000000..67ea2efef
--- /dev/null
+++ b/msgpack-core/src/test/scala/org/msgpack/value/VariableTest.scala
@@ -0,0 +1,310 @@
+//
+// MessagePack for Java
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+package org.msgpack.value
+
+import org.msgpack.core.{MessagePack, MessagePacker, MessageTypeCastException}
+import wvlet.airspec.AirSpec
+import wvlet.airspec.spi.PropertyCheck
+
+import java.time.Instant
+import java.util
+import scala.jdk.CollectionConverters._
+
+/**
+ *
+ */
+class VariableTest extends AirSpec with PropertyCheck {
+ private def check(pack: MessagePacker => Unit, checker: Variable => Unit): Unit = {
+ val packer = MessagePack.newDefaultBufferPacker()
+ pack(packer)
+ val msgpack = packer.toByteArray
+ packer.close()
+ val v = new Variable()
+ val unpacker = MessagePack.newDefaultUnpacker(msgpack)
+ unpacker.unpackValue(v)
+ checker(v)
+ unpacker.close()
+ }
+
+ /**
+ * Test Value -> MsgPack -> Value
+ */
+ private def roundTrip(v: Value): Unit = {
+ val packer = MessagePack.newDefaultBufferPacker()
+ v.writeTo(packer)
+ val msgpack = packer.toByteArray
+ val unpacker = MessagePack.newDefaultUnpacker(msgpack)
+ val v1 = unpacker.unpackValue()
+ unpacker.close()
+ v shouldBe v1
+ v.immutableValue() shouldBe v1
+ }
+
+ private def validateValue[V <: Value](
+ v: V,
+ asNil: Boolean = false,
+ asBoolean: Boolean = false,
+ asInteger: Boolean = false,
+ asFloat: Boolean = false,
+ asBinary: Boolean = false,
+ asString: Boolean = false,
+ asArray: Boolean = false,
+ asMap: Boolean = false,
+ asExtension: Boolean = false,
+ asTimestamp: Boolean = false
+ ): V = {
+ v.isNilValue shouldBe asNil
+ v.isBooleanValue shouldBe asBoolean
+ v.isIntegerValue shouldBe asInteger
+ v.isNumberValue shouldBe asInteger | asFloat
+ v.isFloatValue shouldBe asFloat
+ v.isRawValue shouldBe asBinary | asString
+ v.isBinaryValue shouldBe asBinary
+ v.isStringValue shouldBe asString
+ v.isArrayValue shouldBe asArray
+ v.isMapValue shouldBe asMap
+ v.isExtensionValue shouldBe asExtension | asTimestamp
+ v.isTimestampValue shouldBe asTimestamp
+
+ if (asNil) {
+ v.getValueType shouldBe ValueType.NIL
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asNilValue()
+ }
+ }
+
+ if (asBoolean) {
+ v.getValueType shouldBe ValueType.BOOLEAN
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asBooleanValue()
+ }
+ }
+
+ if (asInteger) {
+ v.getValueType shouldBe ValueType.INTEGER
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asIntegerValue()
+ }
+ }
+
+ if (asFloat) {
+ v.getValueType shouldBe ValueType.FLOAT
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asFloatValue()
+ }
+ }
+
+ if (asBinary | asString) {
+ v.asRawValue()
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asRawValue()
+ }
+ }
+
+ if (asBinary) {
+ v.getValueType shouldBe ValueType.BINARY
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asBinaryValue()
+ }
+ }
+
+ if (asString) {
+ v.getValueType shouldBe ValueType.STRING
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asStringValue()
+ }
+ }
+
+ if (asArray) {
+ v.getValueType shouldBe ValueType.ARRAY
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asArrayValue()
+ }
+ }
+
+ if (asMap) {
+ v.getValueType shouldBe ValueType.MAP
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asMapValue()
+ }
+ }
+
+ if (asExtension) {
+ v.getValueType shouldBe ValueType.EXTENSION
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asExtensionValue()
+ }
+ }
+
+ if (asTimestamp) {
+ v.getValueType shouldBe ValueType.EXTENSION
+ roundTrip(v)
+ } else {
+ intercept[MessageTypeCastException] {
+ v.asTimestampValue()
+ }
+ }
+
+ v
+ }
+
+ test("Variable") {
+ test("read nil") {
+ check(
+ _.packNil,
+ checker = { v =>
+ val iv = validateValue(v.asNilValue(), asNil = true)
+ iv.toJson shouldBe "null"
+ }
+ )
+ }
+
+ test("read integers") {
+ forAll { i: Int =>
+ check(
+ _.packInt(i),
+ checker = { v =>
+ val iv = validateValue(v.asIntegerValue(), asInteger = true)
+ iv.asInt() shouldBe i
+ iv.asLong() shouldBe i.toLong
+ }
+ )
+ }
+ }
+
+ test("read double") {
+ forAll { x: Double =>
+ check(
+ _.packDouble(x),
+ checker = { v =>
+ val iv = validateValue(v.asFloatValue(), asFloat = true)
+ //iv.toDouble shouldBe v
+ //iv.toFloat shouldBe x.toFloat
+ }
+ )
+ }
+ }
+
+ test("read boolean") {
+ forAll { x: Boolean =>
+ check(
+ _.packBoolean(x),
+ checker = { v =>
+ val iv = validateValue(v.asBooleanValue(), asBoolean = true)
+ iv.getBoolean shouldBe x
+ }
+ )
+ }
+ }
+
+ test("read binary") {
+ forAll { x: Array[Byte] =>
+ check(
+ { packer =>
+ packer.packBinaryHeader(x.length); packer.addPayload(x)
+ },
+ checker = { v =>
+ val iv = validateValue(v.asBinaryValue(), asBinary = true)
+ util.Arrays.equals(iv.asByteArray(), x)
+ }
+ )
+ }
+ }
+
+ test("read string") {
+ forAll { x: String =>
+ check(
+ _.packString(x),
+ checker = { v =>
+ val iv = validateValue(v.asStringValue(), asString = true)
+ iv.asString() shouldBe x
+ }
+ )
+ }
+ }
+
+ test("read array") {
+ forAll { x: Seq[Int] =>
+ check(
+ { packer =>
+ packer.packArrayHeader(x.size)
+ x.foreach { packer.packInt(_) }
+ },
+ checker = { v =>
+ val iv = validateValue(v.asArrayValue(), asArray = true)
+ val lst = iv.list().asScala.map(_.asIntegerValue().toInt)
+ lst shouldBe x
+ }
+ )
+ }
+ }
+
+ test("read map") {
+ forAll { x: Seq[Int] =>
+ // Generate map with unique keys
+ val map = x.zipWithIndex.map { case (x, i) => (s"key-${i}", x) }
+ check(
+ { packer =>
+ packer.packMapHeader(map.size)
+ map.foreach { x =>
+ packer.packString(x._1)
+ packer.packInt(x._2)
+ }
+ },
+ checker = { v =>
+ val iv = validateValue(v.asMapValue(), asMap = true)
+ val lst = iv.map().asScala.map(p => (p._1.asStringValue().asString(), p._2.asIntegerValue().asInt())).toSeq
+ lst.sortBy(_._1) shouldBe map.sortBy(_._1)
+ }
+ )
+ }
+ }
+
+ test("read timestamps") {
+ forAll { millis: Long =>
+ val i = Instant.ofEpochMilli(millis)
+ check(
+ _.packTimestamp(i),
+ checker = { v =>
+ val ts = validateValue(v.asTimestampValue(), asTimestamp = true)
+ ts.isTimestampValue shouldBe true
+ ts.toInstant shouldBe i
+ }
+ )
+ }
+ }
+ }
+}