package org.smoothbuild.parse; import static org.hamcrest.core.IsSame.sameInstance; import static org.smoothbuild.lang.function.base.Name.name; import static org.smoothbuild.lang.message.CodeLocation.codeLocation; import static org.testory.Testory.given; import static org.testory.Testory.mock; import static org.testory.Testory.thenCalled; import static org.testory.Testory.thenEqual; import static org.testory.Testory.thenReturned; import static org.testory.Testory.thenThrown; import static org.testory.Testory.when; import java.util.NoSuchElementException; import org.junit.Test; import org.smoothbuild.cli.Console; import org.smoothbuild.lang.function.base.Name; import org.smoothbuild.lang.message.CodeLocation; import com.google.common.collect.ImmutableSet; public class DependencyStackTest { private final Name name1 = name("function1"); private final Name name2 = name("function2"); private final Name name3 = name("function3"); private final Name name4 = name("function4"); private DependencyStack dependencyStack; private DependencyStackElem elem1; private DependencyStackElem elem2; private DependencyStackElem elem3; private Console console; @Test public void stack_is_empty_initially() { given(dependencyStack = new DependencyStack()); when(dependencyStack).isEmpty(); thenReturned(true); } @Test public void stack_is_not_empty_after_pushing_one_element() throws Exception { given(dependencyStack = new DependencyStack()); given(dependencyStack).push(elem()); when(dependencyStack).isEmpty(); thenReturned(false); } @Test public void stack_is_empty_after_pushing_and_popping() { given(dependencyStack = new DependencyStack()); given(dependencyStack).push(elem()); given(dependencyStack).pop(); when(dependencyStack).isEmpty(); thenReturned(true); } @Test public void pushed_element_is_returned_by_pop() throws Exception { given(dependencyStack = new DependencyStack()); given(elem1 = elem()); given(dependencyStack).push(elem1); when(dependencyStack).pop(); thenReturned(elem1); } @Test public void elements_are_poped_in_filo_order() throws Exception { given(dependencyStack = new DependencyStack()); given(elem1 = elem()); given(elem2 = elem()); given(elem3 = elem()); given(dependencyStack).push(elem1); given(dependencyStack).push(elem2); given(dependencyStack).push(elem3); when(dependencyStack).pop(); thenReturned(sameInstance(elem3)); when(dependencyStack).pop(); thenReturned(sameInstance(elem2)); when(dependencyStack).pop(); thenReturned(sameInstance(elem1)); } @Test public void poping_from_empty_stack_throws_exception() throws Exception { given(dependencyStack = new DependencyStack()); when(dependencyStack).pop(); thenThrown(NoSuchElementException.class); } @Test public void poping_after_all_elements_has_been_removed_throws_exception() throws Exception { given(dependencyStack = new DependencyStack()); given(dependencyStack).push(elem()); given(dependencyStack).pop(); when(dependencyStack).pop(); thenThrown(NoSuchElementException.class); } @Test public void peek_returns_top_element() throws Exception { given(dependencyStack = new DependencyStack()); given(dependencyStack).push(elem()); given(elem2 = elem()); given(dependencyStack).push(elem2); when(dependencyStack).peek(); thenReturned(sameInstance(elem2)); } @Test public void peek_does_not_remove_element() throws Exception { given(dependencyStack = new DependencyStack()); given(elem1 = elem()); given(dependencyStack).push(elem1); when(dependencyStack).peek(); thenEqual(dependencyStack.pop(), elem1); } @Test public void peeking_on_empty_stack_throws_exception() throws Exception { given(dependencyStack = new DependencyStack()); when(dependencyStack).peek(); thenThrown(NoSuchElementException.class); } @Test public void peeking_after_all_elements_has_been_removed_throws_exception() throws Exception { given(dependencyStack = new DependencyStack()); given(dependencyStack).push(elem()); given(dependencyStack).pop(); when(dependencyStack).peek(); thenThrown(NoSuchElementException.class); } @Test public void create_cycle_error() throws Exception { given(dependencyStack = new DependencyStack()); given(dependencyStack).push(elem(name1, name2, 1)); given(dependencyStack).push(elem(name2, name3, 2)); given(dependencyStack).push(elem(name3, name4, 3)); given(dependencyStack).push(elem(name4, name2, 4)); given(console = mock(Console.class)); when(dependencyStack).reportAndThrowCycleException(console); thenThrown(ParsingException.class); thenCalled(console).error(codeLocation(2), "Function call graph contains cycle:\n" + name2.value() + codeLocation(2) + " -> " + name3.value() + "\n" + name3.value() + codeLocation(3) + " -> " + name4.value() + "\n" + name4.value() + codeLocation(4) + " -> " + name2.value() + "\n"); } @Test public void create_cycle_error_for_recursive_call() throws Exception { given(dependencyStack = new DependencyStack()); given(dependencyStack).push(elem(name1, name2, 1)); given(dependencyStack).push(elem(name2, name2, 2)); given(console = mock(Console.class)); when(dependencyStack).reportAndThrowCycleException(console); thenThrown(ParsingException.class); thenCalled(console).error(codeLocation(2), "Function call graph contains cycle:\n" + name2.value() + codeLocation(2) + " -> " + name2.value() + "\n"); } private DependencyStackElem elem(Name from, Name to, int location) { ImmutableSet<Dependency> deps = ImmutableSet.of(); DependencyStackElem elem = new DependencyStackElem(from, deps); elem.setMissing(new Dependency(CodeLocation.codeLocation(location), to)); return elem; } private static DependencyStackElem elem() { return new DependencyStackElem(name("name"), ImmutableSet.<Dependency> of()); } }