#include #include #include #include #include "palette.h" using namespace roro::ui; void setUp() {} void tearDown() {} namespace { double channel(uint32_t v) { double c = v / 255.0; return c <= 0.03928 ? c / 12.92 : std::pow((c + 0.055) / 1.055, 2.4); } double luminance(uint16_t c) { uint32_t rgb = rgb888(c); return 0.2126 * channel(rgb >> 16) + 0.7152 * channel((rgb >> 8) & 255) + 0.0722 * channel(rgb & 255); } // The WCAG contrast ratio of two colours as the frame buffer shows them: 1 (the same) to 21. double contrast(uint16_t a, uint16_t b) { double la = luminance(a), lb = luminance(b); return (std::fmax(la, lb) + 0.05) / (std::fmin(la, lb) + 0.05); } void atLeast(double wanted, uint16_t a, uint16_t b, const char* what, int theme, bool light) { char msg[96]; std::snprintf(msg, sizeof msg, "%s %s: %s is %.1f, under %.1f", themeName(theme), light ? "light" : "dark", what, contrast(a, b), wanted); TEST_ASSERT_TRUE_MESSAGE(contrast(a, b) >= wanted, msg); } } // namespace void test_seven_themes_with_names() { TEST_ASSERT_EQUAL(7, kThemeCount); TEST_ASSERT_EQUAL_STRING("roro9stack", themeName(0)); for (int t = 0; t < kThemeCount; ++t) TEST_ASSERT_TRUE(std::string(themeName(t)).size() > 0 && std::string(themeName(t)).size() <= 14); // Out of range: the first, rather than whatever is next in memory. TEST_ASSERT_EQUAL_STRING("roro9stack", themeName(7)); TEST_ASSERT_EQUAL_STRING("roro9stack", themeName(-1)); TEST_ASSERT_EQUAL_PTR(&palette(0, true), &palette(99, true)); } // The frame buffer keeps 3 bits of red and green and 2 of blue: a colour it would round is a // colour that isn't the one shown. void test_every_colour_is_one_the_frame_buffer_holds() { for (int t = 0; t < kThemeCount; ++t) for (int light = 0; light < 2; ++light) { const Palette& p = palette(t, light); const uint16_t* c = &p.background; for (size_t i = 0; i < sizeof(Palette) / sizeof(uint16_t); ++i) { uint32_t rgb = rgb888(c[i]); uint16_t back = static_cast(((rgb >> 19) & 0x1F) << 11 | ((rgb >> 10) & 0x3F) << 5 | ((rgb >> 3) & 0x1F)); TEST_ASSERT_EQUAL_HEX16(back, c[i]); } } } void test_every_theme_reads() { for (int t = 0; t < kThemeCount; ++t) for (int light = 0; light < 2; ++light) { const Palette& p = palette(t, light); atLeast(4.5, p.text, p.background, "text", t, light); atLeast(3, p.muted, p.background, "muted text", t, light); atLeast(1.3, p.muted, p.text, "muted against text", t, light); atLeast(3, p.accent, p.background, "the accent", t, light); atLeast(3, p.onAccent, p.accent, "text on the accent", t, light); atLeast(4.5, p.text, p.bar, "text on the bar", t, light); atLeast(2.5, p.barMuted, p.bar, "idle marks on the bar", t, light); atLeast(2, p.accent, p.bar, "live marks on the bar", t, light); atLeast(3, p.warning, p.background, "a warning", t, light); atLeast(3, p.message, p.background, "a message", t, light); atLeast(3, p.good, p.background, "good", t, light); atLeast(3, p.onAccent, p.warning, "text on a warning Toast", t, light); atLeast(3, p.onAccent, p.message, "text on a message Toast", t, light); atLeast(2, p.faint, p.accent, "the Launcher's selected icon", t, light); atLeast(1.15, p.faint, p.background, "grid lines", t, light); atLeast(2.5, p.red, p.background, "red", t, light); atLeast(2.5, p.yellow, p.background, "yellow", t, light); atLeast(2.5, p.violet, p.background, "violet", t, light); } } void test_the_first_is_the_websites() { const Palette& p = palette(0, false); TEST_ASSERT_EQUAL_HEX32(0x000000, rgb888(p.background)); TEST_ASSERT_EQUAL_HEX32(0x00dbff, rgb888(p.accent)); TEST_ASSERT_EQUAL_HEX32(0x494955, rgb888(p.bar)); TEST_ASSERT_EQUAL_HEX32(0xffffff, rgb888(palette(0, true).background)); } int main() { UNITY_BEGIN(); RUN_TEST(test_seven_themes_with_names); RUN_TEST(test_every_colour_is_one_the_frame_buffer_holds); RUN_TEST(test_every_theme_reads); RUN_TEST(test_the_first_is_the_websites); return UNITY_END(); }