/
githubmirror
/
panama-vector
Обзор
Документация
Войти
/
githubmirror
/
panama-vector
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/jdk/java/nio/Buffer/EqualsCompareTest.java
722 строки
27 KB
Brian Burkhalter
8378808: Refactor java/nio/Buffer TestNG tests to use JUnit
10 мар 2026, 21:44
10 мар 2026, 21:44
73b13f9
Код
Авторство
О чём код?
/* * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.DoubleBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.MappedByteBuffer; import java.nio.ShortBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; import java.util.function.LongFunction; import java.util.stream.IntStream; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardOpenOption.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /* * @test * @bug 8193085 8199773 * @summary tests for buffer equals and compare * @run junit EqualsCompareTest */ public class EqualsCompareTest { // Maximum width in bits static final int MAX_WIDTH = 512; static final Map<Class, Integer> typeToWidth; static { typeToWidth = new HashMap<>(); typeToWidth.put(byte.class, Byte.SIZE); typeToWidth.put(short.class, Short.SIZE); typeToWidth.put(char.class, Character.SIZE); typeToWidth.put(int.class, Integer.SIZE); typeToWidth.put(long.class, Long.SIZE); typeToWidth.put(float.class, Float.SIZE); typeToWidth.put(double.class, Double.SIZE); } static int arraySizeFor(Class<?> type) { assert type.isPrimitive(); return 4 * MAX_WIDTH / typeToWidth.get(type); } enum BufferKind { HEAP, HEAP_VIEW, DIRECT; } static abstract class BufferType<T extends Buffer, E> { final BufferKind k; final Class<?> bufferType; final Class<?> elementType; final MethodHandle eq; final MethodHandle cmp; final MethodHandle mismtch; final MethodHandle getter; final MethodHandle setter; BufferType(BufferKind k, Class<T> bufferType, Class<?> elementType) { this.k = k; this.bufferType = bufferType; this.elementType = elementType; var lookup = MethodHandles.lookup(); try { eq = lookup.findVirtual(bufferType, "equals", MethodType.methodType(boolean.class, Object.class)); cmp = lookup.findVirtual(bufferType, "compareTo", MethodType.methodType(int.class, bufferType)); mismtch = lookup.findVirtual(bufferType, "mismatch", MethodType.methodType(int.class, bufferType)); getter = lookup.findVirtual(bufferType, "get", MethodType.methodType(elementType, int.class)); setter = lookup.findVirtual(bufferType, "put", MethodType.methodType(bufferType, int.class, elementType)); } catch (Exception e) { throw new AssertionError(e); } } @Override public String toString() { return bufferType.getName() + " " + k; } T construct(int length) { return construct(length, ByteOrder.BIG_ENDIAN); } abstract T construct(int length, ByteOrder bo); @SuppressWarnings("unchecked") T slice(T a, int from, int to, boolean dupOtherwiseSlice) { a = (T) a.position(from).limit(to); return (T) (dupOtherwiseSlice ? a.duplicate() : a.slice()); } @SuppressWarnings("unchecked") E get(T a, int i) { try { return (E) getter.invoke(a, i); } catch (RuntimeException | Error e) { throw e; } catch (Throwable t) { throw new Error(t); } } void set(T a, int i, Object v) { try { setter.invoke(a, i, convert(v)); } catch (RuntimeException | Error e) { throw e; } catch (Throwable t) { throw new Error(t); } } abstract Object convert(Object o); boolean equals(T a, T b) { try { return (boolean) eq.invoke(a, b); } catch (RuntimeException | Error e) { throw e; } catch (Throwable t) { throw new Error(t); } } int compare(T a, T b) { try { return (int) cmp.invoke(a, b); } catch (RuntimeException | Error e) { throw e; } catch (Throwable t) { throw new Error(t); } } boolean pairWiseEquals(T a, T b) { if (a.remaining() != b.remaining()) return false; int p = a.position(); for (int i = a.limit() - 1, j = b.limit() - 1; i >= p; i--, j--) if (!get(a, i).equals(get(b, j))) return false; return true; } int mismatch(T a, T b) { try { return (int) mismtch.invoke(a, b); } catch (RuntimeException | Error e) { throw e; } catch (Throwable t) { throw new Error(t); } } static class Bytes extends BufferType<ByteBuffer, Byte> { Bytes(BufferKind k) { super(k, ByteBuffer.class, byte.class); } @Override ByteBuffer construct(int length, ByteOrder bo) { switch (k) { case DIRECT: return ByteBuffer.allocateDirect(length).order(bo); default: case HEAP_VIEW: case HEAP: return ByteBuffer.allocate(length).order(bo); } } @Override Object convert(Object o) { return o instanceof Integer ? ((Integer) o).byteValue() : o; } } static class Chars extends BufferType<CharBuffer, Character> { Chars(BufferKind k) { super(k, CharBuffer.class, char.class); } @Override CharBuffer construct(int length, ByteOrder bo) { switch (k) { case DIRECT: return ByteBuffer.allocateDirect(length * Character.BYTES). order(bo). asCharBuffer(); case HEAP_VIEW: return ByteBuffer.allocate(length * Character.BYTES). order(bo). asCharBuffer(); default: case HEAP: return CharBuffer.allocate(length); } } @Override Object convert(Object o) { return o instanceof Integer ? (char) ((Integer) o).intValue() : o; } CharBuffer transformToStringBuffer(CharBuffer c) { char[] chars = new char[c.remaining()]; c.get(chars); return CharBuffer.wrap(new String(chars)); } } static class Shorts extends BufferType<ShortBuffer, Short> { Shorts(BufferKind k) { super(k, ShortBuffer.class, short.class); } @Override ShortBuffer construct(int length, ByteOrder bo) { switch (k) { case DIRECT: return ByteBuffer.allocateDirect(length * Short.BYTES). order(bo). asShortBuffer(); case HEAP_VIEW: return ByteBuffer.allocate(length * Short.BYTES). order(bo). asShortBuffer(); default: case HEAP: return ShortBuffer.allocate(length); } } @Override Object convert(Object o) { return o instanceof Integer ? ((Integer) o).shortValue() : o; } } static class Ints extends BufferType<IntBuffer, Integer> { Ints(BufferKind k) { super(k, IntBuffer.class, int.class); } @Override IntBuffer construct(int length, ByteOrder bo) { switch (k) { case DIRECT: return ByteBuffer.allocateDirect(length * Integer.BYTES). order(bo). asIntBuffer(); case HEAP_VIEW: return ByteBuffer.allocate(length * Integer.BYTES). order(bo). asIntBuffer(); default: case HEAP: return IntBuffer.allocate(length); } } Object convert(Object o) { return o; } } static class Floats extends BufferType<FloatBuffer, Float> { Floats(BufferKind k) { super(k, FloatBuffer.class, float.class); } @Override FloatBuffer construct(int length, ByteOrder bo) { switch (k) { case DIRECT: return ByteBuffer.allocateDirect(length * Float.BYTES). order(bo). asFloatBuffer(); case HEAP_VIEW: return ByteBuffer.allocate(length * Float.BYTES). order(bo). asFloatBuffer(); default: case HEAP: return FloatBuffer.allocate(length); } } @Override Object convert(Object o) { return o instanceof Integer ? ((Integer) o).floatValue() : o; } @Override boolean pairWiseEquals(FloatBuffer a, FloatBuffer b) { if (a.remaining() != b.remaining()) return false; int p = a.position(); for (int i = a.limit() - 1, j = b.limit() - 1; i >= p; i--, j--) { float av = a.get(i); float bv = b.get(j); if (av != bv && (!Float.isNaN(av) || !Float.isNaN(bv))) return false; } return true; } } static class Longs extends BufferType<LongBuffer, Long> { Longs(BufferKind k) { super(k, LongBuffer.class, long.class); } @Override LongBuffer construct(int length, ByteOrder bo) { switch (k) { case DIRECT: return ByteBuffer.allocateDirect(length * Long.BYTES). order(bo). asLongBuffer(); case HEAP_VIEW: return ByteBuffer.allocate(length * Long.BYTES). order(bo). asLongBuffer(); default: case HEAP: return LongBuffer.allocate(length); } } @Override Object convert(Object o) { return o instanceof Integer ? ((Integer) o).longValue() : o; } } static class Doubles extends BufferType<DoubleBuffer, Double> { Doubles(BufferKind k) { super(k, DoubleBuffer.class, double.class); } @Override DoubleBuffer construct(int length, ByteOrder bo) { switch (k) { case DIRECT: return ByteBuffer.allocateDirect(length * Double.BYTES). order(bo). asDoubleBuffer(); case HEAP_VIEW: return ByteBuffer.allocate(length * Double.BYTES). order(bo). asDoubleBuffer(); default: case HEAP: return DoubleBuffer.allocate(length); } } @Override Object convert(Object o) { return o instanceof Integer ? ((Integer) o).doubleValue() : o; } @Override boolean pairWiseEquals(DoubleBuffer a, DoubleBuffer b) { if (a.remaining() != b.remaining()) return false; int p = a.position(); for (int i = a.limit() - 1, j = b.limit() - 1; i >= p; i--, j--) { double av = a.get(i); double bv = b.get(j); if (av != bv && (!Double.isNaN(av) || !Double.isNaN(bv))) return false; } return true; } } } public static Stream<BufferType> bufferTypesSource() { return Stream.of (new BufferType.Bytes(BufferKind.HEAP), new BufferType.Bytes(BufferKind.DIRECT), new BufferType.Chars(BufferKind.HEAP), new BufferType.Chars(BufferKind.HEAP_VIEW), new BufferType.Chars(BufferKind.DIRECT), new BufferType.Shorts(BufferKind.HEAP), new BufferType.Shorts(BufferKind.HEAP_VIEW), new BufferType.Shorts(BufferKind.DIRECT), new BufferType.Ints(BufferKind.HEAP), new BufferType.Ints(BufferKind.HEAP_VIEW), new BufferType.Ints(BufferKind.DIRECT), new BufferType.Floats(BufferKind.HEAP), new BufferType.Floats(BufferKind.HEAP_VIEW), new BufferType.Floats(BufferKind.DIRECT), new BufferType.Longs(BufferKind.HEAP), new BufferType.Longs(BufferKind.HEAP_VIEW), new BufferType.Longs(BufferKind.DIRECT), new BufferType.Doubles(BufferKind.HEAP), new BufferType.Doubles(BufferKind.HEAP_VIEW), new BufferType.Doubles(BufferKind.DIRECT)); } public static Stream<Arguments> floatBufferTypesSource() { LongFunction<Object> bTof = rb -> Float.intBitsToFloat((int) rb); LongFunction<Object> bToD = Double::longBitsToDouble; return Stream.of (// canonical and non-canonical NaNs // If conversion is a signalling NaN it may be subject to conversion to a // quiet NaN on some processors, even if a copy is performed // The tests assume that if conversion occurs it does not convert to the // canonical NaN Arguments.of(new BufferType.Floats(BufferKind.HEAP), 0x7fc00000L, 0x7f800001L, bTof), Arguments.of(new BufferType.Floats(BufferKind.HEAP_VIEW), 0x7fc00000L, 0x7f800001L, bTof), Arguments.of(new BufferType.Floats(BufferKind.DIRECT), 0x7fc00000L, 0x7f800001L, bTof), Arguments.of(new BufferType.Doubles(BufferKind.HEAP), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD), Arguments.of(new BufferType.Doubles(BufferKind.HEAP_VIEW), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD), Arguments.of(new BufferType.Doubles(BufferKind.DIRECT), 0x7ff8000000000000L, 0x7ff0000000000001L, bToD), // +0.0 and -0.0 Arguments.of(new BufferType.Floats(BufferKind.HEAP), 0x0L, 0x80000000L, bTof), Arguments.of(new BufferType.Floats(BufferKind.HEAP_VIEW), 0x0L, 0x80000000L, bTof), Arguments.of(new BufferType.Floats(BufferKind.DIRECT), 0x0L, 0x80000000L, bTof), Arguments.of(new BufferType.Doubles(BufferKind.HEAP), 0x0L, 0x8000000000000000L, bToD), Arguments.of(new BufferType.Doubles(BufferKind.HEAP_VIEW), 0x0L, 0x8000000000000000L, bToD), Arguments.of(new BufferType.Doubles(BufferKind.DIRECT), 0x0L, 0x8000000000000000L, bToD)); } public static Stream<Arguments> charBufferTypesSource() { return Stream.of (Arguments.of(new BufferType.Chars(BufferKind.HEAP)), Arguments.of(new BufferType.Chars(BufferKind.HEAP_VIEW)), Arguments.of(new BufferType.Chars(BufferKind.DIRECT))); } // Tests all primitive buffers @ParameterizedTest @MethodSource("bufferTypesSource") <E> void testBuffers(BufferType<Buffer, E> bufferType) { // Test with buffers of the same byte order (BE) BiFunction<BufferType<Buffer, E>, Integer, Buffer> constructor = (at, s) -> { Buffer a = at.construct(s); for (int x = 0; x < s; x++) { at.set(a, x, x % 8); } return a; }; testBufferType(bufferType, constructor, constructor); // Test with buffers of different byte order if (bufferType.elementType != byte.class && (bufferType.k == BufferKind.HEAP_VIEW || bufferType.k == BufferKind.DIRECT)) { BiFunction<BufferType<Buffer, E>, Integer, Buffer> leConstructor = (at, s) -> { Buffer a = at.construct(s, ByteOrder.LITTLE_ENDIAN); for (int x = 0; x < s; x++) { at.set(a, x, x % 8); } return a; }; testBufferType(bufferType, constructor, leConstructor); } } // Tests float and double buffers with edge-case values (NaN, -0.0, +0.0) @ParameterizedTest @MethodSource("floatBufferTypesSource") public void testFloatBuffers( BufferType<Buffer, Float> bufferType, long rawBitsA, long rawBitsB, LongFunction<Object> bitsToFloat) { Object av = bitsToFloat.apply(rawBitsA); Object bv = bitsToFloat.apply(rawBitsB); BiFunction<BufferType<Buffer, Float>, Integer, Buffer> allAs = (at, s) -> { Buffer b = at.construct(s); for (int x = 0; x < s; x++) { at.set(b, x, av); } return b; }; BiFunction<BufferType<Buffer, Float>, Integer, Buffer> allBs = (at, s) -> { Buffer b = at.construct(s); for (int x = 0; x < s; x++) { at.set(b, x, bv); } return b; }; BiFunction<BufferType<Buffer, Float>, Integer, Buffer> halfBs = (at, s) -> { Buffer b = at.construct(s); for (int x = 0; x < s / 2; x++) { at.set(b, x, bv); } for (int x = s / 2; x < s; x++) { at.set(b, x, 1); } return b; }; // Sanity check int size = arraySizeFor(bufferType.elementType); assertTrue(bufferType.pairWiseEquals(allAs.apply(bufferType, size), allBs.apply(bufferType, size))); assertTrue(bufferType.equals(allAs.apply(bufferType, size), allBs.apply(bufferType, size))); testBufferType(bufferType, allAs, allBs); testBufferType(bufferType, allAs, halfBs); } // Tests CharBuffer for region sources and CharSequence sources @ParameterizedTest @MethodSource("charBufferTypesSource") public void testCharBuffers(BufferType.Chars charBufferType) { BiFunction<BufferType.Chars, Integer, CharBuffer> constructor = (at, s) -> { CharBuffer a = at.construct(s); for (int x = 0; x < s; x++) { at.set(a, x, x % 8); } return a; }; BiFunction<BufferType.Chars, Integer, CharBuffer> constructorX = constructor. andThen(charBufferType::transformToStringBuffer); testBufferType(charBufferType, constructor, constructorX); } <B extends Buffer, E, BT extends BufferType<B, E>> void testBufferType(BT bt, BiFunction<BT, Integer, B> aConstructor, BiFunction<BT, Integer, B> bConstructor) { int n = arraySizeFor(bt.elementType); for (boolean dupOtherwiseSlice : new boolean[]{ false, true }) { for (int s : ranges(0, n)) { B a = aConstructor.apply(bt, s); B b = bConstructor.apply(bt, s); for (int aFrom : ranges(0, s)) { for (int aTo : ranges(aFrom, s)) { int aLength = aTo - aFrom; B as = aLength != s ? bt.slice(a, aFrom, aTo, dupOtherwiseSlice) : a; for (int bFrom : ranges(0, s)) { for (int bTo : ranges(bFrom, s)) { int bLength = bTo - bFrom; B bs = bLength != s ? bt.slice(b, bFrom, bTo, dupOtherwiseSlice) : b; boolean eq = bt.pairWiseEquals(as, bs); assertEquals(eq, bt.equals(as, bs)); assertEquals(eq, bt.equals(bs, as)); if (eq) { assertEquals(0, bt.compare(as, bs)); assertEquals(0, bt.compare(bs, as)); // If buffers are equal, there shall be no mismatch assertEquals(-1, bt.mismatch(as, bs)); assertEquals(-1, bt.mismatch(bs, as)); } else { int aCb = bt.compare(as, bs); int bCa = bt.compare(bs, as); int v = Integer.signum(aCb) * Integer.signum(bCa); assertEquals(-1, v); int aMs = bt.mismatch(as, bs); int bMs = bt.mismatch(bs, as); assertNotEquals(-1, aMs); assertEquals(bMs, aMs); } } } if (aLength > 0 && !a.isReadOnly()) { for (int i = aFrom; i < aTo; i++) { B c = aConstructor.apply(bt, a.capacity()); B cs = aLength != s ? bt.slice(c, aFrom, aTo, dupOtherwiseSlice) : c; // Create common prefix with a length of i - aFrom bt.set(c, i, -1); assertFalse(bt.equals(c, a)); int cCa = bt.compare(cs, as); int aCc = bt.compare(as, cs); int v = Integer.signum(cCa) * Integer.signum(aCc); assertEquals(-1, v); int cMa = bt.mismatch(cs, as); int aMc = bt.mismatch(as, cs); assertEquals(aMc, cMa); assertEquals(i - aFrom, cMa); } } } } } } } static int[] ranges(int from, int to) { int width = to - from; switch (width) { case 0: return new int[]{}; case 1: return new int[]{from, to}; case 2: return new int[]{from, from + 1, to}; case 3: return new int[]{from, from + 1, from + 2, to}; default: return IntStream.of(from, from + 1, from + 2, to / 2 - 1, to / 2, to / 2 + 1, to - 2, to - 1, to) .filter(i -> i >= from && i <= to) .distinct().toArray(); } } @Test void testHashCode() throws IOException { byte[] bytes = "hello world".getBytes(UTF_8); Path path = Files.createTempFile("", ""); Files.write(path, bytes); try (FileChannel fc = FileChannel.open(path, READ, DELETE_ON_CLOSE)) { MappedByteBuffer one = fc.map(FileChannel.MapMode.READ_ONLY, 0, bytes.length); ByteBuffer two = ByteBuffer.wrap(bytes); assertEquals(two, one); assertEquals(two.hashCode(), one.hashCode()); } } }