@Test
    public void testFeedingAndVisiting() throws InterruptedException, ParseException {
        DocumentType musicType = access().getDocumentTypeManager().getDocumentType("music");
        Document doc1 = new Document(musicType, "id:ns:music::1"); doc1.setFieldValue("artist", "one");
        Document doc2 = new Document(musicType, "id:ns:music::2"); doc2.setFieldValue("artist", "two");
        Document doc3 = new Document(musicType, "id:ns:music::3");

        // Select all music documents where the "artist" field is set
        VisitorParameters parameters = new VisitorParameters("music.artist");
        parameters.setFieldSet("music:artist");
        VisitorControlHandler control = new VisitorControlHandler();
        parameters.setControlHandler(control);
        Set<Document> received = new ConcurrentSkipListSet<>();
        parameters.setLocalDataHandler(new DumpVisitorDataHandler() {
            @Override public void onDocument(Document doc, long timeStamp) {
                received.add(doc);
            }
            @Override public void onRemove(DocumentId id) {
                throw new IllegalStateException("Not supposed to get here");
            }
        });

        // Visit when there are no documents completes immediately
        access.createVisitorSession(parameters).waitUntilDone(0);
        assertSame(VisitorControlHandler.CompletionCode.SUCCESS,
                   control.getResult().getCode());
        assertEquals(Set.of(),
                     received);

        // Sync-put some documents
        SyncSession out = access.createSyncSession(new SyncParameters.Builder().build());
        out.put(new DocumentPut(doc1));
        out.put(new DocumentPut(doc2));
        out.put(new DocumentPut(doc3));
        assertEquals(Map.of(doc1.getId(), doc1,
                            doc2.getId(), doc2,
                            doc3.getId(), doc3),
                     access.documents);

        // Expect a subset of documents to be returned, based on the selection
        access.createVisitorSession(parameters).waitUntilDone(0);
        assertSame(VisitorControlHandler.CompletionCode.SUCCESS,
                   control.getResult().getCode());
        assertEquals(Set.of(doc1, doc2),
                     received);

        // Remove doc2 and set artist for doc3, to see changes are reflected in subsequent visits
        out.remove(new DocumentRemove(doc2.getId()));
        out.update(new DocumentUpdate(musicType, doc3.getId()).addFieldUpdate(FieldUpdate.createAssign(musicType.getField("artist"),
                                                                                                       new StringFieldValue("three"))));
        assertEquals(Map.of(doc1.getId(), doc1,
                            doc3.getId(), doc3),
                     access.documents);
        assertEquals("three",
                     ((StringFieldValue) doc3.getFieldValue("artist")).getString());

        // Visit the documents again, retrieving none of the document fields
        parameters.setFieldSet(DocIdOnly.NAME);
        received.clear();
        access.createVisitorSession(parameters).waitUntilDone(0);
        assertSame(VisitorControlHandler.CompletionCode.SUCCESS,
                   control.getResult().getCode());
        assertEquals(Set.of(new Document(musicType, doc1.getId()), new Document(musicType, doc3.getId())),
                     received);

        // Visit the documents again, throwing an exception in the data handler on doc3
        received.clear();
        parameters.setLocalDataHandler(new DumpVisitorDataHandler() {
            @Override public void onDocument(Document doc, long timeStamp) {
                if (doc3.getId().equals(doc.getId()))
                    throw new RuntimeException("SEGFAULT");
                received.add(doc);
            }
            @Override public void onRemove(DocumentId id) {
                throw new IllegalStateException("Not supposed to get here");
            }
        });
        access.createVisitorSession(parameters).waitUntilDone(0);
        assertSame(VisitorControlHandler.CompletionCode.FAILURE,
                   control.getResult().getCode());
        assertEquals("SEGFAULT",
                     control.getResult().getMessage());
        assertEquals(Set.of(new Document(musicType, doc1.getId())),
                     received);

        // Visit the documents again, aborting after the first document
        received.clear();
        CountDownLatch visitLatch = new CountDownLatch(1);
        CountDownLatch abortLatch = new CountDownLatch(1);
        parameters.setLocalDataHandler(new DumpVisitorDataHandler() {
            @Override public void onDocument(Document doc, long timeStamp) {
                received.add(doc);
                abortLatch.countDown();
                try {
                    visitLatch.await();
                }
                catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            @Override public void onRemove(DocumentId id) { throw new IllegalStateException("Not supposed to get here"); }
        });
        VisitorSession visit = access.createVisitorSession(parameters);
        abortLatch.await();
        control.abort();
        visitLatch.countDown();
        visit.waitUntilDone(0);
        assertSame(VisitorControlHandler.CompletionCode.ABORTED,
                   control.getResult().getCode());
        assertEquals(Set.of(new Document(musicType, doc1.getId())),
                     received);
    }
Runs the superclass tests on this implementation

@author bratseth
documentapi/src/test/java/com/yahoo/documentapi/local/LocalDocumentApiTestCase.java