import XCTest @testable import BusyMirror final class BlockMathTests: XCTestCase { private let d = Date(timeIntervalSince1970: 0) private func block(_ startMin: Int, _ endMin: Int, id: String? = nil) -> Block { Block( start: d.addingTimeInterval(TimeInterval(startMin * 60)), end: d.addingTimeInterval(TimeInterval(endMin * 60)), srcStableID: id, label: nil, notes: nil, occurrence: nil ) } // MARK: - mergeBlocks func testMergeBlocksNoGap() { let blocks = [block(0, 10), block(10, 20)] let merged = mergeBlocks(blocks, gapMinutes: 0) XCTAssertEqual(merged.count, 1) XCTAssertEqual(merged[0].start, blocks[0].start) XCTAssertEqual(merged[0].end, blocks[1].end) } func testMergeBlocksWithGapUnderThreshold() { let blocks = [block(0, 10), block(15, 20)] let merged = mergeBlocks(blocks, gapMinutes: 10) XCTAssertEqual(merged.count, 1) XCTAssertEqual(merged[0].start, blocks[0].start) XCTAssertEqual(merged[0].end, blocks[1].end) } func testMergeBlocksWithGapOverThreshold() { let blocks = [block(0, 10), block(20, 30)] let merged = mergeBlocks(blocks, gapMinutes: 5) XCTAssertEqual(merged.count, 2) } func testMergeBlocksEmpty() { XCTAssertTrue(mergeBlocks([], gapMinutes: 10).isEmpty) } func testMergeBlocksUnsortedInput() { let blocks = [block(30, 40), block(0, 10), block(10, 20)] let merged = mergeBlocks(blocks, gapMinutes: 0) XCTAssertEqual(merged.count, 2) XCTAssertEqual(merged[0].start, blocks[1].start) XCTAssertEqual(merged[0].end, blocks[2].end) XCTAssertEqual(merged[1].start, blocks[0].start) XCTAssertEqual(merged[1].end, blocks[0].end) } // MARK: - coalesce func testCoalesceOverlappingBlocks() { let blocks = [block(0, 15), block(10, 20), block(25, 30)] let result = coalesce(blocks) XCTAssertEqual(result.count, 2) XCTAssertEqual(result[0].start, blocks[0].start) XCTAssertEqual(result[0].end, blocks[1].end) XCTAssertEqual(result[1].start, blocks[2].start) XCTAssertEqual(result[1].end, blocks[2].end) } // MARK: - fullyCovered func testFullyCoveredExactMatch() { let occupied = [block(0, 10)] let b = block(0, 10) XCTAssertTrue(fullyCovered(occupied, block: b, tolMin: 0)) } func testFullyCoveredPartialOverlap() { let occupied = [block(0, 5)] let b = block(0, 10) XCTAssertFalse(fullyCovered(occupied, block: b, tolMin: 0)) } func testFullyCoveredWithTolerance() { let occupied = [block(0, 10)] let b = block(2, 8) XCTAssertTrue(fullyCovered(occupied, block: b, tolMin: 5)) } func testFullyCoveredMultipleSegments() { let occupied = coalesce([block(0, 3), block(3, 10)]) let b = block(0, 10) XCTAssertTrue(fullyCovered(occupied, block: b, tolMin: 0)) } // MARK: - gapsWithin func testGapsWithinNoOccupied() { let b = block(0, 60) let gaps = gapsWithin([], in: b) XCTAssertEqual(gaps.count, 1) XCTAssertEqual(gaps[0].start, b.start) XCTAssertEqual(gaps[0].end, b.end) } func testGapsWithinSingleGap() { let occupied = [block(0, 10), block(20, 30)] let b = block(0, 30) let gaps = gapsWithin(occupied, in: b) XCTAssertEqual(gaps.count, 1) XCTAssertEqual(gaps[0].start, occupied[0].end) XCTAssertEqual(gaps[0].end, occupied[1].start) } func testGapsWithinMultipleGaps() { let occupied = [block(5, 10), block(15, 20)] let b = block(0, 30) let gaps = gapsWithin(occupied, in: b) XCTAssertEqual(gaps.count, 3) XCTAssertEqual(gaps[0].start, b.start) XCTAssertEqual(gaps[0].end, occupied[0].start) XCTAssertEqual(gaps[1].start, occupied[0].end) XCTAssertEqual(gaps[1].end, occupied[1].start) XCTAssertEqual(gaps[2].start, occupied[1].end) XCTAssertEqual(gaps[2].end, b.end) } func testGapsWithinExactFit() { let occupied = [block(0, 10)] let b = block(0, 10) let gaps = gapsWithin(occupied, in: b) XCTAssertTrue(gaps.isEmpty) } // MARK: - uniqueBlocks func testUniqueBlocksByTime() { let blocks = [block(0, 10), block(0, 10), block(10, 20)] let result = uniqueBlocks(blocks, trackByID: false) XCTAssertEqual(result.count, 2) } func testUniqueBlocksByID() { let blocks = [ block(0, 10, id: "a"), block(0, 10, id: "a"), block(5, 15, id: "b") ] let result = uniqueBlocks(blocks, trackByID: true) XCTAssertEqual(result.count, 2) } func testUniqueBlocksByIDDifferentOccurrence() { let b1 = Block(start: d, end: d.addingTimeInterval(600), srcStableID: "a", label: nil, notes: nil, occurrence: d) let b2 = Block(start: d, end: d.addingTimeInterval(600), srcStableID: "a", label: nil, notes: nil, occurrence: d.addingTimeInterval(3600)) let result = uniqueBlocks([b1, b2], trackByID: true) XCTAssertEqual(result.count, 2) } }