/*
* 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.system;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarProxy;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
/**
* @author Thomas Segismont
*/
@Test(singleThreaded = true)
public class SigarAccessHandlerTest {
private static final int THREAD_POOL_SIZE = 100;
private Method getSwapMethod;
private Method getFileSystemMapMethod;
private Sigar sigarMock;
private ExecutorService executorService;
private SigarAccessHandler sigarAccessHandler;
@BeforeClass
private void setFields() throws Exception {
getSwapMethod = SigarProxy.class.getMethod("getSwap", new Class[0]);
getFileSystemMapMethod = SigarProxy.class.getMethod("getFileSystemMap", new Class[0]);
sigarMock = mock(Sigar.class);
// Wait 15 seconds on call to getSwap
when(sigarMock.getSwap()).then(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Thread.sleep(1000 * 15);
return null;
}
});
}
@BeforeMethod
public void setUp() throws Exception {
sigarAccessHandler = new SigarAccessHandler(new SigarAccessHandler.SigarFactory() {
@Override
public Sigar createSigarInstance() {
return sigarMock;
}
});
executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}
@AfterMethod
public void tearDown() throws Exception {
sigarAccessHandler.close();
executorService.shutdownNow();
}
@Test(timeOut = 1000 * 30)
public void testOnDemandSigarInstanceCreation() throws Throwable {
// Start concurrent invocations of the long running getSwap method
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
try {
sigarAccessHandler.invoke(null, getSwapMethod, new Object[0]);
} catch (Throwable ignore) {
}
}
});
}
Thread.sleep(1000 * 8);
assertEquals(sigarAccessHandler.localSigarInstancesCount(), 9);
}
@Test(timeOut = 1000 * 30)
public void testSigarInstanceDestruction() throws Throwable {
// Start concurrent invocations of the short running getFileSystemMapMethod method and an invocation of the long
// running getSwap method
for (int i = 0; i < 10; i++) {
if (i == 0) {
executorService.submit(new Runnable() {
@Override
public void run() {
try {
sigarAccessHandler.invoke(null, getSwapMethod, new Object[0]);
} catch (Throwable ignore) {
}
}
});
} else {
executorService.submit(new Runnable() {
@Override
public void run() {
try {
sigarAccessHandler.invoke(null, getFileSystemMapMethod, new Object[0]);
} catch (Throwable ignore) {
}
}
});
}
}
Thread.sleep(1000 * 8);
assertEquals(sigarAccessHandler.localSigarInstancesCount(), 0);
}
@Test(timeOut = 1000 * 30)
public void testMaxSigarInstanceCreation() throws Throwable {
// Start concurrent invocations of the long running getSwap method
List<Future<Throwable>> futures = new LinkedList<Future<Throwable>>();
for (int i = 0; i < 60; i++) {
futures.add(executorService.submit(new Callable<Throwable>() {
@Override
public Throwable call() throws Exception {
try {
sigarAccessHandler.invoke(null, getSwapMethod, new Object[0]);
} catch (Throwable throwable) {
return throwable;
}
return null;
}
}));
}
Thread.sleep(1000 * 8);
assertEquals(sigarAccessHandler.localSigarInstancesCount(), 50);
int failedCallsCount = 0;
for (Future<Throwable> future : futures) {
Throwable throwable = future.get();
if (throwable != null) {
failedCallsCount++;
assertTrue(throwable instanceof RuntimeException);
assertEquals(throwable.getMessage(), "Too many Sigar instances created");
}
}
assertEquals(failedCallsCount, 9);
}
}