package scotch.compiler.syntax.scope; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static scotch.compiler.syntax.definition.Import.moduleImport; import static scotch.compiler.syntax.scope.Scope.scope; import static scotch.compiler.text.SourceLocation.NULL_SOURCE; import static scotch.symbol.Symbol.qualified; import static scotch.symbol.Symbol.unqualified; import java.util.Optional; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import scotch.symbol.Symbol; import scotch.symbol.SymbolEntry; import scotch.symbol.SymbolResolver; import scotch.compiler.syntax.type.Types; import scotch.compiler.syntax.util.SymbolGenerator; @RunWith(MockitoJUnitRunner.class) public class RootScopeTest { @Mock private SymbolResolver resolver; @Mock private SymbolGenerator symbolGenerator; private Scope rootScope; private Scope module1Scope; private Scope module2Scope; @Before public void setUp() { when(resolver.getEntry(any(Symbol.class))).thenReturn(Optional.empty()); rootScope = scope(symbolGenerator, resolver); module1Scope = rootScope.enterScope("scotch.module1"); module2Scope = rootScope.enterScope("scotch.module2"); } @Test public void nothingShouldBeAnOperator() { assertThat(rootScope.isOperator_(qualified("scotch.module1", "fn")), is(false)); assertThat(rootScope.isOperator_(unqualified("fn")), is(false)); } @Test public void shouldDelegateQualifyingSymbolToSiblingModule() { module1Scope.enterScope(emptyList()).defineValue(qualified("scotch.module1", "fn"), Types.t(1)); module2Scope.enterScope(asList(moduleImport(NULL_SOURCE, "scotch.module1"))); assertThat(module2Scope.qualify(unqualified("fn")), is(Optional.of(qualified("scotch.module1", "fn")))); } @Test public void shouldDelegateToResolver_whenQualifyingSymbolNotFoundInModules() { Symbol symbol = qualified("scotch.module1", "fn"); when(resolver.isDefined(symbol)).thenReturn(true); rootScope.qualify(symbol); verify(resolver).isDefined(symbol); } @Test public void shouldDelegateToResolver_whenGettingEntryForQualifiedSymbolNotFoundInModules() { Symbol symbol = qualified("scotch.module1", "fn"); when(resolver.isDefined(symbol)).thenReturn(true); when(resolver.getEntry(symbol)).thenReturn(Optional.of(mock(SymbolEntry.class))); rootScope.getEntry(symbol); verify(resolver).getEntry(symbol); } @Test public void leavingChildScopeShouldGiveBackRootScope() { assertThat(rootScope.enterScope("test.module").leaveScope(), sameInstance(rootScope)); } @Test(expected = IllegalStateException.class) public void shouldThrow_whenEnteringScopeWithoutModuleName() { rootScope.enterScope(); } @Test(expected = IllegalStateException.class) public void shouldThrow_whenGettingOperator() { rootScope.getOperator(qualified("scotch.module1", "+")); } @Test(expected = IllegalStateException.class) public void shouldThrow_whenGettingValue() { rootScope.getValue(qualified("scotch.module1", "fn")); } @Test(expected = IllegalStateException.class) public void shouldThrow_whenLeavingScope() { rootScope.leaveScope(); } @Test public void shouldBeEmpty_whenQualifyingUndeclaredSymbol() { assertThat(rootScope.qualify(qualified("scotch.module1", "fn")), is(Optional.empty())); } @Test public void shouldThrowBeEmpty_whenQualifyingUnqualifiedSymbol() { assertThat(rootScope.qualify(unqualified("fn")), is(Optional.empty())); } }