95 lines
4.9 KiB
Swift
95 lines
4.9 KiB
Swift
import XCTest
|
|
@testable import TVAnarchyCore
|
|
|
|
final class TransferHealthTests: XCTestCase {
|
|
func testStatusMapping() {
|
|
func h(_ status: Int, complete: Bool = false, rate: Int = 0) -> TransferHealth {
|
|
TransferHealth.classify(status: status, isComplete: complete, rateDownload: rate,
|
|
peersConnected: 5, error: 0, secondsStuck: nil)
|
|
}
|
|
XCTAssertEqual(h(6), .seeding)
|
|
XCTAssertEqual(h(0, complete: true), .done)
|
|
XCTAssertEqual(h(0, complete: false), .stopped)
|
|
XCTAssertEqual(h(1), .checking)
|
|
XCTAssertEqual(h(3), .queued)
|
|
XCTAssertEqual(h(4, rate: 1_000), .downloading)
|
|
}
|
|
|
|
func testErrorWins() {
|
|
// Tracker error (2) and local error (3) are genuine → errored, beating an
|
|
// otherwise-healthy downloading state.
|
|
XCTAssertEqual(
|
|
TransferHealth.classify(status: 4, isComplete: false, rateDownload: 5_000,
|
|
peersConnected: 9, error: 3, secondsStuck: 0), .errored)
|
|
XCTAssertEqual(
|
|
TransferHealth.classify(status: 4, isComplete: false, rateDownload: 5_000,
|
|
peersConnected: 9, error: 2, secondsStuck: 0), .errored)
|
|
}
|
|
|
|
/// A tracker *warning* (error == 1) is one tracker flapping on an otherwise
|
|
/// healthy torrent — must NOT red-flag/notify (would spam on benign warnings).
|
|
func testTrackerWarningIsNotErrored() {
|
|
// Healthy downloading torrent that carries a stale tracker warning.
|
|
XCTAssertEqual(
|
|
TransferHealth.classify(status: 4, isComplete: false, rateDownload: 5_000,
|
|
peersConnected: 9, error: 1, secondsStuck: 0), .downloading)
|
|
// A seeding torrent with a tracker warning is still just seeding.
|
|
XCTAssertEqual(
|
|
TransferHealth.classify(status: 6, isComplete: true, rateDownload: 0,
|
|
peersConnected: 0, error: 1, secondsStuck: nil), .seeding)
|
|
XCTAssertFalse(TransferHealth.seeding.needsAttention)
|
|
}
|
|
|
|
func testStalledVsDeadThresholds() {
|
|
// 0 rate, peers present, past the stall window → stalled.
|
|
XCTAssertEqual(
|
|
TransferHealth.classify(status: 4, isComplete: false, rateDownload: 0,
|
|
peersConnected: 4, error: 0, secondsStuck: 130), .stalled)
|
|
// 0 rate, NO peers, past the dead window → dead.
|
|
XCTAssertEqual(
|
|
TransferHealth.classify(status: 4, isComplete: false, rateDownload: 0,
|
|
peersConnected: 0, error: 0, secondsStuck: 400), .dead)
|
|
// briefly 0 rate, not yet past stall → still downloading (no false alarm).
|
|
XCTAssertEqual(
|
|
TransferHealth.classify(status: 4, isComplete: false, rateDownload: 0,
|
|
peersConnected: 0, error: 0, secondsStuck: 10), .downloading)
|
|
}
|
|
|
|
func testAttentionAndSortOrder() {
|
|
XCTAssertTrue(TransferHealth.errored.needsAttention)
|
|
XCTAssertTrue(TransferHealth.dead.needsAttention)
|
|
XCTAssertFalse(TransferHealth.downloading.needsAttention)
|
|
// attention states sort ahead of active work, which sorts ahead of idle.
|
|
XCTAssertLessThan(TransferHealth.dead.sortRank, TransferHealth.downloading.sortRank)
|
|
XCTAssertLessThan(TransferHealth.downloading.sortRank, TransferHealth.seeding.sortRank)
|
|
}
|
|
|
|
@MainActor
|
|
func testDownloadOrderAttentionFirstThenEta() {
|
|
func row(_ id: Int, eta: Int) -> TorrentRow {
|
|
try! JSONDecoder().decode(TorrentRow.self, from: Data(#"""
|
|
{"id":\#(id),"name":"t\#(id)","percentDone":0.5,"status":4,"doneDate":0,"addedDate":1,
|
|
"haveValid":1,"sizeWhenDone":2,"rateDownload":1,"rateUpload":0,"uploadRatio":0,
|
|
"eta":\#(eta),"downloadDir":"/d"}
|
|
"""#.utf8))
|
|
}
|
|
let soon = row(1, eta: 60), later = row(2, eta: 600), dead = row(3, eta: -1)
|
|
// dead needs attention → first regardless of ETA; then soonest ETA.
|
|
XCTAssertTrue(DownloadsController.downloadOrder(dead, .dead, soon, .downloading))
|
|
XCTAssertTrue(DownloadsController.downloadOrder(soon, .downloading, later, .downloading))
|
|
XCTAssertFalse(DownloadsController.downloadOrder(later, .downloading, soon, .downloading))
|
|
}
|
|
|
|
/// A row from a pre-Part-E cli (no error/peers fields) still decodes + classifies.
|
|
func testRowWithoutDebugFieldsDecodesAndClassifies() throws {
|
|
let json = #"""
|
|
{"id":1,"name":"X","percentDone":0.5,"status":4,"doneDate":0,"addedDate":1,
|
|
"haveValid":1,"sizeWhenDone":2,"rateDownload":1000,"rateUpload":0,
|
|
"uploadRatio":0,"eta":60,"downloadDir":"/d"}
|
|
"""#
|
|
let row = try JSONDecoder().decode(TorrentRow.self, from: Data(json.utf8))
|
|
XCTAssertNil(row.error)
|
|
XCTAssertNil(row.peersConnected)
|
|
XCTAssertEqual(row.health(), .downloading)
|
|
}
|
|
}
|