aboutsummaryrefslogtreecommitdiffstats
path: root/vespajlib/src/test/java/com/yahoo
diff options
context:
space:
mode:
Diffstat (limited to 'vespajlib/src/test/java/com/yahoo')
-rw-r--r--vespajlib/src/test/java/com/yahoo/binaryprefix/BinaryScaledAmountTestCase.java38
-rw-r--r--vespajlib/src/test/java/com/yahoo/cache/CacheTestCase.java197
-rw-r--r--vespajlib/src/test/java/com/yahoo/cache/CalcTestCase.java176
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/ArraySetTestCase.java303
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/BobHashTestCase.java45
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/ByteArrayComparatorTestCase.java38
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/CollectionComparatorTestCase.java40
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/CollectionUtilTest.java50
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/CollectionsBenchMark.java204
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/CopyOnWriteHashMapTestCase.java64
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/FreezableArrayListListener.java74
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/HashletTestCase.java186
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/IntArrayComparatorTestCase.java37
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/LazyMapTest.java285
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/LazySetTest.java265
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/ListMapTestCase.java153
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/ListenableArrayListTestCase.java50
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/MD5TestCase.java33
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/PredicateSplitTestCase.java30
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/TinyIdentitySetTestCase.java302
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/TupleTestCase.java48
-rw-r--r--vespajlib/src/test/java/com/yahoo/compress/IntegerCompressorTest.java105
-rw-r--r--vespajlib/src/test/java/com/yahoo/concurrent/CopyOnWriteHashMapTest.java106
-rw-r--r--vespajlib/src/test/java/com/yahoo/concurrent/EventBarrierTestCase.java168
-rw-r--r--vespajlib/src/test/java/com/yahoo/concurrent/ExecutorsTestCase.java139
-rw-r--r--vespajlib/src/test/java/com/yahoo/concurrent/ReceiverTestCase.java59
-rw-r--r--vespajlib/src/test/java/com/yahoo/concurrent/ThreadFactoryFactoryTest.java44
-rw-r--r--vespajlib/src/test/java/com/yahoo/concurrent/ThreadLocalDirectoryTestCase.java125
-rw-r--r--vespajlib/src/test/java/com/yahoo/concurrent/ThreadRobustListTestCase.java103
-rw-r--r--vespajlib/src/test/java/com/yahoo/data/access/InspectorConformanceTestBase.java365
-rw-r--r--vespajlib/src/test/java/com/yahoo/data/access/simple/SimpleConformanceTestCase.java55
-rw-r--r--vespajlib/src/test/java/com/yahoo/data/access/slime/SlimeConformanceTestCase.java45
-rw-r--r--vespajlib/src/test/java/com/yahoo/data/inspect/slime/.gitignore0
-rw-r--r--vespajlib/src/test/java/com/yahoo/geo/BoundingBoxParserTestCase.java162
-rw-r--r--vespajlib/src/test/java/com/yahoo/geo/DegreesParserTestCase.java282
-rw-r--r--vespajlib/src/test/java/com/yahoo/geo/ZCurveTestCase.java204
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/BlobTestCase.java95
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/ByteWriterTestCase.java444
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/FatalErrorHandlerTestCase.java55
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/FileReadTestCase.java39
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/GrowableBufferOutputStreamTestCase.java126
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/GrowableByteBufferTestCase.java756
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/HexDumpTestCase.java40
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/IOUtilsTestCase.java149
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/ListenerTestCase.java108
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/SlowInflateTestCase.java61
-rw-r--r--vespajlib/src/test/java/com/yahoo/io/reader/NamedReaderTestCase.java131
-rw-r--r--vespajlib/src/test/java/com/yahoo/java7compat/UtilTest.java29
-rw-r--r--vespajlib/src/test/java/com/yahoo/javacc/FastCharStreamTestCase.java181
-rw-r--r--vespajlib/src/test/java/com/yahoo/javacc/UnicodeUtilitiesTestCase.java112
-rw-r--r--vespajlib/src/test/java/com/yahoo/net/HostNameTestCase.java16
-rwxr-xr-xvespajlib/src/test/java/com/yahoo/net/LinuxInetAddressTestCase.java41
-rw-r--r--vespajlib/src/test/java/com/yahoo/net/URITestCase.java512
-rw-r--r--vespajlib/src/test/java/com/yahoo/net/UriToolsTestCase.java25
-rw-r--r--vespajlib/src/test/java/com/yahoo/net/UrlTestCase.java192
-rw-r--r--vespajlib/src/test/java/com/yahoo/net/UrlTokenTestCase.java47
-rw-r--r--vespajlib/src/test/java/com/yahoo/net/UrlTokenizerTestCase.java385
-rw-r--r--vespajlib/src/test/java/com/yahoo/path/PathTest.java128
-rw-r--r--vespajlib/src/test/java/com/yahoo/protect/TestErrorMessage.java31
-rw-r--r--vespajlib/src/test/java/com/yahoo/protect/ValidatorTestCase.java88
-rw-r--r--vespajlib/src/test/java/com/yahoo/reflection/CastingTest.java36
-rw-r--r--vespajlib/src/test/java/com/yahoo/rmi/.gitignore0
-rw-r--r--vespajlib/src/test/java/com/yahoo/slime/BinaryFormatTestCase.java567
-rw-r--r--vespajlib/src/test/java/com/yahoo/slime/JsonBenchmark.java114
-rw-r--r--vespajlib/src/test/java/com/yahoo/slime/JsonFormatTestCase.java273
-rw-r--r--vespajlib/src/test/java/com/yahoo/slime/SlimeTestCase.java330
-rw-r--r--vespajlib/src/test/java/com/yahoo/slime/VisitorTestCase.java101
-rw-r--r--vespajlib/src/test/java/com/yahoo/system/Bar.java7
-rw-r--r--vespajlib/src/test/java/com/yahoo/system/CatchSigTermTestCase.java19
-rw-r--r--vespajlib/src/test/java/com/yahoo/system/CommandLineParserTestCase.java125
-rw-r--r--vespajlib/src/test/java/com/yahoo/system/Foo.java7
-rw-r--r--vespajlib/src/test/java/com/yahoo/system/ForceLoadTestCase.java27
-rw-r--r--vespajlib/src/test/java/com/yahoo/system/ProcessExecuterTestCase.java23
-rw-r--r--vespajlib/src/test/java/com/yahoo/tensor/MapTensorBuilderTestCase.java48
-rw-r--r--vespajlib/src/test/java/com/yahoo/tensor/MapTensorTestCase.java69
-rw-r--r--vespajlib/src/test/java/com/yahoo/tensor/TensorTypeTestCase.java89
-rw-r--r--vespajlib/src/test/java/com/yahoo/tensor/serialization/CompactBinaryFormatTestCase.java78
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/AsciiTest.java187
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/Benchmark.java106
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/BooleanParserTestCase.java35
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/CaseInsensitiveIdentifierTestCase.java46
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/DataTypeIdentifierTestCase.java39
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/DoubleFormatterTestCase.java204
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/DoubleParserTestCase.java158
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/DoubleToStringBenchmark.java123
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/ForwardWriterTestCase.java435
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/GenericWriterTestCase.java107
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/HTMLTestCase.java49
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/IdentifierTestCase.java42
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/JSONTest.java25
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/JSONWriterTestCase.java114
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/JsonMicroBenchmarkTestCase.java563
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/LanguageHacksTestCase.java28
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/LowercaseIdentifierTestCase.java41
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/LowercaseTestCase.java96
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/MapParserMicroBenchmark.java62
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/MapParserTestCase.java71
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/StringAppendMicroBenchmarkTest.java77
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/StringUtilitiesTest.java85
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/Utf8ArrayTestCase.java167
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/Utf8TestCase.java554
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/XMLTestCase.java115
-rw-r--r--vespajlib/src/test/java/com/yahoo/text/XMLWriterTestCase.java178
-rw-r--r--vespajlib/src/test/java/com/yahoo/time/WallClockSourceTestCase.java86
-rw-r--r--vespajlib/src/test/java/com/yahoo/transaction/NestedTransactionTestCase.java181
-rw-r--r--vespajlib/src/test/java/com/yahoo/vespa/objects/BigIdClass.java183
-rw-r--r--vespajlib/src/test/java/com/yahoo/vespa/objects/FieldBaseTestCase.java50
-rw-r--r--vespajlib/src/test/java/com/yahoo/vespa/objects/FooBarIdClass.java35
-rw-r--r--vespajlib/src/test/java/com/yahoo/vespa/objects/ObjectDumperTestCase.java161
-rw-r--r--vespajlib/src/test/java/com/yahoo/vespa/objects/SerializeTestCase.java143
-rw-r--r--vespajlib/src/test/java/com/yahoo/vespa/objects/SomeIdClass.java13
111 files changed, 14843 insertions, 0 deletions
diff --git a/vespajlib/src/test/java/com/yahoo/binaryprefix/BinaryScaledAmountTestCase.java b/vespajlib/src/test/java/com/yahoo/binaryprefix/BinaryScaledAmountTestCase.java
new file mode 100644
index 00000000000..bfb36ec80ce
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/binaryprefix/BinaryScaledAmountTestCase.java
@@ -0,0 +1,38 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.binaryprefix;
+
+import junit.framework.TestCase;
+
+/**
+ * @author tonytv
+ */
+public class BinaryScaledAmountTestCase extends TestCase {
+ public void testConversion() {
+ BinaryScaledAmount oneMeg = new BinaryScaledAmount(1024, BinaryPrefix.kilo);
+
+ assertEquals(1, oneMeg.as(BinaryPrefix.mega));
+ assertEquals(1024, oneMeg.as(BinaryPrefix.kilo));
+ assertEquals(1024*1024, oneMeg.as(BinaryPrefix.unit));
+ assertEquals(1 << 20, oneMeg.hashCode());
+
+ Object v = this;
+ assertEquals(false, oneMeg.equals(v));
+ v = new BinaryScaledAmount(1, BinaryPrefix.mega);
+ assertEquals(true, oneMeg.equals(v));
+ }
+
+ public void testSymbols() {
+ BinaryScaledAmount oneMeg = new BinaryScaledAmount(1024, BinaryPrefix.kilo);
+
+ assertEquals(1, oneMeg.as(BinaryPrefix.fromSymbol('M')));
+ assertEquals(1024, oneMeg.as(BinaryPrefix.fromSymbol('K')));
+
+ boolean ex = false;
+ try {
+ BinaryPrefix invalid = BinaryPrefix.fromSymbol('q');
+ } catch (RuntimeException e) {
+ ex = true;
+ }
+ assertEquals(true, ex);
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/cache/CacheTestCase.java b/vespajlib/src/test/java/com/yahoo/cache/CacheTestCase.java
new file mode 100644
index 00000000000..992974eeb63
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/cache/CacheTestCase.java
@@ -0,0 +1,197 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.cache;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class CacheTestCase extends TestCase {
+
+ public void testBasicGet() {
+ Cache<String, String> cache = new Cache<>(100 * 1024 * 1024, 3600, 10000);
+ String q = "/std_xmls_a00?hits=5&offset=5&query=flowers+shop&tracelevel=4&objid=ffffffffffffffff";
+ String q2 = "/std_xmls_a00?hits=5&offset=5&query=flowers+shop&tracelevel=4&objid=ffffffffffffffff";
+ String r = "result";
+ String r2 = "result2";
+ assertNull(cache.get(q));
+ cache.put(q, r);
+ assertNotNull(cache.get(q));
+ assertEquals(cache.get(q), r);
+ cache.put(q2, r);
+ assertEquals(cache.get(q2), r);
+ cache.put(q, r2);
+ assertEquals(cache.get(q), r2);
+ }
+
+ public void testPutTooLarge() {
+ byte[] tenMB = new byte[10*1024*1024];
+ for (int i = 0 ; i <10*1024*1024 ; i++) {
+ tenMB[i]=127;
+ }
+ byte[] sevenMB = new byte[7*1024*1024];
+ for (int i = 0 ; i <7*1024*1024 ; i++) {
+ sevenMB[i]=127;
+ }
+ Cache<String, byte[]> cache=new Cache<>(9*1024*1024,3600, 100*1024*1024); // 9 MB
+ assertFalse(cache.put("foo", tenMB));
+ assertTrue(cache.put("foo", sevenMB));
+ assertEquals(cache.get("foo"), sevenMB);
+ }
+
+ public void testInvalidate() {
+ byte[] tenMB = new byte[10*1024*1024];
+ for (int i = 0 ; i <10*1024*1024 ; i++) {
+ tenMB[i]=127;
+ }
+ byte[] sevenMB = new byte[7*1024*1024];
+ for (int i = 0 ; i <7*1024*1024 ; i++) {
+ sevenMB[i]=127;
+ }
+ //log.info("10 MB: "+calc.sizeOf(tenMB));
+ //log.info("7 MB: "+calc.sizeOf(sevenMB));
+ Cache<String, byte[]> cache=new Cache<>(11*1024*1024,3600, 100*1024*1024); // 11 MB
+ assertTrue(cache.put("foo", sevenMB));
+ assertTrue(cache.put("bar", tenMB));
+ assertNull(cache.get("foo"));
+ assertEquals(cache.get("bar"), tenMB);
+ }
+
+ public void testInvalidateLRU() {
+ Cache<String, byte[]> cache=new Cache<>(10*1024*1024,3600, 100*1024*1024); // 10 MB
+ byte[] fiveMB = new byte[5*1024*1024];
+ for (int i = 0 ; i <5*1024*1024 ; i++) {
+ fiveMB[i]=127;
+ }
+
+ byte[] twoMB = new byte[2*1024*1024];
+ for (int i = 0 ; i <2*1024*1024 ; i++) {
+ twoMB[i]=127;
+ }
+
+ byte[] fourMB = new byte[4*1024*1024];
+ for (int i = 0 ; i <4*1024*1024 ; i++) {
+ fourMB[i]=127;
+ }
+ assertTrue(cache.put("five", fiveMB));
+ assertTrue(cache.put("two", twoMB));
+ Object dummy = cache.get("five"); // Makes two LRU
+ assertEquals(dummy, fiveMB);
+ assertTrue(cache.put("four", fourMB));
+ assertNull(cache.get("two"));
+ assertEquals(cache.get("five"), fiveMB);
+ assertEquals(cache.get("four"), fourMB);
+
+ // Same, without the access, just to check
+ cache=new Cache<>(10*1024*1024,3600, 100*1024*1024); // 10 MB
+ assertTrue(cache.put("five", fiveMB));
+ assertTrue(cache.put("two", twoMB));
+ assertTrue(cache.put("four", fourMB));
+ assertEquals(cache.get("two"), twoMB);
+ assertNull(cache.get("five"));
+ assertEquals(cache.get("four"), fourMB);
+ }
+
+ public void testPutSameKey() {
+ Cache<String, byte[]> cache=new Cache<>(10*1024*1024,3600, 100*1024*1024); // 10 MB
+ byte[] fiveMB = new byte[5*1024*1024];
+ for (int i = 0 ; i <5*1024*1024 ; i++) {
+ fiveMB[i]=127;
+ }
+
+ byte[] twoMB = new byte[2*1024*1024];
+ for (int i = 0 ; i <2*1024*1024 ; i++) {
+ twoMB[i]=127;
+ }
+
+ byte[] fourMB = new byte[4*1024*1024];
+ for (int i = 0 ; i <4*1024*1024 ; i++) {
+ fourMB[i]=127;
+ }
+ assertTrue(cache.put("five", fiveMB));
+ assertTrue(cache.put("two", twoMB));
+ assertEquals(cache.get("two"), twoMB);
+ assertEquals(cache.get("five"), fiveMB);
+ assertTrue(cache.put("five", twoMB));
+ assertEquals(cache.get("five"), twoMB);
+ assertEquals(cache.get("two"), twoMB);
+ }
+
+ public void testExpire() throws InterruptedException {
+ Cache<String, String> cache=new Cache<>(10*1024*1024,400, 10000); // 10 MB, .4 sec expire
+ cache.put("foo", "bar");
+ cache.put("hey", "ho");
+ assertEquals(cache.get("foo"), "bar");
+ assertEquals(cache.get("hey"), "ho");
+ Thread.sleep(600);
+ assertNull(cache.get("foo"));
+ assertNull(cache.get("hey"));
+ }
+
+ public void testInsertSame() {
+ Cache<String, String> cache=new Cache<>(10*1024*1024,500, 10000); // 10 MB, .5 sec expire
+ String k = "foo";
+ String r = "bar";
+ cache.put(k, r);
+ assertEquals(cache.size(), 1);
+ cache.put(k, r);
+ assertEquals(cache.size(), 1);
+ }
+
+ public void testMaxSize() {
+ Cache<String, byte[]> cache=new Cache<>(20*1024*1024,500, 3*1024*1024);
+ byte[] fourMB = new byte[4*1024*1024];
+ for (int i = 0 ; i <4*1024*1024 ; i++) {
+ fourMB[i]=127;
+ }
+ byte[] twoMB = new byte[2*1024*1024];
+ for (int i = 0 ; i <2*1024*1024 ; i++) {
+ twoMB[i]=127;
+ }
+ assertFalse(cache.put("four", fourMB));
+ assertTrue(cache.put("two", twoMB));
+ assertNull(cache.get("four"));
+ assertNotNull(cache.get("two"));
+ }
+
+ public void testMaxSizeNoLimit() {
+ Cache<String, byte[]> cache=new Cache<>(20*1024*1024,500, -1);
+ byte[] fourMB = new byte[4*1024*1024];
+ for (int i = 0 ; i <4*1024*1024 ; i++) {
+ fourMB[i]=127;
+ }
+ byte[] twoMB = new byte[2*1024*1024];
+ for (int i = 0 ; i <2*1024*1024 ; i++) {
+ twoMB[i]=127;
+ }
+ assertTrue(cache.put("four", fourMB));
+ assertTrue(cache.put("two", twoMB));
+ assertNotNull(cache.get("four"));
+ assertNotNull(cache.get("two"));
+ }
+
+ public void testGetKeysAndValuesAndClear() {
+ Cache<String, String> cache=new Cache<>(10*1024*1024,500, 10000); // 10 MB, .5 sec expire
+ assertEquals(cache.getKeys().size(), 0);
+ assertEquals(cache.getValues().size(), 0);
+ cache.put("a", "b");
+ cache.put("c", "d");
+ cache.put("e", "f");
+ Collection<String> keys = new ArrayList<>();
+ keys.add("a");
+ keys.add("c");
+ keys.add("e");
+ Collection<String> values = new ArrayList<>();
+ values.add("b");
+ values.add("d");
+ values.add("f");
+ assertEquals(cache.getKeys().size(), 3);
+ assertEquals(cache.getValues().size(), 3);
+ assertTrue(cache.getKeys().containsAll(keys));
+ assertTrue(cache.getValues().containsAll(values));
+ cache.clear();
+ assertEquals(cache.getKeys().size(), 0);
+ assertEquals(cache.getValues().size(), 0);
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/cache/CalcTestCase.java b/vespajlib/src/test/java/com/yahoo/cache/CalcTestCase.java
new file mode 100644
index 00000000000..fbec483debb
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/cache/CalcTestCase.java
@@ -0,0 +1,176 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.cache;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CalcTestCase extends junit.framework.TestCase {
+
+ private SizeCalculator calc;
+
+
+ public CalcTestCase (String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ calc = new SizeCalculator();
+ }
+
+ public void testCalc1() {
+ assertEquals(calc.sizeOf(new Object()), 8);
+ }
+
+ public void testCalc2() {
+ assertEquals(calc.sizeOf(new SixtyFourBooleans()), 8+64);
+ }
+
+ public void testBoolean() {
+ assertEquals(8+1, calc.sizeOf(new Boolean(true)));
+ }
+
+ public void testArrayPrimitive() {
+ byte[] eightBytes = new byte[]{1,1,1,1,1,1,1,1,};
+ assertEquals(16+8, calc.sizeOf(eightBytes));
+ }
+
+ public void testArrayObjects() {
+ SixtyFourBooleans[] bunchOfBooleans = new SixtyFourBooleans[]{new SixtyFourBooleans(),
+ new SixtyFourBooleans(), new SixtyFourBooleans()};
+ assertEquals(16+(3*(8+64)+(3*4)), calc.sizeOf(bunchOfBooleans));
+
+ }
+
+ public void testSizeOfList() {
+ SixtyFourBooleans sfb = new SixtyFourBooleans();
+ List<Object> dupList1 = new ArrayList<>();
+ dupList1.add(new Object());
+ dupList1.add(sfb);
+ dupList1.add(sfb);
+ dupList1.add(sfb);
+ List<Object> dupList2 = new ArrayList<>();
+ dupList2.addAll(dupList1);
+ dupList2.add(sfb);
+ dupList2.add(sfb);
+ dupList2.add(sfb);
+ dupList2.add(new Object());
+ dupList2.add(new Object());
+ assertEquals(calc.sizeOf(dupList2), calc.sizeOf(dupList1)+8+8);
+ }
+
+ public void testSizeOfTuple() {
+ SixtyFourBooleans[] bunchOfBooleans = new SixtyFourBooleans[]{new SixtyFourBooleans(),
+ new SixtyFourBooleans(), new SixtyFourBooleans()};
+ SixtyFourBooleans[] bunchOfBooleans2 = new SixtyFourBooleans[]{new SixtyFourBooleans(),
+ new SixtyFourBooleans(), new SixtyFourBooleans()};
+ assertEquals(16+(3*(8+64)+(3*4)), calc.sizeOf(bunchOfBooleans));
+ assertEquals(2* (16+(3*(8+64)+(3*4))), calc.sizeOf(bunchOfBooleans, bunchOfBooleans2));
+ }
+
+ /*public void testEmptyArrayList() {
+ assertEquals(80, calc.sizeOf(new ArrayList()));
+ }*/
+
+ /*public void testFullArrayList() {
+ ArrayList arrayList = new ArrayList(10000);
+
+ for (int i = 0; i < 10000; i++) {
+ arrayList.add(new Object());
+ }
+
+ assertEquals(120040, calc.sizeOf(arrayList));
+ }*/
+
+ /*public void testHashMap() {
+ assertEquals(120, calc.sizeOf(new HashMap()));
+
+ Byte[] all = new Byte[256];
+ for (int i = -128; i < 128; i++) {
+ all[i + 128] = new Byte((byte) i);
+ }
+ assertEquals(5136, calc.sizeOf(all));
+
+ HashMap hm = new HashMap();
+ for (int i = -128; i < 128; i++) {
+ hm.put("" + i, new Byte((byte) i));
+ }
+ assertEquals(30776, calc.sizeOf(hm));
+ }*/
+
+ /*public void testThousandBooleansObjects() {
+ Boolean[] booleans = new Boolean[1000];
+
+ for (int i = 0; i < booleans.length; i++)
+ booleans[i] = new Boolean(true);
+
+ assertEquals(20016, calc.sizeOf(booleans));
+ }*/
+
+ @SuppressWarnings("unused")
+ private static class SixtyFourBooleans {
+ boolean a0;
+ boolean a1;
+ boolean a2;
+ boolean a3;
+ boolean a4;
+ boolean a5;
+ boolean a6;
+ boolean a7;
+ boolean b0;
+ boolean b1;
+ boolean b2;
+ boolean b3;
+ boolean b4;
+ boolean b5;
+ boolean b6;
+ boolean b7;
+ boolean c0;
+ boolean c1;
+ boolean c2;
+ boolean c3;
+ boolean c4;
+ boolean c5;
+ boolean c6;
+ boolean c7;
+ boolean d0;
+ boolean d1;
+ boolean d2;
+ boolean d3;
+ boolean d4;
+ boolean d5;
+ boolean d6;
+ boolean d7;
+ boolean e0;
+ boolean e1;
+ boolean e2;
+ boolean e3;
+ boolean e4;
+ boolean e5;
+ boolean e6;
+ boolean e7;
+ boolean f0;
+ boolean f1;
+ boolean f2;
+ boolean f3;
+ boolean f4;
+ boolean f5;
+ boolean f6;
+ boolean f7;
+ boolean g0;
+ boolean g1;
+ boolean g2;
+ boolean g3;
+ boolean g4;
+ boolean g5;
+ boolean g6;
+ boolean g7;
+ boolean h0;
+ boolean h1;
+ boolean h2;
+ boolean h3;
+ boolean h4;
+ boolean h5;
+ boolean h6;
+ boolean h7;
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/ArraySetTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/ArraySetTestCase.java
new file mode 100644
index 00000000000..5bb15f57420
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/ArraySetTestCase.java
@@ -0,0 +1,303 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * Check ArraySet seems to work. :)
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public final class ArraySetTestCase {
+
+ @Test
+ public void testAdd() {
+ final String string = "abc";
+ final String a = new String(string);
+ final String b = new String(string);
+ final ArraySet<String> t = new ArraySet<>(3);
+ t.add(a);
+ t.add(b);
+ assertEquals(1, t.size());
+ t.add(string);
+ assertEquals(1, t.size());
+ t.add("abd");
+ t.add("abc");
+ t.add("abd");
+ assertEquals(2, t.size());
+
+ }
+
+ @Test
+ public void testAddAll() {
+ final List<String> stuff = doubleAdd();
+ final ArraySet<String> t = new ArraySet<>(
+ stuff.size());
+ t.addAll(stuff);
+ assertEquals(stuff.size() / 2, t.size());
+ }
+
+ private List<String> doubleAdd() {
+ final List<String> stuff = new ArrayList<>();
+ stuff.add("abc");
+ stuff.add("abd");
+ stuff.add("abe");
+ stuff.add("abc");
+ stuff.add("abd");
+ stuff.add("abe");
+ return stuff;
+ }
+
+ @Test
+ public void testContains() {
+ final String string = "abc";
+ final String a = new String(string);
+ final String b = new String(string);
+ final ArraySet<String> t = new ArraySet<>(2);
+ t.add(string);
+ t.add(a);
+ assertEquals(1, t.size());
+ assertTrue(t.contains(a));
+ assertTrue(t.contains(string));
+ assertTrue(t.contains(b));
+ }
+
+ @Test
+ public void testContainsAll() {
+ final String string = "abc";
+ final String a = new String(string);
+ final String b = new String(string);
+ final String c = "c";
+ final List<String> stuff = new ArrayList<>();
+ stuff.add(string);
+ stuff.add(a);
+ stuff.add(b);
+ final ArraySet<String> t = new ArraySet<>(
+ stuff.size());
+ t.addAll(stuff);
+ assertTrue(t.containsAll(stuff));
+ stuff.add(c);
+ assertFalse(t.containsAll(stuff));
+ }
+
+ @Test
+ public void testRemove() {
+ final String string = "abc";
+ final String a = new String(string);
+ final String b = new String(string);
+ final ArraySet<String> t = new ArraySet<>(2);
+ t.add("abc");
+ t.add("abd");
+ t.add("abe");
+ assertEquals(3, t.size());
+ assertFalse(t.remove("ab"));
+ assertTrue(t.remove("abd"));
+ assertFalse(t.remove("abd"));
+ assertEquals(2, t.size());
+ assertTrue(t.remove("abe"));
+ assertFalse(t.remove("abe"));
+ assertTrue(t.remove("abc"));
+ assertTrue(t.isEmpty());
+ }
+
+ @Test
+ public void testRetainAll() {
+ final List<String> stuff = doubleAdd();
+ final ArraySet<String> t = new ArraySet<>(
+ stuff.size());
+ t.addAll(stuff);
+ assertFalse(t.retainAll(stuff));
+ assertEquals(stuff.size() / 2, t.size());
+ t.add("nalle");
+ assertEquals(stuff.size() / 2 + 1, t.size());
+ assertTrue(t.retainAll(stuff));
+ assertEquals(stuff.size() / 2, t.size());
+ }
+
+ @Test
+ public void testToArrayTArray() {
+ final List<String> stuff = doubleAdd();
+ final ArraySet<String> t = new ArraySet<>(
+ stuff.size());
+ t.addAll(stuff);
+ final String[] s = t.toArray(new String[0]);
+ assertEquals(t.size(), s.length);
+ assertEquals(stuff.size() / 2, s.length);
+ }
+
+ @Test
+ public void testGrow() {
+ final ArraySet<Integer> t = new ArraySet<>(5);
+ final int targetSize = 100;
+ for (int i = 0; i < targetSize; ++i) {
+ t.add(i);
+ }
+ assertEquals(targetSize, t.size());
+ int n = 0;
+ for (final Iterator<Integer> i = t.iterator(); i.hasNext();) {
+ assertEquals(Integer.valueOf(n++), i.next());
+ }
+ assertEquals(targetSize, n);
+ }
+
+ @Test
+ public void testBiggerRemoveAll() {
+ final int targetSize = 100;
+ final ArraySet<Integer> t = new ArraySet<>(
+ targetSize);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> remove = buildSubSet(targetSize, t, instances);
+ t.removeAll(remove);
+ assertEquals(targetSize / 2, t.size());
+ for (final Iterator<Integer> i = t.iterator(); i.hasNext();) {
+ final Integer n = i.next();
+ assertTrue(n % 2 == 0);
+ assertFalse(remove.contains(n));
+
+ }
+ }
+
+ @Test
+ public void testBiggerRetainAll() {
+ final int targetSize = 100;
+ final ArraySet<Integer> t = new ArraySet<>(
+ targetSize);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> retain = buildSubSet(targetSize, t, instances);
+ t.retainAll(retain);
+ assertEquals(targetSize / 2, t.size());
+ for (final Iterator<Integer> i = t.iterator(); i.hasNext();) {
+ final Integer n = i.next();
+ assertTrue(n % 2 != 0);
+ assertTrue(retain.contains(n));
+ }
+ }
+
+ private List<Integer> buildSubSet(final int targetSize,
+ final ArraySet<Integer> t, final Integer[] instances) {
+ for (int i = 0; i < targetSize; ++i) {
+ instances[i] = Integer.valueOf(i);
+ t.add(instances[i]);
+ }
+ final List<Integer> subset = new ArrayList<>(50);
+ for (int i = 0; i < targetSize; ++i) {
+ if (i % 2 != 0) {
+ subset.add(instances[i]);
+ }
+ }
+ return subset;
+ }
+
+ @Test
+ public void testMuckingAbout() {
+ final int targetSize = 100;
+ final ArraySet<Integer> t = new ArraySet<>(3);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> retain = buildSubSet(targetSize, t, instances);
+ for (final Integer n : retain) {
+ t.remove(n);
+ assertEquals(targetSize - 1, t.size());
+ t.add(n);
+ assertEquals(targetSize, t.size());
+ }
+ assertEquals(targetSize, t.size());
+ final Integer[] contents = t.toArray(new Integer[0]);
+ Arrays.sort(contents, 0, targetSize);
+ for (int i = 0; i < targetSize; ++i) {
+ assertEquals(instances[i], contents[i]);
+ }
+ }
+
+ @Test
+ public void testMoreDuplicates() {
+ final int targetSize = 100;
+ final ArraySet<Integer> t = new ArraySet<>(3);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> add = buildSubSet(targetSize, t, instances);
+ assertEquals(targetSize, t.size());
+ t.addAll(add);
+ assertEquals(targetSize, t.size());
+ }
+
+ @Test
+ public void testEmptySet() {
+ final int targetSize = 100;
+ final ArraySet<Integer> t = new ArraySet<>(0);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> add = buildSubSet(targetSize, t, instances);
+ for (Integer i : instances) {
+ t.remove(i);
+ }
+ assertEquals(0, t.size());
+ for (Integer i : add) {
+ t.add(i);
+ }
+ assertEquals(targetSize / 2, t.size());
+ }
+
+ @Test
+ public void testSmallEmptySet() {
+ final ArraySet<Integer> t = new ArraySet<>(3);
+ Integer a = new Integer(0), b = new Integer(1), c = new Integer(2);
+ t.add(a);
+ t.add(b);
+ t.add(c);
+ assertEquals(3, t.size());
+ t.remove(a);
+ assertEquals(2, t.size());
+ t.remove(c);
+ assertEquals(1, t.size());
+ t.remove(c);
+ assertEquals(1, t.size());
+ t.remove(b);
+ assertEquals(0, t.size());
+ t.add(b);
+ assertEquals(1, t.size());
+ t.add(b);
+ assertEquals(1, t.size());
+ t.add(a);
+ assertEquals(2, t.size());
+ t.add(a);
+ assertEquals(2, t.size());
+ t.add(c);
+ assertEquals(3, t.size());
+ t.add(c);
+ assertEquals(3, t.size());
+ }
+
+ @Test
+ public void testIterator() {
+ final int targetSize = 100;
+ final ArraySet<Integer> t = new ArraySet<>(0);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> remove = buildSubSet(targetSize, t, instances);
+ int traversed = 0;
+ for (Iterator<Integer> i = t.iterator(); i.hasNext();) {
+ Integer n = i.next();
+ if (remove.contains(n)) {
+ i.remove();
+ }
+ ++traversed;
+ }
+ assertEquals(targetSize, traversed);
+ assertEquals(targetSize / 2, t.size());
+ for (int i = 0; i < instances.length; ++i) {
+ Integer n = instances[i];
+ if (remove.contains(n)) {
+ assertFalse(t.contains(n));
+ } else {
+ assertTrue(t.contains(n));
+ }
+ }
+ }
+}
+
diff --git a/vespajlib/src/test/java/com/yahoo/collections/BobHashTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/BobHashTestCase.java
new file mode 100644
index 00000000000..820adffcfc5
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/BobHashTestCase.java
@@ -0,0 +1,45 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+
+import com.yahoo.collections.BobHash;
+
+
+/**
+ * Basic consistency check of BobHash implementation
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class BobHashTestCase extends junit.framework.TestCase {
+
+ public BobHashTestCase(String name) {
+ super(name);
+ }
+
+ public void testit() {
+ // Teststring: minprice
+ // Basic ASCII string
+ byte[] minprice = { 109, 105, 110, 112, 114, 105, 99, 101 };
+
+ assertEquals(BobHash.hash(minprice, 0), 0x90188543);
+ // Teststring: a\u00FFa\u00FF
+ // String with non-ASCII characters
+ byte[] ayay = { 97, -1, 97, -1 };
+
+ assertEquals(BobHash.hash(ayay, 0), 0x1C798331);
+ // lots of a's to ensure testing unsigned type emulation
+ byte[] aa = {
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97 };
+
+ assertEquals(BobHash.hash(aa, 0), 0xE09ED5E9);
+ // A string which caused problems during developmen of another
+ // feature
+ byte[] lastnamefirstinitial = {
+ 0x6c, 0x61, 0x73, 0x74, 0x6e, 0x61, 0x6d,
+ 0x65, 0x66, 0x69, 0x72, 0x73, 0x74, 0x69, 0x6e, 0x69, 0x74, 0x69,
+ 0x61, 0x6c };
+
+ assertEquals(BobHash.hash(lastnamefirstinitial, 0), 0xF36B4BD3);
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/ByteArrayComparatorTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/ByteArrayComparatorTestCase.java
new file mode 100644
index 00000000000..68b5812dd1e
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/ByteArrayComparatorTestCase.java
@@ -0,0 +1,38 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class ByteArrayComparatorTestCase {
+ @Test
+ public void arrayLength() {
+ byte[] shortArr = new byte[]{(byte) 1, (byte) 2};
+ byte[] longArr = new byte[]{(byte) 0, (byte) 3, (byte) 3, (byte) 3, (byte) 3, (byte) 3};
+
+ assertEquals(-1, ByteArrayComparator.compare(shortArr, longArr));
+ }
+
+ @Test
+ public void compareArrays() {
+ byte[] one = new byte[]{(byte) 1, (byte) 2, (byte) 3, (byte) 3, (byte) 3, (byte) 3};
+ byte[] two = new byte[]{(byte) 0, (byte) 3, (byte) 3, (byte) 3, (byte) 3, (byte) 3};
+
+ assertEquals(1, ByteArrayComparator.compare(one, two));
+ assertEquals(-1, ByteArrayComparator.compare(two, one));
+ }
+
+ @Test
+ public void compareEqualArrays() {
+ byte[] one = new byte[]{(byte) 1, (byte) 2, (byte) 3, (byte) 3, (byte) 3, (byte) 3, (byte) 9};
+ byte[] two = new byte[]{(byte) 1, (byte) 2, (byte) 3, (byte) 3, (byte) 3, (byte) 3, (byte) 9};
+
+ assertEquals(0, ByteArrayComparator.compare(one, two));
+ assertEquals(0, ByteArrayComparator.compare(two, one));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/CollectionComparatorTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/CollectionComparatorTestCase.java
new file mode 100644
index 00000000000..d77636b907f
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/CollectionComparatorTestCase.java
@@ -0,0 +1,40 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class CollectionComparatorTestCase {
+ @Test
+ public void arrayLength() {
+ List<String> shortArr = Arrays.asList("x", "y");
+ List<String> longArr = Arrays.asList("a", "b", "c", "d", "e");
+
+ assertEquals(-1, CollectionComparator.compare(shortArr, longArr));
+ }
+
+ @Test
+ public void compareArrays() {
+ List<String> one = Arrays.asList("b", "c", "d", "d", "e");
+ List<String> two = Arrays.asList("a", "b", "c", "d", "e");
+
+ assertEquals(1, CollectionComparator.compare(one, two));
+ assertEquals(-1, CollectionComparator.compare(two, one));
+ }
+
+ @Test
+ public void compareEqualArrays() {
+ List<String> one = Arrays.asList("a", "b", "c", "d", "e");
+ List<String> two = Arrays.asList("a", "b", "c", "d", "e");
+
+ assertEquals(0, CollectionComparator.compare(one, two));
+ assertEquals(0, CollectionComparator.compare(two, one));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/CollectionUtilTest.java b/vespajlib/src/test/java/com/yahoo/collections/CollectionUtilTest.java
new file mode 100644
index 00000000000..c5a20a4684c
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/CollectionUtilTest.java
@@ -0,0 +1,50 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author tonytv
+ */
+public class CollectionUtilTest {
+ List<Integer> l1 = Arrays.asList(1, 2, 4, 5, 6, 7);
+ List<Integer> l2 = Arrays.asList(3, 4, 5, 6, 7);
+
+ @Before
+ public void shuffle() {
+ Collections.shuffle(l1);
+ Collections.shuffle(l2);
+ }
+
+ @Test
+ public void testMkString() {
+ assertEquals("1, 2, 3, 4",
+ CollectionUtil.mkString(Arrays.asList(1, 2, 3, 4), ", "));
+ }
+
+ @Test
+ public void testEqualContentsIgnoreOrder() {
+ List<Integer> l2Copy = new ArrayList<>();
+ l2Copy.addAll(l2);
+ shuffle();
+ assertTrue(CollectionUtil.equalContentsIgnoreOrder(
+ l2, l2Copy));
+ assertFalse(CollectionUtil.equalContentsIgnoreOrder(
+ l1, l2));
+ }
+
+ @Test
+ public void testSymmetricDifference() {
+ assertTrue(CollectionUtil.equalContentsIgnoreOrder(
+ Arrays.asList(1, 2, 3),
+ CollectionUtil.symmetricDifference(l1, l2)));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/CollectionsBenchMark.java b/vespajlib/src/test/java/com/yahoo/collections/CollectionsBenchMark.java
new file mode 100644
index 00000000000..51cdd11bb7d
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/CollectionsBenchMark.java
@@ -0,0 +1,204 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Created by balder on 1/20/14.
+ */
+public class CollectionsBenchMark {
+ abstract static class BenchMark {
+ protected BenchMark(int numWarmup, int repetitions) {
+ this.numWarmup = numWarmup;
+ this.repetitions = repetitions;
+ }
+ abstract void runOnce();
+ abstract String getEndComment();
+ protected void run() {
+ System.out.println("Starting benchmark warmup '" + getClass().getName() + "'.");
+ for (int i=0; i < numWarmup; i++) {
+ runOnce();
+ }
+ System.out.println("Starting benchmark '" + getClass().getName() + "'.");
+ long startTime=System.currentTimeMillis();
+ for (int i=0; i < repetitions; i++) {
+ runOnce();
+ }
+ long endTime=System.currentTimeMillis();
+ long totalTime=(endTime-startTime);
+ System.out.println("Done in " + totalTime + " ms (" + ((float) totalTime * 1000 / repetitions + " microsecond per repetition.)")); // *2 because we do 2 gets
+ System.out.println("Final remark: " + getEndComment());
+ }
+
+
+ final private int repetitions;
+ final private int numWarmup;
+ }
+
+ static class MapFilterBenchMark extends BenchMark {
+ MapFilterBenchMark(Map<Integer, Integer> s, int numWarmup, int numRepetitions, int numObjects) {
+ super(numWarmup, numRepetitions);
+ this.s = s;
+ objects = new Integer[numObjects];
+ for (int i=0; i < numObjects; i++) {
+ objects[i] = i;
+ }
+ }
+ void runOnce() {
+ for (Integer o : objects) {
+ if (s.put(o, o) == null) {
+ uniqueCount += o;
+ }
+ }
+ }
+ String getEndComment() { return " Unique sum is '" + uniqueCount + "'"; }
+ private final Map<Integer,Integer> s;
+ final Integer [] objects;
+ long uniqueCount = 0;
+ }
+
+ static class SetFilterBenchMark extends BenchMark {
+ SetFilterBenchMark(Set<Integer> s, int numWarmup, int numRepetitions, int numObjects) {
+ super(numWarmup, numRepetitions);
+ this.s = s;
+ objects = new Integer[numObjects];
+ for (int i=0; i < numObjects; i++) {
+ objects[i] = i;
+ }
+ }
+ void runOnce() {
+ for (Integer o : objects) {
+ if ( s.add(o) ) {
+ uniqueCount += o;
+ }
+ }
+ }
+ String getEndComment() { return " Unique sum is '" + uniqueCount + "'"; }
+ private final Set<Integer> s;
+ final Integer [] objects;
+ long uniqueCount = 0;
+ }
+
+ static abstract class SmallMapsBenchMark extends BenchMark {
+ SmallMapsBenchMark(int numWarmup, int numRepetitions, int numObjects, int numUnique) {
+ super(numWarmup, numRepetitions);
+ objects = new Integer[numObjects];
+ for (int i=0; i < numObjects; i++) {
+ objects[i] = i%numUnique;
+ }
+ }
+ void runOnce() {
+ Set<Integer> s = createSet();
+ for (Integer o : objects) {
+ if ( s.add(o) ) {
+ uniqueCount += o;
+ }
+ }
+ }
+ abstract Set<Integer> createSet();
+ String getEndComment() { return " Unique sum is '" + uniqueCount + "'"; }
+ final Integer [] objects;
+ long uniqueCount = 0;
+ }
+
+ static class SmallHashSetBenchMark extends SmallMapsBenchMark
+ {
+ SmallHashSetBenchMark(int numWarmup, int numRepetitions, int numObjects, int numUnique) {
+ super(numWarmup, numRepetitions, numObjects, numUnique);
+ }
+ Set<Integer> createSet() { return new HashSet<Integer>();}
+ }
+
+ static class SmallLazySetBenchMark extends SmallMapsBenchMark
+ {
+ SmallLazySetBenchMark(int numWarmup, int numRepetitions, int numObjects, int numUnique) {
+ super(numWarmup, numRepetitions, numObjects, numUnique);
+ }
+ Set<Integer> createSet() { return new LazySet<Integer>() {
+ @Override
+ protected Set<Integer> newDelegate() {
+ return new HashSet<Integer>();
+ }
+ };
+ }
+ }
+
+ static class SmallLazyTinyBenchMark extends SmallMapsBenchMark
+ {
+ SmallLazyTinyBenchMark(int numWarmup, int numRepetitions, int numObjects, int numUnique) {
+ super(numWarmup, numRepetitions, numObjects, numUnique);
+ }
+ Set<Integer> createSet() { return new LazySet<Integer>() {
+ @Override
+ protected Set<Integer> newDelegate() {
+ return new TinyIdentitySet<Integer>(10);
+ }
+ };
+ }
+ }
+
+ static void benchMarkAll() {
+
+ new MapFilterBenchMark(new HashMap<Integer, Integer>(), 100000, 10000000, 10).run();
+ new MapFilterBenchMark(new IdentityHashMap<Integer, Integer>(10), 100000, 10000000, 10).run();
+ new SetFilterBenchMark(new HashSet<Integer>(), 100000, 10000000, 10).run();
+ new SetFilterBenchMark(new TinyIdentitySet<Integer>(10), 100000, 10000000, 10).run();
+ new SetFilterBenchMark(new TinyIdentitySet<Integer>(10), 100000, 10000000, 15).run();
+ new SetFilterBenchMark(new TinyIdentitySet<Integer>(10), 100000, 10000000, 20).run();
+ new SetFilterBenchMark(new TinyIdentitySet<Integer>(20), 100000, 10000000, 20).run();
+ new SmallHashSetBenchMark(100000, 10000000, 10, 1).run();
+ new SmallLazySetBenchMark(100000, 10000000, 10, 1).run();
+ new SmallHashSetBenchMark(100000, 10000000, 10, 2).run();
+ new SmallLazySetBenchMark(100000, 10000000, 10, 2).run();
+ new SmallLazyTinyBenchMark(100000, 10000000, 10, 2).run();
+ new SmallHashSetBenchMark(100000, 10000000, 10, 10).run();
+ new SmallLazySetBenchMark(100000, 10000000, 10, 10).run();
+
+ new SetFilterBenchMark(new HashSet<Integer>(), 100000, 10000000, 12).run();
+
+ new SetFilterBenchMark(new HashSet<Integer>(), 100000, 10000000, 20).run();
+ new SetFilterBenchMark(new HashSet<Integer>(), 100000, 10000000, 25).run();
+ new SetFilterBenchMark(new HashSet<Integer>(), 100000, 10000000, 30).run();
+ new SmallHashSetBenchMark(100000, 10000000, 1, 1).run();
+
+ }
+
+ static void benchMark() {
+
+ //new MapFilterBenchMark(new HashMap<Integer, Integer>(), 100000, 10000000, 10).run();
+ //new MapFilterBenchMark(new IdentityHashMap<Integer, Integer>(10), 100000, 10000000, 10).run();
+ //new SetFilterBenchMark(new HashSet<Integer>(), 100000, 10000000, 10).run();
+ //new SetFilterBenchMark(new TinyIdentitySet<Integer>(10), 100000, 10000000, 10).run();
+ //new SmallHashSetBenchMark(100000, 10000000, 10, 1).run();
+ //new SmallLazySetBenchMark(100000, 10000000, 10, 1).run();
+ //new SmallHashSetBenchMark(100000, 10000000, 10, 2).run();
+ //new SmallLazySetBenchMark(100000, 10000000, 10, 2).run();
+ new SmallLazyTinyBenchMark(100000, 10000000, 10, 2).run();
+ //new SmallHashSetBenchMark(100000, 10000000, 10, 10).run();
+ //new SmallLazySetBenchMark(100000, 10000000, 10, 10).run();
+
+ //new SetFilterBenchMark(new HashSet<Integer>(), 100000, 10000000, 12).run();
+
+ //new SetFilterBenchMark(new HashSet<Integer>(), 100000, 10000000, 20).run();
+ //new SetFilterBenchMark(new HashSet<Integer>(), 100000, 10000000, 25).run();
+ //new SetFilterBenchMark(new HashSet<Integer>(), 100000, 10000000, 30).run();
+ //new SmallHashSetBenchMark(100000, 10000000, 1, 1).run();
+
+ }
+
+
+ static public void main(String argv[]) {
+ benchMarkAll();
+ ExecutorService tp = Executors.newFixedThreadPool(16);
+
+ for (int i=0; i < 16; i++) {
+ tp.execute(new Runnable() {
+ @Override
+ public void run() {
+ benchMark();
+ }
+ });
+ }
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/CopyOnWriteHashMapTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/CopyOnWriteHashMapTestCase.java
new file mode 100644
index 00000000000..4370a9b46b0
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/CopyOnWriteHashMapTestCase.java
@@ -0,0 +1,64 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class CopyOnWriteHashMapTestCase {
+
+ @Test
+ public void testModifySourceFirst() {
+ CopyOnWriteHashMap<String, String> map = new CopyOnWriteHashMap<>();
+ map.put("a", "a1");
+ map.put("b", "b1");
+ CopyOnWriteHashMap<String,String> clone = map.clone();
+ map.put("c", "c1");
+ clone.remove("a");
+ clone.put("b", "b2");
+ clone.put("d", "d2");
+
+ assertEquals(3, map.size());
+ assertEquals("a1", map.get("a"));
+ assertEquals("b1", map.get("b"));
+ assertEquals("c1", map.get("c"));
+
+ assertEquals(2, clone.size());
+ assertEquals("b2", clone.get("b"));
+ assertEquals("d2", clone.get("d"));
+ }
+
+ @Test
+ public void testModifyTargetFirst() {
+ CopyOnWriteHashMap<String, String> map = new CopyOnWriteHashMap<>();
+ map.put("a", "a1");
+ map.put("b", "b1");
+ CopyOnWriteHashMap<String,String> clone = map.clone();
+ clone.remove("a");
+ map.put("c", "c1");
+ clone.put("b", "b2");
+ clone.put("d", "d2");
+
+ assertEquals(3, map.size());
+ assertEquals("a1", map.get("a"));
+ assertEquals("b1", map.get("b"));
+ assertEquals("c1", map.get("c"));
+
+ assertEquals(2, clone.size());
+ assertEquals("b2", clone.get("b"));
+ assertEquals("d2", clone.get("d"));
+ }
+
+ @Test
+ public void testCallEntrySetThenModify() {
+ CopyOnWriteHashMap<String, String> map = new CopyOnWriteHashMap<>();
+ map.put("a", "a1");
+ map.entrySet();
+ CopyOnWriteHashMap<String,String> clone = map.clone();
+ clone.put("b", "b1");
+ assertEquals(2, clone.size());
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/FreezableArrayListListener.java b/vespajlib/src/test/java/com/yahoo/collections/FreezableArrayListListener.java
new file mode 100644
index 00000000000..762ae9d5b60
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/FreezableArrayListListener.java
@@ -0,0 +1,74 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class FreezableArrayListListener {
+
+ @Test
+ public void testPermitAdd() {
+ FreezableArrayList<String> l = new FreezableArrayList<>(true);
+ l.add("1");
+ l.add("2");
+ l.remove(1);
+ l.freeze();
+ try {
+ l.remove(0);
+ fail("Expected exception");
+ }
+ catch (UnsupportedOperationException expected) {
+ }
+ try {
+ l.set(0, "2");
+ fail("Expected exception");
+ }
+ catch (UnsupportedOperationException expected) {
+ }
+ try {
+ l.add(0, "2");
+ fail("Expected exception");
+ }
+ catch (UnsupportedOperationException expected) {
+ }
+
+ l.add("2");
+ }
+
+ @Test
+ public void testDontPermitAdd() {
+ FreezableArrayList<String> l = new FreezableArrayList<>();
+ l.add("1");
+ l.add("2");
+ l.remove(1);
+ l.freeze();
+ try {
+ l.remove(0);
+ fail("Expected exception");
+ }
+ catch (UnsupportedOperationException expected) {
+ }
+ try {
+ l.set(0, "2");
+ fail("Expected exception");
+ }
+ catch (UnsupportedOperationException expected) {
+ }
+ try {
+ l.add(0, "2");
+ fail("Expected exception");
+ }
+ catch (UnsupportedOperationException expected) {
+ }
+ try {
+ l.add("2");
+ fail("Expected exception");
+ }
+ catch (UnsupportedOperationException expected) {
+ }
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/HashletTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/HashletTestCase.java
new file mode 100644
index 00000000000..1a198726994
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/HashletTestCase.java
@@ -0,0 +1,186 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+public class HashletTestCase {
+
+ @Test
+ public void testCopyEmptyHashlet() {
+ Hashlet<String, Integer> hash = new Hashlet<>();
+ Hashlet<String, Integer> hash2 = new Hashlet<>(hash);
+ assertThat(hash.size(), is(0));
+ assertThat(hash2.size(), is(0));
+ hash.put("foo", 5);
+ hash2.put("bar", 7);
+ assertThat(hash.get("foo"), is(5));
+ assertThat(hash.get("bar"), nullValue());
+ assertThat(hash2.get("foo"), nullValue());
+ assertThat(hash2.get("bar"), is(7));
+ }
+
+ private void verifyEquals(Object a, Object b) {
+ assertEquals(a, b);
+ assertEquals(b, a);
+ }
+ private void verifyNotEquals(Object a, Object b) {
+ assertNotEquals(a, b);
+ assertNotEquals(b, a);
+ }
+
+ @Test
+ public void testThatDifferentGenericsDoesNotEqual() {
+ Hashlet<Long, Long> a = new Hashlet<>();
+ Hashlet<String, Integer> b = new Hashlet<>();
+ verifyEquals(a, b);
+ b.put("a", 1);
+ verifyNotEquals(a, b);
+ a.put(1L, 1L);
+ verifyNotEquals(a, b);
+ }
+ @Test
+ public void testHashCodeAndEquals() {
+ Hashlet<String, Integer> h1 = new Hashlet<>();
+ Hashlet<String, Integer> h2 = new Hashlet<>();
+ assertEquals(h1.hashCode(), h2.hashCode());
+ verifyEquals(h1, h2);
+
+ h1.put("a", 7);
+ assertNotEquals(h1.hashCode(), h2.hashCode());
+ verifyNotEquals(h1, h2);
+
+ h2.put("b", 8);
+ assertNotEquals(h1.hashCode(), h2.hashCode());
+ verifyNotEquals(h1, h2);
+
+ h2.put("a", 7);
+ assertNotEquals(h1.hashCode(), h2.hashCode());
+ verifyNotEquals(h1, h2);
+
+ h1.put("b", 8);
+ assertEquals(h1.hashCode(), h2.hashCode());
+ verifyEquals(h1, h2);
+
+ h1.put("c", null);
+ assertNotEquals(h1.hashCode(), h2.hashCode());
+ verifyNotEquals(h1, h2);
+
+ h2.put("d", null);
+ assertNotEquals(h1.hashCode(), h2.hashCode());
+ verifyNotEquals(h1, h2);
+
+ h2.put("c", null);
+ assertNotEquals(h1.hashCode(), h2.hashCode());
+ verifyNotEquals(h1, h2);
+
+ h1.put("d", null);
+ assertEquals(h1.hashCode(), h2.hashCode());
+ verifyEquals(h1, h2);
+ }
+
+ @Test
+ public void testSetValue() {
+ String A = "a";
+ Hashlet<String, Integer> h = new Hashlet<>();
+ h.put(A, 1);
+ int indexOfA = h.getIndexOfKey(A);
+ assertEquals(new Integer(1), h.value(indexOfA));
+ h.setValue(indexOfA, 2);
+ assertEquals(new Integer(2), h.value(indexOfA));
+ assertEquals(new Integer(2), h.get(A));
+ }
+
+ @Test
+ public void testGet() {
+ Hashlet<String, Integer> h = new Hashlet<>();
+ h.put("a", 1);
+ h.put("b", null);
+ assertEquals(0, h.getIndexOfKey("a"));
+ assertEquals(h.get("a"), h.value(h.getIndexOfKey("a")));
+ assertEquals(1, h.getIndexOfKey("b"));
+ assertEquals(h.get("b"), h.value(h.getIndexOfKey("b")));
+ assertEquals(-1, h.getIndexOfKey("c"));
+ assertNull(h.get("c"));
+ }
+
+ @Test
+ public void testCopyNonEmptyHashlet() {
+ Hashlet<String, Integer> hash = new Hashlet<>();
+ hash.put("foo", 5);
+ hash.put("bar", 7);
+ Hashlet<String, Integer> hash2 = new Hashlet<>(hash);
+ assertThat(hash2.size(), is(2));
+ assertThat(hash2.get("foo"), is(5));
+ assertThat(hash2.get("bar"), is(7));
+ assertThat(hash2.key(0), is("foo"));
+ assertThat(hash2.key(1), is("bar"));
+ assertThat(hash2.value(0), is(5));
+ assertThat(hash2.value(1), is(7));
+ assertThat(hash2.key(0), sameInstance(hash.key(0)));
+ assertThat(hash2.key(1), sameInstance(hash.key(1)));
+ assertThat(hash2.value(0), sameInstance(hash.value(0)));
+ assertThat(hash2.value(1), sameInstance(hash.value(1)));
+ }
+
+ @Test
+ public void testSetValueToNull() {
+ Hashlet<String, Integer> hash = new Hashlet<>();
+ hash.put("foo", 5);
+ hash.put("bar", 7);
+ assertThat(hash.size(), is(2));
+ assertThat(hash.get("foo"), is(5));
+ assertThat(hash.get("bar"), is(7));
+ assertThat(hash.key(0), is("foo"));
+ assertThat(hash.key(1), is("bar"));
+ assertThat(hash.value(0), is(5));
+ assertThat(hash.value(1), is(7));
+ hash.put("foo", null);
+ assertThat(hash.size(), is(2));
+ assertThat(hash.get("foo"), nullValue());
+ assertThat(hash.get("bar"), is(7));
+ assertThat(hash.key(0), is("foo"));
+ assertThat(hash.key(1), is("bar"));
+ assertThat(hash.value(0), nullValue());
+ assertThat(hash.value(1), is(7));
+ }
+
+ @Test
+ public void testIterate() {
+ int n = 100;
+ Hashlet<String, Integer> hash = new Hashlet<>();
+ for (int i = 0; i < n; i++) {
+ String str = ("" + i + "_str_" + i);
+ hash.put(str, i);
+ }
+ assertThat(hash.size(), is(n));
+ for (int i = 0; i < n; i++) {
+ String str = ("" + i + "_str_" + i);
+ assertThat(hash.key(i), is(str));
+ assertThat(hash.value(i), is(i));
+ }
+ }
+
+ @Test
+ public void testManyEntries() {
+ int n = 5000;
+ Hashlet<String, Integer> hash = new Hashlet<>();
+ for (int i = 0; i < n; i++) {
+ String str = ("" + i + "_str_" + i);
+ assertThat(hash.get(str), nullValue());
+ switch (i % 2) {
+ case 1: assertThat(hash.put(str, new Integer(i)), nullValue());
+ }
+ }
+ assertThat(hash.size(), is(n / 2));
+ for (int i = 0; i < n; i++) {
+ String str = ("" + i + "_str_" + i);
+ switch (i % 2) {
+ case 0: assertThat(hash.get(str), nullValue()); break;
+ case 1: assertThat(hash.get(str), is(new Integer(i))); break;
+ }
+ }
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/IntArrayComparatorTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/IntArrayComparatorTestCase.java
new file mode 100644
index 00000000000..c4808a6605c
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/IntArrayComparatorTestCase.java
@@ -0,0 +1,37 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class IntArrayComparatorTestCase {
+ @Test
+ public void arrayLength() {
+ int[] shortArr = new int[]{1, 2};
+ int[] longArr = new int[]{0, 3, 3, 3, 3, 3};
+
+ assertEquals(-1, IntArrayComparator.compare(shortArr, longArr));
+ }
+
+ @Test
+ public void compareArrays() {
+ int[] one = new int[]{1, 2, 3, 3, 3, 3};
+ int[] two = new int[]{0, 3, 3, 3, 3, 3};
+
+ assertEquals(1, IntArrayComparator.compare(one, two));
+ assertEquals(-1, IntArrayComparator.compare(two, one));
+ }
+
+ @Test
+ public void compareEqualArrays() {
+ int[] one = new int[]{1, 2, 3, 3, 3, 3, 9};
+ int[] two = new int[]{1, 2, 3, 3, 3, 3, 9};
+
+ assertEquals(0, IntArrayComparator.compare(one, two));
+ assertEquals(0, IntArrayComparator.compare(two, one));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/LazyMapTest.java b/vespajlib/src/test/java/com/yahoo/collections/LazyMapTest.java
new file mode 100644
index 00000000000..2890d73ebaf
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/LazyMapTest.java
@@ -0,0 +1,285 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class LazyMapTest {
+
+ @Test
+ public void requireThatInitialDelegateIsEmpty() {
+ LazyMap<String, String> map = newLazyMap(new HashMap<String, String>());
+ assertEquals(LazyMap.EmptyMap.class, map.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatEmptyMapPutUpgradesToSingletonMap() {
+ LazyMap<String, String> map = newLazyMap(new HashMap<String, String>());
+ assertNull(map.put("foo", "bar"));
+ assertEquals(LazyMap.SingletonMap.class, map.getDelegate().getClass());
+
+ map = newLazyMap(new HashMap<String, String>());
+ map.putAll(Collections.singletonMap("foo", "bar"));
+ assertEquals(LazyMap.SingletonMap.class, map.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatEmptyMapPutAllEmptyMapDoesNotUpgradeToSingletonMap() {
+ LazyMap<String, String> map = newLazyMap(new HashMap<String, String>());
+ map.putAll(Collections.<String, String>emptyMap());
+ assertEquals(LazyMap.EmptyMap.class, map.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatEmptyMapPutAllUpgradesToFinalMap() {
+ Map<String, String> delegate = new HashMap<>();
+ LazyMap<String, String> map = newLazyMap(delegate);
+ map.putAll(new HashMapBuilder<String, String>()
+ .put("foo", "bar")
+ .put("baz", "cox").map);
+ assertSame(delegate, map.getDelegate());
+ assertEquals(2, delegate.size());
+ assertEquals("bar", delegate.get("foo"));
+ assertEquals("cox", delegate.get("baz"));
+ }
+
+ @Test
+ public void requireThatSingletonMapRemoveEntryDowngradesToEmptyMap() {
+ LazyMap<String, String> map = newSingletonMap("foo", "bar");
+ assertEquals("bar", map.remove("foo"));
+ assertEquals(LazyMap.EmptyMap.class, map.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatSingletonMapRemoveUnknownDoesNotDowngradesToEmptyMap() {
+ LazyMap<String, String> map = newSingletonMap("foo", "bar");
+ assertNull(map.remove("baz"));
+ assertEquals(LazyMap.SingletonMap.class, map.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatSingletonMapValueMayBeChangedInPlace() {
+ LazyMap<String, String> map = newSingletonMap("foo", "bar");
+ Map<String, String> delegate = map.getDelegate();
+ assertEquals("bar", map.put("foo", "baz"));
+ assertEquals("baz", map.get("foo"));
+ assertSame(delegate, map.getDelegate());
+ map.putAll(Collections.singletonMap("foo", "cox"));
+ assertSame(delegate, map.getDelegate());
+ assertEquals("cox", map.get("foo"));
+ }
+
+ @Test
+ public void requireThatSingletonMapPutAllEmptyMapDoesNotUpgradeToFinalMap() {
+ LazyMap<String, String> map = newSingletonMap("foo", "bar");
+ map.putAll(Collections.<String, String>emptyMap());
+ assertEquals(LazyMap.SingletonMap.class, map.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatSingletonMapPutUpgradesToFinalMap() {
+ Map<String, String> delegate = new HashMap<>();
+ LazyMap<String, String> map = newSingletonMap(delegate, "fooKey", "fooVal");
+ map.put("barKey", "barVal");
+ assertSame(delegate, map.getDelegate());
+ assertEquals(2, delegate.size());
+ assertEquals("fooVal", delegate.get("fooKey"));
+ assertEquals("barVal", delegate.get("barKey"));
+ }
+
+ @Test
+ public void requireThatSingletonMapPutAllUpgradesToFinalMap() {
+ Map<String, String> delegate = new HashMap<>();
+ LazyMap<String, String> map = newSingletonMap(delegate, "fooKey", "fooVal");
+ map.putAll(new HashMapBuilder<String, String>()
+ .put("barKey", "barVal")
+ .put("bazKey", "bazVal").map);
+ assertSame(delegate, map.getDelegate());
+ assertEquals(3, delegate.size());
+ assertEquals("fooVal", delegate.get("fooKey"));
+ assertEquals("barVal", delegate.get("barKey"));
+ assertEquals("bazVal", delegate.get("bazKey"));
+ }
+
+ @Test
+ public void requireThatSingletonEntryIsMutable() {
+ LazyMap<String, String> map = newSingletonMap("foo", "bar");
+ Map.Entry<String, String> entry = map.entrySet().iterator().next();
+ entry.setValue("baz");
+ assertEquals("baz", map.get("foo"));
+ }
+
+ @Test
+ public void requireThatSingletonEntryImplementsHashCode() {
+ assertEquals(newSingletonMap("foo", "bar").entrySet().iterator().next().hashCode(),
+ newSingletonMap("foo", "bar").entrySet().iterator().next().hashCode());
+ }
+
+ @Test
+ public void requireThatSingletonEntryImplementsEquals() {
+ Map.Entry<String, String> map = newSingletonMap("foo", "bar").entrySet().iterator().next();
+ assertNotEquals(map, null);
+ assertNotEquals(map, new Object());
+ assertEquals(map, map);
+ assertNotEquals(map, newSingletonMap("baz", "cox").entrySet().iterator().next());
+ assertNotEquals(map, newSingletonMap("foo", "cox").entrySet().iterator().next());
+ assertEquals(map, newSingletonMap("foo", "bar").entrySet().iterator().next());
+ }
+
+ @Test
+ public void requireThatSingletonEntrySetIteratorNextThrowsIfInvokedMoreThanOnce() {
+ LazyMap<String, String> map = newSingletonMap("foo", "bar");
+ Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
+ it.next();
+ try {
+ it.next();
+ fail();
+ } catch (NoSuchElementException e) {
+
+ }
+ try {
+ it.next();
+ fail();
+ } catch (NoSuchElementException e) {
+
+ }
+ }
+
+ @Test
+ public void requireThatSingletonEntrySetIteratorRemoveThrowsIfInvokedBeforeNext() {
+ LazyMap<String, String> map = newSingletonMap("foo", "bar");
+ Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
+ try {
+ it.remove();
+ fail();
+ } catch (IllegalStateException e) {
+
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Map<String, String> makeMockMap() {
+ return Mockito.mock(Map.class);
+ }
+
+ @Test
+ public void requireThatMapDelegates() {
+ Map<String, String> delegate = makeMockMap();
+ Map<String, String> map = newLazyMap(delegate);
+ map.put("foo", "bar");
+ map.put("baz", "cox"); // trigger the assignment of the delegate
+ Mockito.verify(delegate).put("foo", "bar");
+ Mockito.verify(delegate).put("baz", "cox");
+
+ Map<String, String> arg = Collections.singletonMap("baz", "cox");
+ map.putAll(arg);
+ Mockito.verify(delegate).putAll(arg);
+
+ assertEquals(0, map.size());
+ Mockito.verify(delegate).size();
+
+ assertFalse(map.isEmpty());
+ Mockito.verify(delegate).isEmpty();
+
+ assertFalse(map.containsKey("foo"));
+ Mockito.verify(delegate).containsKey("foo");
+
+ assertFalse(map.containsValue("bar"));
+ Mockito.verify(delegate).containsValue("bar");
+
+ assertNull(map.get("foo"));
+ Mockito.verify(delegate).get("foo");
+
+ assertNull(map.remove("foo"));
+ Mockito.verify(delegate).remove("foo");
+
+ map.clear();
+ Mockito.verify(delegate).clear();
+
+ assertTrue(map.keySet().isEmpty());
+ Mockito.verify(delegate).keySet();
+
+ assertTrue(map.values().isEmpty());
+ Mockito.verify(delegate).values();
+
+ assertTrue(map.entrySet().isEmpty());
+ Mockito.verify(delegate).entrySet();
+ }
+
+ @Test
+ public void requireThatHashCodeIsImplemented() {
+ assertEquals(newLazyMap(null).hashCode(),
+ newLazyMap(null).hashCode());
+ }
+
+ @Test
+ public void requireThatEqualsIsImplemented() {
+ Map<Object, Object> lhs = newLazyMap(new HashMap<>());
+ Map<Object, Object> rhs = newLazyMap(new HashMap<>());
+ assertEquals(lhs, lhs);
+ assertEquals(lhs, rhs);
+
+ Object key = new Object();
+ Object val = new Object();
+ lhs.put(key, val);
+ assertEquals(lhs, lhs);
+ assertFalse(lhs.equals(rhs));
+ rhs.put(key, val);
+ assertEquals(lhs, rhs);
+ }
+
+ @Test
+ public void requireThatHashMapFactoryDelegatesToAHashMap() {
+ LazyMap<String, String> map = LazyMap.newHashMap();
+ map.put("foo", "bar");
+ map.put("baz", "cox");
+ assertEquals(HashMap.class, map.getDelegate().getClass());
+ }
+
+ private static <K, V> LazyMap<K, V> newSingletonMap(K key, V value) {
+ return newSingletonMap(new HashMap<K, V>(), key, value);
+ }
+
+ private static <K, V> LazyMap<K, V> newSingletonMap(Map<K, V> delegate, K key, V value) {
+ LazyMap<K, V> map = newLazyMap(delegate);
+ map.put(key, value);
+ return map;
+ }
+
+ private static <K, V> LazyMap<K, V> newLazyMap(final Map<K, V> delegate) {
+ return new LazyMap<K, V>() {
+
+ @Override
+ protected Map<K, V> newDelegate() {
+ return delegate;
+ }
+ };
+ }
+
+ private static class HashMapBuilder<K, V> {
+
+ final Map<K, V> map = new HashMap<>();
+
+ public HashMapBuilder<K, V> put(K key, V value) {
+ map.put(key, value);
+ return this;
+ }
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/LazySetTest.java b/vespajlib/src/test/java/com/yahoo/collections/LazySetTest.java
new file mode 100644
index 00000000000..d71e7ca6e26
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/LazySetTest.java
@@ -0,0 +1,265 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class LazySetTest {
+
+ @Test
+ public void requireThatInitialDelegateIsEmpty() {
+ LazySet<String> set = newLazySet(new HashSet<String>());
+ assertEquals(LazySet.EmptySet.class, set.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatEmptySetAddUpgradesToSingletonSet() {
+ LazySet<String> set = newLazySet(new HashSet<String>());
+ assertTrue(set.add("foo"));
+ assertEquals(LazySet.SingletonSet.class, set.getDelegate().getClass());
+
+ set = newLazySet(new HashSet<String>());
+ assertTrue(set.addAll(Arrays.asList("foo")));
+ assertEquals(LazySet.SingletonSet.class, set.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatEmptySetAddAllEmptySetDoesNotUpgradeToSingletonSet() {
+ LazySet<String> set = newLazySet(new HashSet<String>());
+ assertFalse(set.addAll(Collections.<String>emptySet()));
+ assertEquals(LazySet.EmptySet.class, set.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatEmptySetAddAllUpgradesToFinalSet() {
+ Set<String> delegate = new HashSet<>();
+ LazySet<String> set = newLazySet(delegate);
+ assertTrue(set.addAll(Arrays.asList("foo", "bar")));
+ assertSame(delegate, set.getDelegate());
+ assertEquals(2, delegate.size());
+ assertTrue(delegate.contains("foo"));
+ assertTrue(delegate.contains("bar"));
+ }
+
+ @Test
+ public void requireThatSingletonSetRemoveEntryDowngradesToEmptySet() {
+ LazySet<String> set = newSingletonSet("foo");
+ assertTrue(set.remove("foo"));
+ assertEquals(LazySet.EmptySet.class, set.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatSingletonSetRemoveUnknownDoesNotDowngradesToEmptySet() {
+ LazySet<String> set = newSingletonSet("foo");
+ assertFalse(set.remove("bar"));
+ assertEquals(LazySet.SingletonSet.class, set.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatSingletonSetAddAllEmptySetDoesNotUpgradeToFinalSet() {
+ LazySet<String> set = newSingletonSet("foo");
+ assertFalse(set.addAll(Collections.<String>emptySet()));
+ assertEquals(LazySet.SingletonSet.class, set.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatSingletonSetAddKnownDoesNotUpgradeToFinalSet() {
+ LazySet<String> set = newSingletonSet("foo");
+ assertFalse(set.add("foo"));
+ assertEquals(LazySet.SingletonSet.class, set.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatSingletonSetAddUpgradesToFinalSet() {
+ Set<String> delegate = new HashSet<>();
+ LazySet<String> set = newSingletonSet(delegate, "foo");
+ assertTrue(set.add("bar"));
+ assertSame(delegate, set.getDelegate());
+ assertEquals(2, delegate.size());
+ assertTrue(delegate.contains("foo"));
+ assertTrue(delegate.contains("bar"));
+ }
+
+ @Test
+ public void requireThatSingletonSetAddAllUpgradesToFinalSet() {
+ Set<String> delegate = new HashSet<>();
+ LazySet<String> set = newSingletonSet(delegate, "foo");
+ assertTrue(set.addAll(Arrays.asList("bar")));
+ assertSame(delegate, set.getDelegate());
+ assertEquals(2, delegate.size());
+ assertTrue(delegate.contains("foo"));
+ assertTrue(delegate.contains("bar"));
+
+ delegate = new HashSet<>();
+ set = newSingletonSet(delegate, "foo");
+ assertTrue(set.addAll(Arrays.asList("bar", "baz")));
+ assertSame(delegate, set.getDelegate());
+ assertEquals(3, delegate.size());
+ assertTrue(delegate.contains("foo"));
+ assertTrue(delegate.contains("bar"));
+ assertTrue(delegate.contains("baz"));
+ }
+
+ @Test
+ public void requireThatSingletonIteratorNextThrowsIfInvokedMoreThanOnce() {
+ LazySet<String> set = newSingletonSet("foo");
+ Iterator<String> it = set.iterator();
+ it.next();
+ try {
+ it.next();
+ fail();
+ } catch (NoSuchElementException e) {
+
+ }
+ try {
+ it.next();
+ fail();
+ } catch (NoSuchElementException e) {
+
+ }
+ }
+
+ @Test
+ public void requireThatSingletonIteratorRemoveDowngradesToEmptySet() {
+ LazySet<String> set = newSingletonSet("foo");
+ Iterator<String> it = set.iterator();
+ it.next();
+ it.remove();
+ assertEquals(LazySet.EmptySet.class, set.getDelegate().getClass());
+ }
+
+ @Test
+ public void requireThatSingletonIteratorRemoveThrowsIfInvokedBeforeNext() {
+ LazySet<String> set = newSingletonSet("foo");
+ Iterator<String> it = set.iterator();
+ try {
+ it.remove();
+ fail();
+ } catch (IllegalStateException e) {
+
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Set<String> makeMockSet() {
+ return Mockito.mock(Set.class);
+ }
+
+ @Test
+ public void requireThatSetDelegates() {
+ Set<String> delegate = makeMockSet();
+ Set<String> set = newLazySet(delegate);
+ set.add("foo");
+ set.add("bar"); // trigger the assignment of the delegate
+ Mockito.verify(delegate).add("foo");
+ Mockito.verify(delegate).add("bar");
+
+ Set<String> addAllArg = Collections.singleton("foo");
+ set.addAll(addAllArg);
+ Mockito.verify(delegate).addAll(addAllArg);
+
+ assertEquals(0, set.size());
+ Mockito.verify(delegate).size();
+
+ assertFalse(set.isEmpty());
+ Mockito.verify(delegate).isEmpty();
+
+ assertFalse(set.contains("foo"));
+ Mockito.verify(delegate).contains("foo");
+
+ assertNull(set.iterator());
+ Mockito.verify(delegate).iterator();
+
+ assertNull(set.toArray());
+ Mockito.verify(delegate).toArray();
+
+ String[] toArrayArg = new String[69];
+ assertNull(set.toArray(toArrayArg));
+ Mockito.verify(delegate).toArray(toArrayArg);
+
+ assertFalse(set.remove("foo"));
+ Mockito.verify(delegate).remove("foo");
+
+ Collection<String> containsAllArg = Collections.singletonList("foo");
+ assertFalse(set.containsAll(containsAllArg));
+ Mockito.verify(delegate).containsAll(containsAllArg);
+
+ Collection<String> retainAllArg = Collections.singletonList("foo");
+ assertFalse(set.retainAll(retainAllArg));
+ Mockito.verify(delegate).retainAll(retainAllArg);
+
+ Collection<String> removeAllArg = Collections.singletonList("foo");
+ assertFalse(set.removeAll(removeAllArg));
+ Mockito.verify(delegate).removeAll(removeAllArg);
+
+ set.clear();
+ Mockito.verify(delegate).clear();
+ }
+
+ @Test
+ public void requireThatHashCodeIsImplemented() {
+ assertEquals(newLazySet(null).hashCode(),
+ newLazySet(null).hashCode());
+ }
+
+ @Test
+ public void requireThatEqualsIsImplemented() {
+ Set<Object> lhs = newLazySet(new HashSet<>());
+ Set<Object> rhs = newLazySet(new HashSet<>());
+ assertEquals(lhs, lhs);
+ assertEquals(lhs, rhs);
+
+ Object obj = new Object();
+ lhs.add(obj);
+ assertEquals(lhs, lhs);
+ assertFalse(lhs.equals(rhs));
+ rhs.add(obj);
+ assertEquals(lhs, rhs);
+ }
+
+ @Test
+ public void requireThatHashSetFactoryDelegatesToAHashSet() {
+ LazySet<Integer> set = LazySet.newHashSet();
+ set.add(6);
+ set.add(9);
+ assertEquals(HashSet.class, set.getDelegate().getClass());
+ }
+
+ private static <E> LazySet<E> newSingletonSet(E element) {
+ return newSingletonSet(new HashSet<E>(), element);
+ }
+
+ private static <E> LazySet<E> newSingletonSet(Set<E> delegate, E element) {
+ LazySet<E> set = newLazySet(delegate);
+ set.add(element);
+ return set;
+ }
+
+ private static <E> LazySet<E> newLazySet(final Set<E> delegate) {
+ return new LazySet<E>() {
+
+ @Override
+ protected Set<E> newDelegate() {
+ return delegate;
+ }
+ };
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/ListMapTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/ListMapTestCase.java
new file mode 100644
index 00000000000..29668676222
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/ListMapTestCase.java
@@ -0,0 +1,153 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class ListMapTestCase {
+
+ @Test
+ public void testSimple() {
+ ListMap<String, String> stringMap = new ListMap<>();
+ stringMap.put("foo", "bar");
+ stringMap.put("foo", "far");
+ stringMap.put("bar", "rab");
+
+ List<String> fooValues = stringMap.get("foo");
+ assertEquals(2, fooValues.size());
+ assertEquals("bar", fooValues.get(0));
+ assertEquals("far", fooValues.get(1));
+
+ List<String> barValues = stringMap.get("bar");
+ assertEquals(1, barValues.size());
+ assertEquals("rab", barValues.get(0));
+ }
+
+ @Test
+ public void testAnotherImplementation() {
+ ListMap<String, String> stringMap = new ListMap<>(IdentityHashMap.class);
+ String foo = "foo";
+ String bar = "bar";
+ String far = "far";
+ String rab = "rab";
+
+ stringMap.put(foo, bar);
+ stringMap.put(foo, far);
+ stringMap.put(bar, rab);
+
+ List<String> fooValues = stringMap.get(new String("foo"));
+ assertEquals(0, fooValues.size());
+ fooValues = stringMap.get(foo);
+ assertEquals(2, fooValues.size());
+ assertEquals("bar", fooValues.get(0));
+ assertEquals("far", fooValues.get(1));
+
+
+ List<String> barValues = stringMap.get(new String("bar"));
+ assertEquals(0, barValues.size());
+ barValues = stringMap.get(bar);
+ assertEquals(1, barValues.size());
+ assertEquals("rab", barValues.get(0));
+ }
+
+ @SuppressWarnings("serial")
+ private static class BoomMap extends HashMap<String, String> {
+ @SuppressWarnings("unused")
+ BoomMap() {
+ throw new RuntimeException();
+ }
+ }
+
+ @Test
+ public void testExplodingImplementation() {
+ boolean illegalArgument = false;
+ try {
+ new ListMap<String, String>(BoomMap.class);
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.getCause().getClass() == RuntimeException.class);
+ illegalArgument = true;
+ }
+ assertTrue(illegalArgument);
+ }
+
+ private static final String A = "A";
+ private static final String B = "B";
+ private static final String B0 = "b0";
+
+ private ListMap<String, String> initSimpleMap() {
+ ListMap<String, String> lm = new ListMap<>();
+ lm.put(A, "a0");
+ lm.put(A, "a1");
+ lm.put(B, B0);
+ lm.put(B, "b1");
+ lm.put("C", "c");
+ lm.put("D", "d");
+ return lm;
+ }
+
+ @Test
+ public void testRemoval() {
+ ListMap<String, String> lm = initSimpleMap();
+ assertEquals(2, lm.getList(A).size());
+ assertEquals(4, lm.entrySet().size());
+ lm.removeAll(A);
+ assertEquals(3, lm.entrySet().size());
+ assertEquals(0, lm.getList(A).size());
+ assertEquals(2, lm.getList(B).size());
+ assertTrue(lm.removeValue(B, B0));
+ assertFalse(lm.removeValue(B, B0));
+ assertEquals(1, lm.getList(B).size());
+ assertEquals(3, lm.entrySet().size());
+ }
+
+ @Test
+ public void testGetSet() {
+ ListMap<String, String> lm = initSimpleMap();
+ lm.removeAll(B);
+ Set<Map.Entry<String, List<String>>> l = lm.entrySet();
+ assertEquals(3, l.size());
+ boolean hasA = false;
+ boolean hasB = false;
+ for (Map.Entry<String, List<String>> e : l) {
+ if (e.getKey().equals(A)) {
+ hasA = true;
+ } else if (e.getKey().equals(B)) {
+ hasB = true;
+ }
+ }
+ assertTrue(hasA);
+ assertFalse(hasB);
+ }
+
+ @Test
+ public void testFreeze() {
+ ListMap<String, String> map = initSimpleMap();
+ map.freeze();
+ try {
+ map.put("key", "value");
+ fail("Expected exception");
+ }
+ catch (Exception expected) {
+ }
+ try {
+ map.entrySet().iterator().next().getValue().add("foo");
+ fail("Expected exception");
+ }
+ catch (Exception expected) {
+ }
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/ListenableArrayListTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/ListenableArrayListTestCase.java
new file mode 100644
index 00000000000..e3fb48c7a0e
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/ListenableArrayListTestCase.java
@@ -0,0 +1,50 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+import java.util.ListIterator;
+
+/**
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class ListenableArrayListTestCase {
+
+ @Test
+ public void testIt() {
+ ListenableArrayList<String> list = new ListenableArrayList<>();
+ ArrayListListener listener = new ArrayListListener();
+ list.addListener(listener);
+ assertEquals(0,listener.invoked);
+ list.add("a");
+ assertEquals(1,listener.invoked);
+ list.add(0,"b");
+ assertEquals(2,listener.invoked);
+ list.addAll(Arrays.asList(new String[]{"c", "d"}));
+ assertEquals(3,listener.invoked);
+ list.addAll(1,Arrays.asList(new String[]{"e", "f"}));
+ assertEquals(4,listener.invoked);
+ list.set(0,"g");
+ assertEquals(5,listener.invoked);
+ ListIterator<String> i = list.listIterator();
+ i.add("h");
+ assertEquals(6,listener.invoked);
+ i.next();
+ i.set("i");
+ assertEquals(7,listener.invoked);
+ }
+
+ private static class ArrayListListener implements Runnable {
+
+ int invoked;
+
+ @Override
+ public void run() {
+ invoked++;
+ }
+
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/MD5TestCase.java b/vespajlib/src/test/java/com/yahoo/collections/MD5TestCase.java
new file mode 100644
index 00000000000..a107b21abb1
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/MD5TestCase.java
@@ -0,0 +1,33 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class MD5TestCase extends junit.framework.TestCase {
+ public void testMD5() {
+ MD5 md5 = new MD5();
+ int a = md5.hash("foobar");
+ int b = md5.hash("foobar");
+
+ assertEquals(a, b);
+
+ int c = md5.hash("foo");
+
+ assertTrue(a != c);
+ assertTrue(b != c);
+
+ //rudimentary check; see that all four bytes contain something:
+
+ assertTrue((a & 0xFF000000) != 0);
+ assertTrue((a & 0x00FF0000) != 0);
+ assertTrue((a & 0x0000FF00) != 0);
+ assertTrue((a & 0x000000FF) != 0);
+
+
+ assertTrue((c & 0xFF000000) != 0);
+ assertTrue((c & 0x00FF0000) != 0);
+ assertTrue((c & 0x0000FF00) != 0);
+ assertTrue((c & 0x000000FF) != 0);
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/PredicateSplitTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/PredicateSplitTestCase.java
new file mode 100644
index 00000000000..d1c040809de
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/PredicateSplitTestCase.java
@@ -0,0 +1,30 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertEquals;
+
+public class PredicateSplitTestCase {
+ @Test
+ public void requireThatSplitWorks() {
+ List<Integer> l = new ArrayList<Integer>();
+ l.add(1);
+ l.add(6);
+ l.add(2);
+ l.add(4);
+ l.add(5);
+ PredicateSplit<Integer> result = PredicateSplit.partition(l, x -> (x % 2 == 0));
+ assertEquals((long) result.falseValues.size(), 2L);
+ assertEquals((long) result.falseValues.get(0), 1L);
+ assertEquals((long) result.falseValues.get(1), 5L);
+
+ assertEquals((long) result.trueValues.size(), 3L);
+ assertEquals((long) result.trueValues.get(0), 6L);
+ assertEquals((long) result.trueValues.get(1), 2L);
+ assertEquals((long) result.trueValues.get(2), 4L);
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/TinyIdentitySetTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/TinyIdentitySetTestCase.java
new file mode 100644
index 00000000000..2ba7262530b
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/TinyIdentitySetTestCase.java
@@ -0,0 +1,302 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * Check TinyIdentitySet seems to work. :)
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public final class TinyIdentitySetTestCase {
+
+ @Test
+ public void testAdd() {
+ final String string = "abc";
+ final String a = new String(string);
+ final String b = new String(string);
+ final TinyIdentitySet<String> t = new TinyIdentitySet<>(3);
+ t.add(a);
+ t.add(b);
+ assertEquals(2, t.size());
+ t.add(string);
+ assertEquals(3, t.size());
+ t.add(string);
+ t.add(a);
+ t.add(b);
+ assertEquals(3, t.size());
+
+ }
+
+ @Test
+ public void testAddAll() {
+ final List<String> stuff = doubleAdd();
+ final TinyIdentitySet<String> t = new TinyIdentitySet<>(
+ stuff.size());
+ t.addAll(stuff);
+ assertEquals(stuff.size() / 2, t.size());
+ }
+
+ private List<String> doubleAdd() {
+ final String string = "abc";
+ final String a = new String(string);
+ final String b = new String(string);
+ final String c = "c";
+ final List<String> stuff = new ArrayList<>();
+ stuff.add(string);
+ stuff.add(a);
+ stuff.add(b);
+ stuff.add(c);
+ stuff.add(string);
+ stuff.add(a);
+ stuff.add(b);
+ stuff.add(c);
+ return stuff;
+ }
+
+ @Test
+ public void testContains() {
+ final String string = "abc";
+ final String a = new String(string);
+ final String b = new String(string);
+ final TinyIdentitySet<String> t = new TinyIdentitySet<>(2);
+ t.add(string);
+ t.add(a);
+ assertTrue(t.contains(a));
+ assertTrue(t.contains(string));
+ assertFalse(t.contains(b));
+ }
+
+ @Test
+ public void testContainsAll() {
+ final String string = "abc";
+ final String a = new String(string);
+ final String b = new String(string);
+ final String c = "c";
+ final List<String> stuff = new ArrayList<>();
+ stuff.add(string);
+ stuff.add(a);
+ stuff.add(b);
+ final TinyIdentitySet<String> t = new TinyIdentitySet<>(
+ stuff.size());
+ t.addAll(stuff);
+ assertTrue(t.containsAll(stuff));
+ stuff.add(c);
+ assertFalse(t.containsAll(stuff));
+ }
+
+ @Test
+ public void testRemove() {
+ final String string = "abc";
+ final String a = new String(string);
+ final String b = new String(string);
+ final TinyIdentitySet<String> t = new TinyIdentitySet<>(2);
+ t.add(string);
+ t.add(a);
+ assertFalse(t.remove(b));
+ assertTrue(t.remove(a));
+ assertFalse(t.remove(a));
+ assertTrue(t.remove(string));
+ assertFalse(t.remove(b));
+ }
+
+ @Test
+ public void testRetainAll() {
+ final List<String> stuff = doubleAdd();
+ final TinyIdentitySet<String> t = new TinyIdentitySet<>(
+ stuff.size());
+ t.addAll(stuff);
+ assertFalse(t.retainAll(stuff));
+ assertEquals(stuff.size() / 2, t.size());
+ t.add("nalle");
+ assertEquals(stuff.size() / 2 + 1, t.size());
+ assertTrue(t.retainAll(stuff));
+ assertEquals(stuff.size() / 2, t.size());
+ }
+
+ @Test
+ public void testToArrayTArray() {
+ final List<String> stuff = doubleAdd();
+ final TinyIdentitySet<String> t = new TinyIdentitySet<>(
+ stuff.size());
+ t.addAll(stuff);
+ final String[] s = t.toArray(new String[0]);
+ assertEquals(t.size(), s.length);
+ assertEquals(stuff.size() / 2, s.length);
+ }
+
+ @Test
+ public void testGrow() {
+ final TinyIdentitySet<Integer> t = new TinyIdentitySet<>(5);
+ final int targetSize = 100;
+ for (int i = 0; i < targetSize; ++i) {
+ t.add(i);
+ }
+ assertEquals(targetSize, t.size());
+ int n = 0;
+ for (final Iterator<Integer> i = t.iterator(); i.hasNext();) {
+ assertEquals(Integer.valueOf(n++), i.next());
+ }
+ assertEquals(targetSize, n);
+ }
+
+ @Test
+ public void testBiggerRemoveAll() {
+ final int targetSize = 100;
+ final TinyIdentitySet<Integer> t = new TinyIdentitySet<>(
+ targetSize);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> remove = buildSubSet(targetSize, t, instances);
+ t.removeAll(remove);
+ assertEquals(targetSize / 2, t.size());
+ for (final Iterator<Integer> i = t.iterator(); i.hasNext();) {
+ final Integer n = i.next();
+ assertTrue(n % 2 == 0);
+ assertFalse(remove.contains(n));
+
+ }
+ }
+
+ @Test
+ public void testBiggerRetainAll() {
+ final int targetSize = 100;
+ final TinyIdentitySet<Integer> t = new TinyIdentitySet<>(
+ targetSize);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> retain = buildSubSet(targetSize, t, instances);
+ t.retainAll(retain);
+ assertEquals(targetSize / 2, t.size());
+ for (final Iterator<Integer> i = t.iterator(); i.hasNext();) {
+ final Integer n = i.next();
+ assertTrue(n % 2 != 0);
+ assertTrue(retain.contains(n));
+ }
+ }
+
+ private List<Integer> buildSubSet(final int targetSize,
+ final TinyIdentitySet<Integer> t, final Integer[] instances) {
+ for (int i = 0; i < targetSize; ++i) {
+ instances[i] = Integer.valueOf(i);
+ t.add(instances[i]);
+ }
+ final List<Integer> subset = new ArrayList<>(50);
+ for (int i = 0; i < targetSize; ++i) {
+ if (i % 2 != 0) {
+ subset.add(instances[i]);
+ }
+ }
+ return subset;
+ }
+
+ @Test
+ public void testMuckingAbout() {
+ final int targetSize = 100;
+ final TinyIdentitySet<Integer> t = new TinyIdentitySet<>(3);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> retain = buildSubSet(targetSize, t, instances);
+ for (final Integer n : retain) {
+ t.remove(n);
+ assertEquals(targetSize - 1, t.size());
+ t.add(n);
+ assertEquals(targetSize, t.size());
+ }
+ assertEquals(targetSize, t.size());
+ final Integer[] contents = t.toArray(new Integer[0]);
+ Arrays.sort(contents, 0, targetSize);
+ for (int i = 0; i < targetSize; ++i) {
+ assertEquals(instances[i], contents[i]);
+ }
+ }
+
+ @Test
+ public void testMoreDuplicates() {
+ final int targetSize = 100;
+ final TinyIdentitySet<Integer> t = new TinyIdentitySet<>(3);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> add = buildSubSet(targetSize, t, instances);
+ assertEquals(targetSize, t.size());
+ t.addAll(add);
+ assertEquals(targetSize, t.size());
+ }
+
+ @Test
+ public void testEmptySet() {
+ final int targetSize = 100;
+ final TinyIdentitySet<Integer> t = new TinyIdentitySet<>(0);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> add = buildSubSet(targetSize, t, instances);
+ for (Integer i : instances) {
+ t.remove(i);
+ }
+ assertEquals(0, t.size());
+ for (Integer i : add) {
+ t.add(i);
+ }
+ assertEquals(targetSize / 2, t.size());
+ }
+
+ @Test
+ public void testSmallEmptySet() {
+ final TinyIdentitySet<Integer> t = new TinyIdentitySet<>(3);
+ Integer a = new Integer(0), b = new Integer(1), c = new Integer(2);
+ t.add(a);
+ t.add(b);
+ t.add(c);
+ assertEquals(3, t.size());
+ t.remove(a);
+ assertEquals(2, t.size());
+ t.remove(c);
+ assertEquals(1, t.size());
+ t.remove(c);
+ assertEquals(1, t.size());
+ t.remove(b);
+ assertEquals(0, t.size());
+ t.add(b);
+ assertEquals(1, t.size());
+ t.add(b);
+ assertEquals(1, t.size());
+ t.add(a);
+ assertEquals(2, t.size());
+ t.add(a);
+ assertEquals(2, t.size());
+ t.add(c);
+ assertEquals(3, t.size());
+ t.add(c);
+ assertEquals(3, t.size());
+ }
+
+ @Test
+ public void testIterator() {
+ final int targetSize = 100;
+ final TinyIdentitySet<Integer> t = new TinyIdentitySet<>(0);
+ final Integer[] instances = new Integer[targetSize];
+ final List<Integer> remove = buildSubSet(targetSize, t, instances);
+ int traversed = 0;
+ for (Iterator<Integer> i = t.iterator(); i.hasNext();) {
+ Integer n = i.next();
+ if (remove.contains(n)) {
+ i.remove();
+ }
+ ++traversed;
+ }
+ assertEquals(targetSize, traversed);
+ assertEquals(targetSize / 2, t.size());
+ for (int i = 0; i < instances.length; ++i) {
+ Integer n = instances[i];
+ if (remove.contains(n)) {
+ assertFalse(t.contains(n));
+ } else {
+ assertTrue(t.contains(n));
+ }
+ }
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/TupleTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/TupleTestCase.java
new file mode 100644
index 00000000000..8c7d25431a2
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/collections/TupleTestCase.java
@@ -0,0 +1,48 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.collections;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Test case used for testing and experimenting with the tuple APIs. It seems
+ * Tuple4 is just as horrible as I first assumed, but using quick-fix funtions
+ * in the IDE made writing the code less painful than I guessed..
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class TupleTestCase {
+
+ private static final String _12 = "12";
+ private static final Integer _11 = Integer.valueOf(11);
+
+ Tuple2<Integer, String> instance = new Tuple2<>(_11, _12);
+
+
+ @Test
+ public final void objectStuff() {
+ boolean hashException = false;
+ boolean equalsException = false;
+ assertEquals("Tuple2(11, 12)", instance.toString());
+ try {
+ instance.hashCode();
+ } catch (UnsupportedOperationException e) {
+ hashException = true;
+ }
+ assertTrue(hashException);
+ try {
+ instance.equals(null);
+ } catch (UnsupportedOperationException e) {
+ equalsException = true;
+ }
+ assertTrue(equalsException);
+ }
+
+ @Test
+ public final void basicUse() {
+ assertSame(_11, instance.first);
+ assertSame(_12, instance.second);
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/compress/IntegerCompressorTest.java b/vespajlib/src/test/java/com/yahoo/compress/IntegerCompressorTest.java
new file mode 100644
index 00000000000..46a70a4c956
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/compress/IntegerCompressorTest.java
@@ -0,0 +1,105 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.compress;
+
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * TODO: balder
+ */
+public class IntegerCompressorTest {
+ private void verifyPositiveNumber(int n, byte [] expected) {
+ ByteBuffer buf = ByteBuffer.allocate(expected.length);
+ IntegerCompressor.putCompressedPositiveNumber(n, buf);
+ assertArrayEquals(expected, buf.array());
+ }
+ private void verifyNumber(int n, byte [] expected) {
+ ByteBuffer buf = ByteBuffer.allocate(expected.length);
+ IntegerCompressor.putCompressedNumber(n, buf);
+ assertArrayEquals(expected, buf.array());
+ }
+
+ @Test
+ public void requireThatPositiveNumberCompressCorrectly() {
+ byte [] zero = {0};
+ verifyPositiveNumber(0, zero);
+ byte [] one = {0x01};
+ verifyPositiveNumber(1, one);
+ byte [] x3f = {0x3f};
+ verifyPositiveNumber(0x3f, x3f);
+ byte [] x40 = {(byte)0x80,0x40};
+ verifyPositiveNumber(0x40, x40);
+ byte [] x3fff = {(byte)0xbf, (byte)0xff};
+ verifyPositiveNumber(0x3fff, x3fff);
+ byte [] x4000 = {(byte)0xc0, 0x00, 0x40, 0x00};
+ verifyPositiveNumber(0x4000, x4000);
+ byte [] x3fffffff = {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff};
+ verifyPositiveNumber(0x3fffffff, x3fffffff);
+ byte [] x40000000 = {0,0,0,0};
+ try {
+ verifyPositiveNumber(0x40000000, x40000000);
+ assertTrue(false);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Number '1073741824' too big, must extend encoding", e.getMessage());
+ }
+ try {
+ verifyPositiveNumber(-1, x40000000);
+ assertTrue(false);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Number '-1' must be positive", e.getMessage());
+ }
+ }
+
+ @Test
+ public void requireThatNumberCompressCorrectly() {
+ byte [] zero = {0};
+ verifyNumber(0, zero);
+ byte [] one = {0x01};
+ verifyNumber(1, one);
+ byte [] x1f = {0x1f};
+ verifyNumber(0x1f, x1f);
+ byte [] x20 = {0x40,0x20};
+ verifyNumber(0x20, x20);
+ byte [] x1fff = {0x5f, (byte)0xff};
+ verifyNumber(0x1fff, x1fff);
+ byte [] x2000 = {0x60, 0x00, 0x20, 0x00};
+ verifyNumber(0x2000, x2000);
+ byte [] x1fffffff = {0x7f, (byte)0xff, (byte)0xff, (byte)0xff};
+ verifyNumber(0x1fffffff, x1fffffff);
+ byte [] x20000000 = {0,0,0,0};
+ try {
+ verifyNumber(0x20000000, x20000000);
+ assertTrue(false);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Number '536870912' too big, must extend encoding", e.getMessage());
+ }
+ byte [] mzero = {(byte)0x81};
+ verifyNumber(-1, mzero);
+ byte [] mone = {(byte)0x82};
+ verifyNumber(-2, mone);
+ byte [] mx1f = {(byte)0x9f};
+ verifyNumber(-0x1f, mx1f);
+ byte [] mx20 = {(byte)0xc0,0x20};
+ verifyNumber(-0x20, mx20);
+ byte [] mx1fff = {(byte)0xdf, (byte)0xff};
+ verifyNumber(-0x1fff, mx1fff);
+ byte [] mx2000 = {(byte)0xe0, 0x00, 0x20, 0x00};
+ verifyNumber(-0x2000, mx2000);
+ byte [] mx1fffffff = {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff};
+ verifyNumber(-0x1fffffff, mx1fffffff);
+ byte [] mx20000000 = {0,0,0,0};
+ try {
+ verifyNumber(-0x20000000, mx20000000);
+ assertTrue(false);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Number '-536870912' too big, must extend encoding", e.getMessage());
+ }
+
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/concurrent/CopyOnWriteHashMapTest.java b/vespajlib/src/test/java/com/yahoo/concurrent/CopyOnWriteHashMapTest.java
new file mode 100644
index 00000000000..22619e3865e
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/concurrent/CopyOnWriteHashMapTest.java
@@ -0,0 +1,106 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.concurrent;
+
+import org.junit.Test;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:balder@yahoo-inc.com">Henning Baldersheim</a>
+ * @since 5.2
+ */
+public class CopyOnWriteHashMapTest {
+
+ @Test
+ public void requireThatAccessorsWork() {
+ Map<String, String> map = new CopyOnWriteHashMap<>();
+ assertEquals(0, map.size());
+ assertEquals(true, map.isEmpty());
+ assertEquals(false, map.containsKey("fooKey"));
+ assertEquals(false, map.containsValue("fooVal"));
+ assertNull(map.get("fooKey"));
+ assertNull(map.remove("fooKey"));
+ assertEquals(0, map.keySet().size());
+ assertEquals(0, map.entrySet().size());
+ assertEquals(0, map.values().size());
+
+ map.put("fooKey", "fooVal");
+ assertEquals(1, map.size());
+ assertEquals(false, map.isEmpty());
+ assertEquals(true, map.containsKey("fooKey"));
+ assertEquals(true, map.containsValue("fooVal"));
+ assertEquals("fooVal", map.get("fooKey"));
+ assertEquals(1, map.keySet().size());
+ assertEquals(1, map.entrySet().size());
+ assertEquals(1, map.values().size());
+
+ map.put("barKey", "barVal");
+ assertEquals(2, map.size());
+ assertEquals(false, map.isEmpty());
+ assertEquals(true, map.containsKey("fooKey"));
+ assertEquals(true, map.containsKey("barKey"));
+ assertEquals(true, map.containsValue("fooVal"));
+ assertEquals(true, map.containsValue("barVal"));
+ assertEquals("fooVal", map.get("fooKey"));
+ assertEquals("barVal", map.get("barKey"));
+ assertEquals(2, map.keySet().size());
+ assertEquals(2, map.entrySet().size());
+ assertEquals(2, map.values().size());
+
+ assertEquals("fooVal", map.remove("fooKey"));
+ assertEquals(1, map.size());
+ assertEquals(false, map.isEmpty());
+ assertEquals(false, map.containsKey("fooKey"));
+ assertEquals(true, map.containsKey("barKey"));
+ assertEquals(false, map.containsValue("fooVal"));
+ assertEquals(true, map.containsValue("barVal"));
+ assertNull(map.get("fooKey"));
+ assertEquals("barVal", map.get("barKey"));
+ assertEquals(1, map.keySet().size());
+ assertEquals(1, map.entrySet().size());
+ assertEquals(1, map.values().size());
+ }
+
+ @Test
+ public void requireThatEntrySetDoesNotReflectConcurrentModifications() {
+ Map<String, String> map = new CopyOnWriteHashMap<>();
+ map.put("fooKey", "fooVal");
+
+ Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
+ assertEquals("fooVal", map.remove("fooKey"));
+
+ assertTrue(it.hasNext());
+ Map.Entry<String, String> entry = it.next();
+ assertEquals("fooKey", entry.getKey());
+ assertEquals("fooVal", entry.getValue());
+ }
+
+ @Test
+ public void requireThatKeySetDoesNotReflectConcurrentModifications() {
+ Map<String, String> map = new CopyOnWriteHashMap<>();
+ map.put("fooKey", "fooVal");
+
+ Iterator<String> it = map.keySet().iterator();
+ assertEquals("fooVal", map.remove("fooKey"));
+
+ assertTrue(it.hasNext());
+ assertEquals("fooKey", it.next());
+ }
+
+ @Test
+ public void requireThatValuesDoNotReflectConcurrentModifications() {
+ Map<String, String> map = new CopyOnWriteHashMap<>();
+ map.put("fooKey", "fooVal");
+
+ Iterator<String> it = map.values().iterator();
+ assertEquals("fooVal", map.remove("fooKey"));
+
+ assertTrue(it.hasNext());
+ assertEquals("fooVal", it.next());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/concurrent/EventBarrierTestCase.java b/vespajlib/src/test/java/com/yahoo/concurrent/EventBarrierTestCase.java
new file mode 100644
index 00000000000..eae792effd4
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/concurrent/EventBarrierTestCase.java
@@ -0,0 +1,168 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.concurrent;
+
+import junit.framework.TestCase;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class EventBarrierTestCase extends TestCase {
+
+ public void testEmpty() {
+ // waiting for an empty set of events
+ Barrier b = new Barrier();
+ EventBarrier eb = new EventBarrier();
+
+ assertTrue(!eb.startBarrier(b));
+ assertTrue(!b.done);
+ assertEquals(eb.getNumEvents(), 0);
+ assertEquals(eb.getNumBarriers(), 0);
+
+ int token = eb.startEvent();
+ eb.completeEvent(token);
+
+ assertTrue(!eb.startBarrier(b));
+ assertTrue(!b.done);
+ assertEquals(eb.getNumEvents(), 0);
+ assertEquals(eb.getNumBarriers(), 0);
+ }
+
+ public void testSimple() {
+ // a single barrier waiting for a single event
+ Barrier b = new Barrier();
+ EventBarrier eb = new EventBarrier();
+ assertEquals(eb.getNumEvents(), 0);
+ assertEquals(eb.getNumBarriers(), 0);
+
+ int token = eb.startEvent();
+ assertEquals(eb.getNumEvents(), 1);
+ assertEquals(eb.getNumBarriers(), 0);
+
+ assertTrue(eb.startBarrier(b));
+ assertTrue(!b.done);
+ assertEquals(eb.getNumEvents(), 1);
+ assertEquals(eb.getNumBarriers(), 1);
+
+ eb.completeEvent(token);
+ assertTrue(b.done);
+ assertEquals(eb.getNumEvents(), 0);
+ assertEquals(eb.getNumBarriers(), 0);
+ }
+
+ public void testBarrierChain() {
+ // more than one barrier waiting for the same set of events
+ Barrier b1 = new Barrier();
+ Barrier b2 = new Barrier();
+ Barrier b3 = new Barrier();
+ EventBarrier eb = new EventBarrier();
+ assertEquals(eb.getNumEvents(), 0);
+ assertEquals(eb.getNumBarriers(), 0);
+
+ int token = eb.startEvent();
+ assertEquals(eb.getNumEvents(), 1);
+ assertEquals(eb.getNumBarriers(), 0);
+
+ assertTrue(eb.startBarrier(b1));
+ assertTrue(eb.startBarrier(b2));
+ assertTrue(eb.startBarrier(b3));
+ assertTrue(!b1.done);
+ assertTrue(!b2.done);
+ assertTrue(!b3.done);
+
+ assertEquals(eb.getNumEvents(), 1);
+ assertEquals(eb.getNumBarriers(), 3);
+
+ eb.completeEvent(token);
+ assertTrue(b1.done);
+ assertTrue(b2.done);
+ assertTrue(b3.done);
+ assertEquals(eb.getNumEvents(), 0);
+ assertEquals(eb.getNumBarriers(), 0);
+ }
+
+ public void testEventAfter() {
+ // new events starting after the start of a barrier
+ Barrier b = new Barrier();
+ EventBarrier eb = new EventBarrier();
+ assertEquals(eb.getNumEvents(), 0);
+ assertEquals(eb.getNumBarriers(), 0);
+
+ int token = eb.startEvent();
+ assertEquals(eb.getNumEvents(), 1);
+ assertEquals(eb.getNumBarriers(), 0);
+
+ assertTrue(eb.startBarrier(b));
+ assertTrue(!b.done);
+ assertEquals(eb.getNumEvents(), 1);
+ assertEquals(eb.getNumBarriers(), 1);
+
+ int t2 = eb.startEvent();
+ assertTrue(!b.done);
+ assertEquals(eb.getNumEvents(), 2);
+ assertEquals(eb.getNumBarriers(), 1);
+
+ eb.completeEvent(token);
+ assertTrue(b.done);
+ assertEquals(eb.getNumEvents(), 1);
+ assertEquals(eb.getNumBarriers(), 0);
+
+ eb.completeEvent(t2);
+ assertEquals(eb.getNumEvents(), 0);
+ assertEquals(eb.getNumBarriers(), 0);
+ }
+
+ public void testReorder() {
+ // events completing in a different order than they started
+ Barrier b1 = new Barrier();
+ Barrier b2 = new Barrier();
+ Barrier b3 = new Barrier();
+ EventBarrier eb = new EventBarrier();
+
+ int t1 = eb.startEvent();
+ eb.startBarrier(b1);
+ int t2 = eb.startEvent();
+ eb.startBarrier(b2);
+ int t3 = eb.startEvent();
+ eb.startBarrier(b3);
+ int t4 = eb.startEvent();
+
+ assertEquals(eb.getNumEvents(), 4);
+ assertEquals(eb.getNumBarriers(), 3);
+
+ assertTrue(!b1.done);
+ assertTrue(!b2.done);
+ assertTrue(!b3.done);
+
+ eb.completeEvent(t4);
+ assertTrue(!b1.done);
+ assertTrue(!b2.done);
+ assertTrue(!b3.done);
+
+ eb.completeEvent(t3);
+ assertTrue(!b1.done);
+ assertTrue(!b2.done);
+ assertTrue(!b3.done);
+
+ eb.completeEvent(t1);
+ assertTrue(b1.done);
+ assertTrue(!b2.done);
+ assertTrue(!b3.done);
+
+ eb.completeEvent(t2);
+ assertTrue(b1.done);
+ assertTrue(b2.done);
+ assertTrue(b3.done);
+
+ assertEquals(eb.getNumEvents(), 0);
+ assertEquals(eb.getNumBarriers(), 0);
+ }
+
+ private static class Barrier implements EventBarrier.BarrierWaiter {
+ boolean done = false;
+
+ @Override
+ public void completeBarrier() {
+ done = true;
+ }
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/concurrent/ExecutorsTestCase.java b/vespajlib/src/test/java/com/yahoo/concurrent/ExecutorsTestCase.java
new file mode 100644
index 00000000000..b8f2b0e5c58
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/concurrent/ExecutorsTestCase.java
@@ -0,0 +1,139 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.LinkedList;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ExecutorsTestCase {
+ static private class Runner implements Runnable {
+ static private AtomicInteger threadCount = new AtomicInteger(0);
+ static private class ThreadId extends ThreadLocal<Integer> {
+ @Override
+ protected Integer initialValue() {
+ return new Integer(threadCount.getAndIncrement());
+ }
+ }
+ static private ThreadId threadId = new ThreadId();
+ private volatile int runBy = -1;
+ @Override
+ public void run() {
+ runBy = threadId.get();
+ }
+ int getRunBy() { return runBy; }
+ }
+
+ private static class Producer implements Runnable {
+ private volatile int maxThreadId = 0;
+ private final long timeOutMS;
+ private final ExecutorService consumer;
+ Producer(ExecutorService consumer, long timeOutMS) {
+ this.timeOutMS = timeOutMS;
+ this.consumer = consumer;
+ }
+ @Override
+ public void run() {
+ long now = System.currentTimeMillis();
+ Runner r = new Runner();
+ try {
+ while (now + timeOutMS > System.currentTimeMillis()) {
+ Future<?> f = consumer.submit(r);
+ f.get();
+ maxThreadId = Math.max(maxThreadId, r.getRunBy());
+ Thread.sleep(1);
+
+ }
+ } catch (InterruptedException e) {
+ assertTrue(false);
+ } catch (ExecutionException e) {
+ assertTrue(false);
+ }
+
+ }
+ }
+
+ private void assertThreadId(ExecutorService s, int id) throws InterruptedException, ExecutionException {
+ Runner r = new Runner();
+ Future<?> f = s.submit(r);
+ assertNull(f.get());
+ assertEquals(id, r.getRunBy());
+ }
+ private void assertRoundRobinOrder(ExecutorService s) throws InterruptedException, ExecutionException {
+ assertThreadId(s, 0);
+ assertThreadId(s, 1);
+ assertThreadId(s, 2);
+ assertThreadId(s, 0);
+ assertThreadId(s, 1);
+ assertThreadId(s, 2);
+ assertThreadId(s, 0);
+ assertThreadId(s, 1);
+ }
+ private int measureMaxNumThreadsUsage(ThreadPoolExecutor s, long durationMS, int maxProducers) throws InterruptedException, ExecutionException {
+ s.prestartAllCoreThreads();
+ ExecutorService consumers = Executors.newCachedThreadPool();
+ LinkedList<Future<Producer>> futures = new LinkedList<>();
+ for (int i = 0; i < maxProducers; i++) {
+ Producer p = new Producer(s, durationMS);
+ futures.add(consumers.submit(p, p));
+ }
+ int maxThreadId = 0;
+ try {
+ while (! futures.isEmpty()) {
+ Producer p = futures.remove().get();
+ maxThreadId = Math.max(maxThreadId, p.maxThreadId);
+ }
+ } catch (InterruptedException e) {
+ assertTrue(false);
+ } catch (ExecutionException e) {
+ assertTrue(false);
+ }
+ return maxThreadId;
+ }
+ private void assertStackOrder(ThreadPoolExecutor s) throws InterruptedException, ExecutionException {
+ s.prestartAllCoreThreads();
+ Thread.sleep(10); //Sleep to allow last executing thread to get back on the stack
+ assertThreadId(s, 0);
+ Thread.sleep(10);
+ assertThreadId(s, 0);
+ Thread.sleep(10);
+ assertThreadId(s, 0);
+ Thread.sleep(10);
+ assertThreadId(s, 0);
+ Thread.sleep(10);
+ assertThreadId(s, 0);
+ Thread.sleep(10);
+ assertThreadId(s, 0);
+ Thread.sleep(10);
+ assertThreadId(s, 0);
+ }
+
+ @Ignore // Ignored as it is not deterministic, and probably hard to make deterministic to.
+ @Test
+ public void requireThatExecutionOrderIsPredictable() throws InterruptedException, ExecutionException {
+ Runner.threadCount.set(0);
+ assertRoundRobinOrder(new ThreadPoolExecutor(3, 3, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()));
+ Runner.threadCount.set(0);
+ assertRoundRobinOrder(new ThreadPoolExecutor(3, 3, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(true)));
+ Runner.threadCount.set(0);
+ assertStackOrder(new ThreadPoolExecutor(3, 3, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(false)));
+ }
+
+ @Ignore // Ignored as it might not be deterministic
+ public void requireThatExecutionOrderIsPredictableUnderLoad() throws InterruptedException, ExecutionException {
+ Runner.threadCount.set(0);
+ assertEquals(99, measureMaxNumThreadsUsage(new ThreadPoolExecutor(100, 100, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()), 3000, 10));
+ Runner.threadCount.set(0);
+ assertEquals(99, measureMaxNumThreadsUsage(new ThreadPoolExecutor(100, 100, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(true)), 3000, 10));
+ Runner.threadCount.set(0);
+ //Max 9 concurrent tasks. Might not be deterministic
+ assertEquals(9, measureMaxNumThreadsUsage(new ThreadPoolExecutor(100, 100, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(false)), 3000, 10));
+ Runner.threadCount.set(0);
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/concurrent/ReceiverTestCase.java b/vespajlib/src/test/java/com/yahoo/concurrent/ReceiverTestCase.java
new file mode 100644
index 00000000000..88d5283f46a
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/concurrent/ReceiverTestCase.java
@@ -0,0 +1,59 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.concurrent;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import com.yahoo.collections.Tuple2;
+
+/**
+ * Check for com.yahoo.concurrent.Receiver.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class ReceiverTestCase {
+
+ private static class Worker implements Runnable {
+ private static final String HELLO_WORLD = "Hello, World!";
+ private final Receiver<String> receiver;
+ private final long timeToWait;
+
+ Worker(Receiver<String> receiver, long timeToWait) {
+ this.receiver = receiver;
+ this.timeToWait = timeToWait;
+ }
+
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(timeToWait);
+ } catch (InterruptedException e) {
+ fail("Test was interrupted.");
+ }
+ receiver.put(HELLO_WORLD);
+ }
+ }
+
+ @Test
+ public void testPut() throws InterruptedException {
+ Receiver<String> receiver = new Receiver<>();
+ Worker runnable = new Worker(receiver, 0);
+ Thread worker = new Thread(runnable);
+ worker.start();
+ Tuple2<Receiver.MessageState, String> answer = receiver.get(1000L * 1000L * 1000L);
+ assertEquals(Receiver.MessageState.VALID, answer.first);
+ assertEquals(answer.second, Worker.HELLO_WORLD);
+ }
+
+ @Test
+ public void testTimeOut() throws InterruptedException {
+ Receiver<String> receiver = new Receiver<>();
+ Worker runnable = new Worker(receiver, 1000L * 1000L * 1000L);
+ Thread worker = new Thread(runnable);
+ worker.start();
+ Tuple2<Receiver.MessageState, String> answer = receiver.get(500L);
+ assertEquals(Receiver.MessageState.TIMEOUT, answer.first);
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/concurrent/ThreadFactoryFactoryTest.java b/vespajlib/src/test/java/com/yahoo/concurrent/ThreadFactoryFactoryTest.java
new file mode 100644
index 00000000000..7fc6a9cc390
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/concurrent/ThreadFactoryFactoryTest.java
@@ -0,0 +1,44 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.concurrent;
+
+import org.junit.Test;
+
+
+import java.util.concurrent.ThreadFactory;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+/**
+ * Created with IntelliJ IDEA.
+ * User: balder
+ * Date: 26.04.13
+ * Time: 12:01
+ * To change this template use File | Settings | File Templates.
+ */
+public class ThreadFactoryFactoryTest {
+
+ static class Runner implements Runnable {
+ @Override
+ public void run() {
+
+ }
+ }
+
+ @Test
+ public void requireThatFactoryCreatesCorrectlyNamedThreads() {
+ Thread thread = ThreadFactoryFactory.getThreadFactory("a").newThread(new Runner());
+ assertEquals("a-1-thread-1", thread.getName());
+ thread = ThreadFactoryFactory.getThreadFactory("a").newThread(new Runner());
+ assertEquals("a-2-thread-1", thread.getName());
+ thread = ThreadFactoryFactory.getThreadFactory("b").newThread(new Runner());
+ assertEquals("b-1-thread-1", thread.getName());
+ ThreadFactory factory = ThreadFactoryFactory.getThreadFactory("a");
+ thread = factory.newThread(new Runner());
+ assertEquals("a-3-thread-1", thread.getName());
+ thread = factory.newThread(new Runner());
+ assertEquals("a-3-thread-2", thread.getName());
+ thread = factory.newThread(new Runner());
+ assertEquals("a-3-thread-3", thread.getName());
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/concurrent/ThreadLocalDirectoryTestCase.java b/vespajlib/src/test/java/com/yahoo/concurrent/ThreadLocalDirectoryTestCase.java
new file mode 100644
index 00000000000..d813ae1e18d
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/concurrent/ThreadLocalDirectoryTestCase.java
@@ -0,0 +1,125 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.concurrent;
+
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * Smoke test for multi producer data structure.
+ *
+ * <p>
+ * TODO sorely needs nastier cases
+ * </p>
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class ThreadLocalDirectoryTestCase {
+ private static class SumUpdater implements ThreadLocalDirectory.Updater<Integer, Integer> {
+
+ @Override
+ public Integer update(Integer current, Integer x) {
+ return Integer.valueOf(current.intValue() + x.intValue());
+ }
+
+ @Override
+ public Integer createGenerationInstance(Integer previous) {
+ return Integer.valueOf(0);
+ }
+ }
+
+ private static class ObservableSumUpdater extends SumUpdater implements ThreadLocalDirectory.ObservableUpdater<Integer, Integer> {
+
+ @Override
+ public Integer copy(Integer current) {
+ return current;
+ }
+ }
+
+
+ private static class Counter implements Runnable {
+ ThreadLocalDirectory<Integer, Integer> r;
+
+ Counter(ThreadLocalDirectory<Integer, Integer> r) {
+ this.r = r;
+ }
+
+ @Override
+ public void run() {
+ LocalInstance<Integer, Integer> s = r.getLocalInstance();
+ for (int i = 0; i < 500; ++i) {
+ put(s, i);
+ }
+ }
+
+ void put(LocalInstance<Integer, Integer> s, int i) {
+ r.update(Integer.valueOf(i), s);
+ }
+ }
+
+ private static class CounterAndViewer extends Counter {
+ CounterAndViewer(ThreadLocalDirectory<Integer, Integer> r) {
+ super(r);
+ }
+
+ @Override
+ void put(LocalInstance<Integer, Integer> s, int i) {
+ super.put(s, i);
+ if (i % 10 == 0) {
+ r.view();
+ }
+ }
+ }
+
+ @Test
+ public void sumFromMultipleThreads() {
+ SumUpdater updater = new SumUpdater();
+ ThreadLocalDirectory<Integer, Integer> s = new ThreadLocalDirectory<>(updater);
+ Thread[] threads = new Thread[500];
+ for (int i = 0; i < 500; ++i) {
+ Counter c = new Counter(s);
+ threads[i] = new Thread(c);
+ }
+ runAll(threads);
+ List<Integer> measurements = s.fetch();
+ int sum = 0;
+ for (Integer i : measurements) {
+ sum += i.intValue();
+ }
+ assertTrue("Data lost.", 62375000 == sum);
+ }
+
+ @Test
+ public void sumAndViewFromMultipleThreads() {
+ ObservableSumUpdater updater = new ObservableSumUpdater();
+ ThreadLocalDirectory<Integer, Integer> s = new ThreadLocalDirectory<>(updater);
+ Thread[] threads = new Thread[500];
+ for (int i = 0; i < 500; ++i) {
+ CounterAndViewer c = new CounterAndViewer(s);
+ threads[i] = new Thread(c);
+ }
+ runAll(threads);
+ List<Integer> measurements = s.fetch();
+ int sum = 0;
+ for (Integer i : measurements) {
+ sum += i.intValue();
+ }
+ assertTrue("Data lost.", 62375000 == sum);
+ }
+
+
+ private void runAll(Thread[] threads) {
+ for (Thread t : threads) {
+ t.start();
+ }
+ for (Thread t : threads) {
+ try {
+ t.join();
+ } catch (InterruptedException e) {
+ // nop
+ }
+ }
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/concurrent/ThreadRobustListTestCase.java b/vespajlib/src/test/java/com/yahoo/concurrent/ThreadRobustListTestCase.java
new file mode 100644
index 00000000000..88c7a962c95
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/concurrent/ThreadRobustListTestCase.java
@@ -0,0 +1,103 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import java.util.Iterator;
+
+import org.junit.Test;
+
+/**
+ * Check we keep the consistent view when reading and writing in parallell.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class ThreadRobustListTestCase {
+
+ private static class Writer implements Runnable {
+ private final ThreadRobustList<String> l;
+ private final Receiver<Boolean> sharedLock;
+
+ public Writer(final ThreadRobustList<String> l,
+ final Receiver<Boolean> sharedLock) {
+ this.sharedLock = sharedLock;
+ this.l = l;
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < 5; ++i) {
+ l.add(String.valueOf(i));
+ }
+ sharedLock.put(Boolean.TRUE);
+ for (int i = 5; i < 100 * 1000; ++i) {
+ l.add(String.valueOf(i));
+ }
+ }
+
+ }
+
+ private static class Reader implements Runnable {
+ private final ThreadRobustList<String> l;
+ private final Receiver<Boolean> sharedLock;
+
+ public Reader(final ThreadRobustList<String> l,
+ final Receiver<Boolean> sharedLock) {
+ this.sharedLock = sharedLock;
+ this.l = l;
+ }
+
+ @Override
+ public void run() {
+ int n;
+ int previous;
+
+ try {
+ sharedLock.get(5 * 60 * 1000);
+ } catch (final InterruptedException e) {
+ fail("Test interrupted.");
+ }
+ n = countElements();
+ assertFalse(n < 5);
+ previous = n;
+ for (int i = 0; i < 1000; ++i) {
+ int reverse = reverseCountElements();
+ n = countElements();
+ assertFalse(n < reverse);
+ assertFalse(n < previous);
+ previous = n;
+ }
+ }
+
+ private int reverseCountElements() {
+ int n = 0;
+ for (final Iterator<String> j = l.reverseIterator(); j.hasNext(); j.next()) {
+ ++n;
+ }
+ return n;
+ }
+
+ private int countElements() {
+ int n = 0;
+ for (final Iterator<String> j = l.iterator(); j.hasNext(); j.next()) {
+ ++n;
+ }
+ return n;
+ }
+ }
+
+ @Test
+ public final void test() throws InterruptedException {
+ final ThreadRobustList<String> container = new ThreadRobustList<>();
+ final Receiver<Boolean> lock = new Receiver<>();
+ final Reader r = new Reader(container, lock);
+ final Writer w = new Writer(container, lock);
+ final Thread wt = new Thread(w);
+ wt.start();
+ r.run();
+ wt.join();
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/data/access/InspectorConformanceTestBase.java b/vespajlib/src/test/java/com/yahoo/data/access/InspectorConformanceTestBase.java
new file mode 100644
index 00000000000..4cf449fbf4f
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/data/access/InspectorConformanceTestBase.java
@@ -0,0 +1,365 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.data.access;
+
+import org.junit.Test;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+import com.yahoo.data.access.Inspector;
+import com.yahoo.data.access.ArrayTraverser;
+import com.yahoo.data.access.ObjectTraverser;
+import com.yahoo.data.access.Type;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+
+abstract public class InspectorConformanceTestBase {
+
+ public abstract static class Try {
+ abstract void f();
+ public Exception call() {
+ try {
+ f();
+ } catch (Exception e) {
+ return e;
+ }
+ return null;
+ }
+ }
+
+ public static class Entries implements ArrayTraverser {
+ List<Inspector> entries = new ArrayList<>();
+ public void entry(int idx, Inspector inspector) {
+ entries.add(inspector);
+ }
+ public Entries traverse(Inspector value) {
+ value.traverse(this);
+ return this;
+ }
+ public Entries iterate(Inspector value) {
+ for (Inspector itr: value.entries()) {
+ entries.add(itr);
+ }
+ return this;
+ }
+ public Entries add(Inspector value) {
+ entries.add(value);
+ return this;
+ }
+ }
+
+ public static class Fields implements ObjectTraverser {
+ Map<String,Inspector> fields = new HashMap<>();
+ public void field(String name, Inspector inspector) {
+ fields.put(name, inspector);
+ }
+ public Fields traverse(Inspector value) {
+ value.traverse(this);
+ return this;
+ }
+ public Fields iterate(Inspector value) {
+ for (Map.Entry<String,Inspector> itr: value.fields()) {
+ fields.put(itr.getKey(), itr.getValue());
+ }
+ return this;
+ }
+ public Fields add(String name, Inspector value) {
+ fields.put(name, value);
+ return this;
+ }
+ }
+
+ // This method must be implemented by all tests of concrete
+ // implementations to return an inspector to a structured object
+ // on the following form (for an example, take a look at
+ // com.yahoo.data.access.simple.InspectorConformanceTestCase):
+ //
+ // ARRAY {
+ // [0]: EMPTY
+ // [1]: BOOL: true
+ // [2]: LONG: 10
+ // [3]: DOUBLE: 5.75
+ // [4]: OBJECT {
+ // "foo": STRING: "foo_value"
+ // "bar": DATA: 0x04 0x02
+ // "nested": ARRAY {
+ // [0]: OBJECT {
+ // "hidden": STRING: "treasure"
+ // }
+ // }
+ // }
+ // }
+ public abstract Inspector getData();
+
+ @Test
+ public void testSelfInspectableInspector() throws Exception {
+ final Inspector value = getData();
+ final Inspector self = value.inspect();
+ assertThat(self, is(value));
+ }
+
+ @Test
+ public void testInvalidValue() throws Exception {
+ final Inspector value = getData().entry(10).field("bogus").entry(0);
+ assertThat(value.valid(), is(false));
+ assertThat(value.type(), is(Type.EMPTY));
+ assertThat(value.entryCount(), is(0));
+ assertThat(value.fieldCount(), is(0));
+ assertThat(new Try(){void f() { value.asBool(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asLong(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asDouble(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asString(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asUtf8(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asData(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asBool(true), is(true));
+ assertThat(value.asLong(50), is(50L));
+ assertThat(value.asDouble(20.25), is(20.25));
+ assertThat(value.asString("default"), is("default"));
+ assertThat(value.asUtf8("utf8".getBytes("UTF-8")), is("utf8".getBytes("UTF-8")));
+ assertThat(value.asData("data".getBytes("UTF-8")), is("data".getBytes("UTF-8")));
+ assertThat(new Entries().traverse(value).entries.size(), is(0));
+ assertThat(new Fields().traverse(value).fields.size(), is(0));
+ assertThat(value.entry(0).valid(), is(false));
+ assertThat(value.field("foo").valid(), is(false));
+ assertThat(new Entries().iterate(value).entries.size(), is(0));
+ assertThat(new Fields().iterate(value).fields.size(), is(0));
+ }
+
+ @Test
+ public void testEmptyValue() throws Exception {
+ final Inspector value = getData().entry(0);
+ assertThat(value.valid(), is(true));
+ assertThat(value.type(), is(Type.EMPTY));
+ assertThat(value.entryCount(), is(0));
+ assertThat(value.fieldCount(), is(0));
+ assertThat(value.asBool(), is(false));
+ assertThat(value.asLong(), is(0L));
+ assertThat(value.asDouble(), is(0.0));
+ assertThat(value.asString(), is(""));
+ assertThat(value.asUtf8(), is(new byte[0]));
+ assertThat(value.asData(), is(new byte[0]));
+ assertThat(value.asBool(true), is(true));
+ assertThat(value.asLong(50), is(50L));
+ assertThat(value.asDouble(20.25), is(20.25));
+ assertThat(value.asString("default"), is("default"));
+ assertThat(value.asUtf8("utf8".getBytes("UTF-8")), is("utf8".getBytes("UTF-8")));
+ assertThat(value.asData("data".getBytes("UTF-8")), is("data".getBytes("UTF-8")));
+ assertThat(new Entries().traverse(value).entries.size(), is(0));
+ assertThat(new Fields().traverse(value).fields.size(), is(0));
+ assertThat(value.entry(0).valid(), is(false));
+ assertThat(value.field("foo").valid(), is(false));
+ assertThat(new Entries().iterate(value).entries.size(), is(0));
+ assertThat(new Fields().iterate(value).fields.size(), is(0));
+ }
+
+ @Test
+ public void testBoolValue() throws Exception {
+ final Inspector value = getData().entry(1);
+ assertThat(value.valid(), is(true));
+ assertThat(value.type(), is(Type.BOOL));
+ assertThat(value.entryCount(), is(0));
+ assertThat(value.fieldCount(), is(0));
+ assertThat(value.asBool(), is(true));
+ assertThat(new Try(){void f() { value.asLong(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asDouble(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asString(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asUtf8(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asData(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asBool(false), is(true));
+ assertThat(value.asLong(50), is(50L));
+ assertThat(value.asDouble(20.25), is(20.25));
+ assertThat(value.asString("default"), is("default"));
+ assertThat(value.asUtf8("utf8".getBytes("UTF-8")), is("utf8".getBytes("UTF-8")));
+ assertThat(value.asData("data".getBytes("UTF-8")), is("data".getBytes("UTF-8")));
+ assertThat(new Entries().traverse(value).entries.size(), is(0));
+ assertThat(new Fields().traverse(value).fields.size(), is(0));
+ assertThat(value.entry(0).valid(), is(false));
+ assertThat(value.field("foo").valid(), is(false));
+ assertThat(new Entries().iterate(value).entries.size(), is(0));
+ assertThat(new Fields().iterate(value).fields.size(), is(0));
+ }
+
+ @Test
+ public void testLongValue() throws Exception {
+ final Inspector value = getData().entry(2);
+ assertThat(value.valid(), is(true));
+ assertThat(value.type(), is(Type.LONG));
+ assertThat(value.entryCount(), is(0));
+ assertThat(value.fieldCount(), is(0));
+ assertThat(new Try(){void f() { value.asBool(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asLong(), is(10L));
+ assertThat(value.asDouble(), is(10.0));
+ assertThat(new Try(){void f() { value.asString(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asUtf8(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asData(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asBool(true), is(true));
+ assertThat(value.asLong(50), is(10L));
+ assertThat(value.asDouble(20.25), is(10.0));
+ assertThat(value.asString("default"), is("default"));
+ assertThat(value.asUtf8("utf8".getBytes("UTF-8")), is("utf8".getBytes("UTF-8")));
+ assertThat(value.asData("data".getBytes("UTF-8")), is("data".getBytes("UTF-8")));
+ assertThat(new Entries().traverse(value).entries.size(), is(0));
+ assertThat(new Fields().traverse(value).fields.size(), is(0));
+ assertThat(value.entry(0).valid(), is(false));
+ assertThat(value.field("foo").valid(), is(false));
+ assertThat(new Entries().iterate(value).entries.size(), is(0));
+ assertThat(new Fields().iterate(value).fields.size(), is(0));
+ }
+
+ @Test
+ public void testDoubleValue() throws Exception {
+ final Inspector value = getData().entry(3);
+ assertThat(value.valid(), is(true));
+ assertThat(value.type(), is(Type.DOUBLE));
+ assertThat(value.entryCount(), is(0));
+ assertThat(value.fieldCount(), is(0));
+ assertThat(new Try(){void f() { value.asBool(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asLong(), is(5L));
+ assertThat(value.asDouble(), is(5.75));
+ assertThat(new Try(){void f() { value.asString(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asUtf8(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asData(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asBool(true), is(true));
+ assertThat(value.asLong(50), is(5L));
+ assertThat(value.asDouble(20.25), is(5.75));
+ assertThat(value.asString("default"), is("default"));
+ assertThat(value.asUtf8("utf8".getBytes("UTF-8")), is("utf8".getBytes("UTF-8")));
+ assertThat(value.asData("data".getBytes("UTF-8")), is("data".getBytes("UTF-8")));
+ assertThat(new Entries().traverse(value).entries.size(), is(0));
+ assertThat(new Fields().traverse(value).fields.size(), is(0));
+ assertThat(value.entry(0).valid(), is(false));
+ assertThat(value.field("foo").valid(), is(false));
+ assertThat(new Entries().iterate(value).entries.size(), is(0));
+ assertThat(new Fields().iterate(value).fields.size(), is(0));
+ }
+
+ @Test
+ public void testStringValue() throws Exception {
+ final Inspector value = getData().entry(4).field("foo");
+ assertThat(value.valid(), is(true));
+ assertThat(value.type(), is(Type.STRING));
+ assertThat(value.entryCount(), is(0));
+ assertThat(value.fieldCount(), is(0));
+ assertThat(new Try(){void f() { value.asBool(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asLong(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asDouble(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asString(), is("foo_value"));
+ assertThat(value.asUtf8(), is("foo_value".getBytes("UTF-8")));
+ assertThat(new Try(){void f() { value.asData(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asBool(true), is(true));
+ assertThat(value.asLong(50), is(50L));
+ assertThat(value.asDouble(20.25), is(20.25));
+ assertThat(value.asString("default"), is("foo_value"));
+ assertThat(value.asUtf8("utf8".getBytes("UTF-8")), is("foo_value".getBytes("UTF-8")));
+ assertThat(value.asData("data".getBytes("UTF-8")), is("data".getBytes("UTF-8")));
+ assertThat(new Entries().traverse(value).entries.size(), is(0));
+ assertThat(new Fields().traverse(value).fields.size(), is(0));
+ assertThat(value.entry(0).valid(), is(false));
+ assertThat(value.field("foo").valid(), is(false));
+ assertThat(new Entries().iterate(value).entries.size(), is(0));
+ assertThat(new Fields().iterate(value).fields.size(), is(0));
+ }
+
+ @Test
+ public void testDataValue() throws Exception {
+ final Inspector value = getData().entry(4).field("bar");
+ assertThat(value.valid(), is(true));
+ assertThat(value.type(), is(Type.DATA));
+ assertThat(value.entryCount(), is(0));
+ assertThat(value.fieldCount(), is(0));
+ assertThat(new Try(){void f() { value.asBool(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asLong(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asDouble(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asString(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asUtf8(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asData(), is(new byte[] { (byte)4, (byte)2 }));
+ assertThat(value.asBool(true), is(true));
+ assertThat(value.asLong(50), is(50L));
+ assertThat(value.asDouble(20.25), is(20.25));
+ assertThat(value.asString("default"), is("default"));
+ assertThat(value.asUtf8("utf8".getBytes("UTF-8")), is("utf8".getBytes("UTF-8")));
+ assertThat(value.asData("data".getBytes("UTF-8")), is(new byte[] { (byte)4, (byte)2 }));
+ assertThat(new Entries().traverse(value).entries.size(), is(0));
+ assertThat(new Fields().traverse(value).fields.size(), is(0));
+ assertThat(value.entry(0).valid(), is(false));
+ assertThat(value.field("foo").valid(), is(false));
+ assertThat(new Entries().iterate(value).entries.size(), is(0));
+ assertThat(new Fields().iterate(value).fields.size(), is(0));
+ }
+
+ @Test
+ public void testArrayValue() throws Exception {
+ final Inspector value = getData();
+ List<Inspector> expected_entries = new Entries()
+ .add(value.entry(0))
+ .add(value.entry(1))
+ .add(value.entry(2))
+ .add(value.entry(3))
+ .add(value.entry(4)).entries;
+ assertThat(value.valid(), is(true));
+ assertThat(value.type(), is(Type.ARRAY));
+ assertThat(value.entryCount(), is(expected_entries.size()));
+ assertThat(value.fieldCount(), is(0));
+ assertThat(new Try(){void f() { value.asBool(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asLong(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asDouble(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asString(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asUtf8(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asData(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asBool(true), is(true));
+ assertThat(value.asLong(50), is(50L));
+ assertThat(value.asDouble(20.25), is(20.25));
+ assertThat(value.asString("default"), is("default"));
+ assertThat(value.asUtf8("utf8".getBytes("UTF-8")), is("utf8".getBytes("UTF-8")));
+ assertThat(value.asData("data".getBytes("UTF-8")), is("data".getBytes("UTF-8")));
+ assertThat(new Entries().traverse(value).entries, is(expected_entries));
+ assertThat(new Fields().traverse(value).fields.size(), is(0));
+ assertThat(value.entry(10).valid(), is(false));
+ assertThat(value.field("foo").valid(), is(false));
+ assertThat(new Entries().iterate(value).entries, is(expected_entries));
+ assertThat(new Fields().iterate(value).fields.size(), is(0));
+ }
+
+ @Test
+ public void testObjectValue() throws Exception {
+ final Inspector value = getData().entry(4);
+ Map<String,Inspector> expected_fields = new Fields()
+ .add("foo", value.field("foo"))
+ .add("bar", value.field("bar"))
+ .add("nested", value.field("nested")).fields;
+ assertThat(value.valid(), is(true));
+ assertThat(value.type(), is(Type.OBJECT));
+ assertThat(value.entryCount(), is(0));
+ assertThat(value.fieldCount(), is(expected_fields.size()));
+ assertThat(new Try(){void f() { value.asBool(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asLong(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asDouble(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asString(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asUtf8(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(new Try(){void f() { value.asData(); }}.call(), instanceOf(IllegalStateException.class));
+ assertThat(value.asBool(true), is(true));
+ assertThat(value.asLong(50), is(50L));
+ assertThat(value.asDouble(20.25), is(20.25));
+ assertThat(value.asString("default"), is("default"));
+ assertThat(value.asUtf8("utf8".getBytes("UTF-8")), is("utf8".getBytes("UTF-8")));
+ assertThat(value.asData("data".getBytes("UTF-8")), is("data".getBytes("UTF-8")));
+ assertThat(new Entries().traverse(value).entries.size(), is(0));
+ assertThat(new Fields().traverse(value).fields, is(expected_fields));
+ assertThat(value.entry(0).valid(), is(false));
+ assertThat(value.field("bogus").valid(), is(false));
+ assertThat(new Entries().iterate(value).entries.size(), is(0));
+ assertThat(new Fields().iterate(value).fields, is(expected_fields));
+ }
+
+ @Test
+ public void testNesting() throws Exception {
+ Inspector value1 = getData().entry(4).field("nested");
+ assertThat(value1.type(), is(Type.ARRAY));
+ Inspector value2 = value1.entry(0);
+ assertThat(value2.type(), is(Type.OBJECT));
+ Inspector value3 = value2.field("hidden");
+ assertThat(value3.type(), is(Type.STRING));
+ assertThat(value3.asString(), is("treasure"));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/data/access/simple/SimpleConformanceTestCase.java b/vespajlib/src/test/java/com/yahoo/data/access/simple/SimpleConformanceTestCase.java
new file mode 100644
index 00000000000..4db5d0afde7
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/data/access/simple/SimpleConformanceTestCase.java
@@ -0,0 +1,55 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.data.access.simple;
+
+
+import org.junit.Test;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+
+
+public class SimpleConformanceTestCase extends com.yahoo.data.access.InspectorConformanceTestBase {
+
+ // ARRAY {
+ // [0]: EMPTY
+ // [1]: BOOL: true
+ // [2]: LONG: 10
+ // [3]: DOUBLE: 5.75
+ // [4]: OBJECT {
+ // "foo": STRING: "foo_value"
+ // "bar": DATA: 0x04 0x02
+ // "nested": ARRAY {
+ // [0]: OBJECT {
+ // "hidden": STRING: "treasure"
+ // }
+ // }
+ // }
+ // }
+ public com.yahoo.data.access.Inspector getData() {
+ return new Value.ArrayValue()
+ .add(new Value.EmptyValue())
+ .add(new Value.BoolValue(true))
+ .add(new Value.LongValue(10L))
+ .add(new Value.DoubleValue(5.75))
+ .add(new Value.ObjectValue()
+ .put("foo", new Value.StringValue("foo_value"))
+ .put("bar", new Value.DataValue(new byte[] { (byte)4, (byte)2 }))
+ .put("nested", new Value.ArrayValue()
+ .add(new Value.ObjectValue()
+ .put("hidden", new Value.StringValue("treasure")))));
+ }
+
+ @Test
+ public void testSingletons() {
+ assertThat(Value.empty().valid(), is(true));
+ assertThat(Value.empty().type(), is(com.yahoo.data.access.Type.EMPTY));
+ assertThat(Value.invalid().valid(), is(false));
+ assertThat(Value.invalid().type(), is(com.yahoo.data.access.Type.EMPTY));
+ }
+
+ @Test
+ public void testToString() {
+ String json = getData().toString();
+ String correct = "[null,true,10,5.75,{\"foo\":\"foo_value\",\"bar\":\"0x0402\",\"nested\":[{\"hidden\":\"treasure\"}]}]";
+ assertThat(json, is(correct));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/data/access/slime/SlimeConformanceTestCase.java b/vespajlib/src/test/java/com/yahoo/data/access/slime/SlimeConformanceTestCase.java
new file mode 100644
index 00000000000..ff6e98cfa37
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/data/access/slime/SlimeConformanceTestCase.java
@@ -0,0 +1,45 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.data.access.slime;
+
+
+public class SlimeConformanceTestCase extends com.yahoo.data.access.InspectorConformanceTestBase {
+
+ // ARRAY {
+ // [0]: EMPTY
+ // [1]: BOOL: true
+ // [2]: LONG: 10
+ // [3]: DOUBLE: 5.75
+ // [4]: OBJECT {
+ // "foo": STRING: "foo_value"
+ // "bar": DATA: 0x04 0x02
+ // "nested": ARRAY {
+ // [0]: OBJECT {
+ // "hidden": STRING: "treasure"
+ // }
+ // }
+ // }
+ // }
+ public com.yahoo.data.access.Inspector getData() {
+ com.yahoo.slime.Slime slime = new com.yahoo.slime.Slime();
+ {
+ com.yahoo.slime.Cursor arr = slime.setArray();
+ arr.addNix();
+ arr.addBool(true);
+ arr.addLong(10);
+ arr.addDouble(5.75);
+ {
+ com.yahoo.slime.Cursor obj = arr.addObject();
+ obj.setString("foo", "foo_value");
+ obj.setData("bar", new byte[] { (byte)4, (byte)2 });
+ {
+ com.yahoo.slime.Cursor nested_array = obj.setArray("nested");
+ {
+ com.yahoo.slime.Cursor nested_object = nested_array.addObject();
+ nested_object.setString("hidden", "treasure");
+ }
+ }
+ }
+ }
+ return new SlimeAdapter(slime.get());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/data/inspect/slime/.gitignore b/vespajlib/src/test/java/com/yahoo/data/inspect/slime/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/data/inspect/slime/.gitignore
diff --git a/vespajlib/src/test/java/com/yahoo/geo/BoundingBoxParserTestCase.java b/vespajlib/src/test/java/com/yahoo/geo/BoundingBoxParserTestCase.java
new file mode 100644
index 00000000000..47a8ade2235
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/geo/BoundingBoxParserTestCase.java
@@ -0,0 +1,162 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.geo;
+
+/**
+ * Tests for the BoundingBoxParser class.
+ *
+ * @author Arne J
+ */
+public class BoundingBoxParserTestCase extends junit.framework.TestCase {
+
+ private BoundingBoxParser parser;
+
+ public BoundingBoxParserTestCase(String name) {
+ super(name);
+ }
+
+ private void allZero(BoundingBoxParser data) {
+ assertEquals(0d, data.n);
+ assertEquals(0d, data.s);
+ assertEquals(0d, data.e);
+ assertEquals(0d, data.w);
+ }
+
+ private void all1234(BoundingBoxParser data) {
+ assertEquals(1d, data.n);
+ assertEquals(2d, data.s);
+ assertEquals(3d, data.e);
+ assertEquals(4d, data.w);
+ }
+
+ /**
+ * Tests different inputs that should all produce 0
+ */
+ public void testZero() {
+ parser = new BoundingBoxParser("n=0,s=0,e=0,w=0");
+ allZero(parser);
+ parser = new BoundingBoxParser("N=0,S=0,E=0,W=0");
+ allZero(parser);
+ parser = new BoundingBoxParser("NORTH=0,SOUTH=0,EAST=0,WEST=0");
+ allZero(parser);
+ parser = new BoundingBoxParser("north=0,south=0,east=0,west=0");
+ allZero(parser);
+ parser = new BoundingBoxParser("n=0.0,s=0.0e-17,e=0.0e0,w=0.0e100");
+ allZero(parser);
+ parser = new BoundingBoxParser("s:0.0,w:0.0,n:0.0,e:0.0");
+ allZero(parser);
+ parser = new BoundingBoxParser("s:0.0,w:0.0,n:0.0,e:0.0");
+ allZero(parser);
+ }
+
+ public void testOneTwoThreeFour() {
+ parser = new BoundingBoxParser("n=1,s=2,e=3,w=4");
+ all1234(parser);
+ parser = new BoundingBoxParser("n=1.0,s=2.0,e=3.0,w=4.0");
+ all1234(parser);
+ parser = new BoundingBoxParser("s=2,w=4,n=1,e=3");
+ all1234(parser);
+ parser = new BoundingBoxParser("N=1,S=2,E=3,W=4");
+ all1234(parser);
+ parser = new BoundingBoxParser("S=2,W=4,N=1,E=3");
+ all1234(parser);
+ parser = new BoundingBoxParser("north=1.0,south=2.0,east=3.0,west=4.0");
+ all1234(parser);
+ parser = new BoundingBoxParser("South=2.0 West=4.0 North=1.0 East=3.0");
+ all1234(parser);
+ }
+
+ /**
+ * Tests various legal inputs and print the output
+ */
+ public void testPrint() {
+ String here = "n=63.418417 E=10.433033 S=37.7 W=-122.02";
+ parser = new BoundingBoxParser(here);
+ System.out.println(here+" -> "+parser);
+ }
+
+ public void testGeoPlanetExample() {
+ /* example XML:
+ <boundingBox>
+ <southWest>
+ <latitude>40.183868</latitude>
+ <longitude>-74.819519</longitude>
+ </southWest>
+ <northEast>
+ <latitude>40.248291</latitude>
+ <longitude>-74.728798</longitude>
+ </northEast>
+ </boundingBox>
+
+ can be input as:
+
+ s=40.183868,w=-74.819519,n=40.248291,e=-74.728798
+ */
+ parser = new BoundingBoxParser("south=40.183868,west=-74.819519,north=40.248291,east=-74.728798");
+ assertEquals(40.183868d, parser.s, 0.0000001);
+ assertEquals(-74.819519d, parser.w, 0.0000001);
+ assertEquals(40.248291d, parser.n, 0.0000001);
+ assertEquals(-74.728798d, parser.e, 0.0000001);
+ }
+
+ public void testGwsExample() {
+ /* example XML:
+ <boundingbox>
+ <north>37.44899</north><south>37.3323</south><east>-121.98241</east><west>-122.06566</west>
+ </boundingbox>
+ can be input as: north:37.44899 south:37.3323, east:-121.98241 west:-122.06566
+ */
+ parser = new BoundingBoxParser(" north:37.44899 south:37.3323, east:-121.98241 west:-122.06566 ");
+ assertEquals(37.44899d, parser.n, 0.000001);
+ assertEquals(37.33230d, parser.s, 0.000001);
+ assertEquals(-121.98241d, parser.e, 0.000001);
+ assertEquals(-122.06566d, parser.w, 0.000001);
+ }
+
+ /**
+ * Tests various inputs that contain syntax errors.
+ */
+ public void testInputErrors() {
+ String message = "";
+ try {
+ parser = new BoundingBoxParser("n=10.11,e=2.02");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("Missing bounding box limits, n=true s=false e=true w=false", message);
+
+ try {
+ parser = new BoundingBoxParser("n=11.01,s=10.11,e=xyzzy,w=-122.2");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("Could not parse e limit 'xyzzy' as a number", message);
+
+ try {
+ parser = new BoundingBoxParser("n=11.01,n=10.11,e=-122.0,w=-122.2");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("multiple limits for 'n' boundary", message);
+
+ try {
+ parser = new BoundingBoxParser("s=11.01,s=10.11,e=-122.0,w=-122.2");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("multiple limits for 's' boundary", message);
+
+ try {
+ parser = new BoundingBoxParser("n=11.01,s=10.11,e=-122.0,e=-122.2");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("multiple limits for 'e' boundary", message);
+
+ try {
+ parser = new BoundingBoxParser("n=11.01,s=10.11,w=-122.0,w=-122.2");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("multiple limits for 'w' boundary", message);
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/geo/DegreesParserTestCase.java b/vespajlib/src/test/java/com/yahoo/geo/DegreesParserTestCase.java
new file mode 100644
index 00000000000..ed6fed5cbc7
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/geo/DegreesParserTestCase.java
@@ -0,0 +1,282 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.geo;
+
+/**
+ * Tests for the DegreesParser class.
+ *
+ * @author <a href="mailto:gunnarga@yahoo-inc.com">Gunnar Gauslaa Bergem</a>
+ */
+public class DegreesParserTestCase extends junit.framework.TestCase {
+
+ private DegreesParser parser;
+
+ public DegreesParserTestCase(String name) {
+ super(name);
+ }
+
+ /**
+ * Tests different inputs that should all produce 0 or -0.
+ */
+ public void testZero() {
+ parser = new DegreesParser("N0;E0");
+ assertEquals(0d, parser.latitude);
+ assertEquals(0d, parser.longitude);
+ parser = new DegreesParser("S0;W0");
+ assertEquals(-0d, parser.latitude);
+ assertEquals(-0d, parser.longitude);
+ parser = new DegreesParser("N0.0;E0.0");
+ assertEquals(0d, parser.latitude);
+ assertEquals(0d, parser.longitude);
+ parser = new DegreesParser("S0.0;W0.0");
+ assertEquals(-0d, parser.latitude);
+ assertEquals(-0d, parser.longitude);
+ parser = new DegreesParser("N0\u00B00'0;E0\u00B00'0");
+ assertEquals(0d, parser.latitude);
+ assertEquals(0d, parser.longitude);
+ parser = new DegreesParser("S0\u00B00'0;W0\u00B00'0");
+ assertEquals(-0d, parser.latitude);
+ assertEquals(-0d, parser.longitude);
+ parser = new DegreesParser("S0o0'0;W0o0'0");
+ assertEquals(-0d, parser.latitude);
+ assertEquals(-0d, parser.longitude);
+ }
+
+ /**
+ * Tests various legal inputs and print the output
+ */
+ public void testPrint() {
+ String here = "63N025.105;010E25.982";
+ parser = new DegreesParser(here);
+ System.out.println(here+" -> "+parser.latitude+"/"+parser.longitude+" (lat/long)");
+
+ here = "N63.418417 E10.433033";
+ parser = new DegreesParser(here);
+ System.out.println(here+" -> "+parser.latitude+"/"+parser.longitude+" (lat/long)");
+
+ here = "N63o025.105;E010o25.982";
+ parser = new DegreesParser(here);
+ System.out.println(here+" -> "+parser.latitude+"/"+parser.longitude+" (lat/long)");
+
+ here = "N63.418417;E10.433033";
+ parser = new DegreesParser(here);
+ System.out.println(here+" -> "+parser.latitude+"/"+parser.longitude+" (lat/long)");
+
+ here = "63.418417N;10.433033E";
+ parser = new DegreesParser(here);
+ System.out.println(here+" -> "+parser.latitude+"/"+parser.longitude+" (lat/long)");
+
+ here = "N37.417075;W122.025358";
+ parser = new DegreesParser(here);
+ System.out.println(here+" -> "+parser.latitude+"/"+parser.longitude+" (lat/long)");
+
+ here = "N37\u00B024.983;W122\u00B001.481";
+ parser = new DegreesParser(here);
+ System.out.println(here+" -> "+parser.latitude+"/"+parser.longitude+" (lat/long)");
+ }
+
+ /**
+ * Tests inputs that are close to 0.
+ */
+ public void testNearZero() {
+ parser = new DegreesParser("N0.0001;E0.0001");
+ assertEquals(0.0001, parser.latitude);
+ assertEquals(0.0001, parser.longitude);
+ parser = new DegreesParser("S0.0001;W0.0001");
+ assertEquals(-0.0001, parser.latitude);
+ assertEquals(-0.0001, parser.longitude);
+
+ parser = new DegreesParser("N0.000001;E0.000001");
+ assertEquals(0.000001, parser.latitude);
+ assertEquals(0.000001, parser.longitude);
+ parser = new DegreesParser("S0.000001;W0.000001");
+ assertEquals(-0.000001, parser.latitude);
+ assertEquals(-0.000001, parser.longitude);
+
+ parser = new DegreesParser("N0\u00B00'1;E0\u00B00'1");
+ assertEquals(1/3600d, parser.latitude);
+ assertEquals(1/3600d, parser.longitude);
+ parser = new DegreesParser("S0\u00B00'1;W0\u00B00'1");
+ assertEquals(-1/3600d, parser.latitude);
+ assertEquals(-1/3600d, parser.longitude);
+ }
+
+ /**
+ * Tests inputs that are close to latitude 90/-90 degrees and longitude 180/-180 degrees.
+ */
+ public void testNearBoundary() {
+
+ parser = new DegreesParser("N89.9999;E179.9999");
+ assertEquals(89.9999, parser.latitude);
+ assertEquals(179.9999, parser.longitude);
+ parser = new DegreesParser("S89.9999;W179.9999");
+ assertEquals(-89.9999, parser.latitude);
+ assertEquals(-179.9999, parser.longitude);
+
+ parser = new DegreesParser("N89.999999;E179.999999");
+ assertEquals(89.999999, parser.latitude);
+ assertEquals(179.999999, parser.longitude);
+ parser = new DegreesParser("S89.999999;W179.999999");
+ assertEquals(-89.999999, parser.latitude);
+ assertEquals(-179.999999, parser.longitude);
+
+ parser = new DegreesParser("N89\u00B059'59;E179\u00B059'59");
+ assertEquals(89+59/60d+59/3600d, parser.latitude);
+ assertEquals(179+59/60d+59/3600d, parser.longitude);
+ parser = new DegreesParser("S89\u00B059'59;W179\u00B059'59");
+ assertEquals(-(89+59/60d+59/3600d), parser.latitude);
+ assertEquals(-(179+59/60d+59/3600d), parser.longitude);
+ }
+
+ /**
+ * Tests inputs that are on latitude 90/-90 degrees and longitude 180/-180 degrees.
+ */
+ public void testOnBoundary() {
+ parser = new DegreesParser("N90;E180");
+ assertEquals(90d, parser.latitude);
+ assertEquals(180d, parser.longitude);
+ parser = new DegreesParser("S90;W180");
+ assertEquals(-90d, parser.latitude);
+ assertEquals(-180d, parser.longitude);
+
+ parser = new DegreesParser("N90\u00B00'0;E180\u00B00'0");
+ assertEquals(90d, parser.latitude);
+ assertEquals(180d, parser.longitude);
+ parser = new DegreesParser("S90\u00B00'0;W180\u00B00'0");
+ assertEquals(-90d, parser.latitude);
+ assertEquals(-180d, parser.longitude);
+ }
+
+ /**
+ * Tests inputs that are above latitude 90/-90 degrees and longitude 180/-180 degrees.
+ */
+ public void testAboveBoundary() {
+ String message = "";
+ try {
+ parser = new DegreesParser("N90.0001;E0");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("out of range [-90,+90]: 90.0001", message);
+ try {
+ parser = new DegreesParser("S90.0001;E0");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("out of range [-90,+90]: -90.0001", message);
+ try {
+ parser = new DegreesParser("N0;E180.0001");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("out of range [-180,+180]: 180.0001", message);
+ try {
+ parser = new DegreesParser("N0;W180.0001");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("out of range [-180,+180]: -180.0001", message);
+ try {
+ parser = new DegreesParser("N90.000001;E0");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("out of range [-90,+90]: 90.000001", message);
+ try {
+ parser = new DegreesParser("S90.000001;E0");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("out of range [-90,+90]: -90.000001", message);
+ try {
+ parser = new DegreesParser("N0;E180.000001");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("out of range [-180,+180]: 180.000001", message);
+ try {
+ parser = new DegreesParser("N0;W180.000001");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("out of range [-180,+180]: -180.000001", message);
+ }
+
+ /**
+ * Tests various inputs that contain syntax errors.
+ */
+ public void testInputErrors() {
+ String message = "";
+ try {
+ parser = new DegreesParser("N90;S90");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("found latitude (N or S) twice", message);
+ try {
+ parser = new DegreesParser("E120;W120");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("found longitude (E or W) twice", message);
+ try {
+ parser = new DegreesParser("N90;90");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("end of field without any compass direction seen", message);
+ try {
+ parser = new DegreesParser("N90;E");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("end of field without any number seen", message);
+ try {
+ parser = new DegreesParser(";");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("end of field without any compass direction seen", message);
+ try {
+ parser = new DegreesParser("25;60");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("end of field without any compass direction seen", message);
+ try {
+ parser = new DegreesParser("NW25;SW60");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("already set direction once, cannot add direction: W", message);
+ try {
+ parser = new DegreesParser("N16.25\u00B0;W60");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("cannot have fractional degrees before degrees sign", message);
+ try {
+ parser = new DegreesParser("N16\u00B022.40';W60");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("cannot have fractional minutes before minutes sign", message);
+ try {
+ parser = new DegreesParser("");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("end of field without any compass direction seen", message);
+ try {
+ parser = new DegreesParser("Yahoo!");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("invalid character: Y", message);
+ try {
+ parser = new DegreesParser("N63O025.105;E010O25.982");
+ } catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ assertEquals("invalid character: O", message);
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/geo/ZCurveTestCase.java b/vespajlib/src/test/java/com/yahoo/geo/ZCurveTestCase.java
new file mode 100644
index 00000000000..b5536fca510
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/geo/ZCurveTestCase.java
@@ -0,0 +1,204 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.geo;
+
+/**
+ * Tests for the ZCurve class.
+ *
+ * @author gjoranv
+ */
+public class ZCurveTestCase extends junit.framework.TestCase {
+
+ public ZCurveTestCase(String name) {
+ super(name);
+ }
+
+ /**
+ * Verify that encoded values return the expected bit pattern
+ */
+ public void testEncoding() {
+ int x = 0;
+ int y = 0;
+ long z = ZCurve.encode(x, y);
+ assertEquals(0, z);
+
+ x = Integer.MAX_VALUE;
+ y = Integer.MAX_VALUE;
+ z = ZCurve.encode(x, y);
+ assertEquals(0x3fffffffffffffffL, z);
+
+ x = Integer.MIN_VALUE;
+ y = Integer.MIN_VALUE;
+ z = ZCurve.encode(x, y);
+ assertEquals(0xc000000000000000L, z);
+
+ x = Integer.MIN_VALUE;
+ y = Integer.MAX_VALUE;
+ z = ZCurve.encode(x, y);
+ assertEquals(0x6aaaaaaaaaaaaaaaL, z);
+
+ x = -1;
+ y = -1;
+ z = ZCurve.encode(x, y);
+ assertEquals(0xffffffffffffffffL, z);
+
+ x = Integer.MAX_VALUE / 2;
+ y = Integer.MIN_VALUE / 2;
+ z = ZCurve.encode(x, y);
+ assertEquals(0xa555555555555555L, z);
+ }
+
+ /**
+ * Verify that decoded values are equal to inputs in different cases
+ */
+ public void testDecoding() {
+ int x = 0;
+ int y = 0;
+ long z = ZCurve.encode(x, y);
+ int[] xy = ZCurve.decode(z);
+ assertEquals(x, xy[0]);
+ assertEquals(y, xy[1]);
+
+ x = Integer.MAX_VALUE;
+ y = Integer.MAX_VALUE;
+ z = ZCurve.encode(x, y);
+ xy = ZCurve.decode(z);
+ assertEquals(x, xy[0]);
+ assertEquals(y, xy[1]);
+
+ x = Integer.MIN_VALUE;
+ y = Integer.MIN_VALUE;
+ z = ZCurve.encode(x, y);
+ xy = ZCurve.decode(z);
+ assertEquals(x, xy[0]);
+ assertEquals(y, xy[1]);
+
+ x = Integer.MIN_VALUE;
+ y = Integer.MAX_VALUE;
+ z = ZCurve.encode(x, y);
+ xy = ZCurve.decode(z);
+ assertEquals(x, xy[0]);
+ assertEquals(y, xy[1]);
+
+ x = -18;
+ y = 1333;
+ z = ZCurve.encode(x, y);
+ xy = ZCurve.decode(z);
+ assertEquals(x, xy[0]);
+ assertEquals(y, xy[1]);
+
+ x = -1333;
+ y = 18;
+ z = ZCurve.encode(x, y);
+ xy = ZCurve.decode(z);
+ assertEquals(x, xy[0]);
+ assertEquals(y, xy[1]);
+ }
+
+
+
+ /**
+ * Verify that encoded values return the expected bit pattern
+ */
+ public void testEncoding_slow() {
+ int x = 0;
+ int y = 0;
+ long z = ZCurve.encode_slow(x, y);
+ assertEquals(0, z);
+
+ x = Integer.MIN_VALUE;
+ y = Integer.MIN_VALUE;
+ z = ZCurve.encode_slow(x, y);
+ assertEquals(0xc000000000000000L, z);
+
+ x = Integer.MIN_VALUE;
+ y = Integer.MAX_VALUE;
+ z = ZCurve.encode_slow(x, y);
+ assertEquals(0x6aaaaaaaaaaaaaaaL, z);
+
+ x = Integer.MAX_VALUE;
+ y = Integer.MAX_VALUE;
+ z = ZCurve.encode_slow(x, y);
+ assertEquals(0x3fffffffffffffffL, z);
+
+ x = -1;
+ y = -1;
+ z = ZCurve.encode_slow(x, y);
+ assertEquals(0xffffffffffffffffL, z);
+
+ x = Integer.MAX_VALUE / 2;
+ y = Integer.MIN_VALUE / 2;
+ z = ZCurve.encode_slow(x, y);
+ assertEquals(0xa555555555555555L, z);
+ }
+
+ /**
+ * Verify that decoded values are equal to inputs in different cases
+ */
+ public void testDecoding_slow() {
+ int x = 0;
+ int y = 0;
+ long z = ZCurve.encode_slow(x, y);
+ int[] xy = ZCurve.decode_slow(z);
+ assertEquals(xy[0], x);
+ assertEquals(xy[1], y);
+
+ x = Integer.MAX_VALUE;
+ y = Integer.MAX_VALUE;
+ z = ZCurve.encode_slow(x, y);
+ xy = ZCurve.decode_slow(z);
+ assertEquals(xy[0], x);
+ assertEquals(xy[1], y);
+
+ x = Integer.MIN_VALUE;
+ y = Integer.MIN_VALUE;
+ z = ZCurve.encode_slow(x, y);
+ xy = ZCurve.decode_slow(z);
+ assertEquals(xy[0], x);
+ assertEquals(xy[1], y);
+
+ x = Integer.MIN_VALUE;
+ y = Integer.MAX_VALUE;
+ z = ZCurve.encode_slow(x, y);
+ xy = ZCurve.decode_slow(z);
+ assertEquals(xy[0], x);
+ assertEquals(xy[1], y);
+
+ x = -18;
+ y = 1333;
+ z = ZCurve.encode_slow(x, y);
+ xy = ZCurve.decode_slow(z);
+ assertEquals(xy[0], x);
+ assertEquals(xy[1], y);
+
+ x = -1333;
+ y = 18;
+ z = ZCurve.encode_slow(x, y);
+ xy = ZCurve.decode_slow(z);
+ assertEquals(xy[0], x);
+ assertEquals(xy[1], y);
+ }
+
+ public void testBenchmarkEncoding() {
+ int limit = 2000000;
+
+ long z1 = 0L;
+ long start = System.currentTimeMillis();
+ for (int i=0; i<limit; i++) {
+ z1 += ZCurve.encode(i,-i);
+ }
+ long elapsed = System.currentTimeMillis() - start;
+ System.out.println("Fast method: elapsed time: " + elapsed + " ms");
+ System.out.println("Per encoding: " + elapsed/(1.0*limit) * 1000000 + " ns");
+
+ long z2 = 0L;
+ start = System.currentTimeMillis();
+ for (int i=0; i<limit; i++) {
+ z2 += ZCurve.encode_slow(i,-i);
+ }
+ elapsed = System.currentTimeMillis() - start;
+ System.out.println("Slow method: elapsed time: " + elapsed + " ms");
+ System.out.println("Per encoding: " + elapsed/(1.0*limit) * 1000000 + " ns");
+ assertEquals(z1, z2);
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/io/BlobTestCase.java b/vespajlib/src/test/java/com/yahoo/io/BlobTestCase.java
new file mode 100644
index 00000000000..9643d70ba5e
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/BlobTestCase.java
@@ -0,0 +1,95 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io;
+
+import java.nio.ByteBuffer;
+
+public class BlobTestCase extends junit.framework.TestCase {
+
+ public void testEmpty() {
+ Blob empty = new Blob();
+ assertTrue(empty.get() != null);
+ assertEquals(0, empty.get().length);
+ }
+
+ public void testCopyArray() {
+ byte[] d = { 1, 2, 3 };
+ Blob b = new Blob(d);
+ d[0] = 7;
+ d[1] = 8;
+ d[2] = 9;
+ assertEquals(3, b.get().length);
+ assertEquals(1, b.get()[0]);
+ assertEquals(2, b.get()[1]);
+ assertEquals(3, b.get()[2]);
+ }
+
+ public void testCopyArraySubset() {
+ byte[] d = { 1, 2, 3 };
+ Blob b = new Blob(d, 1, 1);
+ d[0] = 7;
+ d[1] = 8;
+ d[2] = 9;
+ assertEquals(1, b.get().length);
+ assertEquals(2, b.get()[0]);
+ }
+
+ public void testCopyBlob() {
+ byte[] d = { 1, 2, 3 };
+ Blob b = new Blob(d);
+ Blob x = new Blob(b);
+ b.get()[1] = 4;
+ assertEquals(3, x.get().length);
+ assertEquals(1, x.get()[0]);
+ assertEquals(4, b.get()[1]);
+ assertEquals(2, x.get()[1]);
+ assertEquals(3, x.get()[2]);
+ }
+
+ public void testReadBuffer() {
+ ByteBuffer buf = ByteBuffer.allocate(100);
+ buf.put((byte)1);
+ buf.put((byte)2);
+ buf.put((byte)3);
+ buf.flip();
+ assertEquals(3, buf.remaining());
+ Blob b = new Blob(buf);
+ assertEquals(0, buf.remaining());
+ assertEquals(3, b.get().length);
+ assertEquals(1, b.get()[0]);
+ assertEquals(2, b.get()[1]);
+ assertEquals(3, b.get()[2]);
+ }
+
+ public void testReadPartialBuffer() {
+ ByteBuffer buf = ByteBuffer.allocate(100);
+ buf.put((byte)1);
+ buf.put((byte)2);
+ buf.put((byte)3);
+ buf.put((byte)4);
+ buf.put((byte)5);
+ buf.flip();
+ assertEquals(5, buf.remaining());
+ Blob b = new Blob(buf, 3);
+ assertEquals(2, buf.remaining());
+ assertEquals(3, b.get().length);
+ assertEquals(1, b.get()[0]);
+ assertEquals(2, b.get()[1]);
+ assertEquals(3, b.get()[2]);
+ assertEquals(4, buf.get());
+ assertEquals(5, buf.get());
+ assertEquals(0, buf.remaining());
+ }
+
+ public void testWriteBuffer() {
+ byte[] d = { 1, 2, 3 };
+ Blob b = new Blob(d);
+ ByteBuffer buf = ByteBuffer.allocate(100);
+ b.write(buf);
+ buf.flip();
+ assertEquals(3, buf.remaining());
+ assertEquals(1, buf.get());
+ assertEquals(2, buf.get());
+ assertEquals(3, buf.get());
+ assertEquals(0, buf.remaining());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/io/ByteWriterTestCase.java b/vespajlib/src/test/java/com/yahoo/io/ByteWriterTestCase.java
new file mode 100644
index 00000000000..d5025dd03aa
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/ByteWriterTestCase.java
@@ -0,0 +1,444 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.util.Arrays;
+import java.util.IdentityHashMap;
+
+import com.yahoo.text.Utf8;
+import com.yahoo.text.Utf8Array;
+
+/**
+ * Test the ByteWriter class. ByteWriter is also implicitly tested in
+ * com.yahoo.prelude.templates.test.TemplateTestCase.
+ *
+ * @author <a href="mailt:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class ByteWriterTestCase extends junit.framework.TestCase {
+ private CharsetEncoder encoder;
+ private ByteArrayOutputStream stream;
+
+ // TODO split BufferChain tests from ByteWriter tests
+ public ByteWriterTestCase (String name) {
+ super(name);
+ Charset cs = Charset.forName("UTF-8");
+ encoder = cs.newEncoder();
+ stream = new ByteArrayOutputStream();
+
+ }
+
+ /**
+ * A stream which does nothing, but complains if it is called and asked to
+ * do nothing.
+ */
+ private static class CurmudgeonlyStream extends OutputStream {
+
+ static final String ZERO_LENGTH_WRITE = "Was asked to do zero length write.";
+
+ @Override
+ public void write(int b) throws IOException {
+ // NOP
+
+ }
+
+ @Override
+ public void close() throws IOException {
+ // NOP
+ }
+
+ @Override
+ public void flush() throws IOException {
+ // NOP
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ if (len == 0) {
+ throw new IOException(ZERO_LENGTH_WRITE);
+ }
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ if (b.length == 0) {
+ throw new IOException(ZERO_LENGTH_WRITE);
+ }
+ }
+
+ }
+
+ public void testMuchData() throws java.io.IOException {
+ final int SINGLE_BUFFER = 500;
+ final int APPENDS = 500;
+ assertTrue("Code has been changed making constants in this test meaningless, review test.",
+ BufferChain.BUFFERSIZE * BufferChain.MAXBUFFERS < SINGLE_BUFFER * APPENDS);
+ assertTrue("Code has been changed making constants in this test meaningless, review test.",
+ BufferChain.WATERMARK > SINGLE_BUFFER);
+ stream.reset();
+ byte[] c = new byte[SINGLE_BUFFER];
+ Arrays.fill(c, (byte) 'a');
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ for (int i = APPENDS; i > 0; --i) {
+ bw.append(c);
+ }
+ bw.close();
+ byte[] res = stream.toByteArray();
+ assertEquals("BufferChain has duplicated or lost a buffer.", SINGLE_BUFFER * APPENDS, res.length);
+ byte[] completeData = new byte[SINGLE_BUFFER * APPENDS];
+ Arrays.fill(completeData, (byte) 'a');
+ assertTrue("ByteWriter seems to have introduced data errors.", Arrays.equals(completeData, res));
+ }
+
+ public void testLongString() throws IOException {
+ final int length = BufferChain.BUFFERSIZE * BufferChain.MAXBUFFERS * 3;
+ StringBuilder b = new StringBuilder(length);
+ String s;
+ for (int i = length; i > 0; --i) {
+ b.append("\u00E5");
+ }
+ s = b.toString();
+ stream.reset();
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ bw.write(s);
+ bw.close();
+ String res = stream.toString("UTF-8");
+ assertEquals(s, res);
+ }
+
+ public void testNoSpuriousWrite() throws IOException {
+ OutputStream grumpy = new CurmudgeonlyStream();
+ ByteWriter bw = new ByteWriter(grumpy, encoder);
+ final int SINGLE_BUFFER = 500;
+ final int APPENDS = 500;
+ assertTrue("Code has been changed making constants in this test meaningless, review test.",
+ BufferChain.BUFFERSIZE * BufferChain.MAXBUFFERS < SINGLE_BUFFER * APPENDS);
+ assertTrue("Code has been changed making constants in this test meaningless, review test.",
+ BufferChain.WATERMARK > SINGLE_BUFFER);
+ stream.reset();
+ byte[] c = new byte[SINGLE_BUFFER];
+ for (int i = APPENDS; i > 0; --i) {
+ try {
+ bw.append(c);
+ } catch (IOException e) {
+ if (e.getMessage() == CurmudgeonlyStream.ZERO_LENGTH_WRITE) {
+ fail(CurmudgeonlyStream.ZERO_LENGTH_WRITE);
+ } else {
+ throw e;
+ }
+ }
+ }
+ try {
+ bw.close();
+ } catch (IOException e) {
+ if (e.getMessage() == CurmudgeonlyStream.ZERO_LENGTH_WRITE) {
+ fail(CurmudgeonlyStream.ZERO_LENGTH_WRITE);
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ public void testDoubleFlush() throws IOException {
+ stream.reset();
+ byte[] c = new byte[] { 97, 98, 99 };
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ bw.append(c);
+ bw.flush();
+ bw.flush();
+ bw.close();
+ byte[] res = stream.toByteArray();
+ assertTrue(Arrays.equals(new byte[] { 97, 98, 99 }, res));
+ }
+
+ public void testCharArrays() throws java.io.IOException {
+ stream.reset();
+ char[] c = new char[] { 'a', 'b', 'c', '\u00F8' };
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ bw.write(c);
+ bw.close();
+ byte[] res = stream.toByteArray();
+ assertTrue(Arrays.equals(new byte[] { 97, 98, 99, (byte) 0xc3, (byte) 0xb8 }, res));
+ }
+
+ public void testByteBuffers() throws java.io.IOException {
+ stream.reset();
+ ByteBuffer b = ByteBuffer.allocate(16);
+ b.put((byte) 97);
+ b.put((byte) 98);
+ b.put((byte) 99);
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ b.flip();
+ bw.append(b);
+ bw.close();
+ byte[] res = stream.toByteArray();
+ assertTrue(Arrays.equals(new byte[] { 97, 98, 99 }, res));
+ }
+
+ public void testByteArrays() throws java.io.IOException {
+ stream.reset();
+ byte[] c = new byte[] { 97, 98, 99 };
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ bw.append(c);
+ bw.close();
+ byte[] res = stream.toByteArray();
+ assertTrue(Arrays.equals(new byte[] { 97, 98, 99 }, res));
+ }
+
+ public void testByteArrayWithOffset() throws java.io.IOException {
+ final int length = BufferChain.BUFFERSIZE * 3 / 2;
+ final int offset = 1;
+ final byte invalid = 3;
+ final byte valid = 2;
+ stream.reset();
+ byte[] c = new byte[length];
+ c[0] = invalid;
+ for (int i = offset; i < length; ++i) {
+ c[i] = valid;
+ }
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ bw.append(c, offset, length - offset);
+ bw.close();
+ byte[] res = stream.toByteArray();
+ assertEquals(length - offset, res.length);
+ assertEquals(valid, res[0]);
+ }
+
+ public void testStrings() throws java.io.IOException {
+ stream.reset();
+ String c = "abc\u00F8";
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ bw.write(c);
+ bw.close();
+ byte[] res = stream.toByteArray();
+ assertTrue(Arrays.equals(new byte[] { 97, 98, 99, (byte) 0xc3, (byte) 0xb8 }, res));
+ }
+
+ public void testStringsAndByteArrays() throws java.io.IOException {
+ stream.reset();
+ String c = "abc\u00F8";
+ byte[] b = new byte[] { 97, 98, 99 };
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ bw.write(c);
+ bw.append(b);
+ bw.close();
+ byte[] res = stream.toByteArray();
+ assertTrue(Arrays.equals(new byte[] { 97, 98, 99, (byte) 0xc3, (byte) 0xb8, 97, 98, 99 }, res));
+ }
+
+ public void testByteBuffersAndByteArrays() throws java.io.IOException {
+ stream.reset();
+ ByteBuffer b = ByteBuffer.allocate(16);
+ b.put((byte) 97);
+ b.put((byte) 98);
+ b.put((byte) 99);
+ b.flip();
+ byte[] c = new byte[] { 100, 101, 102 };
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ bw.append(b);
+ bw.append(c);
+ bw.close();
+ byte[] res = stream.toByteArray();
+ assertTrue(Arrays.equals(new byte[] { 97, 98, 99, 100, 101, 102 }, res));
+ }
+
+ public void testOverFlow() throws java.io.IOException {
+ stream.reset();
+ byte[] b = new byte[] { 97, 98, 99 };
+ ByteWriter bw = new ByteWriter(stream, encoder);
+ int i = 0;
+ while (i < 5000) {
+ bw.append(b);
+ ++i;
+ }
+ bw.close();
+ byte[] res = stream.toByteArray();
+ assertEquals(15000, res.length);
+ i = 0;
+ int base = 0;
+ while (i < 5000) {
+ byte[] sub = new byte[3];
+ System.arraycopy(res, base, sub, 0, 3);
+ assertTrue(Arrays.equals(new byte[] { 97, 98, 99 }, sub));
+ base += 3;
+ ++i;
+ }
+ }
+
+ public void testUnMappableCharacter() throws java.io.IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ ByteWriter writer = new ByteWriter(stream, Charset.forName("ascii").newEncoder());
+ writer.write("yahoo\u9999bahoo");
+ writer.close();
+ assertTrue(stream.toString("ascii").contains("yahoo"));
+ assertTrue(stream.toString("ascii").contains("bahoo"));
+ }
+
+ public void testNoRecycling() throws IOException {
+ final int SINGLE_BUFFER = 500;
+ final int APPENDS = 500;
+ assertTrue(
+ "Code has been changed making constants in this test meaningless, review test.",
+ BufferChain.BUFFERSIZE * BufferChain.MAXBUFFERS < SINGLE_BUFFER
+ * APPENDS);
+ assertTrue(
+ "Code has been changed making constants in this test meaningless, review test.",
+ BufferChain.WATERMARK > SINGLE_BUFFER);
+ byte[] c = new byte[SINGLE_BUFFER];
+ Arrays.fill(c, (byte) 'a');
+ OnlyUniqueBuffers b = new OnlyUniqueBuffers();
+ try {
+ for (int i = APPENDS; i > 0; --i) {
+ b.insert(ByteBuffer.wrap(c));
+ }
+ b.flush();
+ } catch (IOException e) {
+ if (e.getMessage() == OnlyUniqueBuffers.RECYCLED_BYTE_BUFFER) {
+ fail(OnlyUniqueBuffers.RECYCLED_BYTE_BUFFER);
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ public void testGetEncoding() throws java.io.IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ assertEquals(Utf8.getCharset(), b.getEncoding());
+ b.close();
+ }
+
+ public void testWriteLong() throws java.io.IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ b.write(1000L * 1000L * 1000L * 1000L);
+ b.close();
+ assertArrayEquals(Utf8.toBytes("1000000000000"), stream.toByteArray());
+ }
+
+ public void testWriteInt() throws java.io.IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ b.write((int) 'z');
+ b.close();
+ assertArrayEquals(Utf8.toBytes("z"), stream.toByteArray());
+ }
+
+ public void testSurrogatePairs() throws java.io.IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ b.write(0xD800);
+ b.write(0xDFD0);
+ b.close();
+ assertArrayEquals(Utf8.toBytes("\uD800\uDFD0"), stream.toByteArray());
+ }
+
+ public void testSurrogatePairsMixedWithSingleCharacters() throws java.io.IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ b.write(0x00F8);
+ b.write(0xD800);
+ b.write(0xDFD0);
+ b.write(0x00F8);
+ b.write(0xD800);
+ b.write((int) 'a');
+ b.write(0xDFD0);
+ b.write((int) 'b');
+ b.close();
+ assertArrayEquals(Utf8.toBytes("\u00F8\uD800\uDFD0\u00F8ab"), stream.toByteArray());
+ }
+
+
+ public void testWriteDouble() throws java.io.IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ b.write(12.0d);
+ b.close();
+ assertArrayEquals(Utf8.toBytes("12.0"), stream.toByteArray());
+ }
+
+ public void testWriteFloat() throws java.io.IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ b.write(12.0f);
+ b.close();
+ assertArrayEquals(Utf8.toBytes("12.0"), stream.toByteArray());
+ }
+
+ public void testWriteShort() throws java.io.IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ b.write((short) 12);
+ b.close();
+ assertArrayEquals(Utf8.toBytes("12"), stream.toByteArray());
+ }
+
+ public void testWriteBoolean() throws java.io.IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ b.write(true);
+ b.close();
+ assertArrayEquals(Utf8.toBytes("true"), stream.toByteArray());
+ }
+
+ public void testAppendSingleByte() throws java.io.IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ b.append((byte) 'a');
+ b.close();
+ assertArrayEquals(new byte[] { (byte) 'a' }, stream.toByteArray());
+ }
+
+ public void testAppended() throws IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ final String s = "nalle";
+ b.write(s);
+ b.close();
+ final byte[] bytes = Utf8.toBytes(s);
+ assertArrayEquals(bytes, stream.toByteArray());
+ assertEquals(bytes.length, b.appended());
+ }
+
+ public void testWriteUtf8Array() throws IOException {
+ stream.reset();
+ ByteWriter b = new ByteWriter(stream, encoder);
+ final byte[] bytes = Utf8.toBytes("nalle");
+ b.write(new Utf8Array(bytes));
+ b.close();
+ assertArrayEquals(bytes, stream.toByteArray());
+ }
+
+ private static class OnlyUniqueBuffers implements WritableByteTransmitter {
+ static final String RECYCLED_BYTE_BUFFER = "Got a ByteBuffer instance twice.";
+ private final IdentityHashMap<ByteBuffer, ?> buffers = new IdentityHashMap<ByteBuffer, Object>();
+ private final BufferChain datastore;
+
+ public OnlyUniqueBuffers() {
+ datastore = new BufferChain(this);
+ }
+
+ public void insert(ByteBuffer b) throws IOException {
+ datastore.append(b);
+ }
+
+ @Override
+ public void send(ByteBuffer src) throws IOException {
+ if (buffers.containsKey(src)) {
+ throw new IOException(RECYCLED_BYTE_BUFFER);
+ } else {
+ buffers.put(src, null);
+ }
+ }
+
+ public void flush() throws IOException {
+ datastore.flush();
+ }
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/io/FatalErrorHandlerTestCase.java b/vespajlib/src/test/java/com/yahoo/io/FatalErrorHandlerTestCase.java
new file mode 100644
index 00000000000..d4889c1fa96
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/FatalErrorHandlerTestCase.java
@@ -0,0 +1,55 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io;
+
+import static org.junit.Assert.*;
+
+import java.security.Permission;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Just to remove noise from the coverage report.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class FatalErrorHandlerTestCase {
+ private static final class AvoidExiting extends SecurityManager {
+
+ @Override
+ public void checkPermission(Permission perm) {
+ }
+
+ @Override
+ public void checkExit(int status) {
+ throw new SecurityException();
+ }
+
+ }
+
+ private FatalErrorHandler h;
+
+ @Before
+ public void setUp() throws Exception {
+ h = new FatalErrorHandler();
+ System.setSecurityManager(new AvoidExiting());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ System.setSecurityManager(null);
+ }
+
+ @Test
+ public final void testHandle() {
+ boolean caught = false;
+ try {
+ h.handle(new Throwable(), "abc");
+ } catch (SecurityException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/io/FileReadTestCase.java b/vespajlib/src/test/java/com/yahoo/io/FileReadTestCase.java
new file mode 100644
index 00000000000..5ebdeeb797e
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/FileReadTestCase.java
@@ -0,0 +1,39 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+
+
+public class FileReadTestCase extends junit.framework.TestCase {
+
+ @Test
+ public void testReadByteArray() throws IOException {
+ byte[] thisFile = IOUtils.readFileBytes(new File("src/test/java/com/yahoo/io/FileReadTestCase.java"));
+ String str = new String(thisFile, Charset.forName("US-ASCII"));
+ assertTrue(str.startsWith("// Copyright 2016 Yahoo Inc."));
+ assertTrue(str.endsWith("// Yeppers\n"));
+ }
+
+ @Test
+ public void testReadString() throws IOException {
+ String str = IOUtils.readFile(new File("src/test/java/com/yahoo/io/FileReadTestCase.java"));
+ assertTrue(str.startsWith("// Copyright 2016 Yahoo Inc."));
+ assertTrue(str.endsWith("// Yeppers\n"));
+ }
+
+ @Test
+ public void testReadAllFromReader() throws IOException {
+ assertEquals(IOUtils.readAll(new StringReader("")), "");
+ assertEquals(IOUtils.readAll(new StringReader("hei")), "hei");
+ assertEquals(IOUtils.readAll(new StringReader("hei\nhaa")), "hei\nhaa");
+ assertEquals(IOUtils.readAll(new StringReader("hei\nhaa\n")), "hei\nhaa\n");
+ }
+
+}
+
+// Yeppers
diff --git a/vespajlib/src/test/java/com/yahoo/io/GrowableBufferOutputStreamTestCase.java b/vespajlib/src/test/java/com/yahoo/io/GrowableBufferOutputStreamTestCase.java
new file mode 100644
index 00000000000..71568a3cfbc
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/GrowableBufferOutputStreamTestCase.java
@@ -0,0 +1,126 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io;
+
+import java.nio.channels.WritableByteChannel;
+import java.nio.ByteBuffer;
+import java.io.IOException;
+import com.yahoo.io.GrowableBufferOutputStream;
+
+
+/**
+ * Tests the GrowableBufferOutputStream
+ *
+ * @author <a href="mailto:borud@yahoo-inc.com">Bjorn Borud</a>
+ */
+public class GrowableBufferOutputStreamTestCase extends junit.framework.TestCase {
+ private byte[] testData;
+
+ static class DummyWritableByteChannel implements WritableByteChannel {
+ private ByteBuffer buffer;
+
+ public DummyWritableByteChannel(ByteBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ public int write(ByteBuffer src) throws IOException {
+ int written = Math.min(src.remaining(), buffer.remaining());
+
+ if (buffer.remaining() < src.remaining()) {
+ ByteBuffer tmp = src.slice();
+
+ tmp.limit(written);
+ src.position(src.position() + written);
+ } else {
+ buffer.put(src);
+ }
+ return written;
+ }
+
+ public boolean isOpen() {
+ return true;
+ }
+
+ public void close() throws IOException {}
+ }
+
+ public GrowableBufferOutputStreamTestCase(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ testData = new byte[100];
+ for (int i = 0; i < 100; ++i) {
+ testData[i] = (byte) i;
+ }
+ }
+
+ public void testSimple() throws IOException {
+ GrowableBufferOutputStream g = new GrowableBufferOutputStream(10, 5);
+
+ g.write(testData, 0, 100);
+ g.flush();
+ assertEquals(10, g.numWritableBuffers());
+ assertEquals(100, g.writableSize());
+
+ ByteBuffer sink = ByteBuffer.allocate(60);
+ DummyWritableByteChannel channel = new DummyWritableByteChannel(sink);
+ int written = g.channelWrite(channel);
+
+ assertEquals(60, written);
+ assertEquals(60, sink.position());
+ assertEquals(40, g.writableSize());
+
+ // there should be 4 buffers left now
+ assertEquals(4, g.numWritableBuffers());
+
+ // ensure that we got what we expected
+ for (int i = 0; i < 60; ++i) {
+ if (((int) sink.get(i)) != i) {
+ fail();
+ }
+ }
+
+ // then we write more data
+ g.write(testData, 0, 100);
+ g.flush();
+ assertEquals(140, g.writableSize());
+
+ // ...which implies that we should now have 14 writable buffers
+ assertEquals(14, g.numWritableBuffers());
+
+ // reset the sink so it can consume more data
+ sink.clear();
+
+ // then write more to the DummyWritableByteChannel
+ written = g.channelWrite(channel);
+ assertEquals(60, written);
+ assertEquals(60, sink.position());
+ assertEquals(80, g.writableSize());
+
+ // now there should be 8 buffers
+ assertEquals(8, g.numWritableBuffers());
+
+ // ensure that we got what we expected
+ for (int i = 0; i < 60; ++i) {
+ int val = (int) sink.get(i);
+ int expected = (i + 60) % 100;
+
+ if (val != expected) {
+ fail("Value was " + val + " and not " + i);
+ }
+ }
+
+ // when we clear there should be no buffers
+ g.clear();
+ assertEquals(0, g.numWritableBuffers());
+ assertEquals(0, g.writableSize());
+
+ // ditto after flush after clear
+ g.flush();
+ assertEquals(0, g.numWritableBuffers());
+
+ // flush the cache too
+ g.clearAll();
+ assertEquals(0, g.numWritableBuffers());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/io/GrowableByteBufferTestCase.java b/vespajlib/src/test/java/com/yahoo/io/GrowableByteBufferTestCase.java
new file mode 100644
index 00000000000..ffc3d6dfe64
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/GrowableByteBufferTestCase.java
@@ -0,0 +1,756 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.InvalidMarkException;
+import java.nio.ReadOnlyBufferException;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertArrayEquals;;
+
+/**
+ * Tests GrowableByteBuffer.
+ *
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ */
+public class GrowableByteBufferTestCase extends junit.framework.TestCase {
+ public void testBuffer() {
+ GrowableByteBuffer buf = new GrowableByteBuffer(20, 1.5f);
+
+ buf.putChar((char) 5);
+ assertEquals(2, buf.position());
+
+ buf.putDouble(983.982d);
+ assertEquals(10, buf.position());
+
+ buf.putFloat(94.322f);
+ assertEquals(14, buf.position());
+
+ buf.putInt(98);
+ assertEquals(18, buf.position());
+
+ assertEquals(20, buf.capacity());
+
+ buf.putLong(983L);
+ assertEquals(26, buf.position());
+
+ // Adding fudge factors and other fun to the growth rate,
+ // makes capacity() suboptimal to test, so this should perhaps
+ // be removed
+ // TODO: Better test of growth rate
+ assertEquals(130, buf.capacity());
+
+ buf.putShort((short) 4);
+ assertEquals(28, buf.position());
+
+
+ buf.position(0);
+ assertEquals((char) 5, buf.getChar());
+ assertEquals(2, buf.position());
+
+ assertEquals((int) (983.982d * 1000d), (int) (buf.getDouble() * 1000d));
+ assertEquals(10, buf.position());
+
+ assertEquals((int) (94.322f * 1000f), (int) (buf.getFloat() * 1000f));
+ assertEquals(14, buf.position());
+
+ assertEquals(98, buf.getInt());
+ assertEquals(18, buf.position());
+
+ assertEquals(983L, buf.getLong());
+ assertEquals(26, buf.position());
+
+ assertEquals((short) 4, buf.getShort());
+ assertEquals(28, buf.position());
+
+
+ byte[] twoBytes = new byte[2];
+ buf.put(twoBytes);
+ assertEquals(30, buf.position());
+ assertEquals(130, buf.capacity());
+
+ buf.put((byte) 1);
+ assertEquals(31, buf.position());
+ assertEquals(130, buf.capacity());
+
+ ByteBuffer tmpBuf = ByteBuffer.allocate(15);
+ tmpBuf.putInt(56);
+ tmpBuf.position(0);
+ buf.put(tmpBuf);
+ assertEquals(46, buf.position());
+ assertEquals(130, buf.capacity());
+ }
+
+ public void testGrowth() {
+ GrowableByteBuffer buf = new GrowableByteBuffer(256, 2.0f);
+
+ //add bytes almost to the boundary
+ for (int i = 0; i < 255; i++) {
+ buf.put((byte) 0);
+ }
+
+ //We are just before the boundary now.
+ assertEquals(255, buf.position());
+ assertEquals(256, buf.capacity());
+ assertEquals(256, buf.limit());
+
+ //Test adding one more byte.
+ buf.put((byte) 0);
+ //The buffer is full.
+ assertEquals(256, buf.position());
+ assertEquals(256, buf.capacity());
+ assertEquals(256, buf.limit());
+
+ //Adding one more byte should make it grow.
+ buf.put((byte) 0);
+ assertEquals(257, buf.position());
+ assertEquals(612, buf.capacity());
+ assertEquals(612, buf.limit());
+
+ //add a buffer exactly to the boundary
+ byte[] bytes = new byte[355];
+ buf.put(bytes);
+ assertEquals(612, buf.position());
+ assertEquals(612, buf.capacity());
+ assertEquals(612, buf.limit());
+
+ //adding a one-byte buffer should make it grow again
+ byte[] oneByteBuf = new byte[1];
+ buf.put(oneByteBuf);
+ assertEquals(613, buf.position());
+ assertEquals(1324, buf.capacity());
+ assertEquals(1324, buf.limit());
+
+ //add a large buffer that goes waaay past the boundary and makes it grow yet again,
+ //but that is not enough
+ byte[] largeBuf = new byte[3000];
+ buf.put(largeBuf);
+ //the buffer should be doubled twice now
+ assertEquals(3613, buf.position());
+ assertEquals(5596, buf.capacity());
+ assertEquals(5596, buf.limit());
+
+ //let's try that again, and make the buffer double three times
+ byte[] veryLargeBuf = new byte[20000];
+ buf.put(veryLargeBuf);
+ //the buffer should be doubled three times now
+ assertEquals(23613, buf.position());
+ assertEquals(45468, buf.capacity());
+ assertEquals(45468, buf.limit());
+ }
+
+ public void testBadGrowthFactors() {
+ try {
+ new GrowableByteBuffer(100, 1.0f);
+ assertTrue(false);
+ } catch (IllegalArgumentException iae) {
+ //we're OK
+ }
+ GrowableByteBuffer buf = new GrowableByteBuffer(16, 1.0000001f);
+ buf.putInt(1);
+ assertEquals(16, buf.capacity());
+ buf.putInt(1);
+ assertEquals(16, buf.capacity());
+ buf.putInt(1);
+ assertEquals(16, buf.capacity());
+ buf.putInt(1);
+ assertEquals(16, buf.capacity());
+
+ buf.putInt(1);
+ assertEquals(116, buf.capacity());
+
+ }
+
+ public void testPropertiesNonDirect() {
+ GrowableByteBuffer buf = new GrowableByteBuffer(10, 1.5f);
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+
+ assertEquals(0, buf.position());
+ // GrowableByteBuffer never makes a buffer smaller than 16 bytes
+ assertEquals(16, buf.capacity());
+ assertEquals(16, buf.limit());
+ assertEquals(false, buf.isReadOnly());
+ assertEquals(ByteOrder.LITTLE_ENDIAN, buf.order());
+ assertEquals(false, buf.isDirect());
+
+ buf.put(new byte[17]);
+
+ assertEquals(17, buf.position());
+ assertEquals(124, buf.capacity());
+ assertEquals(124, buf.limit());
+ assertEquals(false, buf.isReadOnly());
+ assertEquals(ByteOrder.LITTLE_ENDIAN, buf.order());
+ assertEquals(false, buf.isDirect());
+ }
+
+ public void testPropertiesDirect() {
+ // allocate* are simply encapsulated, so don't add logic to them,
+ // therefore minimum size becomes what it says
+ GrowableByteBuffer buf = GrowableByteBuffer.allocateDirect(10, 1.5f);
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+
+ assertEquals(0, buf.position());
+ assertEquals(10, buf.capacity());
+ assertEquals(10, buf.limit());
+ assertEquals(false, buf.isReadOnly());
+ assertEquals(ByteOrder.LITTLE_ENDIAN, buf.order());
+ assertEquals(true, buf.isDirect());
+
+ buf.put(new byte[11]);
+
+ assertEquals(11, buf.position());
+ assertEquals(115, buf.capacity());
+ assertEquals(115, buf.limit());
+ assertEquals(false, buf.isReadOnly());
+ assertEquals(ByteOrder.LITTLE_ENDIAN, buf.order());
+ assertEquals(true, buf.isDirect());
+ }
+
+ public void testNumberEncodings() {
+ GrowableByteBuffer buf = new GrowableByteBuffer();
+ buf.putInt1_2_4Bytes(124);
+ buf.putInt2_4_8Bytes(124);
+ buf.putInt1_4Bytes(124);
+
+ buf.putInt1_2_4Bytes(127);
+ buf.putInt2_4_8Bytes(127);
+ buf.putInt1_4Bytes(127);
+
+ buf.putInt1_2_4Bytes(128);
+ buf.putInt2_4_8Bytes(128);
+ buf.putInt1_4Bytes(128);
+
+ buf.putInt1_2_4Bytes(255);
+ buf.putInt2_4_8Bytes(255);
+ buf.putInt1_4Bytes(255);
+
+ buf.putInt1_2_4Bytes(256);
+ buf.putInt2_4_8Bytes(256);
+ buf.putInt1_4Bytes(256);
+
+ buf.putInt1_2_4Bytes(0);
+ buf.putInt2_4_8Bytes(0);
+ buf.putInt1_4Bytes(0);
+
+ buf.putInt1_2_4Bytes(1);
+ buf.putInt2_4_8Bytes(1);
+ buf.putInt1_4Bytes(1);
+
+ try {
+ buf.putInt1_2_4Bytes(Integer.MAX_VALUE);
+ fail("Should have gotten exception here...");
+ } catch (Exception e) { }
+ buf.putInt2_4_8Bytes(Integer.MAX_VALUE);
+ buf.putInt1_4Bytes(Integer.MAX_VALUE);
+
+ try {
+ buf.putInt2_4_8Bytes(Long.MAX_VALUE);
+ fail("Should have gotten exception here...");
+ } catch (Exception e) { }
+
+ buf.putInt1_2_4Bytes(Short.MAX_VALUE);
+ buf.putInt2_4_8Bytes(Short.MAX_VALUE);
+ buf.putInt1_4Bytes(Short.MAX_VALUE);
+
+ buf.putInt1_2_4Bytes(Byte.MAX_VALUE);
+ buf.putInt2_4_8Bytes(Byte.MAX_VALUE);
+ buf.putInt1_4Bytes(Byte.MAX_VALUE);
+
+ try {
+ buf.putInt1_2_4Bytes(-1);
+ fail("Should have gotten exception here...");
+ } catch (Exception e) { }
+ try {
+ buf.putInt2_4_8Bytes(-1);
+ fail("Should have gotten exception here...");
+ } catch (Exception e) { }
+ try {
+ buf.putInt1_4Bytes(-1);
+ fail("Should have gotten exception here...");
+ } catch (Exception e) { }
+
+ try {
+ buf.putInt1_2_4Bytes(Integer.MIN_VALUE);
+ fail("Should have gotten exception here...");
+ } catch (Exception e) { }
+ try {
+ buf.putInt2_4_8Bytes(Integer.MIN_VALUE);
+ fail("Should have gotten exception here...");
+ } catch (Exception e) { }
+ try {
+ buf.putInt1_4Bytes(Integer.MIN_VALUE);
+ fail("Should have gotten exception here...");
+ } catch (Exception e) { }
+
+ try {
+ buf.putInt2_4_8Bytes(Long.MIN_VALUE);
+ fail("Should have gotten exception here...");
+ } catch (Exception e) { }
+
+ int endWritePos = buf.position();
+ buf.position(0);
+
+ assertEquals(124, buf.getInt1_2_4Bytes());
+ assertEquals(124, buf.getInt2_4_8Bytes());
+ assertEquals(124, buf.getInt1_4Bytes());
+
+ assertEquals(127, buf.getInt1_2_4Bytes());
+ assertEquals(127, buf.getInt2_4_8Bytes());
+ assertEquals(127, buf.getInt1_4Bytes());
+
+ assertEquals(128, buf.getInt1_2_4Bytes());
+ assertEquals(128, buf.getInt2_4_8Bytes());
+ assertEquals(128, buf.getInt1_4Bytes());
+
+ assertEquals(255, buf.getInt1_2_4Bytes());
+ assertEquals(255, buf.getInt2_4_8Bytes());
+ assertEquals(255, buf.getInt1_4Bytes());
+
+ assertEquals(256, buf.getInt1_2_4Bytes());
+ assertEquals(256, buf.getInt2_4_8Bytes());
+ assertEquals(256, buf.getInt1_4Bytes());
+
+ assertEquals(0, buf.getInt1_2_4Bytes());
+ assertEquals(0, buf.getInt2_4_8Bytes());
+ assertEquals(0, buf.getInt1_4Bytes());
+
+ assertEquals(1, buf.getInt1_2_4Bytes());
+ assertEquals(1, buf.getInt2_4_8Bytes());
+ assertEquals(1, buf.getInt1_4Bytes());
+
+ assertEquals(Integer.MAX_VALUE, buf.getInt2_4_8Bytes());
+ assertEquals(Integer.MAX_VALUE, buf.getInt1_4Bytes());
+
+ assertEquals(Short.MAX_VALUE, buf.getInt1_2_4Bytes());
+ assertEquals(Short.MAX_VALUE, buf.getInt2_4_8Bytes());
+ assertEquals(Short.MAX_VALUE, buf.getInt1_4Bytes());
+
+ assertEquals(Byte.MAX_VALUE, buf.getInt1_2_4Bytes());
+ assertEquals(Byte.MAX_VALUE, buf.getInt2_4_8Bytes());
+ assertEquals(Byte.MAX_VALUE, buf.getInt1_4Bytes());
+
+ int endReadPos = buf.position();
+
+ assertEquals(endWritePos, endReadPos);
+ }
+ public void testNumberLengths() {
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_4Bytes(0));
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_4Bytes(1));
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_4Bytes(4));
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_4Bytes(31));
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_4Bytes(126));
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_4Bytes(127));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize1_4Bytes(128));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize1_4Bytes(129));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize1_4Bytes(255));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize1_4Bytes(256));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize1_4Bytes(0x7FFFFFFF));
+
+ assertEquals(2, GrowableByteBuffer.getSerializedSize2_4_8Bytes(0));
+ assertEquals(2, GrowableByteBuffer.getSerializedSize2_4_8Bytes(1));
+ assertEquals(2, GrowableByteBuffer.getSerializedSize2_4_8Bytes(4));
+ assertEquals(2, GrowableByteBuffer.getSerializedSize2_4_8Bytes(31));
+ assertEquals(2, GrowableByteBuffer.getSerializedSize2_4_8Bytes(126));
+ assertEquals(2, GrowableByteBuffer.getSerializedSize2_4_8Bytes(127));
+ assertEquals(2, GrowableByteBuffer.getSerializedSize2_4_8Bytes(128));
+ assertEquals(2, GrowableByteBuffer.getSerializedSize2_4_8Bytes(32767));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize2_4_8Bytes(32768));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize2_4_8Bytes(32769));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize2_4_8Bytes(1030493));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize2_4_8Bytes(0x3FFFFFFF));
+ assertEquals(8, GrowableByteBuffer.getSerializedSize2_4_8Bytes(0x40000000));
+ assertEquals(8, GrowableByteBuffer.getSerializedSize2_4_8Bytes(0x40000001));
+
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_2_4Bytes(0));
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_2_4Bytes(1));
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_2_4Bytes(4));
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_2_4Bytes(31));
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_2_4Bytes(126));
+ assertEquals(1, GrowableByteBuffer.getSerializedSize1_2_4Bytes(127));
+ assertEquals(2, GrowableByteBuffer.getSerializedSize1_2_4Bytes(128));
+ assertEquals(2, GrowableByteBuffer.getSerializedSize1_2_4Bytes(16383));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize1_2_4Bytes(16384));
+ assertEquals(4, GrowableByteBuffer.getSerializedSize1_2_4Bytes(16385));
+ }
+
+ public void testSize0() {
+ GrowableByteBuffer buf = new GrowableByteBuffer(0, 2.0f);
+ buf.put((byte) 1);
+ buf.put((byte) 1);
+ }
+
+ public void testExceptionSafety() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ ByteBuffer b = ByteBuffer.allocate(232);
+ for (int i = 0; i < 232; ++i) {
+ b.put((byte) 32);
+ }
+ b.flip();
+ g.put(b);
+ b.flip();
+ g.put(b);
+ assertEquals(464, g.position());
+ g.flip();
+ for (int i = 0; i < 464; ++i) {
+ assertEquals(32, (int) g.get());
+ }
+ }
+
+ public void testGrowthFactorAccessor() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ assertEquals(GrowableByteBuffer.DEFAULT_GROW_FACTOR, g.getGrowFactor());
+ }
+
+ public void testGrowthWithNonZeroMark() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ final int mark = 16;
+ byte[] stuff = new byte[mark];
+ Arrays.fill(stuff, (byte) 37);
+ g.put(stuff);
+ g.mark();
+ stuff = new byte[637];
+ Arrays.fill(stuff, (byte) 38);
+ g.put(stuff);
+ assertEquals(mark, g.getByteBuffer().reset().position());
+ }
+
+ public void testPutInt2_4_8BytesMore() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ g.putInt2_4_8Bytes(0x9000);
+ assertEquals(4, g.position());
+ }
+
+ public void testPutInt2_4_8BytesAs4() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ boolean caught = false;
+ try {
+ g.putInt2_4_8BytesAs4(-1);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ caught = false;
+ try {
+ g.putInt2_4_8BytesAs4(1L << 37);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ g.putInt2_4_8BytesAs4(37);
+ assertEquals(4, g.position());
+ }
+
+ public void testGetInt2_4_8Bytes() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ final long expected3 = 37L;
+ g.putInt2_4_8Bytes(expected3);
+ final long expected2 = 0x9000L;
+ g.putInt2_4_8Bytes(expected2);
+ final long expected = 1L << 56;
+ g.putInt2_4_8Bytes(expected);
+ g.flip();
+ assertEquals(expected3, g.getInt2_4_8Bytes());
+ assertEquals(expected2, g.getInt2_4_8Bytes());
+ assertEquals(expected, g.getInt2_4_8Bytes());
+ }
+
+ public void testSerializedSize2_4_8BytesIllegalValues() {
+ boolean caught = false;
+ try {
+ GrowableByteBuffer.getSerializedSize2_4_8Bytes(-1);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ caught = false;
+ try {
+ GrowableByteBuffer.getSerializedSize2_4_8Bytes((1L << 62) + 1L);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ }
+
+ public void testPutInt1_2_4BytesAs4IllegalValues() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ boolean caught = false;
+ try {
+ g.putInt1_2_4BytesAs4(-1);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ caught = false;
+ try {
+ g.putInt1_2_4BytesAs4((1 << 30) + 1);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ }
+
+ public void testSerializedSize1_2_4BytesIllegalValues() {
+ boolean caught = false;
+ try {
+ GrowableByteBuffer.getSerializedSize1_2_4Bytes(-1);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ caught = false;
+ try {
+ GrowableByteBuffer.getSerializedSize1_2_4Bytes((1 << 30) + 1);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ }
+
+ public void testPutInt1_4BytesAs4() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ boolean caught = false;
+ try {
+ g.putInt1_4BytesAs4(-1);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ g.putInt1_4BytesAs4(37);
+ assertEquals(4, g.position());
+ }
+
+ public void testSerializedSize1_4BytesIllegalValues() {
+ boolean caught = false;
+ try {
+ GrowableByteBuffer.getSerializedSize1_4Bytes(-1);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ }
+
+ public void testBuilders() {
+ GrowableByteBuffer g = GrowableByteBuffer.allocate(1063);
+ assertEquals(1063, g.capacity());
+ g = GrowableByteBuffer.allocate(1063, 37.0f);
+ assertEquals(1063, g.capacity());
+ assertEquals(37.0f, g.getGrowFactor());
+ g = GrowableByteBuffer.allocateDirect(1063);
+ assertTrue(g.isDirect());
+ }
+
+ public void testForwarding() {
+ GrowableByteBuffer g = new GrowableByteBuffer(1063);
+ int first = g.arrayOffset();
+ g.put(0, (byte) 37);
+ assertTrue(g.hasArray());
+ assertEquals((byte) 37, g.array()[first]);
+ g.putChar(0, 'a');
+ assertEquals('a', g.getChar(0));
+ assertEquals('a', g.asCharBuffer().get(0));
+ g.putDouble(0, 10.0d);
+ assertEquals(10.0d, g.getDouble(0));
+ assertEquals(10.0d, g.asDoubleBuffer().get(0));
+ g.putFloat(0, 10.0f);
+ assertEquals(10.0f, g.getFloat(0));
+ assertEquals(10.0f, g.asFloatBuffer().get(0));
+ g.putInt(0, 10);
+ assertEquals(10, g.getInt(0));
+ assertEquals(10, g.asIntBuffer().get(0));
+ g.putLong(0, 10L);
+ assertEquals(10L, g.getLong(0));
+ assertEquals(10L, g.asLongBuffer().get(0));
+ boolean caught = false;
+ try {
+ g.asReadOnlyBuffer().put((byte) 10);
+ } catch (ReadOnlyBufferException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ g.putShort(0, (short) 10);
+ assertEquals((short) 10, g.getShort(0));
+ assertEquals((short) 10, g.asShortBuffer().get(0));
+ g.position(0);
+ g.put((byte) 0);
+ g.put((byte) 10);
+ g.limit(2);
+ g.position(1);
+ g.compact();
+ assertEquals((byte) 10, g.get(0));
+ }
+
+ public void testComparison() {
+ GrowableByteBuffer g0 = new GrowableByteBuffer(32);
+ GrowableByteBuffer g1 = new GrowableByteBuffer(32);
+ assertEquals(g0.hashCode(), g1.hashCode());
+ assertFalse(g0.equals(Integer.valueOf(12)));
+ assertFalse(g0.hashCode() == new GrowableByteBuffer(1063).hashCode());
+ assertTrue(g0.equals(g1));
+ assertEquals(0, g0.compareTo(g1));
+ g0.put((byte) 9);
+ assertFalse(g0.equals(g1));
+ assertEquals(-1, g0.compareTo(g1));
+ }
+
+ public void testDuplicate() {
+ GrowableByteBuffer g0 = new GrowableByteBuffer(32);
+ GrowableByteBuffer g1 = g0.duplicate();
+ g0.put((byte) 12);
+ assertEquals(12, g1.get());
+ }
+
+ public void testGetByteArrayOffsetLen() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ byte[] expected = new byte[] { (byte) 1, (byte) 2, (byte) 3 };
+ for (int i = 0; i < expected.length; ++i) {
+ g.put(expected[i]);
+ }
+ byte[] got = new byte[3];
+ g.flip();
+ g.get(got, 0, got.length);
+ assertArrayEquals(expected, got);
+ }
+
+ public void testPutByteArrayOffsetLen() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ byte[] expected = new byte[] { (byte) 1, (byte) 2, (byte) 3 };
+ g.put(expected, 0, expected.length);
+ byte[] got = new byte[3];
+ g.flip();
+ g.get(got, 0, got.length);
+ assertArrayEquals(expected, got);
+ }
+
+ public void testPutGrowableBuffer() {
+ GrowableByteBuffer g0 = new GrowableByteBuffer(32);
+ byte[] expected = new byte[] { (byte) 1, (byte) 2, (byte) 3 };
+ GrowableByteBuffer g1 = new GrowableByteBuffer(32);
+ g0.put(expected, 0, expected.length);
+ byte[] got = new byte[3];
+ g0.flip();
+ g1.put(g0);
+ g1.flip();
+ g1.get(got, 0, got.length);
+ assertArrayEquals(expected, got);
+ }
+
+ private GrowableByteBuffer fullBuffer() {
+ GrowableByteBuffer g = new GrowableByteBuffer(32);
+ byte[] stuffer = new byte[g.remaining()];
+ Arrays.fill(stuffer, (byte) 'a');
+ g.put(stuffer);
+ return g;
+ }
+
+ public void testPutWithGrow() {
+ GrowableByteBuffer g = fullBuffer();
+ final int capacity = g.capacity();
+ byte[] b = new byte[] { (byte) 'b' };
+ g.put(b, 0, b.length);
+ assertTrue(capacity < g.capacity());
+
+ g = fullBuffer();
+ GrowableByteBuffer toPut = fullBuffer();
+ toPut.flip();
+ g.put(toPut);
+
+ assertTrue(capacity < g.capacity());
+ g = fullBuffer();
+ g.put(g.position(), (byte) 'b');
+ assertTrue(capacity < g.capacity());
+
+ g = fullBuffer();
+ g.putChar('b');
+ assertTrue(capacity < g.capacity());
+ g = fullBuffer();
+ g.putChar(g.position(), 'b');
+ assertTrue(capacity < g.capacity());
+
+ g = fullBuffer();
+ g.putDouble(1.0d);
+ assertTrue(capacity < g.capacity());
+ g = fullBuffer();
+ g.putDouble(g.position(), 1.0d);
+ assertTrue(capacity < g.capacity());
+
+ g = fullBuffer();
+ g.putFloat(1.0f);
+ assertTrue(capacity < g.capacity());
+ g = fullBuffer();
+ g.putFloat(g.position(), 1.0f);
+ assertTrue(capacity < g.capacity());
+
+ g = fullBuffer();
+ g.putInt(g.position(), 1);
+ assertTrue(capacity < g.capacity());
+
+ g = fullBuffer();
+ g.putLong(g.position(), 1L);
+ assertTrue(capacity < g.capacity());
+
+ g = fullBuffer();
+ g.putShort((short) 1);
+ assertTrue(capacity < g.capacity());
+ g = fullBuffer();
+ g.putShort(g.position(), (short) 1);
+ assertTrue(capacity < g.capacity());
+ }
+
+ public void testSlice() {
+ GrowableByteBuffer g0 = new GrowableByteBuffer(32);
+ GrowableByteBuffer g1 = g0.slice();
+ final int expected = 37;
+ g0.putInt(expected);
+ assertEquals(expected, g1.getInt());
+ }
+
+ public void testToString() {
+ assertEquals("GrowableByteBuffer[pos=32 lim=32 cap=32 grow=2.0]",
+ fullBuffer().toString());
+ }
+
+ public void testWrappers() {
+ final byte expected = (byte) 2;
+ byte[] data = new byte[] { (byte) 1, expected, (byte) 3 };
+ final float grow = 9e5f;
+ GrowableByteBuffer g = GrowableByteBuffer.wrap(data, grow);
+ assertEquals(expected, g.get(1));
+ assertEquals(grow, g.getGrowFactor());
+ g = GrowableByteBuffer.wrap(data, 1, 1);
+ assertEquals(expected, g.get());
+ assertEquals(2, g.limit());
+ g = GrowableByteBuffer.wrap(data, 1, 1, grow);
+ assertEquals(expected, g.get());
+ assertEquals(2, g.limit());
+ assertEquals(grow, g.getGrowFactor());
+ }
+
+ public void testByteBufferMethods() {
+ GrowableByteBuffer g = fullBuffer();
+ assertFalse(g.hasRemaining());
+ g.clear();
+ assertTrue(g.hasRemaining());
+ g = fullBuffer();
+ g.mark();
+ g.limit(16);
+ boolean caught = false;
+ try {
+ g.reset();
+ } catch (InvalidMarkException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ caught = false;
+ g = fullBuffer();
+ g.mark();
+ g.position(16);
+ try {
+ g.reset();
+ } catch (InvalidMarkException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/io/HexDumpTestCase.java b/vespajlib/src/test/java/com/yahoo/io/HexDumpTestCase.java
new file mode 100644
index 00000000000..940f0150540
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/HexDumpTestCase.java
@@ -0,0 +1,40 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io;
+
+import org.junit.Test;
+
+import com.yahoo.text.Utf8;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class HexDumpTestCase {
+
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+ private static final Charset UTF16 = Charset.forName("UTF-16");
+
+ @Test
+ public void requireThatToHexStringAcceptsNull() {
+ assertNull(HexDump.toHexString(null));
+ }
+
+ @Test
+ public void requireThatToHexStringIsUnformatted() {
+ assertEquals("6162636465666768696A6B6C6D6E6F707172737475767778797A",
+ HexDump.toHexString("abcdefghijklmnopqrstuvwxyz".getBytes(UTF8)));
+ assertEquals("FEFF006100620063006400650066006700680069006A006B006C00" +
+ "6D006E006F0070007100720073007400750076007700780079007A",
+ HexDump.toHexString("abcdefghijklmnopqrstuvwxyz".getBytes(UTF16)));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/io/IOUtilsTestCase.java b/vespajlib/src/test/java/com/yahoo/io/IOUtilsTestCase.java
new file mode 100644
index 00000000000..7f89cccc6c8
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/IOUtilsTestCase.java
@@ -0,0 +1,149 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io;
+
+import java.io.*;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class IOUtilsTestCase extends junit.framework.TestCase {
+
+ public void testCloseNUllDoesNotFail() {
+ IOUtils.closeWriter(null);
+ IOUtils.closeReader(null);
+ IOUtils.closeInputStream(null);
+ IOUtils.closeOutputStream(null);
+ }
+
+ public void testFileWriter() throws IOException {
+ IOUtils.writeFile("temp1.txt", "hello",false);
+ assertEquals("hello", IOUtils.readFile(new File("temp1.txt")));
+ new File("temp1.txt").delete();
+ }
+
+ public void testFileWriterWithoutEncoding() throws IOException {
+ BufferedWriter writer=null;
+ try {
+ writer=IOUtils.createWriter(new File("temp2.txt"),false);
+ writer.write("hello");
+ }
+ finally {
+ IOUtils.closeWriter(writer);
+ }
+ assertEquals("hello", IOUtils.readFile(new File("temp2.txt")));
+ new File("temp2.txt").delete();
+ }
+
+ public void testFileWriterWithoutEncodingFromFileName() throws IOException {
+ BufferedWriter writer=null;
+ try {
+ writer=IOUtils.createWriter("temp3.txt",false);
+ writer.write("hello");
+ }
+ finally {
+ IOUtils.closeWriter(writer);
+ }
+ assertEquals("hello",IOUtils.readFile(new File("temp3.txt")));
+ new File("temp3.txt").delete();
+ }
+
+ public void testFileCounting() throws IOException {
+ IOUtils.writeFile("temp4.txt","hello\nworld",false);
+ assertEquals(2,IOUtils.countLines("temp4.txt"));
+ new File("temp4.txt").delete();
+ }
+
+ public void testFileCopy() throws IOException {
+ IOUtils.writeFile("temp5.txt","hello",false);
+ IOUtils.copy(new File("temp5.txt"), new File("temp5copy.txt"));
+ assertEquals("hello", IOUtils.readFile(new File("temp5copy.txt")));
+ new File("temp5.txt").delete();
+ new File("temp5copy.txt").delete();
+ }
+
+ public void testFileCopyWithLineCap() throws IOException {
+ IOUtils.writeFile("temp6.txt","hello\nyou\nworld",false);
+ IOUtils.copy("temp6.txt","temp6copy.txt",2);
+ assertEquals("hello\nyou\n", IOUtils.readFile(new File("temp6copy.txt")));
+ new File("temp6.txt").delete();
+ new File("temp6copy.txt").delete();
+ }
+
+ public void testGetLines() throws IOException {
+ IOUtils.writeFile("temp7.txt","hello\nworld",false);
+ List<String> lines=IOUtils.getLines("temp7.txt");
+ assertEquals(2,lines.size());
+ assertEquals("hello",lines.get(0));
+ assertEquals("world",lines.get(1));
+ new File("temp7.txt").delete();
+ }
+
+ public void testFileWriterAppend() throws IOException {
+ boolean append=true;
+ IOUtils.writeFile("temp8.txt", "hello",!append);
+ BufferedWriter writer=null;
+ try {
+ writer=IOUtils.createWriter(new File("temp8.txt"),append);
+ writer.write("\nworld");
+ }
+ finally {
+ IOUtils.closeWriter(writer);
+ }
+ assertEquals("hello\nworld", IOUtils.readFile(new File("temp8.txt")));
+ new File("temp8.txt").delete();
+ }
+
+ public void testCloseAllReaders() throws IOException {
+ StringReader reader1=new StringReader("hello");
+ StringReader reader2=new StringReader("world");
+ IOUtils.closeAll(Arrays.<Reader>asList(reader1, reader2));
+ try {
+ reader1.ready();
+ fail("Expected exception due to reader closed");
+ }
+ catch (IOException e) {
+ // Expected
+ }
+ try {
+ reader2.ready();
+ fail("Expected exception due to reader closed");
+ }
+ catch (IOException e) {
+ // Expected
+ }
+ }
+
+ public void testDirCopying() throws IOException {
+ IOUtils.writeFile("temp1/temp1.txt","hello",false);
+ IOUtils.writeFile("temp1/temp2.txt","world",false);
+ IOUtils.copyDirectory(new File("temp1"), new File("temp2"));
+ assertEquals("hello", IOUtils.readFile(new File("temp2/temp1.txt")));
+ assertEquals("world", IOUtils.readFile(new File("temp2/temp2.txt")));
+ IOUtils.recursiveDeleteDir(new File("temp1"));
+ IOUtils.recursiveDeleteDir(new File("temp2"));
+ assertTrue(!new File("temp1").exists());
+ assertTrue(!new File("temp2").exists());
+ }
+
+ public void testDirCopyingWithFilter() throws IOException {
+ IOUtils.writeFile("temp1/temp1.txt","hello",false);
+ IOUtils.writeFile("temp1/temp2.txt","world",false);
+ IOUtils.writeFile("temp1/temp3.json", "world", false);
+ IOUtils.copyDirectory(new File("temp1"), new File("temp2"), -1, new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".json");
+ }
+ });
+ assertEquals("world", IOUtils.readFile(new File("temp2/temp3.json")));
+ assertFalse(new File("temp2/temp1.txt").exists());
+ assertFalse(new File("temp2/temp2.txt").exists());
+ IOUtils.recursiveDeleteDir(new File("temp1"));
+ IOUtils.recursiveDeleteDir(new File("temp2"));
+ assertTrue(!new File("temp1").exists());
+ assertTrue(!new File("temp2").exists());
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/io/ListenerTestCase.java b/vespajlib/src/test/java/com/yahoo/io/ListenerTestCase.java
new file mode 100644
index 00000000000..e2049017076
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/ListenerTestCase.java
@@ -0,0 +1,108 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.yahoo.collections.Tuple2;
+import com.yahoo.concurrent.Receiver;
+import com.yahoo.concurrent.Receiver.MessageState;
+
+/**
+ * Test a NIO based Reactor pattern implementation, com.yahoo.io.Listener.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class ListenerTestCase {
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ Receiver<Byte> r = new Receiver<>();
+
+ private final class MockConnection implements Connection {
+
+ private SocketChannel channel;
+
+ MockConnection(SocketChannel channel, Listener listener) {
+ this.channel = channel;
+ }
+
+ @Override
+ public void write() throws IOException {
+ }
+
+ @Override
+ public void read() throws IOException {
+ ByteBuffer b = ByteBuffer.allocate(1);
+ channel.read(b);
+ b.flip();
+ r.put(b.get());
+ }
+
+ @Override
+ public void close() throws IOException {
+ channel.close();
+
+ }
+
+ @Override
+ public void connect() throws IOException {
+ }
+
+ @Override
+ public int selectOps() {
+ return SelectionKey.OP_READ;
+ }
+
+ @Override
+ public SocketChannel socketChannel() {
+ return channel;
+ }
+ }
+
+ private final class GetConnection implements ConnectionFactory {
+
+ @Override
+ public Connection newConnection(SocketChannel channel, Listener listener) {
+ return new MockConnection(channel, listener);
+ }
+ }
+
+ @Test
+ public final void testRun() throws IOException, InterruptedException {
+ Listener l = new Listener("ListenerTestCase");
+ l.listen(new GetConnection(), 0);
+ l.start();
+ int port = ((InetSocketAddress) l.acceptors.get(0).socket.getLocalAddress()).getPort();
+ Socket s = new Socket("127.0.0.1", port);
+ final byte expected = 42;
+ s.getOutputStream().write(expected);
+ s.getOutputStream().flush();
+ s.close();
+ Tuple2<MessageState, Byte> received = r.get(60 * 1000);
+ l.acceptors.get(0).interrupt();
+ l.acceptors.get(0).socket.close();
+ l.acceptors.get(0).join();
+ l.interrupt();
+ l.join();
+ assertTrue("Test timed out.", received.first == MessageState.VALID);
+ assertEquals(expected, received.second.byteValue());
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/io/SlowInflateTestCase.java b/vespajlib/src/test/java/com/yahoo/io/SlowInflateTestCase.java
new file mode 100644
index 00000000000..48d8e8cdffe
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/SlowInflateTestCase.java
@@ -0,0 +1,61 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io;
+
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+import java.util.zip.Deflater;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.yahoo.text.Utf8;
+
+/**
+ * Check decompressor used among other things for packed summary fields.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class SlowInflateTestCase {
+
+ private String value;
+ private byte[] raw;
+ private byte[] output;
+ private byte[] compressed;
+ private int compressedDataLength;
+
+ @Before
+ public void setUp() throws Exception {
+ value = "000000000000000000000000000000000000000000000000000000000000000";
+ raw = Utf8.toBytesStd(value);
+ output = new byte[raw.length * 2];
+ Deflater compresser = new Deflater();
+ compresser.setInput(raw);
+ compresser.finish();
+ compressedDataLength = compresser.deflate(output);
+ compresser.end();
+ compressed = Arrays.copyOf(output, compressedDataLength);
+ }
+
+ @Test
+ public final void test() {
+ byte[] unpacked = new SlowInflate().unpack(compressed, raw.length);
+ assertArrayEquals(raw, unpacked);
+ }
+
+ @Test
+ public final void testCorruptData() {
+ compressed[0] = (byte) (compressed[0] ^ compressed[1]);
+ compressed[1] = (byte) (compressed[1] ^ compressed[2]);
+ compressed[2] = (byte) (compressed[2] ^ compressed[3]);
+ compressed[3] = (byte) (compressed[3] ^ compressed[4]);
+ boolean caught = false;
+ try {
+ new SlowInflate().unpack(compressed, raw.length);
+ } catch (RuntimeException e) {
+ caught = true;
+ }
+ assertTrue(caught);
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/io/reader/NamedReaderTestCase.java b/vespajlib/src/test/java/com/yahoo/io/reader/NamedReaderTestCase.java
new file mode 100644
index 00000000000..280d0782bd2
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/io/reader/NamedReaderTestCase.java
@@ -0,0 +1,131 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.io.reader;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.CharBuffer;
+import java.util.Collections;
+
+import com.yahoo.io.reader.NamedReader;
+import com.yahoo.protect.ClassValidator;
+
+/**
+ * Tests all method of NamedReader.
+ *
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class NamedReaderTestCase extends junit.framework.TestCase {
+
+ public void testIt() {
+ StringReader stringReader=new StringReader("hello world");
+ NamedReader r=new NamedReader("test1",stringReader);
+ assertEquals("test1",r.getName());
+ assertEquals("test1",r.toString());
+ assertEquals(stringReader,r.getReader());
+ NamedReader.closeAll(Collections.singletonList(r));
+ NamedReader.closeAll(null); // noop, nor exception
+ }
+
+ public void testMethodMasking() {
+ assertEquals(0,
+ ClassValidator.unmaskedMethodsFromSuperclass(NamedReader.class).size());
+ }
+
+ private static class MarkerReader extends Reader {
+ static final String READ_CHAR_BUFFER = "com.yahoo.io.reader.NamedReaderTestCase.MarkerReader.read(CharBuffer)";
+ static final String READ = "com.yahoo.io.reader.NamedReaderTestCase.MarkerReader.read()";
+ static final String READ_CHAR = "com.yahoo.io.reader.NamedReaderTestCase.MarkerReader.read(char[])";
+ static final String READ_CHAR_INT_INT = "com.yahoo.io.reader.NamedReaderTestCase.MarkerReader.read(char[], int, int)";
+ static final String SKIP_LONG = "com.yahoo.io.reader.NamedReaderTestCase.MarkerReader.skip(long)";
+ static final String READY = "com.yahoo.io.reader.NamedReaderTestCase.MarkerReader.ready()";
+ static final String MARK_SUPPORTED = "com.yahoo.io.reader.NamedReaderTestCase.MarkerReader.markSupported()";
+ static final String MARK_INT = "com.yahoo.io.reader.NamedReaderTestCase.MarkerReader.mark(int)";
+ static final String RESET = "com.yahoo.io.reader.NamedReaderTestCase.MarkerReader.reset()";
+ static final String CLOSE = "com.yahoo.io.reader.NamedReaderTestCase.MarkerReader.close()";
+ String lastMethodHit = null;
+
+ @Override
+ public int read(CharBuffer target) throws IOException {
+ lastMethodHit = READ_CHAR_BUFFER;
+ return 0;
+ }
+
+ @Override
+ public int read() throws IOException {
+ lastMethodHit = READ;
+ return -1;
+ }
+
+ @Override
+ public int read(char[] cbuf) throws IOException {
+ lastMethodHit = READ_CHAR;
+ return 0;
+ }
+
+ @Override
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ lastMethodHit = READ_CHAR_INT_INT;
+ return 0;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ lastMethodHit = SKIP_LONG;
+ return 0;
+ }
+
+ @Override
+ public boolean ready() throws IOException {
+ lastMethodHit = READY;
+ return false;
+ }
+
+ @Override
+ public boolean markSupported() {
+ lastMethodHit = MARK_SUPPORTED;
+ return false;
+ }
+
+ @Override
+ public void mark(int readAheadLimit) throws IOException {
+ lastMethodHit = MARK_INT;
+ }
+
+ @Override
+ public void reset() throws IOException {
+ lastMethodHit = RESET;
+ }
+
+ @Override
+ public void close() throws IOException {
+ lastMethodHit = CLOSE;
+ }
+ }
+
+ public void testAllDelegators() throws IOException {
+ MarkerReader m = new MarkerReader();
+ NamedReader r = new NamedReader("nalle", m);
+ r.read(CharBuffer.allocate(5000));
+ assertEquals(MarkerReader.READ_CHAR_BUFFER, m.lastMethodHit);
+ r.read();
+ assertEquals(MarkerReader.READ, m.lastMethodHit);
+ r.read(new char[5]);
+ assertEquals(MarkerReader.READ_CHAR, m.lastMethodHit);
+ r.read(new char[5], 0, 5);
+ assertEquals(MarkerReader.READ_CHAR_INT_INT, m.lastMethodHit);
+ r.skip(5L);
+ assertEquals(MarkerReader.SKIP_LONG, m.lastMethodHit);
+ r.ready();
+ assertEquals(MarkerReader.READY, m.lastMethodHit);
+ r.markSupported();
+ assertEquals(MarkerReader.MARK_SUPPORTED, m.lastMethodHit);
+ r.mark(5);
+ assertEquals(MarkerReader.MARK_INT, m.lastMethodHit);
+ r.reset();
+ assertEquals(MarkerReader.RESET, m.lastMethodHit);
+ r.close();
+ assertEquals(MarkerReader.CLOSE, m.lastMethodHit);
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/java7compat/UtilTest.java b/vespajlib/src/test/java/com/yahoo/java7compat/UtilTest.java
new file mode 100644
index 00000000000..1f919978b7a
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/java7compat/UtilTest.java
@@ -0,0 +1,29 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.java7compat;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author <a href="mailto:balder@yahoo-inc.com">Henning Baldersheim</a>
+ * @since 5.2
+ */
+
+public class UtilTest {
+
+ @Test
+ public void requireJava7CompatibleDoublePrinting() {
+ if (Util.isJava7Compatible()) {
+ assertEquals("0.004", String.valueOf(0.0040));
+ }else {
+ assertEquals("0.0040", String.valueOf(0.0040));
+ }
+ assertEquals("0.004", Util.toJava7String(0.0040) );
+ }
+
+ @Test
+ public void nonCompatible() {
+ assertEquals(Util.nonJava7CompatibleString("0.0040"), "0.004");
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/javacc/FastCharStreamTestCase.java b/vespajlib/src/test/java/com/yahoo/javacc/FastCharStreamTestCase.java
new file mode 100644
index 00000000000..a73fffc6c5c
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/javacc/FastCharStreamTestCase.java
@@ -0,0 +1,181 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.javacc;
+
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class FastCharStreamTestCase {
+
+ @Test
+ public void requireThatInputCanBeRead() throws IOException {
+ FastCharStream input = new FastCharStream("foo");
+ assertEquals('f', input.readChar());
+ assertEquals('o', input.readChar());
+ assertEquals('o', input.readChar());
+ try {
+ input.readChar();
+ fail();
+ } catch (IOException e) {
+
+ }
+ }
+
+ @Test
+ public void requireThatColumnIsTracked() throws IOException {
+ FastCharStream input = new FastCharStream("foo");
+ assertEquals(1, input.getColumn());
+ input.readChar();
+ assertEquals(2, input.getColumn());
+ input.readChar();
+ assertEquals(3, input.getColumn());
+ input.readChar();
+ assertEquals(4, input.getColumn());
+ }
+
+ @Test
+ public void requireThatLineIsNotTracked() throws IOException {
+ FastCharStream input = new FastCharStream("f\no");
+ assertEquals(-1, input.getLine());
+ input.readChar();
+ assertEquals(-1, input.getLine());
+ input.readChar();
+ assertEquals(-1, input.getLine());
+ input.readChar();
+ assertEquals(-1, input.getLine());
+ }
+
+
+ @Test
+ public void requireThatBackupIsSupported() throws IOException {
+ FastCharStream input = new FastCharStream("foo");
+ assertEquals('f', input.readChar());
+ input.backup(1);
+ assertEquals('f', input.readChar());
+ assertEquals('o', input.readChar());
+ input.backup(2);
+ assertEquals('f', input.readChar());
+ assertEquals('o', input.readChar());
+ assertEquals('o', input.readChar());
+ input.backup(3);
+ assertEquals('f', input.readChar());
+ assertEquals('o', input.readChar());
+ assertEquals('o', input.readChar());
+ input.backup(2);
+ assertEquals('o', input.readChar());
+ assertEquals('o', input.readChar());
+ input.backup(1);
+ assertEquals('o', input.readChar());
+ }
+
+ @Test
+ public void requireThatSuffixIsNotSupported() {
+ try {
+ new FastCharStream("foo").GetSuffix(0);
+ fail();
+ } catch (UnsupportedOperationException e) {
+
+ }
+ }
+
+ @Test
+ public void requireThatDoneDoesNotThrowException() {
+ FastCharStream input = new FastCharStream("foo");
+ input.Done();
+ }
+
+ @Test
+ public void requireThatTokensCanBeRetrieved() throws IOException {
+ FastCharStream input = new FastCharStream("foo bar baz");
+ input.readChar();
+ input.readChar();
+ input.readChar();
+ input.readChar();
+ assertEquals('b', input.BeginToken());
+ assertEquals(5, input.getBeginColumn());
+ assertEquals(-1, input.getBeginLine());
+ assertEquals(6, input.getEndColumn());
+ assertEquals(-1, input.getEndLine());
+ assertEquals('a', input.readChar());
+ assertEquals('r', input.readChar());
+ assertEquals(8, input.getEndColumn());
+ assertEquals(-1, input.getEndLine());
+ assertEquals("bar", input.GetImage());
+ }
+
+ @Test
+ public void requireThatExceptionsDetectLineNumber() {
+ FastCharStream input = new FastCharStream("foo\nbar");
+ assertEquals("line 2, column 1\n" +
+ "At position:\n" +
+ "bar\n" +
+ "^",
+ input.formatException("line -1, column 5"));
+ assertEquals("line 2, column 2\n" +
+ "At position:\n" +
+ "bar\n" +
+ " ^",
+ input.formatException("line -1, column 6"));
+ assertEquals("line 2, column 3\n" +
+ "At position:\n" +
+ "bar\n" +
+ " ^",
+ input.formatException("line -1, column 7"));
+ assertEquals("line 2, column 4\n" +
+ "At position:\n" +
+ "bar\n" +
+ " ^",
+ input.formatException("line -1, column 8"));
+ assertEquals("foo line 2, column 2\n" +
+ "At position:\n" +
+ "bar\n" +
+ " ^",
+ input.formatException("foo line -1, column 6"));
+ assertEquals("foo line 2, column 2 bar\n" +
+ "At position:\n" +
+ "bar\n" +
+ " ^",
+ input.formatException("foo line -1, column 6 bar"));
+ assertEquals("line 2, column 2 bar\n" +
+ "At position:\n" +
+ "bar\n" +
+ " ^",
+ input.formatException("line -1, column 6 bar"));
+ }
+
+ @Test
+ public void requireErrorMsgExceptionAtEOF() {
+ FastCharStream input = new FastCharStream("\n");
+ assertEquals("line 1, column 1\n" +
+ "At position:\n" +
+ "EOF\n" +
+ "^",
+ input.formatException("line -1, column 1"));
+ }
+
+ @Test
+ public void requireThatUnknownExceptionFormatIsIgnored() {
+ FastCharStream input = new FastCharStream("foo\nbar");
+ assertEquals("",
+ input.formatException(""));
+ assertEquals("foo",
+ input.formatException("foo"));
+ assertEquals("foo line -1, column ",
+ input.formatException("foo line -1, column "));
+ assertEquals("foo line -1, column bar",
+ input.formatException("foo line -1, column bar"));
+ }
+
+ @Test
+ public void requireThatIllegalExceptionColumnIsIgnored() {
+ FastCharStream input = new FastCharStream("foo\nbar");
+ assertEquals("line -1, column 9",
+ input.formatException("line -1, column 9"));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/javacc/UnicodeUtilitiesTestCase.java b/vespajlib/src/test/java/com/yahoo/javacc/UnicodeUtilitiesTestCase.java
new file mode 100644
index 00000000000..81329e06308
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/javacc/UnicodeUtilitiesTestCase.java
@@ -0,0 +1,112 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.javacc;
+
+import org.junit.Test;
+
+import static com.yahoo.javacc.UnicodeUtilities.quote;
+import static com.yahoo.javacc.UnicodeUtilities.unquote;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class UnicodeUtilitiesTestCase {
+
+ @Test
+ public void testQuote() {
+ assertEquals("'\\f'", quote("\f", '\''));
+ assertEquals("'\\n'", quote("\n", '\''));
+ assertEquals("'\\r'", quote("\r", '\''));
+ assertEquals("'\\t'", quote("\t", '\''));
+
+ for (char c = 'a'; c <= 'z'; ++c) {
+ assertEquals("'" + c + "'", quote(String.valueOf(c), '\''));
+ }
+
+ assertEquals("'\\u4f73'", quote("\u4f73", '\''));
+ assertEquals("'\\u80fd'", quote("\u80fd", '\''));
+ assertEquals("'\\u7d22'", quote("\u7d22", '\''));
+ assertEquals("'\\u5c3c'", quote("\u5c3c", '\''));
+ assertEquals("'\\u60e0'", quote("\u60e0", '\''));
+ assertEquals("'\\u666e'", quote("\u666e", '\''));
+
+ assertEquals("\"foo\"", quote("foo", '"'));
+ assertEquals("\"'foo\"", quote("'foo", '"'));
+ assertEquals("\"foo'\"", quote("foo'", '"'));
+ assertEquals("\"'foo'\"", quote("'foo'", '"'));
+ assertEquals("\"\\\"foo\"", quote("\"foo", '"'));
+ assertEquals("\"foo\\\"\"", quote("foo\"", '"'));
+ assertEquals("\"\\\"foo\\\"\"", quote("\"foo\"", '"'));
+ assertEquals("\"\\\"'foo'\\\"\"", quote("\"'foo'\"", '"'));
+ assertEquals("\"'\\\"foo\\\"'\"", quote("'\"foo\"'", '"'));
+ assertEquals("\"'f\\\\'o\\\"o\\\\\\\\'\"", quote("'f\\'o\"o\\\\'", '"'));
+
+ assertEquals("\"\\female \\nude fa\\rt fe\\tish\"", quote("\female \nude fa\rt fe\tish", '"'));
+ assertEquals("\"\\u666e\"", quote("\u666e", '"'));
+ }
+
+ @Test
+ public void testQuoteUnquote() {
+ assertEquals("\"foo\"", quote(unquote("'foo'"), '"'));
+ assertEquals("\"\\foo\"", quote(unquote(quote("\foo", '"')), '"'));
+ assertEquals("\u666e", unquote(quote("\u666e", '"')));
+ }
+
+ @Test
+ public void testUnquote() {
+ assertEquals("foo", unquote("foo"));
+ assertEquals("'foo", unquote("'foo"));
+ assertEquals("foo'", unquote("foo'"));
+ assertEquals("foo", unquote("'foo'"));
+ assertEquals("\"foo", unquote("\"foo"));
+ assertEquals("foo\"", unquote("foo\""));
+ assertEquals("foo", unquote("\"foo\""));
+ assertEquals("'foo'", unquote("\"'foo'\""));
+ assertEquals("\"foo\"", unquote("'\"foo\"'"));
+ assertEquals("f'o\"o\\", unquote("'f\\'o\"o\\\\'"));
+
+ assertEquals("\female \nude fa\rt fe\tish", unquote("'\\female \\nude fa\\rt fe\\tish'"));
+ assertEquals("\u666e", unquote("\"\\u666e\""));
+
+ try {
+ unquote("\"\\uSiM0N\"");
+ fail();
+ } catch (IllegalArgumentException e) {
+
+ }
+ assertEquals("simo\n", unquote("'\\s\\i\\m\\o\\n'"));
+ try {
+ unquote("\"foo\"bar\"");
+ fail();
+ } catch (IllegalArgumentException e) {
+
+ }
+ try {
+ unquote("'foo'bar'");
+ fail();
+ } catch (IllegalArgumentException e) {
+
+ }
+ }
+
+ @Test
+ public void requireThatTokenIncludesOnlyAcceptedChars() {
+ assertEquals("\"\\u0000\",\"\\u7777\",\"\\uffff\",",
+ UnicodeUtilities.generateToken(new UnicodeUtilities.Predicate() {
+
+ @Override
+ public boolean accepts(char c) {
+ return c == 0x0000 || c == 0x7777 || c == 0xffff;
+ }
+ }));
+ assertEquals("\"\\u0006\",\"\\u0009\",\"\\u0060\"-\"\\u0069\",",
+ UnicodeUtilities.generateToken(new UnicodeUtilities.Predicate() {
+
+ @Override
+ public boolean accepts(char c) {
+ return c == 0x6 || c == 0x9 || (c >= 0x60 && c <= 0x69);
+ }
+ }));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/net/HostNameTestCase.java b/vespajlib/src/test/java/com/yahoo/net/HostNameTestCase.java
new file mode 100644
index 00000000000..98be9f0ef6f
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/net/HostNameTestCase.java
@@ -0,0 +1,16 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.net;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+
+/**
+ * @author lulf
+ */
+public class HostNameTestCase {
+ @Test
+ public void testHostnameIsFound() {
+ assertFalse(HostName.getLocalhost().isEmpty());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/net/LinuxInetAddressTestCase.java b/vespajlib/src/test/java/com/yahoo/net/LinuxInetAddressTestCase.java
new file mode 100755
index 00000000000..fc1945d2d39
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/net/LinuxInetAddressTestCase.java
@@ -0,0 +1,41 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.net;
+
+import java.net.UnknownHostException;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class LinuxInetAddressTestCase extends junit.framework.TestCase {
+
+ public void testPreferIPv4() throws UnknownHostException {
+ try {
+ // This test only works if there is at least one inet address returned.
+ InetAddress[] arr = LinuxInetAddress.getAllLocal();
+ if (arr.length > 0) {
+ // System.out.println("Got " + arr.length + " addresses.");
+
+ // And it can only make sure it is preferred if there is at least one ip v4 address.
+ boolean ipv4 = false;
+ for (int i = 0; i < arr.length; ++i) {
+ // System.out.println("Address " + i + " is an instance of " + arr[i].getClass() + ".");
+ if (arr[i] instanceof Inet4Address) {
+ ipv4 = true;
+ }
+ }
+
+ // And the only thing we test is that an ip v4 address is preferred.
+ if (ipv4) {
+ InetAddress addr = LinuxInetAddress.getLocalHost();
+ assertNotNull("IPv4 is prefered", addr instanceof Inet4Address);
+ }
+ }
+ }
+ catch (java.net.UnknownHostException e) {
+ // We're on vpn or have no network
+ }
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/net/URITestCase.java b/vespajlib/src/test/java/com/yahoo/net/URITestCase.java
new file mode 100644
index 00000000000..7bb2303d7bb
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/net/URITestCase.java
@@ -0,0 +1,512 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.net;
+
+
+import java.util.List;
+
+
+/**
+ * Tests the URI class
+ *
+ * @author <a href="mailto:bratseth@fast.no">Jon S Bratseth</a>
+ */
+public class URITestCase extends junit.framework.TestCase {
+
+ public URITestCase(String name) {
+ super(name);
+ }
+
+ public void testEquality() {
+ URI one = new URI("http://www.nils.arne.com");
+ URI two = new URI("http://www.nils.arne.com");
+
+ assertEquals(one, two);
+ assertEquals(one.hashCode(), two.hashCode());
+ assertEquals("http://www.nils.arne.com/", one.toString());
+
+ assertEqualURIs(
+ "http://info.t.fast.no/art.php?sid=29&mode=thread&order=0",
+ "http://info.t.fast.no/art.php?sid=29&mode=thread&order=0");
+ assertEqualURIs("http://a/g/", "http://a/g/");
+ assertEquals("http://a/g;x?y#s",
+ new URI("http://a/g;x?y#s", true).stringValue());
+ assertEquals("http://a/g?y#s",
+ new URI("http://a/g?y#s", true).stringValue());
+ assertEqualURIs("http://a/b/c/.g", "http://a/b/c/.g");
+ assertEqualURIs("http://a/b/c/..g", "http://a/b/c/..g");
+ assertEqualURIs("http://a/b/c/g;x=1/y", "http://a/b/c/g;x=1/y");
+ assertEquals("http://a/b/c/g#s/../x",
+ new URI("http://a/b/c/g#s/../x", true).stringValue());
+ assertEquals("http://www.strange_host.com/b",
+ new URI("http://www.strange_host.com/b", true).stringValue());
+ }
+
+ public void testOpaque() {
+ URI uri = new URI("mailto:knut");
+
+ assertEquals("mailto:knut", uri.toString());
+ assertTrue(uri.isOpaque());
+ }
+
+ public void testValid() {
+ assertTrue(
+ new URI("http://www.one.com/isValid?even=if&theres=args").isValid());
+ assertTrue(
+ !new URI("http://www.one.com/isValid?even=if&theres=args").isOpaque());
+
+ assertTrue(!(new URI("not\\uri?", false, true).isValid()));
+
+ assertTrue(new URI("http://www.strange_host.com/b").isValid());
+ assertTrue(!new URI("http://www.strange_host.com/b").isOpaque());
+ }
+
+ public void testSorting() {
+ URI first = new URI("http://aisfirst.kanoo.com");
+ URI second = new URI("www.thentheresw.com");
+
+ assertTrue(first.compareTo(second) < 0);
+ assertTrue(second.compareTo(second) == 0);
+ assertTrue(second.compareTo(first) > 1);
+ }
+
+ public void testHost() {
+ assertEquals("a.b.c", new URI("http://A.B.C:567").getHost());
+ assertEquals("www.kanoo.com",
+ new URI("www.kanoo.com/foo", false, true).getHost());
+ assertEquals("a.b.c", new URI("http://a.b.C/foo").getHost());
+ assertEquals("a.b.c", new URI("http://a.b.C").getHost());
+ assertEquals("a", new URI("http://A").getHost());
+ assertEquals("a", new URI("http://A:80").getHost());
+ }
+
+ public void testUnfragmenting() {
+ assertEquals("http://www.sng.no/a/b/dee?kanoos&at=nught#chapter3",
+ new URI("http://www.sng.no/a/b/cee/../dee?kanoos&at=nught#chapter3", true).stringValue());
+ assertEquals("http://www.sng.no/a/b/dee?kanoos&at=nught",
+ new URI("http://www.sng.no/a/b/cee/../dee?kanoos&at=nught#chapter3", false).stringValue());
+ }
+
+ public void testNormalizing() {
+ // Abbreviation resolving heuristics
+ assertEquals("http://www.a.b/c",
+ new URI("www.a.b/c", false, true).toString());
+ assertEquals("file://x:\\a", new URI("x:\\a", false, true).toString());
+ assertEquals("file://c:/a", new URI("c:/a", false, true).toString());
+
+ // RFC 2396 normalizing
+ assertEqualURIs("http://a/c/d", "http://a/b/../c/d");
+ assertEqualURIs("http://a/b", "http://a/./b");
+
+ // FAST normalizing
+ assertEqualURIs("http://a/", " http://a ");
+ assertEqualURIs("http://a/%e6;m%e5;ha%f8;l", "http://a/\u00E6m\u00E5ha\u00F8l");
+ assertEqualURIs("http://a/&b", "http://a/&amp;b");
+ assertEqualURIs("http://a/", "http://A");
+ assertEqualURIs("http://a/", "http://a:80");
+ assertEqualURIs("https://a/", "https://a:443");
+ assertEqualURIs("http://a/", "http://a.");
+ assertEqualURIs("http://a/b", "http://a//b");
+ assertEqualURIs("http://a/b/", "http://A/b/");
+ assertEqualURIs("http://a/b/", "http://a./b/");
+ assertEqualURIs("http://a/", "http://a/b/../");
+ assertEqualURIs("http://a/../", "http://a/b/../a/../../");
+ assertEqualURIs("http://a/", "http://a/b/../");
+ assertEqualURIs("http://a/b/c/d", "http://a/b/c/d");
+ assertEqualURIs("http://a/b/c", "http://a/b/c#kanoo");
+
+ // Everything combined
+ assertEquals("http://www.a.b/m%e5;l/&/%f8;l&&/",
+ new URI(" WWW.a.B:80//m\u00E5l/.//&amp;/./\u00F8l&amp;&amp;/foo/../upp/./..", true, true).toString());
+ }
+
+ public void testParemeterAdding() {
+ assertEquals("http://a/?knug=zagg",
+ new URI("http://a/").addParameter("knug", "zagg").stringValue());
+ assertEquals("http://a/b?knug=zagg&fjukk=barra",
+ new URI("http://a/b?knug=zagg").addParameter("fjukk", "barra").stringValue());
+ }
+
+ private void assertEqualURIs(String fasit, String test) {
+ assertEquals(fasit, new URI(test).toString());
+ }
+
+ public void testDepth() {
+ assertEquals(0, new URI("test:hit").getDepth());
+ assertEquals(0, new URI("test://hit").getDepth());
+ assertEquals(0, new URI("test://hit/").getDepth());
+ assertEquals(1, new URI("test://hit.test/hello ").getDepth());
+ assertEquals(1, new URI("test://hit.test/hello/").getDepth());
+ assertEquals(0, new URI("test:// ").getDepth());
+ assertEquals(0, new URI("test:///").getDepth());
+ assertEquals(1, new URI("test:////").getDepth());
+ assertEquals(2, new URI("test://hit.test/hello/test2/").getDepth());
+ }
+
+ public void testURLEmpty() {
+ URI uri = new URI("", true);
+ assertTrue(uri.isValid());
+ assertNull(uri.getScheme());
+ assertNull(uri.getHost());
+ assertNull(uri.getDomain());
+ assertNull(uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertNull(uri.getPath());
+ assertNull(uri.getFilename());
+ assertNull(uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLDot() {
+ URI uri = new URI(".", true);
+ assertTrue(uri.isValid());
+ assertNull(uri.getScheme());
+ assertNull(uri.getHost());
+ assertNull(uri.getDomain());
+ assertNull(uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertNull(uri.getPath()); //differs from FastS_URL, "."
+ assertNull(uri.getFilename()); //differs from FastS_URL, "."
+ assertNull(uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLDotDot() {
+ URI uri = new URI("..", true);
+ assertTrue(uri.isValid());
+ assertNull(uri.getScheme());
+ assertNull(uri.getHost());
+ assertNull(uri.getDomain());
+ assertNull(uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertNull(uri.getPath()); //differs from FastS_URL, ".."
+ assertNull(uri.getFilename()); //differs from FastS_URL, ".."
+ assertNull(uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLUninett() {
+ URI uri = new URI("http://180.uninett.no/servlet/online.Bransje", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("180.uninett.no", uri.getHost());
+ assertEquals("uninett.no", uri.getDomain());
+ assertEquals("no", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/servlet/online.Bransje", uri.getPath());
+ assertEquals("online.Bransje", uri.getFilename());
+ assertEquals("Bransje", uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLUnderdusken() {
+ URI uri = new URI("http://www.underdusken.no", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("www.underdusken.no", uri.getHost());
+ assertEquals("underdusken.no", uri.getDomain());
+ assertEquals("no", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("", uri.getPath());
+ assertEquals("", uri.getFilename());
+ assertNull(uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLUnderduskenUholdbar() {
+ URI uri =
+ new URI("http://www.underdusken.no/?page=dusker/html/0008/Uholdbar.html", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("www.underdusken.no", uri.getHost());
+ assertEquals("underdusken.no", uri.getDomain());
+ assertEquals("no", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/", uri.getPath());
+ assertEquals("", uri.getFilename());
+ assertNull(uri.getExtension());
+ assertNull(uri.getParams());
+ assertEquals("page=dusker/html/0008/Uholdbar.html", uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLUniKarlsruhe() {
+ URI uri = new URI("http://www.uni-karlsruhe.de/~ig25/ssh-faq/", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("www.uni-karlsruhe.de", uri.getHost());
+ assertEquals("uni-karlsruhe.de", uri.getDomain());
+ assertEquals("de", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/~ig25/ssh-faq/", uri.getPath());
+ assertEquals("", uri.getFilename());
+ assertNull(uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLDetteErEn() {
+ URI uri = new URI("https://dette.er.en:2020/~janie/index.htm?param1=q&param2=r", true);
+ assertTrue(uri.isValid());
+ assertEquals("https", uri.getScheme());
+ assertEquals("dette.er.en", uri.getHost());
+ assertEquals("er.en", uri.getDomain());
+ assertEquals("en", uri.getMainTld());
+ assertEquals(2020, uri.getPort());
+ assertEquals("/~janie/index.htm", uri.getPath());
+ assertEquals("index.htm", uri.getFilename());
+ assertEquals("htm", uri.getExtension());
+ assertNull(uri.getParams());
+ assertEquals("param1=q&param2=r", uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLSonyCoUk() {
+ URI uri = new URI("http://www.sony.co.uk/", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("www.sony.co.uk", uri.getHost());
+ assertEquals("sony.co.uk", uri.getDomain());
+ assertEquals("uk", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/", uri.getPath());
+ assertEquals("", uri.getFilename());
+ assertNull(uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLSonyCoUk2() {
+ URI uri = new URI("http://sony.co.uk/", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("sony.co.uk", uri.getHost());
+ //TODO: Fix when tldlist is implemented:
+ //assertEquals("sony.co.uk", uri.getDomain());
+ assertEquals("co.uk", uri.getDomain());
+ assertEquals("uk", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/", uri.getPath());
+ assertEquals("", uri.getFilename());
+ assertNull(uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLSomehostSomedomain() {
+ URI uri = new URI("http://somehost.somedomain/this!is!it/boom", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("somehost.somedomain", uri.getHost());
+ assertEquals("somehost.somedomain", uri.getDomain());
+ assertEquals("somedomain", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/this!is!it/boom", uri.getPath());
+ assertEquals("boom", uri.getFilename());
+ assertNull(uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLTestCom() {
+ URI uri = new URI("http://test.com/index.htm?p1=q%20test&p2=r%10d", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("test.com", uri.getHost());
+ assertEquals("test.com", uri.getDomain());
+ assertEquals("com", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/index.htm", uri.getPath());
+ assertEquals("index.htm", uri.getFilename());
+ assertEquals("htm", uri.getExtension());
+ assertNull(uri.getParams());
+ assertEquals("p1=q%20test&p2=r%10d", uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLArthur() {
+ URI uri = new URI("http://arthur/qm/images/qm1.gif", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("arthur", uri.getHost());
+ assertEquals("arthur", uri.getDomain());
+ assertNull(uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/qm/images/qm1.gif", uri.getPath());
+ assertEquals("qm1.gif", uri.getFilename());
+ assertEquals("gif", uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLFooCom() {
+ URI uri = new URI("http://foo.com/ui;.gif", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("foo.com", uri.getHost());
+ assertEquals("foo.com", uri.getDomain());
+ assertEquals("com", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/ui;.gif", uri.getPath());
+ assertEquals("ui", uri.getFilename());
+ assertNull(uri.getExtension());
+ assertEquals(".gif", uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLFooCom2() {
+ URI uri = new URI("http://foo.com/ui;par1=1/par2=2", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("foo.com", uri.getHost());
+ assertEquals("foo.com", uri.getDomain());
+ assertEquals("com", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/ui;par1=1/par2=2", uri.getPath());
+ assertEquals("ui", uri.getFilename());
+ assertNull(uri.getExtension());
+ assertEquals("par1=1/par2=2", uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testURLFooNo() {
+ URI uri = new URI(
+ "http://www.foo.no:8080/path/filename.ext;par1=hello/par2=world?query=test#fragment", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("www.foo.no", uri.getHost());
+ assertEquals("foo.no", uri.getDomain());
+ assertEquals("no", uri.getMainTld());
+ assertEquals(8080, uri.getPort());
+ assertEquals("/path/filename.ext;par1=hello/par2=world", uri.getPath());
+ assertEquals("filename.ext", uri.getFilename());
+ assertEquals("ext", uri.getExtension());
+ assertEquals("par1=hello/par2=world", uri.getParams());
+ assertEquals("query=test", uri.getQuery());
+ assertEquals("fragment", uri.getFragment());
+ }
+
+ public void testURLAmpersand() {
+ URI uri = new URI("http://canonsarang.com/zboard/data/gallery04/HU&BANG.jpg", true);
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("canonsarang.com", uri.getHost());
+ assertEquals("canonsarang.com", uri.getDomain());
+ assertEquals("com", uri.getMainTld());
+ assertEquals(-1, uri.getPort());
+ assertEquals("/zboard/data/gallery04/HU&BANG.jpg", uri.getPath());
+ assertEquals("HU&BANG.jpg", uri.getFilename());
+ assertEquals("jpg", uri.getExtension());
+ assertNull(uri.getParams());
+ assertNull(uri.getQuery());
+ assertNull(uri.getFragment());
+ }
+
+ public void testQMark() {
+ URI uri = new URI("http://foobar/?");
+ assertTrue(uri.isValid());
+ assertEquals("http", uri.getScheme());
+ assertEquals("foobar", uri.getHost());
+ assertEquals("", uri.getQuery());
+ }
+
+ public void testTokenization() {
+ URI uri = new URI("http://this.i_s:5000/wo_ho;ba-lo?gobo#banana", true);
+ List<URI.Token> tokens = uri.tokenize();
+ URI.Token token;
+
+ token = tokens.get(0);
+ assertEquals("http", token.getToken());
+ assertEquals(URI.URLContext.URL_SCHEME, token.getContext());
+
+ token = tokens.get(1);
+ assertEquals("this", token.getToken());
+ assertEquals(URI.URLContext.URL_HOST, token.getContext());
+
+ token = tokens.get(2);
+ assertEquals("i_s", token.getToken());
+ assertEquals(URI.URLContext.URL_HOST, token.getContext());
+
+ token = tokens.get(3);
+ assertEquals("5000", token.getToken());
+ assertEquals(URI.URLContext.URL_PORT, token.getContext());
+
+ token = tokens.get(4);
+ assertEquals("wo_ho", token.getToken());
+ assertEquals(URI.URLContext.URL_PATH, token.getContext());
+
+ token = tokens.get(5);
+ assertEquals("ba-lo", token.getToken());
+ assertEquals(URI.URLContext.URL_PATH, token.getContext());
+
+ token = tokens.get(6);
+ assertEquals("gobo", token.getToken());
+ assertEquals(URI.URLContext.URL_QUERY, token.getContext());
+
+ token = tokens.get(7);
+ assertEquals("banana", token.getToken());
+ assertEquals(URI.URLContext.URL_FRAGMENT, token.getContext());
+
+ try {
+ tokens.get(8);
+ fail();
+ } catch (IndexOutOfBoundsException ioobe) {
+ }
+ }
+
+ // Error reported int bug #2466528
+ public void testFileURIEmptyHost() {
+ URI uri = new URI("file:///C:/Inetpub/wwwroot/DW_SHORTCUTS.htm");
+ List<URI.Token> tokens = uri.tokenize();
+ URI.Token token;
+ token = tokens.get(0);
+ assertEquals("file", token.getToken());
+ assertEquals(URI.URLContext.URL_SCHEME, token.getContext());
+
+ token = tokens.get(1);
+ assertEquals("localhost", token.getToken());
+ assertEquals(URI.URLContext.URL_HOST, token.getContext());
+
+ token = tokens.get(2);
+ assertEquals("C", token.getToken());
+ assertEquals(URI.URLContext.URL_PATH, token.getContext());
+
+ token = tokens.get(3);
+ assertEquals("Inetpub", token.getToken());
+ assertEquals(URI.URLContext.URL_PATH, token.getContext());
+
+ token = tokens.get(4);
+ assertEquals("wwwroot", token.getToken());
+ assertEquals(URI.URLContext.URL_PATH, token.getContext());
+
+ token = tokens.get(5);
+ assertEquals("DW_SHORTCUTS", token.getToken());
+ assertEquals(URI.URLContext.URL_PATH, token.getContext());
+
+ token = tokens.get(6);
+ assertEquals("htm", token.getToken());
+ assertEquals(URI.URLContext.URL_PATH, token.getContext());
+
+ try {
+ tokens.get(7);
+ fail();
+ } catch (IndexOutOfBoundsException ioobe) {
+ }
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/net/UriToolsTestCase.java b/vespajlib/src/test/java/com/yahoo/net/UriToolsTestCase.java
new file mode 100644
index 00000000000..9a17fa341be
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/net/UriToolsTestCase.java
@@ -0,0 +1,25 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.net;
+
+import static org.junit.Assert.*;
+
+import java.net.URISyntaxException;
+
+import org.junit.Test;
+
+/**
+ * Check validity of the URI helper methods.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class UriToolsTestCase {
+
+ private static final String SEARCH_QUERY = "/search/?query=sddocname:music#trick";
+
+ @Test
+ public final void testRawRequest() throws URISyntaxException {
+ java.net.URI uri = new java.net.URI("http://localhost:" + 8080 + SEARCH_QUERY);
+ assertEquals(SEARCH_QUERY, UriTools.rawRequest(uri));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/net/UrlTestCase.java b/vespajlib/src/test/java/com/yahoo/net/UrlTestCase.java
new file mode 100644
index 00000000000..7cf99cf2c5a
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/net/UrlTestCase.java
@@ -0,0 +1,192 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.net;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class UrlTestCase {
+
+ @Test
+ public void requireThatAccessorsWork() {
+ Url url = Url.fromString("scheme://user:pass@host:69/path?query#fragment");
+ assertEquals("scheme://user:pass@host:69/path?query#fragment", url.toString());
+ assertEquals("scheme", url.getScheme());
+ assertEquals(0, url.getSchemeBegin());
+ assertEquals(6, url.getSchemeEnd());
+ assertEquals("user", url.getUserInfo());
+ assertEquals(9, url.getUserInfoBegin());
+ assertEquals(13, url.getUserInfoEnd());
+ assertEquals("pass", url.getPassword());
+ assertEquals(14, url.getPasswordBegin());
+ assertEquals(18, url.getPasswordEnd());
+ assertEquals("host", url.getHost());
+ assertEquals(19, url.getHostBegin());
+ assertEquals(23, url.getHostEnd());
+ assertEquals("69", url.getPortString());
+ assertEquals(24, url.getPortBegin());
+ assertEquals(26, url.getPortEnd());
+ assertEquals(Integer.valueOf(69), url.getPort());
+ assertEquals("/path", url.getPath());
+ assertEquals(26, url.getPathBegin());
+ assertEquals(31, url.getPathEnd());
+ assertEquals("query", url.getQuery());
+ assertEquals(32, url.getQueryBegin());
+ assertEquals(37, url.getQueryEnd());
+ assertEquals("fragment", url.getFragment());
+ assertEquals(38, url.getFragmentBegin());
+ assertEquals(46, url.getFragmentEnd());
+ }
+
+ @Test
+ public void requireThatOffsetsAreNeverOutOfBounds() {
+ Url url = Url.fromString("http:");
+ assertEquals(0, url.getSchemeBegin());
+ assertEquals(4, url.getSchemeEnd());
+ assertEquals(5, url.getUserInfoBegin());
+ assertEquals(5, url.getUserInfoEnd());
+ assertEquals(5, url.getPasswordBegin());
+ assertEquals(5, url.getPasswordEnd());
+ assertEquals(5, url.getHostBegin());
+ assertEquals(5, url.getHostEnd());
+ assertEquals(5, url.getPortBegin());
+ assertEquals(5, url.getPortEnd());
+ assertEquals(5, url.getPathBegin());
+ assertEquals(5, url.getPathEnd());
+ assertEquals(5, url.getQueryBegin());
+ assertEquals(5, url.getQueryEnd());
+ assertEquals(5, url.getFragmentBegin());
+ assertEquals(5, url.getFragmentEnd());
+
+ url = Url.fromString("//host");
+ assertEquals(0, url.getSchemeBegin());
+ assertEquals(0, url.getSchemeEnd());
+ assertEquals(2, url.getUserInfoBegin());
+ assertEquals(2, url.getUserInfoEnd());
+ assertEquals(2, url.getPasswordBegin());
+ assertEquals(2, url.getPasswordEnd());
+ assertEquals(2, url.getHostBegin());
+ assertEquals(6, url.getHostEnd());
+ assertEquals(6, url.getPortBegin());
+ assertEquals(6, url.getPortEnd());
+ assertEquals(6, url.getPathBegin());
+ assertEquals(6, url.getPathEnd());
+ assertEquals(6, url.getQueryBegin());
+ assertEquals(6, url.getQueryEnd());
+ assertEquals(6, url.getFragmentBegin());
+ assertEquals(6, url.getFragmentEnd());
+ }
+
+ @Test
+ public void requireThatCommonSchemesCanBeParsed() {
+ assertParse("ftp://ftp.is.co.za/rfc/rfc1808.txt",
+ "ftp", null, null, "ftp.is.co.za", null, "/rfc/rfc1808.txt", null, null);
+ assertParse("http://www.ietf.org/rfc/rfc 2396.txt",
+ "http", null, null, "www.ietf.org", null, "/rfc/rfc 2396.txt", null, null);
+ assertParse("ldap://[2001:db8::7]/c=GB?objectClass?one",
+ "ldap", null, null, "2001:db8::7", null, "/c=GB", "objectClass?one", null);
+ assertParse("mailto:John.Doe@example.com",
+ "mailto", null, null, null, null, "John.Doe@example.com", null, null);
+ assertParse("news:comp.infosystems.www.servers.unix",
+ "news", null, null, null, null, "comp.infosystems.www.servers.unix", null, null);
+ assertParse("tel:+1-816-555-1212",
+ "tel", null, null, null, null, "+1-816-555-1212", null, null);
+ assertParse("telnet://192.0.2.16:80/",
+ "telnet", null, null, "192.0.2.16", 80, "/", null, null);
+ assertParse("urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
+ "urn", null, null, null, null, "oasis:names:specification:docbook:dtd:xml:4.1.2", null, null);
+ }
+
+ @Test
+ public void requireThatAllComponentsCanBeParsed() {
+ assertParse("scheme:",
+ "scheme", null, null, null, null, null, null, null);
+ assertParse("scheme://",
+ "scheme", null, null, null, null, "//", null, null);
+ assertParse("scheme://host",
+ "scheme", null, null, "host", null, null, null, null);
+ try {
+ assertParse("scheme://host:foo",
+ null, null, null, null, null, null, null, null);
+ fail();
+ } catch (NumberFormatException e) {
+ // expected
+ }
+ assertParse("scheme://host:69",
+ "scheme", null, null, "host", 69, null, null, null);
+ assertParse("scheme://user@host:69",
+ "scheme", "user", null, "host", 69, null, null, null);
+ assertParse("scheme://user:pass@host:69",
+ "scheme", "user", "pass", "host", 69, null, null, null);
+ assertParse("scheme://user:pass@host:69",
+ "scheme", "user", "pass", "host", 69, null, null, null);
+ assertParse("scheme://user:pass@host:69/",
+ "scheme", "user", "pass", "host", 69, "/", null, null);
+ assertParse("scheme://user:pass@host:69/path",
+ "scheme", "user", "pass", "host", 69, "/path", null, null);
+ assertParse("scheme://user:pass@host:69/path?query",
+ "scheme", "user", "pass", "host", 69, "/path", "query", null);
+ assertParse("scheme://user:pass@host:69/path?query#fragment",
+ "scheme", "user", "pass", "host", 69, "/path", "query", "fragment");
+ assertParse("scheme://user@host:69/path?query#fragment",
+ "scheme", "user", null, "host", 69, "/path", "query", "fragment");
+ assertParse("scheme://host:69/path?query#",
+ "scheme", null, null, "host", 69, "/path", "query", null);
+ assertParse("scheme://host:69/path?query#fragment",
+ "scheme", null, null, "host", 69, "/path", "query", "fragment");
+ assertParse("scheme://host/path?query#fragment",
+ "scheme", null, null, "host", null, "/path", "query", "fragment");
+ assertParse("scheme:///path?query#fragment",
+ "scheme", null, null, null, null, "///path", "query", "fragment");
+ assertParse("scheme://?query#fragment",
+ "scheme", null, null, null, null, "//", "query", "fragment");
+ assertParse("scheme://#fragment",
+ "scheme", null, null, null, null, "//", null, "fragment");
+ }
+
+ @Test
+ public void requireThatIPv6CanBeParsed() {
+ assertParse("http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
+ "http", null, null, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", null, null, null, null);
+ assertParse("http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]/path",
+ "http", null, null, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", null, "/path", null, null);
+
+ assertParse("http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80",
+ "http", null, null, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", 80, null, null, null);
+ assertParse("http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80/path",
+ "http", null, null, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", 80, "/path", null, null);
+ }
+
+ private static void assertParse(String input, String scheme, String userInfo, String password, String host,
+ Integer port, String path, String query, String fragment)
+ {
+ Url urlA = Url.fromString(input);
+ assertEquals("Image", input, urlA.toString());
+ assertUrl(urlA, scheme, userInfo, password, host, port, path, query, fragment);
+
+ Url urlB = new Url(urlA.getScheme(), urlA.getUserInfo(), urlA.getPassword(), urlA.getHost(), urlA.getPort(),
+ urlA.getPath(), urlA.getQuery(), urlA.getFragment());
+ assertUrl(urlB, scheme, userInfo, password, host, port, path, query, fragment);
+
+ Url urlC = Url.fromString(urlB.toString());
+ assertEquals(urlB, urlC);
+ assertUrl(urlC, scheme, userInfo, password, host, port, path, query, fragment);
+ }
+
+ private static void assertUrl(Url url, String scheme, String userInfo, String password, String host, Integer port,
+ String path, String query, String fragment)
+ {
+ assertEquals("Scheme", scheme, url.getScheme());
+ assertEquals("User", userInfo, url.getUserInfo());
+ assertEquals("Password", password, url.getPassword());
+ assertEquals("Host", host, url.getHost());
+ assertEquals("Port", port, url.getPort());
+ assertEquals("Path", path, url.getPath());
+ assertEquals("Query", query, url.getQuery());
+ assertEquals("Fragment", fragment, url.getFragment());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/net/UrlTokenTestCase.java b/vespajlib/src/test/java/com/yahoo/net/UrlTokenTestCase.java
new file mode 100644
index 00000000000..ca42a701655
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/net/UrlTokenTestCase.java
@@ -0,0 +1,47 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.net;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class UrlTokenTestCase {
+
+ @Test
+ public void requireThatAccessorsWork() {
+ UrlToken token = new UrlToken(UrlToken.Type.FRAGMENT, 69, "foo", "bar");
+ assertEquals(UrlToken.Type.FRAGMENT, token.getType());
+ assertEquals(69, token.getOffset());
+ assertEquals(3, token.getLength());
+ assertEquals("foo", token.getOrig());
+ assertEquals("bar", token.getTerm());
+ }
+
+ @Test
+ public void requireThatTypeCanNotBeNull() {
+ try {
+ new UrlToken(null, 0, "foo", "bar");
+ fail();
+ } catch (NullPointerException e) {
+
+ }
+ }
+
+ @Test
+ public void requireThatOrigAndTermCanBeNull() {
+ UrlToken token = new UrlToken(UrlToken.Type.SCHEME, 0, null, "foo");
+ assertNull(token.getOrig());
+ assertEquals("foo", token.getTerm());
+
+ token = new UrlToken(UrlToken.Type.SCHEME, 0, "foo", null);
+ assertEquals("foo", token.getOrig());
+ assertNull(token.getTerm());
+
+ token = new UrlToken(UrlToken.Type.SCHEME, 0, null, null);
+ assertNull(token.getOrig());
+ assertNull(token.getTerm());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/net/UrlTokenizerTestCase.java b/vespajlib/src/test/java/com/yahoo/net/UrlTokenizerTestCase.java
new file mode 100644
index 00000000000..d192c0901a6
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/net/UrlTokenizerTestCase.java
@@ -0,0 +1,385 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.net;
+
+import org.junit.Test;
+
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+import static com.yahoo.text.Lowercase.toLowerCase;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class UrlTokenizerTestCase {
+
+ @Test
+ public void requireThatAllTokenCharactersAreAccepted() {
+ assertTerms("a", "a");
+ assertTerms("aa", "aa");
+ assertTerms("aaa", "aaa");
+ for (int c = Character.MIN_VALUE; c < Character.MAX_VALUE; ++c) {
+ if (c == '%') {
+ continue; // escape
+ }
+ String img = String.format("a%ca", c);
+ if ((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c == '-' || c == '_'))
+ {
+ assertTerms(img, toLowerCase(img));
+ } else {
+ assertTerms(img, "a", "a");
+ }
+ }
+ }
+
+ @Test
+ public void requireThatUrlCanBeTokenized() {
+ assertTokenize("",
+ new UrlToken(UrlToken.Type.SCHEME, 0, null, "http"),
+ new UrlToken(UrlToken.Type.PORT, 0, null, "80"));
+ assertTokenize("scheme:",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"));
+ assertTokenize("scheme://host",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.HOST, 9, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 9, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 13, null, UrlTokenizer.TERM_ENDHOST));
+ assertTokenize("scheme://user@host",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.HOST, 14, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 14, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 18, null, UrlTokenizer.TERM_ENDHOST));
+ assertTokenize("scheme://user:pass@host",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST));
+ assertTokenize("scheme://user:pass@host:69",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 24, "69", "69"));
+ assertTokenize("scheme://user:pass@host:69/path",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 24, "69", "69"),
+ new UrlToken(UrlToken.Type.PATH, 27, "path", "path"));
+ assertTokenize("scheme://user:pass@host:69/path?query",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 24, "69", "69"),
+ new UrlToken(UrlToken.Type.PATH, 27, "path", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 32, "query", "query"));
+ assertTokenize("scheme://user:pass@host:69/path?query#fragment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 24, "69", "69"),
+ new UrlToken(UrlToken.Type.PATH, 27, "path", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 32, "query", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 38, "fragment", "fragment"));
+ }
+
+ @Test
+ public void requireThatComponentsCanHaveMultipleTokens() {
+ assertTokenize("sch+eme://us+er:pa+ss@ho+st:69/pa/th?que+ry#frag+ment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "sch", "sch"),
+ new UrlToken(UrlToken.Type.SCHEME, 4, "eme", "eme"),
+ new UrlToken(UrlToken.Type.USERINFO, 10, "us", "us"),
+ new UrlToken(UrlToken.Type.USERINFO, 13, "er", "er"),
+ new UrlToken(UrlToken.Type.PASSWORD, 16, "pa", "pa"),
+ new UrlToken(UrlToken.Type.PASSWORD, 19, "ss", "ss"),
+ new UrlToken(UrlToken.Type.HOST, 22, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 22, "ho", "ho"),
+ new UrlToken(UrlToken.Type.HOST, 25, "st", "st"),
+ new UrlToken(UrlToken.Type.HOST, 27, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 28, "69", "69"),
+ new UrlToken(UrlToken.Type.PATH, 31, "pa", "pa"),
+ new UrlToken(UrlToken.Type.PATH, 34, "th", "th"),
+ new UrlToken(UrlToken.Type.QUERY, 37, "que", "que"),
+ new UrlToken(UrlToken.Type.QUERY, 41, "ry", "ry"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 44, "frag", "frag"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 49, "ment", "ment"));
+ }
+ @Test
+ public void requireThatSequencesOfDelimitersAreCollapsed() {
+ assertTokenize("sch++eme://us++er:pa++ss@ho++st:69/pa/th?que++ry#frag++ment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "sch", "sch"),
+ new UrlToken(UrlToken.Type.SCHEME, 5, "eme", "eme"),
+ new UrlToken(UrlToken.Type.USERINFO, 11, "us", "us"),
+ new UrlToken(UrlToken.Type.USERINFO, 15, "er", "er"),
+ new UrlToken(UrlToken.Type.PASSWORD, 18, "pa", "pa"),
+ new UrlToken(UrlToken.Type.PASSWORD, 22, "ss", "ss"),
+ new UrlToken(UrlToken.Type.HOST, 25, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 25, "ho", "ho"),
+ new UrlToken(UrlToken.Type.HOST, 29, "st", "st"),
+ new UrlToken(UrlToken.Type.HOST, 31, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 32, "69", "69"),
+ new UrlToken(UrlToken.Type.PATH, 35, "pa", "pa"),
+ new UrlToken(UrlToken.Type.PATH, 38, "th", "th"),
+ new UrlToken(UrlToken.Type.QUERY, 41, "que", "que"),
+ new UrlToken(UrlToken.Type.QUERY, 46, "ry", "ry"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 49, "frag", "frag"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 55, "ment", "ment"));
+ }
+
+ @Test
+ public void requireThatIPv6CanBeTokenized() {
+ assertTokenize("scheme://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.HOST, 10, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 10, "2001", "2001"),
+ new UrlToken(UrlToken.Type.HOST, 15, "0db8", "0db8"),
+ new UrlToken(UrlToken.Type.HOST, 20, "85a3", "85a3"),
+ new UrlToken(UrlToken.Type.HOST, 25, "0000", "0000"),
+ new UrlToken(UrlToken.Type.HOST, 30, "0000", "0000"),
+ new UrlToken(UrlToken.Type.HOST, 35, "8a2e", "8a2e"),
+ new UrlToken(UrlToken.Type.HOST, 40, "0370", "0370"),
+ new UrlToken(UrlToken.Type.HOST, 45, "7334", "7334"),
+ new UrlToken(UrlToken.Type.HOST, 49, null, UrlTokenizer.TERM_ENDHOST));
+ }
+
+ @Test
+ public void requireThatTermsAreLowerCased() {
+ assertTokenize("SCHEME://USER:PASS@HOST:69/PATH?QUERY#FRAGMENT",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "SCHEME", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "USER", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "PASS", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "HOST", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 24, "69", "69"),
+ new UrlToken(UrlToken.Type.PATH, 27, "PATH", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 32, "QUERY", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 38, "FRAGMENT", "fragment"));
+ }
+
+ @Test
+ public void requireThatEscapedCharsAreDecoded() {
+ assertTokenize("sch%65me://%75ser:p%61ss@h%6fst:69/p%61th?q%75ery#fr%61gment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "sch%65me", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 11, "%75ser", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 18, "p%61ss", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 25, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 25, "h%6fst", "host"),
+ new UrlToken(UrlToken.Type.HOST, 31, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 32, "69", "69"),
+ new UrlToken(UrlToken.Type.PATH, 35, "p%61th", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 42, "q%75ery", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 50, "fr%61gment", "fragment"));
+ }
+
+ @Test
+ public void requireThatDecodedCharsAreLowerCased() {
+ assertTokenize("sch%45me://%55ser:p%41ss@h%4fst:69/p%41th?q%55ery#fr%41gment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "sch%45me", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 11, "%55ser", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 18, "p%41ss", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 25, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 25, "h%4fst", "host"),
+ new UrlToken(UrlToken.Type.HOST, 31, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 32, "69", "69"),
+ new UrlToken(UrlToken.Type.PATH, 35, "p%41th", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 42, "q%55ery", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 50, "fr%41gment", "fragment"));
+ }
+
+ @Test
+ public void requireThatDecodedCharsCanSplitTokens() {
+ assertTokenize("sch%2beme://us%2ber:pa%2bss@ho%2bst:69/pa/th?que%2bry#frag%2bment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "sch", "sch"),
+ new UrlToken(UrlToken.Type.SCHEME, 6, "eme", "eme"),
+ new UrlToken(UrlToken.Type.USERINFO, 12, "us", "us"),
+ new UrlToken(UrlToken.Type.USERINFO, 17, "er", "er"),
+ new UrlToken(UrlToken.Type.PASSWORD, 20, "pa", "pa"),
+ new UrlToken(UrlToken.Type.PASSWORD, 25, "ss", "ss"),
+ new UrlToken(UrlToken.Type.HOST, 28, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 28, "ho", "ho"),
+ new UrlToken(UrlToken.Type.HOST, 33, "st", "st"),
+ new UrlToken(UrlToken.Type.HOST, 35, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 36, "69", "69"),
+ new UrlToken(UrlToken.Type.PATH, 39, "pa", "pa"),
+ new UrlToken(UrlToken.Type.PATH, 42, "th", "th"),
+ new UrlToken(UrlToken.Type.QUERY, 45, "que", "que"),
+ new UrlToken(UrlToken.Type.QUERY, 51, "ry", "ry"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 54, "frag", "frag"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 61, "ment", "ment"));
+ }
+
+ @Test
+ public void requireThatSchemeCanBeGuessed() {
+ assertTokenize("//host:80",
+ new UrlToken(UrlToken.Type.SCHEME, 0, null, "http"),
+ new UrlToken(UrlToken.Type.HOST, 2, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 2, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 6, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 7, "80", "80"));
+ }
+
+ @Test
+ public void requireThatHostCanBeGuessed() {
+ assertTokenize("file:/path",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "file", "file"),
+ new UrlToken(UrlToken.Type.HOST, 4, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 4, null, "localhost"),
+ new UrlToken(UrlToken.Type.HOST, 4, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PATH, 6, "path", "path"));
+ }
+
+ @Test
+ public void requireThatPortCanBeGuessed() {
+ assertTokenize("http://host",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "http", "http"),
+ new UrlToken(UrlToken.Type.HOST, 7, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 7, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 11, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 11, null, "80"));
+ }
+
+ @Test
+ public void requireThatComponentsAreOptional() {
+ assertTokenize("scheme", "user", "pass", "host", 99, "/path", "query", "fragment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 24, "99", "99"),
+ new UrlToken(UrlToken.Type.PATH, 27, "path", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 32, "query", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 38, "fragment", "fragment"));
+ assertTokenize(null, "user", "pass", "host", 99, "/path", "query", "fragment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, null, "http"),
+ new UrlToken(UrlToken.Type.USERINFO, 2, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 7, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 12, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 12, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 16, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 17, "99", "99"),
+ new UrlToken(UrlToken.Type.PATH, 20, "path", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 25, "query", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 31, "fragment", "fragment"));
+ assertTokenize("scheme", null, "pass", "host", 99, "/path", "query", "fragment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.PASSWORD, 10, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 15, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 15, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 20, "99", "99"),
+ new UrlToken(UrlToken.Type.PATH, 23, "path", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 28, "query", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 34, "fragment", "fragment"));
+ assertTokenize("scheme", null, null, "host", 99, "/path", "query", "fragment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.HOST, 9, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 9, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 13, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 14, "99", "99"),
+ new UrlToken(UrlToken.Type.PATH, 17, "path", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 22, "query", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 28, "fragment", "fragment"));
+ assertTokenize("scheme", null, null, null, 99, "/path", "query", "fragment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.PORT, 8, "99", "99"),
+ new UrlToken(UrlToken.Type.PATH, 11, "path", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 16, "query", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 22, "fragment", "fragment"));
+ assertTokenize("scheme", "user", "pass", "host", null, "/path", "query", "fragment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PATH, 24, "path", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 29, "query", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 35, "fragment", "fragment"));
+ assertTokenize("scheme", "user", "pass", "host", 99, null, "query", "fragment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 24, "99", "99"),
+ new UrlToken(UrlToken.Type.QUERY, 27, "query", "query"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 33, "fragment", "fragment"));
+ assertTokenize("scheme", "user", "pass", "host", 99, "/path", null, "fragment",
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 24, "99", "99"),
+ new UrlToken(UrlToken.Type.PATH, 27, "path", "path"),
+ new UrlToken(UrlToken.Type.FRAGMENT, 32, "fragment", "fragment"));
+ assertTokenize("scheme", "user", "pass", "host", 99, "/path", "query", null,
+ new UrlToken(UrlToken.Type.SCHEME, 0, "scheme", "scheme"),
+ new UrlToken(UrlToken.Type.USERINFO, 9, "user", "user"),
+ new UrlToken(UrlToken.Type.PASSWORD, 14, "pass", "pass"),
+ new UrlToken(UrlToken.Type.HOST, 19, null, UrlTokenizer.TERM_STARTHOST),
+ new UrlToken(UrlToken.Type.HOST, 19, "host", "host"),
+ new UrlToken(UrlToken.Type.HOST, 23, null, UrlTokenizer.TERM_ENDHOST),
+ new UrlToken(UrlToken.Type.PORT, 24, "99", "99"),
+ new UrlToken(UrlToken.Type.PATH, 27, "path", "path"),
+ new UrlToken(UrlToken.Type.QUERY, 32, "query", "query"));
+ }
+
+ private static void assertTokenize(String scheme, String userInfo, String password, String host, Integer port,
+ String path, String query, String fragment, UrlToken... expected)
+ {
+ assertTokenize(new Url(scheme, userInfo, password, host, port, path, query, fragment), expected);
+ }
+
+ private static void assertTokenize(String url, UrlToken... expected) {
+ assertTokenize(Url.fromString(url), expected);
+ }
+
+ private static void assertTokenize(Url url, UrlToken... expected) {
+ Iterator<UrlToken> expectedIt = Arrays.asList(expected).iterator();
+ Iterator<UrlToken> actualIt = new UrlTokenizer(url).tokenize().iterator();
+ while (expectedIt.hasNext()) {
+ assertTrue(actualIt.hasNext());
+ assertEquals(expectedIt.next(), actualIt.next());
+ }
+ assertFalse(expectedIt.hasNext());
+ assertFalse(actualIt.hasNext());
+ }
+
+ private static void assertTerms(String img, String... expected) {
+ List<UrlToken> actual = new LinkedList<>();
+ UrlTokenizer.addTokens(actual, UrlToken.Type.PATH, 0, img, true);
+
+ Iterator<String> expectedIt = Arrays.asList(expected).iterator();
+ Iterator<UrlToken> actualIt = actual.iterator();
+ while (expectedIt.hasNext()) {
+ assertTrue(actualIt.hasNext());
+ assertEquals(expectedIt.next(), actualIt.next().getTerm());
+ }
+ assertFalse(expectedIt.hasNext());
+ assertFalse(actualIt.hasNext());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/path/PathTest.java b/vespajlib/src/test/java/com/yahoo/path/PathTest.java
new file mode 100644
index 00000000000..4d78df0f4e9
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/path/PathTest.java
@@ -0,0 +1,128 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.path;
+
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author lulf
+ * @since 5.1
+ */
+public class PathTest {
+ @Test
+ public void testGetName() {
+ assertThat(getAbsolutePath().getName(), is("baz"));
+ assertThat(getRelativePath().getName(), is("baz"));
+ assertThat(getWithSlashes().getName(), is("baz"));
+ assertThat(getAppended().getName(), is("baz"));
+ assertThat(getOne().getName(), is("foo"));
+ }
+
+ @Test
+ public void testEquals() {
+ assertTrue(getAbsolutePath().equals(getAbsolutePath()));
+ assertTrue(getAbsolutePath().equals(getRelativePath()));
+ assertTrue(getAbsolutePath().equals(getWithSlashes()));
+ assertTrue(getAbsolutePath().equals(getAppended()));
+ assertFalse(getAbsolutePath().equals(getOne()));
+
+ assertTrue(getRelativePath().equals(getAbsolutePath()));
+ assertTrue(getRelativePath().equals(getRelativePath()));
+ assertTrue(getRelativePath().equals(getWithSlashes()));
+ assertTrue(getRelativePath().equals(getAppended()));
+ assertFalse(getRelativePath().equals(getOne()));
+
+ assertTrue(getWithSlashes().equals(getAbsolutePath()));
+ assertTrue(getWithSlashes().equals(getRelativePath()));
+ assertTrue(getWithSlashes().equals(getWithSlashes()));
+ assertTrue(getWithSlashes().equals(getAppended()));
+ assertFalse(getWithSlashes().equals(getOne()));
+
+ assertTrue(getAppended().equals(getAbsolutePath()));
+ assertTrue(getAppended().equals(getRelativePath()));
+ assertTrue(getAppended().equals(getWithSlashes()));
+ assertTrue(getAppended().equals(getAppended()));
+ assertFalse(getAppended().equals(getOne()));
+
+ assertFalse(getOne().equals(getAbsolutePath()));
+ assertFalse(getOne().equals(getRelativePath()));
+ assertFalse(getOne().equals(getWithSlashes()));
+ assertFalse(getOne().equals(getAppended()));
+ assertTrue(getOne().equals(getOne()));
+ }
+
+ @Test
+ public void testGetPath() {
+ assertThat(getAbsolutePath().getRelative(), is("foo/bar/baz"));
+ assertThat(getRelativePath().getRelative(), is("foo/bar/baz"));
+ assertThat(getWithSlashes().getRelative(), is("foo/bar/baz"));
+ assertThat(getAppended().getRelative(), is("foo/bar/baz"));
+ assertThat(getOne().getRelative(), is("foo"));
+ }
+
+ @Test
+ public void testGetParentPath() {
+ assertThat(getAbsolutePath().getParentPath().getRelative(), is("foo/bar"));
+ assertThat(getRelativePath().getParentPath().getRelative(), is("foo/bar"));
+ assertThat(getWithSlashes().getParentPath().getRelative(), is("foo/bar"));
+ assertThat(getAppended().getParentPath().getRelative(), is("foo/bar"));
+ assertThat(getOne().getParentPath().getRelative(), is(""));
+ }
+
+ @Test
+ public void testGetAbsolutePath() {
+ assertThat(getAbsolutePath().getAbsolute(), is("/foo/bar/baz"));
+ assertThat(getAbsolutePath().getParentPath().getAbsolute(), is("/foo/bar"));
+ }
+
+ @Test
+ public void testEmptyPath() {
+ assertThat(Path.createRoot().getName(), is(""));
+ assertThat(Path.createRoot().getRelative(), is(""));
+ assertThat(Path.createRoot().getParentPath().getRelative(), is(""));
+ assertTrue(Path.createRoot().isRoot());
+ }
+
+ @Test
+ public void testDelimiters() {
+ assertThat(Path.fromString("foo/bar", ",").getName(), is("foo/bar"));
+ assertThat(Path.fromString("foo/bar", "/").getName(), is("bar"));
+ assertThat(Path.fromString("foo,bar", "/").getName(), is("foo,bar"));
+ assertThat(Path.fromString("foo,bar", ",").getName(), is("bar"));
+ assertThat(Path.createRoot(",").append("foo").append("bar").getRelative(), is("foo,bar"));
+ }
+
+ @Test
+ public void testAppendPath() {
+ Path p1 = getAbsolutePath();
+ Path p2 = getAbsolutePath();
+ Path p3 = p1.append(p2);
+ assertThat(p1.getAbsolute(), is("/foo/bar/baz"));
+ assertThat(p2.getAbsolute(), is("/foo/bar/baz"));
+ assertThat(p3.getAbsolute(), is("/foo/bar/baz/foo/bar/baz"));
+ }
+
+ private Path getRelativePath() {
+ return Path.fromString("foo/bar/baz");
+ }
+
+ private Path getAbsolutePath() {
+ return Path.fromString("/foo/bar/baz");
+ }
+
+ private Path getWithSlashes() {
+ return Path.fromString("/foo//bar///baz/");
+ }
+
+ private Path getAppended() {
+ return Path.createRoot().append("foo").append("bar").append("baz");
+ }
+
+ private Path getOne() {
+ return Path.fromString("foo");
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/protect/TestErrorMessage.java b/vespajlib/src/test/java/com/yahoo/protect/TestErrorMessage.java
new file mode 100644
index 00000000000..fa611d8fd71
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/protect/TestErrorMessage.java
@@ -0,0 +1,31 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.protect;
+
+/**
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class TestErrorMessage extends junit.framework.TestCase {
+
+ public void testErrorMessages() {
+ ErrorMessage m1=new ErrorMessage(17,"Message");
+ ErrorMessage m2=new ErrorMessage(17,"Message","Detail");
+ ErrorMessage m3=new ErrorMessage(17,"Message","Detail",new Exception("Throwable message"));
+ assertEquals(17,m1.getCode());
+ assertEquals("Message",m1.getMessage());
+ assertEquals("Detail",m2.getDetailedMessage());
+ assertEquals("Throwable message",m3.getCause().getMessage());
+ assertEquals("error : Message (Detail: Throwable message)",m3.toString());
+ }
+
+ public void testErrorMessageEquality() {
+ assertEquals(new ErrorMessage(17,"Message"),new ErrorMessage(17,"Message"));
+ assertFalse(new ErrorMessage(16,"Message").equals(new ErrorMessage(17,"Message")));
+ assertFalse(new ErrorMessage(17,"Message").equals(new ErrorMessage(17,"Other message")));
+ assertFalse(new ErrorMessage(17,"Message").equals(new ErrorMessage(17,"Message","Detail")));
+ assertFalse(new ErrorMessage(17,"Message","Detail").equals(new ErrorMessage(17,"Message")));
+ assertEquals(new ErrorMessage(17,"Message","Detail"),new ErrorMessage(17,"Message","Detail",new Exception()));
+ assertTrue(new ErrorMessage(17,"Message","Detail").equals(new ErrorMessage(17,"Message","Detail")));
+ assertFalse(new ErrorMessage(17,"Message","Detail").equals(new ErrorMessage(17,"Message","Other detail")));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/protect/ValidatorTestCase.java b/vespajlib/src/test/java/com/yahoo/protect/ValidatorTestCase.java
new file mode 100644
index 00000000000..6acbda729e5
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/protect/ValidatorTestCase.java
@@ -0,0 +1,88 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.protect;
+
+/**
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class ValidatorTestCase extends junit.framework.TestCase {
+
+ public void testEnsureNotNull() {
+ try {
+ Validator.ensureNotNull("Description",null);
+ fail("No exception");
+ }
+ catch (Exception e) {
+ // success
+ }
+ }
+
+ public void testEnsureNotInitialized() {
+ try {
+ Validator.ensureNotInitialized("Description","Field-owner","Initialized-field-value");
+ fail("No exception");
+ }
+ catch (Exception e) {
+ // success
+ }
+ }
+
+ public void testEnsureInRange() {
+ try {
+ Validator.ensureInRange("Description",2,4,5);
+ fail("No exception");
+ }
+ catch (Exception e) {
+ // success
+ }
+ }
+
+ public void testSmallerInts() {
+ try {
+ Validator.ensureSmaller("Small-description",3,"Large-description",2);
+ fail("No exception");
+ }
+ catch (Exception e) {
+ // success
+ }
+ }
+
+ public void testSmallerComparables() {
+ try {
+ Validator.ensureSmaller("Small-description","b","Large-description","a");
+ fail("No exception");
+ }
+ catch (Exception e) {
+ // success
+ }
+ }
+
+ public void testEnsure() {
+ try {
+ Validator.ensure("Description",false);
+ fail("No exception");
+ }
+ catch (Exception e) {
+ // success
+ }
+ }
+
+ public void testEnsureInstanceOf() {
+ try {
+ Validator.ensureInstanceOf("Description","item",Integer.class);
+ fail("No exception");
+ }
+ catch (Exception e) {
+ // success
+ }
+ }
+
+ public void testVarArgsEnsure() {
+ Validator.ensure(true, "ignored");
+ try {
+ Validator.ensure(false, "a", "b", "c");
+ } catch (Exception e) {
+ assertEquals("abc", e.getMessage());
+ }
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/reflection/CastingTest.java b/vespajlib/src/test/java/com/yahoo/reflection/CastingTest.java
new file mode 100644
index 00000000000..32b506b206f
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/reflection/CastingTest.java
@@ -0,0 +1,36 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.reflection;
+
+import org.junit.Test;
+
+import java.util.Optional;
+
+import static com.yahoo.text.StringUtilities.quote;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+import static com.yahoo.reflection.Casting.cast;
+
+public class CastingTest {
+ @Test
+ public void valid_cast_gives_present_optional() {
+ Object objectToCast = 12;
+ Optional<Integer> value = cast(Integer.class, objectToCast);
+ assertTrue("Value is not present", value.isPresent());
+ assertThat(value.get(), is(objectToCast));
+ }
+
+ @Test
+ public void invalid_cast_gives_empty_optional() {
+ Object objectToCast = "string";
+ Optional<Integer> value = cast(Integer.class, objectToCast);
+ assertTrue("Value is present", !value.isPresent());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void cast_sample_usage() {
+ Object objectToCast = "illegal";
+ int result = cast(Integer.class, objectToCast).
+ filter(i -> !i.equals(0)).
+ orElseThrow(() -> new IllegalArgumentException("Expected non-zero integer, got " + quote(objectToCast)));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/rmi/.gitignore b/vespajlib/src/test/java/com/yahoo/rmi/.gitignore
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/rmi/.gitignore
diff --git a/vespajlib/src/test/java/com/yahoo/slime/BinaryFormatTestCase.java b/vespajlib/src/test/java/com/yahoo/slime/BinaryFormatTestCase.java
new file mode 100644
index 00000000000..7b42d4e6bda
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/slime/BinaryFormatTestCase.java
@@ -0,0 +1,567 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.slime;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.*;
+
+import static com.yahoo.slime.BinaryFormat.*;
+
+public class BinaryFormatTestCase {
+
+ static final int TYPE_LIMIT = 8;
+ static final int META_LIMIT = 32;
+ static final int MAX_CMPR_SIZE = 10;
+ static final int MAX_NUM_SIZE = 8;
+
+ static final byte enc_t_and_sz(Type t, int size) {
+ assert size <= 30;
+ return encode_type_and_meta(t.ID, size + 1);
+ }
+ static final byte enc_t_and_m(Type t, int meta) {
+ assert meta <= 31;
+ return encode_type_and_meta(t.ID, meta);
+ }
+
+ void verify_cmpr_long(long value, byte[] expect) {
+ BinaryEncoder bof = new BinaryEncoder();
+ bof.encode_cmpr_long(value);
+ byte[] actual = bof.out.toArray();
+ assertThat(actual, is(expect));
+
+ BinaryDecoder bif = new BinaryDecoder();
+ bif.in = new BufferedInput(expect);
+ long got = bif.read_cmpr_long();
+ assertThat(got, is(value));
+ }
+
+ // was verifyBasic
+ void verifyEncoding(Slime slime, byte[] expect) {
+ assertThat(BinaryFormat.encode(slime), is(expect));
+ verifyMultiEncode(expect);
+ }
+
+ void verifyMultiEncode(byte[] expect) {
+ byte[][] buffers = new byte[6][];
+ buffers[0] = expect;
+
+ for (int i = 0; i < 5; ++i) {
+ Slime slime = BinaryFormat.decode(buffers[i]);
+ buffers[i+1] = BinaryFormat.encode(slime);
+ assertThat(buffers[i+1], is(expect));
+ }
+ }
+
+ @Test
+ public void testZigZagConversion() {
+ System.out.println("test zigzag conversion");
+ assertThat(encode_zigzag(0), is((long)0));
+ assertThat(decode_zigzag(encode_zigzag(0)), is(0L));
+
+ assertThat(encode_zigzag(-1), is(1L));
+ assertThat(decode_zigzag(encode_zigzag(-1)), is(-1L));
+
+ assertThat(encode_zigzag(1), is(2L));
+ assertThat(decode_zigzag(encode_zigzag(1)), is(1L));
+
+ assertThat(encode_zigzag(-2), is(3L));
+ assertThat(decode_zigzag(encode_zigzag(-2)), is(-2L));
+
+ assertThat(encode_zigzag(2), is(4L));
+ assertThat(decode_zigzag(encode_zigzag(2)), is(2L));
+
+ assertThat(encode_zigzag(-1000), is(1999L));
+ assertThat(decode_zigzag(encode_zigzag(-1000)), is(-1000L));
+
+ assertThat(encode_zigzag(1000), is(2000L));
+ assertThat(decode_zigzag(encode_zigzag(1000)), is(1000L));
+
+ assertThat(encode_zigzag(-0x8000000000000000L), is(-1L));
+ assertThat(decode_zigzag(encode_zigzag(-0x8000000000000000L)), is(-0x8000000000000000L));
+
+ assertThat(encode_zigzag(0x7fffffffffffffffL), is(-2L));
+ assertThat(decode_zigzag(encode_zigzag(0x7fffffffffffffffL)), is(0x7fffffffffffffffL));
+ }
+
+ @Test
+ public void testDoubleConversion() {
+ System.out.println("test double conversion");
+ assertThat(encode_double(0.0), is(0L));
+ assertThat(decode_double(encode_double(0.0)), is(0.0));
+
+ assertThat(encode_double(1.0), is(0x3ff0000000000000L));
+ assertThat(decode_double(encode_double(1.0)), is(1.0));
+
+ assertThat(encode_double(-1.0), is(0xbff0000000000000L));
+ assertThat(decode_double(encode_double(-1.0)), is(-1.0));
+
+ assertThat(encode_double(2.0), is(0x4000000000000000L));
+ assertThat(decode_double(encode_double(2.0)), is(2.0));
+
+ assertThat(encode_double(-2.0), is(0xc000000000000000L));
+ assertThat(decode_double(encode_double(-2.0)), is(-2.0));
+
+ assertThat(encode_double(-0.0), is(0x8000000000000000L));
+ assertThat(decode_double(encode_double(-0.0)), is(-0.0));
+
+ assertThat(encode_double(3.5), is(0x400c000000000000L));
+ assertThat(decode_double(encode_double(3.5)), is(3.5));
+
+ assertThat(encode_double(65535.875), is(0x40EFFFFC00000000L));
+ assertThat(decode_double(encode_double(65535.875)), is(65535.875));
+ }
+
+ @Test
+ public void testTypeAndMetaMangling() {
+ System.out.println("test type and meta mangling");
+ for (byte type = 0; type < TYPE_LIMIT; ++type) {
+ for (int meta = 0; meta < META_LIMIT; ++meta) {
+ byte mangled = encode_type_and_meta(type, meta);
+ assertThat(decode_type(mangled).ID, is(type));
+ assertThat(decode_meta(mangled), is(meta));
+ }
+ }
+ }
+
+ // was testCmprUlong
+ @Test
+ public void testCmprLong() {
+ System.out.println("test compressed long");
+ {
+ long value = 0;
+ byte[] wanted = { 0 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 127;
+ byte[] wanted = { 127 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 128;
+ byte[] wanted = { -128, 1 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 16383;
+ byte[] wanted = { -1, 127 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 16384;
+ byte[] wanted = { -128, -128, 1 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 2097151;
+ byte[] wanted = { -1, -1, 127 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 2097152;
+ byte[] wanted = { -128, -128, -128, 1 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 268435455;
+ byte[] wanted = { -1, -1, -1, 127 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 268435456;
+ byte[] wanted = { -128, -128, -128, -128, 1 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 34359738367L;
+ byte[] wanted = { -1, -1, -1, -1, 127 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 34359738368L;
+ byte[] wanted = { -128, -128, -128, -128, -128, 1 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 4398046511103L;
+ byte[] wanted = { -1, -1, -1, -1, -1, 127 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 4398046511104L;
+ byte[] wanted = { -128, -128, -128, -128, -128, -128, 1 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 562949953421311L;
+ byte[] wanted = { -1, -1, -1, -1, -1, -1, 127 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 562949953421312L;
+ byte[] wanted = { -128, -128, -128, -128, -128, -128, -128, 1 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 72057594037927935L;
+ byte[] wanted = { -1, -1, -1, -1, -1, -1, -1, 127 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 72057594037927936L;
+ byte[] wanted = { -128, -128, -128, -128, -128, -128, -128, -128, 1 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = 9223372036854775807L;
+ byte[] wanted = { -1, -1, -1, -1, -1, -1, -1, -1, 127 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = -9223372036854775808L;
+ byte[] wanted = { -128, -128, -128, -128, -128, -128, -128, -128, -128, 1 };
+ verify_cmpr_long(value, wanted);
+ }{
+ long value = -1;
+ byte[] wanted = { -1, -1, -1, -1, -1, -1, -1, -1, -1, 1 };
+ verify_cmpr_long(value, wanted);
+ }
+ }
+
+ // testWriteByte -> buffered IO test
+ // testWriteBytes -> buffered IO test
+ // testReadByte -> buffered IO test
+ // testReadBytes -> buffered IO test
+
+ @Test
+ public void testTypeAndSize() {
+ System.out.println("test type and size conversion");
+
+ for (byte type = 0; type < TYPE_LIMIT; ++type) {
+ for (long size = 0; size < 500; ++size) {
+ BufferedOutput expect = new BufferedOutput();
+ BufferedOutput actual = new BufferedOutput();
+
+ if ((size + 1) < META_LIMIT) {
+ expect.put(encode_type_and_meta((int)type, (int)(size +1)));
+ } else {
+ expect.put(type);
+ BinaryEncoder encoder = new BinaryEncoder();
+ encoder.out = expect;
+ encoder.encode_cmpr_long(size);
+ }
+ {
+ BinaryEncoder encoder = new BinaryEncoder();
+ encoder.out = actual;
+ encoder.write_type_and_size(type, size);
+ }
+ assertThat(actual.toArray(), is(expect.toArray()));
+
+ byte[] got = expect.toArray();
+ BinaryDecoder bif = new BinaryDecoder();
+ bif.in = new BufferedInput(got);
+ byte b = bif.in.getByte();
+ Type decodedType = decode_type(b);
+ long decodedSize = bif.read_size(decode_meta(b));
+ assertThat(decodedType.ID, is(type));
+ assertThat(decodedSize, is(size));
+ assertThat(bif.in.getConsumedSize(), is(got.length));
+ assertThat(bif.in.failed(), is(false));
+ }
+ }
+
+ }
+
+ static long build_bits(int type, int n, int pre, boolean hi, BufferedOutput expect) {
+ long value = 0;
+ expect.put(encode_type_and_meta(type, n));
+ for (int i = 0; i < n; ++i) {
+ byte b = (i < pre) ? 0x00 : (byte)(0x11 * (i - pre + 1));
+ expect.put(b);
+ int shift = hi ? ((7 - i) * 8) : (i * 8);
+ long bits = b & 0xff;
+ value |= bits << shift;
+ }
+ return value;
+ }
+
+ @Test
+ public void testTypeAndBytes() {
+ System.out.println("test encoding and decoding of type and bytes");
+ for (byte type = 0; type < TYPE_LIMIT; ++type) {
+ for (int n = 0; n < MAX_NUM_SIZE; ++n) {
+ for (int pre = 0; (pre == 0) || (pre < n); ++pre) {
+ for (int hi = 0; hi < 2; ++hi) {
+ BufferedOutput expbuf = new BufferedOutput();
+ long bits = build_bits(type, n, pre, (hi != 0), expbuf);
+ byte[] expect = expbuf.toArray();
+
+ // test output:
+ BinaryEncoder bof = new BinaryEncoder();
+ bof.out = new BufferedOutput();
+ if (hi != 0) {
+ bof.write_type_and_bytes_be(type, bits);
+ } else {
+ bof.write_type_and_bytes_le(type, bits);
+ }
+ byte[] actual = bof.out.toArray();
+ assertThat(actual, is(expect));
+
+ // test input:
+ BinaryDecoder bif = new BinaryDecoder();
+ bif.in = new BufferedInput(expect);
+ int size = decode_meta(bif.in.getByte());
+ long decodedBits = (hi != 0) ? bif.read_bytes_be(size) : bif.read_bytes_le(size);
+ assertThat(decodedBits, is(bits));
+ assertThat(bif.in.getConsumedSize(), is(expect.length));
+ assertThat(bif.in.failed(), is(false));
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testEmpty() {
+ System.out.println("test encoding empty slime");
+
+ Slime slime = new Slime();
+ BufferedOutput expect = new BufferedOutput();
+ expect.put((byte)0); // num symbols
+ expect.put((byte)0); // nix
+ byte[] actual = BinaryFormat.encode(slime);
+
+ assertThat(actual, is(expect.toArray()));
+ verifyMultiEncode(expect.toArray());
+ }
+
+ @Test
+ public void testBasic() {
+ System.out.println("test encoding slime holding a single basic value");
+ {
+ Slime slime = new Slime();
+ slime.setBool(false);
+ byte[] expect = { 0, Type.BOOL.ID };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ slime.setBool(true);
+ byte[] expect = { 0, enc_t_and_m(Type.BOOL, 1) };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ slime.setLong(0);
+ byte[] expect = { 0, Type.LONG.ID };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ slime.setLong(13);
+ byte[] expect = { 0, enc_t_and_m(Type.LONG, 1), 13*2 };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ slime.setLong(-123456789);
+ final long ev = (2 * 123456789) - 1;
+ byte b1 = (byte)(ev);
+ byte b2 = (byte)(ev >> 8);
+ byte b3 = (byte)(ev >> 16);
+ byte b4 = (byte)(ev >> 24);
+
+ byte[] expect = { 0, enc_t_and_m(Type.LONG, 4), b1, b2, b3, b4 };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ slime.setDouble(0.0);
+ byte[] expect = { 0, Type.DOUBLE.ID };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ slime.setDouble(1.0);
+ byte[] expect = { 0, enc_t_and_m(Type.DOUBLE, 2), (byte)0x3f, (byte)0xf0 };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ slime.setString("");
+ byte[] expect = { 0, enc_t_and_sz(Type.STRING, 0) };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ slime.setString("fo");
+ byte[] expect = { 0, enc_t_and_sz(Type.STRING, 2), 'f', 'o' };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ slime.setString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ byte[] expect = { 0, Type.STRING.ID, 26*2,
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z'
+ };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ slime.setData(new byte[0]);
+ byte[] expect = { 0, enc_t_and_sz(Type.DATA, 0) };
+ verifyEncoding(slime, expect);
+ }
+ {
+ Slime slime = new Slime();
+ byte[] data = { 42, -123 };
+ slime.setData(data);
+ byte[] expect = { 0, enc_t_and_sz(Type.DATA, 2), 42, -123 };
+ verifyEncoding(slime, expect);
+ }
+ }
+
+ @Test
+ public void testBufferedInputWithOffset() {
+ Slime slime = new Slime();
+ byte[] data = { 42, -123 };
+ slime.setData(data);
+ byte[] expect = { 0, enc_t_and_sz(Type.DATA, 2), 42, -123 };
+ verifyEncoding(slime, expect);
+ byte [] overlappingBuffer = new byte [expect.length + 2];
+ System.arraycopy(expect, 0, overlappingBuffer, 1, expect.length);
+ overlappingBuffer[overlappingBuffer.length - 1] = 0;
+ Slime copy = BinaryFormat.decode(overlappingBuffer, 1, expect.length);
+ assertThat(BinaryFormat.encode(slime), is(BinaryFormat.encode(copy)));
+ }
+
+ @Test
+ public void testArray() {
+ System.out.println("test encoding slime holding an array of various basic values");
+ Slime slime = new Slime();
+ Cursor c = slime.setArray();
+ byte[] data = { 'd', 'a', 't', 'a' };
+ c.addNix();
+ c.addBool(true);
+ c.addLong(42);
+ c.addDouble(3.5);
+ c.addString("string");
+ c.addData(data);
+ byte[] expect = {
+ 0, // num symbols
+ enc_t_and_sz(Type.ARRAY, 6), // value type and size
+ 0, // nix
+ enc_t_and_m(Type.BOOL, 1),
+ enc_t_and_m(Type.LONG, 1), 42*2,
+ enc_t_and_m(Type.DOUBLE, 2), 0x40, 0x0c, // 3.5
+ enc_t_and_sz(Type.STRING, 6), 's', 't', 'r', 'i', 'n', 'g',
+ enc_t_and_sz(Type.DATA, 4), 'd', 'a', 't', 'a'
+ };
+ verifyEncoding(slime, expect);
+ }
+
+ @Test
+ public void testObject() {
+ System.out.println("test encoding slime holding an object of various basic values");
+ Slime slime = new Slime();
+ Cursor c = slime.setObject();
+ byte[] data = { 'd', 'a', 't', 'a' };
+ c.setNix("a");
+ c.setBool("b", true);
+ c.setLong("c", 42);
+ c.setDouble("d", 3.5);
+ c.setString("e", "string");
+ c.setData("f", data);
+ byte[] expect = {
+ 6, // num symbols
+ 1, 'a', 1, 'b', 1, 'c', 1, 'd', 1, 'e', 1, 'f', // symbol table
+ enc_t_and_sz(Type.OBJECT, 6), // value type and size
+ 0, 0, // nix
+ 1, enc_t_and_m(Type.BOOL, 1),
+ 2, enc_t_and_m(Type.LONG, 1), 42*2,
+ 3, enc_t_and_m(Type.DOUBLE, 2), 0x40, 0x0c, // 3.5
+ 4, enc_t_and_sz(Type.STRING, 6), 's', 't', 'r', 'i', 'n', 'g',
+ 5, enc_t_and_sz(Type.DATA, 4), 'd', 'a', 't', 'a'
+ };
+ verifyEncoding(slime, expect);
+ }
+
+ @Test
+ public void testNesting() {
+ System.out.println("test encoding slime holding a more complex structure");
+ Slime slime = new Slime();
+ Cursor c1 = slime.setObject();
+ c1.setLong("bar", 10);
+ Cursor c2 = c1.setArray("foo");
+ c2.addLong(20);
+ Cursor c3 = c2.addObject();
+ c3.setLong("answer", 42);
+ byte[] expect = {
+ 3, // num symbols
+ 3, 'b', 'a', 'r',
+ 3, 'f', 'o', 'o',
+ 6, 'a', 'n', 's', 'w', 'e', 'r',
+ enc_t_and_sz(Type.OBJECT, 2), // value type and size
+ 0, enc_t_and_m(Type.LONG, 1), 10*2,
+ 1, enc_t_and_sz(Type.ARRAY, 2), // nested value type and size
+ enc_t_and_m(Type.LONG, 1), 20*2,
+ enc_t_and_sz(Type.OBJECT, 1), // doubly nested value
+ 2, enc_t_and_m(Type.LONG, 1), 42*2
+ };
+ verifyEncoding(slime, expect);
+ }
+
+ @Test
+ public void testSymbolReuse() {
+ System.out.println("test encoding slime reusing symbols");
+ Slime slime = new Slime();
+ Cursor c1 = slime.setArray();
+ {
+ Cursor c2 = c1.addObject();
+ c2.setLong("foo", 10);
+ c2.setLong("bar", 20);
+ }
+ {
+ Cursor c2 = c1.addObject();
+ c2.setLong("foo", 100);
+ c2.setLong("bar", 200);
+ }
+ byte[] expect = {
+ 2, // num symbols
+ 3, 'f', 'o', 'o',
+ 3, 'b', 'a', 'r',
+ enc_t_and_sz(Type.ARRAY, 2), // value type and size
+ enc_t_and_sz(Type.OBJECT, 2), // nested value
+ 0, enc_t_and_m(Type.LONG, 1), 10*2, // foo
+ 1, enc_t_and_m(Type.LONG, 1), 20*2, // bar
+ enc_t_and_sz(Type.OBJECT, 2), // nested value
+ 0, enc_t_and_m(Type.LONG, 1), (byte)(100*2), // foo
+ 1, enc_t_and_m(Type.LONG, 2), (byte)144, 1 // bar: 2*200 = 400 = 256 + 144
+ };
+ verifyEncoding(slime, expect);
+ }
+
+ @Test
+ public void testOptionalDecodeOrder() {
+ System.out.println("test decoding slime with different symbol order");
+ byte[] data = {
+ 5, // num symbols
+ 1, 'd', 1, 'e', 1, 'f', 1, 'b', 1, 'c', // symbol table
+ enc_t_and_sz(Type.OBJECT, 5), // value type and size
+ 3, enc_t_and_m(Type.BOOL, 1), // b
+ 1, enc_t_and_sz(Type.STRING, 6), // e
+ 's', 't', 'r', 'i', 'n', 'g',
+ 0, enc_t_and_m(Type.DOUBLE, 2), 0x40, 0x0c, // d
+ 4, enc_t_and_m(Type.LONG, 1), 5*2, // c
+ 2, enc_t_and_sz(Type.DATA, 4), // f
+ 'd', 'a', 't', 'a'
+ };
+ Slime slime = new Slime();
+ BinaryDecoder decoder = new BinaryDecoder();
+ slime = decoder.decode(data);
+ int consumed = decoder.in.getConsumedSize();
+ assertThat(consumed, is(data.length));
+ Cursor c = slime.get();
+ assertThat(c.valid(), is(true));
+ assertThat(c.type(), is(Type.OBJECT));
+ assertThat(c.children(), is(5));
+ assertThat(c.field("b").asBool(), is(true));
+ assertThat(c.field("c").asLong(), is(5L));
+ assertThat(c.field("d").asDouble(), is(3.5));
+ assertThat(c.field("e").asString(), is("string"));
+ byte[] expd = { 'd', 'a', 't', 'a' };
+ assertThat(c.field("f").asData(), is(expd));
+ assertThat(c.entry(5).valid(), is(false)); // not ARRAY
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/slime/JsonBenchmark.java b/vespajlib/src/test/java/com/yahoo/slime/JsonBenchmark.java
new file mode 100644
index 00000000000..8ee1a91c970
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/slime/JsonBenchmark.java
@@ -0,0 +1,114 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.slime;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.Integer;
+
+/**
+ * Created by balder on 2/26/14.
+ */
+public class JsonBenchmark {
+ private static byte [] createJson(int numElements) {
+ Slime slime = new Slime();
+ Cursor a = slime.setArray();
+ for (int i=0; i < numElements; i++) {
+ Cursor e = a.addObject();
+ e.setString("key", "i");
+ e.setLong("weight", i);
+ }
+ ByteArrayOutputStream bs = new ByteArrayOutputStream();
+ JsonFormat json = new JsonFormat(false);
+ try {
+ json.encode(bs, slime);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return bs.toByteArray();
+ }
+ private static long benchmarkJacksonStreaming(byte [] json, int numIterations) {
+ long count = 0;
+ JsonFactory jsonFactory = new JsonFactory();
+
+ try {
+ for (int i=0; i < numIterations; i++) {
+ JsonParser jsonParser = jsonFactory.createParser(json);
+ JsonToken array = jsonParser.nextToken();
+ for (JsonToken token = jsonParser.nextToken(); ! JsonToken.END_ARRAY.equals(token); token = jsonParser.nextToken()) {
+ if (JsonToken.FIELD_NAME.equals(token) && "weight".equals(jsonParser.getCurrentName())) {
+ token = jsonParser.nextToken();
+ count += jsonParser.getLongValue();
+ }
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return count;
+ }
+ private static long benchmarkJacksonTree(byte [] json, int numIterations) {
+ long count = 0;
+ ObjectMapper mapper = new ObjectMapper();
+ // use the ObjectMapper to read the json string and create a tree
+ try {
+ for (int i=0; i < numIterations; i++) {
+ JsonNode node = mapper.readTree(json);
+ for(JsonNode item : node) {
+ count += item.get("weight").asLong();
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return count;
+ }
+ private static long benchmarkSlime(byte [] json, int numIterations) {
+ long count = 0;
+ for (int i=0; i < numIterations; i++) {
+ JsonDecoder decoder = new JsonDecoder();
+ Slime slime = decoder.decode(new Slime(), json);
+
+ Cursor array = slime.get();
+ int weightSymbol = slime.lookup("weight");
+ for (int j=0, m=slime.get().children(); j < m; j++) {
+ count += array.entry(j).field(weightSymbol).asLong();
+ }
+ }
+ return count;
+ }
+ private static void warmup(byte [] json) {
+ System.out.println(System.currentTimeMillis() + " Warming up");
+ benchmarkSlime(json, 5000);
+ System.out.println(System.currentTimeMillis() + " Done Warming up");
+ }
+
+ /**
+ * jacksons 1000 40000 = 5.6 seconds
+ * jacksont 1000 40000 = 11.0 seconds
+ * slime 1000 40000 = 17.5 seconds
+ * @param argv type, num elements in weigted set, num iterations
+ */
+ static public void main(String argv[]) {
+ String type = argv[0];
+ byte [] json = createJson(Integer.valueOf(argv[1]));
+ warmup(json);
+ int count = Integer.valueOf(argv[2]);
+ System.out.println(System.currentTimeMillis() + " Start");
+ long start = System.currentTimeMillis();
+ long numValues;
+ if ("jacksons".equals(type)) {
+ numValues = benchmarkJacksonStreaming(json, count);
+ } else if ("jacksont".equals(type)) {
+ numValues = benchmarkJacksonTree(json, count);
+ } else{
+ numValues = benchmarkSlime(json, count);
+ }
+ System.out.println(System.currentTimeMillis() + " End with " + numValues + " values in " + (System.currentTimeMillis() - start) + " milliseconds.");
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/slime/JsonFormatTestCase.java b/vespajlib/src/test/java/com/yahoo/slime/JsonFormatTestCase.java
new file mode 100644
index 00000000000..e48a717f150
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/slime/JsonFormatTestCase.java
@@ -0,0 +1,273 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.slime;
+
+import com.yahoo.text.Utf8;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class JsonFormatTestCase {
+
+ @Test
+ public void testBasic() {
+ System.out.println("test encoding slime holding a single basic value");
+ {
+ Slime slime = new Slime();
+ slime.setBool(false);
+ verifyEncoding(slime, "false");
+ }
+
+ {
+ Slime slime = new Slime();
+ slime.setBool(true);
+ verifyEncoding(slime, "true");
+ }
+
+ {
+ Slime slime = new Slime();
+ slime.setLong(0);
+ verifyEncoding(slime, "0");
+ }
+ {
+ Slime slime = new Slime();
+ slime.setLong(13);
+ verifyEncoding(slime, "13");
+ }
+ {
+ Slime slime = new Slime();
+ slime.setLong(-123456789);
+ verifyEncoding(slime, "-123456789");
+ }
+ {
+ Slime slime = new Slime();
+ slime.setDouble(0.0);
+ verifyEncoding(slime, "0.0");
+ }
+ {
+ Slime slime = new Slime();
+ slime.setDouble(1.5);
+ verifyEncoding(slime, "1.5");
+ }
+ {
+ Slime slime = new Slime();
+ slime.setString("");
+ verifyEncoding(slime, "\"\"");
+ }
+ {
+ Slime slime = new Slime();
+ slime.setString("fo");
+ verifyEncoding(slime, "\"fo\"");
+ }
+ {
+ Slime slime = new Slime();
+ slime.setString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ verifyEncoding(slime, "\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"");
+ }
+ {
+ Slime slime = new Slime();
+ slime.setData(new byte[0]);
+ verifyEncoding(slime, "\"0x\"");
+ }
+
+ {
+ Slime slime = new Slime();
+ byte[] data = { 42, -123 };
+ slime.setData(data);
+ String expect = "\"0x2A85\"";
+ verifyEncoding(slime, expect);
+ }
+
+ {
+ Slime slime = new Slime();
+ String expected = "\"my\\nencoded\\rsting\\\\is\\bthe\\fnicest\\t\\\"string\\\"\\u0005\"";
+ slime.setString("my\nencoded\rsting\\is\bthe\fnicest\t\"string\"" + Character.toString((char) 5));
+ verifyEncoding(slime, expected);
+ }
+
+ {
+ Slime slime = new Slime();
+ slime.setDouble(Double.NaN);
+ verifyEncoding(slime, "null");
+ slime.setDouble(Double.NEGATIVE_INFINITY);
+ verifyEncoding(slime, "null");
+ slime.setDouble(Double.POSITIVE_INFINITY);
+ verifyEncoding(slime, "null");
+ }
+ }
+
+ @Test
+ public void testArray() {
+ System.out.println("test encoding slime holding an array of various basic values");
+ Slime slime = new Slime();
+ Cursor c = slime.setArray();
+ byte[] data = { 'd', 'a', 't', 'a' };
+ c.addNix();
+ c.addBool(true);
+ c.addLong(42);
+ c.addDouble(3.5);
+ c.addString("string");
+ c.addData(data);
+
+ verifyEncoding(slime, "[null,true,42,3.5,\"string\",\"0x64617461\"]");
+ }
+
+ @Test
+ public void testObject() {
+ System.out.println("test encoding slime holding an object of various basic values");
+ Slime slime = new Slime();
+ Cursor c = slime.setObject();
+ byte[] data = { 'd', 'a', 't', 'a' };
+ c.setNix("a");
+ c.setBool("b", true);
+ c.setLong("c", 42);
+ c.setDouble("d", 3.5);
+ c.setString("e", "string");
+ c.setData("f", data);
+ verifyEncoding(slime, "{\"a\":null,\"b\":true,\"c\":42,\"d\":3.5,\"e\":\"string\",\"f\":\"0x64617461\"}");
+ String expected = "{\n"
+ + " \"a\": null,\n"
+ + " \"b\": true,\n"
+ + " \"c\": 42,\n"
+ + " \"d\": 3.5,\n"
+ + " \"e\": \"string\",\n"
+ + " \"f\": \"0x64617461\"\n"
+ + "}\n";
+ verifyEncoding(slime, expected, false);
+ }
+
+ @Test
+ public void testNesting() {
+ System.out.println("test encoding slime holding a more complex structure");
+ Slime slime = new Slime();
+ Cursor c1 = slime.setObject();
+ c1.setLong("bar", 10);
+ Cursor c2 = c1.setArray("foo");
+ c2.addLong(20);
+ Cursor c3 = c2.addObject();
+ c3.setLong("answer", 42);
+ verifyEncoding(slime, "{\"bar\":10,\"foo\":[20,{\"answer\":42}]}");
+ }
+
+ @Test
+ public void testDecodeEncode() {
+ System.out.println("test decoding and encoding a json string yields the same string");
+ verifyEncodeDecode("{\"bar\":10,\"foo\":[20,{\"answer\":42}]}", true);
+ String expected = "{\n"
+ + " \"a\": null,\n"
+ + " \"b\": true,\n"
+ + " \"c\": 42,\n"
+ + " \"d\": 3.5,\n"
+ + " \"e\": \"string\",\n"
+ + " \"f\": \"0x64617461\"\n"
+ + "}\n";
+ verifyEncodeDecode(expected, false);
+ }
+
+ @Test
+ public void testDecodeEncodeUtf8() {
+ final String json = "{\n" +
+ " \"rules\": \"# Use unicode equivalents in java source:\\n" +
+ " #\\n" +
+ " # ä½³:\u4f73\"\n" +
+ "}\n";
+ verifyEncodeDecode(json, false);
+ }
+
+ @Test
+ public void testDecodeUtf8() {
+ final String str = "\u4f73:\u4f73";
+ final String json = " {\n" +
+ " \"rules\": \"" + str + "\"\n" +
+ " }\n";
+
+ Slime slime = new Slime();
+ slime = new JsonDecoder().decode(slime, Utf8.toBytesStd(json));
+ Cursor a = slime.get().field("rules");
+ assertThat(a.asString(), is(str));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testThatDecodeIsNotImplemented() throws IOException {
+ new JsonFormat(true).decode(null, null);
+ }
+
+ private void verifyEncoding(Slime slime, String expected) {
+ verifyEncoding(slime, expected, true);
+ }
+
+ @Test
+ public void testEncodingUTF8() throws IOException {
+ Slime slime = new Slime();
+ slime.setString("M\u00E6L");
+ ByteArrayOutputStream a = new ByteArrayOutputStream();
+ new JsonFormat(true).encode(a, slime);
+ String val = new String(a.toByteArray(), "UTF-8");
+ assertEquals("\"M\u00E6L\"", val);
+
+ // TODO Some issues with newline
+ /*
+ slime = new Slime();
+ final String str = "# Use unicode equivalents in java source:\n" +
+ " #\n" +
+ " #\n" +
+ " # ä½³:\u4f73\n";
+ slime.setString(str);
+ a = new ByteArrayOutputStream();
+ new JsonFormat(true).encode(a, slime);
+ val = new String(a.toByteArray(), "UTF-8");
+ assertEquals(str, val);
+ */
+ }
+
+ private void verifyEncoding(Slime slime, String expected, boolean compact) {
+ try {
+ ByteArrayOutputStream a = new ByteArrayOutputStream();
+ new JsonFormat(compact).encode(a, slime);
+ assertEquals(expected, new String(a.toByteArray(), StandardCharsets.UTF_8));
+ } catch (Exception e) {
+ fail("Exception thrown when encoding slime: " + e.getMessage());
+ }
+ }
+
+ private void verifyEncodeDecode(String json, boolean compact) {
+ try {
+ Slime slime = new Slime();
+ new JsonDecoder().decode(slime, Utf8.toBytesStd(json));
+ ByteArrayOutputStream a = new ByteArrayOutputStream();
+ new JsonFormat(compact).encode(a, slime);
+ assertEquals(json, Utf8.toString(a.toByteArray()));
+ } catch (Exception e) {
+ fail("Exception thrown when encoding slime: " + e.getMessage());
+ }
+ }
+
+ private String formatDecimal(double value) {
+ try {
+ Slime slime = new Slime();
+ slime.setDouble(value);
+ ByteArrayOutputStream a = new ByteArrayOutputStream();
+ new JsonFormat(true).encode(a, slime);
+ return new String(a.toByteArray(), StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ @Test
+ public void testDecimalFormat() {
+ assertEquals("0.0", formatDecimal(0.0));
+ assertEquals("1.0", formatDecimal(1.0));
+ assertEquals("2.0", formatDecimal(2.0));
+ assertEquals("1.2", formatDecimal(1.2));
+ assertEquals("3.333333", formatDecimal(3.333333));
+ assertEquals("1.0E20", formatDecimal(1e20));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/slime/SlimeTestCase.java b/vespajlib/src/test/java/com/yahoo/slime/SlimeTestCase.java
new file mode 100644
index 00000000000..371668d3821
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/slime/SlimeTestCase.java
@@ -0,0 +1,330 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.slime;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.*;
+
+public class SlimeTestCase {
+
+ @Test
+ public void testTypeIds() {
+ System.out.println("testing type identifiers...");
+
+ assertThat(Type.NIX.ID, is((byte)0));
+ assertThat(Type.BOOL.ID, is((byte)1));
+ assertThat(Type.LONG.ID, is((byte)2));
+ assertThat(Type.DOUBLE.ID, is((byte)3));
+ assertThat(Type.STRING.ID, is((byte)4));
+ assertThat(Type.DATA.ID, is((byte)5));
+ assertThat(Type.ARRAY.ID, is((byte)6));
+ assertThat(Type.OBJECT.ID, is((byte)7));
+
+ assertThat(Type.values().length, is(8));
+
+ assertThat(Type.values()[0], sameInstance(Type.NIX));
+ assertThat(Type.values()[1], sameInstance(Type.BOOL));
+ assertThat(Type.values()[2], sameInstance(Type.LONG));
+ assertThat(Type.values()[3], sameInstance(Type.DOUBLE));
+ assertThat(Type.values()[4], sameInstance(Type.STRING));
+ assertThat(Type.values()[5], sameInstance(Type.DATA));
+ assertThat(Type.values()[6], sameInstance(Type.ARRAY));
+ assertThat(Type.values()[7], sameInstance(Type.OBJECT));
+ }
+
+ @Test
+ public void testEmpty() {
+ System.out.println("testing empty slime...");
+ Slime slime = new Slime();
+ Cursor cur;
+ for (int i = 0; i < 2; i++) {
+ if (i == 0) {
+ cur = slime.get();
+ assertThat(cur.valid(), is(true));
+ } else {
+ cur = NixValue.invalid();
+ assertThat(cur.valid(), is(false));
+ }
+ assertThat(cur.type(), is(Type.NIX));
+ assertThat(cur.children(), is(0));
+ assertThat(cur.asBool(), is(false));
+ assertThat(cur.asLong(), is((long)0));
+ assertThat(cur.asDouble(), is(0.0));
+ assertThat(cur.asString(), is(""));
+ assertThat(cur.asData(), is(new byte[0]));
+ assertThat(cur.entry(0).valid(), is(false));
+ assertThat(cur.field(0).valid(), is(false));
+ assertThat(cur.field("foo").valid(), is(false));
+ }
+ Inspector insp;
+ for (int i = 0; i < 2; i++) {
+ if (i == 0) {
+ insp = slime.get();
+ assertThat(insp.valid(), is(true));
+ } else {
+ insp = NixValue.invalid();
+ assertThat(insp.valid(), is(false));
+ }
+ assertThat(insp.type(), is(Type.NIX));
+ assertThat(insp.children(), is(0));
+ assertThat(insp.asBool(), is(false));
+ assertThat(insp.asLong(), is((long)0));
+ assertThat(insp.asDouble(), is(0.0));
+ assertThat(insp.asString(), is(""));
+ assertThat(insp.asData(), is(new byte[0]));
+ assertThat(insp.entry(0).valid(), is(false));
+ assertThat(insp.field(0).valid(), is(false));
+ assertThat(insp.field("foo").valid(), is(false));
+ }
+ }
+
+ @Test
+ public void testBasic() {
+ System.out.println("testing basic values...");
+ Slime slime = new Slime();
+
+ System.out.println("testing boolean value");
+ slime.setBool(true);
+ Inspector insp = slime.get();
+ assertThat(insp.valid(), is(true));
+ assertThat(insp.type(), sameInstance(Type.BOOL));
+ assertThat(insp.asBool(), is(true));
+ Cursor cur = slime.get();
+ assertThat(cur.valid(), is(true));
+ assertThat(cur.type(), sameInstance(Type.BOOL));
+ assertThat(cur.asBool(), is(true));
+
+ System.out.println("testing long value");
+ slime.setLong(42);
+ cur = slime.get();
+ insp = slime.get();
+ assertThat(cur.valid(), is(true));
+ assertThat(insp.valid(), is(true));
+ assertThat(cur.type(), sameInstance(Type.LONG));
+ assertThat(insp.type(), sameInstance(Type.LONG));
+ assertThat(cur.asLong(), is((long)42));
+ assertThat(insp.asLong(), is((long)42));
+
+ System.out.println("testing double value");
+ slime.setDouble(4.2);
+ cur = slime.get();
+ insp = slime.get();
+ assertThat(cur.valid(), is(true));
+ assertThat(insp.valid(), is(true));
+ assertThat(cur.type(), sameInstance(Type.DOUBLE));
+ assertThat(insp.type(), sameInstance(Type.DOUBLE));
+ assertThat(cur.asDouble(), is(4.2));
+ assertThat(insp.asDouble(), is(4.2));
+
+ System.out.println("testing string value");
+ slime.setString("fortytwo");
+ cur = slime.get();
+ insp = slime.get();
+ assertThat(cur.valid(), is(true));
+ assertThat(insp.valid(), is(true));
+ assertThat(cur.type(), sameInstance(Type.STRING));
+ assertThat(insp.type(), sameInstance(Type.STRING));
+ assertThat(cur.asString(), is("fortytwo"));
+ assertThat(insp.asString(), is("fortytwo"));
+
+ System.out.println("testing data value");
+ byte[] data = { (byte)4, (byte)2 };
+ slime.setData(data);
+ cur = slime.get();
+ insp = slime.get();
+ assertThat(cur.valid(), is(true));
+ assertThat(insp.valid(), is(true));
+ assertThat(cur.type(), sameInstance(Type.DATA));
+ assertThat(insp.type(), sameInstance(Type.DATA));
+ assertThat(cur.asData(), is(data));
+ assertThat(insp.asData(), is(data));
+ data[0] = 10;
+ data[1] = 20;
+ byte[] data2 = { 10, 20 };
+ assertThat(cur.asData(), is(data2));
+ assertThat(insp.asData(), is(data2));
+ }
+
+ @Test
+ public void testArray() {
+ System.out.println("testing array values...");
+ Slime slime = new Slime();
+ Cursor c = slime.setArray();
+ assertThat(c.valid(), is(true));
+ assertThat(c.type(), is(Type.ARRAY));
+ assertThat(c.children(), is(0));
+ Inspector i = slime.get();
+ assertThat(i.valid(), is(true));
+ assertThat(i.type(), is(Type.ARRAY));
+ assertThat(i.children(), is(0));
+ c.addNix();
+ c.addBool(true);
+ c.addLong(5);
+ c.addDouble(3.5);
+ c.addString("string");
+ byte[] data = { (byte)'d', (byte)'a', (byte)'t', (byte)'a' };
+ c.addData(data);
+ assertThat(c.children(), is(6));
+ assertThat(c.entry(0).valid(), is(true));
+ assertThat(c.entry(1).asBool(), is(true));
+ assertThat(c.entry(2).asLong(), is((long)5));
+ assertThat(c.entry(3).asDouble(), is(3.5));
+ assertThat(c.entry(4).asString(), is("string"));
+ assertThat(c.entry(5).asData(), is(data));
+ assertThat(c.field(5).valid(), is(false)); // not OBJECT
+
+ assertThat(i.children(), is(6));
+ assertThat(i.entry(0).valid(), is(true));
+ assertThat(i.entry(1).asBool(), is(true));
+ assertThat(i.entry(2).asLong(), is((long)5));
+ assertThat(i.entry(3).asDouble(), is(3.5));
+ assertThat(i.entry(4).asString(), is("string"));
+ assertThat(i.entry(5).asData(), is(data));
+ assertThat(i.field(5).valid(), is(false)); // not OBJECT
+ }
+
+ @Test
+ public void testObject() {
+ System.out.println("testing object values...");
+ Slime slime = new Slime();
+ Cursor c = slime.setObject();
+
+ assertThat(c.valid(), is(true));
+ assertThat(c.type(), is(Type.OBJECT));
+ assertThat(c.children(), is(0));
+ Inspector i = slime.get();
+ assertThat(i.valid(), is(true));
+ assertThat(i.type(), is(Type.OBJECT));
+ assertThat(i.children(), is(0));
+
+ c.setNix("a");
+ c.setBool("b", true);
+ c.setLong("c", 5);
+ c.setDouble("d", 3.5);
+ c.setString("e", "string");
+ byte[] data = { (byte)'d', (byte)'a', (byte)'t', (byte)'a' };
+ c.setData("f", data);
+
+ assertThat(c.children(), is(6));
+ assertThat(c.field("a").valid(), is(true));
+ assertThat(c.field("b").asBool(), is(true));
+ assertThat(c.field("c").asLong(), is((long)5));
+ assertThat(c.field("d").asDouble(), is(3.5));
+ assertThat(c.field("e").asString(), is("string"));
+ assertThat(c.field("f").asData(), is(data));
+ assertThat(c.entry(4).valid(), is(false)); // not ARRAY
+
+ assertThat(i.children(), is(6));
+ assertThat(i.field("a").valid(), is(true));
+ assertThat(i.field("b").asBool(), is(true));
+ assertThat(i.field("c").asLong(), is((long)5));
+ assertThat(i.field("d").asDouble(), is(3.5));
+ assertThat(i.field("e").asString(), is("string"));
+ assertThat(i.field("f").asData(), is(data));
+ assertThat(i.entry(4).valid(), is(false)); // not ARRAY
+ }
+
+ @Test
+ public void testChaining() {
+ System.out.println("testing cursor chaining...");
+ {
+ Slime slime = new Slime();
+ Cursor c = slime.setArray();
+ assertThat(c.addLong(5).asLong(), is((long)5));
+ }
+ {
+ Slime slime = new Slime();
+ Cursor c = slime.setObject();
+ assertThat(c.setLong("a", 5).asLong(), is((long)5));
+ }
+ }
+
+ @Test
+ public void testCursorToInspector() {
+ System.out.println("testing proxy conversion...");
+
+ Slime slime = new Slime();
+ Cursor c = slime.setLong(10);
+ Inspector i1 = c;
+ assertThat(i1.asLong(), is((long)10));
+
+ Inspector i2 = slime.get();
+ assertThat(i2.asLong(), is((long)10));
+ }
+
+ @Test
+ public void testNesting() {
+ System.out.println("testing data nesting...");
+ Slime slime = new Slime();
+ {
+ Cursor c1 = slime.setObject();
+ c1.setLong("bar", 10);
+ Cursor c2 = c1.setArray("foo");
+ c2.addLong(20);
+ Cursor c3 = c2.addObject();
+ c3.setLong("answer", 42);
+ }
+ Inspector i = slime.get();
+ assertThat(i.field("bar").asLong(), is((long)10));
+ assertThat(i.field("foo").entry(0).asLong(), is((long)20));
+ assertThat(i.field("foo").entry(1).field("answer").asLong(), is((long)42));
+
+ Cursor c = slime.get();
+ assertThat(c.field("bar").asLong(), is((long)10));
+ assertThat(c.field("foo").entry(0).asLong(), is((long)20));
+ assertThat(c.field("foo").entry(1).field("answer").asLong(), is((long)42));
+ }
+
+ @Test
+ public void testLotsOfSymbolsAndFields() {
+ // put pressure on symbol table and object fields
+ int n = 1000;
+ Slime slime = new Slime();
+ Cursor c = slime.setObject();
+ for (int i = 0; i < n; i++) {
+ String str = ("" + i + "_str_" + i);
+ assertThat(slime.lookup(str), is(SymbolTable.INVALID));
+ assertThat(c.field(str).type(), sameInstance(Type.NIX));
+ switch (i % 2) {
+ case 0: assertThat((int)c.setLong(str, i).asLong(), is(i)); break;
+ case 1: assertThat(slime.insert(str), is(i)); break;
+ }
+ }
+ for (int i = 0; i < n; i++) {
+ String str = ("" + i + "_str_" + i);
+ assertThat(slime.lookup(str), is(i));
+ switch (i % 2) {
+ case 0: assertThat((int)c.field(str).asLong(), is(i)); break;
+ case 1: assertThat((int)c.field(str).asLong(), is(0)); break;
+ }
+ }
+ }
+
+ @Test
+ public void testLotsOfEntries() {
+ // put pressure on array entries
+ int n = 1000;
+ Slime slime = new Slime();
+ Cursor c = slime.setArray();
+ for (int i = 0; i < n; i++) {
+ assertThat((int)c.addLong(i).asLong(), is(i));
+ }
+ for (int i = 0; i < n; i++) {
+ assertThat((int)c.entry(i).asLong(), is(i));
+ }
+ assertThat((int)c.entry(n).asLong(), is(0));
+ }
+
+ @Test
+ public void testToString() {
+ Slime slime = new Slime();
+ Cursor c1 = slime.setArray();
+ c1.addLong(20);
+ Cursor c2 = c1.addObject();
+ c2.setLong("answer", 42);
+ assertThat(slime.get().toString(), is("[20,{\"answer\":42}]"));
+ c1.addString("\u2008");
+ assertThat(slime.get().toString(), is("[20,{\"answer\":42},\"\u2008\"]"));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/slime/VisitorTestCase.java b/vespajlib/src/test/java/com/yahoo/slime/VisitorTestCase.java
new file mode 100644
index 00000000000..949cb4eecf2
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/slime/VisitorTestCase.java
@@ -0,0 +1,101 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.slime;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.mockito.Matchers.argThat;
+import static org.hamcrest.CoreMatchers.sameInstance;
+
+public class VisitorTestCase {
+
+ @Test
+ public void testVisitInvalid() {
+ Visitor visitor = Mockito.mock(Visitor.class);
+ Inspector inspector = new Slime().get().field("invalid");
+ inspector.accept(visitor);
+ Mockito.verify(visitor).visitInvalid();
+ Mockito.verifyNoMoreInteractions(visitor);
+ }
+
+ @Test
+ public void testVisitNix() {
+ Visitor visitor = Mockito.mock(Visitor.class);
+ Inspector inspector = new Slime().get();
+ inspector.accept(visitor);
+ Mockito.verify(visitor).visitNix();
+ Mockito.verifyNoMoreInteractions(visitor);
+ }
+
+ @Test
+ public void testVisitBool() {
+ Visitor visitor = Mockito.mock(Visitor.class);
+ Inspector inspector = new Slime().setBool(true);
+ inspector.accept(visitor);
+ Mockito.verify(visitor).visitBool(true);
+ Mockito.verifyNoMoreInteractions(visitor);
+ }
+
+ @Test
+ public void testVisitLong() {
+ Visitor visitor = Mockito.mock(Visitor.class);
+ Inspector inspector = new Slime().setLong(123);
+ inspector.accept(visitor);
+ Mockito.verify(visitor).visitLong(123);
+ Mockito.verifyNoMoreInteractions(visitor);
+ }
+
+ @Test
+ public void testVisitDouble() {
+ Visitor visitor = Mockito.mock(Visitor.class);
+ Inspector inspector = new Slime().setDouble(123.0);
+ inspector.accept(visitor);
+ Mockito.verify(visitor).visitDouble(123.0);
+ Mockito.verifyNoMoreInteractions(visitor);
+ }
+
+ @Test
+ public void testVisitStringUtf16() {
+ Visitor visitor = Mockito.mock(Visitor.class);
+ Inspector inspector = new Slime().setString("abc");
+ inspector.accept(visitor);
+ Mockito.verify(visitor).visitString("abc");
+ Mockito.verifyNoMoreInteractions(visitor);
+ }
+
+ @Test
+ public void testVisitStringUtf8() {
+ Visitor visitor = Mockito.mock(Visitor.class);
+ Inspector inspector = new Slime().setString(new byte[] {65,66,67});
+ inspector.accept(visitor);
+ Mockito.verify(visitor).visitString(new byte[] {65,66,67});
+ Mockito.verifyNoMoreInteractions(visitor);
+ }
+
+ @Test
+ public void testVisitData() {
+ Visitor visitor = Mockito.mock(Visitor.class);
+ Inspector inspector = new Slime().setData(new byte[] {1,2,3});
+ inspector.accept(visitor);
+ Mockito.verify(visitor).visitData(new byte[] {1,2,3});
+ Mockito.verifyNoMoreInteractions(visitor);
+ }
+
+ @Test
+ public void testVisitArray() {
+ Visitor visitor = Mockito.mock(Visitor.class);
+ Inspector inspector = new Slime().setArray();
+ inspector.accept(visitor);
+ Mockito.verify(visitor).visitArray(argThat(sameInstance(inspector)));
+ Mockito.verifyNoMoreInteractions(visitor);
+ }
+
+ @Test
+ public void testVisitObject() {
+ Visitor visitor = Mockito.mock(Visitor.class);
+ Inspector inspector = new Slime().setObject();
+ inspector.accept(visitor);
+ Mockito.verify(visitor).visitObject(argThat(sameInstance(inspector)));
+ Mockito.verifyNoMoreInteractions(visitor);
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/system/Bar.java b/vespajlib/src/test/java/com/yahoo/system/Bar.java
new file mode 100644
index 00000000000..747b19edaee
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/system/Bar.java
@@ -0,0 +1,7 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+/**
+ * Dummy class to be used to test force loading
+ **/
+public class Bar {}
diff --git a/vespajlib/src/test/java/com/yahoo/system/CatchSigTermTestCase.java b/vespajlib/src/test/java/com/yahoo/system/CatchSigTermTestCase.java
new file mode 100644
index 00000000000..dfe508eb2d6
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/system/CatchSigTermTestCase.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+import com.yahoo.system.CatchSigTerm;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @author arnej27959
+ */
+public class CatchSigTermTestCase extends junit.framework.TestCase {
+
+ public CatchSigTermTestCase(String name) {
+ super(name);
+ }
+
+ public void testThatSetupCompiles() {
+ CatchSigTerm.setup(new AtomicBoolean(false));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/system/CommandLineParserTestCase.java b/vespajlib/src/test/java/com/yahoo/system/CommandLineParserTestCase.java
new file mode 100644
index 00000000000..a2a086e4c65
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/system/CommandLineParserTestCase.java
@@ -0,0 +1,125 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+import junit.framework.TestCase;
+
+public class CommandLineParserTestCase extends TestCase {
+
+ public void testParse1() {
+ String[] args = new String[] {"-d", "-f", "hello.txt"};
+ CommandLineParser parser = new CommandLineParser(args);
+ parser.addLegalBinarySwitch("-f");
+ parser.addLegalUnarySwitch("-d");
+ parser.parse();
+ assertNull(parser.getBinarySwitches().get("-g"));
+ assertFalse(parser.getUnarySwitches().contains("-e"));
+ assertEquals(parser.getBinarySwitches().get("-f"), "hello.txt");
+ assertTrue(parser.getUnarySwitches().contains("-d"));
+ assertFalse(parser.getArguments().contains("-d"));
+ assertFalse(parser.getArguments().contains("-f"));
+ assertFalse(parser.getArguments().contains("-hello.txt"));
+ assertEquals(parser.getArguments().size(), 0);
+ }
+
+ public void testParse2() {
+ String[] args = new String[] {"-d", "-f", "hello.txt", "-XX", "myName", "-o", "output file", "myLastField"};
+ CommandLineParser parser = new CommandLineParser("progname", args);
+ parser.setArgumentExplanation("Bla bla1");
+ parser.setExtendedHelpText("Bla bla blaaaaaaa bla2");
+ parser.addLegalBinarySwitch("-f");
+ parser.addLegalBinarySwitch("-o");
+ parser.addLegalUnarySwitch("-d");
+ parser.addLegalUnarySwitch("-XX");
+ parser.parse();
+ assertNull(parser.getBinarySwitches().get("-g"));
+ assertFalse(parser.getUnarySwitches().contains("-e"));
+ assertEquals(parser.getBinarySwitches().get("-f"), "hello.txt");
+ assertTrue(parser.getUnarySwitches().contains("-d"));
+ assertTrue(parser.getUnarySwitches().contains("-XX"));
+ assertEquals(parser.getBinarySwitches().get("-o"), "output file");
+ assertTrue(parser.getArguments().contains("myName"));
+ assertTrue(parser.getArguments().contains("myLastField"));
+ assertEquals(parser.getUnarySwitches().size(), 2);
+ assertEquals(parser.getBinarySwitches().size(), 2);
+ assertEquals(parser.getArguments().size(), 2);
+ assertEquals(parser.getArguments().get(0), "myName");
+ assertEquals(parser.getArguments().get(1), "myLastField");
+ assertEquals(parser.getUnarySwitches().get(0), "-d");
+ assertEquals(parser.getUnarySwitches().get(1), "-XX");
+
+ try {
+ parser.usageAndThrow();
+ fail("usageAndThrow didn't throw");
+ } catch (Exception e) {
+ assertTrue(e.getMessage().replaceAll("\n", "").matches(".*bla1.*"));
+ assertTrue(e.getMessage().replaceAll("\n", "").matches(".*bla2.*"));
+ }
+ }
+
+ public void testIllegal() {
+ String[] args = new String[] {"-d", "-f", "hello.txt", "-XX", "myName", "-o", "output file", "myLastField"};
+ CommandLineParser parser = new CommandLineParser(args);
+ parser.addLegalBinarySwitch("-f");
+ parser.addLegalBinarySwitch("-o");
+ parser.addLegalUnarySwitch("-d");
+ try {
+ parser.parse();
+ fail("Parse of cmd line with illegal arg worked");
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.getMessage().startsWith("\nusage"));
+ }
+
+ args = new String[] {"-d", "-f", "hello.txt", "-XX", "myName", "-o", "output file", "myLastField"};
+ parser = new CommandLineParser(args);
+ parser.addLegalBinarySwitch("-f");
+ parser.addLegalUnarySwitch("-d");
+ parser.addLegalUnarySwitch("-XX");
+ try {
+ parser.parse();
+ fail("Parse of cmd line with illegal arg worked");
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.getMessage().startsWith("\nusage"));
+ }
+ }
+
+ public void testRequired() {
+ String[] args1 = new String[] {"-d", "-f", "hello.txt", "-XX", "myName", "-o", "output file", "myLastField"};
+ String[] args2 = new String[] {"-XX", "myName", "-o", "output file", "myLastField"};
+ CommandLineParser parser = new CommandLineParser(args1);
+ parser.addLegalBinarySwitch("-f", "test1");
+ parser.addRequiredBinarySwitch("-o", "test2");
+ parser.addLegalUnarySwitch("-d", "test3");
+ parser.addLegalUnarySwitch("-XX", "test4");
+ parser.parse();
+
+ parser = new CommandLineParser(args2);
+ parser.addRequiredBinarySwitch("-o", "test2");
+ parser.addLegalUnarySwitch("-XX", "test4");
+ parser.parse();
+ assertEquals(parser.getBinarySwitches().size(),1);
+ assertEquals(parser.getUnarySwitches().size(),1);
+
+ parser = new CommandLineParser(args2);
+ parser.addLegalUnarySwitch("-XX", "test4");
+ parser.addRequiredBinarySwitch("-f", "test5");
+ parser.addRequiredBinarySwitch("-o", "test6");
+ try {
+ parser.parse();
+ fail("Illegal cmd line parsed");
+ } catch (Exception e) {
+ assertTrue(e.getMessage().startsWith("\nusage"));
+ }
+
+ args1 = new String[] {"-d"};
+ parser = new CommandLineParser(args1);
+ parser.addRequiredUnarySwitch("-d", "(required, there are so many bugs)");
+ try {
+ parser.addLegalBinarySwitch("-d");
+ fail("Switch clobber didn't throw");
+ } catch (Exception e) {
+ assertTrue(e.getMessage().matches(".*already.*"));
+ }
+ parser.parse();
+ assertEquals(parser.getUnarySwitches().get(0), "-d");
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/system/Foo.java b/vespajlib/src/test/java/com/yahoo/system/Foo.java
new file mode 100644
index 00000000000..ea51a80caaa
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/system/Foo.java
@@ -0,0 +1,7 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+/**
+ * Dummy class to be used to test force loading
+ **/
+public class Foo {}
diff --git a/vespajlib/src/test/java/com/yahoo/system/ForceLoadTestCase.java b/vespajlib/src/test/java/com/yahoo/system/ForceLoadTestCase.java
new file mode 100644
index 00000000000..ec4f716247e
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/system/ForceLoadTestCase.java
@@ -0,0 +1,27 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+public class ForceLoadTestCase extends junit.framework.TestCase {
+
+ public ForceLoadTestCase(String name) {
+ super(name);
+ }
+
+ public void testLoadClasses() {
+ try {
+ ForceLoad.forceLoad(getClass().getPackage().getName(), new String[] { "Foo", "Bar" });
+ } catch (ForceLoadError e) {
+ e.printStackTrace();
+ assertTrue(false);
+ }
+ }
+
+ public void testLoadBogusClass() {
+ try {
+ ForceLoad.forceLoad(getClass().getPackage().getName(), new String[] { "Foo", "Bar", "Baz" });
+ } catch (ForceLoadError e) {
+ return;
+ }
+ assertTrue(false);
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/system/ProcessExecuterTestCase.java b/vespajlib/src/test/java/com/yahoo/system/ProcessExecuterTestCase.java
new file mode 100644
index 00000000000..2bf1d8c094a
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/system/ProcessExecuterTestCase.java
@@ -0,0 +1,23 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.system;
+
+import com.yahoo.collections.Pair;
+import com.yahoo.io.IOUtils;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class ProcessExecuterTestCase extends junit.framework.TestCase {
+
+ public void testIt() throws IOException {
+ IOUtils.writeFile("tmp123.txt","hello\nworld",false);
+ ProcessExecuter exec=new ProcessExecuter();
+ assertEquals(new Pair<>(0, "hello\nworld"), exec.exec("cat tmp123.txt"));
+ assertEquals(new Pair<>(0, "hello\nworld"), exec.exec(new String[]{"cat", "tmp123.txt"}));
+ new File("tmp123.txt").delete();
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/tensor/MapTensorBuilderTestCase.java b/vespajlib/src/test/java/com/yahoo/tensor/MapTensorBuilderTestCase.java
new file mode 100644
index 00000000000..92f0e71c7f5
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/tensor/MapTensorBuilderTestCase.java
@@ -0,0 +1,48 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.tensor;
+
+import com.google.common.collect.Sets;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:geirst@yahoo-inc.com">Geir Storli</a>
+ */
+public class MapTensorBuilderTestCase {
+
+ @Test
+ public void requireThatEmptyTensorCanBeBuilt() {
+ Tensor tensor = new MapTensorBuilder().build();
+ assertEquals(0, tensor.dimensions().size());
+ assertEquals("{}", tensor.toString());
+ }
+
+ @Test
+ public void requireThatOneDimensionalTensorCanBeBuilt() {
+ Tensor tensor = new MapTensorBuilder().
+ cell().label("x", "0").value(1).
+ cell().label("x", "1").value(2).build();
+ assertEquals(Sets.newHashSet("x"), tensor.dimensions());
+ assertEquals("{{x:0}:1.0,{x:1}:2.0}", tensor.toString());
+ }
+
+ @Test
+ public void requireThatTwoDimensionalTensorCanBeBuilt() {
+ Tensor tensor = new MapTensorBuilder().
+ cell().label("x", "0").label("y", "0").value(1).
+ cell().label("x", "1").label("y", "0").value(2).build();
+ assertEquals(Sets.newHashSet("x", "y"), tensor.dimensions());
+ assertEquals("{{x:1,y:0}:2.0,{x:0,y:0}:1.0}", tensor.toString());
+ }
+
+ @Test
+ public void requireThatExtraDimensionsCanBeSpecified() {
+ Tensor tensor = new MapTensorBuilder().dimension("y").dimension("z").
+ cell().label("x", "0").value(1).build();
+ assertEquals(Sets.newHashSet("x", "y", "z"), tensor.dimensions());
+ assertEquals("( {{y:-,z:-}:1.0} * {{x:0}:1.0} )", tensor.toString());
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/tensor/MapTensorTestCase.java b/vespajlib/src/test/java/com/yahoo/tensor/MapTensorTestCase.java
new file mode 100644
index 00000000000..13ea0e95dc8
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/tensor/MapTensorTestCase.java
@@ -0,0 +1,69 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.tensor;
+
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Basic tensor tests. Tensor operations are tested in EvaluationTestCase
+ *
+ * @author bratseth
+ */
+public class MapTensorTestCase {
+
+ @Test
+ public void testStringForm() {
+ assertEquals("{}", MapTensor.from("{}").toString());
+ assertEquals("{{d1:l1}:5.0,{d1:l1,d2:l2}:6.0}", MapTensor.from("{ {d1:l1}:5, {d2:l2, d1:l1}:6.0} ").toString());
+ assertEquals("{{d1:l1}:-5.3,{d1:l1,d2:l2}:0.0}", MapTensor.from("{ {d1:l1}:-5.3, {d2:l2, d1:l1}:0}").toString());
+ }
+
+ @Test
+ public void testParseError() {
+ try {
+ MapTensor.from("--");
+ fail("Expected parse error");
+ }
+ catch (IllegalArgumentException expected) {
+ assertEquals("Excepted a string starting by { or (, got '--'", expected.getMessage());
+ }
+ }
+
+ @Test
+ public void testConstruction() {
+ assertEquals("{}", new MapTensor(Collections.emptyMap()).toString());
+ assertEquals("{{}:5.0}", new MapTensor(Collections.singletonMap(TensorAddress.empty, 5.0)).toString());
+
+ Map<TensorAddress, Double> cells = new LinkedHashMap<>();
+ cells.put(TensorAddress.fromSorted(Collections.singletonList(new TensorAddress.Element("d1","l1"))), 5.0);
+ cells.put(TensorAddress.fromSorted(Collections.singletonList(new TensorAddress.Element("d2","l1"))), 6.0);
+ cells.put(TensorAddress.empty, 7.0);
+ assertEquals("{{}:7.0,{d1:l1}:5.0,{d2:l1}:6.0}", new MapTensor(cells).toString());
+ }
+
+ @Test
+ public void testDimensions() {
+ Set<String> dimensions1 = MapTensor.from("{} ").dimensions();
+ assertEquals(0, dimensions1.size());
+
+ Set<String> dimensions2 = MapTensor.from("{ {d1:l1}:5, {d2:l2, d1:l1}:6.0} ").dimensions();
+ assertEquals(2, dimensions2.size());
+ assertTrue(dimensions2.contains("d1"));
+ assertTrue(dimensions2.contains("d2"));
+
+ Set<String> dimensions3 = MapTensor.from("{ {d1:l1, d2:l1}:5, {d2:l2, d3:l1}:6.0} ").dimensions();
+ assertEquals(3, dimensions3.size());
+ assertTrue(dimensions3.contains("d1"));
+ assertTrue(dimensions3.contains("d2"));
+ assertTrue(dimensions3.contains("d3"));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/tensor/TensorTypeTestCase.java b/vespajlib/src/test/java/com/yahoo/tensor/TensorTypeTestCase.java
new file mode 100644
index 00000000000..59d77f6569a
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/tensor/TensorTypeTestCase.java
@@ -0,0 +1,89 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.tensor;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * @author <a href="mailto:geirst@yahoo-inc.com">Geir Storli</a>
+ */
+public class TensorTypeTestCase {
+
+ @Test
+ public void requireThatAnEmptyTensorTypeCanBeSpecified() {
+ assertTensorType("tensor()");
+ }
+
+ @Test
+ public void requireThatBoundIndexedDimensionsCanBeSpecified() {
+ assertTensorType("tensor(x[5])");
+ assertTensorType("tensor(x[5],y[10],z[100])");
+ assertTensorType("tensor(x[5],y[10],z[100])", "tensor( x[5] , y[10] , z[100] )");
+ assertTensorType("tensor(baR_09[10])");
+ }
+
+ @Test
+ public void requireThatUnboundIndexedDimensionsCanBeSpecified() {
+ assertTensorType("tensor(x[])");
+ assertTensorType("tensor(x[],y[],z[])");
+ assertTensorType("tensor(x[],y[],z[])", "tensor( x[] , y[] , z[] )");
+ assertTensorType("tensor(baR_09[])");
+ }
+
+ @Test
+ public void requireThatMappedDimensionsCanBeSpecified() {
+ assertTensorType("tensor(x{})");
+ assertTensorType("tensor(x{},y{},z{})");
+ assertTensorType("tensor(x{},y{},z{})", "tensor( x{} , y{} , z{} )");
+ assertTensorType("tensor(baR_09{})");
+ }
+
+ @Test
+ public void requireThatIndexedBoundDimensionMustHaveNonZeroSize() {
+ assertIllegalTensorType("tensor(x[0])", "Size of bound dimension 'x' must be at least 1");
+ }
+
+ @Test
+ public void requireThatDimensionsMustHaveUniqueNames() {
+ assertIllegalTensorType("tensor(x[10],y[20],x[30])", "'x[10]' and 'x[30]' have the same name");
+ assertIllegalTensorType("tensor(x{},y{},x{})", "'x{}' and 'x{}' have the same name");
+ }
+
+ @Test
+ public void requireThatDimensionsAreOfSameType() {
+ assertIllegalTensorType("tensor(x[10],y[])", "'x[10]' does not have the same type as 'y[]'");
+ assertIllegalTensorType("tensor(x[10],y{})", "'x[10]' does not have the same type as 'y{}'");
+ assertIllegalTensorType("tensor(x[10],y[20],z{})", "'y[20]' does not have the same type as 'z{}'");
+ assertIllegalTensorType("tensor(x[],y{})", "'x[]' does not have the same type as 'y{}'");
+ }
+
+ @Test
+ public void requireThatIllegalSyntaxInSpecThrowsException() {
+ assertIllegalTensorType("foo(x[10])", "Tensor type spec must start with 'tensor(' and end with ')', but was 'foo(x[10])'");
+ assertIllegalTensorType("tensor(x_@[10])", "Failed parsing element 'x_@[10]' in type spec 'tensor(x_@[10])'");
+ assertIllegalTensorType("tensor(x[10a])", "Failed parsing element 'x[10a]' in type spec 'tensor(x[10a])'");
+ assertIllegalTensorType("tensor(x{10})", "Failed parsing element 'x{10}' in type spec 'tensor(x{10})'");
+ }
+
+ private static void assertTensorType(String typeSpec) {
+ assertTensorType(typeSpec, typeSpec);
+ }
+
+ private static void assertTensorType(String expected, String typeSpec) {
+ assertEquals(expected, TensorType.fromSpec(typeSpec).toString());
+ }
+
+ private static void assertIllegalTensorType(String typeSpec, String messageSubstring) {
+ try {
+ TensorType.fromSpec(typeSpec);
+ fail("Exception exception to be thrown with message: '" + messageSubstring + "'");
+ } catch (IllegalArgumentException e) {
+ assertThat(e.getMessage(), containsString(messageSubstring));
+ }
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/tensor/serialization/CompactBinaryFormatTestCase.java b/vespajlib/src/test/java/com/yahoo/tensor/serialization/CompactBinaryFormatTestCase.java
new file mode 100644
index 00000000000..23589577c0c
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/tensor/serialization/CompactBinaryFormatTestCase.java
@@ -0,0 +1,78 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.tensor.serialization;
+
+import com.google.common.collect.Sets;
+import com.yahoo.tensor.MapTensor;
+import com.yahoo.tensor.Tensor;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for the compact binary format.
+ *
+ * TODO: When new formats are added we should refactor this test to test all formats
+ * with the same set of tensor inputs (if feasible).
+ *
+ * @author <a href="mailto:geirst@yahoo-inc.com">Geir Storli</a>
+ */
+public class CompactBinaryFormatTestCase {
+
+ private static void assertSerialization(String tensorString) {
+ assertSerialization(MapTensor.from(tensorString));
+ }
+
+ private static void assertSerialization(String tensorString, Set<String> dimensions) {
+ Tensor tensor = MapTensor.from(tensorString);
+ assertEquals(dimensions, tensor.dimensions());
+ assertSerialization(tensor);
+ }
+
+ private static void assertSerialization(Tensor tensor) {
+ byte[] encodedTensor = TypedBinaryFormat.encode(tensor);
+ Tensor decodedTensor = TypedBinaryFormat.decode(encodedTensor);
+ assertEquals(tensor, decodedTensor);
+ }
+
+ @Test
+ public void testSerializationOfTensorsWithDenseTensorAddresses() {
+ assertSerialization("{}");
+ assertSerialization("{{x:0}:2.0}");
+ assertSerialization("{{x:0}:2.0,{x:1}:3.0}");
+ assertSerialization("{{x:0,y:0}:2.0}");
+ assertSerialization("{{x:0,y:0}:2.0,{x:0,y:1}:3.0}");
+ assertSerialization("{{y:0,x:0}:2.0}");
+ assertSerialization("{{y:0,x:0}:2.0,{y:1,x:0}:3.0}");
+ assertSerialization("{{dimX:labelA,dimY:labelB}:2.0,{dimY:labelC,dimX:labelD}:3.0}");
+ }
+
+ @Test
+ public void testSerializationOfTensorsWithSparseTensorAddresses() {
+ assertSerialization("{{x:0}:2.0, {}:3.0}", Sets.newHashSet("x"));
+ assertSerialization("({{y:-}:1} * {{x:0}:2.0})", Sets.newHashSet("x", "y"));
+ assertSerialization("({{y:-}:1} * {{x:0}:2.0, {}:3.0})", Sets.newHashSet("x", "y"));
+ assertSerialization("({{y:-}:1} * {{x:0}:2.0,{x:1}:3.0})", Sets.newHashSet("x", "y"));
+ assertSerialization("({{z:-}:1} * {{x:0,y:0}:2.0})", Sets.newHashSet("x", "y", "z"));
+ assertSerialization("({{z:-}:1} * {{x:0,y:0}:2.0,{x:0,y:1}:3.0})", Sets.newHashSet("x", "y", "z"));
+ assertSerialization("({{z:-}:1} * {{y:0,x:0}:2.0})", Sets.newHashSet("x", "y", "z"));
+ assertSerialization("({{z:-}:1} * {{y:0,x:0}:2.0,{y:1,x:0}:3.0})", Sets.newHashSet("x", "y", "z"));
+ assertSerialization("({{z:-}:1} * {{}:2.0,{x:0}:3.0,{x:0,y:0}:5.0})", Sets.newHashSet("x", "y", "z"));
+ }
+
+ @Test
+ public void requireThatCompactSerializationFormatDoNotChange() {
+ byte[] encodedTensor = new byte[] {1, // binary format type
+ 2, // num dimensions
+ 2, (byte)'x', (byte)'y', 1, (byte)'z', // dimensions
+ 2, // num cells,
+ 2, (byte)'a', (byte)'b', 0, 64, 0, 0, 0, 0, 0, 0, 0, // cell 0
+ 2, (byte)'c', (byte)'d', 1, (byte)'e', 64, 8, 0, 0, 0, 0, 0, 0}; // cell 1
+ assertEquals(Arrays.toString(encodedTensor),
+ Arrays.toString(TypedBinaryFormat.encode(MapTensor.from("{{xy:ab}:2.0,{xy:cd,z:e}:3.0}"))));
+ }
+
+}
+
diff --git a/vespajlib/src/test/java/com/yahoo/text/AsciiTest.java b/vespajlib/src/test/java/com/yahoo/text/AsciiTest.java
new file mode 100644
index 00000000000..4d598e7b1bb
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/AsciiTest.java
@@ -0,0 +1,187 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class AsciiTest {
+
+ @Test
+ public void requireThatAdditionalCodePointsCanBeEscaped() {
+ assertEquals("\\x66\\x6F\\x6F \\x62ar \\x62az",
+ Ascii.newEncoder(StandardCharsets.UTF_8, 'f', 'o', 'b').encode("foo bar baz"));
+ }
+
+ @Test
+ public void requireThatReadableCharactersAreNotEscaped() {
+ StringBuilder str = new StringBuilder();
+ for (int i = 0x20; i < 0x7F; ++i) {
+ if (i != '\\') {
+ str.appendCodePoint(i);
+ }
+ }
+ assertEncodeUtf8(str.toString(), str.toString());
+ }
+
+ @Test
+ public void requireThatNonReadableCharactersAreEscapedAsUtf8() {
+ for (int i = Character.MIN_CODE_POINT; i < 0x20; ++i) {
+ String expected;
+ switch (i) {
+ case '\f':
+ expected = "\\f";
+ break;
+ case '\n':
+ expected = "\\n";
+ break;
+ case '\r':
+ expected = "\\r";
+ break;
+ case '\t':
+ expected = "\\t";
+ break;
+ default:
+ expected = String.format("\\x%02X", i);
+ break;
+ }
+ assertEncodeUtf8(expected, new StringBuilder().appendCodePoint(i).toString());
+ }
+ for (int i = 0x80; i < 0xC0; ++i) {
+ String expected = String.format("\\xC2\\x%02X", i);
+ assertEncodeUtf8(expected, new StringBuilder().appendCodePoint(i).toString());
+ }
+ for (int i = 0xC0; i < 0x0100; ++i) {
+ String expected = String.format("\\xC3\\x%02X", i - 0x40);
+ assertEncodeUtf8(expected, new StringBuilder().appendCodePoint(i).toString());
+ }
+ for (int i = 0x0100; i < 0x0140; ++i) {
+ String expected = String.format("\\xC4\\x%02X", i - 0x80);
+ assertEncodeUtf8(expected, new StringBuilder().appendCodePoint(i).toString());
+ }
+ }
+
+ @Test
+ public void requireThatBackslashIsEscaped() {
+ assertEncodeUtf8("\\\\", "\\");
+ }
+
+ @Test
+ public void requireThatQuoteIsEscaped() {
+ assertEncodeUtf8("\\x62az", "baz", 'b');
+ assertEncodeUtf8("b\\x61z", "baz", 'a');
+ assertEncodeUtf8("ba\\x7A", "baz", 'z');
+ }
+
+ @Test
+ public void requireThatAnyEscapedCharacterCanBeUnescaped() {
+ assertDecodeUtf8("baz", "\\baz");
+ assertDecodeUtf8("baz", "b\\az");
+ assertDecodeUtf8("baz", "ba\\z");
+ }
+
+ @Test
+ public void requireThatUtf8SequencesAreUnescaped() {
+ for (int i = 0x80; i < 0xC0; ++i) {
+ String str = String.format("\\xC2\\x%02X", i);
+ assertDecodeUtf8(new StringBuilder().appendCodePoint(i).toString(), str);
+ }
+ for (int i = 0xC0; i < 0x0100; ++i) {
+ String str = String.format("\\xC3\\x%02X", i - 0x40);
+ assertDecodeUtf8(new StringBuilder().appendCodePoint(i).toString(), str);
+ }
+ for (int i = 0x0100; i < 0x0140; ++i) {
+ String str = String.format("\\xC4\\x%02X", i - 0x80);
+ assertDecodeUtf8(new StringBuilder().appendCodePoint(i).toString(), str);
+ }
+ }
+
+ @Test
+ public void requireThatUtf8CanBeEncoded() {
+ // First possible sequence of a certain length
+ assertEncodeUtf8("\\x00", "\u0000");
+ assertEncodeUtf8("\\xC2\\x80", "\u0080");
+ assertEncodeUtf8("\\xE0\\xA0\\x80", "\u0800");
+ assertEncodeUtf8("\\x01\\x00", "\u0001\u0000");
+ assertEncodeUtf8("\\x20\\x00", "\u0020\u0000", ' ');
+ assertEncodeUtf8("\\xD0\\x80\\x00", "\u0400\u0000");
+
+ // Last possible sequence of a certain length
+ assertEncodeUtf8("\\x7F", "\u007F");
+ assertEncodeUtf8("\\xDF\\xBF", "\u07FF");
+ assertEncodeUtf8("\\xEF\\xBF\\xBF", "\uFFFF");
+ assertEncodeUtf8("\\x1F\\xEF\\xBF\\xBF", "\u001F\uFFFF");
+ assertEncodeUtf8("\\xCF\\xBF\\xEF\\xBF\\xBF", "\u03FF\uFFFF");
+ assertEncodeUtf8("\\xE7\\xBF\\xBF\\xEF\\xBF\\xBF", "\u7FFF\uFFFF");
+
+ // Other boundary conditions
+ assertEncodeUtf8("\\xED\\x9F\\xBF", "\uD7FF");
+ assertEncodeUtf8("\\xEE\\x80\\x80", "\uE000");
+ assertEncodeUtf8("\\xEF\\xBF\\xBD", "\uFFFD");
+ assertEncodeUtf8("\\x10\\xEF\\xBF\\xBF", "\u0010\uFFFF");
+ assertEncodeUtf8("\\x11\\x00", "\u0011\u0000");
+ }
+
+ @Test
+ public void requireThatUTf8CanBeDecoded() {
+ // First possible sequence of a certain length
+ assertDecodeUtf8("\u0000", "\\x00");
+ assertDecodeUtf8("\u0080", "\\xC2\\x80");
+ assertDecodeUtf8("\u0800", "\\xE0\\xA0\\x80");
+ assertDecodeUtf8("\u0001\u0000", "\\x01\\x00");
+ assertDecodeUtf8("\u0020\u0000", "\\x20\\x00");
+ assertDecodeUtf8("\u0400\u0000", "\\xD0\\x80\\x00");
+
+ // Last possible sequence of a certain length
+ assertDecodeUtf8("\u007F", "\\x7F");
+ assertDecodeUtf8("\u07FF", "\\xDF\\xBF");
+ assertDecodeUtf8("\uFFFF", "\\xEF\\xBF\\xBF");
+ assertDecodeUtf8("\u001F\uFFFF", "\\x1F\\xEF\\xBF\\xBF");
+ assertDecodeUtf8("\u03FF\uFFFF", "\\xCF\\xBF\\xEF\\xBF\\xBF");
+ assertDecodeUtf8("\u7FFF\uFFFF", "\\xE7\\xBF\\xBF\\xEF\\xBF\\xBF");
+
+ // Other boundary conditions
+ assertDecodeUtf8("\uD7FF", "\\xED\\x9F\\xBF");
+ assertDecodeUtf8("\uE000", "\\xEE\\x80\\x80");
+ assertDecodeUtf8("\uFFFD", "\\xEF\\xBF\\xBD");
+ assertDecodeUtf8("\u0010\uFFFF", "\\x10\\xEF\\xBF\\xBF");
+ assertDecodeUtf8("\u0011\u0000", "\\x11\\x00");
+ }
+
+ @Test
+ public void requireThatUnicodeCanBeEncoded() {
+ assertEncodeUtf8("\\xE4\\xB8\\x9C\\xE8\\xA5\\xBF\\xE8\\x87\\xAA\\xE8\\xA1\\x8C\\xE8\\xBD\\xA6",
+ "\u4E1C\u897F\u81EA\u884C\u8F66");
+ }
+
+ @Test
+ public void requireThatUnicodeCanBeDecoded() {
+ assertDecodeUtf8("\u4E1C\u897F\u81EA\u884C\u8F66",
+ "\\xE4\\xB8\\x9C\\xE8\\xA5\\xBF\\xE8\\x87\\xAA\\xE8\\xA1\\x8C\\xE8\\xBD\\xA6");
+ }
+
+ @Test
+ public void requireThatUnicodeIsAllowedInInputString() {
+ assertDecodeUtf8("\u4E1C\u897F\u81EA\u884C\u8F66",
+ "\u4E1C\u897F\u81EA\u884C\u8F66");
+ }
+
+ private static void assertEncodeUtf8(String expected, String str, int... requiresEscape) {
+ String actual = Ascii.encode(str, StandardCharsets.UTF_8, requiresEscape);
+ for (int i = 0; i < actual.length(); i += actual.offsetByCodePoints(i, 1)) {
+ int c = actual.codePointAt(i);
+ assertTrue(Integer.toHexString(c), c >= 0x20 && c <= 0x7F);
+ }
+ assertEquals(expected, actual);
+ }
+
+ private static void assertDecodeUtf8(String expected, String str) {
+ assertEquals(expected, Ascii.decode(str, StandardCharsets.UTF_8));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/Benchmark.java b/vespajlib/src/test/java/com/yahoo/text/Benchmark.java
new file mode 100644
index 00000000000..caa4b57c099
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/Benchmark.java
@@ -0,0 +1,106 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+// import com.google.common.base.Preconditions;
+// import com.google.inject.Provider;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+class Benchmark {
+
+ public static interface Task {
+ public long run(CyclicBarrier barrier, int numIterations) throws Exception;
+ }
+
+
+ public static class TaskProvider {
+ final Class<? extends Task> taskClass;
+ public Task get() {
+ try {
+ return taskClass.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ public TaskProvider(final Class<? extends Task> taskClass) {
+ this.taskClass = taskClass;
+ }
+ }
+
+ private final TaskProvider taskProvider;
+ private final int numIterationsPerThread;
+ private final int numThreads;
+
+ private Benchmark(Builder builder) {
+ Objects.requireNonNull(builder.taskProvider, "taskProvider");
+/*
+ Preconditions.checkArgument(builder.numIterationsPerThread > 0, "numIterationsPerThread; %s",
+ builder.numIterationsPerThread);
+ Preconditions.checkArgument(builder.numThreads > 0, "numThreads; %s",
+ builder.numThreads);
+*/
+ taskProvider = builder.taskProvider;
+ numIterationsPerThread = builder.numIterationsPerThread;
+ numThreads = builder.numThreads;
+ }
+
+ public long run() throws Exception {
+ final CyclicBarrier barrier = new CyclicBarrier(numThreads);
+ List<Callable<Long>> clients = new ArrayList<>(numThreads);
+ for (int i = 0; i < numThreads; ++i) {
+ final Task task = taskProvider.get();
+ clients.add(new Callable<Long>() {
+
+ @Override
+ public Long call() throws Exception {
+ return task.run(barrier, numIterationsPerThread);
+ }
+ });
+ }
+ long maxNanosPerClient = 0;
+ for (Future<Long> result : Executors.newFixedThreadPool(numThreads).invokeAll(clients)) {
+ maxNanosPerClient = Math.max(maxNanosPerClient, result.get());
+ }
+ return TimeUnit.SECONDS.toNanos(1) * numThreads * numIterationsPerThread / maxNanosPerClient;
+ }
+
+ public static class Builder {
+
+ private TaskProvider taskProvider;
+ private int numIterationsPerThread = 1000;
+ private int numThreads = 1;
+
+ public Builder setNumThreads(int numThreads) {
+ this.numThreads = numThreads;
+ return this;
+ }
+
+ public Builder setNumIterationsPerThread(int numIterationsPerThread) {
+ this.numIterationsPerThread = numIterationsPerThread;
+ return this;
+ }
+
+ public Builder setTaskClass(final Class<? extends Task> taskClass) {
+ return setTaskProvider(new TaskProvider(taskClass));
+ }
+
+ public Builder setTaskProvider(TaskProvider taskProvider) {
+ this.taskProvider = taskProvider;
+ return this;
+ }
+
+ public Benchmark build() {
+ return new Benchmark(this);
+ }
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/BooleanParserTestCase.java b/vespajlib/src/test/java/com/yahoo/text/BooleanParserTestCase.java
new file mode 100644
index 00000000000..756d0cd23ff
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/BooleanParserTestCase.java
@@ -0,0 +1,35 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * Control argument checking in BooleanParser.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class BooleanParserTestCase {
+
+ @Test
+ public final void testParseBoolean() {
+ boolean gotException = false;
+ try {
+ BooleanParser.parseBoolean(null);
+ } catch (final NullPointerException e) {
+ gotException = true;
+ }
+ assertTrue(gotException);
+ gotException = false;
+ try {
+ BooleanParser.parseBoolean("nalle");
+ } catch (final IllegalArgumentException e) {
+ gotException = true;
+ }
+ assertTrue(BooleanParser.parseBoolean("true"));
+ assertFalse(BooleanParser.parseBoolean("false"));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/CaseInsensitiveIdentifierTestCase.java b/vespajlib/src/test/java/com/yahoo/text/CaseInsensitiveIdentifierTestCase.java
new file mode 100644
index 00000000000..6c6b5b62506
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/CaseInsensitiveIdentifierTestCase.java
@@ -0,0 +1,46 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: balder
+ * Date: 11.11.12
+ * Time: 11:37
+ * To change this template use File | Settings | File Templates.
+ */
+public class CaseInsensitiveIdentifierTestCase {
+ @Test
+ public void testCaseInsentivitity() {
+ assertEquals(new CaseInsensitiveIdentifier("").toString(), "");
+ assertEquals(new CaseInsensitiveIdentifier("a").toString(), "a");
+ assertEquals(new CaseInsensitiveIdentifier("z").toString(), "z");
+ assertEquals(new CaseInsensitiveIdentifier("B").toString(), "B");
+ assertEquals(new CaseInsensitiveIdentifier("Z").toString(), "Z");
+ assertEquals(new CaseInsensitiveIdentifier("_").toString(), "_");
+ try {
+ assertEquals(new CaseInsensitiveIdentifier("0").toString(), "0");
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal starting character '0' of identifier '0'.");
+ }
+ try {
+ assertEquals(new CaseInsensitiveIdentifier("-").toString(), "-");
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal starting character '-' of identifier '-'.");
+ }
+ assertEquals(new CaseInsensitiveIdentifier("a0_9").toString(), "a0_9");
+ assertEquals(new Identifier("a9Z_").toString(), "a9Z_");
+ try {
+ assertEquals(new CaseInsensitiveIdentifier("a-b").toString(), "a-b");
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal character '-' of identifier 'a-b'.");
+ }
+ assertEquals(new CaseInsensitiveIdentifier("AbC"), new CaseInsensitiveIdentifier("ABC"));
+ assertEquals(new CaseInsensitiveIdentifier("AbC").hashCode(), new CaseInsensitiveIdentifier("ABC").hashCode());
+
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/DataTypeIdentifierTestCase.java b/vespajlib/src/test/java/com/yahoo/text/DataTypeIdentifierTestCase.java
new file mode 100644
index 00000000000..b79f65d9eb2
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/DataTypeIdentifierTestCase.java
@@ -0,0 +1,39 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: balder
+ * Date: 12.11.12
+ * Time: 08:10
+ * To change this template use File | Settings | File Templates.
+ */
+public class DataTypeIdentifierTestCase {
+ @Test
+ public void testDataTypeIdentifier() {
+ assertEquals("", new DataTypeIdentifier("").toString());
+ assertEquals("a", new DataTypeIdentifier("a").toString());
+ assertEquals("_", new DataTypeIdentifier("_").toString());
+ try {
+ assertEquals("aB", new DataTypeIdentifier("aB").toString());
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal character 'B' of identifier 'aB'.");
+ }
+ try {
+ assertEquals("1", new DataTypeIdentifier("1").toString());
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal starting character '1' of identifier '1'.");
+ }
+ assertEquals("a1", new DataTypeIdentifier("a1").toString());
+ assertEquals("array<b>", DataTypeIdentifier.createArrayDataTypeIdentifier(new DataTypeIdentifier("b")).toString());
+ assertEquals("weightedset<b>", DataTypeIdentifier.createWeightedSetTypeIdentifier(new DataTypeIdentifier("b"), false, false).toString());
+ assertEquals("weightedset<b>;add", DataTypeIdentifier.createWeightedSetTypeIdentifier(new DataTypeIdentifier("b"), true, false).toString());
+ assertEquals("weightedset<b>;remove", DataTypeIdentifier.createWeightedSetTypeIdentifier(new DataTypeIdentifier("b"), false, true).toString());
+ assertEquals("weightedset<b>;add;remove", DataTypeIdentifier.createWeightedSetTypeIdentifier(new DataTypeIdentifier("b"), true, true).toString());
+ assertEquals("annotationreference<b>", DataTypeIdentifier.createAnnotationReferenceDataTypeIdentifier(new DataTypeIdentifier("b")).toString());
+ assertEquals("map<k,v>", DataTypeIdentifier.createMapDataTypeIdentifier(new DataTypeIdentifier("k"), new DataTypeIdentifier("v")).toString());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/DoubleFormatterTestCase.java b/vespajlib/src/test/java/com/yahoo/text/DoubleFormatterTestCase.java
new file mode 100644
index 00000000000..21be58e5fd2
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/DoubleFormatterTestCase.java
@@ -0,0 +1,204 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author arnej27959
+ */
+public class DoubleFormatterTestCase {
+
+ @Test
+ public void testZero() {
+ String zero = DoubleFormatter.stringValue(0.0);
+ //assertEquals("0.0", zero);
+ }
+
+ @Test
+ public void testOne() {
+ String one = DoubleFormatter.stringValue(1.0);
+ assertEquals("1.0", one);
+ }
+
+ @Test
+ public void testMinusOne() {
+ String one = DoubleFormatter.stringValue(-1.0);
+ assertEquals("-1.0", one);
+ }
+
+ @Test
+ public void testNanInf() {
+ String plusInf = DoubleFormatter.stringValue(Double.POSITIVE_INFINITY);
+ assertEquals("Infinity", plusInf);
+
+ String notAnum = DoubleFormatter.stringValue(Double.NaN);
+ assertEquals("NaN", notAnum);
+
+ String negInf = DoubleFormatter.stringValue(Double.NEGATIVE_INFINITY);
+ assertEquals("-Infinity", negInf);
+ }
+
+ @Test
+ public void testSeven() {
+ String seven = DoubleFormatter.stringValue(7.0);
+ assertEquals("7.0", seven);
+
+ seven = DoubleFormatter.stringValue(77.0);
+ assertEquals("77.0", seven);
+
+ seven = DoubleFormatter.stringValue(7777.0);
+ assertEquals("7777.0", seven);
+
+ seven = DoubleFormatter.stringValue(7777007777.0);
+ assertEquals("7.777007777E9", seven);
+ }
+
+
+ @Test
+ public void testSomeChosenNumbers() {
+ String s = DoubleFormatter.stringValue(4097.0);
+ assertEquals("4097.0", s);
+
+ s = DoubleFormatter.stringValue(4097.5);
+ assertEquals("4097.5", s);
+
+ s = DoubleFormatter.stringValue(1073741823.0);
+ assertEquals("1.073741823E9", s);
+
+ s = DoubleFormatter.stringValue(1073741823.5);
+ assertEquals("1.0737418235E9", s);
+
+ s = DoubleFormatter.stringValue(1073741825.5);
+ assertEquals("1.0737418255E9", s);
+
+ s = DoubleFormatter.stringValue(1.23456789012345669);
+ assertEquals("1.234567890123457", s);
+ s = DoubleFormatter.stringValue(12.3456789012345673);
+ assertEquals("12.34567890123457", s);
+ s = DoubleFormatter.stringValue(123.456789012345666);
+ assertEquals("123.4567890123457", s);
+ s = DoubleFormatter.stringValue(1234.56789012345666);
+ assertEquals("1234.567890123457", s);
+ s = DoubleFormatter.stringValue(12345.6789012345670);
+ assertEquals("12345.67890123457", s);
+ s = DoubleFormatter.stringValue(123456.789012345674);
+ assertEquals("123456.7890123457", s);
+ s = DoubleFormatter.stringValue(1234567.89012345671);
+ assertEquals("1234567.890123457", s);
+
+ s = DoubleFormatter.stringValue(0.99);
+ // assertEquals("0.99", s);
+
+ s = DoubleFormatter.stringValue(0.5);
+ assertEquals("0.5", s);
+
+ s = DoubleFormatter.stringValue(0.1);
+ // assertEquals("0.1", s);
+
+ s = DoubleFormatter.stringValue(0.00123456789);
+ // assertEquals("0.00123456789", s);
+
+ s = DoubleFormatter.stringValue(0.0000000000001);
+ // assertEquals("0.0000000000001", s);
+ }
+
+ @Test
+ public void testPowersOfTwo() {
+ String twos = DoubleFormatter.stringValue(2.0);
+ assertEquals("2.0", twos);
+
+ twos = DoubleFormatter.stringValue(128.0);
+ assertEquals("128.0", twos);
+
+ twos = DoubleFormatter.stringValue(1048576.0);
+ assertEquals("1048576.0", twos);
+
+ twos = DoubleFormatter.stringValue(1073741824.0);
+ assertEquals("1.073741824E9", twos);
+ }
+
+ @Test
+ public void testSmallNumbers() {
+ for (double d = 1.0; d > 1.0e-200; d *= 0.75) {
+ String fs = DoubleFormatter.stringValue(d);
+ String vs = String.valueOf(d);
+ double rp = Double.valueOf(fs);
+ if (d != rp) {
+ // System.err.println("differs: "+d+" became "+fs+" then instead: "+rp+" diff: "+(d-rp));
+ } else if (! fs.equals(vs)) {
+ // System.err.println("string rep differs: "+vs+" became "+fs);
+ }
+ assertEquals(d, rp, 1.0e-7*d);
+ }
+ }
+
+ @Test
+ public void testVerySmallNumbers() {
+ for (double d = 1.0; d > 1.0e-200; d *= 0.5) {
+ String fs = DoubleFormatter.stringValue(d);
+ String vs = String.valueOf(d);
+ double rp = Double.valueOf(fs);
+ if (d != rp) {
+ // System.err.println("differs: "+d+" became "+fs+" then instead: "+rp+" diff: "+(d-rp));
+ } else if (! fs.equals(vs)) {
+ // System.err.println("string rep differs: "+vs+" became "+fs);
+ }
+ assertEquals(d, rp, 1.0e-13*d);
+ }
+ }
+
+ @Test
+ public void testVeryVerySmallNumbers() {
+ for (double d = 1.0e-200; d > 0; d *= 0.5) {
+ String fs = DoubleFormatter.stringValue(d);
+ String vs = String.valueOf(d);
+ double rp = Double.valueOf(fs);
+ if (d != rp) {
+ // System.err.println("differs: "+d+" became "+fs+" then instead: "+rp+" diff: "+(d-rp));
+ } else if (! fs.equals(vs)) {
+ // System.err.println("string rep differs: "+vs+" became "+fs);
+ }
+ assertEquals(d, rp, 1.0e-13*d);
+ }
+ }
+
+ @Test
+ public void testVeryBigNumbers() {
+ for (double d = 1.0; d < Double.POSITIVE_INFINITY; d *= 2.0) {
+ String fs = DoubleFormatter.stringValue(d);
+ String vs = String.valueOf(d);
+ double rp = Double.valueOf(fs);
+ if (d != rp) {
+ // System.err.println("differs: "+d+" became "+fs+" then instead: "+rp);
+ } else if (! fs.equals(vs)) {
+ // System.err.println("string rep differs: "+vs+" became "+fs);
+ }
+ assertEquals(d, rp, 1.0e-13*d);
+ }
+
+ assertEquals("1.0E200", String.valueOf(1.0e+200));
+
+ String big = DoubleFormatter.stringValue(1.0e+200);
+ assertEquals("1.0E200", big);
+
+ big = DoubleFormatter.stringValue(1.0e+298);
+ assertEquals("1.0E298", big);
+
+ big = DoubleFormatter.stringValue(1.0e+299);
+ assertEquals("1.0E299", big);
+
+ big = DoubleFormatter.stringValue(1.0e+300);
+ assertEquals("1.0E300", big);
+
+ }
+
+ @Test
+ public void testRandomNumbers() {
+ java.util.Random rgen = new java.util.Random(0xCafeBabe);
+ for (int i = 0; i < 123456; i++) {
+ double d = rgen.nextDouble();
+ }
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/DoubleParserTestCase.java b/vespajlib/src/test/java/com/yahoo/text/DoubleParserTestCase.java
new file mode 100644
index 00000000000..88e253e3425
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/DoubleParserTestCase.java
@@ -0,0 +1,158 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author arnej27959
+ */
+public class DoubleParserTestCase {
+
+ @Test
+ public void testZero() {
+ String[] zeros = {
+ "0",
+ "0.",
+ ".0",
+ "0.0",
+ "0.0e0",
+ "0.0e99",
+ "0.0e+300",
+ "0.0e-42"
+ };
+ for (String s : zeros) {
+ double d = DoubleParser.parse(s);
+ assertEquals(0.0, d, 0);
+ }
+ }
+
+ @Test
+ public void testOne() {
+ String[] ones = {
+ "1",
+ "1.",
+ "1.0",
+ "+1",
+ "10.0e-1",
+ "0.1e1",
+ "1000.0e-3",
+ ".001e+3",
+ };
+ for (String s : ones) {
+ System.out.println("parsing: '"+s+"' now");
+ double d = DoubleParser.parse(s);
+ System.out.println("expected: 1.0");
+ System.out.println("actual: "+d);
+ assertEquals(1.0, d, 0);
+ }
+ }
+
+ @Test
+ public void testMinusOne() {
+ String[] numbers = {
+ "-1",
+ "-1.0",
+ "-1.",
+ "-1e0",
+ "-10e-1",
+ };
+ for (String s : numbers) {
+ System.out.println("parsing: '"+s+"' now");
+ double d = DoubleParser.parse(s);
+ System.out.println("expected: -1.0");
+ System.out.println("actual: "+d);
+ assertEquals(-1.0, d, 0);
+ }
+ }
+
+ @Test
+ public void testNanInf() {
+ String[] numbers = {
+ "NaN",
+ "Infinity",
+ "-Infinity",
+ "+Infinity",
+ "+NaN",
+ "-NaN"
+ };
+ for (String s : numbers) {
+ System.out.println("parsing: '"+s+"' now");
+ double d1 = Double.parseDouble(s);
+ double d2 = DoubleParser.parse(s);
+ long lb1 = Double.doubleToRawLongBits(d1);
+ long lb2 = Double.doubleToRawLongBits(d2);
+ assertEquals(lb1, lb2, 0);
+ }
+ }
+
+ @Test
+ public void testSeven() {
+ String[] sevens = {
+ "7",
+ "7.",
+ "7.0",
+ "70.0e-1",
+ "0.7e1",
+ "7000.0e-3",
+ ".007e+3",
+ };
+ for (String s : sevens) {
+ System.out.println("parsing: '"+s+"' now");
+ double d = DoubleParser.parse(s);
+ System.out.println("expected: 7.0");
+ System.out.println("actual: "+d);
+ assertEquals(7.0, d, 0);
+ }
+ }
+
+ @Test
+ public void testVerySmallNumbers() {
+ String[] numbers = {
+ "1.e-320",
+ "-1.e-320",
+ "1.0013378241589014e-303"
+ };
+ for (String s : numbers) {
+ System.out.println("parsing: '"+s+"' now");
+ double d1 = Double.parseDouble(s);
+ double d2 = DoubleParser.parse(s);
+ System.out.println("expected: "+d1);
+ System.out.println("actual: "+d2);
+ assertEquals(d1, d2, 0);
+ }
+ }
+
+ @Test
+ public void testRandomNumbers() {
+ java.util.Random rgen = new java.util.Random(0xCafeBabe);
+ for (int i = 0; i < 123456; i++) {
+ double d = rgen.nextDouble();
+ int exp = rgen.nextInt();
+ d *= Math.pow(1.0000006, exp);
+ String s = Double.toString(d);
+ double d2 = Double.parseDouble(s);
+ double d3 = DoubleParser.parse(s);
+
+ if (d != d2) {
+ System.out.println("WARNING: value ["+d+"] parses as ["+d2+"] by Java");
+ }
+ double allow = 1.0e-14 * d2;
+ if (allow < 0) {
+ allow = -allow;
+ }
+ if (d2 != d3) {
+ long lb2 = Double.doubleToRawLongBits(d2);
+ long lb3 = Double.doubleToRawLongBits(d3);
+ if (lb2 - lb3 > 15 || lb3 - lb2 > 15) {
+ System.out.println("WARNING: string '"+s+"' parses as");
+ System.out.println("["+d2+"] by Java, ["+d3+"] by our method");
+ System.out.println("["+Long.toHexString(lb2)+"] bits vs ["+Long.toHexString(lb3)+"]");
+ System.out.println("==> "+(lb2 - lb3)+" <== diff value");
+ }
+ }
+ assertEquals(d2, d3, allow);
+ }
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/DoubleToStringBenchmark.java b/vespajlib/src/test/java/com/yahoo/text/DoubleToStringBenchmark.java
new file mode 100644
index 00000000000..2e0af153fc7
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/DoubleToStringBenchmark.java
@@ -0,0 +1,123 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author arnej27959
+ */
+public class DoubleToStringBenchmark {
+
+ @Test
+ @Ignore
+ public void benchmarkStringConstruction() throws Exception {
+ List<Class<? extends Benchmark.Task>> taskList = Arrays.asList(UseStringValueOf.class,
+ UseDoubleFormatter.class,
+ UseDoubleFormatter.class,
+ UseStringValueOf.class,
+ UseStringValueOf.class,
+ UseDoubleFormatter.class,
+ UseDoubleFormatter.class,
+ UseStringValueOf.class,
+ UseDoubleFormatter.class,
+ UseStringValueOf.class);
+
+ int maxThreads = 256;
+ int dummy = 0;
+ System.out.print("warmup");
+ for (Class<? extends Benchmark.Task> taskClass : taskList) {
+ dummy += runBenchmark(maxThreads, taskClass);
+ System.out.print(".");
+ }
+ System.out.println(" " + dummy);
+
+ System.out.format("%-35s", "");
+ for (int numThreads = 1; numThreads <= maxThreads; numThreads *= 2) {
+ System.out.format("%13s t ", numThreads);
+ }
+ System.out.println();
+ for (Class<? extends Benchmark.Task> taskClass : taskList) {
+ System.out.format("%-35s", taskClass.getSimpleName());
+ for (int numThreads = 1; numThreads <= maxThreads; numThreads *= 2) {
+ System.out.format("%15d ", runBenchmark(numThreads, taskClass));
+ }
+ System.out.println();
+ }
+ }
+
+ private long runBenchmark(int numThreads, Class<? extends Benchmark.Task> taskClass) throws Exception {
+ return new Benchmark.Builder()
+ .setNumIterationsPerThread(80000)
+ .setNumThreads(numThreads)
+ .setTaskClass(taskClass)
+ .build()
+ .run();
+ }
+
+ public static class UseStringValueOf implements Benchmark.Task {
+
+ private long timeIt(Random randomGen, int num) {
+ long before = System.nanoTime();
+
+ String str = null;
+ double v = 0.0;
+ for (int i = 0; i < num; ++i) {
+ v = randomGen.nextDouble() * 1.0e-2;
+ str = String.valueOf(v);
+ }
+
+ long after = System.nanoTime();
+ assertEquals(""+v, str);
+ return after - before;
+ }
+
+ @Override
+ public long run(CyclicBarrier barrier, int numIterations) throws Exception {
+ Random randomGen = new Random(0xDeadBeef);
+ barrier.await(600, TimeUnit.SECONDS);
+ long t1 = timeIt(randomGen, numIterations / 4);
+ long t2 = timeIt(randomGen, numIterations / 2);
+ long t3 = timeIt(randomGen, numIterations / 4);
+ return t2;
+ }
+ }
+
+ public static class UseDoubleFormatter implements Benchmark.Task {
+
+ private long timeIt(Random randomGen, int num) {
+ long before = System.nanoTime();
+
+ String str = null;
+ double v = 0.0;
+ for (int i = 0; i < num; ++i) {
+ v = randomGen.nextDouble() * 1.0e-2;
+ str = DoubleFormatter.stringValue(v);
+ }
+
+ long after = System.nanoTime();
+ // assertEquals(""+v, str);
+ return after - before;
+ }
+
+
+ @Override
+ public long run(CyclicBarrier barrier, int numIterations) throws Exception {
+ Random randomGen = new Random(0xDeadBeef);
+ barrier.await(600, TimeUnit.SECONDS);
+ long t1 = timeIt(randomGen, numIterations / 4);
+ long t2 = timeIt(randomGen, numIterations / 2);
+ long t3 = timeIt(randomGen, numIterations / 4);
+ return t2;
+ }
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/ForwardWriterTestCase.java b/vespajlib/src/test/java/com/yahoo/text/ForwardWriterTestCase.java
new file mode 100644
index 00000000000..6984c2ec6a8
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/ForwardWriterTestCase.java
@@ -0,0 +1,435 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.yahoo.protect.ClassValidator;
+
+/**
+ * Check all methods forward correctly and wrap exceptions as documented.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ *
+ */
+public class ForwardWriterTestCase {
+ private static final String WRITE_ABSTRACT_UTF8_ARRAY = "write(AbstractUtf8Array)";
+ private static final String WRITE_BOOLEAN = "write(boolean)";
+ private static final String WRITE_CHAR = "write(char)";
+ private static final String WRITE_DOUBLE = "write(double)";
+ private static final String WRITE_FLOAT = "write(float)";
+ private static final String WRITE_CHAR_SEQUENCE = "write(CharSequence)";
+ private static final String WRITE_CHAR_INT_INT = "write(char[], int, int)";
+ private static final String FLUSH = "flush()";
+ private static final String CLOSE = "close()";
+ private static final String WRITE_STRING = "write(String)";
+ private static final String WRITE_LONG = "write(long)";
+ private static final String WRITE_SHORT = "write(short)";
+ private static final String WRITE_BYTE = "write(byte)";
+
+ private static class Boom extends GenericWriter {
+ @Override
+ public GenericWriter write(final char c) throws IOException {
+ method(WRITE_CHAR);
+ final GenericWriter w = super.write(c);
+ explode();
+ return w;
+ }
+
+ @Override
+ public GenericWriter write(final CharSequence s) throws IOException {
+ method(WRITE_CHAR_SEQUENCE);
+ final GenericWriter w = super.write(s);
+ explode();
+ return w;
+ }
+
+ @Override
+ public void write(final String s) throws IOException {
+ method(WRITE_STRING);
+ super.write(s);
+ explode();
+ }
+
+ @Override
+ public GenericWriter write(final long i) throws IOException {
+ method(WRITE_LONG);
+ final GenericWriter w = super.write(i);
+ explode();
+ return w;
+ }
+
+ @Override
+ public void write(final int i) throws IOException {
+ method("write(int)");
+ super.write(i);
+ explode();
+ }
+
+ @Override
+ public GenericWriter write(final short i) throws IOException {
+ method(WRITE_SHORT);
+ final GenericWriter w = super.write(i);
+ explode();
+ return w;
+ }
+
+ @Override
+ public GenericWriter write(final byte i) throws IOException {
+ method(WRITE_BYTE);
+ final GenericWriter w = super.write(i);
+ explode();
+ return w;
+ }
+
+ @Override
+ public GenericWriter write(final double i) throws IOException {
+ method(WRITE_DOUBLE);
+ final GenericWriter w = super.write(i);
+ explode();
+ return w;
+ }
+
+ @Override
+ public GenericWriter write(final float i) throws IOException {
+ method(WRITE_FLOAT);
+ final GenericWriter w = super.write(i);
+ explode();
+ return w;
+ }
+
+ @Override
+ public GenericWriter write(final boolean i) throws IOException {
+ method(WRITE_BOOLEAN);
+ final GenericWriter w = super.write(i);
+ explode();
+ return w;
+ }
+
+ @Override
+ public GenericWriter write(final AbstractUtf8Array v)
+ throws IOException {
+ method(WRITE_ABSTRACT_UTF8_ARRAY);
+ final GenericWriter w = super.write(v);
+ explode();
+ return w;
+ }
+
+ StringBuilder last = new StringBuilder();
+ private boolean explode = false;
+ private boolean toplevel;
+ private String method;
+
+ @Override
+ public void write(final char[] cbuf, final int off, final int len)
+ throws IOException {
+ method(WRITE_CHAR_INT_INT);
+ last.append(cbuf, off, len);
+ explode();
+ }
+
+ @Override
+ public void flush() throws IOException {
+ method(FLUSH);
+ explode();
+
+ }
+
+ @Override
+ public void close() throws IOException {
+ method(CLOSE);
+ explode();
+ }
+
+ private void method(final String method) {
+ if (toplevel) {
+ this.method = method;
+ toplevel = false;
+ }
+ }
+
+ private void explode() throws IOException {
+ if (explode) {
+ throw new IOException(method);
+ }
+ }
+
+ void arm() {
+ explode = true;
+ toplevel = true;
+ }
+ }
+
+ private Boom wrapped;
+ private ForwardWriter forward;
+ private boolean gotException;
+
+ @Before
+ public void setUp() throws Exception {
+ wrapped = new Boom();
+ forward = new ForwardWriter(wrapped);
+ gotException = false;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void requireForwardWriterIsComplete() {
+ final List<Method> methods = ClassValidator
+ .unmaskedMethodsFromSuperclass(ForwardWriter.class);
+ assertEquals(0, methods.size());
+ }
+
+ @Test
+ public final void testWriteInt() {
+ forward.write(0x1ECD);
+ assertEquals("\u1ECD", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ forward.write(0);
+ } catch (final RuntimeException e) {
+ assertEquals("write(int)", e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ @Test
+ public final void testWriteCharArrayIntInt() {
+ writeCharArrayIntInt();
+ assertEquals("0", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeCharArrayIntInt();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_CHAR_INT_INT, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ public void writeCharArrayIntInt() {
+ forward.write(new char[] { '0' }, 0, 1);
+ }
+
+ @Test
+ public final void testFlush() {
+ wrapped.arm();
+ try {
+ forward.flush();
+ } catch (final RuntimeException e) {
+ assertEquals(FLUSH, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ @Test
+ public final void testClose() {
+ wrapped.arm();
+ try {
+ forward.close();
+ } catch (final RuntimeException e) {
+ assertEquals(CLOSE, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ @Test
+ public final void testWriteString() {
+ writeString();
+ assertEquals("0", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeString();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_STRING, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ public void writeString() {
+ forward.write("0");
+ }
+
+ @Test
+ public final void testWriteCharSequence() {
+ writeCharSequence();
+ assertEquals("0", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeCharSequence();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_CHAR_SEQUENCE, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ public void writeCharSequence() {
+ forward.write((CharSequence) "0");
+ }
+
+ @Test
+ public final void testWriteLong() {
+ writeLong();
+ assertEquals("0", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeLong();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_LONG, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ public void writeLong() {
+ forward.write((long) 0);
+ }
+
+ @Test
+ public final void testWriteFloat() {
+ writeFloat();
+ assertEquals("0.0", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeFloat();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_FLOAT, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ public void writeFloat() {
+ forward.write(0.0f);
+ }
+
+ @Test
+ public final void testWriteDouble() {
+ writeDouble();
+ assertEquals("0.0", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeDouble();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_DOUBLE, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ public void writeDouble() {
+ forward.write(0.0d);
+ }
+
+ @Test
+ public final void testWriteShort() {
+ writeShort();
+ assertEquals("0", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeShort();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_SHORT, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ public void writeShort() {
+ forward.write((short) 0);
+ }
+
+ @Test
+ public final void testWriteChar() {
+ writeChar();
+ assertEquals("0", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeChar();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_CHAR, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ public void writeChar() {
+ forward.write('0');
+ }
+
+ @Test
+ public final void testWriteByte() {
+ writeByte();
+ assertEquals("0", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeByte();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_BYTE, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ public void writeByte() {
+ forward.write((byte) 0);
+ }
+
+ @Test
+ public final void testWriteBoolean() {
+ writeBoolean();
+ assertEquals("true", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeBoolean();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_BOOLEAN, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ public void writeBoolean() {
+ forward.write(true);
+ }
+
+ @Test
+ public final void testWriteAbstractUtf8Array() {
+ writeUtf8Array();
+ assertEquals("0", wrapped.last.toString());
+ wrapped.arm();
+ try {
+ writeUtf8Array();
+ } catch (final RuntimeException e) {
+ assertEquals(WRITE_ABSTRACT_UTF8_ARRAY, e.getCause().getMessage());
+ gotException = true;
+ }
+ assertTrue(gotException);
+
+ }
+
+ public void writeUtf8Array() {
+ forward.write(new Utf8Array(Utf8.toBytes("0")));
+ }
+
+ @Test
+ public final void testGetWriter() {
+ assertSame(wrapped, forward.getWriter());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/GenericWriterTestCase.java b/vespajlib/src/test/java/com/yahoo/text/GenericWriterTestCase.java
new file mode 100644
index 00000000000..619065ff7bf
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/GenericWriterTestCase.java
@@ -0,0 +1,107 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Completeness check for GenericWriter.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class GenericWriterTestCase {
+ private static class MockWriter extends GenericWriter {
+ private StringBuilder written = new StringBuilder();
+
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ written.append(String.copyValueOf(cbuf, off, len));
+ }
+
+ @Override
+ public void flush() throws IOException {
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ }
+
+ MockWriter mock;
+
+ @Before
+ public void setUp() throws Exception {
+ mock = new MockWriter();
+ }
+
+ @Test
+ public final void testWriteInt() throws Exception {
+ mock.write(0xa);
+ assertEquals("\n", mock.written.toString());
+ }
+
+ @Test
+ public final void testWriteChar() throws IOException {
+ mock.write('\u0020');
+ assertEquals(" ", mock.written.toString());
+ }
+
+ @Test
+ public final void testWriteCharSequence() throws IOException {
+ mock.write((CharSequence) "abc");
+ assertEquals("abc", mock.written.toString());
+ }
+
+ @Test
+ public final void testWriteString() throws IOException {
+ mock.write("abc");
+ assertEquals("abc", mock.written.toString());
+ }
+
+ @Test
+ public final void testWriteLong() throws IOException {
+ mock.write(42L);
+ assertEquals("42", mock.written.toString());
+ }
+
+ @Test
+ public final void testWriteShort() throws IOException {
+ mock.write((short) 42);
+ assertEquals("42", mock.written.toString());
+ }
+
+ @Test
+ public final void testWriteByte() throws IOException {
+ mock.write((byte) 42);
+ assertEquals("42", mock.written.toString());
+ }
+
+ @Test
+ public final void testWriteDouble() throws IOException {
+ mock.write(0.0d);
+ assertEquals("0.0", mock.written.toString());
+ }
+
+ @Test
+ public final void testWriteFloat() throws IOException {
+ mock.write(0.0f);
+ assertEquals("0.0", mock.written.toString());
+ }
+
+ @Test
+ public final void testWriteBoolean() throws IOException {
+ mock.write(true);
+ assertEquals("true", mock.written.toString());
+ }
+
+ @Test
+ public final void testWriteAbstractUtf8Array() throws IOException {
+ mock.write(new Utf8Array(Utf8.toBytes("abc")));
+ assertEquals("abc", mock.written.toString());
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/HTMLTestCase.java b/vespajlib/src/test/java/com/yahoo/text/HTMLTestCase.java
new file mode 100644
index 00000000000..2eab7d42aed
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/HTMLTestCase.java
@@ -0,0 +1,49 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * @author Bjorn Borud
+ */
+public class HTMLTestCase {
+
+ @Test
+ public void testSimpleEscape() {
+ assertEquals("&quot;this &lt;&amp;&gt; that&quot;",
+ HTML.htmlescape("\"this <&> that\""));
+ }
+
+ @Test
+ public void testBunchOfEscapes() {
+ assertEquals(
+ "&copy;&reg;&Agrave;&Aacute;&Acirc;&Atilde;&Auml;&Aring;&AElig;&Ccedil;",
+ HTML.htmlescape("\u00A9\u00AE\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7"));
+
+ assertEquals(
+ "&Egrave;&Eacute;&Ecirc;&Euml;&Igrave;&Iacute;&Icirc;&Iuml;&ETH;&Ntilde;",
+ HTML.htmlescape("\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1"));
+
+ assertEquals(
+ "&Ograve;&Oacute;&Ocirc;&Otilde;&Ouml;&Oslash;&Ugrave;&Uacute;&Ucirc;&Uuml;",
+ HTML.htmlescape("\u00D2\u00D3\u00D4\u00D5\u00D6\u00D8\u00D9\u00DA\u00DB\u00DC"));
+
+ assertEquals(
+ "&Yacute;&THORN;&szlig;&agrave;&aacute;&acirc;&atilde;&auml;&aring;&aelig;",
+ HTML.htmlescape("\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6"));
+
+ assertEquals(
+ "&ccedil;&egrave;&eacute;&ecirc;&euml;&igrave;&iacute;&icirc;&iuml;&igrave;",
+ HTML.htmlescape("\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00EC"));
+
+ assertEquals(
+ "&iacute;&icirc;&iuml;&eth;&ntilde;&ograve;&oacute;&ocirc;&otilde;&ouml;",
+ HTML.htmlescape("\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6"));
+
+ assertEquals(
+ "&oslash;&ugrave;&uacute;&ucirc;&uuml;&yacute;&thorn;&yuml;",
+ HTML.htmlescape("\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF"));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/IdentifierTestCase.java b/vespajlib/src/test/java/com/yahoo/text/IdentifierTestCase.java
new file mode 100644
index 00000000000..447b109983e
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/IdentifierTestCase.java
@@ -0,0 +1,42 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: balder
+ * Date: 11.11.12
+ * Time: 10:58
+ * To change this template use File | Settings | File Templates.
+ */
+public class IdentifierTestCase {
+ @Test
+ public void testIdentifier() {
+ assertEquals(new Identifier("").toString(), "");
+ assertEquals(new Identifier("a").toString(), "a");
+ assertEquals(new Identifier("z").toString(), "z");
+ assertEquals(new Identifier("B").toString(), "B");
+ assertEquals(new Identifier("Z").toString(), "Z");
+ assertEquals(new Identifier("_").toString(), "_");
+ try {
+ assertEquals(new Identifier("0").toString(), "0");
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal starting character '0' of identifier '0'.");
+ }
+ try {
+ assertEquals(new Identifier("-").toString(), "-");
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal starting character '-' of identifier '-'.");
+ }
+ assertEquals(new Identifier("a0_9").toString(), "a0_9");
+ assertEquals(new Identifier("a9Z_").toString(), "a9Z_");
+ try {
+ assertEquals(new Identifier("a-b").toString(), "a-b");
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal character '-' of identifier 'a-b'.");
+ }
+
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/JSONTest.java b/vespajlib/src/test/java/com/yahoo/text/JSONTest.java
new file mode 100644
index 00000000000..53be5a1bda5
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/JSONTest.java
@@ -0,0 +1,25 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class JSONTest {
+
+ @Test
+ public void testMapToString() {
+ Map<String,Object> map = new LinkedHashMap<>();
+ map.put("a \"key\"", 3);
+ map.put("key2", "value");
+ map.put("key3", 3.3);
+
+ assertEquals("{\"a \\\"key\\\"\":3,\"key2\":\"value\",\"key3\":3.3}", JSON.encode(map));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/JSONWriterTestCase.java b/vespajlib/src/test/java/com/yahoo/text/JSONWriterTestCase.java
new file mode 100644
index 00000000000..16d9fe65769
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/JSONWriterTestCase.java
@@ -0,0 +1,114 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests the JSON writer
+ *
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+@SuppressWarnings("deprecation")
+public class JSONWriterTestCase {
+
+ @Test
+ public void testJSONWriter() throws IOException {
+ OutputStream out = new ByteArrayOutputStream();
+ JSONWriter w = new JSONWriter(out);
+
+ w.beginObject();
+
+ w.beginField("string").value("a string").endField();
+ w.beginField("number").value(37).endField();
+ w.beginField("true").value(true).endField();
+ w.beginField("false").value(false).endField();
+ w.beginField("null").value().endField();
+
+ w.beginField("object").beginObject();
+ w.beginField("nested-array").beginArray().beginArrayValue().value(1).endArrayValue().endArray().endField();
+ w.endObject().endField();
+
+ w.beginField("array").beginArray();
+ w.beginArrayValue().value("item1").endArrayValue();
+ w.beginArrayValue().value("item2").endArrayValue();
+ w.beginArrayValue().beginObject().beginField("nested").value("item3").endField().endObject().endArrayValue();
+ w.endArray().endField();
+
+ w.endObject();
+
+ assertEquals("{\"string\":\"a string\"," +
+ "\"number\":37," +
+ "\"true\":true," +
+ "\"false\":false," +
+ "\"null\":null," +
+ "\"object\":{\"nested-array\":[1]}," +
+ "\"array\":[\"item1\",\"item2\",{\"nested\":\"item3\"}]}",
+ out.toString());
+ }
+
+ @Test
+ public void testJSONWriterEmptyObject() throws IOException {
+ OutputStream out = new ByteArrayOutputStream();
+ JSONWriter w = new JSONWriter(out);
+ w.beginObject();
+ w.endObject();
+
+ assertEquals("{}",out.toString());
+ }
+
+ @Test
+ public void testJSONWriterEmptyArray() throws IOException {
+ OutputStream out = new ByteArrayOutputStream();
+ JSONWriter w = new JSONWriter(out);
+ w.beginArray();
+ w.endArray();
+
+ assertEquals("[]",out.toString());
+ }
+
+ @Test
+ public void testJSONWriterStringOnly() throws IOException {
+ OutputStream out = new ByteArrayOutputStream();
+ JSONWriter w = new JSONWriter(out);
+ w.value("Hello, world!");
+
+ assertEquals("\"Hello, world!\"",out.toString());
+ }
+
+ @Test
+ public void testJSONWriterNestedArrays() throws IOException {
+ OutputStream out = new ByteArrayOutputStream();
+ JSONWriter w = new JSONWriter(out);
+ w.beginArray();
+
+ w.beginArrayValue().beginArray();
+ w.endArray().endArrayValue();
+
+ w.beginArrayValue().beginArray();
+ w.beginArrayValue().value("hello").endArrayValue();
+ w.beginArrayValue().value("world").endArrayValue();
+ w.endArray().endArrayValue();
+
+ w.beginArrayValue().beginArray();
+ w.endArray().endArrayValue();
+
+ w.beginArrayValue().beginArray();
+ w.beginArrayValue().beginArray();
+ w.endArray().endArrayValue();
+ w.endArray().endArrayValue();
+
+ w.beginArrayValue().beginArray();
+ w.endArray().endArrayValue();
+
+ w.endArray();
+
+ assertEquals("[[],[\"hello\",\"world\"],[],[[]],[]]",out.toString());
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/JsonMicroBenchmarkTestCase.java b/vespajlib/src/test/java/com/yahoo/text/JsonMicroBenchmarkTestCase.java
new file mode 100644
index 00000000000..c2c1774ca1d
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/JsonMicroBenchmarkTestCase.java
@@ -0,0 +1,563 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.TreeMap;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+@SuppressWarnings("deprecation")
+public class JsonMicroBenchmarkTestCase {
+
+ private static final long RUNTIME = 20L * 60L * 1000L;
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ enum Strategy {
+ VESPAJLIB, JACKSON;
+ }
+
+ private static abstract class BenchFactory {
+ abstract Bench produce();
+ }
+
+ private static class VespajlibFactory extends BenchFactory {
+
+ @Override
+ Bench produce() {
+ return new OutputWithWriter();
+ }
+
+ }
+
+ private static class JacksonFactory extends BenchFactory {
+ @Override
+ Bench produce() {
+ return new OutputWithGenerator();
+ }
+ }
+
+ private static abstract class Bench implements Runnable {
+ public volatile long runs;
+ public volatile long start;
+ public volatile long end;
+ public volatile long metric;
+
+ /**
+ * Object identity is used to differentiate between different implementation strategies, toString() is used to print a report.
+ *
+ * @return an object with a descriptive toString() for the implementation under test
+ */
+ abstract Object category();
+
+ @Override
+ public final void run() {
+ Random random = new Random(42L);
+ long localBytesWritten = 0L;
+ long localRuns = 0;
+
+ start = System.currentTimeMillis();
+ long target = start + JsonMicroBenchmarkTestCase.RUNTIME;
+
+ while (System.currentTimeMillis() < target) {
+ for (int i = 0; i < 1000; ++i) {
+ localBytesWritten += iterate(random);
+ }
+ localRuns += 1000L;
+ }
+ end = System.currentTimeMillis();
+ runs = localRuns;
+ metric = localBytesWritten;
+ }
+
+ abstract int iterate(Random random);
+ }
+
+ private static final class OutputWithGenerator extends Bench {
+
+ public OutputWithGenerator() {
+ }
+
+
+ int iterate(Random random) {
+ JsonGenerator generator;
+ ByteArrayOutputStream generatorOut = new ByteArrayOutputStream();
+ try {
+ generator = new JsonFactory().createJsonGenerator(generatorOut,
+ JsonEncoding.UTF8);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ try {
+ serialize(generatedDoc(random), generator);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ try {
+ generator.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ return generatorOut.toByteArray().length;
+ }
+
+ static void serialize(Map<String, Object> m, JsonGenerator g) throws IOException {
+ g.writeStartObject();
+ for (Map.Entry<String, Object> e : m.entrySet()) {
+ g.writeFieldName(e.getKey());
+ serializeField(g, e.getValue());
+ }
+ g.writeEndObject();
+ }
+
+ @SuppressWarnings("unchecked")
+ static void serializeField(JsonGenerator g, final Object value)
+ throws IOException {
+ if (value instanceof Map) {
+ serialize((Map<String, Object>) value, g);
+ } else if (value instanceof Number) {
+ g.writeNumber(((Number) value).intValue());
+ } else if (value instanceof String) {
+ g.writeString((String) value);
+ } else if (value instanceof List) {
+ g.writeStartArray();
+ for (Object o : (List<Object>) value) {
+ serializeField(g, o);
+ }
+ g.writeEndArray();
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ @Override
+ Object category() {
+ return Strategy.JACKSON;
+ }
+
+ }
+
+ private static final class OutputWithWriter extends Bench {
+
+ OutputWithWriter() {
+ }
+
+ int iterate(Random random) {
+ ByteArrayOutputStream writerOut = new ByteArrayOutputStream();
+ JSONWriter writer = new JSONWriter(writerOut);
+ try {
+ serialize(generatedDoc(random), writer);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ return writerOut.toByteArray().length;
+ }
+
+ static void serialize(Map<String, Object> m, JSONWriter w) throws IOException {
+ w.beginObject();
+ for (Map.Entry<String, Object> e : m.entrySet()) {
+ w.beginField(e.getKey());
+ final Object value = e.getValue();
+ serializeField(w, value);
+ w.endField();
+ }
+ w.endObject();
+ }
+
+ @SuppressWarnings("unchecked")
+ static void serializeField(JSONWriter w, final Object value)
+ throws IOException {
+ if (value instanceof Map) {
+ serialize((Map<String, Object>) value, w);
+ } else if (value instanceof Number) {
+ w.value((Number) value);
+ } else if (value instanceof String) {
+ w.value((String) value);
+ } else if (value instanceof List) {
+ w.beginArray();
+ for (Object o : (List<Object>) value) {
+ w.beginArrayValue();
+ serializeField(w, o);
+ w.endArrayValue();
+ }
+ w.endArray();
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ @Override
+ Object category() {
+ return Strategy.VESPAJLIB;
+ }
+
+ }
+
+ @Test
+ @Ignore
+ public final void test() throws InterruptedException {
+ final OutputWithWriter forWriter = new OutputWithWriter();
+ Thread writerThread = new Thread(forWriter);
+ final OutputWithGenerator forGenerator = new OutputWithGenerator();
+ Thread generatorThread = new Thread(forGenerator);
+ writerThread.start();
+ generatorThread.start();
+ writerThread.join();
+ generatorThread.join();
+ System.out.println("Generator time: " + (forGenerator.end - forGenerator.start));
+ System.out.println("Writer time: " + (forWriter.end - forWriter.start));
+ System.out.println("Output length from generator: " + forGenerator.metric);
+ System.out.println("Output length from writer: " + forWriter.metric);
+ System.out.println("Iterations with generator: " + forGenerator.runs);
+ System.out.println("Iterations with writer: " + forWriter.runs);
+ System.out.println("Iterations/s with generator: " + ((double) forGenerator.runs / (double) (forGenerator.end - forGenerator.start)) * 1000.0d);
+ System.out.println("Iterations/s with writer: " + ((double) forWriter.runs / (double) (forWriter.end - forWriter.start)) * 1000.0d);
+ }
+
+ @Test
+ @Ignore
+ public final void test16Threads() throws InterruptedException {
+ List<Thread> threads = new ArrayList<>(16);
+ List<Bench> benches = createBenches(8, new VespajlibFactory(), new JacksonFactory());
+
+ for (Bench bench : benches) {
+ threads.add(new Thread(bench));
+ }
+ for (Thread t : threads) {
+ t.start();
+ }
+ for (Thread t : threads) {
+ t.join();
+ }
+
+ System.out.println("8 Jackson threads competing with 8 VespaJLib threads.");
+ metrics(benches, Strategy.JACKSON);
+ metrics(benches, Strategy.VESPAJLIB);
+ }
+
+ @Test
+ @Ignore
+ public final void test16ThreadsJacksonOnly() throws InterruptedException {
+ List<Thread> threads = new ArrayList<>(16);
+ List<Bench> benches = createBenches(16, new JacksonFactory());
+
+ for (Bench bench : benches) {
+ threads.add(new Thread(bench));
+ }
+ for (Thread t : threads) {
+ t.start();
+ }
+ for (Thread t : threads) {
+ t.join();
+ }
+
+ System.out.println("16 Jackson threads.");
+ metrics(benches, Strategy.JACKSON);
+ }
+
+ @Test
+ @Ignore
+ public final void test16ThreadsVespaJlibOnly() throws InterruptedException {
+ List<Thread> threads = new ArrayList<>(16);
+ List<Bench> benches = createBenches(16, new VespajlibFactory());
+
+ for (Bench bench : benches) {
+ threads.add(new Thread(bench));
+ }
+ for (Thread t : threads) {
+ t.start();
+ }
+ for (Thread t : threads) {
+ t.join();
+ }
+
+ System.out.println("16 VespaJLib threads.");
+ metrics(benches, Strategy.VESPAJLIB);
+ }
+
+
+ private void metrics(List<Bench> benches, Strategy choice) {
+ List<Bench> chosen = new ArrayList<>();
+
+ for (Bench b : benches) {
+ if (b.category() == choice) {
+ chosen.add(b);
+ }
+ }
+
+ long[] rawTime = new long[chosen.size()];
+ long[] rawOutputLength = new long[chosen.size()];
+ long[] rawIterations = new long[chosen.size()];
+ double[] rawIterationsPerSecond = new double[chosen.size()];
+
+ for (int i = 0; i < chosen.size(); ++i) {
+ Bench b = chosen.get(i);
+ rawTime[i] = b.end - b.start;
+ rawOutputLength[i] = b.metric;
+ rawIterations[i] = b.runs;
+ rawIterationsPerSecond[i] = ((double) b.runs) / (((double) (b.end - b.start)) / 1000.0d);
+ }
+
+ double avgTime = mean(rawTime);
+ double avgOutputLength = mean(rawOutputLength);
+ double avgIterations = mean(rawIterations);
+ double avgIterationsPerSecond = mean(rawIterationsPerSecond);
+
+ System.out.println("For " + choice + ":");
+ dumpMetric("run time", rawTime, avgTime, "s", 0.001d);
+ dumpMetric("output length", rawOutputLength, avgOutputLength, "bytes", 1.0d);
+ dumpMetric("iterations", rawIterations, avgIterations, "", 1.0d);
+ dumpMetric("iterations per second", rawIterationsPerSecond, avgIterationsPerSecond, "s**-1", 1.0d);
+ }
+
+ private void dumpMetric(String name, long[] raw, double mean, String unit, double scale) {
+ System.out.println("Average " + name + ": " + mean * scale + " " + unit);
+ System.out.println("Mean absolute deviation of " + name + ": " + averageAbsoluteDeviationFromMean(raw, mean) * scale + " " + unit);
+ System.out.println("Minimum " + name + ": " + min(raw) * scale + " " + unit);
+ System.out.println("Maximum " + name + ": " + max(raw) * scale + " " + unit);
+ }
+
+ private void dumpMetric(String name, double[] raw, double mean, String unit, double scale) {
+ System.out.println("Average " + name + ": " + mean * scale + " " + unit);
+ System.out.println("Mean absolute deviation of " + name + ": " + averageAbsoluteDeviationFromMean(raw, mean) * scale + " " + unit);
+ System.out.println("Minimum " + name + ": " + min(raw) * scale + " " + unit);
+ System.out.println("Maximum " + name + ": " + max(raw) * scale + " " + unit);
+ }
+
+ private List<Bench> createBenches(int ofEach, BenchFactory... factories) {
+ List<Bench> l = new ArrayList<>(ofEach * factories.length);
+
+ // note how the bench objects of different objects become intermingled, this is by design
+ for (int i = 0; i < ofEach; ++i) {
+ for (BenchFactory factory : factories) {
+ l.add(factory.produce());
+ }
+ }
+ return l;
+ }
+
+ private double mean(long[] values) {
+ long sum = 0L;
+
+ // ignore overflow :)
+ for (long v : values) {
+ sum += v;
+ }
+ return ((double) sum / (double) values.length);
+ }
+
+ private double mean(double[] values) {
+ double sum = 0L;
+
+ for (double v : values) {
+ sum += v;
+ }
+ return sum / (double) values.length;
+ }
+
+ private double averageAbsoluteDeviationFromMean(long[] values, double mean) {
+ double sum = 0.0d;
+
+ for (long v : values) {
+ sum += Math.abs(mean - (double) v);
+ }
+
+ return sum / (double) values.length;
+ }
+
+ private double averageAbsoluteDeviationFromMean(double[] values, double mean) {
+ double sum = 0.0d;
+
+ for (double v : values) {
+ sum += Math.abs(mean - v);
+ }
+
+ return sum / (double) values.length;
+ }
+
+ private long min(long[] values) {
+ long min = Long.MAX_VALUE;
+
+ for (long v : values) {
+ min = Math.min(min, v);
+ }
+ return min;
+ }
+
+ private double min(double[] values) {
+ double min = Double.MAX_VALUE;
+
+ for (double v : values) {
+ min = Math.min(min, v);
+ }
+ return min;
+ }
+
+ private long max(long[] values) {
+ long max = Long.MIN_VALUE;
+
+ for (long v : values) {
+ max = Math.max(max, v);
+ }
+ return max;
+ }
+
+ private double max(double[] values) {
+ double max = Double.MIN_VALUE;
+
+ for (double v : values) {
+ max = Math.max(max, v);
+ }
+ return max;
+ }
+
+ @SuppressWarnings("null")
+ @Test
+ @Ignore
+ public final void testSanity() throws IOException {
+ @SuppressWarnings("unused")
+ String a, b;
+ {
+ Random random = new Random(42L);
+ JsonGenerator generator = null;
+ ByteArrayOutputStream generatorOut = new ByteArrayOutputStream();
+ try {
+ generator = new JsonFactory().createJsonGenerator(generatorOut,
+ JsonEncoding.UTF8);
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ try {
+ OutputWithGenerator.serialize(generatedDoc(random), generator);
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ try {
+ generator.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ a = generatorOut.toString("UTF-8");
+ }
+ {
+ Random random = new Random(42L);
+ ByteArrayOutputStream writerOut = new ByteArrayOutputStream();
+ JSONWriter writer = new JSONWriter(writerOut);
+ try {
+ OutputWithWriter.serialize(generatedDoc(random), writer);
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail();
+ }
+ b = writerOut.toString("UTF-8");
+ }
+ // dumpToFile("/tmp/a", a);
+ // dumpToFile("/tmp/b", b);
+ }
+
+ @SuppressWarnings("unused")
+ private void dumpToFile(String path, String b) throws IOException {
+ FileWriter f = new FileWriter(path);
+ f.write(b);
+ f.close();
+ }
+
+ static Map<String, Object> generatedDoc(Random random) {
+ return generateObject(random, 0, random.nextInt(8));
+ }
+
+ static String generateFieldName(Random random) {
+ int len = random.nextInt(100) + 3;
+ char[] base = new char[len];
+ for (int i = 0; i < len; ++i) {
+ base[i] = (char) (random.nextInt(26) + 'a');
+ }
+ return new String(base);
+ }
+
+ static byte[] generateByteArrayPayload(Random random) {
+ return null;
+ }
+
+ static String generateStringPayload(Random random) {
+ int len = random.nextInt(100) + random.nextInt(100) + random.nextInt(100) + random.nextInt(100);
+ char[] base = new char[len];
+ for (int i = 0; i < len; ++i) {
+ base[i] = (char) random.nextInt(0xd800);
+ }
+ return new String(base);
+ }
+
+ static Number generateInt(Random random) {
+ return Integer.valueOf(random.nextInt());
+ }
+
+ static List<Object> generateArray(Random random, int nesting, int maxNesting) {
+ int len = random.nextInt(10) + random.nextInt(10) + random.nextInt(10) + random.nextInt(10);
+ List<Object> list = new ArrayList<>(len);
+ for (int i = 0; i < len; ++i) {
+ list.add(generateStuff(random, nesting, maxNesting));
+ }
+ return list;
+ }
+
+ private static Object generateStuff(Random random, int nesting, int maxNesting) {
+ if (nesting >= maxNesting) {
+ return generatePrimitive(random);
+ } else {
+ final int die = random.nextInt(10);
+ if (die == 9) {
+ return generateObject(random, nesting + 1, maxNesting);
+ } else if (die == 8) {
+ return generateArray(random, nesting + 1, maxNesting);
+ } else {
+ return generatePrimitive(random);
+ }
+ }
+ }
+
+ private static Object generatePrimitive(Random random) {
+ if (random.nextInt(2) == 0) {
+ return generateStringPayload(random);
+ } else {
+ return generateInt(random);
+ }
+ }
+
+ static Map<String, Object> generateObject(Random random, int nesting, int maxNesting) {
+ int len = random.nextInt(5) + random.nextInt(5) + random.nextInt(5) + random.nextInt(5);
+ Map<String, Object> m = new TreeMap<>();
+ for (int i = 0; i < len; ++i) {
+ m.put(generateFieldName(random), generateStuff(random, nesting, maxNesting));
+ }
+ return m;
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/LanguageHacksTestCase.java b/vespajlib/src/test/java/com/yahoo/text/LanguageHacksTestCase.java
new file mode 100644
index 00000000000..7b322e44583
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/LanguageHacksTestCase.java
@@ -0,0 +1,28 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test for LanguageHacks.
+ * $Id$
+ */
+@SuppressWarnings("deprecation")
+public class LanguageHacksTestCase {
+
+ @Test
+ public void isCJK() {
+ assertFalse("NULL language", LanguageHacks.isCJK(null));
+ assertTrue(LanguageHacks.isCJK("zh"));
+ assertFalse("Norwegian is CJK", LanguageHacks.isCJK("no"));
+ }
+
+ @Test
+ public void yellDesegments() {
+ assertFalse("NULL language", LanguageHacks.yellDesegments(null));
+ assertTrue(LanguageHacks.yellDesegments("de"));
+ assertFalse("Norwegian desegments", LanguageHacks.yellDesegments("no"));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/LowercaseIdentifierTestCase.java b/vespajlib/src/test/java/com/yahoo/text/LowercaseIdentifierTestCase.java
new file mode 100644
index 00000000000..7d6b066a499
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/LowercaseIdentifierTestCase.java
@@ -0,0 +1,41 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: balder
+ * Date: 11.11.12
+ * Time: 20:54
+ * To change this template use File | Settings | File Templates.
+ */
+public class LowercaseIdentifierTestCase {
+ @Test
+ public void testLowercaseIdentifier() {
+ assertEquals(new LowercaseIdentifier("").toString(), "");
+ assertEquals(new LowercaseIdentifier("a").toString(), "a");
+ assertEquals(new LowercaseIdentifier("z").toString(), "z");
+ assertEquals(new LowercaseIdentifier("_").toString(), "_");
+ try {
+ assertEquals(new Identifier("0").toString(), "0");
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal starting character '0' of identifier '0'.");
+ }
+ try {
+ assertEquals(new LowercaseIdentifier("Z").toString(), "z");
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal uppercase character 'Z' of identifier 'Z'.");
+ }
+ try {
+ assertEquals(new LowercaseIdentifier("aZb").toString(), "azb");
+ } catch (IllegalArgumentException e) {
+ assertEquals(e.getMessage(), "Illegal uppercase character 'Z' of identifier 'aZb'.");
+ }
+
+
+
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/LowercaseTestCase.java b/vespajlib/src/test/java/com/yahoo/text/LowercaseTestCase.java
new file mode 100644
index 00000000000..420d058892b
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/LowercaseTestCase.java
@@ -0,0 +1,96 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.Locale;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ * @since 5.1.14
+ */
+public class LowercaseTestCase {
+
+ @Test
+ public void testAZ() {
+ {
+ String lowercase = Lowercase.toLowerCase("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ assertThat(lowercase, equalTo("abcdefghijklmnopqrstuvwxyz"));
+ }
+ {
+ String lowercase = Lowercase.toLowerCase("abcdefghijklmnopqrstuvwxyz");
+ assertThat(lowercase, equalTo("abcdefghijklmnopqrstuvwxyz"));
+ }
+ {
+ String lowercase = Lowercase.toLowerCase("AbCDEfGHIJklmnoPQRStuvwXyz");
+ assertThat(lowercase, equalTo("abcdefghijklmnopqrstuvwxyz"));
+ }
+
+ {
+ String lowercase = Lowercase.toLowerCase("@+#");
+ assertThat(lowercase, equalTo("@+#"));
+ }
+ {
+ String lowercase = Lowercase.toLowerCase("[]");
+ assertThat(lowercase, equalTo("[]"));
+ }
+ {
+ String lowercase = Lowercase.toLowerCase("{}");
+ assertThat(lowercase, equalTo("{}"));
+ }
+ {
+ String lowercase = Lowercase.toLowerCase("\u00cd\u00f4");
+ assertThat(lowercase, equalTo("\u00ed\u00f4"));
+ }
+ }
+
+ @Test
+ @Ignore
+ public void performance() {
+ Lowercase.toLowerCase("warmup");
+ String lowercaseInput = "abcdefghijklmnopqrstuvwxyz";
+ String uppercaseInput = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ String mixedcaseInput = "AbCDEfGHIJklmnoPQRStuvwXyz";
+
+ System.err.println("Lowercase input: ");
+ testPerformance(lowercaseInput);
+
+ System.err.println("Uppercase input: ");
+ testPerformance(uppercaseInput);
+
+ System.err.println("Mixed-case input: ");
+ testPerformance(mixedcaseInput);
+ }
+
+ private void testPerformance(String input) {
+ final int NUM = 10000000;
+ long elapsedTimeOwnImpl;
+ {
+ long startTimeOwnImpl = System.currentTimeMillis();
+ for (int i = 0; i < NUM; i++) {
+ Lowercase.toLowerCase(input);
+ }
+ elapsedTimeOwnImpl = System.currentTimeMillis() - startTimeOwnImpl;
+ System.err.println("Own implementation: " + elapsedTimeOwnImpl);
+ }
+
+ long elapsedTimeJava;
+ {
+ long startTimeJava = System.currentTimeMillis();
+ for (int i = 0; i < NUM; i++) {
+ input.toLowerCase(Locale.ENGLISH);
+ }
+ elapsedTimeJava = System.currentTimeMillis() - startTimeJava;
+ System.err.println("Java's implementation: " + elapsedTimeJava);
+ }
+
+ long diff = elapsedTimeJava - elapsedTimeOwnImpl;
+ double diffPercentage = (((double) diff) / ((double) elapsedTimeJava)) * 100.0;
+ System.err.println("Own implementation is " + diffPercentage + " % faster.");
+
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/MapParserMicroBenchmark.java b/vespajlib/src/test/java/com/yahoo/text/MapParserMicroBenchmark.java
new file mode 100644
index 00000000000..21ab4fd4309
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/MapParserMicroBenchmark.java
@@ -0,0 +1,62 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A benchmark of map parsing.
+ * Expected time on Jon's mac: 200 microseconds per 1k size map.
+ *
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class MapParserMicroBenchmark {
+
+ private static String generateValues(int size) {
+ StringBuilder b = new StringBuilder("{");
+ for (int i=0; i<size; i++)
+ b.append("a").append(i).append(":").append(i+1).append(",");
+ b.setLength(b.length() - 1);
+ b.append("}");
+ return b.toString();
+ }
+
+ public void benchmark(int repetitions,int mapSize) {
+ String values = generateValues(mapSize);
+ System.out.println("Ranking expression parsing");
+ System.out.println(" warming up");
+ rankingExpressionParserParse(1000, values, mapSize);
+ long startTime = System.currentTimeMillis();
+ System.out.println( "starting ....");
+ rankingExpressionParserParse(repetitions, values, mapSize);
+ long totalTime = System.currentTimeMillis() - startTime;
+ System.out.println(" Total time: " + totalTime + " ms, time per expression: " + (totalTime*1000/repetitions) + " microseconds");
+ }
+
+ private void rankingExpressionParserParse(int repetitions, String values, int expectedMapSize) {
+ Map<String,Double> map = new HashMap<>();
+ for (int i=0; i<repetitions; i++) {
+ rankingExpressionParserParse(values, map);
+ if ( map.size()!=expectedMapSize)
+ throw new RuntimeException("Expected size: " + expectedMapSize + ", actual size: " + map.size());
+ map.clear();
+ }
+ }
+ private Map<String,Double> rankingExpressionParserParse(String values, Map<String,Double> map) {
+ return new DoubleMapParser().parse(values,map);
+ }
+
+ public static void main(String[] args) {
+ new MapParserMicroBenchmark().benchmark(100*1000,1000);
+ }
+
+ private static class DoubleMapParser extends MapParser<Double> {
+
+ @Override
+ protected Double parseValue(String s) {
+ return Double.parseDouble(s);
+ }
+
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/MapParserTestCase.java b/vespajlib/src/test/java/com/yahoo/text/MapParserTestCase.java
new file mode 100644
index 00000000000..7bf11c277e1
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/MapParserTestCase.java
@@ -0,0 +1,71 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+import java.util.Map;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class MapParserTestCase {
+
+ private static final double delta=0.0001;
+
+ @Test
+ public void testEmpty() {
+ assertEquals(0, new DoubleMapParser().parseToMap("{}").size());
+ }
+
+ @Test
+ public void testPlain() {
+ Map<String,Double> values=new DoubleMapParser().parseToMap("{a:0.33,foo:-1.13,bar:1}");
+ assertEquals(3, values.size());
+ assertEquals(0.33d,values.get("a"),delta);
+ assertEquals(-1.13d,values.get("foo"),delta);
+ assertEquals(1.0d,values.get("bar"),delta);
+ }
+
+ @Test
+ public void testNoisy() {
+ Map<String,Double> values=new DoubleMapParser().parseToMap(" { a:0.33, foo:-1.13,bar:1,\"key:colon,\":1.2, '}':0}");
+ assertEquals(5, values.size());
+ assertEquals(0.33d,values.get("a"),delta);
+ assertEquals(-1.13d,values.get("foo"),delta);
+ assertEquals(1.0d,values.get("bar"),delta);
+ assertEquals(1.2,values.get("key:colon,"),delta);
+ assertEquals(0,values.get("}"),delta);
+ }
+
+ @Test
+ public void testInvalid() {
+ assertException("Missing quoted string termination","Expected a string terminated by '\"' starting at position 9 but was 'f'","{a:0.33,\"foo:1,bar:1}");
+ assertException("Missing map termination","Expected a value followed by ',' or '}' starting at position 10 but was '1'","{a:0.33,b:1");
+ assertException("Missing map start","Expected '{' starting at position 0 but was 'a'","a:0.33,b:1}");
+ assertException("Missing comma separator","Expected a legal value from position 3 to 11 but was '0.33 b:1'","{a:0.33 b:1}");
+ assertException("A single key with no value","Expected a key followed by ':' starting at position 1 but was 'f'","{foo}");
+ assertException("A key with no value","Expected ':' starting at position 4 but was ','","{foo,a:2}");
+ assertException("Invalid value","Expected a legal value from position 9 to 19 but was 'notanumber'","{invalid:notanumber}");
+ assertException("Double key","Expected a legal value from position 3 to 6 but was 'a:1'","{a:a:1}");
+ }
+
+ private void assertException(String explanation,String exceptionString,String invalidMapString) {
+ try {
+ Map<String,Double> map=new DoubleMapParser().parseToMap(invalidMapString);
+ fail("Expected exception on: " + explanation + " but parsed to " + map);
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("Expected message on: " + explanation,exceptionString,e.getCause().getMessage());
+ }
+ }
+
+ public static final class DoubleMapParser extends MapParser<Double> {
+
+ @Override
+ protected Double parseValue(String value) {
+ return Double.parseDouble(value);
+ }
+
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/StringAppendMicroBenchmarkTest.java b/vespajlib/src/test/java/com/yahoo/text/StringAppendMicroBenchmarkTest.java
new file mode 100644
index 00000000000..69d62d59be5
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/StringAppendMicroBenchmarkTest.java
@@ -0,0 +1,77 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+
+/**
+ * Compares alternative ways of appending strings
+ *
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+public class StringAppendMicroBenchmarkTest {
+
+ private static abstract class Benchmark {
+
+ private int repetitions=10000000;
+
+ public void execute() {
+ System.out.println("Executing benchmark '" + getName() + "' ...");
+ append(100000); // warm-up
+ long start=System.currentTimeMillis();
+ append(repetitions);
+ long duration=System.currentTimeMillis()-start;
+ System.out.println("Completed " + repetitions + " repetitions in " + duration + " ms\n");
+ }
+
+ private int append(int repetitions) {
+ String prefix="hello";
+ int totalSize=0;
+ for (int i=0; i<repetitions; i++) {
+ String full=appendStrings(prefix, String.valueOf(i));
+ totalSize+=full.length();
+ }
+ return totalSize;
+ }
+
+ protected abstract String getName();
+ protected abstract String appendStrings(String a,String b);
+
+ }
+
+ private static final class PlusOperatorBenchmark extends Benchmark {
+
+ @Override
+ protected String getName() { return "Plus operator"; }
+
+ @Override
+ protected String appendStrings(String a, String b) {
+ return a+b;
+ }
+
+ }
+
+ private static final class StringConcatBenchmark extends Benchmark {
+
+ @Override
+ protected String getName() { return "String concat"; }
+
+ @Override
+ protected String appendStrings(String a, String b) {
+ return a.concat(b);
+ }
+
+ }
+
+ /**
+ * Make Clover shut up about this in the coverage report.
+ */
+ @Test
+ public void shutUpClover() {
+ }
+
+ public static void main(String[] args) {
+ new PlusOperatorBenchmark().execute(); // Typical number on my box with Java 7: 1000 ms
+ new StringConcatBenchmark().execute(); // Typical number on my box with Java 7: 1150 ms
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/StringUtilitiesTest.java b/vespajlib/src/test/java/com/yahoo/text/StringUtilitiesTest.java
new file mode 100644
index 00000000000..bebee69e7e5
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/StringUtilitiesTest.java
@@ -0,0 +1,85 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+
+public class StringUtilitiesTest {
+
+ @Test
+ public void testEscape() {
+ assertEquals("abz019ABZ", StringUtilities.escape("abz019ABZ"));
+ assertEquals("\\t", StringUtilities.escape("\t"));
+ assertEquals("\\n", StringUtilities.escape("\n"));
+ assertEquals("\\r", StringUtilities.escape("\r"));
+ assertEquals("\\\"", StringUtilities.escape("\""));
+ assertEquals("\\f", StringUtilities.escape("\f"));
+ assertEquals("\\\\", StringUtilities.escape("\\"));
+ assertEquals("\\x05", StringUtilities.escape("" + (char) 5));
+ assertEquals("\\tA\\ncombined\\r\\x055test", StringUtilities.escape("\tA\ncombined\r" + ((char) 5) + "5test"));
+ assertEquals("A\\x20space\\x20separated\\x20string", StringUtilities.escape("A space separated string", ' '));
+ }
+
+ @Test
+ public void testUnescape() {
+ assertEquals("abz019ABZ", StringUtilities.unescape("abz019ABZ"));
+ assertEquals("\t", StringUtilities.unescape("\\t"));
+ assertEquals("\n", StringUtilities.unescape("\\n"));
+ assertEquals("\r", StringUtilities.unescape("\\r"));
+ assertEquals("\"", StringUtilities.unescape("\\\""));
+ assertEquals("\f", StringUtilities.unescape("\\f"));
+ assertEquals("\\", StringUtilities.unescape("\\\\"));
+ assertEquals("" + (char) 5, StringUtilities.unescape("\\x05"));
+ assertEquals("\tA\ncombined\r" + ((char) 5) + "5test", StringUtilities.unescape("\\tA\\ncombined\\r\\x055test"));
+ assertEquals("A space separated string", StringUtilities.unescape("A\\x20space\\x20separated\\x20string"));
+ }
+
+ @Test
+ public void testImplode() {
+ assertEquals(StringUtilities.implode(null, null), null);
+ assertEquals(StringUtilities.implode(new String[0], null), "");
+ assertEquals(StringUtilities.implode(new String[] {"foo"}, null), "foo");
+ assertEquals(StringUtilities.implode(new String[] {"foo"}, "asdfsdfsadfsadfasdfs"), "foo");
+ assertEquals(StringUtilities.implode(new String[] {"foo", "bar"}, null), "foobar");
+ assertEquals(StringUtilities.implode(new String[] {"foo", "bar"}, "\n"), "foo\nbar");
+ assertEquals(StringUtilities.implode(new String[] {"foo"}, "\n"), "foo");
+ assertEquals(StringUtilities.implode(new String[] {"foo", "bar", null}, "\n"), "foo\nbar\nnull");
+ assertEquals(StringUtilities.implode(new String[] {"foo", "bar"}, "\n"), "foo\nbar");
+ assertEquals(StringUtilities.implode(new String[] {"foo", "bar", "baz"}, null), "foobarbaz");
+
+ }
+
+ @Test
+ public void testImplodeMultiline() {
+ assertEquals(StringUtilities.implodeMultiline(Arrays.asList("foo", "bar")), "foo\nbar");
+ assertEquals(StringUtilities.implodeMultiline(Arrays.asList("")), "");
+ assertEquals(StringUtilities.implodeMultiline(null), null);
+ assertEquals(StringUtilities.implodeMultiline(Arrays.asList("\n")), "\n");
+ }
+
+ @Test
+ public void testTruncation() {
+ String a = "abbc";
+ assertTrue(a == StringUtilities.truncateSequencesIfNecessary(a, 2));
+ assertTrue(a != StringUtilities.truncateSequencesIfNecessary(a, 1));
+ assertEquals("abc", StringUtilities.truncateSequencesIfNecessary(a, 1));
+ assertEquals("abc", StringUtilities.truncateSequencesIfNecessary("aabbccc", 1));
+ assertEquals("abc", StringUtilities.truncateSequencesIfNecessary("abcc", 1));
+ assertEquals("abc", StringUtilities.truncateSequencesIfNecessary("aabc", 1));
+ assertEquals("abcb", StringUtilities.truncateSequencesIfNecessary("abcb", 1));
+ assertEquals("g g g g g g g g g g\n g g g g g g g g g g\n g g g g g g g g g g", StringUtilities.truncateSequencesIfNecessary("g g g g g g g g g g\n g g g g g g g g g g\n g g g g g g g g g g", 5));
+ }
+
+
+ @Test
+ public void testStripSuffix() {
+ assertThat(StringUtilities.stripSuffix("abc.def", ".def"), is("abc"));
+ assertThat(StringUtilities.stripSuffix("abc.def", ""), is("abc.def"));
+ assertThat(StringUtilities.stripSuffix("", ".def"), is(""));
+ assertThat(StringUtilities.stripSuffix("", ""), is(""));
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/Utf8ArrayTestCase.java b/vespajlib/src/test/java/com/yahoo/text/Utf8ArrayTestCase.java
new file mode 100644
index 00000000000..4195113e2e1
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/Utf8ArrayTestCase.java
@@ -0,0 +1,167 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import static org.junit.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Check the Utf8Array API behaves as expected.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class Utf8ArrayTestCase {
+ private String raw;
+ private byte[] rawBytes;
+ private Utf8Array toCheck;
+
+ @Before
+ public void setUp() {
+ raw = "0123456789";
+ rawBytes = Utf8.toBytes(raw);
+ toCheck = new Utf8Array(rawBytes);
+ }
+
+ @Test
+ public final void testGetByteLength() {
+ assertEquals(rawBytes.length, toCheck.getByteLength());
+ }
+
+ @Test
+ public final void testGetBytes() {
+ assertSame(rawBytes, toCheck.getBytes());
+ }
+
+ @Test
+ public final void testGetByteOffset() {
+ assertEquals(0, toCheck.getByteOffset());
+ }
+
+ @Test
+ public final void testUtf8ArrayByteArrayIntInt() {
+ Utf8Array otherConstructed = new Utf8Array(rawBytes, 0, rawBytes.length);
+ assertNotSame(rawBytes, otherConstructed.getBytes());
+ assertArrayEquals(rawBytes, otherConstructed.getBytes());
+ }
+
+ @Test
+ public final void testUtf8ArrayByteBufferInt() {
+ final ByteBuffer wrapper = ByteBuffer.wrap(rawBytes);
+ Utf8Array otherConstructed = new Utf8Array(wrapper, wrapper.remaining());
+ assertNotSame(rawBytes, otherConstructed.getBytes());
+ assertArrayEquals(rawBytes, otherConstructed.getBytes());
+ }
+
+ @Test
+ public final void testHashCode() {
+ Utf8Array other = new Utf8Array(Utf8.toBytes(" a23456789"));
+ assertFalse(other.hashCode() == toCheck.hashCode());
+ }
+
+ @Test
+ public final void testWriteTo() {
+ ByteBuffer b = ByteBuffer.allocate(rawBytes.length * 2);
+ byte[] copy = new byte[rawBytes.length];
+ toCheck.writeTo(b);
+ assertEquals(rawBytes.length, b.position());
+ b.position(0);
+ b.limit(rawBytes.length);
+ b.get(copy);
+ assertArrayEquals(rawBytes, copy);
+ }
+
+ @Test
+ public final void testGetByte() {
+ assertEquals('8', toCheck.getByte(8));
+ }
+
+ @Test
+ public final void testWrap() {
+ ByteBuffer b1 = toCheck.wrap();
+ ByteBuffer b2 = ByteBuffer.wrap(rawBytes);
+ byte[] c1 = new byte[b1.remaining()];
+ byte[] c2 = new byte[b2.remaining()];
+ b1.get(c1);
+ b2.get(c2);
+ assertArrayEquals(c2, c1);
+ }
+
+ @Test
+ public final void testIsEmpty() {
+ assertFalse(toCheck.isEmpty());
+ assertTrue(new Utf8Array(new byte[] {}).isEmpty());
+ }
+
+ @Test
+ public final void testEqualsObject() {
+ assertTrue(toCheck.equals(raw));
+ assertFalse(toCheck.equals(new Utf8Array(new byte[] {})));
+ assertFalse(toCheck.equals(new Utf8Array(Utf8.toBytes(" " + raw.substring(1)))));
+ assertTrue(toCheck.equals(toCheck));
+ assertTrue(toCheck.equals(new Utf8Array(rawBytes)));
+ }
+
+ @Test
+ public final void testToString() {
+ assertEquals(raw, toCheck.toString());
+ }
+
+ @Test
+ public final void testCompareTo() {
+ assertTrue(toCheck.compareTo(new Utf8Array(new byte[] {})) > 0);
+ assertTrue(toCheck.compareTo(new Utf8Array(Utf8.toBytes(raw + raw))) < 0);
+ assertTrue(toCheck.compareTo(new Utf8Array(Utf8.toBytes(" " + raw.substring(1)))) > 0);
+ assertTrue(toCheck.compareTo(new Utf8Array(Utf8.toBytes("a" + raw.substring(1)))) < 0);
+ assertTrue(toCheck.compareTo(new Utf8Array(rawBytes)) == 0);
+ }
+
+ @Test
+ public final void testPartial() {
+ final int length = 3;
+ final int offset = 1;
+ Utf8PartialArray partial = new Utf8PartialArray(rawBytes, offset, length);
+ assertEquals(length, partial.getByteLength());
+ assertEquals(offset, partial.getByteOffset());
+ byte[] expected = new byte[length];
+ ByteBuffer intermediate = ByteBuffer.allocate(rawBytes.length * 2);
+ System.arraycopy(rawBytes, offset, expected, 0, length);
+ partial.writeTo(intermediate);
+ intermediate.flip();
+ byte written[] = new byte[intermediate.remaining()];
+ intermediate.get(written);
+ assertArrayEquals(expected, written);
+ }
+
+ @Test
+ public final void testUtf8Strings() {
+ String nalle = "nalle";
+ Utf8String utf = new Utf8String(new Utf8Array(Utf8.toBytes(nalle)));
+ assertEquals('n', utf.charAt(0));
+ assertEquals(nalle.length(), utf.length());
+ assertEquals("alle", utf.subSequence(1, 5));
+ assertTrue(utf.equals(new Utf8String(new Utf8Array(Utf8.toBytes(nalle)))));
+ assertTrue(utf.equals(nalle));
+ }
+
+ @Test
+ public final void testAscii7bitLowercase() {
+ final byte [] expected = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 ,0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 ,0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26 ,0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 ,0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 ,0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76 ,0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 ,0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76 ,0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
+ };
+ byte [] org = new byte[128];
+ for (byte b = 0; b >= 0; b++) {
+ org[b] = b;
+ }
+ assertArrayEquals(expected, new Utf8Array(org).ascii7BitLowerCase().getBytes());
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/Utf8TestCase.java b/vespajlib/src/test/java/com/yahoo/text/Utf8TestCase.java
new file mode 100644
index 00000000000..53ee1bb004a
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/Utf8TestCase.java
@@ -0,0 +1,554 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharsetEncoder;
+import java.util.Arrays;
+
+import static com.yahoo.text.Lowercase.toLowerCase;
+import static com.yahoo.text.Utf8.calculateBytePositions;
+import static com.yahoo.text.Utf8.calculateStringPositions;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:borud@yahoo-inc.com">Bjorn Borud</a>
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class Utf8TestCase {
+
+ private static final String TEST_STRING = "This is just sort of random mix. \u5370\u57df\u60c5\u5831\u53EF\u4EE5\u6709x\u00e9\u00e8";
+ private static final int[] TEST_CODEPOINTS = {0x0, 0x7f, 0x80, 0x7ff, 0x800, 0xd7ff, 0xe000, 0xffff, 0x10000, 0x10ffff,
+ 0x34, 0x355, 0x2567, 0xfff, 0xe987, 0x100abc
+ };
+
+ public void dumpSome() throws java.io.IOException {
+ int i = 32;
+ int j = 3;
+ int cnt = 0;
+ while (i < 0x110000) {
+ if (i < 0xD800 || i >= 0xE000) ++cnt;
+ i += j;
+ ++j;
+ }
+ System.out.println("allocate "+cnt+" array entries");
+ int codes[] = new int[cnt];
+ i = 32;
+ j = 3;
+ cnt = 0;
+ while (i < 0x110000) {
+ if (i < 0xD800 || i >= 0xE000) codes[cnt++] = i;
+ i += j;
+ ++j;
+ }
+ assertEquals(cnt, codes.length);
+ System.out.println("fill "+cnt+" array entries");
+ String str = new String(codes, 0, cnt);
+ byte[] arr = Utf8.toBytes(str);
+ java.io.FileOutputStream fos = new java.io.FileOutputStream("random-long-utf8.dat");
+ fos.write(arr);
+ fos.close();
+ }
+
+ public void dumpMore() throws java.io.IOException {
+ java.text.Normalizer.Form form = java.text.Normalizer.Form.NFKC;
+
+ java.io.FileOutputStream fos = new java.io.FileOutputStream("lowercase-table.dat");
+ for (int i = 0; i < 0x110000; i++) {
+ StringBuilder b = new StringBuilder();
+ b.appendCodePoint(i);
+ String n1 = b.toString();
+ String n2 = java.text.Normalizer.normalize(b, form);
+ if (n1.equals(n2)) {
+ String l = toLowerCase(n1);
+ int chars = l.length();
+ int codes = l.codePointCount(0, chars);
+ if (codes != 1) {
+ System.out.println("codepoint "+i+" transformed into "+codes+" codepoints: "+n1+" -> "+l);
+ } else {
+ int lc = l.codePointAt(0);
+ if (lc != i) {
+ String o = "lowercase( "+i+" )= "+lc+"\n";
+ byte[] arr = Utf8.toBytes(o);
+ fos.write(arr);
+ }
+ }
+ }
+ }
+ fos.close();
+ }
+
+ @Test
+ public void testSimple() {
+ String s1 = "test";
+ String s2 = "f\u00F8rst";
+ String s3 = "\u00C5pen";
+ byte[] b4 = { (byte) 0xE5, (byte) 0xA4, (byte) 0x89, (byte) 0xE6,
+ (byte) 0x85, (byte) 0x8B };
+
+ byte[] b1 = Utf8.toBytes(s1);
+ byte[] b2 = Utf8.toBytes(s2);
+ byte[] b3 = Utf8.toBytes(s3);
+ String s4 = Utf8.toString(b4);
+
+ assertEquals('t', b1[0]);
+ assertEquals('e', b1[1]);
+ assertEquals('s', b1[2]);
+ assertEquals('t', b1[3]);
+
+ assertEquals('f', b2[0]);
+ assertEquals((byte) 0xC3, b2[1]);
+ assertEquals((byte) 0xB8, b2[2]);
+ assertEquals('r', b2[3]);
+ assertEquals('s', b2[4]);
+ assertEquals('t', b2[5]);
+
+ assertEquals((byte) 0xC3, b3[0]);
+ assertEquals((byte) 0x85, b3[1]);
+ assertEquals('p', b3[2]);
+ assertEquals('e', b3[3]);
+ assertEquals('n', b3[4]);
+
+ assertEquals('\u5909', s4.charAt(0));
+ assertEquals('\u614B', s4.charAt(1));
+
+ String ss1 = Utf8.toString(b1);
+ String ss2 = Utf8.toString(b2);
+ String ss3 = Utf8.toString(b3);
+ byte[] bb4 = Utf8.toBytes(s4);
+
+ assertEquals(s1, ss1);
+ assertEquals(s3, ss3);
+ assertEquals(s2, ss2);
+ assertEquals(Utf8.toString(b4), Utf8.toString(bb4));
+ }
+
+ private int javaCountBytes(String str) {
+ byte[] octets = Utf8.toBytes(str);
+ return octets.length;
+ }
+
+ private String makeString(int codePoint) {
+ char[] chars = Character.toChars(codePoint);
+ return String.valueOf(chars);
+ }
+
+ @Test
+ public void testByteCounting() {
+ for (int c : TEST_CODEPOINTS) {
+ String testCharacter = makeString(c);
+ assertEquals(javaCountBytes(testCharacter), Utf8.byteCount(testCharacter));
+ }
+ assertEquals(javaCountBytes(TEST_STRING), Utf8.byteCount(TEST_STRING));
+ }
+
+ @Test
+ public void testTotalBytes() {
+ //Test with a random mix of
+ assertEquals(1,Utf8.totalBytes((byte)0x05));
+ assertEquals(4,Utf8.totalBytes((byte)0xF3));
+ assertEquals(4,Utf8.totalBytes((byte)0xF0));
+ assertEquals(1,Utf8.totalBytes((byte)0x7F));
+ assertEquals(2,Utf8.totalBytes((byte)0xC2));
+ assertEquals(3,Utf8.totalBytes((byte)0xE0));
+ }
+
+ @Test
+ public void testUnitCounting() {
+ for (int c : TEST_CODEPOINTS) {
+ String testCharacter = makeString(c);
+ byte[] utf8 = Utf8.toBytes(testCharacter);
+ assertEquals(testCharacter.length(), Utf8.unitCount(utf8));
+ assertEquals(testCharacter.length(), Utf8.unitCount(utf8[0]));
+ }
+ byte[] stringAsUtf8 = Utf8.toBytes(TEST_STRING);
+ assertEquals(TEST_STRING.length(), Utf8.unitCount(stringAsUtf8));
+
+
+ }
+
+ @Test
+ public void testCumbersomeEncoding() {
+ String[] a = {"abc", "def", "ghi\u00e8"};
+ int[] aLens = {3, 3, 5};
+ CharsetEncoder ce = Utf8.getNewEncoder();
+ ByteBuffer forWire = ByteBuffer.allocate(500);
+
+ for (int i = 0; i < a.length; i++) {
+ forWire.putInt(aLens[i]);
+ Utf8.toBytes(a[i], 0,
+ a[i].length(), forWire, ce);
+ }
+ forWire.flip();
+ int totalLimit = forWire.limit();
+ for (String anA : a) {
+ int len = forWire.getInt();
+ forWire.limit(forWire.position() + len);
+ String s = Utf8.toString(forWire);
+ assertEquals(anA, s);
+ forWire.limit(totalLimit);
+ }
+ assertEquals(0, forWire.remaining());
+ }
+
+ @Test
+ public void basic() {
+ String foo = "Washington";
+ int[] indexes = calculateBytePositions(foo);
+ assertThat(indexes.length, is(foo.length() + 1));
+ for (int i = 0; i < indexes.length; i++) {
+ assertThat(indexes[i], is(i));
+ }
+ }
+
+ @Test
+ public void decodeBasic() {
+ byte[] foo = Utf8.toBytes("Washington");
+ int[] indexes = calculateStringPositions(foo);
+ assertThat(indexes.length, is(foo.length + 1));
+ for (int i = 0; i < indexes.length; i++) {
+ assertThat(indexes[i], is(i));
+ }
+ }
+
+ @Test
+ public void highBytes() {
+ String foo = "\u0128st\u0200e";
+ //utf-8
+ // 0xC4A8 0x73 0x74 0xC880 0x65
+ int[] indexes = calculateBytePositions(foo);
+ assertThat(indexes.length, is(foo.length() + 1));
+ assertThat(indexes[0], is(0)); //128
+ assertThat(indexes[1], is(2)); //s
+ assertThat(indexes[2], is(3)); //t
+ assertThat(indexes[3], is(4)); //200
+ assertThat(indexes[4], is(6)); //e
+ }
+
+ @Test
+ public void decodeHighBytes() {
+ byte[] foo = Utf8.toBytes("\u0128st\u0200e");
+ //utf-8
+ // 0xC4A8 0x73 0x74 0xC880 0x65
+ int[] indexes = calculateStringPositions(foo);
+ assertThat(indexes.length, is(foo.length + 1));
+ assertThat(indexes[0], is(0)); //128
+ assertThat(indexes[1], is(0)); //128
+ assertThat(indexes[2], is(1)); //s
+ assertThat(indexes[3], is(2)); //t
+ assertThat(indexes[4], is(3)); //200
+ assertThat(indexes[5], is(3)); //200
+ assertThat(indexes[6], is(4)); //e
+ }
+
+ @Test
+ public void moreHighBytes() {
+ String foo = "\u0200\u0201\u0202abc\u0300def\u0301g\u07ff\u0800a\uffffa";
+ //utf-8
+ //0xC880 0xC881 0xC882 0x61 0x62 0x63 0xCC80 0x64 0x65 0x66 0xCC81 0x67 0xDFBF 0xE0A080 0x61 0xEFBFBF 0x61
+ int[] indexes = calculateBytePositions(foo);
+ assertThat(indexes.length, is(foo.length() + 1));
+ assertThat(indexes[0], is(0)); //200
+ assertThat(indexes[1], is(2)); //201
+ assertThat(indexes[2], is(4)); //202
+ assertThat(indexes[3], is(6)); //a
+ assertThat(indexes[4], is(7)); //b
+ assertThat(indexes[5], is(8)); //c
+ assertThat(indexes[6], is(9)); //300
+ assertThat(indexes[7], is(11)); //d
+ assertThat(indexes[8], is(12)); //e
+ assertThat(indexes[9], is(13)); //f
+ assertThat(indexes[10], is(14)); //301
+ assertThat(indexes[11], is(16)); //g
+ assertThat(indexes[12], is(17)); //7ff
+ assertThat(indexes[13], is(19)); //800
+ assertThat(indexes[14], is(22)); //a
+ assertThat(indexes[15], is(23)); //ffff
+ assertThat(indexes[16], is(26)); //a
+ }
+
+ @Test
+ public void decodeMoreHighBytes() {
+ String foo = "\u0200\u0201\u0202abc\u0300def\u0301g\u07ff\u0800a\uffffa";
+ //utf-8
+ //0xC880 0xC881 0xC882 0x61 0x62 0x63 0xCC80 0x64 0x65 0x66 0xCC81 0x67 0xDFBF 0xE0A080 0x61 0xEFBFBF 0x61
+ int[] indexes = calculateStringPositions(Utf8.toBytes(foo));
+ assertThat(indexes.length, is(28));
+ assertThat(indexes[0], is(0)); //200
+ assertThat(indexes[1], is(0)); //200
+ assertThat(indexes[2], is(1)); //201
+ assertThat(indexes[3], is(1)); //201
+ assertThat(indexes[4], is(2)); //202
+ assertThat(indexes[5], is(2)); //202
+ assertThat(indexes[6], is(3)); //a
+ assertThat(indexes[7], is(4)); //b
+ assertThat(indexes[8], is(5)); //c
+ assertThat(indexes[9], is(6)); //300
+ assertThat(indexes[10], is(6)); //300
+ assertThat(indexes[11], is(7)); //d
+ assertThat(indexes[12], is(8)); //e
+ assertThat(indexes[13], is(9)); //f
+ assertThat(indexes[14], is(10)); //301
+ assertThat(indexes[15], is(10)); //301
+ assertThat(indexes[16], is(11)); //g
+ assertThat(indexes[17], is(12)); //7ff
+ assertThat(indexes[18], is(12)); //7ff
+ assertThat(indexes[19], is(13)); //800
+ assertThat(indexes[20], is(13)); //800
+ assertThat(indexes[21], is(13)); //800
+ assertThat(indexes[22], is(14)); //a
+ assertThat(indexes[23], is(15)); //ffff
+ assertThat(indexes[24], is(15)); //ffff
+ assertThat(indexes[25], is(15)); //ffff
+ assertThat(indexes[26], is(16)); //a
+ }
+
+ @Test
+ public void testOptimisticEncoder() {
+ for (char i=0; i < 256; i++) {
+ StringBuilder sb = new StringBuilder();
+ for (char c=0; c < i; c++) {
+ sb.append(c);
+ }
+ assertTrue(Arrays.equals(Utf8.toBytesStd(sb.toString()), Utf8.toBytes(sb.toString())));
+ }
+ }
+
+ @Test
+ public void testLong()
+ {
+ for (long l=-0x10000; l < 0x10000; l++) {
+ assertLongEquals(l);
+ }
+ assertLongEquals(Long.MAX_VALUE);
+ assertLongEquals(Long.MIN_VALUE);
+ }
+
+ private void assertLongEquals(long l) {
+ byte [] a = Utf8.toBytes(String.valueOf(l));
+ byte [] b = Utf8.toAsciiBytes(l);
+ if (!Arrays.equals(a, b)) {
+ assertTrue(Arrays.equals(a, b));
+ }
+ }
+
+ @Test
+ public void testBoolean() {
+ assertEquals("true", String.valueOf(true));
+ assertEquals("false", String.valueOf(false));
+ assertTrue(Arrays.equals(Utf8.toAsciiBytes(true), new Utf8String(String.valueOf(true)).getBytes()));
+ assertTrue(Arrays.equals(Utf8.toAsciiBytes(false), new Utf8String(String.valueOf(false)).getBytes()));
+ }
+ @Test
+ public void testInt()
+ {
+ for (int l=-0x10000; l < 0x10000; l++) {
+ byte [] a = Utf8.toBytes(String.valueOf(l));
+ byte [] b = Utf8.toAsciiBytes(l);
+ if (!Arrays.equals(a, b)) {
+ assertTrue(Arrays.equals(a, b));
+ }
+ }
+ }
+ @Test
+ public void testShort()
+ {
+ for (short l=-0x1000; l < 0x1000; l++) {
+ byte [] a = Utf8.toBytes(String.valueOf(l));
+ byte [] b = Utf8.toAsciiBytes(l);
+ if (!Arrays.equals(a, b)) {
+ assertTrue(Arrays.equals(a, b));
+ }
+ }
+ }
+
+ @Test
+ public void surrogatePairs() {
+ String foo = "a\uD800\uDC00b";
+ //unicode
+ //0x61 0x10000 0x62
+ //utf-16
+ //0x61 0xD800DC00 0x62
+ //utf-8
+ //0x61 0xF0908080 0x62
+ int[] indexes = calculateBytePositions(foo);
+ assertThat(indexes.length, is(foo.length() + 1));
+ assertThat(indexes[0], is(0)); //a
+ assertThat(indexes[1], is(1)); //10000
+ assertThat(indexes[2], is(1)); //10000, second of surrogate pair
+ assertThat(indexes[3], is(5)); //b
+ }
+
+ @Test
+ public void decodeSurrogatePairs() {
+ String foo = "a\uD800\uDC00b";
+ //unicode
+ //0x61 0x10000 0x62
+ //utf-16
+ //0x61 0xD800DC00 0x62
+ //utf-8
+ //0x61 0xF0908080 0x62
+ int[] indexes = calculateStringPositions(Utf8.toBytes(foo));
+ assertThat(indexes.length, is(7));
+ assertThat(indexes[0], is(0)); //a
+ assertThat(indexes[1], is(1)); //10000
+ assertThat(indexes[2], is(1)); //10000
+ assertThat(indexes[3], is(1)); //10000
+ assertThat(indexes[4], is(1)); //10000
+ assertThat(indexes[5], is(2)); //b
+ }
+
+ @Test
+ public void encodeStartEndPositions() {
+ String foo = "abcde";
+ int start = 0;
+ int length = foo.length(); //5
+ int end = start + length;
+
+ int[] indexes = calculateBytePositions(foo);
+ int byteStart = indexes[start];
+ int byteEnd = indexes[end];
+ int byteLength = byteEnd - byteStart;
+
+ assertThat(byteStart, equalTo(start));
+ assertThat(byteEnd, equalTo(end));
+ assertThat(byteLength, equalTo(length));
+ }
+
+ @Test
+ public void encodeStartEndPositionsMultibyteCharsAtEnd() {
+ String foo = "\u0200abcde\uD800\uDC00";
+ int start = 0;
+ int length = foo.length(); //8
+ int end = start + length;
+
+ int[] indexes = calculateBytePositions(foo);
+ int byteStart = indexes[start];
+ int byteEnd = indexes[end];
+ int byteLength = byteEnd - byteStart;
+
+ //utf-8
+ //0xC880 a b c d e 0xD800DC00
+
+ assertThat(byteStart, equalTo(start));
+ assertThat(byteEnd, equalTo(11));
+ assertThat(byteLength, equalTo(11));
+ }
+
+ @Test
+ public void decodeStartEndPositions() {
+ byte[] foo = Utf8.toBytes("abcde");
+ int start = 0;
+ int length = foo.length; //5
+ int end = start + length;
+
+ int[] indexes = calculateStringPositions(foo);
+ int stringStart = indexes[start];
+ int stringEnd = indexes[end];
+ int stringLength = stringEnd - stringStart;
+
+ assertThat(stringStart, equalTo(start));
+ assertThat(stringEnd, equalTo(end));
+ assertThat(stringLength, equalTo(length));
+ }
+
+ @Test
+ public void decodeStartEndPositionsMultibyteCharsAtEnd() {
+ byte[] foo = Utf8.toBytes("\u0200abcde\uD800\uDC00");
+ int start = 0;
+ int length = foo.length; //11
+ int end = start + length;
+
+ int[] indexes = calculateStringPositions(foo);
+ int stringStart = indexes[start];
+ int stringEnd = indexes[end];
+ int stringLength = stringEnd - stringStart;
+
+ //utf-8
+ //0xC880 a b c d e 0xD800DC00
+
+ assertThat(stringStart, equalTo(start));
+ assertThat(stringEnd, equalTo(8));
+ assertThat(stringLength, equalTo(8));
+ }
+
+ @Test
+ public void emptyInputStringResultsInArrayWithSingleZero() {
+ byte[] empty = new byte[] {};
+ int[] indexes = calculateStringPositions(empty);
+ assertThat(indexes.length, is(1));
+ assertThat(indexes[0], is(0));
+ }
+
+ @Test
+ public void testEncoding() {
+ for (int c : TEST_CODEPOINTS) {
+ byte[] encoded = Utf8.encode(c);
+ String testCharacter = makeString(c);
+ byte[] utf8 = Utf8.toBytes(testCharacter);
+ assertArrayEquals(utf8, encoded);
+ }
+ byte[] stringAsUtf8 = Utf8.toBytes(TEST_STRING);
+ byte[] handEncoded = new byte[Utf8.byteCount(TEST_STRING)];
+ for (int i = 0, j = 0; i < TEST_STRING.length(); i = TEST_STRING.offsetByCodePoints(i, 1)) {
+ j = Utf8.encode(TEST_STRING.codePointAt(i), handEncoded, j);
+ }
+ assertArrayEquals(stringAsUtf8, handEncoded);
+ }
+
+ @Test
+ public void testStreamEncoding() throws IOException {
+ for (int c : TEST_CODEPOINTS) {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ Utf8.encode(c, buffer);
+ byte[] encoded = buffer.toByteArray();
+ String testCharacter = makeString(c);
+ byte[] utf8 = Utf8.toBytes(testCharacter);
+ assertArrayEquals(utf8, encoded);
+ }
+ byte[] stringAsUtf8 = Utf8.toBytes(TEST_STRING);
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ for (int i = 0; i < TEST_STRING.length(); i = TEST_STRING.offsetByCodePoints(i, 1)) {
+ Utf8.encode(TEST_STRING.codePointAt(i), buffer);
+ }
+ byte[] handEncoded = buffer.toByteArray();
+ assertArrayEquals(stringAsUtf8, handEncoded);
+ }
+
+ @Test
+ public void testByteBufferEncoding() {
+ for (int c : TEST_CODEPOINTS) {
+ ByteBuffer buffer = ByteBuffer.allocate(4);
+ Utf8.encode(c, buffer);
+ byte[] encoded = new byte[buffer.position()];
+ buffer.flip();
+ for (int i = 0; i < encoded.length; ++i) {
+ encoded[i] = buffer.get();
+ }
+ String testCharacter = makeString(c);
+ byte[] utf8 = Utf8.toBytes(testCharacter);
+ assertArrayEquals(utf8, encoded);
+ }
+ byte[] stringAsUtf8 = Utf8.toBytes(TEST_STRING);
+ ByteBuffer buffer = ByteBuffer.allocate(TEST_STRING.length() * 4);
+ for (int i = 0; i < TEST_STRING.length(); i = TEST_STRING.offsetByCodePoints(i, 1)) {
+ Utf8.encode(TEST_STRING.codePointAt(i), buffer);
+ }
+ byte[] handEncoded = new byte[buffer.position()];
+ buffer.flip();
+ for (int i = 0; i < handEncoded.length; ++i) {
+ handEncoded[i] = buffer.get();
+ }
+ assertArrayEquals(stringAsUtf8, handEncoded);
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/XMLTestCase.java b/vespajlib/src/test/java/com/yahoo/text/XMLTestCase.java
new file mode 100644
index 00000000000..6320efc2f11
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/XMLTestCase.java
@@ -0,0 +1,115 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.Test;
+
+import java.io.StringReader;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+
+/**
+ * @author <a href="mailto:borud@yahoo-inc.com">Bjorn Borud</a>
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class XMLTestCase {
+
+ @Test
+ public void testSimple() {
+ String s1 = "this is a < test";
+ String s2 = "this is a & test";
+ String s3 = "this is a \" test";
+ String s4 = "this is a <\" test";
+ String s5 = "this is a low \u001F test";
+
+ assertEquals("this is a &lt; test", XML.xmlEscape(s1, true));
+ assertEquals("this is a &amp; test", XML.xmlEscape(s2, true));
+
+ // quotes are only escaped in attributes
+ //
+ assertEquals("this is a &quot; test", XML.xmlEscape(s3, true));
+ assertEquals("this is a \" test", XML.xmlEscape(s3, false));
+
+ // quotes are only escaped in attributes. prevent bug
+ // no. 187006 from happening again!
+ //
+ assertEquals("this is a &lt;&quot; test", XML.xmlEscape(s4, true));
+ assertEquals("this is a &lt;\" test", XML.xmlEscape(s4, false));
+
+ assertEquals("this is a low \uFFFD test", XML.xmlEscape(s5, false));
+ String s = XML.xmlEscape(s5, false, false);
+ assertEquals(0x1F, s.toCharArray()[14]);
+ }
+
+ @Test
+ public void testInvalidUnicode() {
+ assertEquals("a\ufffd\ufffdb",XML.xmlEscape("a\uffff\uffffb", false));
+ }
+
+ @Test
+ public void testInvalidUnicodeAlongWithEscaping() {
+ assertEquals("a\ufffd\ufffdb&amp;",XML.xmlEscape("a\ufffe\uffffb&", false));
+ }
+
+ @Test
+ public void testWhenFirstCharacterMustBeEscaped() {
+ assertEquals("&amp;co", XML.xmlEscape("&co", false));
+ assertEquals("\ufffd is a perfectly fine character;",
+ XML.xmlEscape("\u0000 is a perfectly fine character;", false));
+ }
+
+ @Test
+ public void testLineNoise() {
+ assertEquals("\ufffda\ufffd\ufffd\ufffdb&amp;\u380c\ufb06\uD87E\uDDF2\ufffd \ufffd",
+ XML.xmlEscape("\u0001a\u0000\ufffe\uffffb&\u380c\ufb06\uD87E\uDDF2\uD87E \uD87E", false));
+ }
+
+ @Test
+ public void testZeroLength() {
+ assertEquals("", XML.xmlEscape("", false));
+ }
+
+ @Test
+ public void testAllEscaped() {
+ assertEquals("&amp;\ufffd\ufffd", XML.xmlEscape("&\u0000\uffff", false));
+ }
+
+ @Test
+ public void testNoneEscaped() {
+ assertEquals("a\ud87e\uddf2\u00e5", XML.xmlEscape("a\ud87e\uddf2\u00e5", false));
+ }
+
+ @Test
+ public void testReturnSameIfNoQuoting() {
+ String a = "abc";
+ String b = XML.xmlEscape(a, false);
+ assertSame("xmlEscape should return its input if no change is necessary.",
+ a, b);
+ }
+
+ @Test
+ public void testValidAttributeNames() {
+ assertTrue(XML.isName(":A_a\u00C0\u00D8\u00F8\u0370\u037F\u200C\u2070\u2C00\u3001\uF900\uFDF0\uD800\uDC00"));
+ assertFalse(XML.isName(" "));
+ assertFalse(XML.isName(": "));
+ assertTrue(XML.isName("sss"));
+ }
+
+ @Test
+ public void testExceptionContainingLineNumberAndColumnNumber() {
+ final String invalidXml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
+ "<foo";
+ try {
+ XML.getDocument(new StringReader(invalidXml));
+ fail("Did not get expected exception");
+ } catch (IllegalArgumentException e) {
+ System.out.println(e.getMessage());
+ assertTrue(e.getMessage().contains("Could not parse '"));
+ assertTrue(e.getMessage().contains("error at line 2, column 5"));
+ }
+ }
+}
diff --git a/vespajlib/src/test/java/com/yahoo/text/XMLWriterTestCase.java b/vespajlib/src/test/java/com/yahoo/text/XMLWriterTestCase.java
new file mode 100644
index 00000000000..dc3530fdd97
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/text/XMLWriterTestCase.java
@@ -0,0 +1,178 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.text;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.StringWriter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * This test is currently incomplete. Also much tested in the prelude module though.
+ *
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ */
+@SuppressWarnings("deprecation")
+public class XMLWriterTestCase {
+
+ private XMLWriter xml;
+
+ @Before
+ public void setUp() {
+ xml = new XMLWriter(new StringWriter());
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void test3Levels() {
+ xml.openTag("l1").openTag("l2").openTag("l3").closeTag().closeTag().closeTag();
+ assertEquals(
+ "<l1>\n" +
+ "\n" +
+ " <l2>\n" +
+ " <l3/>\n" +
+ " </l2>\n" +
+ "\n" +
+ "</l1>\n"
+ , getWritten());
+ }
+
+ private String getWritten() {
+ xml.close();
+ return xml.getWrapped().toString();
+ }
+
+ @Test
+ public void test3LevelsCustomFormatting() {
+ xml=new XMLWriter(new StringWriter(),1,-1);
+ xml.openTag("l1").openTag("l2").openTag("l3").closeTag().closeTag().closeTag();
+ assertEquals(
+ "<l1>\n" +
+ " <l2>\n" +
+ " <l3/>\n" +
+ " </l2>\n" +
+ "</l1>\n"
+ , getWritten());
+ }
+
+ @Test
+ public void test4LevelsA() {
+ xml.openTag("l1");
+ xml.openTag("l21").closeTag();
+ xml.openTag("l22");
+ xml.openTag("l31").openTag("l4").closeTag().closeTag();
+ xml.openTag("l32").closeTag();
+ xml.closeTag();
+ xml.closeTag();
+ assertEquals(
+ "<l1>\n" +
+ "\n" +
+ " <l21/>\n" +
+ "\n" +
+ " <l22>\n" +
+ " <l31>\n" +
+ " <l4/>\n" +
+ " </l31>\n" +
+ " <l32/>\n" +
+ " </l22>\n" +
+ "\n" +
+ "</l1>\n"
+ , getWritten());
+ }
+
+ @Test
+ public void test4LevelsB() {
+ xml.openTag("l1");
+ xml.openTag("l21");
+ xml.openTag("l31").closeTag();
+ xml.openTag("l32").openTag("l4").closeTag().closeTag();
+ xml.closeTag();
+ xml.openTag("l22").closeTag();
+ xml.closeTag();
+ assertEquals(
+ "<l1>\n" +
+ "\n" +
+ " <l21>\n" +
+ " <l31/>\n" +
+ " <l32>\n" +
+ " <l4/>\n" +
+ " </l32>\n" +
+ " </l21>\n" +
+ "\n" +
+ " <l22/>\n" +
+ "\n" +
+ "</l1>\n"
+ , getWritten());
+ }
+
+ @Test
+ public void testEmpty() {
+ xml.openTag("l1").closeTag();
+ assertEquals(
+ "<l1/>\n"
+ , getWritten());
+ }
+
+ @Test
+ public void checkHeader() {
+ xml.xmlHeader("utf-8");
+ assertEquals("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n", getWritten());
+ }
+
+ @Test
+ public void forcedAttribute() {
+ xml.openTag("a").forceAttribute(new Utf8String("nalle"), "\"").closeTag();
+ assertEquals("<a nalle=\"&quot;\"/>\n", getWritten());
+ }
+
+ @Test
+ public void attributeString() {
+ xml.openTag("a").attribute(new Utf8String("nalle"), new Utf8String("b")).closeTag();
+ assertEquals("<a nalle=\"b\"/>\n", getWritten());
+ }
+
+ @Test
+ public void attributeLong() {
+ xml.openTag("a").attribute(new Utf8String("nalle"), 5L).closeTag();
+ assertEquals("<a nalle=\"5\"/>\n", getWritten());
+ }
+
+ @Test
+ public void attributeBoolean() {
+ xml.openTag("a").attribute(new Utf8String("nalle"), true).closeTag();
+ assertEquals("<a nalle=\"true\"/>\n", getWritten());
+ }
+
+ @Test
+ public void content() {
+ xml.content("a\na", false).content("a\na", true);
+ assertEquals("a\naa\na", getWritten());
+ }
+
+ @Test
+ public void escapedContent() {
+ xml.escapedContent("a&\na", false).escapedContent("a&\na", true);
+ assertEquals("a&\naa&\na", getWritten());
+ }
+
+ @Test
+ public void escapedAsciiContent() {
+ xml.escapedAsciiContent("a&\na", false).escapedAsciiContent("a&\na", true);
+ assertEquals("a&\naa&\na", getWritten());
+ }
+
+ @Test
+ public void isIn() {
+ assertFalse(xml.isIn("a"));
+ xml.openTag("a");
+ assertTrue(xml.isIn("a"));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/time/WallClockSourceTestCase.java b/vespajlib/src/test/java/com/yahoo/time/WallClockSourceTestCase.java
new file mode 100644
index 00000000000..e26591c9c64
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/time/WallClockSourceTestCase.java
@@ -0,0 +1,86 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.time;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
+public class WallClockSourceTestCase {
+
+ @Test
+ public void testSimple() {
+ long actualBefore = System.currentTimeMillis();
+ WallClockSource clock = new WallClockSource();
+ long nanos = clock.currentTimeNanos();
+ long micros = nanos / 1000;
+ long millis = micros / 1000;
+ long actualAfter = System.currentTimeMillis();
+
+ assertTrue(actualBefore <= millis);
+ assertTrue(millis <= actualAfter);
+ }
+
+ @Test
+ public void testWithAdjust() {
+ WallClockSource clock = new WallClockSource();
+ long diffB = 0;
+ long diffA = 0;
+ for (int i = 0; i < 66666; i++) {
+ long actualB = System.currentTimeMillis();
+ clock.adjust();
+ long nanos = clock.currentTimeNanos();
+ long actualA = System.currentTimeMillis();
+ long micros = nanos / 1000;
+ long millis = micros / 1000;
+ diffB = Math.max(diffB, actualB - millis);
+ diffA = Math.max(diffA, millis - actualA);
+ // System.out.println("adj Timing values, before: "+actualB+" <= guess: "+millis+" <= after: "+actualA);
+ }
+ System.out.println("adjust test: biggest difference (beforeTime - guess): "+diffB);
+ System.out.println("adjust test: biggest difference (guess - afterTime): "+diffA);
+ assertTrue("actual time before sample must be <= wallclocksource, diff: " + diffB, diffB < 2);
+ assertTrue("actual time after sample must be >= wallclocksource, diff: " + diffA, diffA < 2);
+ }
+
+ @Test
+ public void testNoAdjust() {
+ WallClockSource clock = new WallClockSource();
+ long diffB = 0;
+ long diffA = 0;
+ for (int i = 0; i < 66666; i++) {
+ long actualB = System.currentTimeMillis();
+ long nanos = clock.currentTimeNanos();
+ long actualA = System.currentTimeMillis();
+ long micros = nanos / 1000;
+ long millis = micros / 1000;
+ diffB = Math.max(diffB, actualB - millis);
+ diffA = Math.max(diffA, millis - actualA);
+ // System.out.println("noadj Timing values, before: "+actualB+" <= guess: "+millis+" <= after: "+actualA);
+ }
+ System.out.println("noadjust test: biggest difference (beforeTime - guess): "+diffB);
+ System.out.println("noadjust test: biggest difference (guess - afterTime): "+diffA);
+ assertTrue("actual time before sample must be <= wallclocksource, diff: " + diffB, diffB < 3);
+ assertTrue("actual time after sample must be >= wallclocksource, diff: " + diffA, diffA < 3);
+ }
+
+ @Test
+ public void testAutoAdjust() {
+ WallClockSource clock = WallClockSource.get();
+ long diffB = 0;
+ long diffA = 0;
+ for (int i = 0; i < 66666; i++) {
+ long actualB = System.currentTimeMillis();
+ long nanos = clock.currentTimeNanos();
+ long actualA = System.currentTimeMillis();
+ long micros = nanos / 1000;
+ long millis = micros / 1000;
+ diffB = Math.max(diffB, actualB - millis);
+ diffA = Math.max(diffA, millis - actualA);
+ // System.out.println("noadj Timing values, before: "+actualB+" <= guess: "+millis+" <= after: "+actualA);
+ }
+ System.out.println("autoadjust test: biggest difference (beforeTime - guess): "+diffB);
+ System.out.println("autoadjust test: biggest difference (guess - afterTime): "+diffA);
+ assertTrue("actual time before sample must be <= wallclocksource, diff: " + diffB, diffB < 3);
+ assertTrue("actual time after sample must be >= wallclocksource, diff: " + diffA, diffA < 3);
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/transaction/NestedTransactionTestCase.java b/vespajlib/src/test/java/com/yahoo/transaction/NestedTransactionTestCase.java
new file mode 100644
index 00000000000..d75daa506b4
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/transaction/NestedTransactionTestCase.java
@@ -0,0 +1,181 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.transaction;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author bratseth
+ */
+public class NestedTransactionTestCase {
+
+ @Test
+ public void testNestedTransaction() {
+ NestedTransaction t = new NestedTransaction();
+ t.add(new TransactionTypeB("B1"), TransactionTypeC.class);
+ t.add(new TransactionTypeC("C1"));
+ t.add(new TransactionTypeA("A1"), TransactionTypeB.class);
+ t.add(new TransactionTypeA("A2"));
+ t.add(new TransactionTypeC("C2"));
+
+ // Add two tasks to run after commit
+ MutableInteger tasksRun = new MutableInteger();
+ t.onCommitted(() -> { tasksRun.value++; });
+ t.onCommitted(() -> { tasksRun.value++; });
+
+ assertEquals(3, t.transactions().size());
+ assertEquals(TransactionTypeA.class, t.transactions().get(0).getClass());
+ assertEquals(TransactionTypeB.class, t.transactions().get(1).getClass());
+ assertEquals(TransactionTypeC.class, t.transactions().get(2).getClass());
+
+ assertEquals("A1", ((MockOperation)t.transactions().get(0).operations().get(0)).name);
+ assertEquals("A2", ((MockOperation)t.transactions().get(0).operations().get(1)).name);
+ assertEquals("B1", ((MockOperation)t.transactions().get(1).operations().get(0)).name);
+ assertEquals("C1", ((MockOperation)t.transactions().get(2).operations().get(0)).name);
+ assertEquals("C2", ((MockOperation)t.transactions().get(2).operations().get(1)).name);
+
+ t.commit();
+ assertTrue(((MockTransaction)t.transactions().get(0)).committed);
+ assertTrue(((MockTransaction)t.transactions().get(1)).committed);
+ assertTrue(((MockTransaction)t.transactions().get(2)).committed);
+ assertEquals("After commit tasks are run", 2, tasksRun.value);
+ }
+
+ @Test
+ public void testNestedTransactionFailingOnCommit() {
+ NestedTransaction t = new NestedTransaction();
+ t.add(new TransactionTypeC("C1"));
+ t.add(new TransactionTypeA("A1"), TransactionTypeB.class, FailAtCommitTransactionType.class);
+ t.add(new TransactionTypeA("A2"));
+ t.add(new FailAtCommitTransactionType("Fail"), TransactionTypeC.class);
+ t.add(new TransactionTypeC("C2"));
+ t.add(new TransactionTypeB("B1"), TransactionTypeC.class, FailAtCommitTransactionType.class);
+
+ // Add task to run after commit
+ MutableInteger tasksRun = new MutableInteger();
+ t.onCommitted(() -> {
+ tasksRun.value++;
+ });
+
+ assertEquals(4, t.transactions().size());
+ assertEquals(TransactionTypeA.class, t.transactions().get(0).getClass());
+ assertEquals(TransactionTypeB.class, t.transactions().get(1).getClass());
+ assertEquals(FailAtCommitTransactionType.class, t.transactions().get(2).getClass());
+ assertEquals(TransactionTypeC.class, t.transactions().get(3).getClass());
+
+ try { t.commit(); } catch (IllegalStateException expected) { }
+ assertTrue(((MockTransaction)t.transactions().get(0)).rolledback);
+ assertTrue(((MockTransaction)t.transactions().get(1)).rolledback);
+ assertFalse(((MockTransaction) t.transactions().get(2)).committed);
+ assertEquals("After commit tasks are not run", 0, tasksRun.value);
+ }
+
+ @Test
+ public void testConflictingOrdering() {
+ NestedTransaction t = new NestedTransaction();
+ t.add(new TransactionTypeA("A1"), TransactionTypeB.class);
+ t.add(new TransactionTypeB("B1"), TransactionTypeC.class);
+ t.add(new TransactionTypeC("C1"), TransactionTypeA.class);
+ try {
+ t.commit();
+ fail("Expected exception");
+ }
+ catch (IllegalStateException expected) {
+ }
+ }
+
+ private static class TransactionTypeA extends MockTransaction {
+ public TransactionTypeA(String name) { super(name); }
+ }
+
+ private static class TransactionTypeB extends MockTransaction {
+ public TransactionTypeB(String name) { super(name); }
+ }
+
+ private static class TransactionTypeC extends MockTransaction {
+ public TransactionTypeC(String name) { super(name); }
+ }
+
+ private static class FailAtCommitTransactionType extends MockTransaction {
+ public FailAtCommitTransactionType(String name) { super(name); }
+ @Override
+ public void commit() {
+ throw new RuntimeException();
+ }
+ }
+
+ private static class MockTransaction implements Transaction {
+
+ public boolean prepared = false, committed = false, rolledback = false;
+ private List<Operation> operations = new ArrayList<>();
+
+ public MockTransaction(String name) {
+ operations.add(new MockOperation(name));
+ }
+
+ @Override
+ public Transaction add(Operation operation) {
+ operations.add(operation);
+ return this;
+ }
+
+ @Override
+ public Transaction add(List<Operation> operation) {
+ operations.addAll(operation);
+ return this;
+ }
+
+ @Override
+ public List<Operation> operations() {
+ return operations;
+ }
+
+ @Override
+ public void prepare() {
+ prepared = true;
+ }
+
+ @Override
+ public void commit() {
+ if ( ! prepared)
+ throw new IllegalStateException("Commit before prepare");
+ committed = true;
+ }
+
+ @Override
+ public void rollbackOrLog() {
+ if ( ! committed)
+ throw new IllegalStateException("Rollback before commit");
+ rolledback = true;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ }
+
+ private static class MockOperation implements Transaction.Operation {
+
+ public String name;
+
+ public MockOperation(String name) {
+ this.name = name;
+ }
+
+ }
+
+ private static class MutableInteger {
+
+ public int value = 0;
+
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/vespa/objects/BigIdClass.java b/vespajlib/src/test/java/com/yahoo/vespa/objects/BigIdClass.java
new file mode 100644
index 00000000000..3c1065369ca
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/vespa/objects/BigIdClass.java
@@ -0,0 +1,183 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.objects;
+
+import java.nio.ByteBuffer;
+
+public class BigIdClass extends Identifiable
+{
+ public static final int classId = registerClass(42, BigIdClass.class);
+
+ static public final FieldBase fByte = new FieldBase("myByte");
+ static public final FieldBase fShort = new FieldBase("myShort");
+ static public final FieldBase fInt = new FieldBase("myInt");
+ static public final FieldBase fLong = new FieldBase("myLong");
+ static public final FieldBase fFloat = new FieldBase("myFloat");
+ static public final FieldBase fDouble = new FieldBase("myDouble");
+ static public final FieldBase fArrayOne = new FieldBase("myArrayOne");
+ static public final FieldBase fArrayTwo = new FieldBase("myArrayTwo");
+ static public final FieldBase fByteBuffer = new FieldBase("myByteBuffer");
+ static public final FieldBase fString = new FieldBase("myString");
+ static public final FieldBase fAlternate = new FieldBase("myAlternate");
+ static public final FieldBase fChildOne = new FieldBase("childOne");
+ static public final FieldBase fChildTwo = new FieldBase("childTwo");
+
+ private byte myByte = 42;
+ private short myShort = 4242;
+ private int myInt = 424242;
+ private long myLong = 9876543210L;
+ private float myFloat = 42.42f;
+ private double myDouble = 42.4242e-42;
+
+ private byte[] myArrayOne = new byte[5];
+ private byte[] myArrayTwo = new byte[10];
+ private ByteBuffer myByteBuffer;
+
+ private String myString = "default-value";
+ private String myAlternate = "some \u2603 Utf8";
+
+ private Identifiable childOne = null;
+ private Identifiable childTwo = new FooBarIdClass();
+
+ @Override
+ public void visitMembers(ObjectVisitor visitor) {
+ super.visitMembers(visitor);
+ visitor.visit("", childOne);
+ visitor.visit("one", childOne);
+ visitor.visit("two", childTwo);
+ visitor.visit(null, childTwo);
+ visitor.visit("myArrayOne", myArrayOne);
+ }
+
+ public BigIdClass() {
+ myArrayOne[0] = 1;
+ myArrayOne[1] = 2;
+ myArrayOne[2] = 3;
+ myArrayOne[3] = 4;
+ myArrayOne[4] = 5;
+
+ myArrayTwo[0] = 6;
+ myArrayTwo[1] = 7;
+ myArrayTwo[2] = 8;
+
+ myArrayTwo[9] = 9;
+ }
+
+ public BigIdClass(int value) {
+ myByte = (byte)value;
+ myShort = (short)value;
+ myInt = value;
+ myLong = value;
+ myLong <<= 30;
+ myLong ^= value;
+ myFloat = (float)(value + 0.000001*value);
+ myDouble = 123456.789*value + 0.987654321*value;
+ myArrayOne[1] = (byte)(value >> 1);
+ myArrayOne[2] = (byte)(value >> 5);
+ myArrayOne[3] = (byte)(value >> 9);
+
+ myArrayTwo[3] = (byte)(value >> 2);
+ myArrayTwo[4] = (byte)(value >> 4);
+ myArrayTwo[5] = (byte)(value >> 6);
+ myArrayTwo[6] = (byte)(value >> 8);
+
+ myString = Integer.toString(value);
+ myAlternate = "a \u2603 " + Integer.toString(value) + " b";
+
+ childOne = new FooBarIdClass();
+ childTwo = null;
+ }
+
+ @Override
+ protected int onGetClassId() {
+ return classId;
+ }
+
+ @Override
+ protected void onSerialize(Serializer buf) {
+ buf.putByte(fByte, myByte);
+ buf.putShort(fShort, myShort);
+ buf.putInt(fInt, myInt);
+ buf.putLong(fLong, myLong);
+ buf.putFloat(fFloat, myFloat);
+ buf.putDouble(fDouble, myDouble);
+ buf.put(fArrayOne, myArrayOne);
+ buf.put(fArrayTwo, myArrayTwo);
+ /* buf.put(fByteBuffer, myByteBuffer); */
+ buf.put(fString, myString);
+ putUtf8(buf, myAlternate);
+
+ serializeOptional(buf, childOne);
+ serializeOptional(buf, childTwo);
+ }
+
+ @Override
+ protected void onDeserialize(Deserializer buf) {
+
+ myByte = buf.getByte(fByte);
+ myShort = buf.getShort(fShort);
+ myInt = buf.getInt(fInt);
+ myLong = buf.getLong(fLong);
+ myFloat = buf.getFloat(fFloat);
+ myDouble = buf.getDouble(fDouble);
+
+ myArrayOne = buf.getBytes(fArrayOne, 5);
+ myArrayTwo = buf.getBytes(fArrayTwo, 10);
+
+ myString = buf.getString(fString);
+ myAlternate = getUtf8(buf);
+
+ childOne = deserializeOptional(buf);
+ childTwo = deserializeOptional(buf);
+ }
+
+ public boolean equals(Object other) {
+ if (super.equals(other) && other instanceof BigIdClass) {
+ boolean allEq = true;
+ BigIdClass o = (BigIdClass)other;
+ if (myByte != o.myByte) { allEq = false; }
+ if (myShort != o.myShort) { allEq = false; }
+ if (myInt != o.myInt) { allEq = false; }
+ if (myLong != o.myLong) { allEq = false; }
+ if (myFloat != o.myFloat) { allEq = false; }
+ if (myDouble != o.myDouble) { allEq = false; }
+ if (! myString.equals(o.myString)) { allEq = false; }
+ if (! equals(childOne, o.childOne)) { allEq = false; }
+ if (! equals(childTwo, o.childTwo)) { allEq = false; }
+ if (childTwo != null && o.childTwo == null) { allEq = false; }
+ return allEq;
+ }
+ return false;
+ }
+
+/***
+ public boolean diff(BigIdClass o) {
+ boolean allEq = true;
+
+ if (myByte != o.myByte) { System.out.println("myByte differ: "+myByte+" != "+o.myByte); allEq = false; }
+ if (myShort != o.myShort) { System.out.println("myShort differ: "+myShort+" != "+o.myShort); allEq = false; }
+ if (myInt != o.myInt) { System.out.println("myInt differ: "+myInt+" != "+o.myInt); allEq = false; }
+ if (myLong != o.myLong) { System.out.println("myLong differ: "+myLong+" != "+o.myLong); allEq = false; }
+ if (myFloat != o.myFloat) { System.out.println("myFloat differ: "+myFloat+" != "+o.myFloat); allEq = false; }
+ if (myDouble != o.myDouble) { System.out.println("myDouble differ: "+myDouble+" != "+o.myDouble); allEq = false; }
+ if (! myString.equals(o.myString)) { System.out.println("myString differ: "+myString+" != "+o.myString); allEq = false; }
+ if (childOne == null && o.childOne != null) {
+ System.err.println("childOne is null, o.childOne is: "+o.childOne);
+ allEq = false;
+ }
+ if (childOne != null && o.childOne == null) {
+ System.err.println("o.childOne is null, childOne is: "+childOne);
+ allEq = false;
+ }
+ if (childTwo == null && o.childTwo != null) {
+ System.err.println("childTwo is null, o.childTwo is: "+o.childTwo);
+ allEq = false;
+ }
+ if (childTwo != null && o.childTwo == null) {
+ System.err.println("o.childTwo is null, childTwo is: "+childTwo);
+ allEq = false;
+ }
+ return allEq;
+ }
+***/
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/vespa/objects/FieldBaseTestCase.java b/vespajlib/src/test/java/com/yahoo/vespa/objects/FieldBaseTestCase.java
new file mode 100644
index 00000000000..d60184c5616
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/vespa/objects/FieldBaseTestCase.java
@@ -0,0 +1,50 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.objects;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author arnej27959
+ */
+public class FieldBaseTestCase {
+
+ @Test
+ public void testFieldBaseAPI() {
+ String s1 = "test";
+ FieldBase f1 = new FieldBase(s1);
+ FieldBase f2 = new FieldBase("tESt");
+ FieldBase f3 = new FieldBase("TEST");
+
+ assertThat(f1.getName(), is(s1));
+ assertThat(f1, equalTo(f1));
+ assertThat(f1, equalTo(new FieldBase("test")));
+ assertThat(f1, equalTo(f2));
+ assertThat(f1, equalTo(f3));
+
+ assertThat(f1.hashCode(), equalTo(s1.hashCode()));
+ assertThat(f1.hashCode(), equalTo(f2.hashCode()));
+ assertThat(f1.hashCode(), equalTo(f3.hashCode()));
+
+ assertThat(f1.toString(), equalTo("field test"));
+
+ FieldBase f4 = new FieldBase("foo");
+ FieldBase f5 = new FieldBase("bar");
+ FieldBase f6 = new FieldBase("qux");
+
+ assertThat(f1, not(equalTo(f4)));
+ assertThat(f1, not(equalTo(f5)));
+ assertThat(f1, not(equalTo(f6)));
+
+ assertThat(f1.hashCode(), not(equalTo(f4.hashCode())));
+ assertThat(f1.hashCode(), not(equalTo(f5.hashCode())));
+ assertThat(f1.hashCode(), not(equalTo(f6.hashCode())));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/vespa/objects/FooBarIdClass.java b/vespajlib/src/test/java/com/yahoo/vespa/objects/FooBarIdClass.java
new file mode 100644
index 00000000000..9aa5716d4ee
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/vespa/objects/FooBarIdClass.java
@@ -0,0 +1,35 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.objects;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class FooBarIdClass extends Identifiable
+{
+ public static final int classId = registerClass(17, FooBarIdClass.class);
+
+ private String foo = "def-foo";
+ private int bar = 42;
+
+ private List<Integer> lst = new ArrayList<>();
+
+ public FooBarIdClass() {
+ lst.add(17);
+ lst.add(42);
+ lst.add(666);
+ }
+
+ @Override
+ protected int onGetClassId() {
+ return classId;
+ }
+
+ @Override
+ public void visitMembers(ObjectVisitor visitor) {
+ super.visitMembers(visitor);
+ visitor.visit("foo", foo);
+ visitor.visit("bar", bar);
+ visitor.visit("lst", lst);
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/vespa/objects/ObjectDumperTestCase.java b/vespajlib/src/test/java/com/yahoo/vespa/objects/ObjectDumperTestCase.java
new file mode 100644
index 00000000000..03a03ba063f
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/vespa/objects/ObjectDumperTestCase.java
@@ -0,0 +1,161 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.objects;
+
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author arnej27959
+ */
+public class ObjectDumperTestCase {
+
+ @Test
+ public void testSimple() {
+ String s1 = "test";
+
+ ObjectDumper defOD = new ObjectDumper();
+ ObjectDumper oneOD = new ObjectDumper(1);
+
+ defOD.visit("s1", s1);
+ oneOD.visit("s2", s1);
+
+ assertEquals("s1: 'test'\n", defOD.toString());
+ assertEquals("s2: 'test'\n", oneOD.toString());
+ }
+
+ @Test
+ public void testBig() {
+ BigIdClass b = new BigIdClass();
+ ObjectDumper oneOD = new ObjectDumper(1);
+
+ oneOD.visit("biggie", b);
+
+ assertThat(oneOD.toString(), equalTo(
+"biggie: BigIdClass {\n"+
+" classId: 42\n"+
+" : <NULL>\n"+
+" one: <NULL>\n"+
+" two: FooBarIdClass {\n"+
+" classId: 17\n"+
+" foo: 'def-foo'\n"+
+" bar: 42\n"+
+" lst: List {\n"+
+" [0]: 17\n"+
+" [1]: 42\n"+
+" [2]: 666\n"+
+" }\n"+
+" }\n"+
+" FooBarIdClass {\n"+
+" classId: 17\n"+
+" foo: 'def-foo'\n"+
+" bar: 42\n"+
+" lst: List {\n"+
+" [0]: 17\n"+
+" [1]: 42\n"+
+" [2]: 666\n"+
+" }\n"+
+" }\n"+
+" myArrayOne: byte[] {\n"+
+" [0]: 1\n"+
+" [1]: 2\n"+
+" [2]: 3\n"+
+" [3]: 4\n"+
+" [4]: 5\n"+
+" }\n"+
+"}\n"));
+
+ ObjectDumper defOD = new ObjectDumper();
+ defOD.visit("", b);
+ assertThat(b.toString(), equalTo(b.toString()));
+ }
+
+ @Test
+ public void testOne() {
+ SomeIdClass s3 = new SomeIdClass();
+
+ ObjectDumper defOD = new ObjectDumper();
+ ObjectDumper oneOD = new ObjectDumper(1);
+
+ defOD.visit("s3", s3);
+ oneOD.visit("s4", s3);
+
+ assertEquals("s3: SomeIdClass {\n classId: 1234321\n}\n", defOD.toString());
+ assertEquals("s4: SomeIdClass {\n classId: 1234321\n}\n", oneOD.toString());
+ }
+
+ @Test
+ public void testTwo() {
+ FooBarIdClass s5 = new FooBarIdClass();
+
+ ObjectDumper defOD = new ObjectDumper();
+ ObjectDumper oneOD = new ObjectDumper(1);
+
+ defOD.visit("s5", s5);
+ oneOD.visit("s6", s5);
+
+ assertThat(defOD.toString(), is("s5: FooBarIdClass {\n"+
+ " classId: 17\n"+
+ " foo: 'def-foo'\n"+
+ " bar: 42\n"+
+ " lst: List {\n"+
+ " [0]: 17\n"+
+ " [1]: 42\n"+
+ " [2]: 666\n"+
+ " }\n"+
+ "}\n"));
+ assertThat(oneOD.toString(), is("s6: FooBarIdClass {\n"+
+ " classId: 17\n"+
+ " foo: 'def-foo'\n"+
+ " bar: 42\n"+
+ " lst: List {\n"+
+ " [0]: 17\n"+
+ " [1]: 42\n"+
+ " [2]: 666\n"+
+ " }\n"+
+ "}\n"));
+
+ }
+
+ @Test
+ public void testRegistry() {
+ assertThat(FooBarIdClass.classId, is(17));
+ int x = Identifiable.registerClass(17, FooBarIdClass.class);
+ assertThat(x, is(17));
+ boolean caught = false;
+ try {
+ x = Identifiable.registerClass(17, SomeIdClass.class);
+ } catch (IllegalArgumentException e) {
+ caught = true;
+ assertThat(e.getMessage(), is(
+"Can not register class 'class com.yahoo.vespa.objects.SomeIdClass' with id 17,"+
+" because it already maps to class 'class com.yahoo.vespa.objects.FooBarIdClass'."));
+ }
+ assertThat(x, is(17));
+ assertThat(caught, is(true));
+
+ Identifiable s7 = Identifiable.createFromId(17);
+ ObjectDumper defOD = new ObjectDumper();
+ defOD.visit("s7", s7);
+ assertThat(defOD.toString(), is("s7: FooBarIdClass {\n"+
+ " classId: 17\n"+
+ " foo: 'def-foo'\n"+
+ " bar: 42\n"+
+ " lst: List {\n"+
+ " [0]: 17\n"+
+ " [1]: 42\n"+
+ " [2]: 666\n"+
+ " }\n"+
+ "}\n"));
+
+ Identifiable nsi = Identifiable.createFromId(717273);
+ assertThat(nsi, is((Identifiable)null));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/vespa/objects/SerializeTestCase.java b/vespajlib/src/test/java/com/yahoo/vespa/objects/SerializeTestCase.java
new file mode 100644
index 00000000000..122cdf24a89
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/vespa/objects/SerializeTestCase.java
@@ -0,0 +1,143 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.objects;
+
+import com.yahoo.io.GrowableByteBuffer;
+import java.nio.ByteOrder;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author arnej27959
+ */
+public class SerializeTestCase {
+
+ @Test
+ public void testSimple() {
+ SomeIdClass s = new SomeIdClass();
+ BufferSerializer buf = new BufferSerializer(new GrowableByteBuffer());
+ s.serialize(buf);
+ buf.flip();
+ s.deserialize(buf);
+ }
+
+ @Test
+ public void testOne() {
+ SomeIdClass s = new SomeIdClass();
+ BufferSerializer buf = new BufferSerializer(new GrowableByteBuffer());
+ s.serializeWithId(buf);
+ buf.flip();
+ Identifiable s2 = Identifiable.create(buf);
+ assertThat((s2 instanceof SomeIdClass), is(true));
+ }
+
+ @Test
+ public void testUnderflow() {
+ BufferSerializer buf = new BufferSerializer(new GrowableByteBuffer());
+ buf.putByte(null, (byte)123);
+ buf.flip();
+ boolean caught = false;
+ try {
+ byte[] val = buf.getBytes(null, 2);
+ } catch (IllegalArgumentException e) {
+ // System.out.println(e);
+ caught = true;
+ }
+ assertThat(caught, is(true));
+ }
+
+ @Test
+ public void testIdNotFound() {
+ BufferSerializer buf = new BufferSerializer(new GrowableByteBuffer());
+ buf.putInt(null, 717273);
+ buf.flip();
+ boolean caught = false;
+ try {
+ Identifiable nsi = Identifiable.create(buf);
+ } catch (IllegalArgumentException e) {
+ // System.out.println(e);
+ caught = true;
+ }
+ assertThat(caught, is(true));
+ }
+
+ @Test
+ public void testOrdering() {
+ BufferSerializer buf = new BufferSerializer(new GrowableByteBuffer());
+ assertThat(buf.order(), is(ByteOrder.BIG_ENDIAN));
+ buf.putInt(null, 0x11223344);
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+ buf.putInt(null, 0x55667788);
+ assertThat(buf.order(), is(ByteOrder.LITTLE_ENDIAN));
+ buf.flip();
+ assertThat(buf.getByte(null), is((byte)0x11));
+ assertThat(buf.getByte(null), is((byte)0x22));
+ assertThat(buf.getShort(null), is((short)0x4433));
+ buf.order(ByteOrder.BIG_ENDIAN);
+ assertThat(buf.getByte(null), is((byte)0x88));
+ assertThat(buf.getByte(null), is((byte)0x77));
+ assertThat(buf.getShort(null), is((short)0x6655));
+ }
+
+ @Test
+ public void testBig() {
+ BigIdClass dv = new BigIdClass();
+ BigIdClass ov = new BigIdClass(6667666);
+ BigIdClass bv = new BigIdClass(123456789);
+
+ assertThat(BigIdClass.classId, is(42));
+ assertThat(dv.getClassId(), is(42));
+ assertThat(ov.getClassId(), is(42));
+ assertThat(bv.getClassId(), is(42));
+
+ assertThat(ov.equals(dv), is(false));
+ assertThat(dv.equals(bv), is(false));
+ assertThat(bv.equals(ov), is(false));
+
+ BufferSerializer buf = new BufferSerializer(new GrowableByteBuffer());
+ ov.serialize(buf);
+ buf.flip();
+ dv.deserialize(buf);
+ assertThat(ov, equalTo(dv));
+ assertThat(dv, equalTo(ov));
+ buf = new BufferSerializer(new GrowableByteBuffer());
+ bv.serializeWithId(buf);
+ buf.flip();
+ dv.deserializeWithId(buf);
+ assertThat(bv, equalTo(dv));
+ assertThat(dv, equalTo(bv));
+
+ buf = new BufferSerializer(new GrowableByteBuffer());
+ SomeIdClass s = new SomeIdClass();
+ assertThat(dv.equals(s), is(false));
+ assertThat(ov.equals(s), is(false));
+ assertThat(bv.equals(s), is(false));
+ assertThat(dv.equals(new Object()), is(false));
+
+ s.serializeWithId(buf);
+ buf.flip();
+ boolean caught = false;
+ try {
+ dv.deserializeWithId(buf);
+ } catch (IllegalArgumentException ex) {
+ caught = true;
+ // System.out.println(ex);
+ }
+ assertThat(caught, is(true));
+ buf = new BufferSerializer(new GrowableByteBuffer());
+ buf.putLong(null, 0x7777777777777777L);
+ buf.flip();
+ caught = false;
+ try {
+ dv.deserializeWithId(buf);
+ } catch (IllegalArgumentException ex) {
+ caught = true;
+ // System.out.println(ex);
+ }
+ assertThat(caught, is(true));
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/vespa/objects/SomeIdClass.java b/vespajlib/src/test/java/com/yahoo/vespa/objects/SomeIdClass.java
new file mode 100644
index 00000000000..6c24ba1367d
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/vespa/objects/SomeIdClass.java
@@ -0,0 +1,13 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.objects;
+
+public class SomeIdClass extends Identifiable
+{
+ public static final int classId = registerClass(1234321, SomeIdClass.class);
+
+ @Override
+ protected int onGetClassId() {
+ return classId;
+ }
+
+}