Browse Source

Add more test cases for Sentinel core internal (e.g. Entry, CtSph)

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
master
Eric Zhao 6 years ago
parent
commit
e50fc55d68
5 changed files with 601 additions and 11 deletions
  1. +32
    -7
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/AsyncEntryTest.java
  2. +134
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/CtEntryTest.java
  3. +365
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/CtSphTest.java
  4. +68
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/EntryTest.java
  5. +2
    -4
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/context/ContextTest.java

+ 32
- 7
sentinel-core/src/test/java/com/alibaba/csp/sentinel/AsyncEntryTest.java View File

@@ -1,9 +1,11 @@
package com.alibaba.csp.sentinel;

import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextTestUtil;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;

import org.junit.After;
import org.junit.Test;

import static org.junit.Assert.*;
@@ -22,6 +24,8 @@ public class AsyncEntryTest {
try {
ContextUtil.enter(contextName);
Context curContext = ContextUtil.getContext();
Entry previousEntry = new CtEntry(new StringResourceWrapper("entry-sync", EntryType.IN),
null, curContext);
AsyncEntry entry = new AsyncEntry(new StringResourceWrapper("testCleanCurrentEntryInLocal", EntryType.OUT),
null, curContext);

@@ -29,9 +33,26 @@ public class AsyncEntryTest {

entry.cleanCurrentEntryInLocal();
assertNotSame(entry, curContext.getCurEntry());
assertSame(previousEntry, curContext.getCurEntry());
} finally {
ContextTestUtil.cleanUpContext();
}
}

@Test(expected = IllegalStateException.class)
public void testCleanCurrentEntryInLocalError() {
final String contextName = "abc";
try {
ContextUtil.enter(contextName);
Context curContext = ContextUtil.getContext();
AsyncEntry entry = new AsyncEntry(new StringResourceWrapper("testCleanCurrentEntryInLocal", EntryType.OUT),
null, curContext);

entry.cleanCurrentEntryInLocal();

entry.cleanCurrentEntryInLocal();
} finally {
ContextUtil.getContext().setCurEntry(null);
ContextUtil.exit();
ContextTestUtil.cleanUpContext();
}
}

@@ -47,8 +68,6 @@ public class AsyncEntryTest {
assertNull(entry.getAsyncContext());

entry.initAsyncContext();
System.out.println(curContext.getName());
System.out.println(curContext.getOrigin());

Context asyncContext = entry.getAsyncContext();
assertNotNull(asyncContext);
@@ -58,19 +77,25 @@ public class AsyncEntryTest {
assertSame(entry, asyncContext.getCurEntry());
assertTrue(asyncContext.isAsync());
} finally {
ContextUtil.getContext().setCurEntry(null);
ContextUtil.exit();
ContextTestUtil.cleanUpContext();
}
}

@Test(expected = IllegalStateException.class)
@Test
public void testDuplicateInitAsyncContext() {
Context context = new Context(null, "abc");
AsyncEntry entry = new AsyncEntry(new StringResourceWrapper("testDuplicateInitAsyncContext", EntryType.OUT),
null, context);
entry.initAsyncContext();
Context asyncContext = entry.getAsyncContext();

// Duplicate init.
entry.initAsyncContext();
assertSame(asyncContext, entry.getAsyncContext());
}

@After
public void tearDown() {
ContextTestUtil.cleanUpContext();
}
}

+ 134
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/CtEntryTest.java View File

@@ -0,0 +1,134 @@
package com.alibaba.csp.sentinel;

import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextTestUtil;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.context.NullContext;
import com.alibaba.csp.sentinel.node.Node;
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;

import org.junit.After;
import org.junit.Before;
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 CtEntryTest {

@Test
public void testExitNotMatchCurEntry() {
String contextName = "context-rpc";
ContextUtil.enter(contextName);
Context context = ContextUtil.getContext();
CtEntry entry1 = null;
CtEntry entry2 = null;
try {
entry1 = new CtEntry(new StringResourceWrapper("res1", EntryType.IN),
null, ContextUtil.getContext());
assertSame(entry1, context.getCurEntry());
entry2 = new CtEntry(new StringResourceWrapper("res2", EntryType.IN),
null, ContextUtil.getContext());
assertSame(entry2, context.getCurEntry());

// Forget to exit for entry 2...
// Directly exit for entry 1, then boom...
entry1.exit();
} catch (ErrorEntryFreeException ex) {
assertNotNull(entry1);
assertNotNull(entry2);
assertNull(entry1.context);
assertNull(entry2.context);
assertNull(context.getCurEntry());
return;
} finally {
ContextUtil.exit();
}
fail("Mismatch entry-exit should throw an ErrorEntryFreeException");
}

private Context getFakeDefaultContext() {
return new Context(null, Constants.CONTEXT_DEFAULT_NAME);
}

@Test
public void testExitLastEntryWithDefaultContext() {
final Context defaultContext = getFakeDefaultContext();
ContextUtil.runOnContext(defaultContext, new Runnable() {
@Override
public void run() {
CtEntry entry = new CtEntry(new StringResourceWrapper("res", EntryType.IN),
null, ContextUtil.getContext());
assertSame(entry, defaultContext.getCurEntry());
assertSame(defaultContext, ContextUtil.getContext());
entry.exit();
assertNull(defaultContext.getCurEntry());
// Default context will be automatically exited.
assertNull(ContextUtil.getContext());
}
});

}

@Test
public void testExitTwoLastEntriesWithCustomContext() {
String contextName = "context-rpc";
ContextUtil.enter(contextName);
Context context = ContextUtil.getContext();
try {
CtEntry entry1 = new CtEntry(new StringResourceWrapper("resA", EntryType.IN),
null, context);
entry1.exit();
assertEquals(context, ContextUtil.getContext());
CtEntry entry2 = new CtEntry(new StringResourceWrapper("resB", EntryType.IN),
null, context);
entry2.exit();
assertEquals(context, ContextUtil.getContext());
} finally {
ContextUtil.exit();
assertNull(ContextUtil.getContext());
}
}

@Test
public void testEntryAndExitWithNullContext() {
Context context = new NullContext();
CtEntry entry = new CtEntry(new StringResourceWrapper("testEntryAndExitWithNullContext", EntryType.IN),
null, context);
assertNull(context.getCurEntry());
entry.exit();
assertNull(context.getCurEntry());
// Won't true exit, so the context won't be cleared.
assertEquals(context, entry.context);
}

@Test
public void testGetLastNode() {
Context context = new NullContext();
CtEntry entry = new CtEntry(new StringResourceWrapper("testGetLastNode", EntryType.IN),
null, context);
assertNull(entry.parent);
assertNull(entry.getLastNode());
Entry parentEntry = mock(Entry.class);
Node node = mock(Node.class);
when(parentEntry.getCurNode()).thenReturn(node);
entry.parent = parentEntry;
assertSame(node, entry.getLastNode());
}

@Before
public void setUp() throws Exception {
ContextTestUtil.cleanUpContext();
ContextTestUtil.resetContextMap();
}

@After
public void tearDown() throws Exception {
ContextTestUtil.cleanUpContext();
ContextTestUtil.resetContextMap();
}
}

+ 365
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/CtSphTest.java View File

@@ -0,0 +1,365 @@
package com.alibaba.csp.sentinel;

import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextTestUtil;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.slotchain.SlotChainProvider;
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
import com.alibaba.csp.sentinel.slots.block.BlockException;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

/**
* Test cases for Sentinel internal {@link CtSph}.
*
* @author Eric Zhao
*/
public class CtSphTest {

private final CtSph ctSph = new CtSph();

private void testCustomContextEntryWithFullContextSize(String resourceName, boolean async) {
fillFullContext();
ResourceWrapper resourceWrapper = new StringResourceWrapper(resourceName, EntryType.IN);
String contextName = "custom-context-" + System.currentTimeMillis();
ContextUtil.enter(contextName, "9527");

// Prepare a slot that "should not pass". If entered the slot, exception will be thrown.
addShouldNotPassSlotFor(resourceWrapper);

Entry entry = null;
try {
if (async) {
entry = ctSph.asyncEntry(resourceName, resourceWrapper.getType(), 1);
} else {
entry = ctSph.entry(resourceWrapper, 1);
}
} catch (BlockException ex) {
fail("Unexpected blocked: " + ex.getClass().getCanonicalName());
} finally {
if (entry != null) {
entry.exit();
}
ContextUtil.exit();
}
}

@Test
public void testCustomContextSyncEntryWithFullContextSize() {
String resourceName = "testCustomContextSyncEntryWithFullContextSize";
testCustomContextEntryWithFullContextSize(resourceName, false);
}

@Test
public void testCustomContextAsyncEntryWithFullContextSize() {
String resourceName = "testCustomContextAsyncEntryWithFullContextSize";
testCustomContextEntryWithFullContextSize(resourceName, true);
}

private void testDefaultContextEntryWithFullContextSize(String resourceName, boolean async) {
fillFullContext();
ResourceWrapper resourceWrapper = new StringResourceWrapper(resourceName, EntryType.IN);

// Prepare a slot that "should pass".
ShouldPassSlot slot = addShouldPassSlotFor(resourceWrapper);
assertFalse(slot.entered || slot.exited);

Entry entry = null;
try {
if (!async) {
entry = ctSph.entry(resourceWrapper, 1);
} else {
entry = ctSph.asyncEntry(resourceName, resourceWrapper.getType(), 1);
Context asyncContext = ((AsyncEntry)entry).getAsyncContext();
assertTrue(ContextUtil.isDefaultContext(asyncContext));
assertTrue(asyncContext.isAsync());
}
assertTrue(ContextUtil.isDefaultContext(ContextUtil.getContext()));
assertTrue(slot.entered);
} catch (BlockException ex) {
fail("Unexpected blocked: " + ex.getClass().getCanonicalName());
} finally {
if (entry != null) {
entry.exit();
assertTrue(slot.exited);
}
}
}

@Test
public void testDefaultContextSyncEntryWithFullContextSize() {
String resourceName = "testDefaultContextSyncEntryWithFullContextSize";
testDefaultContextEntryWithFullContextSize(resourceName, false);
}

@Test
public void testDefaultContextAsyncEntryWithFullContextSize() {
String resourceName = "testDefaultContextAsyncEntryWithFullContextSize";
testDefaultContextEntryWithFullContextSize(resourceName, true);
}

@Test
public void testEntryAndAsyncEntryWhenSwitchOff() {
// Turn off the switch.
Constants.ON = false;

String resourceNameA = "resSync";
String resourceNameB = "resAsync";
ResourceWrapper resourceWrapperA = new StringResourceWrapper(resourceNameA, EntryType.IN);
ResourceWrapper resourceWrapperB = new StringResourceWrapper(resourceNameB, EntryType.IN);

// Prepare a slot that "should not pass". If entered the slot, exception will be thrown.
addShouldNotPassSlotFor(resourceWrapperA);
addShouldNotPassSlotFor(resourceWrapperB);

Entry entry = null;
AsyncEntry asyncEntry = null;
try {
entry = ctSph.entry(resourceWrapperA, 1);
asyncEntry = ctSph.asyncEntry(resourceNameB, resourceWrapperB.getType(), 1);
} catch (BlockException ex) {
fail("Unexpected blocked: " + ex.getClass().getCanonicalName());
} finally {
if (asyncEntry != null) {
asyncEntry.exit();
}
if (entry != null) {
entry.exit();
}
Constants.ON = true;
}
}

@Test
public void testAsyncEntryNormalPass() {
String resourceName = "testAsyncEntryNormalPass";
ResourceWrapper resourceWrapper = new StringResourceWrapper(resourceName, EntryType.IN);
AsyncEntry entry = null;

// Prepare a slot that "should pass".
ShouldPassSlot slot = addShouldPassSlotFor(resourceWrapper);
assertFalse(slot.entered || slot.exited);

ContextUtil.enter("abc");
Entry previousEntry = ContextUtil.getContext().getCurEntry();
try {
entry = ctSph.asyncEntry(resourceName, EntryType.IN, 1);
assertTrue(slot.entered);
assertFalse(slot.exited);
Context asyncContext = entry.getAsyncContext();
assertNotNull(asyncContext);
assertSame(entry, asyncContext.getCurEntry());
assertNotSame("The async entry should not be added to current context",
entry, ContextUtil.getContext().getCurEntry());
assertSame(previousEntry, ContextUtil.getContext().getCurEntry());
} catch (BlockException ex) {
fail("Unexpected blocked: " + ex.getClass().getCanonicalName());
} finally {
if (entry != null) {
Context asyncContext = entry.getAsyncContext();
entry.exit();
assertTrue(slot.exited);
assertNull(entry.getAsyncContext());
assertSame(previousEntry, asyncContext.getCurEntry());
}
ContextUtil.exit();
}
}

@Test
public void testAsyncEntryNestedInSyncEntryNormalBlocked() {
String previousResourceName = "fff";
String resourceName = "testAsyncEntryNestedInSyncEntryNormalBlocked";
ResourceWrapper resourceWrapper = new StringResourceWrapper(resourceName, EntryType.IN);

// Prepare a slot that "must block".
MustBlockSlot slot = addMustBlockSlot(resourceWrapper);
assertFalse(slot.exited);
// Previous entry should pass.
addShouldPassSlotFor(new StringResourceWrapper(previousResourceName, EntryType.IN));
ContextUtil.enter("bcd-" + System.currentTimeMillis());

AsyncEntry entry = null;
Entry syncEntry = null;
Entry previousEntry = null;
try {
// First enter a sync resource.
syncEntry = ctSph.entry(previousResourceName, EntryType.IN, 1);
// Record current entry (previous for next).
previousEntry = ContextUtil.getContext().getCurEntry();
// Then enter an async resource.
entry = ctSph.asyncEntry(resourceName, EntryType.IN, 1);

// Should not pass here.
} catch (BlockException ex) {
assertNotNull(previousEntry);
assertNull(entry);
assertTrue(slot.exited);
assertSame(previousEntry, ContextUtil.getContext().getCurEntry());
return;
} finally {
assertNull(entry);
assertNotNull(syncEntry);

syncEntry.exit();
ContextUtil.exit();
}
fail("This async entry is expected to be blocked");
}

private void testEntryAmountExceeded(boolean async) {
fillFullResources();
Entry entry = null;
try {
if (!async) {
entry = ctSph.entry("testSync", EntryType.IN, 1);
} else {
entry = ctSph.asyncEntry("testSync", EntryType.IN, 1);
}
assertNull(((CtEntry)entry).chain);
if (!async) {
assertSame(entry, ContextUtil.getContext().getCurEntry());
} else {
Context asyncContext = ((AsyncEntry)entry).getAsyncContext();
assertNotNull(asyncContext);
assertSame(entry, asyncContext.getCurEntry());
}
} catch (BlockException ex) {
fail("Unexpected blocked: " + ex.getClass().getCanonicalName());
} finally {
if (entry != null) {
entry.exit();
}
}
}

@Test
public void testEntryAmountExceededForSyncEntry() {
testEntryAmountExceeded(false);
}

@Test
public void testEntryAmountExceededForAsyncEntry() {
testEntryAmountExceeded(true);
}

@Test
public void testLookUpSlotChain() {
ResourceWrapper r1 = new StringResourceWrapper("firstRes", EntryType.IN);
assertFalse(CtSph.getChainMap().containsKey(r1));
ProcessorSlot<Object> chainR1 = ctSph.lookProcessChain(r1);
assertNotNull("The slot chain for r1 should be created", chainR1);
assertSame("Should return the cached slot chain once it has been created", chainR1, ctSph.lookProcessChain(r1));

fillFullResources();
ResourceWrapper r2 = new StringResourceWrapper("secondRes", EntryType.IN);
assertFalse(CtSph.getChainMap().containsKey(r2));
assertNull("The slot chain for r2 should not be created because amount exceeded", ctSph.lookProcessChain(r2));
assertNull(ctSph.lookProcessChain(r2));
}

private void fillFullContext() {
for (int i = 0; i < Constants.MAX_CONTEXT_NAME_SIZE; i++) {
ContextUtil.enter("test-context-" + i);
ContextUtil.exit();
}
}

private void fillFullResources() {
for (int i = 0; i < Constants.MAX_SLOT_CHAIN_SIZE; i++) {
ResourceWrapper resourceWrapper = new StringResourceWrapper("test-resource-" + i, EntryType.IN);
CtSph.getChainMap().put(resourceWrapper, SlotChainProvider.newSlotChain());
}
}

private void addShouldNotPassSlotFor(ResourceWrapper resourceWrapper) {
ProcessorSlotChain slotChain = new DefaultProcessorSlotChain();
slotChain.addLast(new ShouldNotPassSlot());
CtSph.getChainMap().put(resourceWrapper, slotChain);
}

private ShouldPassSlot addShouldPassSlotFor(ResourceWrapper resourceWrapper) {
ProcessorSlotChain slotChain = new DefaultProcessorSlotChain();
ShouldPassSlot shouldPassSlot = new ShouldPassSlot();
slotChain.addLast(shouldPassSlot);
CtSph.getChainMap().put(resourceWrapper, slotChain);
return shouldPassSlot;
}

private MustBlockSlot addMustBlockSlot(ResourceWrapper resourceWrapper) {
ProcessorSlotChain slotChain = new DefaultProcessorSlotChain();
MustBlockSlot mustBlockSlot = new MustBlockSlot();
slotChain.addLast(mustBlockSlot);
CtSph.getChainMap().put(resourceWrapper, slotChain);
return mustBlockSlot;
}

private class ShouldNotPassSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode param, int count,
Object... args) {
throw new IllegalStateException("Should not enter this slot!");
}

@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
throw new IllegalStateException("Should not exit this slot!");
}
}

private class MustBlockSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
boolean exited = false;

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode param, int count,
Object... args) throws Throwable {
throw new BlockException("custom") {};
}

@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
exited = true;
}
}

private class ShouldPassSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
boolean entered = false;
boolean exited = false;

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode param, int count,
Object... args) {
entered = true;
}

@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
exited = true;
}
}

@Before
public void setUp() throws Exception {
ContextTestUtil.cleanUpContext();
ContextTestUtil.resetContextMap();
CtSph.resetChainMap();
}

@After
public void tearDown() throws Exception {
ContextTestUtil.cleanUpContext();
ContextTestUtil.resetContextMap();
CtSph.resetChainMap();
}
}

+ 68
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/EntryTest.java View File

@@ -0,0 +1,68 @@
package com.alibaba.csp.sentinel;

import com.alibaba.csp.sentinel.node.Node;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;

import org.junit.Test;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

/**
* Test cases for {@link Entry}.
*
* @author Eric Zhao
*/
public class EntryTest {

@Test
public void testEntryExitCounts() {
ResourceWrapper resourceWrapper = new StringResourceWrapper("resA", EntryType.IN);
TestEntry entry = new TestEntry(resourceWrapper);
entry.exit();
assertEquals(-1, entry.count);
entry.exit(9);
assertEquals(-10, entry.count);
}

@Test
public void testEntryFieldsGetSet() {
ResourceWrapper resourceWrapper = new StringResourceWrapper("resA", EntryType.IN);
Entry entry = new TestEntry(resourceWrapper);
assertSame(resourceWrapper, entry.getResourceWrapper());
Throwable error = new IllegalStateException();
entry.setError(error);
assertSame(error, entry.getError());
Node curNode = mock(Node.class);
entry.setCurNode(curNode);
assertSame(curNode, entry.getCurNode());
Node originNode = mock(Node.class);
entry.setOriginNode(originNode);
assertSame(originNode, entry.getOriginNode());
}

private class TestEntry extends Entry {

int count = 0;

TestEntry(ResourceWrapper resourceWrapper) {
super(resourceWrapper);
}

@Override
public void exit(int count, Object... args) throws ErrorEntryFreeException {
this.count -= count;
}

@Override
protected Entry trueExit(int count, Object... args) throws ErrorEntryFreeException {
return null;
}

@Override
public Node getLastNode() {
return null;
}
}
}

+ 2
- 4
sentinel-core/src/test/java/com/alibaba/csp/sentinel/context/ContextTest.java View File

@@ -16,7 +16,6 @@
package com.alibaba.csp.sentinel.context;

import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.TestUtil;

import org.junit.After;
import org.junit.Before;
@@ -39,7 +38,7 @@ public class ContextTest {

@After
public void cleanUp() {
TestUtil.cleanUpContext();
ContextTestUtil.cleanUpContext();
}

@Test
@@ -80,8 +79,7 @@ public class ContextTest {
}

private void resetContextMap() {
ContextUtil.resetContextMap();
Constants.ROOT.removeChildList();
ContextTestUtil.resetContextMap();
}

@Test


Loading…
Cancel
Save