Task 4.3: trace on all|job SEL... [level] [lines N] [file PATH], off and status, parsed from the raw line; invalid arguments are named and leave the settings unchanged; refcounted next/current settings; status shows the settings in effect or pending, whole arrays as <base>[*], and stored trace memory against its bound. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
649 lines
22 KiB
C
649 lines
22 KiB
C
/*
|
|
* test_repl_trace_cmd.c - the `trace` command grammar and the trace
|
|
* settings lifecycle (scheduler-sim-decision-trace spec, task 4.3;
|
|
* design.md "ReplTraceControl", "Trace settings lifecycle").
|
|
*
|
|
* Links the trace control (src/repl_trace.c), the store
|
|
* (src/repl_trace_store.c, for the memory line of `trace status`), the
|
|
* record model, the renderer and the shared REPL formatting module. The
|
|
* store's job-table reads are satisfied by fixture definitions of
|
|
* sched_get_job() and repl_job_state_name() (no jobs exist here).
|
|
*
|
|
* A session in progress is simulated through the state the session hooks
|
|
* (task 4.6) will maintain: `cur` takes a reference to `next` and
|
|
* `session_in_progress` is set; the end drops `cur` and clears the flag.
|
|
*
|
|
* Covers:
|
|
* - every grammar form: `on all`, `on job SEL...`, `level`, `lines`,
|
|
* `file PATH` (bare, quoted, with spaces), `off`, `status` (1.1, 1.2,
|
|
* 1.5, 1.8, 1.12);
|
|
* - selectors: plain job, element, element range, bare id (traced as a
|
|
* plain job and as every element of an array with that id, 1.2, 1.3);
|
|
* - every invalid-argument class leaves `next` untouched (1.9);
|
|
* - a path longer than 63 characters, a 4095-byte path accepted and a
|
|
* 4096-byte path rejected, and 20 job selectors (1.12);
|
|
* - commands replace only `next` (1.4, 1.6, 1.10);
|
|
* - `trace status` before, during and after a session (1.7).
|
|
*/
|
|
#define _GNU_SOURCE
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <unity/unity.h>
|
|
|
|
#include "repl_internal.h"
|
|
#include "sched/trace.h"
|
|
#include "sim.h"
|
|
#include "utils.h"
|
|
|
|
#define PLAIN_JOB MAKE_JOBID(1, 2, 1000, 0x0, (uint64_t)42)
|
|
#define PLAIN_JOB2 MAKE_JOBID(1, 2, 1000, 0x0, (uint64_t)43)
|
|
#define ARRAY_SEQ ((uint64_t)32768 * 5)
|
|
#define ARRAY_BASE MAKE_JOBID(1, 2, 1000, 0x0, ARRAY_SEQ)
|
|
#define ELEM(k) \
|
|
MAKE_JOBID(1, 2, 1000, TAG_JOB_ARRAY, ARRAY_SEQ | (uint64_t)(k))
|
|
#define ARRAY2_SEQ ((uint64_t)32768 * 6)
|
|
#define ARRAY2_BASE MAKE_JOBID(1, 2, 1000, 0x0, ARRAY2_SEQ)
|
|
#define ELEM2(k) \
|
|
MAKE_JOBID(1, 2, 1000, TAG_JOB_ARRAY, ARRAY2_SEQ | (uint64_t)(k))
|
|
|
|
static xce_sched_sim_driver_t g_sim;
|
|
static xce_sched_repl_state_t g_rs;
|
|
|
|
/* ---- fixture job table (the store's no-trace replies; unused here) ------ */
|
|
|
|
xce_sched_job_t* sched_get_job(xce_sched_core_t* core, uint64_t jobid)
|
|
{
|
|
(void)core;
|
|
(void)jobid;
|
|
return NULL;
|
|
}
|
|
|
|
const char* repl_job_state_name(uint32_t state)
|
|
{
|
|
(void)state;
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
void setUp(void)
|
|
{
|
|
memset(&g_sim, 0, sizeof(g_sim));
|
|
memset(&g_rs, 0, sizeof(g_rs));
|
|
g_rs.units_mode = XCE_REPL_UNITS_BOTH;
|
|
g_sim.repl_state = &g_rs;
|
|
}
|
|
|
|
void tearDown(void)
|
|
{
|
|
repl_trace_settings_unref(g_rs.trace.next);
|
|
repl_trace_settings_unref(g_rs.trace.cur);
|
|
g_rs.trace.next = NULL;
|
|
g_rs.trace.cur = NULL;
|
|
repl_trace_store_fini(&g_rs.trace);
|
|
g_sim.repl_state = NULL;
|
|
}
|
|
|
|
/* ---- helpers ------------------------------------------------------------- */
|
|
|
|
static char* fmt_id(uint64_t jobid)
|
|
{
|
|
static char bufs[8][64];
|
|
static int next = 0;
|
|
char* b = bufs[next++ % 8];
|
|
|
|
fmt_jobid(b, 64, jobid);
|
|
return b;
|
|
}
|
|
|
|
/* Runs one raw `trace ...` line with stdout redirected to a temp file and
|
|
* returns what it printed (caller frees). */
|
|
static char* run(const char* line)
|
|
{
|
|
char tmp[] = "/tmp/xce_trace_cmd_stdout_XXXXXX";
|
|
int fd = mkstemp(tmp);
|
|
int saved;
|
|
off_t len;
|
|
char* buf;
|
|
|
|
TEST_ASSERT_TRUE(fd >= 0);
|
|
fflush(stdout);
|
|
saved = dup(STDOUT_FILENO);
|
|
TEST_ASSERT_TRUE(saved >= 0);
|
|
TEST_ASSERT_TRUE(dup2(fd, STDOUT_FILENO) >= 0);
|
|
|
|
TEST_ASSERT_FALSE(repl_trace_cmd(&g_sim, line));
|
|
|
|
fflush(stdout);
|
|
dup2(saved, STDOUT_FILENO);
|
|
close(saved);
|
|
|
|
len = lseek(fd, 0, SEEK_CUR);
|
|
TEST_ASSERT_TRUE(len >= 0);
|
|
buf = (char*)calloc(1, (size_t)len + 1);
|
|
TEST_ASSERT_NOT_NULL(buf);
|
|
TEST_ASSERT_TRUE(lseek(fd, 0, SEEK_SET) == 0);
|
|
if (len > 0) {
|
|
TEST_ASSERT_TRUE(read(fd, buf, (size_t)len) == len);
|
|
}
|
|
close(fd);
|
|
unlink(tmp);
|
|
return buf;
|
|
}
|
|
|
|
static void assert_text(const char* want, const char* got)
|
|
{
|
|
if (strcmp(want, got) != 0) {
|
|
fprintf(stderr, "---- want ----\n%s---- got ----\n%s----\n", want,
|
|
got);
|
|
}
|
|
TEST_ASSERT_EQUAL_STRING(want, got);
|
|
}
|
|
|
|
static void assert_run(const char* line, const char* want)
|
|
{
|
|
char* got = run(line);
|
|
|
|
assert_text(want, got);
|
|
free(got);
|
|
}
|
|
|
|
/* Runs a line that must succeed (prints no error) and returns `next`. */
|
|
static xce_repl_trace_settings_t* run_ok(const char* line)
|
|
{
|
|
char* got = run(line);
|
|
|
|
if (strstr(got, "invalid") != NULL) {
|
|
fprintf(stderr, "unexpected error for '%s': %s", line, got);
|
|
}
|
|
TEST_ASSERT_NULL(strstr(got, "invalid"));
|
|
free(got);
|
|
return g_rs.trace.next;
|
|
}
|
|
|
|
static const xce_sched_trace_ctl_t* next_ctl(void)
|
|
{
|
|
TEST_ASSERT_NOT_NULL(g_rs.trace.next);
|
|
return repl_trace_settings_ctl(g_rs.trace.next);
|
|
}
|
|
|
|
static const char* next_path(void)
|
|
{
|
|
TEST_ASSERT_NOT_NULL(g_rs.trace.next);
|
|
return repl_trace_settings_path(g_rs.trace.next);
|
|
}
|
|
|
|
/* The simulated session hooks (task 4.6 owns the real ones). */
|
|
static void session_begin(void)
|
|
{
|
|
g_rs.trace.cur = repl_trace_settings_ref(g_rs.trace.next);
|
|
g_rs.trace.session_in_progress = true;
|
|
}
|
|
|
|
static void session_end(void)
|
|
{
|
|
repl_trace_settings_unref(g_rs.trace.cur);
|
|
g_rs.trace.cur = NULL;
|
|
g_rs.trace.session_in_progress = false;
|
|
}
|
|
|
|
static char* store_line(void)
|
|
{
|
|
static char b[160];
|
|
|
|
snprintf(b, sizeof(b),
|
|
" stored traces: %zu bytes used of %zu bytes (64 MiB)\n",
|
|
repl_trace_store_bytes(&g_rs.trace), XCE_REPL_TRACE_STORE_BOUND);
|
|
return b;
|
|
}
|
|
|
|
/* ---- grammar ------------------------------------------------------------- */
|
|
|
|
/* 1.1, 1.8, 9.1: `on all` with every default. */
|
|
static void test_on_all_defaults(void)
|
|
{
|
|
const xce_sched_trace_ctl_t* ctl;
|
|
|
|
assert_run("trace on all",
|
|
"[sim] trace: enabled; applies from the next session\n");
|
|
ctl = next_ctl();
|
|
TEST_ASSERT_TRUE(ctl->all);
|
|
TEST_ASSERT_EQUAL_INT(0, ctl->nsel);
|
|
TEST_ASSERT_EQUAL_INT(XCE_TRACE_LEVEL_SUMMARY, ctl->level);
|
|
TEST_ASSERT_EQUAL_INT(XCE_TRACE_LINE_BOUND_DEFAULT, ctl->line_bound);
|
|
TEST_ASSERT_NULL(next_path());
|
|
TEST_ASSERT_TRUE(sched_trace_ctl_wants(ctl, PLAIN_JOB));
|
|
TEST_ASSERT_TRUE(sched_trace_ctl_wants(ctl, ELEM(9)));
|
|
}
|
|
|
|
/* Level, line bound and file, in any order before `file`; extra
|
|
* whitespace and tabs. */
|
|
static void test_on_all_options(void)
|
|
{
|
|
const xce_sched_trace_ctl_t* ctl;
|
|
|
|
run_ok(" trace\ton all level full lines 50 file /tmp/t.log ");
|
|
ctl = next_ctl();
|
|
TEST_ASSERT_TRUE(ctl->all);
|
|
TEST_ASSERT_EQUAL_INT(XCE_TRACE_LEVEL_FULL, ctl->level);
|
|
TEST_ASSERT_EQUAL_INT(50, ctl->line_bound);
|
|
TEST_ASSERT_EQUAL_STRING("/tmp/t.log", next_path());
|
|
|
|
run_ok("trace on all lines 100000 level summary");
|
|
ctl = next_ctl();
|
|
TEST_ASSERT_EQUAL_INT(XCE_TRACE_LEVEL_SUMMARY, ctl->level);
|
|
TEST_ASSERT_EQUAL_INT(100000, ctl->line_bound);
|
|
TEST_ASSERT_NULL(next_path());
|
|
}
|
|
|
|
/* 1.2, 1.3: every selector form. A bare id is a plain job or an array
|
|
* that may not exist yet, so it selects both. */
|
|
static void test_on_job_selectors(void)
|
|
{
|
|
char line[512];
|
|
const xce_sched_trace_ctl_t* ctl;
|
|
|
|
snprintf(line, sizeof(line), "trace on job %s %s[3] %s[5-6] level full",
|
|
fmt_id(PLAIN_JOB), fmt_id(ARRAY_BASE), fmt_id(ARRAY2_BASE));
|
|
run_ok(line);
|
|
ctl = next_ctl();
|
|
TEST_ASSERT_FALSE(ctl->all);
|
|
TEST_ASSERT_EQUAL_INT(XCE_TRACE_LEVEL_FULL, ctl->level);
|
|
|
|
/* a bare id that is not array-aligned can only be a plain job
|
|
* (parse_jobsel rejects [k] on it), so it adds no whole-array selector */
|
|
TEST_ASSERT_TRUE(sched_trace_ctl_wants(ctl, PLAIN_JOB));
|
|
TEST_ASSERT_FALSE(sched_trace_ctl_wants(
|
|
ctl, MAKE_JOBID(1, 2, 1000, TAG_JOB_ARRAY, (uint64_t)42)));
|
|
TEST_ASSERT_FALSE(sched_trace_ctl_wants(ctl, PLAIN_JOB2));
|
|
/* one element */
|
|
TEST_ASSERT_TRUE(sched_trace_ctl_wants(ctl, ELEM(3)));
|
|
TEST_ASSERT_FALSE(sched_trace_ctl_wants(ctl, ELEM(2)));
|
|
TEST_ASSERT_FALSE(sched_trace_ctl_wants(ctl, ELEM(4)));
|
|
/* an element range */
|
|
TEST_ASSERT_FALSE(sched_trace_ctl_wants(ctl, ELEM2(4)));
|
|
TEST_ASSERT_TRUE(sched_trace_ctl_wants(ctl, ELEM2(5)));
|
|
TEST_ASSERT_TRUE(sched_trace_ctl_wants(ctl, ELEM2(6)));
|
|
TEST_ASSERT_FALSE(sched_trace_ctl_wants(ctl, ELEM2(7)));
|
|
|
|
/* an aligned bare id is a plain job or an array that may not exist
|
|
* yet: it selects the job and every element, including later ones */
|
|
snprintf(line, sizeof(line), "trace on job %s", fmt_id(ARRAY_BASE));
|
|
run_ok(line);
|
|
ctl = next_ctl();
|
|
TEST_ASSERT_TRUE(sched_trace_ctl_wants(ctl, ELEM(1)));
|
|
TEST_ASSERT_TRUE(sched_trace_ctl_wants(ctl, ELEM(32767)));
|
|
TEST_ASSERT_TRUE(sched_trace_ctl_wants(ctl, ARRAY_BASE));
|
|
TEST_ASSERT_FALSE(sched_trace_ctl_wants(ctl, ELEM2(1)));
|
|
}
|
|
|
|
/* 1.12: 20 job selectors on one line (more than the tokenizer's 16). */
|
|
static void test_twenty_selectors(void)
|
|
{
|
|
char line[2048];
|
|
size_t off;
|
|
int i;
|
|
const xce_sched_trace_ctl_t* ctl;
|
|
|
|
off = (size_t)snprintf(line, sizeof(line), "trace on job");
|
|
for (i = 0; i < 20; i++) {
|
|
off += (size_t)snprintf(
|
|
line + off, sizeof(line) - off, " %s",
|
|
fmt_id(MAKE_JOBID(1, 2, 1000, 0x0, (uint64_t)(100 + i))));
|
|
}
|
|
snprintf(line + off, sizeof(line) - off, " lines 75");
|
|
run_ok(line);
|
|
ctl = next_ctl();
|
|
TEST_ASSERT_EQUAL_INT(75, ctl->line_bound);
|
|
for (i = 0; i < 20; i++) {
|
|
TEST_ASSERT_TRUE(sched_trace_ctl_wants(
|
|
ctl, MAKE_JOBID(1, 2, 1000, 0x0, (uint64_t)(100 + i))));
|
|
}
|
|
TEST_ASSERT_FALSE(sched_trace_ctl_wants(
|
|
ctl, MAKE_JOBID(1, 2, 1000, 0x0, (uint64_t)120)));
|
|
}
|
|
|
|
/* 1.12: the file path is the rest of the line: quotes, spaces, a path
|
|
* longer than 63 characters and up to 4095 bytes. */
|
|
static void test_file_paths(void)
|
|
{
|
|
char line[8192];
|
|
char path[4097];
|
|
char* got;
|
|
|
|
run_ok("trace on all file \"/tmp/a dir/t.log\" ");
|
|
TEST_ASSERT_EQUAL_STRING("/tmp/a dir/t.log", next_path());
|
|
run_ok("trace on all file '/tmp/q.log'");
|
|
TEST_ASSERT_EQUAL_STRING("/tmp/q.log", next_path());
|
|
run_ok("trace on all level full file /tmp/x y.log");
|
|
TEST_ASSERT_EQUAL_STRING("/tmp/x y.log", next_path());
|
|
|
|
/* 200 characters */
|
|
memset(path, 'p', 200);
|
|
path[0] = '/';
|
|
path[200] = '\0';
|
|
snprintf(line, sizeof(line), "trace on all file %s", path);
|
|
run_ok(line);
|
|
TEST_ASSERT_EQUAL_STRING(path, next_path());
|
|
|
|
/* 4095 bytes accepted, quoted */
|
|
memset(path, 'p', 4095);
|
|
path[0] = '/';
|
|
path[4095] = '\0';
|
|
snprintf(line, sizeof(line), "trace on all file \"%s\"", path);
|
|
run_ok(line);
|
|
TEST_ASSERT_EQUAL_STRING(path, next_path());
|
|
|
|
/* 4096 bytes rejected, and the 4095-byte setting stays */
|
|
memset(path, 'p', 4096);
|
|
path[0] = '/';
|
|
path[4096] = '\0';
|
|
snprintf(line, sizeof(line), "trace on all lines 60 file %s", path);
|
|
got = run(line);
|
|
assert_text("[sim] trace: invalid file: path is 4096 bytes, longer than "
|
|
"4095\n",
|
|
got);
|
|
free(got);
|
|
TEST_ASSERT_EQUAL_INT(4095, (int)strlen(next_path()));
|
|
TEST_ASSERT_EQUAL_INT(XCE_TRACE_LINE_BOUND_DEFAULT, next_ctl()->line_bound);
|
|
}
|
|
|
|
/* 1.5: `off` clears next. */
|
|
static void test_off(void)
|
|
{
|
|
run_ok("trace on all");
|
|
assert_run("trace off",
|
|
"[sim] trace: disabled; applies from the next session\n");
|
|
TEST_ASSERT_NULL(g_rs.trace.next);
|
|
assert_run(" trace off ",
|
|
"[sim] trace: disabled; applies from the next session\n");
|
|
TEST_ASSERT_NULL(g_rs.trace.next);
|
|
}
|
|
|
|
/* ---- invalid arguments (1.9) --------------------------------------------- */
|
|
|
|
static void assert_rejected(const char* line, const char* want)
|
|
{
|
|
xce_repl_trace_settings_t* before = g_rs.trace.next;
|
|
|
|
assert_run(line, want);
|
|
TEST_ASSERT_TRUE(before == g_rs.trace.next);
|
|
}
|
|
|
|
static void test_invalid_arguments_leave_settings(void)
|
|
{
|
|
char line[256];
|
|
char want[512];
|
|
|
|
run_ok("trace on all level full lines 70 file /tmp/keep.log");
|
|
|
|
assert_rejected("trace", "[sim] trace: usage: trace on all|job <jobid> "
|
|
"... [level summary|full] [lines N] "
|
|
"[file PATH] | trace off | trace status\n");
|
|
assert_rejected("trace bogus", "[sim] trace: invalid command: bogus\n");
|
|
assert_rejected("trace on", "[sim] trace: invalid target: (missing)\n");
|
|
assert_rejected("trace on everyone",
|
|
"[sim] trace: invalid target: everyone\n");
|
|
assert_rejected("trace on job",
|
|
"[sim] trace: invalid job id: (missing)\n");
|
|
assert_rejected("trace on job level full",
|
|
"[sim] trace: invalid job id: (missing)\n");
|
|
snprintf(want, sizeof(want), "[sim] trace: invalid job id: 1-2-x (%s)\n",
|
|
jobsel_strerror(JOBSEL_ESYNTAX));
|
|
assert_rejected("trace on job 1-2-x", want);
|
|
snprintf(line, sizeof(line), "trace on job %s 7", fmt_id(PLAIN_JOB));
|
|
snprintf(want, sizeof(want), "[sim] trace: invalid job id: 7 (%s)\n",
|
|
jobsel_strerror(JOBSEL_ESYNTAX));
|
|
assert_rejected(line, want);
|
|
assert_rejected("trace on all level verbose",
|
|
"[sim] trace: invalid level: verbose\n");
|
|
assert_rejected("trace on all level",
|
|
"[sim] trace: invalid level: (missing)\n");
|
|
assert_rejected("trace on all lines 49",
|
|
"[sim] trace: invalid lines: 49\n");
|
|
assert_rejected("trace on all lines 100001",
|
|
"[sim] trace: invalid lines: 100001\n");
|
|
assert_rejected("trace on all lines -60",
|
|
"[sim] trace: invalid lines: -60\n");
|
|
assert_rejected("trace on all lines 1e3",
|
|
"[sim] trace: invalid lines: 1e3\n");
|
|
assert_rejected("trace on all lines 99999999999999999999999",
|
|
"[sim] trace: invalid lines: 99999999999999999999999\n");
|
|
assert_rejected("trace on all lines",
|
|
"[sim] trace: invalid lines: (missing)\n");
|
|
assert_rejected("trace on all colour red",
|
|
"[sim] trace: invalid option: colour\n");
|
|
snprintf(line, sizeof(line), "trace on all job %s", fmt_id(PLAIN_JOB));
|
|
assert_rejected(line, "[sim] trace: invalid option: job\n");
|
|
assert_rejected("trace on all file",
|
|
"[sim] trace: invalid file: (missing)\n");
|
|
assert_rejected("trace on all file \"\"",
|
|
"[sim] trace: invalid file: (missing)\n");
|
|
assert_rejected("trace off now", "[sim] trace: invalid argument: now\n");
|
|
assert_rejected("trace status x", "[sim] trace: invalid argument: x\n");
|
|
|
|
TEST_ASSERT_TRUE(next_ctl()->all);
|
|
TEST_ASSERT_EQUAL_INT(XCE_TRACE_LEVEL_FULL, next_ctl()->level);
|
|
TEST_ASSERT_EQUAL_INT(70, next_ctl()->line_bound);
|
|
TEST_ASSERT_EQUAL_STRING("/tmp/keep.log", next_path());
|
|
}
|
|
|
|
/* ---- settings lifecycle and status (1.4, 1.6, 1.7, 1.10) ---------------- */
|
|
|
|
static void test_status_before_any_command(void)
|
|
{
|
|
char want[512];
|
|
|
|
snprintf(want, sizeof(want),
|
|
"[sim] trace status (next session):\n"
|
|
" enabled: no\n"
|
|
"%s",
|
|
store_line());
|
|
assert_run("trace status", want);
|
|
}
|
|
|
|
static void test_status_next_settings(void)
|
|
{
|
|
char want[1024];
|
|
char line[256];
|
|
|
|
snprintf(line, sizeof(line),
|
|
"trace on job %s %s[1-3] %s level full lines 300 file /tmp/s.log",
|
|
fmt_id(PLAIN_JOB), fmt_id(ARRAY_BASE), fmt_id(PLAIN_JOB2));
|
|
run_ok(line);
|
|
snprintf(want, sizeof(want),
|
|
"[sim] trace status (next session):\n"
|
|
" enabled: yes\n"
|
|
" selection: jobs %s,%s[1-3],%s\n"
|
|
" level: full\n"
|
|
" line bound: 300\n"
|
|
" file: /tmp/s.log\n"
|
|
"%s",
|
|
fmt_id(PLAIN_JOB), fmt_id(ARRAY_BASE), fmt_id(PLAIN_JOB2),
|
|
store_line());
|
|
assert_run(" trace status ", want);
|
|
}
|
|
|
|
/* A bare array id selects every element, including later ones; status
|
|
* shows it as <base>[*] rather than as a plain job id. */
|
|
static void test_status_whole_array_selection(void)
|
|
{
|
|
char want[1024];
|
|
char line[256];
|
|
|
|
snprintf(line, sizeof(line), "trace on job %s %s", fmt_id(PLAIN_JOB),
|
|
fmt_id(ARRAY_BASE));
|
|
run_ok(line);
|
|
snprintf(want, sizeof(want),
|
|
"[sim] trace status (next session):\n"
|
|
" enabled: yes\n"
|
|
" selection: jobs %s,%s[*]\n"
|
|
" level: summary\n"
|
|
" line bound: 1000\n"
|
|
" file: none\n"
|
|
"%s",
|
|
fmt_id(PLAIN_JOB), fmt_id(ARRAY_BASE), store_line());
|
|
assert_run("trace status", want);
|
|
}
|
|
|
|
/* A command during a session replaces only next; cur keeps the settings
|
|
* the session started with until the session ends. */
|
|
static void test_status_before_during_after_session(void)
|
|
{
|
|
char want[1024];
|
|
char line[256];
|
|
xce_repl_trace_settings_t* started;
|
|
xce_sched_trace_rec_t* rec;
|
|
xce_sched_trace_ctl_t rctl;
|
|
|
|
/* a stored trace so the memory line is non-zero */
|
|
memset(&rctl, 0, sizeof(rctl));
|
|
rctl.all = true;
|
|
rctl.line_bound = 1000;
|
|
rec = sched_trace_rec_new(&rctl, PLAIN_JOB, XCE_TRACE_REC_NOT_FOUND);
|
|
TEST_ASSERT_NOT_NULL(rec);
|
|
repl_trace_store_put(&g_rs.trace, rec);
|
|
TEST_ASSERT_TRUE(repl_trace_store_bytes(&g_rs.trace) > 0);
|
|
|
|
/* before */
|
|
run_ok("trace on all");
|
|
snprintf(want, sizeof(want),
|
|
"[sim] trace status (next session):\n"
|
|
" enabled: yes\n"
|
|
" selection: all jobs\n"
|
|
" level: summary\n"
|
|
" line bound: 1000\n"
|
|
" file: none\n"
|
|
"%s",
|
|
store_line());
|
|
assert_run("trace status", want);
|
|
|
|
/* during, unchanged: one block */
|
|
session_begin();
|
|
started = g_rs.trace.cur;
|
|
snprintf(want, sizeof(want),
|
|
"[sim] trace status (in effect for the session in progress):\n"
|
|
" enabled: yes\n"
|
|
" selection: all jobs\n"
|
|
" level: summary\n"
|
|
" line bound: 1000\n"
|
|
" file: none\n"
|
|
"%s",
|
|
store_line());
|
|
assert_run("trace status", want);
|
|
|
|
/* during, replaced: cur is untouched, the replacement is shown */
|
|
snprintf(line, sizeof(line), "trace on job %s level full file /tmp/n.log",
|
|
fmt_id(PLAIN_JOB));
|
|
run_ok(line);
|
|
TEST_ASSERT_TRUE(started == g_rs.trace.cur);
|
|
TEST_ASSERT_TRUE(g_rs.trace.cur != g_rs.trace.next);
|
|
TEST_ASSERT_TRUE(repl_trace_settings_ctl(g_rs.trace.cur)->all);
|
|
snprintf(want, sizeof(want),
|
|
"[sim] trace status (in effect for the session in progress):\n"
|
|
" enabled: yes\n"
|
|
" selection: all jobs\n"
|
|
" level: summary\n"
|
|
" line bound: 1000\n"
|
|
" file: none\n"
|
|
"[sim] trace status (replaces at the next session):\n"
|
|
" enabled: yes\n"
|
|
" selection: jobs %s\n"
|
|
" level: full\n"
|
|
" line bound: 1000\n"
|
|
" file: /tmp/n.log\n"
|
|
"%s",
|
|
fmt_id(PLAIN_JOB), store_line());
|
|
assert_run("trace status", want);
|
|
|
|
/* during, disabled */
|
|
run_ok("trace off");
|
|
TEST_ASSERT_TRUE(started == g_rs.trace.cur);
|
|
snprintf(want, sizeof(want),
|
|
"[sim] trace status (in effect for the session in progress):\n"
|
|
" enabled: yes\n"
|
|
" selection: all jobs\n"
|
|
" level: summary\n"
|
|
" line bound: 1000\n"
|
|
" file: none\n"
|
|
"[sim] trace status (replaces at the next session):\n"
|
|
" enabled: no\n"
|
|
"%s",
|
|
store_line());
|
|
assert_run("trace status", want);
|
|
|
|
/* after: the session's settings are released, next is shown alone */
|
|
run_ok("trace on all lines 500");
|
|
session_end();
|
|
snprintf(want, sizeof(want),
|
|
"[sim] trace status (next session):\n"
|
|
" enabled: yes\n"
|
|
" selection: all jobs\n"
|
|
" level: summary\n"
|
|
" line bound: 500\n"
|
|
" file: none\n"
|
|
"%s",
|
|
store_line());
|
|
assert_run("trace status", want);
|
|
}
|
|
|
|
/* A session that started with tracing off, then enabled mid-session. */
|
|
static void test_status_session_started_off(void)
|
|
{
|
|
char want[512];
|
|
|
|
session_begin();
|
|
TEST_ASSERT_NULL(g_rs.trace.cur);
|
|
run_ok("trace on all level full");
|
|
snprintf(want, sizeof(want),
|
|
"[sim] trace status (in effect for the session in progress):\n"
|
|
" enabled: no\n"
|
|
"[sim] trace status (replaces at the next session):\n"
|
|
" enabled: yes\n"
|
|
" selection: all jobs\n"
|
|
" level: full\n"
|
|
" line bound: 1000\n"
|
|
" file: none\n"
|
|
"%s",
|
|
store_line());
|
|
assert_run("trace status", want);
|
|
session_end();
|
|
}
|
|
|
|
/* Reference counting: NULL-safe, and a settings object outlives a
|
|
* replacement while another reference holds it. */
|
|
static void test_settings_refcount(void)
|
|
{
|
|
xce_repl_trace_settings_t* s;
|
|
|
|
TEST_ASSERT_NULL(repl_trace_settings_ref(NULL));
|
|
repl_trace_settings_unref(NULL);
|
|
|
|
s = run_ok("trace on all lines 123 file /tmp/r.log");
|
|
TEST_ASSERT_TRUE(repl_trace_settings_ref(s) == s);
|
|
run_ok("trace off");
|
|
/* still alive through the extra reference (ASAN would flag a free) */
|
|
TEST_ASSERT_EQUAL_INT(123, repl_trace_settings_ctl(s)->line_bound);
|
|
TEST_ASSERT_EQUAL_STRING("/tmp/r.log", repl_trace_settings_path(s));
|
|
repl_trace_settings_unref(s);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_on_all_defaults);
|
|
RUN_TEST(test_on_all_options);
|
|
RUN_TEST(test_on_job_selectors);
|
|
RUN_TEST(test_twenty_selectors);
|
|
RUN_TEST(test_file_paths);
|
|
RUN_TEST(test_off);
|
|
RUN_TEST(test_invalid_arguments_leave_settings);
|
|
RUN_TEST(test_status_before_any_command);
|
|
RUN_TEST(test_status_next_settings);
|
|
RUN_TEST(test_status_whole_array_selection);
|
|
RUN_TEST(test_status_before_during_after_session);
|
|
RUN_TEST(test_status_session_started_off);
|
|
RUN_TEST(test_settings_refcount);
|
|
return UNITY_END();
|
|
}
|