feat(display): add R8 portrait observer layouts

This commit is contained in:
agessaman
2026-08-28 09:21:38 -07:00
parent 85fd9b09be
commit fc361ca94b
11 changed files with 482 additions and 12 deletions
+1
View File
@@ -30,6 +30,7 @@ does not reflect the GoogleTest count — run the built binary directly
| `test_mqtt_topic_router` | `src/helpers/MQTTTopicRouter.h` | complete preset/custom topic-routing contract; MeshRank all types except raw; required identifiers; invalid inputs/slots; exact buffer boundaries |
| `test_mqtt_connection_policy` | `src/helpers/MQTTConnectionPolicy.h` | reconnect guard/backoff/stagger and breaker transitions; stable reset; JWT lifetime/renewal policy; exact timing boundaries and 32-bit `millis()` rollover; WiFi current-outage start sticky across STA reconnect attempts |
| `test_alert_fault_policy` | `src/helpers/AlertFaultPolicy.h` | WiFi/MQTT fault edge detector; `OutageSnapshot` (down / started_ms / initiating reason) fed to tick and `formatWifiAlert`; reason-8 reconnects change neither duration nor initiating reason; flap between status polls; down at `millis()==0`; packed 64-bit cross-task word; rate-limit floor and first-fire; 5 s poll cadence and `millis()` rollover |
| `test_display_viewport` | `src/helpers/ui/DisplayViewport.h`, `src/helpers/ui/DisplayFrameSignature.h` | logical-to-physical portrait mapping; fractional span coverage; fitted-width conversion; preferred/fallback text scaling; stable visible-frame change detection |
| `test_mqtt_packet_queue_policy` | `src/helpers/MQTTPacketQueuePolicy.h` | queue-full eviction; stale-disconnect flush; adaptive drain limits; bounded QoS0 retries; exact timing boundaries and 32-bit `millis()` rollover |
| `test_mqtt_packet_filter` | `src/helpers/MQTTPacketFilter.h` | per-slot 0-15 allowlist parsing/formatting, numeric and named spellings; exact bounds; membership; candidate/eligible split and retry-completion policy; pre-queue union gate; default-mask detection |
| `test_mqtt_runtime_buffer_lifecycle` | `src/helpers/MQTTRuntimeBufferLifecycle.h` | idempotent allocation/release; partial-allocation degradation; retry of only missing buffers |
@@ -0,0 +1,83 @@
#include "helpers/ui/DisplayFrameSignature.h"
#include "helpers/ui/DisplayViewport.h"
#include <gtest/gtest.h>
namespace {
const DisplayViewport::Geometry kPortrait{ 128, 64, 240, 320 };
} // namespace
TEST(DisplayViewport, MapsLogicalBoundsToPortraitPanel) {
EXPECT_EQ(0, kPortrait.mapX(0));
EXPECT_EQ(240, kPortrait.mapX(128));
EXPECT_EQ(0, kPortrait.mapY(0));
EXPECT_EQ(320, kPortrait.mapY(64));
}
TEST(DisplayViewport, MapsObserverRowsAcrossPortraitHeight) {
EXPECT_EQ(0, kPortrait.mapY(0));
EXPECT_EQ(70, kPortrait.mapY(14));
EXPECT_EQ(100, kPortrait.mapY(20));
EXPECT_EQ(120, kPortrait.mapY(24));
EXPECT_EQ(150, kPortrait.mapY(30));
EXPECT_EQ(200, kPortrait.mapY(40));
EXPECT_EQ(240, kPortrait.mapY(48));
EXPECT_EQ(250, kPortrait.mapY(50));
}
TEST(DisplayViewport, FractionalHorizontalSpansHaveNoGapsOrOverlaps) {
int16_t previous_end = 0;
int total_width = 0;
for (int x = 0; x < 128; ++x) {
int16_t start = kPortrait.mapX(x);
int16_t end = kPortrait.mapX(x + 1);
EXPECT_EQ(previous_end, start);
EXPECT_TRUE(end - start == 1 || end - start == 2);
total_width += end - start;
previous_end = end;
}
EXPECT_EQ(240, previous_end);
EXPECT_EQ(240, total_width);
}
TEST(DisplayViewport, ConvertsFittedPhysicalWidthBackToLogicalWidth) {
EXPECT_EQ(0, kPortrait.logicalWidthForPhysical(0));
EXPECT_EQ(64, kPortrait.logicalWidthForPhysical(120));
EXPECT_EQ(122, kPortrait.logicalWidthForPhysical(228));
EXPECT_EQ(128, kPortrait.logicalWidthForPhysical(240));
}
TEST(DisplayViewport, SelectsPreferredTextScaleOnlyWhenItFits) {
EXPECT_EQ(2, DisplayViewport::selectTextScale(114, 2, 1, 240));
EXPECT_EQ(1, DisplayViewport::selectTextScale(150, 2, 1, 240));
EXPECT_EQ(1, DisplayViewport::selectTextScale(192, 2, 1, 229));
EXPECT_EQ(1, DisplayViewport::selectTextScale(300, 2, 1, 240));
}
TEST(DisplayFrameSignature, ChangesWithVisibleContent) {
uint32_t initial = DisplayFrameSignature::INITIAL;
uint32_t home = DisplayFrameSignature::append(initial, "home");
EXPECT_EQ(home, DisplayFrameSignature::append(initial, "home"));
EXPECT_NE(home, DisplayFrameSignature::append(initial, "setup"));
EXPECT_NE(DisplayFrameSignature::append(home, "10.0.0.1"), DisplayFrameSignature::append(home, "10.0.0.2"));
}
TEST(DisplayFrameSignature, KeepsAdjacentFieldsDistinct) {
uint32_t first = DisplayFrameSignature::append(DisplayFrameSignature::INITIAL, "ab");
first = DisplayFrameSignature::append(first, "c");
uint32_t second = DisplayFrameSignature::append(DisplayFrameSignature::INITIAL, "a");
second = DisplayFrameSignature::append(second, "bc");
EXPECT_NE(first, second);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}