mirror of
https://gitee.com/dromara/hutool.git
synced 2026-02-09 09:16:26 +08:00
add test
This commit is contained in:
@@ -208,7 +208,7 @@ public class HexUtil extends Hex {
|
||||
* @param value float值
|
||||
* @return 16进制字符串
|
||||
*/
|
||||
public static String toHex(float value) {
|
||||
public static String toHex(final float value) {
|
||||
return Integer.toHexString(Float.floatToIntBits(value));
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ public class HexUtil extends Hex {
|
||||
* @param value 16进制字符串
|
||||
* @return 16进制字符串float值
|
||||
*/
|
||||
public static float hexToFloat(String value) {
|
||||
public static float hexToFloat(final String value) {
|
||||
return Float.intBitsToFloat(Integer.parseUnsignedInt(removeHexPrefix(value), 16));
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ public class HexUtil extends Hex {
|
||||
* @param value double值
|
||||
* @return 16进制字符串
|
||||
*/
|
||||
public static String toHex(double value) {
|
||||
public static String toHex(final double value) {
|
||||
return Long.toHexString(Double.doubleToLongBits(value));
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ public class HexUtil extends Hex {
|
||||
* @param value 16进制字符串
|
||||
* @return 16进制字符串double值
|
||||
*/
|
||||
public static double hexToDouble(String value) {
|
||||
public static double hexToDouble(final String value) {
|
||||
return Double.longBitsToDouble(Long.parseUnsignedLong(removeHexPrefix(value), 16));
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ public class HexUtil extends Hex {
|
||||
* </pre>
|
||||
*
|
||||
* @param hexStr Hex字符串
|
||||
* @param prefix 自定义前缀,如0x
|
||||
* @param prefix 自定义前缀,如0x,此前缀每个16进制数前都添加
|
||||
* @return 格式化后的字符串
|
||||
*/
|
||||
public static String format(final String hexStr, final String prefix) {
|
||||
|
||||
@@ -26,8 +26,8 @@ import java.util.Arrays;
|
||||
* 它们分别根据字串计算 64 和 128 位的散列值。这些算法不适用于加密,但适合用在散列表等处。
|
||||
*
|
||||
* <p>
|
||||
* 代码来自:https://github.com/rolandhe/string-tools<br>
|
||||
* 原始算法:https://github.com/google/cityhash
|
||||
* 代码来自:<a href="https://github.com/rolandhe/string-tools">string-tools</a><br>
|
||||
* 原始算法:<a href="https://github.com/google/cityhash">cityhash</a>
|
||||
*
|
||||
* @author hexiufeng
|
||||
* @since 5.2.5
|
||||
|
||||
@@ -23,9 +23,9 @@ import java.nio.ByteBuffer;
|
||||
* 除了卓越的性能外,他们还以算法生成而著称。
|
||||
*
|
||||
* <p>
|
||||
* 官方实现:https://github.com/jandrewrogers/MetroHash
|
||||
* 官方文档:http://www.jandrewrogers.com/2015/05/27/metrohash/
|
||||
* 来自:https://github.com/postamar/java-metrohash/
|
||||
* 官方实现:<a href="https://github.com/jandrewrogers/MetroHash">MetroHash</a>
|
||||
* 官方文档:<a href="http://www.jandrewrogers.com/2015/05/27/metrohash/">metrohash</a>
|
||||
* 来自:<a href="https://github.com/postamar/java-metrohash/">java-metrohash</a>
|
||||
*
|
||||
* @author Marius Posta
|
||||
* @param <R> 返回值类型,为this类型
|
||||
|
||||
@@ -24,9 +24,9 @@ import java.nio.ByteOrder;
|
||||
* 除了卓越的性能外,他们还以算法生成而著称。
|
||||
*
|
||||
* <p>
|
||||
* 官方实现:https://github.com/jandrewrogers/MetroHash
|
||||
* 官方文档:http://www.jandrewrogers.com/2015/05/27/metrohash/
|
||||
* 来自:https://github.com/postamar/java-metrohash/
|
||||
* 官方实现:<a href="https://github.com/jandrewrogers/MetroHash">MetroHash</a>
|
||||
* 官方文档:<a href="http://www.jandrewrogers.com/2015/05/27/metrohash/">metrohash</a>
|
||||
* 来自:<a href="https://github.com/postamar/java-metrohash/">java-metrohash</a>
|
||||
*
|
||||
* @param <R> 返回值类型,为this类型
|
||||
* @author Marius Posta
|
||||
|
||||
@@ -27,9 +27,10 @@ import java.nio.ByteOrder;
|
||||
* 除了卓越的性能外,他们还以算法生成而著称。
|
||||
*
|
||||
* <p>
|
||||
* 官方实现:https://github.com/jandrewrogers/MetroHash
|
||||
* 官方文档:http://www.jandrewrogers.com/2015/05/27/metrohash/
|
||||
* 来自:https://github.com/postamar/java-metrohash/
|
||||
* 官方实现:<a href="https://github.com/jandrewrogers/MetroHash">MetroHash</a>
|
||||
* 官方文档:<a href="http://www.jandrewrogers.com/2015/05/27/metrohash/">metrohash</a>
|
||||
* 来自:<a href="https://github.com/postamar/java-metrohash/">java-metrohash</a>
|
||||
*
|
||||
* @author Marius Posta
|
||||
*/
|
||||
public class MetroHash128 extends AbstractMetroHash<MetroHash128> implements Hash128<byte[]> {
|
||||
|
||||
@@ -26,9 +26,10 @@ import java.nio.ByteOrder;
|
||||
* 除了卓越的性能外,他们还以算法生成而著称。
|
||||
*
|
||||
* <p>
|
||||
* 官方实现:https://github.com/jandrewrogers/MetroHash
|
||||
* 官方文档:http://www.jandrewrogers.com/2015/05/27/metrohash/
|
||||
* 来自:https://github.com/postamar/java-metrohash/
|
||||
* 官方实现:<a href="https://github.com/jandrewrogers/MetroHash">MetroHash</a>
|
||||
* 官方文档:<a href="http://www.jandrewrogers.com/2015/05/27/metrohash/">metrohash</a>
|
||||
* 来自:<a href="https://github.com/postamar/java-metrohash/">java-metrohash</a>
|
||||
*
|
||||
* @author Marius Posta
|
||||
*/
|
||||
public class MetroHash64 extends AbstractMetroHash<MetroHash64> implements Hash64<byte[]> {
|
||||
|
||||
@@ -18,7 +18,7 @@ package cn.hutool.v7.core.codec;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class RadixUtilTest {
|
||||
@Test
|
||||
@@ -29,4 +29,151 @@ public class RadixUtilTest {
|
||||
RadixUtil.decode(radixs, bad);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeInt() {
|
||||
// Test binary conversion (base 2)
|
||||
assertEquals("1010", RadixUtil.encode("01", 10));
|
||||
|
||||
// Test base 3 conversion
|
||||
assertEquals("101", RadixUtil.encode("012", 10));
|
||||
|
||||
// Test base 16 conversion
|
||||
assertEquals("A", RadixUtil.encode("0123456789ABCDEF", 10));
|
||||
|
||||
// Test with 34-radix
|
||||
assertEquals("Y", RadixUtil.encode(RadixUtil.RADIXS_34, 32));
|
||||
|
||||
// Test with 59-radix
|
||||
assertEquals("b", RadixUtil.encode(RadixUtil.RADIXS_59, 11));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeLong() {
|
||||
// Test binary conversion (base 2)
|
||||
assertEquals("1010", RadixUtil.encode("01", 10L));
|
||||
|
||||
// Test base 3 conversion
|
||||
assertEquals("101", RadixUtil.encode("012", 10L));
|
||||
|
||||
// Test larger number
|
||||
assertEquals("RR", RadixUtil.encode("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 999L));
|
||||
|
||||
// Test with shuffle 34-radix
|
||||
assertEquals("R", RadixUtil.encode(RadixUtil.RADIXS_SHUFFLE_34, 22));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeToInt() {
|
||||
// Test binary decoding (base 2)
|
||||
assertEquals(10, RadixUtil.decodeToInt("01", "1010"));
|
||||
|
||||
// Test base 3 decoding
|
||||
assertEquals(10, RadixUtil.decodeToInt("012", "101"));
|
||||
|
||||
// Test base 16 decoding
|
||||
assertEquals(10, RadixUtil.decodeToInt("0123456789ABCDEF", "A"));
|
||||
|
||||
// Test with 34-radix
|
||||
assertEquals(30, RadixUtil.decodeToInt(RadixUtil.RADIXS_34, "W"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecode() {
|
||||
// Test binary decoding (base 2)
|
||||
assertEquals(10L, RadixUtil.decode("01", "1010"));
|
||||
|
||||
// Test base 3 decoding
|
||||
assertEquals(10L, RadixUtil.decode("012", "101"));
|
||||
|
||||
// Test base 16 decoding
|
||||
assertEquals(10L, RadixUtil.decode("0123456789ABCDEF", "A"));
|
||||
|
||||
// Test with 59-radix
|
||||
assertEquals(11L, RadixUtil.decode(RadixUtil.RADIXS_59, "b"));
|
||||
|
||||
// Test with shuffle 59-radix
|
||||
assertEquals(1L, RadixUtil.decode(RadixUtil.RADIXS_SHUFFLE_59, "vh"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeDecodeRoundTrip() {
|
||||
// Test round trip for various bases
|
||||
assertEquals(42, RadixUtil.decodeToInt("01", RadixUtil.encode("01", 42)));
|
||||
assertEquals(123, RadixUtil.decodeToInt("0123456789", RadixUtil.encode("0123456789", 123)));
|
||||
assertEquals(255, RadixUtil.decodeToInt("0123456789ABCDEF", RadixUtil.encode("0123456789ABCDEF", 255)));
|
||||
|
||||
// Test with predefined radixes
|
||||
assertEquals(1000, RadixUtil.decodeToInt(RadixUtil.RADIXS_34, RadixUtil.encode(RadixUtil.RADIXS_34, 1000)));
|
||||
assertEquals(2000, RadixUtil.decodeToInt(RadixUtil.RADIXS_59, RadixUtil.encode(RadixUtil.RADIXS_59, 2000)));
|
||||
assertEquals(500, RadixUtil.decodeToInt(RadixUtil.RADIXS_SHUFFLE_34, RadixUtil.encode(RadixUtil.RADIXS_SHUFFLE_34, 500)));
|
||||
assertEquals(750, RadixUtil.decodeToInt(RadixUtil.RADIXS_SHUFFLE_59, RadixUtil.encode(RadixUtil.RADIXS_SHUFFLE_59, 750)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEdgeCases() {
|
||||
// Test zero
|
||||
assertEquals("0", RadixUtil.encode("01", 0));
|
||||
assertEquals(0, RadixUtil.decodeToInt("01", "0"));
|
||||
|
||||
// Test single digit numbers
|
||||
assertEquals("1", RadixUtil.encode("01", 1));
|
||||
assertEquals(1, RadixUtil.decodeToInt("01", "1"));
|
||||
|
||||
// Test with larger numbers
|
||||
assertEquals("11111111", RadixUtil.encode("01", 255)); // 255 in binary
|
||||
assertEquals(255, RadixUtil.decodeToInt("01", "11111111"));
|
||||
|
||||
// Test negative numbers (using the special handling in encode)
|
||||
assertEquals(4294967254L, RadixUtil.decode("01", RadixUtil.encode("01", -42)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantValue")
|
||||
@Test
|
||||
public void testPredefinedRadixes() {
|
||||
// Test 34-radix constants
|
||||
assertNotNull(RadixUtil.RADIXS_34);
|
||||
assertEquals(34, RadixUtil.RADIXS_34.length());
|
||||
assertFalse(RadixUtil.RADIXS_34.contains("I"));
|
||||
assertFalse(RadixUtil.RADIXS_34.contains("O"));
|
||||
|
||||
// Test 59-radix constants
|
||||
assertNotNull(RadixUtil.RADIXS_59);
|
||||
assertEquals(59, RadixUtil.RADIXS_59.length());
|
||||
assertFalse(RadixUtil.RADIXS_59.contains("I"));
|
||||
assertFalse(RadixUtil.RADIXS_59.contains("O"));
|
||||
assertFalse(RadixUtil.RADIXS_59.contains("l"));
|
||||
|
||||
// Test shuffle radixes
|
||||
assertNotNull(RadixUtil.RADIXS_SHUFFLE_34);
|
||||
assertEquals(34, RadixUtil.RADIXS_SHUFFLE_34.length());
|
||||
|
||||
assertNotNull(RadixUtil.RADIXS_SHUFFLE_59);
|
||||
assertEquals(59, RadixUtil.RADIXS_SHUFFLE_59.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidInputs() {
|
||||
// Test invalid radix (too short)
|
||||
assertThrows(RuntimeException.class, () -> RadixUtil.encode("0", 10)); // only 1 char
|
||||
|
||||
// Test decode with null radix
|
||||
assertThrows(IllegalArgumentException.class, () -> RadixUtil.decode(null, "10"));
|
||||
|
||||
// Test decode with empty string
|
||||
assertThrows(IllegalArgumentException.class, () -> RadixUtil.decode("01", ""));
|
||||
|
||||
// Test decode with invalid character (already tested in original method)
|
||||
final String radixs = "0123456789ABC"; // base 13
|
||||
final String bad = "1X3"; // 'X' 不在 radix 中
|
||||
assertThrows(IllegalArgumentException.class, () -> RadixUtil.decode(radixs, bad));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongValueEncodeDecode() {
|
||||
final long testValue = 1234567890L;
|
||||
final String encoded = RadixUtil.encode("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", testValue);
|
||||
final long decoded = RadixUtil.decode("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", encoded);
|
||||
assertEquals(testValue, decoded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.codec;
|
||||
package cn.hutool.v7.core.codec.binary;
|
||||
|
||||
import cn.hutool.v7.core.codec.binary.Base32;
|
||||
import cn.hutool.v7.core.util.ByteUtil;
|
||||
import cn.hutool.v7.core.util.RandomUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.codec;
|
||||
package cn.hutool.v7.core.codec.binary;
|
||||
|
||||
import cn.hutool.v7.core.codec.binary.Base58;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.codec;
|
||||
package cn.hutool.v7.core.codec.binary;
|
||||
|
||||
import cn.hutool.v7.core.codec.binary.Base62;
|
||||
import cn.hutool.v7.core.util.RandomUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.codec;
|
||||
package cn.hutool.v7.core.codec.binary;
|
||||
|
||||
import cn.hutool.v7.core.codec.binary.Base64;
|
||||
import cn.hutool.v7.core.util.ByteUtil;
|
||||
import cn.hutool.v7.core.util.CharsetUtil;
|
||||
import cn.hutool.v7.core.util.RandomUtil;
|
||||
@@ -0,0 +1,202 @@
|
||||
package cn.hutool.v7.core.codec.binary;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class HexUtilTest {
|
||||
|
||||
@Test
|
||||
public void testEncodeColor() {
|
||||
final Color red = new Color(255, 0, 0);
|
||||
assertEquals("#ff0000", HexUtil.encodeColor(red));
|
||||
|
||||
final Color green = new Color(0, 255, 0);
|
||||
assertEquals("#00ff00", HexUtil.encodeColor(green));
|
||||
|
||||
final Color blue = new Color(0, 0, 255);
|
||||
assertEquals("#0000ff", HexUtil.encodeColor(blue));
|
||||
|
||||
final Color black = new Color(0, 0, 0);
|
||||
assertEquals("#000000", HexUtil.encodeColor(black));
|
||||
|
||||
final Color white = new Color(255, 255, 255);
|
||||
assertEquals("#ffffff", HexUtil.encodeColor(white));
|
||||
|
||||
// Test with single digit values (should be padded with 0)
|
||||
final Color testColor = new Color(1, 16, 255);
|
||||
assertEquals("#0110ff", HexUtil.encodeColor(testColor));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeColorWithPrefix() {
|
||||
final Color red = new Color(255, 0, 0);
|
||||
assertEquals("0xff0000", HexUtil.encodeColor(red, "0x"));
|
||||
assertEquals("#ff0000", HexUtil.encodeColor(red, "#"));
|
||||
assertEquals("ff0000", HexUtil.encodeColor(red, ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeColor() {
|
||||
assertEquals(new Color(255, 0, 0), HexUtil.decodeColor("#ff0000"));
|
||||
assertEquals(new Color(0, 255, 0), HexUtil.decodeColor("#00ff00"));
|
||||
assertEquals(new Color(0, 0, 255), HexUtil.decodeColor("#0000ff"));
|
||||
assertEquals(new Color(255, 0, 0), HexUtil.decodeColor("0xff0000"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsHexNumber() {
|
||||
assertTrue(HexUtil.isHexNumber("ff"));
|
||||
assertTrue(HexUtil.isHexNumber("FF"));
|
||||
assertTrue(HexUtil.isHexNumber("0xff"));
|
||||
assertTrue(HexUtil.isHexNumber("0XFF"));
|
||||
assertTrue(HexUtil.isHexNumber("#ff"));
|
||||
assertTrue(HexUtil.isHexNumber("123abc"));
|
||||
assertTrue(HexUtil.isHexNumber("0x123abc"));
|
||||
assertTrue(HexUtil.isHexNumber("#123abc"));
|
||||
|
||||
assertFalse(HexUtil.isHexNumber(""));
|
||||
assertFalse(HexUtil.isHexNumber(null));
|
||||
assertFalse(HexUtil.isHexNumber("gg")); // g is not hex digit
|
||||
assertFalse(HexUtil.isHexNumber("-ff"));
|
||||
assertFalse(HexUtil.isHexNumber("ff-"));
|
||||
assertFalse(HexUtil.isHexNumber("12 34")); // space not allowed
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToUnicodeHex() {
|
||||
assertEquals("\\u4f60", HexUtil.toUnicodeHex('你'));
|
||||
assertEquals("\\u0048", HexUtil.toUnicodeHex('H'));
|
||||
assertEquals("\\u0065", HexUtil.toUnicodeHex('e'));
|
||||
assertEquals("\\u006c", HexUtil.toUnicodeHex('l'));
|
||||
assertEquals("\\u006f", HexUtil.toUnicodeHex('o'));
|
||||
|
||||
// Test with integer values
|
||||
assertEquals("\\u0041", HexUtil.toUnicodeHex(65)); // 'A'
|
||||
assertEquals("\\u0000", HexUtil.toUnicodeHex(0));
|
||||
assertEquals("\\uffff", HexUtil.toUnicodeHex(65535)); // max char value
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHexFromInt() {
|
||||
assertEquals("ff", HexUtil.toHex(255));
|
||||
assertEquals("0", HexUtil.toHex(0));
|
||||
assertEquals("10", HexUtil.toHex(16));
|
||||
assertEquals("64", HexUtil.toHex(100));
|
||||
assertEquals("ffff", HexUtil.toHex(65535));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexToInt() {
|
||||
assertEquals(255, HexUtil.hexToInt("ff"));
|
||||
assertEquals(255, HexUtil.hexToInt("0xff"));
|
||||
assertEquals(255, HexUtil.hexToInt("#ff"));
|
||||
assertEquals(0, HexUtil.hexToInt("0"));
|
||||
assertEquals(16, HexUtil.hexToInt("10"));
|
||||
assertEquals(100, HexUtil.hexToInt("64"));
|
||||
assertEquals(65535, HexUtil.hexToInt("ffff"));
|
||||
assertEquals(65535, HexUtil.hexToInt("0xffff"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHexFromLong() {
|
||||
assertEquals("ff", HexUtil.toHex(255L));
|
||||
assertEquals("0", HexUtil.toHex(0L));
|
||||
assertEquals("10", HexUtil.toHex(16L));
|
||||
assertEquals("ffffffffffffffff", HexUtil.toHex(-1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexToLong() {
|
||||
assertEquals(255L, HexUtil.hexToLong("ff"));
|
||||
assertEquals(255L, HexUtil.hexToLong("0xff"));
|
||||
assertEquals(0L, HexUtil.hexToLong("0"));
|
||||
assertEquals(16L, HexUtil.hexToLong("10"));
|
||||
assertThrows(NumberFormatException.class, ()-> HexUtil.hexToLong("ffffffffffffffff"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHexFromFloat() {
|
||||
assertEquals("40490fdb", HexUtil.toHex((float) Math.PI));
|
||||
assertEquals("0", HexUtil.toHex(0.0f));
|
||||
assertEquals("3f800000", HexUtil.toHex(1.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexToFloat() {
|
||||
assertEquals(Math.PI, HexUtil.hexToFloat("40490fdb"), 0.0001f);
|
||||
assertEquals(0.0f, HexUtil.hexToFloat("0"), 0.0001f);
|
||||
assertEquals(1.0f, HexUtil.hexToFloat("3f800000"), 0.0001f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHexFromDouble() {
|
||||
assertEquals("400921fb54442d18", HexUtil.toHex(Math.PI));
|
||||
assertEquals("0", HexUtil.toHex(0.0));
|
||||
assertEquals("3ff0000000000000", HexUtil.toHex(1.0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexToDouble() {
|
||||
assertEquals(Math.PI, HexUtil.hexToDouble("400921fb54442d18"), 0.0001);
|
||||
assertEquals(0.0, HexUtil.hexToDouble("0"), 0.0001);
|
||||
assertEquals(1.0, HexUtil.hexToDouble("3ff0000000000000"), 0.0001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppendHex() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
HexUtil.appendHex(sb, (byte) 255, true); // lowercase
|
||||
assertEquals("ff", sb.toString());
|
||||
|
||||
sb = new StringBuilder();
|
||||
HexUtil.appendHex(sb, (byte) 255, false); // uppercase
|
||||
assertEquals("FF", sb.toString());
|
||||
|
||||
sb = new StringBuilder();
|
||||
HexUtil.appendHex(sb, (byte) 0, true);
|
||||
assertEquals("00", sb.toString());
|
||||
|
||||
sb = new StringBuilder();
|
||||
HexUtil.appendHex(sb, (byte) 16, true);
|
||||
assertEquals("10", sb.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToBigInteger() {
|
||||
assertNull(HexUtil.toBigInteger(null));
|
||||
assertEquals(new java.math.BigInteger("ff", 16), HexUtil.toBigInteger("ff"));
|
||||
assertEquals(new java.math.BigInteger("ff", 16), HexUtil.toBigInteger("0xff"));
|
||||
assertEquals(new java.math.BigInteger("ff", 16), HexUtil.toBigInteger("#ff"));
|
||||
assertEquals(new java.math.BigInteger("0", 16), HexUtil.toBigInteger("0"));
|
||||
assertEquals(new java.math.BigInteger("1234abcd", 16), HexUtil.toBigInteger("1234abcd"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormat() {
|
||||
assertEquals("", HexUtil.format(""));
|
||||
assertEquals("a", HexUtil.format("a"));
|
||||
assertEquals("ab", HexUtil.format("ab"));
|
||||
assertEquals("ab cd", HexUtil.format("abcd"));
|
||||
assertEquals("ab cd ef", HexUtil.format("abcdef"));
|
||||
assertEquals("ab cd ef 12", HexUtil.format("abcdef12"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatWithPrefix() {
|
||||
assertEquals("0xab", HexUtil.format("ab", "0x"));
|
||||
assertEquals("0xab 0xcd", HexUtil.format("abcd", "0x"));
|
||||
assertEquals("#ab", HexUtil.format("ab", "#"));
|
||||
assertEquals("#ab #cd", HexUtil.format("abcd", "#"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatWithPrefixAndSeparator() {
|
||||
assertEquals("0xab 0xcd", HexUtil.format("abcd", "0x", " "));
|
||||
assertEquals("0xab:0xcd", HexUtil.format("abcd", "0x", ":"));
|
||||
assertEquals("ab-cd", HexUtil.format("abcd", "", "-"));
|
||||
assertEquals("ab cd ef 12", HexUtil.format("abcdef12", null, null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package cn.hutool.v7.core.codec.hash;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class ConsistentHashTest {
|
||||
|
||||
@Test
|
||||
public void testConstructorWithDefaultHashFunction() {
|
||||
final List<String> nodes = Arrays.asList("node1", "node2", "node3");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(3, nodes);
|
||||
|
||||
assertNotNull(consistentHash);
|
||||
// Cannot directly access numberOfReplicas and circle as they are private
|
||||
// We'll test the functionality instead
|
||||
final String node = consistentHash.get("testKey");
|
||||
assertNotNull(node);
|
||||
assertTrue(nodes.contains(node));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorWithCustomHashFunction() {
|
||||
final Hash32<Object> customHash = Object::hashCode;
|
||||
final List<String> nodes = Arrays.asList("server1", "server2", "server3");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(customHash, 2, nodes);
|
||||
|
||||
assertNotNull(consistentHash);
|
||||
// Test functionality instead of accessing private fields
|
||||
final String node = consistentHash.get("testKey");
|
||||
assertNotNull(node);
|
||||
assertTrue(nodes.contains(node));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNode() {
|
||||
final List<String> nodes = Collections.singletonList("initial");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(2, nodes);
|
||||
|
||||
// Test that we can add a new node
|
||||
consistentHash.add("newNode");
|
||||
|
||||
// Verify that the new node can be retrieved for some keys
|
||||
final String result = consistentHash.get("someKey");
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveNode() {
|
||||
final List<String> nodes = Arrays.asList("node1", "node2", "node3");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(2, nodes);
|
||||
|
||||
// Initially, there should be nodes
|
||||
final String initialResult = consistentHash.get("testKey");
|
||||
assertNotNull(initialResult);
|
||||
|
||||
// Remove a node
|
||||
consistentHash.remove("node2");
|
||||
|
||||
// After removal, there should still be nodes to handle requests
|
||||
final String resultAfterRemoval = consistentHash.get("testKey");
|
||||
assertNotNull(resultAfterRemoval);
|
||||
// The result might be different, but should not be null if other nodes exist
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNode() {
|
||||
final List<String> nodes = Arrays.asList("server1", "server2", "server3");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(3, nodes);
|
||||
|
||||
// Test that we can get a node for a key
|
||||
final String node = consistentHash.get("key1");
|
||||
assertNotNull(node);
|
||||
assertTrue(nodes.contains(node));
|
||||
|
||||
// Test with different keys
|
||||
final String node2 = consistentHash.get("key2");
|
||||
assertNotNull(node2);
|
||||
assertTrue(nodes.contains(node2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNodeWithEmptyCircle() {
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(2, Collections.emptyList());
|
||||
|
||||
// Should return null when there are no nodes
|
||||
final String node = consistentHash.get("anyKey");
|
||||
assertNull(node);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConsistency() {
|
||||
final List<String> nodes = Arrays.asList("server1", "server2", "server3", "server4", "server5");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(10, nodes);
|
||||
|
||||
// Test that the same key always maps to the same node
|
||||
final String node1 = consistentHash.get("consistentKey");
|
||||
final String node2 = consistentHash.get("consistentKey");
|
||||
final String node3 = consistentHash.get("consistentKey");
|
||||
|
||||
assertEquals(node1, node2);
|
||||
assertEquals(node2, node3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadDistribution() {
|
||||
final List<String> nodes = Arrays.asList("server1", "server2", "server3");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(10, nodes);
|
||||
|
||||
// Map many keys to nodes to test distribution
|
||||
final int[] hits = new int[3];
|
||||
for (int i = 0; i < 100; i++) {
|
||||
final String node = consistentHash.get("key" + i);
|
||||
switch (node) {
|
||||
case "server1":
|
||||
hits[0]++;
|
||||
break;
|
||||
case "server2":
|
||||
hits[1]++;
|
||||
break;
|
||||
case "server3":
|
||||
hits[2]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that all servers got some traffic (with higher replica count, distribution should be relatively balanced)
|
||||
for (final int hitCount : hits) {
|
||||
assertTrue(hitCount > 0, "Each server should receive some load");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNodeAdditionDoesNotDisruptTooMuch() {
|
||||
final List<String> initialNodes = Arrays.asList("server1", "server2");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(10, initialNodes);
|
||||
|
||||
// Map keys to nodes with initial setup
|
||||
final String[] initialMappings = new String[50];
|
||||
for (int i = 0; i < 50; i++) {
|
||||
initialMappings[i] = consistentHash.get("key" + i);
|
||||
}
|
||||
|
||||
// Add a new node
|
||||
consistentHash.add("server3");
|
||||
|
||||
// Map the same keys again
|
||||
int remappedCount = 0;
|
||||
for (int i = 0; i < 50; i++) {
|
||||
final String newMapping = consistentHash.get("key" + i);
|
||||
if (!initialMappings[i].equals(newMapping)) {
|
||||
remappedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that not ALL keys are remapped (which would happen with naive modulo hashing)
|
||||
assertTrue(remappedCount < 50, "Not all keys should be remapped when adding a new node");
|
||||
// In a well-distributed consistent hash, typically only a fraction of keys are remapped
|
||||
assertTrue(remappedCount <= 20, "Remapping should be minimal");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNodeRemovalDoesNotDisruptTooMuch() {
|
||||
final List<String> initialNodes = Arrays.asList("server1", "server2", "server3");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(10, initialNodes);
|
||||
|
||||
// Map keys to nodes with initial setup
|
||||
final String[] initialMappings = new String[50];
|
||||
for (int i = 0; i < 50; i++) {
|
||||
initialMappings[i] = consistentHash.get("key" + i);
|
||||
}
|
||||
|
||||
// Remove a node
|
||||
consistentHash.remove("server3");
|
||||
|
||||
// Map the same keys again
|
||||
int remappedCount = 0;
|
||||
for (int i = 0; i < 50; i++) {
|
||||
final String newMapping = consistentHash.get("key" + i);
|
||||
if (!newMapping.equals(initialMappings[i])) {
|
||||
remappedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that not ALL keys are remapped
|
||||
assertTrue(remappedCount < 50, "Not all keys should be remapped when removing a node");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleNode() {
|
||||
final List<String> nodes = Collections.singletonList("onlyServer");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(5, nodes);
|
||||
|
||||
// All keys should map to the same server
|
||||
for (int i = 0; i < 20; i++) {
|
||||
assertEquals("onlyServer", consistentHash.get("key" + i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManyVirtualNodes() {
|
||||
final List<String> nodes = Arrays.asList("node1", "node2");
|
||||
final ConsistentHash<String> consistentHash = new ConsistentHash<>(100, nodes); // Many replicas
|
||||
|
||||
// Should still work normally
|
||||
final String node = consistentHash.get("testKey");
|
||||
assertNotNull(node);
|
||||
assertTrue(nodes.contains(node));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user