package org.jtwig.parser.cache;
import org.jtwig.environment.Environment;
import org.jtwig.model.tree.Node;
import org.jtwig.parser.JtwigParser;
import org.jtwig.resource.reference.ResourceReference;
import org.junit.Test;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import static junit.framework.TestCase.assertSame;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.mock;
public class InMemoryConcurrentPersistentTemplateCacheTest {
private InMemoryConcurrentPersistentTemplateCache underTest = new InMemoryConcurrentPersistentTemplateCache();
@Test
public void multipleRequestsRetrievingBeforeCompleteParse() throws Exception {
int parties = 100;
final Node node = mock(Node.class);
final Environment environment = mock(Environment.class);
Semaphore semaphore = new Semaphore(0);
ExecutorService executorService = new ThreadPoolExecutor(parties, parties, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
final CountDownLatch countDownLatch = new CountDownLatch(parties);
AtomicInteger counter = new AtomicInteger(0);
final JtwigParser jtwigParser = new ThreadedJtwigParser(counter, semaphore, node);
final ResourceReference resource = mock(ResourceReference.class);
for (int i = 0; i < parties; i++) {
executorService.submit(new NamedRunnable(String.valueOf(i), new Runnable() {
@Override
public void run() {
countDownLatch.countDown();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Node result = underTest.get(jtwigParser, environment, resource);
assertSame(result, node);
}
}));
}
countDownLatch.await();
assertThat(counter.get(), equalTo(0));
Thread.sleep(2000);
semaphore.release(parties);
executorService.shutdown();
Node result = underTest.get(jtwigParser, environment, resource);
assertSame(result, node);
assertThat(counter.get(), equalTo(1));
}
private static class NamedRunnable implements Runnable {
private final String name;
private final Runnable runnable;
private NamedRunnable(String name, Runnable runnable) {
this.name = name;
this.runnable = runnable;
}
@Override
public void run() {
this.runnable.run();
}
}
private static class ThreadedJtwigParser implements JtwigParser {
private final AtomicInteger counter;
private final Semaphore semaphore;
private final Node node;
private ThreadedJtwigParser(AtomicInteger counter, Semaphore semaphore, Node node) {
this.counter = counter;
this.semaphore = semaphore;
this.node = node;
}
@Override
public Node parse(Environment environment, ResourceReference resource) {
try {
semaphore.acquire();
counter.incrementAndGet();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return node;
}
}
}