From 9c9039582e1cf4303a9ecc7c1e4cfc9c16e07559 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Fri, 13 Jul 2012 14:45:19 +0000 Subject: [PATCH] LUCENE-4211: add AssertingReader/Codec git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1361243 13f79535-47bb-0310-9956-ffa450edef68 --- .../index/MultiDocsAndPositionsEnum.java | 2 + .../apache/lucene/index/MultiDocsEnum.java | 1 + .../lucene/search/highlight/TokenSources.java | 3 + .../codecs/asserting/AssertingCodec.java | 94 +++++ .../asserting/AssertingPostingsFormat.java | 88 ++++ .../asserting/AssertingTermVectorsFormat.java | 72 ++++ .../lucene/codecs/asserting/package.html | 25 ++ .../lucene/index/AssertingAtomicReader.java | 379 ++++++++++++++++++ .../index/AssertingDirectoryReader.java | 90 +++++ .../org/apache/lucene/index/RandomCodec.java | 2 + .../apache/lucene/util/LuceneTestCase.java | 12 +- .../util/TestRuleSetupAndRestoreClassEnv.java | 3 + .../services/org.apache.lucene.codecs.Codec | 1 + .../org.apache.lucene.codecs.PostingsFormat | 2 + 14 files changed, 773 insertions(+), 1 deletion(-) create mode 100644 lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingCodec.java create mode 100644 lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java create mode 100644 lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingTermVectorsFormat.java create mode 100644 lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/package.html create mode 100644 lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java create mode 100644 lucene/test-framework/src/java/org/apache/lucene/index/AssertingDirectoryReader.java diff --git a/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java b/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java index 777ca0ae46b..b15f1ec69b5 100644 --- a/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java +++ b/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java @@ -55,6 +55,7 @@ public final class MultiDocsAndPositionsEnum extends DocsAndPositionsEnum { this.subs[i].slice = subs[i].slice; } upto = -1; + doc = -1; current = null; return this; } @@ -69,6 +70,7 @@ public final class MultiDocsAndPositionsEnum extends DocsAndPositionsEnum { @Override public int freq() throws IOException { + assert current != null; return current.freq(); } diff --git a/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java b/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java index 1e09ef7dba6..2d0fd252d06 100644 --- a/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java +++ b/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java @@ -52,6 +52,7 @@ public final class MultiDocsEnum extends DocsEnum { this.subs[i].slice = subs[i].slice; } upto = -1; + doc = -1; current = null; return this; } diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenSources.java b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenSources.java index 1824741b9c7..8606e171925 100644 --- a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenSources.java +++ b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/TokenSources.java @@ -37,6 +37,7 @@ import org.apache.lucene.index.Fields; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; +import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.util.ArrayUtil; import org.apache.lucene.util.BytesRef; @@ -128,6 +129,8 @@ public class TokenSources { if (termsEnum.next() != null) { DocsAndPositionsEnum dpEnum = termsEnum.docsAndPositions(null, null, false); if (dpEnum != null) { + int doc = dpEnum.nextDoc(); + assert doc >= 0 && doc != DocIdSetIterator.NO_MORE_DOCS; int pos = dpEnum.nextPosition(); if (pos >= 0) { return true; diff --git a/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingCodec.java b/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingCodec.java new file mode 100644 index 00000000000..30f10e6753a --- /dev/null +++ b/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingCodec.java @@ -0,0 +1,94 @@ +package org.apache.lucene.codecs.asserting; + +/* + * 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. + */ + +import org.apache.lucene.codecs.Codec; +import org.apache.lucene.codecs.DocValuesFormat; +import org.apache.lucene.codecs.FieldInfosFormat; +import org.apache.lucene.codecs.LiveDocsFormat; +import org.apache.lucene.codecs.NormsFormat; +import org.apache.lucene.codecs.PostingsFormat; +import org.apache.lucene.codecs.SegmentInfoFormat; +import org.apache.lucene.codecs.StoredFieldsFormat; +import org.apache.lucene.codecs.TermVectorsFormat; +import org.apache.lucene.codecs.lucene40.Lucene40Codec; // javadocs @link +import org.apache.lucene.codecs.lucene40.Lucene40DocValuesFormat; +import org.apache.lucene.codecs.lucene40.Lucene40FieldInfosFormat; +import org.apache.lucene.codecs.lucene40.Lucene40LiveDocsFormat; +import org.apache.lucene.codecs.lucene40.Lucene40NormsFormat; +import org.apache.lucene.codecs.lucene40.Lucene40SegmentInfoFormat; +import org.apache.lucene.codecs.lucene40.Lucene40StoredFieldsFormat; + +/** + * Acts like {@link Lucene40Codec} but with additional asserts. + */ +public class AssertingCodec extends Codec { + + private final PostingsFormat postings = new AssertingPostingsFormat(); + private final SegmentInfoFormat infos = new Lucene40SegmentInfoFormat(); + private final StoredFieldsFormat fields = new Lucene40StoredFieldsFormat(); + private final FieldInfosFormat fieldInfos = new Lucene40FieldInfosFormat(); + private final TermVectorsFormat vectors = new AssertingTermVectorsFormat(); + private final DocValuesFormat docValues = new Lucene40DocValuesFormat(); + private final NormsFormat norms = new Lucene40NormsFormat(); + private final LiveDocsFormat liveDocs = new Lucene40LiveDocsFormat(); + + public AssertingCodec() { + super("Asserting"); + } + + @Override + public PostingsFormat postingsFormat() { + return postings; + } + + @Override + public DocValuesFormat docValuesFormat() { + return docValues; + } + + @Override + public StoredFieldsFormat storedFieldsFormat() { + return fields; + } + + @Override + public TermVectorsFormat termVectorsFormat() { + return vectors; + } + + @Override + public FieldInfosFormat fieldInfosFormat() { + return fieldInfos; + } + + @Override + public SegmentInfoFormat segmentInfoFormat() { + return infos; + } + + @Override + public NormsFormat normsFormat() { + return norms; + } + + @Override + public LiveDocsFormat liveDocsFormat() { + return liveDocs; + } +} diff --git a/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java b/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java new file mode 100644 index 00000000000..fb33e38deb2 --- /dev/null +++ b/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingPostingsFormat.java @@ -0,0 +1,88 @@ +package org.apache.lucene.codecs.asserting; + +/* + * 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. + */ + +import java.io.IOException; + +import org.apache.lucene.codecs.FieldsConsumer; +import org.apache.lucene.codecs.FieldsProducer; +import org.apache.lucene.codecs.PostingsFormat; +import org.apache.lucene.codecs.lucene40.Lucene40PostingsFormat; +import org.apache.lucene.index.AssertingAtomicReader; +import org.apache.lucene.index.FieldsEnum; +import org.apache.lucene.index.SegmentReadState; +import org.apache.lucene.index.SegmentWriteState; +import org.apache.lucene.index.Terms; + +/** + * Just like {@link Lucene40PostingsFormat} but with additional asserts. + */ +public class AssertingPostingsFormat extends PostingsFormat { + private final PostingsFormat in = new Lucene40PostingsFormat(); + + public AssertingPostingsFormat() { + super("Asserting"); + } + + // TODO: we could add some useful checks here? + @Override + public FieldsConsumer fieldsConsumer(SegmentWriteState state) throws IOException { + return in.fieldsConsumer(state); + } + + @Override + public FieldsProducer fieldsProducer(SegmentReadState state) throws IOException { + return new AssertingFieldsProducer(in.fieldsProducer(state)); + } + + static class AssertingFieldsProducer extends FieldsProducer { + private final FieldsProducer in; + + AssertingFieldsProducer(FieldsProducer in) { + this.in = in; + } + + @Override + public void close() throws IOException { + in.close(); + } + + @Override + public FieldsEnum iterator() throws IOException { + FieldsEnum iterator = in.iterator(); + assert iterator != null; + return new AssertingAtomicReader.AssertingFieldsEnum(iterator); + } + + @Override + public Terms terms(String field) throws IOException { + Terms terms = in.terms(field); + return terms == null ? null : new AssertingAtomicReader.AssertingTerms(terms); + } + + @Override + public int size() throws IOException { + return in.size(); + } + + @Override + public long getUniqueTermCount() throws IOException { + return in.getUniqueTermCount(); + } + } +} diff --git a/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingTermVectorsFormat.java b/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingTermVectorsFormat.java new file mode 100644 index 00000000000..eacb4d9995d --- /dev/null +++ b/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/AssertingTermVectorsFormat.java @@ -0,0 +1,72 @@ +package org.apache.lucene.codecs.asserting; + +/* + * 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. + */ + +import java.io.IOException; + +import org.apache.lucene.codecs.TermVectorsFormat; +import org.apache.lucene.codecs.TermVectorsReader; +import org.apache.lucene.codecs.TermVectorsWriter; +import org.apache.lucene.codecs.lucene40.Lucene40TermVectorsFormat; +import org.apache.lucene.index.AssertingAtomicReader; +import org.apache.lucene.index.FieldInfos; +import org.apache.lucene.index.Fields; +import org.apache.lucene.index.SegmentInfo; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.IOContext; + +/** + * Just like {@link Lucene40TermVectorsFormat} but with additional asserts. + */ +public class AssertingTermVectorsFormat extends TermVectorsFormat { + private final TermVectorsFormat in = new Lucene40TermVectorsFormat(); + + @Override + public TermVectorsReader vectorsReader(Directory directory, SegmentInfo segmentInfo, FieldInfos fieldInfos, IOContext context) throws IOException { + return new AssertingTermVectorsReader(in.vectorsReader(directory, segmentInfo, fieldInfos, context)); + } + + @Override + public TermVectorsWriter vectorsWriter(Directory directory, SegmentInfo segmentInfo, IOContext context) throws IOException { + return in.vectorsWriter(directory, segmentInfo, context); + } + + static class AssertingTermVectorsReader extends TermVectorsReader { + private final TermVectorsReader in; + + AssertingTermVectorsReader(TermVectorsReader in) { + this.in = in; + } + + @Override + public void close() throws IOException { + in.close(); + } + + @Override + public Fields get(int doc) throws IOException { + Fields fields = in.get(doc); + return fields == null ? null : new AssertingAtomicReader.AssertingFields(fields); + } + + @Override + public TermVectorsReader clone() { + return new AssertingTermVectorsReader(in.clone()); + } + } +} diff --git a/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/package.html b/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/package.html new file mode 100644 index 00000000000..a4c89449799 --- /dev/null +++ b/lucene/test-framework/src/java/org/apache/lucene/codecs/asserting/package.html @@ -0,0 +1,25 @@ + + + + + + + +Codec for testing that asserts various contracts of the codec apis. + + diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java b/lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java new file mode 100644 index 00000000000..c082826ea86 --- /dev/null +++ b/lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java @@ -0,0 +1,379 @@ +package org.apache.lucene.index; + +import java.io.IOException; + +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.util.Bits; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.automaton.CompiledAutomaton; + +/* + * 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. + */ + +/** + * A {@link FilterAtomicReader} that can be used to apply + * additional checks for tests. + */ +public class AssertingAtomicReader extends FilterAtomicReader { + + public AssertingAtomicReader(AtomicReader in) { + super(in); + // check some basic reader sanity + assert in.maxDoc() >= 0; + assert in.numDocs() <= in.maxDoc(); + assert in.numDeletedDocs() + in.numDocs() == in.maxDoc(); + assert !in.hasDeletions() || in.numDeletedDocs() > 0 && in.numDocs() < in.maxDoc(); + } + + @Override + public Fields fields() throws IOException { + Fields fields = super.fields(); + return fields == null ? null : new AssertingFields(fields); + } + + @Override + public Fields getTermVectors(int docID) throws IOException { + Fields fields = super.getTermVectors(docID); + return fields == null ? null : new AssertingFields(fields); + } + + /** + * Wraps a Fields but with additional asserts + */ + public static class AssertingFields extends FilterFields { + public AssertingFields(Fields in) { + super(in); + } + + @Override + public FieldsEnum iterator() throws IOException { + FieldsEnum fieldsEnum = super.iterator(); + assert fieldsEnum != null; + return new AssertingFieldsEnum(fieldsEnum); + } + + @Override + public Terms terms(String field) throws IOException { + Terms terms = super.terms(field); + return terms == null ? null : new AssertingTerms(terms); + } + } + + /** + * Wraps a FieldsEnum but with additional asserts + */ + public static class AssertingFieldsEnum extends FilterFieldsEnum { + public AssertingFieldsEnum(FieldsEnum in) { + super(in); + } + + @Override + public Terms terms() throws IOException { + Terms terms = super.terms(); + return terms == null ? null : new AssertingTerms(terms); + } + } + + /** + * Wraps a Terms but with additional asserts + */ + public static class AssertingTerms extends FilterTerms { + public AssertingTerms(Terms in) { + super(in); + } + + @Override + public TermsEnum intersect(CompiledAutomaton automaton, BytesRef bytes) throws IOException { + TermsEnum termsEnum = super.intersect(automaton, bytes); + assert termsEnum != null; + return new AssertingTermsEnum(termsEnum); + } + + @Override + public TermsEnum iterator(TermsEnum reuse) throws IOException { + // TODO: should we give this thing a random to be super-evil, + // and randomly *not* unwrap? + if (reuse instanceof AssertingTermsEnum) { + reuse = ((AssertingTermsEnum) reuse).in; + } + TermsEnum termsEnum = super.iterator(reuse); + assert termsEnum != null; + return new AssertingTermsEnum(termsEnum); + } + } + + static class AssertingTermsEnum extends FilterTermsEnum { + private enum State {INITIAL, POSITIONED, UNPOSITIONED}; + private State state = State.INITIAL; + + public AssertingTermsEnum(TermsEnum in) { + super(in); + } + + @Override + public DocsEnum docs(Bits liveDocs, DocsEnum reuse, boolean needsFreqs) throws IOException { + assert state == State.POSITIONED: "docs(...) called on unpositioned TermsEnum"; + + // TODO: should we give this thing a random to be super-evil, + // and randomly *not* unwrap? + if (reuse instanceof AssertingDocsEnum) { + reuse = ((AssertingDocsEnum) reuse).in; + } + DocsEnum docs = super.docs(liveDocs, reuse, needsFreqs); + return docs == null ? null : new AssertingDocsEnum(docs); + } + + @Override + public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, boolean needsOffsets) throws IOException { + assert state == State.POSITIONED: "docsAndPositions(...) called on unpositioned TermsEnum"; + + // TODO: should we give this thing a random to be super-evil, + // and randomly *not* unwrap? + if (reuse instanceof AssertingDocsAndPositionsEnum) { + reuse = ((AssertingDocsAndPositionsEnum) reuse).in; + } + DocsAndPositionsEnum docs = super.docsAndPositions(liveDocs, reuse, needsOffsets); + return docs == null ? null : new AssertingDocsAndPositionsEnum(docs); + } + + // TODO: we should separately track if we are 'at the end' ? + // someone should not call next() after it returns null!!!! + @Override + public BytesRef next() throws IOException { + assert state == State.INITIAL || state == State.POSITIONED: "next() called on unpositioned TermsEnum"; + BytesRef result = super.next(); + if (result == null) { + state = State.UNPOSITIONED; + } else { + state = State.POSITIONED; + } + return result; + } + + @Override + public long ord() throws IOException { + assert state == State.POSITIONED : "ord() called on unpositioned TermsEnum"; + return super.ord(); + } + + @Override + public int docFreq() throws IOException { + assert state == State.POSITIONED : "docFreq() called on unpositioned TermsEnum"; + return super.docFreq(); + } + + @Override + public long totalTermFreq() throws IOException { + assert state == State.POSITIONED : "totalTermFreq() called on unpositioned TermsEnum"; + return super.totalTermFreq(); + } + + @Override + public BytesRef term() throws IOException { + assert state == State.POSITIONED : "term() called on unpositioned TermsEnum"; + return super.term(); + } + + @Override + public void seekExact(long ord) throws IOException { + super.seekExact(ord); + state = State.POSITIONED; + } + + @Override + public SeekStatus seekCeil(BytesRef term, boolean useCache) throws IOException { + SeekStatus result = super.seekCeil(term, useCache); + if (result == SeekStatus.END) { + state = State.UNPOSITIONED; + } else { + state = State.POSITIONED; + } + return result; + } + + @Override + public boolean seekExact(BytesRef text, boolean useCache) throws IOException { + if (super.seekExact(text, useCache)) { + state = State.POSITIONED; + return true; + } else { + state = State.UNPOSITIONED; + return false; + } + } + + @Override + public TermState termState() throws IOException { + assert state == State.POSITIONED : "termState() called on unpositioned TermsEnum"; + return super.termState(); + } + + @Override + public void seekExact(BytesRef term, TermState state) throws IOException { + super.seekExact(term, state); + this.state = State.POSITIONED; + } + } + + static enum DocsEnumState { START, ITERATING, FINISHED }; + static class AssertingDocsEnum extends FilterDocsEnum { + private DocsEnumState state = DocsEnumState.START; + + public AssertingDocsEnum(DocsEnum in) { + super(in); + int docid = in.docID(); + assert docid == -1 || docid == DocIdSetIterator.NO_MORE_DOCS : "invalid initial doc id: " + docid; + } + + @Override + public int nextDoc() throws IOException { + assert state != DocsEnumState.FINISHED : "nextDoc() called after NO_MORE_DOCS"; + int nextDoc = super.nextDoc(); + assert nextDoc >= 0 : "invalid doc id: " + nextDoc; + if (nextDoc == DocIdSetIterator.NO_MORE_DOCS) { + state = DocsEnumState.FINISHED; + } else { + state = DocsEnumState.ITERATING; + } + return nextDoc; + } + + @Override + public int advance(int target) throws IOException { + assert state != DocsEnumState.FINISHED : "advance() called after NO_MORE_DOCS"; + int advanced = super.advance(target); + assert advanced >= 0 : "invalid doc id: " + advanced; + assert advanced >= target : "backwards advance from: " + target + " to: " + advanced; + if (advanced == DocIdSetIterator.NO_MORE_DOCS) { + state = DocsEnumState.FINISHED; + } else { + state = DocsEnumState.ITERATING; + } + return advanced; + } + + // NOTE: We don't assert anything for docId(). Specifically DocsEnum javadocs + // are ambiguous with DocIdSetIterator here, DocIdSetIterator says its ok + // to call this method before nextDoc(), just that it must be -1 or NO_MORE_DOCS! + + @Override + public int freq() throws IOException { + assert state != DocsEnumState.START : "freq() called before nextDoc()/advance()"; + assert state != DocsEnumState.FINISHED : "freq() called after NO_MORE_DOCS"; + int freq = super.freq(); + assert freq > 0; + return freq; + } + } + + static class AssertingDocsAndPositionsEnum extends FilterDocsAndPositionsEnum { + private DocsEnumState state = DocsEnumState.START; + private int positionMax = 0; + private int positionCount = 0; + + public AssertingDocsAndPositionsEnum(DocsAndPositionsEnum in) { + super(in); + int docid = in.docID(); + assert docid == -1 || docid == DocIdSetIterator.NO_MORE_DOCS : "invalid initial doc id: " + docid; + } + + @Override + public int nextDoc() throws IOException { + assert state != DocsEnumState.FINISHED : "nextDoc() called after NO_MORE_DOCS"; + int nextDoc = super.nextDoc(); + assert nextDoc >= 0 : "invalid doc id: " + nextDoc; + positionCount = 0; + if (nextDoc == DocIdSetIterator.NO_MORE_DOCS) { + state = DocsEnumState.FINISHED; + positionMax = 0; + } else { + state = DocsEnumState.ITERATING; + positionMax = super.freq(); + } + return nextDoc; + } + + @Override + public int advance(int target) throws IOException { + assert state != DocsEnumState.FINISHED : "advance() called after NO_MORE_DOCS"; + int advanced = super.advance(target); + assert advanced >= 0 : "invalid doc id: " + advanced; + assert advanced >= target : "backwards advance from: " + target + " to: " + advanced; + positionCount = 0; + if (advanced == DocIdSetIterator.NO_MORE_DOCS) { + state = DocsEnumState.FINISHED; + positionMax = 0; + } else { + state = DocsEnumState.ITERATING; + positionMax = super.freq(); + } + return advanced; + } + + @Override + public int freq() throws IOException { + assert state != DocsEnumState.START : "freq() called before nextDoc()/advance()"; + assert state != DocsEnumState.FINISHED : "freq() called after NO_MORE_DOCS"; + int freq = super.freq(); + assert freq > 0; + return freq; + } + + @Override + public int nextPosition() throws IOException { + assert state != DocsEnumState.START : "nextPosition() called before nextDoc()/advance()"; + assert state != DocsEnumState.FINISHED : "nextPosition() called after NO_MORE_DOCS"; + assert positionCount < positionMax : "nextPosition() called more than freq() times!"; + int position = super.nextPosition(); + assert position >= 0 || position == -1 : "invalid position: " + position; + positionCount++; + return position; + } + + @Override + public int startOffset() throws IOException { + assert state != DocsEnumState.START : "startOffset() called before nextDoc()/advance()"; + assert state != DocsEnumState.FINISHED : "startOffset() called after NO_MORE_DOCS"; + assert positionCount > 0 : "startOffset() called before nextPosition()!"; + return super.startOffset(); + } + + @Override + public int endOffset() throws IOException { + assert state != DocsEnumState.START : "endOffset() called before nextDoc()/advance()"; + assert state != DocsEnumState.FINISHED : "endOffset() called after NO_MORE_DOCS"; + assert positionCount > 0 : "endOffset() called before nextPosition()!"; + return super.endOffset(); + } + + @Override + public BytesRef getPayload() throws IOException { + assert state != DocsEnumState.START : "getPayload() called before nextDoc()/advance()"; + assert state != DocsEnumState.FINISHED : "getPayload() called after NO_MORE_DOCS"; + assert positionCount > 0 : "getPayload() called before nextPosition()!"; + return super.getPayload(); + } + + @Override + public boolean hasPayload() { + assert state != DocsEnumState.START : "hasPayload() called before nextDoc()/advance()"; + assert state != DocsEnumState.FINISHED : "hasPayload() called after NO_MORE_DOCS"; + assert positionCount > 0 : "hasPayload() called before nextPosition()!"; + return super.hasPayload(); + } + } +} \ No newline at end of file diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/AssertingDirectoryReader.java b/lucene/test-framework/src/java/org/apache/lucene/index/AssertingDirectoryReader.java new file mode 100644 index 00000000000..1453cbeb86f --- /dev/null +++ b/lucene/test-framework/src/java/org/apache/lucene/index/AssertingDirectoryReader.java @@ -0,0 +1,90 @@ +package org.apache.lucene.index; + +/* + * 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. + */ + +import java.io.IOException; +import java.util.List; + +/** + * A {@link DirectoryReader} that wraps all its subreaders with + * {@link AssertingAtomicReader} + */ +public class AssertingDirectoryReader extends DirectoryReader { + protected DirectoryReader in; + + public AssertingDirectoryReader(DirectoryReader in) { + super(in.directory(), wrap(in.getSequentialSubReaders())); + this.in = in; + } + + private static AtomicReader[] wrap(List readers) { + AtomicReader[] wrapped = new AtomicReader[readers.size()]; + for (int i = 0; i < readers.size(); i++) { + wrapped[i] = new AssertingAtomicReader(readers.get(i)); + } + return wrapped; + } + + @Override + protected DirectoryReader doOpenIfChanged() throws IOException { + DirectoryReader d = in.doOpenIfChanged(); + return d == null ? null : new AssertingDirectoryReader(d); + } + + @Override + protected DirectoryReader doOpenIfChanged(IndexCommit commit) throws IOException { + DirectoryReader d = in.doOpenIfChanged(commit); + return d == null ? null : new AssertingDirectoryReader(d); + } + + @Override + protected DirectoryReader doOpenIfChanged(IndexWriter writer, boolean applyAllDeletes) throws IOException { + DirectoryReader d = in.doOpenIfChanged(writer, applyAllDeletes); + return d == null ? null : new AssertingDirectoryReader(d); + } + + @Override + public long getVersion() { + return in.getVersion(); + } + + @Override + public boolean isCurrent() throws IOException { + return in.isCurrent(); + } + + @Override + public IndexCommit getIndexCommit() throws IOException { + return in.getIndexCommit(); + } + + @Override + protected void doClose() throws IOException { + in.doClose(); + } + + @Override + public Object getCoreCacheKey() { + return in.getCoreCacheKey(); + } + + @Override + public Object getCombinedCoreAndDeletesKey() { + return in.getCombinedCoreAndDeletesKey(); + } +} diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/RandomCodec.java b/lucene/test-framework/src/java/org/apache/lucene/index/RandomCodec.java index 1c113d38abc..d9973fd12a4 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/index/RandomCodec.java +++ b/lucene/test-framework/src/java/org/apache/lucene/index/RandomCodec.java @@ -28,6 +28,7 @@ import java.util.Random; import java.util.Set; import org.apache.lucene.codecs.PostingsFormat; +import org.apache.lucene.codecs.asserting.AssertingPostingsFormat; import org.apache.lucene.codecs.lucene40.Lucene40Codec; import org.apache.lucene.codecs.lucene40.Lucene40PostingsFormat; import org.apache.lucene.codecs.lucene40ords.Lucene40WithOrds; @@ -99,6 +100,7 @@ public class RandomCodec extends Lucene40Codec { new NestedPulsingPostingsFormat(), new Lucene40WithOrds(), new SimpleTextPostingsFormat(), + new AssertingPostingsFormat(), new MemoryPostingsFormat(true, random.nextFloat()), new MemoryPostingsFormat(false, random.nextFloat())); diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java index 0a4a8b7e490..b9b91282f02 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java +++ b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java @@ -1010,7 +1010,7 @@ public abstract class LuceneTestCase extends Assert { // TODO: remove this, and fix those tests to wrap before putting slow around: final boolean wasOriginallyAtomic = r instanceof AtomicReader; for (int i = 0, c = random.nextInt(6)+1; i < c; i++) { - switch(random.nextInt(4)) { + switch(random.nextInt(5)) { case 0: r = SlowCompositeReaderWrapper.wrap(r); break; @@ -1041,6 +1041,16 @@ public abstract class LuceneTestCase extends Assert { new FieldFilterAtomicReader(ar, fields, true) ); break; + case 4: + // Häckidy-Hick-Hack: a standard Reader will cause FC insanity, so we use + // QueryUtils' reader with a fake cache key, so insanity checker cannot walk + // along our reader: + if (r instanceof AtomicReader) { + r = new FCInvisibleMultiReader(new AssertingAtomicReader((AtomicReader)r)); + } else if (r instanceof DirectoryReader) { + r = new FCInvisibleMultiReader(new AssertingDirectoryReader((DirectoryReader)r)); + } + break; default: fail("should not get here"); } diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/TestRuleSetupAndRestoreClassEnv.java b/lucene/test-framework/src/java/org/apache/lucene/util/TestRuleSetupAndRestoreClassEnv.java index 19b1047e535..9c63a909ce0 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/util/TestRuleSetupAndRestoreClassEnv.java +++ b/lucene/test-framework/src/java/org/apache/lucene/util/TestRuleSetupAndRestoreClassEnv.java @@ -30,6 +30,7 @@ import java.util.TimeZone; import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.PostingsFormat; import org.apache.lucene.codecs.appending.AppendingCodec; +import org.apache.lucene.codecs.asserting.AssertingCodec; import org.apache.lucene.codecs.lucene40.Lucene40Codec; import org.apache.lucene.codecs.mockrandom.MockRandomPostingsFormat; import org.apache.lucene.codecs.simpletext.SimpleTextCodec; @@ -189,6 +190,8 @@ final class TestRuleSetupAndRestoreClassEnv extends AbstractBeforeAfterRule { codec = new SimpleTextCodec(); } else if ("Appending".equals(TEST_CODEC) || ("random".equals(TEST_CODEC) && randomVal == 8 && !shouldAvoidCodec("Appending"))) { codec = new AppendingCodec(); + } else if ("Asserting".equals(TEST_CODEC) || ("random".equals(TEST_CODEC) && randomVal == 7 && !shouldAvoidCodec("Asserting"))) { + codec = new AssertingCodec(); } else if (!"random".equals(TEST_CODEC)) { codec = Codec.forName(TEST_CODEC); } else if ("random".equals(TEST_POSTINGSFORMAT)) { diff --git a/lucene/test-framework/src/resources/META-INF/services/org.apache.lucene.codecs.Codec b/lucene/test-framework/src/resources/META-INF/services/org.apache.lucene.codecs.Codec index 4a812de77e5..08972ef00d3 100644 --- a/lucene/test-framework/src/resources/META-INF/services/org.apache.lucene.codecs.Codec +++ b/lucene/test-framework/src/resources/META-INF/services/org.apache.lucene.codecs.Codec @@ -13,3 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. +org.apache.lucene.codecs.asserting.AssertingCodec diff --git a/lucene/test-framework/src/resources/META-INF/services/org.apache.lucene.codecs.PostingsFormat b/lucene/test-framework/src/resources/META-INF/services/org.apache.lucene.codecs.PostingsFormat index 5242e64129d..f5a965d8fa6 100644 --- a/lucene/test-framework/src/resources/META-INF/services/org.apache.lucene.codecs.PostingsFormat +++ b/lucene/test-framework/src/resources/META-INF/services/org.apache.lucene.codecs.PostingsFormat @@ -20,3 +20,5 @@ org.apache.lucene.codecs.mocksep.MockSepPostingsFormat org.apache.lucene.codecs.nestedpulsing.NestedPulsingPostingsFormat org.apache.lucene.codecs.ramonly.RAMOnlyPostingsFormat org.apache.lucene.codecs.lucene40ords.Lucene40WithOrds +org.apache.lucene.codecs.asserting.AssertingPostingsFormat +