/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.core.pc.inventory;
import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
import static org.rhq.core.domain.measurement.AvailabilityType.UNKNOWN;
import static org.rhq.core.domain.measurement.AvailabilityType.UP;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.fail;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.pluginapi.availability.AvailabilityFacet;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
/**
* @author Elias Ross
*/
@Test
public class AvailabilityProxyTest implements AvailabilityFacet {
private final Log LOG = LogFactory.getLog(AvailabilityProxyTest.class);
private volatile int timeout = 1;
private AvailabilityType returnedAvail = UP;
private final ExecutorService executor = Executors.newCachedThreadPool();
@BeforeMethod
@AfterMethod
public void clearInterrupts() {
// make sure we don't leave interrupt flag set, if somehow the test set it
Thread.interrupted();
}
/**
* Run a test. Note this may not be 100% reliable, as it depends on thread execution to
* happen according to our sleep schedule...
*/
public void test() throws InterruptedException {
// mock out the resource container
ResourceComponent<?> resourceComponent = Mockito.mock(ResourceComponent.class);
Mockito.when(resourceComponent.getAvailability()).thenAnswer(new Answer<AvailabilityType>() {
public AvailabilityType answer(InvocationOnMock invocation) throws Throwable {
return AvailabilityProxyTest.this.getAvailability();
}
});
ResourceContainer resourceContainer = Mockito.mock(ResourceContainer.class);
Mockito.when(resourceContainer.getResourceClassLoader()).thenReturn(getClass().getClassLoader());
Mockito.when(resourceContainer.getResourceComponent()).thenReturn(resourceComponent);
// our one proxy we want to call concurrently
final TestAvailabilityProxy ap = new TestAvailabilityProxy(resourceContainer);
// make sure our mock object uses our own thread pool when submitting the task
Mockito.when(resourceContainer.submitAvailabilityCheck(ap)).thenAnswer(new Answer<Future<AvailabilityType>>() {
public Future<AvailabilityType> answer(InvocationOnMock invocation) throws Throwable {
return executor.submit(ap);
}
});
LOG.debug("proxy " + ap);
assertEquals("should be up", UP, ap.getAvailability()); // waits 1ms and returns synchronously
timeout = 1200;
assertEquals("should be down", UNKNOWN, ap.getAvailability()); // waits 1s and times out
Thread.sleep(300); // now waited total of 1s + .3s = 1.3 sec > 1.2s
assertEquals("should be up now", UP, ap.getAvailability()); // waits 1s and returns last reported value (UP)
ap.setAsyncTimeout(1020L);
Thread.sleep(50); // waited 1.050 seconds
try {
ap.getAvailability(); // this submits another which we need to let finish
fail("should timeout 1020, waited 1050");
} catch (TimeoutException e) {
}
// wait for the last submit to return
Thread.sleep(1210);
LOG.debug("proxy " + ap);
// try disabling sync checks
// - start returning DOWN avail in order to perform a sync disable and then re-enable
// - go back to default async timeout, we don't want it to trigger anymore
// short timeout but longer than the sync timeout to force several sync timeouts
returnedAvail = DOWN;
ap.setAsyncTimeout(null);
timeout = 75;
ap.setSyncTimeout(50L);
while (!ap.isSyncDisabled()) {
ap.getAvailability();
Thread.sleep(50L);
}
// go back to returning UP so we can re-enable sync checking
// make the sync check a half second so we can prove that sync checking is not happening
returnedAvail = UP;
timeout = 500;
ap.setSyncTimeout(500L);
long start = System.currentTimeMillis();
assertEquals("should be DOWN", DOWN, ap.getAvailability());
assert System.currentTimeMillis() - start < 100 : "Should have been fast, returning old avail";
// wait for the last submit to return
Thread.sleep(510);
// check for re-enable sync checks
assertEquals("should be UP", UP, ap.getAvailability());
assertEquals("should be enabled", false, ap.isSyncDisabled());
// wait for the last submit to return
Thread.sleep(510);
// test interrupt handling
LOG.debug("interrupt this thread");
Thread.currentThread().interrupt();
assertEquals("cancellation", AvailabilityType.UNKNOWN, ap.getAvailability());
assertEquals(true, Thread.currentThread().isInterrupted());
}
@Override
public synchronized AvailabilityType getAvailability() {
try {
LOG.debug("sleep " + timeout);
Thread.sleep(timeout);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
LOG.debug("return " + returnedAvail.getName());
return returnedAvail;
}
private class TestAvailabilityProxy extends AvailabilityProxy {
private Long asyncTimeout = null;
private Long syncTimeout = null;
public TestAvailabilityProxy(ResourceContainer rc) {
super(rc);
}
@Override
public AvailabilityType getAvailability() {
// System.out.println("DevDebug 0 [" + System.currentTimeMillis() + "] getAvail() timeout=[" + timeout + "], syncTimeout=[" + syncTimeout + "], asyncTimeout=[" + asyncTimeout + "]");
return super.getAvailability();
}
public void setAsyncTimeout(Long asyncTimeout) {
this.asyncTimeout = asyncTimeout;
}
public void setSyncTimeout(Long syncTimeout) {
this.syncTimeout = syncTimeout;
}
@Override
protected long getSyncTimeout() {
return null == syncTimeout ? super.getSyncTimeout() : syncTimeout;
}
@Override
protected long getAsyncTimeout() {
return null == asyncTimeout ? super.getAsyncTimeout() : asyncTimeout;
}
}
}