/*
* Copyright 2011 Red Hat, Inc. and/or its affiliates.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
package org.infinispan.io;
import org.infinispan.Cache;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.SingleCacheManagerTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.testng.Assert.*;
import static org.testng.Assert.assertEquals;
@Test(testName = "io.GridFileTest", groups = "functional")
public class GridFileTest extends SingleCacheManagerTest {
private Cache<String, byte[]> dataCache;
private Cache<String, GridFile.Metadata> metadataCache;
private GridFilesystem fs;
@Override
protected EmbeddedCacheManager createCacheManager() throws Exception {
return new DefaultCacheManager();
}
@BeforeMethod
protected void setUp() throws Exception {
dataCache = cacheManager.getCache("data");
metadataCache = cacheManager.getCache("metadata");
fs = new GridFilesystem(dataCache, metadataCache);
}
public void testGridFS() throws IOException {
File gridDir = fs.getFile("/test");
assert gridDir.mkdirs();
File gridFile = fs.getFile("/test/myfile.txt");
assert gridFile.createNewFile();
}
public void testGetFile() throws Exception {
assertEquals(fs.getFile("file.txt").getPath(), "file.txt");
assertEquals(fs.getFile("/file.txt").getPath(), "/file.txt");
assertEquals(fs.getFile("myDir/file.txt").getPath(), "myDir/file.txt");
assertEquals(fs.getFile("/myDir/file.txt").getPath(), "/myDir/file.txt");
assertEquals(fs.getFile("myDir", "file.txt").getPath(), "myDir/file.txt");
assertEquals(fs.getFile("/myDir", "file.txt").getPath(), "/myDir/file.txt");
File dir = fs.getFile("/myDir");
assertEquals(fs.getFile(dir, "file.txt").getPath(), "/myDir/file.txt");
dir = fs.getFile("myDir");
assertEquals(fs.getFile(dir, "file.txt").getPath(), "myDir/file.txt");
}
public void testCreateNewFile() throws IOException {
File file = fs.getFile("file.txt");
assertTrue(file.createNewFile()); // file should be created successfully
assertFalse(file.createNewFile()); // file should not be created, because it already exists
}
@Test(expectedExceptions = IOException.class)
public void testCreateNewFileInNonExistentDir() throws IOException {
File file = fs.getFile("nonExistent/file.txt");
file.createNewFile();
}
public void testNonExistentFileIsNeitherFileNorDirectory() throws IOException {
File file = fs.getFile("nonExistentFile.txt");
assertFalse(file.exists());
assertFalse(file.isFile());
assertFalse(file.isDirectory());
}
public void testMkdir() throws IOException {
assertFalse(mkdir(""));
assertFalse(mkdir("/"));
assertFalse(mkdir("/nonExistentParentDir/subDir"));
assertTrue(mkdir("myDir1"));
assertTrue(mkdir("myDir1/mySubDir1"));
assertTrue(mkdir("/myDir2"));
assertTrue(mkdir("/myDir2/mySubDir2"));
createFile("/file.txt");
assertFalse(mkdir("/file.txt/dir"));
}
private boolean mkdir(String pathname) {
return fs.getFile(pathname).mkdir();
}
public void testMkdirs() throws IOException {
assertFalse(mkdirs(""));
assertFalse(mkdirs("/"));
assertTrue(mkdirs("myDir1"));
assertTrue(mkdirs("myDir2/mySubDir"));
assertTrue(mkdirs("/myDir3"));
assertTrue(mkdirs("/myDir4/mySubDir"));
assertTrue(mkdirs("/myDir5/subDir/secondSubDir"));
createFile("/file.txt");
assertFalse(mkdirs("/file.txt/dir"));
}
private boolean mkdirs(String pathname) {
return fs.getFile(pathname).mkdirs();
}
public void testGetParent() throws IOException {
File file = fs.getFile("file.txt");
assertEquals(file.getParent(), null);
file = fs.getFile("/parentdir/file.txt");
assertEquals(file.getParent(), "/parentdir");
file = fs.getFile("/parentdir/subdir/file.txt");
assertEquals(file.getParent(), "/parentdir/subdir");
}
public void testGetParentFile() throws IOException {
File file = fs.getFile("file.txt");
assertNull(file.getParentFile());
file = fs.getFile("/parentdir/file.txt");
File parentDir = file.getParentFile();
assertTrue(parentDir instanceof GridFile);
assertEquals(parentDir.getPath(), "/parentdir");
}
@Test(expectedExceptions = FileNotFoundException.class)
public void testWritingToDirectoryThrowsException1() throws IOException {
GridFile dir = (GridFile) createDir();
fs.getOutput(dir); // should throw exception
}
@Test(expectedExceptions = FileNotFoundException.class)
public void testWritingToDirectoryThrowsException2() throws IOException {
File dir = createDir();
fs.getOutput(dir.getPath()); // should throw exception
}
@Test(expectedExceptions = FileNotFoundException.class)
public void testReadingFromDirectoryThrowsException1() throws IOException {
File dir = createDir();
fs.getInput(dir); // should throw exception
}
@Test(expectedExceptions = FileNotFoundException.class)
public void testReadingFromDirectoryThrowsException2() throws IOException {
File dir = createDir();
fs.getInput(dir.getPath()); // should throw exception
}
private File createDir() {
return createDir("mydir");
}
private File createDir(String pathname) {
File dir = fs.getFile(pathname);
boolean created = dir.mkdir();
assert created;
return dir;
}
public void testWriteAcrossMultipleChunksWithNonDefaultChunkSize() throws Exception {
writeToFile("multipleChunks.txt",
"This text spans multiple chunks, because each chunk is only 10 bytes long.",
10); // chunkSize = 10
String text = getContents("multipleChunks.txt");
assertEquals(text, "This text spans multiple chunks, because each chunk is only 10 bytes long.");
}
public void testWriteAcrossMultipleChunksWithNonDefaultChunkSizeAfterFileIsExplicitlyCreated() throws Exception {
GridFile file = (GridFile) fs.getFile("multipleChunks.txt", 20); // chunkSize = 20
file.createNewFile();
writeToFile("multipleChunks.txt",
"This text spans multiple chunks, because each chunk is only 20 bytes long.",
10); // chunkSize = 10 (but it is ignored, because the file was already created with chunkSize = 20
String text = getContents("multipleChunks.txt");
assertEquals(text, "This text spans multiple chunks, because each chunk is only 20 bytes long.");
}
public void testAppend() throws Exception {
writeToFile("append.txt", "Hello");
appendToFile("append.txt", "World");
assertEquals(getContents("append.txt"), "HelloWorld");
}
public void testAppendWithDifferentChunkSize() throws Exception {
writeToFile("append.txt", "Hello", 2); // chunkSize = 2
appendToFile("append.txt", "World", 5); // chunkSize = 5
assertEquals(getContents("append.txt"), "HelloWorld");
}
public void testAppendToEmptyFile() throws Exception {
appendToFile("empty.txt", "Hello");
assertEquals(getContents("empty.txt"), "Hello");
}
public void testDeleteRemovesAllChunks() throws Exception {
assertEquals(numberOfChunksInCache(), 0);
assertEquals(numberOfMetadataEntries(), 0);
writeToFile("delete.txt", "delete me", 100);
GridFile file = (GridFile) fs.getFile("delete.txt");
boolean deleted = file.delete(true);
assertTrue(deleted);
assertFalse(file.exists());
assertEquals(numberOfChunksInCache(), 0);
assertEquals(numberOfMetadataEntries(), 0);
}
public void testOverwritingFileDoesNotLeaveExcessChunksInCache() throws Exception {
assertEquals(numberOfChunksInCache(), 0);
writeToFile("leak.txt", "12345abcde12345", 5); // file length = 15, chunkSize = 5
assertEquals(numberOfChunksInCache(), 3);
writeToFile("leak.txt", "12345", 5); // file length = 5, chunkSize = 5
assertEquals(numberOfChunksInCache(), 1);
}
public void testLastModified() throws Exception {
assertEquals(fs.getFile("nonExistentFile.txt").lastModified(), 0);
long time1 = System.currentTimeMillis();
File file = createFile("file.txt");
long time2 = System.currentTimeMillis();
assertTrue(time1 <= file.lastModified());
assertTrue(file.lastModified() <= time2);
Thread.sleep(100);
time1 = System.currentTimeMillis();
writeToFile(file.getPath(), "foo");
time2 = System.currentTimeMillis();
assertTrue(time1 <= file.lastModified());
assertTrue(file.lastModified() <= time2);
}
public void testList() throws Exception {
assertNull(fs.getFile("nonExistentDir").list());
assertEquals(createDir("/emptyDir").list().length, 0);
File dir = createDirWithFiles();
String[] filenames = dir.list();
assertEquals(
asSet(filenames),
asSet("foo1.txt", "foo2.txt", "bar1.txt", "bar2.txt", "fooDir", "barDir"));
}
public void testListWithFilenameFilter() throws Exception {
File dir = createDirWithFiles();
String[] filenames = dir.list(new FooFilenameFilter());
assertEquals(
asSet(filenames),
asSet("foo1.txt", "foo2.txt", "fooDir"));
}
public void testListFiles() throws Exception {
assertNull(fs.getFile("nonExistentDir").listFiles());
assertEquals(createDir("/emptyDir").listFiles().length, 0);
File dir = createDirWithFiles();
File[] files = dir.listFiles();
assertEquals(
asSet(getPaths(files)),
asSet("/myDir/foo1.txt", "/myDir/foo2.txt", "/myDir/fooDir",
"/myDir/bar1.txt", "/myDir/bar2.txt", "/myDir/barDir"));
}
public void testListFilesWithFilenameFilter() throws Exception {
File dir = createDirWithFiles();
FooFilenameFilter filter = new FooFilenameFilter();
filter.expectDir(dir);
File[] files = dir.listFiles(filter);
assertEquals(
asSet(getPaths(files)),
asSet("/myDir/foo1.txt", "/myDir/foo2.txt", "/myDir/fooDir"));
}
public void testListFilesWithFileFilter() throws Exception {
File dir = createDirWithFiles();
File[] files = dir.listFiles(new FooFileFilter());
assertEquals(
asSet(getPaths(files)),
asSet("/myDir/foo1.txt", "/myDir/foo2.txt", "/myDir/fooDir"));
}
public void testRootDir() throws Exception {
File rootDir = fs.getFile("/");
assertTrue(rootDir.exists());
assertTrue(rootDir.isDirectory());
createFile("/foo.txt");
String[] filenames = rootDir.list();
assertNotNull(filenames);
assertEquals(filenames.length, 1);
assertEquals(filenames[0], "foo.txt");
}
public void testReadableChannel() throws Exception {
String content = "This is the content of channelTest.txt";
writeToFile("/channelTest.txt", content, 10);
ReadableGridFileChannel channel = fs.getReadableChannel("/channelTest.txt");
try {
assertTrue(channel.isOpen());
ByteBuffer buffer = ByteBuffer.allocate(1000);
channel.read(buffer);
assertEquals(getStringFrom(buffer), content);
} finally {
channel.close();
}
assertFalse(channel.isOpen());
}
public void testReadableChannelPosition() throws Exception {
writeToFile("/position.txt", "0123456789", 3);
ReadableGridFileChannel channel = fs.getReadableChannel("/position.txt");
try {
assertEquals(channel.position(), 0);
channel.position(5);
assertEquals(channel.position(), 5);
assertEquals(getStringFromChannel(channel, 3), "567");
assertEquals(channel.position(), 8);
channel.position(2);
assertEquals(channel.position(), 2);
assertEquals(getStringFromChannel(channel, 5), "23456");
assertEquals(channel.position(), 7);
} finally {
channel.close();
}
}
public void testWritableChannel() throws Exception {
WritableGridFileChannel channel = fs.getWritableChannel("/channelTest.txt", false, 10);
try {
assertTrue(channel.isOpen());
channel.write(ByteBuffer.wrap("This file spans multiple chunks.".getBytes()));
} finally {
channel.close();
}
assertFalse(channel.isOpen());
assertEquals(getContents("/channelTest.txt"), "This file spans multiple chunks.");
}
public void testWritableChannelAppend() throws Exception {
writeToFile("/append.txt", "Initial text.", 3);
WritableGridFileChannel channel = fs.getWritableChannel("/append.txt", true);
try {
channel.write(ByteBuffer.wrap("Appended text.".getBytes()));
} finally {
channel.close();
}
assertEquals(getContents("/append.txt"), "Initial text.Appended text.");
}
public void testGetAbsolutePath() throws IOException {
assertEquals(fs.getFile("/file.txt").getAbsolutePath(), "/file.txt");
assertEquals(fs.getFile("file.txt").getAbsolutePath(), "/file.txt");
assertEquals(fs.getFile("dir/file.txt").getAbsolutePath(), "/dir/file.txt");
}
public void testGetAbsoluteFile() throws IOException {
assertTrue(fs.getFile("file.txt").getAbsoluteFile() instanceof GridFile);
assertEquals(fs.getFile("/file.txt").getAbsoluteFile().getPath(), "/file.txt");
assertEquals(fs.getFile("file.txt").getAbsoluteFile().getPath(), "/file.txt");
assertEquals(fs.getFile("dir/file.txt").getAbsoluteFile().getPath(), "/dir/file.txt");
}
public void testIsAbsolute() throws IOException {
assertTrue(fs.getFile("/file.txt").isAbsolute());
assertFalse(fs.getFile("file.txt").isAbsolute());
}
public void testLeadingSeparatorIsOptional() throws IOException {
File gridFile = fs.getFile("file.txt");
assert gridFile.createNewFile();
assertTrue(fs.getFile("file.txt").exists());
assertTrue(fs.getFile("/file.txt").exists());
File dir = fs.getFile("dir");
boolean dirCreated = dir.mkdir();
assertTrue(dirCreated);
assertTrue(fs.getFile("dir").exists());
assertTrue(fs.getFile("/dir").exists());
}
public void testGetName() throws IOException {
assertEquals(fs.getFile("").getName(), "");
assertEquals(fs.getFile("/").getName(), "");
assertEquals(fs.getFile("file.txt").getName(), "file.txt");
assertEquals(fs.getFile("/file.txt").getName(), "file.txt");
assertEquals(fs.getFile("/dir/file.txt").getName(), "file.txt");
assertEquals(fs.getFile("/dir/subdir/file.txt").getName(), "file.txt");
assertEquals(fs.getFile("dir/subdir/file.txt").getName(), "file.txt");
}
public void testEquals() throws Exception {
assertFalse(fs.getFile("").equals(null));
assertTrue(fs.getFile("").equals(fs.getFile("")));
assertTrue(fs.getFile("").equals(fs.getFile("/")));
assertTrue(fs.getFile("foo.txt").equals(fs.getFile("foo.txt")));
assertTrue(fs.getFile("foo.txt").equals(fs.getFile("/foo.txt")));
assertFalse(fs.getFile("foo.txt").equals(fs.getFile("FOO.TXT")));
assertFalse(fs.getFile("/foo.txt").equals(new File("/foo.txt")));
}
private String getStringFromChannel(ReadableByteChannel channel, int length) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(length);
channel.read(buffer);
return getStringFrom(buffer);
}
private String getStringFrom(ByteBuffer buffer) {
buffer.flip();
byte[] buf = new byte[buffer.remaining()];
buffer.get(buf);
return new String(buf);
}
private String[] getPaths(File[] files) {
String[] paths = new String[files.length];
for (int i = 0; i < files.length; i++) {
File file = files[i];
paths[i] = file.getPath();
}
return paths;
}
private Set<String> asSet(String... strings) {
return new HashSet<String>(Arrays.asList(strings));
}
private File createDirWithFiles() throws IOException {
File dir = createDir("/myDir");
createFile("/myDir/foo1.txt");
createFile("/myDir/foo2.txt");
createFile("/myDir/bar1.txt");
createFile("/myDir/bar2.txt");
createDir("/myDir/fooDir");
createFile("/myDir/fooDir/foo.txt");
createFile("/myDir/fooDir/bar.txt");
createDir("/myDir/barDir");
return dir;
}
private File createFile(String pathname) throws IOException {
File file = fs.getFile(pathname);
assert file.createNewFile();
return file;
}
private int numberOfChunksInCache() {
return dataCache.size();
}
private int numberOfMetadataEntries() {
return metadataCache.size();
}
private void appendToFile(String filePath, String text) throws IOException {
appendToFile(filePath, text, null);
}
private void appendToFile(String filePath, String text, Integer chunkSize) throws IOException {
writeToFile(filePath, text, true, chunkSize);
}
private void writeToFile(String filePath, String text) throws IOException {
writeToFile(filePath, text, null);
}
private void writeToFile(String filePath, String text, Integer chunkSize) throws IOException {
writeToFile(filePath, text, false, chunkSize);
}
private void writeToFile(String filePath, String text, boolean append, Integer chunkSize) throws IOException {
OutputStream out = chunkSize == null
? fs.getOutput(filePath, append)
: fs.getOutput(filePath, append, chunkSize);
try {
out.write(text.getBytes());
} finally {
out.close();
}
}
private String getContents(String filePath) throws IOException {
InputStream in = fs.getInput(filePath);
try {
byte[] buf = new byte[1000];
int bytesRead = in.read(buf);
return new String(buf, 0, bytesRead);
} finally {
in.close();
}
}
private static class FooFilenameFilter implements FilenameFilter {
private File expectedDir;
@Override
public boolean accept(File dir, String name) {
if (expectedDir != null)
assertEquals(dir, expectedDir, "accept() invoked with unexpected dir");
return name.startsWith("foo");
}
public void expectDir(File dir) {
expectedDir = dir;
}
}
private static class FooFileFilter implements FileFilter {
@Override
public boolean accept(File file) {
return file.getName().startsWith("foo");
}
}
}