/* 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 lucy

import "testing"
import "os"
import "reflect"

import "git-wip-us.apache.org/repos/asf/lucy-clownfish.git/runtime/go/clownfish"

func TestIndexerAddDoc(t *testing.T) {
	schema := createTestSchema()
	index := NewRAMFolder("")
	indexer, _ := OpenIndexer(&OpenIndexerArgs{
		Create: true,
		Index:  index,
		Schema: schema,
	})
	indexer.AddDoc(&testDoc{Content: "foo"})
	indexer.AddDoc(map[string]interface{}{"content": "foo"})
	doc := NewDoc(0)
	doc.Store("content", "foo")
	indexer.AddDoc(doc)
	indexer.Commit()
	searcher, _ := OpenIndexSearcher(index)
	if got := searcher.DocFreq("content", "foo"); got != 3 {
		t.Errorf("Didn't index all docs -- DocMax: %d", got)
	}
}

func TestIndexerAddIndex(t *testing.T) {
	var err error
	origIndex := "_test_go_indexer_add_index"
	defer os.RemoveAll(origIndex)
	schema := createTestSchema()
	indexer, err := OpenIndexer(&OpenIndexerArgs{
		Create: true,
		Index:  origIndex,
		Schema: schema,
	})
	if err != nil {
		t.Errorf("OpenIndexer: %v", err)
		return
	}
	err = indexer.AddDoc(&testDoc{Content: "foo"})
	if err != nil {
		t.Errorf("AddDoc: %v", err)
	}
	err = indexer.Commit()
	if err != nil {
		t.Errorf("Commit FS index: %v", err)
	}

	indexer, _ = OpenIndexer(&OpenIndexerArgs{
		Create: true,
		Index:  NewRAMFolder(""),
		Schema: schema,
	})
	err = indexer.AddIndex(origIndex)
	if err != nil {
		t.Errorf("AddIndex: %v", err)
	}
	err = indexer.Commit()
	if err != nil {
		t.Errorf("AddIndex: %v", err)
	}
}

func TestIndexerDeletions(t *testing.T) {
	index := createTestIndex("foo", "bar", "baz", "gazi")
	indexer, _ := OpenIndexer(&OpenIndexerArgs{Index: index})
	err := indexer.DeleteByTerm("content", "foo")
	if err != nil {
		t.Errorf("DeleteByTerm: %v", err)
	}
	indexer.DeleteByQuery(NewTermQuery("content", "bar"))
	if err != nil {
		t.Errorf("DeleteByQuery: %v", err)
	}
	indexer.DeleteByDocID(3)
	if err != nil {
		t.Errorf("DeleteByDocID: %v", err)
	}
	err = indexer.Commit()
	if err != nil {
		t.Errorf("Commit: %v", err)
	}
	searcher, _ := OpenIndexSearcher(index)
	if count := searcher.GetReader().DocCount(); count != 1 {
		t.Errorf("Some deletions didn't go through (count=%d)", count)
	}
}

func TestIndexerMisc(t *testing.T) {
	var err error
	index := createTestIndex("foo", "bar", "baz")
	indexer, _ := OpenIndexer(&OpenIndexerArgs{Index: index})
	if _, ok := indexer.GetSchema().(Schema); !ok {
		t.Errorf("GetSchema")
	}
	if _, ok := indexer.getStockDoc().(Doc); !ok {
		t.Errorf("getStockDoc")
	}
	if _, ok := indexer.getSegWriter().(SegWriter); !ok {
		t.Errorf("getSegWriter")
	}
	indexer.AddDoc(&testDoc{Content: "gazi"})
	indexer.Optimize()
	err = indexer.PrepareCommit()
	if err != nil {
		t.Errorf("PrepareCommit: %v", err)
	}
	err = indexer.Commit()
	if err != nil {
		t.Errorf("Commit: %v", err)
	}
}

func TestBackgroundMergerMisc(t *testing.T) {
	var err error
	index := createTestIndex("foo", "bar", "baz")
	merger, _ := OpenBackgroundMerger(index, nil)
	merger.Optimize()
	err = merger.PrepareCommit()
	if err != nil {
		t.Errorf("PrepareCommit: %v", err)
	}
	err = merger.Commit()
	if err != nil {
		t.Errorf("Commit: %v", err)
	}
}

func TestIndexManagerAccessors(t *testing.T) {
	host := "dev.example.com"
	manager := NewIndexManager(host)
	if got := manager.GetHost(); got != host {
		t.Errorf("GetHost: %v", got)
	}
	folder := NewRAMFolder("")
	manager.SetFolder(folder)
	if got := manager.GetFolder(); !reflect.DeepEqual(folder, got) {
		t.Errorf("SetFolder/GetFolder")
	}
	manager.SetWriteLockTimeout(72)
	if got := manager.GetWriteLockTimeout(); got != 72 {
		t.Errorf("set/GetWriteLockTimeout: %d", got)
	}
	manager.SetWriteLockInterval(42)
	if got := manager.GetWriteLockInterval(); got != 42 {
		t.Errorf("set/GetWriteLockInterval: %d", got)
	}
	manager.setMergeLockTimeout(73)
	if got := manager.getMergeLockTimeout(); got != 73 {
		t.Errorf("set/getMergeLockTimeout: %d", got)
	}
	manager.setMergeLockInterval(43)
	if got := manager.getMergeLockInterval(); got != 43 {
		t.Errorf("set/getMergeLockInterval: %d", got)
	}
}

func TestIndexManagerLocks(t *testing.T) {
	manager := NewIndexManager("")
	manager.SetFolder(NewRAMFolder(""))
	if _, ok := manager.makeWriteLock().(Lock); !ok {
		t.Errorf("MakeWriteLock")
	}
	if _, ok := manager.makeMergeLock().(Lock); !ok {
		t.Errorf("makeMergeLock")
	}
	snapFile := "snapshot_4a.json"
	if _, ok := manager.makeSnapshotLock(snapFile).(Lock); !ok {
		t.Errorf("makeDeletionLock")
	}
}

func TestIndexManagerMergeData(t *testing.T) {
	var err error
	manager := NewIndexManager("")
	manager.SetFolder(NewRAMFolder(""))
	err = manager.WriteMergeData(42)
	if err != nil {
		t.Errorf("WriteMergeData: %v", err)
	}
	mergeData, err := manager.ReadMergeData()
	if err != nil {
		t.Errorf("ReadMergeData: %v", err)
	}
	if got, ok := mergeData["cutoff"].(string); !ok || got != "42" {
		t.Errorf("ReadMergeData: %v", got)
	}
	err = manager.RemoveMergeData()
	if err != nil {
		t.Errorf("RemoveMergeData: %v", err)
	}
}

func TestIndexManagerMisc(t *testing.T) {
	manager := NewIndexManager("")
	snapshot := NewSnapshot()
	snapshot.AddEntry("seg_4")
	snapshot.AddEntry("seg_5")
	if got := manager.highestSegNum(snapshot); got != 5 {
		t.Errorf("highestSegNum: %d", got)
	}
}

func TestIndexManagerRecycle(t *testing.T) {
	index := createTestIndex("foo", "bar", "baz")
	manager := NewIndexManager("")
	manager.SetFolder(index)
	indexer, _ := OpenIndexer(&OpenIndexerArgs{Index: index})
	searcher, _ := OpenIndexSearcher(index)
	reader := searcher.GetReader().(PolyReader)
	delWriter := indexer.getSegWriter().getDelWriter()
	segReaders, err := manager.Recycle(reader, delWriter, 0, true)
	if err != nil || len(segReaders) != 1 {
		t.Errorf("Recycle: (%d SegReaders) %v", len(segReaders), err)
	}
}

func TestTermInfoMisc(t *testing.T) {
	tinfo := NewTermInfo(1000)
	if got := tinfo.GetDocFreq(); got != 1000 {
		t.Errorf("GetDocFreq: %d", got)
	}
	tinfo.SetDocFreq(1001)
	if got := tinfo.GetDocFreq(); got != 1001 {
		t.Errorf("Set/GetDocFreq: %d", got)
	}
	tinfo.SetLexFilePos(1002)
	if got := tinfo.GetLexFilePos(); got != 1002 {
		t.Errorf("Set/GetLexFilePos: %d", got)
	}
	tinfo.SetPostFilePos(1003)
	if got := tinfo.GetPostFilePos(); got != 1003 {
		t.Errorf("Set/GetPostFilePos: %d", got)
	}
	tinfo.SetSkipFilePos(1002)
	if got := tinfo.GetSkipFilePos(); got != 1002 {
		t.Errorf("Set/GetSkipFilePos: %d", got)
	}
	other := NewTermInfo(42)
	other.Mimic(tinfo)
	if got := other.GetDocFreq(); got != tinfo.GetDocFreq() {
		t.Errorf("Mimic: (%d != %d)", got, tinfo.GetDocFreq())
	}
	other = tinfo.Clone().(TermInfo)
	if got := other.GetDocFreq(); got != tinfo.GetDocFreq() {
		t.Errorf("Clone: (%d != %d)", got, tinfo.GetDocFreq())
	}
	tinfo.reset()
	if got := tinfo.GetDocFreq(); got != 0 {
		t.Errorf("Reset: expected 0, got %d", got)
	}
}

func TestBitVecDelDocsMisc(t *testing.T) {
	folder := NewRAMFolder("")
	out, _ := folder.OpenOut("bits")
	out.WriteU32(0xDEADBEEF)
	out.Close()
	bv := NewBitVecDelDocs(folder, "bits")
	if !bv.Get(31) {
		t.Errorf("Get returned false")
	}
}

func TestTermVectorMisc(t *testing.T) {

	positions := []int32{0, 3}
	startOffsets := []int32{0, 20}
	endOffsets := []int32{2, 22}
	tv := NewTermVector("content", "red yellow green red blue", positions, startOffsets, endOffsets)
	if got := tv.GetPositions(); !reflect.DeepEqual(got, positions) {
		t.Errorf("GetPositions: %v", got)
	}
	if got := tv.GetStartOffsets(); !reflect.DeepEqual(got, startOffsets) {
		t.Errorf("GetStartOffsets: %v", got)
	}
	if got := tv.GetEndOffsets(); !reflect.DeepEqual(got, endOffsets) {
		t.Errorf("GetEndOffsets: %v", got)
	}

	folder := NewRAMFolder("")
	out, _ := folder.OpenOut("dump")
	tv.serialize(out)
	out.Close()
	in, _ := folder.OpenIn("dump")
	dupe := clownfish.GetClass(tv).MakeObj().(TermVector).deserialize(in)
	if !tv.Equals(dupe) {
		t.Errorf("Unsuccessful serialization round trip")
	}
}

func TestDocVectorMisc(t *testing.T) {
	schema := NewSchema()
	spec := NewFullTextType(NewStandardTokenizer())
	spec.SetHighlightable(true)
	schema.SpecField("content", spec)
	folder := NewRAMFolder("")
	indexer, _ := OpenIndexer(&OpenIndexerArgs{Index: folder, Schema: schema, Create: true})
	indexer.AddDoc(&testDoc{Content: "foo bar baz"})
	indexer.Commit()
	searcher, _ := OpenIndexSearcher(folder)
	dv, _ := searcher.fetchDocVec(1)
	fieldBuf := dv.fieldBuf("content");
	if fieldBuf == nil {
		t.Errorf("fieldBuf returned nil")
	}
	dv.addFieldBuf("content", fieldBuf)
	if got := dv.termVector("content", "bar"); got == nil {
		t.Errorf("termVector returned nil")
	}

	out, _ := folder.OpenOut("dump")
	dv.serialize(out)
	out.Close()
	in, _ := folder.OpenIn("dump")
	dupe := clownfish.GetClass(dv).MakeObj().(DocVector).deserialize(in)
	in.Close()
	if _, ok := dupe.(DocVector); !ok {
		t.Errorf("serialize/deserialize")
	}
}

func TestSnapshotMisc(t *testing.T) {
	var err error
	snapshot := NewSnapshot()
	snapshot.AddEntry("foo")
	snapshot.AddEntry("bar")
	snapshot.DeleteEntry("bar")
	if got := snapshot.NumEntries(); got != 1 {
		t.Errorf("Add/DeleteEntry, NumEntries: %d", got)
	}
	if got := snapshot.List(); !reflect.DeepEqual(got, []string{"foo"}) {
		t.Errorf("List: %v", got)
	}
	folder := NewRAMFolder("")
	err = snapshot.WriteFile(folder, "")
	if err != nil {
		t.Errorf("WriteFile: %v", err)
	}
	other := NewSnapshot()
	_, err = other.ReadFile(folder, "")
	if err != nil {
		t.Errorf("ReadFile: %v", err)
	}

	path := "snapshot_4.json"
	snapshot.SetPath(path)
	if got := snapshot.GetPath(); got != path {
		t.Errorf("SetPath/GetPath: %v", path)
	}
}

func TestSortCacheMisc(t *testing.T) {
	var err error

	schema := NewSchema()
	spec := NewFullTextType(NewStandardTokenizer())
	spec.SetSortable(true)
	schema.SpecField("content", spec)
	folder := NewRAMFolder("")
	indexer, _ := OpenIndexer(&OpenIndexerArgs{Index: folder, Schema: schema, Create: true})
	indexer.AddDoc(&testDoc{Content: "foo"})
	indexer.AddDoc(&testDoc{Content: "bar"})
	indexer.AddDoc(&testDoc{Content: "baz"})
	indexer.AddDoc(make(map[string]interface{}))
	indexer.Commit()

	ixReader, _ := OpenIndexReader(folder, nil, nil)
	segReaders := ixReader.SegReaders()
	sortReader := segReaders[0].Fetch("Lucy::Index::SortReader").(SortReader)
	sortCache, _ := sortReader.fetchSortCache("content")

	if card := sortCache.GetCardinality(); card != 4 {
		t.Errorf("GetCardinality: %d", card)
	}
	if width := sortCache.GetOrdWidth(); width != 2 {
		t.Errorf("GetOrdWidth: %d", width)
	}

	lowest, err := sortCache.Value(0)
	if _, ok := lowest.(string); err != nil || !ok || lowest != "bar" {
		t.Errorf("Value: %v", err)
	}
	if ord, err := sortCache.Ordinal(1); err != nil || ord != 2 { // "foo" is ordinal 2
		t.Errorf("Ordinal: %d, %v", ord, err)
	}
	if nullOrd := sortCache.GetNullOrd(); nullOrd != 3 {
		t.Errorf("GetNullOrd: %d", nullOrd)
	}

	if ord, err := sortCache.Find("foo"); err != nil || ord != 2 {
		t.Errorf("Find: %d, %v", ord, err)
	}

	if sortCache.getNativeOrds() {
		t.Errorf("recent index shouldn't have native ords")
	}
}

func TestSimilarityMisc(t *testing.T) {
	sim := NewSimilarity()
	if _, ok := sim.makePosting().(Posting); !ok {
		t.Errorf("makePosting")
	}
	if got := sim.tF(4.0); got != 2.0 {
		t.Errorf("tF: %f", got)
	}
	if got := sim.iDF(40, 1000); got <= 0 {
		t.Errorf("iDF: %f", got)
	}
	if got := sim.coord(3, 4); got <= 0 {
		t.Errorf("coord: %f", got)
	}
	if got := sim.LengthNorm(4); got != 0.5 {
		t.Errorf("LengthNorm: %f", got)
	}
	if got := sim.queryNorm(4); got != 0.5 {
		t.Errorf("queryNorm: %f", got)
	}
	if got := sim.encodeNorm(sim.decodeNorm(42)); got != 42 {
		t.Errorf("encode/decodeNorm: %d", got)
	}
}

func TestSimilarityRoundTrip(t *testing.T) {
	sim := NewSimilarity()
	dupe := sim.load(sim.dump())
	if !sim.Equals(dupe) {
		t.Errorf("Dump/Load round-trip")
	}
	folder := NewRAMFolder("")
	out, _ := folder.OpenOut("dump")
	sim.serialize(out)
	out.Close()
	in, _ := folder.OpenIn("dump")
	dupe = clownfish.GetClass(sim).MakeObj().(Similarity).deserialize(in)
	if !sim.Equals(dupe) {
		t.Errorf("serialize/deserialize round-trip")
	}
}

func TestSegmentMisc(t *testing.T) {
	var err error

	seg := NewSegment(4)
	if name := seg.GetName(); name != "seg_4" {
		t.Errorf("GetName: %s", name)
	}
	if num := seg.GetNumber(); num != 4 {
		t.Errorf("GetNumber: %d", num)
	}

	if num := seg.AddField("field1"); num != 1 {
		t.Errorf("AddField: %d", num)
	}
	if field := seg.FieldName(1); field != "field1" {
		t.Errorf("FieldName: %v", field)
	}
	if num := seg.FieldNum("field1"); num != 1 {
		t.Errorf("FieldNum: %d", num)
	}

	metadata := map[string]interface{}{
		"foo": "1",
		"bar": "2",
	}
	seg.StoreMetadata("mycomponent", metadata)
	if got := seg.FetchMetadata("mycomponent"); !reflect.DeepEqual(got, metadata) {
		t.Errorf("Store/FetchMetadata: %v", got)
	}
	if got := seg.getMetadata(); got["mycomponent"] == nil {
		t.Errorf("%v", got)
	}

	seg.SetCount(42)
	seg.incrementCount(5)
	if got := seg.GetCount(); got != 47 {
		t.Errorf("SetCount/GetCount: %d", got)
	}

	folder := NewRAMFolder("")
	folder.MkDir("seg_4")
	err = seg.WriteFile(folder)
	if err != nil {
		t.Errorf("WriteFile: %v", err)
	}
	dupe := NewSegment(4)
	dupe.ReadFile(folder)
	if err != nil {
		t.Errorf("WriteFile: %v", err)
	}

	other := NewSegment(5)
	if got := seg.CompareTo(other); got >= 0 {
		t.Errorf("CompareTo (seg 4 vs seg 5): %d", got)
	}
}

func TestFilePurgerMisc(t *testing.T) {
	folder := NewRAMFolder("")
	oldSnapshot := NewSnapshot()
	oldSnapshot.AddEntry("foo")
	out, _ := folder.OpenOut("foo")
	out.Close()
	oldSnapshot.WriteFile(folder, "")

	snapshot := NewSnapshot()
	snapshot.WriteFile(folder, "")
	purger := NewFilePurger(folder, nil)
	purger.purgeSnapshots(snapshot)
	purger.purgeAbortedMerge()
	if folder.exists("foo") {
		t.Errorf("Failed to purge file")
	}
}

func TestInvEntryMisc(t *testing.T) {
	entry := NewInverterEntry(nil, "content", 1)
	other := NewInverterEntry(nil, "title", 2)
	if got := entry.CompareTo(other); got >= 0 {
		t.Errorf("CompareTo compares field numbers: %d", got)
	}
}

func TestInverterMisc(t *testing.T) {

	schema := NewSchema()
	spec := NewFullTextType(NewStandardTokenizer())
	schema.SpecField("title", spec)
	schema.SpecField("content", spec)
	seg := NewSegment(4)
	seg.AddField("title")
	seg.AddField("content")

	inverter := NewInverter(schema, seg)
	inverter.SetBoost(2.0)
	if got := inverter.GetBoost(); got != 2.0 {
		t.Errorf("Set/GetBoost: %f", got)
	}
	if got := inverter.GetDoc(); got != nil {
		t.Errorf("GetDoc should return nil when not yet set")
	}
	inverter.Clear()

	doc := NewDoc(42)
	doc.Store("title", "Foo")
	doc.Store("content", "foo foo foo foo foo")
	inverter.SetDoc(doc)
	if docID := inverter.GetDoc().GetDocID(); docID != 42 {
		t.Errorf("Set/GetDoc: %d", docID)
	}
	inverter.Clear()
	// Can't test AddField.
	// inverter.AddField(entry)
	inverter.InvertDoc(doc)


	numFields := inverter.Iterate()
	if numFields != 2 {
		t.Errorf("Iterate: %d", numFields)
	}
	fieldNum := inverter.Next()
	if fieldNum != 1 {
		t.Errorf("Next: %d", fieldNum)
	}
	if got := inverter.GetFieldName(); got != "title" {
		t.Errorf("GetFieldName during first iter: %s", got)
	}
	if got := inverter.GetValue(); got != "Foo" {
		t.Errorf("GetValue during first iter: %v", got)
	}
	if got, ok := inverter.GetType().(FullTextType); !ok {
		t.Errorf("GetType: %T", got)
	}
	if got, ok := inverter.GetAnalyzer().(StandardTokenizer); !ok {
		t.Errorf("GetType: %T", got)
	}
	if got, ok := inverter.GetSimilarity().(Similarity); !ok {
		t.Errorf("GetSimilarity: %T", got)
	}
	if got, ok := inverter.GetInversion().(Inversion); !ok {
		t.Errorf("GetInversion: %T", got)
	}

	if nextFieldNum := inverter.Next(); nextFieldNum != 2 {
		t.Errorf("Next second iter: %d", nextFieldNum)
	}

	if done := inverter.Next(); done != 0 {
		t.Errorf("Next should return 0 when exhausted: %d", done)
	}
	if got := inverter.GetFieldName(); got != "" {
		t.Errorf("GetFieldName after iterator exhausted: %s", got)
	}
	if got := inverter.GetValue(); got != nil {
		t.Errorf("GetValue after iterator exhausted: %v", got)
	}
	if got := inverter.GetType(); got != nil {
		t.Errorf("GetType after iterator exhausted: %v", got)
	}
	if got := inverter.GetAnalyzer(); got != nil {
		t.Errorf("GetAnalyzer after iterator exhausted: %v", got)
	}
	if got := inverter.GetSimilarity(); got != nil {
		t.Errorf("GetSimilarity after iterator exhausted: %v", got)
	}
	if got := inverter.GetInversion(); got != nil {
		t.Errorf("GetInversion after iterator exhausted: %v", got)
	}
}

// Use SegLexicon to air out the Lexicon interface.
func TestLexiconBasics(t *testing.T) {
	folder := createTestIndex("a", "b", "c")
	ixReader, _ := OpenIndexReader(folder, nil, nil)
	segReaders := ixReader.SegReaders()
	lexReader := segReaders[0].Fetch("Lucy::Index::LexiconReader").(LexiconReader)
	segLex, _ := lexReader.Lexicon("content", nil)
	if field := segLex.getField(); field != "content" {
		t.Errorf("getField: %s", field)
	}
	segLex.Next()
	if got := segLex.GetTerm(); got != "a" {
		t.Errorf("GetTerm: %v", got)
	}
	if docFreq := segLex.docFreq(); docFreq != 1 {
		t.Errorf("docFreq: %d", docFreq)
	}
	if !segLex.Next() || !segLex.Next() {
		t.Errorf("Iterate")
	}
	if segLex.Next() {
		t.Errorf("Iteration should be finished")
	}
	segLex.Seek("b")
	if got := segLex.GetTerm(); got != "b" {
		t.Errorf("Seek: %v", got)
	}
	segLex.Reset()
	if !segLex.Next() {
		t.Errorf("Next after Reset")
	}
}

func TestPostingListBasics(t *testing.T) {
	folder := createTestIndex("c", "b b b", "a", "b",)
	ixReader, _ := OpenIndexReader(folder, nil, nil)
	segReaders := ixReader.SegReaders()
	pListReader := segReaders[0].Fetch("Lucy::Index::PostingListReader").(PostingListReader)
	pList, _ := pListReader.PostingList("content", nil)
	pList.Seek("b")
	if docFreq := pList.GetDocFreq(); docFreq != 2 {
		t.Errorf("GetDocFreq: %d", docFreq)
	}
	if got := pList.Next(); got != 2 {
		t.Errorf("Next: %d", got)
	}
	if docID := pList.GetDocID(); docID != 2 {
		t.Errorf("GetDocID: %d", docID)
	}
	if got := pList.Next(); got != 4 {
		t.Errorf("Next (second iter): %d", got)
	}
	if got := pList.Next(); got != 0 {
		t.Error("Next (done): %d", got)
	}
}

func TestPostingBasics(t *testing.T) {
	sim := NewSimilarity()
	posting := NewMatchPosting(sim)
	posting.SetDocID(42)
	if got := posting.GetDocID(); got != 42 {
		t.Errorf("Set/GetDocID: %d", got)
	}
	posting.Reset()
	if got := posting.getFreq(); got != 0 {
		t.Errorf("getFreq: %d", got)
	}
}

// This function runs Close(), so the reader becomes unusable afterwards.
func runDataReaderCommon(t *testing.T, reader DataReader, runAggregator bool) {
	if runAggregator {
		got, err := reader.Aggregator([]DataReader{}, []int32{})
		if got == nil || err != nil {
			t.Errorf("Aggregator: %#v, %v", got, err)
		}
	}
	if got := reader.GetSchema(); false {
		t.Errorf("GetSchema: %v", got)
	}
	if got := reader.GetFolder(); false {
		t.Errorf("GetFolder: %v", got)
	}
	if got := reader.GetSnapshot(); false {
		t.Errorf("GetSnapshot: %v", got)
	}
	if got := reader.GetSegments(); false {
		t.Errorf("GetSegments: %#v", got)
	}
	if got := reader.GetSegment(); false {
		t.Errorf("GetSegment: %#v", got)
	}
	if got := reader.GetSegTick(); false {
		t.Errorf("GetSegTick: %d", got)
	}
	if err := reader.Close(); err != nil {
		t.Errorf("Close: %v", err)
	}
}

func TestIndexReaderMisc(t *testing.T) {
	folder := createTestIndex("a", "b", "c")
	reader, _ := OpenIndexReader(folder, nil, nil)
	if segReaders := reader.SegReaders(); len(segReaders) != 1 {
		t.Errorf("SegReaders: %#v", segReaders)
	}
	if offsets := reader.Offsets(); offsets[0] != 0 {
		t.Errorf("Offsets: %#v", offsets)
	}
	if got, err := reader.Obtain("Lucy::Index::DocReader"); got == nil || err != nil {
		t.Errorf("Obtain should succeed for DocReader: %#v, %v", got, err)
	}
	if got, err := reader.Obtain("Nope"); got != nil || err == nil {
		t.Errorf("Obtain should fail for non-existent API name: %v", err)
	}
	if got := reader.Fetch("Lucy::Index::DocReader"); got == nil  {
		t.Errorf("Fetch should succeed for DocReader")
	}
	if got := reader.Fetch("Nope"); got != nil {
		t.Errorf("Fetch should return nil for non-existent API name: %v", got)
	}
	if got := reader.DocMax(); got != 3 {
		t.Errorf("DocMax: %d", got);
	}
	if got := reader.DocCount(); got != 3 {
		t.Errorf("DocCount: %d", got);
	}
	if got := reader.DelCount(); got != 0 {
		t.Errorf("DelCount: %d", got);
	}
	runDataReaderCommon(t, reader, false)
}

func TestIndexReaderOpen(t *testing.T) {
	folder := createTestIndex("a", "b", "c")
	if got, err := OpenIndexReader(folder, nil, nil); got == nil || err != nil {
		t.Errorf("nil Snapshot and IndexManager: %v", err)
	}
	snapshot := NewSnapshot()
	snapshot.ReadFile(folder, "")
	if got, err := OpenIndexReader(folder, snapshot, nil); got == nil || err != nil {
		t.Errorf("With Snapshot: %v", err)
	}
	manager := NewIndexManager("")
	manager.SetFolder(folder)
	if got, err := OpenIndexReader(folder, nil, manager); got == nil || err != nil {
		t.Errorf("With IndexManager: %v", err)
	}
	if got, err := OpenIndexReader("no-index-here", nil, nil); got != nil || err == nil {
		t.Errorf("Non-existent index path")
	}
}

func TestDefaultDocReaderMisc(t *testing.T) {
	folder := createTestIndex("a", "b", "c")
	ixReader, _ := OpenIndexReader(folder, nil, nil)
	segReaders := ixReader.SegReaders()
	reader := segReaders[0].Fetch("Lucy::Index::DocReader").(DefaultDocReader)
	doc := make(map[string]interface{})
	if err := reader.ReadDoc(2, doc); err != nil {
		t.Errorf("ReadDoc: %v", err)
	}
	runDataReaderCommon(t, reader, true)
}

func TestPolyDocReaderMisc(t *testing.T) {
	folder := createTestIndex("a", "b", "c")
	ixReader, _ := OpenIndexReader(folder, nil, nil)
	reader := ixReader.Fetch("Lucy::Index::DocReader").(PolyDocReader)
	doc := make(map[string]interface{})
	if err := reader.ReadDoc(2, doc); err != nil {
		t.Errorf("ReadDoc: %v", err)
	}
	runDataReaderCommon(t, reader, true)
}

func TestLexReaderMisc(t *testing.T) {
	folder := createTestIndex("a", "b", "c")
	ixReader, _ := OpenIndexReader(folder, nil, nil)
	segReaders := ixReader.SegReaders()
	lexReader := segReaders[0].Fetch("Lucy::Index::LexiconReader").(LexiconReader)
	if got, err := lexReader.Lexicon("content", nil); got == nil || err != nil {
		t.Errorf("Lexicon should succeed: %v", err)
	}
	if got, err := lexReader.Lexicon("content", "foo"); got == nil || err != nil {
		t.Errorf("Lexicon with term should succeed: %v", err)
	}
	if got, err := lexReader.Lexicon("nope", nil); got != nil || err != nil {
		t.Errorf("Lexicon for non-field should return nil: %v", err)
	}
	if got, err := lexReader.DocFreq("content", "b"); got != 1 || err != nil {
		t.Errorf("DocFreq: %d, %v", got, err)
	}
	if got, err := lexReader.DocFreq("content", "nope"); got != 0 || err != nil {
		t.Errorf("DocFreq should be 0: %d, %v", got, err)
	}
	if got, err := lexReader.fetchTermInfo("content", "a"); got == nil || err != nil {
		t.Errorf("fetchTermInfo should succeed: %v", err)
	}
	if got, err := lexReader.fetchTermInfo("content", "nope"); got != nil || err != nil {
		t.Errorf("fetchTermInfo with non-existent term should return nil: %v", err)
	}
	runDataReaderCommon(t, lexReader, false)
}

func TestPostingListReaderMisc(t *testing.T) {
	folder := createTestIndex("a", "b", "c")
	ixReader, _ := OpenIndexReader(folder, nil, nil)
	segReaders := ixReader.SegReaders()
	pListReader := segReaders[0].Fetch("Lucy::Index::PostingListReader").(PostingListReader)
	if got, err := pListReader.PostingList("content", nil); got == nil || err != nil {
		t.Errorf("PostingList should succeed: %v", err)
	}
	if got, err := pListReader.PostingList("content", "foo"); got == nil || err != nil {
		t.Errorf("PostingList with term should succeed: %v", err)
	}
	if got, err := pListReader.PostingList("nope", nil); got != nil || err != nil {
		t.Errorf("PostingList for non-field should return nil: %v", err)
	}
	runDataReaderCommon(t, pListReader, false)
}

func TestHighlightReaderMisc(t *testing.T) {
	folder := createTestIndex("a", "b", "c")
	ixReader, _ := OpenIndexReader(folder, nil, nil)
	segReaders := ixReader.SegReaders()
	reader := segReaders[0].Fetch("Lucy::Index::HighlightReader").(HighlightReader)
	if got, err := reader.FetchDocVec(2); got == nil || err != nil {
		t.Errorf("FetchDocVec: %v", err)
	}
	if got, err := reader.FetchDocVec(4); got != nil || err == nil {
		t.Errorf("FetchDocVec catch error: %#v", got)
	}
	runDataReaderCommon(t, reader, true)
}

func TestDeletionsReaderMisc(t *testing.T) {
	folder := createTestIndex("a", "b", "c")
	ixReader, _ := OpenIndexReader(folder, nil, nil)
	segReaders := ixReader.SegReaders()
	delReader := segReaders[0].Fetch("Lucy::Index::DeletionsReader").(DeletionsReader)
	if count := delReader.delCount(); count != 0 {
		t.Errorf("delCount: %d", count);
	}
	if matcher := delReader.iterator(); matcher == nil {
		t.Errorf("iterator: %#v", matcher)
	}
	runDataReaderCommon(t, delReader, true)
}

func TestSortReaderMisc(t *testing.T) {
	folder := createTestIndex("a", "b", "c")
	ixReader, _ := OpenIndexReader(folder, nil, nil)
	segReaders := ixReader.SegReaders()
	sortReader := segReaders[0].Fetch("Lucy::Index::SortReader").(SortReader)
	if got, err := sortReader.fetchSortCache("content"); got == nil || err != nil {
		t.Errorf("fetchSortCache should succeed: %v", err)
	}
	if got, err := sortReader.fetchSortCache("nope"); got != nil || err != nil {
		t.Errorf("fetchSortCache for non-field should return nil: %v", err)
	}
	runDataReaderCommon(t, sortReader, false)
}

func runDataWriterCommon(t *testing.T, api string) {
	abcIndex := createTestIndex("a", "b", "c")
	indexer, _ := OpenIndexer(&OpenIndexerArgs{Index: abcIndex})
	dataWriter := indexer.getSegWriter().Fetch(api).(DataWriter)

	if got := dataWriter.GetSnapshot(); false {
		t.Errorf("GetSnapshot: %v", got)
	}
	if got := dataWriter.GetSegment(); false {
		t.Errorf("GetSegment: %#v", got)
	}
	if got := dataWriter.GetPolyReader(); false {
		t.Errorf("GetPolyReader: %#v", got)
	}
	if got := dataWriter.GetSchema(); false {
		t.Errorf("GetSchema: %v", got)
	}
	if got := dataWriter.GetFolder(); false {
		t.Errorf("GetFolder: %v", got)
	}

	doc := NewDoc(1)
	doc.Store("content", "three blind mice")
	inverter := NewInverter(dataWriter.GetSchema(), dataWriter.GetSegment())
	inverter.SetDoc(doc)
	inverter.InvertDoc(doc)
	if err := dataWriter.addInvertedDoc(inverter, 1); err != nil {
		t.Errorf("addInvertedDoc: %v", err)
	}

	segReaders := indexer.getSegWriter().GetPolyReader().SegReaders()
	abcSegReader := segReaders[0]
	if err := dataWriter.MergeSegment(abcSegReader, []int32{0, 2, 0, 3}); err != nil {
		t.Errorf("MergeSegment: %v", err)
	}

	// TODO
	//if err := dataWriter.DeleteSegment(fooSegReader); err != nil {
	//	t.Errorf("DeleteSegment: %v", err)
	//}

	xyzIndex := createTestIndex("x", "y", "z")
	xyzReader, _ := OpenIndexReader(xyzIndex, nil, nil)
	xyzSegReaders := xyzReader.SegReaders()
	if err := dataWriter.AddSegment(xyzSegReaders[0], []int32{0, 4, 5, 6}); err != nil {
		t.Errorf("AddSegment: %v", err)
	}

	if err := dataWriter.Finish(); err != nil {
		t.Errorf("Finish: %v", err)
	}
}

// TODO
//func TestSortWriterMisc(t *testing.T) {
//	runDataWriterCommon(t, "Lucy::Index::SortWriter")
//}

func TestDeletionsWriterMisc(t *testing.T) {
	index := createTestIndex("a", "b", "c")
	indexer, _ := OpenIndexer(&OpenIndexerArgs{Index: index})
	delWriter := indexer.getSegWriter().Fetch("Lucy::Index::DeletionsWriter").(DeletionsWriter)
	if delWriter.Updated() {
		t.Errorf("Not yet updated")
	}

	if err := delWriter.DeleteByTerm("content", "a"); err != nil {
		t.Errorf("DeleteByTerm: %v", err)
	}
	if err := delWriter.DeleteByQuery(NewTermQuery("content", "b")); err != nil {
		t.Errorf("DeleteByQuery: %v", err)
	}
	if err := delWriter.deleteByDocID(3); err != nil {
		t.Errorf("deleteByDocID: %v", err)
	}
	if !delWriter.Updated() {
		t.Errorf("Now we're updated")
	}

	if got := delWriter.SegDelCount("seg_1"); got != 3 {
		t.Errorf("SegDelCount: %d", got)
	}
	segReaders := delWriter.GetPolyReader().SegReaders()
	if dels, err := delWriter.segDeletions(segReaders[0]); dels == nil || err != nil {
		t.Errorf("segDeletions: %v", err)
	}
}

func TestSegWriterMisc(t *testing.T) {
	index := createTestIndex("a", "b", "c")
	ixReader, _ := OpenIndexReader(index, nil, nil)
	polyReader := ixReader.(PolyReader)
	schema := polyReader.GetSchema()
	segment := NewSegment(2)
	snapshot := polyReader.GetSnapshot()
	segWriter := NewSegWriter(schema, snapshot, segment, polyReader)
	if err := segWriter.PrepSegDir(); err != nil {
		t.Errorf("PrepSegDir: %v", err)
	}
	doc := NewDoc(1)
	if err := segWriter.AddDoc(doc, 1.0); err != nil {
		t.Errorf("AddDoc: %v", err)
	}
}
