mirror of
https://github.com/apple/container.git
synced 2026-09-22 23:55:34 +00:00
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.
This commit is contained in:
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "cb5cf6c245fe60f647e4e106917dc2e6559eda5170af37e63e52e549b2eed566",
|
||||
"originHash" : "21dad499f34492edb861e54fe1e03ee3b00aa3d7371af6b09253bf04495427b9",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "async-http-client",
|
||||
@@ -15,8 +15,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/containerization.git",
|
||||
"state" : {
|
||||
"revision" : "56c06be9a9473b6df9031dd217551e6ad9a97d29",
|
||||
"version" : "0.1.0"
|
||||
"revision" : "4b05e5f2313e881ee048f7063b30e73070fbd1b1",
|
||||
"version" : "0.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ if let path = ProcessInfo.processInfo.environment["CONTAINERIZATION_PATH"] {
|
||||
scDependency = .package(path: path)
|
||||
scVersion = "latest"
|
||||
} else {
|
||||
scVersion = "0.1.0"
|
||||
scVersion = "0.1.1"
|
||||
scDependency = .package(url: "https://github.com/apple/containerization.git", exact: Version(stringLiteral: scVersion))
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
public struct XPCClient: Sendable {
|
||||
// Access to `connection` is protected by a lock
|
||||
private nonisolated(unsafe) let connection: xpc_connection_t
|
||||
private let q: DispatchQueue?
|
||||
private let service: String
|
||||
|
||||
@@ -61,23 +61,24 @@ extension ProgressBar {
|
||||
|
||||
/// Performs a check to see if the progress bar should be finished.
|
||||
public func checkIfFinished() {
|
||||
if let totalTasks = state.totalTasks {
|
||||
var finished = true
|
||||
var defined = false
|
||||
if let totalTasks = state.totalTasks, totalTasks > 0 {
|
||||
// For tasks, we're showing the current task rather then the number of completed tasks.
|
||||
guard state.tasks > totalTasks else {
|
||||
return
|
||||
}
|
||||
finished = finished && state.tasks == totalTasks
|
||||
defined = true
|
||||
}
|
||||
if let totalItems = state.totalItems {
|
||||
guard state.items == totalItems else {
|
||||
return
|
||||
}
|
||||
if let totalItems = state.totalItems, totalItems > 0 {
|
||||
finished = finished && state.items == totalItems
|
||||
defined = true
|
||||
}
|
||||
if let totalSize = state.totalSize {
|
||||
guard state.size == totalSize else {
|
||||
return
|
||||
}
|
||||
if let totalSize = state.totalSize, totalSize > 0 {
|
||||
finished = finished && state.size == totalSize
|
||||
defined = true
|
||||
}
|
||||
if defined && finished {
|
||||
finish()
|
||||
}
|
||||
finish()
|
||||
}
|
||||
|
||||
/// Sets the current tasks.
|
||||
@@ -92,9 +93,14 @@ extension ProgressBar {
|
||||
|
||||
/// Performs an addition to the current tasks.
|
||||
/// - Parameter tasks: The tasks to add to the current tasks.
|
||||
public func add(tasks toAdd: Int, render: Bool = true) {
|
||||
let newTasks = state.tasks + toAdd
|
||||
set(tasks: newTasks, render: render)
|
||||
public func add(tasks delta: Int, render: Bool = true) {
|
||||
_state.withLock {
|
||||
let newTasks = $0.tasks + delta
|
||||
$0.tasks = newTasks
|
||||
}
|
||||
if render {
|
||||
self.render()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the total tasks.
|
||||
@@ -108,10 +114,15 @@ extension ProgressBar {
|
||||
|
||||
/// Performs an addition to the total tasks.
|
||||
/// - Parameter totalTasks: The tasks to add to the total tasks.
|
||||
public func add(totalTasks toAdd: Int, render: Bool = true) {
|
||||
let totalTasks = state.totalTasks ?? 0
|
||||
let newTotalTasks = totalTasks + toAdd
|
||||
set(totalTasks: newTotalTasks, render: render)
|
||||
public func add(totalTasks delta: Int, render: Bool = true) {
|
||||
_state.withLock {
|
||||
let totalTasks = $0.totalTasks ?? 0
|
||||
let newTotalTasks = totalTasks + delta
|
||||
$0.totalTasks = newTotalTasks
|
||||
}
|
||||
if render {
|
||||
self.render()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the items name.
|
||||
@@ -134,9 +145,14 @@ extension ProgressBar {
|
||||
|
||||
/// Performs an addition to the current items.
|
||||
/// - Parameter items: The items to add to the current items.
|
||||
public func add(items toAdd: Int, render: Bool = true) {
|
||||
let newItems = state.items + toAdd
|
||||
set(items: newItems, render: render)
|
||||
public func add(items delta: Int, render: Bool = true) {
|
||||
_state.withLock {
|
||||
let newItems = $0.items + delta
|
||||
$0.items = newItems
|
||||
}
|
||||
if render {
|
||||
self.render()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the total items.
|
||||
@@ -150,10 +166,15 @@ extension ProgressBar {
|
||||
|
||||
/// Performs an addition to the total items.
|
||||
/// - Parameter totalItems: The items to add to the total items.
|
||||
public func add(totalItems toAdd: Int, render: Bool = true) {
|
||||
let totalItems = state.totalItems ?? 0
|
||||
let newTotalItems = totalItems + toAdd
|
||||
set(totalItems: newTotalItems, render: render)
|
||||
public func add(totalItems delta: Int, render: Bool = true) {
|
||||
_state.withLock {
|
||||
let totalItems = $0.totalItems ?? 0
|
||||
let newTotalItems = totalItems + delta
|
||||
$0.totalItems = newTotalItems
|
||||
}
|
||||
if render {
|
||||
self.render()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the current size.
|
||||
@@ -167,9 +188,14 @@ extension ProgressBar {
|
||||
|
||||
/// Performs an addition to the current size.
|
||||
/// - Parameter size: The size to add to the current size.
|
||||
public func add(size toAdd: Int64, render: Bool = true) {
|
||||
let newSize = state.size + toAdd
|
||||
set(size: newSize, render: render)
|
||||
public func add(size delta: Int64, render: Bool = true) {
|
||||
_state.withLock {
|
||||
let newSize = $0.size + delta
|
||||
$0.size = newSize
|
||||
}
|
||||
if render {
|
||||
self.render()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the total size.
|
||||
@@ -183,9 +209,14 @@ extension ProgressBar {
|
||||
|
||||
/// Performs an addition to the total size.
|
||||
/// - Parameter totalSize: The size to add to the total size.
|
||||
public func add(totalSize toAdd: Int64, render: Bool = true) {
|
||||
let totalSize = state.totalSize ?? 0
|
||||
let newTotalSize = totalSize + toAdd
|
||||
set(totalSize: newTotalSize, render: render)
|
||||
public func add(totalSize delta: Int64, render: Bool = true) {
|
||||
_state.withLock {
|
||||
let totalSize = $0.totalSize ?? 0
|
||||
let newTotalSize = totalSize + delta
|
||||
$0.totalSize = newTotalSize
|
||||
}
|
||||
if render {
|
||||
self.render()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,9 @@ import SendableProperty
|
||||
/// A progress bar that updates itself as tasks are completed.
|
||||
public final class ProgressBar: Sendable {
|
||||
let config: ProgressConfig
|
||||
// `@SendableProperty` adds `_state: Synchronized<State>`, which can be updated inside a lock using `_state.withLock()`.
|
||||
@SendableProperty
|
||||
var state: State
|
||||
var state = State()
|
||||
@SendableProperty
|
||||
var printedWidth = 0
|
||||
let term: FileHandle?
|
||||
@@ -97,7 +98,7 @@ public final class ProgressBar: Sendable {
|
||||
printFullDescription()
|
||||
}
|
||||
|
||||
while !isFinished {
|
||||
while !state.finished {
|
||||
let intervalNanoseconds = UInt64(intervalSeconds * 1_000_000_000)
|
||||
render()
|
||||
state.iteration += 1
|
||||
@@ -117,11 +118,15 @@ public final class ProgressBar: Sendable {
|
||||
|
||||
/// Finishes the progress bar.
|
||||
public func finish() {
|
||||
guard !isFinished else {
|
||||
guard !state.finished else {
|
||||
return
|
||||
}
|
||||
|
||||
state.finished = true
|
||||
|
||||
// The last render.
|
||||
render(force: true)
|
||||
|
||||
if !config.disableProgressUpdates && !config.clearOnFinish {
|
||||
displayText(state.output, terminating: "\n")
|
||||
}
|
||||
@@ -143,8 +148,8 @@ extension ProgressBar {
|
||||
return timeDifferenceSeconds
|
||||
}
|
||||
|
||||
func render() {
|
||||
guard term != nil && !config.disableProgressUpdates && !isFinished else {
|
||||
func render(force: Bool = false) {
|
||||
guard term != nil && !config.disableProgressUpdates && (force || !state.finished) else {
|
||||
return
|
||||
}
|
||||
let output = draw()
|
||||
@@ -154,8 +159,12 @@ extension ProgressBar {
|
||||
func draw() -> String {
|
||||
var components = [String]()
|
||||
if config.showSpinner && !config.showProgressBar {
|
||||
let spinnerIcon = config.theme.getSpinnerIcon(state.iteration)
|
||||
components.append("\(spinnerIcon)")
|
||||
if !state.finished {
|
||||
let spinnerIcon = config.theme.getSpinnerIcon(state.iteration)
|
||||
components.append("\(spinnerIcon)")
|
||||
} else {
|
||||
components.append("\(config.theme.done)")
|
||||
}
|
||||
}
|
||||
|
||||
if config.showTasks, let totalTasks = state.totalTasks {
|
||||
@@ -176,13 +185,13 @@ extension ProgressBar {
|
||||
let total = state.totalSize ?? Int64(state.totalItems ?? 0)
|
||||
|
||||
if config.showPercent && total > 0 && allowProgress {
|
||||
components.append("\(state.percent)")
|
||||
components.append("\(state.finished ? "100%" : state.percent)")
|
||||
}
|
||||
|
||||
if config.showProgressBar, total > 0, allowProgress {
|
||||
let usedWidth = components.joined(separator: " ").count + 45 /* the maximum number of characters we may need */
|
||||
let remainingWidth = max(config.width - usedWidth, 1 /* the minumum width of a progress bar */)
|
||||
let barLength = Int(Int64(remainingWidth) * value / total)
|
||||
let barLength = state.finished ? remainingWidth : Int(Int64(remainingWidth) * value / total)
|
||||
let barPaddingLength = remainingWidth - barLength
|
||||
let bar = "\(String(repeating: config.theme.bar, count: barLength))\(String(repeating: " ", count: barPaddingLength))"
|
||||
components.append("|\(bar)|")
|
||||
@@ -195,40 +204,56 @@ extension ProgressBar {
|
||||
if !state.itemsName.isEmpty {
|
||||
itemsName = " \(state.itemsName)"
|
||||
}
|
||||
if let totalItems = state.totalItems {
|
||||
additionalComponents.append("\(state.items.formattedNumber()) of \(totalItems.formattedNumber())\(itemsName)")
|
||||
if state.finished {
|
||||
if let totalItems = state.totalItems {
|
||||
additionalComponents.append("\(totalItems.formattedNumber())\(itemsName)")
|
||||
}
|
||||
} else {
|
||||
additionalComponents.append("\(state.items.formattedNumber())\(itemsName)")
|
||||
if let totalItems = state.totalItems {
|
||||
additionalComponents.append("\(state.items.formattedNumber()) of \(totalItems.formattedNumber())\(itemsName)")
|
||||
} else {
|
||||
additionalComponents.append("\(state.items.formattedNumber())\(itemsName)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if state.size > 0 && allowProgress {
|
||||
var formattedCombinedSize = ""
|
||||
if config.showSize {
|
||||
var formattedSize = state.size.formattedSize()
|
||||
formattedSize = adjustFormattedSize(formattedSize)
|
||||
if let totalSize = state.totalSize {
|
||||
var formattedTotalSize = totalSize.formattedSize()
|
||||
formattedTotalSize = adjustFormattedSize(formattedTotalSize)
|
||||
formattedCombinedSize = combineSize(size: formattedSize, totalSize: formattedTotalSize)
|
||||
} else {
|
||||
formattedCombinedSize = formattedSize
|
||||
if state.finished {
|
||||
if config.showSize {
|
||||
if let totalSize = state.totalSize {
|
||||
var formattedTotalSize = totalSize.formattedSize()
|
||||
formattedTotalSize = adjustFormattedSize(formattedTotalSize)
|
||||
additionalComponents.append(formattedTotalSize)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var formattedCombinedSize = ""
|
||||
if config.showSize {
|
||||
var formattedSize = state.size.formattedSize()
|
||||
formattedSize = adjustFormattedSize(formattedSize)
|
||||
if let totalSize = state.totalSize {
|
||||
var formattedTotalSize = totalSize.formattedSize()
|
||||
formattedTotalSize = adjustFormattedSize(formattedTotalSize)
|
||||
formattedCombinedSize = combineSize(size: formattedSize, totalSize: formattedTotalSize)
|
||||
} else {
|
||||
formattedCombinedSize = formattedSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var formattedSpeed = ""
|
||||
if config.showSpeed {
|
||||
formattedSpeed = "\(state.sizeSpeed ?? state.averageSizeSpeed)"
|
||||
formattedSpeed = adjustFormattedSize(formattedSpeed)
|
||||
}
|
||||
var formattedSpeed = ""
|
||||
if config.showSpeed {
|
||||
formattedSpeed = "\(state.sizeSpeed ?? state.averageSizeSpeed)"
|
||||
formattedSpeed = adjustFormattedSize(formattedSpeed)
|
||||
}
|
||||
|
||||
if config.showSize && config.showSpeed {
|
||||
additionalComponents.append(formattedCombinedSize)
|
||||
additionalComponents.append(formattedSpeed)
|
||||
} else if config.showSize {
|
||||
additionalComponents.append(formattedCombinedSize)
|
||||
} else if config.showSpeed {
|
||||
additionalComponents.append(formattedSpeed)
|
||||
if config.showSize && config.showSpeed {
|
||||
additionalComponents.append(formattedCombinedSize)
|
||||
additionalComponents.append(formattedSpeed)
|
||||
} else if config.showSize {
|
||||
additionalComponents.append(formattedCombinedSize)
|
||||
} else if config.showSpeed {
|
||||
additionalComponents.append(formattedSpeed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,16 @@
|
||||
public protocol ProgressTheme: Sendable {
|
||||
/// The icons used to represent a spinner.
|
||||
var spinner: [String] { get }
|
||||
/// The icons used to represent a progress bar.
|
||||
/// The icon used to represent a progress bar.
|
||||
var bar: String { get }
|
||||
/// The icon used to indicate that a progress bar finished.
|
||||
var done: String { get }
|
||||
}
|
||||
|
||||
public struct DefaultProgressTheme: ProgressTheme {
|
||||
public let spinner = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
||||
public let bar = "█"
|
||||
public let done = "✔"
|
||||
}
|
||||
|
||||
extension ProgressTheme {
|
||||
|
||||
@@ -30,6 +30,16 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -40,6 +50,17 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -93,6 +114,18 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -176,6 +209,19 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -190,6 +236,21 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -216,6 +277,20 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -229,6 +304,20 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -260,6 +349,18 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -294,6 +395,19 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -361,6 +475,19 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -397,6 +524,20 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -410,6 +551,20 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -463,6 +618,23 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -487,6 +659,19 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
@@ -504,6 +689,24 @@ final class ProgressBarTests: XCTestCase {
|
||||
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",
|
||||
|
||||
Reference in New Issue
Block a user