[ASTERIXDB-2204][STO] Add an abstract test case for an IIndexCursor
- user model changes: no
- storage format changes: no
- interface changes: no
details:
- Add a base test for all implementations of IIndexCursor
- The test case includes the following scenarios:
--- testNormalLifeCycle
--- testCreateDestroySucceed
--- testDoubleOpenFails
--- testCloseWithoutOpenFails
--- testDoubleCloseFails
--- testHasNextBeforeOpenFails
--- testHasNextAfterCloseFails
--- testNextBeforeOpenFails
--- testNextAfterCloseFails
--- testDestroyWhileOpenFails
--- testOpenAfterDestroyFails
--- testCloseAfterDestroyFails
--- testNextAfterDestroyFails
--- testHasNextAfterDestroyFails
--- testGetTupleReturnsNullAfterDestroy
- Add a base implementation of the interface that conforms
to the expected lifecycle and can be extended by
any cursor implementation.
- The test is run on the base implementation.
Change-Id: I7c32dd560367d84403ffa3d9cb69ff80d715fdc5
Reviewed-on: https://asterix-gerrit.ics.uci.edu/2291
Sonar-Qube: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Michael Blow <mblow@apache.org>
diff --git a/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/unit/EnforcedIndexCursorTest.java b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/unit/EnforcedIndexCursorTest.java
new file mode 100644
index 0000000..8fe689e
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/unit/EnforcedIndexCursorTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.hyracks.tests.unit;
+
+import org.apache.hyracks.storage.common.EnforcedIndexCursor;
+import org.apache.hyracks.storage.common.ICursorInitialState;
+import org.apache.hyracks.storage.common.IIndexCursor;
+import org.apache.hyracks.storage.common.ISearchPredicate;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class EnforcedIndexCursorTest extends IIndexCursorTest {
+ @Override
+ protected List<ISearchPredicate> createSearchPredicates() {
+ List<ISearchPredicate> predicates = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ predicates.add(Mockito.mock(ISearchPredicate.class));
+ }
+ return predicates;
+ }
+
+ @Override
+ protected ICursorInitialState createCursorInitialState() {
+ return Mockito.mock(ICursorInitialState.class);
+ }
+
+ @Override
+ protected IIndexCursor createCursor() {
+ return new EnforcedIndexCursor();
+ }
+}
diff --git a/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/unit/IIndexCursorTest.java b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/unit/IIndexCursorTest.java
new file mode 100644
index 0000000..4d0f287
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-examples/hyracks-integration-tests/src/test/java/org/apache/hyracks/tests/unit/IIndexCursorTest.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.hyracks.tests.unit;
+
+import org.apache.hyracks.storage.common.ICursorInitialState;
+import org.apache.hyracks.storage.common.IIndexCursor;
+import org.apache.hyracks.storage.common.ISearchPredicate;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * This is a test class that forms the basis for unit tests of different implementations of the IIndexCursor interface
+ */
+public abstract class IIndexCursorTest {
+ @Test
+ public void testNormalLifeCycle() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ for (ISearchPredicate predicate : predicates) {
+ cursor.open(initialState, predicate);
+ while (cursor.hasNext()) {
+ cursor.next();
+ }
+ cursor.close();
+ }
+ cursor.destroy();
+ }
+
+ @Test
+ public void testCreateDestroySucceed() throws Exception {
+ IIndexCursor cursor = createCursor();
+ cursor.destroy();
+ }
+
+ @Test
+ public void testDoubleOpenFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ cursor.open(initialState, predicates.get(0));
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.open(initialState, predicates.get(0));
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ cursor.close();
+ cursor.destroy();
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testCloseWithoutOpenFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.close();
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ cursor.destroy();
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testDoubleCloseFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ cursor.open(initialState, predicates.get(0));
+ cursor.close();
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.close();
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ cursor.destroy();
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testHasNextBeforeOpenFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.hasNext();
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ cursor.destroy();
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testHasNextAfterCloseFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ cursor.open(initialState, predicates.get(0));
+ cursor.close();
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.hasNext();
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ cursor.destroy();
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testNextBeforeOpenFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.next();
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ cursor.destroy();
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testNextAfterCloseFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ cursor.open(initialState, predicates.get(0));
+ cursor.close();
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.next();
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ cursor.destroy();
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testDestroyWhileOpenFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ cursor.open(initialState, predicates.get(0));
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.destroy();
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ cursor.close();
+ cursor.destroy();
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testOpenAfterDestroyFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ cursor.open(initialState, predicates.get(0));
+ cursor.close();
+ cursor.destroy();
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.open(initialState, predicates.get(0));
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testCloseAfterDestroyFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ cursor.open(initialState, predicates.get(0));
+ cursor.close();
+ cursor.destroy();
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.close();
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testNextAfterDestroyFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ cursor.open(initialState, predicates.get(0));
+ cursor.close();
+ cursor.destroy();
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.next();
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testHasNextAfterDestroyFails() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ cursor.open(initialState, predicates.get(0));
+ cursor.close();
+ cursor.destroy();
+ boolean expectedExceptionThrown = false;
+ try {
+ cursor.hasNext();
+ } catch (Exception e) {
+ expectedExceptionThrown = true;
+ }
+ Assert.assertTrue(expectedExceptionThrown);
+ }
+
+ @Test
+ public void testGetTupleReturnsNullAfterDestroy() throws Exception {
+ IIndexCursor cursor = createCursor();
+ ICursorInitialState initialState = createCursorInitialState();
+ List<ISearchPredicate> predicates = createSearchPredicates();
+ cursor.open(initialState, predicates.get(0));
+ cursor.close();
+ cursor.destroy();
+ Assert.assertNull(cursor.getTuple());
+ }
+
+ protected abstract List<ISearchPredicate> createSearchPredicates();
+
+ protected abstract ICursorInitialState createCursorInitialState();
+
+ protected abstract IIndexCursor createCursor();
+}
diff --git a/hyracks-fullstack/hyracks/hyracks-storage-common/src/main/java/org/apache/hyracks/storage/common/EnforcedIndexCursor.java b/hyracks-fullstack/hyracks/hyracks-storage-common/src/main/java/org/apache/hyracks/storage/common/EnforcedIndexCursor.java
new file mode 100644
index 0000000..62a56e6
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-storage-common/src/main/java/org/apache/hyracks/storage/common/EnforcedIndexCursor.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.hyracks.storage.common;
+
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.dataflow.common.data.accessors.ITupleReference;
+
+public class EnforcedIndexCursor implements IIndexCursor {
+ enum State {
+ CLOSED,
+ OPENED,
+ DESTROYED
+ }
+
+ private State state = State.CLOSED;
+
+ @Override
+ public void open(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
+ if (state != State.CLOSED) {
+ throw new IllegalStateException("Cannot open a cursor in the state " + state);
+ }
+ doOpen(initialState, searchPred);
+ state = State.OPENED;
+ }
+
+ protected void doOpen(ICursorInitialState initialState, ISearchPredicate searchPred) throws HyracksDataException {
+ // Do nothing
+ }
+
+ @Override
+ public boolean hasNext() throws HyracksDataException {
+ if (state != State.OPENED) {
+ throw new IllegalStateException("Cannot call hasNext() on a cursor in the state " + state);
+ }
+ return doHasNext();
+ }
+
+ protected boolean doHasNext() throws HyracksDataException {
+ return false;
+ }
+
+ @Override
+ public void next() throws HyracksDataException {
+ if (state != State.OPENED) {
+ throw new IllegalStateException("Cannot call next() on a cursor in the state " + state);
+ }
+ doNext();
+ }
+
+ protected void doNext() throws HyracksDataException {
+ // Do nothing
+ }
+
+ @Override
+ public void destroy() throws HyracksDataException {
+ if (state != State.CLOSED) {
+ throw new IllegalStateException("Cannot destroy a cursor in the state " + state);
+ }
+ doDestroy();
+ state = State.DESTROYED;
+ }
+
+ protected void doDestroy() throws HyracksDataException {
+ // Do nothing
+ }
+
+ @Override
+ public void close() throws HyracksDataException {
+ if (state != State.OPENED) {
+ throw new IllegalStateException("Cannot close a cursor in the state " + state);
+ }
+ doClose();
+ state = State.CLOSED;
+ }
+
+ private void doClose() throws HyracksDataException {
+ // Do nothing
+ }
+
+ @Override
+ public ITupleReference getTuple() {
+ return null;
+ }
+
+ @Override
+ public ITupleReference getFilterMinTuple() {
+ return null;
+ }
+
+ @Override
+ public ITupleReference getFilterMaxTuple() {
+ return null;
+ }
+}