Back to Repositories

Testing Rolling Number Counter Implementation in Netflix Hystrix

This test suite validates the functionality of HystrixRollingNumber, a core component of Netflix’s Hystrix library that handles rolling counters and metrics tracking. The tests verify bucket creation, counter increments, rolling windows, and various event tracking mechanisms.

Test Coverage Overview

The test suite provides comprehensive coverage of HystrixRollingNumber functionality including:
  • Bucket creation and management
  • Counter increments and rolling sums
  • Time-based bucket rotation
  • Multiple event type tracking
  • Maximum value tracking
  • Cumulative counter behavior

Implementation Analysis

The testing approach uses JUnit to validate the rolling number implementation with:
  • Mocked time functionality for controlled testing
  • Verification of bucket creation/reset logic
  • Event increment accuracy checks
  • Edge case handling for window transitions

Technical Details

Testing tools and configuration:
  • JUnit test framework
  • MockedTime class for time control
  • Configurable window sizes and bucket counts
  • Atomic counter implementations
  • Multiple event type enums

Best Practices Demonstrated

The test suite demonstrates quality testing practices including:
  • Thorough boundary testing
  • Isolated unit testing
  • Clear test method naming
  • Comprehensive assertion coverage
  • Proper test setup and teardown

netflix/hystrix

hystrix-core/src/test/java/com/netflix/hystrix/util/HystrixRollingNumberTest.java

            
/**
 * Copyright 2015 Netflix, Inc.
 *
 * 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
 *
 * 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.netflix.hystrix.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Test;

import com.netflix.hystrix.util.HystrixRollingNumber.Time;

public class HystrixRollingNumberTest {

    @Test
    public void testCreatesBuckets() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);
            // confirm the initial settings
            assertEquals(200, counter.timeInMilliseconds);
            assertEquals(10, counter.numberOfBuckets);
            assertEquals(20, counter.bucketSizeInMillseconds);

            // we start out with 0 buckets in the queue
            assertEquals(0, counter.buckets.size());

            // add a success in each interval which should result in all 10 buckets being created with 1 success in each
            for (int i = 0; i < counter.numberOfBuckets; i++) {
                counter.increment(HystrixRollingNumberEvent.SUCCESS);
                time.increment(counter.bucketSizeInMillseconds);
            }

            // confirm we have all 10 buckets
            assertEquals(10, counter.buckets.size());

            // add 1 more and we should still only have 10 buckets since that's the max
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            assertEquals(10, counter.buckets.size());

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testResetBuckets() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            // we start out with 0 buckets in the queue
            assertEquals(0, counter.buckets.size());

            // add 1
            counter.increment(HystrixRollingNumberEvent.SUCCESS);

            // confirm we have 1 bucket
            assertEquals(1, counter.buckets.size());

            // confirm we still have 1 bucket
            assertEquals(1, counter.buckets.size());

            // add 1
            counter.increment(HystrixRollingNumberEvent.SUCCESS);

            // we should now have a single bucket with no values in it instead of 2 or more buckets
            assertEquals(1, counter.buckets.size());

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testEmptyBucketsFillIn() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            // add 1
            counter.increment(HystrixRollingNumberEvent.SUCCESS);

            // we should have 1 bucket
            assertEquals(1, counter.buckets.size());

            // wait past 3 bucket time periods (the 1st bucket then 2 empty ones)
            time.increment(counter.bucketSizeInMillseconds * 3);

            // add another
            counter.increment(HystrixRollingNumberEvent.SUCCESS);

            // we should have 4 (1 + 2 empty + 1 new one) buckets
            assertEquals(4, counter.buckets.size());

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testIncrementInSingleBucket() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            // increment
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.FAILURE);
            counter.increment(HystrixRollingNumberEvent.FAILURE);
            counter.increment(HystrixRollingNumberEvent.TIMEOUT);

            // we should have 1 bucket
            assertEquals(1, counter.buckets.size());

            // the count should be 4
            assertEquals(4, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.SUCCESS).sum());
            assertEquals(2, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.FAILURE).sum());
            assertEquals(1, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.TIMEOUT).sum());

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testTimeout() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            // increment
            counter.increment(HystrixRollingNumberEvent.TIMEOUT);

            // we should have 1 bucket
            assertEquals(1, counter.buckets.size());

            // the count should be 1
            assertEquals(1, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.TIMEOUT).sum());
            assertEquals(1, counter.getRollingSum(HystrixRollingNumberEvent.TIMEOUT));

            // sleep to get to a new bucket
            time.increment(counter.bucketSizeInMillseconds * 3);

            // incremenet again in latest bucket
            counter.increment(HystrixRollingNumberEvent.TIMEOUT);

            // we should have 4 buckets
            assertEquals(4, counter.buckets.size());

            // the counts of the last bucket
            assertEquals(1, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.TIMEOUT).sum());

            // the total counts
            assertEquals(2, counter.getRollingSum(HystrixRollingNumberEvent.TIMEOUT));

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testShortCircuited() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            // increment
            counter.increment(HystrixRollingNumberEvent.SHORT_CIRCUITED);

            // we should have 1 bucket
            assertEquals(1, counter.buckets.size());

            // the count should be 1
            assertEquals(1, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.SHORT_CIRCUITED).sum());
            assertEquals(1, counter.getRollingSum(HystrixRollingNumberEvent.SHORT_CIRCUITED));

            // sleep to get to a new bucket
            time.increment(counter.bucketSizeInMillseconds * 3);

            // incremenet again in latest bucket
            counter.increment(HystrixRollingNumberEvent.SHORT_CIRCUITED);

            // we should have 4 buckets
            assertEquals(4, counter.buckets.size());

            // the counts of the last bucket
            assertEquals(1, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.SHORT_CIRCUITED).sum());

            // the total counts
            assertEquals(2, counter.getRollingSum(HystrixRollingNumberEvent.SHORT_CIRCUITED));

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testThreadPoolRejection() {
        testCounterType(HystrixRollingNumberEvent.THREAD_POOL_REJECTED);
    }

    @Test
    public void testFallbackSuccess() {
        testCounterType(HystrixRollingNumberEvent.FALLBACK_SUCCESS);
    }

    @Test
    public void testFallbackFailure() {
        testCounterType(HystrixRollingNumberEvent.FALLBACK_FAILURE);
    }

    @Test
    public void testExceptionThrow() {
        testCounterType(HystrixRollingNumberEvent.EXCEPTION_THROWN);
    }

    private void testCounterType(HystrixRollingNumberEvent type) {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            // increment
            counter.increment(type);

            // we should have 1 bucket
            assertEquals(1, counter.buckets.size());

            // the count should be 1
            assertEquals(1, counter.buckets.getLast().getAdder(type).sum());
            assertEquals(1, counter.getRollingSum(type));

            // sleep to get to a new bucket
            time.increment(counter.bucketSizeInMillseconds * 3);

            // increment again in latest bucket
            counter.increment(type);

            // we should have 4 buckets
            assertEquals(4, counter.buckets.size());

            // the counts of the last bucket
            assertEquals(1, counter.buckets.getLast().getAdder(type).sum());

            // the total counts
            assertEquals(2, counter.getRollingSum(type));

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testIncrementInMultipleBuckets() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            // increment
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.FAILURE);
            counter.increment(HystrixRollingNumberEvent.FAILURE);
            counter.increment(HystrixRollingNumberEvent.TIMEOUT);
            counter.increment(HystrixRollingNumberEvent.TIMEOUT);
            counter.increment(HystrixRollingNumberEvent.SHORT_CIRCUITED);

            // sleep to get to a new bucket
            time.increment(counter.bucketSizeInMillseconds * 3);

            // increment
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.FAILURE);
            counter.increment(HystrixRollingNumberEvent.FAILURE);
            counter.increment(HystrixRollingNumberEvent.FAILURE);
            counter.increment(HystrixRollingNumberEvent.TIMEOUT);
            counter.increment(HystrixRollingNumberEvent.SHORT_CIRCUITED);

            // we should have 4 buckets
            assertEquals(4, counter.buckets.size());

            // the counts of the last bucket
            assertEquals(2, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.SUCCESS).sum());
            assertEquals(3, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.FAILURE).sum());
            assertEquals(1, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.TIMEOUT).sum());
            assertEquals(1, counter.buckets.getLast().getAdder(HystrixRollingNumberEvent.SHORT_CIRCUITED).sum());

            // the total counts
            assertEquals(6, counter.getRollingSum(HystrixRollingNumberEvent.SUCCESS));
            assertEquals(5, counter.getRollingSum(HystrixRollingNumberEvent.FAILURE));
            assertEquals(3, counter.getRollingSum(HystrixRollingNumberEvent.TIMEOUT));
            assertEquals(2, counter.getRollingSum(HystrixRollingNumberEvent.SHORT_CIRCUITED));

            // wait until window passes
            time.increment(counter.timeInMilliseconds);

            // increment
            counter.increment(HystrixRollingNumberEvent.SUCCESS);

            // the total counts should now include only the last bucket after a reset since the window passed
            assertEquals(1, counter.getRollingSum(HystrixRollingNumberEvent.SUCCESS));
            assertEquals(0, counter.getRollingSum(HystrixRollingNumberEvent.FAILURE));
            assertEquals(0, counter.getRollingSum(HystrixRollingNumberEvent.TIMEOUT));

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testCounterRetrievalRefreshesBuckets() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            // increment
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.SUCCESS);
            counter.increment(HystrixRollingNumberEvent.FAILURE);
            counter.increment(HystrixRollingNumberEvent.FAILURE);

            // sleep to get to a new bucket
            time.increment(counter.bucketSizeInMillseconds * 3);

            // we should have 1 bucket since nothing has triggered the update of buckets in the elapsed time
            assertEquals(1, counter.buckets.size());

            // the total counts
            assertEquals(4, counter.getRollingSum(HystrixRollingNumberEvent.SUCCESS));
            assertEquals(2, counter.getRollingSum(HystrixRollingNumberEvent.FAILURE));

            // we should have 4 buckets as the counter 'gets' should have triggered the buckets being created to fill in time
            assertEquals(4, counter.buckets.size());

            // wait until window passes
            time.increment(counter.timeInMilliseconds);

            // the total counts should all be 0 (and the buckets cleared by the get, not only increment)
            assertEquals(0, counter.getRollingSum(HystrixRollingNumberEvent.SUCCESS));
            assertEquals(0, counter.getRollingSum(HystrixRollingNumberEvent.FAILURE));

            // increment
            counter.increment(HystrixRollingNumberEvent.SUCCESS);

            // the total counts should now include only the last bucket after a reset since the window passed
            assertEquals(1, counter.getRollingSum(HystrixRollingNumberEvent.SUCCESS));
            assertEquals(0, counter.getRollingSum(HystrixRollingNumberEvent.FAILURE));

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testUpdateMax1() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            // increment
            counter.updateRollingMax(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE, 10);

            // we should have 1 bucket
            assertEquals(1, counter.buckets.size());

            // the count should be 10
            assertEquals(10, counter.buckets.getLast().getMaxUpdater(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE).max());
            assertEquals(10, counter.getRollingMaxValue(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE));

            // sleep to get to a new bucket
            time.increment(counter.bucketSizeInMillseconds * 3);

            // increment again in latest bucket
            counter.updateRollingMax(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE, 20);

            // we should have 4 buckets
            assertEquals(4, counter.buckets.size());

            // the max
            assertEquals(20, counter.buckets.getLast().getMaxUpdater(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE).max());

            // counts per bucket
            long values[] = counter.getValues(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE);
            assertEquals(10, values[0]); // oldest bucket
            assertEquals(0, values[1]);
            assertEquals(0, values[2]);
            assertEquals(20, values[3]); // latest bucket

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testUpdateMax2() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            // increment
            counter.updateRollingMax(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE, 10);
            counter.updateRollingMax(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE, 30);
            counter.updateRollingMax(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE, 20);

            // we should have 1 bucket
            assertEquals(1, counter.buckets.size());

            // the count should be 30
            assertEquals(30, counter.buckets.getLast().getMaxUpdater(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE).max());
            assertEquals(30, counter.getRollingMaxValue(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE));

            // sleep to get to a new bucket
            time.increment(counter.bucketSizeInMillseconds * 3);

            counter.updateRollingMax(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE, 30);
            counter.updateRollingMax(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE, 30);
            counter.updateRollingMax(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE, 50);

            // we should have 4 buckets
            assertEquals(4, counter.buckets.size());

            // the count
            assertEquals(50, counter.buckets.getLast().getMaxUpdater(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE).max());
            assertEquals(50, counter.getValueOfLatestBucket(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE));

            // values per bucket
            long values[] = counter.getValues(HystrixRollingNumberEvent.THREAD_MAX_ACTIVE);
            assertEquals(30, values[0]); // oldest bucket
            assertEquals(0, values[1]);
            assertEquals(0, values[2]);
            assertEquals(50, values[3]); // latest bucket

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testMaxValue() {
        MockedTime time = new MockedTime();
        try {
            HystrixRollingNumberEvent type = HystrixRollingNumberEvent.THREAD_MAX_ACTIVE;

            HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);

            counter.updateRollingMax(type, 10);

            // sleep to get to a new bucket
            time.increment(counter.bucketSizeInMillseconds);

            counter.updateRollingMax(type, 30);

            // sleep to get to a new bucket
            time.increment(counter.bucketSizeInMillseconds);

            counter.updateRollingMax(type, 40);

            // sleep to get to a new bucket
            time.increment(counter.bucketSizeInMillseconds);

            counter.updateRollingMax(type, 15);

            assertEquals(40, counter.getRollingMaxValue(type));

        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception: " + e.getMessage());
        }
    }

    @Test
    public void testEmptySum() {
        MockedTime time = new MockedTime();
        HystrixRollingNumberEvent type = HystrixRollingNumberEvent.COLLAPSED;
        HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);
        assertEquals(0, counter.getRollingSum(type));
    }

    @Test
    public void testEmptyMax() {
        MockedTime time = new MockedTime();
        HystrixRollingNumberEvent type = HystrixRollingNumberEvent.THREAD_MAX_ACTIVE;
        HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);
        assertEquals(0, counter.getRollingMaxValue(type));
    }

    @Test
    public void testEmptyLatestValue() {
        MockedTime time = new MockedTime();
        HystrixRollingNumberEvent type = HystrixRollingNumberEvent.THREAD_MAX_ACTIVE;
        HystrixRollingNumber counter = new HystrixRollingNumber(time, 200, 10);
        assertEquals(0, counter.getValueOfLatestBucket(type));
    }

    @Test
    public void testRolling() {
        MockedTime time = new MockedTime();
        HystrixRollingNumberEvent type = HystrixRollingNumberEvent.THREAD_MAX_ACTIVE;
        HystrixRollingNumber counter = new HystrixRollingNumber(time, 20, 2);
        // iterate over 20 buckets on a queue sized for 2
        for (int i = 0; i < 20; i++) {
            // first bucket
            counter.getCurrentBucket();
            try {
                time.increment(counter.bucketSizeInMillseconds);
            } catch (Exception e) {
                // ignore
            }

            assertEquals(2, counter.getValues(type).length);

            counter.getValueOfLatestBucket(type);

            // System.out.println("Head: " + counter.buckets.state.get().head);
            // System.out.println("Tail: " + counter.buckets.state.get().tail);
        }
    }

    @Test
    public void testCumulativeCounterAfterRolling() {
        MockedTime time = new MockedTime();
        HystrixRollingNumberEvent type = HystrixRollingNumberEvent.SUCCESS;
        HystrixRollingNumber counter = new HystrixRollingNumber(time, 20, 2);

        assertEquals(0, counter.getCumulativeSum(type));

        // iterate over 20 buckets on a queue sized for 2
        for (int i = 0; i < 20; i++) {
            // first bucket
            counter.increment(type);
            try {
                time.increment(counter.bucketSizeInMillseconds);
            } catch (Exception e) {
                // ignore
            }

            assertEquals(2, counter.getValues(type).length);

            counter.getValueOfLatestBucket(type);

        }

        // cumulative count should be 20 (for the number of loops above) regardless of buckets rolling
        assertEquals(20, counter.getCumulativeSum(type));
    }

    @Test
    public void testCumulativeCounterAfterRollingAndReset() {
        MockedTime time = new MockedTime();
        HystrixRollingNumberEvent type = HystrixRollingNumberEvent.SUCCESS;
        HystrixRollingNumber counter = new HystrixRollingNumber(time, 20, 2);

        assertEquals(0, counter.getCumulativeSum(type));

        // iterate over 20 buckets on a queue sized for 2
        for (int i = 0; i < 20; i++) {
            // first bucket
            counter.increment(type);
            try {
                time.increment(counter.bucketSizeInMillseconds);
            } catch (Exception e) {
                // ignore
            }

            assertEquals(2, counter.getValues(type).length);

            counter.getValueOfLatestBucket(type);

            if (i == 5 || i == 15) {
                // simulate a reset occurring every once in a while
                // so we ensure the absolute sum is handling it okay
                counter.reset();
            }
        }

        // cumulative count should be 20 (for the number of loops above) regardless of buckets rolling
        assertEquals(20, counter.getCumulativeSum(type));
    }

    @Test
    public void testCumulativeCounterAfterRollingAndReset2() {
        MockedTime time = new MockedTime();
        HystrixRollingNumberEvent type = HystrixRollingNumberEvent.SUCCESS;
        HystrixRollingNumber counter = new HystrixRollingNumber(time, 20, 2);

        assertEquals(0, counter.getCumulativeSum(type));

        counter.increment(type);
        counter.increment(type);
        counter.increment(type);

        // iterate over 20 buckets on a queue sized for 2
        for (int i = 0; i < 20; i++) {
            try {
                time.increment(counter.bucketSizeInMillseconds);
            } catch (Exception e) {
                // ignore
            }

            if (i == 5 || i == 15) {
                // simulate a reset occurring every once in a while
                // so we ensure the absolute sum is handling it okay
                counter.reset();
            }
        }

        // no increments during the loop, just some before and after
        counter.increment(type);
        counter.increment(type);

        // cumulative count should be 5 regardless of buckets rolling
        assertEquals(5, counter.getCumulativeSum(type));
    }

    @Test
    public void testCumulativeCounterAfterRollingAndReset3() {
        MockedTime time = new MockedTime();
        HystrixRollingNumberEvent type = HystrixRollingNumberEvent.SUCCESS;
        HystrixRollingNumber counter = new HystrixRollingNumber(time, 20, 2);

        assertEquals(0, counter.getCumulativeSum(type));

        counter.increment(type);
        counter.increment(type);
        counter.increment(type);

        // iterate over 20 buckets on a queue sized for 2
        for (int i = 0; i < 20; i++) {
            try {
                time.increment(counter.bucketSizeInMillseconds);
            } catch (Exception e) {
                // ignore
            }
        }

        // since we are rolling over the buckets it should reset naturally

        // no increments during the loop, just some before and after
        counter.increment(type);
        counter.increment(type);

        // cumulative count should be 5 regardless of buckets rolling
        assertEquals(5, counter.getCumulativeSum(type));
    }

    private static class MockedTime implements Time {

        private AtomicInteger time = new AtomicInteger(0);

        @Override
        public long getCurrentTimeInMillis() {
            return time.get();
        }

        public void increment(int millis) {
            time.addAndGet(millis);
        }

    }

}