rainbow-dragon/Tests/ForgeProxyTests/WatchFolderTests.swift

132 lines
4.5 KiB
Swift

import XCTest
import Foundation
@testable import ForgeProxy
final class WatchFolderTests: XCTestCase {
var dir: String!
override func setUpWithError() throws {
dir = NSTemporaryDirectory() + "forge-watch-\(UUID().uuidString)"
try FileManager.default.createDirectory(atPath: dir, withIntermediateDirectories: true)
}
override func tearDown() {
try? FileManager.default.removeItem(atPath: dir)
}
// New stable file enqueued exactly once.
func testNewFileEnqueuedOnce() async throws {
let watcher = WatchFolder(
path: dir,
extensions: ["mov"],
pollInterval: .milliseconds(30),
stabilityChecks: 2)
var enqueued = [String]()
let stream = await watcher.start()
try Data("clip".utf8).write(to: URL(fileURLWithPath: dir + "/C001.mov"))
let collector = Task {
for await path in stream {
enqueued.append(path)
}
}
try await Task.sleep(for: .milliseconds(400))
await watcher.stop()
_ = await collector.result
XCTAssertEqual(enqueued.count, 1)
XCTAssertTrue(enqueued[0].hasSuffix("C001.mov"))
}
// Growing file (partial write) not enqueued until size stable.
func testGrowingFileWaitsForStability() async throws {
let watcher = WatchFolder(
path: dir,
extensions: ["mov"],
pollInterval: .milliseconds(40),
stabilityChecks: 2)
let path = dir + "/GROW.mov"
try Data("start".utf8).write(to: URL(fileURLWithPath: path))
let stream = await watcher.start()
actor Seen {
var paths = [String]()
var at: [ContinuousClock.Instant] = []
func add(_ p: String) {
paths.append(p)
at.append(.now)
}
}
let seen = Seen()
let collector = Task {
for await p in stream {
await seen.add(p)
}
}
// Keep growing for ~200ms.
for i in 0..<5 {
try await Task.sleep(for: .milliseconds(40))
let h = FileHandle(forWritingAtPath: path)!
h.seekToEndOfFile()
h.write(Data("more\(i)".utf8))
try h.close()
}
let growEnd = ContinuousClock.now
try await Task.sleep(for: .milliseconds(400))
await watcher.stop()
_ = await collector.result
let paths = await seen.paths
let times = await seen.at
XCTAssertEqual(paths.count, 1)
// Enqueue happened after growth stopped.
XCTAssertGreaterThan(times[0], growEnd)
}
// Extension filter.
func testExtensionFilter() async throws {
let watcher = WatchFolder(path: dir, extensions: ["mov"], pollInterval: .milliseconds(30), stabilityChecks: 1)
try Data("x".utf8).write(to: URL(fileURLWithPath: dir + "/note.txt"))
try Data("y".utf8).write(to: URL(fileURLWithPath: dir + "/C002.mov"))
let stream = await watcher.start()
var enqueued = [String]()
let collector = Task {
for await p in stream { enqueued.append(p) }
}
try await Task.sleep(for: .milliseconds(300))
await watcher.stop()
_ = await collector.result
XCTAssertEqual(enqueued.count, 1)
XCTAssertTrue(enqueued[0].hasSuffix(".mov"))
}
// Already-processed file skipped across watcher runs via skip list.
func testSkipListPersistsAcrossRuns() async throws {
try Data("x".utf8).write(to: URL(fileURLWithPath: dir + "/C003.mov"))
let skipList = ProcessedSkipList()
let w1 = WatchFolder(path: dir, extensions: ["mov"], pollInterval: .milliseconds(30), stabilityChecks: 1, skipList: skipList)
let s1 = await w1.start()
var first = [String]()
let c1 = Task { for await p in s1 { first.append(p) } }
try await Task.sleep(for: .milliseconds(250))
await w1.stop()
_ = await c1.result
XCTAssertEqual(first.count, 1)
// Second watcher with same skip list: nothing new.
let w2 = WatchFolder(path: dir, extensions: ["mov"], pollInterval: .milliseconds(30), stabilityChecks: 1, skipList: skipList)
let s2 = await w2.start()
var second = [String]()
let c2 = Task { for await p in s2 { second.append(p) } }
try await Task.sleep(for: .milliseconds(250))
await w2.stop()
_ = await c2.result
XCTAssertTrue(second.isEmpty)
}
}