Java tutorial
/* * Copyright 2009-2011 Collaborative Research Centre SFB 632 * * 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 annis.sqlgen; import static annis.sqlgen.TableAccessStrategy.COMPONENT_TABLE; import static annis.sqlgen.TableAccessStrategy.EDGE_ANNOTATION_TABLE; import static annis.sqlgen.TableAccessStrategy.NODE_ANNOTATION_TABLE; import static annis.sqlgen.TableAccessStrategy.NODE_TABLE; import static annis.sqlgen.TableAccessStrategy.RANK_TABLE; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.commons.collections.Bag; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import annis.model.QueryNode; import annis.model.QueryAnnotation; public class TestTableAccessStrategy { private Map<String, Integer> expected; private TableAccessStrategy tableAccessStrategy; @Mock private QueryNode node23; @Mock private Set<QueryAnnotation> annotations; // // TODO: refactor into ViewConstrainedTableAccessStrategy // @Mock private CorpusSelectionStrategy corpusSelectionStrategy; @Before public void setup() { initMocks(this); when(node23.getId()).thenReturn(23L); when(annotations.size()).thenReturn(3); // // TODO: refactor into ViewConstrainedTableAccessStrategy // when(corpusSelectionStrategy.viewName(anyString())).thenAnswer(new Answer<String>() { // // // return string unchanged // public String answer(InvocationOnMock invocation) throws Throwable { // return (String) invocation.getArguments()[0]; // } // // }); // tableAccessStrategy = new TableAccessStrategy(node23, corpusSelectionStrategy); tableAccessStrategy = new TableAccessStrategy(node23); expected = new HashMap<String, Integer>(); expected.put(NODE_TABLE, 0); expected.put(RANK_TABLE, 0); expected.put(COMPONENT_TABLE, 0); expected.put(NODE_ANNOTATION_TABLE, 0); expected.put(EDGE_ANNOTATION_TABLE, 0); } ///// used source tables // a fresh node always uses the struct table @Test public void computeSourceTablesFreshNode() { expectTableCount(NODE_TABLE, 1); assertUsedTables(); } // root node uses rank table @Test public void computeSourceTablesRootNode() { when(node23.isRoot()).thenReturn(true); expectTableCount(NODE_TABLE, 1); expectTableCount(RANK_TABLE, 1); expectTableCount(COMPONENT_TABLE, 1); assertUsedTables(); } // if rank and component table are materialized, root node only uses struct and rank table @Test public void computeSourceTablesRootNodeComponentAliasedToRank() { when(node23.isRoot()).thenReturn(true); tableAccessStrategy.addTableAlias(COMPONENT_TABLE, RANK_TABLE); expectTableCount(NODE_TABLE, 1); expectTableCount(RANK_TABLE, 1); assertUsedTables(); } // if rank, component and struct table are materialized, root node only uses struct table @Test public void computeSourceTablesRootNodeRankAndComponentAliasedToStruct() { when(node23.isRoot()).thenReturn(true); tableAccessStrategy.addTableAlias(COMPONENT_TABLE, NODE_TABLE); tableAccessStrategy.addTableAlias(RANK_TABLE, NODE_TABLE); expectTableCount(NODE_TABLE, 1); assertUsedTables(); } // root node uses rank and component table @Test public void computeSourceTablesPartOfEdge() { when(node23.isPartOfEdge()).thenReturn(true); expectTableCount(NODE_TABLE, 1); expectTableCount(RANK_TABLE, 1); expectTableCount(COMPONENT_TABLE, 1); assertUsedTables(); } // if rank, component and struct table are materialized, root node only uses struct table @Test public void computeSourceTablesPartOfEdgeRankAliasedToStruct() { when(node23.isPartOfEdge()).thenReturn(true); tableAccessStrategy.addTableAlias(RANK_TABLE, NODE_TABLE); tableAccessStrategy.addTableAlias(COMPONENT_TABLE, NODE_TABLE); expectTableCount(NODE_TABLE, 1); assertUsedTables(); } // join node annotations @Test public void computeSourceTablesNodeAnnotations() { when(node23.getNodeAnnotations()).thenReturn(annotations); expectTableCount(NODE_TABLE, 1); expectTableCount(NODE_ANNOTATION_TABLE, 3); assertUsedTables(); } // if NODE_ANNOTATION_TABLE and STRUCT_TABLE are materialized, only use as many tables as there are annotations @Test public void computeSourceTablesNodeAnnotationsAliasedToStruct() { when(node23.getNodeAnnotations()).thenReturn(annotations); tableAccessStrategy.addTableAlias(NODE_ANNOTATION_TABLE, NODE_TABLE); expectTableCount(NODE_TABLE, 3); assertUsedTables(); } // join edge annotations @Test public void computeSourceTablesEdgeAnnotations() { when(node23.getEdgeAnnotations()).thenReturn(annotations); expectTableCount(NODE_TABLE, 1); expectTableCount(RANK_TABLE, 1); expectTableCount(COMPONENT_TABLE, 1); expectTableCount(EDGE_ANNOTATION_TABLE, 3); assertUsedTables(); } // if edge_annotation, component and rank are materialized, only use as many tables as there are annotations @Test public void computeSourceTablesEdgeAnnotationsAliasedToRank() { when(node23.getEdgeAnnotations()).thenReturn(annotations); tableAccessStrategy.addTableAlias(COMPONENT_TABLE, RANK_TABLE); tableAccessStrategy.addTableAlias(EDGE_ANNOTATION_TABLE, RANK_TABLE); expectTableCount(NODE_TABLE, 1); expectTableCount(RANK_TABLE, 3); assertUsedTables(); } // if edge_annotation, rank, component and struct are materialized, only use as many tables as there are annotations @Test public void computeSourceTablesEdgeAnnotationsAliasedToStruct() { when(node23.getEdgeAnnotations()).thenReturn(annotations); tableAccessStrategy.addTableAlias(COMPONENT_TABLE, NODE_TABLE); tableAccessStrategy.addTableAlias(RANK_TABLE, NODE_TABLE); tableAccessStrategy.addTableAlias(EDGE_ANNOTATION_TABLE, NODE_TABLE); expectTableCount(NODE_TABLE, 3); assertUsedTables(); } // all tables are materialized, use as many tables as there are annotations @Test public void computeSourceTablesNodeAndEdgeAnnotationsAliasedToStruct() { when(node23.isRoot()).thenReturn(true); when(node23.isPartOfEdge()).thenReturn(true); when(node23.getNodeAnnotations()).thenReturn(annotations); when(node23.getEdgeAnnotations()).thenReturn(annotations); tableAccessStrategy.addTableAlias(RANK_TABLE, NODE_TABLE); tableAccessStrategy.addTableAlias(COMPONENT_TABLE, NODE_TABLE); tableAccessStrategy.addTableAlias(EDGE_ANNOTATION_TABLE, NODE_TABLE); tableAccessStrategy.addTableAlias(NODE_ANNOTATION_TABLE, NODE_TABLE); expectTableCount(NODE_TABLE, 6); assertUsedTables(); } ///// table and column names // table with default name @Test public void tableName() { assertEquals("foo", tableAccessStrategy.tableName("foo")); } // table with mapped name (materialization) @Test public void tableNameAliased() { tableAccessStrategy.addTableAlias("foo", "FOO"); assertEquals("FOO", tableAccessStrategy.tableName("foo")); } // column with default name @Test public void columnName() { assertEquals("bar", tableAccessStrategy.columnName("foo", "bar")); } // column with mapped name @Test public void columnNameAliased() { tableAccessStrategy.addColumnAlias("foo", "bar", "BAR"); assertEquals("BAR", tableAccessStrategy.columnName("foo", "bar")); } ///// table and column aliases // only use one struct table @Test public void aliasedTableStruct() { assertSingleTable(NODE_TABLE); } // use one table per node annotation @Test public void aliasedTableNodeAnnotations() { when(node23.getNodeAnnotations()).thenReturn(annotations); assertMultipleTables(NODE_ANNOTATION_TABLE, 3); } // use one table per edge annotation @Test public void aliasedTableEdgeAnnotations() { when(node23.getEdgeAnnotations()).thenReturn(annotations); assertMultipleTables(EDGE_ANNOTATION_TABLE, 3); } // node and edge annotations use different table @Test public void aliasedTableNodeAndEdgeAnnotationsDifferentTable() { when(node23.getNodeAnnotations()).thenReturn(annotations); when(node23.getEdgeAnnotations()).thenReturn(annotations); assertMultipleTables(NODE_ANNOTATION_TABLE, 3); assertMultipleTables(EDGE_ANNOTATION_TABLE, 3); } // node and edge annotations use same table, alias first node annotations then edge annotations @SuppressWarnings("unchecked") @Test public void aliasedTableNodeAndEdgeAnnotationsSameTable() { final int NODE_ANNOTATION_COUNT = 2; Set<QueryAnnotation> nodeAnnotations = mock(Set.class); when(nodeAnnotations.size()).thenReturn(NODE_ANNOTATION_COUNT); when(node23.getNodeAnnotations()).thenReturn(nodeAnnotations); final int EDGE_ANNOTATION_COUNT = 3; Set<QueryAnnotation> edgeAnnotations = mock(Set.class); when(edgeAnnotations.size()).thenReturn(EDGE_ANNOTATION_COUNT); when(node23.getEdgeAnnotations()).thenReturn(edgeAnnotations); tableAccessStrategy.addTableAlias(NODE_ANNOTATION_TABLE, NODE_TABLE); tableAccessStrategy.addTableAlias(EDGE_ANNOTATION_TABLE, NODE_TABLE); assertMultipleAliasedTables(NODE_ANNOTATION_TABLE, NODE_ANNOTATION_COUNT, NODE_TABLE, 0); assertMultipleAliasedTables(EDGE_ANNOTATION_TABLE, EDGE_ANNOTATION_COUNT, NODE_TABLE, NODE_ANNOTATION_COUNT - 1); } // only one struct table can be used @Test(expected = IllegalArgumentException.class) public void onlyOneStructTable() { tableAccessStrategy.aliasedTable(NODE_TABLE, 2); } // only one rank table can be used @Test(expected = IllegalArgumentException.class) public void onlyOneRankTable() { tableAccessStrategy.aliasedTable(RANK_TABLE, 2); } // throw exception if trying to access a table for an unknown edge annotation @Test(expected = IllegalArgumentException.class) public void edgeAnnotationsUnknownAnnotation() { when(node23.getEdgeAnnotations()).thenReturn(annotations); tableAccessStrategy.aliasedTable(EDGE_ANNOTATION_TABLE, 4); } // column alias for a table that is only used once @Test public void aliasedColumn() { tableAccessStrategy.addColumnAlias(NODE_TABLE, "bar", "BAR"); assertEquals(NODE_TABLE + "23.BAR", tableAccessStrategy.aliasedColumn(NODE_TABLE, "bar")); } // column alias for a table that is used many times @Test public void aliasedColumnManyTables() { when(node23.getNodeAnnotations()).thenReturn(annotations); tableAccessStrategy.addTableAlias(NODE_ANNOTATION_TABLE, NODE_TABLE); tableAccessStrategy.addColumnAlias(NODE_TABLE, "foo", "FOO"); tableAccessStrategy.addColumnAlias(NODE_ANNOTATION_TABLE, "bar", "BAR"); assertEquals(NODE_TABLE + "23_1.FOO", tableAccessStrategy.aliasedColumn(NODE_TABLE, "foo")); assertEquals(NODE_TABLE + "23_1.BAR", tableAccessStrategy.aliasedColumn(NODE_ANNOTATION_TABLE, "bar", 1)); assertEquals(NODE_TABLE + "23_2.BAR", tableAccessStrategy.aliasedColumn(NODE_ANNOTATION_TABLE, "bar", 2)); assertEquals(NODE_TABLE + "23_3.BAR", tableAccessStrategy.aliasedColumn(NODE_ANNOTATION_TABLE, "bar", 3)); } ///// table usage // fresh node uses no table (except struct) @Test public void usedTablesFreshNode() { assertThat(tableAccessStrategy.usesEdgeAnnotationTable(), is(false)); assertThat(tableAccessStrategy.usesNodeAnnotationTable(), is(false)); } // if node has an edge, use rank table @Test public void usesRankTableEdge() { when(node23.isPartOfEdge()).thenReturn(true); assertThat(tableAccessStrategy.usesRankTable(), is(true)); } // if node is root, use rank table @Test public void usesRankTableRoot() { when(node23.isRoot()).thenReturn(true); assertThat(tableAccessStrategy.usesRankTable(), is(true)); } // if node has edge annotations, use rank table and edge annotation table @Test public void usedTableEdgeAnnotation() { when(node23.getEdgeAnnotations()).thenReturn(annotations); assertThat(tableAccessStrategy.usesRankTable(), is(true)); assertThat(tableAccessStrategy.usesEdgeAnnotationTable(), is(true)); } // if node has node annotations, use node annotation table @Test public void usedTableNodeAnnotation() { when(node23.getNodeAnnotations()).thenReturn(annotations); assertThat(tableAccessStrategy.usesNodeAnnotationTable(), is(true)); } // join of two tables is materialized @Test public void isMaterializedTrue() { tableAccessStrategy.addTableAlias(RANK_TABLE, NODE_TABLE); assertThat(tableAccessStrategy.isMaterialized(RANK_TABLE, NODE_TABLE), is(true)); } // join of two tables is not materialized @Test public void isMaterializedFalse() { assertThat(tableAccessStrategy.isMaterialized(RANK_TABLE, NODE_TABLE), is(false)); } // // TODO: refactor into ViewConstrainedTableAccessStrategy // // use view name if corpus selection is done through views // @Test // public void tableNameUsesView() { // final String STRUCT_TABLE_VIEW = "STRUCT_TABLE_VIEW"; // when(corpusSelectionStrategy.viewName(NODE_TABLE)).thenReturn(STRUCT_TABLE_VIEW); // assertThat(tableAccessStrategy.tableName(NODE_TABLE), is(STRUCT_TABLE_VIEW)); // } // // TODO: refactor into ViewConstrainedTableAccessStrategy // // access to the aliased name, not the view // @Test // public void tableNameDontUseView() { // final String STRUCT_TABLE_VIEW = "STRUCT_TABLE_VIEW"; // when(corpusSelectionStrategy.viewName(NODE_TABLE)).thenReturn(STRUCT_TABLE_VIEW); // assertThat(tableAccessStrategy.tableName(NODE_TABLE, false), is(NODE_TABLE)); // } ///// private helper private void expectTableCount(String table, int count) { expected.put(table, count); } private void assertUsedTables() { assertThat(tableAccessStrategy.computeSourceTables(), hasTables(expected)); } private void assertSingleTable(String table) { assertThat(tableAccessStrategy.aliasedTable(table, 1), is(table + node23.getId())); } private void assertMultipleTables(String table, int count) { for (int i = 1; i <= count; ++i) assertThat(tableAccessStrategy.aliasedTable(table, i), is(table + node23.getId() + "_" + i)); } private void assertMultipleAliasedTables(String table, int count, String alias, int offset) { for (int i = 2; i <= count; ++i) assertThat(tableAccessStrategy.aliasedTable(table, i), is(alias + node23.getId() + "_" + (i + offset))); } private Matcher<Bag> hasTables(final Map<String, Integer> expectedTables) { return new TypeSafeMatcher<Bag>() { @Override public boolean matchesSafely(Bag item) { for (String table : expectedTables.keySet()) { if (item.getCount(table) != expectedTables.get(table)) return false; } return true; } public void describeTo(Description description) { description.appendValue(expectedTables); } }; } }