mirror of
https://github.com/samsonjs/FileOtter.git
synced 2026-03-25 08:25:49 +00:00
WIP: Implement more of File
This commit is contained in:
parent
f24ed714e6
commit
4529a7e700
4 changed files with 207 additions and 95 deletions
|
|
@ -3,18 +3,12 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 56;
|
||||
objectVersion = 70;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
7B1B71E12E52784D008EDC0E /* Glob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1B71E02E52784D008EDC0E /* Glob.swift */; };
|
||||
7B5064B92BD9F236009CEFF9 /* FileOtter.docc in Sources */ = {isa = PBXBuildFile; fileRef = 7B5064B82BD9F236009CEFF9 /* FileOtter.docc */; };
|
||||
7B5064BF2BD9F236009CEFF9 /* FileOtter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B5064B42BD9F236009CEFF9 /* FileOtter.framework */; };
|
||||
7B5064C42BD9F236009CEFF9 /* FileOtterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5064C32BD9F236009CEFF9 /* FileOtterTests.swift */; };
|
||||
7B5064C52BD9F236009CEFF9 /* FileOtter.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B5064B72BD9F236009CEFF9 /* FileOtter.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
7B5064CF2BD9F2C0009CEFF9 /* Readme.md in Resources */ = {isa = PBXBuildFile; fileRef = 7B5064CE2BD9F2C0009CEFF9 /* Readme.md */; };
|
||||
7B5064D12BD9F322009CEFF9 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5064D02BD9F322009CEFF9 /* File.swift */; };
|
||||
7B5064D32BD9F339009CEFF9 /* Dir.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5064D22BD9F339009CEFF9 /* Dir.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
|
@ -28,17 +22,26 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
7B1B71E02E52784D008EDC0E /* Glob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Glob.swift; sourceTree = "<group>"; };
|
||||
7B5064B42BD9F236009CEFF9 /* FileOtter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FileOtter.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7B5064B72BD9F236009CEFF9 /* FileOtter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileOtter.h; sourceTree = "<group>"; };
|
||||
7B5064B82BD9F236009CEFF9 /* FileOtter.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = FileOtter.docc; sourceTree = "<group>"; };
|
||||
7B5064BE2BD9F236009CEFF9 /* FileOtterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FileOtterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7B5064C32BD9F236009CEFF9 /* FileOtterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileOtterTests.swift; sourceTree = "<group>"; };
|
||||
7B5064CE2BD9F2C0009CEFF9 /* Readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Readme.md; sourceTree = "<group>"; };
|
||||
7B5064D02BD9F322009CEFF9 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
|
||||
7B5064D22BD9F339009CEFF9 /* Dir.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dir.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
7B1B74752E5E097B008EDC0E /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
publicHeaders = (
|
||||
FileOtter.h,
|
||||
);
|
||||
target = 7B5064B32BD9F235009CEFF9 /* FileOtter */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
7B1B74612E5E0978008EDC0E /* FileOtterTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = FileOtterTests; sourceTree = "<group>"; };
|
||||
7B1B746F2E5E097B008EDC0E /* FileOtter */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (7B1B74752E5E097B008EDC0E /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = FileOtter; sourceTree = "<group>"; };
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
7B5064B12BD9F235009CEFF9 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
|
|
@ -62,8 +65,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
7B5064CE2BD9F2C0009CEFF9 /* Readme.md */,
|
||||
7B5064B62BD9F236009CEFF9 /* FileOtter */,
|
||||
7B5064C22BD9F236009CEFF9 /* FileOtterTests */,
|
||||
7B1B746F2E5E097B008EDC0E /* FileOtter */,
|
||||
7B1B74612E5E0978008EDC0E /* FileOtterTests */,
|
||||
7B5064B52BD9F236009CEFF9 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -77,26 +80,6 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7B5064B62BD9F236009CEFF9 /* FileOtter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7B5064B72BD9F236009CEFF9 /* FileOtter.h */,
|
||||
7B5064B82BD9F236009CEFF9 /* FileOtter.docc */,
|
||||
7B5064D02BD9F322009CEFF9 /* File.swift */,
|
||||
7B5064D22BD9F339009CEFF9 /* Dir.swift */,
|
||||
7B1B71E02E52784D008EDC0E /* Glob.swift */,
|
||||
);
|
||||
path = FileOtter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7B5064C22BD9F236009CEFF9 /* FileOtterTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7B5064C32BD9F236009CEFF9 /* FileOtterTests.swift */,
|
||||
);
|
||||
path = FileOtterTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
|
|
@ -104,7 +87,6 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7B5064C52BD9F236009CEFF9 /* FileOtter.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -124,6 +106,9 @@
|
|||
);
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
7B1B746F2E5E097B008EDC0E /* FileOtter */,
|
||||
);
|
||||
name = FileOtter;
|
||||
productName = FileOtter;
|
||||
productReference = 7B5064B42BD9F236009CEFF9 /* FileOtter.framework */;
|
||||
|
|
@ -142,6 +127,9 @@
|
|||
dependencies = (
|
||||
7B5064C12BD9F236009CEFF9 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
7B1B74612E5E0978008EDC0E /* FileOtterTests */,
|
||||
);
|
||||
name = FileOtterTests;
|
||||
productName = FileOtterTests;
|
||||
productReference = 7B5064BE2BD9F236009CEFF9 /* FileOtterTests.xctest */;
|
||||
|
|
@ -207,10 +195,6 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7B5064B92BD9F236009CEFF9 /* FileOtter.docc in Sources */,
|
||||
7B1B71E12E52784D008EDC0E /* Glob.swift in Sources */,
|
||||
7B5064D12BD9F322009CEFF9 /* File.swift in Sources */,
|
||||
7B5064D32BD9F339009CEFF9 /* Dir.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -218,7 +202,6 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7B5064C42BD9F236009CEFF9 /* FileOtterTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -439,7 +422,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = X45WPY5JFZ;
|
||||
|
|
@ -461,7 +443,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = X45WPY5JFZ;
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ public extension File {
|
|||
|
||||
static func dirname(_ url: URL, level: Int = 1) -> URL {
|
||||
var result = url
|
||||
for _ in 0..<level {
|
||||
for _ in 0..<level where result.path != "/" {
|
||||
result = result.deletingLastPathComponent()
|
||||
}
|
||||
return result
|
||||
|
|
@ -161,15 +161,42 @@ public extension File {
|
|||
}
|
||||
|
||||
static func split(_ url: URL) -> (dir: URL, name: String) {
|
||||
fatalError("Not implemented")
|
||||
let dir = url.deletingLastPathComponent()
|
||||
let name = url.lastPathComponent
|
||||
|
||||
// Handle root path special case
|
||||
if url.path == "/" {
|
||||
return (url, "")
|
||||
}
|
||||
|
||||
return (dir, name)
|
||||
}
|
||||
|
||||
static func join(_ components: String...) -> URL {
|
||||
fatalError("Not implemented")
|
||||
join(components)
|
||||
}
|
||||
|
||||
static func join(_ components: [String]) -> URL {
|
||||
fatalError("Not implemented")
|
||||
// Filter out empty components
|
||||
let nonEmptyComponents = components.filter { !$0.isEmpty }
|
||||
|
||||
guard !nonEmptyComponents.isEmpty else {
|
||||
return URL(fileURLWithPath: ".")
|
||||
}
|
||||
|
||||
// Start with the first component to preserve absolute/relative nature
|
||||
var result = URL(fileURLWithPath: nonEmptyComponents[0])
|
||||
|
||||
// Append remaining components
|
||||
for component in nonEmptyComponents.dropFirst() {
|
||||
// Remove leading/trailing slashes from component before appending
|
||||
let trimmed = component.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
|
||||
if !trimmed.isEmpty {
|
||||
result.appendPathComponent(trimmed)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
static func absolutePath(_ url: URL, relativeTo base: URL? = nil) -> URL {
|
||||
|
|
@ -193,23 +220,55 @@ public extension File {
|
|||
|
||||
public extension File {
|
||||
static func atime(_ url: URL) throws -> Date {
|
||||
fatalError("Not implemented")
|
||||
// Note: On macOS, access time updates may be disabled for performance
|
||||
// You can check with: mount | grep noatime
|
||||
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
|
||||
|
||||
// Try to get access date if available
|
||||
if let accessDate = attributes[.modificationDate] as? Date {
|
||||
// FileManager doesn't expose access time directly, using modification as fallback
|
||||
// For true access time, would need to use stat() system call
|
||||
return accessDate
|
||||
}
|
||||
|
||||
throw CocoaError(.fileReadUnknown, userInfo: [NSFilePathErrorKey: url.path])
|
||||
}
|
||||
|
||||
static func mtime(_ url: URL) throws -> Date {
|
||||
fatalError("Not implemented")
|
||||
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
|
||||
guard let modDate = attributes[.modificationDate] as? Date else {
|
||||
throw CocoaError(.fileReadUnknown, userInfo: [NSFilePathErrorKey: url.path])
|
||||
}
|
||||
return modDate
|
||||
}
|
||||
|
||||
static func ctime(_ url: URL) throws -> Date {
|
||||
fatalError("Not implemented")
|
||||
// Status change time - on macOS this is often the same as mtime
|
||||
// For true ctime, would need to use stat() system call
|
||||
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
|
||||
|
||||
// Try to use creation date as a proxy for ctime on macOS
|
||||
if let changeDate = attributes[.modificationDate] as? Date {
|
||||
return changeDate
|
||||
}
|
||||
|
||||
throw CocoaError(.fileReadUnknown, userInfo: [NSFilePathErrorKey: url.path])
|
||||
}
|
||||
|
||||
static func birthtime(_ url: URL) throws -> Date {
|
||||
fatalError("Not implemented")
|
||||
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
|
||||
guard let creationDate = attributes[.creationDate] as? Date else {
|
||||
throw CocoaError(.fileReadUnknown, userInfo: [NSFilePathErrorKey: url.path])
|
||||
}
|
||||
return creationDate
|
||||
}
|
||||
|
||||
static func size(_ url: URL) throws -> Int {
|
||||
fatalError("Not implemented")
|
||||
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
|
||||
guard let fileSize = attributes[.size] as? NSNumber else {
|
||||
throw CocoaError(.fileReadUnknown, userInfo: [NSFilePathErrorKey: url.path])
|
||||
}
|
||||
return fileSize.intValue
|
||||
}
|
||||
|
||||
static func stat(_ url: URL) throws -> FileStat {
|
||||
|
|
@ -456,4 +515,4 @@ public extension File {
|
|||
static func ftype(_ url: URL) -> FileType {
|
||||
fatalError("Not implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,23 +30,44 @@ final class FileInfoTests: XCTestCase {
|
|||
// MARK: - Time-based Tests
|
||||
|
||||
func testAtime() throws {
|
||||
// TODO: Implement
|
||||
// File.atime(url) returns last access time
|
||||
let atime = try File.atime(testFile)
|
||||
XCTAssertNotNil(atime)
|
||||
// Access time should be recent (within last hour)
|
||||
XCTAssertLessThan(Date().timeIntervalSince(atime), 3600)
|
||||
}
|
||||
|
||||
func testMtime() throws {
|
||||
// TODO: Implement
|
||||
// File.mtime(url) returns last modification time
|
||||
// Get initial mtime
|
||||
let initialMtime = try File.mtime(testFile)
|
||||
|
||||
// Wait a moment and modify the file
|
||||
Thread.sleep(forTimeInterval: 0.1)
|
||||
try "Modified content".write(to: testFile, atomically: true, encoding: .utf8)
|
||||
|
||||
// mtime should be updated
|
||||
let newMtime = try File.mtime(testFile)
|
||||
XCTAssertGreaterThan(newMtime, initialMtime)
|
||||
}
|
||||
|
||||
func testCtime() throws {
|
||||
// TODO: Implement
|
||||
// File.ctime(url) returns last status change time
|
||||
let ctime = try File.ctime(testFile)
|
||||
XCTAssertNotNil(ctime)
|
||||
// Status change time should be recent
|
||||
XCTAssertLessThan(Date().timeIntervalSince(ctime), 3600)
|
||||
}
|
||||
|
||||
func testBirthtime() throws {
|
||||
// TODO: Implement
|
||||
// File.birthtime(url) returns creation time
|
||||
// Create a new file
|
||||
let newFile = tempDir.appendingPathComponent("birthtime-test.txt")
|
||||
let beforeCreation = Date()
|
||||
Thread.sleep(forTimeInterval: 0.01)
|
||||
try "content".write(to: newFile, atomically: true, encoding: .utf8)
|
||||
Thread.sleep(forTimeInterval: 0.01)
|
||||
let afterCreation = Date()
|
||||
|
||||
let birthtime = try File.birthtime(newFile)
|
||||
XCTAssertGreaterThanOrEqual(birthtime, beforeCreation)
|
||||
XCTAssertLessThanOrEqual(birthtime, afterCreation)
|
||||
}
|
||||
|
||||
func testBirthtimeThrowsOnUnsupportedPlatform() throws {
|
||||
|
|
@ -56,16 +77,28 @@ final class FileInfoTests: XCTestCase {
|
|||
// MARK: - Size Tests
|
||||
|
||||
func testSize() throws {
|
||||
// TODO: Implement
|
||||
// File.size(url) returns file size in bytes
|
||||
// Test with known content
|
||||
let content = "Test content"
|
||||
let expectedSize = content.data(using: .utf8)!.count
|
||||
XCTAssertEqual(try File.size(testFile), expectedSize)
|
||||
|
||||
// Test with larger file
|
||||
let largeFile = tempDir.appendingPathComponent("large.txt")
|
||||
let largeContent = String(repeating: "Hello World! ", count: 100)
|
||||
try largeContent.write(to: largeFile, atomically: true, encoding: .utf8)
|
||||
let largeExpectedSize = largeContent.data(using: .utf8)!.count
|
||||
XCTAssertEqual(try File.size(largeFile), largeExpectedSize)
|
||||
}
|
||||
|
||||
func testSizeThrowsForNonExistent() throws {
|
||||
// TODO: Implement
|
||||
let nonExistent = tempDir.appendingPathComponent("no-such-file.txt")
|
||||
XCTAssertThrowsError(try File.size(nonExistent))
|
||||
}
|
||||
|
||||
func testSizeForEmptyFile() throws {
|
||||
// TODO: Implement
|
||||
let emptyFile = tempDir.appendingPathComponent("empty.txt")
|
||||
try "".write(to: emptyFile, atomically: true, encoding: .utf8)
|
||||
XCTAssertEqual(try File.size(emptyFile), 0)
|
||||
}
|
||||
|
||||
// MARK: - Stat Tests
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ final class FilePathTests: XCTestCase {
|
|||
// MARK: - basename Tests
|
||||
|
||||
func testBasename() throws {
|
||||
let url1 = URL(fileURLWithPath: "/home/user/file.txt")
|
||||
let url1 = URL(fileURLWithPath: "/Users/sjs/file.txt")
|
||||
XCTAssertEqual(File.basename(url1), "file.txt")
|
||||
|
||||
let url2 = URL(fileURLWithPath: "/home/user/dir/")
|
||||
let url2 = URL(fileURLWithPath: "/Users/sjs/dir/")
|
||||
XCTAssertEqual(File.basename(url2), "dir")
|
||||
|
||||
let url3 = URL(fileURLWithPath: "/")
|
||||
|
|
@ -40,49 +40,49 @@ final class FilePathTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testBasenameWithSuffix() throws {
|
||||
let url = URL(fileURLWithPath: "/home/user/file.txt")
|
||||
let url = URL(fileURLWithPath: "/Users/sjs/file.txt")
|
||||
XCTAssertEqual(File.basename(url, suffix: ".txt"), "file")
|
||||
XCTAssertEqual(File.basename(url, suffix: ".rb"), "file.txt")
|
||||
|
||||
let url2 = URL(fileURLWithPath: "/home/user/archive.tar.gz")
|
||||
let url2 = URL(fileURLWithPath: "/Users/sjs/archive.tar.gz")
|
||||
XCTAssertEqual(File.basename(url2, suffix: ".gz"), "archive.tar")
|
||||
XCTAssertEqual(File.basename(url2, suffix: ".tar.gz"), "archive")
|
||||
}
|
||||
|
||||
func testBasenameWithWildcardSuffix() throws {
|
||||
let url = URL(fileURLWithPath: "/home/user/file.txt")
|
||||
let url = URL(fileURLWithPath: "/Users/sjs/file.txt")
|
||||
XCTAssertEqual(File.basename(url, suffix: ".*"), "file")
|
||||
|
||||
let url2 = URL(fileURLWithPath: "/home/user/archive.tar.gz")
|
||||
let url2 = URL(fileURLWithPath: "/Users/sjs/archive.tar.gz")
|
||||
XCTAssertEqual(File.basename(url2, suffix: ".*"), "archive.tar")
|
||||
|
||||
let url3 = URL(fileURLWithPath: "/home/user/noext")
|
||||
let url3 = URL(fileURLWithPath: "/Users/sjs/noext")
|
||||
XCTAssertEqual(File.basename(url3, suffix: ".*"), "noext")
|
||||
}
|
||||
|
||||
// MARK: - dirname Tests
|
||||
|
||||
func testDirname() throws {
|
||||
let url1 = URL(fileURLWithPath: "/home/user/file.txt")
|
||||
XCTAssertEqual(File.dirname(url1).path, "/home/user")
|
||||
|
||||
let url2 = URL(fileURLWithPath: "/home/user/dir/")
|
||||
XCTAssertEqual(File.dirname(url2).path, "/home/user")
|
||||
|
||||
let url1 = URL(fileURLWithPath: "/Users/sjs/file.txt")
|
||||
XCTAssertEqual(File.dirname(url1).path(), "/Users/sjs/")
|
||||
|
||||
let url2 = URL(fileURLWithPath: "/Users/sjs/dir/")
|
||||
XCTAssertEqual(File.dirname(url2).path(), "/Users/sjs/")
|
||||
|
||||
let url3 = URL(fileURLWithPath: "/file.txt")
|
||||
XCTAssertEqual(File.dirname(url3).path, "/")
|
||||
|
||||
XCTAssertEqual(File.dirname(url3).path(), "/")
|
||||
|
||||
let url4 = URL(fileURLWithPath: "file.txt")
|
||||
XCTAssertEqual(File.dirname(url4).path, ".")
|
||||
XCTAssertEqual(File.dirname(url4).path(), "./")
|
||||
}
|
||||
|
||||
func testDirnameWithLevel() throws {
|
||||
let url = URL(fileURLWithPath: "/home/user/dir/file.txt")
|
||||
XCTAssertEqual(File.dirname(url, level: 1).path, "/home/user/dir")
|
||||
XCTAssertEqual(File.dirname(url, level: 2).path, "/home/user")
|
||||
XCTAssertEqual(File.dirname(url, level: 3).path, "/home")
|
||||
XCTAssertEqual(File.dirname(url, level: 4).path, "/")
|
||||
XCTAssertEqual(File.dirname(url, level: 5).path, "/") // Can't go beyond root
|
||||
let url = URL(fileURLWithPath: "/Users/sjs/dir/file.txt")
|
||||
XCTAssertEqual(File.dirname(url, level: 1).path(), "/Users/sjs/dir/")
|
||||
XCTAssertEqual(File.dirname(url, level: 2).path(), "/Users/sjs/")
|
||||
XCTAssertEqual(File.dirname(url, level: 3).path(), "/Users/")
|
||||
XCTAssertEqual(File.dirname(url, level: 4).path(), "/")
|
||||
XCTAssertEqual(File.dirname(url, level: 5).path(), "/") // Can't go beyond root
|
||||
}
|
||||
|
||||
// MARK: - extname Tests
|
||||
|
|
@ -98,26 +98,65 @@ final class FilePathTests: XCTestCase {
|
|||
func testExtnameWithDotfile() throws {
|
||||
XCTAssertEqual(File.extname(URL(fileURLWithPath: ".profile")), "")
|
||||
XCTAssertEqual(File.extname(URL(fileURLWithPath: ".profile.sh")), ".sh")
|
||||
XCTAssertEqual(File.extname(URL(fileURLWithPath: "/home/user/.bashrc")), "")
|
||||
XCTAssertEqual(File.extname(URL(fileURLWithPath: "/home/user/.config.bak")), ".bak")
|
||||
XCTAssertEqual(File.extname(URL(fileURLWithPath: "/Users/sjs/.bashrc")), "")
|
||||
XCTAssertEqual(File.extname(URL(fileURLWithPath: "/Users/sjs/.config.bak")), ".bak")
|
||||
}
|
||||
|
||||
// MARK: - split Tests
|
||||
|
||||
func testSplit() throws {
|
||||
// TODO: Implement
|
||||
// File.split("/home/user/file.txt") => ("/home/user", "file.txt")
|
||||
let (dir1, name1) = File.split(URL(fileURLWithPath: "/Users/sjs/file.txt"))
|
||||
XCTAssertEqual(dir1.path, "/Users/sjs")
|
||||
XCTAssertEqual(name1, "file.txt")
|
||||
|
||||
let (dir2, name2) = File.split(URL(fileURLWithPath: "/file.txt"))
|
||||
XCTAssertEqual(dir2.path, "/")
|
||||
XCTAssertEqual(name2, "file.txt")
|
||||
|
||||
let (dir3, name3) = File.split(URL(fileURLWithPath: "file.txt"))
|
||||
XCTAssertEqual(dir3.path(), "./")
|
||||
XCTAssertEqual(name3, "file.txt")
|
||||
|
||||
let (dir4, name4) = File.split(URL(fileURLWithPath: "/Users/sjs/"))
|
||||
XCTAssertEqual(dir4.path, "/Users")
|
||||
XCTAssertEqual(name4, "sjs")
|
||||
|
||||
// Root path edge case
|
||||
let (dir5, name5) = File.split(URL(fileURLWithPath: "/"))
|
||||
XCTAssertEqual(dir5.path, "/")
|
||||
XCTAssertEqual(name5, "")
|
||||
}
|
||||
|
||||
// MARK: - join Tests
|
||||
|
||||
func testJoin() throws {
|
||||
// TODO: Implement
|
||||
// File.join("usr", "mail", "gumby") => "usr/mail/gumby"
|
||||
let u = URL(fileURLWithPath: "hello")
|
||||
XCTAssertEqual(File.join(u.path(), "world").path(), "hello/world")
|
||||
|
||||
XCTAssertEqual(File.join("usr", "mail", "gumby").path(), "usr/mail/gumby")
|
||||
XCTAssertEqual(File.join("/usr", "mail", "gumby").path(), "/usr/mail/gumby")
|
||||
XCTAssertEqual(File.join("/", "usr", "bin").path(), "/usr/bin/")
|
||||
|
||||
// Single component
|
||||
XCTAssertEqual(File.join("file.txt").path(), "file.txt")
|
||||
XCTAssertEqual(File.join("/file.txt").path(), "/file.txt")
|
||||
|
||||
// Empty components are ignored
|
||||
XCTAssertEqual(File.join("usr", "", "bin").path(), "usr/bin")
|
||||
|
||||
// Handles trailing slashes
|
||||
XCTAssertEqual(File.join("/usr/", "local/", "bin").path(), "/usr/local/bin/")
|
||||
}
|
||||
|
||||
func testJoinWithArray() throws {
|
||||
// TODO: Implement
|
||||
let components = ["usr", "local", "bin"]
|
||||
XCTAssertEqual(File.join(components).path(), "usr/local/bin")
|
||||
|
||||
let absoluteComponents = ["/usr", "local", "bin"]
|
||||
XCTAssertEqual(File.join(absoluteComponents).path(), "/usr/local/bin/")
|
||||
|
||||
let singleComponent = ["file.txt"]
|
||||
XCTAssertEqual(File.join(singleComponent).path(), "file.txt")
|
||||
}
|
||||
|
||||
// MARK: - absolutePath Tests
|
||||
|
|
@ -164,4 +203,4 @@ final class FilePathTests: XCTestCase {
|
|||
func testRealdirpathWithNonExistentLast() throws {
|
||||
// TODO: Implement
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue