From 318ac9726a5354e9192baa3cc07308524bf81cec Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Sat, 7 Nov 2015 14:17:17 -0500 Subject: [PATCH 01/15] ACCUMULO-626 Initial outline of the test harness. --- iterator-test-harness/.gitignore | 26 ++++++ iterator-test-harness/pom.xml | 52 ++++++++++++ .../iteratortest/IteratorTestCase.java | 35 ++++++++ .../iteratortest/IteratorTestInput.java | 77 +++++++++++++++++ .../iteratortest/IteratorTestOutput.java | 82 ++++++++++++++++++ .../iteratortest/IteratorTestReport.java | 63 ++++++++++++++ .../iteratortest/IteratorTestRunner.java | 83 +++++++++++++++++++ .../src/test/resources/log4j.properties | 22 +++++ pom.xml | 1 + 9 files changed, 441 insertions(+) create mode 100644 iterator-test-harness/.gitignore create mode 100644 iterator-test-harness/pom.xml create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java create mode 100644 iterator-test-harness/src/test/resources/log4j.properties diff --git a/iterator-test-harness/.gitignore b/iterator-test-harness/.gitignore new file mode 100644 index 00000000000..e7d7fb1758a --- /dev/null +++ b/iterator-test-harness/.gitignore @@ -0,0 +1,26 @@ +# 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. + +# Maven ignores +/target/ + +# IDE ignores +/.settings/ +/.project +/.classpath +/.pydevproject +/.idea +/*.iml +/target/ diff --git a/iterator-test-harness/pom.xml b/iterator-test-harness/pom.xml new file mode 100644 index 00000000000..3ddd8411bae --- /dev/null +++ b/iterator-test-harness/pom.xml @@ -0,0 +1,52 @@ + + + + 4.0.0 + + org.apache.accumulo + accumulo-project + 1.8.0-SNAPSHOT + + accumulo-iterator-test-harness + Apache Accumulo Iterator Test Harness + A library for testing Apache Accumulo Iterators. + + + log4j + log4j + + + org.apache.accumulo + accumulo-core + + + org.slf4j + slf4j-api + + + junit + junit + test + + + org.easymock + easymock + test + + + diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java new file mode 100644 index 00000000000..cf750b0d7a1 --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java @@ -0,0 +1,35 @@ +/* + * 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.accumulo.iteratortest; + +import org.apache.accumulo.core.iterators.SortedKeyValueIterator; + +/** + * An interface that accepts some input for testing a {@link SortedKeyValueIterator}, runs the specific implementation + * of the test and returns the outcome from that iterator. + */ +public interface IteratorTestCase { + + /** + * Run the implementation's test against the given input. + * + * @param testInput The input to test. + * @return The output of the test with the input. + */ + IteratorTestOutput test(IteratorTestInput testInput); + +} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java new file mode 100644 index 00000000000..ace223ad3ee --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java @@ -0,0 +1,77 @@ +/* + * 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.accumulo.iteratortest; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; + +import org.apache.accumulo.core.client.IteratorSetting; +import org.apache.accumulo.core.client.Scanner; +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Range; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.iterators.SortedKeyValueIterator; + +/** + * The necessary user-input to invoke a test on a {@link SortedKeyValueIterator}. + */ +public class IteratorTestInput { + + private final Class> iteratorClass; + private final Map iteratorOptions; + private final Range range; + private final SortedMap input; + + /** + * Construct an instance of the test input. + * + * @param iteratorClass The class for the iterator to test + * @param iteratorOptions Options, if any, to provide to the iterator ({@link IteratorSetting}'s Map of properties) + * @param range The Range of data to query ({@link Scanner#setRange(Range)}) + * @param input A sorted collection of Key-Value pairs acting as the table. + */ + public IteratorTestInput(Class> iteratorClass, + Map iteratorOptions, Range range, SortedMap input) { + // Already immutable + this.iteratorClass = Objects.requireNonNull(iteratorClass); + // Make it immutable to the test + this.iteratorOptions = Collections.unmodifiableMap(Objects.requireNonNull(iteratorOptions)); + // Already immutable + this.range = Objects.requireNonNull(range); + // Make it immutable to the test + this.input = Collections.unmodifiableSortedMap((Objects.requireNonNull(input))); + } + + public Class> getIteratorClass() { + return iteratorClass; + } + + public Map getIteratorOptions() { + return iteratorOptions; + } + + public Range getRange() { + return range; + } + + public SortedMap getInput() { + return input; + } + +} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java new file mode 100644 index 00000000000..03b7273b329 --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java @@ -0,0 +1,82 @@ +/* + * 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.accumulo.iteratortest; + +import java.util.Collections; +import java.util.Objects; +import java.util.SortedMap; + +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Value; + +/** + * The expected results from invoking a {@link IteratorTestCase} on a {@link IteratorTestInput}. The output + * will be either a {@link SortedMap} of Keys and Values or an exception but never both. If one of these members + * is null, the other is guaranteed to be non-null. + */ +public class IteratorTestOutput { + + private final SortedMap output; + private final Exception exception; + + /** + * Create an instance of the class. + * + * @param output The sorted collection of Key-Value pairs generated by an Iterator. + */ + public IteratorTestOutput(SortedMap output) { + this.output = Collections.unmodifiableSortedMap(Objects.requireNonNull(output)); + this.exception = null; + } + + public IteratorTestOutput(Exception e) { + this.output = null; + this.exception = Objects.requireNonNull(e); + } + + /** + * Returns the output from the iterator. + * + * @return The sorted Key-Value pairs from an iterator, null if an exception was thrown. + */ + public SortedMap getOutput() { + return output; + } + + /** + * @return True if there is output, false if the output is null. + */ + public boolean hasOutput() { + return null != output; + } + + /** + * Returns the exception thrown by the iterator. + * + * @return The exception thrown by the iterator, null if no exception was thrown. + */ + public Exception getException() { + return exception; + } + + /** + * @return True if there is an exception, null if the iterator successfully generated Key-Value pairs. + */ + public boolean hasException() { + return null != exception; + } +} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java new file mode 100644 index 00000000000..1efcc08152f --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java @@ -0,0 +1,63 @@ +/* + * 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.accumulo.iteratortest; + +import java.util.Objects; + +/** + * A summary of the invocation of an {@link IteratorTestInput} over a {@link IteratorTestCase} + * with the expected {@link IteratorTestOutput}. + */ +public class IteratorTestReport { + + private final IteratorTestInput input; + private final IteratorTestOutput expectedOutput; + private final IteratorTestCase testCase; + private final IteratorTestOutput actualOutput; + + public IteratorTestReport(IteratorTestInput input, IteratorTestOutput expectedOutput, IteratorTestOutput actualOutput, IteratorTestCase testCase) { + this.input = Objects.requireNonNull(input); + this.expectedOutput = Objects.requireNonNull(expectedOutput); + this.testCase = Objects.requireNonNull(testCase); + this.actualOutput = Objects.requireNonNull(actualOutput); + } + + public IteratorTestInput getInput() { + return input; + } + + public IteratorTestOutput getExpectedOutput() { + return expectedOutput; + } + + public IteratorTestCase getTestCase() { + return testCase; + } + + public IteratorTestOutput getActualOutput() { + return actualOutput; + } + + /** + * Evaluate whether the test passed or failed. + * + * @return True if the actual output matches the expected output, false otherwise. + */ + public boolean didTestSucceed() { + return expectedOutput.getOutput().equals(actualOutput); + } +} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java new file mode 100644 index 00000000000..6a5026ed858 --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java @@ -0,0 +1,83 @@ +/* + * 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.accumulo.iteratortest; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * A runner for invoking some tests over some input and expecting some output. + */ +public class IteratorTestRunner { + + private final IteratorTestInput testInput; + private final IteratorTestOutput testOutput; + private final Collection testCases; + + /** + * Construct an instance of the class. + * + * @param testInput The input to the tests + * @param testOutput The expected output given the input + * @param testCases The test cases to invoke + */ + public IteratorTestRunner(IteratorTestInput testInput, IteratorTestOutput testOutput, Collection testCases) { + this.testInput = testInput; + this.testOutput = testOutput; + this.testCases = testCases; + } + + public IteratorTestInput getTestInput() { + return testInput; + } + + public IteratorTestOutput getTestOutput() { + return testOutput; + } + + public Collection getTestCases() { + return testCases; + } + + /** + * Invokes each test case on the input, verifying the output. + * + * @return true if all tests passed, false + */ + public List runTests() { + List testReports = new ArrayList<>(testCases.size()); + for (IteratorTestCase testCase : testCases) { + IteratorTestOutput actualOutput = null; + + try { + actualOutput = testCase.test(testInput); + } catch (Exception e) { + actualOutput = new IteratorTestOutput(e); + } + + // Sanity-check on the IteratorTestCase implementation. + if (null == actualOutput) { + throw new IllegalStateException("IteratorTestCase implementations should always return a non-null IteratorTestOutput. " + testCase.getClass().getName() + " did not!"); + } + + testReports.add(new IteratorTestReport(testInput, testOutput, actualOutput, testCase)); + } + + return testReports; + } +} diff --git a/iterator-test-harness/src/test/resources/log4j.properties b/iterator-test-harness/src/test/resources/log4j.properties new file mode 100644 index 00000000000..9a98a8836ed --- /dev/null +++ b/iterator-test-harness/src/test/resources/log4j.properties @@ -0,0 +1,22 @@ +# 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. + +log4j.rootLogger=INFO, CA +log4j.appender.CA=org.apache.log4j.ConsoleAppender +log4j.appender.CA.layout=org.apache.log4j.PatternLayout +log4j.appender.CA.layout.ConversionPattern=%d{ISO8601} [%-8c{2}] %-5p: %m%n + +log4j.logger.org.apache.accumulo.core.client.impl.ServerClient=ERROR +log4j.logger.org.apache.zookeeper=ERROR diff --git a/pom.xml b/pom.xml index ba66b01e6cb..c18342e6f14 100644 --- a/pom.xml +++ b/pom.xml @@ -79,6 +79,7 @@ docs examples/simple fate + iterator-test-harness maven-plugin minicluster proxy From 133e3d035ae751ce84626041c3a1d6396fd294c3 Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Sat, 7 Nov 2015 16:09:17 -0500 Subject: [PATCH 02/15] Create a basic JUnit4 test case that wraps IteratorTestRunner. --- iterator-test-harness/pom.xml | 15 ++-- .../iteratortest/IteratorTestInput.java | 9 ++ .../iteratortest/IteratorTestOutput.java | 28 ++++++ .../iteratortest/IteratorTestReport.java | 31 ++++--- .../junit4/BaseJUnit4IteratorTest.java | 89 +++++++++++++++++++ .../testcases/NoopIteratorTestCase.java | 33 +++++++ .../iteratortest/JUnitFrameworkTest.java | 77 ++++++++++++++++ 7 files changed, 262 insertions(+), 20 deletions(-) create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java create mode 100644 iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java diff --git a/iterator-test-harness/pom.xml b/iterator-test-harness/pom.xml index 3ddd8411bae..16a688cb221 100644 --- a/iterator-test-harness/pom.xml +++ b/iterator-test-harness/pom.xml @@ -26,6 +26,11 @@ Apache Accumulo Iterator Test Harness A library for testing Apache Accumulo Iterators. + + junit + junit + optional + log4j log4j @@ -38,15 +43,5 @@ org.slf4j slf4j-api - - junit - junit - test - - - org.easymock - easymock - test - diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java index ace223ad3ee..d1f1a5a84e2 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java @@ -74,4 +74,13 @@ public SortedMap getInput() { return input; } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + sb.append("[iteratorClass=").append(iteratorClass) + .append(", iteratorOptions=").append(iteratorOptions) + .append(", range=").append(range) + .append(", input='").append(input).append("']"); + return sb.toString(); + } } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java index 03b7273b329..9c2a0c39a71 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java @@ -79,4 +79,32 @@ public Exception getException() { public boolean hasException() { return null != exception; } + + @Override + public boolean equals(Object o) { + if (!(o instanceof IteratorTestOutput)) { + return false; + } + + IteratorTestOutput other = (IteratorTestOutput) o; + if (hasOutput()) { + if (!other.hasOutput()) { + return false; + } + return output.equals(other.output); + } + + if (!other.hasException()) { + return false; + } + return exception.equals(other.getException()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + sb.append("[Output='").append(output) + .append("', exception=").append(exception).append("]"); + return sb.toString(); + } } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java index 1efcc08152f..41075b00e90 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java @@ -19,45 +19,56 @@ import java.util.Objects; /** - * A summary of the invocation of an {@link IteratorTestInput} over a {@link IteratorTestCase} - * with the expected {@link IteratorTestOutput}. + * A summary of the invocation of an {@link IteratorTestInput} over a {@link IteratorTestCase} with the expected {@link IteratorTestOutput}. */ public class IteratorTestReport { - + private final IteratorTestInput input; private final IteratorTestOutput expectedOutput; private final IteratorTestCase testCase; private final IteratorTestOutput actualOutput; - + public IteratorTestReport(IteratorTestInput input, IteratorTestOutput expectedOutput, IteratorTestOutput actualOutput, IteratorTestCase testCase) { this.input = Objects.requireNonNull(input); this.expectedOutput = Objects.requireNonNull(expectedOutput); this.testCase = Objects.requireNonNull(testCase); this.actualOutput = Objects.requireNonNull(actualOutput); } - + public IteratorTestInput getInput() { return input; } - + public IteratorTestOutput getExpectedOutput() { return expectedOutput; } - + public IteratorTestCase getTestCase() { return testCase; } - + public IteratorTestOutput getActualOutput() { return actualOutput; } - + /** * Evaluate whether the test passed or failed. * * @return True if the actual output matches the expected output, false otherwise. */ public boolean didTestSucceed() { - return expectedOutput.getOutput().equals(actualOutput); + return expectedOutput.equals(actualOutput); + } + + public String getSummary() { + StringBuilder sb = new StringBuilder(64); + // @formatter:off + sb.append("IteratorTestReport Summary: \n") + .append("\tTest Case = ").append(testCase.getClass().getName()) + .append("\tInput Data = '").append(input).append("'\n") + .append("\tExpected Output = '").append(expectedOutput).append("'\n") + .append("\tActual Output = '").append(actualOutput).append("'\n"); + // @formatter:on + return sb.toString(); } } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java new file mode 100644 index 00000000000..1a1b21941d1 --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java @@ -0,0 +1,89 @@ +/* + * 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.accumulo.iteratortest.junit4; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.accumulo.iteratortest.IteratorTestCase; +import org.apache.accumulo.iteratortest.IteratorTestInput; +import org.apache.accumulo.iteratortest.IteratorTestOutput; +import org.apache.accumulo.iteratortest.IteratorTestReport; +import org.apache.accumulo.iteratortest.IteratorTestRunner; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** + * A base JUnit4 test class for users to leverage with the JUnit Parameterized Runner. + *

+ * Users should extend this class and implement a static method using the {@code @Parameters} annotation. + *

+ * @Parameters
+ * public static Object[][] data() {
+ *   IteratorTestInput input = createIteratorInput();
+ *   IteratorTestOutput expectedOutput = createIteratorOuput();
+ *   List<IteratorTestCase> testCases = createTestCases();
+ *   return BaseJUnit4IteratorTest.createParameters(input, expectedOutput, testCases);
+ * }
+ * 
+ * + */ +@RunWith(Parameterized.class) +public class BaseJUnit4IteratorTest { + + public final IteratorTestRunner runner; + + public BaseJUnit4IteratorTest(IteratorTestInput input, IteratorTestOutput expectedOutput, IteratorTestCase testCase) { + this.runner = new IteratorTestRunner(input, expectedOutput, Collections.singleton(testCase)); + } + + /** + * A helper function to convert input, output and a list of test cases into a two-dimensional array for JUnit's Parameterized runner. + * + * @param input The input + * @param output The output + * @param testCases A list of desired test cases to run. + * @return A two dimensional array suitable to pass as JUnit's parameters. + */ + public static Object[][] createParameters(IteratorTestInput input, IteratorTestOutput output, Collection testCases) { + Object[][] parameters = new Object[testCases.size()][3]; + Iterator testCaseIter = testCases.iterator(); + for (int i = 0; testCaseIter.hasNext(); i++) { + final IteratorTestCase testCase = testCaseIter.next(); + parameters[i] = new Object[] {input, output, testCase}; + } + return parameters; + } + + @Test + public void testIterator() { + List reports = runner.runTests(); + assertEquals(1, reports.size()); + + IteratorTestReport report = reports.get(0); + assertNotNull(report); + + assertTrue(report.getSummary(), report.didTestSucceed()); + } +} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java new file mode 100644 index 00000000000..828a68098b6 --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java @@ -0,0 +1,33 @@ +/* + * 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.accumulo.iteratortest.testcases; + +import org.apache.accumulo.iteratortest.IteratorTestCase; +import org.apache.accumulo.iteratortest.IteratorTestInput; +import org.apache.accumulo.iteratortest.IteratorTestOutput; + +/** + * An IteratorTestCase implementation that returns the original input. + */ +public class NoopIteratorTestCase implements IteratorTestCase { + + @Override + public IteratorTestOutput test(IteratorTestInput testInput) { + return new IteratorTestOutput(testInput.getInput()); + } + +} diff --git a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java new file mode 100644 index 00000000000..199f8213201 --- /dev/null +++ b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java @@ -0,0 +1,77 @@ +/* + * 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.accumulo.iteratortest; + +import java.util.Collections; +import java.util.List; +import java.util.TreeMap; + +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Range; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.iterators.IteratorEnvironment; +import org.apache.accumulo.core.iterators.WrappingIterator; +import org.apache.accumulo.iteratortest.junit4.BaseJUnit4IteratorTest; +import org.apache.accumulo.iteratortest.testcases.NoopIteratorTestCase; +import org.junit.runners.Parameterized.Parameters; + +/** + * A Basic test asserting that the framework is functional. + */ +public class JUnitFrameworkTest extends BaseJUnit4IteratorTest{ + + @Parameters + public static Object[][] parameters() { + IteratorTestInput input = getIteratorInput(); + IteratorTestOutput output = getIteratorOutput(); + List tests = Collections. singletonList(new NoopIteratorTestCase()); + return BaseJUnit4IteratorTest.createParameters(input, output, tests); + } + + private static final TreeMap DATA = createData(); + + private static TreeMap createData() { + TreeMap data = new TreeMap<>(); + data.put(new Key("1", "a", ""), new Value("1a".getBytes())); + data.put(new Key("2", "a", ""), new Value("2a".getBytes())); + data.put(new Key("3", "a", ""), new Value("3a".getBytes())); + return data; + } + + private static IteratorTestInput getIteratorInput() { + return new IteratorTestInput(IdentityIterator.class, Collections. emptyMap(), new Range(), DATA); + } + + private static IteratorTestOutput getIteratorOutput() { + return new IteratorTestOutput(DATA); + } + + public JUnitFrameworkTest(IteratorTestInput input, IteratorTestOutput expectedOutput, IteratorTestCase testCase) { + super(input, expectedOutput, testCase); + } + + /** + * Noop iterator implementation. + */ + private static class IdentityIterator extends WrappingIterator { + + @Override + public IdentityIterator deepCopy(IteratorEnvironment env) { + return new IdentityIterator(); + } + } +} From 5968a6a63083b90971f9866279b9349afbc6b006 Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Sat, 7 Nov 2015 17:22:13 -0500 Subject: [PATCH 03/15] Some initial tests against WholeRowIterator --- iterator-test-harness/pom.xml | 7 +- .../iteratortest/IteratorTestCase.java | 7 +- .../iteratortest/IteratorTestInput.java | 20 +-- .../iteratortest/IteratorTestOutput.java | 53 ++++++- .../iteratortest/IteratorTestReport.java | 16 +-- .../iteratortest/IteratorTestRunner.java | 12 +- .../iteratortest/IteratorTestUtil.java | 42 ++++++ .../SimpleIteratorEnvironment.java | 80 +++++++++++ .../junit4/BaseJUnit4IteratorTest.java | 18 ++- .../testcases/InstantiationTestCase.java | 47 +++++++ .../testcases/MultipleHasTopCalls.java | 88 ++++++++++++ .../testcases/NoopIteratorTestCase.java | 2 +- .../iteratortest/JUnitFrameworkTest.java | 2 +- .../iteratortest/WholeRowIteratorTest.java | 130 ++++++++++++++++++ 14 files changed, 487 insertions(+), 37 deletions(-) create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/environments/SimpleIteratorEnvironment.java create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/MultipleHasTopCalls.java create mode 100644 iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java diff --git a/iterator-test-harness/pom.xml b/iterator-test-harness/pom.xml index 16a688cb221..d7ac3eecbe2 100644 --- a/iterator-test-harness/pom.xml +++ b/iterator-test-harness/pom.xml @@ -26,10 +26,11 @@ Apache Accumulo Iterator Test Harness A library for testing Apache Accumulo Iterators. + junit junit - optional + log4j @@ -39,6 +40,10 @@ org.apache.accumulo accumulo-core + + org.apache.hadoop + hadoop-client + org.slf4j slf4j-api diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java index cf750b0d7a1..b1e573bb85d 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java @@ -19,15 +19,16 @@ import org.apache.accumulo.core.iterators.SortedKeyValueIterator; /** - * An interface that accepts some input for testing a {@link SortedKeyValueIterator}, runs the specific implementation - * of the test and returns the outcome from that iterator. + * An interface that accepts some input for testing a {@link SortedKeyValueIterator}, runs the specific implementation of the test and returns the outcome from + * that iterator. */ public interface IteratorTestCase { /** * Run the implementation's test against the given input. * - * @param testInput The input to test. + * @param testInput + * The input to test. * @return The output of the test with the input. */ IteratorTestOutput test(IteratorTestInput testInput); diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java index d1f1a5a84e2..943bb0dc3d8 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestInput.java @@ -41,13 +41,17 @@ public class IteratorTestInput { /** * Construct an instance of the test input. * - * @param iteratorClass The class for the iterator to test - * @param iteratorOptions Options, if any, to provide to the iterator ({@link IteratorSetting}'s Map of properties) - * @param range The Range of data to query ({@link Scanner#setRange(Range)}) - * @param input A sorted collection of Key-Value pairs acting as the table. + * @param iteratorClass + * The class for the iterator to test + * @param iteratorOptions + * Options, if any, to provide to the iterator ({@link IteratorSetting}'s Map of properties) + * @param range + * The Range of data to query ({@link Scanner#setRange(Range)}) + * @param input + * A sorted collection of Key-Value pairs acting as the table. */ - public IteratorTestInput(Class> iteratorClass, - Map iteratorOptions, Range range, SortedMap input) { + public IteratorTestInput(Class> iteratorClass, Map iteratorOptions, Range range, + SortedMap input) { // Already immutable this.iteratorClass = Objects.requireNonNull(iteratorClass); // Make it immutable to the test @@ -77,9 +81,7 @@ public SortedMap getInput() { @Override public String toString() { StringBuilder sb = new StringBuilder(64); - sb.append("[iteratorClass=").append(iteratorClass) - .append(", iteratorOptions=").append(iteratorOptions) - .append(", range=").append(range) + sb.append("[iteratorClass=").append(iteratorClass).append(", iteratorOptions=").append(iteratorOptions).append(", range=").append(range) .append(", input='").append(input).append("']"); return sb.toString(); } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java index 9c2a0c39a71..35bf99adb37 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java @@ -24,28 +24,65 @@ import org.apache.accumulo.core.data.Value; /** - * The expected results from invoking a {@link IteratorTestCase} on a {@link IteratorTestInput}. The output - * will be either a {@link SortedMap} of Keys and Values or an exception but never both. If one of these members - * is null, the other is guaranteed to be non-null. + * The expected results from invoking a {@link IteratorTestCase} on a {@link IteratorTestInput}. The output will be either a {@link SortedMap} of Keys and + * Values or an exception but never both. If one of these members is null, the other is guaranteed to be non-null. */ public class IteratorTestOutput { + /** + * An outcome about what happened during a test case. + */ + public enum TestOutcome { + /** + * The IteratorTestCase proactively passed. + */ + PASSED, + /** + * The IteratorTestCase proactively failed. + */ + FAILED, + /** + * The IteratorTestCase completed, but the pass/fail should be determined by the other context. + */ + COMPLETED + } + private final SortedMap output; private final Exception exception; + private final TestOutcome outcome; + + public IteratorTestOutput(TestOutcome outcome) { + this.outcome = outcome; + if (outcome == TestOutcome.COMPLETED) { + throw new IllegalArgumentException("This constructor is only for use with PASSED and FAILED"); + } + output = null; + exception = null; + } /** * Create an instance of the class. * - * @param output The sorted collection of Key-Value pairs generated by an Iterator. + * @param output + * The sorted collection of Key-Value pairs generated by an Iterator. */ public IteratorTestOutput(SortedMap output) { this.output = Collections.unmodifiableSortedMap(Objects.requireNonNull(output)); this.exception = null; + this.outcome = TestOutcome.COMPLETED; } public IteratorTestOutput(Exception e) { this.output = null; this.exception = Objects.requireNonNull(e); + this.outcome = TestOutcome.FAILED; + } + + /** + * @return The outcome of the test. + */ + public TestOutcome getTestOutcome() { + return outcome; } /** @@ -87,6 +124,11 @@ public boolean equals(Object o) { } IteratorTestOutput other = (IteratorTestOutput) o; + + if (outcome != other.outcome) { + return false; + } + if (hasOutput()) { if (!other.hasOutput()) { return false; @@ -103,8 +145,7 @@ public boolean equals(Object o) { @Override public String toString() { StringBuilder sb = new StringBuilder(64); - sb.append("[Output='").append(output) - .append("', exception=").append(exception).append("]"); + sb.append("[outcome=").append(outcome).append(", output='").append(output).append("', exception=").append(exception).append("]"); return sb.toString(); } } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java index 41075b00e90..6782b8c63c3 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java @@ -22,35 +22,35 @@ * A summary of the invocation of an {@link IteratorTestInput} over a {@link IteratorTestCase} with the expected {@link IteratorTestOutput}. */ public class IteratorTestReport { - + private final IteratorTestInput input; private final IteratorTestOutput expectedOutput; private final IteratorTestCase testCase; private final IteratorTestOutput actualOutput; - + public IteratorTestReport(IteratorTestInput input, IteratorTestOutput expectedOutput, IteratorTestOutput actualOutput, IteratorTestCase testCase) { this.input = Objects.requireNonNull(input); this.expectedOutput = Objects.requireNonNull(expectedOutput); this.testCase = Objects.requireNonNull(testCase); this.actualOutput = Objects.requireNonNull(actualOutput); } - + public IteratorTestInput getInput() { return input; } - + public IteratorTestOutput getExpectedOutput() { return expectedOutput; } - + public IteratorTestCase getTestCase() { return testCase; } - + public IteratorTestOutput getActualOutput() { return actualOutput; } - + /** * Evaluate whether the test passed or failed. * @@ -59,7 +59,7 @@ public IteratorTestOutput getActualOutput() { public boolean didTestSucceed() { return expectedOutput.equals(actualOutput); } - + public String getSummary() { StringBuilder sb = new StringBuilder(64); // @formatter:off diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java index 6a5026ed858..d38f8a33d14 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java @@ -32,9 +32,12 @@ public class IteratorTestRunner { /** * Construct an instance of the class. * - * @param testInput The input to the tests - * @param testOutput The expected output given the input - * @param testCases The test cases to invoke + * @param testInput + * The input to the tests + * @param testOutput + * The expected output given the input + * @param testCases + * The test cases to invoke */ public IteratorTestRunner(IteratorTestInput testInput, IteratorTestOutput testOutput, Collection testCases) { this.testInput = testInput; @@ -72,7 +75,8 @@ public List runTests() { // Sanity-check on the IteratorTestCase implementation. if (null == actualOutput) { - throw new IllegalStateException("IteratorTestCase implementations should always return a non-null IteratorTestOutput. " + testCase.getClass().getName() + " did not!"); + throw new IllegalStateException("IteratorTestCase implementations should always return a non-null IteratorTestOutput. " + testCase.getClass().getName() + + " did not!"); } testReports.add(new IteratorTestReport(testInput, testOutput, actualOutput, testCase)); diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java new file mode 100644 index 00000000000..b412d125599 --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java @@ -0,0 +1,42 @@ +/* + * 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.accumulo.iteratortest; + +import java.util.Objects; + +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.iterators.SortedKeyValueIterator; +import org.apache.accumulo.core.iterators.SortedMapIterator; + +/** + * A collection of methods that are helpful to the development of {@link IteratorTestCase}s. + */ +public class IteratorTestUtil { + + public static SortedKeyValueIterator instantiateIterator(IteratorTestInput input) { + try { + return Objects.requireNonNull(input.getIteratorClass()).newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static SortedKeyValueIterator createSource(IteratorTestInput input) { + return new SortedMapIterator(Objects.requireNonNull(input).getInput()); + } +} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/environments/SimpleIteratorEnvironment.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/environments/SimpleIteratorEnvironment.java new file mode 100644 index 00000000000..07f1bb25383 --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/environments/SimpleIteratorEnvironment.java @@ -0,0 +1,80 @@ +/* + * 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.accumulo.iteratortest.environments; + +import java.io.IOException; + +import org.apache.accumulo.core.client.admin.SamplerConfiguration; +import org.apache.accumulo.core.conf.AccumuloConfiguration; +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.iterators.IteratorEnvironment; +import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope; +import org.apache.accumulo.core.iterators.SortedKeyValueIterator; +import org.apache.accumulo.core.security.Authorizations; + +/** + * A simple implementation of {@link IteratorEnvironment} which is unimplemented. + */ +public class SimpleIteratorEnvironment implements IteratorEnvironment { + + @Override + public SortedKeyValueIterator reserveMapFileReader(String mapFileName) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public AccumuloConfiguration getConfig() { + throw new UnsupportedOperationException(); + } + + @Override + public IteratorScope getIteratorScope() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isFullMajorCompaction() { + throw new UnsupportedOperationException(); + } + + @Override + public void registerSideChannel(SortedKeyValueIterator iter) { + throw new UnsupportedOperationException(); + } + + @Override + public Authorizations getAuthorizations() { + throw new UnsupportedOperationException(); + } + + @Override + public IteratorEnvironment cloneWithSamplingEnabled() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSamplingEnabled() { + throw new UnsupportedOperationException(); + } + + @Override + public SamplerConfiguration getSamplerConfiguration() { + throw new UnsupportedOperationException(); + } + +} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java index 1a1b21941d1..1809d99e245 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java @@ -33,11 +33,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A base JUnit4 test class for users to leverage with the JUnit Parameterized Runner. *

* Users should extend this class and implement a static method using the {@code @Parameters} annotation. + * *

  * @Parameters
  * public static Object[][] data() {
@@ -51,6 +54,7 @@
  */
 @RunWith(Parameterized.class)
 public class BaseJUnit4IteratorTest {
+  private static final Logger log = LoggerFactory.getLogger(BaseJUnit4IteratorTest.class);
 
   public final IteratorTestRunner runner;
 
@@ -61,10 +65,13 @@ public BaseJUnit4IteratorTest(IteratorTestInput input, IteratorTestOutput expect
   /**
    * A helper function to convert input, output and a list of test cases into a two-dimensional array for JUnit's Parameterized runner.
    *
-   * @param input The input
-   * @param output The output
-   * @param testCases A list of desired test cases to run.
-   * @return A two dimensional array suitable to pass as JUnit's parameters. 
+   * @param input
+   *          The input
+   * @param output
+   *          The output
+   * @param testCases
+   *          A list of desired test cases to run.
+   * @return A two dimensional array suitable to pass as JUnit's parameters.
    */
   public static Object[][] createParameters(IteratorTestInput input, IteratorTestOutput output, Collection testCases) {
     Object[][] parameters = new Object[testCases.size()][3];
@@ -85,5 +92,8 @@ public void testIterator() {
     assertNotNull(report);
 
     assertTrue(report.getSummary(), report.didTestSucceed());
+
+    // Present for manual verification
+    log.trace("Expected: {}, Actual: {}", report.getExpectedOutput(), report.getActualOutput());
   }
 }
diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java
new file mode 100644
index 00000000000..efbf9f448bf
--- /dev/null
+++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java
@@ -0,0 +1,47 @@
+/*
+ * 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.accumulo.iteratortest.testcases;
+
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
+import org.apache.accumulo.iteratortest.IteratorTestCase;
+import org.apache.accumulo.iteratortest.IteratorTestInput;
+import org.apache.accumulo.iteratortest.IteratorTestOutput;
+import org.apache.accumulo.iteratortest.IteratorTestOutput.TestOutcome;
+
+/**
+ * TestCase to assert that an Iterator has a no-args constructor.
+ */
+public class InstantiationTestCase implements IteratorTestCase {
+
+  @Override
+  public IteratorTestOutput test(IteratorTestInput testInput) {
+    Class> clz = testInput.getIteratorClass();
+
+    try {
+      // We should be able to instantiate the Iterator given the Class
+      @SuppressWarnings("unused")
+      SortedKeyValueIterator iter = clz.newInstance();
+    } catch (Exception e) {
+      return new IteratorTestOutput(e);
+    }
+
+    return new IteratorTestOutput(TestOutcome.PASSED);
+  }
+
+}
diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/MultipleHasTopCalls.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/MultipleHasTopCalls.java
new file mode 100644
index 00000000000..d9052844460
--- /dev/null
+++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/MultipleHasTopCalls.java
@@ -0,0 +1,88 @@
+/*
+ * 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.accumulo.iteratortest.testcases;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Random;
+import java.util.TreeMap;
+
+import org.apache.accumulo.core.data.ByteSequence;
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
+import org.apache.accumulo.iteratortest.IteratorTestCase;
+import org.apache.accumulo.iteratortest.IteratorTestInput;
+import org.apache.accumulo.iteratortest.IteratorTestOutput;
+import org.apache.accumulo.iteratortest.IteratorTestUtil;
+import org.apache.accumulo.iteratortest.environments.SimpleIteratorEnvironment;
+
+/**
+ * TestCase which asserts that multiple calls to {@link SortedKeyValueIterator#hasTop()} should not alter the internal state of the iterator and should not
+ * return different values due to multiple, sequential invocations.
+ * 

+ * This test case will call {@code hasTop()} multiple times, verifying that each call returns the same value as the first. + */ +public class MultipleHasTopCalls implements IteratorTestCase { + + private final Random random; + + public MultipleHasTopCalls() { + this.random = new Random(); + } + + @Override + public IteratorTestOutput test(IteratorTestInput testInput) { + final SortedKeyValueIterator skvi = IteratorTestUtil.instantiateIterator(testInput); + final SortedKeyValueIterator source = IteratorTestUtil.createSource(testInput); + + try { + skvi.init(source, testInput.getIteratorOptions(), new SimpleIteratorEnvironment()); + skvi.seek(testInput.getRange(), Collections. emptySet(), false); + return new IteratorTestOutput(consume(skvi)); + } catch (IOException e) { + return new IteratorTestOutput(e); + } + } + + TreeMap consume(SortedKeyValueIterator skvi) throws IOException { + TreeMap data = new TreeMap<>(); + while (skvi.hasTop()) { + // Check 1 to 5 times. If hasTop returned true, it should continue to return true. + for (int i = 0; i < random.nextInt(5) + 1; i++) { + if (!skvi.hasTop()) { + throw badStateException(true); + } + } + data.put(skvi.getTopKey(), skvi.getTopValue()); + skvi.next(); + } + + // Check 1 to 5 times. Once hasTop returned false, it should continue to return false + for (int i = 0; i < random.nextInt(5) + 1; i++) { + if (skvi.hasTop()) { + throw badStateException(false); + } + } + return data; + } + + IllegalStateException badStateException(boolean expectedState) { + return new IllegalStateException("Multiple sequential calls to hasTop should not alter the state or return value of the iterator. Expected '" + + expectedState + ", but got '" + !expectedState + "'."); + } +} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java index 828a68098b6..86b17cda6db 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java @@ -21,7 +21,7 @@ import org.apache.accumulo.iteratortest.IteratorTestOutput; /** - * An IteratorTestCase implementation that returns the original input. + * An IteratorTestCase implementation that returns the original input without any external action. */ public class NoopIteratorTestCase implements IteratorTestCase { diff --git a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java index 199f8213201..627aed78fee 100644 --- a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java +++ b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java @@ -32,7 +32,7 @@ /** * A Basic test asserting that the framework is functional. */ -public class JUnitFrameworkTest extends BaseJUnit4IteratorTest{ +public class JUnitFrameworkTest extends BaseJUnit4IteratorTest { @Parameters public static Object[][] parameters() { diff --git a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java new file mode 100644 index 00000000000..e8ded9dd73b --- /dev/null +++ b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java @@ -0,0 +1,130 @@ +/* + * 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.accumulo.iteratortest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map.Entry; +import java.util.TreeMap; + +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Range; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.iterators.user.WholeRowIterator; +import org.apache.accumulo.iteratortest.junit4.BaseJUnit4IteratorTest; +import org.apache.accumulo.iteratortest.testcases.MultipleHasTopCalls; +import org.apache.hadoop.io.Text; +import org.junit.runners.Parameterized.Parameters; + +/** + * Framework tests for {@link WholeRowIterator}. + */ +public class WholeRowIteratorTest extends BaseJUnit4IteratorTest { + + @Parameters + public static Object[][] parameters() { + IteratorTestInput input = getIteratorInput(); + IteratorTestOutput output = getIteratorOutput(); + List tests = Arrays. asList(new MultipleHasTopCalls()); + return BaseJUnit4IteratorTest.createParameters(input, output, tests); + } + + private static final TreeMap INPUT_DATA = createInputData(); + private static final TreeMap OUTPUT_DATA = createOutputData(); + + private static TreeMap createInputData() { + TreeMap data = new TreeMap<>(); + + data.put(new Key("1", "", "a"), new Value("1a".getBytes())); + data.put(new Key("1", "", "b"), new Value("1b".getBytes())); + data.put(new Key("1", "a", "a"), new Value("1aa".getBytes())); + data.put(new Key("1", "a", "b"), new Value("1ab".getBytes())); + data.put(new Key("1", "b", "a"), new Value("1ba".getBytes())); + + data.put(new Key("2", "a", "a"), new Value("2aa".getBytes())); + data.put(new Key("2", "a", "b"), new Value("2ab".getBytes())); + data.put(new Key("2", "a", "c"), new Value("2ac".getBytes())); + data.put(new Key("2", "c", "c"), new Value("2cc".getBytes())); + + data.put(new Key("3", "a", ""), new Value("3a".getBytes())); + + return data; + } + + private static TreeMap createOutputData() { + TreeMap data = new TreeMap<>(); + + Text row = null; + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + + // Generate the output data from the input data + for (Entry entry : INPUT_DATA.entrySet()) { + if (null == row) { + row = entry.getKey().getRow(); + } + + if (!row.equals(entry.getKey().getRow())) { + // Moved to the next row + try { + // Serialize and save + Value encoded = WholeRowIterator.encodeRow(keys, values); + data.put(new Key(row), encoded); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // Empty the aggregated k-v's + keys = new ArrayList<>(); + values = new ArrayList<>(); + // Set the new current row + row = entry.getKey().getRow(); + } + + // Aggregate the current row + keys.add(entry.getKey()); + values.add(entry.getValue()); + } + + if (!keys.isEmpty()) { + try { + Value encoded = WholeRowIterator.encodeRow(keys, values); + data.put(new Key(row), encoded); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return data; + } + + private static IteratorTestInput getIteratorInput() { + return new IteratorTestInput(WholeRowIterator.class, Collections. emptyMap(), new Range(), INPUT_DATA); + } + + private static IteratorTestOutput getIteratorOutput() { + return new IteratorTestOutput(OUTPUT_DATA); + } + + public WholeRowIteratorTest(IteratorTestInput input, IteratorTestOutput expectedOutput, IteratorTestCase testCase) { + super(input, expectedOutput, testCase); + } + +} From 1475fa5275102c355fe3ad3781a76538adda609b Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Sat, 7 Nov 2015 17:31:58 -0500 Subject: [PATCH 04/15] Add a base TestCase that performs verification on the TestOutput. --- .../iteratortest/IteratorTestCase.java | 11 +++++++ .../iteratortest/IteratorTestReport.java | 2 +- .../iteratortest/IteratorTestUtil.java | 2 +- .../testcases/InstantiationTestCase.java | 5 +++ .../testcases/MultipleHasTopCalls.java | 3 +- .../testcases/NoopIteratorTestCase.java | 9 +++++- .../testcases/OutputVerifyingTestCase.java | 31 +++++++++++++++++++ .../iteratortest/WholeRowIteratorTest.java | 3 +- 8 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/OutputVerifyingTestCase.java diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java index b1e573bb85d..ec161893ff6 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java @@ -33,4 +33,15 @@ public interface IteratorTestCase { */ IteratorTestOutput test(IteratorTestInput testInput); + /** + * Compute whether or not the expected and actual output is a success or failure for this implementation. + * + * @param expected + * The expected output from the user. + * @param actual + * The actual output from the test + * @return True if the test case passes, false if it doesn't. + */ + boolean verify(IteratorTestOutput expected, IteratorTestOutput actual); + } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java index 6782b8c63c3..89c8a767a39 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java @@ -57,7 +57,7 @@ public IteratorTestOutput getActualOutput() { * @return True if the actual output matches the expected output, false otherwise. */ public boolean didTestSucceed() { - return expectedOutput.equals(actualOutput); + return testCase.verify(expectedOutput, actualOutput); } public String getSummary() { diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java index b412d125599..d0a00acb6b7 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java @@ -24,7 +24,7 @@ import org.apache.accumulo.core.iterators.SortedMapIterator; /** - * A collection of methods that are helpful to the development of {@link IteratorTestCase}s. + * A collection of methods that are helpful to the development of {@link IteratorTestCase}s. */ public class IteratorTestUtil { diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java index efbf9f448bf..17923c403f4 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java @@ -44,4 +44,9 @@ public IteratorTestOutput test(IteratorTestInput testInput) { return new IteratorTestOutput(TestOutcome.PASSED); } + public boolean verify(IteratorTestOutput expected, IteratorTestOutput actual) { + // Ignore what the user provided as expected output, just check that we instantiated the iterator successfully. + return TestOutcome.PASSED == actual.getTestOutcome(); + } + } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/MultipleHasTopCalls.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/MultipleHasTopCalls.java index d9052844460..34bf7761c58 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/MultipleHasTopCalls.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/MultipleHasTopCalls.java @@ -25,7 +25,6 @@ import org.apache.accumulo.core.data.Key; import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.iterators.SortedKeyValueIterator; -import org.apache.accumulo.iteratortest.IteratorTestCase; import org.apache.accumulo.iteratortest.IteratorTestInput; import org.apache.accumulo.iteratortest.IteratorTestOutput; import org.apache.accumulo.iteratortest.IteratorTestUtil; @@ -37,7 +36,7 @@ *

* This test case will call {@code hasTop()} multiple times, verifying that each call returns the same value as the first. */ -public class MultipleHasTopCalls implements IteratorTestCase { +public class MultipleHasTopCalls extends OutputVerifyingTestCase { private final Random random; diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java index 86b17cda6db..56990fb4516 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java @@ -19,6 +19,7 @@ import org.apache.accumulo.iteratortest.IteratorTestCase; import org.apache.accumulo.iteratortest.IteratorTestInput; import org.apache.accumulo.iteratortest.IteratorTestOutput; +import org.apache.accumulo.iteratortest.IteratorTestOutput.TestOutcome; /** * An IteratorTestCase implementation that returns the original input without any external action. @@ -27,7 +28,13 @@ public class NoopIteratorTestCase implements IteratorTestCase { @Override public IteratorTestOutput test(IteratorTestInput testInput) { - return new IteratorTestOutput(testInput.getInput()); + return new IteratorTestOutput(TestOutcome.PASSED); + } + + @Override + public boolean verify(IteratorTestOutput expected, IteratorTestOutput actual) { + // Always passes + return true; } } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/OutputVerifyingTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/OutputVerifyingTestCase.java new file mode 100644 index 00000000000..9e4eeba1870 --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/OutputVerifyingTestCase.java @@ -0,0 +1,31 @@ +/* + * 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.accumulo.iteratortest.testcases; + +import org.apache.accumulo.iteratortest.IteratorTestCase; +import org.apache.accumulo.iteratortest.IteratorTestOutput; + +/** + * Base {@link IteratorTestCase} implementation that performs verifiation on the expected and actual outcome. + */ +public abstract class OutputVerifyingTestCase implements IteratorTestCase { + + public boolean verify(IteratorTestOutput expected, IteratorTestOutput actual) { + return expected.equals(actual); + } + +} diff --git a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java index e8ded9dd73b..7d5606cbeea 100644 --- a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java +++ b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java @@ -29,6 +29,7 @@ import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.iterators.user.WholeRowIterator; import org.apache.accumulo.iteratortest.junit4.BaseJUnit4IteratorTest; +import org.apache.accumulo.iteratortest.testcases.InstantiationTestCase; import org.apache.accumulo.iteratortest.testcases.MultipleHasTopCalls; import org.apache.hadoop.io.Text; import org.junit.runners.Parameterized.Parameters; @@ -42,7 +43,7 @@ public class WholeRowIteratorTest extends BaseJUnit4IteratorTest { public static Object[][] parameters() { IteratorTestInput input = getIteratorInput(); IteratorTestOutput output = getIteratorOutput(); - List tests = Arrays. asList(new MultipleHasTopCalls()); + List tests = Arrays. asList(new MultipleHasTopCalls(), new InstantiationTestCase()); return BaseJUnit4IteratorTest.createParameters(input, output, tests); } From 99c1ccbdd7568e3ffda373c23542bf7488480507 Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Sat, 7 Nov 2015 17:52:30 -0500 Subject: [PATCH 05/15] Add a utility to dynamically find all provided IteratorTestCases. --- iterator-test-harness/pom.xml | 4 ++ .../iteratortest/IteratorTestCaseFinder.java | 61 +++++++++++++++++++ .../iteratortest/IteratorTestOutput.java | 1 + .../iteratortest/IteratorTestReport.java | 2 + .../iteratortest/IteratorTestRunner.java | 7 +++ .../iteratortest/IteratorTestUtil.java | 1 + .../junit4/BaseJUnit4IteratorTest.java | 2 +- .../testcases/InstantiationTestCase.java | 1 - .../{ => testcases}/IteratorTestCase.java | 4 +- .../testcases/NoopIteratorTestCase.java | 40 ------------ .../testcases/OutputVerifyingTestCase.java | 1 - .../iteratortest/WholeRowIteratorTest.java | 6 +- .../{ => framework}/JUnitFrameworkTest.java | 25 +++++++- pom.xml | 5 ++ 14 files changed, 110 insertions(+), 50 deletions(-) create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCaseFinder.java rename iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/{ => testcases}/IteratorTestCase.java (91%) delete mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java rename iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/{ => framework}/JUnitFrameworkTest.java (77%) diff --git a/iterator-test-harness/pom.xml b/iterator-test-harness/pom.xml index d7ac3eecbe2..71ec528e593 100644 --- a/iterator-test-harness/pom.xml +++ b/iterator-test-harness/pom.xml @@ -44,6 +44,10 @@ org.apache.hadoop hadoop-client + + org.reflections + reflections + org.slf4j slf4j-api diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCaseFinder.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCaseFinder.java new file mode 100644 index 00000000000..7c6e7514982 --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCaseFinder.java @@ -0,0 +1,61 @@ +/* + * 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.accumulo.iteratortest; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.apache.accumulo.iteratortest.testcases.IteratorTestCase; +import org.reflections.Reflections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A class to ease finding published test cases. + */ +public class IteratorTestCaseFinder { + private static final Logger log = LoggerFactory.getLogger(IteratorTestCaseFinder.class); + + /** + * Instantiates all test cases provided. + * + * @return A list of {@link IteratorTestCase}s. + */ + public static List findAllTestCases() { + log.info("Searching {}", IteratorTestCase.class.getPackage().getName()); + Reflections reflections = new Reflections(IteratorTestCase.class.getPackage().getName()); + + final List testCases = new ArrayList<>(); + final Set> classes = reflections.getSubTypesOf(IteratorTestCase.class); + for (Class clz : classes) { + if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) { + log.debug("Skipping " + clz); + continue; + } + + try { + testCases.add(clz.newInstance()); + } catch (IllegalAccessException | InstantiationException e) { + log.warn("Could not instantiate {}", clz, e); + } + } + + return testCases; + } +} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java index 35bf99adb37..0a19727cfea 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestOutput.java @@ -22,6 +22,7 @@ import org.apache.accumulo.core.data.Key; import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.iteratortest.testcases.IteratorTestCase; /** * The expected results from invoking a {@link IteratorTestCase} on a {@link IteratorTestInput}. The output will be either a {@link SortedMap} of Keys and diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java index 89c8a767a39..a52e8837547 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestReport.java @@ -18,6 +18,8 @@ import java.util.Objects; +import org.apache.accumulo.iteratortest.testcases.IteratorTestCase; + /** * A summary of the invocation of an {@link IteratorTestInput} over a {@link IteratorTestCase} with the expected {@link IteratorTestOutput}. */ diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java index d38f8a33d14..90c8265cd46 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java @@ -20,10 +20,15 @@ import java.util.Collection; import java.util.List; +import org.apache.accumulo.iteratortest.testcases.IteratorTestCase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * A runner for invoking some tests over some input and expecting some output. */ public class IteratorTestRunner { + private static final Logger log = LoggerFactory.getLogger(IteratorTestRunner.class); private final IteratorTestInput testInput; private final IteratorTestOutput testOutput; @@ -65,6 +70,8 @@ public Collection getTestCases() { public List runTests() { List testReports = new ArrayList<>(testCases.size()); for (IteratorTestCase testCase : testCases) { + log.info("Invoking {} on {}", testCase.getClass().getName(), testInput.getIteratorClass().getName()); + IteratorTestOutput actualOutput = null; try { diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java index d0a00acb6b7..a703ae7205a 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java @@ -22,6 +22,7 @@ import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.iterators.SortedKeyValueIterator; import org.apache.accumulo.core.iterators.SortedMapIterator; +import org.apache.accumulo.iteratortest.testcases.IteratorTestCase; /** * A collection of methods that are helpful to the development of {@link IteratorTestCase}s. diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java index 1809d99e245..66e9dbcbd35 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/junit4/BaseJUnit4IteratorTest.java @@ -25,11 +25,11 @@ import java.util.Iterator; import java.util.List; -import org.apache.accumulo.iteratortest.IteratorTestCase; import org.apache.accumulo.iteratortest.IteratorTestInput; import org.apache.accumulo.iteratortest.IteratorTestOutput; import org.apache.accumulo.iteratortest.IteratorTestReport; import org.apache.accumulo.iteratortest.IteratorTestRunner; +import org.apache.accumulo.iteratortest.testcases.IteratorTestCase; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java index 17923c403f4..3bbfb7fa243 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/InstantiationTestCase.java @@ -19,7 +19,6 @@ import org.apache.accumulo.core.data.Key; import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.iterators.SortedKeyValueIterator; -import org.apache.accumulo.iteratortest.IteratorTestCase; import org.apache.accumulo.iteratortest.IteratorTestInput; import org.apache.accumulo.iteratortest.IteratorTestOutput; import org.apache.accumulo.iteratortest.IteratorTestOutput.TestOutcome; diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IteratorTestCase.java similarity index 91% rename from iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java rename to iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IteratorTestCase.java index ec161893ff6..f7495af624d 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IteratorTestCase.java @@ -14,9 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.accumulo.iteratortest; +package org.apache.accumulo.iteratortest.testcases; import org.apache.accumulo.core.iterators.SortedKeyValueIterator; +import org.apache.accumulo.iteratortest.IteratorTestInput; +import org.apache.accumulo.iteratortest.IteratorTestOutput; /** * An interface that accepts some input for testing a {@link SortedKeyValueIterator}, runs the specific implementation of the test and returns the outcome from diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java deleted file mode 100644 index 56990fb4516..00000000000 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/NoopIteratorTestCase.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.accumulo.iteratortest.testcases; - -import org.apache.accumulo.iteratortest.IteratorTestCase; -import org.apache.accumulo.iteratortest.IteratorTestInput; -import org.apache.accumulo.iteratortest.IteratorTestOutput; -import org.apache.accumulo.iteratortest.IteratorTestOutput.TestOutcome; - -/** - * An IteratorTestCase implementation that returns the original input without any external action. - */ -public class NoopIteratorTestCase implements IteratorTestCase { - - @Override - public IteratorTestOutput test(IteratorTestInput testInput) { - return new IteratorTestOutput(TestOutcome.PASSED); - } - - @Override - public boolean verify(IteratorTestOutput expected, IteratorTestOutput actual) { - // Always passes - return true; - } - -} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/OutputVerifyingTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/OutputVerifyingTestCase.java index 9e4eeba1870..5a46e4ed335 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/OutputVerifyingTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/OutputVerifyingTestCase.java @@ -16,7 +16,6 @@ */ package org.apache.accumulo.iteratortest.testcases; -import org.apache.accumulo.iteratortest.IteratorTestCase; import org.apache.accumulo.iteratortest.IteratorTestOutput; /** diff --git a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java index 7d5606cbeea..bb3e1e9b1a0 100644 --- a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java +++ b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java @@ -18,7 +18,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map.Entry; @@ -29,8 +28,7 @@ import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.iterators.user.WholeRowIterator; import org.apache.accumulo.iteratortest.junit4.BaseJUnit4IteratorTest; -import org.apache.accumulo.iteratortest.testcases.InstantiationTestCase; -import org.apache.accumulo.iteratortest.testcases.MultipleHasTopCalls; +import org.apache.accumulo.iteratortest.testcases.IteratorTestCase; import org.apache.hadoop.io.Text; import org.junit.runners.Parameterized.Parameters; @@ -43,7 +41,7 @@ public class WholeRowIteratorTest extends BaseJUnit4IteratorTest { public static Object[][] parameters() { IteratorTestInput input = getIteratorInput(); IteratorTestOutput output = getIteratorOutput(); - List tests = Arrays. asList(new MultipleHasTopCalls(), new InstantiationTestCase()); + List tests = IteratorTestCaseFinder.findAllTestCases(); return BaseJUnit4IteratorTest.createParameters(input, output, tests); } diff --git a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/framework/JUnitFrameworkTest.java similarity index 77% rename from iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java rename to iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/framework/JUnitFrameworkTest.java index 627aed78fee..133db622b8e 100644 --- a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/JUnitFrameworkTest.java +++ b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/framework/JUnitFrameworkTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.accumulo.iteratortest; +package org.apache.accumulo.iteratortest.framework; import java.util.Collections; import java.util.List; @@ -25,8 +25,11 @@ import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.iterators.IteratorEnvironment; import org.apache.accumulo.core.iterators.WrappingIterator; +import org.apache.accumulo.iteratortest.IteratorTestInput; +import org.apache.accumulo.iteratortest.IteratorTestOutput; +import org.apache.accumulo.iteratortest.IteratorTestOutput.TestOutcome; import org.apache.accumulo.iteratortest.junit4.BaseJUnit4IteratorTest; -import org.apache.accumulo.iteratortest.testcases.NoopIteratorTestCase; +import org.apache.accumulo.iteratortest.testcases.IteratorTestCase; import org.junit.runners.Parameterized.Parameters; /** @@ -34,6 +37,24 @@ */ public class JUnitFrameworkTest extends BaseJUnit4IteratorTest { + /** + * An IteratorTestCase implementation that returns the original input without any external action. + */ + public static class NoopIteratorTestCase implements IteratorTestCase { + + @Override + public IteratorTestOutput test(IteratorTestInput testInput) { + return new IteratorTestOutput(TestOutcome.PASSED); + } + + @Override + public boolean verify(IteratorTestOutput expected, IteratorTestOutput actual) { + // Always passes + return true; + } + + } + @Parameters public static Object[][] parameters() { IteratorTestInput input = getIteratorInput(); diff --git a/pom.xml b/pom.xml index c18342e6f14..589c3ab27e2 100644 --- a/pom.xml +++ b/pom.xml @@ -526,6 +526,11 @@ powermock-reflect ${powermock.version} + + org.reflections + reflections + 0.9.10 + org.slf4j slf4j-api From 50e0e83cf2593711855875cbfe951cb2cc740a4a Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Sun, 8 Nov 2015 03:03:44 -0500 Subject: [PATCH 06/15] DeepCopy and ReSeek test cases. --- .../iteratortest/IteratorTestRunner.java | 1 + .../iteratortest/IteratorTestUtil.java | 3 +- .../SimpleIteratorEnvironment.java | 2 +- .../testcases/DeepCopyTestCase.java | 63 +++++++++++ .../testcases/ReSeekTestCase.java | 103 ++++++++++++++++++ .../iteratortest/WholeRowIteratorTest.java | 18 +++ .../src/test/resources/log4j.properties | 2 + 7 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/DeepCopyTestCase.java create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java index 90c8265cd46..99825a4a41b 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestRunner.java @@ -77,6 +77,7 @@ public List runTests() { try { actualOutput = testCase.test(testInput); } catch (Exception e) { + log.error("Failed to invoke {} on {}", testCase.getClass().getName(), testInput.getIteratorClass().getName(), e); actualOutput = new IteratorTestOutput(e); } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java index a703ae7205a..5155ba9c3ca 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java @@ -22,6 +22,7 @@ import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.iterators.SortedKeyValueIterator; import org.apache.accumulo.core.iterators.SortedMapIterator; +import org.apache.accumulo.core.iterators.system.ColumnFamilySkippingIterator; import org.apache.accumulo.iteratortest.testcases.IteratorTestCase; /** @@ -38,6 +39,6 @@ public static SortedKeyValueIterator instantiateIterator(IteratorTest } public static SortedKeyValueIterator createSource(IteratorTestInput input) { - return new SortedMapIterator(Objects.requireNonNull(input).getInput()); + return new ColumnFamilySkippingIterator(new SortedMapIterator(Objects.requireNonNull(input).getInput())); } } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/environments/SimpleIteratorEnvironment.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/environments/SimpleIteratorEnvironment.java index 07f1bb25383..6204212449d 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/environments/SimpleIteratorEnvironment.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/environments/SimpleIteratorEnvironment.java @@ -69,7 +69,7 @@ public IteratorEnvironment cloneWithSamplingEnabled() { @Override public boolean isSamplingEnabled() { - throw new UnsupportedOperationException(); + return false; } @Override diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/DeepCopyTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/DeepCopyTestCase.java new file mode 100644 index 00000000000..1a608c1256b --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/DeepCopyTestCase.java @@ -0,0 +1,63 @@ +/* + * 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.accumulo.iteratortest.testcases; + +import java.io.IOException; +import java.util.Collections; +import java.util.TreeMap; + +import org.apache.accumulo.core.data.ByteSequence; +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.iterators.SortedKeyValueIterator; +import org.apache.accumulo.iteratortest.IteratorTestInput; +import org.apache.accumulo.iteratortest.IteratorTestOutput; +import org.apache.accumulo.iteratortest.IteratorTestUtil; +import org.apache.accumulo.iteratortest.environments.SimpleIteratorEnvironment; + +/** + * Test case that verifies that an iterator can use the generated instance from {@code deepCopy}. + */ +public class DeepCopyTestCase extends OutputVerifyingTestCase { + + @Override + public IteratorTestOutput test(IteratorTestInput testInput) { + final SortedKeyValueIterator skvi = IteratorTestUtil.instantiateIterator(testInput); + final SortedKeyValueIterator source = IteratorTestUtil.createSource(testInput); + + try { + skvi.init(source, testInput.getIteratorOptions(), new SimpleIteratorEnvironment()); + + SortedKeyValueIterator copy = skvi.deepCopy(new SimpleIteratorEnvironment()); + + copy.seek(testInput.getRange(), Collections. emptySet(), false); + return new IteratorTestOutput(consume(copy)); + } catch (IOException e) { + return new IteratorTestOutput(e); + } + } + + TreeMap consume(SortedKeyValueIterator skvi) throws IOException { + TreeMap data = new TreeMap<>(); + while (skvi.hasTop()) { + data.put(skvi.getTopKey(), skvi.getTopValue()); + skvi.next(); + } + return data; + } + +} diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java new file mode 100644 index 00000000000..f9a226cc87b --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java @@ -0,0 +1,103 @@ +/* + * 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.accumulo.iteratortest.testcases; + +import java.io.IOException; +import java.util.Collections; +import java.util.Random; +import java.util.TreeMap; + +import org.apache.accumulo.core.data.ByteSequence; +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Range; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.iterators.SortedKeyValueIterator; +import org.apache.accumulo.iteratortest.IteratorTestInput; +import org.apache.accumulo.iteratortest.IteratorTestOutput; +import org.apache.accumulo.iteratortest.IteratorTestUtil; +import org.apache.accumulo.iteratortest.environments.SimpleIteratorEnvironment; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Test case that verifies that an iterator can use the generated instance from {@code deepCopy}. + */ +public class ReSeekTestCase extends OutputVerifyingTestCase { + private static final Logger log = LoggerFactory.getLogger(ReSeekTestCase.class); + private static final int RESEEK_INTERVAL = 4; + + private final Random random; + + public ReSeekTestCase() { + this.random = new Random(); + } + + @Override + public IteratorTestOutput test(IteratorTestInput testInput) { + final SortedKeyValueIterator skvi = IteratorTestUtil.instantiateIterator(testInput); + final SortedKeyValueIterator source = IteratorTestUtil.createSource(testInput); + + try { + skvi.init(source, testInput.getIteratorOptions(), new SimpleIteratorEnvironment()); + skvi.seek(testInput.getRange(), Collections. emptySet(), false); + return new IteratorTestOutput(consume(skvi, testInput)); + } catch (IOException e) { + return new IteratorTestOutput(e); + } + } + + TreeMap consume(SortedKeyValueIterator skvi, IteratorTestInput testInput) throws IOException { + final TreeMap data = new TreeMap<>(); + final Range origRange = testInput.getRange(); + int reseekCount = random.nextInt(RESEEK_INTERVAL); + + int i = 0; + while (skvi.hasTop()) { + data.put(skvi.getTopKey(), skvi.getTopValue()); + + /* + * One of the trickiest cases in writing iterators: + * + * After any result is returned from a TabletServer to the client, the Iterator in the TabletServer's memory may be torn down. To preserve the state and + * guarantee that all records are received, the TabletServer does remember the last Key it returned to the client. It will recreate the Iterator (stack), + * and seek it using an updated Range. This range's start key is set to the last Key returned, non-inclusive. + */ + if (i % RESEEK_INTERVAL == reseekCount) { + // Last key + Key reSeekStartKey = skvi.getTopKey(); + + // Deepcopy the iterator + skvi = skvi.deepCopy(new SimpleIteratorEnvironment()); + + // The new range, resume where we left off (non-inclusive) + final Range newRange = new Range(reSeekStartKey, false, origRange.getEndKey(), origRange.isEndKeyInclusive()); + log.debug("Re-seeking to {}", newRange); + + // Seek there + skvi.seek(newRange, Collections. emptySet(), false); + } else { + // Every other time, it's a simple call to next() + skvi.next(); + } + + i++; + } + + return data; + } + +} diff --git a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java index bb3e1e9b1a0..0b116f2ca6b 100644 --- a/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java +++ b/iterator-test-harness/src/test/java/org/apache/accumulo/iteratortest/WholeRowIteratorTest.java @@ -64,6 +64,24 @@ private static TreeMap createInputData() { data.put(new Key("3", "a", ""), new Value("3a".getBytes())); + data.put(new Key("4", "a", "b"), new Value("4ab".getBytes())); + + data.put(new Key("5", "a", "a"), new Value("5aa".getBytes())); + data.put(new Key("5", "a", "b"), new Value("5ab".getBytes())); + data.put(new Key("5", "a", "c"), new Value("5ac".getBytes())); + data.put(new Key("5", "a", "d"), new Value("5ad".getBytes())); + + data.put(new Key("6", "", "a"), new Value("6a".getBytes())); + data.put(new Key("6", "", "b"), new Value("6b".getBytes())); + data.put(new Key("6", "", "c"), new Value("6c".getBytes())); + data.put(new Key("6", "", "d"), new Value("6d".getBytes())); + data.put(new Key("6", "", "e"), new Value("6e".getBytes())); + data.put(new Key("6", "1", "a"), new Value("61a".getBytes())); + data.put(new Key("6", "1", "b"), new Value("61b".getBytes())); + data.put(new Key("6", "1", "c"), new Value("61c".getBytes())); + data.put(new Key("6", "1", "d"), new Value("61d".getBytes())); + data.put(new Key("6", "1", "e"), new Value("61e".getBytes())); + return data; } diff --git a/iterator-test-harness/src/test/resources/log4j.properties b/iterator-test-harness/src/test/resources/log4j.properties index 9a98a8836ed..3b2c8e7fae5 100644 --- a/iterator-test-harness/src/test/resources/log4j.properties +++ b/iterator-test-harness/src/test/resources/log4j.properties @@ -20,3 +20,5 @@ log4j.appender.CA.layout.ConversionPattern=%d{ISO8601} [%-8c{2}] %-5p: %m%n log4j.logger.org.apache.accumulo.core.client.impl.ServerClient=ERROR log4j.logger.org.apache.zookeeper=ERROR +log4j.logger.org.apache.accumulo.iteratortest=DEBUG +log4j.logger.org.apache.accumulo.iteratortest.testcases=DEBUG \ No newline at end of file From e49f4ac5007068cfa109b0bc0a4279839692a428 Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Sun, 8 Nov 2015 03:05:57 -0500 Subject: [PATCH 07/15] More docs. --- .../accumulo/iteratortest/testcases/ReSeekTestCase.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java index f9a226cc87b..28ba789a2fb 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java @@ -38,6 +38,10 @@ */ public class ReSeekTestCase extends OutputVerifyingTestCase { private static final Logger log = LoggerFactory.getLogger(ReSeekTestCase.class); + + /** + * Let N be a random number between [0, RESEEK_INTERVAL). After every Nth entry "returned" to the client, recreate and reseek the iterator. + */ private static final int RESEEK_INTERVAL = 4; private final Random random; From 0d9207365f14460481efd06034989167e3dfaeab Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Wed, 9 Dec 2015 23:17:12 -0500 Subject: [PATCH 08/15] Remove Reflections, use Guava. --- iterator-test-harness/pom.xml | 4 --- .../iteratortest/IteratorTestCaseFinder.java | 31 ++++++++++++++----- pom.xml | 5 --- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/iterator-test-harness/pom.xml b/iterator-test-harness/pom.xml index 71ec528e593..d7ac3eecbe2 100644 --- a/iterator-test-harness/pom.xml +++ b/iterator-test-harness/pom.xml @@ -44,10 +44,6 @@ org.apache.hadoop hadoop-client - - org.reflections - reflections - org.slf4j slf4j-api diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCaseFinder.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCaseFinder.java index 7c6e7514982..7546460ce08 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCaseFinder.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestCaseFinder.java @@ -16,16 +16,19 @@ */ package org.apache.accumulo.iteratortest; +import java.io.IOException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; -import java.util.Set; import org.apache.accumulo.iteratortest.testcases.IteratorTestCase; -import org.reflections.Reflections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.collect.ImmutableSet; +import com.google.common.reflect.ClassPath; +import com.google.common.reflect.ClassPath.ClassInfo; + /** * A class to ease finding published test cases. */ @@ -39,18 +42,32 @@ public class IteratorTestCaseFinder { */ public static List findAllTestCases() { log.info("Searching {}", IteratorTestCase.class.getPackage().getName()); - Reflections reflections = new Reflections(IteratorTestCase.class.getPackage().getName()); + ClassPath cp; + try { + cp = ClassPath.from(IteratorTestCaseFinder.class.getClassLoader()); + } catch (IOException e) { + throw new RuntimeException(e); + } + ImmutableSet classes = cp.getTopLevelClasses(IteratorTestCase.class.getPackage().getName()); final List testCases = new ArrayList<>(); - final Set> classes = reflections.getSubTypesOf(IteratorTestCase.class); - for (Class clz : classes) { - if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) { + // final Set> classes = reflections.getSubTypesOf(IteratorTestCase.class); + for (ClassInfo classInfo : classes) { + Class clz; + try { + clz = Class.forName(classInfo.getName()); + } catch (Exception e) { + log.warn("Could not get class for " + classInfo.getName(), e); + continue; + } + + if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers()) || !IteratorTestCase.class.isAssignableFrom(clz)) { log.debug("Skipping " + clz); continue; } try { - testCases.add(clz.newInstance()); + testCases.add((IteratorTestCase) clz.newInstance()); } catch (IllegalAccessException | InstantiationException e) { log.warn("Could not instantiate {}", clz, e); } diff --git a/pom.xml b/pom.xml index 589c3ab27e2..c18342e6f14 100644 --- a/pom.xml +++ b/pom.xml @@ -526,11 +526,6 @@ powermock-reflect ${powermock.version} - - org.reflections - reflections - 0.9.10 - org.slf4j slf4j-api From 7b10aed93b34322bf0f50d1a776069e2cbb914a6 Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Wed, 9 Dec 2015 23:17:34 -0500 Subject: [PATCH 09/15] Insert an iterator that reuses instances of Key and Value. --- .../iteratortest/IteratorTestUtil.java | 2 +- .../iteratortest/SimpleKVReusingIterator.java | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/SimpleKVReusingIterator.java diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java index 5155ba9c3ca..4185846d09e 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/IteratorTestUtil.java @@ -39,6 +39,6 @@ public static SortedKeyValueIterator instantiateIterator(IteratorTest } public static SortedKeyValueIterator createSource(IteratorTestInput input) { - return new ColumnFamilySkippingIterator(new SortedMapIterator(Objects.requireNonNull(input).getInput())); + return new SimpleKVReusingIterator(new ColumnFamilySkippingIterator(new SortedMapIterator(Objects.requireNonNull(input).getInput()))); } } diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/SimpleKVReusingIterator.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/SimpleKVReusingIterator.java new file mode 100644 index 00000000000..9174b69e1e5 --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/SimpleKVReusingIterator.java @@ -0,0 +1,87 @@ +/* + * 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.accumulo.iteratortest; + +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +import org.apache.accumulo.core.data.ByteSequence; +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Range; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.iterators.IteratorEnvironment; +import org.apache.accumulo.core.iterators.SortedKeyValueIterator; + +/** + * Internally, Accumulo reuses the same instance of Key and Value to reduce the number of objects to be garbage collected. This iterator simulates that. + */ +public class SimpleKVReusingIterator implements SortedKeyValueIterator { + + private final SortedKeyValueIterator source; + private final Key topKey = new Key(); + private final Value topValue = new Value(); + + public SimpleKVReusingIterator(SortedKeyValueIterator source) { + this.source = source; + } + + @Override + public void init(SortedKeyValueIterator source, Map options, IteratorEnvironment env) throws IOException { + this.source.init(source, options, env); + } + + @Override + public boolean hasTop() { + return source.hasTop(); + } + + @Override + public void next() throws IOException { + source.next(); + load(); + } + + @Override + public void seek(Range range, Collection columnFamilies, boolean inclusive) throws IOException { + source.seek(range, columnFamilies, inclusive); + load(); + } + + @Override + public Key getTopKey() { + return topKey; + } + + @Override + public Value getTopValue() { + return topValue; + } + + @Override + public SortedKeyValueIterator deepCopy(IteratorEnvironment env) { + SortedKeyValueIterator newSource = source.deepCopy(env); + return new SimpleKVReusingIterator(newSource); + } + + private void load() { + if (hasTop()) { + topKey.set(source.getTopKey()); + topValue.set(source.getTopValue().get()); + } + } +} From 649c034dca1435ce80128e99b86258ffcfbfafbd Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Wed, 9 Dec 2015 23:18:30 -0500 Subject: [PATCH 10/15] Better comments on the pom's use of junit. We'd have to split out the junit bindings from this module to properly use optional. Do it later if there is interest. --- iterator-test-harness/pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/iterator-test-harness/pom.xml b/iterator-test-harness/pom.xml index d7ac3eecbe2..d54a0866053 100644 --- a/iterator-test-harness/pom.xml +++ b/iterator-test-harness/pom.xml @@ -26,11 +26,10 @@ Apache Accumulo Iterator Test Harness A library for testing Apache Accumulo Iterators. - + junit junit - log4j From 8f402791d5175d7f3e53874823f25517ccbb18f0 Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Wed, 9 Dec 2015 23:19:09 -0500 Subject: [PATCH 11/15] More accurate iterator recreation for iterator reseek test case. --- .../accumulo/iteratortest/testcases/ReSeekTestCase.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java index 28ba789a2fb..d5395262a3e 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/ReSeekTestCase.java @@ -84,8 +84,11 @@ TreeMap consume(SortedKeyValueIterator skvi, IteratorTestI // Last key Key reSeekStartKey = skvi.getTopKey(); - // Deepcopy the iterator - skvi = skvi.deepCopy(new SimpleIteratorEnvironment()); + // Make a new instance of the iterator + skvi = IteratorTestUtil.instantiateIterator(testInput); + final SortedKeyValueIterator sourceCopy = IteratorTestUtil.createSource(testInput); + + skvi.init(sourceCopy, testInput.getIteratorOptions(), new SimpleIteratorEnvironment()); // The new range, resume where we left off (non-inclusive) final Range newRange = new Range(reSeekStartKey, false, origRange.getEndKey(), origRange.isEndKeyInclusive()); From 4948deabdae8c1ad99d3318e868fb3b14b98eb16 Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Wed, 9 Dec 2015 23:19:30 -0500 Subject: [PATCH 12/15] Keith's suggestion for a test that uses multiple deep copies. --- .../testcases/IsolatedDeepCopiesTestCase.java | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java new file mode 100644 index 00000000000..b529b3153bd --- /dev/null +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java @@ -0,0 +1,124 @@ +/* + * 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.accumulo.iteratortest.testcases; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.TreeMap; + +import org.apache.accumulo.core.data.ByteSequence; +import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Value; +import org.apache.accumulo.core.iterators.SortedKeyValueIterator; +import org.apache.accumulo.iteratortest.IteratorTestInput; +import org.apache.accumulo.iteratortest.IteratorTestOutput; +import org.apache.accumulo.iteratortest.IteratorTestUtil; +import org.apache.accumulo.iteratortest.environments.SimpleIteratorEnvironment; + +/** + * Test case that verifies that copies do not impact one another. + */ +public class IsolatedDeepCopiesTestCase extends OutputVerifyingTestCase { + + @Override + public IteratorTestOutput test(IteratorTestInput testInput) { + final SortedKeyValueIterator skvi = IteratorTestUtil.instantiateIterator(testInput); + final SortedKeyValueIterator source = IteratorTestUtil.createSource(testInput); + + try { + skvi.init(source, testInput.getIteratorOptions(), new SimpleIteratorEnvironment()); + + SortedKeyValueIterator copy1 = skvi.deepCopy(new SimpleIteratorEnvironment()); + SortedKeyValueIterator copy2 = copy1.deepCopy(new SimpleIteratorEnvironment()); + + skvi.seek(testInput.getRange(), Collections. emptySet(), false); + copy1.seek(testInput.getRange(), Collections. emptySet(), false); + copy2.seek(testInput.getRange(), Collections. emptySet(), false); + + TreeMap output = consumeMany(Arrays.asList(skvi, copy1, copy2)); + + return new IteratorTestOutput(output); + } catch (IOException e) { + return new IteratorTestOutput(e); + } + } + + TreeMap consumeMany(Collection> iterators) throws IOException { + TreeMap data = new TreeMap<>(); + // All of the copies should have consistent results from concurrent use + while (allHasTop(iterators)) { + data.put(getTopKey(iterators), getTopValue(iterators)); + next(iterators); + } + + // All of the iterators should be consumed. + for (SortedKeyValueIterator iter : iterators) { + if (iter.hasTop()) { + return null; + } + } + + return data; + } + + boolean allHasTop(Collection> iterators) { + for (SortedKeyValueIterator iter : iterators) { + if (!iter.hasTop()) { + return false; + } + } + return true; + } + + Key getTopKey(Collection> iterators) { + boolean first = true; + Key topKey = null; + for (SortedKeyValueIterator iter : iterators) { + if (first) { + topKey = iter.getTopKey(); + first = false; + } else if (!topKey.equals(iter.getTopKey())) { + throw new IllegalStateException("Inconsistent keys between two iterators: " + topKey + " " + iter.getTopKey()); + } + } + + return topKey; + } + + Value getTopValue(Collection> iterators) { + boolean first = true; + Value topValue = null; + for (SortedKeyValueIterator iter : iterators) { + if (first) { + topValue = iter.getTopValue(); + first = false; + } else if (!topValue.equals(iter.getTopValue())) { + throw new IllegalStateException("Inconsistent values between two iterators: " + topValue + " " + iter.getTopValue()); + } + } + + return topValue; + } + + void next(Collection> iterators) throws IOException { + for (SortedKeyValueIterator iter : iterators) { + iter.next(); + } + } +} From b856bcc25a8645f60fb1efee850e503a0541589d Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Wed, 9 Dec 2015 23:20:28 -0500 Subject: [PATCH 13/15] Whitespace fixes. --- .../iteratortest/testcases/IsolatedDeepCopiesTestCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java index b529b3153bd..b87496210e0 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java @@ -109,7 +109,7 @@ Value getTopValue(Collection> iterators) { topValue = iter.getTopValue(); first = false; } else if (!topValue.equals(iter.getTopValue())) { - throw new IllegalStateException("Inconsistent values between two iterators: " + topValue + " " + iter.getTopValue()); + throw new IllegalStateException("Inconsistent values between two iterators: " + topValue + " " + iter.getTopValue()); } } From d95666d62bd5d43e94c42416a45d9bbb3430d28d Mon Sep 17 00:00:00 2001 From: Dylan Hutchison Date: Thu, 10 Dec 2015 10:20:40 -0800 Subject: [PATCH 14/15] Test scenario of deepCopy in the middle of an iteration. --- .../testcases/IsolatedDeepCopiesTestCase.java | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java index b87496210e0..2bc1e636977 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java @@ -20,10 +20,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Random; +import java.util.Set; import java.util.TreeMap; import org.apache.accumulo.core.data.ByteSequence; import org.apache.accumulo.core.data.Key; +import org.apache.accumulo.core.data.Range; import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.iterators.SortedKeyValueIterator; import org.apache.accumulo.iteratortest.IteratorTestInput; @@ -36,6 +39,8 @@ */ public class IsolatedDeepCopiesTestCase extends OutputVerifyingTestCase { + private final Random random = new Random(); + @Override public IteratorTestOutput test(IteratorTestInput testInput) { final SortedKeyValueIterator skvi = IteratorTestUtil.instantiateIterator(testInput); @@ -47,11 +52,15 @@ public IteratorTestOutput test(IteratorTestInput testInput) { SortedKeyValueIterator copy1 = skvi.deepCopy(new SimpleIteratorEnvironment()); SortedKeyValueIterator copy2 = copy1.deepCopy(new SimpleIteratorEnvironment()); - skvi.seek(testInput.getRange(), Collections. emptySet(), false); - copy1.seek(testInput.getRange(), Collections. emptySet(), false); - copy2.seek(testInput.getRange(), Collections. emptySet(), false); + Range seekRange = testInput.getRange(); + Set seekColumnFamilies = Collections. emptySet(); + boolean seekInclusive = false; + + skvi.seek(testInput.getRange(), seekColumnFamilies, seekInclusive); + copy1.seek(testInput.getRange(), seekColumnFamilies, seekInclusive); + copy2.seek(testInput.getRange(), seekColumnFamilies, seekInclusive); - TreeMap output = consumeMany(Arrays.asList(skvi, copy1, copy2)); + TreeMap output = consumeMany(Arrays.asList(skvi, copy1, copy2), seekRange, seekColumnFamilies, seekInclusive); return new IteratorTestOutput(output); } catch (IOException e) { @@ -59,10 +68,18 @@ public IteratorTestOutput test(IteratorTestInput testInput) { } } - TreeMap consumeMany(Collection> iterators) throws IOException { + TreeMap consumeMany(Collection> iterators, Range range, Set seekColumnFamilies, + boolean seekInclusive) throws IOException { TreeMap data = new TreeMap<>(); // All of the copies should have consistent results from concurrent use while (allHasTop(iterators)) { + // occasionally deep copy one of the existing iterators + if (random.nextInt(3) == 0) { + SortedKeyValueIterator newcopy = getRandomElement(iterators).deepCopy(new SimpleIteratorEnvironment()); + newcopy.seek(new Range(getTopKey(iterators), true, range.getEndKey(), range.isEndKeyInclusive()), seekColumnFamilies, seekInclusive); + iterators.add(newcopy); + } + data.put(getTopKey(iterators), getTopValue(iterators)); next(iterators); } @@ -77,6 +94,17 @@ TreeMap consumeMany(Collection> ite return data; } + private E getRandomElement(Collection iterators) { + if (iterators == null || iterators.size() == 0) + throw new IllegalArgumentException("should not pass an empty collection"); + int num = random.nextInt(iterators.size()); + for (E e : iterators) { + if (num-- == 0) + return e; + } + throw new AssertionError(); + } + boolean allHasTop(Collection> iterators) { for (SortedKeyValueIterator iter : iterators) { if (!iter.hasTop()) { From 357390b76a916be56be71c92579ff29cf755567e Mon Sep 17 00:00:00 2001 From: Josh Elser Date: Fri, 11 Dec 2015 00:30:21 -0500 Subject: [PATCH 15/15] ACCUMULO-626 Slight cleanup to Dylan's changes. Closes joshelser/accumulo#2 --- .../testcases/IsolatedDeepCopiesTestCase.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java index 2bc1e636977..b3be0c45d41 100644 --- a/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java +++ b/iterator-test-harness/src/main/java/org/apache/accumulo/iteratortest/testcases/IsolatedDeepCopiesTestCase.java @@ -17,6 +17,7 @@ package org.apache.accumulo.iteratortest.testcases; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -33,11 +34,14 @@ import org.apache.accumulo.iteratortest.IteratorTestOutput; import org.apache.accumulo.iteratortest.IteratorTestUtil; import org.apache.accumulo.iteratortest.environments.SimpleIteratorEnvironment; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Test case that verifies that copies do not impact one another. */ public class IsolatedDeepCopiesTestCase extends OutputVerifyingTestCase { + private static final Logger log = LoggerFactory.getLogger(IsolatedDeepCopiesTestCase.class); private final Random random = new Random(); @@ -60,7 +64,7 @@ public IteratorTestOutput test(IteratorTestInput testInput) { copy1.seek(testInput.getRange(), seekColumnFamilies, seekInclusive); copy2.seek(testInput.getRange(), seekColumnFamilies, seekInclusive); - TreeMap output = consumeMany(Arrays.asList(skvi, copy1, copy2), seekRange, seekColumnFamilies, seekInclusive); + TreeMap output = consumeMany(new ArrayList<>(Arrays.asList(skvi, copy1, copy2)), seekRange, seekColumnFamilies, seekInclusive); return new IteratorTestOutput(output); } catch (IOException e) { @@ -75,8 +79,10 @@ TreeMap consumeMany(Collection> ite while (allHasTop(iterators)) { // occasionally deep copy one of the existing iterators if (random.nextInt(3) == 0) { + log.debug("Deep-copying and re-seeking an iterator"); SortedKeyValueIterator newcopy = getRandomElement(iterators).deepCopy(new SimpleIteratorEnvironment()); newcopy.seek(new Range(getTopKey(iterators), true, range.getEndKey(), range.isEndKeyInclusive()), seekColumnFamilies, seekInclusive); + // keep using the new one too, should act like the others iterators.add(newcopy); }