Skip to content

Commit

Permalink
fix: avoid overflow in progress bar percentage (#1178)
Browse files Browse the repository at this point in the history
Signed-off-by: Billy Zha <jinzha1@microsoft.com>
  • Loading branch information
qweeah authored Nov 8, 2023
1 parent 92f9c8b commit 5e62c14
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 6 deletions.
13 changes: 8 additions & 5 deletions cmd/oras/internal/display/progress/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type status struct {
func newStatus() *status {
return &status{
offset: -1,
total: humanize.ToBytes(0),
lastRenderTime: time.Now(),
}
}
Expand Down Expand Up @@ -99,9 +100,6 @@ func (s *status) String(width int) (string, string) {
// todo: doesn't support multiline prompt
total := uint64(s.descriptor.Size)
var percent float64
if s.offset >= 0 {
percent = float64(s.offset) / float64(total)
}

name := s.descriptor.Annotations["org.opencontainers.image.title"]
if name == "" {
Expand All @@ -112,10 +110,15 @@ func (s *status) String(width int) (string, string) {
// mark(1) bar(22) speed(8) action(<=11) name(<=126) size_per_size(<=13) percent(8) time(>=6)
// └─ digest(72)
var offset string
switch percent {
case 1: // 100%, show exact size
switch s.done {
case true: // 100%, show exact size
offset = fmt.Sprint(s.total.Size)
percent = 1
default: // 0% ~ 99%, show 2-digit precision
if total != 0 && s.offset >= 0 {
// percentage calculatable
percent = float64(s.offset) / float64(total)
}
offset = fmt.Sprintf("%.2f", humanize.RoundTo(s.total.Size*percent))
}
right := fmt.Sprintf(" %s/%s %6.2f%% %6s", offset, s.total, percent*100, s.durationString())
Expand Down
36 changes: 35 additions & 1 deletion cmd/oras/internal/display/progress/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ func Test_status_String(t *testing.T) {
if err := testutils.OrderedMatch(statusStr+digestStr, " [\x1b[7m\x1b[0m....................]", s.prompt, "application/v.", "0.00/2 B", "0.00%", s.descriptor.Digest.String()); err != nil {
t.Error(err)
}

// done
s.Update(&status{
endTime: time.Now(),
Expand All @@ -67,6 +66,41 @@ func Test_status_String(t *testing.T) {
}
}

func Test_status_String_zeroWitdth(t *testing.T) {
// zero status and progress
s := newStatus()
if status, digest := s.String(console.MinWidth); status != zeroStatus || digest != zeroDigest {
t.Errorf("status.String() = %v, %v, want %v, %v", status, digest, zeroStatus, zeroDigest)
}

// not done
s.Update(&status{
prompt: "test",
descriptor: ocispec.Descriptor{
MediaType: "application/vnd.oci.empty.oras.test.v1+json",
Size: 0,
Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
},
startTime: time.Now().Add(-time.Minute),
offset: 0,
total: humanize.ToBytes(0),
})
// not done
statusStr, digestStr := s.String(120)
if err := testutils.OrderedMatch(statusStr+digestStr, " [\x1b[7m\x1b[0m....................]", s.prompt, s.descriptor.MediaType, "0.00/0 B", "0.00%", s.descriptor.Digest.String()); err != nil {
t.Error(err)
}
// done
s.Update(&status{
endTime: time.Now(),
offset: s.descriptor.Size,
descriptor: s.descriptor,
})
statusStr, digestStr = s.String(120)
if err := testutils.OrderedMatch(statusStr+digestStr, "✓", s.prompt, s.descriptor.MediaType, "0/0 B", "100.00%", s.descriptor.Digest.String()); err != nil {
t.Error(err)
}
}
func Test_status_durationString(t *testing.T) {
// zero duration
s := newStatus()
Expand Down

0 comments on commit 5e62c14

Please sign in to comment.