| /* |
| * 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.lucene.index; |
| |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import org.apache.lucene.codecs.Codec; |
| import org.apache.lucene.codecs.CodecUtil; |
| import org.apache.lucene.mockfile.ExtrasFS; |
| import org.apache.lucene.search.Sort; |
| import org.apache.lucene.store.BaseDirectoryWrapper; |
| import org.apache.lucene.store.Directory; |
| import org.apache.lucene.store.IOContext; |
| import org.apache.lucene.store.IndexInput; |
| import org.apache.lucene.store.IndexOutput; |
| import org.apache.lucene.util.LuceneTestCase; |
| import org.apache.lucene.util.StringHelper; |
| import org.apache.lucene.util.TestUtil; |
| import org.apache.lucene.util.Version; |
| |
| public class TestSegmentInfos extends LuceneTestCase { |
| |
| public void testIllegalCreatedVersion() { |
| IllegalArgumentException e = |
| expectThrows(IllegalArgumentException.class, () -> new SegmentInfos(5)); |
| assertEquals("indexCreatedVersionMajor must be >= 6, got: 5", e.getMessage()); |
| e = |
| expectThrows( |
| IllegalArgumentException.class, () -> new SegmentInfos(Version.LATEST.major + 1)); |
| assertEquals( |
| "indexCreatedVersionMajor is in the future: " + (Version.LATEST.major + 1), e.getMessage()); |
| } |
| |
| // LUCENE-5954 |
| public void testVersionsNoSegments() throws IOException { |
| SegmentInfos sis = new SegmentInfos(Version.LATEST.major); |
| BaseDirectoryWrapper dir = newDirectory(); |
| dir.setCheckIndexOnClose(false); |
| sis.commit(dir); |
| sis = SegmentInfos.readLatestCommit(dir); |
| assertNull(sis.getMinSegmentLuceneVersion()); |
| assertEquals(Version.LATEST, sis.getCommitLuceneVersion()); |
| dir.close(); |
| } |
| |
| // LUCENE-5954 |
| public void testVersionsOneSegment() throws IOException { |
| BaseDirectoryWrapper dir = newDirectory(); |
| dir.setCheckIndexOnClose(false); |
| byte id[] = StringHelper.randomId(); |
| Codec codec = Codec.getDefault(); |
| |
| SegmentInfos sis = new SegmentInfos(Version.LATEST.major); |
| SegmentInfo info = |
| new SegmentInfo( |
| dir, |
| Version.LUCENE_9_0_0, |
| Version.LUCENE_9_0_0, |
| "_0", |
| 1, |
| false, |
| Codec.getDefault(), |
| Collections.<String, String>emptyMap(), |
| id, |
| Collections.<String, String>emptyMap(), |
| null); |
| info.setFiles(Collections.<String>emptySet()); |
| codec.segmentInfoFormat().write(dir, info, IOContext.DEFAULT); |
| SegmentCommitInfo commitInfo = |
| new SegmentCommitInfo(info, 0, 0, -1, -1, -1, StringHelper.randomId()); |
| |
| sis.add(commitInfo); |
| sis.commit(dir); |
| sis = SegmentInfos.readLatestCommit(dir); |
| assertEquals(Version.LUCENE_9_0_0, sis.getMinSegmentLuceneVersion()); |
| assertEquals(Version.LATEST, sis.getCommitLuceneVersion()); |
| dir.close(); |
| } |
| |
| // LUCENE-5954 |
| public void testVersionsTwoSegments() throws IOException { |
| BaseDirectoryWrapper dir = newDirectory(); |
| dir.setCheckIndexOnClose(false); |
| byte id[] = StringHelper.randomId(); |
| Codec codec = Codec.getDefault(); |
| |
| SegmentInfos sis = new SegmentInfos(Version.LATEST.major); |
| SegmentInfo info = |
| new SegmentInfo( |
| dir, |
| Version.LUCENE_9_0_0, |
| Version.LUCENE_9_0_0, |
| "_0", |
| 1, |
| false, |
| Codec.getDefault(), |
| Collections.<String, String>emptyMap(), |
| id, |
| Collections.<String, String>emptyMap(), |
| null); |
| info.setFiles(Collections.<String>emptySet()); |
| codec.segmentInfoFormat().write(dir, info, IOContext.DEFAULT); |
| SegmentCommitInfo commitInfo = |
| new SegmentCommitInfo(info, 0, 0, -1, -1, -1, StringHelper.randomId()); |
| sis.add(commitInfo); |
| |
| info = |
| new SegmentInfo( |
| dir, |
| Version.LUCENE_9_0_0, |
| Version.LUCENE_9_0_0, |
| "_1", |
| 1, |
| false, |
| Codec.getDefault(), |
| Collections.<String, String>emptyMap(), |
| id, |
| Collections.<String, String>emptyMap(), |
| null); |
| info.setFiles(Collections.<String>emptySet()); |
| codec.segmentInfoFormat().write(dir, info, IOContext.DEFAULT); |
| commitInfo = new SegmentCommitInfo(info, 0, 0, -1, -1, -1, StringHelper.randomId()); |
| sis.add(commitInfo); |
| |
| sis.commit(dir); |
| byte[] commitInfoId0 = sis.info(0).getId(); |
| byte[] commitInfoId1 = sis.info(1).getId(); |
| sis = SegmentInfos.readLatestCommit(dir); |
| assertEquals(Version.LUCENE_9_0_0, sis.getMinSegmentLuceneVersion()); |
| assertEquals(Version.LATEST, sis.getCommitLuceneVersion()); |
| assertEquals( |
| StringHelper.idToString(commitInfoId0), StringHelper.idToString(sis.info(0).getId())); |
| assertEquals( |
| StringHelper.idToString(commitInfoId1), StringHelper.idToString(sis.info(1).getId())); |
| dir.close(); |
| } |
| |
| /** Test toString method */ |
| public void testToString() throws Throwable { |
| SegmentInfo si; |
| final Directory dir = newDirectory(); |
| Codec codec = Codec.getDefault(); |
| |
| // diagnostics map |
| Map<String, String> diagnostics = Map.of("key1", "value1", "key2", "value2"); |
| |
| // attributes map |
| Map<String, String> attributes = Map.of("akey1", "value1", "akey2", "value2"); |
| |
| // diagnostics X, attributes X |
| si = |
| new SegmentInfo( |
| dir, |
| Version.LATEST, |
| Version.LATEST, |
| "TEST", |
| 10000, |
| false, |
| codec, |
| Collections.emptyMap(), |
| StringHelper.randomId(), |
| new HashMap<>(), |
| Sort.INDEXORDER); |
| assertEquals( |
| "TEST(" + Version.LATEST.toString() + ")" + ":C10000" + ":[indexSort=<doc>]", |
| si.toString()); |
| |
| // diagnostics O, attributes X |
| si = |
| new SegmentInfo( |
| dir, |
| Version.LATEST, |
| Version.LATEST, |
| "TEST", |
| 10000, |
| false, |
| codec, |
| diagnostics, |
| StringHelper.randomId(), |
| new HashMap<>(), |
| Sort.INDEXORDER); |
| assertEquals( |
| "TEST(" |
| + Version.LATEST.toString() |
| + ")" |
| + ":C10000" |
| + ":[indexSort=<doc>]" |
| + ":[diagnostics=" |
| + diagnostics |
| + "]", |
| si.toString()); |
| |
| // diagnostics X, attributes O |
| si = |
| new SegmentInfo( |
| dir, |
| Version.LATEST, |
| Version.LATEST, |
| "TEST", |
| 10000, |
| false, |
| codec, |
| Collections.emptyMap(), |
| StringHelper.randomId(), |
| attributes, |
| Sort.INDEXORDER); |
| assertEquals( |
| "TEST(" |
| + Version.LATEST.toString() |
| + ")" |
| + ":C10000" |
| + ":[indexSort=<doc>]" |
| + ":[attributes=" |
| + attributes |
| + "]", |
| si.toString()); |
| |
| // diagnostics O, attributes O |
| si = |
| new SegmentInfo( |
| dir, |
| Version.LATEST, |
| Version.LATEST, |
| "TEST", |
| 10000, |
| false, |
| codec, |
| diagnostics, |
| StringHelper.randomId(), |
| attributes, |
| Sort.INDEXORDER); |
| assertEquals( |
| "TEST(" |
| + Version.LATEST.toString() |
| + ")" |
| + ":C10000" |
| + ":[indexSort=<doc>]" |
| + ":[diagnostics=" |
| + diagnostics |
| + "]" |
| + ":[attributes=" |
| + attributes |
| + "]", |
| si.toString()); |
| |
| dir.close(); |
| } |
| |
| public void testIDChangesOnAdvance() throws IOException { |
| try (BaseDirectoryWrapper dir = newDirectory()) { |
| dir.setCheckIndexOnClose(false); |
| byte id[] = StringHelper.randomId(); |
| SegmentInfo info = |
| new SegmentInfo( |
| dir, |
| Version.LUCENE_9_0_0, |
| Version.LUCENE_9_0_0, |
| "_0", |
| 1, |
| false, |
| Codec.getDefault(), |
| Collections.<String, String>emptyMap(), |
| StringHelper.randomId(), |
| Collections.<String, String>emptyMap(), |
| null); |
| SegmentCommitInfo commitInfo = new SegmentCommitInfo(info, 0, 0, -1, -1, -1, id); |
| assertEquals(StringHelper.idToString(id), StringHelper.idToString(commitInfo.getId())); |
| commitInfo.advanceDelGen(); |
| assertNotEquals(StringHelper.idToString(id), StringHelper.idToString(commitInfo.getId())); |
| |
| id = commitInfo.getId(); |
| commitInfo.advanceDocValuesGen(); |
| assertNotEquals(StringHelper.idToString(id), StringHelper.idToString(commitInfo.getId())); |
| |
| id = commitInfo.getId(); |
| commitInfo.advanceFieldInfosGen(); |
| assertNotEquals(StringHelper.idToString(id), StringHelper.idToString(commitInfo.getId())); |
| SegmentCommitInfo clone = commitInfo.clone(); |
| id = commitInfo.getId(); |
| assertEquals(StringHelper.idToString(id), StringHelper.idToString(commitInfo.getId())); |
| assertEquals(StringHelper.idToString(id), StringHelper.idToString(clone.getId())); |
| |
| commitInfo.advanceFieldInfosGen(); |
| assertNotEquals(StringHelper.idToString(id), StringHelper.idToString(commitInfo.getId())); |
| assertEquals( |
| "clone changed but shouldn't", |
| StringHelper.idToString(id), |
| StringHelper.idToString(clone.getId())); |
| } |
| } |
| |
| public void testBitFlippedTriggersCorruptIndexException() throws IOException { |
| BaseDirectoryWrapper dir = newDirectory(); |
| dir.setCheckIndexOnClose(false); |
| byte id[] = StringHelper.randomId(); |
| Codec codec = Codec.getDefault(); |
| |
| SegmentInfos sis = new SegmentInfos(Version.LATEST.major); |
| SegmentInfo info = |
| new SegmentInfo( |
| dir, |
| Version.LATEST, |
| Version.LATEST, |
| "_0", |
| 1, |
| false, |
| Codec.getDefault(), |
| Collections.<String, String>emptyMap(), |
| id, |
| Collections.<String, String>emptyMap(), |
| null); |
| info.setFiles(Collections.<String>emptySet()); |
| codec.segmentInfoFormat().write(dir, info, IOContext.DEFAULT); |
| SegmentCommitInfo commitInfo = |
| new SegmentCommitInfo(info, 0, 0, -1, -1, -1, StringHelper.randomId()); |
| sis.add(commitInfo); |
| |
| info = |
| new SegmentInfo( |
| dir, |
| Version.LATEST, |
| Version.LATEST, |
| "_1", |
| 1, |
| false, |
| Codec.getDefault(), |
| Collections.<String, String>emptyMap(), |
| id, |
| Collections.<String, String>emptyMap(), |
| null); |
| info.setFiles(Collections.<String>emptySet()); |
| codec.segmentInfoFormat().write(dir, info, IOContext.DEFAULT); |
| commitInfo = new SegmentCommitInfo(info, 0, 0, -1, -1, -1, StringHelper.randomId()); |
| sis.add(commitInfo); |
| |
| sis.commit(dir); |
| |
| BaseDirectoryWrapper corruptDir = newDirectory(); |
| corruptDir.setCheckIndexOnClose(false); |
| boolean corrupt = false; |
| for (String file : dir.listAll()) { |
| if (file.startsWith(IndexFileNames.SEGMENTS)) { |
| try (IndexInput in = dir.openInput(file, IOContext.DEFAULT); |
| IndexOutput out = corruptDir.createOutput(file, IOContext.DEFAULT)) { |
| final long corruptIndex = TestUtil.nextLong(random(), 0, in.length() - 1); |
| out.copyBytes(in, corruptIndex); |
| final int b = Byte.toUnsignedInt(in.readByte()) + TestUtil.nextInt(random(), 0x01, 0xff); |
| out.writeByte((byte) b); |
| out.copyBytes(in, in.length() - in.getFilePointer()); |
| } |
| try (IndexInput in = corruptDir.openInput(file, IOContext.DEFAULT)) { |
| CodecUtil.checksumEntireFile(in); |
| if (VERBOSE) { |
| System.out.println("TEST: Altering the file did not update the checksum, aborting..."); |
| } |
| return; |
| } catch (CorruptIndexException e) { |
| // ok |
| } |
| corrupt = true; |
| } else if (ExtrasFS.isExtra(file) == false) { |
| corruptDir.copyFrom(dir, file, file, IOContext.DEFAULT); |
| } |
| } |
| assertTrue("No segments file found", corrupt); |
| |
| expectThrowsAnyOf( |
| Arrays.asList( |
| CorruptIndexException.class, |
| IndexFormatTooOldException.class, |
| IndexFormatTooNewException.class), |
| () -> SegmentInfos.readLatestCommit(corruptDir)); |
| dir.close(); |
| corruptDir.close(); |
| } |
| } |