/*
* The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
* (the "License"). You may not use this work except in compliance with the License, which is
* available at www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied, as more fully set forth in the License.
*
* See the NOTICE file distributed with this work for information regarding copyright ownership.
*/
package alluxio.fuse;
import static jnr.constants.platform.OpenFlags.O_RDONLY;
import static jnr.constants.platform.OpenFlags.O_RDWR;
import static jnr.constants.platform.OpenFlags.O_WRONLY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import alluxio.AlluxioURI;
import alluxio.Configuration;
import alluxio.ConfigurationTestUtils;
import alluxio.PropertyKey;
import alluxio.client.file.FileInStream;
import alluxio.client.file.FileOutStream;
import alluxio.client.file.FileSystem;
import alluxio.client.file.URIStatus;
import alluxio.wire.FileInfo;
import com.google.common.cache.LoadingCache;
import jnr.ffi.Pointer;
import jnr.ffi.Runtime;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ru.serce.jnrfuse.ErrorCodes;
import ru.serce.jnrfuse.struct.FuseFileInfo;
import java.util.Collections;
import java.util.List;
/**
* Isolation tests for {@link AlluxioFuseFileSystem}.
*/
// TODO(andreareale): this test suite should be completed
public class AlluxioFuseFileSystemTest {
private static final String TEST_ROOT_PATH = "/t/root";
private static final AlluxioURI BASE_EXPECTED_URI =
new AlluxioURI(TEST_ROOT_PATH);
private AlluxioFuseFileSystem mFuseFs;
private FileSystem mFileSystem;
private FuseFileInfo mFileInfo;
@Before
public void before() throws Exception {
Configuration.set(PropertyKey.FUSE_CACHED_PATHS_MAX, "0");
final List<String> empty = Collections.emptyList();
AlluxioFuseOptions opts = new AlluxioFuseOptions(
"/doesnt/matter", TEST_ROOT_PATH, false, empty);
mFileSystem = mock(FileSystem.class);
mFuseFs = new AlluxioFuseFileSystem(mFileSystem, opts);
mFileInfo = allocateNativeFileInfo();
}
@After
public void after() {
ConfigurationTestUtils.resetConfiguration();
}
@Test
public void create() throws Exception {
mFileInfo.flags.set(O_WRONLY.intValue());
mFuseFs.create("/foo/bar", 0, mFileInfo);
AlluxioURI expectedPath = BASE_EXPECTED_URI.join("/foo/bar");
verify(mFileSystem).createFile(expectedPath);
}
@Test
public void createWrongFlags() throws Exception {
mFileInfo.flags.set(O_RDONLY.intValue());
int ret = mFuseFs.create("/foo/bar", 0, mFileInfo);
verifyZeroInteractions(mFileSystem);
assertEquals("Expected invalid access", -ErrorCodes.EACCES(), ret);
mFileInfo.flags.set(O_RDWR.intValue());
ret = mFuseFs.create("/foo/bar", 0, mFileInfo);
verifyZeroInteractions(mFileSystem);
assertEquals("Expected invalid access", -ErrorCodes.EACCES(), ret);
}
@Test
public void flush() throws Exception {
FileOutStream fos = mock(FileOutStream.class);
AlluxioURI anyURI = any();
when(mFileSystem.createFile(anyURI)).thenReturn(fos);
// open a file
mFileInfo.flags.set(O_WRONLY.intValue());
mFuseFs.create("/foo/bar", 0, mFileInfo);
//then call flush into it
mFuseFs.flush("/foo/bar", mFileInfo);
verify(fos).flush();
}
@Test
public void mkDir() throws Exception {
mFuseFs.mkdir("/foo/bar", -1);
verify(mFileSystem).createDirectory(BASE_EXPECTED_URI.join("/foo/bar"));
}
@Test
public void open() throws Exception {
// mocks set-up
AlluxioURI expectedPath = BASE_EXPECTED_URI.join("/foo/bar");
FileInfo fi = new FileInfo();
fi.setFolder(false);
URIStatus status = new URIStatus(fi);
when(mFileSystem.exists(expectedPath)).thenReturn(true);
when(mFileSystem.getStatus(expectedPath)).thenReturn(status);
mFileInfo.flags.set(O_RDONLY.intValue());
// actual test
mFuseFs.open("/foo/bar", mFileInfo);
verify(mFileSystem).exists(expectedPath);
verify(mFileSystem).openFile(expectedPath);
}
@Test
public void openWrongFlags() throws Exception {
mFileInfo.flags.set(O_RDWR.intValue());
// actual test
int ret = mFuseFs.open("/foo/bar", mFileInfo);
verifyZeroInteractions(mFileSystem);
assertEquals("Should return an access error", -ErrorCodes.EACCES(), ret);
mFileInfo.flags.set(O_WRONLY.intValue());
ret = mFuseFs.open("/foo/bar", mFileInfo);
verifyZeroInteractions(mFileSystem);
assertEquals("Should return an access error", -ErrorCodes.EACCES(), ret);
}
@Test
public void read() throws Exception {
// mocks set-up
AlluxioURI expectedPath = BASE_EXPECTED_URI.join("/foo/bar");
FileInfo fi = new FileInfo();
fi.setFolder(false);
URIStatus status = new URIStatus(fi);
when(mFileSystem.exists(expectedPath)).thenReturn(true);
when(mFileSystem.getStatus(expectedPath)).thenReturn(status);
FileInStream fakeInStream = mock(FileInStream.class);
when(fakeInStream.read(any(byte[].class), anyInt(), anyInt())).then(new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
byte[] myDest = (byte[]) invocationOnMock.getArguments()[0];
for (byte i = 0; i < 4; i++) {
myDest[i] = i;
}
return 4;
}
});
when(mFileSystem.openFile(expectedPath)).thenReturn(fakeInStream);
mFileInfo.flags.set(O_RDONLY.intValue());
// prepare something to read to it
Runtime r = Runtime.getSystemRuntime();
Pointer ptr = r.getMemoryManager().allocateTemporary(4, true);
// actual test
mFuseFs.open("/foo/bar", mFileInfo);
mFuseFs.read("/foo/bar", ptr, 4, 0, mFileInfo);
final byte[] dst = new byte[4];
ptr.get(0, dst, 0, 4);
final byte[] expected = new byte[] {0, 1, 2, 3};
assertArrayEquals("Source and dst data should be equal", expected, dst);
}
@Test
public void write() throws Exception {
FileOutStream fos = mock(FileOutStream.class);
AlluxioURI anyURI = any();
when(mFileSystem.createFile(anyURI)).thenReturn(fos);
// open a file
mFileInfo.flags.set(O_WRONLY.intValue());
mFuseFs.create("/foo/bar", 0, mFileInfo);
// prepare something to write into it
Runtime r = Runtime.getSystemRuntime();
Pointer ptr = r.getMemoryManager().allocateTemporary(4, true);
byte[] expected = {42, -128, 1, 3};
ptr.put(0, expected, 0, 4);
mFuseFs.write("/foo/bar", ptr, 4, 0, mFileInfo);
verify(fos).write(expected);
}
@Test
public void pathTranslation() throws Exception {
final LoadingCache<String, AlluxioURI> resolver =
mFuseFs.getPathResolverCache();
AlluxioURI expected = new AlluxioURI(TEST_ROOT_PATH);
AlluxioURI actual = resolver.apply("/");
Assert.assertEquals("/ should resolve to " + expected, expected, actual);
expected = new AlluxioURI(TEST_ROOT_PATH + "/home/foo");
actual = resolver.apply("/home/foo");
Assert.assertEquals("/home/foo should resolve to " + expected, expected, actual);
}
// Allocate native memory for a FuseFileInfo data struct and return its pointer
private FuseFileInfo allocateNativeFileInfo() {
final Runtime runtime = Runtime.getSystemRuntime();
final Pointer pt = runtime.getMemoryManager().allocateTemporary(36, true);
return FuseFileInfo.of(pt);
}
}