BucketTimerTest.java

/**
 * Copyright 2013 Netflix, Inc.
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * 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.netflix.servo.monitor;


import com.netflix.servo.util.UnmodifiableSet;
import org.testng.annotations.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotEquals;

public class BucketTimerTest extends AbstractMonitorTest<BucketTimer> {

  @Override
  public BucketTimer newInstance(String name) {
    return new BucketTimer(
        MonitorConfig.builder(name).build(),
        new BucketConfig.Builder().withBuckets(new long[]{10L, 20L}).build()
    );
  }

  @Test
  public void testRecord() throws Exception {
    BucketTimer c = newInstance("foo");
    Map<String, Number> expectedValues;

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 0L);
    expectedValues.put("min", 0L);
    expectedValues.put("max", 0L);
    expectedValues.put("bucket=10ms", 0L);
    expectedValues.put("bucket=20ms", 0L);
    expectedValues.put("bucket=overflow", 0L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 0L);

    c.record(40);

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 40L);
    expectedValues.put("min", 40L);
    expectedValues.put("max", 40L);
    expectedValues.put("bucket=10ms", 0L);
    expectedValues.put("bucket=20ms", 0L);
    expectedValues.put("bucket=overflow", 1L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 1L);

    c.record(10);

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 50L);
    expectedValues.put("min", 10L);
    expectedValues.put("max", 40L);
    expectedValues.put("bucket=10ms", 1L);
    expectedValues.put("bucket=20ms", 0L);
    expectedValues.put("bucket=overflow", 1L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 2L);

    c.record(5);

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 55L);
    expectedValues.put("min", 5L);
    expectedValues.put("max", 40L);
    expectedValues.put("bucket=10ms", 2L);
    expectedValues.put("bucket=20ms", 0L);
    expectedValues.put("bucket=overflow", 1L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 3L);

    c.record(20);

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 75L);
    expectedValues.put("min", 5L);
    expectedValues.put("max", 40L);
    expectedValues.put("bucket=10ms", 2L);
    expectedValues.put("bucket=20ms", 1L);
    expectedValues.put("bucket=overflow", 1L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 4L);

    c.record(125);

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 200L);
    expectedValues.put("min", 5L);
    expectedValues.put("max", 125L);
    expectedValues.put("bucket=10ms", 2L);
    expectedValues.put("bucket=20ms", 1L);
    expectedValues.put("bucket=overflow", 2L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 5L);
  }

  @Test
  public void testRecordDifferentUnits() throws Exception {
    BucketTimer c = new BucketTimer(
        MonitorConfig.builder("foo").build(),
        new BucketConfig.Builder().withBuckets(new long[]{10000L, 20000L}).withTimeUnit(TimeUnit.NANOSECONDS).build(),
        TimeUnit.MICROSECONDS
    );;
    Map<String, Number> expectedValues;

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 0L);
    expectedValues.put("min", 0L);
    expectedValues.put("max", 0L);
    expectedValues.put("bucket=10000ns", 0L);
    expectedValues.put("bucket=20000ns", 0L);
    expectedValues.put("bucket=overflow", 0L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 0L);

    c.record(40);

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 40L);
    expectedValues.put("min", 40L);
    expectedValues.put("max", 40L);
    expectedValues.put("bucket=10000ns", 0L);
    expectedValues.put("bucket=20000ns", 0L);
    expectedValues.put("bucket=overflow", 1L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 1L);

    c.record(10);

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 50L);
    expectedValues.put("min", 10L);
    expectedValues.put("max", 40L);
    expectedValues.put("bucket=10000ns", 1L);
    expectedValues.put("bucket=20000ns", 0L);
    expectedValues.put("bucket=overflow", 1L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 2L);

    c.record(5);

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 55L);
    expectedValues.put("min", 5L);
    expectedValues.put("max", 40L);
    expectedValues.put("bucket=10000ns", 2L);
    expectedValues.put("bucket=20000ns", 0L);
    expectedValues.put("bucket=overflow", 1L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 3L);

    c.record(20);

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 75L);
    expectedValues.put("min", 5L);
    expectedValues.put("max", 40L);
    expectedValues.put("bucket=10000ns", 2L);
    expectedValues.put("bucket=20000ns", 1L);
    expectedValues.put("bucket=overflow", 1L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 4L);

    c.record(125);

    expectedValues = new HashMap<>();
    expectedValues.put("totalTime", 200L);
    expectedValues.put("min", 5L);
    expectedValues.put("max", 125L);
    expectedValues.put("bucket=10000ns", 2L);
    expectedValues.put("bucket=20000ns", 1L);
    expectedValues.put("bucket=overflow", 2L);
    assertMonitors(c.getMonitors(), expectedValues);
    assertEquals(c.getCount(0).longValue(), 5L);
  }

  private void assertMonitors(List<Monitor<?>> monitors, Map<String, Number> expectedValues) {
    Set<String> exclude = UnmodifiableSet.of("count", "min", "max");
    String[] namespaces = new String[]{"statistic", "servo.bucket"};
    for (Monitor<?> monitor : monitors) {
      for (String namespace : namespaces) {
        final String tag = monitor.getConfig().getTags().getValue(namespace);
        if (tag != null && !exclude.contains(tag)) {
          final Number actual = (Number) monitor.getValue();
          final Number expected = expectedValues.get(tag);
          assertEquals(actual, expected, namespace + "." + tag);
        }
      }
    }
  }

  @Test
  public void testEqualsCount() throws Exception {
    BucketTimer c1 = newInstance("foo");
    BucketTimer c2 = newInstance("foo");
    assertEquals(c1, c2);

    c1.record(42);
    assertNotEquals(c1, c2);
    c2.record(42);
    assertEquals(c1, c2);

    c1.record(11);
    assertNotEquals(c1, c2);
    c2.record(11);
    assertEquals(c1, c2);
  }

  @Test
  public void testHashCode() throws Exception {
    BucketTimer c1 = newInstance("foo");
    BucketTimer c2 = newInstance("foo");
    assertEquals(c1.hashCode(), c2.hashCode());

    c1.record(42);
    assertNotEquals(c1.hashCode(), c2.hashCode());
    c2.record(42);
    assertEquals(c1.hashCode(), c2.hashCode());

    c1.record(11);
    assertNotEquals(c1.hashCode(), c2.hashCode());
    c2.record(11);
    assertEquals(c1.hashCode(), c2.hashCode());
  }
}