add some test cases for util package (#70)

* add check for CollectionUtil.subSet from > to

Change-Id: I32d83f5738250eca23ae16c2ccbf52059a874bf6
diff --git a/src/main/java/com/baidu/hugegraph/util/CollectionUtil.java b/src/main/java/com/baidu/hugegraph/util/CollectionUtil.java
index 5a1c6a6..85ab87c 100644
--- a/src/main/java/com/baidu/hugegraph/util/CollectionUtil.java
+++ b/src/main/java/com/baidu/hugegraph/util/CollectionUtil.java
@@ -96,7 +96,13 @@
     }
 
     public static boolean allUnique(Collection<?> collection) {
-        return collection.stream().allMatch(new HashSet<>()::add);
+        HashSet<Object> set = new HashSet<>(collection.size());
+        for (Object elem : collection) {
+            if (!set.add(elem)) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
@@ -108,10 +114,15 @@
      * @return sub-set of original set [from, to)
      */
     public static <T> Set<T> subSet(Set<T> original, int from, int to) {
-        List<T> list = new ArrayList<>(original);
-        if (to == -1) {
+        E.checkArgument(from >= 0,
+                        "Invalid from parameter of subSet(): %s", from);
+        if (to < 0) {
             to = original.size();
+        } else {
+            E.checkArgument(to >= from,
+                            "Invalid to parameter of subSet(): %s", to);
         }
+        List<T> list = new ArrayList<>(original);
         return new LinkedHashSet<>(list.subList(from, to));
     }
 
diff --git a/src/main/java/com/baidu/hugegraph/util/DateUtil.java b/src/main/java/com/baidu/hugegraph/util/DateUtil.java
index 2295101..a3f117a 100644
--- a/src/main/java/com/baidu/hugegraph/util/DateUtil.java
+++ b/src/main/java/com/baidu/hugegraph/util/DateUtil.java
@@ -19,7 +19,6 @@
 
 package com.baidu.hugegraph.util;
 
-import java.text.ParseException;
 import java.util.Date;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -46,13 +45,7 @@
     public static Date parse(String value) {
         for (Map.Entry<String, String> entry : VALID_DFS.entrySet()) {
             if (value.matches(entry.getKey())) {
-                try {
-                    return parse(value, entry.getValue());
-                } catch (ParseException e) {
-                    throw new IllegalArgumentException(String.format(
-                              "%s, expect format: %s",
-                              e.getMessage(), entry.getValue()));
-                }
+                return parse(value, entry.getValue());
             }
         }
         throw new IllegalArgumentException(String.format(
@@ -60,7 +53,7 @@
                   VALID_DFS.values(), value));
     }
 
-    public static Date parse(String value, String df) throws ParseException {
+    public static Date parse(String value, String df) {
         SafeDateFormat dateFormat = getDateFormat(df);
         return dateFormat.parse(value);
     }
diff --git a/src/main/java/com/baidu/hugegraph/util/NumericUtil.java b/src/main/java/com/baidu/hugegraph/util/NumericUtil.java
index f1b4f88..b07138a 100644
--- a/src/main/java/com/baidu/hugegraph/util/NumericUtil.java
+++ b/src/main/java/com/baidu/hugegraph/util/NumericUtil.java
@@ -290,6 +290,14 @@
     }
 
     public static boolean isNumber(Class<?> clazz) {
+        if (clazz.isPrimitive()) {
+            if (clazz == int.class || clazz == long.class ||
+                clazz == float.class || clazz == double.class ||
+                clazz == short.class || clazz == byte.class) {
+                return true;
+            }
+            return false;
+        }
         return Number.class.isAssignableFrom(clazz);
     }
 
@@ -321,11 +329,21 @@
      *          a negative int if first is numerically less than second;
      *          a positive int if first is numerically greater than second.
      */
-    @SuppressWarnings("unchecked")
     public static int compareNumber(Object first, Number second) {
+        if (first == null) {
+            E.checkArgument(first != null,
+                            "The first parameter can't be null");
+        }
+        if (second == null) {
+            E.checkArgument(second != null,
+                            "The second parameter can't be null");
+        }
+
         if (first instanceof Number && first instanceof Comparable &&
             first.getClass().equals(second.getClass())) {
-            return ((Comparable<Number>) first).compareTo(second);
+            @SuppressWarnings("unchecked")
+            Comparable<Number> cmpFirst = (Comparable<Number>) first;
+            return cmpFirst.compareTo(second);
         }
 
         Function<Object, BigDecimal> toBig = (number) -> {
@@ -333,7 +351,7 @@
                 return new BigDecimal(number.toString());
             } catch (NumberFormatException e) {
                 throw new IllegalArgumentException(String.format(
-                          "Can't compare between %s and %s, " +
+                          "Can't compare between '%s' and '%s', " +
                           "they must be numbers", first, second));
             }
         };
diff --git a/src/main/java/com/baidu/hugegraph/util/VersionUtil.java b/src/main/java/com/baidu/hugegraph/util/VersionUtil.java
index 891cab0..94e4d3a 100644
--- a/src/main/java/com/baidu/hugegraph/util/VersionUtil.java
+++ b/src/main/java/com/baidu/hugegraph/util/VersionUtil.java
@@ -23,7 +23,6 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.net.URL;
-import java.util.Objects;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
 
@@ -85,11 +84,16 @@
           // Class not from JAR
           return null;
         }
-        // Get manifest
         int offset = classPath.lastIndexOf("!");
         assert offset > 0;
-        String manifestPath = classPath.substring(0, offset + 1) +
-                              "/META-INF/MANIFEST.MF";
+        // Get manifest file path
+        String manifestPath = classPath.substring(0, offset + 1);
+        return getImplementationVersion(manifestPath);
+    }
+
+    public static String getImplementationVersion(String manifestPath) {
+        manifestPath += "/META-INF/MANIFEST.MF";
+
         Manifest manifest = null;
         try {
             manifest = new Manifest(new URL(manifestPath).openStream());
@@ -158,13 +162,24 @@
 
         /************************** Version define **************************/
 
-        private String version;
+        private final String version;
+        private final int[] parts;
 
         public Version(String version) {
             E.checkArgumentNotNull(version, "The version is null");
             E.checkArgument(version.matches("[0-9]+(\\.[0-9]+)*"),
                             "Invalid version format: %s", version);
             this.version = version;
+            this.parts = parseVersion(version);
+        }
+
+        private static int[] parseVersion(String version) {
+            String[] parts = version.split("\\.");
+            int[] partsNumber = new int[parts.length];
+            for (int i = 0; i < parts.length; i++) {
+                partsNumber[i] = Integer.parseInt(parts[i]);
+            }
+            return partsNumber;
         }
 
         public final String get() {
@@ -176,14 +191,12 @@
             if (that == null) {
                 return 1;
             }
-            String[] thisParts = this.get().split("\\.");
-            String[] thatParts = that.get().split("\\.");
+            int[] thisParts = this.parts;
+            int[] thatParts = that.parts;
             int length = Math.max(thisParts.length, thatParts.length);
             for (int i = 0; i < length; i++) {
-                int thisPart = i < thisParts.length ?
-                               Integer.parseInt(thisParts[i]) : 0;
-                int thatPart = i < thatParts.length ?
-                               Integer.parseInt(thatParts[i]) : 0;
+                int thisPart = i < thisParts.length ? thisParts[i] : 0;
+                int thatPart = i < thatParts.length ? thatParts[i] : 0;
                 if (thisPart < thatPart) {
                     return -1;
                 }
@@ -210,7 +223,15 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(this.version);
+            int hash = 0;
+            for (int i = this.parts.length - 1; i >= 0; i--) {
+                int part = this.parts[i];
+                if (part == 0 && hash == 0) {
+                    continue;
+                }
+                hash = 31 * hash + Integer.hashCode(part);
+            }
+            return hash;
         }
 
         @Override
diff --git a/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java b/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
index f6855ec..19b12b7 100644
--- a/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
+++ b/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
@@ -54,8 +54,10 @@
 import com.baidu.hugegraph.unit.util.BytesTest;
 import com.baidu.hugegraph.unit.util.CollectionUtilTest;
 import com.baidu.hugegraph.unit.util.DateUtilTest;
+import com.baidu.hugegraph.unit.util.EcheckTest;
 import com.baidu.hugegraph.unit.util.HashUtilTest;
 import com.baidu.hugegraph.unit.util.InsertionOrderUtilTest;
+import com.baidu.hugegraph.unit.util.LogTest;
 import com.baidu.hugegraph.unit.util.LongEncodingTest;
 import com.baidu.hugegraph.unit.util.NumericUtilTest;
 import com.baidu.hugegraph.unit.util.OrderLimitMapTest;
@@ -96,8 +98,10 @@
 
     BytesTest.class,
     CollectionUtilTest.class,
+    EcheckTest.class,
     HashUtilTest.class,
     InsertionOrderUtilTest.class,
+    LogTest.class,
     NumericUtilTest.class,
     ReflectionUtilTest.class,
     StringUtilTest.class,
diff --git a/src/test/java/com/baidu/hugegraph/unit/util/CollectionUtilTest.java b/src/test/java/com/baidu/hugegraph/unit/util/CollectionUtilTest.java
index e7e9e95..a1e4750 100644
--- a/src/test/java/com/baidu/hugegraph/unit/util/CollectionUtilTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/util/CollectionUtilTest.java
@@ -20,9 +20,11 @@
 package com.baidu.hugegraph.unit.util;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -103,6 +105,12 @@
 
         List<Integer> list4 = ImmutableList.of(1, 2, 3, 4);
         Assert.assertFalse(CollectionUtil.prefixOf(list4, list));
+
+        List<Integer> list5 = ImmutableList.of(1, 2, 4);
+        Assert.assertFalse(CollectionUtil.prefixOf(list5, list));
+
+        List<Integer> list6 = Arrays.asList(1, 2, null);
+        Assert.assertFalse(CollectionUtil.prefixOf(list6, list));
     }
 
     @Test
@@ -158,6 +166,30 @@
 
         subSet = CollectionUtil.subSet(originSet, 0, 5);
         Assert.assertEquals(ImmutableSet.of(1, 2, 3, 4, 5), subSet);
+
+        subSet = CollectionUtil.subSet(originSet, 2, -1);
+        Assert.assertEquals(ImmutableSet.of(3, 4, 5), subSet);
+
+        subSet = CollectionUtil.subSet(originSet, 2, -100);
+        Assert.assertEquals(ImmutableSet.of(3, 4, 5), subSet);
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            CollectionUtil.subSet(originSet, 2, 1);
+        }, e -> {
+            Assert.assertContains("Invalid to parameter ", e.getMessage());
+        });
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            CollectionUtil.subSet(originSet, -1, 2);
+        }, e -> {
+            Assert.assertContains("Invalid from parameter ", e.getMessage());
+        });
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            CollectionUtil.subSet(originSet, -10, 2);
+        }, e -> {
+            Assert.assertContains("Invalid from parameter ", e.getMessage());
+        });
     }
 
     @Test
@@ -182,9 +214,9 @@
         first.add(3);
 
         List<Integer> second = new ArrayList<>();
-
         second.add(4);
         second.add(5);
+
         Collection<Integer> results = CollectionUtil.intersect(first, second);
         Assert.assertEquals(0, results.size());
         Assert.assertEquals(3, first.size());
@@ -199,6 +231,26 @@
         results = CollectionUtil.intersect(first, second);
         Assert.assertEquals(3, results.size());
         Assert.assertEquals(3, first.size());
+
+        Set<Integer> set = new HashSet<>();
+        set.add(1);
+        set.add(3);
+        set.add(6);
+
+        results = CollectionUtil.intersect(set, second);
+        Assert.assertInstanceOf(HashSet.class, results);
+        Assert.assertEquals(2, results.size());
+        Assert.assertEquals(3, set.size());
+
+        set = new LinkedHashSet<>();
+        set.add(1);
+        set.add(2);
+        set.add(6);
+
+        results = CollectionUtil.intersect(set, second);
+        Assert.assertInstanceOf(LinkedHashSet.class, results);
+        Assert.assertEquals(2, results.size());
+        Assert.assertEquals(3, set.size());
     }
 
     @Test
@@ -247,6 +299,16 @@
 
         second.add(1);
         Assert.assertTrue(CollectionUtil.hasIntersection(first, second));
+
+        second = new HashSet<>();
+        second.add(4);
+        second.add(5);
+        second.add(6);
+        second.add(7);
+        Assert.assertFalse(CollectionUtil.hasIntersection(first, second));
+
+        second.add(3);
+        Assert.assertTrue(CollectionUtil.hasIntersection(first, second));
     }
 
     @Test
@@ -264,6 +326,16 @@
 
         second.add(1);
         Assert.assertTrue(CollectionUtil.hasIntersection(first, second));
+
+        second = new HashSet<>();
+        second.add(4);
+        second.add(5);
+        second.add(6);
+        second.add(7);
+        Assert.assertFalse(CollectionUtil.hasIntersection(first, second));
+
+        second.add(3);
+        Assert.assertTrue(CollectionUtil.hasIntersection(first, second));
     }
 
     @Test
diff --git a/src/test/java/com/baidu/hugegraph/unit/util/DateUtilTest.java b/src/test/java/com/baidu/hugegraph/unit/util/DateUtilTest.java
index 9ac0956..5a6e54c 100644
--- a/src/test/java/com/baidu/hugegraph/unit/util/DateUtilTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/util/DateUtilTest.java
@@ -19,7 +19,6 @@
 
 package com.baidu.hugegraph.unit.util;
 
-import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -38,13 +37,27 @@
     public void testParse() {
         Date date1 = DateUtil.parse("2020-06-12 12:00:00");
         Date date2 = DateUtil.parse("2020-06-13");
+        Assert.assertNotEquals(date1, date2);
         Assert.assertTrue(date1.before(date2));
 
+        Date date3 = DateUtil.parse("2020-06-12");
+        Date date4 = DateUtil.parse("2020-06-12 00:00:00.00");
+        Assert.assertEquals(date3, date4);
+
+        Date date5 = DateUtil.parse("2020-06-12 00:00:00.001");
+        Assert.assertNotEquals(date3, date5);
+        Assert.assertTrue(date3.before(date5));
+
         Assert.assertThrows(IllegalArgumentException.class, () -> {
             DateUtil.parse("2018-");
         }, e -> {
-            Assert.assertContains("Expected date format is:",
-                                  e.getMessage());
+            Assert.assertContains("Expected date format is:", e.getMessage());
+        });
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            DateUtil.parse("2018-15-07 12:00:00.f");
+        }, e -> {
+            Assert.assertContains("Expected date format is:", e.getMessage());
         });
 
         Assert.assertThrows(IllegalArgumentException.class, () -> {
@@ -81,8 +94,9 @@
                     throw new RuntimeException(e);
                 }
                 try {
-                    DateUtil.parse("0", "yyyy");
-                } catch (ParseException e) {
+                    Assert.assertEquals(new Date(-62167248343000L),
+                                        DateUtil.parse("0", "yyyy"));
+                } catch (Exception e) {
                     errorCount.incrementAndGet();
                 }
             });
diff --git a/src/test/java/com/baidu/hugegraph/unit/util/EcheckTest.java b/src/test/java/com/baidu/hugegraph/unit/util/EcheckTest.java
new file mode 100644
index 0000000..46b9113
--- /dev/null
+++ b/src/test/java/com/baidu/hugegraph/unit/util/EcheckTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit.util;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.unit.BaseUnitTest;
+import com.baidu.hugegraph.util.E;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class EcheckTest extends BaseUnitTest {
+
+    @Test
+    public void testCheckNotNull() {
+        E.checkNotNull(0, "test");
+        E.checkNotNull(new Object(), "test");
+        E.checkNotNull("1", "test");
+        E.checkNotNull(ImmutableList.of(), "test");
+
+        Assert.assertThrows(NullPointerException.class, () -> {
+            E.checkNotNull(null, "test");
+        }, e -> {
+            Assert.assertContains("The 'test' can't be null", e.getMessage());
+        });
+
+        Assert.assertThrows(NullPointerException.class, () -> {
+            E.checkNotNull(null, "test2");
+        }, e -> {
+            Assert.assertContains("The 'test2' can't be null", e.getMessage());
+        });
+    }
+
+    @Test
+    public void testCheckNotNullWithOwner() {
+        E.checkNotNull(0, "test", "obj");
+        E.checkNotNull(new Object(), "test", "obj");
+        E.checkNotNull("1", "test", "obj");
+        E.checkNotNull(ImmutableList.of(), "test", "obj");
+
+        Assert.assertThrows(NullPointerException.class, () -> {
+            E.checkNotNull(null, "test", "obj");
+        }, e -> {
+            Assert.assertContains("The 'test' of 'obj' can't be null",
+                                  e.getMessage());
+        });
+
+        Assert.assertThrows(NullPointerException.class, () -> {
+            E.checkNotNull(null, "test2", "obj2");
+        }, e -> {
+            Assert.assertContains("The 'test2' of 'obj2' can't be null",
+                                  e.getMessage());
+        });
+    }
+
+    @Test
+    public void testCheckNotEmpty() {
+        E.checkNotEmpty(ImmutableList.of(0), "test");
+        E.checkNotEmpty(ImmutableList.of(""), "test");
+        E.checkNotEmpty(ImmutableList.of(1, 2), "test");
+        E.checkNotEmpty(ImmutableSet.of(0), "test");
+        E.checkNotEmpty(ImmutableSet.of(""), "test");
+        E.checkNotEmpty(ImmutableSet.of("1", "2"), "test");
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            E.checkNotEmpty(ImmutableList.of(), "test");
+        }, e -> {
+            Assert.assertContains("The 'test' can't be empty", e.getMessage());
+        });
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            E.checkNotEmpty(ImmutableSet.of(), "test2");
+        }, e -> {
+            Assert.assertContains("The 'test2' can't be empty", e.getMessage());
+        });
+    }
+
+    @Test
+    public void testCheckNotEmptyWithOwner() {
+        E.checkNotEmpty(ImmutableList.of(0), "test", "obj");
+        E.checkNotEmpty(ImmutableList.of(""), "test", "obj");
+        E.checkNotEmpty(ImmutableList.of(1, 2), "test", "obj");
+        E.checkNotEmpty(ImmutableSet.of(0), "test", "obj");
+        E.checkNotEmpty(ImmutableSet.of(""), "test", "obj");
+        E.checkNotEmpty(ImmutableSet.of("1", "2"), "test", "obj");
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            E.checkNotEmpty(ImmutableList.of(), "test", "obj");
+        }, e -> {
+            Assert.assertContains("The 'test' of 'obj' can't be empty",
+                                  e.getMessage());
+        });
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            E.checkNotEmpty(ImmutableSet.of(), "test2", "obj2");
+        }, e -> {
+            Assert.assertContains("The 'test2' of 'obj2' can't be empty",
+                                  e.getMessage());
+        });
+    }
+
+    @Test
+    public void testCheckArgument() {
+        E.checkArgument(true, "test");
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            E.checkArgument(false, "Invalid parameter %s", 123);
+        }, e -> {
+            Assert.assertContains("Invalid parameter 123", e.getMessage());
+        });
+    }
+
+    @Test
+    public void testCheckArgumentNotNull() {
+        E.checkArgumentNotNull("", "test");
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            E.checkArgumentNotNull(null, "Invalid parameter %s", "null");
+        }, e -> {
+            Assert.assertContains("Invalid parameter null", e.getMessage());
+        });
+    }
+
+    @Test
+    public void testCheckState() {
+        E.checkState(true, "test");
+
+        Assert.assertThrows(IllegalStateException.class, () -> {
+            E.checkState(false, "Invalid state '%s'", "FAIL");
+        }, e -> {
+            Assert.assertContains("Invalid state 'FAIL'", e.getMessage());
+        });
+    }
+}
diff --git a/src/test/java/com/baidu/hugegraph/unit/util/HashUtilTest.java b/src/test/java/com/baidu/hugegraph/unit/util/HashUtilTest.java
index 39d3076..ada7842 100644
--- a/src/test/java/com/baidu/hugegraph/unit/util/HashUtilTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/util/HashUtilTest.java
@@ -23,6 +23,7 @@
 
 import com.baidu.hugegraph.testutil.Assert;
 import com.baidu.hugegraph.unit.BaseUnitTest;
+import com.baidu.hugegraph.util.Bytes;
 import com.baidu.hugegraph.util.HashUtil;
 
 public class HashUtilTest extends BaseUnitTest {
@@ -49,6 +50,27 @@
     }
 
     @Test
+    public void testHashWithBytes() {
+        // hash 32 bits (4 bytes)
+
+        byte[] h = HashUtil.hash(b(""));
+        Assert.assertEquals(4, h.length);
+        Assert.assertEquals("00000000", hex(h));
+
+        h = HashUtil.hash(b("q"));
+        Assert.assertEquals(4, h.length);
+        Assert.assertEquals("e80982ff", hex(h));
+
+        h = HashUtil.hash(b("qq"));
+        Assert.assertEquals(4, h.length);
+        Assert.assertEquals("252ef918", hex(h));
+
+        h = HashUtil.hash(b("qwertyuiop[]asdfghjkl;'zxcvbnm,./"));
+        Assert.assertEquals(4, h.length);
+        Assert.assertEquals("fcc1f9fa", hex(h));
+    }
+
+    @Test
     public void testHash128() {
         // hash 128 bits (16 bytes)
 
@@ -68,4 +90,33 @@
         Assert.assertEquals(32, h.length());
         Assert.assertEquals("49780e7800e613230520ed7b1116fef5", h);
     }
+
+    @Test
+    public void testHash128WithBytes() {
+        // hash 128 bits (16 bytes)
+
+        byte[] h = HashUtil.hash128(b(""));
+        Assert.assertEquals(16, h.length);
+        Assert.assertEquals("00000000000000000000000000000000", hex(h));
+
+        h = HashUtil.hash128(b("q"));
+        Assert.assertEquals(16, h.length);
+        Assert.assertEquals("b1aba139b20c3ebcf667a14f41c7d17c", hex(h));
+
+        h = HashUtil.hash128(b("qq"));
+        Assert.assertEquals(16, h.length);
+        Assert.assertEquals("2dbabe8ac8d8ce9eedc4b97add0f7c7c", hex(h));
+
+        h = HashUtil.hash128(b("qwertyuiop[]asdfghjkl;'zxcvbnm,./"));
+        Assert.assertEquals(16, h.length);
+        Assert.assertEquals("49780e7800e613230520ed7b1116fef5", hex(h));
+    }
+
+    private static byte[] b(String string) {
+        return string.getBytes();
+    }
+
+    private static String hex(byte[] bytes) {
+        return Bytes.toHex(bytes);
+    }
 }
diff --git a/src/test/java/com/baidu/hugegraph/unit/util/LogTest.java b/src/test/java/com/baidu/hugegraph/unit/util/LogTest.java
new file mode 100644
index 0000000..9afe842
--- /dev/null
+++ b/src/test/java/com/baidu/hugegraph/unit/util/LogTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit.util;
+
+import org.junit.Test;
+import org.slf4j.Logger;
+
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.unit.BaseUnitTest;
+import com.baidu.hugegraph.util.Log;
+
+public class LogTest extends BaseUnitTest {
+
+    @Test
+    public void testLog() {
+        Logger log1 = Log.logger(LogTest.class);
+        Logger log2 = Log.logger("com.baidu.hugegraph.unit.util.LogTest");
+        Logger log3 = Log.logger("test");
+
+        Assert.assertEquals(log1, log2);
+        Assert.assertNotEquals(log1, log3);
+
+        log1.info("Info: testLog({})", LogTest.class);
+        log2.info("Info: testLog({})", "com.baidu.hugegraph.unit.util.LogTest");
+        log3.info("Info: testLog({})", "test");
+    }
+}
diff --git a/src/test/java/com/baidu/hugegraph/unit/util/NumericUtilTest.java b/src/test/java/com/baidu/hugegraph/unit/util/NumericUtilTest.java
index a7221b9..00e70ad 100644
--- a/src/test/java/com/baidu/hugegraph/unit/util/NumericUtilTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/util/NumericUtilTest.java
@@ -20,6 +20,7 @@
 package com.baidu.hugegraph.unit.util;
 
 import java.math.BigDecimal;
+import java.util.Date;
 
 import org.junit.Test;
 
@@ -382,10 +383,90 @@
                             NumericUtil.maxValueOf(Double.class));
 
         Assert.assertThrows(IllegalArgumentException.class, () -> {
-            NumericUtil.minValueOf(null);
+            NumericUtil.maxValueOf(null);
         });
         Assert.assertThrows(IllegalArgumentException.class, () -> {
-            NumericUtil.minValueOf(Character.class);
+            NumericUtil.maxValueOf(Character.class);
+        });
+    }
+
+    @Test
+    public void testIsNumber() {
+        Assert.assertEquals(true, NumericUtil.isNumber(byte.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(Byte.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(short.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(Short.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(int.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(Integer.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(long.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(Long.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(float.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(Float.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(double.class));
+        Assert.assertEquals(true, NumericUtil.isNumber(Double.class));
+
+        Assert.assertEquals(false, NumericUtil.isNumber(char.class));
+        Assert.assertEquals(false, NumericUtil.isNumber(Character.class));
+
+        Assert.assertEquals(true, NumericUtil.isNumber(1));
+        Assert.assertEquals(true, NumericUtil.isNumber(1L));
+        Assert.assertEquals(true, NumericUtil.isNumber(1.0f));
+        Assert.assertEquals(true, NumericUtil.isNumber(1.0d));
+        Assert.assertEquals(false, NumericUtil.isNumber('1'));
+        Assert.assertEquals(false, NumericUtil.isNumber((Object) null));
+    }
+
+    @Test
+    public void testConvertToNumber() {
+        Assert.assertEquals(1, NumericUtil.convertToNumber(1));
+        Assert.assertEquals(1.2, NumericUtil.convertToNumber(1.2));
+
+        Assert.assertEquals(new BigDecimal(1.25),
+                            NumericUtil.convertToNumber("1.25"));
+
+        Date date = new Date();
+        Assert.assertEquals(date.getTime(), NumericUtil.convertToNumber(date));
+
+        Assert.assertEquals(null, NumericUtil.convertToNumber(null));
+    }
+
+    @Test
+    public void testCompareNumber() {
+        Assert.assertEquals(0, NumericUtil.compareNumber(2, 2));
+        Assert.assertEquals(1, NumericUtil.compareNumber(10, 2));
+        Assert.assertEquals(-1, NumericUtil.compareNumber(1, 2));
+
+        Assert.assertEquals(-1, NumericUtil.compareNumber("1", 2));
+        Assert.assertEquals(0, NumericUtil.compareNumber("2.0", 2));
+        Assert.assertEquals(1, NumericUtil.compareNumber("2.00001", 2));
+        Assert.assertEquals(1, NumericUtil.compareNumber("3.8", 2));
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            NumericUtil.compareNumber(null, 1);
+        }, e -> {
+            Assert.assertContains("The first parameter can't be null",
+                                  e.getMessage());
+        });
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            NumericUtil.compareNumber(2, null);
+        }, e -> {
+            Assert.assertContains("The second parameter can't be null",
+                                  e.getMessage());
+        });
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            NumericUtil.compareNumber("2", null);
+        }, e -> {
+            Assert.assertContains("The second parameter can't be null",
+                                  e.getMessage());
+        });
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            NumericUtil.compareNumber("f", 2);
+        }, e -> {
+            Assert.assertContains("Can't compare between 'f' and '2'",
+                                  e.getMessage());
         });
     }
 
diff --git a/src/test/java/com/baidu/hugegraph/unit/util/StringUtilTest.java b/src/test/java/com/baidu/hugegraph/unit/util/StringUtilTest.java
index f37ba41..6840f7c 100644
--- a/src/test/java/com/baidu/hugegraph/unit/util/StringUtilTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/util/StringUtilTest.java
@@ -83,6 +83,72 @@
         });
     }
 
+    @Test
+    public void testCharsCharAt() {
+        Chars chars = Chars.of("123");
+        Assert.assertEquals('1', chars.charAt(0));
+        Assert.assertEquals('2', chars.charAt(1));
+        Assert.assertEquals('3', chars.charAt(2));
+
+        Assert.assertThrows(ArrayIndexOutOfBoundsException.class, () -> {
+            chars.charAt(3);
+        });
+        Assert.assertThrows(ArrayIndexOutOfBoundsException.class, () -> {
+            chars.charAt(-1);
+        });
+    }
+
+    @Test
+    public void testCharsSubSequence() {
+        Chars chars = Chars.of("123");
+        Assert.assertEquals(Chars.of("1"), chars.subSequence(0, 1));
+        Assert.assertEquals(Chars.of("12"), chars.subSequence(0, 2));
+        Assert.assertEquals(Chars.of("2"), chars.subSequence(1, 2));
+        Assert.assertEquals(Chars.of("23"), chars.subSequence(1, 3));
+        Assert.assertEquals(Chars.of("123"), chars.subSequence(0, 3));
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            chars.subSequence(2, 1);
+        }, e -> {
+            Assert.assertContains("Invalid end parameter 1", e.getMessage());
+        });
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            chars.subSequence(-1, 2);
+        }, e -> {
+            Assert.assertContains("Invalid start parameter -1", e.getMessage());
+        });
+
+        Assert.assertThrows(IllegalArgumentException.class, () -> {
+            chars.subSequence(1, -1);
+        }, e -> {
+            Assert.assertContains("Invalid end parameter -1", e.getMessage());
+        });
+    }
+
+    @Test
+    public void testCharsEquals() {
+        Chars chars1 = Chars.of("123");
+        Chars chars2 = Chars.of("123");
+        Chars chars3 = Chars.of("12");
+
+        Assert.assertEquals(chars1, chars2);
+        Assert.assertNotEquals(chars1, chars3);
+        Assert.assertNotEquals(chars1, "123");
+        Assert.assertNotEquals(chars1, null);
+    }
+
+    @Test
+    public void testCharsHashCode() {
+        Chars chars1 = Chars.of("123");
+        Chars chars2 = Chars.of("123");
+        Chars chars3 = Chars.of("12");
+
+        Assert.assertEquals(chars1.hashCode(), chars2.hashCode());
+        Assert.assertEquals(chars1.hashCode(), "123".hashCode());
+        Assert.assertNotEquals(chars1.hashCode(), chars3.hashCode());
+    }
+
     private static List<String> guavaSplit(String line, String delimiter) {
         return Splitter.on(delimiter).splitToList(line);
     }
diff --git a/src/test/java/com/baidu/hugegraph/unit/util/VersionUtilTest.java b/src/test/java/com/baidu/hugegraph/unit/util/VersionUtilTest.java
index 29e91fd..49fcdac 100644
--- a/src/test/java/com/baidu/hugegraph/unit/util/VersionUtilTest.java
+++ b/src/test/java/com/baidu/hugegraph/unit/util/VersionUtilTest.java
@@ -19,6 +19,8 @@
 
 package com.baidu.hugegraph.unit.util;
 
+import java.net.MalformedURLException;
+
 import org.junit.Test;
 
 import com.baidu.hugegraph.testutil.Assert;
@@ -118,4 +120,53 @@
             VersionUtil.check(version, "0.7", "1.0", "test-component");
         });
     }
+
+    @Test
+    public void testGetImplementationVersion() throws MalformedURLException {
+        // Can't mock Class: https://github.com/mockito/mockito/issues/1734
+        //Class<?> clazz = Mockito.mock(Class.class);
+        //Mockito.when(clazz.getSimpleName()).thenReturn("fake");
+        //Mockito.when(clazz.getResource("fake.class")).thenReturn(manifest);
+
+        String manifestPath = "file:./src/test/resources";
+        Assert.assertEquals("1.8.6.0",
+                            VersionUtil.getImplementationVersion(manifestPath));
+
+        manifestPath = "file:./src/test/resources2";
+        Assert.assertEquals(null,
+                            VersionUtil.getImplementationVersion(manifestPath));
+    }
+
+    @Test
+    public void testVersion() {
+        // Test equals
+        Version v1 = VersionUtil.Version.of("0.2.1");
+        Version v2 = VersionUtil.Version.of("0.2.1");
+        Assert.assertEquals(v1, v1);
+        Assert.assertEquals(v1, v2);
+
+        Version v3 = VersionUtil.Version.of("0.2.0");
+        Version v4 = VersionUtil.Version.of("0.2");
+        Assert.assertEquals(v3, v4);
+
+        Version v5 = VersionUtil.Version.of("0.2.3");
+        Version v6 = VersionUtil.Version.of("0.3.2");
+        Assert.assertNotEquals(v5, v6);
+        Assert.assertNotEquals(v5, null);
+        Assert.assertNotEquals(v5, "0.2.3");
+
+        // Test hashCode
+        Assert.assertEquals(1023, v1.hashCode());
+        Assert.assertEquals(1023, v2.hashCode());
+        Assert.assertEquals(62, v3.hashCode());
+        Assert.assertEquals(62, v4.hashCode());
+        Assert.assertEquals(2945, v5.hashCode());
+        Assert.assertEquals(2015, v6.hashCode());
+
+        // Test compareTo
+        Assert.assertEquals(0, v1.compareTo(v2));
+        Assert.assertEquals(1, v1.compareTo(v3));
+        Assert.assertEquals(-1, v1.compareTo(v5));
+        Assert.assertEquals(1, v1.compareTo(null));
+    }
 }
diff --git a/src/test/resources/META-INF/MANIFEST.MF b/src/test/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..fe44310
--- /dev/null
+++ b/src/test/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0

+Implementation-Version: 1.8.6.0

+Archiver-Version: Plexus Archiver

+Built-By: jermy

+Specification-Title: hugegraph-common

+Created-By: Apache Maven 3.3.9

+Build-Jdk: 1.8.0_111

+Specification-Version: 1.8.6

+