blob: 69680a217b2b5fecc3bb7cd3be0fddd74b2e87cd [file] [log] [blame]
/*=============================================================================#
# 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));
}
}