package org.dcache.chimera;
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.AbstractFuture;
import com.google.common.util.concurrent.ListenableFuture;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.dcache.acl.ACE;
import org.dcache.acl.enums.AccessMask;
import org.dcache.acl.enums.AceType;
import org.dcache.acl.enums.RsType;
import org.dcache.acl.enums.Who;
import org.dcache.chimera.posix.Stat;
import org.dcache.util.Checksum;
import org.dcache.util.ChecksumType;
import static org.dcache.chimera.FileSystemProvider.StatCacheOption.NO_STAT;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
public class BasicTest extends ChimeraTestCaseHelper {
private static final Logger LOG = LoggerFactory.getLogger(BasicTest.class);
@Test
public void testLevelRemoveOnDelete() throws Exception {
final int level = 1;
FsInode inode = _rootInode.create("testLevelRemoveOnDelete", 0, 0, 0644);
_fs.createFileLevel(inode, level);
FsInode levelInode = new FsInode(_fs, inode.ino(), level);
assertTrue(levelInode.exists());
_fs.remove(_rootInode, "testLevelRemoveOnDelete", inode);
levelInode = new FsInode(_fs, inode.ino(), level);
assertFalse(levelInode.exists());
}
@Test
public void testLs() throws Exception {
List<HimeraDirectoryEntry> list = DirectoryStreamHelper.listOf(_rootInode);
assertTrue("Root Dir cant be empty", list.size() > 0);
}
@Test
public void testMkDir() throws Exception {
Stat stat = _rootInode.stat();
FsInode newDir = _rootInode.mkdir("junit");
assertEquals("mkdir have to incrise parent's nlink count by one",
_rootInode.stat().getNlink(), stat.getNlink() + 1);
assertTrue("mkdir have to update parent's mtime", _rootInode.stat().getMTime() > stat.getMTime());
assertEquals("new dir should have link count equal to two", newDir.stat().getNlink(), 2);
assertTrue("change count is not updated", stat.getGeneration() != _rootInode.stat().getGeneration());
}
@Test
public void testMkDirByPath() throws Exception {
Stat stat = _rootInode.stat();
FsInode newDir = _fs.mkdir("/junit");
assertEquals("mkdir has to increase parent's nlink count by one",
stat.getNlink() + 1, _rootInode.stat().getNlink());
assertTrue("mkdir has to update parent's mtime", _rootInode.stat().getMTime() > stat.getMTime());
assertEquals("new dir should have link count equal to two", 2, newDir.stat().getNlink());
assertTrue("change count is not updated", stat.getGeneration() != _rootInode.stat().getGeneration());
}
@Test
public void testMkDirInSetGidDir() throws Exception {
FsInode dir1 = _rootInode.mkdir("junit", 1, 2, 02755);
FsInode dir2 = dir1.mkdir("test", 1, 3, 0755);
assertEquals("owner is not respected", dir2.stat().getUid(), 1);
assertEquals("setgid is not respected", dir2.stat().getGid(), 2);
assertEquals("setgid is not respected",
dir2.stat().getMode() & UnixPermission.S_PERMS, 02755);
}
@Test
public void testMkDirWithTags() throws Exception {
byte[] bytes = "value".getBytes();
FsInode dir1 = _fs.mkdir(_rootInode, "junit", 1, 2, 02755, Collections.emptyList(), ImmutableMap.of("tag", bytes));
assertThat(_fs.getAllTags(dir1), hasEntry("tag", bytes));
}
@Test
public void testCreateFile() throws Exception {
FsInode base = _rootInode.mkdir("junit");
Stat stat = base.stat();
Thread.sleep(1); // required to ensure file created in distinct millisecond
FsInode newFile = base.create("testCreateFile", 0, 0, 0644);
assertEquals("file creation has to increase parent's nlink count by one",
base.stat().getNlink(), stat.getNlink() + 1);
assertTrue("file creation has to update parent's mtime", base.stat().getMTime() > stat.getMTime());
assertEquals("new file should have link count equal to one", newFile.stat().getNlink(), 1);
assertTrue("change count is not updated", stat.getGeneration() != base.stat().getGeneration());
}
@Test
public void testCreateFilePermission() throws Exception {
FsInode base = _rootInode.mkdir("junit");
int mode1 = 0644;
int mode2 = 0755;
FsInode file1 = base.create("testCreateFilePermission1", 0, 0, mode1);
FsInode file2 = base.create("testCreateFilePermission2", 0, 0, mode2);
assertEquals("creare pemissions are not respected",
file1.stat().getMode() & UnixPermission.S_PERMS, mode1);
assertEquals("creare pemissions are not respected",
file2.stat().getMode() & UnixPermission.S_PERMS, mode2);
}
@Test
public void testCreateFilePermissionAndOwnerOnSetGidDirectory() throws Exception {
FsInode base = _rootInode.mkdir("junit", 1, 2, 02775);
int mode1 = 0644;
int mode2 = 0755;
FsInode file1 = base.create("testCreateFilePermission1", 0, 0, mode1);
FsInode file2 = base.create("testCreateFilePermission2", 0, 0, mode2);
assertEquals("owner is not respected", file1.stat().getUid(), 0);
assertEquals("setgid is not respected", file1.stat().getGid(), 2);
assertEquals("create pemissions are not respected",
file1.stat().getMode() & UnixPermission.S_PERMS, mode1);
assertEquals("owner is not respected", file2.stat().getUid(), 0);
assertEquals("setgid is not respected", file2.stat().getGid(), 2);
assertEquals("create pemissions are not respected",
file2.stat().getMode() & UnixPermission.S_PERMS, mode2);
}
@Test // (expected=FileExistsChimeraFsException.class)
public void testCreateFileDup() throws Exception {
FsInode base = _rootInode.mkdir("junit");
try {
base.create("testCreateFile", 0, 0, 0644);
base.create("testCreateFile", 0, 0, 0644);
fail("you can't create a file twice");
} catch (FileExistsChimeraFsException fee) {
// OK
}
}
@Test // (expected=FileExistsChimeraFsException.class)
public void testCreateDirDup() throws Exception {
FsInode base = _rootInode.mkdir("junit");
try {
base.mkdir("testCreateDir");
base.mkdir("testCreateDir");
fail("you can't create a directory twice");
} catch (FileExistsChimeraFsException fee) {
// OK
}
}
@Test(expected=DirNotEmptyHimeraFsException.class)
public void testDeleteNonEmptyDir() throws Exception {
FsInode base = _rootInode.mkdir("junit");
base.create("testCreateFile", 0, 0, 0644);
_rootInode.remove("junit");
}
@Test
public void testDeleteFile() throws Exception {
FsInode base = _rootInode.mkdir("junit");
base.create("testCreateFile", 0, 0, 0644);
Stat stat = base.stat();
Thread.sleep(1); // ensure updated directory mtime is distinct from creation mtime
base.remove("testCreateFile");
assertEquals("remove have to decrease parents link count", base.stat().getNlink(), stat.getNlink() - 1);
assertFalse("remove have to update parent's mtime", stat.getMTime() == base.stat().getMTime());
}
@Test
public void testDeleteDir() throws Exception {
FsInode base = _rootInode.mkdir("junit");
base.create("testCreateDir", 0, 0, 0644);
Stat stat = base.stat();
Thread.sleep(1); // ensure updated directory mtime is distinct from creation mtime
base.remove("testCreateDir");
assertEquals("remove have to decrease parents link count", base.stat().getNlink(), stat.getNlink() - 1);
assertFalse("remove have to update parent's mtime", stat.getMTime() == base.stat().getMTime());
}
@Test(expected=FileNotFoundHimeraFsException.class)
public void testDeleteNonExistingFile() throws Exception {
_rootInode.remove("testCreateFile");
}
@Test(expected = FileNotFoundHimeraFsException.class)
public void testCreateInNonExistingDir() throws Exception {
FsInode missingDir = new FsInode(_fs, Long.MAX_VALUE);
_fs.createFile(missingDir, "aFile");
}
@Test
public void testDeleteInFile() throws Exception {
FsInode fileInode = _rootInode.create("testCreateFile", 0, 0, 0644);
try {
fileInode.remove("anObject");
fail("you can't remove an object in file Inode");
} catch (IOHimeraFsException ioe) {
// OK
}
}
@Test
public void testCreateInFile() throws Exception {
FsInode fileInode = _rootInode.create("testCreateFile", 0, 0, 0644);
try {
fileInode.create("anObject", 0, 0, 0644);
fail("you can't create an object in file Inode");
} catch (NotDirChimeraException nde) {
// OK
}
}
private final static int PARALLEL_THREADS_COUNT = 10;
/**
* Run some database activity in a separate thread, providing a
* ListenableFuture of that activity's result.
* A <i>CountDownLatch</i> is accepted for synchronising the start of the
* task.
*/
private static class ParallelDbFuture<T> extends AbstractFuture<T> implements Runnable
{
private final Thread _thread;
private final Callable<T> _task;
private final CountDownLatch _start;
public ParallelDbFuture(CountDownLatch start, Callable<T> task)
{
_thread = new Thread(this);
_start = start;
_task = task;
}
public void start()
{
_thread.start();
}
@Override
public void run() {
_start.countDown();
try {
_start.await();
set(_task.call());
} catch (Exception e) {
setException(e);
}
}
}
@Test(timeout=60_000)
public void testParallelCreate() throws ChimeraFsException
{
FsInode base = _rootInode.mkdir("junit");
Stat stat = base.stat();
CountDownLatch start = new CountDownLatch(PARALLEL_THREADS_COUNT);
List<ParallelDbFuture> futures = IntStream.range(0, PARALLEL_THREADS_COUNT)
.mapToObj(i -> "file-" + i)
.map(name -> new ParallelDbFuture<>(start, () -> base.create(name, 0, 0, 0644)))
.collect(Collectors.toList());
futures.stream().forEach(ParallelDbFuture::start);
int nlink = stat.getNlink();
int exceptions = 0;
for (ListenableFuture<Void> f : futures) {
try {
f.get();
nlink++;
} catch (ExecutionException e) {
LOG.warn("ExecutionException, triggered by {}", String.valueOf(e.getCause()));
exceptions++;
} catch (InterruptedException e) {
LOG.warn("Interrupted.");
}
}
assertEquals("Checking nlink count of dir", nlink, base.stat().getNlink());
// REVISIT: we fail this test if any exceptions are found. The
// motivation is to draw attendion to any such exception, but exceptions
// due to TransientDataAccessException or RecoverableDataAccessException
// are not indicative of an error.
assertEquals("Expecting no exceptions", 0, exceptions);
}
@Test
public void testHardLink() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode fileInode = base.create("hardLinkTestSourceFile", 0, 0, 0644);
Stat stat = fileInode.stat();
FsInode hardLinkInode = _fs.createHLink(base, fileInode, "hardLinkTestDestinationFile");
assertEquals("hard link's have to increase link count by one", stat.getNlink() + 1, hardLinkInode.stat().getNlink());
_fs.remove(base, "hardLinkTestDestinationFile", hardLinkInode);
assertTrue("removing of hard link have to decrease link count by one", 1 == fileInode.stat().getNlink());
}
@Test
public void testRemoveLinkToDir() throws Exception {
FsInode base = _rootInode.mkdir("junit");
_fs.createLink(base, "aLink", "/junit");
}
@Test
public void testRemoveLinkToSomewhare() throws Exception {
FsInode linkBase = _rootInode.mkdir("links");
_fs.createLink(linkBase, "file123", 0, 0, 0644, "/files/file123".getBytes(Charsets.UTF_8));
_fs.remove("/links/file123");
}
@Test
public void testRemoveLinkToFile() throws Exception {
FsInode fileBase = _rootInode.mkdir("files");
FsInode linkBase = _rootInode.mkdir("links");
FsInode fileInode = fileBase.create("file123", 0, 0, 0644);
_fs.createLink(linkBase, "file123", 0, 0, 0644, "/files/file123".getBytes(Charsets.UTF_8));
_fs.remove("/links/file123");
assertTrue("original file is gone!", fileInode.exists());
}
@Ignore("broken test, normal filesystems do not allow directory hard-links. Why does chimera?")
@Test
public void testDirHardLink() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode dirInode = base.mkdir("dirHadrLinkTestSrouceDir");
FsInode hardLinkInode = _fs.createHLink(base, dirInode, "dirHadrLinkTestDestinationDir");
}
@Test
public void testSymLink() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode fileInode = base.create("testCreateFile", 0, 0, 0644);
try {
fileInode.createLink("aLink", 0, 0, 0644, "../junit".getBytes());
fail("can't create a link in non directory inode");
} catch (NotDirChimeraException e) {
// OK
}
}
@Test
public void testRenameNonExistSameDir() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode fileInode = base.create("testCreateFile", 0, 0, 0644);
Stat preStatBase = base.stat();
Stat preStatFile = fileInode.stat();
boolean ok = _fs.rename(fileInode, base, "testCreateFile", base, "testCreateFile2");
assertTrue("can't move", ok);
assertEquals("link count of base directory should not be modified in case of rename", preStatBase.getNlink(), base.stat().getNlink());
assertEquals("link count of file shold not be modified in case of rename", preStatFile.getNlink(), fileInode.stat().getNlink());
}
@Test
public void testRenameExistSameDir() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode fileInode = base.create("testCreateFile", 0, 0, 0644);
FsInode file2Inode = base.create("testCreateFile2", 0, 0, 0644);
Stat preStatBase = base.stat();
file2Inode.setStatCache(null);
boolean ok = _fs.rename(fileInode, base, "testCreateFile", base, "testCreateFile2");
assertTrue("can't move", ok);
assertEquals("link count of base directory should decrease by one", preStatBase.getNlink() - 1, base.stat().getNlink());
assertFalse("ghost file", file2Inode.exists());
}
@Test
public void testRenameNonExistNotSameDir() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode base2 = _rootInode.mkdir("junit2");
FsInode fileInode = base.create("testCreateFile", 0, 0, 0644);
Stat preStatBase = base.stat();
Stat preStatBase2 = base2.stat();
Stat preStatFile = fileInode.stat();
boolean ok = _fs.rename(fileInode, base, "testCreateFile", base2, "testCreateFile2");
assertTrue("can't move", ok);
assertEquals("link count of source directory should decrese on move out", preStatBase.getNlink() - 1,
base.stat().getNlink());
assertEquals("link count of destination directory should increase on move in", preStatBase2.getNlink() + 1, base2.stat().getNlink());
assertEquals("link count of file shold not be modified on move", preStatFile.getNlink(),
fileInode.stat().getNlink());
}
@Test
public void testRenameExistNotSameDir() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode base2 = _rootInode.mkdir("junit2");
FsInode fileInode = base.create("testCreateFile", 0, 0, 0644);
FsInode fileInode2 = base2.create("testCreateFile2", 0, 0, 0644);
Stat preStatBase = base.stat();
Stat preStatBase2 = base2.stat();
Stat preStatFile = fileInode.stat();
fileInode2.setStatCache(null);
boolean ok = _fs.rename(fileInode, base, "testCreateFile", base2, "testCreateFile2");
assertTrue("can't move", ok);
assertEquals("link count of source directory should decrese on move out", preStatBase.getNlink() - 1,
base.stat().getNlink());
assertEquals("link count of destination directory should not be modified on replace", preStatBase2.getNlink(), base2.stat().getNlink());
assertEquals("link count of file shold not be modified on move", preStatFile.getNlink(), fileInode.stat().getNlink());
assertFalse("ghost file", fileInode2.exists());
}
@Test
public void testRenameHardLinkToItselfSameDir() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode fileInode = base.create("testCreateFile", 0, 0, 0644);
FsInode linkInode = _fs.createHLink(base, fileInode, "testCreateFile2");
Stat preStatBase = base.stat();
Stat preStatFile = fileInode.stat();
boolean ok = _fs.rename(fileInode, base, "testCreateFile", base, "testCreateFile2");
assertFalse("rename of hardlink to itself should do nothing", ok);
assertEquals("link count of base directory should not be modified in case of rename", preStatBase.getNlink(), base.stat().getNlink());
assertEquals("link count of file should not be modified in case of rename", preStatFile.getNlink(),
fileInode.stat().getNlink());
}
@Test
public void testRenameHardLinkToItselfNotSameDir() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode base2 = _rootInode.mkdir("junit2");
FsInode fileInode = base.create("testCreateFile", 0, 0, 0644);
FsInode linkInode = _fs.createHLink(base2, fileInode, "testCreateFile2");
Stat preStatBase = base.stat();
Stat preStatBase2 = base2.stat();
Stat preStatFile = fileInode.stat();
boolean ok = _fs.rename(fileInode, base, "testCreateFile", base2, "testCreateFile2");
assertFalse("rename of hardlink to itself should do nothing", ok);
assertEquals("link count of source directory should not be modified in case of rename", preStatBase.getNlink(),
base.stat().getNlink());
assertEquals("link count of destination directory should not be modified in case of rename", preStatBase2.getNlink(), base2.stat().getNlink());
assertEquals("link count of file should not be modified in case of rename", preStatFile.getNlink(), fileInode.stat().getNlink());
}
@Test
public void testRemoveFileById() throws Exception
{
int n = _fs.listDir(_rootInode).length;
FsInode file = _rootInode.create("foo", 0, 0, 0644);
_fs.remove(file);
assertEquals(n, _fs.listDir(_rootInode).length);
}
@Test
public void testRemoveSeveralHardlinksById() throws Exception
{
int n = _fs.listDir(_rootInode).length;
FsInode file = _rootInode.create("foo", 0, 0, 0644);
_fs.createHLink(_rootInode, file, "bar");
_fs.remove(file);
assertEquals(n, _fs.listDir(_rootInode).length);
assertEquals(n, _rootInode.stat().getNlink());
}
@Test
public void testRemoveDirById() throws Exception
{
int n =_fs.listDir(_rootInode).length;
FsInode foo = _rootInode.mkdir("foo");
_fs.remove(foo);
assertEquals(n, _fs.listDir(_rootInode).length);
}
@Test(expected=DirNotEmptyHimeraFsException.class)
public void testRemoveNonEmptyDirById() throws Exception
{
FsInode foo = _rootInode.mkdir("foo");
FsInode bar = foo.mkdir("bar");
_fs.remove(foo);
}
@Test(expected=InvalidArgumentChimeraException.class)
public void testRemoveRootById() throws Exception {
_fs.remove(_rootInode);
}
@Test(expected=FileNotFoundHimeraFsException.class)
public void testRemoveNonexistgById() throws Exception {
FsInode inode = new FsInode(_fs, Long.MAX_VALUE);
_fs.remove(inode);
}
@Test(expected=FileNotFoundHimeraFsException.class)
public void testRemoveNonexistgByPath() throws Exception {
FsInode base = _rootInode.mkdir("junit");
base.remove("notexist");
}
@Test
public void testRemoveFileByPath() throws Exception {
int n = _fs.listDir(_rootInode).length;
FsInode file = _rootInode.create("foo", 0, 0, 0644);
_fs.remove("/foo");
assertEquals(n, _fs.listDir(_rootInode).length);
}
@Test
public void testRemoveDirByPath() throws Exception {
int n = _fs.listDir(_rootInode).length;
FsInode foo = _rootInode.mkdir("foo");
_fs.remove("/foo");
assertEquals(n, _fs.listDir(_rootInode).length);
}
@Test(expected=DirNotEmptyHimeraFsException.class)
public void testRemoveNonEmptyDirByPath() throws Exception
{
FsInode foo = _rootInode.mkdir("foo");
FsInode bar = foo.mkdir("bar");
_fs.remove("/foo");
}
@Test(expected=InvalidArgumentChimeraException.class)
public void testRemoveRootByPath() throws Exception {
_fs.remove("/");
}
@Test
public void testAddLocationForNonexistong() throws Exception {
FsInode inode = new FsInode(_fs, Long.MAX_VALUE);
try {
_fs.addInodeLocation(inode, StorageGenericLocation.DISK, "/dev/null");
fail("was able to add cache location for non existing file");
} catch (FileNotFoundHimeraFsException e) {
// OK
}
}
@Test
public void testDupAddLocation() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode fileInode = base.create("testCreateFile", 0, 0, 0644);
_fs.addInodeLocation(fileInode, StorageGenericLocation.DISK, "/dev/null");
_fs.addInodeLocation(fileInode, StorageGenericLocation.DISK, "/dev/null");
}
@Test(expected = FileNotFoundHimeraFsException.class)
public void testSetSizeNotExist() throws Exception {
FsInode inode = new FsInode(_fs, Long.MAX_VALUE);
Stat stat = new Stat();
stat.setSize(1);
_fs.setInodeAttributes(inode, 0, stat);
}
@Test(expected = FileNotFoundHimeraFsException.class)
public void testChowneNotExist() throws Exception {
FsInode inode = new FsInode(_fs, Long.MAX_VALUE);
Stat stat = new Stat();
stat.setUid(3750);
_fs.setInodeAttributes(inode, 0, stat);
}
@Test
public void testUpdateLevelNotExist() throws Exception {
FsInode inode = new FsInode(_fs, Long.MAX_VALUE);
try {
byte[] data = "bla".getBytes();
_fs.write(inode, 1, 0, data, 0, data.length);
fail("was able to update level of non existing file");
} catch (FileNotFoundHimeraFsException e) {
// OK
}
}
@Test
public void testUpdateChecksumNotExist() throws Exception {
FsInode inode = new FsInode(_fs, Long.MAX_VALUE);
try {
_fs.setInodeChecksum(inode, 1, "asum");
fail("was able to update checksum of non existing file");
} catch (FileNotFoundHimeraFsException e) {
// OK
}
}
@Test
public void testUpdateChecksum() throws Exception {
String sum = "abc";
FsInode base = _rootInode.mkdir("junit");
FsInode fileInode = base.create("testCreateFile", 0, 0, 0644);
_fs.setInodeChecksum(fileInode, 1, sum);
assertHasChecksum(new Checksum(ChecksumType.getChecksumType(1), sum), fileInode);
}
@Ignore("Functionality not yet written, but desired")
@Test
public void testUpdateChecksumDifferTypes() throws Exception {
String sum1 = "abc1";
String sum2 = "abc2";
FsInode base = _rootInode.mkdir("junit");
FsInode fileInode = base.create("testCreateFile", 0, 0, 0644);
_fs.setInodeChecksum(fileInode, 1, sum1);
_fs.setInodeChecksum(fileInode, 2, sum2);
assertHasChecksum(new Checksum(ChecksumType.getChecksumType(1), sum1), fileInode);
assertHasChecksum(new Checksum(ChecksumType.getChecksumType(2), sum2), fileInode);
}
@Test
public void testResolveLinkOnPathToId() throws Exception {
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
FsInode linkInode = _rootInode.createLink("aLink", 0, 0, 055, "testDir".getBytes());
FsInode inode = _fs.path2inode("aLink", _rootInode);
assertEquals("Link resolution did not work", dirInode, inode);
}
@Test
public void testResolveLinkOnPathToIds() throws Exception
{
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
FsInode linkInode = _rootInode.createLink("aLink", 0, 0, 055, "testDir".getBytes());
List<FsInode> inodes = _fs.path2inodes("aLink", _rootInode);
assertEquals("Link resolution did not work",
Lists.newArrayList(_rootInode, linkInode, dirInode),
inodes);
}
@Test
public void testResolveLinkOnPathToIdRelative() throws Exception {
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
FsInode linkInode = _rootInode.createLink("aLink", 0, 0, 055, "../testDir".getBytes());
FsInode inode = _fs.path2inode("aLink", _rootInode);
assertEquals("Link resolution did not work", dirInode, inode);
}
@Test
public void testResolveLinkOnPathToIdsRelative() throws Exception
{
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
FsInode linkInode = _rootInode.createLink("aLink", 0, 0, 055, "../testDir".getBytes());
List<FsInode> inodes = _fs.path2inodes("aLink", _rootInode);
assertEquals("Link resolution did not work",
Lists.newArrayList(_rootInode, linkInode, _rootInode, dirInode),
inodes);
}
@Test(expected = FileExistsChimeraFsException.class)
public void testLinkWithExistingName() throws Exception {
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
_rootInode.create("aLink", 0, 0, 055);
_rootInode.createLink("aLink", 0, 0, 055, "../testDir".getBytes());
}
@Test
public void testResolveLinkOnPathToIdAbsolute() throws Exception {
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
FsInode subdirInode = dirInode.mkdir("testDir2", 0, 0, 0755);
FsInode linkInode = dirInode.createLink("aLink", 0, 0, 055, "/testDir/testDir2".getBytes());
FsInode inode = _fs.path2inode("aLink", dirInode);
assertEquals("Link resolution did not work", subdirInode, inode);
}
@Test
public void testResolveLinkOnPathToIdsAbsolute() throws Exception
{
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
FsInode subdirInode = dirInode.mkdir("testDir2", 0, 0, 0755);
FsInode linkInode = dirInode.createLink("aLink", 0, 0, 055, "/testDir/testDir2".getBytes());
List<FsInode> inodes = _fs.path2inodes("aLink", dirInode);
assertEquals("Link resolution did not work",
Lists.newArrayList(dirInode, linkInode, _rootInode, dirInode, subdirInode),
inodes);
}
@Test
public void testUpdateCtimeOnSetOwner() throws Exception {
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
long oldCtime = dirInode.stat().getCTime();
Stat stat = new Stat();
stat.setUid(3750);
dirInode.setStat(stat);
assertTrue("The ctime is not updated", dirInode.stat().getCTime() >= oldCtime);
}
@Test
public void testUpdateCtimeOnSetGroup() throws Exception {
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
long oldCtime = dirInode.stat().getCTime();
long oldChage = dirInode.stat().getGeneration();
Stat stat = new Stat();
stat.setGid(3750);
dirInode.setStat(stat);
assertTrue("The ctime is not updated", dirInode.stat().getCTime() >= oldCtime);
assertTrue("change count is not updated", dirInode.stat().getGeneration() != oldChage);
}
@Test
public void testUpdateCtimeOnSetMode() throws Exception {
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
long oldCtime = dirInode.stat().getCTime();
long oldChage = dirInode.stat().getGeneration();
Stat stat = new Stat();
stat.setMode(0700);
dirInode.setStat(stat);
assertTrue("The ctime is not updated", dirInode.stat().getCTime() >= oldCtime);
assertTrue("change count is not updated", dirInode.stat().getGeneration() != oldChage);
}
@Test
public void testUpdateMtimeOnSetSize() throws Exception {
FsInode inode = _rootInode.create("file", 0, 0, 0755);
long oldMtime = inode.stat().getMTime();
long oldChage = inode.stat().getGeneration();
Stat stat = new Stat();
stat.setSize(17);
inode.setStat(stat);
assertTrue("The mtime is not updated", inode.stat().getMTime() >= oldMtime);
assertTrue("change count is not updated", inode.stat().getGeneration() != oldChage);
}
@Test
public void testSetAcl() throws Exception {
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
RsType rsType = RsType.FILE;
List<ACE> aces = new ArrayList<>();
aces.add(new ACE(AceType.ACCESS_DENIED_ACE_TYPE, 0, AccessMask.ADD_SUBDIRECTORY.getValue(), Who.USER, 1001));
aces.add(new ACE(AceType.ACCESS_ALLOWED_ACE_TYPE, 0, AccessMask.ADD_FILE.getValue(), Who.USER, 1001));
_fs.setACL(dirInode, aces);
List<ACE> l2 = _fs.getACL(dirInode);
assertEquals(aces, l2);
}
@Test
public void testReSetAcl() throws Exception {
FsInode dirInode = _rootInode.mkdir("testDir", 0, 0, 0755);
RsType rsType = RsType.FILE;
List<ACE> aces = new ArrayList<>();
aces.add(new ACE(AceType.ACCESS_DENIED_ACE_TYPE, 0,
AccessMask.ADD_SUBDIRECTORY.getValue(), Who.USER, 1001));
aces.add(new ACE(AceType.ACCESS_ALLOWED_ACE_TYPE, 0,
AccessMask.ADD_FILE.getValue(), Who.USER, 1001));
_fs.setACL(dirInode, aces);
_fs.setACL(dirInode, new ArrayList<ACE>() );
assertTrue(_fs.getACL(dirInode).isEmpty());
}
@Test(expected=FileNotFoundHimeraFsException.class)
public void testGetInodeByPathNotExist() throws Exception {
_fs.path2inode("/some/nonexisting/path");
fail("Expected exception not thrown");
}
@Test
public void testMoveSubdirectory() throws Exception {
FsInode dir01 = _rootInode.mkdir("dir01", 0, 0, 0755);
FsInode dir02 = dir01.mkdir("dir02", 0, 0, 0755);
FsInode dir03 = dir02.mkdir("dir03", 0, 0, 0755);
FsInode dir11 = _rootInode.mkdir("dir11", 0, 0, 0755);
FsInode dir12 = dir11.mkdir("dir12", 0, 0, 0755);
FsInode dir13 = dir12.mkdir("dir13", 0, 0, 0755);
_fs.rename(dir02, dir01, "dir02", dir13, "dir14");
FsInode newInode = _fs.inodeOf(dir13, "dir14", NO_STAT);
assertEquals("Invalid parent", dir13, newInode.inodeOf("..", NO_STAT));
}
@Test(expected = NotDirChimeraException.class)
public void testMoveIntoFile() throws Exception {
FsInode src = _rootInode.create("testMoveIntoFile1", 0, 0, 0644);
FsInode dest = _rootInode.create("testMoveIntoFile2", 0, 0, 0644);
_fs.rename(src, _rootInode, "testMoveIntoFile1", dest, "testMoveIntoFile3");
}
@Test(expected = FileExistsChimeraFsException.class)
public void testMoveIntoDir() throws Exception {
FsInode src = _rootInode.create("testMoveIntoDir", 0, 0, 0644);
FsInode dir = _rootInode.mkdir("dir", 0, 0, 0755);
_fs.rename(src, _rootInode, "testMoveIntoDir", _rootInode, "dir");
}
@Test(expected = FileNotFoundHimeraFsException.class)
public void testMoveNotExists() throws Exception {
_fs.rename(_rootInode, _rootInode, "foo", _rootInode, "bar");
}
@Test(expected = DirNotEmptyHimeraFsException.class)
public void testMoveNotEmptyDir() throws Exception {
FsInode dir1 = _rootInode.mkdir("dir1", 0, 0, 0755);
FsInode dir2 = _rootInode.mkdir("dir2", 0, 0, 0755);
FsInode src = dir2.create("testMoveIntoDir", 0, 0, 0644);
_fs.rename(dir1, _rootInode, "dir1", _rootInode, "dir2");
}
@Test
public void testMoveExistingWithLevel() throws Exception {
FsInode base = _rootInode.mkdir("junit");
FsInode inode1 = base.create("testCreateFile1", 0, 0, 0644);
FsInode inode2 = base.create("testCreateFile2", 0, 0, 0644);
FsInode level1of1 = new FsInode(_fs, inode1.ino(), 1);
byte[] data = "hello".getBytes();
level1of1.write(0, data, 0, data.length);
assertTrue(_fs.rename(inode2, base, "testCreateFile2", base, "testCreateFile1"));
}
@Test(expected = InvalidNameChimeraException.class)
public void testNameTooDirLong() throws Exception {
String tooLongName = Strings.repeat("a", 257);
FsInode base = _rootInode.mkdir(tooLongName);
}
@Test(expected = InvalidNameChimeraException.class)
public void testNameTooFileLong() throws Exception {
String tooLongName = Strings.repeat("a", 257);
FsInode base = _rootInode.create(tooLongName, 0, 0, 0644);
}
@Test(expected = InvalidNameChimeraException.class)
public void testNameTooMoveLong() throws Exception {
String tooLongName = Strings.repeat("a", 257);
FsInode inode = _rootInode.mkdir("testNameTooMoveLong");
_fs.rename(inode, _rootInode, "testNameTooMoveLong", _rootInode, tooLongName);
}
@Test
public void testChangeTagOwner() throws Exception {
final String tagName = "myTag";
FsInode base = _rootInode.mkdir("junit");
_fs.createTag(base, tagName);
FsInode tagInode = new FsInode_TAG(_fs, base.ino(), tagName);
Stat stat = new Stat();
stat.setUid(1);
tagInode.setStat(stat);
assertEquals(1, tagInode.stat().getUid());
}
@Test
public void testChangeTagOwnerGroup() throws Exception {
final String tagName = "myTag";
FsInode base = _rootInode.mkdir("junit");
_fs.createTag(base, tagName);
FsInode tagInode = new FsInode_TAG(_fs, base.ino(), tagName);
Stat stat = new Stat();
stat.setGid(1);
tagInode.setStat(stat);
assertEquals(1, tagInode.stat().getGid());
}
@Test
public void testChangeTagMode() throws Exception {
final String tagName = "myTag";
FsInode base = _rootInode.mkdir("junit");
_fs.createTag(base, tagName);
FsInode tagInode = new FsInode_TAG(_fs, base.ino(), tagName);
Stat stat = new Stat();
stat.setMode(0007);
tagInode.setStat(stat);
assertEquals(0007 | UnixPermission.S_IFREG, tagInode.stat().getMode());
}
@Test
public void testUpdateTagMtimeOnWrite() throws Exception {
final String tagName = "myTag";
final byte[] data1 = "some data".getBytes();
final byte[] data2 = "some other data".getBytes();
FsInode base = _rootInode.mkdir("junit");
_fs.createTag(base, tagName);
FsInode tagInode = new FsInode_TAG(_fs, base.ino(), tagName);
tagInode.write(0, data1, 0, data1.length);
Stat statBefore = tagInode.stat();
tagInode.write(0, data2, 0, data2.length);
Stat statAfter = tagInode.stat();
assertTrue(statAfter.getMTime() >= statBefore.getMTime());
}
@Test
public void testSetAttribitesOnTag() throws Exception {
final String tagName = "myTag";
FsInode base = _rootInode.mkdir("junit");
_fs.createTag(base, tagName);
FsInode tagInode = new FsInode_TAG(_fs, base.ino(), tagName);
Stat stat = tagInode.stat();
Stat baseStat = base.stat();
stat.setUid(123);
_fs.setInodeAttributes(tagInode, 0, stat);
assertEquals(baseStat, base.stat());
}
@Test(expected = FileNotFoundHimeraFsException.class)
public void testGetParentOnRoot() throws Exception {
String id = _rootInode.statCache().getId();
_rootInode.inodeOf(".(parent)(" + id + ")", NO_STAT);
}
@Test
public void testGenerationOnReaddir() throws Exception {
FsInode inode = _rootInode.mkdir("junit");
Stat stat = new Stat();
inode.setStat(stat); // to bump generation
try (DirectoryStreamB<HimeraDirectoryEntry> dirStream = _fs.newDirectoryStream(_rootInode)) {
for (HimeraDirectoryEntry entry : dirStream) {
if (entry.getName().equals("junit") && entry.getStat().getGeneration() == 1) {
return;
}
}
fail();
}
}
private void assertHasChecksum(Checksum expectedChecksum, FsInode inode) throws Exception {
for(Checksum checksum: _fs.getInodeChecksums(inode)) {
if (checksum.equals(expectedChecksum)) {
return;
}
}
fail("No checksums");
}
@Test(expected = InvalidNameChimeraException.class)
public void testDeleteDot() throws Exception {
FsInode base = _rootInode.mkdir("dir1");
base.remove(".");
}
@Test(expected = InvalidNameChimeraException.class)
public void testDeleteDotAtEnd() throws Exception {
FsInode base = _rootInode.mkdir("dir1");
_fs.remove("/dir1/.");
}
@Test(expected = InvalidNameChimeraException.class)
public void testDeleteDotDot() throws Exception {
FsInode base = _rootInode.mkdir("dir1");
base.remove("..");
}
@Test(expected = IsDirChimeraException.class)
public void testSetSizeOnDir() throws Exception {
FsInode dir = _rootInode.mkdir("dir1");
Stat stat = new Stat();
stat.setSize(1);
dir.setStat(stat);
}
@Test(expected = InvalidArgumentChimeraException.class)
public void testSetSizeOnNonFile() throws Exception {
FsInode dir = _rootInode.mkdir("dir1");
FsInode link = _rootInode.createLink("link1", 1, 1, 0777, "dir1".getBytes(StandardCharsets.UTF_8));
Stat stat = new Stat();
stat.setSize(1);
link.setStat(stat);
}
@Test(expected = FileExistsChimeraFsException.class)
public void testCreateDuplicateTag() throws Exception {
FsInode dir = _rootInode.mkdir("dir1");
_fs.createTag(dir, "aTag", 0, 0, 0644);
_fs.createTag(dir, "aTag", 0, 0, 0644);
}
@Test
public void testPathofUnicodePath() throws ChimeraFsException {
FsInode file = _rootInode.create("JC385 - 1300C 12h 15X - \uc624\uc5fc.jpg", 0, 0, 0644);
FsInode_PATHOF pathof = new FsInode_PATHOF(_fs, file.ino());
byte[] buffer = new byte[64];
int len = pathof.read(0, buffer, 0, 64);
assertThat(len, is(equalTo(36)));
}
@Test
public void testPathofOnPartOfUnicodePath() throws ChimeraFsException {
FsInode file = _rootInode.create("JC385 - 1300C 12h 15X - \uc624\uc5fc.jpg", 0, 0, 0644);
FsInode_PATHOF pathof = new FsInode_PATHOF(_fs, file.ino());
byte[] buffer = new byte[12];
int len = pathof.read(23, buffer, 0, 8);
assertThat(len, is(8));
assertArrayEquals(buffer, new byte[]{45, 32, -20, -104, -92, -20, -105, -68, 0, 0, 0, 0});
}
@Test
public void testNameofUnicodePath() throws ChimeraFsException {
FsInode file = _rootInode.create("JC385 - 1300C 12h 15X - \uc624\uc5fc.jpg", 0, 0, 0644);
FsInode_NAMEOF nameof = new FsInode_NAMEOF(_fs, file.ino());
byte[] buffer = new byte[64];
int len = nameof.read(0, buffer, 0, 64);
assertThat(len, is(35));
}
@Test
public void testNameofOnPartOfUnicodePath() throws ChimeraFsException {
FsInode file = _rootInode.create("JC385 - 1300C 12h 15X - \uc624\uc5fc.jpg", 0, 0, 0644);
FsInode_NAMEOF nameof = new FsInode_NAMEOF(_fs, file.ino());
byte[] buffer = new byte[12];
int len = nameof.read(23, buffer, 0, 8);
assertThat(len, is(8));
assertArrayEquals(buffer, new byte[]{32, -20, -104, -92, -20, -105, -68, 46, 0, 0, 0, 0});
}
@Test
public void testCreateBlockDev() throws ChimeraFsException {
_fs.createFile(_rootInode, "aBlockDev", 0, 0, 0644 | UnixPermission.S_IFBLK, UnixPermission.S_IFBLK);
}
@Test
public void testCreateCharDev() throws ChimeraFsException {
_fs.createFile(_rootInode, "aCharDev", 0, 0, 0644 | UnixPermission.S_IFCHR, UnixPermission.S_IFCHR);
}
@Test
public void testCreateSocketDev() throws ChimeraFsException {
_fs.createFile(_rootInode, "aSocket", 0, 0, 0644 | UnixPermission.S_IFSOCK, UnixPermission.S_IFSOCK);
}
@Test
public void testCreateFifoDev() throws ChimeraFsException {
_fs.createFile(_rootInode, "aFifo", 0, 0, 0644 | UnixPermission.S_IFIFO, UnixPermission.S_IFIFO);
}
@Test
public void testCreateSymLink() throws ChimeraFsException {
_fs.createFile(_rootInode, "aSymlink", 0, 0, 0644 | UnixPermission.S_IFLNK, UnixPermission.S_IFLNK);
}
@Test(expected = IllegalArgumentException.class)
public void testCreateDir() throws ChimeraFsException {
_fs.createFile(_rootInode, "aDir", 0, 0, 0755 | UnixPermission.S_IFDIR, UnixPermission.S_IFDIR);
}
@Test
public void testLevelCreation() throws ChimeraFsException {
FsInode file = _fs.createFile(_rootInode, "aFile", 0, 0, 0755 | UnixPermission.S_IFREG, UnixPermission.S_IFREG);
FsInode level = _fs.createFileLevel(file, 2);
byte[] data = "some random data".getBytes(StandardCharsets.UTF_8);
int n = level.write(0, data, 0, data.length);
assertEquals("incorrect number of bytes", data.length, n);
byte[] moreData = "some more random data".getBytes(StandardCharsets.UTF_8);
n = level.write(0, moreData, 0, moreData.length);
assertEquals("incorrect number of bytes", moreData.length, n);
}
}