package org.ovirt.engine.core.bll.network.macpool;
import static java.util.Collections.singletonList;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.commons.lang.math.LongRange;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.utils.lock.LockedObjectFactory;
@RunWith(MockitoJUnitRunner.class)
public class DecoratedMacPoolFactoryTest {
@Mock
private MacPool macPool;
@Mock
private MacPoolDecorator macPoolDecoratorA;
@Mock
private MacPoolDecorator macPoolDecoratorB;
@Mock
private LockedObjectFactory lockedObjectFactory;
@Before
public void setUp() {
when(macPool.getId()).thenReturn(Guid.newGuid());
}
@Test
public void testCreateDecoratedPoolWhenNoDecoratorsAreRequested() {
MacPool decoratedPool = decoratePoolWhileNotUsingLocking(Collections.emptyList());
assertThat(decoratedPool, is(macPool));
}
@Test
public void testEqualyDecoratedPoolsUseSameLock() {
multipleDecoratorsUsesSameLock(macPoolDecoratorA, macPoolDecoratorA);
}
@Test
public void testDifferenlyDecoratedPoolsUseSameLock() {
multipleDecoratorsUsesSameLock(macPoolDecoratorA, macPoolDecoratorB);
}
private void multipleDecoratorsUsesSameLock(MacPoolDecorator... decorators) {
DecoratedMacPoolFactory factory = new DecoratedMacPoolFactory(lockedObjectFactory);
Arrays.stream(decorators)
.forEach(decorator -> factory.createDecoratedPool(macPool, singletonList(decorator)));
ArgumentCaptor<ReentrantReadWriteLock> captor = ArgumentCaptor.forClass(ReentrantReadWriteLock.class);
verify(lockedObjectFactory, times(2))
.createLockingInstance(eq(macPool), eq(MacPool.class), captor.capture());
//there's is one distinct item —> all locks are same.
assertThat(captor.getAllValues().stream().distinct().count(), is(1L));
}
@Test
public void verifyDecoratorOrder() {
DecoratedMacPoolFactory factory = createDecoratedMacPoolFactoryWithDisabledLocking();
MacPool decoratedPool = factory.createDecoratedPool(macPool,
Arrays.asList(macPoolDecoratorA, macPoolDecoratorB));
assertThat(decoratedPool, is(macPoolDecoratorB));
ArgumentCaptor<MacPool> firstDecoratorMacPoolArgumentCaptor = ArgumentCaptor.forClass(MacPool.class);
verify(macPoolDecoratorB).setMacPool(firstDecoratorMacPoolArgumentCaptor.capture());
assertThat(firstDecoratorMacPoolArgumentCaptor.getValue(), is(macPoolDecoratorA));
ArgumentCaptor<MacPool> secondDecoratorMacPoolArgumentCaptor = ArgumentCaptor.forClass(MacPool.class);
verify(macPoolDecoratorA).setMacPool(secondDecoratorMacPoolArgumentCaptor.capture());
assertThat(secondDecoratorMacPoolArgumentCaptor.getValue(), is(macPool));
verify(macPool).getId();
Mockito.verifyNoMoreInteractions(macPoolDecoratorA, macPoolDecoratorB, macPool);
}
@Test
public void testTwoDifferentPoolsShouldUsesDifferentLock() {
List<MacPoolDecorator> noDecorators = Collections.emptyList();
MacPool anotherMacPool = mock(MacPool.class);
/*
* here we just want to return some proxied 'locked' MacPool. 'Any' should not be problem, as this argument
* is verified below. Here we just want to return different 'locked' MacPool for different calls, and check if
* they were returned.
*/
MacPool dummyLockedMacPool1 = mock(MacPool.class);
MacPool dummyLockedMacPool2 = mock(MacPool.class);
when(lockedObjectFactory.createLockingInstance(eq(macPool), eq(MacPool.class), any()))
.thenReturn(dummyLockedMacPool1);
when(lockedObjectFactory.createLockingInstance(eq(anotherMacPool), eq(MacPool.class), any()))
.thenReturn(dummyLockedMacPool2);
DecoratedMacPoolFactory factory = new DecoratedMacPoolFactory(lockedObjectFactory);
assertThat(factory.createDecoratedPool(macPool, noDecorators), is(dummyLockedMacPool1));
assertThat(factory.createDecoratedPool(anotherMacPool, noDecorators), is(dummyLockedMacPool2));
ArgumentCaptor<ReentrantReadWriteLock> captor1 = ArgumentCaptor.forClass(ReentrantReadWriteLock.class);
verify(lockedObjectFactory).createLockingInstance(eq(macPool), eq(MacPool.class), captor1.capture());
ArgumentCaptor<ReentrantReadWriteLock> captor2 = ArgumentCaptor.forClass(ReentrantReadWriteLock.class);
verify(lockedObjectFactory).createLockingInstance(eq(anotherMacPool), eq(MacPool.class), captor2.capture());
assertNotEquals(captor1.getValue(), captor2.getValue());
}
private MacPool decoratePoolWhileNotUsingLocking(List<MacPoolDecorator> decorators) {
return decoratePoolWhileNotUsingLocking(this.macPool, decorators);
}
private MacPool decoratePoolWhileNotUsingLocking(MacPool pool, List<MacPoolDecorator> decorators) {
return createDecoratedMacPoolFactoryWithDisabledLocking().createDecoratedPool(pool, decorators);
}
private DecoratedMacPoolFactory createDecoratedMacPoolFactoryWithDisabledLocking() {
doAnswer(invocation -> invocation.getArguments()[0])
.when(lockedObjectFactory).createLockingInstance(any(), eq(MacPool.class), any());
return new DecoratedMacPoolFactory(lockedObjectFactory);
}
@Test
public void testToString() throws Exception {
Guid underlyingPoolId = Guid.newGuid();
MacPoolUsingRanges underlyingPool =
new MacPoolUsingRanges(underlyingPoolId, Collections.singletonList(new LongRange(1, 2)), false);
DelegatingMacPoolDecorator decoratorA = new DelegatingMacPoolDecorator();
DelegatingMacPoolDecorator decoratorB = new DelegatingMacPoolDecorator();
MacPool decoratedPool = decoratePoolWhileNotUsingLocking(underlyingPool, Arrays.asList(decoratorA, decoratorB));
String expectedToStringResult = String.format(
"%1$s:{macPool='%2$s:{macPool='%3$s:{id='%4$s'}'}'}",
decoratorA.getClass().getSimpleName(),
decoratorB.getClass().getSimpleName(),
underlyingPool.getClass().getSimpleName(),
underlyingPoolId
);
assertThat(decoratedPool.toString(), is(expectedToStringResult));
}
}