Update test cases for circuit breaking

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
Eric Zhao 2020-05-18 23:00:59 +08:00
parent c0c27c86b7
commit 07b811196b
5 changed files with 417 additions and 185 deletions

View File

@ -0,0 +1,213 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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.alibaba.csp.sentinel.slots.block.degrade;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker.State;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStateChangeObserver;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry;
import com.alibaba.csp.sentinel.test.AbstractTimeBasedTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
* @author jialiang.linjl
* @author Eric Zhao
*/
public class CircuitBreakingIntegrationTest extends AbstractTimeBasedTest {
@Before
public void setUp() {
DegradeRuleManager.loadRules(new ArrayList<DegradeRule>());
}
@After
public void tearDown() throws Exception {
DegradeRuleManager.loadRules(new ArrayList<DegradeRule>());
}
private boolean entryAndSleepFor(String res, int sleepMs) {
Entry entry = null;
try {
entry = SphU.entry(res);
sleep(sleepMs);
} catch (BlockException ex) {
return false;
} catch (Exception ex) {
Tracer.traceEntry(ex, entry);
} finally {
if (entry != null) {
entry.exit();
}
}
return true;
}
private boolean entryWithErrorIfPresent(String res, Exception ex) {
Entry entry = null;
try {
entry = SphU.entry(res);
if (ex != null) {
Tracer.traceEntry(ex, entry);
}
sleep(ThreadLocalRandom.current().nextInt(5, 10));
} catch (BlockException b) {
return false;
} finally {
if (entry != null) {
entry.exit();
}
}
return true;
}
@Test
public void testSlowRequestMode() throws Exception {
CircuitBreakerStateChangeObserver observer = mock(CircuitBreakerStateChangeObserver.class);
setCurrentMillis(System.currentTimeMillis() / 1000 * 1000);
int retryTimeoutSec = 5;
int maxRt = 50;
int statIntervalMs = 20000;
int minRequestAmount = 10;
String res = "CircuitBreakingIntegrationTest_testSlowRequestMode";
EventObserverRegistry.getInstance().addStateChangeObserver(res, observer);
DegradeRuleManager.loadRules(Arrays.asList(
new DegradeRule(res).setTimeWindow(retryTimeoutSec).setCount(maxRt)
.setStatIntervalMs(statIntervalMs).setMinRequestAmount(minRequestAmount)
.setSlowRatioThreshold(0.8d).setGrade(0)
));
// Try first N requests where N = minRequestAmount.
for (int i = 0; i < minRequestAmount; i++) {
if (i < 7) {
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20)));
} else {
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(-20, -10)));
}
}
// Till now slow ratio should be 70%.
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20)));
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20)));
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20)));
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20)));
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20)));
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20)));
// Circuit breaker has transformed to OPEN since here.
verify(observer)
.onStateChange(eq(State.CLOSED), eq(State.OPEN), any(DegradeRule.class), anyDouble());
assertEquals(State.OPEN, DegradeRuleManager.getCircuitBreakers(res).get(0).currentState());
assertFalse(entryAndSleepFor(res, 1));
sleepSecond(1);
assertFalse(entryAndSleepFor(res, 1));
sleepSecond(retryTimeoutSec);
// Test HALF-OPEN to OPEN.
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20)));
verify(observer)
.onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class));
verify(observer)
.onStateChange(eq(State.HALF_OPEN), eq(State.OPEN), any(DegradeRule.class), anyDouble());
// Wait for next retry timeout;
reset(observer);
sleepSecond(retryTimeoutSec + 1);
assertTrue(entryAndSleepFor(res, maxRt - ThreadLocalRandom.current().nextInt(10, 20)));
verify(observer)
.onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class));
verify(observer)
.onStateChange(eq(State.HALF_OPEN), eq(State.CLOSED), any(DegradeRule.class), nullable(Double.class));
// Now circuit breaker has been closed.
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20)));
EventObserverRegistry.getInstance().removeStateChangeObserver(res);
}
@Test
public void testExceptionRatioMode() throws Exception {
CircuitBreakerStateChangeObserver observer = mock(CircuitBreakerStateChangeObserver.class);
setCurrentMillis(System.currentTimeMillis() / 1000 * 1000);
int retryTimeoutSec = 5;
double maxRatio = 0.5;
int statIntervalMs = 25000;
final int minRequestAmount = 10;
String res = "CircuitBreakingIntegrationTest_testExceptionRatioMode";
EventObserverRegistry.getInstance().addStateChangeObserver(res, observer);
DegradeRuleManager.loadRules(Arrays.asList(
new DegradeRule(res).setTimeWindow(retryTimeoutSec).setCount(maxRatio)
.setStatIntervalMs(statIntervalMs).setMinRequestAmount(minRequestAmount)
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
));
// Try first N requests where N = minRequestAmount.
for (int i = 0; i < minRequestAmount - 1; i++) {
if (i < 6) {
assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException()));
} else {
assertTrue(entryWithErrorIfPresent(res, null));
}
}
// Till now slow ratio should be 60%.
assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException()));
// Circuit breaker has transformed to OPEN since here.
assertEquals(State.OPEN, DegradeRuleManager.getCircuitBreakers(res).get(0).currentState());
assertFalse(entryWithErrorIfPresent(res, null));
sleepSecond(2);
assertFalse(entryWithErrorIfPresent(res, null));
sleepSecond(retryTimeoutSec);
// Test HALF-OPEN to OPEN.
assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException()));
verify(observer)
.onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class));
verify(observer)
.onStateChange(eq(State.HALF_OPEN), eq(State.OPEN), any(DegradeRule.class), anyDouble());
// Wait for next retry timeout;
reset(observer);
sleepSecond(retryTimeoutSec + 1);
assertTrue(entryWithErrorIfPresent(res, null));
verify(observer)
.onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class));
verify(observer)
.onStateChange(eq(State.HALF_OPEN), eq(State.CLOSED), any(DegradeRule.class), nullable(Double.class));
// Now circuit breaker has been closed.
assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException()));
EventObserverRegistry.getInstance().removeStateChangeObserver(res);
}
@Test
public void testExceptionCountMode() throws Throwable {
// TODO
}
}

View File

@ -15,8 +15,14 @@
*/
package com.alibaba.csp.sentinel.slots.block.degrade;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import java.util.ArrayList;
import java.util.Arrays;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
@ -28,6 +34,32 @@ import static org.junit.Assert.*;
*/
public class DegradeRuleManagerTest {
@Before
public void setUp() throws Exception {
DegradeRuleManager.loadRules(new ArrayList<DegradeRule>());
}
@After
public void tearDown() throws Exception {
DegradeRuleManager.loadRules(new ArrayList<DegradeRule>());
}
@Test
public void loadSameRuleUseSameCircuitBreaker() {
String resource = "loadSameRuleUseSameCircuitBreaker";
DegradeRule rule = new DegradeRule(resource)
.setCount(100)
.setSlowRatioThreshold(0.9d)
.setTimeWindow(20)
.setStatIntervalMs(20000);
DegradeRuleManager.loadRules(Arrays.asList(rule));
CircuitBreaker cb = DegradeRuleManager.getCircuitBreakers(resource).get(0);
DegradeRuleManager.loadRules(Arrays.asList(rule,
new DegradeRule("abc").setTimeWindow(20).setCount(20).setSlowRatioThreshold(0.8d)));
assertSame(cb, DegradeRuleManager.getCircuitBreakers(resource).get(0));
}
@Test
public void testIsValidRule() {
DegradeRule rule1 = new DegradeRule("abc");
@ -46,13 +78,19 @@ public class DegradeRuleManagerTest {
DegradeRule rule5 = new DegradeRule("Sentinel")
.setCount(97)
.setGrade(RuleConstant.DEGRADE_GRADE_RT)
.setTimeWindow(15)
.setRtSlowRequestAmount(0);
.setSlowRatioThreshold(15)
.setTimeWindow(15);
DegradeRule rule6 = new DegradeRule("Sentinel")
.setCount(0.93d)
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
.setTimeWindow(20)
.setMinRequestAmount(0);
DegradeRule rule7 = new DegradeRule("Sentinel")
.setCount(100)
.setSlowRatioThreshold(0.8d)
.setTimeWindow(10)
.setStatIntervalMs(0)
.setMinRequestAmount(20);
assertFalse(DegradeRuleManager.isValidRule(rule1));
assertFalse(DegradeRuleManager.isValidRule(rule2));
assertFalse(DegradeRuleManager.isValidRule(rule3));
@ -61,5 +99,6 @@ public class DegradeRuleManagerTest {
assertFalse(DegradeRuleManager.isValidRule(rule4));
assertFalse(DegradeRuleManager.isValidRule(rule5));
assertFalse(DegradeRuleManager.isValidRule(rule6));
assertFalse(DegradeRuleManager.isValidRule(rule7));
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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.alibaba.csp.sentinel.slots.block.degrade;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* @author Eric Zhao
*/
public class DegradeRuleTest {
@Test
public void testRuleEquals() {
DegradeRule degradeRule1 = new DegradeRule();
DegradeRule degradeRule2 = new DegradeRule();
int minRequestAmount = 20;
double count = 1.0;
int timeWindow = 2;
degradeRule1.setMinRequestAmount(minRequestAmount);
degradeRule1.setCount(count);
degradeRule1.setTimeWindow(timeWindow);
degradeRule1.setGrade(RuleConstant.DEGRADE_GRADE_RT);
degradeRule2.setMinRequestAmount(minRequestAmount);
degradeRule2.setCount(count);
degradeRule2.setGrade(RuleConstant.DEGRADE_GRADE_RT);
degradeRule2.setTimeWindow(timeWindow);
assertEquals(degradeRule1, degradeRule2);
degradeRule2.setMinRequestAmount(100);
assertNotEquals(degradeRule1, degradeRule2);
}
}

View File

@ -1,182 +0,0 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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.alibaba.csp.sentinel.slots.block.degrade;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.node.ClusterNode;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author jialiang.linjl
*/
public class DegradeTest {
@Test
public void testAverageRtDegrade() throws InterruptedException {
String key = "test_degrade_average_rt";
ClusterNode cn = mock(ClusterNode.class);
ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn);
Context context = mock(Context.class);
DefaultNode node = mock(DefaultNode.class);
when(node.getClusterNode()).thenReturn(cn);
when(cn.avgRt()).thenReturn(2d);
int rtSlowRequestAmount = 10;
DegradeRule rule = new DegradeRule();
rule.setCount(1);
rule.setResource(key);
rule.setTimeWindow(2);
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
rule.setRtSlowRequestAmount(rtSlowRequestAmount);
//Will true
for (int i = 0; i < rtSlowRequestAmount - 1; i++) {
assertTrue(rule.passCheck(context, node, 1));
}
// The third time will fail.
assertFalse(rule.passCheck(context, node, 1));
assertFalse(rule.passCheck(context, node, 1));
// Restore.
TimeUnit.MILLISECONDS.sleep(2200);
assertTrue(rule.passCheck(context, node, 1));
}
@Test
public void testExceptionRatioModeDegrade() throws Throwable {
String key = "test_degrade_exception_ratio";
ClusterNode cn = mock(ClusterNode.class);
ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn);
Context context = mock(Context.class);
DefaultNode node = mock(DefaultNode.class);
when(node.getClusterNode()).thenReturn(cn);
DegradeRule rule = new DegradeRule();
rule.setCount(0.15);
rule.setResource(key);
rule.setTimeWindow(2);
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
rule.setMinRequestAmount(20);
// Will true. While totalQps < minRequestAmount
when(cn.totalQps()).thenReturn(8d);
assertTrue(rule.passCheck(context, node, 1));
// Will true.
when(cn.totalQps()).thenReturn(21d);
when(cn.successQps()).thenReturn(9d);
when(cn.exceptionQps()).thenReturn(9d);
assertTrue(rule.passCheck(context, node, 1));
// Will true. While totalQps > minRequestAmount and exceptionRation < count
when(cn.totalQps()).thenReturn(100d);
when(cn.successQps()).thenReturn(90d);
when(cn.exceptionQps()).thenReturn(10d);
assertTrue(rule.passCheck(context, node, 1));
// Will fail. While totalQps > minRequestAmount and exceptionRation > count
rule.setMinRequestAmount(5);
when(cn.totalQps()).thenReturn(12d);
when(cn.successQps()).thenReturn(8d);
when(cn.exceptionQps()).thenReturn(6d);
assertFalse(rule.passCheck(context, node, 1));
// Restore from the degrade timeout.
TimeUnit.MILLISECONDS.sleep(2200);
// Will pass.
when(cn.totalQps()).thenReturn(106d);
when(cn.successQps()).thenReturn(100d);
assertTrue(rule.passCheck(context, node, 1));
}
@Test
public void testExceptionCountModeDegrade() throws Throwable {
String key = "test_degrade_exception_count";
ClusterNode cn = mock(ClusterNode.class);
when(cn.totalException()).thenReturn(10L);
ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn);
Context context = mock(Context.class);
DefaultNode node = mock(DefaultNode.class);
when(node.getClusterNode()).thenReturn(cn);
DegradeRule rule = new DegradeRule();
rule.setCount(4);
rule.setResource(key);
rule.setTimeWindow(2);
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
when(cn.totalException()).thenReturn(4L);
// Will fail.
assertFalse(rule.passCheck(context, node, 1));
// Restore from the degrade timeout.
TimeUnit.MILLISECONDS.sleep(2200);
when(cn.totalException()).thenReturn(0L);
// Will pass.
assertTrue(rule.passCheck(context, node, 1));
}
@Test
public void testEquals() {
DegradeRule degradeRule1 = new DegradeRule();
DegradeRule degradeRule2 = new DegradeRule();
assertTrue(degradeRule1.equals(degradeRule2));
int rtSlowRequestAmount = 10;
int minRequestAmount = 20;
double count = 1.0;
int timeWindow = 2;
degradeRule1.setRtSlowRequestAmount(rtSlowRequestAmount);
degradeRule1.setMinRequestAmount(minRequestAmount);
degradeRule1.setCount(count);
degradeRule1.setTimeWindow(timeWindow);
degradeRule1.setGrade(RuleConstant.DEGRADE_GRADE_RT);
degradeRule2.setRtSlowRequestAmount(rtSlowRequestAmount);
degradeRule2.setMinRequestAmount(minRequestAmount);
degradeRule2.setCount(count);
degradeRule2.setGrade(RuleConstant.DEGRADE_GRADE_RT);
degradeRule2.setTimeWindow(timeWindow);
assertTrue(degradeRule1.equals(degradeRule2));
degradeRule2.setMinRequestAmount(100);
assertFalse(degradeRule1.equals(degradeRule2));
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
* 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.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker.State;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ExceptionCircuitBreaker.SimpleErrorCounter;
import com.alibaba.csp.sentinel.slots.statistic.base.LeapArray;
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
import com.alibaba.csp.sentinel.test.AbstractTimeBasedTest;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Eric Zhao
*/
public class ExceptionCircuitBreakerTest extends AbstractTimeBasedTest {
@Test
@SuppressWarnings("unchecked")
public void testStateChangeAndTryAcquire() {
int retryTimeout = 10;
DegradeRule rule = new DegradeRule("abc")
.setCount(0.5d)
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
.setStatIntervalMs(20 * 1000)
.setTimeWindow(retryTimeout)
.setMinRequestAmount(10);
LeapArray<SimpleErrorCounter> stat = mock(LeapArray.class);
SimpleErrorCounter counter = new SimpleErrorCounter();
WindowWrap<SimpleErrorCounter> bucket = new WindowWrap<>(20000, 0, counter);
when(stat.currentWindow()).thenReturn(bucket);
ExceptionCircuitBreaker cb = new ExceptionCircuitBreaker(rule, stat);
assertTrue(cb.tryPass());
assertTrue(cb.tryPass());
setCurrentMillis(System.currentTimeMillis());
cb.fromCloseToOpen(0.52d);
assertEquals(State.OPEN, cb.currentState());
assertFalse(cb.tryPass());
assertFalse(cb.tryPass());
// Wait for next retry checkpoint.
sleepSecond(retryTimeout);
sleep(100);
// Try a request to trigger state transformation.
assertTrue(cb.tryPass());
assertEquals(State.HALF_OPEN, cb.currentState());
// Mark this request as error
cb.onRequestComplete(20, new IllegalArgumentException());
assertEquals(State.OPEN, cb.currentState());
// Wait for next retry checkpoint.
sleepSecond(retryTimeout);
sleep(100);
assertTrue(cb.tryPass());
assertEquals(State.HALF_OPEN, cb.currentState());
setCurrentMillis(System.currentTimeMillis());
// Mark this request as success.
cb.onRequestComplete(20, null);
assertEquals(State.CLOSED, cb.currentState());
}
@Test
@SuppressWarnings("unchecked")
public void testRecordErrorOrSuccess() {
DegradeRule rule = new DegradeRule("abc")
.setCount(0.5d)
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
.setStatIntervalMs(20 * 1000)
.setTimeWindow(10)
.setMinRequestAmount(10);
LeapArray<SimpleErrorCounter> stat = mock(LeapArray.class);
SimpleErrorCounter counter = new SimpleErrorCounter();
WindowWrap<SimpleErrorCounter> bucket = new WindowWrap<>(20000, 0, counter);
when(stat.currentWindow()).thenReturn(bucket);
CircuitBreaker cb = new ExceptionCircuitBreaker(rule, stat);
cb.onRequestComplete(15, null);
assertEquals(1L, counter.getTotalCount().longValue());
assertEquals(0L, counter.getErrorCount().longValue());
cb.onRequestComplete(15, new IllegalArgumentException());
assertEquals(2L, counter.getTotalCount().longValue());
assertEquals(1L, counter.getErrorCount().longValue());
}
}