package org.rhq.core.pc.measurement;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
import java.util.HashMap;
import java.util.Set;
import org.testng.Assert;
import org.testng.annotations.Test;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.TargetsContainer;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.pc.PluginContainer;
import org.rhq.core.pc.inventory.ForceAvailabilityExecutor;
import org.rhq.core.pc.inventory.ResourceContainer;
import org.rhq.plugins.test.SingleResourceDiscoveryComponent;
import org.rhq.plugins.test.measurement.BZ834019ResourceComponent;
import org.rhq.plugins.test.measurement.BZ834019ResourceComponent.CollectedMetric;
import org.rhq.test.arquillian.AfterDiscovery;
import org.rhq.test.arquillian.BeforeDiscovery;
import org.rhq.test.arquillian.FakeServerInventory;
import org.rhq.test.arquillian.MockingServerServices;
import org.rhq.test.arquillian.ResourceComponentInstances;
import org.rhq.test.arquillian.ResourceContainers;
import org.rhq.test.arquillian.RunDiscovery;
import org.rhq.test.shrinkwrap.RhqAgentPluginArchive;
/**
* Test for BZ 834019. This test requires a long time to complete (about 2.5 minutes) because
* we have to wait for the minimum collection intervals (30s) to pass several times.
*/
@RunDiscovery
public class LateMeasurementRescheduleTest extends Arquillian {
@Deployment(name = "TwoMetricPlugin")
@TargetsContainer("connected-pc-with-metric-collection")
public static RhqAgentPluginArchive getTestPlugin() {
RhqAgentPluginArchive pluginJar = ShrinkWrap.create(RhqAgentPluginArchive.class, "bz834019-plugin-1.0.jar");
HashMap<String, String> replacements = new HashMap<String, String>();
replacements.put("@@@discovery@@@", SingleResourceDiscoveryComponent.class.getName());
replacements.put("@@@class@@@", BZ834019ResourceComponent.class.getName());
replacements.put("@@@metric1.interval@@@", "30000");
replacements.put("@@@metric2.interval@@@", "35000"); // this MUST be 35s - if you make it 30s, the collections won't be late!
return pluginJar.setPluginDescriptorFromTemplate("two-metric-rhq-plugin.xml", replacements).addClasses(
SingleResourceDiscoveryComponent.class, BZ834019ResourceComponent.class);
}
@ArquillianResource
private PluginContainer pluginContainer;
@ArquillianResource
public MockingServerServices serverServices;
private FakeServerInventory fakeServerInventory;
private FakeServerInventory.CompleteDiscoveryChecker discoveryCompleteChecker;
@ResourceContainers(plugin = "TwoMetricPlugin", resourceType = "TwoMetricServer")
private Set<ResourceContainer> containers;
@ResourceComponentInstances(plugin = "TwoMetricPlugin", resourceType = "TwoMetricServer")
private Set<BZ834019ResourceComponent> components;
@BeforeDiscovery(testMethods = "testBZ834019")
public void resetServerServices() throws Exception {
serverServices.resetMocks();
fakeServerInventory = new FakeServerInventory();
discoveryCompleteChecker = fakeServerInventory.createAsyncDiscoveryCompletionChecker(1);
// autoimport everything
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then(
fakeServerInventory.getResourceSyncInfo());
// set up the metric schedules using the metric metadata to determine default intervals and enablement
when(serverServices.getDiscoveryServerService().postProcessNewlyCommittedResources(any(Set.class))).then(
fakeServerInventory.postProcessNewlyCommittedResources());
}
@AfterDiscovery
public void waitForAsyncDiscoveries() throws Exception {
if (discoveryCompleteChecker != null) {
discoveryCompleteChecker.waitForDiscoveryComplete(10000);
// Since the avail job is not running, make sure our discovered resources get their initial UP avail
new ForceAvailabilityExecutor(pluginContainer.getInventoryManager()).call();
}
}
@Test(groups = "pc.itest.bz834019", priority = 20)
public void testBZ834019() throws Exception {
Assert.assertNotNull(pluginContainer);
Assert.assertTrue(pluginContainer.isStarted());
// make sure we have the resource container
Assert.assertEquals(containers.size(), 1, "missing container");
// make sure we have the resource component
Assert.assertEquals(components.size(), 1, "missing component");
assert containers.iterator().next().getResource().getInventoryStatus() == InventoryStatus.COMMITTED;
BZ834019ResourceComponent server = this.components.iterator().next();
// do things to test BZ 834019. Here's what is happening:
// - server has metric1 and metric2 with intervals of 30s and 35s respectively
// (MUST make sure metric2 is no less than 35s, else the collections won't be late!)
// - the resource component will sleep in its getValues for 91s - no metrics will be reported the first 91s
// - the PC will call getValues() in this sequence:
// ** metric1 - starting at time 0 (due to the sleep in our getValues, this won't complete until time 91)
// ** metric2 - starting at time 0 (completes at time 91)
// ** metric1 - starting at time 30 (completes at time 91)
// ** metric2 - starting at time 60 (completes at time 91)
// ** metric2 - starting at time 90 (completes at time 91)
// ** metric2 - starting at time 105 (completes at time 105)
// ** metric1 - starting at time [121..150] (completes at time 90 + 30 + [1..30])
//
// Metric 1 is late because it was supposed to start at t60 but instead came up for eval at t=90. It is then
// rescheduled by our fix for (currentTime=t90 + delay=30s + randomInterval=[1..30] based on the 30s Interval).
// And you can see above, that is when the next request to collect metric1 is done.
// Without the fix, the next collection of metric1 would have been at around time 60 + (interval) == 60 + (30) == 90
// rather than just blindly sleeping, make this test complete as fast as possible by
// stop waiting as soon as we see metric1 having been collected at least 3 times.
CollectedMetric[] collectedMetrics = null;
boolean keepWaiting = true;
int waitCycles = 0;
while (keepWaiting) {
Thread.sleep(1000);
int numberOfTimesMetric1WasCollectedSoFar = 0;
collectedMetrics = server.collectedMetrics.toArray(new CollectedMetric[0]); // copy it fast, this is synchronized so our component's thread won't break us
for (CollectedMetric collectedMetric : collectedMetrics) {
if (collectedMetric.metricName.equals("metric1")) {
numberOfTimesMetric1WasCollectedSoFar++;
}
}
if (numberOfTimesMetric1WasCollectedSoFar >= 3) {
keepWaiting = false;
}
if (waitCycles++ > 180) {
keepWaiting = false; // stop waiting but the test will probably fail because we should have had three metric1 collections by now
}
}
// round all the time values to the nearest second
for (CollectedMetric metric : collectedMetrics) {
metric.collectedTime = getRoundedSeconds(metric.collectedTime);
metric.finishedTime = getRoundedSeconds(metric.finishedTime);
System.out.println("BZ 834019 test metric=" + metric.metricName + "; start=" + metric.collectedTime
+ "; stop=" + metric.finishedTime);
}
// now look at the timings and make sure we see metric1 pushed out into the future like our bug fix wants it to be
int collectionNumber = 0;
for (CollectedMetric metric : collectedMetrics) {
if ("metric1".equals(metric.metricName)) {
collectionNumber++; // this tracks that we are on the Nth time the metric was collected, we expect 3 total
if (collectionNumber == 1) {
assert metric.collectedTime <= 3; // should be 0, but give it some leeway in case our test box is slow
} else if (collectionNumber == 2) {
assert metric.collectedTime >= 29 && metric.collectedTime <= 32; // should be 30, but give it some leeway
} else if (collectionNumber == 3) {
assert metric.collectedTime >= 120 && metric.collectedTime <= 151; // should between 90 + 30 + [1..30] = [121..150], but give it some leeway
}
}
}
assert collectionNumber >= 3 : "test should have collected metric1 at least 3 times: " + collectionNumber;
}
private long getRoundedSeconds(long millis) {
return (long) ((millis / 1000.0) + 0.5);
}
}