feat(xce-test-rc-file): task 1.2 rc-file line-loading via pluggable runner
Adds rcfile_load(path, runner) to rcfile.c/rcfile.h: reads lines with getline() (no fixed-size buffer, so a long line like a multi-peer `set masters ...` can never be silently truncated or split), strips the trailing newline, invokes the caller-supplied runner once per line, and stops at the first nonzero runner return, propagating that value. A missing rc file is a silent no-op; any other open failure prints one stderr warning and returns 0. Found during GREEN: fopen() succeeds on a directory path on this platform, so the EISDIR failure only surfaces on the first getline() call (ferror(f) set) -- added a post-loop ferror() check to catch it (bug-1716). 5 new unit tests (11 total with task 1.1): ENOENT-silent, per-line-in-order with newline stripped, stop-on-nonzero with exact propagation, non-ENOENT open-failure warning, and a >4KB single line proving no truncation/split. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
This commit is contained in:
parent
b3c462ede4
commit
77aba21fb0
4 changed files with 305 additions and 6 deletions
|
|
@ -9,7 +9,7 @@
|
||||||
- Observable: a unit test run demonstrates the correct location is chosen for each of the three environment combinations above, and a subsequent unrelated test's own `$HOME`/`$XDG_CONFIG_HOME` reads are unaffected
|
- Observable: a unit test run demonstrates the correct location is chosen for each of the three environment combinations above, and a subsequent unrelated test's own `$HOME`/`$XDG_CONFIG_HOME` reads are unaffected
|
||||||
- _Requirements: 1.1_
|
- _Requirements: 1.1_
|
||||||
|
|
||||||
- [ ] 1.2 Implement rc-file line-loading with a pluggable command runner, with unit tests
|
- [x] 1.2 Implement rc-file line-loading with a pluggable command runner, with unit tests
|
||||||
- Read the rc file line by line using `getline()` (no fixed-size buffer), invoking a caller-supplied runner once per line with the trailing newline removed
|
- Read the rc file line by line using `getline()` (no fixed-size buffer), invoking a caller-supplied runner once per line with the trailing newline removed
|
||||||
- Treat a missing rc file as a no-op success (no error), and a present-but-unreadable rc file as a non-fatal warning that still allows startup to proceed
|
- Treat a missing rc file as a no-op success (no error), and a present-but-unreadable rc file as a non-fatal warning that still allows startup to proceed
|
||||||
- Stop reading immediately the first time the runner signals a stop, and propagate that signal to the caller without processing any remaining lines
|
- Stop reading immediately the first time the runner signals a stop, and propagate that signal to the caller without processing any remaining lines
|
||||||
|
|
@ -53,3 +53,10 @@
|
||||||
- Observable: the existing scripts' outcomes are unchanged, demonstrating this feature introduces no regression for callers that know nothing about it
|
- Observable: the existing scripts' outcomes are unchanged, demonstrating this feature introduces no regression for callers that know nothing about it
|
||||||
- _Requirements: 1.3_
|
- _Requirements: 1.3_
|
||||||
- _Depends: 2.2_
|
- _Depends: 2.2_
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
- Task 1.1: `controller_unit_test` can silently die with exit code 1/2 before reaching later-registered test groups if `/tmp` fills up — a pre-existing, already-tracked leak (bug-924) where test fixtures under `/tmp/xce_*` aren't cleaned up across many prior sessions' runs can exhaust disk and make `mkdtemp()` in unrelated tests (e.g. `test_repl_apply.c`) `exit(1)` on `ENOSPC`. If a later task's `controller_unit_test` run dies early or shows an unexplained failure in an unrelated file, check `df -h /tmp` before assuming a regression in this task's own code.
|
||||||
|
- Task 1.1: the 2 pre-existing `test_config_repl.c` failures (`test_conf_trim_keeps_only_n_versions`, `test_conf_get_saved_tag_no_config`) are the tracked `bug-1561` overlayfs-artifact regression, unrelated to this spec — expect them to keep appearing in every subsequent task's `controller_unit_test` run; do not treat them as a new regression.
|
||||||
|
- Task 1.2: `fopen()` succeeds on a directory path on this platform's glibc — the EISDIR failure only surfaces on the first `getline()` call (`errno=EISDIR`, `ferror(f)` set). A naive `if (!f)` open-failure check misses this; a post-loop `ferror(f)` check is required to catch it (see `rcfile_load()`, bug-1716).
|
||||||
|
- Task 1.2: an ASAN heap-use-after-free was observed ONCE in an ad-hoc standalone `controller_unit_test` run (`commit_resolve_pending`/`fsync_worker`/`tap_teardown` in `test_apply_pump.c`) — did not reproduce on a second run or in the canonical `xce-build.sh` run. Unrelated to this spec's files; logged as bug-1717 (unreproduced flake) for tracking. If it recurs on a later task's run, capture the full ASAN report before assuming it's this spec's fault.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include "rcfile.h"
|
#include "rcfile.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -36,3 +37,44 @@ char* rcfile_resolve_path(void)
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int rcfile_load(const char* path, int (*runner)(char* line))
|
||||||
|
{
|
||||||
|
if (!path)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
FILE* f = fopen(path, "r");
|
||||||
|
if (!f) {
|
||||||
|
if (errno == ENOENT)
|
||||||
|
return 0; /* missing rc file: silent no-op success */
|
||||||
|
fprintf(stderr, "xce_test: warning: could not open rc file %s: %s\n",
|
||||||
|
path, strerror(errno));
|
||||||
|
return 0; /* present but unreadable: non-fatal warning */
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = 0;
|
||||||
|
char* line = NULL;
|
||||||
|
size_t cap = 0;
|
||||||
|
ssize_t n;
|
||||||
|
while ((n = getline(&line, &cap, f)) != -1) {
|
||||||
|
if (n > 0 && line[n - 1] == '\n')
|
||||||
|
line[n - 1] = '\0';
|
||||||
|
rc = runner(line);
|
||||||
|
if (rc != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fopen() succeeds on a directory on this platform -- the failure only
|
||||||
|
* surfaces on the first read (getline() returns -1 with errno==EISDIR
|
||||||
|
* and ferror(f) set, feof(f) NOT set). Treat that the same as an open
|
||||||
|
* failure: one non-fatal stderr warning, no runner calls attributed to
|
||||||
|
* it (rc is still 0 here since no line was ever successfully read). */
|
||||||
|
if (rc == 0 && ferror(f)) {
|
||||||
|
fprintf(stderr, "xce_test: warning: could not read rc file %s: %s\n",
|
||||||
|
path, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
fclose(f);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
/*
|
/*
|
||||||
* rcfile.h -- xce_test startup rc-file path resolution (xce-test-rc-file
|
* rcfile.h -- xce_test startup rc-file path resolution and line-loading
|
||||||
* spec, task 1.1).
|
* (xce-test-rc-file spec, tasks 1.1/1.2).
|
||||||
*
|
*
|
||||||
* A standalone, dependency-light module following the same pattern as
|
* A standalone, dependency-light module following the same pattern as
|
||||||
* configcompare.c/h -- no dependency on test.c internals, linked into both
|
* configcompare.c/h -- no dependency on test.c internals, linked into both
|
||||||
* the xce_test executable and controller_unit_test.
|
* the xce_test executable and controller_unit_test.
|
||||||
*
|
*
|
||||||
* This task implements path resolution only (rcfile_resolve_path()).
|
* Task 1.1 implemented path resolution (rcfile_resolve_path()). Task 1.2
|
||||||
* Line-loading (rcfile_load()) is a later task in the same spec and will
|
* adds line-loading (rcfile_load()) with a pluggable runner callback.
|
||||||
* extend this file, not replace it.
|
* Wiring rcfile_load() into xce_test's own startup/main() is a later task
|
||||||
|
* in the same spec (2.2) and is out of scope here -- this module has no
|
||||||
|
* knowledge of xce_test commands, contexts, or client state.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Resolves the rc-file path per the XDG Base Directory Specification:
|
/* Resolves the rc-file path per the XDG Base Directory Specification:
|
||||||
|
|
@ -18,3 +20,20 @@
|
||||||
* a heap string the caller must free(), or NULL if neither variable yields
|
* a heap string the caller must free(), or NULL if neither variable yields
|
||||||
* a usable base directory. */
|
* a usable base directory. */
|
||||||
char* rcfile_resolve_path(void);
|
char* rcfile_resolve_path(void);
|
||||||
|
|
||||||
|
/* Opens `path` and calls `runner(line)` once per line (trailing newline
|
||||||
|
* stripped), stopping early if `runner` returns nonzero. Lines are read via
|
||||||
|
* getline() with no length cap -- a line is never truncated or split
|
||||||
|
* regardless of length.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* 0 - path is NULL, the file does not exist (ENOENT),
|
||||||
|
* the file could not be opened for another reason
|
||||||
|
* (a warning is printed to stderr; non-fatal), or
|
||||||
|
* every line ran and every runner call returned 0.
|
||||||
|
* nonzero (== runner's) - the runner requested an abort; no further lines
|
||||||
|
* are read.
|
||||||
|
*
|
||||||
|
* Preconditions: `runner` is non-NULL when `path` is non-NULL.
|
||||||
|
* Postconditions: the file handle is always closed before returning. */
|
||||||
|
int rcfile_load(const char* path, int (*runner)(char* line));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <unity/unity.h>
|
#include <unity/unity.h>
|
||||||
|
|
||||||
|
|
@ -176,6 +179,229 @@ static void test_env_save_restore_roundtrips_all_three_prior_states(void)
|
||||||
env_restore(name, &real_prior);
|
env_restore(name, &real_prior);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- rcfile_load() tests (task 1.2) ---------------------------------- */
|
||||||
|
|
||||||
|
/* Test-local temp dir helper, mirroring test_configcompare.c's
|
||||||
|
* fresh_dir()/cleanup_dir() pattern -- keeps rc-file fixtures out of the
|
||||||
|
* shared /tmp leak this sandbox already tracks (bug-924) by cleaning up
|
||||||
|
* after itself instead of leaving an mkdtemp() dir behind forever. */
|
||||||
|
static char g_dir[256];
|
||||||
|
|
||||||
|
static void fresh_dir(void)
|
||||||
|
{
|
||||||
|
strcpy(g_dir, "/tmp/xce_rcfile_test_XXXXXX");
|
||||||
|
TEST_ASSERT_NOT_NULL(mkdtemp(g_dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup_dir(void)
|
||||||
|
{
|
||||||
|
char cmd[512];
|
||||||
|
snprintf(cmd, sizeof(cmd), "rm -rf %s", g_dir);
|
||||||
|
system(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_file(const char* path, const char* content, size_t len)
|
||||||
|
{
|
||||||
|
FILE* f = fopen(path, "wb");
|
||||||
|
TEST_ASSERT_NOT_NULL(f);
|
||||||
|
if (len > 0)
|
||||||
|
TEST_ASSERT_EQUAL_INT((int)len, (int)fwrite(content, 1, len, f));
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fake runner: records every line it is called with (heap copies, in call
|
||||||
|
* order) and, once scripted via fail_at_call, returns fail_rc on that call
|
||||||
|
* (1-based) -- lets a test assert both the exact propagated return value
|
||||||
|
* and the exact point iteration stopped. */
|
||||||
|
#define RCFILE_TEST_MAX_LINES 8
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* lines[RCFILE_TEST_MAX_LINES];
|
||||||
|
int count;
|
||||||
|
int fail_at_call; /* 1-based; 0 = never fail */
|
||||||
|
int fail_rc;
|
||||||
|
} rcfile_fake_runner_state_t;
|
||||||
|
|
||||||
|
static rcfile_fake_runner_state_t g_fake;
|
||||||
|
|
||||||
|
static void fake_runner_reset(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < g_fake.count; i++) {
|
||||||
|
free(g_fake.lines[i]);
|
||||||
|
g_fake.lines[i] = NULL;
|
||||||
|
}
|
||||||
|
g_fake.count = 0;
|
||||||
|
g_fake.fail_at_call = 0;
|
||||||
|
g_fake.fail_rc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fake_runner(char* line)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_LESS_THAN_INT(RCFILE_TEST_MAX_LINES, g_fake.count);
|
||||||
|
g_fake.lines[g_fake.count] = strdup(line);
|
||||||
|
TEST_ASSERT_NOT_NULL(g_fake.lines[g_fake.count]);
|
||||||
|
g_fake.count++;
|
||||||
|
if (g_fake.fail_at_call != 0 && g_fake.count == g_fake.fail_at_call)
|
||||||
|
return g_fake.fail_rc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_rcfile_load_returns_0_and_never_calls_runner_when_path_missing(void)
|
||||||
|
{
|
||||||
|
fresh_dir();
|
||||||
|
fake_runner_reset();
|
||||||
|
|
||||||
|
char missing_path[300];
|
||||||
|
snprintf(missing_path, sizeof(missing_path), "%s/does_not_exist", g_dir);
|
||||||
|
|
||||||
|
int rc = rcfile_load(missing_path, fake_runner);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, g_fake.count);
|
||||||
|
cleanup_dir();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_rcfile_load_calls_runner_once_per_line_in_order_newline_stripped(void)
|
||||||
|
{
|
||||||
|
fresh_dir();
|
||||||
|
fake_runner_reset();
|
||||||
|
|
||||||
|
char path[300];
|
||||||
|
snprintf(path, sizeof(path), "%s/rc", g_dir);
|
||||||
|
/* Last line deliberately has no trailing newline -- getline() must
|
||||||
|
* still hand it to the runner intact, not require a final \n. */
|
||||||
|
const char* content =
|
||||||
|
"set masters host1:1,host2:2\nworker\njoin\nquit";
|
||||||
|
write_file(path, content, strlen(content));
|
||||||
|
|
||||||
|
int rc = rcfile_load(path, fake_runner);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||||
|
TEST_ASSERT_EQUAL_INT(4, g_fake.count);
|
||||||
|
TEST_ASSERT_EQUAL_STRING("set masters host1:1,host2:2", g_fake.lines[0]);
|
||||||
|
TEST_ASSERT_EQUAL_STRING("worker", g_fake.lines[1]);
|
||||||
|
TEST_ASSERT_EQUAL_STRING("join", g_fake.lines[2]);
|
||||||
|
TEST_ASSERT_EQUAL_STRING("quit", g_fake.lines[3]);
|
||||||
|
cleanup_dir();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_rcfile_load_stops_after_nonzero_runner_and_propagates_value(void)
|
||||||
|
{
|
||||||
|
fresh_dir();
|
||||||
|
fake_runner_reset();
|
||||||
|
g_fake.fail_at_call = 2;
|
||||||
|
g_fake.fail_rc = 42;
|
||||||
|
|
||||||
|
char path[300];
|
||||||
|
snprintf(path, sizeof(path), "%s/rc", g_dir);
|
||||||
|
const char* content = "line1\nline2\nline3\nline4\n";
|
||||||
|
write_file(path, content, strlen(content));
|
||||||
|
|
||||||
|
int rc = rcfile_load(path, fake_runner);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_INT(42, rc);
|
||||||
|
TEST_ASSERT_EQUAL_INT(2, g_fake.count);
|
||||||
|
TEST_ASSERT_EQUAL_STRING("line1", g_fake.lines[0]);
|
||||||
|
TEST_ASSERT_EQUAL_STRING("line2", g_fake.lines[1]);
|
||||||
|
cleanup_dir();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A present-but-unreadable-as-a-regular-file path (a directory) must warn
|
||||||
|
* to stderr (non-fatal) and return 0 without ever calling the runner --
|
||||||
|
* proven here by actually capturing stderr via dup/dup2 (not just
|
||||||
|
* asserting the return value), then restoring the original fd exactly so
|
||||||
|
* later test files in this same combined binary see stderr unaffected. */
|
||||||
|
static void test_rcfile_load_directory_path_warns_and_returns_0(void)
|
||||||
|
{
|
||||||
|
fresh_dir();
|
||||||
|
fake_runner_reset();
|
||||||
|
|
||||||
|
fflush(stderr);
|
||||||
|
int saved_stderr_fd = dup(fileno(stderr));
|
||||||
|
TEST_ASSERT_TRUE(saved_stderr_fd >= 0);
|
||||||
|
|
||||||
|
char capture_path[300];
|
||||||
|
snprintf(capture_path, sizeof(capture_path), "%s/stderr_capture", g_dir);
|
||||||
|
FILE* redirected = freopen(capture_path, "w", stderr);
|
||||||
|
TEST_ASSERT_NOT_NULL(redirected);
|
||||||
|
|
||||||
|
int rc = rcfile_load(g_dir, fake_runner); /* g_dir is a directory */
|
||||||
|
|
||||||
|
fflush(stderr);
|
||||||
|
TEST_ASSERT_TRUE(dup2(saved_stderr_fd, fileno(stderr)) >= 0);
|
||||||
|
close(saved_stderr_fd);
|
||||||
|
clearerr(stderr);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, g_fake.count);
|
||||||
|
|
||||||
|
FILE* f = fopen(capture_path, "r");
|
||||||
|
TEST_ASSERT_NOT_NULL(f);
|
||||||
|
char buf[512];
|
||||||
|
size_t n = fread(buf, 1, sizeof(buf) - 1, f);
|
||||||
|
buf[n] = '\0';
|
||||||
|
fclose(f);
|
||||||
|
TEST_ASSERT_TRUE(n > 0);
|
||||||
|
TEST_ASSERT_NOT_NULL(strstr(buf, g_dir));
|
||||||
|
|
||||||
|
cleanup_dir();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Proves the getline()-not-fgets() design decision actually holds: a
|
||||||
|
* single line well beyond any typical small fixed-size buffer (e.g. the
|
||||||
|
* historic 256/1024-byte stack buffers this design note calls out) must
|
||||||
|
* reach the runner completely intact and unsplit -- a real content/length
|
||||||
|
* equality check, not just "didn't crash". */
|
||||||
|
static void test_rcfile_load_long_line_reaches_runner_intact_and_unsplit(void)
|
||||||
|
{
|
||||||
|
fresh_dir();
|
||||||
|
fake_runner_reset();
|
||||||
|
|
||||||
|
char path[300];
|
||||||
|
snprintf(path, sizeof(path), "%s/rc", g_dir);
|
||||||
|
|
||||||
|
/* Build a >4KB single line shaped like a real long `set masters` rc
|
||||||
|
* command: many comma-separated host:port pairs on one line. */
|
||||||
|
size_t cap = 8192;
|
||||||
|
char* long_line = (char*)malloc(cap);
|
||||||
|
TEST_ASSERT_NOT_NULL(long_line);
|
||||||
|
size_t off = (size_t)snprintf(long_line, cap, "set masters ");
|
||||||
|
int pair_no = 0;
|
||||||
|
while (off < 4096 + 200) {
|
||||||
|
int written = snprintf(long_line + off, cap - off,
|
||||||
|
"%shost%d:1000%d", pair_no ? "," : "",
|
||||||
|
pair_no, pair_no % 10);
|
||||||
|
TEST_ASSERT_TRUE(written > 0);
|
||||||
|
off += (size_t)written;
|
||||||
|
pair_no++;
|
||||||
|
}
|
||||||
|
TEST_ASSERT_TRUE(off > 4096);
|
||||||
|
|
||||||
|
/* File content: the long line, then a short trailer line, so a
|
||||||
|
* truncation/splitting bug would also show up as an extra/garbled
|
||||||
|
* runner call rather than only a length mismatch. */
|
||||||
|
size_t file_len = off + 1 /* \n */ + strlen("quit") + 1 /* \n */;
|
||||||
|
char* file_content = (char*)malloc(file_len + 1);
|
||||||
|
TEST_ASSERT_NOT_NULL(file_content);
|
||||||
|
int fc_len = snprintf(file_content, file_len + 1, "%s\nquit\n", long_line);
|
||||||
|
TEST_ASSERT_EQUAL_INT((int)file_len, fc_len);
|
||||||
|
|
||||||
|
write_file(path, file_content, file_len);
|
||||||
|
|
||||||
|
int rc = rcfile_load(path, fake_runner);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||||
|
TEST_ASSERT_EQUAL_INT(2, g_fake.count);
|
||||||
|
TEST_ASSERT_TRUE(strlen(g_fake.lines[0]) > 4096);
|
||||||
|
TEST_ASSERT_EQUAL_INT((int)off, (int)strlen(g_fake.lines[0]));
|
||||||
|
TEST_ASSERT_EQUAL_STRING(long_line, g_fake.lines[0]);
|
||||||
|
TEST_ASSERT_EQUAL_STRING("quit", g_fake.lines[1]);
|
||||||
|
|
||||||
|
free(long_line);
|
||||||
|
free(file_content);
|
||||||
|
cleanup_dir();
|
||||||
|
}
|
||||||
|
|
||||||
void test_rcfile(void)
|
void test_rcfile(void)
|
||||||
{
|
{
|
||||||
UnitySetTestFile(__FILE__);
|
UnitySetTestFile(__FILE__);
|
||||||
|
|
@ -185,4 +411,9 @@ void test_rcfile(void)
|
||||||
RUN_TEST(test_resolve_returns_null_when_neither_var_usable_both_unset);
|
RUN_TEST(test_resolve_returns_null_when_neither_var_usable_both_unset);
|
||||||
RUN_TEST(test_resolve_returns_null_when_neither_var_usable_both_empty);
|
RUN_TEST(test_resolve_returns_null_when_neither_var_usable_both_empty);
|
||||||
RUN_TEST(test_env_save_restore_roundtrips_all_three_prior_states);
|
RUN_TEST(test_env_save_restore_roundtrips_all_three_prior_states);
|
||||||
|
RUN_TEST(test_rcfile_load_returns_0_and_never_calls_runner_when_path_missing);
|
||||||
|
RUN_TEST(test_rcfile_load_calls_runner_once_per_line_in_order_newline_stripped);
|
||||||
|
RUN_TEST(test_rcfile_load_stops_after_nonzero_runner_and_propagates_value);
|
||||||
|
RUN_TEST(test_rcfile_load_directory_path_warns_and_returns_0);
|
||||||
|
RUN_TEST(test_rcfile_load_long_line_reaches_runner_intact_and_unsplit);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue