Skip to content

fix: clamp ProgressBar barLength to non-negative value to avoid crash#1525

Open
ErwanLegrand wants to merge 1 commit into
apple:mainfrom
ErwanLegrand:crash
Open

fix: clamp ProgressBar barLength to non-negative value to avoid crash#1525
ErwanLegrand wants to merge 1 commit into
apple:mainfrom
ErwanLegrand:crash

Conversation

@ErwanLegrand
Copy link
Copy Markdown

A negative progress value (e.g. from a race in progress events) would produce a negative barLength and crash inside String(repeating:count:). Wrap the computed length in max(0, ...) and add a regression test that calls set(size: -10) and exercises draw(state:detail:).

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

This fixes an Apple container crash

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

A negative progress value (e.g. from a race in progress events) would
produce a negative barLength and crash inside String(repeating:count:).
Wrap the computed length in max(0, ...) and add a regression test that
calls set(size: -10) and exercises draw(state:detail:).
@jglogan jglogan requested a review from dkovba May 11, 2026 16:05
let usedWidth = (useColor ? joinedComponents.visibleLength : joinedComponents.count) + 45
let remainingWidth = max(config.width - usedWidth, 1)
let barLength = min(remainingWidth, state.finished ? remainingWidth : Int(Int64(remainingWidth) * value / total))
let barLength = min(remainingWidth, max(0, state.finished ? remainingWidth : Int(Int64(remainingWidth) * value / total)))
Copy link
Copy Markdown
Contributor

@jglogan jglogan May 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dkovba I'd welcome suggestions on how to make this more readable. The clamp makes sense in terms of preventing a negative bar length, but the way it's written, it's pretty complicated to reason about.

There is no need to include the entire thing in the clamp since remainingWidth is already guaranteed to be greater than 1.

The value Int(Int64(remainingWidth) * value / total)) should be extracted to its own let assignment with a constant name that makes sense.

This would make more sense to me if we did something like:

// reserve spaces between components, plus 45 for components rendered after the bar (size, speed, time, etc.)
let reservedWidth = (useColor ? joinedComponents.visibleLength : joinedComponents.count) + 45
let completedBarWidth = max(config.width - usedWidth, 1)
let progressWidth = max(0, Int(Int64(completedBarWidth) * value / total))
let currentBarWidth: Int
if state.finished {
    currentBarWidth = completedBarWidth
} else {
    currentBarWidth = min(completedBarWidth, progressWidth)
}

Copy link
Copy Markdown
Contributor

@jglogan jglogan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix looks good but let's make the logic a little more readable. Thanks!

@github-actions
Copy link
Copy Markdown

Code Coverage

Tier Line Coverage
Unit 33.3%
Integration 20.43%
Combined 53.15%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants