Add WindowsAclFileAttributeViewsTest (Part 2)

This change adds support for faking Windows JNA calls
and uses faked JNA interfaces to test the
WindowsAclFileAttributeViews.getAclViews() method.

TODO: Add tests for Share ACL, and DFS ACLs.

Code Review: http://codereview.appspot.com/82070043
diff --git a/test/com/google/enterprise/adaptor/fs/AclView.java b/test/com/google/enterprise/adaptor/fs/AclView.java
index 233061e..1398e63 100644
--- a/test/com/google/enterprise/adaptor/fs/AclView.java
+++ b/test/com/google/enterprise/adaptor/fs/AclView.java
@@ -173,15 +173,18 @@
       return name;
     }
 
+    // This implementation of equals() is a bit unusual, because I may compare
+    // instances of two different implementations of the interface and I do not
+    // want to match GroupPrincipals (which extend UserPrincipals).
     @Override
     public boolean equals(Object obj) {
       if (obj == null) {
         return false;
       }
-      if (getClass() != obj.getClass()) {
+      if (!(obj instanceof UserPrincipal) || obj instanceof GroupPrincipal) {
         return false;
       }
-      return name.equals(obj.toString());
+      return getName().equals(((UserPrincipal) obj).getName());
     }
   }
 
@@ -189,5 +192,18 @@
     Group(String name) {
       super(name);
     }
+
+    // This implementation of equals() is a bit unusual, because I may compare
+    // instances of two different implementations of the interface.
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == null) {
+        return false;
+      }
+      if (!(obj instanceof GroupPrincipal)) {
+        return false;
+      }
+      return getName().equals(((GroupPrincipal) obj).getName());
+    }
   }
 }
diff --git a/test/com/google/enterprise/adaptor/fs/UnsupportedAdvapi32.java b/test/com/google/enterprise/adaptor/fs/UnsupportedAdvapi32.java
new file mode 100644
index 0000000..3af7592
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/UnsupportedAdvapi32.java
@@ -0,0 +1,390 @@
+// 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.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import com.sun.jna.WString;
+import com.sun.jna.platform.win32.Advapi32;
+import com.sun.jna.platform.win32.WinBase.FILETIME;
+import com.sun.jna.platform.win32.WinBase.PROCESS_INFORMATION;
+import com.sun.jna.platform.win32.WinBase.SECURITY_ATTRIBUTES;
+import com.sun.jna.platform.win32.WinBase.STARTUPINFO;
+import com.sun.jna.platform.win32.WinNT.HANDLE;
+import com.sun.jna.platform.win32.WinNT.HANDLEByReference;
+import com.sun.jna.platform.win32.WinNT.LUID;
+import com.sun.jna.platform.win32.WinNT.PSID;
+import com.sun.jna.platform.win32.WinNT.PSIDByReference;
+import com.sun.jna.platform.win32.WinNT.TOKEN_PRIVILEGES;
+import com.sun.jna.platform.win32.WinReg.HKEY;
+import com.sun.jna.platform.win32.WinReg.HKEYByReference;
+import com.sun.jna.platform.win32.Winsvc.SC_HANDLE;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS;
+import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_PROCESS;
+import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.ptr.LongByReference;
+import com.sun.jna.ptr.PointerByReference;
+
+/**
+ * An Implementation of the Advapi32 Interface that throws
+ * UnsupportedOperationException for everything.  Tests may
+ * subclass this and override those methods used by the object
+ * under test.
+ */
+public class UnsupportedAdvapi32 implements Advapi32 {
+
+  @Override
+  public boolean GetUserNameW(char[] buffer, IntByReference len) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean LookupAccountName(String lpSystemName, String lpAccountName,
+      PSID Sid, IntByReference cbSid, char[] ReferencedDomainName,
+      IntByReference cchReferencedDomainName, PointerByReference peUse) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean LookupAccountSid(String lpSystemName, PSID Sid,
+      char[] lpName, IntByReference cchName, char[] ReferencedDomainName,
+      IntByReference cchReferencedDomainName, PointerByReference peUse) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ConvertSidToStringSid(PSID Sid, PointerByReference StringSid) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ConvertStringSidToSid(String StringSid, PSIDByReference Sid) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetLengthSid(PSID pSid) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean IsValidSid(PSID pSid) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean IsWellKnownSid(PSID pSid, int wellKnownSidType) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean CreateWellKnownSid(int wellKnownSidType, PSID domainSid,
+      PSID pSid, IntByReference cbSid) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean LogonUser(String lpszUsername, String lpszDomain,
+      String lpszPassword, int logonType, int logonProvider,
+      HANDLEByReference phToken) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean OpenThreadToken(HANDLE ThreadHandle, int DesiredAccess,
+      boolean OpenAsSelf, HANDLEByReference TokenHandle) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean OpenProcessToken(HANDLE ProcessHandle, int DesiredAccess,
+      HANDLEByReference TokenHandle) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean DuplicateToken(HANDLE ExistingTokenHandle,
+      int ImpersonationLevel, HANDLEByReference DuplicateTokenHandle) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean DuplicateTokenEx(HANDLE hExistingToken, int dwDesiredAccess,
+      SECURITY_ATTRIBUTES lpTokenAttributes, int ImpersonationLevel,
+      int TokenType, HANDLEByReference phNewToken) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetTokenInformation(HANDLE tokenHandle,
+      int tokenInformationClass, Structure tokenInformation,
+      int tokenInformationLength, IntByReference returnLength) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ImpersonateLoggedOnUser(HANDLE hToken) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ImpersonateSelf(int ImpersonationLevel) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean RevertToSelf() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegOpenKeyEx(HKEY hKey, String lpSubKey, int ulOptions,
+      int samDesired, HKEYByReference phkResult) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegQueryValueEx(HKEY hKey, String lpValueName, int lpReserved,
+      IntByReference lpType, char[] lpData, IntByReference lpcbData) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegQueryValueEx(HKEY hKey, String lpValueName, int lpReserved,
+      IntByReference lpType, byte[] lpData, IntByReference lpcbData) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegQueryValueEx(HKEY hKey, String lpValueName, int lpReserved,
+      IntByReference lpType, IntByReference lpData,
+      IntByReference lpcbData) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegQueryValueEx(HKEY hKey, String lpValueName, int lpReserved,
+      IntByReference lpType, LongByReference lpData,
+      IntByReference lpcbData) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegQueryValueEx(HKEY hKey, String lpValueName, int lpReserved,
+      IntByReference lpType, Pointer lpData, IntByReference lpcbData) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegCloseKey(HKEY hKey) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegDeleteValue(HKEY hKey, String lpValueName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegSetValueEx(HKEY hKey, String lpValueName, int Reserved,
+      int dwType, char[] lpData, int cbData) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegSetValueEx(HKEY hKey, String lpValueName, int Reserved,
+      int dwType, byte[] lpData, int cbData) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegCreateKeyEx(HKEY hKey, String lpSubKey, int Reserved,
+      String lpClass, int dwOptions, int samDesired,
+      SECURITY_ATTRIBUTES lpSecurityAttributes,
+      HKEYByReference phkResult, IntByReference lpdwDisposition) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegDeleteKey(HKEY hKey, String name) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegEnumKeyEx(HKEY hKey, int dwIndex, char[] lpName,
+      IntByReference lpcName, IntByReference reserved, char[] lpClass,
+      IntByReference lpcClass, FILETIME lpftLastWriteTime) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegEnumValue(HKEY hKey, int dwIndex, char[] lpValueName,
+      IntByReference lpcchValueName, IntByReference reserved,
+      IntByReference lpType, byte[] lpData, IntByReference lpcbData) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegQueryInfoKey(HKEY hKey, char[] lpClass,
+      IntByReference lpcClass, IntByReference lpReserved,
+      IntByReference lpcSubKeys, IntByReference lpcMaxSubKeyLen,
+      IntByReference lpcMaxClassLen, IntByReference lpcValues,
+      IntByReference lpcMaxValueNameLen, IntByReference lpcMaxValueLen,
+      IntByReference lpcbSecurityDescriptor,
+      FILETIME lpftLastWriteTime) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int RegGetValue(HKEY hkey, String lpSubKey, String lpValue,
+      int dwFlags, IntByReference pdwType, byte[] pvData,
+      IntByReference pcbData) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE RegisterEventSource(String lpUNCServerName,
+      String lpSourceName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean DeregisterEventSource(HANDLE hEventLog) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE OpenEventLog(String lpUNCServerName, String lpSourceName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean CloseEventLog(HANDLE hEventLog) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetNumberOfEventLogRecords(HANDLE hEventLog,
+      IntByReference NumberOfRecords) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ReportEvent(HANDLE hEventLog, int wType, int wCategory,
+      int dwEventID, PSID lpUserSid, int wNumStrings, int dwDataSize,
+      String[] lpStrings, Pointer lpRawData) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ClearEventLog(HANDLE hEventLog, String lpBackupFileName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean BackupEventLog(HANDLE hEventLog, String lpBackupFileName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE OpenBackupEventLog(String lpUNCServerName, String lpFileName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ReadEventLog(HANDLE hEventLog, int dwReadFlags,
+      int dwRecordOffset, Pointer lpBuffer, int nNumberOfBytesToRead,
+      IntByReference pnBytesRead, IntByReference pnMinNumberOfBytesNeeded) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetOldestEventLogRecord(HANDLE hEventLog,
+      IntByReference OldestRecord) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean QueryServiceStatusEx(SC_HANDLE hService, int InfoLevel,
+      SERVICE_STATUS_PROCESS lpBuffer, int cbBufSize,
+      IntByReference pcbBytesNeeded) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ControlService(SC_HANDLE hService, int dwControl,
+      SERVICE_STATUS lpServiceStatus) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean StartService(SC_HANDLE hService, int dwNumServiceArgs,
+      String[] lpServiceArgVectors) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean CloseServiceHandle(SC_HANDLE hSCObject) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public SC_HANDLE OpenService(SC_HANDLE hSCManager, String lpServiceName,
+      int dwDesiredAccess) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public SC_HANDLE OpenSCManager(String lpMachineName, String lpDatabaseName,
+      int dwDesiredAccess) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean CreateProcessAsUser(HANDLE hToken, String lpApplicationName,
+      String lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes,
+      SECURITY_ATTRIBUTES lpThreadAttributes, boolean bInheritHandles,
+      int dwCreationFlags, String lpEnvironment,
+      String lpCurrentDirectory, STARTUPINFO lpStartupInfo,
+      PROCESS_INFORMATION lpProcessInformation) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean AdjustTokenPrivileges(HANDLE TokenHandle,
+      boolean DisableAllPrivileges, TOKEN_PRIVILEGES NewState,
+      int BufferLength, TOKEN_PRIVILEGES PreviousState,
+      IntByReference ReturnLength) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean LookupPrivilegeName(String lpSystemName, LUID lpLuid,
+      char[] lpName, IntByReference cchName) {
+    throw new UnsupportedOperationException();
+  }
+  @Override
+  public boolean LookupPrivilegeValue(String lpSystemName, String lpName,
+      LUID lpLuid) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetFileSecurity(WString lpFileName,
+      int RequestedInformation, Pointer pointer, int nLength,
+      IntByReference lpnLengthNeeded) {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/UnsupportedKernel32.java b/test/com/google/enterprise/adaptor/fs/UnsupportedKernel32.java
new file mode 100644
index 0000000..4703742
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/UnsupportedKernel32.java
@@ -0,0 +1,509 @@
+// 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.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.platform.win32.Kernel32;
+import com.sun.jna.platform.win32.Tlhelp32.PROCESSENTRY32;
+import com.sun.jna.platform.win32.WinBase.FILETIME;
+import com.sun.jna.platform.win32.WinBase.OVERLAPPED;
+import com.sun.jna.platform.win32.WinBase.PROCESS_INFORMATION;
+import com.sun.jna.platform.win32.WinBase.SECURITY_ATTRIBUTES;
+import com.sun.jna.platform.win32.WinBase.STARTUPINFO;
+import com.sun.jna.platform.win32.WinBase.SYSTEMTIME;
+import com.sun.jna.platform.win32.WinNT.FILE_NOTIFY_INFORMATION;
+import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.ptr.LongByReference;
+import com.sun.jna.ptr.PointerByReference;
+
+import java.nio.Buffer;
+
+/**
+ * An implementation of the Kernel32 Interface that throws
+ * UnsupportedOperationException for everything.  Tests may
+ * subclass this and override those methods used by the object
+ * under test.
+ */
+public class UnsupportedKernel32 implements Kernel32 {
+
+  @Override
+  public int FormatMessage(int dwFlags, Pointer lpSource, int dwMessageId,
+      int dwLanguageId, Buffer lpBuffer, int nSize, Pointer va_list) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ReadFile(HANDLE hFile, Buffer lpBuffer,
+      int nNumberOfBytesToRead, IntByReference lpNumberOfBytesRead,
+      OVERLAPPED lpOverlapped) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public Pointer LocalFree(Pointer hLocal) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public Pointer GlobalFree(Pointer hGlobal) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HMODULE GetModuleHandle(String name) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void GetSystemTime(SYSTEMTIME lpSystemTime) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void GetLocalTime(SYSTEMTIME lpSystemTime) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetTickCount() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetCurrentThreadId() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE GetCurrentThread() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetCurrentProcessId() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE GetCurrentProcess() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetProcessId(HANDLE process) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetProcessVersion(int processId) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetExitCodeProcess(HANDLE hProcess,
+      IntByReference lpExitCode) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean TerminateProcess(HANDLE hProcess, int uExitCode) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetLastError() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void SetLastError(int dwErrCode) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetDriveType(String lpRootPathName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int FormatMessage(int dwFlags, Pointer lpSource, int dwMessageId,
+      int dwLanguageId, Pointer lpBuffer, int nSize, Pointer va_list) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int FormatMessage(int dwFlags, Pointer lpSource, int dwMessageId,
+      int dwLanguageId, PointerByReference lpBuffer, int nSize,
+      Pointer va_list) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE CreateFile(String lpFileName, int dwDesiredAccess,
+      int dwShareMode, SECURITY_ATTRIBUTES lpSecurityAttributes,
+      int dwCreationDisposition, int dwFlagsAndAttributes,
+      HANDLE hTemplateFile) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean CopyFile(String lpExistingFileName, String lpNewFileName,
+      boolean bFailIfExists) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean MoveFile(String lpExistingFileName, String lpNewFileName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean MoveFileEx(String lpExistingFileName, String lpNewFileName,
+      DWORD dwFlags) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean CreateDirectory(String lpPathName,
+      SECURITY_ATTRIBUTES lpSecurityAttributes) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ReadFile(HANDLE hFile, Pointer lpBuffer,
+      int nNumberOfBytesToRead, IntByReference lpNumberOfBytesRead,
+      OVERLAPPED lpOverlapped) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE CreateIoCompletionPort(HANDLE FileHandle,
+      HANDLE ExistingCompletionPort, Pointer CompletionKey,
+      int NumberOfConcurrentThreads) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetQueuedCompletionStatus(HANDLE CompletionPort,
+      IntByReference lpNumberOfBytes,
+      ULONG_PTRByReference lpCompletionKey,
+      PointerByReference lpOverlapped, int dwMilliseconds) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean PostQueuedCompletionStatus(HANDLE CompletionPort,
+      int dwNumberOfBytesTransferred, Pointer dwCompletionKey,
+      OVERLAPPED lpOverlapped) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int WaitForSingleObject(HANDLE hHandle, int dwMilliseconds) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int WaitForMultipleObjects(int nCount, HANDLE[] hHandle,
+      boolean bWaitAll, int dwMilliseconds) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean DuplicateHandle(HANDLE hSourceProcessHandle,
+      HANDLE hSourceHandle, HANDLE hTargetProcessHandle,
+      HANDLEByReference lpTargetHandle,
+      int dwDesiredAccess, boolean bInheritHandle, int dwOptions) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean CloseHandle(HANDLE hObject) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean ReadDirectoryChangesW(HANDLE directory,
+      FILE_NOTIFY_INFORMATION info, int length,
+      boolean watchSubtree, int notifyFilter,
+      IntByReference bytesReturned, OVERLAPPED overlapped,
+      OVERLAPPED_COMPLETION_ROUTINE completionRoutine) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetShortPathName(String lpszLongPath, char[] lpdzShortPath,
+            int cchBuffer) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public Pointer LocalAlloc(int uFlags, int uBytes) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean WriteFile(HANDLE hFile, byte[] lpBuffer,
+      int nNumberOfBytesToWrite, IntByReference lpNumberOfBytesWritten,
+      OVERLAPPED lpOverlapped) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE CreateEvent(SECURITY_ATTRIBUTES lpEventAttributes,
+      boolean bManualReset, boolean bInitialState, String lpName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean SetEvent(HANDLE hEvent) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean PulseEvent(HANDLE hEvent) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE CreateFileMapping(HANDLE hFile,
+      SECURITY_ATTRIBUTES lpAttributes, int flProtect,
+      int dwMaximumSizeHigh, int dwMaximumSizeLow, String lpName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public Pointer MapViewOfFile(HANDLE hFileMappingObject, int dwDesiredAccess,
+      int dwFileOffsetHigh, int dwFileOffsetLow, int dwNumberOfBytesToMap) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean UnmapViewOfFile(Pointer lpBaseAddress) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetComputerName(char[] buffer, IntByReference lpnSize) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE OpenThread(int dwDesiredAccess, boolean bInheritHandle,
+      int dwThreadId) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean CreateProcess(String lpApplicationName, String lpCommandLine,
+      SECURITY_ATTRIBUTES lpProcessAttributes,
+      SECURITY_ATTRIBUTES lpThreadAttributes,
+      boolean bInheritHandles, DWORD dwCreationFlags,
+      Pointer lpEnvironment, String lpCurrentDirectory,
+      STARTUPINFO lpStartupInfo,
+      PROCESS_INFORMATION lpProcessInformation) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean CreateProcessW(String lpApplicationName, char[] lpCommandLine,
+      SECURITY_ATTRIBUTES lpProcessAttributes,
+      SECURITY_ATTRIBUTES lpThreadAttributes,
+      boolean bInheritHandles, DWORD dwCreationFlags,
+      Pointer lpEnvironment, String lpCurrentDirectory,
+      STARTUPINFO lpStartupInfo,
+      PROCESS_INFORMATION lpProcessInformation) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE OpenProcess(int fdwAccess, boolean fInherit, int IDProcess) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public DWORD GetTempPath(DWORD nBufferLength, char[] buffer) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public DWORD GetVersion() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetVersionEx(OSVERSIONINFO lpVersionInfo) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetVersionEx(OSVERSIONINFOEX lpVersionInfo) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void GetSystemInfo(SYSTEM_INFO lpSystemInfo) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void GetNativeSystemInfo(SYSTEM_INFO lpSystemInfo) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean IsWow64Process(HANDLE hProcess, IntByReference Wow64Process) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetLogicalProcessorInformation(Pointer buffer,
+      DWORDByReference returnLength) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GlobalMemoryStatusEx(MEMORYSTATUSEX lpBuffer) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetFileTime(HANDLE hFile, FILETIME lpCreationTime,
+      FILETIME lpLastAccessTime, FILETIME lpLastWriteTime) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int SetFileTime(HANDLE hFile, FILETIME lpCreationTime,
+      FILETIME lpLastAccessTime, FILETIME lpLastWriteTime) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean SetFileAttributes(String lpFileName, DWORD dwFileAttributes) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public DWORD GetLogicalDriveStrings(DWORD nBufferLength, char[] lpBuffer) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetDiskFreeSpaceEx(String lpDirectoryName,
+      LARGE_INTEGER lpFreeBytesAvailable,
+      LARGE_INTEGER lpTotalNumberOfBytes,
+      LARGE_INTEGER lpTotalNumberOfFreeBytes) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean DeleteFile(String filename) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean CreatePipe(HANDLEByReference hReadPipe,
+      HANDLEByReference hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes,
+      int nSize) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean SetHandleInformation(HANDLE hObject, int dwMask, int dwFlags) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetFileAttributes(String lpFileName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetFileType(HANDLE hFile) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean DeviceIoControl(HANDLE hDevice, int dwIoControlCode,
+      Pointer lpInBuffer, int nInBufferSize, Pointer lpOutBuffer,
+      int nOutBufferSize, IntByReference lpBytesReturned,
+      Pointer lpOverlapped) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean GetDiskFreeSpaceEx(String lpDirectoryName,
+      LongByReference lpFreeBytesAvailable,
+      LongByReference lpTotalNumberOfBytes,
+      LongByReference lpTotalNumberOfFreeBytes) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public HANDLE CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean Process32First(HANDLE hSnapshot, PROCESSENTRY32 lppe) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean Process32Next(HANDLE hSnapshot, PROCESSENTRY32 lppe) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean SetEnvironmentVariable(String lpName, String lpValue) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetEnvironmentVariable(String lpName, char[] lpBuffer, int nSize) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public LCID GetSystemDefaultLCID() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public LCID GetUserDefaultLCID() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int GetPrivateProfileInt(String appName, String keyName,
+      int defaultValue, String fileName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public DWORD GetPrivateProfileString(String lpAppName, String lpKeyName,
+      String lpDefault, char[] lpReturnedString, DWORD nSize,
+      String lpFileName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public boolean WritePrivateProfileString(String lpAppName, String lpKeyName,
+      String lpString, String lpFileName) {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java b/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java
index 2b87cbe..8852150 100644
--- a/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java
+++ b/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java
@@ -14,25 +14,54 @@
 
 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 com.google.enterprise.adaptor.fs.AclView.GenericPermission.*;
+
 import static org.junit.Assert.*;
 import static org.junit.Assume.*;
 
+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.Preconditions;
+
+import com.google.enterprise.adaptor.fs.WinApi.Netapi32Ex;
+import com.google.enterprise.adaptor.fs.WinApi.Shlwapi;
+
 import org.junit.*;
 import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
 
+import com.sun.jna.Memory;
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import com.sun.jna.WString;
+import com.sun.jna.platform.win32.Advapi32;
 import com.sun.jna.platform.win32.Advapi32Util.Account;
+import com.sun.jna.platform.win32.Kernel32;
+import com.sun.jna.platform.win32.W32Errors;
 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 com.sun.jna.ptr.IntByReference;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 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.AclFileAttributeView;
 import java.nio.file.attribute.GroupPrincipal;
 import java.nio.file.attribute.UserPrincipal;
+import java.util.Arrays;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Set;
 
 /** Tests for {@link WindowsAclFileAttributeViews} */
@@ -41,12 +70,36 @@
   private final WindowsAclFileAttributeViews wafav =
       new TestAclFileAttributeViews();
 
+  private Path tempRoot;
+
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Before
+  public void setUp() throws Exception {
+    tempRoot = temp.getRoot().getCanonicalFile().toPath();
+  }
+
+  private Path newTempDir(String name) throws IOException {
+    return temp.newFolder(name).toPath().toRealPath();
+  }
+
+  private Path newTempFile(String name) throws IOException {
+    return temp.newFile(name).toPath().toRealPath();
+  }
+
+  private Path newTempFile(Path parent, String name) throws IOException {
+    Preconditions.checkArgument(parent.startsWith(tempRoot));
+    return Files.createFile(parent.resolve(name));
+  }
+
   @Test
   public void testNewAclEntryUnsupportedAccessType() throws Exception {
     WinNT.ACCESS_ACEStructure ace = new AceBuilder()
+        .setSid(new AccountSid())
         .setType(WinNT.SYSTEM_AUDIT_ACE_TYPE).build();
     assertNull(wafav.newAclEntry(ace));
   }
@@ -55,7 +108,7 @@
   public void testNewAclEntryUnresolvableSid() throws Exception {
     TestHelper.assumeOsIsWindows(); // For new Win32Exception().
     WinNT.ACCESS_ACEStructure ace = new AceBuilder()
-        .setSid(new AccountSid(null)).build();
+        .setSid(new AccountSid()).build();
     assertNull(wafav.newAclEntry(ace));
   }
 
@@ -227,60 +280,262 @@
     assertEquals(expected, aclEntry.flags());
   }
 
+  @Test
+  public void testGetAclViewsEmptyAcl() throws Exception {
+    AclFileAttributeViews aclViews = getAclViews();
+    assertNotNull(aclViews);
+    assertTrue(aclViews.getDirectAclView().getAcl().isEmpty());
+    assertTrue(aclViews.getInheritedAclView().getAcl().isEmpty());
+  }
+
+  @Test
+  public void testGetAclViewsSingleDirectAce() throws Exception {
+    AclFileAttributeView expected = new AclView(
+        user("domain\\user").type(ALLOW).perms(GENERIC_READ)
+        .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+    WinNT.ACCESS_ACEStructure ace = new AceBuilder()
+        .setSid(AccountSid.user("user", "domain"))
+        .setPerms(WinNT.GENERIC_READ)
+        .setFlags(WinNT.OBJECT_INHERIT_ACE, WinNT.CONTAINER_INHERIT_ACE)
+        .build();
+    AclFileAttributeViews aclViews = getAclViews(ace);
+    assertNotNull(aclViews);
+    assertTrue(aclViews.getInheritedAclView().getAcl().isEmpty());
+    assertEquals(expected.getAcl(), aclViews.getDirectAclView().getAcl());
+  }
+
+  @Test
+  public void testGetAclViewsSingleInheritedAce() throws Exception {
+    AclFileAttributeView expected = new AclView(
+        user("domain\\user").type(ALLOW).perms(GENERIC_READ)
+        .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+    WinNT.ACCESS_ACEStructure ace = new AceBuilder()
+        .setSid(AccountSid.user("user", "domain"))
+        .setPerms(WinNT.GENERIC_READ)
+        .setFlags(WinNT.OBJECT_INHERIT_ACE, WinNT.CONTAINER_INHERIT_ACE,
+                  WinNT.INHERITED_ACE)
+        .build();
+    AclFileAttributeViews aclViews = getAclViews(ace);
+    assertNotNull(aclViews);
+    assertTrue(aclViews.getDirectAclView().getAcl().isEmpty());
+    assertEquals(expected.getAcl(), aclViews.getInheritedAclView().getAcl());
+  }
+
+  @Test
+  public void testGetAclViewsInheritedAndDirectAces() throws Exception {
+    AclFileAttributeView expectedInherited = new AclView(
+        group("Everyone").type(ALLOW).perms(GENERIC_READ)
+        .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+        group("Administrators").type(ALLOW).perms(GENERIC_ALL)
+        .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+    AclFileAttributeView expectedDirect = new AclView(
+        user("BEDROCK\\Fred").type(ALLOW).perms(GENERIC_EXECUTE)
+        .flags(FILE_INHERIT),
+        user("BEDROCK\\Barney").type(DENY).perms(GENERIC_WRITE)
+        .flags(DIRECTORY_INHERIT));
+    AclFileAttributeViews aclViews = getAclViews(new AceBuilder()
+        .setSid(AccountSid.user("Fred", "BEDROCK"))
+        .setPerms(WinNT.GENERIC_EXECUTE)
+        .setFlags(WinNT.OBJECT_INHERIT_ACE)
+        .build(),
+        new AceBuilder()
+        .setSid(AccountSid.group("Everyone", null))
+        .setPerms(WinNT.GENERIC_READ)
+        .setFlags(WinNT.OBJECT_INHERIT_ACE, WinNT.CONTAINER_INHERIT_ACE,
+                  WinNT.INHERITED_ACE)
+        .build(),
+        new AceBuilder()
+        .setSid(AccountSid.group("Administrators", null))
+        .setPerms(WinNT.GENERIC_ALL)
+        .setFlags(WinNT.OBJECT_INHERIT_ACE, WinNT.CONTAINER_INHERIT_ACE,
+                  WinNT.INHERITED_ACE)
+        .build(),
+        new AceBuilder()
+        .setSid(AccountSid.user("Barney", "BEDROCK"))
+        .setType(WinNT.ACCESS_DENIED_ACE_TYPE)
+        .setPerms(WinNT.GENERIC_WRITE)
+        .setFlags(WinNT.CONTAINER_INHERIT_ACE)
+        .build());
+
+    assertNotNull(aclViews);
+    assertEquals(expectedDirect.getAcl(), aclViews.getDirectAclView().getAcl());
+    assertEquals(expectedInherited.getAcl(),
+                 aclViews.getInheritedAclView().getAcl());
+  }
+
+  private AclFileAttributeViews getAclViews(WinNT.ACCESS_ACEStructure... aces)
+      throws Exception {
+    WinNT.ACL acl = new WinNT.ACL();
+    WinNT.SECURITY_DESCRIPTOR_RELATIVE securityDescriptor =
+        new WinNT.SECURITY_DESCRIPTOR_RELATIVE();
+    int totalSize = securityDescriptor.size() + acl.size();
+    for (WinNT.ACCESS_ACEStructure ace : aces) {
+      totalSize += ace.AceSize;
+    }
+
+    // Serialize the structures into a buffer.
+    final byte[] buffer = new byte[totalSize];
+    int offset = 0;
+    // The start of the ACL follows the securityDescriptor in memory.
+    securityDescriptor.Dacl = securityDescriptor.size();
+    securityDescriptor.write();
+    securityDescriptor.getPointer().read(0, buffer, offset,
+                                         securityDescriptor.size());
+    offset += securityDescriptor.size();
+    acl.AceCount = (short) aces.length;
+    acl.write();
+    acl.getPointer().read(0, buffer, offset, acl.size());
+    offset += acl.size();
+    for (WinNT.ACCESS_ACEStructure ace : aces) {
+      ace.write();
+      ace.getPointer().read(0, buffer, offset, ace.AceSize);
+      offset += ace.AceSize;
+    }
+
+    Kernel32 kernel32 = new UnsupportedKernel32() {
+        @Override
+        public int GetLastError() {
+          // For when GetFileSecurity returns false.
+          return W32Errors.ERROR_INSUFFICIENT_BUFFER;
+        }
+      };
+    Advapi32 advapi32 = new UnsupportedAdvapi32() {
+        @Override
+        public boolean GetFileSecurity(WString lpFileName,
+            int RequestedInformation, Pointer pointer, int nLength,
+            IntByReference lpnLengthNeeded) {
+          if (nLength < buffer.length) {
+            lpnLengthNeeded.setValue(buffer.length);
+            return false;
+          } else {
+            pointer.write(0, buffer, 0, nLength);
+            return true;
+          }
+        }
+      };
+
+    WindowsAclFileAttributeViews wafav =
+        new TestAclFileAttributeViews(advapi32, kernel32, null, null, null);
+    return wafav.getAclViews(newTempFile("test"));
+  }
+
+  /**
+   * Test the first IOException that can be thrown out of getFileSecurity().
+   * In that method, the first call to advapi32.GetFileSecurity() is
+   * expected to return W32Errors.ERROR_INSUFFICIENT_BUFFER and the
+   * required buffer size.  This test returns a different error, which
+   * gets rethrown as an IOException.
+   */
+  @Test
+  public void testGetAclViewsException1() throws Exception {
+    TestHelper.assumeOsIsWindows(); // For new Win32Exception().
+    thrown.expect(IOException.class);
+    testGetAclViewsException(W32Errors.ERROR_MORE_DATA);
+  }
+
+  /**
+   * Test the second IOException that can be thrown out of getFileSecurity().
+   * In that method, the second call to advapi32.GetFileSecurity() is
+   * not expected to return any error.  This test returns an error on both
+   * calls - the expected error for the first call and that same error for the
+   * second call.
+   */
+  @Test
+  public void testGetAclViewsException2() throws Exception {
+    TestHelper.assumeOsIsWindows(); // For new Win32Exception().
+    thrown.expect(IOException.class);
+    testGetAclViewsException(W32Errors.ERROR_INSUFFICIENT_BUFFER);
+  }
+
+  private void testGetAclViewsException(final int errorCode) throws Exception {
+    Kernel32 kernel32 = new UnsupportedKernel32() {
+        @Override
+        public int GetLastError() {
+          return errorCode;
+        }
+      };
+    Advapi32 advapi32 = new UnsupportedAdvapi32() {
+        @Override
+        public boolean GetFileSecurity(WString lpFileName,
+            int RequestedInformation, Pointer pointer, int nLength,
+            IntByReference lpnLengthNeeded) {
+          lpnLengthNeeded.setValue(10);
+          return false;
+        }
+      };
+    WindowsAclFileAttributeViews wafav =
+        new TestAclFileAttributeViews(advapi32, kernel32, null, null, null);
+    wafav.getAclViews(newTempFile("test"));
+  }
+
   static class AceBuilder {
-    private Ace ace = new Ace();
+    private byte type;
+    private byte flags;
+    private int perms;
+    private AccountSid sid;
 
     public AceBuilder setType(byte type) {
-      ace.AceType = type;
+      this.type = type;
       return this;
     }
 
     public AceBuilder setFlags(byte... flags) {
       for (byte flag : flags) {
-        ace.AceFlags |= flag;
+        this.flags |= flag;
       }
       return this;
     }
 
     public AceBuilder setPerms(int... perms) {
       for (int perm : perms) {
-        ace.Mask |= perm;
+        this.perms |= perm;
       }
       return this;
     }
 
-    public AceBuilder setSid(WinNT.PSID sid) {
-      ace.setSID(sid);
+    public AceBuilder setSid(AccountSid sid) {
+      this.sid = sid;
       return this;
     }
 
     public WinNT.ACCESS_ACEStructure build() {
+      // Because ACCESS_ACEStructure does not allow me to set the SID
+      // directly, I must create a serialized ACE containing a Pointer
+      // to my AccountSid, then create a new ACE from that memory.
+      WinNT.ACCESS_ACEStructure ace = new Ace();
+      ace.AceType = type;
+      ace.AceFlags = flags;
+      ace.Mask = perms;
+      ace.AceSize = (short)(ace.size() + Pointer.SIZE);
+      ace.write();
+      byte[] buffer = new byte[ace.AceSize];
+      ace.getPointer().read(0, buffer, 0, ace.size());
+      Memory memory = new Memory(buffer.length);
+      memory.write(0, buffer, 0, ace.size());
+      sid.write();
+      // See ACCESS_ACEStructure(Pointer p) constructor for mystery offsets.
+      memory.setPointer(4 + 4, sid.getPointer());
+      ace = new Ace(memory);
+      assertEquals(ace.getSID().sid, sid.getPointer());
       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;
+    public Ace() {
     }
 
-    @Override
-    public WinNT.PSID getSID() {
-      return (sid != null) ? sid : super.getSID();
+    public Ace(Pointer p) {
+      super(p);
     }
 
     @Override
     public String getSidString() {
-      return (sid != null) ? sid.toString() : super.getSidString();
+      return new AccountSid(getSID().sid).toString();
     }
   }
 
-  /** A SID implemention that wraps an Account, avoiding AD lookup. */
-  static class AccountSid extends WinNT.PSID {
-    private final Account account;
+  public static class AccountSid extends Structure {
 
     public static AccountSid user(String name, String domain) {
       return new AccountSid(SID_NAME_USE.SidTypeUser, name, domain);
@@ -290,31 +545,47 @@
       return new AccountSid(SID_NAME_USE.SidTypeGroup, name, domain);
     }
 
-    public AccountSid(Account account) {
-      this.account = account;
+    @Override
+    protected List getFieldOrder() {
+      return Arrays.asList(new String[] { "type", "name", "domain" });
+    }
+
+    public int type;
+    public String name;
+    public String domain;
+
+    public AccountSid() {
+    }
+
+    public AccountSid(Pointer p) {
+      super(p);
+      read();
     }
 
     public AccountSid(int type, String name, String domain) {
-      account = new Account();
-      account.accountType = type;
-      account.name = name;
-      account.domain = domain;
+      this.type = type;
+      this.name = name;
+      this.domain = domain;
     }
 
     public Account getAccount() throws Win32Exception {
-      if (account == null) {
+      if (name == null && domain == null) {
         throw new Win32Exception(WinError.ERROR_NONE_MAPPED);
+      } else {
+        Account account = new Account();
+        account.accountType = type;
+        account.name = name;
+        account.domain = domain;
+        return account;
       }
-      return account;
     }
 
     @Override
     public String toString() {
-      if (account == null) {
+      if (name == null && domain == null) {
         return "null";
       } else {
-        return (account.domain == null) ? account.name
-            : account.domain + "\\" + account.name;
+        return (domain == null) ? name : domain + "\\" + name;
       }
     }
   }
@@ -328,13 +599,14 @@
       super(null, null, null, null, null);
     }
 
+    public TestAclFileAttributeViews(Advapi32 advapi32, Kernel32 kernel32,
+      Mpr mpr, Netapi32Ex netapi32, Shlwapi shlwapi) {
+      super(advapi32, kernel32, mpr, netapi32, shlwapi);
+    }
+
     @Override
     Account getAccountBySid(WinNT.PSID sid) throws Win32Exception {
-      if (sid instanceof AccountSid) {
-        return ((AccountSid) sid).getAccount();
-      } else {
-        return super.getAccountBySid(sid);
-      }
+      return new AccountSid(sid.sid).getAccount();
     }
   }
 }