PDF-Archiver/PDF-Archiver

View on GitHub
ArchiveCore/Tests/ArchiveBackendTests/DeepDirectoryWatcherTests.swift

Summary

Maintainability
A
1 hr
Test Coverage
//
//  DeepDirectoryWatcherTests.swift
//
//  Created by Julian Kahnert on 24.06.20.
//

@testable import ArchiveBackend
import ArchiveSharedConstants
import XCTest

final class DeepDirectoryWatcherTests: XCTestCase {

    var watcher: DirectoryDeepWatcher?
    var tempDir: URL?
    var files: [URL]?

    override func setUpWithError() throws {
        try super.setUpWithError()
        tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)

        guard let tempDir = tempDir else {
            XCTFail("TempDir could not be created.")
            return
        }
        for folderIndex in 0..<100 {
            let folder = tempDir.appendingPathComponent("\(folderIndex)")
            try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true, attributes: nil)
            for _ in 0..<400 {
                let file = folder.appendingPathComponent(UUID().uuidString)
                try "TEST".write(to: file, atomically: true, encoding: .utf8)
            }
        }

        files = FileManager.default.getFilesRecursive(at: tempDir)
        XCTAssertEqual(files?.count, 40000)
    }

    override func tearDownWithError() throws {
        try super.tearDownWithError()
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        guard let tempDir = tempDir else { return }
        try? FileManager.default.removeItem(at: tempDir)

        watcher = nil
    }

    func testFolderRemove() throws {
        guard let path = tempDir else { return }

        let folders = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles])
        let folderToRemove = try XCTUnwrap(folders.shuffled().first)

        let expectation = XCTestExpectation(description: "Document processing completed.")
        watcher = try DirectoryDeepWatcher(path, withHandler: { _ in
            expectation.fulfill()
        })

        try FileManager.default.removeItem(at: folderToRemove)

        wait(for: [expectation], timeout: 3)
        watcher = nil
    }

    func testFileRemove() throws {
        guard let path = tempDir else { return }

        let fileToRemove = try XCTUnwrap(files?.shuffled().first)

        let expectation = XCTestExpectation(description: "Document processing completed.")
        watcher = try DirectoryDeepWatcher(path, withHandler: { changedUrl in
            guard changedUrl == fileToRemove.deletingLastPathComponent() else { return }
            expectation.fulfill()
        })

        try FileManager.default.removeItem(at: fileToRemove)

        wait(for: [expectation], timeout: 3)
        watcher = nil
    }

    func testMultipleFileRemove() throws {
        guard let path = tempDir else { return }

        let filesToRemove = try XCTUnwrap(files?.shuffled().prefix(5))

        let parentFolders = Set(filesToRemove.map { $0.deletingLastPathComponent() })

        let expectations = parentFolders.reduce(into: [URL: XCTestExpectation]()) { (result, parentUrl) in
            result[parentUrl] = XCTestExpectation(description: "Document processing completed of \(parentUrl.absoluteString).")
        }

        watcher = try DirectoryDeepWatcher(path, withHandler: { changedUrl in
            expectations[changedUrl]?.fulfill()
        })

        for file in filesToRemove {
            DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(Int.random(in: 0..<5))) {
                try? FileManager.default.removeItem(at: file)
            }
        }

        wait(for: expectations.map(\.value), timeout: 30)
        watcher = nil
    }

    func testFileRemoveLong() throws {
        guard let path = tempDir else { return }

        let expectation = XCTestExpectation(description: "Document processing completed.")
        watcher = try DirectoryDeepWatcher(path, withHandler: { _ in
            expectation.fulfill()
        })

        let fileToRemove = try XCTUnwrap(files?.shuffled().first)
        DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(7)) {
            do {
                try FileManager.default.removeItem(at: fileToRemove)
            } catch {
                XCTFail("Error: \(error)")
            }
        }

        wait(for: [expectation], timeout: 20)
        watcher = nil
    }
}