blob: 5995b513330f43d234237a7693d2d658e6368f46 [file] [log] [blame]
/*
* Copyright 2013 Google Inc.
*
* Licensed 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 com.google.common.jimfs;
import static com.google.common.jimfs.Name.PARENT;
import static com.google.common.jimfs.Name.SELF;
import static com.google.common.jimfs.TestUtils.regularFile;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import com.google.common.base.Functions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Tests for {@link Directory}.
*
* @author Colin Decker
*/
@RunWith(JUnit4.class)
public class DirectoryTest {
private Directory root;
private Directory dir;
@Before
public void setUp() {
root = Directory.createRoot(0, Name.simple("/"));
dir = Directory.create(1);
root.link(Name.simple("foo"), dir);
}
@Test
public void testRootDirectory() {
assertThat(root.entryCount()).is(3); // two for parent/self, one for dir
assertThat(root.isEmpty()).isFalse();
assertThat(root.entryInParent()).isEqualTo(entry(root, "/", root));
assertThat(root.entryInParent().name()).isEqualTo(Name.simple("/"));
assertParentAndSelf(root, root, root);
}
@Test
public void testEmptyDirectory() {
assertThat(dir.entryCount()).is(2);
assertThat(dir.isEmpty()).isTrue();
assertParentAndSelf(dir, root, dir);
}
@Test
public void testGet() {
assertThat(root.get(Name.simple("foo"))).isEqualTo(entry(root, "foo", dir));
assertThat(dir.get(Name.simple("foo"))).isNull();
assertThat(root.get(Name.simple("Foo"))).isNull();
}
@Test
public void testLink() {
assertThat(dir.get(Name.simple("bar"))).isNull();
File bar = Directory.create(2);
dir.link(Name.simple("bar"), bar);
assertThat(dir.get(Name.simple("bar"))).isEqualTo(entry(dir, "bar", bar));
}
@Test
public void testLink_existingNameFails() {
try {
root.link(Name.simple("foo"), Directory.create(2));
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testLink_parentAndSelfNameFails() {
try {
dir.link(Name.simple("."), Directory.create(2));
fail();
} catch (IllegalArgumentException expected) {
}
try {
dir.link(Name.simple(".."), Directory.create(2));
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testGet_normalizingCaseInsensitive() {
File bar = Directory.create(2);
Name barName = caseInsensitive("bar");
dir.link(barName, bar);
DirectoryEntry expected = new DirectoryEntry(dir, barName, bar);
assertThat(dir.get(caseInsensitive("bar"))).isEqualTo(expected);
assertThat(dir.get(caseInsensitive("BAR"))).isEqualTo(expected);
assertThat(dir.get(caseInsensitive("Bar"))).isEqualTo(expected);
assertThat(dir.get(caseInsensitive("baR"))).isEqualTo(expected);
}
@Test
public void testUnlink() {
assertThat(root.get(Name.simple("foo"))).isNotNull();
root.unlink(Name.simple("foo"));
assertThat(root.get(Name.simple("foo"))).isNull();
}
@Test
public void testUnlink_nonExistentNameFails() {
try {
dir.unlink(Name.simple("bar"));
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testUnlink_parentAndSelfNameFails() {
try {
dir.unlink(Name.simple("."));
fail();
} catch (IllegalArgumentException expected) {
}
try {
dir.unlink(Name.simple(".."));
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testUnlink_normalizingCaseInsensitive() {
dir.link(caseInsensitive("bar"), Directory.create(2));
assertThat(dir.get(caseInsensitive("bar"))).isNotNull();
dir.unlink(caseInsensitive("BAR"));
assertThat(dir.get(caseInsensitive("bar"))).isNull();
}
@Test
public void testLinkDirectory() {
Directory newDir = Directory.create(10);
assertThat(newDir.entryInParent()).isNull();
assertThat(newDir.get(Name.SELF).file()).isEqualTo(newDir);
assertThat(newDir.get(Name.PARENT)).isNull();
assertThat(newDir.links()).is(1);
dir.link(Name.simple("foo"), newDir);
assertThat(newDir.entryInParent()).isEqualTo(entry(dir, "foo", newDir));
assertThat(newDir.parent()).isEqualTo(dir);
assertThat(newDir.entryInParent().name()).isEqualTo(Name.simple("foo"));
assertThat(newDir.get(Name.SELF)).isEqualTo(entry(newDir, ".", newDir));
assertThat(newDir.get(Name.PARENT)).isEqualTo(entry(newDir, "..", dir));
assertThat(newDir.links()).is(2);
}
@Test
public void testUnlinkDirectory() {
Directory newDir = Directory.create(10);
dir.link(Name.simple("foo"), newDir);
assertThat(dir.links()).is(3);
assertThat(newDir.entryInParent()).isEqualTo(entry(dir, "foo", newDir));
assertThat(newDir.links()).is(2);
dir.unlink(Name.simple("foo"));
assertThat(dir.links()).is(2);
assertThat(newDir.entryInParent()).isEqualTo(entry(dir, "foo", newDir));
assertThat(newDir.get(Name.SELF).file()).isEqualTo(newDir);
assertThat(newDir.get(Name.PARENT)).isEqualTo(entry(newDir, "..", dir));
assertThat(newDir.links()).is(1);
}
@Test
public void testSnapshot() {
root.link(Name.simple("bar"), regularFile(10));
root.link(Name.simple("abc"), regularFile(10));
// does not include . or .. and is sorted by the name
assertThat(root.snapshot())
.containsExactly(Name.simple("abc"), Name.simple("bar"), Name.simple("foo"));
}
@Test
public void testSnapshot_sortsUsingStringAndNotCanonicalValueOfNames() {
dir.link(caseInsensitive("FOO"), regularFile(10));
dir.link(caseInsensitive("bar"), regularFile(10));
ImmutableSortedSet<Name> snapshot = dir.snapshot();
Iterable<String> strings = Iterables.transform(snapshot, Functions.toStringFunction());
// "FOO" comes before "bar"
// if the order were based on the normalized, canonical form of the names ("foo" and "bar"),
// "bar" would come first
assertThat(strings).iteratesAs("FOO", "bar");
}
// Tests for internal hash table implementation
private static final Directory A = Directory.create(0);
@Test
public void testInitialState() {
assertThat(dir.entryCount()).is(2);
assertThat(ImmutableSet.copyOf(dir)).containsExactly(
new DirectoryEntry(dir, Name.SELF, dir),
new DirectoryEntry(dir, Name.PARENT, root));
assertThat(dir.get(Name.simple("foo"))).isNull();
}
@Test
public void testPutAndGet() {
dir.put(entry("foo"));
assertThat(dir.entryCount()).is(3);
assertThat(ImmutableSet.copyOf(dir)).contains(entry("foo"));
assertThat(dir.get(Name.simple("foo"))).isEqualTo(entry("foo"));
dir.put(entry("bar"));
assertThat(dir.entryCount()).is(4);
assertThat(ImmutableSet.copyOf(dir))
.containsAllOf(entry("foo"), entry("bar"));
assertThat(dir.get(Name.simple("foo"))).isEqualTo(entry("foo"));
assertThat(dir.get(Name.simple("bar"))).isEqualTo(entry("bar"));
}
@Test
public void testPutEntryForExistingNameIsIllegal() {
dir.put(entry("foo"));
try {
dir.put(entry("foo"));
fail();
} catch (IllegalArgumentException expected) {}
}
@Test
public void testRemove() {
dir.put(entry("foo"));
dir.put(entry("bar"));
dir.remove(Name.simple("foo"));
assertThat(dir.entryCount()).is(3);
assertThat(ImmutableSet.copyOf(dir)).containsExactly(
entry("bar"),
new DirectoryEntry(dir, Name.SELF, dir),
new DirectoryEntry(dir, Name.PARENT, root));
assertThat(dir.get(Name.simple("foo"))).isNull();
assertThat(dir.get(Name.simple("bar"))).isEqualTo(entry("bar"));
dir.remove(Name.simple("bar"));
assertThat(dir.entryCount()).is(2);
dir.put(entry("bar"));
dir.put(entry("foo")); // these should just succeeded
}
@Test
public void testManyPutsAndRemoves() {
// test resizing/rehashing
Set<DirectoryEntry> entriesInDir = new HashSet<>();
entriesInDir.add(new DirectoryEntry(dir, Name.SELF, dir));
entriesInDir.add(new DirectoryEntry(dir, Name.PARENT, root));
// add 1000 entries
for (int i = 0; i < 1000; i++) {
DirectoryEntry entry = entry(String.valueOf(i));
dir.put(entry);
entriesInDir.add(entry);
assertThat(ImmutableSet.copyOf(dir)).isEqualTo(entriesInDir);
for (DirectoryEntry expected : entriesInDir) {
assertThat(dir.get(expected.name())).isEqualTo(expected);
}
}
// remove 1000 entries
for (int i = 0; i < 1000; i++) {
dir.remove(Name.simple(String.valueOf(i)));
entriesInDir.remove(entry(String.valueOf(i)));
assertThat(ImmutableSet.copyOf(dir)).isEqualTo(entriesInDir);
for (DirectoryEntry expected : entriesInDir) {
assertThat(dir.get(expected.name())).isEqualTo(expected);
}
}
// mixed adds and removes
for (int i = 0; i < 10000; i++) {
DirectoryEntry entry = entry(String.valueOf(i));
dir.put(entry);
entriesInDir.add(entry);
if (i > 0 && i % 20 == 0) {
String nameToRemove = String.valueOf(i / 2);
dir.remove(Name.simple(nameToRemove));
entriesInDir.remove(entry(nameToRemove));
}
}
// for this one, only test that the end result is correct
// takes too long to test at each iteration
assertThat(ImmutableSet.copyOf(dir)).isEqualTo(entriesInDir);
for (DirectoryEntry expected : entriesInDir) {
assertThat(dir.get(expected.name())).isEqualTo(expected);
}
}
private static DirectoryEntry entry(String name) {
return new DirectoryEntry(A, Name.simple(name), A);
}
private static DirectoryEntry entry(Directory dir, String name, @Nullable File file) {
return new DirectoryEntry(dir, Name.simple(name), file);
}
private static void assertParentAndSelf(Directory dir, File parent, File self) {
assertThat(dir).isEqualTo(self);
assertThat(dir.parent()).isEqualTo(parent);
assertThat(dir.get(PARENT)).isEqualTo(entry((Directory) self, "..", parent));
assertThat(dir.get(SELF)).isEqualTo(entry((Directory) self, ".", self));
}
private static Name caseInsensitive(String name) {
return Name.create(name, PathNormalization.CASE_FOLD_UNICODE.apply(name));
}
}