listFolders( File rootFolder )
+ {
+ File[] files = fileSystem.listFiles( rootFolder );
return files == null ? Collections.emptyList()
: Stream.of( files ).filter( fileSystem::isDirectory ).collect( toList() );
}
+
+ /**
+ * Removes content of the lucene directory denoted by the given {@link File file}. This might seem unnecessary
+ * since we cleanup the folder using {@link FileSystemAbstraction file system} but in fact for testing we often use
+ * in-memory directories whose content can't be removed via the file system.
+ *
+ * Uses {@link FileUtils#windowsSafeIOOperation(FileUtils.FileOperation)} underneath.
+ *
+ * @param folder the path to the directory to cleanup.
+ * @throws IOException if removal operation fails.
+ */
+ private void cleanupLuceneDirectory( File folder ) throws IOException
+ {
+ try ( Directory dir = directoryFactory.open( folder ) )
+ {
+ String[] indexFiles = dir.listAll();
+ for ( String indexFile : indexFiles )
+ {
+ FileUtils.windowsSafeIOOperation( () -> dir.deleteFile( indexFile ) );
+ }
+ }
+ }
}
diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/storage/PartitionedIndexStorageTest.java b/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/storage/PartitionedIndexStorageTest.java
new file mode 100644
index 0000000000000..bb28630a9debc
--- /dev/null
+++ b/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/storage/PartitionedIndexStorageTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.kernel.api.impl.index.storage;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.store.Directory;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+
+import org.neo4j.helpers.ArrayUtil;
+import org.neo4j.io.IOUtils;
+import org.neo4j.io.fs.DefaultFileSystemAbstraction;
+import org.neo4j.io.fs.FileSystemAbstraction;
+import org.neo4j.io.fs.StoreChannel;
+import org.neo4j.kernel.api.impl.index.IndexWriterConfigs;
+import org.neo4j.test.DefaultFileSystemRule;
+import org.neo4j.test.TargetDirectory;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.neo4j.helpers.collection.IteratorUtil.asSet;
+
+public class PartitionedIndexStorageTest
+{
+ private static final String INDEX_ID = "testIndex";
+
+ @Rule
+ public final DefaultFileSystemRule fsRule = new DefaultFileSystemRule();
+ @Rule
+ public final TargetDirectory.TestDirectory testDir = TargetDirectory.testDirForTest( getClass(), fsRule.get() );
+
+ private DefaultFileSystemAbstraction fs;
+ private PartitionedIndexStorage storage;
+
+ @Before
+ public void createIndexStorage() throws Exception
+ {
+ fs = fsRule.get();
+ storage = new PartitionedIndexStorage( getOrCreateDirFactory( fs ), fs, testDir.graphDbDir(), INDEX_ID );
+ }
+
+ @Test
+ public void prepareFolderCreatesFolder() throws IOException
+ {
+ File folder = createRandomFolder( testDir.graphDbDir() );
+
+ storage.prepareFolder( folder );
+
+ assertTrue( fs.fileExists( folder ) );
+ }
+
+ @Test
+ public void prepareFolderRemovesFromFileSystem() throws IOException
+ {
+ File folder = createRandomFolder( testDir.graphDbDir() );
+ createRandomFilesAndFolders( folder );
+
+ storage.prepareFolder( folder );
+
+ assertTrue( fs.fileExists( folder ) );
+ assertTrue( ArrayUtil.isEmpty( fs.listFiles( folder ) ) );
+ }
+
+ @Test
+ public void prepareFolderRemovesFromLucene() throws IOException
+ {
+ File folder = createRandomFolder( testDir.graphDbDir() );
+ Directory dir = createRandomLuceneDir( folder );
+
+ assertFalse( ArrayUtil.isEmpty( dir.listAll() ) );
+
+ storage.prepareFolder( folder );
+
+ assertTrue( fs.fileExists( folder ) );
+ assertTrue( ArrayUtil.isEmpty( dir.listAll() ) );
+ }
+
+ @Test
+ public void openIndexDirectoriesForEmptyIndex() throws IOException
+ {
+ File indexFolder = storage.getIndexFolder();
+
+ Map directories = storage.openIndexDirectories();
+
+ assertTrue( directories.isEmpty() );
+ }
+
+ @Test
+ public void openIndexDirectories() throws IOException
+ {
+ File indexFolder = storage.getIndexFolder();
+ createRandomLuceneDir( indexFolder ).close();
+ createRandomLuceneDir( indexFolder ).close();
+
+ Map directories = storage.openIndexDirectories();
+ try
+ {
+ assertEquals( 2, directories.size() );
+ for ( Directory dir : directories.values() )
+ {
+ assertFalse( ArrayUtil.isEmpty( dir.listAll() ) );
+ }
+ }
+ finally
+ {
+ IOUtils.closeAll( directories.values() );
+ }
+ }
+
+ @Test
+ public void listFoldersForEmptyFolder() throws IOException
+ {
+ File indexFolder = storage.getIndexFolder();
+ fs.mkdirs( indexFolder );
+
+ List folders = storage.listFolders();
+
+ assertTrue( folders.isEmpty() );
+ }
+
+ @Test
+ public void listFolders() throws IOException
+ {
+ File indexFolder = storage.getIndexFolder();
+ fs.mkdirs( indexFolder );
+
+ createRandomFile( indexFolder );
+ createRandomFile( indexFolder );
+ File folder1 = createRandomFolder( indexFolder );
+ File folder2 = createRandomFolder( indexFolder );
+
+ List folders = storage.listFolders();
+
+ assertEquals( asSet( folder1, folder2 ), new HashSet<>( folders ) );
+ }
+
+ private void createRandomFilesAndFolders( File rootFolder ) throws IOException
+ {
+ int count = ThreadLocalRandom.current().nextInt( 10 ) + 1;
+ for ( int i = 0; i < count; i++ )
+ {
+ if ( ThreadLocalRandom.current().nextBoolean() )
+ {
+ createRandomFile( rootFolder );
+ }
+ else
+ {
+ createRandomFolder( rootFolder );
+ }
+ }
+ }
+
+ private Directory createRandomLuceneDir( File rootFolder ) throws IOException
+ {
+ File folder = createRandomFolder( rootFolder );
+ DirectoryFactory directoryFactory = getOrCreateDirFactory( fs );
+ Directory directory = directoryFactory.open( folder );
+ try ( IndexWriter writer = new IndexWriter( directory, IndexWriterConfigs.standardConfig() ) )
+ {
+ writer.addDocument( randomDocument() );
+ writer.commit();
+ }
+ return directory;
+ }
+
+ private void createRandomFile( File rootFolder ) throws IOException
+ {
+ File file = new File( rootFolder, RandomStringUtils.randomAlphabetic( 5 ) );
+ try ( StoreChannel channel = fs.create( file ) )
+ {
+ channel.writeAll( ByteBuffer.allocate( 100 ) );
+ }
+ }
+
+ private File createRandomFolder( File rootFolder ) throws IOException
+ {
+ File folder = new File( rootFolder, RandomStringUtils.randomAlphabetic( 5 ) );
+ fs.mkdirs( folder );
+ return folder;
+ }
+
+ private static Document randomDocument()
+ {
+ Document doc = new Document();
+ doc.add( new StringField( "field", RandomStringUtils.randomAlphabetic( 5 ), Field.Store.YES ) );
+ return doc;
+ }
+
+ private static DirectoryFactory getOrCreateDirFactory( FileSystemAbstraction fs )
+ {
+ return fs.getOrCreateThirdPartyFileSystem( DirectoryFactory.class,
+ clazz -> new DirectoryFactory.InMemoryDirectoryFactory() );
+ }
+}