package com.github.mustachejava;
import com.github.mustachejavabenchmarks.BenchmarkTest;
import org.junit.Test;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import static com.github.mustachejavabenchmarks.BenchmarkTest.skip;
import static junit.framework.Assert.assertEquals;
/**
* Inspired by an unconfirmed bug report.
*/
public class ConcurrencyTest {
static Random r = new SecureRandom();
private static class TestObject {
final int a;
final int b;
final int c;
int aa() throws InterruptedException {
Thread.sleep(r.nextInt(10));
return a;
}
int bb() throws InterruptedException {
Thread.sleep(r.nextInt(10));
return b;
}
int cc() throws InterruptedException {
Thread.sleep(r.nextInt(10));
return c;
}
Callable<Integer> calla() throws InterruptedException {
return new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(r.nextInt(10));
return a;
}
};
}
Callable<Integer> callb() throws InterruptedException {
return new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(r.nextInt(10));
return b;
}
};
}
Callable<Integer> callc() throws InterruptedException {
return new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(r.nextInt(10));
return c;
}
};
}
private TestObject(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
}
// Alternate render
static String render(TestObject to) {
return to.a + ":" + to.b + ":" + to.c;
}
@Test
public void testConcurrentExecution() throws InterruptedException {
if (skip()) return;
String template = "{{aa}}:{{bb}}:{{cc}}";
final Mustache test = new DefaultMustacheFactory().compile(new StringReader(template), "test");
ExecutorService es = Executors.newCachedThreadPool();
final AtomicInteger total = new AtomicInteger();
final Semaphore semaphore = new Semaphore(100);
for (int i = 0; i < 100000; i++) {
semaphore.acquire();
es.submit(new Runnable() {
@Override
public void run() {
try {
TestObject testObject = new TestObject(r.nextInt(), r.nextInt(), r.nextInt());
StringWriter sw = new StringWriter();
test.execute(sw, testObject).close();
if (!render(testObject).equals(sw.toString())) {
total.incrementAndGet();
}
} catch (IOException e) {
// Can't fail
e.printStackTrace();
System.exit(1);
} finally {
semaphore.release();
}
}
});
}
// Wait for them all to complete
semaphore.acquire(100);
assertEquals(0, total.intValue());
}
@Test
public void testConcurrentExecutionWithConcurrentTemplate() throws InterruptedException {
if (skip()) return;
String template = "{{calla}}:{{callb}}:{{callc}}";
ExecutorService es = Executors.newCachedThreadPool();
DefaultMustacheFactory dmf = new DefaultMustacheFactory();
dmf.setExecutorService(es);
final Mustache test = dmf.compile(new StringReader(template), "test");
final AtomicInteger total = new AtomicInteger();
final Semaphore semaphore = new Semaphore(100);
for (int i = 0; i < 100000; i++) {
semaphore.acquire();
es.submit(new Runnable() {
@Override
public void run() {
try {
TestObject testObject = new TestObject(r.nextInt(), r.nextInt(), r.nextInt());
StringWriter sw = new StringWriter();
test.execute(sw, testObject).close();
if (!render(testObject).equals(sw.toString())) {
total.incrementAndGet();
}
} catch (IOException e) {
// Can't fail
e.printStackTrace();
System.exit(1);
} finally {
semaphore.release();
}
}
});
}
// Wait for them all to complete
semaphore.acquire(100);
assertEquals(0, total.intValue());
}
}