Skip to content

Commit c52d810

Browse files
committed
[Utility] Add SingleLineProgressBar
This progress bar is visually better if the terminal is "dumb".
1 parent 26ec917 commit c52d810

File tree

3 files changed

+56
-7
lines changed

3 files changed

+56
-7
lines changed

Diff for: Sources/Commands/SwiftTestTool.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ final class ParallelTestRunner {
463463
init(testPath: AbsolutePath, processSet: ProcessSet) {
464464
self.testPath = testPath
465465
self.processSet = processSet
466-
progressBar = createProgressBar(forStream: stdoutStream, header: "Tests")
466+
progressBar = createProgressBar(forStream: stdoutStream, header: "Testing:")
467467
}
468468

469469
/// Whether to display output from successful tests.
@@ -539,7 +539,7 @@ final class ParallelTestRunner {
539539

540540
// Wait till all threads finish execution.
541541
workers.forEach { $0.join() }
542-
progressBar.complete()
542+
progressBar.complete(success: outputs.failure.isEmpty)
543543

544544
if shouldOutputSuccess {
545545
printOutput(outputs.success)

Diff for: Sources/Utility/ProgressBar.swift

+53-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,44 @@ import Basic
1313
/// A protocol to operate on terminal based progress bars.
1414
public protocol ProgressBarProtocol {
1515
func update(percent: Int, text: String)
16-
func complete()
16+
func complete(success: Bool)
17+
}
18+
19+
/// A single line progress bar.
20+
public final class SingleLineProgressBar: ProgressBarProtocol {
21+
private let header: String
22+
private var isClear: Bool
23+
private var stream: OutputByteStream
24+
private var displayed: Set<Int> = []
25+
26+
init(stream: OutputByteStream, header: String) {
27+
self.stream = stream
28+
self.header = header
29+
self.isClear = true
30+
}
31+
32+
public func update(percent: Int, text: String) {
33+
if isClear {
34+
stream <<< header
35+
stream <<< "\n"
36+
stream.flush()
37+
isClear = false
38+
}
39+
40+
let displayPercentage = Int(Double(percent / 10).rounded(.down)) * 10
41+
if percent != 100, !displayed.contains(displayPercentage) {
42+
stream <<< String(displayPercentage) <<< ".. "
43+
displayed.insert(displayPercentage)
44+
}
45+
stream.flush()
46+
}
47+
48+
public func complete(success: Bool) {
49+
if success {
50+
stream <<< "OK"
51+
stream.flush()
52+
}
53+
}
1754
}
1855

1956
/// Simple ProgressBar which shows the update text in new lines.
@@ -41,7 +78,7 @@ public final class SimpleProgressBar: ProgressBarProtocol {
4178
stream.flush()
4279
}
4380

44-
public func complete() {
81+
public func complete(success: Bool) {
4582
}
4683
}
4784

@@ -90,15 +127,27 @@ public final class ProgressBar: ProgressBarProtocol {
90127
term.moveCursor(up: 1)
91128
}
92129

93-
public func complete() {
130+
public func complete(success: Bool) {
94131
term.endLine()
95132
}
96133
}
97134

98135
/// Creates colored or simple progress bar based on the provided output stream.
99136
public func createProgressBar(forStream stream: OutputByteStream, header: String) -> ProgressBarProtocol {
100-
if let stdStream = stream as? LocalFileOutputByteStream, let term = TerminalController(stream: stdStream) {
137+
guard let stdStream = stream as? LocalFileOutputByteStream else {
138+
return SimpleProgressBar(stream: stream, header: header)
139+
}
140+
141+
// If we have a terminal, use animated progress bar.
142+
if let term = TerminalController(stream: stdStream) {
101143
return ProgressBar(term: term, header: header)
102144
}
145+
146+
// If the terminal is dumb, use single line progress bar.
147+
if TerminalController.terminalType(stdStream) == .dumb {
148+
return SingleLineProgressBar(stream: stream, header: header)
149+
}
150+
151+
// Use simple progress bar by default.
103152
return SimpleProgressBar(stream: stream, header: header)
104153
}

Diff for: Tests/UtilityTests/ProgressBarTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ final class ProgressBarTests: XCTestCase {
9999
for i in 0...5 {
100100
bar.update(percent: i, text: String(i))
101101
}
102-
bar.complete()
102+
bar.complete(success: true)
103103
}
104104

105105
static var allTests = [

0 commit comments

Comments
 (0)