Skip to content

Commit

Permalink
Test the progress display
Browse files Browse the repository at this point in the history
'progress.c' has seen a few fixes recently [1], and, unfortunately,
some of those fixes required further fixes [2].  It seems it's time to
have a few tests focusing on the subtleties of the progress display.

Add the 'test-tool progress' subcommand to help testing the progress
display, reading instructions from standard input and turning them
into calls to the display_progress() and display_throughput()
functions with the given parameters.

The progress display is, however, critically dependent on timing,
because it's only updated once every second or, if the toal is known
in advance, every 1%, and there is the throughput rate as well.  These
make the progress display far too undeterministic for testing as-is.
To address this, add a few testing-specific variables and functions to
'progress.c', allowing the the new test helper to:

  - Disable the triggered-every-second SIGALRM and set the
    'progress_update' flag explicitly based in the input instructions.
    This way the progress line will be updated deterministically when
    the test wants it to be updated.

  - Specify the time elapsed since start_progress() to make the
    throughput rate calculations deterministic.

Add the new test script 't0500-progress-display.sh' to check a few
simple cases with and without throughput, and that a shorter progress
line properly covers up the previously displayed line in different
situations.

[1] See commits 545dc34 (progress: break too long progress bar
    lines, 2019-04-12) and 9f1fd84 (progress: clear previous
    progress update dynamically, 2019-04-12).
[2] 1aed1a5 (progress: avoid empty line when breaking the progress
    line, 2019-05-19)

Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
szeder authored and gitster committed Sep 17, 2019
1 parent bbf4756 commit 2bb74b5
Show file tree
Hide file tree
Showing 6 changed files with 400 additions and 2 deletions.
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -728,6 +728,7 @@ TEST_BUILTINS_OBJS += test-parse-options.o
TEST_BUILTINS_OBJS += test-path-utils.o
TEST_BUILTINS_OBJS += test-pkt-line.o
TEST_BUILTINS_OBJS += test-prio-queue.o
TEST_BUILTINS_OBJS += test-progress.o
TEST_BUILTINS_OBJS += test-reach.o
TEST_BUILTINS_OBJS += test-read-cache.o
TEST_BUILTINS_OBJS += test-read-midx.o
Expand Down
32 changes: 30 additions & 2 deletions progress.c
Expand Up @@ -45,6 +45,19 @@ struct progress {

static volatile sig_atomic_t progress_update;

/*
* These are only intended for testing the progress output, i.e. exclusively
* for 'test-tool progress'.
*/
int progress_testing;
uint64_t progress_test_ns = 0;
void progress_test_force_update(void); /* To silence -Wmissing-prototypes */
void progress_test_force_update(void)
{
progress_update = 1;
}


static void progress_interval(int signum)
{
progress_update = 1;
Expand All @@ -55,6 +68,9 @@ static void set_progress_signal(void)
struct sigaction sa;
struct itimerval v;

if (progress_testing)
return;

progress_update = 0;

memset(&sa, 0, sizeof(sa));
Expand All @@ -72,6 +88,10 @@ static void set_progress_signal(void)
static void clear_progress_signal(void)
{
struct itimerval v = {{0,},};

if (progress_testing)
return;

setitimer(ITIMER_REAL, &v, NULL);
signal(SIGALRM, SIG_IGN);
progress_update = 0;
Expand Down Expand Up @@ -154,6 +174,14 @@ static void throughput_string(struct strbuf *buf, uint64_t total,
strbuf_humanise_rate(buf, rate * 1024);
}

static uint64_t progress_getnanotime(struct progress *progress)
{
if (progress_testing)
return progress->start_ns + progress_test_ns;
else
return getnanotime();
}

void display_throughput(struct progress *progress, uint64_t total)
{
struct throughput *tp;
Expand All @@ -164,7 +192,7 @@ void display_throughput(struct progress *progress, uint64_t total)
return;
tp = progress->throughput;

now_ns = getnanotime();
now_ns = progress_getnanotime(progress);

if (!tp) {
progress->throughput = tp = xcalloc(1, sizeof(*tp));
Expand Down Expand Up @@ -296,7 +324,7 @@ void stop_progress_msg(struct progress **p_progress, const char *msg)
struct throughput *tp = progress->throughput;

if (tp) {
uint64_t now_ns = getnanotime();
uint64_t now_ns = progress_getnanotime(progress);
unsigned int misecs, rate;
misecs = ((now_ns - progress->start_ns) * 4398) >> 32;
rate = tp->curr_total / (misecs ? misecs : 1);
Expand Down
81 changes: 81 additions & 0 deletions t/helper/test-progress.c
@@ -0,0 +1,81 @@
/*
* A test helper to exercise the progress display.
*
* Reads instructions from standard input, one instruction per line:
*
* "progress <items>" - Call display_progress() with the given item count
* as parameter.
* "throughput <bytes> <millis> - Call display_throughput() with the given
* byte count as parameter. The 'millis'
* specify the time elapsed since the
* start_progress() call.
* "update" - Set the 'progress_update' flag.
*
* See 't0500-progress-display.sh' for examples.
*/
#include "test-tool.h"
#include "gettext.h"
#include "parse-options.h"
#include "progress.h"
#include "strbuf.h"

/*
* These are defined in 'progress.c', but are not exposed in 'progress.h',
* because they are exclusively for testing.
*/
extern int progress_testing;
extern uint64_t progress_test_ns;
void progress_test_force_update(void);

int cmd__progress(int argc, const char **argv)
{
uint64_t total = 0;
const char *title;
struct strbuf line = STRBUF_INIT;
struct progress *progress;

const char *usage[] = {
"test-tool progress [--total=<n>] <progress-title>",
NULL
};
struct option options[] = {
OPT_INTEGER(0, "total", &total, "total number of items"),
OPT_END(),
};

argc = parse_options(argc, argv, NULL, options, usage, 0);
if (argc != 1)
die("need a title for the progress output");
title = argv[0];

progress_testing = 1;
progress = start_progress(title, total);
while (strbuf_getline(&line, stdin) != EOF) {
char *end;

if (skip_prefix(line.buf, "progress ", (const char **) &end)) {
uint64_t item_count = strtoull(end, &end, 10);
if (*end != '\0')
die("invalid input: '%s'\n", line.buf);
display_progress(progress, item_count);
} else if (skip_prefix(line.buf, "throughput ",
(const char **) &end)) {
uint64_t byte_count, test_ms;

byte_count = strtoull(end, &end, 10);
if (*end != ' ')
die("invalid input: '%s'\n", line.buf);
test_ms = strtoull(end + 1, &end, 10);
if (*end != '\0')
die("invalid input: '%s'\n", line.buf);
progress_test_ns = test_ms * 1000 * 1000;
display_throughput(progress, byte_count);
} else if (!strcmp(line.buf, "update"))
progress_test_force_update();
else
die("invalid input: '%s'\n", line.buf);
}
stop_progress(&progress);

return 0;
}
1 change: 1 addition & 0 deletions t/helper/test-tool.c
Expand Up @@ -42,6 +42,7 @@ static struct test_cmd cmds[] = {
{ "path-utils", cmd__path_utils },
{ "pkt-line", cmd__pkt_line },
{ "prio-queue", cmd__prio_queue },
{ "progress", cmd__progress },
{ "reach", cmd__reach },
{ "read-cache", cmd__read_cache },
{ "read-midx", cmd__read_midx },
Expand Down
1 change: 1 addition & 0 deletions t/helper/test-tool.h
Expand Up @@ -32,6 +32,7 @@ int cmd__parse_options(int argc, const char **argv);
int cmd__path_utils(int argc, const char **argv);
int cmd__pkt_line(int argc, const char **argv);
int cmd__prio_queue(int argc, const char **argv);
int cmd__progress(int argc, const char **argv);
int cmd__reach(int argc, const char **argv);
int cmd__read_cache(int argc, const char **argv);
int cmd__read_midx(int argc, const char **argv);
Expand Down

0 comments on commit 2bb74b5

Please sign in to comment.