All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.io7m.jmulticlose.tests.CloseableCollectionTest Maven / Gradle / Ivy
/*
* Copyright © 2018 Mark Raynsford https://www.io7m.com
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.io7m.jmulticlose.tests;
import com.io7m.jmulticlose.core.CloseableCollection;
import com.io7m.jmulticlose.core.CloseableCollectionType;
import com.io7m.jmulticlose.core.ClosingResourceFailedException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Tests for {@link CloseableCollection}.
*/
public final class CloseableCollectionTest
{
private static final Logger LOG = LoggerFactory.getLogger(
CloseableCollectionTest.class);
/**
* An empty collection raises no exceptions.
*
* @throws ClosingResourceFailedException On errors
*/
@Test
public void testEmpty0()
throws ClosingResourceFailedException
{
final CloseableCollectionType collection =
CloseableCollection.create();
collection.close();
}
/**
* An empty collection raises no exceptions.
*
* @throws IOException On errors
*/
@Test
public void testEmpty1()
throws IOException
{
final CloseableCollectionType collection =
CloseableCollection.create(IOException::new);
assertEquals(0, collection.size());
collection.close();
}
/**
* Adding a resource to a closed collection is not permitted.
*
* @throws IOException On errors
*/
@Test
public void testClosed0()
throws IOException
{
final CloseableCollectionType collection =
CloseableCollection.create(IOException::new);
collection.close();
Assertions.assertThrows(IllegalStateException.class, () -> {
collection.add(new Resource(0));
});
}
/**
* Resources are closed.
*
* @throws Exception On errors
*/
@Test
public void testSimple()
throws Exception
{
final Resource r0;
final Resource r1;
final Resource r2;
try (CloseableCollectionType c = CloseableCollection.create()) {
r0 = c.add(new Resource(0));
assertEquals(1, c.size());
r1 = c.add(new Resource(1));
assertEquals(2, c.size());
r2 = c.add(new Resource(2));
assertEquals(3, c.size());
}
Assertions.assertTrue(r0.closed, "r0 closed");
Assertions.assertTrue(r1.closed, "r1 closed");
Assertions.assertTrue(r2.closed, "r2 closed");
}
/**
* Crashing resources don't prevent closing.
*/
@Test
public void testFailure()
{
final Resources resources = new Resources();
Assertions.assertThrows(ClosingResourceFailedException.class, () -> {
try (CloseableCollectionType c = CloseableCollection.create()) {
resources.r0 = c.add(new Resource(0));
assertEquals(1, c.size());
resources.r1 = c.add(new ResourceCrasher(1));
assertEquals(2, c.size());
resources.r2 = c.add(new Resource(2));
assertEquals(3, c.size());
resources.r3 = c.add(new ResourceCrasher(3));
assertEquals(4, c.size());
}
});
Assertions.assertTrue(resources.r0.closed, "r0 closed");
Assertions.assertTrue(resources.r1.closed, "r1 closed");
Assertions.assertTrue(resources.r2.closed, "r2 closed");
Assertions.assertTrue(resources.r3.closed, "r3 closed");
}
/**
* Multithreaded closing works.
*/
@Test
@RepeatedTest(value = 100, failureThreshold = 1)
public void testThreadSafety()
throws InterruptedException
{
final var resources = new ArrayList();
for (int index = 0; index < 1000; ++index) {
resources.add(new Resource(index));
}
final var collection = CloseableCollection.create();
for (final var resource : resources) {
collection.add(resource);
}
final var executor = Executors.newFixedThreadPool(20);
try {
for (int index = 0; index < 20; ++index) {
executor.execute(() -> {
try {
collection.close();
} catch (final Throwable e) {
throw new RuntimeException(e);
}
});
}
} finally {
executor.shutdown();
executor.awaitTermination(5L, TimeUnit.SECONDS);
}
Assertions.assertAll(
resources.stream()
.map(x -> (Executable) () -> {
Assertions.assertTrue(x.closed);
assertEquals(1, x.attempts.get());
})
.collect(Collectors.toList())
);
}
private static final class Resources
{
Resource r0;
ResourceCrasher r1;
Resource r2;
ResourceCrasher r3;
Resources()
{
}
}
private final class Resource implements Closeable
{
private final int x;
private volatile boolean closed;
private final AtomicInteger attempts;
Resource(final int in_x)
{
this.x = in_x;
this.attempts = new AtomicInteger();
}
@Override
public void close()
{
LOG.debug("Resource close " + this.x);
this.closed = true;
this.attempts.incrementAndGet();
}
}
private final class ResourceCrasher implements Closeable
{
private final int x;
private boolean closed;
ResourceCrasher(final int in_x)
{
this.x = in_x;
}
@Override
public void close()
throws IOException
{
LOG.debug("ResourceCrasher close " + this.x);
this.closed = true;
throw new IOException("Failed " + this.x);
}
}
}