StepCounterTest.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.annotations.DataSourceType;
import com.netflix.servo.util.ManualClock;
import org.testng.annotations.Test;

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

public class StepCounterTest {

  private static final double DELTA = 1e-06;
  private final ManualClock clock = new ManualClock(50 * Pollers.POLLING_INTERVALS[1]);

  public StepCounter newInstance(String name) {
    return new StepCounter(MonitorConfig.builder(name).build(), clock);
  }

  private long time(long t) {
    return t * 1000 + Pollers.POLLING_INTERVALS[1];
  }

  @Test
  public void testSimpleTransition() {
    long start = time(1);
    clock.set(start);
    StepCounter c = newInstance("c");
    assertEquals(c.getValue(1).doubleValue(), 0.0, DELTA);
    assertEquals(c.getCurrentCount(1), 0L);

    clock.set(start + 2000);
    c.increment();
    assertEquals(c.getValue(1).doubleValue(), 0.0, DELTA);
    assertEquals(c.getCurrentCount(1), 1L);

    clock.set(start + 8000);
    c.increment();
    assertEquals(c.getValue(1).doubleValue(), 0.0, DELTA);
    assertEquals(c.getCurrentCount(1), 2L);

    clock.set(start + 12000);
    c.increment();
    assertEquals(c.getValue(1).doubleValue(), 2.0 / 10.0, DELTA);
    assertEquals(c.getCurrentCount(1), 1L);
  }


  @Test
  public void testInitialPollIsZero() {
    clock.set(time(1));
    StepCounter c = newInstance("foo");
    assertEquals(c.getValue(1).doubleValue(), 0.0, DELTA);
  }

  @Test
  public void testHasRightType() throws Exception {
    assertEquals(newInstance("foo").getConfig().getTags().getValue(DataSourceType.KEY),
        "NORMALIZED");
  }

  @Test
  public void testBoundaryTransition() {
    clock.set(time(1));
    StepCounter c = newInstance("foo");

    // Should all go to one bucket
    c.increment();
    clock.set(time(4));
    c.increment();
    clock.set(time(9));
    c.increment();

    // Should cause transition
    clock.set(time(10));
    c.increment();
    clock.set(time(19));
    c.increment();

    // Check counts
    assertEquals(c.getValue(1).doubleValue(), 0.3, DELTA);
    assertEquals(c.getCurrentCount(1), 2);
  }

  @Test
  public void testResetPreviousValue() {
    clock.set(time(1));
    StepCounter c = newInstance("foo");
    for (int i = 1; i <= 100000; ++i) {
      c.increment();
      clock.set(time(i * 10 + 1));
      assertEquals(c.getValue(1).doubleValue(), 0.1, DELTA);
    }
  }

  @Test
  public void testMissedInterval() {
    clock.set(time(1));
    StepCounter c = newInstance("foo");
    c.getValue(1);

    // Multiple updates without polling
    c.increment(1);
    clock.set(time(4));
    c.increment(1);
    clock.set(time(14));
    c.increment(1);
    clock.set(time(24));
    c.increment(1);
    clock.set(time(34));
    c.increment(1);

    // Check counts
    assertEquals(c.getValue(1).doubleValue(), 0.1);
    assertEquals(c.getCurrentCount(1), 1);
  }

  @Test
  public void testMissedIntervalNoIncrements() {
    clock.set(time(1));
    StepCounter c = newInstance("foo");
    c.getValue(1);

    // Gaps without polling
    c.increment(5);
    clock.set(time(34));

    // Check counts, previous and current interval should be 0
    assertEquals(c.getValue(1).doubleValue(), 0.0);
    assertEquals(c.getCurrentCount(1), 0);
  }

  @Test
  public void testNonMonotonicClock() {
    clock.set(time(1));
    StepCounter c = newInstance("foo");
    c.getValue(1);

    c.increment();
    c.increment();
    clock.set(time(10));
    c.increment();
    clock.set(time(9)); // Should get ignored
    c.increment();
    assertEquals(c.getCurrentCount(1), 2);
    c.increment();
    clock.set(time(10));
    c.increment();
    c.increment();
    assertEquals(c.getCurrentCount(1), 5);

    // Check rate for previous interval
    assertEquals(c.getValue(1).doubleValue(), 0.2, DELTA);
  }

  @Test
  public void testGetValueTwice() {
    ManualClock manualClock = new ManualClock(60000L);

    StepCounter c = new StepCounter(MonitorConfig.builder("test").build(), manualClock);
    c.increment();
    for (int i = 2; i < 10; ++i) {
      manualClock.set(i * 60000L);
      c.increment();
      c.getValue(0);
      assertEquals(c.getValue(0).doubleValue(), 1 / 60.0, DELTA);
    }
  }

  @Test
  public void testIncrAfterMissedSteps() {
    clock.set(time(1));
    StepCounter c = newInstance("foo");
    c.increment();

    clock.set(time(11));
    assertEquals(c.getValue(1).doubleValue(), 0.1, DELTA);

    clock.set(time(31));
    c.increment();
    c.increment();
    c.increment();
    clock.set(time(41));
    assertEquals(c.getValue(1).doubleValue(), 0.3, DELTA);
  }
}