myxce/src/scheduler/testsuite/test_repl_trace_cmd.c
Rongsong Shen aa4c43c897
feat(scheduler-sim-decision-trace): parse the trace command and manage trace settings
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>
2026-10-08 14:05:36 +00:00

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();
}