Files
container/Tests/TerminalProgressTests/ProgressBarTests.swift
Dmitry Kovba 206f3cc07c Improve accuracy of progress updates (#144)
This PR resolves the problem of dropped progress updates and ensures the
accuracy of the information provided in the progress bar. Additionally,
it adds the displaying of the finished state to the progress bar.
Requires merging and tagging
https://github.com/apple/containerization/pull/91.
2025-06-12 23:36:38 -07:00

756 lines
22 KiB
Swift

//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
//
import XCTest
@testable import TerminalProgress
final class ProgressBarTests: XCTestCase {
func testSpinner() async throws {
let config = try ProgressConfig(
description: "Task"
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testSpinnerFinished() async throws {
let config = try ProgressConfig(
description: "Task"
)
let progress = ProgressBar(config: config)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "✔ Task [0s]")
}
func testNoSpinner() async throws {
let config = try ProgressConfig(
description: "Task",
showSpinner: false
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "Task [0s]")
}
func testNoSpinnerFinished() async throws {
let config = try ProgressConfig(
description: "Task",
showSpinner: false
)
let progress = ProgressBar(config: config)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "Task [0s]")
}
func testNoTasks() async throws {
let config = try ProgressConfig(
description: "Task",
showTasks: false
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testTasks() async throws {
let config = try ProgressConfig(
description: "Task",
showTasks: true
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testTasksAdd() async throws {
let config = try ProgressConfig(
description: "Task",
showTasks: true
)
let progress = ProgressBar(config: config)
progress.add(tasks: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testTasksSet() async throws {
let config = try ProgressConfig(
description: "Task",
showTasks: true
)
let progress = ProgressBar(config: config)
progress.set(tasks: 2)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testTotalTasks() async throws {
let config = try ProgressConfig(
description: "Task",
showTasks: true,
totalTasks: 2
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ [0/2] Task [0s]")
}
func testTotalTasksFinished() async throws {
let config = try ProgressConfig(
description: "Task",
showTasks: true,
totalTasks: 2
)
let progress = ProgressBar(config: config)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "✔ [0/2] Task [0s]")
}
func testTotalTasksAdd() async throws {
let config = try ProgressConfig(
description: "Task",
showTasks: true,
totalTasks: 1
)
let progress = ProgressBar(config: config)
progress.add(totalTasks: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ [0/2] Task [0s]")
}
func testTotalTasksSet() async throws {
let config = try ProgressConfig(
description: "Task",
showTasks: true,
totalTasks: 1
)
let progress = ProgressBar(config: config)
progress.set(totalTasks: 2)
let output = progress.draw()
XCTAssertEqual(output, "⠋ [0/2] Task [0s]")
}
func testTotalTasksInvalid() throws {
do {
let _ = try ProgressConfig(description: "test", totalTasks: 0)
} catch ProgressConfig.Error.invalid(_) {
return
}
XCTFail("expected ProgressConfig.Error.invalid")
}
func testDescription() async throws {
let config = try ProgressConfig(
description: "Task"
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testNoDescription() async throws {
let config = try ProgressConfig()
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ [0s]")
}
func testNoPercent() async throws {
let config = try ProgressConfig(
description: "Task",
showPercent: false,
totalItems: 2
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testPercentHidden() async throws {
let config = try ProgressConfig(
description: "Task",
showPercent: true
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testPercentItems() async throws {
let config = try ProgressConfig(
description: "Task",
showPercent: true,
totalItems: 2
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% [0s]")
}
func testPercentItemsFinished() async throws {
let config = try ProgressConfig(
description: "Task",
showPercent: true,
totalItems: 2
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "✔ Task 100% [0s]")
}
func testPercentSize() async throws {
let config = try ProgressConfig(
description: "Task",
showPercent: true,
showSize: false,
showSpeed: false,
totalSize: 2
)
let progress = ProgressBar(config: config)
progress.set(size: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% [0s]")
}
func testPercentSizeFinished() async throws {
let config = try ProgressConfig(
description: "Task",
showPercent: true,
showSize: false,
showSpeed: false,
totalSize: 2
)
let progress = ProgressBar(config: config)
progress.set(size: 1)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "✔ Task 100% [0s]")
}
func testNoProgressBar() async throws {
let config = try ProgressConfig(
description: "Task",
showProgressBar: false,
totalItems: 2,
width: 57
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% [0s]")
}
func testProgressBar() async throws {
let config = try ProgressConfig(
description: "Task",
showProgressBar: true,
totalItems: 2,
width: 57
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
let output = progress.draw()
XCTAssertEqual(output, "Task 50% |██ | [0s]")
}
func testProgressBarFinished() async throws {
let config = try ProgressConfig(
description: "Task",
showProgressBar: true,
totalItems: 2,
width: 57
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "Task 100% |███| [0s]")
}
func testProgressBarMinWidth() async throws {
let config = try ProgressConfig(
description: "Task",
showProgressBar: true,
totalItems: 2,
width: 13
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
let output = progress.draw()
XCTAssertEqual(output, "Task 50% | | [0s]")
}
func testProgressBarMinWidthFinished() async throws {
let config = try ProgressConfig(
description: "Task",
showProgressBar: true,
totalItems: 2,
width: 13
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "Task 100% |█| [0s]")
}
func testNoItems() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: false
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testItemsZero() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testItemsAdd() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true
)
let progress = ProgressBar(config: config)
progress.add(items: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task (1 it) [0s]")
}
func testItemsAddFinish() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true
)
let progress = ProgressBar(config: config)
progress.add(items: 1)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "✔ Task [0s]")
}
func testItemsSet() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true
)
let progress = ProgressBar(config: config)
progress.set(items: 2)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task (2 it) [0s]")
}
func testTotalItemsZeroItems() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true,
totalItems: 1
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 0% [0s]")
}
func testTotalItems() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true,
totalItems: 2
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% (1 of 2 it) [0s]")
}
func testTotalItemsFinish() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true,
totalItems: 2
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "✔ Task 100% (2 it) [0s]")
}
func testTotalItemsAdd() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true,
totalItems: 1
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
progress.add(totalItems: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% (1 of 2 it) [0s]")
}
func testTotalItemsSet() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true,
totalItems: 1
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
progress.set(totalItems: 2)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% (1 of 2 it) [0s]")
}
func testTotalItemsInvalid() throws {
do {
let _ = try ProgressConfig(description: "test", totalItems: 0)
} catch ProgressConfig.Error.invalid(_) {
return
}
XCTFail("expected ProgressConfig.Error.invalid")
}
func testNoSize() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: false
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testSizeZero() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testSizeAdd() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true,
showSpeed: false
)
let progress = ProgressBar(config: config)
progress.add(size: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task (1 byte) [0s]")
}
func testSizeAddFinish() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true,
showSpeed: false
)
let progress = ProgressBar(config: config)
progress.add(size: 1)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "✔ Task [0s]")
}
func testSizeSet() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true,
showSpeed: false
)
let progress = ProgressBar(config: config)
progress.set(size: 2)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task (2 bytes) [0s]")
}
func testTotalSizeZeroSize() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true,
totalSize: 1
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 0% [0s]")
}
func testTotalSizeDifferentUnits() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true,
showSpeed: false,
totalSize: 2
)
let progress = ProgressBar(config: config)
progress.set(size: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% (1 byte/2 bytes) [0s]")
}
func testTotalSizeDifferentUnitsFinish() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true,
showSpeed: false,
totalSize: 2
)
let progress = ProgressBar(config: config)
progress.set(size: 1)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "✔ Task 100% (2 bytes) [0s]")
}
func testTotalSizeSameUnits() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true,
showSpeed: false,
totalSize: 4
)
let progress = ProgressBar(config: config)
progress.set(size: 2)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% (2/4 bytes) [0s]")
}
func testTotalSizeSameUnitsFinish() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true,
showSpeed: false,
totalSize: 4
)
let progress = ProgressBar(config: config)
progress.set(size: 2)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "✔ Task 100% (4 bytes) [0s]")
}
func testTotalSizeAdd() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true,
showSpeed: false,
totalSize: 3
)
let progress = ProgressBar(config: config)
progress.set(size: 2)
progress.add(totalSize: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% (2/4 bytes) [0s]")
}
func testTotalSizeSet() async throws {
let config = try ProgressConfig(
description: "Task",
showSize: true,
showSpeed: false,
totalSize: 3
)
let progress = ProgressBar(config: config)
progress.set(size: 2)
progress.set(totalSize: 4)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% (2/4 bytes) [0s]")
}
func testTotalSizeInvalid() throws {
do {
let _ = try ProgressConfig(description: "test", totalSize: 0)
} catch ProgressConfig.Error.invalid(_) {
return
}
XCTFail("expected ProgressConfig.Error.invalid")
}
func testItemsAndSize() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true,
showSize: true,
showSpeed: false,
totalItems: 2,
totalSize: 4
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
progress.set(size: 2)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% (1 of 2 it, 2/4 bytes) [0s]")
}
func testItemsAndSizeFinish() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true,
showSize: true,
showSpeed: false,
totalItems: 2,
totalSize: 4
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
progress.set(size: 2)
progress.finish()
let output = progress.draw()
XCTAssertEqual(output, "✔ Task 100% (2 it, 4 bytes) [0s]")
}
func testNoSpeed() async throws {
let config = try ProgressConfig(
description: "Task",
showSpeed: false,
totalSize: 4
)
let progress = ProgressBar(config: config)
progress.set(size: 2)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% (2/4 bytes) [0s]")
}
func testSpeed() async throws {
let config = try ProgressConfig(
description: "Task",
showSpeed: true,
totalSize: 4
)
let progress = ProgressBar(config: config)
progress.set(size: 2)
let output = progress.draw()
XCTAssertTrue(output.contains("/s"))
}
func testSpeedFinish() async throws {
let config = try ProgressConfig(
description: "Task",
showSpeed: true,
totalSize: 4
)
let progress = ProgressBar(config: config)
progress.set(size: 2)
progress.finish()
let output = progress.draw()
XCTAssertFalse(output.contains("/s"))
}
func testItemsSizeAndSpeed() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true,
showSize: true,
showSpeed: true,
totalItems: 2,
totalSize: 4
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
progress.set(size: 2)
let output = progress.draw()
XCTAssertTrue(output.contains("1 of 2 it, 2/4 bytes"))
XCTAssertTrue(output.contains("/s"))
}
func testItemsSizeAndSpeedFinish() async throws {
let config = try ProgressConfig(
description: "Task",
showItems: true,
showSize: true,
showSpeed: true,
totalItems: 2,
totalSize: 4
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
progress.set(size: 2)
progress.finish()
let output = progress.draw()
XCTAssertTrue(output.contains("2 it, 4 bytes"))
XCTAssertFalse(output.contains("/s"))
}
func testNoTime() async throws {
let config = try ProgressConfig(
description: "Task",
showTime: false
)
let progress = ProgressBar(config: config)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task")
}
func testTime() async throws {
let config = try ProgressConfig(
description: "Task",
showTime: true
)
let progress = ProgressBar(config: config)
sleep(1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [1s]")
}
func testIgnoreSmallSize() async throws {
let config = try ProgressConfig(
description: "Task",
ignoreSmallSize: true,
totalSize: 4
)
let progress = ProgressBar(config: config)
progress.set(size: 2)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task [0s]")
}
func testItemsName() async throws {
let config = try ProgressConfig(
description: "Task",
itemsName: "files",
showItems: true,
totalItems: 2
)
let progress = ProgressBar(config: config)
progress.set(items: 1)
let output = progress.draw()
XCTAssertEqual(output, "⠋ Task 50% (1 of 2 files) [0s]")
}
}