modules/enterprise/server/server-metrics/pom.xml | 15 modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricBaselineCalculator.java | 95 ---- modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsBaselineCalculator.java | 95 ++++ modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/ArithmeticMeanCalculatorTest.java | 93 ++++ modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsBaselineCalculatorTest.java | 228 ++++++++++ 5 files changed, 431 insertions(+), 95 deletions(-)
New commits: commit 8f96d812511fc82bf6a57300ffed918b457792b3 Author: Stefan Negrea snegrea@redhat.com Date: Wed Dec 26 13:33:02 2012 -0600
Add unit tests for the metrics baseline calculation and arithmetic mean calculator.
Also, min and max of a baseline are now zero if the average calculation succeeds but data is purged from the db before min and max queries.
diff --git a/modules/enterprise/server/server-metrics/pom.xml b/modules/enterprise/server/server-metrics/pom.xml index 0e5b43a..ef9524b 100644 --- a/modules/enterprise/server/server-metrics/pom.xml +++ b/modules/enterprise/server/server-metrics/pom.xml @@ -121,6 +121,21 @@ <artifactId>slf4j-log4j12</artifactId> <version>1.7.1</version> </dependency> + + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-testng</artifactId> + <version>${powermock.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito</artifactId> + <version>${powermock.version}</version> + <scope>test</scope> + </dependency> + </dependencies>
<build> diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricBaselineCalculator.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricBaselineCalculator.java deleted file mode 100644 index c3322e8..0000000 --- a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricBaselineCalculator.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * RHQ Management Platform - * Copyright (C) 2005-2012 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.server.metrics; - -import java.util.ArrayList; -import java.util.List; - -import com.datastax.driver.core.Session; - -import org.rhq.core.domain.measurement.MeasurementBaseline; -import org.rhq.core.domain.util.PageOrdering; - -/** - * @author Stefan Negrea - */ -public class MetricBaselineCalculator { - - private MetricsDAO metricsDAO; - - public MetricBaselineCalculator(Session session) { - this.metricsDAO = new MetricsDAO(session); - } - - public List<MeasurementBaseline> calculateBaselines(List<Integer> scheduleIds, long startTime, long endTime) { - List<MeasurementBaseline> calculatedBaselines = new ArrayList<MeasurementBaseline>(); - - MeasurementBaseline measurementBaseline; - for (Integer scheduleId : scheduleIds) { - measurementBaseline = this.calculateBaseline(scheduleId, startTime, endTime); - if (measurementBaseline != null) { - calculatedBaselines.add(measurementBaseline); - } - } - - return calculatedBaselines; - } - - private MeasurementBaseline calculateBaseline(Integer scheduleId, long startTime, long endTime) { - List<AggregatedNumericMetric> metrics = this.metricsDAO.findAggregateMetrics(MetricsTable.ONE_HOUR, scheduleId, startTime, endTime); - - if (metrics.size() != 0) { - ArithmeticMeanCalculator mean = new ArithmeticMeanCalculator(); - - for (AggregatedNumericMetric entry : metrics) { - mean.add(entry.getAvg()); - } - - double min = Double.MIN_VALUE; - List<Double> results = this.metricsDAO.findAggregateSimpleMetric(MetricsTable.ONE_HOUR, AggregateType.MIN, - scheduleId, startTime, endTime, PageOrdering.ASC, 1); - if(results.size() != 0){ - min = results.get(0); - } - - double max = Double.MAX_VALUE; - results = this.metricsDAO.findAggregateSimpleMetric(MetricsTable.ONE_HOUR, AggregateType.MAX, scheduleId, - startTime, endTime, PageOrdering.DESC, 1); - if (results.size() != 0) { - max = results.get(0); - } - - MeasurementBaseline baseline = new MeasurementBaseline(); - baseline.setMax(max); - baseline.setMin(min); - baseline.setMean(mean.getArithmeticMean()); - baseline.setScheduleId(scheduleId); - - return baseline; - } - - return null; - } -} diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsBaselineCalculator.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsBaselineCalculator.java new file mode 100644 index 0000000..3ce6f5d --- /dev/null +++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsBaselineCalculator.java @@ -0,0 +1,95 @@ +/* + * + * RHQ Management Platform + * Copyright (C) 2005-2012 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.server.metrics; + +import java.util.ArrayList; +import java.util.List; + +import com.datastax.driver.core.Session; + +import org.rhq.core.domain.measurement.MeasurementBaseline; +import org.rhq.core.domain.util.PageOrdering; + +/** + * @author Stefan Negrea + */ +public class MetricsBaselineCalculator { + + private MetricsDAO metricsDAO; + + public MetricsBaselineCalculator(Session session) { + this.metricsDAO = new MetricsDAO(session); + } + + public List<MeasurementBaseline> calculateBaselines(List<Integer> scheduleIds, long startTime, long endTime) { + List<MeasurementBaseline> calculatedBaselines = new ArrayList<MeasurementBaseline>(); + + MeasurementBaseline measurementBaseline; + for (Integer scheduleId : scheduleIds) { + measurementBaseline = this.calculateBaseline(scheduleId, startTime, endTime); + if (measurementBaseline != null) { + calculatedBaselines.add(measurementBaseline); + } + } + + return calculatedBaselines; + } + + private MeasurementBaseline calculateBaseline(Integer scheduleId, long startTime, long endTime) { + List<AggregatedNumericMetric> metrics = this.metricsDAO.findAggregateMetrics(MetricsTable.ONE_HOUR, scheduleId, startTime, endTime); + + if (metrics.size() != 0) { + ArithmeticMeanCalculator mean = new ArithmeticMeanCalculator(); + + for (AggregatedNumericMetric entry : metrics) { + mean.add(entry.getAvg()); + } + + double min = 0; + List<Double> results = this.metricsDAO.findAggregateSimpleMetric(MetricsTable.ONE_HOUR, AggregateType.MIN, + scheduleId, startTime, endTime, PageOrdering.ASC, 1); + if(results.size() != 0){ + min = results.get(0); + } + + double max = 0; + results = this.metricsDAO.findAggregateSimpleMetric(MetricsTable.ONE_HOUR, AggregateType.MAX, scheduleId, + startTime, endTime, PageOrdering.DESC, 1); + if (results.size() != 0) { + max = results.get(0); + } + + MeasurementBaseline baseline = new MeasurementBaseline(); + baseline.setMax(max); + baseline.setMin(min); + baseline.setMean(mean.getArithmeticMean()); + baseline.setScheduleId(scheduleId); + + return baseline; + } + + return null; + } +} diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/ArithmeticMeanCalculatorTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/ArithmeticMeanCalculatorTest.java new file mode 100644 index 0000000..2b9653a --- /dev/null +++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/ArithmeticMeanCalculatorTest.java @@ -0,0 +1,93 @@ +/* + * RHQ Management Platform + * Copyright 2011, Red Hat Middleware LLC, and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package org.rhq.server.metrics; + +import java.util.Random; + +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * @author Stefan Negrea + * + */ +public class ArithmeticMeanCalculatorTest { + + private static final double TEST_PRECISION = Math.pow(10, -10); + + @Test + public void simpleTest() { + ArithmeticMeanCalculator objectUnderTest = new ArithmeticMeanCalculator(); + + objectUnderTest.add(1); + objectUnderTest.add(2); + objectUnderTest.add(3); + objectUnderTest.add(4); + objectUnderTest.add(5); + objectUnderTest.add(6); + + Assert.assertEquals(objectUnderTest.getArithmeticMean(), 3.5); + } + + @Test + public void simpleResetTest() { + ArithmeticMeanCalculator objectUnderTest = new ArithmeticMeanCalculator(); + + objectUnderTest.add(1); + objectUnderTest.add(2); + objectUnderTest.add(3); + objectUnderTest.add(4); + objectUnderTest.add(5); + objectUnderTest.add(6); + objectUnderTest.add(7); + objectUnderTest.add(8); + + Assert.assertEquals(objectUnderTest.getArithmeticMean(), 4.5, TEST_PRECISION); + + objectUnderTest.reset(); + objectUnderTest.add(1); + objectUnderTest.add(2); + objectUnderTest.add(3); + + Assert.assertEquals(objectUnderTest.getArithmeticMean(), 2.0, TEST_PRECISION); + } + + @Test + public void randomNumberWithResetTest() { + ArithmeticMeanCalculator objectUnderTest = new ArithmeticMeanCalculator(); + + Random random = new Random(1243); + + for (int j = 0; j < 5; j++) { + double sum = 0; + objectUnderTest.reset(); + + for (int i = 0; i < 123; i++) { + double randomNumber = random.nextDouble() * 100; + objectUnderTest.add(randomNumber); + sum += randomNumber; + } + + Assert.assertEquals(objectUnderTest.getArithmeticMean(), sum / 123, TEST_PRECISION); + } + } + +} diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsBaselineCalculatorTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsBaselineCalculatorTest.java new file mode 100644 index 0000000..669b8bf --- /dev/null +++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsBaselineCalculatorTest.java @@ -0,0 +1,228 @@ +/* + * RHQ Management Platform + * Copyright 2011, Red Hat Middleware LLC, and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package org.rhq.server.metrics; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import com.datastax.driver.core.Session; + +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockObjectFactory; +import org.testng.Assert; +import org.testng.IObjectFactory; +import org.testng.annotations.ObjectFactory; +import org.testng.annotations.Test; + +import org.rhq.core.domain.measurement.MeasurementBaseline; +import org.rhq.core.domain.util.PageOrdering; + +/** + * @author Stefan Negrea + * + */ +@PrepareForTest({ MetricsBaselineCalculator.class }) +public class MetricsBaselineCalculatorTest { + + private static final double TEST_PRECISION = Math.pow(10, -10); + + @ObjectFactory + public IObjectFactory getObjectFactory() { + return new PowerMockObjectFactory(); + } + + @Test + public void noCalculationTest() throws Exception { + + //tell the method story as it happens: mock dependencies and configure + //those dependencies to get the method under test to completion. + Session mockSession = mock(Session.class); + MetricsDAO mockMetricsDAO = mock(MetricsDAO.class); + PowerMockito.whenNew(MetricsDAO.class).withParameterTypes(Session.class).withArguments(eq(mockSession)) + .thenReturn(mockMetricsDAO); + + when(mockMetricsDAO.findAggregateMetrics(eq(MetricsTable.ONE_HOUR), eq(1), eq(0), eq(1))).thenReturn( + new ArrayList<AggregatedNumericMetric>()); + + //create object to test and inject required dependencies + MetricsBaselineCalculator objectUnderTest = new MetricsBaselineCalculator(mockSession); + + //run code under test + List<MeasurementBaseline> result = objectUnderTest.calculateBaselines(Arrays.asList(0), 0, 1); + + //verify the results (Assert and mock verification) + Assert.assertEquals(result.size(), 0); + verify(mockMetricsDAO, times(1)).findAggregateMetrics(eq(MetricsTable.ONE_HOUR), any(Integer.class), + any(Integer.class), any(Integer.class)); + verifyNoMoreInteractions(mockMetricsDAO); + } + + @Test + public void randomDataTest() throws Exception { + //generate random data + Random random = new Random(); + List<AggregatedNumericMetric> randomData = new ArrayList<AggregatedNumericMetric>(); + + for (int i = 0; i < 123; i++) { + AggregatedNumericMetric randomMetric = new AggregatedNumericMetric(); + randomMetric.setAvg(random.nextDouble() * 1000); + randomData.add(randomMetric); + } + + double average = 0; + for (AggregatedNumericMetric metric : randomData) { + average += metric.getAvg(); + } + average = average / 123; + + double expectedMax = 99999; + double expectedMin = 1.1111; + int expectedScheduleId= 567; + long expectedStartTime = 135; + long expectedEndTime = 246; + long beforeComputeTime = System.currentTimeMillis(); + + //tell the method story as it happens: mock dependencies and configure + //those dependencies to get the method under test to completion. + Session mockSession = mock(Session.class); + MetricsDAO mockMetricsDAO = mock(MetricsDAO.class); + PowerMockito.whenNew(MetricsDAO.class).withParameterTypes(Session.class).withArguments(eq(mockSession)) + .thenReturn(mockMetricsDAO); + + when( + mockMetricsDAO.findAggregateMetrics(eq(MetricsTable.ONE_HOUR), eq(expectedScheduleId), + eq(expectedStartTime), eq(expectedEndTime))).thenReturn(randomData); + + when( + mockMetricsDAO.findAggregateSimpleMetric(eq(MetricsTable.ONE_HOUR), eq(AggregateType.MIN), + eq(expectedScheduleId), eq(expectedStartTime), eq(expectedEndTime), eq(PageOrdering.ASC), eq(1))) + .thenReturn(Arrays.asList(expectedMin)); + + when( + mockMetricsDAO.findAggregateSimpleMetric(eq(MetricsTable.ONE_HOUR), eq(AggregateType.MAX), + eq(expectedScheduleId), eq(expectedStartTime), eq(expectedEndTime), eq(PageOrdering.DESC), eq(1))) + .thenReturn(Arrays.asList(expectedMax)); + + //create object to test and inject required dependencies + MetricsBaselineCalculator objectUnderTest = new MetricsBaselineCalculator(mockSession); + + //run code under test + List<MeasurementBaseline> result = objectUnderTest.calculateBaselines(Arrays.asList(expectedScheduleId), + expectedStartTime, expectedEndTime); + + //verify the results (Assert and mock verification) + Assert.assertEquals(result.size(), 1); + + MeasurementBaseline baselineResult = result.get(0); + Assert.assertEquals(baselineResult.getMean(), average, TEST_PRECISION); + Assert.assertEquals(baselineResult.getMax(), expectedMax, TEST_PRECISION); + Assert.assertEquals(baselineResult.getMin(), expectedMin, TEST_PRECISION); + Assert.assertEquals(baselineResult.getScheduleId(), expectedScheduleId); + if (baselineResult.getComputeTime().getTime() >= System.currentTimeMillis()) { + Assert.fail("Back compute time, the computation was forward dated."); + } + if (baselineResult.getComputeTime().getTime() <= beforeComputeTime) { + Assert.fail("Back compute time, the computation was backdated."); + } + + verify(mockMetricsDAO, times(1)).findAggregateMetrics(eq(MetricsTable.ONE_HOUR), eq(expectedScheduleId), + eq(expectedStartTime), eq(expectedEndTime)); + verify(mockMetricsDAO, times(1)).findAggregateSimpleMetric(eq(MetricsTable.ONE_HOUR), eq(AggregateType.MIN), + eq(expectedScheduleId), eq(expectedStartTime), eq(expectedEndTime), eq(PageOrdering.ASC), eq(1)); + verify(mockMetricsDAO, times(1)).findAggregateSimpleMetric(eq(MetricsTable.ONE_HOUR), eq(AggregateType.MAX), + eq(expectedScheduleId), eq(expectedStartTime), eq(expectedEndTime), eq(PageOrdering.DESC), eq(1)); + + verifyNoMoreInteractions(mockMetricsDAO); + } + + @Test + public void noMinMaxDataTest() throws Exception { + //generate random data + Random random = new Random(); + List<AggregatedNumericMetric> randomData = new ArrayList<AggregatedNumericMetric>(); + + for (int i = 0; i < 123; i++) { + AggregatedNumericMetric randomMetric = new AggregatedNumericMetric(); + randomMetric.setAvg(random.nextDouble() * 1000); + randomData.add(randomMetric); + } + + double average = 0; + for (AggregatedNumericMetric metric : randomData) { + average += metric.getAvg(); + } + average = average / 123; + + double expectedMinMax = 0; + int expectedScheduleId = 567; + long expectedStartTime = 135; + long expectedEndTime = 246; + + //tell the method story as it happens: mock dependencies and configure + //those dependencies to get the method under test to completion. + Session mockSession = mock(Session.class); + MetricsDAO mockMetricsDAO = mock(MetricsDAO.class); + PowerMockito.whenNew(MetricsDAO.class).withParameterTypes(Session.class).withArguments(eq(mockSession)) + .thenReturn(mockMetricsDAO); + + when( + mockMetricsDAO.findAggregateMetrics(eq(MetricsTable.ONE_HOUR), eq(expectedScheduleId), + eq(expectedStartTime), eq(expectedEndTime))).thenReturn(randomData); + + + //create object to test and inject required dependencies + MetricsBaselineCalculator objectUnderTest = new MetricsBaselineCalculator(mockSession); + + //run code under test + List<MeasurementBaseline> result = objectUnderTest.calculateBaselines(Arrays.asList(expectedScheduleId), + expectedStartTime, expectedEndTime); + + //verify the results (Assert and mock verification) + Assert.assertEquals(result.size(), 1); + + MeasurementBaseline baselineResult = result.get(0); + Assert.assertEquals(baselineResult.getMean(), average, TEST_PRECISION); + Assert.assertEquals(baselineResult.getMax(), expectedMinMax, TEST_PRECISION); + Assert.assertEquals(baselineResult.getMin(), expectedMinMax, TEST_PRECISION); + Assert.assertEquals(baselineResult.getScheduleId(), expectedScheduleId); + + verify(mockMetricsDAO, times(1)).findAggregateMetrics(eq(MetricsTable.ONE_HOUR), eq(expectedScheduleId), + eq(expectedStartTime), eq(expectedEndTime)); + verify(mockMetricsDAO, times(1)).findAggregateSimpleMetric(eq(MetricsTable.ONE_HOUR), eq(AggregateType.MIN), + eq(expectedScheduleId), eq(expectedStartTime), eq(expectedEndTime), eq(PageOrdering.ASC), eq(1)); + verify(mockMetricsDAO, times(1)).findAggregateSimpleMetric(eq(MetricsTable.ONE_HOUR), eq(AggregateType.MAX), + eq(expectedScheduleId), eq(expectedStartTime), eq(expectedEndTime), eq(PageOrdering.DESC), eq(1)); + + verifyNoMoreInteractions(mockMetricsDAO); + } + +}
rhq-commits@lists.fedorahosted.org