Merge branch 'master' of https://code.google.com/p/plexi.fs into FixShareAcl2
Conflicts:
src/com/google/enterprise/adaptor/fs/FsAdaptor.java
test/com/google/enterprise/adaptor/fs/FsAdaptorTest.java
diff --git a/src/com/google/enterprise/adaptor/fs/FsAdaptor.java b/src/com/google/enterprise/adaptor/fs/FsAdaptor.java
index 972cc84..d8a6103 100644
--- a/src/com/google/enterprise/adaptor/fs/FsAdaptor.java
+++ b/src/com/google/enterprise/adaptor/fs/FsAdaptor.java
@@ -134,13 +134,15 @@
/** The namespace applied to ACL Principals. */
private String namespace;
+ /** The filesystem change monitor. */
+ private FsMonitor monitor;
+
private AdaptorContext context;
private Path rootPath;
private boolean isDfsUnc;
private DocId rootPathDocId;
private FileDelegate delegate;
- private FsMonitor monitor;
- private ShareAcls lastPushedShareAcls = new ShareAcls(null, null);
+ private ShareAcls lastPushedShareAcls = null;
public FsAdaptor() {
// At the moment, we only support Windows.
@@ -152,6 +154,31 @@
}
}
+ @VisibleForTesting
+ FsAdaptor(FileDelegate delegate) {
+ this.delegate = delegate;
+ }
+
+ @VisibleForTesting
+ Set<String> getSupportedWindowsAccounts() {
+ return supportedWindowsAccounts;
+ }
+
+ @VisibleForTesting
+ String getBuiltinPrefix() {
+ return builtinPrefix;
+ }
+
+ @VisibleForTesting
+ String getNamespace() {
+ return namespace;
+ }
+
+ @VisibleForTesting
+ BlockingQueue<Path> getFsMonitorQueue() {
+ return monitor.getQueue();
+ }
+
@Override
public void initConfig(Config config) {
config.addKey(CONFIG_SRC, null);
@@ -238,11 +265,16 @@
@Override
public void destroy() {
delegate.destroy();
- monitor.destroy();
- monitor = null;
+ // TODO (bmj): The check for null monitor is strictly for the tests,
+ // some of which may not have fully initialized the adaptor. Maybe
+ // look into handling this less obtrusively in the future.
+ if (monitor != null) {
+ monitor.destroy();
+ monitor = null;
+ }
}
- private ShareAcls getShareAcls() throws IOException {
+ private ShareAcls readShareAcls() throws IOException {
Acl shareAcl;
Acl dfsShareAcl;
@@ -305,14 +337,14 @@
// The pusher does not support fragments in named resources.
// Feed a DocId that is just the SHARE_ACL fragment to avoid
// collisions with the root docid.
- ShareAcls shareAcls = getShareAcls();
+ ShareAcls shareAcls = readShareAcls();
Map<DocId, Acl> namedResources = new HashMap<DocId, Acl>();
- if (forcePush || ((shareAcls.dfsShareAcl != null)
- && !shareAcls.dfsShareAcl.equals(lastPushedShareAcls.dfsShareAcl))) {
+ if ((shareAcls.dfsShareAcl != null) && (forcePush ||
+ !shareAcls.dfsShareAcl.equals(lastPushedShareAcls.dfsShareAcl))) {
namedResources.put(DFS_SHARE_ACL_DOCID, shareAcls.dfsShareAcl);
}
- if (forcePush || ((shareAcls.shareAcl != null)
- && !shareAcls.shareAcl.equals(lastPushedShareAcls.shareAcl))) {
+ if ((shareAcls.shareAcl != null) && (forcePush ||
+ !shareAcls.shareAcl.equals(lastPushedShareAcls.shareAcl))) {
namedResources.put(SHARE_ACL_DOCID, shareAcls.shareAcl);
}
if (namedResources.size() > 0) {
@@ -443,10 +475,10 @@
// Populate the document content.
if (docIsDirectory) {
HtmlResponseWriter writer = createHtmlResponseWriter(resp);
- writer.start(id, getPathName(doc));
+ writer.start(id, getFileName(doc));
for (Path file : delegate.newDirectoryStream(doc)) {
if (isSupportedPath(file)) {
- writer.addLink(delegate.newDocId(file), getPathName(file));
+ writer.addLink(delegate.newDocId(file), getFileName(file));
}
}
writer.finish();
@@ -483,12 +515,14 @@
}
@VisibleForTesting
- String getPathName(Path file) {
+ String getFileName(Path file) {
// NOTE: file.getFileName() fails for UNC paths. Use file.toFile() instead.
- return file.toFile().getName();
+ String name = file.toFile().getName();
+ return name.isEmpty() ? file.getRoot().toString() : name;
}
- private boolean isSupportedPath(Path p) throws IOException {
+ @VisibleForTesting
+ boolean isSupportedPath(Path p) throws IOException {
return delegate.isRegularFile(p) || delegate.isDirectory(p);
}
@@ -496,7 +530,8 @@
* Verifies that the file is a descendant of the root directory,
* and that it, nor none of its ancestors, is hidden.
*/
- private boolean isVisibleDescendantOfRoot(Path doc) throws IOException {
+ @VisibleForTesting
+ boolean isVisibleDescendantOfRoot(Path doc) throws IOException {
for (Path file = doc; file != null; file = file.getParent()) {
if (delegate.isHidden(file)) {
if (doc.equals(file)) {
@@ -531,8 +566,8 @@
Preconditions.checkNotNull(pusher, "the DocId pusher may not be null");
Preconditions.checkArgument(maxFeedSize > 0,
"the maxFeedSize must be greater than zero");
- Preconditions.checkArgument(maxLatencyMillis > 0,
- "the maxLatencyMillis must be greater than zero");
+ Preconditions.checkArgument(maxLatencyMillis >= 0,
+ "the maxLatencyMillis must be greater than or equal to zero");
this.pusher = pusher;
this.maxFeedSize = maxFeedSize;
this.maxLatencyMillis = maxLatencyMillis;
diff --git a/src/com/google/enterprise/adaptor/fs/HtmlResponseWriter.java b/src/com/google/enterprise/adaptor/fs/HtmlResponseWriter.java
index 3cf0f47..e0d50f3 100644
--- a/src/com/google/enterprise/adaptor/fs/HtmlResponseWriter.java
+++ b/src/com/google/enterprise/adaptor/fs/HtmlResponseWriter.java
@@ -15,6 +15,7 @@
package com.google.enterprise.adaptor.fs;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
import com.google.enterprise.adaptor.DocId;
import com.google.enterprise.adaptor.DocIdEncoder;
@@ -213,10 +214,10 @@
}
private String computeLabel(String label, DocId doc) {
- if (label == null || "".equals(label)) {
+ if (Strings.isNullOrEmpty(label)) {
// Use the last part of the URL if an item doesn't have a title. The last
// part of the URL will generally be a filename in this case.
- String[] parts = doc.getUniqueId().split("/");
+ String[] parts = doc.getUniqueId().split("/", 0);
label = parts[parts.length - 1];
}
return label;
diff --git a/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java b/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java
index 199b568..b70e455 100644
--- a/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java
+++ b/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java
@@ -14,6 +14,7 @@
package com.google.enterprise.adaptor.fs;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
@@ -67,12 +68,9 @@
*/
class WindowsAclFileAttributeViews {
- private static final Logger log =
+ private static final Logger log =
Logger.getLogger(WindowsAclFileAttributeViews.class.getName());
- private static final Kernel32 KERNEL32 = Kernel32.INSTANCE;
- private static final Advapi32 ADVAPI32 = Advapi32.INSTANCE;
-
/** This pattern parses a UNC path to get the host and share details. */
private static final Pattern UNC_PATTERN =
Pattern.compile("^\\\\\\\\([^\\\\]+)\\\\([^\\\\]+)");
@@ -88,7 +86,7 @@
Collections.unmodifiableSet(Sets.newHashSet(SID_NAME_USE.SidTypeUser));
/** Map of NT GENERIC permissions to NT FILE permissions. */
- private static final Map<Integer, Integer> GENERIC_PERMS_MAP =
+ private static final Map<Integer, Integer> GENERIC_PERMS_MAP =
Collections.unmodifiableMap(new HashMap<Integer, Integer>() {
{
put(WinNT.GENERIC_READ, WinNT.FILE_GENERIC_READ);
@@ -142,6 +140,28 @@
}
});
+ private final Advapi32 advapi32;
+ private final Kernel32 kernel32;
+ private final Mpr mpr;
+ private final Netapi32Ex netapi32;
+ private final Shlwapi shlwapi;
+
+ /** Constructor used for production. */
+ public WindowsAclFileAttributeViews() {
+ this(Advapi32.INSTANCE, Kernel32.INSTANCE, Mpr.INSTANCE,
+ Netapi32Ex.INSTANCE,Shlwapi.INSTANCE);
+ }
+
+ /** Constructor used by the tests. */
+ @VisibleForTesting
+ WindowsAclFileAttributeViews(Advapi32 advapi32, Kernel32 kernel32,
+ Mpr mpr, Netapi32Ex netapi32, Shlwapi shlwapi) {
+ this.advapi32 = advapi32;
+ this.kernel32 = kernel32;
+ this.mpr = mpr;
+ this.netapi32 = netapi32;
+ this.shlwapi = shlwapi;
+ }
/**
* Returns a container for the direct and inherited ACLs for
@@ -152,7 +172,7 @@
*/
public AclFileAttributeViews getAclViews(Path path) throws IOException {
String pathname = path.toRealPath(LinkOption.NOFOLLOW_LINKS).toString();
- WinNT.ACCESS_ACEStructure[] aces = getFileSecurity(pathname,
+ WinNT.ACCESS_ACEStructure[] aces = getFileSecurity(pathname,
WinNT.DACL_SECURITY_INFORMATION |
WinNT.PROTECTED_DACL_SECURITY_INFORMATION |
WinNT.UNPROTECTED_DACL_SECURITY_INFORMATION);
@@ -160,7 +180,7 @@
ImmutableList.Builder<AclEntry> direct = ImmutableList.builder();
for (WinNT.ACCESS_ACEStructure ace : aces) {
- AclEntry aclEntry = newAclEntry(ace);
+ AclEntry aclEntry = newAclEntry(ace);
if (aclEntry != null) {
if ((ace.AceFlags & WinNT.INHERITED_ACE) == WinNT.INHERITED_ACE) {
inherited.add(aclEntry);
@@ -192,23 +212,22 @@
*/
public AclFileAttributeView getShareAclView(Path path)
throws IOException, UnsupportedOperationException {
- if (Shlwapi.INSTANCE.PathIsUNC(path.toString())) {
+ if (shlwapi.PathIsUNC(path.toString())) {
log.log(Level.FINEST, "Using a UNC path.");
return getUncShareAclView(path.toString());
- } else if (Shlwapi.INSTANCE.PathIsNetworkPath(path.toString())) {
+ } else if (shlwapi.PathIsNetworkPath(path.toString())) {
log.log(Level.FINEST, "Using a mapped drive.");
- // Call WNetGetUniversalNameW with the size needed for
+ // Call WNetGetUniversalNameW with the size needed for
// UNIVERSAL_NAME_INFO. If WNetGetUniversalNameW returns ERROR_MORE_DATA
// that indicates that a larger buffer is needed. If this happens, make
// a second call to WNetGetUniversalNameW with a buffer big enough.
- Mpr mprlib = Mpr.INSTANCE;
Memory buf = new Memory(1024);
IntByReference bufSize = new IntByReference((int) buf.size());
- int result = mprlib.WNetGetUniversalNameW(path.getRoot().toString(),
+ int result = mpr.WNetGetUniversalNameW(path.getRoot().toString(),
Mpr.UNIVERSAL_NAME_INFO_LEVEL, buf, bufSize);
if (result == WinNT.ERROR_MORE_DATA) {
buf = new Memory(bufSize.getValue());
- result = Mpr.INSTANCE.WNetGetUniversalNameW(path.getRoot().toString(),
+ result = mpr.WNetGetUniversalNameW(path.getRoot().toString(),
Mpr.UNIVERSAL_NAME_INFO_LEVEL, buf, bufSize);
}
if (result != WinNT.NO_ERROR) {
@@ -239,12 +258,11 @@
new Object[] { host, share });
return getShareAclView(host, share);
}
-
+
private AclFileAttributeView getShareAclView(String host, String share)
throws IOException {
- Netapi32Ex netapi32 = Netapi32Ex.INSTANCE;
PointerByReference buf = new PointerByReference();
-
+
// Call NetShareGetInfo with a 502 to get the security descriptor of the
// share. The security descriptor contains the Acl details for the share
// that the adaptor needs.
@@ -299,7 +317,7 @@
* @return AclEntry representing the ace, or {@code null} if a valid
* AclEntry could not be created from the ace.
*/
- public static AclEntry newAclEntry(WinNT.ACCESS_ACEStructure ace) {
+ public AclEntry newAclEntry(WinNT.ACCESS_ACEStructure ace) {
// Map the type.
AclEntryType aclType = ACL_TYPE_MAP.get(ace.AceType);
if (aclType == null) {
@@ -311,7 +329,7 @@
// Map the user.
Account account;
try {
- account = Advapi32Util.getAccountBySid(ace.getSID());
+ account = getAccountBySid(ace.getSID());
} catch (Win32Exception e) {
// Only the least significant 16-bits signifies the HR code.
if ((e.getHR().intValue() & 0xFFFF) == WinError.ERROR_NONE_MAPPED) {
@@ -336,7 +354,7 @@
new Object[] { accountName, accountType });
return null;
}
-
+
// Expand NT GENERIC_* permissions to their FILE_GENERIC_* equivalents.
int aceMask = ace.Mask;
for (Map.Entry<Integer, Integer> e : GENERIC_PERMS_MAP.entrySet()) {
@@ -352,7 +370,7 @@
aclPerms.add(e.getValue());
}
}
-
+
// Map the flags.
Set<AclEntryFlag> aclFlags = EnumSet.noneOf(AclEntryFlag.class);
for (Map.Entry<Byte, AclEntryFlag> e : ACL_FLAGS_MAP.entrySet()) {
@@ -369,6 +387,11 @@
.build();
}
+ @VisibleForTesting
+ Account getAccountBySid(WinNT.PSID sid) throws Win32Exception {
+ return Advapi32Util.getAccountBySid(sid);
+ }
+
// One-to-one corresponance to WinNT.SID_NAME_USE "enumeration".
private static final List<String> SID_TYPE_NAMES = ImmutableList.of(
"Unknown", "User", "Group", "Domain", "Alias", "Well-known Group",
@@ -408,7 +431,8 @@
}
}
- private interface Mpr extends StdCallLibrary {
+ @VisibleForTesting
+ interface Mpr extends StdCallLibrary {
Mpr INSTANCE = (Mpr) Native.loadLibrary("Mpr", Mpr.class,
W32APIOptions.UNICODE_OPTIONS);
@@ -442,21 +466,21 @@
WString wpath = new WString(pathname);
IntByReference lengthNeeded = new IntByReference();
- if (ADVAPI32.GetFileSecurity(wpath, daclType, null, 0, lengthNeeded)) {
+ if (advapi32.GetFileSecurity(wpath, daclType, null, 0, lengthNeeded)) {
throw new RuntimeException("GetFileSecurity was expected to fail with "
+ "ERROR_INSUFFICIENT_BUFFER");
}
- int rc = KERNEL32.GetLastError();
+ int rc = kernel32.GetLastError();
if (rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
throw new IOException("Failed GetFileSecurity", new Win32Exception(rc));
}
Memory memory = new Memory(lengthNeeded.getValue());
- if (!ADVAPI32.GetFileSecurity(wpath, daclType, memory, (int) memory.size(),
+ if (!advapi32.GetFileSecurity(wpath, daclType, memory, (int) memory.size(),
lengthNeeded)) {
throw new IOException("Failed GetFileSecurity",
- new Win32Exception(KERNEL32.GetLastError()));
+ new Win32Exception(kernel32.GetLastError()));
}
WinNT.SECURITY_DESCRIPTOR_RELATIVE securityDescriptor =
diff --git a/src/com/google/enterprise/adaptor/fs/WindowsFileDelegate.java b/src/com/google/enterprise/adaptor/fs/WindowsFileDelegate.java
index 18b139b..07c106d 100644
--- a/src/com/google/enterprise/adaptor/fs/WindowsFileDelegate.java
+++ b/src/com/google/enterprise/adaptor/fs/WindowsFileDelegate.java
@@ -99,7 +99,7 @@
ImmutableList.Builder<AclEntry> builder = ImmutableList.builder();
for (WinNT.ACCESS_ACEStructure ace : dacl.getACEStructures()) {
- AclEntry entry = WindowsAclFileAttributeViews.newAclEntry(ace);
+ AclEntry entry = aclViews.newAclEntry(ace);
if (entry != null) {
builder.add(entry);
}
diff --git a/test/com/google/enterprise/adaptor/fs/AccumulatingDocIdPusher.java b/test/com/google/enterprise/adaptor/fs/AccumulatingDocIdPusher.java
new file mode 100644
index 0000000..5c38e25
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/AccumulatingDocIdPusher.java
@@ -0,0 +1,96 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// 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.enterprise.adaptor.fs;
+
+import com.google.enterprise.adaptor.Acl;
+import com.google.enterprise.adaptor.DocId;
+import com.google.enterprise.adaptor.ExceptionHandler;
+import com.google.enterprise.adaptor.GroupPrincipal;
+import com.google.enterprise.adaptor.Principal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+class AccumulatingDocIdPusher extends UnsupportedDocIdPusher {
+ private List<Record> records = new ArrayList<Record>();
+ private List<Map<DocId, Acl>> namedResouces
+ = new ArrayList<Map<DocId, Acl>>();
+
+ @Override
+ public DocId pushDocIds(Iterable<DocId> docIds)
+ throws InterruptedException {
+ return pushDocIds(docIds, null);
+ }
+
+ @Override
+ public DocId pushDocIds(Iterable<DocId> docIds,
+ ExceptionHandler handler)
+ throws InterruptedException {
+ List<Record> records = new ArrayList<Record>();
+ for (DocId docId : docIds) {
+ records.add(new Record.Builder(docId).build());
+ }
+ Record record = pushRecords(records, handler);
+ return record == null ? null : record.getDocId();
+ }
+
+ @Override
+ public Record pushRecords(Iterable<Record> records)
+ throws InterruptedException {
+ return pushRecords(records, null);
+ }
+
+ @Override
+ public Record pushRecords(Iterable<Record> records,
+ ExceptionHandler handler)
+ throws InterruptedException {
+ for (Record record : records) {
+ this.records.add(record);
+ }
+ return null;
+ }
+
+ @Override
+ public DocId pushNamedResources(Map<DocId, Acl> resources)
+ throws InterruptedException {
+ return pushNamedResources(resources, null);
+ }
+
+ @Override
+ public DocId pushNamedResources(Map<DocId, Acl> resources,
+ ExceptionHandler handler)
+ throws InterruptedException {
+ namedResouces.add(Collections.unmodifiableMap(
+ new TreeMap<DocId, Acl>(resources)));
+ return null;
+ }
+
+ public List<Record> getRecords() {
+ return Collections.unmodifiableList(records);
+ }
+
+ public List<Map<DocId, Acl>> getNamedResources() {
+ return Collections.unmodifiableList(namedResouces);
+ }
+
+ public void reset() {
+ records.clear();
+ namedResouces.clear();
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/FsAdaptorTest.java b/test/com/google/enterprise/adaptor/fs/FsAdaptorTest.java
index 9eb2d16..efb317c 100644
--- a/test/com/google/enterprise/adaptor/fs/FsAdaptorTest.java
+++ b/test/com/google/enterprise/adaptor/fs/FsAdaptorTest.java
@@ -1,4 +1,4 @@
-// Copyright 2013 Google Inc. All Rights Reserved.
+// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,38 +14,965 @@
package com.google.enterprise.adaptor.fs;
-import com.google.enterprise.adaptor.AccumulatingDocIdPusher;
+import static com.google.enterprise.adaptor.fs.AclView.user;
+import static com.google.enterprise.adaptor.fs.AclView.group;
+import static com.google.enterprise.adaptor.fs.AclView.GenericPermission.*;
import static org.junit.Assert.*;
+import static java.nio.file.attribute.AclEntryFlag.*;
+import static java.nio.file.attribute.AclEntryPermission.*;
+import static java.nio.file.attribute.AclEntryType.*;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import com.google.enterprise.adaptor.Acl;
+import com.google.enterprise.adaptor.Acl.InheritanceType;
+import com.google.enterprise.adaptor.AdaptorContext;
+import com.google.enterprise.adaptor.Config;
+import com.google.enterprise.adaptor.DocId;
+import com.google.enterprise.adaptor.DocIdPusher.Record;
+import com.google.enterprise.adaptor.GroupPrincipal;
+import com.google.enterprise.adaptor.UserPrincipal;
+
import org.junit.*;
import org.junit.rules.ExpectedException;
+import java.io.File;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.attribute.AclFileAttributeView;
+import java.nio.file.attribute.FileTime;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
/** Test cases for {@link FsAdaptor}. */
public class FsAdaptorTest {
+
+ static final String ROOT = "/";
+ static final Path rootPath = Paths.get(ROOT);
+ static final DocId shareAclDocId = new DocId("shareAcl");
+
+ private final Set<String> windowsAccounts = ImmutableSet.of(
+ "BUILTIN\\Administrators", "Everyone", "BUILTIN\\Users",
+ "BUILTIN\\Guest", "NT AUTHORITY\\INTERACTIVE",
+ "NT AUTHORITY\\Authenticated Users");
+ private final String builtinPrefix = "BUILTIN\\";
+ private final String namespace = "Default";
+
+ private AdaptorContext context = new MockAdaptorContext();
+ private AccumulatingDocIdPusher pusher =
+ (AccumulatingDocIdPusher) context.getDocIdPusher();
+ private Config config = context.getConfig();
+ private MockFile root = new MockFile(ROOT, true);
+ private MockFileDelegate delegate = new MockFileDelegate(root);
+ private FsAdaptor adaptor = new FsAdaptor(delegate);
+ private DocId rootDocId;
+
+ @Before
+ public void setUp() throws Exception {
+ rootDocId = delegate.newDocId(rootPath);
+ adaptor.initConfig(config);
+ config.overrideKey("filesystemadaptor.src", root.getPath());
+ config.overrideKey("adaptor.incrementalPollPeriodSecs", "0");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ adaptor.destroy();
+ }
+
@Rule
public ExpectedException thrown = ExpectedException.none();
+ private Path getPath(String path) {
+ return rootPath.resolve(path);
+ }
+
+ private DocId getDocId(String path) throws IOException {
+ return delegate.newDocId(getPath(path));
+ }
+
@Test
- public void testGetPathName() throws Exception {
- TestHelper.assumeOsIsWindows();
- FsAdaptor adaptor = new FsAdaptor();
- assertEquals("share", adaptor.getPathName(Paths.get("\\\\host/share/")));
- assertEquals("folder2",
- adaptor.getPathName(Paths.get("C:/folder1/folder2/")));
+ public void testAdaptorStartupShutdown() throws Exception {
+ // Construction of Adaptor happened in setUp(), and
+ // destruction will happen in tearDown(), so the only
+ // thing left is to init the context.
+ adaptor.init(context);
+ }
+
+ @Test
+ public void testAdaptorInitNoSourcePath() throws Exception {
+ config.overrideKey("filesystemadaptor.src", "");
+ thrown.expect(IOException.class);
+ adaptor.init(context);
+ }
+
+ @Test
+ public void testAdaptorInitNonRootSourcePath() throws Exception {
+ root.addChildren(new MockFile("subdir", true));
+ config.overrideKey("filesystemadaptor.src", getPath("subdir").toString());
+ thrown.expect(IllegalStateException.class);
+ adaptor.init(context);
+ }
+
+ @Test
+ public void testAdaptorInitDfsUncActiveStorageUnc() throws Exception {
+ root.setDfsUncActiveStorageUnc(Paths.get("\\\\dfshost\\share"));
+ adaptor.init(context);
+ }
+
+ @Test
+ public void testAdaptorInitNonLinkDfsUncActiveStorageUnc() throws Exception {
+ root.setDfsUncActiveStorageUnc(rootPath);
+ thrown.expect(IOException.class);
+ adaptor.init(context);
+ }
+
+ @Test
+ public void testAdaptorInitSupportedWindowsAccounts() throws Exception {
+ String accounts = "Everyone, BUILTIN\\Users, NT AUTH\\New Users";
+ Set<String> expected =
+ ImmutableSet.of("Everyone", "BUILTIN\\Users", "NT AUTH\\New Users");
+ config.overrideKey("filesystemadaptor.supportedAccounts", accounts);
+ adaptor.init(context);
+ assertEquals(expected, adaptor.getSupportedWindowsAccounts());
+ }
+
+ @Test
+ public void testAdaptorInitBuiltinGroupPrefix() throws Exception {
+ String expected = "TestPrefix";
+ config.overrideKey("filesystemadaptor.builtinGroupPrefix", expected);
+ adaptor.init(context);
+ assertEquals(expected, adaptor.getBuiltinPrefix());
+ }
+
+ @Test
+ public void testAdaptorInitNamespace() throws Exception {
+ String expected = "TestNamespace";
+ config.overrideKey("adaptor.namespace", expected);
+ adaptor.init(context);
+ assertEquals(expected, adaptor.getNamespace());
+ }
+
+ @Test
+ public void testGetFolderName() throws Exception {
+ assertEquals("share", adaptor.getFileName(Paths.get("\\\\host/share/")));
+ assertEquals("folder2",
+ adaptor.getFileName(Paths.get("C:/folder1/folder2/")));
+ assertEquals("folder2",
+ adaptor.getFileName(Paths.get("/folder1/folder2/")));
+ assertEquals("share", adaptor.getFileName(Paths.get("\\\\host/share")));
+ assertEquals("folder1",
+ adaptor.getFileName(Paths.get("/folder1")));
+ assertEquals(File.separator, // Windows flips the '/' to '\'.
+ adaptor.getFileName(Paths.get("/")));
+ assertEquals("C:\\",
+ adaptor.getFileName(Paths.get("C:\\")));
+ }
+
+ @Test
+ public void testIsSupportedPath() throws Exception {
+ root.addChildren(new MockFile("foo"), new MockFile("bar", true),
+ new MockFile("link").setIsRegularFile(false));
+ assertTrue(adaptor.isSupportedPath(rootPath));
+ assertTrue(adaptor.isSupportedPath(getPath("foo")));
+ assertTrue(adaptor.isSupportedPath(getPath("bar")));
+ assertFalse(adaptor.isSupportedPath(getPath("link")));
+ }
+
+ @Test
+ public void testIsVisibleDescendantOfRoot() throws Exception {
+ adaptor.init(context);
+ root.addChildren(new MockFile("foo"),
+ new MockFile("hidden.txt").setIsHidden(true),
+ new MockFile("dir1", true).addChildren(new MockFile("bar"),
+ new MockFile("hidden.pdf").setIsHidden(true)),
+ new MockFile("hidden.dir", true).setIsHidden(true).addChildren(
+ new MockFile("baz")));
+ assertTrue(adaptor.isVisibleDescendantOfRoot(rootPath));
+ assertTrue(adaptor.isVisibleDescendantOfRoot(getPath("foo")));
+ assertTrue(adaptor.isVisibleDescendantOfRoot(getPath("dir1")));
+ assertTrue(adaptor.isVisibleDescendantOfRoot(getPath("dir1/bar")));
+ assertFalse(adaptor.isVisibleDescendantOfRoot(getPath("hidden.txt")));
+ assertFalse(adaptor.isVisibleDescendantOfRoot(getPath("dir1/hidden.pdf")));
+ assertFalse(adaptor.isVisibleDescendantOfRoot(getPath("hidden.dir")));
+ assertFalse(adaptor.isVisibleDescendantOfRoot(getPath("hidden.dir/baz")));
+ assertFalse(adaptor.isVisibleDescendantOfRoot(null));
+ }
+
+ @Test
+ public void testGetDocIds() throws Exception {
+ adaptor.init(context);
+ adaptor.getDocIds(pusher);
+
+ // We should just push the root docid.
+ List<Record> records = pusher.getRecords();
+ assertEquals(1, records.size());
+ assertEquals(delegate.newDocId(rootPath), records.get(0).getDocId());
+
+ // We should have pushed an acl for the share.
+ List<Map<DocId, Acl>> namedResources = pusher.getNamedResources();
+ Acl expected = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitGroups(Collections.singleton(new GroupPrincipal("Everyone")))
+ .setInheritanceType(InheritanceType.AND_BOTH_PERMIT).build();
+ assertEquals(1, namedResources.size());
+ assertEquals(1, namedResources.get(0).size());
+ assertEquals(expected, namedResources.get(0).get(shareAclDocId));
+ }
+
+ @Test
+ public void testGetDocIdsDfs() throws Exception {
+ Path uncPath = Paths.get("\\\\dfshost\\share");
+ root.setDfsUncActiveStorageUnc(uncPath);
+ root.setDfsShareAclView(MockFile.FULL_ACCESS_ACLVIEW);
+ adaptor.init(context);
+ adaptor.getDocIds(pusher);
+
+ // We should just push the root docid.
+ List<Record> records = pusher.getRecords();
+ assertEquals(1, records.size());
+ assertEquals(delegate.newDocId(rootPath), records.get(0).getDocId());
+
+ // We should have pushed acls for the share and the dfsShare.
+ List<Map<DocId, Acl>> namedResources = pusher.getNamedResources();
+ DocId dfsShareAcl = new DocId("dfsShareAcl");
+ Acl expectedDfsShareAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitGroups(Collections.singleton(new GroupPrincipal("Everyone")))
+ .setInheritanceType(InheritanceType.AND_BOTH_PERMIT).build();
+ Acl expectedShareAcl = new Acl.Builder(expectedDfsShareAcl)
+ .setInheritFrom(dfsShareAcl).build();
+ assertEquals(1, namedResources.size());
+ Map<DocId, Acl> acls = namedResources.get(0);
+ assertEquals(2, acls.size());
+ assertEquals(expectedDfsShareAcl, acls.get(dfsShareAcl));
+ assertEquals(expectedShareAcl, acls.get(shareAclDocId));
+ }
+
+ @Test
+ public void testGetDocIdsBrokenDfs() throws Exception {
+ Path uncPath = Paths.get("\\\\dfshost\\share");
+ root.setDfsUncActiveStorageUnc(uncPath);
+ root.setDfsShareAclView(MockFile.FULL_ACCESS_ACLVIEW);
+ adaptor.init(context);
+
+ // Now make the active storage disappear.
+ root.setDfsUncActiveStorageUnc(null);
+ thrown.expect(IOException.class);
+ adaptor.getDocIds(pusher);
+ }
+
+ @Test
+ public void testGetDocContentUnsupportedPath() throws Exception {
+ root.addChildren(new MockFile("unsupported").setIsRegularFile(false));
+ adaptor.init(context);
+ MockResponse response = new MockResponse();
+ adaptor.getDocContent(new MockRequest(getDocId("unsupported")), response);
+ assertTrue(response.notFound);
+ }
+
+ /*
+ * This test was invalidated when the check for isSupportedPath()
+ * was moved above the check for bad DocIds in the beginning of
+ * FsAdaptor.getDocumentContent(). This test still passes, but
+ * the notFound response is coming from the earlier check for
+ * isSupportedPath(), not from the bad DocId.
+ */
+ @Test
+ public void testGetDocContentBadDocId() throws Exception {
+ root.addChildren(new MockFile("badfile"));
+ adaptor.init(context);
+ MockResponse response = new MockResponse();
+ // The requested DocId is missing the root component of the path.
+ adaptor.getDocContent(new MockRequest(new DocId("badfile")), response);
+ assertTrue(response.notFound);
+ }
+
+ @Test
+ public void testGetDocContentHiddenFile() throws Exception {
+ root.addChildren(new MockFile("hidden.txt").setIsHidden(true));
+ adaptor.init(context);
+ MockResponse response = new MockResponse();
+ adaptor.getDocContent(new MockRequest(getDocId("hidden.txt")), response);
+ assertTrue(response.notFound);
+ }
+
+ @Test
+ public void testGetDocContentHiddenDirectory() throws Exception {
+ root.addChildren(new MockFile("hidden.dir", true).setIsHidden(true));
+ adaptor.init(context);
+ MockResponse response = new MockResponse();
+ adaptor.getDocContent(new MockRequest(getDocId("hidden.dir")), response);
+ assertTrue(response.notFound);
+ }
+
+ @Test
+ public void testGetDocContentRegularFile() throws Exception {
+ String fname = "test.html";
+ Date modifyDate = new Date(30000);
+ FileTime modifyTime = FileTime.fromMillis(modifyDate.getTime());
+ String content = "<html><title>Hello World</title></html>";
+ root.addChildren(new MockFile(fname).setLastModifiedTime(modifyTime)
+ .setFileContents(content).setContentType("text/html"));
+ adaptor.init(context);
+
+ MockResponse response = new MockResponse();
+ adaptor.getDocContent(new MockRequest(getDocId(fname)), response);
+ assertFalse(response.notFound);
+ assertEquals(modifyDate, response.lastModified);
+ assertEquals(getPath(fname).toUri(), response.displayUrl);
+ assertEquals("text/html", response.contentType);
+ assertEquals(content, response.content.toString("UTF-8"));
+ // TODO: check metadata.
+ assertNotNull(response.metadata.get("Creation Time"));
+ // ACL checked in other tests.
+ }
+
+ @Test
+ public void testGetDocContentRootAcl() throws Exception {
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitGroups(Collections.singleton(new GroupPrincipal("Everyone")))
+ .setInheritFrom(shareAclDocId)
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES).build();
+ adaptor.init(context);
+ MockRequest request = new MockRequest(delegate.newDocId(rootPath));
+ MockResponse response = new MockResponse();
+ adaptor.getDocContent(request, response);
+ assertEquals(expectedAcl, response.acl);
+ }
+
+ @Test
+ public void testGetDocContentEmptyAcl() throws Exception {
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setInheritFrom(rootDocId, "childFilesAcl")
+ .setInheritanceType(InheritanceType.LEAF_NODE).build();
+ testFileAcl(MockFile.EMPTY_ACLVIEW, null, expectedAcl);
+ }
+
+ @Test
+ public void testGetDocContentDirectAcl() throws Exception {
+ AclFileAttributeView aclView = new AclView((user("joe")
+ .type(ALLOW).perms(GENERIC_READ).build()));
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitUsers(Collections.singleton(new UserPrincipal("joe")))
+ .setInheritFrom(rootDocId, "childFilesAcl")
+ .setInheritanceType(InheritanceType.LEAF_NODE).build();
+ testFileAcl(aclView, null, expectedAcl);
+ }
+
+ @Test
+ public void testGetDocContentNoInheritAcl() throws Exception {
+ AclFileAttributeView aclView = new AclView((user("joe")
+ .type(ALLOW).perms(GENERIC_READ).build()));
+ // Should inherit from the share, not the parent.
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitUsers(Collections.singleton(new UserPrincipal("joe")))
+ .setInheritFrom(shareAclDocId)
+ .setInheritanceType(InheritanceType.LEAF_NODE).build();
+ testFileAcl(aclView, MockFile.EMPTY_ACLVIEW, expectedAcl);
+ }
+
+ private void testFileAcl(AclFileAttributeView aclView,
+ AclFileAttributeView inheritAclView, Acl expectedAcl) throws Exception {
+ String fname = "acltest";
+ root.addChildren(new MockFile(fname).setAclView(aclView)
+ .setInheritedAclView(inheritAclView));
+ adaptor.init(context);
+ MockResponse response = new MockResponse();
+ adaptor.getDocContent(new MockRequest(getDocId(fname)), response);
+ assertEquals(expectedAcl, response.acl);
+ }
+
+ /** Test that LastAccessTime is restored after reading the file. */
+ @Test
+ public void testPreserveFileLastAccessTime() throws Exception {
+ testPreserveFileLastAccessTime(new MockFile("test") {
+ @Override
+ InputStream newInputStream() throws IOException {
+ setLastAccessTime(FileTime.fromMillis(
+ getLastAccessTime().toMillis() + 1000));
+ return super.newInputStream();
+ }
+ });
+ }
+
+ /** Test LastAccessTime is restored even if exception opening the file. */
+ @Test
+ public void testPreserveFileLastAccessTimeException1() throws Exception {
+ thrown.expect(IOException.class);
+ testPreserveFileLastAccessTime(new MockFile("test") {
+ @Override
+ InputStream newInputStream() throws IOException {
+ setLastAccessTime(FileTime.fromMillis(
+ getLastAccessTime().toMillis() + 1000));
+ throw new IOException("newInputStream");
+ }
+ });
+ }
+
+ /** Test LastAccessTime is restored even if exception reading the file. */
+ @Test
+ public void testPreserveFileLastAccessTimeException2() throws Exception {
+ thrown.expect(IOException.class);
+ testPreserveFileLastAccessTime(new MockFile("test") {
+ @Override
+ InputStream newInputStream() throws IOException {
+ setLastAccessTime(FileTime.fromMillis(
+ getLastAccessTime().toMillis() + 1000));
+ return new FilterInputStream(super.newInputStream()) {
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ throw new IOException("read");
+ }
+ };
+ }
+ });
+ }
+
+ /** Test LastAccessTime is restored even if exception closing the file. */
+ @Test
+ public void testPreserveFileLastAccessTimeException3() throws Exception {
+ thrown.expect(IOException.class);
+ testPreserveFileLastAccessTime(new MockFile("test") {
+ @Override
+ InputStream newInputStream() throws IOException {
+ setLastAccessTime(FileTime.fromMillis(
+ getLastAccessTime().toMillis() + 1000));
+ return new FilterInputStream(super.newInputStream()) {
+ @Override
+ public void close() throws IOException {
+ throw new IOException("close");
+ }
+ };
+ }
+ });
+ }
+
+ /** Test that failure to restore LastAccessTime is not fatal. */
+ @Test
+ public void testPreserveFileLastAccessTimeException4() throws Exception {
+ testNoPreserveFileLastAccessTime(new MockFile("test") {
+ @Override
+ InputStream newInputStream() throws IOException {
+ setLastAccessTime(FileTime.fromMillis(
+ getLastAccessTime().toMillis() + 1000));
+ return super.newInputStream();
+ }
+ @Override
+ MockFile setLastAccessTime(FileTime accessTime) throws IOException {
+ if (MockFile.DEFAULT_FILETIME.equals(getLastAccessTime())) {
+ // Let the above setting from newInputStream go through.
+ return super.setLastAccessTime(accessTime);
+ } else {
+ // But fail the attempt to restore from FsAdaptor.
+ throw new IOException("Restore LastAccessTime");
+ }
+ }
+ });
+ }
+
+ private void testPreserveFileLastAccessTime(MockFile file) throws Exception {
+ testFileLastAccessTime(file, true);
+ }
+
+ private void testNoPreserveFileLastAccessTime(MockFile file)
+ throws Exception {
+ testFileLastAccessTime(file, false);
+ }
+
+ private void testFileLastAccessTime(MockFile file, boolean isPreserved)
+ throws Exception {
+ String contents = "Test contents";
+ file.setFileContents(contents);
+ root.addChildren(file);
+ adaptor.init(context);
+ MockResponse response = new MockResponse();
+ FileTime lastAccess = file.getLastAccessTime();
+ adaptor.getDocContent(new MockRequest(getDocId(file.getName())), response);
+ // Verify we indeed accessed the file
+ assertEquals(contents, response.content.toString("UTF-8"));
+ if (isPreserved) {
+ assertEquals(lastAccess, file.getLastAccessTime());
+ } else {
+ assertFalse(lastAccess.equals(file.getLastAccessTime()));
+ }
+ }
+
+ @Test
+ public void testGetDocContentRoot() throws Exception {
+ testGetDocContentDirectory(rootPath, rootPath.toString());
+ // ACLs checked in other tests.
+ }
+
+ @Test
+ public void testGetDocContentDirectory() throws Exception {
+ String fname = "test.dir";
+ root.addChildren(new MockFile(fname, true));
+ testGetDocContentDirectory(getPath(fname), fname);
+ // ACLs checked in other tests.
+ }
+
+ private void testGetDocContentDirectory(Path path, String label)
+ throws Exception {
+ MockFile dir = delegate.getFile(path);
+ FileTime modifyTime = dir.getLastModifiedTime();
+ Date modifyDate = new Date(modifyTime.toMillis());
+ dir.addChildren(new MockFile("test.txt"), new MockFile("subdir", true));
+ adaptor.init(context);
+ MockRequest request = new MockRequest(delegate.newDocId(path));
+ MockResponse response = new MockResponse();
+ adaptor.getDocContent(request, response);
+ assertFalse(response.notFound);
+ assertEquals(modifyDate, response.lastModified);
+ assertEquals(path.toUri(), response.displayUrl);
+ assertEquals("text/html; charset=UTF-8", response.contentType);
+ String expectedContent = "<!DOCTYPE html>\n<html><head><title>Folder "
+ + label + "</title></head><body><h1>Folder " + label + "</h1>"
+ + "<li><a href=\"subdir/\">subdir</a></li>"
+ + "<li><a href=\"test.txt\">test.txt</a></li></body></html>";
+ assertEquals(expectedContent, response.content.toString("UTF-8"));
+ assertNotNull(response.metadata.get("Creation Time"));
+ }
+
+ @Test
+ public void testGetDocContentDefaultRootAcls() throws Exception {
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitGroups(Collections.singleton(new GroupPrincipal("Everyone")))
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(shareAclDocId).build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", expectedAcl,
+ "allFilesAcl", expectedAcl,
+ "childFoldersAcl", expectedAcl,
+ "childFilesAcl", expectedAcl);
+ testGetDocContentAcls(rootPath, expectedAcl, expectedResources);
+ }
+
+ @Test
+ public void testGetDocContentInheritOnlyRootAcls() throws Exception {
+ AclFileAttributeView inheritOnlyAclView = new AclView(
+ user("Longfellow Deeds").type(ALLOW).perms(GENERIC_READ)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT, INHERIT_ONLY),
+ group("Administrators").type(ALLOW).perms(GENERIC_ALL)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ root.setAclView(inheritOnlyAclView);
+
+ // The root ACL should only include Administrators, not Mr. Deeds.
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitGroups(Collections.singleton(
+ new GroupPrincipal("Administrators")))
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(shareAclDocId).build();
+ // But the childrens' inherited ACLs should include Mr. Deeds
+ Acl expectedInheritableAcl = new Acl.Builder(expectedAcl)
+ .setPermitUsers(Collections.singleton(
+ new UserPrincipal("Longfellow Deeds")))
+ .build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", expectedInheritableAcl,
+ "allFilesAcl", expectedInheritableAcl,
+ "childFoldersAcl", expectedInheritableAcl,
+ "childFilesAcl", expectedInheritableAcl);
+ testGetDocContentAcls(rootPath, expectedAcl, expectedResources);
+ }
+
+ @Test
+ public void testGetDocContentNoPropagateRootAcls() throws Exception {
+ AclFileAttributeView noPropagateAclView = new AclView(
+ user("Barren von Dink").type(ALLOW).perms(GENERIC_READ)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT, NO_PROPAGATE_INHERIT),
+ group("Administrators").type(ALLOW).perms(GENERIC_ALL)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ root.setAclView(noPropagateAclView);
+
+ // The root ACL should include both Administrators and the Barren.
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitUsers(Collections.singleton(
+ new UserPrincipal("Barren von Dink")))
+ .setPermitGroups(Collections.singleton(
+ new GroupPrincipal("Administrators")))
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(shareAclDocId).build();
+ // The direct childrens' inherited ACLs should include both the
+ // Administrators and the Barren, but grandchildren should not
+ // inherit the Barren's NO_PROPAGATE permission.
+ Acl expectedNonChildAcl = new Acl.Builder(expectedAcl)
+ .setPermitUsers(Collections.<UserPrincipal>emptySet()).build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", expectedNonChildAcl,
+ "allFilesAcl", expectedNonChildAcl,
+ "childFoldersAcl", expectedAcl,
+ "childFilesAcl", expectedAcl);
+ testGetDocContentAcls(rootPath, expectedAcl, expectedResources);
+ }
+
+ @Test
+ public void testGetDocContentFilesOnlyRootAcls() throws Exception {
+ AclFileAttributeView noPropagateAclView = new AclView(
+ user("For Your Files Only").type(ALLOW).perms(GENERIC_READ)
+ .flags(FILE_INHERIT),
+ group("Administrators").type(ALLOW).perms(GENERIC_ALL)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ root.setAclView(noPropagateAclView);
+
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitUsers(Collections.singleton(
+ new UserPrincipal("For Your Files Only")))
+ .setPermitGroups(Collections.singleton(
+ new GroupPrincipal("Administrators")))
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(shareAclDocId).build();
+ // Folders shouldn't include the file-only permissions.
+ Acl expectedFolderAcl = new Acl.Builder(expectedAcl)
+ .setPermitUsers(Collections.<UserPrincipal>emptySet()).build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", expectedFolderAcl,
+ "allFilesAcl", expectedAcl,
+ "childFoldersAcl", expectedFolderAcl,
+ "childFilesAcl", expectedAcl);
+ testGetDocContentAcls(rootPath, expectedAcl, expectedResources);
+ }
+
+ @Test
+ public void testGetDocContentFoldersOnlyRootAcls() throws Exception {
+ AclFileAttributeView noPropagateAclView = new AclView(
+ user("Fluff 'n Folder").type(ALLOW).perms(GENERIC_READ)
+ .flags(DIRECTORY_INHERIT),
+ group("Administrators").type(ALLOW).perms(GENERIC_ALL)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ root.setAclView(noPropagateAclView);
+
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitUsers(Collections.singleton(
+ new UserPrincipal("Fluff 'n Folder")))
+ .setPermitGroups(Collections.singleton(
+ new GroupPrincipal("Administrators")))
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(shareAclDocId).build();
+ // Files shouldn't include the folder-only permissions.
+ Acl expectedFilesAcl = new Acl.Builder(expectedAcl)
+ .setPermitUsers(Collections.<UserPrincipal>emptySet()).build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", expectedAcl,
+ "allFilesAcl", expectedFilesAcl,
+ "childFoldersAcl", expectedAcl,
+ "childFilesAcl", expectedFilesAcl);
+ testGetDocContentAcls(rootPath, expectedAcl, expectedResources);
+ }
+
+ @Test
+ public void testGetDocContentDefaultDirectoryAcls() throws Exception {
+ String name = "subdir";
+ root.addChildren(new MockFile(name, true));
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(rootDocId, "childFoldersAcl").build();
+ Acl expectedFoldersAcl = new Acl.Builder(expectedAcl)
+ .setInheritFrom(rootDocId, "allFoldersAcl").build();
+ Acl expectedFilesAcl = new Acl.Builder(expectedAcl)
+ .setInheritFrom(rootDocId, "allFilesAcl").build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", expectedFoldersAcl,
+ "allFilesAcl", expectedFilesAcl,
+ "childFoldersAcl", expectedFoldersAcl,
+ "childFilesAcl", expectedFilesAcl);
+ testGetDocContentAcls(getPath(name), expectedAcl, expectedResources);
+ }
+
+ @Test
+ public void testGetDocContentNoInheritDirectoryAcls() throws Exception {
+ String name = "subdir";
+ AclFileAttributeView orphanAclView = new AclView(user("Annie").type(ALLOW)
+ .perms(GENERIC_READ).flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ root.addChildren(new MockFile(name, true).setAclView(orphanAclView)
+ .setInheritedAclView(MockFile.EMPTY_ACLVIEW));
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitUsers(Collections.singleton(new UserPrincipal("Annie")))
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(shareAclDocId).build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", expectedAcl,
+ "allFilesAcl", expectedAcl,
+ "childFoldersAcl", expectedAcl,
+ "childFilesAcl", expectedAcl);
+ testGetDocContentAcls(getPath(name), expectedAcl, expectedResources);
+ }
+
+ @Test
+ public void testGetDocContentInheritOnlyDirectoryAcls() throws Exception {
+ String name = "subdir";
+ AclFileAttributeView inheritOnlyAclView = new AclView(
+ user("Longfellow Deeds").type(ALLOW).perms(GENERIC_READ)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT, INHERIT_ONLY),
+ group("Administrators").type(ALLOW).perms(GENERIC_ALL)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ root.addChildren(new MockFile(name, true).setAclView(inheritOnlyAclView));
+
+ // The root ACL should only include Administrators, not Mr. Deeds.
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitGroups(Collections.singleton(
+ new GroupPrincipal("Administrators")))
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(rootDocId, "childFoldersAcl").build();
+ // But the childrens' inherited ACLs should include Mr. Deeds
+ Acl expectedFoldersAcl = new Acl.Builder(expectedAcl)
+ .setPermitUsers(Collections.singleton(
+ new UserPrincipal("Longfellow Deeds")))
+ .setInheritFrom(rootDocId, "allFoldersAcl").build();
+ Acl expectedFilesAcl = new Acl.Builder(expectedFoldersAcl)
+ .setInheritFrom(rootDocId, "allFilesAcl").build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", expectedFoldersAcl,
+ "allFilesAcl", expectedFilesAcl,
+ "childFoldersAcl", expectedFoldersAcl,
+ "childFilesAcl", expectedFilesAcl);
+ testGetDocContentAcls(getPath(name), expectedAcl, expectedResources);
+ }
+
+ @Test
+ public void testGetDocContentNoPropagateDirectoryAcls() throws Exception {
+ String name = "subdir";
+ AclFileAttributeView noPropagateAclView = new AclView(
+ user("Barren von Dink").type(ALLOW).perms(GENERIC_READ)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT, NO_PROPAGATE_INHERIT),
+ group("Administrators").type(ALLOW).perms(GENERIC_ALL)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ root.addChildren(new MockFile(name, true).setAclView(noPropagateAclView));
+
+ // The root ACL should include both Administrators and the Barren.
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitUsers(Collections.singleton(
+ new UserPrincipal("Barren von Dink")))
+ .setPermitGroups(Collections.singleton(
+ new GroupPrincipal("Administrators")))
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(rootDocId, "childFoldersAcl").build();
+ // The direct childrens' inherited ACLs should include both the
+ // Administrators and the Barren, but grandchildren should not
+ // inherit the Barren's NO_PROPAGATE permission.
+ Acl expectedNonChildAcl = new Acl.Builder(expectedAcl)
+ .setPermitUsers(Collections.<UserPrincipal>emptySet()).build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", new Acl.Builder(expectedNonChildAcl)
+ .setInheritFrom(rootDocId, "allFoldersAcl").build(),
+ "allFilesAcl", new Acl.Builder(expectedNonChildAcl)
+ .setInheritFrom(rootDocId, "allFilesAcl").build(),
+ "childFoldersAcl", new Acl.Builder(expectedAcl)
+ .setInheritFrom(rootDocId, "allFoldersAcl").build(),
+ "childFilesAcl", new Acl.Builder(expectedAcl)
+ .setInheritFrom(rootDocId, "allFilesAcl").build());
+ testGetDocContentAcls(getPath(name), expectedAcl, expectedResources);
+ }
+
+ @Test
+ public void testGetDocContentFilesOnlyDirectoryAcls() throws Exception {
+ String name = "subdir";
+ AclFileAttributeView filesOnlyAclView = new AclView(
+ user("For Your Files Only").type(ALLOW).perms(GENERIC_READ)
+ .flags(FILE_INHERIT),
+ group("Administrators").type(ALLOW).perms(GENERIC_ALL)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ root.addChildren(new MockFile(name, true).setAclView(filesOnlyAclView));
+
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitUsers(Collections.singleton(
+ new UserPrincipal("For Your Files Only")))
+ .setPermitGroups(Collections.singleton(
+ new GroupPrincipal("Administrators")))
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(rootDocId, "childFoldersAcl").build();
+ // Folders shouldn't include the file-only permissions.
+ Acl expectedFolderAcl = new Acl.Builder(expectedAcl)
+ .setPermitUsers(Collections.<UserPrincipal>emptySet()).build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", new Acl.Builder(expectedFolderAcl)
+ .setInheritFrom(rootDocId, "allFoldersAcl").build(),
+ "allFilesAcl", new Acl.Builder(expectedAcl)
+ .setInheritFrom(rootDocId, "allFilesAcl").build(),
+ "childFoldersAcl", new Acl.Builder(expectedFolderAcl)
+ .setInheritFrom(rootDocId, "allFoldersAcl").build(),
+ "childFilesAcl", new Acl.Builder(expectedAcl)
+ .setInheritFrom(rootDocId, "allFilesAcl").build());
+ testGetDocContentAcls(getPath(name), expectedAcl, expectedResources);
+ }
+
+ @Test
+ public void testGetDocContentFoldersOnlyDirectoryAcls() throws Exception {
+ String name = "subdir";
+ AclFileAttributeView foldersOnlyAclView = new AclView(
+ user("Fluff 'n Folder").type(ALLOW).perms(GENERIC_READ)
+ .flags(DIRECTORY_INHERIT),
+ group("Administrators").type(ALLOW).perms(GENERIC_ALL)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ root.addChildren(new MockFile(name, true).setAclView(foldersOnlyAclView));
+
+ Acl expectedAcl = new Acl.Builder().setEverythingCaseInsensitive()
+ .setPermitUsers(Collections.singleton(
+ new UserPrincipal("Fluff 'n Folder")))
+ .setPermitGroups(Collections.singleton(
+ new GroupPrincipal("Administrators")))
+ .setInheritanceType(InheritanceType.CHILD_OVERRIDES)
+ .setInheritFrom(rootDocId, "childFoldersAcl").build();
+ // Files shouldn't include the folder-only permissions.
+ Acl expectedFilesAcl = new Acl.Builder(expectedAcl)
+ .setPermitUsers(Collections.<UserPrincipal>emptySet()).build();
+ Map<String, Acl> expectedResources = ImmutableMap.of(
+ "allFoldersAcl", new Acl.Builder(expectedAcl)
+ .setInheritFrom(rootDocId, "allFoldersAcl").build(),
+ "allFilesAcl", new Acl.Builder(expectedFilesAcl)
+ .setInheritFrom(rootDocId, "allFilesAcl").build(),
+ "childFoldersAcl", new Acl.Builder(expectedAcl)
+ .setInheritFrom(rootDocId, "allFoldersAcl").build(),
+ "childFilesAcl", new Acl.Builder(expectedFilesAcl)
+ .setInheritFrom(rootDocId, "allFilesAcl").build());
+ testGetDocContentAcls(getPath(name), expectedAcl, expectedResources);
+ }
+
+ private void testGetDocContentAcls(Path path, Acl expectedAcl,
+ Map<String, Acl> expectedAclResources) throws Exception {
+ adaptor.init(context);
+ MockRequest request = new MockRequest(delegate.newDocId(path));
+ MockResponse response = new MockResponse();
+ adaptor.getDocContent(request, response);
+ assertEquals(expectedAcl, response.acl);
+ assertEquals(expectedAclResources, response.namedResources);
+ }
+
+ @Test
+ public void testMonitorZeroFeedSize() throws Exception {
+ config.overrideKey("feed.maxUrls", "0");
+ thrown.expect(IllegalArgumentException.class);
+ adaptor.init(context);
+ }
+
+ @Test
+ public void testMonitorZeroLatency() throws Exception {
+ config.overrideKey("adaptor.incrementalPollPeriodSecs", "0");
+ adaptor.init(context);
+ }
+
+
+ @Test
+ public void testMonitorSubZeroLatency() throws Exception {
+ config.overrideKey("adaptor.incrementalPollPeriodSecs", "-1");
+ thrown.expect(IllegalArgumentException.class);
+ adaptor.init(context);
+ }
+
+ @Test
+ public void testMonitorUnsupportedPath() throws Exception {
+ root.addChildren(new MockFile("unsuppored").setIsRegularFile(false));
+ adaptor.init(context);
+ BlockingQueue<Path> queue = adaptor.getFsMonitorQueue();
+ queue.add(getPath("unsupported"));
+ Thread.sleep(100L); // Allow FsMonitor to drain the queue.
+ // Verify it has been processed, but not been fed.
+ assertEquals(0, queue.size());
+ assertEquals(0, pusher.getRecords().size());
+ assertEquals(0, pusher.getNamedResources().size());
+ }
+
+ @Test
+ public void testMonitorOneFile() throws Exception {
+ String name = "test";
+ root.addChildren(new MockFile(name));
+ adaptor.init(context);
+ testMonitor(name);
+ }
+
+ @Test
+ public void testMonitorOneDirectory() throws Exception {
+ String name = "test.dir";
+ root.addChildren(new MockFile(name, true));
+ adaptor.init(context);
+ testMonitor(name);
+ }
+
+ @Test
+ public void testMonitorMultipleItems() throws Exception {
+ String dirname = "subdir";
+ String fname = "test.txt";
+ root.addChildren(new MockFile(dirname, true), new MockFile(fname));
+ adaptor.init(context);
+ testMonitor(dirname, fname);
+ }
+
+ @Test
+ public void testMonitorMultipleBatches() throws Exception {
+ String dirname = "subdir";
+ String fname = "test.txt";
+ root.addChildren(new MockFile(dirname, true), new MockFile(fname));
+ adaptor.init(context);
+ testMonitor(dirname);
+ // Make sure the previous batch does not get fed again.
+ pusher.reset();
+ testMonitor(fname);
+ }
+
+ private void testMonitor(String... names) throws Exception {
+ BlockingQueue<Path> queue = adaptor.getFsMonitorQueue();
+ for (String name : names) {
+ queue.add(getPath(name));
+ }
+ Thread.sleep(100L); // Allow FsMonitor to drain the queue.
+ assertEquals(0, queue.size());
+ assertEquals(0, pusher.getNamedResources().size());
+ Set<Record> expected = Sets.newHashSet();
+ for (String name : names) {
+ expected.add(new Record.Builder(getDocId(name))
+ .setCrawlImmediately(true).build());
+ }
+ assertEquals(expected, Sets.newHashSet(pusher.getRecords()));
}
@Test
public void testIncrementalShareAcls() throws Exception {
- FsAdaptor adaptor = new FsAdaptor();
- AccumulatingDocIdPusher pusher = new AccumulatingDocIdPusher();
+ adaptor.init(context);
+
+ AclFileAttributeView aclView = new AclView(
+ user("joe").type(ALLOW).perms(GENERIC_READ)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ Acl acl =
+ newBuilder(aclView).getAcl().setInheritanceType(
+ InheritanceType.AND_BOTH_PERMIT).build();
+ root.setShareAclView(aclView);
+
adaptor.getDocIds(pusher);
+ List<Map<DocId, Acl>> namedResources = pusher.getNamedResources();
+ assertEquals(1, namedResources.size());
+ assertEquals(acl, namedResources.get(0).get(shareAclDocId));
+
+ // Clear the pusher and call getModifiedDocIds. Nothing should be
+ // pushed since the share Acl has not changed.
+ pusher.reset();
+ adaptor.getModifiedDocIds(pusher);
+ namedResources = pusher.getNamedResources();
+ assertEquals(0, namedResources.size());
+
+ // Change the share Acl and confirm that that the share Acl is pushed.
+ aclView = new AclView(user("mary").type(ALLOW).perms(GENERIC_READ)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ acl = newBuilder(aclView).getAcl().setInheritanceType(
+ InheritanceType.AND_BOTH_PERMIT).build();
+ root.setShareAclView(aclView);
adaptor.getModifiedDocIds(pusher);
- assertEquals("share", adaptor.getPathName(Paths.get("\\\\host/share/")));
- assertEquals("folder2",
- adaptor.getPathName(Paths.get("C:/folder1/folder2/")));
+ namedResources = pusher.getNamedResources();
+ assertEquals(1, namedResources.size());
+ assertEquals(acl, namedResources.get(0).get(shareAclDocId));
+ }
+
+ /** Returns an AclBuilder for the AclFileAttributeView. */
+ private AclBuilder newBuilder(AclFileAttributeView aclView) {
+ return new AclBuilder(Paths.get("foo", "bar"),
+ aclView, windowsAccounts, builtinPrefix, namespace);
}
}
diff --git a/test/com/google/enterprise/adaptor/fs/MockAdaptorContext.java b/test/com/google/enterprise/adaptor/fs/MockAdaptorContext.java
new file mode 100644
index 0000000..5749306
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/MockAdaptorContext.java
@@ -0,0 +1,110 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// 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.enterprise.adaptor.fs;
+
+import com.google.enterprise.adaptor.AdaptorContext;
+import com.google.enterprise.adaptor.AuthnAuthority;
+import com.google.enterprise.adaptor.AuthzAuthority;
+import com.google.enterprise.adaptor.Config;
+import com.google.enterprise.adaptor.DocIdEncoder;
+import com.google.enterprise.adaptor.DocIdPusher;
+import com.google.enterprise.adaptor.ExceptionHandler;
+import com.google.enterprise.adaptor.PollingIncrementalLister;
+import com.google.enterprise.adaptor.SensitiveValueDecoder;
+import com.google.enterprise.adaptor.Session;
+import com.google.enterprise.adaptor.StatusSource;
+
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+/**
+ * Mock of {@link AdaptorContext}.
+ */
+class MockAdaptorContext implements AdaptorContext {
+ private final Config config = new Config();
+ private final DocIdPusher docIdPusher = new AccumulatingDocIdPusher();
+ private final DocIdEncoder docIdEncoder = new MockDocIdCodec();
+
+ @Override
+ public Config getConfig() {
+ return config;
+ }
+
+ @Override
+ public DocIdPusher getDocIdPusher() {
+ return docIdPusher;
+ }
+
+ @Override
+ public DocIdEncoder getDocIdEncoder() {
+ return docIdEncoder;
+ }
+
+ @Override
+ public void addStatusSource(StatusSource source) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setGetDocIdsFullErrorHandler(ExceptionHandler handler) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ExceptionHandler getGetDocIdsFullErrorHandler() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setGetDocIdsIncrementalErrorHandler(
+ ExceptionHandler handler) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ExceptionHandler getGetDocIdsIncrementalErrorHandler() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SensitiveValueDecoder getSensitiveValueDecoder() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public HttpContext createHttpContext(String path, HttpHandler handler) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Session getUserSession(HttpExchange ex, boolean create) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setPollingIncrementalLister(PollingIncrementalLister lister) {
+ }
+
+ @Override
+ public void setAuthnAuthority(AuthnAuthority authnAuthority) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setAuthzAuthority(AuthzAuthority authzAuthority) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/MockDocIdCodec.java b/test/com/google/enterprise/adaptor/fs/MockDocIdCodec.java
new file mode 100644
index 0000000..e669083
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/MockDocIdCodec.java
@@ -0,0 +1,37 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// 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.enterprise.adaptor.fs;
+
+import com.google.enterprise.adaptor.DocId;
+import com.google.enterprise.adaptor.DocIdEncoder;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Mock of {@link DocIdCodec}.
+ */
+class MockDocIdCodec implements DocIdEncoder {
+ @Override
+ public URI encodeDocId(DocId docId) {
+ try {
+ String id = docId.getUniqueId();
+ return new URI("http", "localhost",
+ id.startsWith("/") ? id : "/" + id, null);
+ } catch (URISyntaxException ex) {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/MockFile.java b/test/com/google/enterprise/adaptor/fs/MockFile.java
new file mode 100644
index 0000000..eb97926
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/MockFile.java
@@ -0,0 +1,399 @@
+// Copyright 2009 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.enterprise.adaptor.fs;
+
+import static com.google.enterprise.adaptor.fs.AclView.user;
+import static com.google.enterprise.adaptor.fs.AclView.group;
+
+import static java.nio.file.attribute.AclEntryFlag.*;
+import static java.nio.file.attribute.AclEntryPermission.*;
+import static java.nio.file.attribute.AclEntryType.*;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.attribute.AclFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.DirectoryStream;
+import java.nio.file.NotDirectoryException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+class MockFile {
+ static final String SEPARATOR = "/";
+ static final FileTime DEFAULT_FILETIME = FileTime.fromMillis(10000);
+ static final AclFileAttributeView FULL_ACCESS_ACLVIEW =
+ new AclView(group("Everyone").type(ALLOW)
+ .perms(READ_DATA, READ_ATTRIBUTES, READ_NAMED_ATTRS, READ_ACL)
+ .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+ static final AclFileAttributeView EMPTY_ACLVIEW = new AclView();
+
+ private MockFile parent;
+ private String name;
+ private boolean isHidden = false;
+ private boolean isRegularFile;
+ private boolean isDirectory;
+ private List<MockFile> directoryContents;
+ private Path dfsUncActiveStorageUnc;
+ private AclFileAttributeView dfsShareAclView;
+ private AclFileAttributeView shareAclView;
+ private AclFileAttributeView aclView;
+ private AclFileAttributeView inheritedAclView;
+ private FileTime creationTime = DEFAULT_FILETIME;
+ private FileTime lastModifiedTime = DEFAULT_FILETIME;
+ private FileTime lastAccessTime = DEFAULT_FILETIME;
+ private String contentType;
+ private byte[] fileContents;
+
+ /**
+ * Create a regular file with the specified {@code name}.
+ *
+ * @param name the name of the file
+ */
+ MockFile(String name) {
+ this(name, false);
+ }
+
+ /**
+ * Create a file or directory with the specified {@code name}.
+ *
+ * @param name the name of the file or directory
+ * @param isDirectory true if this is a directory, false if regular file
+ */
+ MockFile(String name, boolean isDirectory) {
+ Preconditions.checkNotNull(name, "name cannot be null");
+ this.name = name;
+ this.isRegularFile = !isDirectory;
+ this.isDirectory = isDirectory;
+ if (isDirectory) {
+ directoryContents = new ArrayList<MockFile>();
+ } else {
+ setFileContents("Contents of " + name);
+ }
+ }
+
+ /**
+ * Add the supplied files/directories as children of this MockFile.
+ * This automatically sets isDirectory and resets isRegularFile on this
+ * MockFile, and registers this file as the parent of all the children.
+ */
+ MockFile addChildren(MockFile... children) {
+ Preconditions.checkState(isDirectory, "not a directory %s", getPath());
+ for (MockFile child : children) {
+ child.parent = this;
+ directoryContents.add(child);
+ }
+ return this;
+ }
+
+ /**
+ * Returns the child of the given name.
+ */
+ MockFile getChild(String name) throws FileNotFoundException {
+ Preconditions.checkNotNull(name, "name cannot be null");
+ Preconditions.checkState(isDirectory, "not a directory %s", getPath());
+ Iterator<MockFile> it = directoryContents.iterator();
+ while (it.hasNext()) {
+ MockFile f = it.next();
+ if (f.name.equals(name)) {
+ return f;
+ }
+ }
+ throw new FileNotFoundException(
+ "no such file: " + getPath() + SEPARATOR + name);
+ }
+
+ /**
+ * Return the path to this file or directory.
+ */
+ String getPath() {
+ if (parent == null) {
+ return name;
+ } else {
+ String parentPath = parent.getPath();
+ return (parentPath.endsWith(SEPARATOR))
+ ? parentPath + name : parentPath + SEPARATOR + name;
+ }
+ }
+
+ MockFile setName(String name) {
+ Preconditions.checkNotNull(name, "name cannot be null");
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Return the name to this file or directory.
+ */
+ String getName() {
+ return name;
+ }
+
+ /**
+ * Return the parent directory of this file or directory,
+ * or null if there is no parent.
+ */
+ MockFile getParent() {
+ return parent;
+ }
+
+ boolean isDirectory() throws IOException {
+ return isDirectory;
+ }
+
+ /** If false, maybe a directory, pipe, device, broken link, hidden, etc. */
+ MockFile setIsRegularFile(boolean isRegularFile) {
+ this.isRegularFile = isRegularFile;
+ return this;
+ }
+
+ boolean isRegularFile() throws IOException {
+ return isRegularFile;
+ }
+
+ MockFile setIsHidden(boolean isHidden) {
+ this.isHidden = isHidden;
+ return this;
+ }
+
+ boolean isHidden() throws IOException {
+ return isHidden;
+ }
+
+ MockFile setCreationTime(FileTime creationTime) {
+ Preconditions.checkNotNull(creationTime, "time cannot be null");
+ this.creationTime = creationTime;
+ return this;
+ }
+
+ FileTime getCreationTime() throws IOException {
+ return creationTime;
+ }
+
+ MockFile setLastModifiedTime(FileTime lastModifiedTime) {
+ Preconditions.checkNotNull(lastModifiedTime, "time cannot be null");
+ this.lastModifiedTime = lastModifiedTime;
+ return this;
+ }
+
+ FileTime getLastModifiedTime() throws IOException {
+ return lastModifiedTime;
+ }
+
+ /** Note that the adaptor calls this setter. */
+ MockFile setLastAccessTime(FileTime lastAccessTime) throws IOException {
+ Preconditions.checkNotNull(lastAccessTime, "time cannot be null");
+ this.lastAccessTime = lastAccessTime;
+ return this;
+ }
+
+ FileTime getLastAccessTime() throws IOException {
+ return lastAccessTime;
+ }
+
+ BasicFileAttributes readBasicAttributes() throws IOException {
+ return new MockBasicFileAttributes();
+ }
+
+ MockFile setDfsUncActiveStorageUnc(Path unc) {
+ this.dfsUncActiveStorageUnc = unc;
+ return this;
+ }
+
+ Path getDfsUncActiveStorageUnc() throws IOException {
+ return dfsUncActiveStorageUnc;
+ }
+
+ MockFile setDfsShareAclView(AclFileAttributeView aclView) {
+ this.dfsShareAclView = aclView;
+ return this;
+ }
+
+ AclFileAttributeView getDfsShareAclView() throws IOException {
+ return dfsShareAclView;
+ }
+
+ MockFile setShareAclView(AclFileAttributeView aclView) {
+ this.shareAclView = aclView;
+ return this;
+ }
+
+ AclFileAttributeView getShareAclView() throws IOException {
+ return (shareAclView == null) ? FULL_ACCESS_ACLVIEW : shareAclView;
+ }
+
+ MockFile setAclView(AclFileAttributeView aclView) {
+ this.aclView = aclView;
+ return this;
+ }
+
+ AclFileAttributeView getAclView() throws IOException {
+ if (aclView == null) {
+ return (parent == null) ? FULL_ACCESS_ACLVIEW : EMPTY_ACLVIEW;
+ } else {
+ return aclView;
+ }
+ }
+
+ MockFile setInheritedAclView(AclFileAttributeView aclView) {
+ this.inheritedAclView = aclView;
+ return this;
+ }
+
+ AclFileAttributeView getInheritedAclView() throws IOException {
+ if (inheritedAclView == null) {
+ if (parent == null) {
+ // root has no inherited ACL
+ return EMPTY_ACLVIEW;
+ } else if (parent.parent == null) {
+ // root's children inherit its ACL
+ return parent.getAclView();
+ } else {
+ // all other children inherit from their parent
+ return parent.getInheritedAclView();
+ }
+ } else {
+ return inheritedAclView;
+ }
+ }
+
+ MockFile setContentType(String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+
+ String getContentType() throws IOException {
+ return isRegularFile ? contentType : null;
+ }
+
+ MockFile setFileContents(String fileContents) {
+ Preconditions.checkNotNull(fileContents, "fileContents cannot be null");
+ setFileContents(fileContents.getBytes(Charsets.UTF_8));
+ if (contentType == null) {
+ contentType = "text/plain";
+ }
+ return this;
+ }
+
+ MockFile setFileContents(byte[] fileContents) {
+ Preconditions.checkState(isRegularFile, "not a regular file %s", getPath());
+ Preconditions.checkNotNull(fileContents, "fileContents cannot be null");
+ this.fileContents = fileContents;
+ return this;
+ }
+
+ InputStream newInputStream() throws IOException {
+ Preconditions.checkState(isRegularFile, "not a regular file %s", getPath());
+ return new ByteArrayInputStream(fileContents);
+ }
+
+ DirectoryStream<Path> newDirectoryStream() throws IOException {
+ if (!isDirectory) {
+ throw new NotDirectoryException("not a directory " + getPath());
+ }
+ return new MockDirectoryStream(directoryContents);
+ }
+
+ @Override
+ public String toString() {
+ return getPath();
+ }
+
+ private class MockBasicFileAttributes implements BasicFileAttributes {
+
+ @Override
+ public Object fileKey() {
+ return MockFile.this;
+ }
+
+ @Override
+ public FileTime creationTime() {
+ return creationTime;
+ }
+
+ @Override
+ public FileTime lastAccessTime() {
+ return lastAccessTime;
+ }
+
+ @Override
+ public FileTime lastModifiedTime() {
+ return lastModifiedTime;
+ }
+
+ @Override
+ public boolean isDirectory() {
+ return isDirectory;
+ }
+
+ @Override
+ public boolean isRegularFile() {
+ return isRegularFile;
+ }
+
+ @Override
+ public boolean isOther() {
+ return !(isDirectory || isRegularFile);
+ }
+
+ @Override
+ public boolean isSymbolicLink() {
+ return false;
+ }
+
+ @Override
+ public long size() {
+ if (isRegularFile && fileContents != null) {
+ return fileContents.length;
+ } else {
+ return 0L;
+ }
+ }
+ }
+
+ private class MockDirectoryStream implements DirectoryStream<Path> {
+ private Iterator<Path> iterator;
+
+ MockDirectoryStream(List<MockFile> files) {
+ ArrayList<Path> paths = new ArrayList<Path>();
+ for (MockFile file : files) {
+ paths.add(Paths.get(file.getPath()));
+ }
+ Collections.sort(paths);
+ iterator = paths.iterator();
+ }
+
+ @Override
+ public Iterator<Path> iterator() {
+ Preconditions.checkState(iterator != null,
+ "multiple attempts to get iterator");
+ Iterator<Path> rtn = iterator;
+ iterator = null;
+ return rtn;
+ }
+
+ @Override
+ public void close() {}
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/MockFileDelegate.java b/test/com/google/enterprise/adaptor/fs/MockFileDelegate.java
new file mode 100644
index 0000000..9a4c6f8
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/MockFileDelegate.java
@@ -0,0 +1,178 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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.enterprise.adaptor.fs;
+
+import com.google.common.base.Preconditions;
+import com.google.enterprise.adaptor.DocId;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.attribute.AclFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.BlockingQueue;
+import java.util.Iterator;
+
+class MockFileDelegate implements FileDelegate {
+
+ private final MockFile root;
+
+ MockFileDelegate(MockFile root) {
+ Preconditions.checkNotNull(root, "root cannot be null");
+ this.root = root;
+ }
+
+ /**
+ * Returns the {@link MockFile} identified by the supplied {@link Path}.
+ * @throws FileNotFoundException if the file is not found.
+ */
+ MockFile getFile(Path doc) throws FileNotFoundException {
+ Preconditions.checkNotNull(doc, "doc cannot be null");
+ MockFile file = root;
+ Iterator<Path> iter = doc.iterator();
+ if (doc.getRoot() != null) {
+ // Using startsWith because Path adds a trailing backslash to
+ // UNC roots. The second check accounts for Windows Path
+ // implementation flipping slashes on Unix paths.
+ if (!(doc.getRoot().toString().startsWith(root.getPath()) ||
+ root.getPath().equals(doc.getRoot().toString().replace('\\', '/')))) {
+ throw new FileNotFoundException("not found: " + doc.toString());
+ }
+ } else if (iter.hasNext()) {
+ if (!(root.getPath().equals(iter.next().toString()))) {
+ throw new FileNotFoundException("not found: " + doc.toString());
+ }
+ }
+ while (iter.hasNext()) {
+ file = file.getChild(iter.next().toString());
+ }
+ return file;
+ }
+
+ @Override
+ public Path getPath(String pathname) throws IOException {
+ Preconditions.checkNotNull(pathname, "pathname cannot be null");
+ return Paths.get(pathname);
+ }
+
+ @Override
+ public boolean isDirectory(Path doc) throws IOException {
+ try {
+ return getFile(doc).isDirectory();
+ } catch (FileNotFoundException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isRegularFile(Path doc) throws IOException {
+ try {
+ return getFile(doc).isRegularFile();
+ } catch (FileNotFoundException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isHidden(Path doc) throws IOException {
+ try {
+ return getFile(doc).isHidden();
+ } catch (FileNotFoundException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public BasicFileAttributes readBasicAttributes(Path doc) throws IOException {
+ return getFile(doc).readBasicAttributes();
+ }
+
+ @Override
+ public void setLastAccessTime(Path doc, FileTime time) throws IOException {
+ getFile(doc).setLastAccessTime(time);
+ }
+
+ @Override
+ public String probeContentType(Path doc) throws IOException {
+ return getFile(doc).getContentType();
+ }
+
+ @Override
+ public InputStream newInputStream(Path doc) throws IOException {
+ return getFile(doc).newInputStream();
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(Path doc) throws IOException {
+ return getFile(doc).newDirectoryStream();
+ }
+
+ @Override
+ public DocId newDocId(Path doc) throws IOException {
+ String id = doc.toString().replace('\\', '/');
+ if (isDirectory(doc) && !id.endsWith("/")) {
+ id += "/";
+ }
+ if (id.startsWith("//")) {
+ // String.replaceFirst uses regular expression string and replacement
+ // so they need to be escaped appropriately. The above String.replace
+ // does NOT use expressions so regex escaping is not needed.
+ id = id.replaceFirst("//", "\\\\\\\\");
+ }
+ return new DocId(id);
+ }
+
+ @Override
+ public AclFileAttributeViews getAclViews(Path doc) throws IOException {
+ MockFile file = getFile(doc);
+ return new AclFileAttributeViews(file.getAclView(),
+ file.getInheritedAclView());
+ }
+
+ @Override
+ public AclFileAttributeView getShareAclView(Path doc) throws IOException {
+ return root.getShareAclView();
+ }
+
+ @Override
+ public AclFileAttributeView getDfsShareAclView(Path doc) throws IOException {
+ return root.getDfsShareAclView();
+ }
+
+ @Override
+ public Path getDfsUncActiveStorageUnc(Path doc) throws IOException {
+ return root.getDfsUncActiveStorageUnc();
+ }
+
+ @Override
+ public void startMonitorPath(Path watchPath, BlockingQueue<Path> queue)
+ throws IOException {
+ // TODO (bmj): implementation
+ }
+
+ @Override
+ public void stopMonitorPath() {
+ // TODO (bmj): implementation
+ }
+
+ @Override
+ public void destroy() {
+ stopMonitorPath();
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/MockFileDelegateTest.java b/test/com/google/enterprise/adaptor/fs/MockFileDelegateTest.java
new file mode 100644
index 0000000..91f85f8
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/MockFileDelegateTest.java
@@ -0,0 +1,222 @@
+// Copyright 2014 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.enterprise.adaptor.fs;
+
+import static org.junit.Assert.*;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.CharStreams;
+import com.google.enterprise.adaptor.DocId;
+
+import org.junit.*;
+import org.junit.rules.ExpectedException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Iterator;
+
+/**
+ * Test cases for {@link MockFileDelegate}.
+ */
+public class MockFileDelegateTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testConstructorNullRoot() throws Exception {
+ thrown.expect(NullPointerException.class);
+ new MockFileDelegate(null);
+ }
+
+ @Test
+ public void testGetPathNullPathname() throws Exception {
+ thrown.expect(NullPointerException.class);
+ new MockFileDelegate(new MockFile("root", true)).getPath(null);
+ }
+
+ @Test
+ public void testGetPath() throws Exception {
+ String delim = File.separator;
+ FileDelegate delegate = new MockFileDelegate(new MockFile("root", true));
+ assertEquals("root", delegate.getPath("root").toString());
+ assertEquals("root" + delim + "foo" + delim + "bar",
+ delegate.getPath("root/foo/bar").toString());
+ assertEquals(delim + "foo" + delim + "bar" + delim + "baz",
+ delegate.getPath("/foo/bar/baz").toString());
+ assertEquals("\\\\server\\share\\dir\\file.txt",
+ delegate.getPath("\\\\server\\share\\dir\\file.txt").toString());
+ }
+
+ @Test
+ public void testGetFileNotFound() throws Exception {
+ MockFile root = new MockFile("root", true).addChildren(new MockFile("foo"));
+ MockFileDelegate delegate = new MockFileDelegate(root);
+ thrown.expect(FileNotFoundException.class);
+ delegate.getFile(delegate.getPath("root/nonExistent"));
+ }
+
+ @Test
+ public void testGetFileRelativeRoot() throws Exception {
+ testGetFile("root");
+ }
+
+ @Test
+ public void testGetFileUncRoot() throws Exception {
+ testGetFile("\\\\host\\share");
+ testGetFile("\\\\host\\share\\");
+ }
+
+ @Test
+ public void testGetFileUnixRoot() throws Exception {
+ testGetFile("/");
+ }
+
+ @Test
+ public void testGetFileDosRoot() throws Exception {
+ testGetFile("C:\\");
+ }
+
+ private void testGetFile(String rootPath) throws Exception {
+ MockFile root = new MockFile(rootPath, true).addChildren(
+ new MockFile("dir1", true).addChildren(new MockFile("foo")),
+ new MockFile("dir2", true).addChildren(new MockFile("bar")),
+ new MockFile("test.txt"));
+ MockFileDelegate delegate = new MockFileDelegate(root);
+
+ assertSame(root, delegate.getFile(delegate.getPath(rootPath)));
+ testGetFile(delegate, getPath(rootPath, "test.txt"));
+ testGetFile(delegate, getPath(rootPath, "dir1"));
+ testGetFile(delegate, getPath(rootPath, "dir2"));
+ testGetFile(delegate, getPath(rootPath, "dir1/foo"));
+ testGetFile(delegate, getPath(rootPath, "dir2/bar"));
+ }
+
+ private String getPath(String parent, String child) {
+ return (parent.endsWith("/")) ? parent + child : parent + "/" + child;
+ }
+
+ private void testGetFile(MockFileDelegate delegate, String pathname)
+ throws Exception {
+ Path path = delegate.getPath(pathname);
+ MockFile file = delegate.getFile(path);
+ assertEquals(pathname, file.getPath());
+ MockFile parent = file.getParent();
+ if (parent != null) {
+ assertSame(parent, delegate.getFile(path.getParent()));
+ }
+ }
+
+ @Test
+ public void testNewDocId() throws Exception {
+ MockFile root = new MockFile("root", true).addChildren(
+ new MockFile("dir1", true).addChildren(new MockFile("foo")));
+ FileDelegate delegate = new MockFileDelegate(root);
+ assertEquals(new DocId("root/"), getDocId(delegate, "root"));
+ assertEquals(new DocId("root/dir1/"), getDocId(delegate, "root/dir1"));
+ assertEquals(new DocId("root/dir1/foo"),
+ getDocId(delegate, "root/dir1/foo"));
+ }
+
+ private DocId getDocId(FileDelegate delegate, String pathname)
+ throws Exception {
+ return delegate.newDocId(delegate.getPath(pathname));
+ }
+
+ @Test
+ public void testSetLastAccessTime() throws Exception {
+ MockFile root = new MockFile("root", true)
+ .addChildren(new MockFile("test.txt"));
+ FileDelegate delegate = new MockFileDelegate(root);
+ Path path = delegate.getPath("root/test.txt");
+
+ BasicFileAttributes attrs = delegate.readBasicAttributes(path);
+ assertEquals(MockFile.DEFAULT_FILETIME, attrs.lastAccessTime());
+
+ FileTime accessTime = FileTime.fromMillis(40000);
+ delegate.setLastAccessTime(path, accessTime);
+ attrs = delegate.readBasicAttributes(path);
+ assertEquals(accessTime, attrs.lastAccessTime());
+ }
+
+ /**
+ * Most of the MockDelegate methods are simple passthrough calls to the
+ * MockFile methods. This is a sanity check to make sure I'm calling the
+ * right ones.
+ */
+ @Test
+ public void testPassthroughGetters() throws Exception {
+ FileTime createTime = FileTime.fromMillis(20000);
+ FileTime modifyTime = FileTime.fromMillis(30000);
+ FileTime accessTime = FileTime.fromMillis(40000);
+ String content = "<html><title>Hello World</title></html>";
+ MockFile root = new MockFile("root", true)
+ .addChildren(
+ new MockFile("test.html").setCreationTime(createTime)
+ .setLastModifiedTime(modifyTime).setLastAccessTime(accessTime)
+ .setFileContents(content).setContentType("text/html"));
+
+ FileDelegate delegate = new MockFileDelegate(root);
+ Path path = delegate.getPath("root");
+ assertTrue(delegate.isDirectory(path));
+ assertFalse(delegate.isRegularFile(path));
+ assertNull(delegate.getDfsUncActiveStorageUnc(path));
+ Path uncPath = delegate.getPath("\\\\server\\share");
+ root.setDfsUncActiveStorageUnc(uncPath);
+ assertEquals(uncPath, delegate.getDfsUncActiveStorageUnc(path));
+ assertNull(delegate.getDfsShareAclView(path));
+ root.setDfsShareAclView(MockFile.FULL_ACCESS_ACLVIEW);
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW,
+ delegate.getDfsShareAclView(path));
+ AclFileAttributeViews aclViews = delegate.getAclViews(path);
+ assertNotNull(aclViews);
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW, aclViews.getDirectAclView());
+ assertEquals(MockFile.EMPTY_ACLVIEW, aclViews.getInheritedAclView());
+
+ path = delegate.getPath("root/test.html");
+ assertTrue(delegate.isRegularFile(path));
+ assertFalse(delegate.isDirectory(path));
+ assertFalse(delegate.isHidden(path));
+ assertEquals("text/html", delegate.probeContentType(path));
+ assertEquals(content, readContents(delegate, path));
+ aclViews = delegate.getAclViews(path);
+ assertNotNull(aclViews);
+ assertEquals(MockFile.EMPTY_ACLVIEW, aclViews.getDirectAclView());
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW, aclViews.getInheritedAclView());
+
+ BasicFileAttributes attrs = delegate.readBasicAttributes(path);
+ assertTrue(attrs.isRegularFile());
+ assertFalse(attrs.isDirectory());
+ assertFalse(attrs.isOther());
+ assertEquals(createTime, attrs.creationTime());
+ assertEquals(modifyTime, attrs.lastModifiedTime());
+ assertEquals(accessTime, attrs.lastAccessTime());
+ assertEquals(content.getBytes(Charsets.UTF_8).length, attrs.size());
+ }
+
+ private String readContents(FileDelegate delegate, Path path)
+ throws IOException {
+ return CharStreams.toString(
+ new InputStreamReader(delegate.newInputStream(path), Charsets.UTF_8));
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/MockFileTest.java b/test/com/google/enterprise/adaptor/fs/MockFileTest.java
new file mode 100644
index 0000000..c65992b
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/MockFileTest.java
@@ -0,0 +1,309 @@
+// Copyright 2014 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.enterprise.adaptor.fs;
+
+import static org.junit.Assert.*;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.CharStreams;
+
+import org.junit.*;
+import org.junit.rules.ExpectedException;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Iterator;
+
+/**
+ * Test cases for {@link MockFile}.
+ */
+public class MockFileTest {
+
+ private static final FileTime createTime = FileTime.fromMillis(20000);
+ private static final FileTime modifyTime = FileTime.fromMillis(30000);
+ private static final FileTime accessTime = FileTime.fromMillis(40000);
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testConstructorNullName() throws Exception {
+ thrown.expect(NullPointerException.class);
+ new MockFile(null);
+ }
+
+ /** Test constructor for regular files. */
+ @Test
+ public void testFileConstructor() throws Exception {
+ MockFile f = new MockFile("test");
+ checkDefaultConfig(f, "test", false);
+ assertEquals("Contents of test", readContents(f));
+ assertEquals("text/plain", f.getContentType());
+ }
+
+ /** Test constructor for directories . */
+ @Test
+ public void testDirectoryConstructor() throws Exception {
+ MockFile f = new MockFile("test", true);
+ checkDefaultConfig(f, "test", true);
+ DirectoryStream<Path> ds = f.newDirectoryStream();
+ assertNotNull(ds);
+ assertFalse(ds.iterator().hasNext());
+ ds.close();
+ }
+
+ /**
+ * Verifies some of the default file attributes (no explicit setters called).
+ */
+ private void checkDefaultConfig(MockFile file, String name, boolean isDir)
+ throws Exception {
+ assertEquals(name, file.getName());
+ assertEquals(isDir, file.isDirectory());
+ assertEquals(!isDir, file.isRegularFile());
+ assertFalse(file.isHidden());
+ assertNull(file.getParent());
+ assertEquals(MockFile.DEFAULT_FILETIME, file.getCreationTime());
+ assertEquals(MockFile.DEFAULT_FILETIME, file.getLastModifiedTime());
+ assertEquals(MockFile.DEFAULT_FILETIME, file.getLastAccessTime());
+ }
+
+ private String readContents(MockFile file) throws IOException {
+ return CharStreams.toString(
+ new InputStreamReader(file.newInputStream(), Charsets.UTF_8));
+ }
+
+ @Test
+ public void testSetName() throws Exception {
+ assertEquals("foo", new MockFile("test").setName("foo").getName());
+ }
+
+ @Test
+ public void testSetIsRegularFile() throws Exception {
+ MockFile f = new MockFile("test").setIsRegularFile(false);
+ assertFalse(f.isRegularFile());
+ assertFalse(f.isDirectory());
+
+ f.setIsRegularFile(true);
+ assertTrue(f.isRegularFile());
+ assertFalse(f.isDirectory());
+ }
+
+ @Test
+ public void testSetIsHidden() throws Exception {
+ MockFile f = new MockFile("test");
+ assertFalse(f.isHidden()); // default is false.
+ assertTrue(f.setIsHidden(true).isHidden());
+ assertFalse(f.setIsHidden(false).isHidden());
+ }
+
+ @Test
+ public void testSetFileTimes() throws Exception {
+ MockFile f = new MockFile("test").setCreationTime(createTime)
+ .setLastModifiedTime(modifyTime).setLastAccessTime(accessTime);
+ assertEquals(createTime, f.getCreationTime());
+ assertEquals(modifyTime, f.getLastModifiedTime());
+ assertEquals(accessTime, f.getLastAccessTime());
+ }
+
+ @Test
+ public void testReadBasicAttributesRegularFile() throws Exception {
+ MockFile f = new MockFile("test").setCreationTime(createTime)
+ .setLastModifiedTime(modifyTime).setLastAccessTime(accessTime);
+
+ BasicFileAttributes bfa = f.readBasicAttributes();
+ assertNotNull(bfa);
+ assertSame(f, bfa.fileKey());
+ assertEquals(createTime, bfa.creationTime());
+ assertEquals(modifyTime, bfa.lastModifiedTime());
+ assertEquals(accessTime, bfa.lastAccessTime());
+ assertTrue(bfa.isRegularFile());
+ assertFalse(bfa.isDirectory());
+ assertFalse(bfa.isSymbolicLink());
+ assertFalse(bfa.isOther());
+ assertEquals(readContents(f).length(), bfa.size());
+ }
+
+ @Test
+ public void testReadBasicAttributesDirectory() throws Exception {
+ MockFile f = new MockFile("test", true).setCreationTime(createTime)
+ .setLastModifiedTime(modifyTime).setLastAccessTime(accessTime);
+
+ BasicFileAttributes bfa = f.readBasicAttributes();
+ assertNotNull(bfa);
+ assertSame(f, bfa.fileKey());
+ assertEquals(createTime, bfa.creationTime());
+ assertEquals(modifyTime, bfa.lastModifiedTime());
+ assertEquals(accessTime, bfa.lastAccessTime());
+ assertFalse(bfa.isRegularFile());
+ assertTrue(bfa.isDirectory());
+ assertFalse(bfa.isSymbolicLink());
+ assertFalse(bfa.isOther());
+ assertEquals(0L, bfa.size());
+ }
+
+ @Test
+ public void testReadBasicAttributesSpecialFile() throws Exception {
+ // If neither file, nor directory, then it is "special".
+ MockFile f = new MockFile("test", false).setIsRegularFile(false);
+ BasicFileAttributes bfa = f.readBasicAttributes();
+ assertFalse(bfa.isRegularFile());
+ assertFalse(bfa.isDirectory());
+ assertFalse(bfa.isSymbolicLink());
+ assertTrue(bfa.isOther());
+ assertEquals(0L, bfa.size());
+ }
+
+ @Test
+ public void testSetFileContents() throws Exception {
+ String expected = "Hello World";
+ MockFile f = new MockFile("test.txt").setFileContents(expected);
+ assertEquals(expected, readContents(f));
+ }
+
+ @Test
+ public void testSetFileContentsBytes() throws Exception {
+ String expected = "<html><title>Hello World</title></html>";
+ MockFile f = new MockFile("test.html")
+ .setFileContents(expected.getBytes(Charsets.UTF_8))
+ .setContentType("text/html");
+ assertEquals(expected, readContents(f));
+ assertEquals("text/html", f.getContentType());
+ }
+
+ @Test
+ public void testChildren() throws Exception {
+ MockFile root = new MockFile("root", true);
+ MockFile dir1 = new MockFile("dir1", true);
+ MockFile dir2 = new MockFile("dir2", true);
+ MockFile test = new MockFile("test.txt");
+ root.addChildren(test, dir1, dir2);
+ assertNull(root.getParent());
+ assertSame(root, dir1.getParent());
+ assertSame(root, dir2.getParent());
+ assertSame(root, test.getParent());
+ checkDirectoryListing(root, dir1, dir2, test);
+
+ // Test getChild().
+ assertSame(dir1, root.getChild("dir1"));
+ assertSame(dir2, root.getChild("dir2"));
+ assertSame(test, root.getChild("test.txt"));
+
+ // Add another file.
+ MockFile newer = new MockFile("newer.txt");
+ root.addChildren(newer);
+ checkDirectoryListing(root, dir1, dir2, newer, test);
+ }
+
+ private void checkDirectoryListing(MockFile parent, MockFile... children)
+ throws IOException {
+ DirectoryStream<Path> ds = parent.newDirectoryStream();
+ assertNotNull(ds);
+ Iterator<Path> iter = ds.iterator();
+ assertNotNull(iter);
+ for (MockFile child : children) {
+ assertEquals(child.getName(), iter.next().getFileName().toString());
+ }
+ assertFalse(iter.hasNext());
+ ds.close();
+ }
+
+ @Test
+ public void testGetPath() throws Exception {
+ MockFile root = new MockFile("root", true);
+ MockFile dir1 = new MockFile("dir1", true);
+ MockFile test = new MockFile("test.txt");
+ MockFile test1 = new MockFile("test.txt");
+ root.addChildren(test, dir1);
+ dir1.addChildren(test1);
+ assertEquals("root", root.getPath());
+ assertEquals("root/dir1", dir1.getPath());
+ assertEquals("root/test.txt", test.getPath());
+ assertEquals("root/dir1/test.txt", test1.getPath());
+ }
+
+ @Test
+ public void testGetChildNullName() throws Exception {
+ thrown.expect(NullPointerException.class);
+ new MockFile("root", true).getChild(null);
+ }
+
+ @Test
+ public void testGetChildNotFound() throws Exception {
+ thrown.expect(FileNotFoundException.class);
+ new MockFile("root", true).getChild("nonExistent");
+ }
+
+ @Test
+ public void testGetDfsUncActiveStorageUnc() throws Exception {
+ MockFile root = new MockFile("root", true);
+ assertNull(root.getDfsUncActiveStorageUnc());
+ Path uncPath = Paths.get("\\\\server\\share");
+ root.setDfsUncActiveStorageUnc(uncPath);
+ assertEquals(uncPath, root.getDfsUncActiveStorageUnc());
+ }
+
+ @Test
+ public void testGetDfsShareAclView() throws Exception {
+ MockFile root = new MockFile("root", true);
+ assertNull(root.getDfsShareAclView());
+ root.setDfsShareAclView(MockFile.FULL_ACCESS_ACLVIEW);
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW, root.getDfsShareAclView());
+ }
+
+ @Test
+ public void testDefaultRootAclViews() throws Exception {
+ MockFile root = new MockFile("root", true);
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW, root.getShareAclView());
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW, root.getAclView());
+ assertEquals(MockFile.EMPTY_ACLVIEW, root.getInheritedAclView());
+ }
+
+ @Test
+ public void testDefaultNonRootAclViews() throws Exception {
+ MockFile root = new MockFile("root", true);
+ MockFile dir1 = new MockFile("dir1", true);
+ MockFile foo = new MockFile("foo");
+ MockFile bar = new MockFile("bar");
+ root.addChildren(dir1, foo);
+ dir1.addChildren(bar);
+ assertEquals(MockFile.EMPTY_ACLVIEW, dir1.getAclView());
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW, dir1.getInheritedAclView());
+ assertEquals(MockFile.EMPTY_ACLVIEW, foo.getAclView());
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW, foo.getInheritedAclView());
+ assertEquals(MockFile.EMPTY_ACLVIEW, bar.getAclView());
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW, bar.getInheritedAclView());
+ }
+
+ @Test
+ public void testSetAclViews() throws Exception {
+ MockFile root = new MockFile("root", true);
+ MockFile foo = new MockFile("foo");
+ root.addChildren(foo);
+ assertEquals(MockFile.EMPTY_ACLVIEW, foo.getAclView());
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW, foo.getInheritedAclView());
+
+ foo.setAclView(MockFile.FULL_ACCESS_ACLVIEW);
+ foo.setInheritedAclView(MockFile.EMPTY_ACLVIEW);
+ assertEquals(MockFile.FULL_ACCESS_ACLVIEW, foo.getAclView());
+ assertEquals(MockFile.EMPTY_ACLVIEW, foo.getInheritedAclView());
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/MockRequest.java b/test/com/google/enterprise/adaptor/fs/MockRequest.java
new file mode 100644
index 0000000..850d2fa
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/MockRequest.java
@@ -0,0 +1,50 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// 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.enterprise.adaptor.fs;
+
+import com.google.enterprise.adaptor.DocId;
+import com.google.enterprise.adaptor.Request;
+
+import java.util.Date;
+
+/** A trivial implemenation of {@link Request} */
+class MockRequest implements Request {
+ private final DocId docid;
+ private final Date lastAccess;
+
+ MockRequest(DocId docid) {
+ this(docid, null);
+ }
+
+ MockRequest(DocId docid, Date lastAccess) {
+ this.docid = docid;
+ this.lastAccess = lastAccess;
+ }
+
+ @Override
+ public boolean hasChangedSinceLastAccess(Date lastModified) {
+ return lastModified.after(lastAccess);
+ }
+
+ @Override
+ public Date getLastAccessTime() {
+ return lastAccess;
+ }
+
+ @Override
+ public DocId getDocId() {
+ return docid;
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/MockResponse.java b/test/com/google/enterprise/adaptor/fs/MockResponse.java
new file mode 100644
index 0000000..edbbaaa
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/MockResponse.java
@@ -0,0 +1,124 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// 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.enterprise.adaptor.fs;
+
+import com.google.enterprise.adaptor.Acl;
+import com.google.enterprise.adaptor.Response;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An implementation of {@link Response} that implements only those items that
+ * the adaptor uses.
+ */
+class MockResponse implements Response {
+
+ boolean notModified = false;
+ boolean notFound = false;
+ String contentType;
+ Date lastModified;
+ URI displayUrl;
+ Acl acl;
+ Map<String, String> metadata = new HashMap<String, String>();
+ Map<String, Acl> namedResources = new HashMap<String, Acl>();
+ ByteArrayOutputStream content;
+
+ @Override
+ public void respondNotModified() throws IOException {
+ notModified = true;
+ }
+
+ @Override
+ public void respondNotFound() throws IOException {
+ notFound = true;
+ }
+
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ content = new ByteArrayOutputStream();
+ return content;
+ }
+
+ @Override
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ @Override
+ public void setLastModified(Date lastModified) {
+ this.lastModified = lastModified;
+ }
+
+ @Override
+ public void addMetadata(String key, String value) {
+ metadata.put(key, value);
+ }
+
+ @Override
+ public void setAcl(Acl acl) {
+ this.acl = acl;
+ }
+
+ @Override
+ public void putNamedResource(String fragment, Acl acl) {
+ namedResources.put(fragment, acl);
+ }
+
+ @Override
+ public void setDisplayUrl(URI displayUrl) {
+ this.displayUrl = displayUrl;
+ }
+
+ @Override
+ public void setSecure(boolean secure) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void addAnchor(URI uri, String text) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setNoIndex(boolean noIndex) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setNoFollow(boolean noFollow) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setNoArchive(boolean noArchive) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setCrawlOnce(boolean crawlOnce) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setLock(boolean lock) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/NioFileDelegateTest.java b/test/com/google/enterprise/adaptor/fs/NioFileDelegateTest.java
index ae5eaa7..4db3dce 100644
--- a/test/com/google/enterprise/adaptor/fs/NioFileDelegateTest.java
+++ b/test/com/google/enterprise/adaptor/fs/NioFileDelegateTest.java
@@ -45,15 +45,16 @@
public TemporaryFolder temp = new TemporaryFolder();
private Path newTempDir(String name) throws IOException {
- return temp.newFolder(name).toPath();
+ return temp.newFolder(name).toPath().toRealPath();
}
private Path newTempFile(String name) throws IOException {
- return temp.newFile(name).toPath();
+ return temp.newFile(name).toPath().toRealPath();
}
private Path newTempFile(Path parent, String name) throws IOException {
- Preconditions.checkArgument(parent.startsWith(temp.getRoot().toPath()));
+ Preconditions.checkArgument(parent.startsWith(temp.getRoot().toPath()
+ .toRealPath()));
return Files.createFile(parent.resolve(name));
}
@@ -162,7 +163,7 @@
@Test
public void testNewDocId() throws Exception {
- Path root = temp.getRoot().toPath();
+ Path root = temp.getRoot().toPath().toRealPath();
Path dir = newTempDir("testDir");
Path file = newTempFile(dir, "test");
diff --git a/test/com/google/enterprise/adaptor/fs/UnsupportedDocIdPusher.java b/test/com/google/enterprise/adaptor/fs/UnsupportedDocIdPusher.java
new file mode 100644
index 0000000..e9b3439
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/UnsupportedDocIdPusher.java
@@ -0,0 +1,83 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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.enterprise.adaptor.fs;
+
+import com.google.enterprise.adaptor.Acl;
+import com.google.enterprise.adaptor.DocId;
+import com.google.enterprise.adaptor.DocIdPusher;
+import com.google.enterprise.adaptor.ExceptionHandler;
+import com.google.enterprise.adaptor.GroupPrincipal;
+import com.google.enterprise.adaptor.Principal;
+
+import java.util.Collection;
+import java.util.Map;
+
+/** Throws UnsupportedOperationException for all calls. */
+class UnsupportedDocIdPusher implements DocIdPusher {
+
+ @Override
+ public DocId pushDocIds(Iterable<DocId> docIds)
+ throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public DocId pushDocIds(Iterable<DocId> docIds,
+ ExceptionHandler handler)
+ throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Record pushRecords(Iterable<Record> records)
+ throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Record pushRecords(Iterable<Record> records,
+ ExceptionHandler handler)
+ throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public DocId pushNamedResources(Map<DocId, Acl> resources)
+ throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public DocId pushNamedResources(Map<DocId, Acl> resources,
+ ExceptionHandler handler)
+ throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public GroupPrincipal pushGroupDefinitions(
+ Map<GroupPrincipal, ? extends Collection<Principal>> defs,
+ boolean caseSensitive) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public GroupPrincipal pushGroupDefinitions(
+ Map<GroupPrincipal, ? extends Collection<Principal>> defs,
+ boolean caseSensitive, ExceptionHandler handler)
+ throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java b/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java
new file mode 100644
index 0000000..2b87cbe
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java
@@ -0,0 +1,340 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// 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.enterprise.adaptor.fs;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+import org.junit.*;
+import org.junit.rules.ExpectedException;
+
+import com.sun.jna.platform.win32.Advapi32Util.Account;
+import com.sun.jna.platform.win32.Win32Exception;
+import com.sun.jna.platform.win32.WinError;
+import com.sun.jna.platform.win32.WinNT;
+import com.sun.jna.platform.win32.WinNT.SID_NAME_USE;
+
+import java.nio.file.attribute.AclEntry;
+import java.nio.file.attribute.AclEntryFlag;
+import java.nio.file.attribute.AclEntryPermission;
+import java.nio.file.attribute.AclEntryType;
+import java.nio.file.attribute.GroupPrincipal;
+import java.nio.file.attribute.UserPrincipal;
+import java.util.EnumSet;
+import java.util.Set;
+
+/** Tests for {@link WindowsAclFileAttributeViews} */
+public class WindowsAclFileAttributeViewsTest {
+
+ private final WindowsAclFileAttributeViews wafav =
+ new TestAclFileAttributeViews();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testNewAclEntryUnsupportedAccessType() throws Exception {
+ WinNT.ACCESS_ACEStructure ace = new AceBuilder()
+ .setType(WinNT.SYSTEM_AUDIT_ACE_TYPE).build();
+ assertNull(wafav.newAclEntry(ace));
+ }
+
+ @Test
+ public void testNewAclEntryUnresolvableSid() throws Exception {
+ TestHelper.assumeOsIsWindows(); // For new Win32Exception().
+ WinNT.ACCESS_ACEStructure ace = new AceBuilder()
+ .setSid(new AccountSid(null)).build();
+ assertNull(wafav.newAclEntry(ace));
+ }
+
+ @Test
+ public void testNewAclEntryUnsupportedAccountType() throws Exception {
+ WinNT.ACCESS_ACEStructure ace = new AceBuilder()
+ .setSid(new AccountSid(SID_NAME_USE.SidTypeUnknown, "", "")).build();
+ assertNull(wafav.newAclEntry(ace));
+ }
+
+ @Test
+ public void testNewAclEntryUserPrincipal() throws Exception {
+ testNewAclEntryUserPrincipal(AccountSid.user("userName", null), "userName");
+ }
+
+ @Test
+ public void testNewAclEntryUserWithDomainPrincipal() throws Exception {
+ testNewAclEntryUserPrincipal(AccountSid.user("userName", "domain"),
+ "domain\\userName");
+ }
+
+ private void testNewAclEntryUserPrincipal(AccountSid account,
+ String expectedName) throws Exception {
+ WinNT.ACCESS_ACEStructure ace = new AceBuilder().setSid(account).build();
+ AclEntry aclEntry = wafav.newAclEntry(ace);
+ assertNotNull(aclEntry);
+ UserPrincipal principal = aclEntry.principal();
+ assertNotNull(principal);
+ assertFalse(principal instanceof GroupPrincipal);
+ assertEquals(expectedName, principal.getName());
+ }
+
+ @Test
+ public void testNewAclEntryGroupPrincipal() throws Exception {
+ testNewAclEntryGroupPrincipal(AccountSid.group("groupName", null),
+ "groupName");
+ }
+
+ @Test
+ public void testNewAclEntryGroupWithDomainPrincipal() throws Exception {
+ testNewAclEntryGroupPrincipal(AccountSid.group("groupName", "domain"),
+ "domain\\groupName");
+ }
+
+ @Test
+ public void testNewAclEntryAliasPrincipal() throws Exception {
+ AccountSid account =
+ new AccountSid(SID_NAME_USE.SidTypeAlias, "alias", "domain");
+ testNewAclEntryGroupPrincipal(account, "domain\\alias");
+ }
+
+ @Test
+ public void testNewAclEntryWellKnownGroupPrincipal() throws Exception {
+ AccountSid account =
+ new AccountSid(SID_NAME_USE.SidTypeWellKnownGroup, "wellKnown", null);
+ testNewAclEntryGroupPrincipal(account, "wellKnown");
+ }
+
+ private void testNewAclEntryGroupPrincipal(AccountSid account,
+ String expectedName) throws Exception {
+ WinNT.ACCESS_ACEStructure ace = new AceBuilder().setSid(account).build();
+ AclEntry aclEntry = wafav.newAclEntry(ace);
+ assertNotNull(aclEntry);
+ UserPrincipal principal = aclEntry.principal();
+ assertNotNull(principal);
+ assertTrue(principal instanceof GroupPrincipal);
+ assertEquals(expectedName, principal.getName());
+ }
+
+ @Test
+ public void testNewAclEntryIndividualPermissions() throws Exception {
+ testNewAclEntryPermissions(WinNT.FILE_READ_DATA,
+ AclEntryPermission.READ_DATA);
+ testNewAclEntryPermissions(WinNT.FILE_READ_ATTRIBUTES,
+ AclEntryPermission.READ_ATTRIBUTES);
+ testNewAclEntryPermissions(WinNT.FILE_READ_EA,
+ AclEntryPermission.READ_NAMED_ATTRS);
+ testNewAclEntryPermissions(WinNT.READ_CONTROL,
+ AclEntryPermission.READ_ACL);
+ testNewAclEntryPermissions(WinNT.FILE_WRITE_DATA,
+ AclEntryPermission.WRITE_DATA);
+ testNewAclEntryPermissions(WinNT.FILE_APPEND_DATA,
+ AclEntryPermission.APPEND_DATA);
+ testNewAclEntryPermissions(WinNT.FILE_WRITE_ATTRIBUTES,
+ AclEntryPermission.WRITE_ATTRIBUTES);
+ testNewAclEntryPermissions(WinNT.FILE_WRITE_EA,
+ AclEntryPermission.WRITE_NAMED_ATTRS);
+ testNewAclEntryPermissions(WinNT.WRITE_DAC,
+ AclEntryPermission.WRITE_ACL);
+ testNewAclEntryPermissions(WinNT.WRITE_OWNER,
+ AclEntryPermission.WRITE_OWNER);
+ testNewAclEntryPermissions(WinNT.DELETE,
+ AclEntryPermission.DELETE);
+ testNewAclEntryPermissions(WinNT.FILE_DELETE_CHILD,
+ AclEntryPermission.DELETE_CHILD);
+ testNewAclEntryPermissions(WinNT.SYNCHRONIZE,
+ AclEntryPermission.SYNCHRONIZE);
+ testNewAclEntryPermissions(WinNT.FILE_EXECUTE,
+ AclEntryPermission.EXECUTE);
+ }
+
+ @Test
+ public void testNewAclEntryFullPermissions() throws Exception {
+ testNewAclEntryPermissions(WinNT.FILE_ALL_ACCESS,
+ AclEntryPermission.values());
+
+ }
+
+ @Test
+ public void testNewAclEntryGenericPermissions() throws Exception {
+ testNewAclEntryPermissions(WinNT.GENERIC_READ, AclEntryPermission.READ_DATA,
+ AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_NAMED_ATTRS,
+ AclEntryPermission.READ_ACL, AclEntryPermission.SYNCHRONIZE);
+ testNewAclEntryPermissions(WinNT.GENERIC_WRITE,
+ AclEntryPermission.WRITE_DATA, AclEntryPermission.APPEND_DATA,
+ AclEntryPermission.READ_ACL, AclEntryPermission.WRITE_ATTRIBUTES,
+ AclEntryPermission.WRITE_NAMED_ATTRS, AclEntryPermission.SYNCHRONIZE);
+ testNewAclEntryPermissions(WinNT.GENERIC_EXECUTE,
+ AclEntryPermission.EXECUTE, AclEntryPermission.READ_ATTRIBUTES,
+ AclEntryPermission.READ_ACL, AclEntryPermission.SYNCHRONIZE);
+ testNewAclEntryPermissions(WinNT.GENERIC_ALL, AclEntryPermission.values());
+ }
+
+ private void testNewAclEntryPermissions(int acePermissions,
+ AclEntryPermission... expectedPermissions) throws Exception {
+ Set<AclEntryPermission> expected =
+ EnumSet.noneOf(AclEntryPermission.class);
+ for (AclEntryPermission perm : expectedPermissions) {
+ expected.add(perm);
+ }
+ WinNT.ACCESS_ACEStructure ace = new AceBuilder()
+ .setSid(AccountSid.user("userName", null))
+ .setPerms(acePermissions)
+ .build();
+ AclEntry aclEntry = wafav.newAclEntry(ace);
+ assertNotNull(aclEntry);
+ assertEquals(expected, aclEntry.permissions());
+ }
+
+ @Test
+ public void testNewAclEntryIndividualFlags() throws Exception {
+ testNewAclEntryFlags(WinNT.OBJECT_INHERIT_ACE, AclEntryFlag.FILE_INHERIT);
+ testNewAclEntryFlags(WinNT.INHERIT_ONLY_ACE, AclEntryFlag.INHERIT_ONLY);
+ testNewAclEntryFlags(WinNT.CONTAINER_INHERIT_ACE,
+ AclEntryFlag.DIRECTORY_INHERIT);
+ testNewAclEntryFlags(WinNT.NO_PROPAGATE_INHERIT_ACE,
+ AclEntryFlag.NO_PROPAGATE_INHERIT);
+ }
+
+ @Test
+ public void testNewAclEntryMultipleFlags() throws Exception {
+ testNewAclEntryFlags((byte) (WinNT.OBJECT_INHERIT_ACE |
+ WinNT.CONTAINER_INHERIT_ACE | WinNT.INHERIT_ONLY_ACE |
+ WinNT.NO_PROPAGATE_INHERIT_ACE), AclEntryFlag.values());
+ }
+
+ private void testNewAclEntryFlags(byte aceFlags,
+ AclEntryFlag... expectedFlags) throws Exception {
+ Set<AclEntryFlag> expected = EnumSet.noneOf(AclEntryFlag.class);
+ for (AclEntryFlag flag : expectedFlags) {
+ expected.add(flag);
+ }
+ WinNT.ACCESS_ACEStructure ace = new AceBuilder()
+ .setSid(AccountSid.user("userName", null))
+ .setFlags(aceFlags)
+ .build();
+ AclEntry aclEntry = wafav.newAclEntry(ace);
+ assertNotNull(aclEntry);
+ assertEquals(expected, aclEntry.flags());
+ }
+
+ static class AceBuilder {
+ private Ace ace = new Ace();
+
+ public AceBuilder setType(byte type) {
+ ace.AceType = type;
+ return this;
+ }
+
+ public AceBuilder setFlags(byte... flags) {
+ for (byte flag : flags) {
+ ace.AceFlags |= flag;
+ }
+ return this;
+ }
+
+ public AceBuilder setPerms(int... perms) {
+ for (int perm : perms) {
+ ace.Mask |= perm;
+ }
+ return this;
+ }
+
+ public AceBuilder setSid(WinNT.PSID sid) {
+ ace.setSID(sid);
+ return this;
+ }
+
+ public WinNT.ACCESS_ACEStructure build() {
+ return ace;
+ }
+ }
+
+ static class Ace extends WinNT.ACCESS_ACEStructure {
+ // psid is not publicly settable in ACCESS_ACEStructure.
+ private WinNT.PSID sid;
+
+ public void setSID(WinNT.PSID sid) {
+ this.sid = sid;
+ }
+
+ @Override
+ public WinNT.PSID getSID() {
+ return (sid != null) ? sid : super.getSID();
+ }
+
+ @Override
+ public String getSidString() {
+ return (sid != null) ? sid.toString() : super.getSidString();
+ }
+ }
+
+ /** A SID implemention that wraps an Account, avoiding AD lookup. */
+ static class AccountSid extends WinNT.PSID {
+ private final Account account;
+
+ public static AccountSid user(String name, String domain) {
+ return new AccountSid(SID_NAME_USE.SidTypeUser, name, domain);
+ }
+
+ public static AccountSid group(String name, String domain) {
+ return new AccountSid(SID_NAME_USE.SidTypeGroup, name, domain);
+ }
+
+ public AccountSid(Account account) {
+ this.account = account;
+ }
+
+ public AccountSid(int type, String name, String domain) {
+ account = new Account();
+ account.accountType = type;
+ account.name = name;
+ account.domain = domain;
+ }
+
+ public Account getAccount() throws Win32Exception {
+ if (account == null) {
+ throw new Win32Exception(WinError.ERROR_NONE_MAPPED);
+ }
+ return account;
+ }
+
+ @Override
+ public String toString() {
+ if (account == null) {
+ return "null";
+ } else {
+ return (account.domain == null) ? account.name
+ : account.domain + "\\" + account.name;
+ }
+ }
+ }
+
+ /**
+ * An subclass of WindowsAclFileAttributeViews that avoids making
+ * actual Windows API calls.
+ */
+ static class TestAclFileAttributeViews extends WindowsAclFileAttributeViews {
+ public TestAclFileAttributeViews() {
+ super(null, null, null, null, null);
+ }
+
+ @Override
+ Account getAccountBySid(WinNT.PSID sid) throws Win32Exception {
+ if (sid instanceof AccountSid) {
+ return ((AccountSid) sid).getAccount();
+ } else {
+ return super.getAccountBySid(sid);
+ }
+ }
+ }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/WindowsFileDelegateTest.java b/test/com/google/enterprise/adaptor/fs/WindowsFileDelegateTest.java
index f345598..0a536eb 100644
--- a/test/com/google/enterprise/adaptor/fs/WindowsFileDelegateTest.java
+++ b/test/com/google/enterprise/adaptor/fs/WindowsFileDelegateTest.java
@@ -72,11 +72,11 @@
}
private Path newTempDir(String name) throws IOException {
- return temp.newFolder(name).toPath();
+ return temp.newFolder(name).toPath().toRealPath();
}
private Path newTempFile(String name) throws IOException {
- return temp.newFile(name).toPath();
+ return temp.newFile(name).toPath().toRealPath();
}
private Path newTempFile(Path parent, String name) throws IOException {