Files
roro9stack/test/test_shell_log/test_shell_log.cpp
T
twislaandClaude Opus 5.5 7b5df713ad
CI / build (pull_request) Successful in 1m38s
Site / build (pull_request) Successful in 10s
Shell: only its own replies, Apps by their names, and rm as Unix has it (#67)
The Shell shows the replies to its own commands and nothing else. The
console knows who each line is printed for (Console::As, Console::origin):
a command run from the Shell prints as the Shell's, and what answers it
later from another task carries that along (ls, tasks, du, cp, update
check, sd list, screenshot, gemini get). Ctrl+b shows everything instead.
This replaces the ten-second window, which was a guess.

An App's name with a capital opens it (Notes, Irc, Wifi, Gnss, Gemini, Lora,
Storage, Shell, System, Settings), from the Shell and from the consoles.

rm needs -r for a folder, here and over the consoles. In the Shell a file,
or a folder with something in it, is asked about unless -f; an empty folder
with -r goes without a word.

The Shell now hands its line to the main loop to run: run from inside the
key handler, rm on a folder overflowed the loop's stack and crashed the
device. `info` says which App is in front.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-07 11:35:29 +02:00

89 lines
3.8 KiB
C++

#include <unity.h>
#include <string>
#include <vector>
#include "shell_log.h"
using namespace roro;
void setUp() {}
void tearDown() {}
static void feed(ShellLog& log, const std::string& text) { log.feed(text.data(), text.size()); }
void test_lines_arrive_in_pieces() {
ShellLog log;
feed(log, "firmware: roro");
TEST_ASSERT_EQUAL(0, log.lines().size());
feed(log, "9stack\r\nuptime: 1s\n");
TEST_ASSERT_EQUAL(2, log.lines().size());
TEST_ASSERT_EQUAL_STRING("firmware: roro9stack", log.lines()[0].c_str());
TEST_ASSERT_EQUAL_STRING("uptime: 1s", log.lines()[1].c_str());
}
void test_the_heap_line_is_never_kept() {
ShellLog log;
feed(log, "status: heap 105000 min 90000\nirc: connected\n");
TEST_ASSERT_EQUAL(1, log.lines().size());
TEST_ASSERT_EQUAL_STRING("irc: connected", log.lines()[0].c_str());
}
void test_the_oldest_lines_go_past_4_kb() {
ShellLog log;
for (int i = 0; i < 200; i++) feed(log, std::string(49, 'x') + "\n"); // 10 KB in all
size_t bytes = 0;
for (auto& l : log.lines()) bytes += l.size() + 1;
TEST_ASSERT_TRUE(bytes <= ShellLog::kMaxBytes);
TEST_ASSERT_TRUE(log.lines().size() >= 80);
uint32_t before = log.revision();
log.add("> info");
TEST_ASSERT_EQUAL_STRING("> info", log.lines().back().c_str());
TEST_ASSERT_TRUE(log.revision() != before);
log.clear();
TEST_ASSERT_EQUAL(0, log.lines().size());
}
static const char* kHelp =
"info firmware, uptime, memory\n"
"ls [folder] | du <path> | mkdir <path> | rm <path> the SD card\n"
"lora probe | lora status | lora rx on|off the LoRa radio\n"
"log level <0-5> ESP-IDF log level\n"
"sd card | sd list | cat <path> | log <text> | burst\n"
"get <path> | put <path> <size> <sha256> | screenshot (Debug Console only) binary\n"
"Irc | Notes | Storage open that App\n";
void test_command_words_come_from_the_help_text() {
auto words = commandWords(kHelp);
const char* expected[] = {"info", "ls", "du", "mkdir", "rm", "lora", "log", "sd", "cat", "burst", "get", "put", "screenshot", "Irc", "Notes", "Storage"};
TEST_ASSERT_EQUAL(sizeof expected / sizeof expected[0], words.size());
for (size_t i = 0; i < words.size(); i++) TEST_ASSERT_EQUAL_STRING(expected[i], words[i].c_str());
}
void test_tab_completes_the_first_word() {
auto words = commandWords(kHelp);
std::vector<std::string> matches;
TEST_ASSERT_EQUAL_STRING("info ", completeCommand("in", words, matches).c_str()); // the only one: with its space
TEST_ASSERT_EQUAL(0, matches.size());
TEST_ASSERT_EQUAL_STRING("l", completeCommand("l", words, matches).c_str()); // ls, lora, log: they agree on no more
TEST_ASSERT_EQUAL(3, matches.size());
TEST_ASSERT_EQUAL_STRING("mkdir ", completeCommand("m", words, matches).c_str());
TEST_ASSERT_EQUAL_STRING("lo", completeCommand("lo", words, matches).c_str()); // lora, log
TEST_ASSERT_EQUAL(2, matches.size());
TEST_ASSERT_EQUAL_STRING("zz", completeCommand("zz", words, matches).c_str()); // nothing: as it was
TEST_ASSERT_EQUAL_STRING("ls /no", completeCommand("ls /no", words, matches).c_str()); // past the first word
TEST_ASSERT_EQUAL_STRING("", completeCommand("", words, matches).c_str());
TEST_ASSERT_EQUAL_STRING("Notes ", completeCommand("N", words, matches).c_str()); // an App: a capital
TEST_ASSERT_EQUAL_STRING("n", completeCommand("n", words, matches).c_str()); // and no command starts with n here
}
int main(int, char**) {
UNITY_BEGIN();
RUN_TEST(test_lines_arrive_in_pieces);
RUN_TEST(test_the_heap_line_is_never_kept);
RUN_TEST(test_the_oldest_lines_go_past_4_kb);
RUN_TEST(test_command_words_come_from_the_help_text);
RUN_TEST(test_tab_completes_the_first_word);
return UNITY_END();
}