annis.sqlgen.TestTableAccessStrategy.java Source code

Java tutorial

Introduction

Here is the source code for annis.sqlgen.TestTableAccessStrategy.java

Source

/*
 * 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);
            }

        };
    }

}