aurora-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From zma...@apache.org
Subject [05/37] aurora git commit: Import of Twitter Commons.
Date Tue, 25 Aug 2015 18:19:19 GMT
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/objectsize/ObjectSizeCalculatorTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/objectsize/ObjectSizeCalculatorTest.java b/commons/src/test/java/com/twitter/common/objectsize/ObjectSizeCalculatorTest.java
new file mode 100644
index 0000000..cf64a9b
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/objectsize/ObjectSizeCalculatorTest.java
@@ -0,0 +1,216 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.objectsize;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.objectsize.ObjectSizeCalculator.MemoryLayoutSpecification;
+
+import static org.junit.Assert.assertEquals;
+
+public class ObjectSizeCalculatorTest {
+
+  private int A;
+  private int O;
+  private int R;
+  private int S;
+  private ObjectSizeCalculator objectSizeCalculator;
+
+  @Before
+  public void setUp() {
+    MemoryLayoutSpecification memoryLayoutSpecification =
+        new MemoryLayoutSpecification() {
+          @Override public int getArrayHeaderSize() {
+            return 16;
+          }
+          @Override public int getObjectHeaderSize() {
+            return 12;
+          }
+          @Override public int getObjectPadding() {
+            return 8;
+          }
+          @Override public int getReferenceSize() {
+            return 4;
+          }
+          @Override public int getSuperclassFieldPadding() {
+            return 4;
+          }
+        };
+
+    A = memoryLayoutSpecification.getArrayHeaderSize();
+    O = memoryLayoutSpecification.getObjectHeaderSize();
+    R = memoryLayoutSpecification.getReferenceSize();
+    S = memoryLayoutSpecification.getSuperclassFieldPadding();
+
+    objectSizeCalculator = new ObjectSizeCalculator(memoryLayoutSpecification);
+  }
+
+  @Test
+  public void testRounding() {
+    assertEquals(0, roundTo(0, 8));
+    assertEquals(8, roundTo(1, 8));
+    assertEquals(8, roundTo(7, 8));
+    assertEquals(8, roundTo(8, 8));
+    assertEquals(16, roundTo(9, 8));
+    assertEquals(16, roundTo(15, 8));
+    assertEquals(16, roundTo(16, 8));
+    assertEquals(24, roundTo(17, 8));
+  }
+
+  @Test
+  public void testObjectSize() {
+    assertSizeIs(O, new Object());
+  }
+
+  static class ObjectWithFields {
+    int length;
+    int offset;
+    int hashcode;
+    char[] data = {};
+  }
+
+  @Test
+  public void testObjectWithFields() {
+    assertSizeIs(O + 3 * 4 + R + A, new ObjectWithFields());
+  }
+
+  public static class Class1 {
+    private boolean b1;
+  }
+
+  @Test
+  public void testOneBooleanSize() {
+    assertSizeIs(O + 1, new Class1());
+  }
+
+  public static class Class2 extends Class1 {
+    private int i1;
+  }
+
+  @Test
+  public void testSimpleSubclassSize() {
+    assertSizeIs(O + roundTo(1, S) + 4, new Class2());
+  }
+
+  @Test
+  public void testZeroLengthArray() {
+    assertSizeIs(A, new byte[0]);
+    assertSizeIs(A, new int[0]);
+    assertSizeIs(A, new long[0]);
+    assertSizeIs(A, new Object[0]);
+  }
+
+  @Test
+  public void testByteArrays() {
+    assertSizeIs(A + 1, new byte[1]);
+    assertSizeIs(A + 8, new byte[8]);
+    assertSizeIs(A + 9, new byte[9]);
+  }
+
+  @Test
+  public void testCharArrays() {
+    assertSizeIs(A + 2 * 1, new char[1]);
+    assertSizeIs(A + 2 * 4, new char[4]);
+    assertSizeIs(A + 2 * 5, new char[5]);
+  }
+
+  @Test
+  public void testIntArrays() {
+    assertSizeIs(A + 4 * 1, new int[1]);
+    assertSizeIs(A + 4 * 2, new int[2]);
+    assertSizeIs(A + 4 * 3, new int[3]);
+  }
+
+  @Test
+  public void testLongArrays() {
+    assertSizeIs(A + 8 * 1, new long[1]);
+    assertSizeIs(A + 8 * 2, new long[2]);
+    assertSizeIs(A + 8 * 3, new long[3]);
+  }
+
+  @Test
+  public void testObjectArrays() {
+    assertSizeIs(A + R * 1, new Object[1]);
+    assertSizeIs(A + R * 2, new Object[2]);
+    assertSizeIs(A + R * 3, new Object[3]);
+    assertSizeIs(A + R * 1, new String[1]);
+    assertSizeIs(A + R * 2, new String[2]);
+    assertSizeIs(A + R * 3, new String[3]);
+  }
+
+  public static class Circular {
+    Circular c;
+  }
+
+  @Test
+  public void testCircular() {
+    Circular c1 = new Circular();
+    long size = objectSizeCalculator.calculateObjectSize(c1);
+    c1.c = c1;
+    assertEquals(size, objectSizeCalculator.calculateObjectSize(c1));
+  }
+
+  static class ComplexObject<T> {
+    static class Node<T> {
+      final T value;
+      Node<T> previous;
+      Node<T> next;
+
+      Node(T value) {
+        this.value = value;
+      }
+    }
+
+    private Node<T> first;
+    private Node<T> last;
+
+    void add(T item) {
+      Node<T> node = new Node<T>(item);
+      if (first == null) {
+        first = node;
+      } else {
+        last.next = node;
+        node.previous = last;
+      }
+      last = node;
+    }
+  }
+
+  @Test
+  public void testComplexObject() {
+    ComplexObject<Object> l = new ComplexObject<Object>();
+    l.add(new Object());
+    l.add(new Object());
+    l.add(new Object());
+
+    long expectedSize = 0;
+    expectedSize += roundTo(O + 2 * R, 8); // The complex object itself plus first and last refs.
+    expectedSize += roundTo(O + 3 * R, 8) * 3; // 3 Nodes - each with 3 object references.
+    expectedSize += roundTo(O, 8) * 3; // 3 vanilla objects contained in the node values.
+
+    assertSizeIs(expectedSize, l);
+  }
+
+  private void assertSizeIs(long size, Object o) {
+    assertEquals(roundTo(size, 8), objectSizeCalculator.calculateObjectSize(o));
+  }
+
+  private static long roundTo(long x, int multiple) {
+    return ObjectSizeCalculator.roundTo(x, multiple);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/quantity/AmountTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/quantity/AmountTest.java b/commons/src/test/java/com/twitter/common/quantity/AmountTest.java
new file mode 100644
index 0000000..3e1430b
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/quantity/AmountTest.java
@@ -0,0 +1,120 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.quantity;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Ordering;
+
+import org.junit.Test;
+
+import static com.google.common.testing.junit4.JUnitAsserts.assertNotEqual;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author John Sirois
+ */
+public class AmountTest {
+
+  @Test
+  public void testEquals() {
+    assertEquals("expected value equality semantics",
+        Amount.of(1L, Time.DAYS), Amount.of(1L, Time.DAYS));
+
+    assertEquals("expected equality to be calculated from amounts converted to a common unit",
+        Amount.of(1L, Time.DAYS), Amount.of(24L, Time.HOURS));
+
+    assertNotEqual("expected unit conversions for equality tests to not lose precision",
+        Amount.of(25L, Time.HOURS), Amount.of(1L, Time.DAYS));
+    assertNotEqual("expected unit conversions for equality tests to not lose precision",
+        Amount.of(1L, Time.DAYS), Amount.of(25L, Time.HOURS));
+
+    assertFalse("expected value equality to work only for the same Number types",
+        Amount.of(1L, Time.DAYS).equals(Amount.of(1.0, Time.DAYS)));
+    assertFalse("expected value equality to work only for the same Number types",
+        Amount.of(1L, Time.DAYS).equals(Amount.of(1, Time.DAYS)));
+
+    assertFalse("amounts with incompatible units should never be equal even if their values are",
+        Amount.of(1L, Time.NANOSECONDS).equals(Amount.of(1L, Data.BITS)));
+  }
+
+  @Test
+  public void testComparisonMixedUnits() {
+    assertTrue(Amount.of(1, Time.MINUTES).compareTo(Amount.of(59, Time.SECONDS)) > 0);
+    assertTrue(Amount.of(1, Time.MINUTES).compareTo(Amount.of(60, Time.SECONDS)) == 0);
+    assertTrue(Amount.of(1, Time.MINUTES).compareTo(Amount.of(61, Time.SECONDS)) < 0);
+
+    assertTrue(Amount.of(59, Time.SECONDS).compareTo(Amount.of(1, Time.MINUTES)) < 0);
+    assertTrue(Amount.of(60, Time.SECONDS).compareTo(Amount.of(1, Time.MINUTES)) == 0);
+    assertTrue(Amount.of(61, Time.SECONDS).compareTo(Amount.of(1, Time.MINUTES)) > 0);
+  }
+
+  @Test
+  @SuppressWarnings("unchecked") // Needed because type information lost in vargs.
+  public void testOrderingMixedUnits() {
+    assertEquals(
+        Lists.newArrayList(
+            Amount.of(1, Data.BITS),
+            Amount.of(1, Data.KB),
+            Amount.of(1, Data.MB),
+            Amount.of(1, Data.MB)),
+        Ordering.natural().sortedCopy(Lists.newArrayList(
+            Amount.of(1, Data.KB),
+            Amount.of(1024, Data.KB),
+            Amount.of(1, Data.BITS),
+            Amount.of(1, Data.MB))));
+  }
+
+  @Test
+  @SuppressWarnings("unchecked") // Needed because type information lost in vargs.
+  public void testOrderingSameUnits() {
+    assertEquals(
+        Lists.newArrayList(
+            Amount.of(1, Time.MILLISECONDS),
+            Amount.of(2, Time.MILLISECONDS),
+            Amount.of(3, Time.MILLISECONDS),
+            Amount.of(4, Time.MILLISECONDS)),
+        Ordering.natural().sortedCopy(Lists.newArrayList(
+            Amount.of(3, Time.MILLISECONDS),
+            Amount.of(2, Time.MILLISECONDS),
+            Amount.of(1, Time.MILLISECONDS),
+            Amount.of(4, Time.MILLISECONDS))));
+  }
+
+  @Test
+  public void testConvert() {
+    Amount<Long, Time> integralDuration = Amount.of(15L, Time.MINUTES);
+    assertEquals(Long.valueOf(15 * 60 * 1000), integralDuration.as(Time.MILLISECONDS));
+
+    assertEquals("expected conversion losing precision to use truncation",
+        Long.valueOf(0), integralDuration.as(Time.HOURS));
+
+    assertEquals("expected conversion losing precision to use truncation",
+        Long.valueOf(0), Amount.of(45L, Time.MINUTES).as(Time.HOURS));
+
+    Amount<Double, Time> decimalDuration = Amount.of(15.0, Time.MINUTES);
+    assertEquals(Double.valueOf(15 * 60 * 1000), decimalDuration.as(Time.MILLISECONDS));
+    assertEquals(Double.valueOf(0.25), decimalDuration.as(Time.HOURS));
+  }
+
+  @Test(expected = Amount.TypeOverflowException.class)
+  public void testAmountThrowsTypeOverflowException() {
+    Amount.of(1000, Time.DAYS).asChecked(Time.MILLISECONDS);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/ApproximateHistogramTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/ApproximateHistogramTest.java b/commons/src/test/java/com/twitter/common/stats/ApproximateHistogramTest.java
new file mode 100644
index 0000000..9eecceb
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/ApproximateHistogramTest.java
@@ -0,0 +1,309 @@
+package com.twitter.common.stats;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+
+import com.twitter.common.objectsize.ObjectSizeCalculator;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Data;
+import com.twitter.common.stats.ApproximateHistogram;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ApproximateHistogramTest {
+  final int b = 10;
+  final int h = 3;
+
+  @Test
+  public void testCollapse() {
+    ApproximateHistogram hist = new ApproximateHistogram();
+    long[] buf1 = {2,5,7};
+    long[] buf2 = {3,8,9};
+    long[] expected = {3,7,9};
+    long[] result = new long[3];
+    // [2,5,7] weight 2 and [3,8,9] weight 3
+    // weight x array + concat = [2,2,5,5,7,7,3,3,3,8,8,8,9,9,9]
+    // sort = [2,2,3,3,3,5,5,7,7,8,8,8,9,9,9]
+    // select every nth elems = [3,7,9]  (n = sum weight / 2, ie. 5/3 = 2)
+    // [2,2,3,3,3,5,5,7,7,8,8,8,9,9,9]
+    //  . . ^ . . . . ^ . . . . ^ . .
+    //  [-------] [-------] [-------] we make 3 packets of 5 elements and take the middle
+
+    ApproximateHistogram.collapse(buf1, 2, buf2, 3, result);
+    assertArrayEquals(result, expected);
+
+    long[] buf3 = {2, 5, 7, 9};
+    long[] buf4 = {3, 8, 9, 12};
+    long[] expected2 = {3, 7, 9, 12};
+    long[] result2 = new long[4];
+    ApproximateHistogram.collapse(buf3, 2, buf4, 2, result2);
+    assertArrayEquals(expected2, result2);
+  }
+
+  @Test
+  public void testRecCollapse() {
+    long[] empty = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    long[] full = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+    ApproximateHistogram hist = new ApproximateHistogram(b, h);
+    assertArrayEquals(empty, hist.buffer[0]);
+    assertArrayEquals(empty, hist.buffer[1]);
+
+    initializeValues(hist, b, Suppliers.ofInstance(1L));
+    assertArrayEquals(full, hist.buffer[0]);
+    assertArrayEquals(empty, hist.buffer[1]);
+
+    initializeValues(hist, b, Suppliers.ofInstance(1L));
+    assertArrayEquals(full, hist.buffer[0]);
+    assertArrayEquals(full, hist.buffer[1]);
+
+    hist.add(1);
+    assertEquals(2, hist.currentTop);
+    // Buffers are not cleared so we can't check that!
+    assertArrayEquals(full, hist.buffer[2]);
+
+    initializeValues(hist, 2 * b, Suppliers.ofInstance(1L));
+    assertEquals(3, hist.currentTop);
+    assertArrayEquals(full, hist.buffer[3]);
+  }
+
+  @Test
+  public void testReachingMaxDepth() {
+    ApproximateHistogram hist = new ApproximateHistogram(b, h);
+
+    initializeValues(hist, 8 * b, Suppliers.ofInstance(1L));
+    assertEquals(3, hist.currentTop);
+
+    hist.add(1);
+    assertEquals(3, hist.currentTop);
+  }
+
+  @Test
+  public void testMem() {
+    for (int b = 10; b < 100; b += 10) {
+      for (int h = 4; h < 16; h += 4) {
+        ApproximateHistogram hist = new ApproximateHistogram(b, h);
+        long actualSize = ObjectSizeCalculator.getObjectSize(hist);
+        long estimatedSize = ApproximateHistogram.memoryUsage(b, h);
+        assertTrue("Consume less memory than the constraint", actualSize < estimatedSize);
+      }
+    }
+  }
+
+  @Test
+  public void testMemConstraint() {
+    ImmutableList.Builder<Amount<Long, Data>> builder = ImmutableList.builder();
+    builder.add(Amount.of(1L, Data.KB));
+    builder.add(Amount.of(4L, Data.KB));
+    builder.add(Amount.of(8L, Data.KB));
+    builder.add(Amount.of(16L, Data.KB));
+    builder.add(Amount.of(32L, Data.KB));
+    builder.add(Amount.of(64L, Data.KB));
+    builder.add(Amount.of(256L, Data.KB));
+    builder.add(Amount.of(1L, Data.MB));
+    builder.add(Amount.of(16L, Data.MB));
+    builder.add(Amount.of(32L, Data.MB));
+    List<Amount<Long, Data>> sizes = builder.build();
+
+    for (Amount<Long, Data> maxSize: sizes) {
+      ApproximateHistogram hist = new ApproximateHistogram(maxSize);
+      for (long i = 0; i < 1000 * 1000; i++) { hist.add(i); }
+      long size = ObjectSizeCalculator.getObjectSize(hist);
+      assertTrue(size < maxSize.as(Data.BYTES));
+    }
+  }
+
+  @Test
+  public void testLowMemoryPrecision() {
+    double e = ApproximateHistogram.DEFAULT_PRECISION.getEpsilon();
+    int n = ApproximateHistogram.DEFAULT_PRECISION.getN();
+    int defaultDepth = ApproximateHistogram.computeDepth(e, n);
+    int defaultBufferSize = ApproximateHistogram.computeBufferSize(defaultDepth, n);
+
+    ApproximateHistogram hist = new ApproximateHistogram(Amount.of(1L, Data.KB));
+    int depth = hist.buffer.length - 1;
+    int bufferSize = hist.buffer[0].length;
+
+    assertTrue(depth > defaultDepth);
+    assertTrue(bufferSize < defaultBufferSize);
+  }
+
+  @Test
+  public void testHighMemoryPrecision() {
+    double e = ApproximateHistogram.DEFAULT_PRECISION.getEpsilon();
+    int n = ApproximateHistogram.DEFAULT_PRECISION.getN();
+    int defaultDepth = ApproximateHistogram.computeDepth(e, n);
+    int defaultBufferSize = ApproximateHistogram.computeBufferSize(defaultDepth, n);
+
+    ApproximateHistogram hist = new ApproximateHistogram(Amount.of(1L, Data.MB));
+    int depth = hist.buffer.length - 1;
+    int bufferSize = hist.buffer[0].length;
+
+    assertTrue(depth < defaultDepth);
+    assertTrue(bufferSize > defaultBufferSize);
+  }
+
+  private void initIndexArray(ApproximateHistogram hist, int b) {
+    Arrays.fill(hist.indices, b - 1);
+    int buf0Size = Math.min(b, hist.leafCount);
+    int buf1Size = Math.max(0, hist.leafCount - buf0Size);
+    hist.indices[0] = buf0Size - 1;
+    hist.indices[1] = buf1Size - 1;
+  }
+
+  private long getBiggest(ApproximateHistogram hist) {
+    int j = hist.biggest(hist.indices);
+    int idx = hist.indices[j];
+    hist.indices[j] -= 1;
+    return hist.buffer[j][idx];
+  }
+
+  @Test
+  public void testBiggestIndexFinder() {
+    ApproximateHistogram hist = new ApproximateHistogram(b, h);
+    int n = 3;
+    for (int i=1; i <= n; i++) {
+      hist.add(i);
+    }
+
+    initIndexArray(hist, b);
+    for (int i=1; i <= n; i++) {
+      assertEquals(n - i + 1, getBiggest(hist));
+    }
+
+    n = 2 * b;
+    for (int i=4; i <= n; i++) {
+      hist.add(i);
+    }
+
+    initIndexArray(hist, b);
+    for (int i=1; i <= n; i++) {
+      assertEquals(n - i + 1, getBiggest(hist));
+    }
+
+    hist.add(2*b + 1);
+    n += 1;
+
+    initIndexArray(hist, b);
+    assertEquals(n, getBiggest(hist));
+
+    for (int i=2; i <= n; i += 2) {
+      assertEquals(n - i + 1, getBiggest(hist));
+    }
+  }
+
+  @Test
+  public void testIsBufferEmpty() {
+    ApproximateHistogram hist = new ApproximateHistogram(b, h);
+
+    for (int i=0; i < 3*b; i++) {
+      hist.add(i);
+    }
+    assertEquals(false, hist.isBufferEmpty(2));
+    assertEquals(true, hist.isBufferEmpty(3));
+
+    for (int i=0; i < 2*b; i++) {
+      hist.add(i);
+    }
+    assertEquals(true, hist.isBufferEmpty(2));
+    assertEquals(false, hist.isBufferEmpty(3));
+  }
+
+  @Test
+  public void testHistogramWithNegative() {
+    ApproximateHistogram hist = new ApproximateHistogram();
+    hist.add(-1L);
+    assertEquals(-1L, hist.getQuantile(0.0));
+    assertEquals(-1L, hist.getQuantile(0.5));
+    assertEquals(-1L, hist.getQuantile(1.0));
+  }
+
+  @Test
+  public void testHistogramWithEdgeCases() {
+    ApproximateHistogram hist = new ApproximateHistogram();
+    hist.add(Long.MIN_VALUE);
+    assertEquals(Long.MIN_VALUE, hist.getQuantile(0.0));
+    assertEquals(Long.MIN_VALUE, hist.getQuantile(1.0));
+
+    hist.add(Long.MAX_VALUE);
+    assertEquals(Long.MIN_VALUE, hist.getQuantile(0.0));
+    assertEquals(Long.MAX_VALUE, hist.getQuantile(1.0));
+  }
+
+  @Test
+  public void testQueryZerothQuantile() {
+    // Tests that querying the zeroth quantile does not throw an exception
+    ApproximateHistogram hist = new ApproximateHistogram(b, h);
+    initializeValues(hist, 10, Suppliers.ofInstance(1L));
+    assertEquals(1L, hist.getQuantile(0.0));
+  }
+
+  @Test
+  public void testSmallDataCase() {
+    // Tests that querying the zeroth quantile does not throw an exception
+    ApproximateHistogram hist = new ApproximateHistogram(b, h);
+    initializeValues(hist, 1, Suppliers.ofInstance(1L));
+    assertEquals(1L, hist.getQuantile(0.5));
+  }
+
+  @Test
+  public void testSimpleCase() {
+    ApproximateHistogram hist = new ApproximateHistogram();
+    int n = 10;
+    initializeValues(hist, n, monotonic());
+    for (int i = 1; i <= n; i++) {
+      double q = i / 10.0;
+      assertEquals(i, hist.getQuantile(q), 1.0);
+    }
+  }
+
+  @Test
+  public void testGetQuantiles() {
+    ApproximateHistogram hist = new ApproximateHistogram();
+    int n = 10;
+    initializeValues(hist, n, monotonic());
+    double[] quantiles = new double[n];
+    for (int i = 0; i < n; i++) {
+      quantiles[i] = (i + 1) / 10.0;
+    }
+    long[] results = hist.getQuantiles(quantiles);
+    for (int i = 0; i < n; i++) {
+      long res = results[i];
+      double q = quantiles[i];
+      assertEquals(hist.getQuantile(q), res);
+    }
+  }
+
+  @Test
+  public void testYetAnotherGetQuantiles() {
+    // this test originates from issue CSL-586
+    ApproximateHistogram hist = new ApproximateHistogram();
+    hist.add(0);
+    hist.add(4);
+    hist.add(9);
+    hist.add(8);
+    double[] quantiles = new double[]{0.5, 0.9, 0.99};
+    long[] expected = new long[]{8,9,9};
+    assertArrayEquals(hist.getQuantiles(quantiles), expected);
+  }
+
+  private static void initializeValues(ApproximateHistogram hist, int n, Supplier<Long> what) {
+    for (int i=0; i<n ; i++) {
+      hist.add(what.get());
+    }
+  }
+
+  private static Supplier<Long> monotonic() {
+    return new Supplier<Long>() {
+      long i = 0;
+      @Override public Long get() { return ++i; }
+    };
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/ElapsedTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/ElapsedTest.java b/commons/src/test/java/com/twitter/common/stats/ElapsedTest.java
new file mode 100644
index 0000000..3a7ec34
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/ElapsedTest.java
@@ -0,0 +1,70 @@
+package com.twitter.common.stats;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.util.testing.FakeTicker;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author William Farner
+ */
+public class ElapsedTest {
+
+  private static final Amount<Long, Time> ONE_SECOND = Amount.of(1L, Time.SECONDS);
+
+  private static final String NAME = "elapsed";
+
+  private FakeTicker ticker;
+
+  @Before
+  public void setUp() {
+    ticker = new FakeTicker();
+    Stats.flush();
+  }
+
+  private Elapsed elapsed(Time granularity) {
+    return new Elapsed(NAME, granularity, ticker);
+  }
+
+  @Test
+  public void testTimeSince() {
+    Elapsed elapsed = elapsed(Time.MILLISECONDS);
+    checkValue(0);
+    ticker.advance(ONE_SECOND);
+    checkValue(1000);
+
+    elapsed.reset();
+    checkValue(0);
+
+    elapsed.reset();
+    ticker.advance(ONE_SECOND);
+    checkValue(1000);
+    ticker.advance(ONE_SECOND);
+    checkValue(2000);
+    ticker.advance(ONE_SECOND);
+    checkValue(3000);
+    ticker.advance(ONE_SECOND);
+    checkValue(4000);
+  }
+
+  @Test
+  public void testGranularity() {
+    Elapsed elapsed = elapsed(Time.HOURS);
+    checkValue(0);
+    ticker.advance(Amount.of(1L, Time.DAYS));
+    checkValue(24);
+
+    elapsed.reset();
+    ticker.advance(Amount.of(1L, Time.MINUTES));
+    checkValue(0);
+  }
+
+  private void checkValue(long expected) {
+    long actual = (Long) Stats.getVariable(NAME).read();
+    assertEquals(expected, actual);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/EntropyTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/EntropyTest.java b/commons/src/test/java/com/twitter/common/stats/EntropyTest.java
new file mode 100644
index 0000000..43c04c5
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/EntropyTest.java
@@ -0,0 +1,61 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import com.google.common.collect.Lists;
+import junit.framework.TestCase;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * Tests Entropy.
+ *
+ * @author Gilad Mishne
+ */
+public class EntropyTest extends TestCase {
+
+  private void assertEqualsWithDeviation(double expected, double predicted, double deviation) {
+    assertTrue(String.format("%2.4f not within %2.4f distance of %2.4f",
+        predicted, deviation, expected),
+        Math.abs(expected - predicted) <= deviation);
+  }
+
+  @Test
+  public void test() throws Exception {
+    List<Integer> numbers = Lists.newArrayList();
+    double deviation = 0.01;
+
+    assertEqualsWithDeviation(new Entropy<Integer>(numbers).entropy(), 0, deviation);
+
+    numbers.add(1);
+    assertEqualsWithDeviation(new Entropy<Integer>(numbers).entropy(), 0, deviation);
+
+    numbers.add(2);
+    assertEqualsWithDeviation(new Entropy<Integer>(numbers).entropy(), 1, deviation);
+
+    numbers.addAll(Lists.newArrayList(1, 2));
+    assertEqualsWithDeviation(new Entropy<Integer>(numbers).entropy(), 1, deviation);
+    assertEqualsWithDeviation(new Entropy<Integer>(numbers).perplexity(), 2, deviation);
+
+    numbers.addAll(Lists.newArrayList(2, 2, 3, 4));
+    assertEqualsWithDeviation(new Entropy<Integer>(numbers).entropy(), 1.75, deviation);
+
+    numbers.addAll(Lists.newArrayList(1, 1, 1, 1));
+    assertEqualsWithDeviation(new Entropy<Integer>(numbers).entropy(), 1.625, deviation);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/MergedHistogramTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/MergedHistogramTest.java b/commons/src/test/java/com/twitter/common/stats/MergedHistogramTest.java
new file mode 100644
index 0000000..96b5988
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/MergedHistogramTest.java
@@ -0,0 +1,97 @@
+// =================================================================================================
+// Copyright 2013 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import org.junit.Test;
+
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Data;
+
+import static org.junit.Assert.assertEquals;
+
+public class MergedHistogramTest {
+
+  @Test
+  public void testEmptyMergedHistogram() {
+    ApproximateHistogram empty[] = new ApproximateHistogram[0];
+    Histogram mergedHistogram = ApproximateHistogram.merge(empty);
+
+    assertEquals(0L, mergedHistogram.getQuantile(0.5));
+  }
+
+  @Test
+  public void testMergedSimilarHistogram() {
+    int n = 10;
+    ApproximateHistogram histograms[] = new ApproximateHistogram[n];
+    for (int i = 0; i < n; i++) {
+      ApproximateHistogram h = new ApproximateHistogram();
+      h.add(i);
+      histograms[i] = h;
+    }
+
+    Histogram mergedHistogram = ApproximateHistogram.merge(histograms);
+    assertEquals(0L, mergedHistogram.getQuantile(0.0));
+    assertEquals(1L, mergedHistogram.getQuantile(0.1));
+    assertEquals(5L, mergedHistogram.getQuantile(0.5));
+    assertEquals(9L, mergedHistogram.getQuantile(0.9));
+    assertEquals(9L, mergedHistogram.getQuantile(0.99));
+  }
+
+  @Test
+  public void testMergedDifferentHistogram() {
+    int n = 10;
+    ApproximateHistogram histograms[] = new ApproximateHistogram[n];
+    for (int i = 0; i < n; i++) {
+      ApproximateHistogram h = new ApproximateHistogram(Amount.of(2L + 4*i, Data.KB));
+      h.add(i);
+      histograms[i] = h;
+    }
+
+    Histogram mergedHistogram = ApproximateHistogram.merge(histograms);
+    assertEquals(0L, mergedHistogram.getQuantile(0.0));
+    assertEquals(1L, mergedHistogram.getQuantile(0.1));
+    assertEquals(5L, mergedHistogram.getQuantile(0.5));
+    assertEquals(9L, mergedHistogram.getQuantile(0.9));
+    assertEquals(9L, mergedHistogram.getQuantile(0.99));
+  }
+
+  @Test
+  public void testMergedBigHistogram() {
+    int n = 10;
+    int m = 5000;
+    ApproximateHistogram histograms[] = new ApproximateHistogram[n];
+    int x = 0;
+    for (int i = 0; i < n; i++) {
+      ApproximateHistogram h = new ApproximateHistogram();
+      while(x < m * (i + 1)) {
+        h.add(x);
+        x += 1;
+      }
+      histograms[i] = h;
+    }
+    long sum = m * n;
+
+    double maxError = ApproximateHistogram.DEFAULT_PRECISION.getEpsilon() *
+        ApproximateHistogram.DEFAULT_PRECISION.getN();
+    Histogram mergedHistogram = ApproximateHistogram.merge(histograms);
+    for (int i = 1; i < 10; i++) {
+      double q = i / 10.0;
+      double expected = q * sum;
+      assertEquals(expected, mergedHistogram.getQuantile(q), maxError);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/MovingAverageTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/MovingAverageTest.java b/commons/src/test/java/com/twitter/common/stats/MovingAverageTest.java
new file mode 100644
index 0000000..8791820
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/MovingAverageTest.java
@@ -0,0 +1,86 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import static org.easymock.EasyMock.expect;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Test for MovingAverage.
+ *
+ * @author William Farner
+ */
+public class MovingAverageTest extends EasyMockTest {
+
+  private Stat<Integer> input;
+
+  @Before
+  public void setUp() {
+    input = createMock(new Clazz<Stat<Integer>>() {});
+  }
+
+  @Test
+  public void testEmptySeries() {
+    runTest(Lists.<Integer>newArrayList(), Lists.<Double>newArrayList());
+  }
+
+  @Test
+  public void testConstantValues() {
+    runTest(
+        Lists.newArrayList( 5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5,  5),
+        Lists.newArrayList(5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d, 5d));
+  }
+
+  @Test
+  public void testLinear() {
+    runTest(
+        Lists.newArrayList( 1,    2,  3,    4,  5,    6,  7,    8,  9,   10,   11,   12,   13),
+        Lists.newArrayList(1d, 1.5d, 2d, 2.5d, 3d, 3.5d, 4d, 4.5d, 5d, 5.5d, 6.5d, 7.5d, 8.5d));
+  }
+
+  @Test
+  public void testStep() {
+    runTest(
+        Lists.newArrayList( 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10, 10, 10, 10, 10),
+        Lists.newArrayList(0d, 0d, 0d, 0d, 0d, 0d, 0d, 0d, 0d, 0d, 0d, 1d, 2d, 3d, 4d, 5d));
+  }
+
+  private void runTest(List<Integer> inputs, List<Double> expectedOutputs) {
+    expect(input.getName()).andReturn("test").atLeastOnce();
+    for (int value : inputs) {
+      expect(input.read()).andReturn(value);
+    }
+
+    control.replay();
+
+    MovingAverage<Integer> movingAvg = MovingAverage.of(input, 10 /* window size */);
+
+    for (double output : expectedOutputs) {
+      assertThat(movingAvg.sample(), is(output));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/MovingWindowDeltaTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/MovingWindowDeltaTest.java b/commons/src/test/java/com/twitter/common/stats/MovingWindowDeltaTest.java
new file mode 100644
index 0000000..1cb6cbb
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/MovingWindowDeltaTest.java
@@ -0,0 +1,103 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import com.google.common.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.xml.bind.annotation.XmlElement;
+
+import com.google.common.base.Supplier;
+
+/**
+ * Test for MovingWindowDelta.
+ *
+ * @author Feng Zhuge
+ */
+public class MovingWindowDeltaTest {
+  private static final int DEFAULT_WINDOW_SIZE = 5;
+
+  private AtomicLong externalValue = new AtomicLong();
+
+  public Supplier<AtomicLong> getSupplier() {
+    return new Supplier<AtomicLong>() {
+      @Override
+      public AtomicLong get() {
+        return externalValue;
+      }
+    };
+  }
+
+  @Test
+  public void testOneSample() {
+    MovingWindowDelta<AtomicLong> movingWindowDelta = MovingWindowDelta.of(
+      "test", getSupplier(), DEFAULT_WINDOW_SIZE);
+
+    externalValue.getAndSet(7l);
+    externalValue.getAndSet(11l);
+
+    assertEquals(11l, movingWindowDelta.doSample().longValue());
+  }
+
+  @Test
+  public void testMultipleSamples() {
+    MovingWindowDelta<AtomicLong> movingWindowDelta = MovingWindowDelta.of(
+      "test", getSupplier(), DEFAULT_WINDOW_SIZE);
+
+    externalValue.getAndSet(3l);
+    assertEquals(3l, movingWindowDelta.doSample().longValue());
+    externalValue.getAndSet(8l);
+    assertEquals(8l, movingWindowDelta.doSample().longValue());
+  }
+
+  @Test
+  public void TestExpiringCounts() {
+    MovingWindowDelta<AtomicLong> movingWindowDelta = MovingWindowDelta.of(
+      "test", getSupplier(), DEFAULT_WINDOW_SIZE);
+
+    long expectedDelta;
+    for (long i = 0; i < 100; ++i) {
+      expectedDelta = i < DEFAULT_WINDOW_SIZE ? i + 1 : DEFAULT_WINDOW_SIZE;
+
+      externalValue.getAndSet(i + 1);
+      assertEquals(expectedDelta, movingWindowDelta.doSample().longValue());
+    }
+  }
+
+  @Test
+  public void TestDifferentValueExpiring() {
+    MovingWindowDelta<AtomicLong> movingWindowDelta =
+        MovingWindowDelta.of("test", getSupplier(), 5);
+
+    long ret = 0l;
+    for (long  i = 0; i < 10; ++i) {
+      externalValue.getAndSet(i * i);
+      ret = movingWindowDelta.doSample();
+    }
+    assertEquals(65l, ret);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/NumericStatExporterTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/NumericStatExporterTest.java b/commons/src/test/java/com/twitter/common/stats/NumericStatExporterTest.java
new file mode 100644
index 0000000..3fc2557
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/NumericStatExporterTest.java
@@ -0,0 +1,147 @@
+// =================================================================================================
+// Copyright 2012 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+
+import com.twitter.common.application.ShutdownRegistry;
+import com.twitter.common.base.Closure;
+import com.twitter.common.base.Command;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.testing.mockito.MockitoTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for {@code NumericStatExporter}
+ */
+public class NumericStatExporterTest extends MockitoTest {
+  private static final Amount<Long, Time> TEST_EXPORT_INTERVAL = Amount.of(1L, Time.MINUTES);
+  private static final String MOCK_STAT_NAME = "NumericStatExporterTest_mock_stat";
+  private static final int MOCK_STAT_READ_VALUE = 0;
+  private static final int MOCK_STAT_SAMPLED_VALUE = 1;
+
+  @Mock
+  private Closure<Map<String, ? extends Number>> mockExportSink;
+  @Mock
+  private ScheduledExecutorService mockExecutor;
+  @Mock
+  private ShutdownRegistry mockShutdownRegistry;
+  @Mock
+  private RecordingStat<Integer> mockRecordingStat;
+
+  @Captor
+  private ArgumentCaptor<Runnable> runnableCaptor;
+  @Captor
+  private ArgumentCaptor<Command> commandCaptor;
+  @Captor
+  private ArgumentCaptor<Map<String, ? extends Number>> statReadValueMapCaptor;
+
+
+  private NumericStatExporter numericStatExporter;
+
+  @Before
+  public void setUp() {
+    when(mockRecordingStat.getName()).thenReturn(MOCK_STAT_NAME);
+    when(mockRecordingStat.read()).thenReturn(MOCK_STAT_READ_VALUE);
+    when(mockRecordingStat.sample()).thenReturn(MOCK_STAT_SAMPLED_VALUE);
+    Stats.export(mockRecordingStat);
+
+    numericStatExporter
+        = new NumericStatExporter(mockExportSink, mockExecutor, TEST_EXPORT_INTERVAL);
+  }
+
+  @Test
+  public void testStartMethodScheduleExport() {
+    numericStatExporter.start(mockShutdownRegistry);
+
+    // Verify the executor is scheduled properly.
+    verify(mockExecutor).scheduleAtFixedRate(runnableCaptor.capture(),
+        anyLong(), anyLong(), Matchers.<TimeUnit>anyObject());
+    // Verify the behavior of the schedule runnable.
+    runnableCaptor.getValue().run();
+    verify(mockExportSink).execute(statReadValueMapCaptor.capture());
+    // Verify stat reading behavior.
+    assertEquals(MOCK_STAT_READ_VALUE, statReadValueMapCaptor.getValue().get(MOCK_STAT_NAME));
+  }
+
+  @Test
+  public void testStartMethodShutdownRegistryFinalSampleAndExport() {
+    numericStatExporter.start(mockShutdownRegistry);
+
+    // Verify the shutdown registry is called.
+    verify(mockShutdownRegistry).addAction(commandCaptor.capture());
+    // Verify the behavior of the shutdown registry command.
+    commandCaptor.getValue().execute();
+
+    // The shutdown command calls stop(), which we'll test separately.
+
+    // Now verifies the final sample and export behavior.
+    verify(mockExportSink).execute(statReadValueMapCaptor.capture());
+    // Verify stat sampling and reading behavior.
+    assertEquals(MOCK_STAT_SAMPLED_VALUE, statReadValueMapCaptor.getValue().get(MOCK_STAT_NAME));
+  }
+
+  @Test
+  public void testStopMethodAwaitTerminationReturnsFast() throws Exception {
+    when(mockExecutor.awaitTermination(anyLong(), Matchers.<TimeUnit>anyObject()))
+        .thenReturn(true);
+    numericStatExporter.stop();
+    verify(mockExecutor).awaitTermination(eq(1L), eq(TimeUnit.SECONDS));
+    verifyNoMoreInteractions(mockExecutor);
+  }
+
+  @Test
+  public void testStopMethodAwaitTerminationReturnsSlowly() throws Exception {
+    when(mockExecutor.awaitTermination(anyLong(), Matchers.<TimeUnit>anyObject()))
+        .thenReturn(false);
+    numericStatExporter.stop();
+    verify(mockExecutor, times(2)).awaitTermination(eq(1L), eq(TimeUnit.SECONDS));
+    verify(mockExecutor).shutdownNow();
+    verifyNoMoreInteractions(mockExecutor);
+  }
+
+  @Test
+  public void testStopMethodAwaitTerminationInterrupted() throws Exception {
+    when(mockExecutor.awaitTermination(anyLong(), Matchers.<TimeUnit>anyObject()))
+        .thenThrow(new InterruptedException("mock failure"));
+    numericStatExporter.stop();
+    verify(mockExecutor).awaitTermination(eq(1L), eq(TimeUnit.SECONDS));
+    verify(mockExecutor).shutdownNow();
+    verifyNoMoreInteractions(mockExecutor);
+    // We need to reset the thread's interrupt flag so other tests who uses certain
+    // concurrent calls like latches and various waits wouldn't fail.
+    Thread.currentThread().interrupted();
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/PercentileTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/PercentileTest.java b/commons/src/test/java/com/twitter/common/stats/PercentileTest.java
new file mode 100644
index 0000000..e3b7efe
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/PercentileTest.java
@@ -0,0 +1,207 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import com.google.common.collect.Lists;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+
+/**
+ * @author William Farner
+ */
+public class PercentileTest {
+
+  private static final double EPSILON = 1e-6;
+  private static final float SAMPLE_RATE = 100;
+  private static final double[] PERCENTILES = new double[] {0, 10, 50, 90, 99, 99.9, 99.99, 100};
+
+  private Percentile<Integer> percentiles;
+
+  @Before
+  public void setUp() {
+    percentiles = new Percentile<Integer>("test", SAMPLE_RATE, PERCENTILES);
+  }
+
+  @Test
+  public void testNoData() {
+    checkPercentiles(percentiles, 0, 0, 0, 0, 0, 0, 0, 0);
+    checkValuesAreFlushed(percentiles);
+  }
+
+  @Test
+  public void testSingleValue() {
+    percentiles.record(10);
+    checkPercentiles(percentiles, 10, 10, 10, 10, 10, 10, 10, 10);
+    checkValuesAreFlushed(percentiles);
+  }
+
+  @Test
+  public void testConstant() {
+    for (int i = 0; i < 100; i++) {
+      percentiles.record(10);
+    }
+
+    checkPercentiles(percentiles, 10, 10, 10, 10, 10, 10, 10, 10);
+    checkValuesAreFlushed(percentiles);
+  }
+
+  @Test
+  public void testLinear() {
+    for (int i = 0; i < 10001; i++) {
+      percentiles.record(i);
+    }
+
+    checkPercentiles(percentiles, 0, 1000, 5000, 9000, 9900, 9990, 9999, 10000);
+    checkValuesAreFlushed(percentiles);
+  }
+
+  @Test
+  public void testMultipleSampleWindows() {
+    Percentile<Integer> mypercentile = new Percentile<Integer>("test", 2, null, PERCENTILES);
+
+    for (int i = 0; i < 10000; i++) {
+      mypercentile.record(i);
+    }
+    // Large number filler so that our percentile hit an integer index.
+    mypercentile.record(90000);
+    checkPercentiles(mypercentile, 0, 1000, 5000, 9000, 9900, 9990, 9999, 90000);
+
+    for (int i = 10000; i < 20000; i++) {
+      mypercentile.record(i);
+    }
+    checkPercentiles(mypercentile, 0, 2000, 10000, 18000, 19800, 19980, 19998, 90000);
+
+    for (int i = 20000; i < 30000; i++) {
+      mypercentile.record(i);
+    }
+    // Previous filler is flushed from the sample queue. Refill.
+    mypercentile.record(90000);
+    checkPercentiles(mypercentile, 10000, 12000, 20000, 28000, 29800, 29980, 29998, 90000);
+  }
+
+
+  @Test
+  public void testNullSampler() {
+    int N = 10001;
+    Percentile<Integer> mypercentile = new Percentile<Integer>("test", 1, null, PERCENTILES);
+    for (int i = 0; i < N; i++) {
+      mypercentile.record(i);
+    }
+    assertThat(mypercentile.samples.size(), is(N));
+    checkPercentiles(mypercentile, 0, 1000, 5000, 9000, 9900, 9990, 9999, 10000);
+    checkValuesAreFlushed(mypercentile);
+  }
+
+  @Test
+  public void testReverseLinear() {
+    for (int i = 0; i < 10001; i++) {
+      percentiles.record(i);
+    }
+
+    checkPercentiles(percentiles, 0, 1000, 5000, 9000, 9900, 9990, 9999, 10000);
+    checkValuesAreFlushed(percentiles);
+  }
+
+  @Test
+  public void testShuffledSteps() {
+    List<Integer> values = Lists.newArrayList();
+    for (int i = 0; i < 1000; i++) {
+      for (int j = 0; j < 10; j++) {
+        values.add(i);
+      }
+    }
+    values.add(2000);
+    Collections.shuffle(values);
+    for (int sample : values) {
+      percentiles.record(sample);
+    }
+
+    checkPercentiles(percentiles, 0, 100, 500, 900, 990, 999, 999, 2000);
+    checkValuesAreFlushed(percentiles);
+  }
+
+  @Test
+  public void testNegativeValues() {
+    List<Integer> values = Lists.newArrayList();
+    for (int i = 0; i < 1000; i++) {
+      for (int j = 0; j < 10; j++) {
+        values.add(-1 * i);
+      }
+    }
+    values.add(-2000);
+    Collections.shuffle(values);
+    for (int sample : values) {
+      percentiles.record(sample);
+    }
+
+    checkPercentiles(percentiles, -2000, -900, -500, -100, -10, -1, 0, 0);
+    checkValuesAreFlushed(percentiles);
+  }
+
+  @Test
+  public void testPercentileInterpolates() {
+    for (int i = 0; i < 9999; i++) {
+      percentiles.record(i);
+    }
+
+    checkPercentiles(percentiles, 0, 999.8, 4999, 8998.2, 9898.02, 9988.002, 9997.0002, 9998);
+    checkValuesAreFlushed(percentiles);
+  }
+
+  @Test
+  public void testHonorsBufferLimit() {
+    for (int i = 0; i < 1000; i++) {
+      percentiles.record(0);
+    }
+
+    // Now fill the buffer with a constant.
+    for (int i = 0; i < Percentile.MAX_BUFFER_SIZE; i++) {
+      percentiles.record(1);
+    }
+
+    assertThat(percentiles.samples.size(), is(Percentile.MAX_BUFFER_SIZE));
+    checkPercentiles(percentiles, 1, 1, 1, 1, 1, 1, 1, 1);
+    checkValuesAreFlushed(percentiles);
+  }
+
+  private void checkPercentiles(Percentile<Integer> input_percentiles, double... values) {
+    assertThat(values.length, is(PERCENTILES.length));
+
+    for (int i = 0; i < values.length; i++) {
+      checkPercentile(input_percentiles, PERCENTILES[i], values[i]);
+    }
+  }
+
+  private void checkValuesAreFlushed(Percentile<Integer> input_percentiles, double... values) {
+    // Check that the values were flushed.
+    for (int i = 0; i < values.length; i++) {
+      checkPercentile(input_percentiles, PERCENTILES[i], 0);
+    }
+    assertThat(percentiles.samples.isEmpty(), is(true));
+  }
+
+  private void checkPercentile(Percentile<Integer> input_percentiles,
+                               double percentile, double value) {
+    assertEquals(value, input_percentiles.getPercentile(percentile).sample(), EPSILON);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/PipelineStatsTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/PipelineStatsTest.java b/commons/src/test/java/com/twitter/common/stats/PipelineStatsTest.java
new file mode 100644
index 0000000..9656119
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/PipelineStatsTest.java
@@ -0,0 +1,150 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import com.google.common.collect.Sets;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.util.Clock;
+import com.twitter.common.util.testing.FakeClock;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests the PipelineStats class.
+ *
+ * @author William Farner
+ */
+public class PipelineStatsTest {
+
+  private Clock clock = new FakeClock();
+  private PipelineStats stats;
+
+  @Before
+  public void setUp() {
+    stats = new PipelineStats("test", Sets.newHashSet("a", "b", "c"), clock, Time.MILLISECONDS);
+  }
+
+  @Test
+  public void testEmptyFlow() {
+    PipelineStats.Snapshot pipeline = stats.newSnapshot();
+    pipeline.end();
+
+    checkSample("a", 0, 0);
+    checkSample("b", 0, 0);
+    checkSample("c", 0, 0);
+    checkSample("full", 1, 0);
+  }
+
+  @Test
+  public void testSimpleFlow() throws Exception {
+    PipelineStats.Snapshot pipeline = stats.newSnapshot();
+    pipeline.start("a");
+    clock.waitFor(10);
+    pipeline.start("b");
+    clock.waitFor(20);
+    pipeline.start("c");
+    clock.waitFor(30);
+    pipeline.end();
+
+    checkSample("a", 1, 10);
+    checkSample("b", 1, 20);
+    checkSample("c", 1, 30);
+    checkSample("full", 1, 60);
+  }
+
+  @Test
+  public void testEarlyExit() throws Exception {
+    PipelineStats.Snapshot pipeline = stats.newSnapshot();
+    pipeline.start("a");
+    clock.waitFor(10);
+    pipeline.start("b");
+    clock.waitFor(20);
+    pipeline.end();
+
+    checkSample("a", 1, 10);
+    checkSample("b", 1, 20);
+    checkSample("full", 1, 30);
+  }
+
+  @Test
+  public void testDuplicatedStages() throws Exception {
+    PipelineStats.Snapshot pipeline = stats.newSnapshot();
+    pipeline.start("a");
+    clock.waitFor(10);
+    pipeline.start("b");
+    clock.waitFor(20);
+    pipeline.start("b");
+    clock.waitFor(10);
+    pipeline.start("b");
+    clock.waitFor(50);
+    pipeline.start("c");
+    clock.waitFor(30);
+    pipeline.start("c");
+    clock.waitFor(70);
+    pipeline.end();
+
+    checkSample("a", 1, 10);
+    checkSample("b", 3, 80);
+    checkSample("c", 2, 100);
+    checkSample("full", 1, 190);
+  }
+
+  @Test
+  public void testSimultaneousSnapshots() throws Exception {
+    PipelineStats.Snapshot pipeline1 = stats.newSnapshot();
+    PipelineStats.Snapshot pipeline2 = stats.newSnapshot();
+    pipeline1.start("a");
+    clock.waitFor(10);
+    pipeline2.start("a");
+    pipeline1.start("b");
+    clock.waitFor(20);
+    pipeline2.start("b");
+    clock.waitFor(10);
+    pipeline2.start("c");
+    clock.waitFor(10);
+    pipeline2.end();
+
+    // Only pipeline2 was recorded, so we should not see pipeline1 in the time series yet.
+    checkSample("a", 1, 20);
+    checkSample("b", 1, 10);
+    checkSample("c", 1, 10);
+    checkSample("full", 1, 40);
+
+    pipeline1.start("c");
+    clock.waitFor(30);
+    pipeline1.end();
+
+    // The current sample will now be the sum of pipeline1 and pipeline2.
+    checkSample("a", 2, 30);
+    checkSample("b", 2, 50);
+    checkSample("c", 2, 40);
+    checkSample("full", 2, 120);
+  }
+
+  private void checkSample(String stage, long events, long latency) {
+    AtomicLong eventsCounter = stats.getStatsForStage(stage).getEventCounter();
+    AtomicLong latencyCounter = stats.getStatsForStage(stage).getTotalCounter();
+
+    assertThat(eventsCounter.get(), is(events));
+    assertThat(latencyCounter.get(), is(latency));
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/PrintableHistogramTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/PrintableHistogramTest.java b/commons/src/test/java/com/twitter/common/stats/PrintableHistogramTest.java
new file mode 100644
index 0000000..b1c024d
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/PrintableHistogramTest.java
@@ -0,0 +1,30 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import junit.framework.TestCase;
+
+public class PrintableHistogramTest extends TestCase {
+
+  public void testPrintHistogram() {
+    PrintableHistogram hist = new PrintableHistogram(10, 20, 30, 40, 50, 60, 70, 80, 90, 100);
+    for (int i = 10; i > 0; i--) {
+      hist.addValue(i * 10, 10 - i);
+    }
+    System.out.println(hist);
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/RateTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/RateTest.java b/commons/src/test/java/com/twitter/common/stats/RateTest.java
new file mode 100644
index 0000000..9c5058a
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/RateTest.java
@@ -0,0 +1,242 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import com.twitter.common.base.Supplier;
+import com.twitter.common.util.testing.FakeTicker;
+import org.easymock.IMocksControl;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.util.testing.FakeTicker;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+
+/**
+ * @author William Farner
+ */
+public class RateTest {
+
+  private static final int ONE_SEC = 1000000000;
+  private static final double EPSILON = 1E-6;
+
+  private IMocksControl control;
+  private FakeTicker ticker;
+
+  private Stat<Integer> input;
+
+  @Before
+  @SuppressWarnings("unchecked")
+  public void setUp() {
+    control = createControl();
+
+    ticker = new FakeTicker();
+    input = control.createMock(Stat.class);
+  }
+
+  @After
+  public void verify() {
+    Stats.flush();
+    control.verify();
+  }
+
+  @Test
+  public void testInputsRegistered() {
+    expect(input.getName()).andReturn("test");
+    expectLastCall().atLeastOnce();
+
+    control.replay();
+
+    Rate.of(input);
+    assertNotNull(Stats.getVariable("test"));
+  }
+
+  @Test
+  public void testNoHistory() throws Exception {
+    expectCalls(0);
+
+    control.replay();
+
+    assertResults();
+  }
+
+  @Test
+  public void testFlat() throws Exception {
+    expectCalls(10, 10);
+
+    control.replay();
+
+    assertResults(0);
+  }
+
+  @Test
+  public void testFixedRate() throws Exception {
+    expectCalls(10, 20, 30, 40);
+
+    control.replay();
+
+    assertResults(10, 10, 10);
+  }
+
+  @Test
+  public void testVariableRate() throws Exception {
+    expectCalls(10, 20, 50, 150);
+
+    control.replay();
+
+    assertResults(10, 30, 100);
+  }
+
+  @Test
+  public void testVariableRateAtomicLong() throws Exception {
+    AtomicLong value = new AtomicLong();
+    Rate<AtomicLong> rate = Rate.of("test", value).withTicker(ticker).build();
+
+    ticker.waitNanos(ONE_SEC);
+    value.set(10);
+    assertEquals(0d, rate.sample(), EPSILON);
+
+    ticker.waitNanos(ONE_SEC);
+    value.set(20);
+    assertEquals(10d, rate.sample(), EPSILON);
+
+    ticker.waitNanos(ONE_SEC);
+    value.set(50);
+    assertEquals(30d, rate.sample(), EPSILON);
+
+    ticker.waitNanos(ONE_SEC);
+    value.set(100);
+    assertEquals(50d, rate.sample(), EPSILON);
+
+    control.replay();
+  }
+
+  @Test
+  public void testNegativeRate() throws Exception {
+    expectCalls(40, 30, 20, 10);
+
+    control.replay();
+
+    assertResults(-10, -10, -10);
+  }
+
+  @Test
+  public void testZeroDelta() throws Exception {
+    expectCalls(10, 10, 10);
+
+    control.replay();
+
+    assertResults(0, 0);
+  }
+
+  @Test
+  public void testLongWindow() throws Exception {
+    expectCalls(10, 10, 0, 10, 10);
+
+    control.replay();
+
+    assertResults(Rate.of(input).withWindowSize(3).withTicker(ticker).build(), 0, -5, 0, 0);
+  }
+
+  @Test
+  public void testRateOfRate() throws Exception {
+    expectCalls(10, 20, 30, 40, 50, 60);
+
+    control.replay();
+
+    Rate<Integer> rate = Rate.of(input).withTicker(ticker).build();
+    Rate<Double> rateOfRate = Rate.of(rate).withTicker(ticker).build();
+
+    assertThat(rate.sample(), is(0d));
+    assertThat(rateOfRate.sample(), is(0d));
+    ticker.waitNanos(ONE_SEC);
+    assertThat(rate.sample(), is(10d));
+    assertThat(rateOfRate.sample(), is(10d));
+    ticker.waitNanos(ONE_SEC);
+    assertThat(rate.sample(), is(10d));
+    assertThat(rateOfRate.sample(), is(0d));
+    ticker.waitNanos(ONE_SEC);
+    assertThat(rate.sample(), is(10d));
+    assertThat(rateOfRate.sample(), is(0d));
+    ticker.waitNanos(ONE_SEC);
+    assertThat(rate.sample(), is(10d));
+    assertThat(rateOfRate.sample(), is(0d));
+    ticker.waitNanos(ONE_SEC);
+    assertThat(rate.sample(), is(10d));
+    assertThat(rateOfRate.sample(), is(0d));
+  }
+
+  @Test
+  public void testScaleFactor() throws Exception {
+    expectCalls(10, 20, 30, 40);
+
+    control.replay();
+
+    assertResults(Rate.of(input).withTicker(ticker).withScaleFactor(10).build(), 100, 100, 100);
+  }
+
+  @Test
+  public void testFractionalScaleFactor() throws Exception {
+    expectCalls(10, 20, 30, 40);
+
+    control.replay();
+
+    assertResults(Rate.of(input).withTicker(ticker).withScaleFactor(0.1).build(), 1, 1, 1);
+  }
+
+  @Test
+  public void testSupplier() throws Exception {
+    Supplier<Long> supplier = new Supplier<Long>() {
+      long value = 0;
+      @Override public Long get() {
+        value += 10;
+        return value;
+      }
+    };
+
+    control.replay();
+    assertResults(Rate.of("test", supplier).withTicker(ticker).build(), 10, 10, 10);
+  }
+
+  private void expectCalls(int... samples) {
+    expect(input.getName()).andReturn("test");
+    expectLastCall().atLeastOnce();
+    for (int sample : samples) {
+      expect(input.read()).andReturn(sample);
+    }
+  }
+
+  private void assertResults(double... results) throws Exception {
+    assertResults(Rate.of(input).withTicker(ticker).build(), results);
+  }
+
+  private void assertResults(Rate rate, double... results) throws Exception {
+    // First result is always zero.
+    assertEquals(0d, rate.sample().doubleValue(), EPSILON);
+    ticker.waitNanos(ONE_SEC);
+
+    for (double result : results) {
+      assertEquals(result, rate.sample().doubleValue(), EPSILON);
+      ticker.waitNanos(ONE_SEC);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/ReservoirSamplerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/ReservoirSamplerTest.java b/commons/src/test/java/com/twitter/common/stats/ReservoirSamplerTest.java
new file mode 100644
index 0000000..d2e1f8c
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/ReservoirSamplerTest.java
@@ -0,0 +1,63 @@
+package com.twitter.common.stats;
+
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import com.twitter.common.testing.easymock.EasyMockTest;
+import com.twitter.common.util.Random;
+
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests the Reservoir Sampler code
+ *
+ * @author Delip Rao
+ */
+public class ReservoirSamplerTest extends EasyMockTest {
+
+  private Random random;
+
+  @Before
+  public void setUp() throws Exception {
+    random = createMock(Random.class);
+  }
+
+  @Test
+  public void testSampling() throws Exception {
+    int mockValues[] = {3, 4, 5, 6, 7};
+    for (int value : mockValues) {
+      expect(random.nextInt(value + 1)).andReturn(value);
+    }
+    control.replay();
+
+    ReservoirSampler<Integer> sampler = new ReservoirSampler<Integer>(3, random);
+    List<Integer> stream = ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8);
+    for (int i : stream) {
+      sampler.sample(i);
+    }
+    List<Integer> expectedSamples = ImmutableList.of(1, 2, 3);
+    assertEquals("The samples should be 1, 2, 3", expectedSamples,
+        ImmutableList.copyOf(sampler.getSamples()));
+  }
+
+  @Test
+  public void testNoSampling() throws Exception {
+    // no calls to random.nextInt should happen in this test
+    control.replay();
+    List<Integer> stream = ImmutableList.of(1, 2, 3);
+    // reservoir is larger than the stream. No sampling should happen here.
+    ReservoirSampler<Integer> sampler = new ReservoirSampler<Integer>(20);
+    for (int i : stream) {
+      sampler.sample(i);
+    }
+    assertEquals("The samples should be same as the stream", stream,
+        ImmutableList.copyOf(sampler.getSamples()));
+  }
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/StatisticsTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/StatisticsTest.java b/commons/src/test/java/com/twitter/common/stats/StatisticsTest.java
new file mode 100644
index 0000000..0f6adf5
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/StatisticsTest.java
@@ -0,0 +1,119 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Tests the functionality of the Statistics class.
+ *
+ * @author William Farner
+ */
+public class StatisticsTest extends TestCase {
+  private static final double ERROR_THRESHOLD = 1e-10;
+
+  private static final List<Integer> EMPTY_SET = ImmutableList.of();
+
+  private static final List<Integer> TEST_SET_A = Arrays.asList(76117373, 76167137, 75870125, 75880508, 78099974,
+                                       77810738, 75763975, 78042301, 76109165, 77816921,
+                                       76115544, 76075750, 75391297, 75597249, 77793835,
+                                       76001118, 77752542, 78413670, 60351776, 75607235,
+                                       76057629, 80011920, 24067379, 75767484, 80052983,
+                                       79278613, 75600277);
+
+  private Statistics createAndLoad(List<Integer> values) {
+    Statistics stats = new Statistics();
+    for (long value : values) {
+      stats.accumulate(value);
+    }
+
+    return stats;
+  }
+
+  private void checkWithinThreshold(double expected, double actual) {
+    assertTrue(Math.abs(expected - actual) < ERROR_THRESHOLD);
+  }
+
+  public void testMin() {
+    // min is undefined for an empty set, but it should not fail.
+    Statistics stats = createAndLoad(EMPTY_SET);
+    stats.min();
+
+    stats = createAndLoad(TEST_SET_A);
+    assertEquals(24067379, stats.min());
+  }
+
+  public void testMax() {
+    // max is undefined for an empty set, but it should not fail.
+    Statistics stats = createAndLoad(EMPTY_SET);
+    stats.max();
+
+    stats = createAndLoad(TEST_SET_A);
+    assertEquals(80052983, stats.max());
+  }
+
+  public void testMean() {
+    // mean is undefined for an empty set, but it should not fail.
+    Statistics stats = createAndLoad(EMPTY_SET);
+    stats.mean();
+
+    stats = createAndLoad(TEST_SET_A);
+    checkWithinThreshold(7.435609325925925E7, stats.mean());
+  }
+
+  public void testVariance() {
+    Statistics stats = createAndLoad(EMPTY_SET);
+    assertEquals(Double.NaN, stats.variance());
+
+    stats = createAndLoad(TEST_SET_A);
+    checkWithinThreshold(1.089077613763465E14, stats.variance());
+  }
+
+  public void testStandardDeviation() {
+    Statistics stats = createAndLoad(EMPTY_SET);
+    assertEquals(Double.NaN, stats.standardDeviation());
+
+    stats = createAndLoad(TEST_SET_A);
+    checkWithinThreshold(1.0435888145066835E7, stats.standardDeviation());
+  }
+
+  public void testPopulationSize() {
+    Statistics stats = createAndLoad(EMPTY_SET);
+    assertEquals(0L, stats.populationSize());
+
+    stats = createAndLoad(TEST_SET_A);
+    assertEquals(TEST_SET_A.size(), stats.populationSize());
+  }
+
+  public void testSum() {
+    Statistics stats = createAndLoad(EMPTY_SET);
+    assertEquals(0L, stats.sum());
+
+    stats = createAndLoad(TEST_SET_A);
+    long expectedSum = 0;
+    for (long x: TEST_SET_A) {
+      expectedSum += x;
+    }
+    assertEquals(expectedSum, stats.sum());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/StatsTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/StatsTest.java b/commons/src/test/java/com/twitter/common/stats/StatsTest.java
new file mode 100644
index 0000000..4cf7bab
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/StatsTest.java
@@ -0,0 +1,113 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.google.common.util.concurrent.AtomicDouble;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author William Farner
+ */
+public class StatsTest {
+
+  @After
+  public void tearDown() {
+    Stats.flush();
+  }
+
+  @Test
+  public void testSimpleExport() {
+    AtomicLong var = Stats.exportLong("test_long");
+    assertCounter("test_long", 0);
+    var.incrementAndGet();
+    assertCounter("test_long", 1);
+    var.addAndGet(100);
+    assertCounter("test_long", 101);
+  }
+
+  @Test
+  public void testDoubleExport() {
+    AtomicDouble var = Stats.exportDouble("test_double");
+    assertCounter("test_double", 0.0);
+    var.addAndGet(1.1);
+    assertCounter("test_double", 1.1);
+    var.addAndGet(5.55);
+    assertCounter("test_double", 6.65);
+  }
+
+  @Test
+  public void testNotSame() {
+    AtomicLong firstExport = Stats.exportLong("somevar");
+    firstExport.incrementAndGet();
+    firstExport.incrementAndGet();
+    assertCounter("somevar", 2L);
+    AtomicLong secondExport = Stats.exportLong("somevar");
+    assertNotSame(firstExport, secondExport);
+    secondExport.incrementAndGet();
+    assertCounter("somevar", 2L); // We keep the first one!
+  }
+
+  @Test
+  public void testNormalizesSpace() {
+    AtomicLong leading = Stats.exportLong("  leading space");
+    AtomicLong trailing = Stats.exportLong("trailing space   ");
+    AtomicLong surround = Stats.exportLong("   surround space   ");
+
+    leading.incrementAndGet();
+    trailing.incrementAndGet();
+    surround.incrementAndGet();
+    assertCounter("__leading_space", 1);
+    assertCounter("trailing_space___", 1);
+    assertCounter("___surround_space___", 1);
+  }
+
+  @Test
+  public void testNormalizesIllegalChars() {
+    AtomicLong colon = Stats.exportLong("a:b");
+    AtomicLong plus = Stats.exportLong("b+c");
+    AtomicLong hyphen = Stats.exportLong("c-d");
+    AtomicLong slash = Stats.exportLong("d/f");
+
+    colon.incrementAndGet();
+    plus.incrementAndGet();
+    hyphen.incrementAndGet();
+    slash.incrementAndGet();
+    assertCounter("a_b", 1);
+    assertCounter("b_c", 1);
+    assertCounter("c_d", 1);
+    assertCounter("d_f", 1);
+  }
+
+  private void assertCounter(String name, long value) {
+    assertThat(Stats.<Long>getVariable(name).read(), is(value));
+  }
+
+  private void assertCounter(String name, double value) {
+    Double var = (Double) Stats.getVariable(name).read();
+    assertEquals(var, value, 1e-6);
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/stats/TimeSeriesRepositoryImplTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/stats/TimeSeriesRepositoryImplTest.java b/commons/src/test/java/com/twitter/common/stats/TimeSeriesRepositoryImplTest.java
new file mode 100644
index 0000000..56e0bdf
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/stats/TimeSeriesRepositoryImplTest.java
@@ -0,0 +1,132 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
+
+import com.google.common.collect.ImmutableList;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.testing.easymock.EasyMockTest;
+import com.twitter.common.util.testing.FakeClock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.createStrictControl;
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author William Farner
+ */
+public class TimeSeriesRepositoryImplTest extends EasyMockTest {
+
+  private static final Amount<Long, Time> RETENTION_PERIOD = Amount.of(10L, Time.MINUTES);
+  private static final Amount<Long, Time> SAMPLE_PERIOD = Amount.of(1L, Time.SECONDS);
+
+  private StatRegistry statRegistry;
+  private TimeSeriesRepositoryImpl repo;
+  private FakeClock clock;
+
+  @Before
+  public void setUp() {
+    control = createStrictControl();
+    statRegistry = control.createMock(StatRegistry.class);
+    repo = new TimeSeriesRepositoryImpl(statRegistry, SAMPLE_PERIOD, RETENTION_PERIOD);
+    clock = new FakeClock();
+  }
+
+  @After
+  public void after() {
+    control.verify();
+  }
+
+  @Test
+  public void testSamplesInOrder() {
+    RecordingStat<Integer> statA = mockedStat();
+    RecordingStat<Integer> statB = mockedStat();
+    RecordingStat<Integer> statC = mockedStat();
+    RecordingStat<Integer> statD = mockedStat();
+
+    expect(statRegistry.getStats())
+        .andReturn(ImmutableList.<RecordingStat<? extends Number>>of(statB, statA, statC, statD));
+
+    expect(statB.getName()).andReturn("statB");
+    expect(statB.sample()).andReturn(1);
+
+    expect(statA.getName()).andReturn("statA");
+    expect(statA.sample()).andReturn(2);
+
+    expect(statC.getName()).andReturn("statC");
+    expect(statC.sample()).andReturn(3);
+
+    expect(statD.getName()).andReturn("statD");
+    expect(statD.sample()).andReturn(4);
+
+    control.replay();
+    repo.runSampler(clock);
+  }
+
+  @Test
+  public void testDelayedExport() throws InterruptedException {
+    RecordingStat<Integer> earlyExport = mockedStat();
+
+    for (int i = 1; i <= 4; i++) {
+      expect(statRegistry.getStats())
+        .andReturn(ImmutableList.<RecordingStat<? extends Number>>of(earlyExport));
+      expect(earlyExport.getName()).andReturn("early");
+      expect(earlyExport.sample()).andReturn(i * 2);
+    }
+
+    RecordingStat<Integer> delayedExport = mockedStat();
+    expect(statRegistry.getStats())
+        .andReturn(ImmutableList.<RecordingStat<? extends Number>>of(earlyExport, delayedExport));
+    expect(earlyExport.getName()).andReturn("early");
+    expect(earlyExport.sample()).andReturn(10);
+    expect(delayedExport.getName()).andReturn("delayed");
+    expect(delayedExport.sample()).andReturn(100);
+
+    control.replay();
+
+    clock.setNowMillis(1000);
+
+    for (int i = 0; i < 4; i++) {
+      repo.runSampler(clock);
+      clock.waitFor(1000);
+    }
+
+    expectTimestamps(1000L, 2000L, 3000L, 4000L);
+    expectSeriesData("early", 2, 4, 6, 8);
+
+    repo.runSampler(clock);
+
+    expectTimestamps(1000L, 2000L, 3000L, 4000L, 5000L);
+    expectSeriesData("early", 2, 4, 6, 8, 10);
+    expectSeriesData("delayed", 0L, 0L, 0L, 0L, 100);
+  }
+
+  private RecordingStat<Integer> mockedStat() {
+    return createMock(new Clazz<RecordingStat<Integer>>() { });
+  }
+
+  private void expectTimestamps(Number... timestamps) {
+    assertEquals(ImmutableList.copyOf(timestamps), ImmutableList.copyOf(repo.getTimestamps()));
+  }
+
+  private void expectSeriesData(String series, Number... values) {
+    assertEquals(ImmutableList.copyOf(values), ImmutableList.copyOf(repo.get(series).getSamples()));
+  }
+}


Mime
View raw message