• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / progressbar / 1687673904

25 Feb 2025 01:29PM UTC coverage: 54.651% (-45.3%) from 100.0%
1687673904

Pull #9

gitlab-ci

lluiscampos
fix: Fix "overflow" on the reported percentage

The last tick will be of the block size being read, which may be bigger
than the remaining size.

On big files this is not appreciable, but for example if you are writing
a file of less than the block size the progress will end with >100%.

Changelog: Title
Ticket: MEN-8127

Signed-off-by: Lluis Campos <lluis.campos@northern.tech>
Pull Request #9: MEN-8127: Fix "overflow" on the reported percentage

4 of 4 new or added lines in 1 file covered. (100.0%)

7 existing lines in 1 file now uncovered.

47 of 86 relevant lines covered (54.65%)

3.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.65
/progress.go
1
// Copyright 2021 Northern.tech AS
2
//
3
//        Licensed under the Apache License, Version 2.0 (the "License");
4
//        you may not use this file except in compliance with the License.
5
//        You may obtain a copy of the License at
6
//
7
//            http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//        Unless required by applicable law or agreed to in writing, software
10
//        distributed under the License is distributed on an "AS IS" BASIS,
11
//        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//        See the License for the specific language governing permissions and
13
//        limitations under the License.
14
package progressbar
15

16
// TODO -- Add terminal width respect
17

18
import (
19
        "fmt"
20
        "io"
21
        "os"
22
        "strings"
23

24
        "github.com/mattn/go-isatty"
25
        // "golang.org/x/sys/unix" - Do not add for now (split into minimal and not (?))
26
)
27

28
type Renderer interface {
29
        Render(int) // Write the progressbar
30
}
31

32
type Bar struct {
33
        Size         int64
34
        currentCount int64
35
        Renderer
36
        Percentage int
37
}
38

39
func New(size int64) *Bar {
×
40
        if isatty.IsTerminal(os.Stderr.Fd()) {
×
41
                return &Bar{
×
42
                        Renderer: &TTYRenderer{
×
43
                                Out:            os.Stderr,
×
44
                                ProgressMarker: ".",
×
45
                                terminalWidth:  70,
×
46
                        },
×
47
                        Size: size,
×
48
                }
×
49
        } else {
×
50
                return &Bar{
×
51
                        Renderer: &NoTTYRenderer{
×
52
                                Out:            os.Stderr,
×
53
                                ProgressMarker: ".",
×
54
                                terminalWidth:  70,
×
55
                        },
×
56
                        Size: size,
×
57
                }
×
58
        }
×
59
}
60

61
func (b *Bar) Tick(n int64) {
12✔
62
        b.currentCount += n
12✔
63
        if b.Size > 0 {
24✔
64
                percentage := int((float64(b.currentCount) / float64(b.Size)) * 100)
12✔
65
                b.Percentage = percentage
12✔
66
                b.Renderer.Render(percentage)
12✔
67
        }
12✔
68
}
69

UNCOV
70
func (b *Bar) Reset() {
×
UNCOV
71
        b.currentCount = 0
×
UNCOV
72
        b.Renderer.Render(0)
×
UNCOV
73
}
×
74

75
func (b *Bar) Finish() {
×
76
        b.Renderer.Render(100)
×
77
}
×
78

79
type TTYRenderer struct {
80
        Out            io.Writer // Output device
81
        ProgressMarker string
82
        terminalWidth  int
83
        lastPercentage int
84
}
85

86
func (p *TTYRenderer) Render(percentage int) {
6✔
87
        if percentage <= p.lastPercentage {
6✔
UNCOV
88
                return
×
UNCOV
89
        }
×
90
        // Render to a max of 100%. It may happen that the last Tick may be bigger than the remaining size
91
        if percentage > 100 {
7✔
92
                percentage = 100
1✔
93
        }
1✔
94
        suffix := fmt.Sprintf(" - %3d %%", percentage)
6✔
95
        widthAvailable := p.terminalWidth - len(suffix)
6✔
96
        number_of_dots := int((float64(widthAvailable) * float64(percentage)) / 100)
6✔
97
        number_of_fillers := widthAvailable - number_of_dots
6✔
98
        if percentage < 0 {
6✔
UNCOV
99
                return
×
100
        }
×
101
        if number_of_dots < 0 {
6✔
102
                return
×
103
        }
×
104
        if number_of_fillers < 0 {
6✔
105
                return
×
106
        }
×
107
        p.lastPercentage = percentage
6✔
108
        fmt.Fprintf(p.Out, "\r%s%s%s",
6✔
109
                strings.Repeat(p.ProgressMarker, number_of_dots),
6✔
110
                strings.Repeat(" ", number_of_fillers),
6✔
111
                suffix)
6✔
112
        if percentage == 100 {
8✔
113
                fmt.Fprintln(p.Out)
2✔
114
        }
2✔
115
}
116

117
type NoTTYRenderer struct {
118
        Out              io.Writer // Output device
119
        ProgressMarker   string
120
        lastNumberOfDots int
121
        terminalWidth    int
122
        headerPrinted    bool
123
}
124

125
func (p *NoTTYRenderer) Render(percentage int) {
6✔
126
        if p.lastNumberOfDots >= p.terminalWidth {
7✔
127
                return
1✔
128
        }
1✔
129
        if !p.headerPrinted {
6✔
130
                // width, evenly distributed in half, taken away the characters
1✔
131
                // 0%,50% & 100%
1✔
132
                w := (p.terminalWidth - 2 - 3 - 4) / 2
1✔
133
                fmt.Fprintf(p.Out, "0%%%[1]*s50%%%[1]*[2]s100%%\n", w, " ")
1✔
134
                fmt.Fprintf(p.Out, "|%s|%[1]s|\n", strings.Repeat("-", (p.terminalWidth-3)/2))
1✔
135
                p.headerPrinted = true
1✔
136
        }
1✔
137
        slope := float64(p.terminalWidth) / 100
5✔
138
        fx := slope * float64(percentage)
5✔
139
        if int(fx) > p.lastNumberOfDots {
10✔
140
                numberOfDots := int(fx) - p.lastNumberOfDots
5✔
141
                str := strings.Repeat(p.ProgressMarker, numberOfDots)
5✔
142
                if numberOfDots+p.lastNumberOfDots > p.terminalWidth {
5✔
143
                        // Print only the difference
×
144
                        str = strings.Repeat(p.ProgressMarker, p.terminalWidth-p.lastNumberOfDots)
×
145
                        numberOfDots = p.terminalWidth - p.lastNumberOfDots
×
146
                }
×
147
                p.lastNumberOfDots += numberOfDots
5✔
148
                fmt.Fprintf(p.Out, str)
5✔
149
        }
150
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc