diff --git a/runtime/bin/builtin_natives.cc b/runtime/bin/builtin_natives.cc index 33be02e9fd9..b4b44d09aa0 100644 --- a/runtime/bin/builtin_natives.cc +++ b/runtime/bin/builtin_natives.cc @@ -44,6 +44,7 @@ V(File_CreateLink, 2) \ V(File_LinkTarget, 1) \ V(File_Delete, 1) \ + V(File_DeleteLink, 1) \ V(File_Directory, 1) \ V(File_FullPath, 1) \ V(File_OpenStdio, 1) \ diff --git a/runtime/bin/directory_android.cc b/runtime/bin/directory_android.cc index 1fb42cb1e70..1b51fbd5413 100644 --- a/runtime/bin/directory_android.cc +++ b/runtime/bin/directory_android.cc @@ -239,7 +239,11 @@ static bool DeleteRecursively(PathBuffer* path) { if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) { return false; } else if (S_ISLNK(st.st_mode)) { - return (remove(path->data) == 0); + if (TEMP_FAILURE_RETRY(stat(path->data, &st)) == -1) { + return false; + } else if (S_ISDIR(st.st_mode)) { + return (unlink(path->data) == 0); + } } if (!path->Add(File::PathSeparator())) return false; @@ -441,7 +445,11 @@ char* Directory::CreateTemp(const char* const_template) { bool Directory::Delete(const char* dir_name, bool recursive) { if (!recursive) { - return (TEMP_FAILURE_RETRY(remove(dir_name)) == 0); + if (File::GetType(dir_name, false) == File::kIsLink && + File::GetType(dir_name, true) == File::kIsDirectory) { + return (TEMP_FAILURE_RETRY(unlink(dir_name)) == 0); + } + return (TEMP_FAILURE_RETRY(rmdir(dir_name)) == 0); } else { PathBuffer path; if (!path.Add(dir_name)) { diff --git a/runtime/bin/directory_linux.cc b/runtime/bin/directory_linux.cc index a316633d8e5..28b26a484e5 100644 --- a/runtime/bin/directory_linux.cc +++ b/runtime/bin/directory_linux.cc @@ -239,7 +239,11 @@ static bool DeleteRecursively(PathBuffer* path) { if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) { return false; } else if (S_ISLNK(st.st_mode)) { - return (remove(path->data) == 0); + if (TEMP_FAILURE_RETRY(stat(path->data, &st)) == -1) { + return false; + } else if (S_ISDIR(st.st_mode)) { + return (unlink(path->data) == 0); + } } if (!path->Add(File::PathSeparator())) return false; @@ -409,7 +413,11 @@ char* Directory::CreateTemp(const char* const_template) { bool Directory::Delete(const char* dir_name, bool recursive) { if (!recursive) { - return (TEMP_FAILURE_RETRY(remove(dir_name)) == 0); + if (File::GetType(dir_name, false) == File::kIsLink && + File::GetType(dir_name, true) == File::kIsDirectory) { + return (TEMP_FAILURE_RETRY(unlink(dir_name)) == 0); + } + return (TEMP_FAILURE_RETRY(rmdir(dir_name)) == 0); } else { PathBuffer path; if (!path.Add(dir_name)) { diff --git a/runtime/bin/directory_macos.cc b/runtime/bin/directory_macos.cc index 401f80cf94d..c59555a4312 100644 --- a/runtime/bin/directory_macos.cc +++ b/runtime/bin/directory_macos.cc @@ -239,7 +239,11 @@ static bool DeleteRecursively(PathBuffer* path) { if (TEMP_FAILURE_RETRY(lstat(path->data, &st)) == -1) { return false; } else if (S_ISLNK(st.st_mode)) { - return (remove(path->data) == 0); + if (TEMP_FAILURE_RETRY(stat(path->data, &st)) == -1) { + return false; + } else if (S_ISDIR(st.st_mode)) { + return (unlink(path->data) == 0); + } } if (!path->Add(File::PathSeparator())) return false; @@ -409,7 +413,11 @@ char* Directory::CreateTemp(const char* const_template) { bool Directory::Delete(const char* dir_name, bool recursive) { if (!recursive) { - return (TEMP_FAILURE_RETRY(remove(dir_name)) == 0); + if (File::GetType(dir_name, false) == File::kIsLink && + File::GetType(dir_name, true) == File::kIsDirectory) { + return (TEMP_FAILURE_RETRY(unlink(dir_name)) == 0); + } + return (TEMP_FAILURE_RETRY(rmdir(dir_name)) == 0); } else { PathBuffer path; if (!path.Add(dir_name)) { diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc index d4b0aafa6d2..7ee9b593cd2 100644 --- a/runtime/bin/directory_win.cc +++ b/runtime/bin/directory_win.cc @@ -6,6 +6,7 @@ #if defined(TARGET_OS_WINDOWS) #include "bin/directory.h" +#include "bin/file.h" #include // NOLINT #include // NOLINT @@ -48,6 +49,25 @@ class PathBuffer { } }; +// If link_name points to a link, IsBrokenLink will return true if link_name +// points to an invalid target. +static bool IsBrokenLink(const wchar_t* link_name) { + HANDLE handle = CreateFileW( + link_name, + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + if (handle == INVALID_HANDLE_VALUE) { + return true; + } else { + CloseHandle(handle); + return false; + } +} + // Forward declarations. static bool ListRecursively(PathBuffer* path, @@ -122,18 +142,13 @@ static bool HandleEntry(LPWIN32_FIND_DATAW find_file_data, if (!follow_links) { return HandleLink(find_file_data->cFileName, path, listing); } - // Attempt to list the directory, to see if it's a valid link or not. int path_length = path->length; if (!path->Add(find_file_data->cFileName)) return false; - if (!path->Add(L"\\*")) return false; - WIN32_FIND_DATAW tmp_file_data; - HANDLE find_handle = FindFirstFileW(path->data, &tmp_file_data); + bool broken = IsBrokenLink(path->data); path->Reset(path_length); - if (find_handle == INVALID_HANDLE_VALUE) { - // Invalid handle, report as (broken) link. + if (broken) { + // Report as (broken) link. return HandleLink(find_file_data->cFileName, path, listing); - } else { - FindClose(find_handle); } } if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { @@ -253,8 +268,12 @@ static bool DeleteRecursively(PathBuffer* path) { DWORD attributes = GetFileAttributesW(path->data); if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { - // Just delete the junction itself. - return RemoveDirectoryW(path->data) != 0; + if (IsBrokenLink(path->data)) { + return false; + } else { + // Just delete the junction itself. + return RemoveDirectoryW(path->data) != 0; + } } if (!path->Add(L"\\*")) return false; @@ -318,6 +337,7 @@ static Directory::ExistsResult ExistsHelper(const wchar_t* dir_name) { } } bool exists = (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + exists = exists && !IsBrokenLink(dir_name); return exists ? Directory::EXISTS : Directory::DOES_NOT_EXIST; } @@ -407,13 +427,16 @@ bool Directory::Delete(const char* dir_name, bool recursive) { bool result = false; const wchar_t* system_dir_name = StringUtils::Utf8ToWide(dir_name); if (!recursive) { - result = (RemoveDirectoryW(system_dir_name) != 0); + if (File::GetType(dir_name, true) == File::kIsDirectory) { + result = (RemoveDirectoryW(system_dir_name) != 0); + } else { + SetLastError(ERROR_DIRECTORY); + } } else { PathBuffer path; - if (!path.Add(system_dir_name)) { - return false; + if (path.Add(system_dir_name)) { + result = DeleteRecursively(&path); } - result = DeleteRecursively(&path); } free(const_cast(system_dir_name)); return result; diff --git a/runtime/bin/file.cc b/runtime/bin/file.cc index 5c54ed85793..70833d11063 100644 --- a/runtime/bin/file.cc +++ b/runtime/bin/file.cc @@ -488,6 +488,22 @@ void FUNCTION_NAME(File_Delete)(Dart_NativeArguments args) { } +void FUNCTION_NAME(File_DeleteLink)(Dart_NativeArguments args) { + Dart_EnterScope(); + const char* str = + DartUtils::GetStringValue(Dart_GetNativeArgument(args, 0)); + bool result = File::DeleteLink(str); + if (result) { + Dart_SetReturnValue(args, Dart_NewBoolean(result)); + } else { + Dart_Handle err = DartUtils::NewDartOSError(); + if (Dart_IsError(err)) Dart_PropagateError(err); + Dart_SetReturnValue(args, err); + } + Dart_ExitScope(); +} + + void FUNCTION_NAME(File_Directory)(Dart_NativeArguments args) { Dart_EnterScope(); const char* str = @@ -997,6 +1013,20 @@ static CObject* FileWriteListRequest(const CObjectArray& request) { } +static CObject* FileDeleteLinkRequest(const CObjectArray& request) { + if (request.Length() == 2 && request[1]->IsString()) { + CObjectString filename(request[1]); + bool result = File::DeleteLink(filename.CString()); + if (result) { + return CObject::True(); + } else { + return CObject::NewOSError(); + } + } + return CObject::False(); +} + + static void FileService(Dart_Port dest_port_id, Dart_Port reply_port_id, Dart_CObject* message) { @@ -1063,6 +1093,9 @@ static void FileService(Dart_Port dest_port_id, case File::kWriteListRequest: response = FileWriteListRequest(request); break; + case File::kDeleteLinkRequest: + response = FileDeleteLinkRequest(request); + break; default: UNREACHABLE(); } diff --git a/runtime/bin/file.h b/runtime/bin/file.h index 0895a8466d3..ac5af00491b 100644 --- a/runtime/bin/file.h +++ b/runtime/bin/file.h @@ -76,7 +76,8 @@ class File { kWriteByteRequest = 15, kReadRequest = 16, kReadListRequest = 17, - kWriteListRequest = 18 + kWriteListRequest = 18, + kDeleteLinkRequest = 19 }; ~File(); @@ -131,6 +132,7 @@ class File { static bool Create(const char* path); static bool CreateLink(const char* path, const char* target); static bool Delete(const char* path); + static bool DeleteLink(const char* path); static off_t LengthFromPath(const char* path); static time_t LastModified(const char* path); static char* LinkTarget(const char* pathname); diff --git a/runtime/bin/file_android.cc b/runtime/bin/file_android.cc index 21dcaa956b3..2d7c99d1cba 100644 --- a/runtime/bin/file_android.cc +++ b/runtime/bin/file_android.cc @@ -166,11 +166,25 @@ bool File::CreateLink(const char* name, const char* target) { bool File::Delete(const char* name) { - int status = TEMP_FAILURE_RETRY(remove(name)); - if (status == -1) { - return false; + File::Type type = File::GetType(name, true); + if (type == kIsFile) { + return TEMP_FAILURE_RETRY(unlink(name)) == 0; + } else if (type == kIsDirectory) { + errno = EISDIR; + } else { + errno = ENOENT; } - return true; + return false; +} + + +bool File::DeleteLink(const char* name) { + File::Type type = File::GetType(name, false); + if (type == kIsLink) { + return TEMP_FAILURE_RETRY(unlink(name)) == 0; + } + errno = EINVAL; + return false; } diff --git a/runtime/bin/file_linux.cc b/runtime/bin/file_linux.cc index ceac63052bb..8126ff98179 100644 --- a/runtime/bin/file_linux.cc +++ b/runtime/bin/file_linux.cc @@ -167,11 +167,25 @@ bool File::CreateLink(const char* name, const char* target) { bool File::Delete(const char* name) { - int status = TEMP_FAILURE_RETRY(remove(name)); - if (status == -1) { - return false; + File::Type type = File::GetType(name, true); + if (type == kIsFile) { + return TEMP_FAILURE_RETRY(unlink(name)) == 0; + } else if (type == kIsDirectory) { + errno = EISDIR; + } else { + errno = ENOENT; } - return true; + return false; +} + + +bool File::DeleteLink(const char* name) { + File::Type type = File::GetType(name, false); + if (type == kIsLink) { + return TEMP_FAILURE_RETRY(unlink(name)) == 0; + } + errno = EINVAL; + return false; } diff --git a/runtime/bin/file_macos.cc b/runtime/bin/file_macos.cc index 301424b3daf..a5af90428f1 100644 --- a/runtime/bin/file_macos.cc +++ b/runtime/bin/file_macos.cc @@ -169,11 +169,25 @@ bool File::CreateLink(const char* name, const char* target) { bool File::Delete(const char* name) { - int status = TEMP_FAILURE_RETRY(remove(name)); - if (status == -1) { - return false; + File::Type type = File::GetType(name, true); + if (type == kIsFile) { + return TEMP_FAILURE_RETRY(unlink(name)) == 0; + } else if (type == kIsDirectory) { + errno = EISDIR; + } else { + errno = ENOENT; } - return true; + return false; +} + + +bool File::DeleteLink(const char* name) { + File::Type type = File::GetType(name, false); + if (type == kIsLink) { + return TEMP_FAILURE_RETRY(unlink(name)) == 0; + } + errno = EINVAL; + return false; } diff --git a/runtime/bin/file_patch.dart b/runtime/bin/file_patch.dart index e1e1bfaac08..c246002ee8d 100644 --- a/runtime/bin/file_patch.dart +++ b/runtime/bin/file_patch.dart @@ -13,6 +13,7 @@ patch class _File { native "File_CreateLink"; /* patch */ static _linkTarget(String path) native "File_LinkTarget"; /* patch */ static _delete(String path) native "File_Delete"; + /* patch */ static _deleteLink(String path) native "File_DeleteLink"; /* patch */ static _directory(String path) native "File_Directory"; /* patch */ static _lengthFromPath(String path) native "File_LengthFromPath"; /* patch */ static _lastModified(String path) native "File_LastModified"; diff --git a/runtime/bin/file_win.cc b/runtime/bin/file_win.cc index 3780fca056c..86e808c1dda 100644 --- a/runtime/bin/file_win.cc +++ b/runtime/bin/file_win.cc @@ -259,19 +259,25 @@ bool File::CreateLink(const char* utf8_name, const char* utf8_target) { bool File::Delete(const char* name) { const wchar_t* system_name = StringUtils::Utf8ToWide(name); + int status = _wremove(system_name); + free(const_cast(system_name)); + return status != -1; +} + + +bool File::DeleteLink(const char* name) { + const wchar_t* system_name = StringUtils::Utf8ToWide(name); + bool result = false; DWORD attributes = GetFileAttributesW(system_name); if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { // It's a junction(link), delete it. - return RemoveDirectoryW(system_name) != 0; + result = (RemoveDirectoryW(system_name) != 0); } else { - int status = _wremove(system_name); - free(const_cast(system_name)); - if (status == -1) { - return false; - } - return true; + SetLastError(ERROR_NOT_A_REPARSE_POINT); } + free(const_cast(system_name)); + return result; } @@ -480,15 +486,11 @@ File::StdioHandleType File::GetStdioHandleType(int fd) { File::Type File::GetType(const char* pathname, bool follow_links) { const wchar_t* name = StringUtils::Utf8ToWide(pathname); - WIN32_FIND_DATAW file_data; - HANDLE find_handle = FindFirstFileW(name, &file_data); - if (find_handle == INVALID_HANDLE_VALUE) { - // TODO(whesse): Distinguish other errors from does not exist. - return File::kDoesNotExist; - } - FindClose(find_handle); - DWORD attributes = file_data.dwFileAttributes; - if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { + DWORD attributes = GetFileAttributesW(name); + File::Type result = kIsFile; + if (attributes == INVALID_FILE_ATTRIBUTES) { + result = kDoesNotExist; + } else if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { if (follow_links) { HANDLE dir_handle = CreateFileW( name, @@ -499,26 +501,19 @@ File::Type File::GetType(const char* pathname, bool follow_links) { FILE_FLAG_BACKUP_SEMANTICS, NULL); if (dir_handle == INVALID_HANDLE_VALUE) { - // TODO(whesse): Distinguish other errors from does not exist. - return File::kDoesNotExist; + result = File::kIsLink; } else { CloseHandle(dir_handle); - return File::kIsDirectory; + result = File::kIsDirectory; } } else { - DWORD reparse_tag = file_data.dwReserved0; - if (reparse_tag == IO_REPARSE_TAG_SYMLINK || - reparse_tag == IO_REPARSE_TAG_MOUNT_POINT) { - return File::kIsLink; - } else { - return File::kDoesNotExist; - } + result = kIsLink; } } else if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { - return File::kIsDirectory; - } else { - return File::kIsFile; + result = kIsDirectory; } + free(const_cast(name)); + return result; } diff --git a/sdk/lib/io/file_impl.dart b/sdk/lib/io/file_impl.dart index 57b8cd13749..1a9e46888e2 100644 --- a/sdk/lib/io/file_impl.dart +++ b/sdk/lib/io/file_impl.dart @@ -220,6 +220,7 @@ const int _WRITE_BYTE_REQUEST = 15; const int _READ_REQUEST = 16; const int _READ_LIST_REQUEST = 17; const int _WRITE_LIST_REQUEST = 18; +const int _DELETE_LINK_REQUEST = 19; // Base class for _File and _RandomAccessFile with shared functions. class _FileBase { diff --git a/sdk/lib/io/link.dart b/sdk/lib/io/link.dart index fee4f97fc62..b91ae5a64e5 100644 --- a/sdk/lib/io/link.dart +++ b/sdk/lib/io/link.dart @@ -108,6 +108,8 @@ abstract class Link extends FileSystemEntity { class _Link extends FileSystemEntity implements Link { final String path; + SendPort _fileService; + _Link(String this.path); _Link.fromPath(Path inputPath) : path = inputPath.toNativePath(); @@ -134,9 +136,7 @@ class _Link extends FileSystemEntity implements Link { target = _makeWindowsLinkTarget(target); } var result = _File._createLink(path, target); - if (result is OSError) { - throw new LinkIOException("Error in Link.createSync", result); - } + throwIfError(result, "Cannot create link '$path'"); } // Put target into the form "\??\C:\my\target\dir". @@ -164,11 +164,21 @@ class _Link extends FileSystemEntity implements Link { } Future delete() { - return new File(path).delete().then((_) => this); + _ensureFileService(); + List request = new List(2); + request[0] = _DELETE_LINK_REQUEST; + request[1] = path; + return _fileService.call(request).then((response) { + if (_isErrorResponse(response)) { + throw _exceptionFromResponse(response, "Cannot delete link '$path'"); + } + return this; + }); } void deleteSync() { - new File(path).deleteSync(); + var result = _File._deleteLink(path); + throwIfError(result, "Cannot delete link '$path'"); } Future target() { @@ -178,11 +188,25 @@ class _Link extends FileSystemEntity implements Link { String targetSync() { var result = _File._linkTarget(path); - if (result is OSError) { - throw new LinkIOException("Error in Link.targetSync", result); - } + throwIfError(result, "Cannot read link '$path'"); return result; } + + static throwIfError(Object result, String msg) { + if (result is OSError) { + throw new FileIOException(msg, result); + } + } + + bool _isErrorResponse(response) { + return response is List && response[0] != _SUCCESS_RESPONSE; + } + + void _ensureFileService() { + if (_fileService == null) { + _fileService = _FileUtils._newServicePort(); + } + } } diff --git a/tests/standalone/io/file_system_delete_test.dart b/tests/standalone/io/file_system_delete_test.dart new file mode 100644 index 00000000000..8768ed7a0ca --- /dev/null +++ b/tests/standalone/io/file_system_delete_test.dart @@ -0,0 +1,393 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "dart:async"; +import "dart:io"; + +Future throws(callback()) { + return new Future.immediate(null) + .then((_) => callback()) + .then((_) { throw "Expected error"; }, + onError: (_) {}); +} + +void testDeleteFileSync() { + var tmp = new Directory("").createTempSync(); + var path = "${tmp.path}${Platform.pathSeparator}"; + + var file = new File("${path}myFile"); + + file.createSync(); + + Expect.isTrue(file.existsSync()); + new File(file.path).deleteSync(); + Expect.isFalse(file.existsSync()); + + file.createSync(); + + Expect.isTrue(file.existsSync()); + Expect.throws(() => new Directory(file.path).deleteSync()); + Expect.isTrue(file.existsSync()); + + Expect.isTrue(file.existsSync()); + Expect.throws(() => new Directory(file.path).deleteSync(recursive: true)); + Expect.isTrue(file.existsSync()); + + Expect.isTrue(file.existsSync()); + Expect.throws(() => new Link(file.path).deleteSync()); + Expect.isTrue(file.existsSync()); + + file.deleteSync(); + Expect.isFalse(file.existsSync()); + + tmp.deleteSync(); +} + +void testDeleteFile() { + new Directory("").createTemp().then((tmp) { + var path = "${tmp.path}${Platform.pathSeparator}"; + var file = new File("${path}myFile"); + return file.create() + .then((_) => file.exists().then(Expect.isTrue)) + .then((_) => new File(file.path).delete()) + .then((_) => file.exists().then(Expect.isFalse)) + + .then((_) => file.create()) + + .then((_) => file.exists().then(Expect.isTrue)) + .then((_) => throws(() => new Directory(file.path).delete())) + .then((_) => file.exists().then(Expect.isTrue)) + + .then((_) => file.exists().then(Expect.isTrue)) + .then((_) => throws( + () => new Directory(file.path).delete(recursive: true))) + .then((_) => file.exists().then(Expect.isTrue)) + + .then((_) => file.exists().then(Expect.isTrue)) + .then((_) => throws(() => new Link(file.path).delete())) + .then((_) => file.exists().then(Expect.isTrue)) + + .then((_) => file.delete()) + .then((_) => tmp.delete()); + }); +} + +void testDeleteDirectorySync() { + var tmp = new Directory("").createTempSync(); + var path = "${tmp.path}${Platform.pathSeparator}"; + + var dir = new Directory("${path}myDirectory"); + + dir.createSync(); + + Expect.isTrue(dir.existsSync()); + new Directory(dir.path).deleteSync(); + Expect.isFalse(dir.existsSync()); + + dir.createSync(); + + Expect.isTrue(dir.existsSync()); + new Directory(dir.path).deleteSync(recursive: true); + Expect.isFalse(dir.existsSync()); + + dir.createSync(); + + Expect.isTrue(dir.existsSync()); + Expect.throws(() => new File(dir.path).deleteSync()); + Expect.isTrue(dir.existsSync()); + + Expect.isTrue(dir.existsSync()); + Expect.throws(() => new Link(dir.path).deleteSync()); + Expect.isTrue(dir.existsSync()); + + dir.deleteSync(); + Expect.isFalse(dir.existsSync()); + + tmp.deleteSync(); +} + +void testDeleteDirectory() { + new Directory("").createTemp().then((tmp) { + var path = "${tmp.path}${Platform.pathSeparator}"; + var dir = new Directory("${path}myDirectory"); + return dir.create() + .then((_) => dir.exists().then(Expect.isTrue)) + .then((_) => new Directory(dir.path).delete()) + .then((_) => dir.exists().then(Expect.isFalse)) + + .then((_) => dir.create()) + + .then((_) => dir.exists().then(Expect.isTrue)) + .then((_) => new Directory(dir.path).delete(recursive: true)) + .then((_) => dir.exists().then(Expect.isFalse)) + + .then((_) => dir.create()) + + .then((_) => dir.exists().then(Expect.isTrue)) + .then((_) => throws(() => new File(dir.path).delete())) + .then((_) => dir.exists().then(Expect.isTrue)) + + .then((_) => dir.exists().then(Expect.isTrue)) + .then((_) => throws(() => new Link(dir.path).delete())) + .then((_) => dir.exists().then(Expect.isTrue)) + + .then((_) => dir.delete()) + .then((_) => tmp.delete()); + }); +} + +void testDeleteFileLinkSync() { + var tmp = new Directory("").createTempSync(); + var path = "${tmp.path}${Platform.pathSeparator}"; + + var file = new File("${path}myFile"); + file.createSync(); + + var link = new Link("${path}myLink"); + + link.createSync(file.path); + + Expect.isTrue(link.existsSync()); + new File(link.path).deleteSync(); + Expect.isFalse(link.existsSync()); + + link.createSync(file.path); + + Expect.isTrue(link.existsSync()); + new Link(link.path).deleteSync(); + Expect.isFalse(link.existsSync()); + + link.createSync(file.path); + + Expect.isTrue(link.existsSync()); + Expect.throws(() => new Directory(link.path).deleteSync()); + Expect.isTrue(link.existsSync()); + + Expect.isTrue(link.existsSync()); + Expect.throws(() => new Directory(link.path).deleteSync(recursive: true)); + Expect.isTrue(link.existsSync()); + + link.deleteSync(); + Expect.isFalse(link.existsSync()); + + Expect.isTrue(file.existsSync()); + file.deleteSync(); + Expect.isFalse(file.existsSync()); + + tmp.deleteSync(); +} + +void testDeleteFileLink() { + new Directory("").createTemp().then((tmp) { + var path = "${tmp.path}${Platform.pathSeparator}"; + var file = new File("${path}myFile"); + var link = new Link("${path}myLink"); + return file.create() + .then((_) => link.create(file.path)) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => new File(link.path).delete()) + .then((_) => link.exists().then(Expect.isFalse)) + + .then((_) => link.create(file.path)) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => new Link(link.path).delete()) + .then((_) => link.exists().then(Expect.isFalse)) + + .then((_) => link.create(file.path)) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => throws(() => new Directory(link.path).delete())) + .then((_) => link.exists().then(Expect.isTrue)) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => throws( + () => new Directory(link.path).delete(recursive: true))) + .then((_) => link.exists().then(Expect.isTrue)) + + .then((_) => link.deleteSync()) + .then((_) => link.exists().then(Expect.isFalse)) + + .then((_) => file.exists().then(Expect.isTrue)) + .then((_) => file.delete()) + .then((_) => file.exists().then(Expect.isFalse)) + + .then((_) => tmp.delete()); + }); +} + +void testDeleteDirectoryLinkSync() { + var tmp = new Directory("").createTempSync(); + var path = "${tmp.path}${Platform.pathSeparator}"; + + var directory = new Directory("${path}myDirectory"); + directory.createSync(); + + var link = new Link("${path}myLink"); + + link.createSync(directory.path); + + Expect.isTrue(link.existsSync()); + new Link(link.path).deleteSync(); + Expect.isFalse(link.existsSync()); + + link.createSync(directory.path); + + Expect.isTrue(link.existsSync()); + new Directory(link.path).deleteSync(); + Expect.isFalse(link.existsSync()); + + link.createSync(directory.path); + + Expect.isTrue(link.existsSync()); + new Directory(link.path).deleteSync(recursive: true); + Expect.isFalse(link.existsSync()); + + link.createSync(directory.path); + + Expect.isTrue(link.existsSync()); + Expect.throws(() => new File(link.path).deleteSync()); + Expect.isTrue(link.existsSync()); + + link.deleteSync(); + Expect.isFalse(link.existsSync()); + + Expect.isTrue(directory.existsSync()); + directory.deleteSync(); + Expect.isFalse(directory.existsSync()); + + tmp.deleteSync(); +} + +void testDeleteDirectoryLink() { + new Directory("").createTemp().then((tmp) { + var path = "${tmp.path}${Platform.pathSeparator}"; + var dir = new Directory("${path}myDir"); + var link = new Link("${path}myLink"); + return dir.create() + .then((_) => link.create(dir.path)) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => new Directory(link.path).delete()) + .then((_) => link.exists().then(Expect.isFalse)) + + .then((_) => link.create(dir.path)) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => new Directory(link.path).delete(recursive: true)) + .then((_) => link.exists().then(Expect.isFalse)) + + .then((_) => link.create(dir.path)) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => new Link(link.path).delete()) + .then((_) => link.exists().then(Expect.isFalse)) + + .then((_) => link.create(dir.path)) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => throws(() => new File(link.path).delete())) + .then((_) => link.exists().then(Expect.isTrue)) + + .then((_) => link.deleteSync()) + .then((_) => link.exists().then(Expect.isFalse)) + + .then((_) => dir.exists().then(Expect.isTrue)) + .then((_) => dir.delete()) + .then((_) => dir.exists().then(Expect.isFalse)) + + .then((_) => tmp.delete()); + }); +} + +void testDeleteBrokenLinkSync() { + var tmp = new Directory("").createTempSync(); + var path = "${tmp.path}${Platform.pathSeparator}"; + + var directory = new Directory("${path}myDirectory"); + directory.createSync(); + + var link = new Link("${path}myLink"); + + link.createSync(directory.path); + directory.deleteSync(); + + Expect.isTrue(link.existsSync()); + new Link(link.path).deleteSync(); + Expect.isFalse(link.existsSync()); + + directory.createSync(); + link.createSync(directory.path); + directory.deleteSync(); + + Expect.isTrue(link.existsSync()); + Expect.throws(() => new Directory(link.path).deleteSync()); + Expect.isTrue(link.existsSync()); + + Expect.isTrue(link.existsSync()); + Expect.throws(() => new Directory(link.path).deleteSync(recursive: true)); + Expect.isTrue(link.existsSync()); + + Expect.isTrue(link.existsSync()); + Expect.throws(() => new File(link.path).deleteSync()); + Expect.isTrue(link.existsSync()); + + link.deleteSync(); + Expect.isFalse(link.existsSync()); + + tmp.deleteSync(); +} + +void testDeleteBrokenLink() { + new Directory("").createTemp().then((tmp) { + var path = "${tmp.path}${Platform.pathSeparator}"; + var dir = new Directory("${path}myDir"); + var link = new Link("${path}myLink"); + return dir.create() + .then((_) => link.create(dir.path)) + .then((_) => dir.delete()) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => new Link(link.path).delete()) + .then((_) => link.exists().then(Expect.isFalse)) + + .then((_) => dir.create()) + .then((_) => link.create(dir.path)) + .then((_) => dir.delete()) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => throws(() => new Directory(link.path).delete())) + .then((_) => link.exists().then(Expect.isTrue)) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => throws( + () => new Directory(link.path).delete(recursive: true))) + .then((_) => link.exists().then(Expect.isTrue)) + + .then((_) => link.exists().then(Expect.isTrue)) + .then((_) => throws(() => new File(link.path).delete())) + .then((_) => link.exists().then(Expect.isTrue)) + + .then((_) => link.deleteSync()) + .then((_) => link.exists().then(Expect.isFalse)) + + .then((_) => tmp.delete()); + }); +} + +void main() { + testDeleteFileSync(); + testDeleteFile(); + testDeleteDirectorySync(); + testDeleteDirectory(); + if (Platform.operatingSystem != 'windows') { + testDeleteFileLinkSync(); + testDeleteFileLink(); + } + testDeleteDirectoryLinkSync(); + testDeleteDirectoryLink(); + testDeleteBrokenLinkSync(); + testDeleteBrokenLink(); +} diff --git a/tests/standalone/io/file_system_exists_test.dart b/tests/standalone/io/file_system_exists_test.dart new file mode 100644 index 00000000000..14bd71a2da2 --- /dev/null +++ b/tests/standalone/io/file_system_exists_test.dart @@ -0,0 +1,204 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "dart:io"; + +void testFileExistsSync() { + var tmp = new Directory("").createTempSync(); + var path = "${tmp.path}${Platform.pathSeparator}"; + + var file = new File("${path}myFile"); + file.createSync(); + + Expect.isTrue(new File(file.path).existsSync()); + Expect.isFalse(new Directory(file.path).existsSync()); + Expect.isFalse(new Link(file.path).existsSync()); + + file.deleteSync(); + Expect.isFalse(file.existsSync()); + + tmp.deleteSync(); +} + +void testFileExists() { + new Directory("").createTemp().then((tmp) { + var path = "${tmp.path}${Platform.pathSeparator}"; + var file = new File("${path}myFile"); + return file.create() + .then((_) => new File(file.path).exists().then(Expect.isTrue)) + .then((_) => new Directory(file.path).exists().then(Expect.isFalse)) + .then((_) => new Link(file.path).exists().then(Expect.isFalse)) + + .then((_) => file.delete()) + .then((_) => tmp.delete()); + }); +} + +void testDirectoryExistsSync() { + var tmp = new Directory("").createTempSync(); + var path = "${tmp.path}${Platform.pathSeparator}"; + + var dir = new Directory("${path}myDirectory"); + dir.createSync(); + + Expect.isFalse(new File(dir.path).existsSync()); + Expect.isTrue(new Directory(dir.path).existsSync()); + Expect.isFalse(new Link(dir.path).existsSync()); + + dir.deleteSync(); + Expect.isFalse(dir.existsSync()); + + tmp.deleteSync(); +} + +void testDirectoryExists() { + new Directory("").createTemp().then((tmp) { + var path = "${tmp.path}${Platform.pathSeparator}"; + var dir = new Directory("${path}myDirectory"); + return dir.create() + .then((_) => new File(dir.path).exists().then(Expect.isFalse)) + .then((_) => new Directory(dir.path).exists().then(Expect.isTrue)) + .then((_) => new Link(dir.path).exists().then(Expect.isFalse)) + + .then((_) => dir.delete()) + .then((_) => tmp.delete()); + }); +} + +void testFileLinkExistsSync() { + var tmp = new Directory("").createTempSync(); + var path = "${tmp.path}${Platform.pathSeparator}"; + + var file = new File("${path}myFile"); + file.createSync(); + + var link = new Link("${path}myLink"); + link.createSync(file.path); + + Expect.isTrue(new File(link.path).existsSync()); + Expect.isFalse(new Directory(link.path).existsSync()); + Expect.isTrue(new Link(link.path).existsSync()); + + link.deleteSync(); + Expect.isFalse(link.existsSync()); + + file.deleteSync(); + Expect.isFalse(file.existsSync()); + + tmp.deleteSync(); +} + +void testFileLinkExists() { + new Directory("").createTemp().then((tmp) { + var path = "${tmp.path}${Platform.pathSeparator}"; + var file = new File("${path}myFile"); + var link = new Link("${path}myLink"); + return file.create() + .then((_) => link.create(file.path)) + + .then((_) => new File(link.path).exists().then(Expect.isTrue)) + .then((_) => new Directory(link.path).exists().then(Expect.isFalse)) + .then((_) => new Link(link.path).exists().then(Expect.isTrue)) + + .then((_) => link.delete()) + .then((_) => file.delete()) + .then((_) => tmp.delete()); + }); +} + +void testDirectoryLinkExistsSync() { + var tmp = new Directory("").createTempSync(); + var path = "${tmp.path}${Platform.pathSeparator}"; + + var directory = new Directory("${path}myDirectory"); + directory.createSync(); + + var link = new Link("${path}myLink"); + link.createSync(directory.path); + + Expect.isFalse(new File(link.path).existsSync()); + Expect.isTrue(new Directory(link.path).existsSync()); + Expect.isTrue(new Link(link.path).existsSync()); + + link.deleteSync(); + Expect.isFalse(link.existsSync()); + + directory.deleteSync(); + Expect.isFalse(directory.existsSync()); + + tmp.deleteSync(); +} + +void testDirectoryLinkExists() { + new Directory("").createTemp().then((tmp) { + var path = "${tmp.path}${Platform.pathSeparator}"; + var dir = new Directory("${path}myDir"); + var link = new Link("${path}myLink"); + return dir.create() + .then((_) => link.create(dir.path)) + + .then((_) => new File(link.path).exists().then(Expect.isFalse)) + .then((_) => new Directory(link.path).exists().then(Expect.isTrue)) + .then((_) => new Link(link.path).exists().then(Expect.isTrue)) + + .then((_) => link.delete()) + .then((_) => dir.delete()) + .then((_) => tmp.delete()); + }); +} + +void testBrokenLinkExistsSync() { + var tmp = new Directory("").createTempSync(); + var path = "${tmp.path}${Platform.pathSeparator}"; + + var directory = new Directory("${path}myDirectory"); + directory.createSync(); + + var link = new Link("${path}myLink"); + link.createSync(directory.path); + directory.deleteSync(); + + Expect.isFalse(new File(link.path).existsSync()); + Expect.isFalse(new Directory(link.path).existsSync()); + Expect.isTrue(new Link(link.path).existsSync()); + + link.deleteSync(); + Expect.isFalse(link.existsSync()); + + tmp.deleteSync(); +} + +void testBrokenLinkExists() { + new Directory("").createTemp().then((tmp) { + var path = "${tmp.path}${Platform.pathSeparator}"; + var dir = new Directory("${path}myDir"); + var link = new Link("${path}myLink"); + return dir.create() + .then((_) => link.create(dir.path)) + .then((_) => dir.delete()) + + .then((_) => new File(link.path).exists().then(Expect.isFalse)) + .then((_) => new Directory(link.path).exists().then(Expect.isFalse)) + .then((_) => new Link(link.path).exists().then(Expect.isTrue)) + + .then((_) => link.delete()) + .then((_) => tmp.delete()); + }); +} + +void main() { + testFileExistsSync(); + testFileExists(); + testDirectoryExistsSync(); + testDirectoryExists(); + if (Platform.operatingSystem != 'windows') { + testFileLinkExistsSync(); + testFileLinkExists(); + } + testDirectoryLinkExistsSync(); + testDirectoryLinkExists(); + testBrokenLinkExistsSync(); + testBrokenLinkExists(); +} + diff --git a/tests/standalone/io/file_system_links_test.dart b/tests/standalone/io/file_system_links_test.dart index 588f1574704..f3a3148c483 100644 --- a/tests/standalone/io/file_system_links_test.dart +++ b/tests/standalone/io/file_system_links_test.dart @@ -58,7 +58,7 @@ testFileExistsCreate() { FileSystemEntity.typeSync(x, followLinks: false)); Expect.equals(x, new Link(y).targetSync()); - new File(y).deleteSync(); + new Link(y).deleteSync(); Expect.isFalse(FileSystemEntity.isLinkSync(y)); Expect.isFalse(FileSystemEntity.isLinkSync(x)); Expect.equals(FileSystemEntityType.NOT_FOUND, FileSystemEntity.typeSync(y)); diff --git a/tests/standalone/io/windows_file_system_links_test.dart b/tests/standalone/io/windows_file_system_links_test.dart index f161b0e12ae..4e26ec73926 100644 --- a/tests/standalone/io/windows_file_system_links_test.dart +++ b/tests/standalone/io/windows_file_system_links_test.dart @@ -30,13 +30,13 @@ testJunctionTypeDelete() { // Test Junction pointing to a missing directory. new Directory(x).deleteSync(); - Expect.isTrue(new Directory(y).existsSync()); + Expect.isTrue(new Link(y).existsSync()); Expect.isFalse(new Directory(x).existsSync()); Expect.isTrue(FileSystemEntity.isLinkSync(y)); Expect.isFalse(FileSystemEntity.isLinkSync(x)); Expect.isFalse(FileSystemEntity.isDirectorySync(y)); Expect.isFalse(FileSystemEntity.isDirectorySync(x)); - Expect.equals(FileSystemEntityType.NOT_FOUND, + Expect.equals(FileSystemEntityType.LINK, FileSystemEntity.typeSync(y)); Expect.equals(FileSystemEntityType.NOT_FOUND, FileSystemEntity.typeSync(x)); @@ -47,7 +47,7 @@ testJunctionTypeDelete() { Expect.equals(x, new Link(y).targetSync()); // Delete Junction pointing to a missing directory. - new Directory(y).deleteSync(); + new Link(y).deleteSync(); Expect.isFalse(FileSystemEntity.isLinkSync(y)); Expect.equals(FileSystemEntityType.NOT_FOUND, FileSystemEntity.typeSync(y));