Index: lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java	(revision 1360880)
+++ lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java	(working copy)
@@ -1006,11 +1006,11 @@
    */
   public static IndexReader maybeWrapReader(IndexReader r) throws IOException {
     Random random = random();
-    if (rarely()) {
+    if (true || /* nocommit: */ rarely()) {
       // 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(4 /* nocommit: random.nextInt(5) */) {
           case 0:
             r = SlowCompositeReaderWrapper.wrap(r);
             break;
@@ -1041,6 +1041,14 @@
               new FieldFilterAtomicReader(ar, fields, true)
             );
             break;
+          case 4:
+            // TODO: we should make an AssertingCompositeReader too!
+            if (r instanceof AtomicReader) {
+              r = new AssertingAtomicReader((AtomicReader)r);
+            } else if (r instanceof DirectoryReader) {
+              r = new AssertingDirectoryReader((DirectoryReader)r);
+            }
+            break;
           default:
             fail("should not get here");
         }
Index: lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java	(revision 0)
+++ lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java	(working copy)
@@ -0,0 +1,276 @@
+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);
+  }
+
+  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);
+    }
+  }
+  
+  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);
+    }
+  }
+  
+  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 {
+    public AssertingTermsEnum(TermsEnum in) {
+      super(in);
+    }
+
+    @Override
+    public DocsEnum docs(Bits liveDocs, DocsEnum reuse, boolean needsFreqs) throws IOException {
+      // 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 {
+      // 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);
+    }
+  }
+  
+  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 iterator is exhausted!";
+      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 iterator is exhausted!";
+      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()";
+      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 iterator is exhausted!";
+      int nextDoc = super.nextDoc();
+      assert nextDoc >= 0 : "invalid doc id: " + nextDoc;
+      if (nextDoc == DocIdSetIterator.NO_MORE_DOCS) {
+        state = DocsEnumState.FINISHED;
+      } else {
+        state = DocsEnumState.ITERATING;
+      }
+      positionCount = 0;
+      positionMax = super.freq();
+      return nextDoc;
+    }
+
+    @Override
+    public int advance(int target) throws IOException {
+      assert state != DocsEnumState.FINISHED : "advance() called after iterator is exhausted!";
+      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;
+      }
+      positionCount = 0;
+      positionMax = super.freq();
+      return advanced;
+    }
+
+    @Override
+    public int freq() throws IOException {
+      assert state != DocsEnumState.START : "freq() called before nextDoc()/advance()";
+      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 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 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 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 positionCount > 0 : "getPayload() called before nextPosition()!";
+      return super.getPayload();
+    }
+
+    @Override
+    public boolean hasPayload() {
+      assert state != DocsEnumState.START : "hasPayload() called before nextDoc()/advance()";
+      assert positionCount > 0 : "hasPayload() called before nextPosition()!";
+      return super.hasPayload();
+    }
+  }
+}

Property changes on: lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
Index: lucene/test-framework/src/java/org/apache/lucene/index/AssertingDirectoryReader.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/index/AssertingDirectoryReader.java	(revision 0)
+++ lucene/test-framework/src/java/org/apache/lucene/index/AssertingDirectoryReader.java	(working copy)
@@ -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<? extends AtomicReader> 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();
+  }
+}

Property changes on: lucene/test-framework/src/java/org/apache/lucene/index/AssertingDirectoryReader.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
