package de.vksi.c4j.acceptancetest.s4; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.sameInstance; import static org.junit.Assert.assertThat; import static org.junit.matchers.JUnitMatchers.containsString; import java.lang.reflect.Field; import java.util.List; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import de.vksi.c4j.systemtest.TransformerAwareRule; import de.vksi.c4j.acceptancetest.stack.Stack; import de.vksi.c4j.acceptancetest.stack.StackContract; import de.vksi.c4j.acceptancetest.stack.StackSpec; /** * Tests for postconditions of {@link StackContract} */ public class NNPostS4Test { @Rule public TransformerAwareRule transformerAware = new TransformerAwareRule(); @Rule public ExpectedException thrown = ExpectedException.none(); private Stack<String> stack; @Before public void before() { stack = new Stack<String>(2); } @Test public void countPostconditionFulfilled() throws Exception { stack.count(); } @Test public void countPostconditionViolatedLowerBound() throws Exception { Stack<String> brokenStack = new Stack<String>(10) { @Override public int count() { return -1; } }; thrown.expect(AssertionError.class); thrown.expectMessage("result >= 0"); brokenStack.count(); } @Test public void countPostconditionViolatedUpperBound() throws Exception { Stack<String> brokenStack = new Stack<String>(10) { @Override public int count() { return 11; } }; thrown.expect(AssertionError.class); thrown.expectMessage("count <= capacity"); brokenStack.count(); } @Test public void pushPostconditionFulfilled() throws Exception { stack.push("bottom"); } @Test public void pushConditionViolatedByNotPushing() throws Exception { Stack<String> brokenStack = new Stack<String>(10) { @Override public String top() { return null; } }; thrown.expect(AssertionError.class); thrown.expectMessage("x set"); brokenStack.push("bottom"); } @Test public void popPostconditionFulfilled() throws Exception { stack.push("string"); stack.pop(); } @Test public void popPostconditionViolatedByChangingValues() throws Exception { Stack<String> brokenStack = new Stack<String>(10) { @Override public void pop() { super.pop(); try { List<String> values = getPrivateValuesFieldFromStack(this); values.set(0, "BROKEN"); } catch (Exception e) { throw new RuntimeException(e); } }; }; brokenStack.push("string 1"); brokenStack.push("string 2"); thrown.expect(AssertionError.class); thrown.expectMessage("values unchanged"); brokenStack.pop(); } @Test public void topPostconditionFulfilled() throws Exception { String x = "teststring"; stack.push(x); assertThat(stack.top(), is(sameInstance(x))); } @Test public void topPostconditionViolatedByReturningFifoInsteadOfLifo() throws Exception { Stack<String> brokenStack = new Stack<String>(2) { @Override public String top() { return get(0); } }; List<String> values = getPrivateValuesFieldFromStack(brokenStack); values.add("first"); values.add("second"); thrown.expect(AssertionError.class); thrown.expectMessage(containsString("result == top_item")); brokenStack.top(); } @Test public void isFullPostconditionFulfilled() throws Exception { stack.isFull(); stack.push("first"); stack.isFull(); stack.push("last"); stack.isFull(); } @Test public void isFullPostcondtionViolatedInCaseOfFullStack() throws Exception { Stack<String> brokenStack = new Stack<String>(2) { @Override public boolean isFull() { return count() == 10; }; }; brokenStack.isFull(); brokenStack.push("first"); brokenStack.push("second"); thrown.expect(AssertionError.class); thrown.expectMessage(containsString("count < capacity")); brokenStack.isFull(); } @Test public void isFullPostcondtionViolatedInCaseOfNotFullStack() throws Exception { Stack<String> brokenStack = new Stack<String>(2) { @Override public boolean isFull() { return true; }; }; thrown.expect(AssertionError.class); thrown.expectMessage(containsString("count == capacity")); brokenStack.isFull(); } @Test public void isEmptyPostconditionFulfilled() throws Exception { stack.isEmpty(); stack.push("first"); stack.isEmpty(); } @Test public void isEmptyPostcondtionViolatedInCaseOfNotEmptyStack() throws Exception { Stack<String> brokenStack = new Stack<String>(2) { @Override public boolean isEmpty() { return true; }; }; brokenStack.isEmpty(); brokenStack.push("first"); thrown.expect(AssertionError.class); thrown.expectMessage(containsString("count == 0")); brokenStack.isEmpty(); } @Test public void isEmptyPostcondtionViolatedInCaseOfEmptyStack() throws Exception { Stack<String> brokenStack = new Stack<String>(2) { @Override public boolean isEmpty() { return false; }; }; thrown.expect(AssertionError.class); thrown.expectMessage(containsString("count > 0")); brokenStack.isEmpty(); } @Test public void capacityPostconditionFulfilled() throws Exception { stack.capacity(); } @Test public void capacityPostconditionViolatedWithAnonymousSubclass() throws Exception { Stack<String> brokenStack = new Stack<String>(1) { // this condition is necessary because capacity() is already called during the verification of // the postcondition of the constructor of Stack<T> // and we want the constructor call to pass the contract @Override public int capacity() { if (new Exception().getStackTrace()[1].getClassName().equals(NNPostS4Test.class.getName())) { return -1; } return super.capacity(); } }; thrown.expect(AssertionError.class); thrown.expectMessage(containsString("result > 0")); brokenStack.capacity(); } @Test public void capacityPostconditionViolatedWithBrokenStack() throws Exception { stack = new BrokenStack<String>(1); thrown.expect(AssertionError.class); thrown.expectMessage(containsString("result > 0")); stack.capacity(); } @Test public void capacityPostconditionViolatedWithBrokenStackWithoutClassContract() throws Exception { BrokenStackWithoutClassContract<String> brokenStack = new BrokenStackWithoutClassContract<String>(1); thrown.expect(AssertionError.class); thrown.expectMessage(containsString("result > 0")); brokenStack.capacity(); } private static class BrokenStack<T> extends Stack<T> { public BrokenStack(int capacity) { super(capacity); } @Override public int capacity() { if (new Exception().getStackTrace()[1].getClassName().equals(NNPostS4Test.class.getName())) { return -1; } return super.capacity(); } } private static class BrokenStackWithoutClassContract<T> implements StackSpec<T> { private Stack<T> delegatee; public BrokenStackWithoutClassContract(int capacity) { delegatee = new Stack<T>(capacity); } @Override public int capacity() { return -1; } // methods delegated to delegatee @Override public int count() { return delegatee.count(); } @Override public void push(T x) { delegatee.push(x); } @Override public void pop() { delegatee.pop(); } @Override public T top() { return delegatee.top(); } @Override public boolean isFull() { return delegatee.isFull(); } @Override public boolean isEmpty() { return delegatee.isEmpty(); } @Override public T get(int index) { return delegatee.get(index); } } private <T> List<T> getPrivateValuesFieldFromStack(Stack<T> stack) { Field valuesField; try { valuesField = Stack.class.getDeclaredField("values"); valuesField.setAccessible(true); @SuppressWarnings("unchecked") List<T> values = (List<T>) valuesField.get(stack); return values; } catch (Exception e) { throw new RuntimeException(e); } } }