/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.core.security.impl;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.mockito.Mockito;
import org.testng.annotations.Test;
import com.google.common.collect.Maps;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.security.Security;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.core.security.impl.test.MockSecuritySource;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.ObjectId;
import com.opengamma.id.UniqueId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.test.Timeout;
/**
* Tests the {@link CoalescingSecuritySource} class.
*/
@Test(groups = TestGroup.INTEGRATION)
public class CoalescingSecuritySourceTest {
private static void join(final CyclicBarrier barrier) {
try {
barrier.await(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw new OpenGammaRuntimeException("interrupted", e);
}
}
private static void sleep() {
try {
Thread.sleep(Timeout.standardTimeoutMillis() / 4);
} catch (InterruptedException e) {
throw new OpenGammaRuntimeException("interrupted", e);
}
}
public void testGetSecurity_byUniqueId_a() throws Exception {
final UniqueId uidA = UniqueId.of("Test", "A");
final Security secA = Mockito.mock(Security.class);
final UniqueId uidB = UniqueId.of("Test", "B");
final Security secB = Mockito.mock(Security.class);
final UniqueId uidC = UniqueId.of("Test", "C");
final Security secC = Mockito.mock(Security.class);
final CyclicBarrier barrier = new CyclicBarrier(4);
final MockSecuritySource underlying = new MockSecuritySource() {
int _state;
@Override
public Security get(final UniqueId uid) {
assertEquals(_state++, 0);
join(barrier); //1
assertEquals(uid, uidA);
// Pause for a bit to make sure that the other threads get blocked in their getSecurity methods
sleep();
return secA;
}
@Override
public Map<UniqueId, Security> get(final Collection<UniqueId> uids) {
assertEquals(_state++, 1);
assertEquals(uids.size(), 2);
assertTrue(uids.contains(uidB));
assertTrue(uids.contains(uidC));
final Map<UniqueId, Security> result = Maps.newHashMapWithExpectedSize(2);
result.put(uidB, secB);
result.put(uidC, secC);
return result;
}
};
final CoalescingSecuritySource coalescing = new CoalescingSecuritySource(underlying);
// Start three threads. One will do the first write, the other two will be blocked. Then one of the other two will do a second
// write that includes that required by the third. The third will do no I/O itself.
final ExecutorService exec = Executors.newCachedThreadPool();
try {
final Future<?> a = exec.submit(new Runnable() {
@Override
public void run() {
final Security s = coalescing.get(uidA);
assertSame(s, secA);
}
});
final Future<?> b = exec.submit(new Runnable() {
@Override
public void run() {
join(barrier);
final Security s = coalescing.get(uidB);
assertSame(s, secB);
}
});
final Future<?> c = exec.submit(new Runnable() {
@Override
public void run() {
join(barrier);
final Security s = coalescing.get(uidC);
assertSame(s, secC);
}
});
join(barrier);
a.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
b.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
c.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
} finally {
exec.shutdownNow();
}
}
public void testGetSecurity_byUniqueId_b() throws Exception {
final UniqueId uidA = UniqueId.of("Test", "A");
final Security secA = Mockito.mock(Security.class);
final UniqueId uidB = UniqueId.of("Test", "B");
final Security secB = Mockito.mock(Security.class);
final UniqueId uidC = UniqueId.of("Test", "C");
final Security secC = Mockito.mock(Security.class);
final CyclicBarrier barrier1 = new CyclicBarrier(3);
final CyclicBarrier barrier2 = new CyclicBarrier(2);
final MockSecuritySource underlying = new MockSecuritySource() {
int _state;
@Override
public Security get(final UniqueId uid) {
assertEquals(_state++, 0);
join(barrier1);
assertEquals(uid, uidA);
// Pause for a bit to make sure that the other thread gets blocked in their getSecurity methods
sleep();
return secA;
}
@Override
public Map<UniqueId, Security> get(final Collection<UniqueId> uids) {
assertEquals(_state++, 1);
assertEquals(uids.size(), 2);
assertTrue(uids.contains(uidB));
assertTrue(uids.contains(uidC));
final Map<UniqueId, Security> result = Maps.newHashMapWithExpectedSize(2);
result.put(uidB, secB);
result.put(uidC, secC);
return result;
}
};
final CoalescingSecuritySource coalescing = new CoalescingSecuritySource(underlying) {
@Override
protected void releaseOtherWritingThreads() {
join(barrier2); // 1 + 2 // release the third thread
}
};
// Start two threads. One will do the first write, the other will be blocked. Suppressing releaseOtherThreads means a third
// call will try to write its own value plus those from the other threads. The second thread will do no I/O itself.
final ExecutorService exec = Executors.newCachedThreadPool();
try {
final Future<?> a = exec.submit(new Runnable() {
@Override
public void run() {
final Security s = coalescing.get(uidA);
assertSame(s, secA);
}
});
final Future<?> b = exec.submit(new Runnable() {
@Override
public void run() {
join(barrier1);
final Security s = coalescing.get(uidB);
assertSame(s, secB);
}
});
final Future<?> c = exec.submit(new Runnable() {
@Override
public void run() {
join(barrier2); // 1
final Security s = coalescing.get(uidC);
assertSame(s, secC);
}
});
join(barrier1);
a.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
b.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
join(barrier2); // 2
c.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
} finally {
exec.shutdownNow();
}
}
public void testGetSecurities_byUniqueId_a() throws Exception {
final UniqueId uidA = UniqueId.of("Test", "A");
final Security secA = Mockito.mock(Security.class);
final UniqueId uidB = UniqueId.of("Test", "B");
final Security secB = Mockito.mock(Security.class);
final UniqueId uidC = UniqueId.of("Test", "C");
final Security secC = Mockito.mock(Security.class);
final CyclicBarrier barrier = new CyclicBarrier(4);
final MockSecuritySource underlying = new MockSecuritySource() {
int _state;
@Override
public Map<UniqueId, Security> get(final Collection<UniqueId> uids) {
final Map<UniqueId, Security> result = Maps.newHashMapWithExpectedSize(uids.size());
if (++_state == 1) {
assertEquals(uids.size(), 2);
join(barrier);
assertTrue(uids.contains(uidA));
assertTrue(uids.contains(uidB));
result.put(uidA, secA);
result.put(uidB, secB);
// Pause for a bit to make sure that the other threads get blocked in their getSecurity methods
sleep();
} else if (_state == 2) {
assertEquals(uids.size(), 3);
assertTrue(uids.contains(uidA));
assertTrue(uids.contains(uidB));
assertTrue(uids.contains(uidC));
result.put(uidA, secA);
result.put(uidB, secB);
result.put(uidC, secC);
} else {
fail();
}
return result;
}
};
final CoalescingSecuritySource coalescing = new CoalescingSecuritySource(underlying);
// Start three threads. One will do the first write, the other two will be blocked. Then one of the other two will do a second
// write that includes that required by the third. The third will do no I/O itself.
final ExecutorService exec = Executors.newCachedThreadPool();
try {
final Future<?> a = exec.submit(new Runnable() {
@Override
public void run() {
final Map<UniqueId, Security> result = coalescing.get(Arrays.asList(uidA, uidB));
assertEquals(result.size(), 2);
assertSame(result.get(uidA), secA);
assertSame(result.get(uidB), secB);
}
});
final Future<?> b = exec.submit(new Runnable() {
@Override
public void run() {
join(barrier);
final Map<UniqueId, Security> result = coalescing.get(Arrays.asList(uidA, uidC));
assertEquals(result.size(), 2);
assertSame(result.get(uidA), secA);
assertSame(result.get(uidC), secC);
}
});
final Future<?> c = exec.submit(new Runnable() {
@Override
public void run() {
join(barrier);
final Map<UniqueId, Security> result = coalescing.get(Arrays.asList(uidB, uidC));
assertEquals(result.size(), 2);
assertSame(result.get(uidB), secB);
assertSame(result.get(uidC), secC);
}
});
join(barrier);
a.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
b.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
c.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
} finally {
exec.shutdownNow();
}
}
public void testGetSecurities_byUniqueId_b() throws Exception {
final UniqueId uidA = UniqueId.of("Test", "A");
final Security secA = Mockito.mock(Security.class);
final UniqueId uidB = UniqueId.of("Test", "B");
final Security secB = Mockito.mock(Security.class);
final UniqueId uidC = UniqueId.of("Test", "C");
final Security secC = Mockito.mock(Security.class);
final CyclicBarrier barrier1 = new CyclicBarrier(3);
final CyclicBarrier barrier2 = new CyclicBarrier(2);
final MockSecuritySource underlying = new MockSecuritySource() {
int _state;
@Override
public Map<UniqueId, Security> get(final Collection<UniqueId> uids) {
final Map<UniqueId, Security> result = Maps.newHashMapWithExpectedSize(uids.size());
if (++_state == 1) {
assertEquals(uids.size(), 2);
join(barrier1);
assertTrue(uids.contains(uidA));
assertTrue(uids.contains(uidB));
result.put(uidA, secA);
result.put(uidB, secB);
// Pause for a bit to make sure that the other threads get blocked in their getSecurity methods
sleep();
} else if (_state == 2) {
assertEquals(uids.size(), 3);
assertTrue(uids.contains(uidA));
assertTrue(uids.contains(uidB));
assertTrue(uids.contains(uidC));
result.put(uidA, secA);
result.put(uidB, secB);
result.put(uidC, secC);
} else {
fail();
}
return result;
}
};
final CoalescingSecuritySource coalescing = new CoalescingSecuritySource(underlying) {
@Override
protected void releaseOtherWritingThreads() {
join(barrier2); // 1 + 2 // release the third thread
}
};
// Start two threads. One will do the first write, the other will be blocked. Suppressing releaseOtherThreads means a third
// call will try to write its own value plus those from the other threads. The second thread will do no I/O itself.
final ExecutorService exec = Executors.newCachedThreadPool();
try {
final Future<?> a = exec.submit(new Runnable() {
@Override
public void run() {
final Map<UniqueId, Security> result = coalescing.get(Arrays.asList(uidA, uidB));
assertEquals(result.size(), 2);
assertSame(result.get(uidA), secA);
assertSame(result.get(uidB), secB);
}
});
final Future<?> b = exec.submit(new Runnable() {
@Override
public void run() {
join(barrier1);
final Map<UniqueId, Security> result = coalescing.get(Arrays.asList(uidA, uidC));
assertEquals(result.size(), 2);
assertSame(result.get(uidA), secA);
assertSame(result.get(uidC), secC);
}
});
final Future<?> c = exec.submit(new Runnable() {
@Override
public void run() {
join(barrier2); //1
final Map<UniqueId, Security> result = coalescing.get(Arrays.asList(uidB, uidC));
assertEquals(result.size(), 2);
assertSame(result.get(uidB), secB);
assertSame(result.get(uidC), secC);
}
});
join(barrier1);
a.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
b.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
join(barrier2); // 1 + 2
c.get(Timeout.standardTimeoutMillis(), TimeUnit.MILLISECONDS);
} finally {
exec.shutdownNow();
}
}
public void testGetSecurity_byObjectId() {
final SecuritySource underlying = Mockito.mock(SecuritySource.class);
final SecuritySource coalescing = new CoalescingSecuritySource(underlying);
coalescing.get(ObjectId.of("Test", "Test"), VersionCorrection.LATEST);
Mockito.verify(underlying).get(ObjectId.of("Test", "Test"), VersionCorrection.LATEST);
}
public void testGetSecurities_byExternalIdBundleVersionCorrection() {
final SecuritySource underlying = Mockito.mock(SecuritySource.class);
final SecuritySource coalescing = new CoalescingSecuritySource(underlying);
coalescing.get(ExternalIdBundle.EMPTY, VersionCorrection.LATEST);
Mockito.verify(underlying).get(ExternalIdBundle.EMPTY, VersionCorrection.LATEST);
}
public void testGetSecurities_byExternalIdBundle() {
final SecuritySource underlying = Mockito.mock(SecuritySource.class);
final SecuritySource coalescing = new CoalescingSecuritySource(underlying);
coalescing.get(ExternalIdBundle.EMPTY);
Mockito.verify(underlying).get(ExternalIdBundle.EMPTY);
}
public void testGetSecurity_byExternalIdBundle() {
final SecuritySource underlying = Mockito.mock(SecuritySource.class);
final SecuritySource coalescing = new CoalescingSecuritySource(underlying);
coalescing.get(ExternalIdBundle.EMPTY);
Mockito.verify(underlying).get(ExternalIdBundle.EMPTY);
}
public void testGetSecurity_byExternalIdBundleVersionCorrection() {
final SecuritySource underlying = Mockito.mock(SecuritySource.class);
final SecuritySource coalescing = new CoalescingSecuritySource(underlying);
coalescing.get(ExternalIdBundle.EMPTY, VersionCorrection.LATEST);
Mockito.verify(underlying).get(ExternalIdBundle.EMPTY, VersionCorrection.LATEST);
}
}