| /*=============================================================================# |
| # Copyright (c) 2020, 2021 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.rj.data.impl; |
| |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertFalse; |
| import static org.junit.jupiter.api.Assertions.assertThrows; |
| import static org.junit.jupiter.api.Assertions.assertTrue; |
| import static org.junit.jupiter.api.Assertions.fail; |
| |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.util.function.IntConsumer; |
| import java.util.function.LongConsumer; |
| import java.util.function.Supplier; |
| |
| import org.opentest4j.AssertionFailedError; |
| |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| |
| import org.eclipse.statet.rj.data.RJIO; |
| import org.eclipse.statet.rj.data.RStore; |
| |
| |
| @NonNullByDefault |
| public abstract class AbstractRStoreTest { |
| |
| |
| static boolean isBigDataEnabled(final int factor) { |
| return AbstractRawStore.DEFAULT_LONG_DATA_SEGMENT_LENGTH <= (1 << 16); |
| } |
| |
| |
| private static class IndexMessageSupplier implements Supplier<String> { |
| |
| private final String part1; |
| private long index; |
| |
| public IndexMessageSupplier(final String part1) { |
| this.part1= part1; |
| } |
| |
| @Override |
| public String get() { |
| return this.part1 + " at data index [" + this.index + "]"; |
| } |
| |
| } |
| |
| private static final IndexMessageSupplier ARRAY_DIFFERS_MESSAGE= new IndexMessageSupplier("array differs"); |
| private static final IndexMessageSupplier STORE_DIFFERS_MESSAGE= new IndexMessageSupplier("store differs"); |
| |
| static final Supplier<String> arrayDiffersAt(final int index) { |
| final IndexMessageSupplier supplier= ARRAY_DIFFERS_MESSAGE; |
| supplier.index= index; |
| return supplier; |
| } |
| |
| static final Supplier<String> storeDiffersAt(final int index) { |
| final IndexMessageSupplier supplier= STORE_DIFFERS_MESSAGE; |
| supplier.index= index; |
| return supplier; |
| } |
| |
| |
| static class CaseData<TStore extends RStore<?>> { |
| |
| |
| final String label; |
| |
| final int length; |
| |
| final boolean[] nas; |
| |
| |
| public CaseData(final String label, final boolean[] nas) { |
| this.label= label; |
| this.length= nas.length; |
| this.nas= nas; |
| } |
| |
| public CaseData(final String label, final int length) { |
| this.label= label; |
| this.length= length; |
| this.nas= new boolean[length]; |
| } |
| |
| |
| private byte[] externalReference= nonNullLateInit(); |
| |
| protected void setReference(final TStore store) { |
| this.externalReference= writeExternal(store); |
| } |
| |
| byte[] getExternalBytes() { |
| return this.externalReference; |
| } |
| |
| |
| @Override |
| public String toString() { |
| return this.label; |
| } |
| |
| } |
| |
| static int getSegmentCount(final CaseData<?> data, final int segmentLength) { |
| if (data.length == 0) { |
| return 0; |
| } |
| return 1 + (data.length - 1) / segmentLength; |
| } |
| |
| |
| @FunctionalInterface |
| protected static interface StoreConstructor<TStore extends RStore<?>> { |
| |
| TStore create(final RJIO in, final int length) throws IOException; |
| |
| } |
| |
| static byte[] writeExternal(final RStore<?> store) { |
| try { |
| final ByteArrayOutputStream byteOut= new ByteArrayOutputStream(); |
| final ObjectOutputStream objOut= new ObjectOutputStream(byteOut); |
| final RJIO out= RJIO.get(objOut); |
| try { |
| out.flags= 0; |
| out.writeLong(store.getLength()); |
| ((ExternalizableRStore)store).writeExternal(out); |
| } |
| finally { |
| out.disconnect(objOut); |
| } |
| objOut.close(); |
| return byteOut.toByteArray(); |
| } |
| catch (final IOException e) { |
| throw new AssertionFailedError(null, e); |
| } |
| } |
| |
| static <TStore extends RStore<?>> TStore readExternal( |
| final StoreConstructor<TStore> constructor, final byte[] serBytes) { |
| try { |
| final ByteArrayInputStream byteIn= new ByteArrayInputStream(serBytes); |
| final ObjectInputStream objIn= new ObjectInputStream(byteIn); |
| final RJIO in= RJIO.get(objIn); |
| try { |
| in.flags= 0; |
| final long length= in.readLong(); |
| if (length > Integer.MAX_VALUE) { |
| fail("actual: " + length); |
| } |
| return constructor.create(in, (int)length); |
| } |
| finally { |
| in.disconnect(objIn); |
| } |
| } |
| catch (final IOException e) { |
| throw new AssertionFailedError(null, e); |
| } |
| } |
| |
| |
| static void checkLength(final CaseData<?> data, final RStore<?> store) { |
| assertEquals(data.length, store.getLength()); |
| } |
| |
| static void checkIsNA(final CaseData<?> data, final RStore<?> store) { |
| for (int i= 0; i < data.length; i++) { |
| final int i0= i; |
| if (data.nas[i0]) { |
| assertTrue(store.isNA(i0), storeDiffersAt(i0)); |
| assertTrue(store.isNA((long)i0), storeDiffersAt(i0)); |
| } |
| else { |
| assertFalse(store.isNA(i0), storeDiffersAt(i0)); |
| assertFalse(store.isNA((long)i0), storeDiffersAt(i0)); |
| } |
| } |
| assertIndexOutOfBounds(data, store::isNA, store::isNA); |
| } |
| |
| static void checkIsMissingNonNum(final CaseData<?> data, final RStore<?> store) { |
| for (int i= 0; i < data.length; i++) { |
| final int i0= i; |
| if (data.nas[i0]) { |
| assertTrue(store.isMissing(i0), storeDiffersAt(i0)); |
| assertTrue(store.isMissing((long)i0), storeDiffersAt(i0)); |
| } |
| else { |
| assertFalse(store.isMissing(i0), storeDiffersAt(i0)); |
| assertFalse(store.isMissing((long)i0), storeDiffersAt(i0)); |
| } |
| } |
| assertIndexOutOfBounds(data, store::isMissing, store::isMissing); |
| } |
| |
| |
| static void assertUnsupported(final CaseData<?> data, |
| final IntConsumer intMethod, final LongConsumer longMethod) { |
| if (data.length > 0) { |
| assertThrows(UnsupportedOperationException.class, () -> intMethod.accept(0)); |
| assertThrows(UnsupportedOperationException.class, () -> longMethod.accept(0)); |
| } |
| assertThrows(UnsupportedOperationException.class, () -> intMethod.accept(-1)); |
| assertThrows(UnsupportedOperationException.class, () -> longMethod.accept(-1)); |
| assertThrows(UnsupportedOperationException.class, () -> intMethod.accept(data.length)); |
| assertThrows(UnsupportedOperationException.class, () -> longMethod.accept(data.length)); |
| assertThrows(UnsupportedOperationException.class, () -> longMethod.accept(Long.MAX_VALUE)); |
| } |
| |
| static void assertIndexOutOfBounds(final CaseData<?> data, |
| final IntConsumer intMethod, final LongConsumer longMethod) { |
| assertThrows(IndexOutOfBoundsException.class, () -> intMethod.accept(-1)); |
| assertThrows(IndexOutOfBoundsException.class, () -> longMethod.accept(-1)); |
| assertThrows(IndexOutOfBoundsException.class, () -> intMethod.accept(data.length)); |
| assertThrows(IndexOutOfBoundsException.class, () -> longMethod.accept(data.length)); |
| assertThrows(IndexOutOfBoundsException.class, () -> longMethod.accept(Long.MAX_VALUE)); |
| } |
| |
| } |