selftests/bpf: Add compare_text_to_expected() helper

Some selftests generate text (such as BTF dump) and check it against
an expectation committed nearby.

There is no shared way to do that. prog_tests/btf_dump.c assembles an
"awk ... | diff -u" pipeline and hands it to system(). Any other test
wanting the same behaviour needs to reproduce both the comparison and
the reporting of a mismatch.

test_progs captures per-subtest output by pointing the stdout and
stderr FILE * globals at a memstream. But a child process inherits
descriptors, not the globals. So the "| diff -u" goes to the console
instead of the subtest log and is absent from the failure report.

Add a helper that compares two strings and runs diff(1) on mismatch,
properly relaying the output to stdout. Add tests for the helper.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
Link: https://lore.kernel.org/r/20260828215207.3105313-2-ihor.solodrai@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Ihor Solodrai 2026-08-28 14:52:02 -07:00 committed by Alexei Starovoitov
parent 776c86a3e7
commit bd71904697
3 changed files with 69 additions and 0 deletions

View File

@ -179,3 +179,26 @@ void test_prog_tests_framework_expected_msgs(void)
}
}
}
void test_prog_tests_framework_compare_text(void)
{
int err;
if (test__start_subtest("compare_text_match")) {
err = compare_text_to_expected("same\n", "same\n");
ASSERT_EQ(err, 0, "match_rc");
test__end_subtest();
}
if (test__start_subtest("compare_text_mismatch")) {
err = compare_text_to_expected("line two\n", "line one\n");
fflush(stdout);
ASSERT_EQ(err, -1, "mismatch_rc");
ASSERT_HAS_SUBSTR(env.subtest_state->log_buf, "-line one",
"diff_has_expected");
ASSERT_HAS_SUBSTR(env.subtest_state->log_buf, "+line two",
"diff_has_actual");
test__end_subtest();
}
}

View File

@ -534,3 +534,46 @@ int stack_mprotect(void)
PROT_READ | PROT_WRITE | PROT_EXEC);
return ret;
}
int compare_text_to_expected(const char *actual, const char *expected)
{
char exp_path[] = "/tmp/selftest_expected.XXXXXX";
char act_path[] = "/tmp/selftest_actual.XXXXXX";
char buf[512], cmd[128];
int exp_fd, act_fd;
FILE *p;
if (!strcmp(actual, expected))
return 0;
exp_fd = mkstemp(exp_path);
act_fd = mkstemp(act_path);
if (exp_fd < 0 || act_fd < 0) {
fprintf(stdout, "output differs, no temp file for a diff\n");
goto out;
}
dprintf(exp_fd, "%s", expected);
dprintf(act_fd, "%s", actual);
snprintf(cmd, sizeof(cmd), "diff -u '%s' '%s'", exp_path, act_path);
p = popen(cmd, "r");
if (!p) {
fprintf(stdout, "output differs, '%s' did not run\n", cmd);
goto out;
}
while (fgets(buf, sizeof(buf), p))
fputs(buf, stdout);
pclose(p);
out:
if (exp_fd >= 0) {
close(exp_fd);
unlink(exp_path);
}
if (act_fd >= 0) {
close(act_fd);
unlink(act_path);
}
return -1;
}

View File

@ -61,4 +61,7 @@ int testing_prog_flags(void);
bool is_jit_enabled(void);
int stack_mprotect(void);
/* Runs diff(1) on mismatch */
int compare_text_to_expected(const char *actual, const char *expected);
#endif /* __TESTING_HELPERS_H */