diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index 87191bb2b..0f886cd2a 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -21,7 +21,7 @@
-
+
diff --git a/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTest.java b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTest.java
index 5bc29c45c..d7cc58db4 100644
--- a/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTest.java
+++ b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTest.java
@@ -11,6 +11,7 @@
import com.refinedmods.refinedstorage2.api.network.impl.node.relay.RelayInputNetworkNode;
import com.refinedmods.refinedstorage2.api.network.impl.node.relay.RelayOutputNetworkNode;
import com.refinedmods.refinedstorage2.api.network.impl.node.storage.StorageNetworkNode;
+import com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer.StorageTransferNetworkNode;
import com.refinedmods.refinedstorage2.network.test.nodefactory.ControllerNetworkNodeFactory;
import com.refinedmods.refinedstorage2.network.test.nodefactory.DetectorNetworkNodeFactory;
import com.refinedmods.refinedstorage2.network.test.nodefactory.ExporterNetworkNodeFactory;
@@ -22,6 +23,7 @@
import com.refinedmods.refinedstorage2.network.test.nodefactory.RelayOutputNetworkNodeFactory;
import com.refinedmods.refinedstorage2.network.test.nodefactory.SimpleNetworkNodeFactory;
import com.refinedmods.refinedstorage2.network.test.nodefactory.StorageNetworkNodeFactory;
+import com.refinedmods.refinedstorage2.network.test.nodefactory.StorageTransferNetworkNodeFactory;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -44,5 +46,6 @@
@RegisterNetworkNode(value = DetectorNetworkNodeFactory.class, clazz = DetectorNetworkNode.class)
@RegisterNetworkNode(value = RelayInputNetworkNodeFactory.class, clazz = RelayInputNetworkNode.class)
@RegisterNetworkNode(value = RelayOutputNetworkNodeFactory.class, clazz = RelayOutputNetworkNode.class)
+@RegisterNetworkNode(value = StorageTransferNetworkNodeFactory.class, clazz = StorageTransferNetworkNode.class)
public @interface NetworkTest {
}
diff --git a/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTestFixtures.java b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTestFixtures.java
index 4cbfc678c..aa4a5a1c5 100644
--- a/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTestFixtures.java
+++ b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/NetworkTestFixtures.java
@@ -15,6 +15,8 @@
import com.refinedmods.refinedstorage2.api.resource.list.ResourceListImpl;
import com.refinedmods.refinedstorage2.network.test.fake.FakePermissions;
+import java.util.LinkedHashMap;
+
public final class NetworkTestFixtures {
public static final ComponentMapFactory NETWORK_COMPONENT_MAP_FACTORY =
new ComponentMapFactory<>();
@@ -30,7 +32,7 @@ public final class NetworkTestFixtures {
);
NETWORK_COMPONENT_MAP_FACTORY.addFactory(
StorageNetworkComponent.class,
- network -> new StorageNetworkComponentImpl(new ResourceListImpl())
+ network -> new StorageNetworkComponentImpl(new ResourceListImpl(new LinkedHashMap<>()))
);
NETWORK_COMPONENT_MAP_FACTORY.addFactory(
SecurityNetworkComponent.class,
diff --git a/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/nodefactory/StorageTransferNetworkNodeFactory.java b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/nodefactory/StorageTransferNetworkNodeFactory.java
new file mode 100644
index 000000000..3dc766bd3
--- /dev/null
+++ b/refinedstorage2-network-test/src/main/java/com/refinedmods/refinedstorage2/network/test/nodefactory/StorageTransferNetworkNodeFactory.java
@@ -0,0 +1,23 @@
+package com.refinedmods.refinedstorage2.network.test.nodefactory;
+
+import com.refinedmods.refinedstorage2.api.network.impl.node.AbstractNetworkNode;
+import com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer.StorageTransferNetworkNode;
+import com.refinedmods.refinedstorage2.network.test.AddNetworkNode;
+
+import java.util.Map;
+
+public class StorageTransferNetworkNodeFactory extends AbstractNetworkNodeFactory {
+ public static final String PROPERTY_ENERGY_USAGE_PER_STORAGE = "energy_usage_per_storage";
+ public static final String PROPERTY_SIZE = "size";
+
+ @Override
+ protected AbstractNetworkNode innerCreate(final AddNetworkNode ctx, final Map properties) {
+ final long energyUsagePerStorage = (long) properties.getOrDefault(PROPERTY_ENERGY_USAGE_PER_STORAGE, 0L);
+ final int size = (int) properties.getOrDefault(PROPERTY_SIZE, 6);
+ return new StorageTransferNetworkNode(
+ getEnergyUsage(properties),
+ energyUsagePerStorage,
+ size
+ );
+ }
+}
diff --git a/refinedstorage2-network-test/src/test/java/com/refinedmods/refinedstorage2/network/test/NetworkNodeFactoryTest.java b/refinedstorage2-network-test/src/test/java/com/refinedmods/refinedstorage2/network/test/NetworkNodeFactoryTest.java
index bfe41ac4f..cff8bdbf9 100644
--- a/refinedstorage2-network-test/src/test/java/com/refinedmods/refinedstorage2/network/test/NetworkNodeFactoryTest.java
+++ b/refinedstorage2-network-test/src/test/java/com/refinedmods/refinedstorage2/network/test/NetworkNodeFactoryTest.java
@@ -11,6 +11,7 @@
import com.refinedmods.refinedstorage2.api.network.impl.node.relay.RelayInputNetworkNode;
import com.refinedmods.refinedstorage2.api.network.impl.node.relay.RelayOutputNetworkNode;
import com.refinedmods.refinedstorage2.api.network.impl.node.storage.StorageNetworkNode;
+import com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer.StorageTransferNetworkNode;
import org.junit.jupiter.api.Test;
@@ -41,6 +42,8 @@ class NetworkNodeFactoryTest {
RelayInputNetworkNode relayInput;
@AddNetworkNode
RelayOutputNetworkNode relayOutput;
+ @AddNetworkNode
+ StorageTransferNetworkNode storageTransfer;
@Test
void testInitialization() {
diff --git a/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferListener.java b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferListener.java
new file mode 100644
index 000000000..b4e34ced4
--- /dev/null
+++ b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferListener.java
@@ -0,0 +1,6 @@
+package com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer;
+
+@FunctionalInterface
+public interface StorageTransferListener {
+ void onTransferSuccess(int index);
+}
diff --git a/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferMode.java b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferMode.java
new file mode 100644
index 000000000..58e8eed37
--- /dev/null
+++ b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferMode.java
@@ -0,0 +1,6 @@
+package com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer;
+
+public enum StorageTransferMode {
+ INSERT,
+ EXTRACT
+}
diff --git a/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNode.java b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNode.java
new file mode 100644
index 000000000..0680ffccd
--- /dev/null
+++ b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNode.java
@@ -0,0 +1,141 @@
+package com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer;
+
+import com.refinedmods.refinedstorage2.api.network.impl.node.AbstractStorageContainerNetworkNode;
+import com.refinedmods.refinedstorage2.api.network.node.NetworkNodeActor;
+import com.refinedmods.refinedstorage2.api.network.storage.StorageNetworkComponent;
+import com.refinedmods.refinedstorage2.api.resource.ResourceAmount;
+import com.refinedmods.refinedstorage2.api.resource.ResourceKey;
+import com.refinedmods.refinedstorage2.api.storage.Actor;
+import com.refinedmods.refinedstorage2.api.storage.Storage;
+import com.refinedmods.refinedstorage2.api.storage.TransferHelper;
+import com.refinedmods.refinedstorage2.api.storage.limited.LimitedStorage;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.function.Predicate;
+import java.util.function.ToLongFunction;
+import javax.annotation.Nullable;
+
+public class StorageTransferNetworkNode extends AbstractStorageContainerNetworkNode {
+ private final Actor actor = new NetworkNodeActor(this);
+
+ private StorageTransferMode mode = StorageTransferMode.INSERT;
+ @Nullable
+ private ToLongFunction transferQuotaProvider;
+ @Nullable
+ private StorageTransferListener listener;
+
+ public StorageTransferNetworkNode(final long energyUsage, final long energyUsagePerStorage, final int size) {
+ super(energyUsage, energyUsagePerStorage, size);
+ }
+
+ public void setMode(final StorageTransferMode mode) {
+ this.mode = mode;
+ }
+
+ public void setTransferQuotaProvider(final ToLongFunction transferQuotaProvider) {
+ this.transferQuotaProvider = transferQuotaProvider;
+ }
+
+ public void setListener(@Nullable final StorageTransferListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void doWork() {
+ super.doWork();
+ if (!isActive() || network == null) {
+ return;
+ }
+ final StorageNetworkComponent networkStorage = network.getComponent(StorageNetworkComponent.class);
+ for (int i = 0; i < storages.length / 2; ++i) {
+ final Storage storage = storages[i];
+ if (storage == null) {
+ continue;
+ }
+ final Result result = transfer(storage, networkStorage);
+ if (processResult(result, i)) {
+ return;
+ }
+ }
+ }
+
+ private Result transfer(final Storage storage, final StorageNetworkComponent networkStorage) {
+ if (transferQuotaProvider == null) {
+ return Result.FAILURE;
+ }
+ final long transferQuota = transferQuotaProvider.applyAsLong(storage);
+ if (mode == StorageTransferMode.INSERT) {
+ return transfer(storage, networkStorage, transferQuota, this::hasNoExtractableResources);
+ }
+ return transfer(
+ networkStorage,
+ storage,
+ transferQuota,
+ source -> hasNoExtractableResources(source) || storageIsFull(storage)
+ );
+ }
+
+ private Result transfer(final Storage source,
+ final Storage destination,
+ final long transferQuota,
+ final Predicate readyPredicate) {
+ if (readyPredicate.test(source)) {
+ return Result.SUCCESS;
+ }
+ if (transfer(source, destination, transferQuota)) {
+ return readyPredicate.test(source)
+ ? Result.SUCCESS
+ : Result.PARTIAL;
+ }
+ return Result.FAILURE;
+ }
+
+ private boolean transfer(final Storage source, final Storage destination, final long transferQuota) {
+ long remainder = transferQuota;
+ final Collection sourceContents = new LinkedHashSet<>(source.getAll());
+ for (final ResourceAmount resourceAmount : sourceContents) {
+ final ResourceKey resource = resourceAmount.getResource();
+ if (!isAllowed(resource)) {
+ continue;
+ }
+ final long amount = Math.min(remainder, resourceAmount.getAmount());
+ final long transferred = TransferHelper.transfer(resource, amount, actor, source, destination, source);
+ remainder -= transferred;
+ if (remainder == 0) {
+ return true;
+ }
+ }
+ return remainder != transferQuota;
+ }
+
+ private boolean hasNoExtractableResources(final Storage source) {
+ return source.getAll().stream().noneMatch(resourceAmount -> isAllowed(resourceAmount.getResource()));
+ }
+
+ private boolean storageIsFull(final Storage storage) {
+ return storage instanceof LimitedStorage limitedStorage
+ && limitedStorage.getCapacity() > 0
+ && limitedStorage.getCapacity() == limitedStorage.getStored();
+ }
+
+ private boolean processResult(final Result result, final int index) {
+ if (result.isSuccess()) {
+ if (result == Result.SUCCESS && listener != null) {
+ listener.onTransferSuccess(index);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private enum Result {
+ SUCCESS,
+ PARTIAL,
+ FAILURE;
+
+ private boolean isSuccess() {
+ return this == PARTIAL || this == SUCCESS;
+ }
+ }
+}
diff --git a/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/package-info.java b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/package-info.java
new file mode 100644
index 000000000..6f817bc33
--- /dev/null
+++ b/refinedstorage2-network/src/main/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/package-info.java
@@ -0,0 +1,7 @@
+@ParametersAreNonnullByDefault
+@FieldsAndMethodsAreNonnullByDefault
+package com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer;
+
+import com.refinedmods.refinedstorage2.api.core.FieldsAndMethodsAreNonnullByDefault;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeProviderImpl.java b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/ProviderImpl.java
similarity index 66%
rename from refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeProviderImpl.java
rename to refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/ProviderImpl.java
index 5181ab680..a55d74eda 100644
--- a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeProviderImpl.java
+++ b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/ProviderImpl.java
@@ -1,13 +1,12 @@
-package com.refinedmods.refinedstorage2.api.network.impl.node.storage;
+package com.refinedmods.refinedstorage2.api.network.impl.node;
-import com.refinedmods.refinedstorage2.api.network.impl.node.AbstractStorageContainerNetworkNode;
import com.refinedmods.refinedstorage2.api.storage.Storage;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
-class StorageNetworkNodeProviderImpl implements AbstractStorageContainerNetworkNode.Provider {
+public class ProviderImpl implements AbstractStorageContainerNetworkNode.Provider {
private final Map storages = new HashMap<>();
@Override
diff --git a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/PriorityStorageNetworkNodeTest.java b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/PriorityStorageNetworkNodeTest.java
index b5b7fa837..f1761eac5 100644
--- a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/PriorityStorageNetworkNodeTest.java
+++ b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/PriorityStorageNetworkNodeTest.java
@@ -1,6 +1,7 @@
package com.refinedmods.refinedstorage2.api.network.impl.node.storage;
import com.refinedmods.refinedstorage2.api.core.Action;
+import com.refinedmods.refinedstorage2.api.network.impl.node.ProviderImpl;
import com.refinedmods.refinedstorage2.api.network.storage.StorageNetworkComponent;
import com.refinedmods.refinedstorage2.api.storage.EmptyActor;
import com.refinedmods.refinedstorage2.api.storage.Storage;
@@ -26,11 +27,11 @@ class PriorityStorageNetworkNodeTest {
@AddNetworkNode
StorageNetworkNode b;
- StorageNetworkNodeProviderImpl provider;
+ ProviderImpl provider;
@BeforeEach
void setUp() {
- provider = new StorageNetworkNodeProviderImpl();
+ provider = new ProviderImpl();
}
@ParameterizedTest
@@ -41,13 +42,13 @@ void shouldRespectPriority(
) {
// Arrange
final Storage storage1 = new LimitedStorageImpl(100);
- final StorageNetworkNodeProviderImpl provider1 = new StorageNetworkNodeProviderImpl();
+ final ProviderImpl provider1 = new ProviderImpl();
provider1.set(1, storage1);
a.setProvider(provider1);
a.setActive(true);
final Storage storage2 = new LimitedStorageImpl(100);
- final StorageNetworkNodeProviderImpl provider2 = new StorageNetworkNodeProviderImpl();
+ final ProviderImpl provider2 = new ProviderImpl();
provider2.set(1, storage2);
b.setProvider(provider2);
b.setActive(true);
diff --git a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeTest.java b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeTest.java
index 74241524f..441da0459 100644
--- a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeTest.java
+++ b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storage/StorageNetworkNodeTest.java
@@ -2,6 +2,7 @@
import com.refinedmods.refinedstorage2.api.core.Action;
import com.refinedmods.refinedstorage2.api.network.Network;
+import com.refinedmods.refinedstorage2.api.network.impl.node.ProviderImpl;
import com.refinedmods.refinedstorage2.api.network.storage.StorageNetworkComponent;
import com.refinedmods.refinedstorage2.api.resource.ResourceAmount;
import com.refinedmods.refinedstorage2.api.resource.filter.FilterMode;
@@ -55,11 +56,11 @@ class StorageNetworkNodeTest {
})
StorageNetworkNode sut;
- StorageNetworkNodeProviderImpl provider;
+ ProviderImpl provider;
@BeforeEach
void setUp() {
- provider = new StorageNetworkNodeProviderImpl();
+ provider = new ProviderImpl();
}
@Test
diff --git a/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNodeTest.java b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNodeTest.java
new file mode 100644
index 000000000..0249b8f3e
--- /dev/null
+++ b/refinedstorage2-network/src/test/java/com/refinedmods/refinedstorage2/api/network/impl/node/storagetransfer/StorageTransferNetworkNodeTest.java
@@ -0,0 +1,718 @@
+package com.refinedmods.refinedstorage2.api.network.impl.node.storagetransfer;
+
+import com.refinedmods.refinedstorage2.api.core.Action;
+import com.refinedmods.refinedstorage2.api.network.impl.node.ProviderImpl;
+import com.refinedmods.refinedstorage2.api.network.storage.StorageNetworkComponent;
+import com.refinedmods.refinedstorage2.api.resource.ResourceAmount;
+import com.refinedmods.refinedstorage2.api.resource.ResourceKey;
+import com.refinedmods.refinedstorage2.api.resource.filter.FilterMode;
+import com.refinedmods.refinedstorage2.api.resource.list.ResourceListImpl;
+import com.refinedmods.refinedstorage2.api.storage.Actor;
+import com.refinedmods.refinedstorage2.api.storage.EmptyActor;
+import com.refinedmods.refinedstorage2.api.storage.InMemoryStorageImpl;
+import com.refinedmods.refinedstorage2.api.storage.Storage;
+import com.refinedmods.refinedstorage2.api.storage.limited.LimitedStorageImpl;
+import com.refinedmods.refinedstorage2.network.test.AddNetworkNode;
+import com.refinedmods.refinedstorage2.network.test.InjectNetworkStorageComponent;
+import com.refinedmods.refinedstorage2.network.test.NetworkTest;
+import com.refinedmods.refinedstorage2.network.test.SetupNetwork;
+
+import java.util.LinkedHashMap;
+import java.util.Set;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+import static com.refinedmods.refinedstorage2.network.test.fake.FakeResources.A;
+import static com.refinedmods.refinedstorage2.network.test.fake.FakeResources.A_ALTERNATIVE;
+import static com.refinedmods.refinedstorage2.network.test.fake.FakeResources.B;
+import static com.refinedmods.refinedstorage2.network.test.fake.FakeResources.C;
+import static com.refinedmods.refinedstorage2.network.test.fake.FakeResources.D;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+@NetworkTest
+@SetupNetwork
+class StorageTransferNetworkNodeTest {
+ @AddNetworkNode
+ StorageTransferNetworkNode sut;
+ StorageTransferListener listener;
+ ProviderImpl provider;
+
+ @BeforeEach
+ void setUp() {
+ provider = new ProviderImpl();
+ listener = mock(StorageTransferListener.class);
+ sut.setListener(listener);
+ }
+
+ @Test
+ void shouldNotTransferWithoutNetwork(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+
+ final Storage source = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 20L);
+
+ // Act
+ sut.setNetwork(null);
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).isEmpty();
+ assertThat(source.getAll()).isNotEmpty();
+ }
+
+ @Test
+ void shouldNotTransferWhenInactive(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+
+ final Storage source = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 20L);
+
+ // Act
+ sut.setActive(false);
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).isEmpty();
+ assertThat(source.getAll()).isNotEmpty();
+ }
+
+ @ParameterizedTest
+ @EnumSource(StorageTransferMode.class)
+ void shouldNotTransferWithoutTransferQuotaProvider(
+ final StorageTransferMode mode,
+ @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage
+ ) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+ networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+
+ final Storage source = new InMemoryStorageImpl();
+ source.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setMode(mode);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(A, 5)
+ );
+ assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(B, 5)
+ );
+ }
+
+ @Test
+ void shouldInsert(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+
+ final Storage source0 = new LimitedStorageImpl(1) {
+ @Override
+ public long extract(final ResourceKey resource,
+ final long amount,
+ final Action action,
+ final Actor actor) {
+ return 0;
+ }
+ };
+ source0.insert(A, 1, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(0, source0);
+
+ final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source1.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(1, source1);
+
+ final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source2.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(2, source2);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 20L);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(A, 5),
+ new ResourceAmount(B, 5),
+ new ResourceAmount(C, 10)
+ );
+ assertThat(source0.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(A, 1)
+ );
+ assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(C, 25),
+ new ResourceAmount(D, 5)
+ );
+ assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(D, 5)
+ );
+ verify(listener, never()).onTransferSuccess(anyInt());
+ }
+
+ @Test
+ void shouldInsertAllowlist(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+
+ final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source1.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set((sut.getSize() / 2) - 2, source1);
+
+ final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source2.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set((sut.getSize() / 2) - 1, source2);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 20L);
+ sut.setFilterMode(FilterMode.ALLOW);
+ sut.setFilters(Set.of(A, B));
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(A, 5),
+ new ResourceAmount(B, 5)
+ );
+ assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(C, 35),
+ new ResourceAmount(D, 5)
+ );
+ assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(D, 5)
+ );
+ verify(listener, times(1)).onTransferSuccess((sut.getSize() / 2) - 2);
+ }
+
+ @Test
+ void shouldInsertBlocklist(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+
+ final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source1.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(0, source1);
+
+ final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source2.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(1, source2);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 20L);
+ sut.setFilterMode(FilterMode.BLOCK);
+ sut.setFilters(Set.of(A, B));
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(C, 20)
+ );
+ assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(A, 5),
+ new ResourceAmount(B, 5),
+ new ResourceAmount(C, 15),
+ new ResourceAmount(D, 5)
+ );
+ assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(D, 5)
+ );
+ verify(listener, never()).onTransferSuccess(anyInt());
+ }
+
+ @Test
+ void shouldNotifyListenerWhenReadyInsertingBecauseStorageWasAlreadyEmpty() {
+ // Arrange
+ final Storage source = new InMemoryStorageImpl();
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 15L);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldNotifyListenerWhenReadyInsertingAllResources(
+ @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage
+ ) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+
+ final Storage source = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 15L);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(source.getAll()).isEmpty();
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldNotifyListenerWhenReadyInsertingAllResourcesAndUsingFilterButInsertedNothing() {
+ // Arrange
+ final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(0, source1);
+
+ final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source2.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(1, source2);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 15L);
+ sut.setFilterMode(FilterMode.ALLOW);
+ sut.setFilters(Set.of(A));
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(B, 5),
+ new ResourceAmount(C, 5)
+ );
+ assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(D, 5)
+ );
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldNotifyListenerWhenReadyInsertingAllResourcesAndUsingFilterButStillInsertedSomething(
+ @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage
+ ) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+
+ final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source1.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(0, source1);
+
+ final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source2.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(1, source2);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 15L);
+ sut.setFilterMode(FilterMode.ALLOW);
+ sut.setFilters(Set.of(A));
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(B, 5),
+ new ResourceAmount(C, 5)
+ );
+ assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(D, 5)
+ );
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldNotNotifyListenerWhenReadyInsertingAllResourcesAndNetworkIsFull(
+ @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage
+ ) {
+ // Arrange
+ networkStorage.addSource(new LimitedStorageImpl(15));
+
+ final Storage source1 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source1.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source1.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(0, source1);
+
+ final Storage source2 = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source2.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(1, source2);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 100L);
+
+ // Act
+ sut.doWork();
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(A, 5),
+ new ResourceAmount(B, 5),
+ new ResourceAmount(C, 5)
+ );
+ assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(D, 5)
+ );
+ assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(A, 5)
+ );
+ verify(listener, never()).onTransferSuccess(anyInt());
+ }
+
+ @Test
+ void shouldExtract(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+ networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+
+ final Storage source1 = new LimitedStorageImpl(0);
+ provider.set(0, source1);
+
+ final Storage source2 = new InMemoryStorageImpl();
+ provider.set(1, source2);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 20L);
+ sut.setMode(StorageTransferMode.EXTRACT);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(C, 25),
+ new ResourceAmount(D, 5)
+ );
+ assertThat(source2.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(A, 5),
+ new ResourceAmount(B, 5),
+ new ResourceAmount(C, 10)
+ );
+ assertThat(source1.getAll()).isEmpty();
+ verify(listener, never()).onTransferSuccess(anyInt());
+ }
+
+ @Test
+ void shouldExtractAllowlist(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+ networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+
+ final Storage source1 = new InMemoryStorageImpl();
+ provider.set(0, source1);
+
+ final Storage source2 = new InMemoryStorageImpl();
+ provider.set(1, source2);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 20L);
+ sut.setMode(StorageTransferMode.EXTRACT);
+ sut.setFilterMode(FilterMode.ALLOW);
+ sut.setFilters(Set.of(A, B));
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(C, 35),
+ new ResourceAmount(D, 5)
+ );
+ assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(A, 5),
+ new ResourceAmount(B, 5)
+ );
+ assertThat(source2.getAll()).isEmpty();
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldExtractBlocklist(@InjectNetworkStorageComponent final StorageNetworkComponent networkStorage) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+ networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(C, 35, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+
+ final Storage source1 = new InMemoryStorageImpl();
+ provider.set(0, source1);
+
+ final Storage source2 = new InMemoryStorageImpl();
+ provider.set(1, source2);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 20L);
+ sut.setMode(StorageTransferMode.EXTRACT);
+ sut.setFilterMode(FilterMode.BLOCK);
+ sut.setFilters(Set.of(A, B));
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(A, 5),
+ new ResourceAmount(B, 5),
+ new ResourceAmount(C, 15),
+ new ResourceAmount(D, 5)
+ );
+ assertThat(source1.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(C, 20)
+ );
+ assertThat(source2.getAll()).isEmpty();
+ verify(listener, never()).onTransferSuccess(anyInt());
+ }
+
+ @Test
+ void shouldNotifyListenerWhenReadyExtractingBecauseStorageWasAlreadyEmpty() {
+ // Arrange
+ final Storage source = new InMemoryStorageImpl();
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 15L);
+ sut.setMode(StorageTransferMode.EXTRACT);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldNotifyListenerWhenReadyExtractingAllResources(
+ @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage
+ ) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+ networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+
+ final Storage source = new InMemoryStorageImpl();
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 15L);
+ sut.setMode(StorageTransferMode.EXTRACT);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).isEmpty();
+ assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(A, 5),
+ new ResourceAmount(B, 5),
+ new ResourceAmount(C, 5)
+ );
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldNotifyListenerWhenReadyExtractingAllResourcesAndUsingFilterButInsertedNothing(
+ @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage
+ ) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+ networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+
+ final Storage source = new InMemoryStorageImpl();
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 15L);
+ sut.setFilterMode(FilterMode.ALLOW);
+ sut.setFilters(Set.of(A));
+ sut.setMode(StorageTransferMode.EXTRACT);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(source.getAll()).isEmpty();
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(B, 5),
+ new ResourceAmount(C, 5),
+ new ResourceAmount(D, 5)
+ );
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldNotifyListenerWhenReadyExtractingAllResourcesAndUsingFilterButStillExtractedSomething(
+ @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage
+ ) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>())));
+ networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+
+ final Storage source = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 15L);
+ sut.setFilterMode(FilterMode.ALLOW);
+ sut.setFilters(Set.of(A));
+ sut.setMode(StorageTransferMode.EXTRACT);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(A, 5)
+ );
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(B, 5),
+ new ResourceAmount(C, 5)
+ );
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldNotifyListenerWhenExtractingAllResourcesAndReachingCapacity(
+ @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage
+ ) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+ networkStorage.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ networkStorage.insert(C, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+
+ final Storage source = new LimitedStorageImpl(10);
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 100L);
+ sut.setMode(StorageTransferMode.EXTRACT);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(A, 5),
+ new ResourceAmount(B, 5)
+ );
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(C, 5)
+ );
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldRespectNormalizer(
+ @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage
+ ) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+
+ final Storage source = new InMemoryStorageImpl(new ResourceListImpl(new LinkedHashMap<>()));
+ source.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source.insert(A_ALTERNATIVE, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source.insert(B, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ source.insert(D, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(0, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 20L);
+ sut.setFilterMode(FilterMode.ALLOW);
+ sut.setFilters(Set.of(A));
+ sut.setNormalizer(resource -> resource == A || resource == A_ALTERNATIVE ? A : resource);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(A, 5),
+ new ResourceAmount(A_ALTERNATIVE, 5)
+ );
+ assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrder(
+ new ResourceAmount(B, 5),
+ new ResourceAmount(D, 5)
+ );
+ verify(listener, times(1)).onTransferSuccess(0);
+ }
+
+ @Test
+ void shouldNotTransferAtIndexHigherThanHalf(
+ @InjectNetworkStorageComponent final StorageNetworkComponent networkStorage
+ ) {
+ // Arrange
+ networkStorage.addSource(new InMemoryStorageImpl());
+
+ final Storage source = new InMemoryStorageImpl();
+ source.insert(A, 5, Action.EXECUTE, EmptyActor.INSTANCE);
+ provider.set(sut.getSize() / 2, source);
+
+ sut.setProvider(provider);
+ sut.setTransferQuotaProvider(storage -> 20L);
+
+ // Act
+ sut.doWork();
+
+ // Assert
+ assertThat(networkStorage.getAll()).isEmpty();
+ assertThat(source.getAll()).usingRecursiveFieldByFieldElementComparator().containsExactly(
+ new ResourceAmount(A, 5)
+ );
+ verify(listener, never()).onTransferSuccess(1);
+ }
+}
diff --git a/refinedstorage2-resource-api/src/main/java/com/refinedmods/refinedstorage2/api/resource/list/ResourceListImpl.java b/refinedstorage2-resource-api/src/main/java/com/refinedmods/refinedstorage2/api/resource/list/ResourceListImpl.java
index d9a2c42b1..3ba666d75 100644
--- a/refinedstorage2-resource-api/src/main/java/com/refinedmods/refinedstorage2/api/resource/list/ResourceListImpl.java
+++ b/refinedstorage2-resource-api/src/main/java/com/refinedmods/refinedstorage2/api/resource/list/ResourceListImpl.java
@@ -11,11 +11,19 @@
import org.apiguardian.api.API;
/**
- * An implementation of a {@link ResourceList} that stores the resource entries in a {@link HashMap}.
+ * An implementation of a {@link ResourceList} that stores the resource entries in memory.
*/
@API(status = API.Status.STABLE, since = "2.0.0-milestone.1.2")
public class ResourceListImpl implements ResourceList {
- private final Map entries = new HashMap<>();
+ private final Map entries;
+
+ public ResourceListImpl(final Map entries) {
+ this.entries = entries;
+ }
+
+ public ResourceListImpl() {
+ this(new HashMap<>());
+ }
@Override
public OperationResult add(final ResourceKey resource, final long amount) {
diff --git a/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/InMemoryStorageImpl.java b/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/InMemoryStorageImpl.java
index d03e7aa85..dbdf0cb0b 100644
--- a/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/InMemoryStorageImpl.java
+++ b/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/InMemoryStorageImpl.java
@@ -15,9 +15,17 @@
*/
@API(status = API.Status.STABLE, since = "2.0.0-milestone.1.0")
public class InMemoryStorageImpl implements Storage {
- private final ResourceList list = new ResourceListImpl();
+ private final ResourceList list;
private long stored;
+ public InMemoryStorageImpl(final ResourceList list) {
+ this.list = list;
+ }
+
+ public InMemoryStorageImpl() {
+ this(new ResourceListImpl());
+ }
+
@Override
public long extract(final ResourceKey resource, final long amount, final Action action, final Actor actor) {
ResourceAmount.validate(resource, amount);
diff --git a/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/StateTrackedStorage.java b/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/StateTrackedStorage.java
index 6e2da1903..70da6d0ce 100644
--- a/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/StateTrackedStorage.java
+++ b/refinedstorage2-storage-api/src/main/java/com/refinedmods/refinedstorage2/api/storage/StateTrackedStorage.java
@@ -17,6 +17,7 @@ public class StateTrackedStorage implements TrackedStorage, LimitedStorage {
private final Storage delegate;
@Nullable
private final Listener listener;
+
private StorageState state;
public StateTrackedStorage(final Storage delegate, @Nullable final Listener listener) {