trees: show its phases as N of M, with progress, elapsed time and ETA (closes #113)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
trees now runs in four phases, open, read, group and write, each under the progress display scan uses, from the line naming the database to the summary line. open covers a wait on a busy database. read counts the records first, as scan's load does, then reads them against that count; group counts the directories, and write the groups. The display gains a unit so that each rate names what its phase counts; every scan phase passes files and prints as before. The tests that pinned trees' stderr now allow the phase lines between the database line and the summary. Judgement call: opening the database is a phase of its own, so there are four phases, not the three the issue sketches. Judgement call: the count and the read are separate queries, as in scan's load, so during a scan the read can end slightly past or short of its total. Model: opus-5-5
This commit is contained in:
+64
-19
@@ -86,30 +86,44 @@ func TestProgressLine(t *testing.T) {
|
||||
},
|
||||
want: "[1/5] load: counting the records, elapsed 10s, eta unknown",
|
||||
}, {
|
||||
p: &progress{label: scanPhase("hash"), total: 100, count: 20},
|
||||
p: &progress{
|
||||
label: scanPhase("hash"), unit: scanUnit, total: 100, count: 20,
|
||||
},
|
||||
want: "[3/5] hash: [20/100] 20%, 2 files/s, elapsed 10s, eta 40s",
|
||||
}, {
|
||||
p: &progress{label: scanPhase("hash"), total: 100},
|
||||
p: &progress{label: scanPhase("hash"), unit: scanUnit, total: 100},
|
||||
want: "[3/5] hash: [0/100] 0%, 0 files/s, elapsed 10s, eta unknown",
|
||||
}, {
|
||||
p: &progress{label: scanPhase("update"), total: 0},
|
||||
p: &progress{label: scanPhase("update"), unit: scanUnit, total: 0},
|
||||
want: "[4/5] update: [0/0] 100%, 0 files/s, elapsed 10s, eta 0s",
|
||||
}, {
|
||||
p: &progress{
|
||||
label: scanPhase("walk"), total: 100, estimate: true, count: 20,
|
||||
label: scanPhase("walk"), unit: scanUnit, total: 100,
|
||||
estimate: true, count: 20,
|
||||
},
|
||||
want: "[2/5] walk: [20/~100] 20%, 2 files/s, elapsed 10s, eta 40s",
|
||||
}, {
|
||||
// Past the estimate the walk cannot tell how long it has left.
|
||||
p: &progress{
|
||||
label: scanPhase("walk"), total: 100, estimate: true, count: 120,
|
||||
label: scanPhase("walk"), unit: scanUnit, total: 100,
|
||||
estimate: true, count: 120,
|
||||
},
|
||||
want: "[2/5] walk: [120/~100] 120%, 12 files/s, elapsed 10s, " +
|
||||
"eta unknown",
|
||||
}, {
|
||||
// A first scan's walk has no estimate.
|
||||
p: &progress{label: scanPhase("walk"), total: -1, count: 20},
|
||||
p: &progress{
|
||||
label: scanPhase("walk"), unit: scanUnit, total: -1, count: 20,
|
||||
},
|
||||
want: "[2/5] walk: 20 files, 2 files/s, elapsed 10s, eta unknown",
|
||||
}, {
|
||||
// The rate is in what the phase counts.
|
||||
p: &progress{
|
||||
label: treesPhase("group"), unit: "directories", total: 100,
|
||||
count: 20,
|
||||
},
|
||||
want: "[3/4] group: [20/100] 20%, 2 directories/s, elapsed 10s, " +
|
||||
"eta 40s",
|
||||
}}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -129,7 +143,7 @@ func TestLineFitsRoom(t *testing.T) {
|
||||
|
||||
ago := time.Now().Add(-10 * time.Second)
|
||||
hash := &progress{
|
||||
label: scanPhase("hash"), total: 100, count: 20,
|
||||
label: scanPhase("hash"), unit: scanUnit, total: 100, count: 20,
|
||||
start: ago, rateStart: ago,
|
||||
}
|
||||
content := &progress{
|
||||
@@ -183,7 +197,8 @@ func TestSetTotalStartsRate(t *testing.T) {
|
||||
|
||||
waited := time.Now().Add(-20 * time.Second)
|
||||
p := &progress{
|
||||
label: scanPhase("load"), total: -1, start: waited, rateStart: waited,
|
||||
label: scanPhase("load"), unit: scanUnit, total: -1, start: waited,
|
||||
rateStart: waited,
|
||||
}
|
||||
|
||||
p.setTotal(100)
|
||||
@@ -226,7 +241,9 @@ func awaitOutput(t *testing.T, stderr func() string, want string,
|
||||
//nolint:paralleltest // captureStderr replaces the process-wide os.Stderr
|
||||
func TestPlainProgressPrintsAtStartAndEnd(t *testing.T) {
|
||||
stderr := captureStderr(t)
|
||||
p := newProgress(progressParams{label: scanPhase("hash"), total: 1000})
|
||||
p := newProgress(progressParams{
|
||||
label: scanPhase("hash"), unit: scanUnit, total: 1000,
|
||||
})
|
||||
|
||||
for range 1000 {
|
||||
p.increment()
|
||||
@@ -289,8 +306,14 @@ func TestTerminalRedrawsWhileWaiting(t *testing.T) {
|
||||
params progressParams
|
||||
want string // how the line shows a count of 50
|
||||
}{
|
||||
{progressParams{label: scanPhase("walk"), total: -1}, "50 files,"},
|
||||
{progressParams{label: scanPhase("hash"), total: 100}, "[50/100],"},
|
||||
{
|
||||
progressParams{label: scanPhase("walk"), unit: scanUnit, total: -1},
|
||||
"50 files,",
|
||||
},
|
||||
{
|
||||
progressParams{label: scanPhase("hash"), unit: scanUnit, total: 100},
|
||||
"[50/100],",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -341,8 +364,9 @@ func drawLate(stderr func() string, params progressParams, width int,
|
||||
}
|
||||
|
||||
// TestTerminalLinesKeepETA checks the line each display shows on an
|
||||
// 80-column terminal at a large scan's counts and times: the rate is
|
||||
// left out, so that the elapsed time and the ETA stay whole.
|
||||
// 80-column terminal at a large scan's counts and times, and the line
|
||||
// of trees' read phase at the same counts: the rate is left out, so
|
||||
// that the elapsed time and the ETA stay whole.
|
||||
//
|
||||
//nolint:paralleltest // captureStderr replaces the process-wide os.Stderr
|
||||
func TestTerminalLinesKeepETA(t *testing.T) {
|
||||
@@ -354,18 +378,29 @@ func TestTerminalLinesKeepETA(t *testing.T) {
|
||||
}{{
|
||||
// Past its estimate, the walk cannot tell how long it has left.
|
||||
params: progressParams{
|
||||
label: scanPhase("walk"), total: 1200000, estimate: true,
|
||||
label: scanPhase("walk"), unit: scanUnit, total: 1200000,
|
||||
estimate: true,
|
||||
},
|
||||
want: "[2/5] walk: [1234567/~1200000] 103%, elapsed 2h30m0s, " +
|
||||
"eta unknown",
|
||||
}, {
|
||||
params: progressParams{label: scanPhase("hash"), total: 9876543},
|
||||
params: progressParams{
|
||||
label: scanPhase("hash"), unit: scanUnit, total: 9876543,
|
||||
},
|
||||
want: "[3/5] hash: [1234567/9876543] 12%, elapsed 2h30m0s, " +
|
||||
"eta 17h30m0s",
|
||||
}, {
|
||||
params: progressParams{label: scanPhase("content"), total: 9876543},
|
||||
params: progressParams{
|
||||
label: scanPhase("content"), unit: scanUnit, total: 9876543,
|
||||
},
|
||||
want: "[5/5] content: [1234567/9876543] 12%, elapsed 2h30m0s, " +
|
||||
"eta 17h30m0s",
|
||||
}, {
|
||||
params: progressParams{
|
||||
label: treesPhase("read"), unit: "records", total: 9876543,
|
||||
},
|
||||
want: "[2/4] read: [1234567/9876543] 12%, elapsed 2h30m0s, " +
|
||||
"eta 17h30m0s",
|
||||
}}
|
||||
|
||||
for _, tc := range cases {
|
||||
@@ -389,7 +424,9 @@ func TestTerminalLinesFitWidth(t *testing.T) {
|
||||
|
||||
for _, width := range []int{120, 100, 80, 72, 60, 50, 40} {
|
||||
for _, total := range []int64{9876543, 20000} {
|
||||
params := progressParams{label: scanPhase("content"), total: total}
|
||||
params := progressParams{
|
||||
label: scanPhase("content"), unit: scanUnit, total: total,
|
||||
}
|
||||
out := drawLate(stderr, params, width, total/8)
|
||||
|
||||
if width == 120 && !strings.Contains(out, "% |") {
|
||||
@@ -426,8 +463,16 @@ func TestTerminalNarrowedRedrawsInPlace(t *testing.T) {
|
||||
params progressParams
|
||||
narrow int
|
||||
}{
|
||||
{progressParams{label: scanPhase("walk"), total: -1}, 60},
|
||||
{progressParams{label: scanPhase("hash"), total: 9876543}, 90},
|
||||
{
|
||||
progressParams{label: scanPhase("walk"), unit: scanUnit, total: -1},
|
||||
60,
|
||||
},
|
||||
{
|
||||
progressParams{
|
||||
label: scanPhase("hash"), unit: scanUnit, total: 9876543,
|
||||
},
|
||||
90,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
|
||||
Reference in New Issue
Block a user