mirror of
https://github.com/samsonjs/FileOtter.git
synced 2026-03-25 08:25:49 +00:00
210 lines
6.7 KiB
Swift
210 lines
6.7 KiB
Swift
//
|
|
// FileInfoTests.swift
|
|
// FileOtterTests
|
|
//
|
|
// Created by Sami Samhuri on 2025-08-19.
|
|
//
|
|
|
|
@testable import FileOtter
|
|
import XCTest
|
|
|
|
final class FileInfoTests: XCTestCase {
|
|
var tempDir: URL!
|
|
var testFile: URL!
|
|
|
|
override func setUpWithError() throws {
|
|
tempDir = URL.temporaryDirectory
|
|
.appendingPathComponent("FileInfoTests-\(UUID().uuidString)")
|
|
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
|
|
|
|
testFile = tempDir.appendingPathComponent("test.txt")
|
|
try "Test content".write(to: testFile, atomically: true, encoding: .utf8)
|
|
}
|
|
|
|
override func tearDownWithError() throws {
|
|
if FileManager.default.fileExists(atPath: tempDir.path) {
|
|
try FileManager.default.removeItem(at: tempDir)
|
|
}
|
|
}
|
|
|
|
// MARK: - Time-based Tests
|
|
|
|
func testAtime() throws {
|
|
let atime = try File.atime(testFile)
|
|
XCTAssertNotNil(atime)
|
|
// Access time should be recent (within last hour)
|
|
XCTAssertLessThan(Date().timeIntervalSince(atime), 3600)
|
|
}
|
|
|
|
func testMtime() throws {
|
|
// 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 {
|
|
let ctime = try File.ctime(testFile)
|
|
XCTAssertNotNil(ctime)
|
|
// Status change time should be recent
|
|
XCTAssertLessThan(Date().timeIntervalSince(ctime), 3600)
|
|
}
|
|
|
|
func testBirthtime() throws {
|
|
// 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 {
|
|
// TODO: Implement if platform doesn't support birthtime
|
|
}
|
|
|
|
// MARK: - Size Tests
|
|
|
|
func testSize() throws {
|
|
// 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 {
|
|
let nonExistent = tempDir.appendingPathComponent("no-such-file.txt")
|
|
XCTAssertThrowsError(try File.size(nonExistent))
|
|
}
|
|
|
|
func testSizeForEmptyFile() throws {
|
|
let emptyFile = tempDir.appendingPathComponent("empty.txt")
|
|
try "".write(to: emptyFile, atomically: true, encoding: .utf8)
|
|
XCTAssertEqual(try File.size(emptyFile), 0)
|
|
}
|
|
|
|
// MARK: - Stat Tests
|
|
|
|
func testStat() throws {
|
|
let stat = try File.fileStatus(testFile)
|
|
|
|
// Verify basic properties
|
|
XCTAssertGreaterThan(stat.ino, 0) // inode should be positive
|
|
XCTAssertGreaterThan(stat.uid, 0) // uid should be positive
|
|
XCTAssertGreaterThan(stat.gid, 0) // gid should be positive
|
|
XCTAssertEqual(stat.size, 12) // "Test content" is 12 bytes
|
|
|
|
// Verify times are reasonable
|
|
XCTAssertLessThan(Date().timeIntervalSince(stat.mtime), 3600)
|
|
XCTAssertLessThan(Date().timeIntervalSince(stat.atime), 3600)
|
|
XCTAssertNotNil(stat.birthtime)
|
|
}
|
|
|
|
func testStatThrowsForNonExistent() throws {
|
|
let nonExistent = tempDir.appendingPathComponent("nonexistent")
|
|
XCTAssertThrowsError(try File.fileStatus(nonExistent))
|
|
}
|
|
|
|
func testLstat() throws {
|
|
// For regular files, lstat should be same as stat
|
|
let lstat = try File.linkStatus(testFile)
|
|
let stat = try File.fileStatus(testFile)
|
|
|
|
XCTAssertEqual(lstat.size, stat.size)
|
|
XCTAssertEqual(lstat.ino, stat.ino)
|
|
XCTAssertEqual(lstat.mode, stat.mode)
|
|
}
|
|
|
|
func testLstatForSymlink() throws {
|
|
// Create a larger target file
|
|
let targetFile = tempDir.appendingPathComponent("target.txt")
|
|
let targetContent = "This is the target file content"
|
|
try targetContent.write(to: targetFile, atomically: true, encoding: .utf8)
|
|
|
|
// Create symlink
|
|
let symlinkURL = tempDir.appendingPathComponent("symlink.txt")
|
|
try FileManager.default.createSymbolicLink(at: symlinkURL, withDestinationURL: targetFile)
|
|
|
|
let lstat = try File.linkStatus(symlinkURL)
|
|
let stat = try File.fileStatus(symlinkURL)
|
|
|
|
// lstat should return symlink's own stats (smaller size)
|
|
// stat should follow the symlink to the target (larger size)
|
|
XCTAssertNotEqual(lstat.size, stat.size)
|
|
XCTAssertEqual(stat.size, Int64(targetContent.data(using: .utf8)!.count))
|
|
|
|
// lstat should indicate it's a symlink via mode
|
|
let isLink = (lstat.mode & 0o170000) == 0o120000 // S_IFLNK
|
|
XCTAssertTrue(isLink)
|
|
}
|
|
|
|
// MARK: - Instance Method Tests
|
|
|
|
func testInstanceAtime() throws {
|
|
// TODO: Implement
|
|
// file.atime returns access time
|
|
}
|
|
|
|
func testInstanceMtime() throws {
|
|
// TODO: Implement
|
|
// file.mtime returns modification time
|
|
}
|
|
|
|
func testInstanceCtime() throws {
|
|
// TODO: Implement
|
|
// file.ctime returns status change time
|
|
}
|
|
|
|
func testInstanceBirthtime() throws {
|
|
// TODO: Implement
|
|
// file.birthtime returns creation time
|
|
}
|
|
|
|
func testInstanceSize() throws {
|
|
// TODO: Implement
|
|
// file.size returns file size
|
|
}
|
|
|
|
func testInstanceStat() throws {
|
|
// TODO: Implement
|
|
// file.stat() returns FileStat object
|
|
}
|
|
|
|
func testInstanceLstat() throws {
|
|
// TODO: Implement
|
|
// file.lstat() doesn't follow symlinks
|
|
}
|
|
|
|
// MARK: - FileStat Tests
|
|
|
|
func testFileStatProperties() throws {
|
|
// TODO: Implement
|
|
// Verify all FileStat properties are populated correctly
|
|
}
|
|
|
|
func testFileStatForDirectory() throws {
|
|
// TODO: Implement
|
|
}
|
|
|
|
func testFileStatForSymlink() throws {
|
|
// TODO: Implement
|
|
}
|
|
}
|