/*
* -----------------------------------------------------------------------\
* PerfCake
*
* Copyright (C) 2010 - 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* -----------------------------------------------------------------------/
*/
package org.perfcake.reporting.reporter;
import org.perfcake.RunInfo;
import org.perfcake.TestSetup;
import org.perfcake.agent.AgentThread;
import org.perfcake.common.Period;
import org.perfcake.common.PeriodType;
import org.perfcake.reporting.Measurement;
import org.perfcake.reporting.MeasurementUnit;
import org.perfcake.reporting.ReportManager;
import org.perfcake.reporting.ReportingException;
import org.perfcake.reporting.destination.DummyDestination;
import org.perfcake.util.ObjectFactory;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.List;
import java.util.Properties;
/**
* Tests features of {@link org.perfcake.reporting.reporter.MemoryUsageReporter}.
*
* @author <a href="mailto:pavel.macik@gmail.com">Pavel Macík</a>
*/
@Test(groups = { "unit" })
public class MemoryUsageReporterTest {
private static final long ITERATION_COUNT = 1000L;
private static final String AGENT_HOSTNAME = "localhost";
private static final String AGENT_PORT = "19266";
private static final File TEST_OUTPUT_DIR = new File(TestSetup.createTempDir("perfcake-memory-usage"));
@BeforeClass
public void startPerfCakeAgent() {
final Thread agentThread = new Thread(new AgentThread("hostname=" + AGENT_HOSTNAME + ",port=" + AGENT_PORT));
agentThread.start();
if (TEST_OUTPUT_DIR.exists()) {
if (TEST_OUTPUT_DIR.isFile()) {
TEST_OUTPUT_DIR.delete();
}
} else {
TEST_OUTPUT_DIR.mkdir();
}
}
@AfterClass
public void tearDown() throws IOException {
final File[] files = TEST_OUTPUT_DIR.listFiles();
if (files != null) {
for (final File f : files) {
f.delete();
}
}
Files.delete(TEST_OUTPUT_DIR.toPath());
}
@Test
public void testMemoryUsageReporterWithMemoryLeakDetection() throws InstantiationException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, InterruptedException {
final Properties reporterProperties = new Properties();
reporterProperties.put("agentHostname", AGENT_HOSTNAME);
reporterProperties.put("agentPort", AGENT_PORT);
reporterProperties.put("memoryLeakSlopeThreshold", "1"); // 1 byte per second (that should cause positive memory leak detection)
reporterProperties.put("usedMemoryTimeWindowSize", "3");
reporterProperties.put("memoryLeakDetectionEnabled", "true");
final List<Measurement> measurementList = testMemoryUsageReporter(reporterProperties);
final int mls = measurementList.size();
Assert.assertEquals(mls, 11, "Number of Measurement sent to destination");
for (final Measurement m : measurementList) {
Assert.assertNotNull(m.get("Used"), "Used memory result");
Assert.assertNotNull(m.get("Total"), "Total memory result");
Assert.assertNotNull(m.get("Max"), "Max memory result");
}
final Measurement firstM = measurementList.get(0);
final Measurement lastM = measurementList.get(mls - 1);
Assert.assertNull(firstM.get("UsedTrend"), "Used memory trend (first measurement)");
Assert.assertNull(firstM.get("MemoryLeak"), "Command leak detection (first measurement)");
Assert.assertNotNull(lastM.get("UsedTrend"), "Used memory trend (last measurement)");
Assert.assertNotNull(lastM.get("MemoryLeak"), "Command leak detection (last measurement)");
}
@Test
public void testMemoryUsageReporterWithoutMemoryLeakDetection() throws InstantiationException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, InterruptedException {
final Properties reporterProperties = new Properties();
reporterProperties.put("agentHostname", AGENT_HOSTNAME);
reporterProperties.put("agentPort", AGENT_PORT);
final List<Measurement> measurementList = testMemoryUsageReporter(reporterProperties);
final int mls = measurementList.size();
Assert.assertEquals(mls, 11, "Number of Measurement sent to destination");
for (final Measurement m : measurementList) {
Assert.assertNotNull(m.get("Used"), "Used memory result");
Assert.assertNotNull(m.get("Total"), "Total memory result");
Assert.assertNotNull(m.get("Max"), "Max memory result");
}
final Measurement firstM = measurementList.get(0);
final Measurement lastM = measurementList.get(mls - 1);
Assert.assertNull(firstM.get("UsedTrend"), "No used memory trend (first measurement)");
Assert.assertNull(firstM.get("MemoryLeak"), "No memory leak detection (first measurement)");
Assert.assertNull(lastM.get("UsedTrend"), "No used memory trend (last measurement)");
Assert.assertNull(lastM.get("MemoryLeak"), "No memory leak detection (last measurement)");
}
@Test
public void testMemoryUsageReporterWithoutMemoryLeakDetectionWithGC() throws InstantiationException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, InterruptedException {
final Properties reporterProperties = new Properties();
reporterProperties.put("agentHostname", AGENT_HOSTNAME);
reporterProperties.put("agentPort", AGENT_PORT);
reporterProperties.put("performGcOnMemoryUsage", "true");
final List<Measurement> measurementList = testMemoryUsageReporter(reporterProperties);
final int mls = measurementList.size();
Assert.assertEquals(mls, 11, "Number of Measurement sent to destination");
for (final Measurement m : measurementList) {
Assert.assertNotNull(m.get("Used"), "Used memory result");
Assert.assertNotNull(m.get("Total"), "Total memory result");
Assert.assertNotNull(m.get("Max"), "Max memory result");
}
final Measurement firstM = measurementList.get(0);
final Measurement lastM = measurementList.get(mls - 1);
Assert.assertNull(firstM.get("UsedTrend"), "No used memory trend (first measurement)");
Assert.assertNull(firstM.get("MemoryLeak"), "No memory leak detection (first measurement)");
Assert.assertNull(lastM.get("UsedTrend"), "No used memory trend (last measurement)");
Assert.assertNull(lastM.get("MemoryLeak"), "No memory leak detection (last measurement)");
}
@Test
public void testMemoryUsageReporterWithMemoryLeakDetectionWithHeapDump() throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, InterruptedException {
final Properties reporterProperties = new Properties();
reporterProperties.put("agentHostname", AGENT_HOSTNAME);
reporterProperties.put("agentPort", AGENT_PORT);
reporterProperties.put("memoryLeakSlopeThreshold", "1"); // 1 byte per second (that should cause positive memory leak detection)
reporterProperties.put("usedMemoryTimeWindowSize", "3");
reporterProperties.put("memoryLeakDetectionEnabled", "true");
reporterProperties.put("memoryDumpOnLeak", "true");
final File dumpFile = new File(TEST_OUTPUT_DIR, "heapdumpB-" + System.currentTimeMillis() + ".bin");
reporterProperties.put("memoryDumpFile", dumpFile.getAbsoluteFile());
final List<Measurement> measurementList = testMemoryUsageReporter(reporterProperties);
final int mls = measurementList.size();
Assert.assertEquals(mls, 11, "Number of Measurement sent to destination");
for (final Measurement m : measurementList) {
Assert.assertNotNull(m.get("Used"), "Used memory result");
Assert.assertNotNull(m.get("Total"), "Total memory result");
Assert.assertNotNull(m.get("Max"), "Max memory result");
}
final Measurement firstM = measurementList.get(0);
final Measurement lastM = measurementList.get(mls - 1);
Assert.assertNull(firstM.get("UsedTrend"), "Used memory trend (first measurement)");
Assert.assertNull(firstM.get("MemoryLeak"), "Command leak detection (first measurement)");
Assert.assertNotNull(lastM.get("UsedTrend"), "Used memory trend (last measurement)");
Assert.assertNotNull(lastM.get("MemoryLeak"), "Command leak detection (last measurement)");
Assert.assertTrue(dumpFile.exists(), "Dump file " + dumpFile.getAbsolutePath() + " should exist.");
}
@Test
public void testMemoryUsageReporterWithMemoryLeakDetectionWithHeapDumpExistingFile() throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, InterruptedException {
final Properties reporterProperties = new Properties();
reporterProperties.put("agentHostname", AGENT_HOSTNAME);
reporterProperties.put("agentPort", AGENT_PORT);
reporterProperties.put("memoryLeakSlopeThreshold", "1"); // 1 byte per second (that should cause positive memory leak detection)
reporterProperties.put("usedMemoryTimeWindowSize", "3");
reporterProperties.put("memoryLeakDetectionEnabled", "true");
reporterProperties.put("memoryDumpOnLeak", "true");
final File dumpFile = new File(TEST_OUTPUT_DIR, "heapdumpA-" + System.currentTimeMillis() + ".bin");
Assert.assertTrue(dumpFile.createNewFile());
reporterProperties.put("memoryDumpFile", dumpFile.getAbsoluteFile());
final List<Measurement> measurementList = testMemoryUsageReporter(reporterProperties);
Assert.assertTrue(dumpFile.exists(), "Dump file " + dumpFile.getAbsolutePath() + " should exist.");
final File dumpFile0 = new File(dumpFile.getAbsolutePath() + ".0");
Assert.assertTrue(dumpFile0.exists(), "Dump file " + dumpFile0.getAbsolutePath() + " should exist.");
}
private List<Measurement> testMemoryUsageReporter(final Properties reporterProperties) throws InstantiationException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, InterruptedException {
final MemoryUsageReporter mur = (MemoryUsageReporter) ObjectFactory.summonInstance(MemoryUsageReporter.class.getName(), reporterProperties);
Assert.assertNotNull(mur, "Reporter's instance");
Assert.assertEquals(mur.getAgentHostname(), AGENT_HOSTNAME, "Agent hostname");
Assert.assertEquals(mur.getAgentPort(), AGENT_PORT, "Agent port");
final Properties destinationProperties = new Properties();
final DummyDestination dest = (DummyDestination) ObjectFactory.summonInstance(DummyDestination.class.getName(), destinationProperties);
mur.registerDestination(dest, new Period(PeriodType.ITERATION, 100));
final RunInfo ri = new RunInfo(new Period(PeriodType.ITERATION, ITERATION_COUNT));
final ReportManager rm = new ReportManager();
rm.registerReporter(mur);
rm.setRunInfo(ri);
rm.start();
MeasurementUnit mu = null;
dest.resetObservedMeasurements();
dest.setObserving(true);
try {
for (int i = 0; i < ITERATION_COUNT; i++) {
mu = rm.newMeasurementUnit();
mu.startMeasure();
Thread.sleep(2);
mu.stopMeasure();
rm.report(mu);
}
} catch (InterruptedException | ReportingException e) {
e.printStackTrace();
Assert.fail("Exception should not be thrown.", e);
}
Thread.sleep(500); // make sure all reported values made it in the queue
rm.stop();
List<Measurement> measurementList = dest.getObservedMeasurements();
int tries = 0;
while (measurementList.size() < ITERATION_COUNT && tries++ < 10) {
Thread.sleep(10);
dest.getObservedMeasurements().forEach(item -> {
if (!measurementList.contains(item)) {
measurementList.add(item);
}
});
}
dest.setObserving(false);
return measurementList;
}
}