Consolidate companion components
This commit is contained in:
1 parent
aab8f5038a
commit
ebee99af81
32 files changed
+286
-287
No files matched your search
File renamed without changes.
Vendored
+14
-14
@@ -1,6 +1,6 @@
|
||||
#include <Arduino.h>
|
||||
#include <helpers/CompanionJohn.h>
|
||||
#include <helpers/bible/JohnData.generated.h>
|
||||
#include <helpers/CompanionReader.h>
|
||||
#include <helpers/bible/ReaderData.generated.h>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
@@ -10,23 +10,23 @@ using namespace mesh::bible;
|
||||
|
||||
int main(int argc, char**) {
|
||||
Reference ref = {};
|
||||
for (const char* cmd : {"get John 3:16", " GET\tJoHn 3:16 \t", "get john 03:16"}) {
|
||||
for (const char* cmd : {"get reader 3:16", " GET\treader 3:16 \t", "get reader 03:16"}) {
|
||||
assert(parse(cmd, ref) == ParseResult::Valid && ref.chapter == 3 && ref.verse == 16);
|
||||
}
|
||||
for (const char* cmd : {"get John", "get John 0:1", "get John 22:1",
|
||||
"get John 1:52", "get John 3:0", "get John -3:16", "get John 3:16-18",
|
||||
"get John 3:16 junk", "get John 9999999999:1", "get John 1:9999999999",
|
||||
"get John 3:", "get John 3:16\x1b", "get John 3 :16", "get John :16"}) {
|
||||
for (const char* cmd : {"get reader", "get reader 0:1", "get reader 22:1",
|
||||
"get reader 1:52", "get reader 3:0", "get reader -3:16", "get reader 3:16-18",
|
||||
"get reader 3:16 junk", "get reader 9999999999:1", "get reader 1:9999999999",
|
||||
"get reader 3:", "get reader 3:16\x1b", "get reader 3 :16", "get reader :16"}) {
|
||||
assert(parse(cmd, ref) == ParseResult::Invalid);
|
||||
}
|
||||
for (const char* cmd : {"get name", "forget John 3:16", "get johnny 3:16",
|
||||
"get 1 John 3:16", "getJohn 3:16", "", "get J", "get", "g"}) {
|
||||
for (const char* cmd : {"get name", "forget reader 3:16", "get readerny 3:16",
|
||||
"get 1 reader 3:16", "getreader 3:16", "", "get J", "get", "g"}) {
|
||||
assert(parse(cmd, ref) == ParseResult::NoMatch);
|
||||
Stream output;
|
||||
assert(!mesh::handleJohnCommand(cmd, output) && output.text.empty());
|
||||
assert(!mesh::handleReaderCommand(cmd, output) && output.text.empty());
|
||||
}
|
||||
assert(parse(nullptr, ref) == ParseResult::NoMatch);
|
||||
const Corpus& corpus = generated::johnCorpus;
|
||||
const Corpus& corpus = generated::readerCorpus;
|
||||
struct Guarded { uint32_t pre; char scratch[kBlockSize]; uint32_t post; } buf = {};
|
||||
buf.pre = 0x11223344; buf.post = 0xaabbccdd;
|
||||
unsigned count = 0;
|
||||
@@ -36,9 +36,9 @@ int main(int argc, char**) {
|
||||
assert(lookup(corpus, {chapter, verse}, buf.scratch, kBlockSize, text) == LookupResult::Found);
|
||||
assert(buf.pre == 0x11223344 && buf.post == 0xaabbccdd);
|
||||
char cmd[32];
|
||||
snprintf(cmd, sizeof(cmd), "get John %u:%u", chapter, verse);
|
||||
snprintf(cmd, sizeof(cmd), "get reader %u:%u", chapter, verse);
|
||||
Stream output;
|
||||
assert(mesh::handleJohnCommand(cmd, output));
|
||||
assert(mesh::handleReaderCommand(cmd, output));
|
||||
const std::string expected = "\r\n" + std::string(text) + "\r\n";
|
||||
assert(output.text.find(expected) != std::string::npos);
|
||||
for (const unsigned char* c = reinterpret_cast<const unsigned char*>(text); *c; ++c)
|
||||
@@ -88,7 +88,7 @@ int main(int argc, char**) {
|
||||
assert(text == nullptr && buf.pre == 0x11223344 && buf.post == 0xaabbccdd);
|
||||
}
|
||||
Stream invalid;
|
||||
assert(mesh::handleJohnCommand("get John 3:99", invalid));
|
||||
assert(mesh::handleReaderCommand("get reader 3:99", invalid));
|
||||
assert(invalid.text.find("ERROR") != std::string::npos);
|
||||
if (argc == 1) std::puts("All 879 verses, terminal output, parser and corruption checks passed");
|
||||
}
|
||||
test/fixtures/companion_john/test_reader.cpp → test/fixtures/companion_reader/test_reader_screen.cpp
Vendored
+50
-50
@@ -1,5 +1,5 @@
|
||||
#include <helpers/CompanionJohn.h>
|
||||
#include <helpers/bible/JohnBookmarkFiles.h>
|
||||
#include <helpers/CompanionReader.h>
|
||||
#include <helpers/bible/ReaderBookmarkFiles.h>
|
||||
#include <helpers/ui/UIScreen.h>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
@@ -41,18 +41,18 @@ struct Files {
|
||||
struct FakeMesh {
|
||||
Files files;
|
||||
int saves = 0;
|
||||
bool loadJohnBookmark(Position& p) { return loadReaderBookmark(files, p); }
|
||||
bool saveJohnBookmark(Position p) { ++saves; return saveReaderBookmark(files, p); }
|
||||
bool loadReaderBookmark(Position& p) { return mesh::bible::loadReaderBookmark(files, p); }
|
||||
bool saveReaderBookmark(Position p) { ++saves; return mesh::bible::saveReaderBookmark(files, p); }
|
||||
} the_mesh;
|
||||
|
||||
class UITask {
|
||||
public:
|
||||
bool closed = false;
|
||||
std::string alert;
|
||||
void closeJohnReader() { closed = true; }
|
||||
void closeReader() { closed = true; }
|
||||
void showAlert(const char* text, int) { alert = text; }
|
||||
};
|
||||
#include "../../../examples/companion_radio/ui-new/JohnReaderScreen.h"
|
||||
#include "../../../examples/companion_radio/ui-new/ReaderScreen.h"
|
||||
|
||||
class Display : public DisplayDriver {
|
||||
int x = 0, y = 0;
|
||||
@@ -141,25 +141,25 @@ int main(int argc, char** argv) {
|
||||
if (argc > 2) assert(std::freopen(argv[2], "w", stdout));
|
||||
Display display(128, 64);
|
||||
UITask task;
|
||||
JohnReaderScreen first(&task, &display);
|
||||
ReaderScreen first(&task, &display);
|
||||
first.open(); first.render(display);
|
||||
std::puts("FRAME 128 64 John 1:1");
|
||||
std::puts("FRAME 128 64 reader 1:1");
|
||||
display.dump();
|
||||
assert(the_mesh.saveJohnBookmark(pos(91))); // John 3:16
|
||||
JohnReaderScreen verse(&task, &display);
|
||||
assert(the_mesh.saveReaderBookmark(pos(91))); // reader 3:16
|
||||
ReaderScreen verse(&task, &display);
|
||||
verse.open();
|
||||
for (int part = 0; part < (UI_SMALL_MESSAGE_FONT ? 1 : 2); ++part) {
|
||||
display.clear(); verse.render(display);
|
||||
std::printf("FRAME 128 64 John 3:16 part %d\n", part + 1);
|
||||
std::printf("FRAME 128 64 reader 3:16 part %d\n", part + 1);
|
||||
display.dump();
|
||||
verse.handleInput(KEY_NEXT);
|
||||
}
|
||||
#if UI_SMALL_MESSAGE_FONT
|
||||
Display tiny(72, 40);
|
||||
assert(the_mesh.saveJohnBookmark(pos(91)));
|
||||
JohnReaderScreen tiny_verse(&task, &tiny);
|
||||
assert(the_mesh.saveReaderBookmark(pos(91)));
|
||||
ReaderScreen tiny_verse(&task, &tiny);
|
||||
tiny_verse.open(); tiny_verse.render(tiny);
|
||||
std::puts("FRAME 72 40 John 3:16 on a tiny screen (5px)");
|
||||
std::puts("FRAME 72 40 reader 3:16 on a tiny screen (5px)");
|
||||
tiny.dump();
|
||||
tiny.resize(40, 72);
|
||||
tiny_verse.render(tiny);
|
||||
@@ -199,9 +199,9 @@ int main(int argc, char** argv) {
|
||||
auto measure = [&expected_text](const char* line) { return expected_text.getTextWidth(line); };
|
||||
UITask task;
|
||||
the_mesh = FakeMesh{};
|
||||
JohnReaderScreen screen(&task, &display);
|
||||
ReaderScreen screen(&task, &display);
|
||||
screen.open();
|
||||
assert(screen.flush() && the_mesh.saves == 0); // don't create John 1:1
|
||||
assert(screen.flush() && the_mesh.saves == 0); // don't create reader 1:1
|
||||
#if UI_BUTTON_READER_HINT
|
||||
display.clear(); screen.render(display);
|
||||
Display footer(display.width(), display.height());
|
||||
@@ -237,7 +237,7 @@ int main(int argc, char** argv) {
|
||||
char scratch[kBlockSize];
|
||||
for (uint16_t verse = 0; verse < kVerseCount; ++verse) {
|
||||
const char* text;
|
||||
assert(mesh::readJohnVerse(referenceAt(verse), scratch, sizeof(scratch), text) == LookupResult::Found);
|
||||
assert(mesh::readReaderVerse(referenceAt(verse), scratch, sizeof(scratch), text) == LookupResult::Found);
|
||||
memmove(scratch, text, strlen(text) + 1);
|
||||
std::string rebuilt;
|
||||
uint16_t offset = 0;
|
||||
@@ -304,27 +304,27 @@ int main(int argc, char** argv) {
|
||||
touch_gesture(x, screen.readerTouchBar()->top, x, screen.readerTouchBar()->top);
|
||||
};
|
||||
tap(2); assert(screen.flush());
|
||||
Position saved; assert(the_mesh.loadJohnBookmark(saved) && saved.verse == 1);
|
||||
Position saved; assert(the_mesh.loadReaderBookmark(saved) && saved.verse == 1);
|
||||
tap(1); assert(screen.flush());
|
||||
assert(!the_mesh.loadJohnBookmark(saved) && saved.atStart());
|
||||
assert(!the_mesh.loadReaderBookmark(saved) && saved.atStart());
|
||||
tap(3); assert(screen.flush());
|
||||
assert(the_mesh.loadJohnBookmark(saved) && referenceAt(saved.verse).chapter == 2);
|
||||
assert(the_mesh.loadReaderBookmark(saved) && referenceAt(saved.verse).chapter == 2);
|
||||
tap(0); assert(screen.flush());
|
||||
assert(!the_mesh.loadJohnBookmark(saved) && saved.atStart());
|
||||
assert(!the_mesh.loadReaderBookmark(saved) && saved.atStart());
|
||||
tap(4); assert(task.closed);
|
||||
task.closed = false;
|
||||
touch_gesture(120,70,120,70); assert(screen.flush());
|
||||
assert(the_mesh.loadJohnBookmark(saved) && saved.verse == 1);
|
||||
assert(the_mesh.loadReaderBookmark(saved) && saved.verse == 1);
|
||||
touch_gesture(40,70,40,70); assert(screen.flush());
|
||||
assert(!the_mesh.loadJohnBookmark(saved) && saved.atStart());
|
||||
assert(!the_mesh.loadReaderBookmark(saved) && saved.atStart());
|
||||
touch_gesture(120,70,40,70); assert(screen.flush());
|
||||
assert(the_mesh.loadJohnBookmark(saved) && saved.verse == 1);
|
||||
assert(the_mesh.loadReaderBookmark(saved) && saved.verse == 1);
|
||||
touch_gesture(40,70,120,70); assert(screen.flush());
|
||||
assert(!the_mesh.loadJohnBookmark(saved) && saved.atStart());
|
||||
assert(!the_mesh.loadReaderBookmark(saved) && saved.atStart());
|
||||
touch_gesture(80,100,80,50); assert(screen.flush());
|
||||
assert(the_mesh.loadJohnBookmark(saved) && referenceAt(saved.verse).chapter == 2);
|
||||
assert(the_mesh.loadReaderBookmark(saved) && referenceAt(saved.verse).chapter == 2);
|
||||
touch_gesture(80,50,80,100); assert(screen.flush());
|
||||
assert(!the_mesh.loadJohnBookmark(saved) && saved.atStart());
|
||||
assert(!the_mesh.loadReaderBookmark(saved) && saved.atStart());
|
||||
touch_gesture(80,0,80,0); assert(task.closed);
|
||||
#endif
|
||||
}
|
||||
@@ -337,7 +337,7 @@ int main(int argc, char** argv) {
|
||||
the_mesh = FakeMesh{};
|
||||
Display chapters_display(128, 64);
|
||||
UITask chapters_task;
|
||||
JohnReaderScreen chapters(&chapters_task, &chapters_display);
|
||||
ReaderScreen chapters(&chapters_task, &chapters_display);
|
||||
chapters.open();
|
||||
assert(chapters.handleInput(KEY_UP));
|
||||
assert(chapters.flush() && the_mesh.saves == 0);
|
||||
@@ -345,7 +345,7 @@ int main(int argc, char** argv) {
|
||||
assert(chapters.handleInput(KEY_DOWN));
|
||||
assert(chapters.flush());
|
||||
Position saved;
|
||||
assert(the_mesh.loadJohnBookmark(saved));
|
||||
assert(the_mesh.loadReaderBookmark(saved));
|
||||
assert(referenceAt(saved.verse).chapter == chapter && saved.offset == 0);
|
||||
assert(referenceAt(saved.verse).verse == 1);
|
||||
}
|
||||
@@ -353,66 +353,66 @@ int main(int argc, char** argv) {
|
||||
assert(chapters.handleInput(KEY_DOWN));
|
||||
assert(chapters.flush() && the_mesh.saves == writes);
|
||||
// Moving backwards from the middle of a chapter reaches the preceding one.
|
||||
assert(the_mesh.saveJohnBookmark(pos(91, 12))); // 3:16, with a stale page offset
|
||||
JohnReaderScreen middle(&chapters_task, &chapters_display);
|
||||
assert(the_mesh.saveReaderBookmark(pos(91, 12))); // 3:16, with a stale page offset
|
||||
ReaderScreen middle(&chapters_task, &chapters_display);
|
||||
middle.open();
|
||||
assert(middle.handleInput(KEY_UP) && middle.flush());
|
||||
Position chapter_saved;
|
||||
assert(the_mesh.loadJohnBookmark(chapter_saved));
|
||||
assert(the_mesh.loadReaderBookmark(chapter_saved));
|
||||
assert(referenceAt(chapter_saved.verse).chapter == 2);
|
||||
assert(referenceAt(chapter_saved.verse).verse == 1 && chapter_saved.offset == 0);
|
||||
assert(middle.handleInput(KEY_UP) && middle.flush());
|
||||
assert(!the_mesh.loadJohnBookmark(chapter_saved) && chapter_saved.atStart());
|
||||
assert(!the_mesh.loadReaderBookmark(chapter_saved) && chapter_saved.atStart());
|
||||
|
||||
// Resume within a split verse; independent screen and store lifecycle.
|
||||
the_mesh = FakeMesh{};
|
||||
Display display(64,32);
|
||||
UITask task;
|
||||
JohnReaderScreen first(&task, &display);
|
||||
ReaderScreen first(&task, &display);
|
||||
first.open(); first.handleInput(KEY_NEXT);
|
||||
display.clear(); first.render(display); const auto body = display.body();
|
||||
first.poll(); assert(the_mesh.saves == 0);
|
||||
test_now += 2000; first.poll(); assert(the_mesh.saves == 1);
|
||||
first.poll(); assert(the_mesh.saves == 1);
|
||||
Position saved;
|
||||
assert(the_mesh.loadJohnBookmark(saved) && saved.verse == 0 && saved.offset > 0);
|
||||
JohnReaderScreen rebooted(&task, &display);
|
||||
assert(the_mesh.loadReaderBookmark(saved) && saved.verse == 0 && saved.offset > 0);
|
||||
ReaderScreen rebooted(&task, &display);
|
||||
rebooted.open(); display.clear(); rebooted.render(display);
|
||||
assert(display.body() == body);
|
||||
rebooted.handleInput(KEY_PREV); assert(rebooted.flush());
|
||||
assert(!the_mesh.files.exists("/john.pos") && !the_mesh.files.exists("/john.pos.bak"));
|
||||
assert(!the_mesh.loadJohnBookmark(saved) && saved.atStart());
|
||||
assert(!the_mesh.files.exists("/reader.pos") && !the_mesh.files.exists("/reader.pos.bak"));
|
||||
assert(!the_mesh.loadReaderBookmark(saved) && saved.atStart());
|
||||
|
||||
#if UI_SMALL_MESSAGE_FONT
|
||||
// A bookmark from the old 6-pixel-wide font resumes on the new page that
|
||||
// contains those words; it does not advance to another verse or lose them.
|
||||
char scratch[kBlockSize];
|
||||
const char* text;
|
||||
assert(mesh::readJohnVerse({3, 16}, scratch, sizeof(scratch), text) == LookupResult::Found);
|
||||
assert(mesh::readReaderVerse({3, 16}, scratch, sizeof(scratch), text) == LookupResult::Found);
|
||||
const auto legacy_measure = [](const char* line) { return strlen(line) * 6; };
|
||||
const auto legacy = readerPage(text, 0, 128, 5, legacy_measure);
|
||||
assert(text[legacy.next]);
|
||||
the_mesh = FakeMesh{};
|
||||
assert(the_mesh.saveJohnBookmark(pos(91, legacy.next)));
|
||||
assert(the_mesh.saveReaderBookmark(pos(91, legacy.next)));
|
||||
Display migrated_display(128,64);
|
||||
mesh::ui::SmallMessageText compact(migrated_display);
|
||||
const auto compact_measure = [&compact](const char* line) { return compact.getTextWidth(line); };
|
||||
const auto compact_page = readerPage(text, legacy.next, 128,
|
||||
compact.lineCount(12, migrated_display.bodyBottom()), compact_measure);
|
||||
assert(compact_page.start <= legacy.next && compact_page.next > legacy.next);
|
||||
JohnReaderScreen migrated(&task, &migrated_display);
|
||||
ReaderScreen migrated(&task, &migrated_display);
|
||||
migrated.open(); migrated.render(migrated_display); assert(migrated.flush());
|
||||
assert(the_mesh.loadJohnBookmark(saved) && saved == pos(91, compact_page.start));
|
||||
assert(compact_page.parts == 1); // John 3:16 now fits in one V4 screen
|
||||
assert(the_mesh.loadReaderBookmark(saved) && saved == pos(91, compact_page.start));
|
||||
assert(compact_page.parts == 1); // reader 3:16 now fits in one V4 screen
|
||||
|
||||
// Reflow an already-open reader when panel geometry crosses the 5px/6px
|
||||
// threshold, and when a tiny panel rotates into the stacked-header layout.
|
||||
for (const auto& dimensions : {std::make_pair(72,40), {128,64}, {40,72}, {64,128}, {32,128}, {160,80}, {80,160}, {159,80}}) {
|
||||
assert(the_mesh.saveJohnBookmark(pos(91, legacy.next)));
|
||||
assert(the_mesh.saveReaderBookmark(pos(91, legacy.next)));
|
||||
Display changed(72,40);
|
||||
JohnReaderScreen active(&task, &changed);
|
||||
ReaderScreen active(&task, &changed);
|
||||
active.open(); active.render(changed); assert(active.flush());
|
||||
Position before; assert(the_mesh.loadJohnBookmark(before));
|
||||
Position before; assert(the_mesh.loadReaderBookmark(before));
|
||||
changed.resize(dimensions.first, dimensions.second);
|
||||
mesh::ui::SmallMessageText small_font(changed);
|
||||
const bool small = changed.useSmallMessageFont();
|
||||
@@ -424,10 +424,10 @@ int main(int argc, char** argv) {
|
||||
new_measure);
|
||||
assert(new_page.start <= before.offset && new_page.next > before.offset);
|
||||
active.render(changed); assert(active.flush());
|
||||
Position after; assert(the_mesh.loadJohnBookmark(after));
|
||||
Position after; assert(the_mesh.loadReaderBookmark(after));
|
||||
assert(after == pos(91, new_page.start));
|
||||
const auto rendered = changed.body();
|
||||
JohnReaderScreen reopened(&task, &changed);
|
||||
ReaderScreen reopened(&task, &changed);
|
||||
reopened.open(); changed.clear(); reopened.render(changed);
|
||||
assert(changed.body() == rendered);
|
||||
}
|
||||
@@ -459,12 +459,12 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
// A reset between renames recovers the previous record, not the .tmp.
|
||||
Files interrupted = original;
|
||||
assert(interrupted.rename("/john.pos", "/john.pos.bak"));
|
||||
assert(interrupted.rename("/reader.pos", "/reader.pos.bak"));
|
||||
assert(loadReaderBookmark(interrupted, saved) && saved == pos(4, 12));
|
||||
assert(saveReaderBookmark(interrupted, pos(20)));
|
||||
assert(saveReaderBookmark(interrupted, pos(0)) && interrupted.data.empty());
|
||||
Files damaged;
|
||||
damaged.data["/john.pos"] = {1, 2, 3};
|
||||
damaged.data["/reader.pos"] = {1, 2, 3};
|
||||
assert(!loadReaderBookmark(damaged, saved) && saved.atStart());
|
||||
assert(saveReaderBookmark(damaged, pos(10)));
|
||||
assert(loadReaderBookmark(damaged, saved) && saved == pos(10));
|
||||
@@ -159,20 +159,20 @@ int main() {
|
||||
displayPowerPrefs().inbox = DisplayInboxMode::Pending;
|
||||
assert(!messages.hasMessages());
|
||||
|
||||
#if COMPANION_FEATURE_JOHN
|
||||
#if COMPANION_FEATURE_READER
|
||||
// Receiving and downloading messages must leave an open reader alone.
|
||||
Screen reader;
|
||||
task.john_reader = task.curr = &reader;
|
||||
task.reader = task.curr = &reader;
|
||||
for (bool connected : {false, true}) {
|
||||
task.connected = connected;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
mesh.receive("while reading");
|
||||
assert(task.isJohnReaderActive());
|
||||
assert(task.isReaderActive());
|
||||
}
|
||||
for (int remaining : {1, 0}) {
|
||||
mesh.download();
|
||||
assert(task.getMsgCount() == remaining);
|
||||
assert(task.isJohnReaderActive());
|
||||
assert(task.isReaderActive());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -284,7 +284,7 @@ int main() {
|
||||
with tempfile.TemporaryDirectory(prefix="mesh-inbox-test-") as directory:
|
||||
executable = Path(directory) / "inbox.exe"
|
||||
result = subprocess.run([
|
||||
compiler, "-std=c++17", f"-DCOMPANION_FEATURE_JOHN={reader_enabled}",
|
||||
compiler, "-std=c++17", f"-DCOMPANION_FEATURE_READER={reader_enabled}",
|
||||
"-I" + str(ROOT / "src"), "-I" + str(ROOT / "test/mocks"),
|
||||
"-x", "c++", "-", str(ROOT / "src/helpers/ui/MomentaryButton.cpp"),
|
||||
str(ROOT / "src/helpers/ui/DisplayDriver.cpp"), "-o", str(executable),
|
||||
|
||||
@@ -14,8 +14,8 @@ from test_message_navigation import PREAMBLE
|
||||
from test_replay_reset_integration import extract_braced
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
FIXTURE = ROOT / "test/fixtures/companion_john"
|
||||
spec = importlib.util.spec_from_file_location("pack_john", ROOT / "tools/bible/pack_john.py")
|
||||
FIXTURE = ROOT / "test/fixtures/companion_reader"
|
||||
spec = importlib.util.spec_from_file_location("pack_reader", ROOT / "tools/bible/pack_reader.py")
|
||||
packer = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(packer)
|
||||
|
||||
@@ -48,10 +48,10 @@ class CompanionReaderTest(unittest.TestCase):
|
||||
|
||||
def test_ascii_typography_preserves_words_case_and_pinned_source(self):
|
||||
original = copy.deepcopy(self.document)
|
||||
sample = '\u201cJohn\u2019s\u00a0Word\u201d\u2014\u2018Test\u2019\u2013Yes!'
|
||||
self.assertEqual(packer.ascii_verse(sample, 64), b'"John\'s Word"-\'Test\'-Yes!')
|
||||
self.assertEqual(packer.ascii_verse('Already ASCII: John 3:16.', 64),
|
||||
b'Already ASCII: John 3:16.')
|
||||
sample = '\u201creader\u2019s\u00a0Word\u201d\u2014\u2018Test\u2019\u2013Yes!'
|
||||
self.assertEqual(packer.ascii_verse(sample, 64), b'"reader\'s Word"-\'Test\'-Yes!')
|
||||
self.assertEqual(packer.ascii_verse('Already ASCII: reader 3:16.', 64),
|
||||
b'Already ASCII: reader 3:16.')
|
||||
# Bounds apply after conversion, not to the original multibyte form.
|
||||
self.assertEqual(packer.ascii_verse('\u201cHi\u201d', 4), b'"Hi"')
|
||||
with self.assertRaises(ValueError):
|
||||
@@ -92,8 +92,8 @@ class CompanionReaderTest(unittest.TestCase):
|
||||
str(ROOT / "src/helpers/ota/OtaTinf.c"), "-o", str(obj)])
|
||||
self.run_checked([cxx, "-std=c++17", "-Os", "-Wall", "-Wextra", "-Werror",
|
||||
*flags, "-I" + str(ROOT / "src"), "-I" + str(FIXTURE),
|
||||
str(FIXTURE / "test_john.cpp"),
|
||||
str(ROOT / "src/helpers/CompanionJohn.cpp"), str(obj), "-o", str(binary)])
|
||||
str(FIXTURE / "test_reader_lookup.cpp"),
|
||||
str(ROOT / "src/helpers/CompanionReader.cpp"), str(obj), "-o", str(binary)])
|
||||
result = self.run_checked([str(binary), "--dump"])
|
||||
actual = dict(line.split("\t", 1) for line in result.splitlines())
|
||||
self.assertEqual(actual, self.expected_verses)
|
||||
@@ -105,28 +105,28 @@ class CompanionReaderTest(unittest.TestCase):
|
||||
for flags in ([], ["-DESP32_PLATFORM=1"], ["-DNRF52_PLATFORM=1"],
|
||||
["-DESP32_PLATFORM=1", "-DCOMPANION_RADIO_FULL=1"],
|
||||
["-DESP32_PLATFORM=1", "-DCOMPANION_RADIO_FULL=1",
|
||||
"-DENABLE_USB_INTERFACE=1", "-DCOMPANION_FEATURE_JOHN=0"]):
|
||||
"-DENABLE_USB_INTERFACE=1", "-DCOMPANION_FEATURE_READER=0"]):
|
||||
result = self.run_checked([cxx, "-E", "-P", *flags,
|
||||
str(ROOT / "src/helpers/CompanionJohn.cpp")])
|
||||
self.assertNotIn("johnData", result)
|
||||
self.assertNotIn("handleJohnCommand", result)
|
||||
str(ROOT / "src/helpers/CompanionReader.cpp")])
|
||||
self.assertNotIn("readerData", result)
|
||||
self.assertNotIn("handleReaderCommand", result)
|
||||
|
||||
def test_terminal_dispatch_is_separate_from_bounded_api_replies(self):
|
||||
source = (ROOT / "examples/companion_radio/MyMesh.cpp").read_text(encoding="utf-8")
|
||||
start = source.index("void MyMesh::handleTerminalCommand(")
|
||||
end = source.index("\nvoid MyMesh::enterCLIRescue()", start)
|
||||
terminal = source[start:end]
|
||||
self.assertEqual(source.count("mesh::handleJohnCommand("), 1)
|
||||
self.assertLess(terminal.index("mesh::handleJohnCommand("),
|
||||
self.assertEqual(source.count("mesh::handleReaderCommand("), 1)
|
||||
self.assertLess(terminal.index("mesh::handleReaderCommand("),
|
||||
terminal.index("char local_reply[160]"))
|
||||
self.assertIn("get John <chapter>:<verse> (World English Bible, offline)", terminal)
|
||||
self.assertEqual(terminal.count("#if COMPANION_FEATURE_JOHN"), 2)
|
||||
self.assertIn("get reader <chapter>:<verse> (World English Bible, offline)", terminal)
|
||||
self.assertEqual(terminal.count("#if COMPANION_FEATURE_READER"), 2)
|
||||
|
||||
def test_reader_navigation_and_bookmark_recovery(self):
|
||||
cc, cxx = shutil.which("gcc"), shutil.which("g++")
|
||||
if not cc or not cxx:
|
||||
self.skipTest("host GCC and G++ required")
|
||||
with tempfile.TemporaryDirectory(prefix="meshcore-john-reader-") as directory:
|
||||
with tempfile.TemporaryDirectory(prefix="meshcore-reader-reader-") as directory:
|
||||
directory = Path(directory)
|
||||
flags = ["-DNRF52_PLATFORM=1", "-DCOMPANION_RADIO_FULL=1", "-DENABLE_USB_INTERFACE=1"]
|
||||
obj, binary = directory / "tinf.o", directory / "reader.exe"
|
||||
@@ -139,21 +139,21 @@ class CompanionReaderTest(unittest.TestCase):
|
||||
f"-DUI_BUTTON_READER_HINT={button_hint}",
|
||||
f"-DUI_READER_TOUCH_BAR={touch_bar}",
|
||||
"-I" + str(ROOT / "src"), "-I" + str(FIXTURE),
|
||||
str(FIXTURE / "test_reader.cpp"), str(ROOT / "src/helpers/CompanionJohn.cpp"),
|
||||
str(FIXTURE / "test_reader_screen.cpp"), str(ROOT / "src/helpers/CompanionReader.cpp"),
|
||||
str(obj), "-o", str(binary)])
|
||||
self.run_checked([str(binary)])
|
||||
|
||||
def test_reader_button_routing_and_profile_gate(self):
|
||||
ui = (ROOT / "examples/companion_radio/ui-new/UITask.cpp").read_text(encoding="utf-8")
|
||||
long_press = ui[ui.index("char UITask::handleLongPress("):]
|
||||
self.assertLess(long_press.index("isJohnReaderActive()"), long_press.index("enterCLIRescue()"))
|
||||
self.assertLess(long_press.index("isReaderActive()"), long_press.index("enterCLIRescue()"))
|
||||
self.assertLess(long_press.index("isRadioPage()"), long_press.index("enterCLIRescue()"))
|
||||
self.assertIn("#if COMPANION_FEATURE_JOHN\n#include \"JohnReaderScreen.h\"", ui)
|
||||
self.assertIn("#if COMPANION_FEATURE_READER\n#include \"ReaderScreen.h\"", ui)
|
||||
self.assertLess(ui.index("#define UI_BUTTON_READER_HINT 1"),
|
||||
ui.index('#include "JohnReaderScreen.h"'))
|
||||
ui.index('#include "ReaderScreen.h"'))
|
||||
# Queue downloads preserve the current screen unconditionally now;
|
||||
# test_companion_inbox exercises this with the reader enabled.
|
||||
self.assertIn("else if (isJohnReaderActive())", ui)
|
||||
self.assertIn("else if (isReaderActive())", ui)
|
||||
self.assertIn("c = handleLongPress(KEY_ENTER);", ui)
|
||||
self.assertIn("handleDoubleClick(KEY_PREV)", ui)
|
||||
|
||||
@@ -166,7 +166,7 @@ struct Board {
|
||||
bool isUsbHostConnected() const { return host; }
|
||||
};
|
||||
struct Interfaces { bool takePairingRequest() { return false; } };
|
||||
struct JohnReaderScreen : Screen { int saves = 0; void flush() { ++saves; } };
|
||||
struct ReaderScreen : Screen { int saves = 0; void flush() { ++saves; } };
|
||||
'''
|
||||
members = r'''
|
||||
Board* _board;
|
||||
@@ -189,12 +189,12 @@ int main() {
|
||||
displayPowerPrefs().usb = {DisplayMode::On, 5};
|
||||
Display display;
|
||||
Screen home;
|
||||
JohnReaderScreen reader;
|
||||
ReaderScreen reader;
|
||||
Board board;
|
||||
Interfaces interfaces;
|
||||
UITask task(display);
|
||||
task._board = &board; task._interfaceManager = &interfaces;
|
||||
task.curr = task.home = &home; task.john_reader = &reader;
|
||||
task.curr = task.home = &home; task.reader = &reader;
|
||||
task.servicePairingState();
|
||||
assert(!display.isOn() && reader.saves == 0);
|
||||
task.curr = &reader;
|
||||
@@ -204,7 +204,7 @@ int main() {
|
||||
assert(display.isOn() && reader.saves == 0);
|
||||
g_mock_millis = 5000; task.servicePairingState();
|
||||
assert(!display.isOn());
|
||||
const int expected_saves = COMPANION_FEATURE_JOHN ? 1 : 0;
|
||||
const int expected_saves = COMPANION_FEATURE_READER ? 1 : 0;
|
||||
assert(reader.saves == expected_saves);
|
||||
task.servicePairingState();
|
||||
assert(reader.saves == expected_saves); // No repeated writes while dark.
|
||||
@@ -233,7 +233,7 @@ int main() {
|
||||
for enabled in (0, 1):
|
||||
with self.subTest(reader_enabled=enabled):
|
||||
binary = directory / "power.exe"
|
||||
self.run_checked([compiler, "-std=c++17", f"-DCOMPANION_FEATURE_JOHN={enabled}",
|
||||
self.run_checked([compiler, "-std=c++17", f"-DCOMPANION_FEATURE_READER={enabled}",
|
||||
"-I" + str(ROOT / "src"), "-I" + str(ROOT / "test/mocks"),
|
||||
str(directory / "power.cpp"),
|
||||
str(ROOT / "src/helpers/ui/MomentaryButton.cpp"),
|
||||
|
||||
@@ -22,14 +22,14 @@ class IndicatorMessagesProfileTest(unittest.TestCase):
|
||||
# Compile the real gate: HAS_TOUCH must not overwrite the explicit
|
||||
# physical-button footer setting with zero.
|
||||
start = ui.index("#ifndef UI_BUTTON_READER_HINT")
|
||||
gate = ui[start:ui.index("#if COMPANION_FEATURE_JOHN", start)]
|
||||
gate = ui[start:ui.index("#if COMPANION_FEATURE_READER", start)]
|
||||
result = subprocess.run(["c++", "-E", "-P", "-x", "c++", "-"],
|
||||
input="#define HAS_TOUCH 1\n#define UI_BUTTON_READER_HINT 1\n" + gate +
|
||||
"\n#if UI_BUTTON_READER_HINT != 1\n#error reader hint disabled\n#endif\n",
|
||||
text=True, capture_output=True)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("static_cast<MsgPreviewScreen*>(msg_preview)->readerTouchBar()", ui)
|
||||
self.assertIn("static_cast<JohnReaderScreen*>(john_reader)->readerTouchBar()", ui)
|
||||
self.assertIn("static_cast<ReaderScreen*>(reader)->readerTouchBar()", ui)
|
||||
|
||||
def test_indicator_enables_messages_home_page(self):
|
||||
profile = PROFILE.read_text()
|
||||
|
||||
@@ -117,15 +117,15 @@ public:
|
||||
UIScreen* curr=nullptr;
|
||||
UIScreen* msg_preview=nullptr;
|
||||
UIScreen* home=nullptr;
|
||||
UIScreen* john_reader=nullptr;
|
||||
UIScreen* reader=nullptr;
|
||||
uint32_t _auto_off=0,_next_refresh=0;
|
||||
int buzzer_changes=0;
|
||||
int getMsgCount() const { return 0; }
|
||||
explicit UITask(DisplayDriver& display) : _display(&display) {}
|
||||
bool isJohnReaderActive() const { return john_reader && curr==john_reader; }
|
||||
bool isReaderActive() const { return reader && curr==reader; }
|
||||
void gotoHomeScreen() { curr=home; }
|
||||
void toggleBuzzer() { ++buzzer_changes; }
|
||||
void showJohnReader() { curr=john_reader; }
|
||||
void showReader() { curr=reader; }
|
||||
void showAlert(const char*,int) {}
|
||||
char handleLongPress(char c) { return c; }
|
||||
char handleMultiClick(char,bool);
|
||||
@@ -159,7 +159,7 @@ int main() {
|
||||
HomeScreen home(&task);
|
||||
Screen group;
|
||||
MsgPreviewScreen messages(&task);
|
||||
task.home=&home; task.msg_preview=task.curr=&messages; task.john_reader=&group;
|
||||
task.home=&home; task.msg_preview=task.curr=&messages; task.reader=&group;
|
||||
messages.addPreview(1,"Alice","public old",0,"Public");
|
||||
messages.addPreview(1,"Bob","public new",0,"Public");
|
||||
messages.addPreview(1,"Carol","second",2,"Second");
|
||||
@@ -289,7 +289,7 @@ int main() {
|
||||
g_mock_millis+=25; task.pollButton();
|
||||
g_mock_millis+=MOMENTARY_BUTTON_MULTI_CLICK_MS; task.pollButton();
|
||||
assert(home.key==0);
|
||||
#if COMPANION_FEATURE_JOHN
|
||||
#if COMPANION_FEATURE_READER
|
||||
task.curr=&group;
|
||||
gesture(task,1); assert(group.key==KEY_NEXT && group.events==1);
|
||||
gesture(task,2); assert(group.key==KEY_PREV && group.events==2);
|
||||
@@ -391,7 +391,7 @@ class MessageNavigationTest(unittest.TestCase):
|
||||
binary = Path(temp) / "navigation"
|
||||
result = subprocess.run([
|
||||
"c++", "-std=c++17", signedness,
|
||||
f"-DCOMPANION_FEATURE_JOHN={enabled}",
|
||||
f"-DCOMPANION_FEATURE_READER={enabled}",
|
||||
*flags,
|
||||
"-I" + str(ROOT / "src"), "-I" + str(ROOT / "test/mocks"),
|
||||
"-fsanitize=address,undefined", "-fno-pie", "-no-pie",
|
||||
|
||||
@@ -223,7 +223,7 @@ class ReaderTouchCoordinatesTest(unittest.TestCase):
|
||||
source = (ROOT / "examples/companion_radio/ui-new/UITask.cpp").read_text()
|
||||
self.assertIn("layout.header_divider_y + 1", source)
|
||||
self.assertIn("if (!_display->isOn()) reader_touch_bar = nullptr;", source)
|
||||
reader = (ROOT / "examples/companion_radio/ui-new/JohnReaderScreen.h").read_text()
|
||||
reader = (ROOT / "examples/companion_radio/ui-new/ReaderScreen.h").read_text()
|
||||
self.assertIn("makeReaderTouchBar(hint_text, bottom, top - 1)", reader)
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user