Permalink
Browse files

Added basic functionality for measuring methods and generating csv re…

…ports
  • Loading branch information...
1 parent bf53bbd commit 1db30e912aefb6a73bbf0b917c137c828fc37267 Jakub Holy committed May 15, 2012
@@ -0,0 +1,7 @@
+package net.jakubholy.jeeutils.perfstats;
+
+public interface Clock {
+
+ long getCurrentTimeMillis();
+
+}
@@ -0,0 +1,15 @@
+package net.jakubholy.jeeutils.perfstats;
+
+public class ControllableClock implements Clock {
+
+ private long time;
+
+ public void addSeconds(int i) {
+ time += i*1000;
+ }
+
+ public long getCurrentTimeMillis() {
+ return time;
+ }
+
+}
@@ -0,0 +1,51 @@
+package net.jakubholy.jeeutils.perfstats;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class Metric {
+
+ private long count;
+ private List<Long> measurements = new ArrayList<Long>();
+ private final String name;
+
+ public Metric(String name) {
+ this.name = name;
+ }
+
+ public long getCount() {
+ return count;
+ }
+
+ public void incrementCount() {
+ ++count;
+ }
+
+ public long getAverage() {
+ long total = 0;
+
+ for (Long measurement : measurements) {
+ total += measurement;
+ }
+
+ return total / measurements.size();
+ }
+
+ void recordMeasurement(long timeUsed) {
+ measurements.add(timeUsed);
+ }
+
+ public long getMax() {
+ return Collections.max(measurements);
+ }
+
+ public long getMin() {
+ return Collections.min(measurements);
+ }
+
+ public String toCsv() {
+ return name + ',' + count + ',' + getMax() + ',' + getMin() + ',' + getAverage();
+ }
+
+}
@@ -0,0 +1,19 @@
+package net.jakubholy.jeeutils.perfstats;
+
+public class OngoingMeasurement {
+
+ private final Clock clock;
+ private final Metric metric;
+ private long startTime;
+
+ public OngoingMeasurement(Clock clock, Metric metric) {
+ this.clock = clock;
+ this.metric = metric;
+ startTime = clock.getCurrentTimeMillis();
+ }
+
+ public void stop() {
+ metric.recordMeasurement(clock.getCurrentTimeMillis() - startTime);
+ }
+
+}
@@ -0,0 +1,46 @@
+package net.jakubholy.jeeutils.perfstats;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class PerfStats {
+
+ private static final Map<String, Metric> metricByMethod = new HashMap<String, Metric>();
+
+ private static Clock clock = new SystemClock();
+
+ public static OngoingMeasurement startFor(String methodName) {
+ if (methodName == null) {
+ throw new IllegalArgumentException("methodName is required");
+ }
+ if(!metricByMethod.containsKey(methodName)) {
+ metricByMethod.put(methodName, new Metric(methodName));
+ }
+
+ Metric metric = metricByMethod.get(methodName);
+ metric.incrementCount();
+
+ OngoingMeasurement ongoingMeasurement = new OngoingMeasurement(clock, metric);
+ return ongoingMeasurement;
+ }
+
+ public static Metric getMetric(String methodName) {
+ return metricByMethod.get(methodName);
+ }
+
+ public static void reset() {
+ metricByMethod.clear();
+ }
+
+ static void setClock(Clock clock) {
+ PerfStats.clock = clock;
+ }
+
+ public static String reportAsCsv() {
+ StringBuilder csv = new StringBuilder("metric,count,max,min,avg\n");
+ for (Metric metric : metricByMethod.values()) {
+ csv.append(metric.toCsv()).append('\n');
+ }
+ return csv.toString();
+ }
+}
@@ -0,0 +1,8 @@
+package net.jakubholy.jeeutils.perfstats;
+
+public class SystemClock implements Clock {
+
+ public long getCurrentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+}
@@ -0,0 +1,26 @@
+package net.jakubholy.jeeutils.perfstats;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+
+public class PerfStatsTest {
+
+ @Test
+ public void should_measure_time() throws Exception {
+ final long beforeStartMs = System.currentTimeMillis();
+ OngoingMeasurement measurement = PerfStats.startFor("anotherMethod");
+ Thread.sleep(10);
+ measurement.stop();
+ final long afterStartMs = System.currentTimeMillis();
+
+ long measuredDuration = PerfStats.getMetric("anotherMethod").getMax();
+ assertThat(measuredDuration, is(greaterThanOrEqualTo(1l)));
+ assertThat(measuredDuration, is(lessThanOrEqualTo(afterStartMs - beforeStartMs)));
+ }
+
+}
@@ -0,0 +1,105 @@
+package net.jakubholy.jeeutils.perfstats;
+
+import static net.jakubholy.jeeutils.perfstats.PerfStats.getMetric;
+import static net.jakubholy.jeeutils.perfstats.PerfStats.startFor;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class PerfStatsWithControllableClockTest {
+
+ private ControllableClock clock;
+
+ @Before
+ public void setUp() {
+ PerfStats.reset();
+ clock = new ControllableClock();
+ PerfStats.setClock(clock);
+ }
+
+ @Test
+ public void testAll() {
+ // record 3 measurements
+ OngoingMeasurement first = startFor("myMetode");
+ clock.addSeconds(1);
+ first.stop();
+
+ OngoingMeasurement second = startFor("myMetode");
+ clock.addSeconds(3);
+ second.stop();
+
+ OngoingMeasurement third = startFor("myMetode");
+ clock.addSeconds(5);
+ third.stop();
+
+ // get count, avg, max, min
+ Metric metric = getMetric("myMetode");
+ assertEquals(3, metric.getCount());
+ assertEquals(3000, metric.getAverage());
+ assertEquals(5000, metric.getMax());
+ assertEquals(1000, metric.getMin());
+
+ // report metrics recorded as CSV: methodName, count, max, min, avg
+ String report = PerfStats.reportAsCsv();
+ assertThat(report , containsString("myMetode,3,5000,1000,3000\n"));
+ assertThat(report , containsString("metric,count,max,min,avg\n"));
+ }
+
+ @Test
+ public void one_measurement_should_be_counted() {
+ OngoingMeasurement measurement = startFor("myMetode");
+ measurement.stop();
+
+ assertEquals(1, getMetric("myMetode").getCount());
+ }
+
+ @Test
+ public void two_measurements_should_be_counted() {
+ startFor("myMetode").stop();
+ startFor("myMetode").stop();
+
+ assertEquals(2, getMetric("myMetode").getCount());
+ }
+
+ @Test
+ public void two_different_metrics_should_be_counted_separately() {
+ startFor("oneMethod").stop();
+ startFor("anotherMethod").stop();
+
+ assertEquals(1, getMetric("oneMethod").getCount());
+ assertEquals(1, getMetric("anotherMethod").getCount());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void methodName_null_should_throw_exception() throws Exception {
+ startFor(null);
+ }
+
+ @Test
+ public void initial_average_should_be_zero() throws Exception {
+ startFor("myMetode").stop();
+
+ assertEquals(0, getMetric("myMetode").getAverage());
+ }
+
+ @Test
+ public void should_record_average() throws Exception {
+ OngoingMeasurement first = startFor("myMetode");
+ clock.addSeconds(1);
+ first.stop();
+
+ OngoingMeasurement second = startFor("myMetode");
+ clock.addSeconds(3);
+ second.stop();
+
+ assertEquals(2000, getMetric("myMetode").getAverage());
+ }
+
+ /*
+ * TODO TESTS
+ * - OngoingMeasurement allows stop() to be called only once
+ */
+
+}

0 comments on commit 1db30e9

Please sign in to comment.