/* * RHQ Management Platform * Copyright (C) 2005-2008 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, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * 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 and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser 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 java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests the Java-only non-native layer. * * @author John Mazzitelli */ @Test(groups = "native.system") public class JavaTest { @AfterMethod @BeforeMethod public void terminateNativeLibrary() { try { SystemInfoFactory.shutdown(); } catch (Throwable ignore) { } } /** * Will return <code>true</code> if this is on Windows platform - this test needs Windows because it will want to * execute Windows executables (albeit using Java API). * * @param testName the name of the test that is asking to be performed * * @return <code>true</code> if the platform is the correct one that our tests need */ private boolean isCorrectTestPlatform(String testName) { if (System.getProperty("os.name").indexOf("Windows") == -1) { System.out.println("~~~ [" + testName + "] Test machine is not the correct platform for this test " + "- cannot test the Java non-native API"); return false; } System.out.println("Running non-native java test: " + testName); return true; } /** * Tests getting network adapter information. */ // since the move to SIGAR, Java-only sysinfo doesn't support network adapters /* public void testGetNetworkAdapterInfo() { SystemInfo sysinfo = SystemInfoFactory.createJavaSystemInfo(); List<NetworkAdapterInfo> adapters = sysinfo.getAllNetworkAdapters(); assert adapters != null; assert adapters.size() > 0 : "You can only run this on a machine with at least one detectable network interface"; for (NetworkAdapterInfo adapter : adapters) { assert adapter.getName() != null : adapter; assert adapter.getDisplayName() != null : adapter; assert adapter.getDescription() != null : adapter; assert adapter.getUnicastAddresses() != null : adapter; assert adapter.getMacAddressString() == null : "How did Java find the Mac address? " + adapter; assert adapter.getType().equals("unknown") : "How did Java find the type? " + adapter; assert adapter.getOperationalStatus() == NetworkAdapterInfo.OperationState.UNKNOWN : "How did Java find the status? " + adapter; assert adapter.isDhcpEnabled() == null : "How did Java find the DHCP enabled flag? " + adapter; assert adapter.getDnsServers() == null : "How did Java find the DNS servers? " + adapter; assert adapter.getMulticastAddresses() == null : "How did Java find the multicast addresses? " + adapter; for (InetAddress addr : adapter.getUnicastAddresses()) { assert addr.getAddress() != null; assert addr.getHostAddress() != null; } System.out.println("Network adapter found: " + adapter); } } */ /** * Just a test to run concurrent requests through the java layer. * * @throws Exception */ public void testConcurrency() throws Exception { final SystemInfo sysinfo = SystemInfoFactory.createJavaSystemInfo(); // final int hostname = sysinfo.getHostname().hashCode(); final int osname = sysinfo.getOperatingSystemName().hashCode(); final int osver = sysinfo.getOperatingSystemVersion().hashCode(); final List<Throwable> errors = new Vector<Throwable>(); final AtomicInteger count = new AtomicInteger(0); class TestConcurrencyTask implements Callable<Object> { public Object call() { try { count.incrementAndGet(); // assert sysinfo.getHostname().hashCode() == hostname; assert sysinfo.getOperatingSystemName().hashCode() == osname; assert sysinfo.getOperatingSystemVersion().hashCode() == osver; } catch (Throwable t) { errors.add(t); } return null; } } // run numTasks total tasks, with numConcurrency being the number we run concurrently at any one time int numTasks = 100; int numConcurrency = 50; Collection<Callable<Object>> tasks = new ArrayList<Callable<Object>>(numTasks); for (int i = 0; i < numTasks; i++) { tasks.add(new TestConcurrencyTask()); } ExecutorService pool = Executors.newFixedThreadPool(numConcurrency); System.gc(); long beforeFree = Runtime.getRuntime().freeMemory(); long beforeTotal = Runtime.getRuntime().totalMemory(); System.gc(); List<Future<Object>> futures = pool.invokeAll(tasks, 120L, TimeUnit.SECONDS); for (Future<Object> future : futures) { future.get(30L, TimeUnit.SECONDS); assert future.isDone() : "A future failed to complete"; } System.gc(); long afterFree = Runtime.getRuntime().freeMemory(); long afterTotal = Runtime.getRuntime().totalMemory(); pool.shutdown(); assert pool.awaitTermination(120, TimeUnit.SECONDS) : "Wow - needed to wait longer than 120 seconds to finish"; assert count.get() == numTasks : "For some reason, we didn't run [" + numTasks + "] tasks: " + count.get(); assert errors.size() == 0 : "Got some errors: " + errors; long beforeUsed = beforeTotal - beforeFree; long afterUsed = afterTotal - afterFree; System.out.println("1 native concurrency mem usage: before(free/total/used):after(free/total/used)\n" + beforeFree + '/' + beforeTotal + '/' + beforeUsed + ':' + afterFree + '/' + afterTotal + '/' + afterUsed); if (!((afterUsed - beforeUsed) < 500000)) { System.out.println("Hmm.. why did the concurrency test leave 500KB memory still used?"); } long firstTestUsed = afterUsed; // run it a second time count.set(0); pool = Executors.newFixedThreadPool(numConcurrency); System.gc(); beforeFree = Runtime.getRuntime().freeMemory(); beforeTotal = Runtime.getRuntime().totalMemory(); System.gc(); futures = pool.invokeAll(tasks, 120L, TimeUnit.SECONDS); for (Future<Object> future : futures) { future.get(30L, TimeUnit.SECONDS); assert future.isDone() : "A future failed to complete"; } System.gc(); afterFree = Runtime.getRuntime().freeMemory(); afterTotal = Runtime.getRuntime().totalMemory(); pool.shutdown(); assert pool.awaitTermination(120, TimeUnit.SECONDS) : "Wow - needed to wait longer than 120 seconds to finish"; assert count.get() == numTasks : "For some reason, we didn't run [" + numTasks + "] tasks: " + count.get(); assert errors.size() == 0 : "Got some errors: " + errors; beforeUsed = beforeTotal - beforeFree; afterUsed = afterTotal - afterFree; System.out.println("2 native concurrency mem usage: before(free/total/used):after(free/total/used)\n" + beforeFree + '/' + beforeTotal + '/' + beforeUsed + ':' + afterFree + '/' + afterTotal + '/' + afterUsed); if (!((afterUsed - beforeUsed) < 2500000)) { System.out.println("Hmm.. why did the concurrency test leave 250KB memory still used?"); } long secondTestUsed = afterUsed; if (!(firstTestUsed >= (secondTestUsed - 50000))) { System.out.println("Hmm.. why did the second concurrency test leak more than 50K mem?"); } } /** * Test waiting for a process and killing it if we timeout. */ public void testExecuteWithWaitKill() { if (!isCorrectTestPlatform("testExecuteWithWaitKill")) { return; } SystemInfo platform = SystemInfoFactory.createJavaSystemInfo(); ProcessExecution pe = new ProcessExecution("C:\\WINDOWS\\system32\\sol.exe"); pe.setKillOnTimeout(true); pe.setWaitForCompletion(3000L); long before = System.currentTimeMillis(); ProcessExecutionResults results = platform.executeProcess(pe); long after = System.currentTimeMillis(); assert (after - before) >= 3000L : "Did not wait for 3000ms: Waited=" + (after - before); assert results.getError() == null : "Failed to exec process: " + results; } /** * Test executing a process. */ public void testExecute() { if (!isCorrectTestPlatform("testExecute")) { return; } SystemInfo platform = SystemInfoFactory.createJavaSystemInfo(); ProcessExecution pe = new ProcessExecution("C:\\WINDOWS\\system32\\cmd.exe"); pe.setWorkingDirectory("C:\\"); pe.setCaptureOutput(true); pe.setArguments(new String[] { "/C", "dir" }); ProcessExecutionResults results = platform.executeProcess(pe); assert results.getError() == null : "Failed to exec process: " + results; assert results.getExitCode().intValue() == 0 : "Failed to get a 0 exit code: " + results; assert results.getCapturedOutput().length() > 0 : "Missing output"; Map envVars = new HashMap(); envVars.put("WINTESTVAR", "hello there"); pe.setEnvironmentVariables(envVars); pe.setArguments(new String[] { "/C", "echo", "%WINTESTVAR%" }); results = platform.executeProcess(pe); assert results.getError() == null : "Failed to exec process: " + results; assert results.getExitCode().intValue() == 0 : "Failed to get a 0 exit code: " + results; assert results.getCapturedOutput().length() > 0 : "Missing output"; assert results.getCapturedOutput().indexOf("hello there") > -1 : "Not the expected output: " + results.getCapturedOutput(); } public void testJBNATIVE16() { if (!isCorrectTestPlatform("testJBNATIVE16")) { return; } SystemInfo platform = SystemInfoFactory.createJavaSystemInfo(); assert platform != null; boolean isNative = platform.isNative(); String hostname = platform.getHostname(); String operatingSystemName = platform.getOperatingSystemName(); assert !isNative : "Should not have been native"; assert platform instanceof JavaSystemInfo : "Should have be given a JavaSystemInfo object"; assert hostname != null; assert operatingSystemName != null; System.out.println("is-native=" + isNative); System.out.println("hostname=" + hostname); System.out.println("os name=" + operatingSystemName); platform = SystemInfoFactory.createJavaSystemInfo(); ProcessExecution pe = new ProcessExecution("C:\\WINDOWS\\system32\\cmd.exe"); pe.setWorkingDirectory("C:\\"); pe.setCaptureOutput(true); pe.setArguments(new String[] { "/C", "dir" }); ProcessExecutionResults results = platform.executeProcess(pe); assert results.getError() == null : "Failed to exec process: " + results; assert results.getExitCode().intValue() == 0 : "Failed to get a 0 exit code: " + results; assert results.getCapturedOutput().length() > 0 : "Missing output"; Map envVars = new HashMap(); envVars.put("WINTESTVAR", "hello there"); pe.setEnvironmentVariables(envVars); pe.setArguments(new String[] { "/C", "echo", "%WINTESTVAR%" }); results = platform.executeProcess(pe); assert results.getError() == null : "Failed to exec process: " + results; assert results.getExitCode().intValue() == 0 : "Failed to get a 0 exit code: " + results; assert results.getCapturedOutput().length() > 0 : "Missing output"; assert results.getCapturedOutput().indexOf("hello there") > -1 : "Not the expected output: " + results.getCapturedOutput(); } /** * Just a test to run concurrent requests through the java layer that executes processes. * * @throws Exception */ public void testConcurrencyExec() throws Exception { final SystemInfo sysinfo = SystemInfoFactory.createJavaSystemInfo(); final List<Throwable> errors = new Vector<Throwable>(); final int[] count = new int[] { 0 }; final ProcessExecution start = new ProcessExecution("dummy"); setupProgram(start); class TestExecConcurrencyTask implements Callable<Object> { public Object call() { try { synchronized (count) { count[0]++; } ProcessExecutionResults results = sysinfo.executeProcess(start); if (results.getError() != null) { throw results.getError(); } if (start.isCaptureOutput()) { assert results.getCapturedOutput() != null : "no captured output"; for (int i = 0; i < 10; i++) { if (results.getCapturedOutput().length() > 0) { break; } Thread.sleep(500L); // give it a chance to slurp the output } assert results.getCapturedOutput().length() > 0 : "captured output is empty"; } } catch (Throwable t) { errors.add(t); } return null; } } // run numTasks total tasks, with numConcurrency being the number we run concurrently at any one time int numTasks = 25; int numConcurrency = 15; Collection<Callable<Object>> tasks = new ArrayList<Callable<Object>>(numTasks); for (int i = 0; i < numTasks; i++) { tasks.add(new TestExecConcurrencyTask()); } ExecutorService pool = Executors.newFixedThreadPool(numConcurrency); System.gc(); long beforeFree = Runtime.getRuntime().freeMemory(); long beforeTotal = Runtime.getRuntime().totalMemory(); System.gc(); pool.invokeAll(tasks, 30L, TimeUnit.SECONDS); System.gc(); long afterFree = Runtime.getRuntime().freeMemory(); long afterTotal = Runtime.getRuntime().totalMemory(); pool.shutdown(); assert pool.awaitTermination(120, TimeUnit.SECONDS) : "Wow - needed to wait longer than 120 seconds to finish"; assert count[0] == numTasks : "For some reason, we didn't run [" + numTasks + "] tasks: " + count[0]; assert errors.size() == 0 : "Got some errors: " + errors; long beforeUsed = beforeTotal - beforeFree; long afterUsed = afterTotal - afterFree; System.out.println("1 native concurrency mem usage: before(free/total/used):after(free/total/used)\n" + beforeFree + '/' + beforeTotal + '/' + beforeUsed + ':' + afterFree + '/' + afterTotal + '/' + afterUsed); if (!((afterUsed - beforeUsed) < 500000)) { System.out.println("Hmm.. why did the concurrency test leave 500KB memory still used?"); } long firstTestUsed = afterUsed; // run it a second time start.setCaptureOutput(true); // see if this causes any problems start.setWaitForCompletion(1000L); count[0] = 0; pool = Executors.newFixedThreadPool(numConcurrency); System.gc(); beforeFree = Runtime.getRuntime().freeMemory(); beforeTotal = Runtime.getRuntime().totalMemory(); System.gc(); pool.invokeAll(tasks, 30L, TimeUnit.SECONDS); System.gc(); afterFree = Runtime.getRuntime().freeMemory(); afterTotal = Runtime.getRuntime().totalMemory(); pool.shutdown(); assert pool.awaitTermination(120, TimeUnit.SECONDS) : "Wow - needed to wait longer than 120 seconds to finish"; assert count[0] == numTasks : "For some reason, we didn't run [" + numTasks + "] tasks: " + count[0]; assert errors.size() == 0 : "Got some errors: " + errors; beforeUsed = beforeTotal - beforeFree; afterUsed = afterTotal - afterFree; System.out.println("2 native concurrency mem usage: before(free/total/used):after(free/total/used)\n" + beforeFree + '/' + beforeTotal + '/' + beforeUsed + ':' + afterFree + '/' + afterTotal + '/' + afterUsed); if (!((afterUsed - beforeUsed) < 2500000)) { System.out.println("Hmm.. why did the concurrency test leave 250KB memory still used?"); } long secondTestUsed = afterUsed; if (!(firstTestUsed >= (secondTestUsed - 50000))) { System.out.println("Hmm.. why did the second concurrency test leak more than 50K mem?"); } } /** * Picks a program that can be executed on the test platform. This is not fool proof, might need to tweek this in * case, for example, some platforms do not have "/bin/ls". * * @param start */ private void setupProgram(ProcessExecution start) { // just pick some short-lived executable if (File.separatorChar == '\\') { start.setExecutable("C:\\WINDOWS\\system32\\find.exe"); start.setArguments(new String[] { "/C", "\"yaddayadda\"", "C:\\WINDOWS\\WIN.INI" }); } else { start.setExecutable("/bin/ls"); start.setArguments(new String[] { "/bin" }); } start.setCaptureOutput(false); start.setWaitForCompletion(-1); return; } }