Files
onionHEN/source/util/tests/test_platform_notify.c

474 lines
20 KiB
C

/* Host tests for onion_notify_format (pure string path). */
#include "test_harness.h"
#include "cJSON.hpp"
#include <onion/notify.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
static void format_msg(char *out, size_t out_sz, int wm, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
onion_notify_format(out, out_sz, wm, fmt, ap);
va_end(ap);
}
static int test_notify_format_prefix(void) {
char out[128];
format_msg(out, sizeof(out), 1, "hello %s", "world");
TEST_ASSERT_STREQ("[OnionHEN] hello world", out);
return 0;
}
static int test_notify_format_truncates(void) {
char out[16];
format_msg(out, sizeof(out), 1, "0123456789ABCDEFGHIJ");
/* must be NUL-terminated and start with watermark prefix */
TEST_ASSERT_TRUE(out[sizeof(out) - 1] == '\0' || strlen(out) < sizeof(out));
TEST_ASSERT_TRUE(strncmp(out, "[OnionHEN]", 10) == 0);
return 0;
}
static int test_notify_format_no_watermark(void) {
char out[128];
format_msg(out, sizeof(out), 0, "hello %s", "world");
TEST_ASSERT_STREQ("hello world", out);
return 0;
}
static int test_notify_send_noop(void) {
/* hits sceKernelSendNotificationRequest stub — must not crash */
onion_notify(1, "host test notify %d", 7);
onion_notify_debug(0, "host test debug notify %d", 7);
return 0;
}
static unsigned char g_debug_notify_request[0xC30];
static size_t g_debug_notify_request_size;
static int32_t capture_debug_notify(int32_t device, void *request, size_t size,
int32_t blocking) {
(void)device;
(void)blocking;
g_debug_notify_request_size = size;
if (request && size <= sizeof(g_debug_notify_request))
memcpy(g_debug_notify_request, request, size);
return 0;
}
static int test_notify_debug_localized(void) {
memset(g_debug_notify_request, 0xff, sizeof(g_debug_notify_request));
g_debug_notify_request_size = 0;
onion_notify_set_send(capture_debug_notify);
onion_notify_set_language(ONION_NOTIFY_LANG_ZH_HANS);
onion_notify_debug(/*show_watermark=*/0, "notify.remote_play.pairing_cancelled");
TEST_ASSERT_TRUE(g_debug_notify_request_size ==
sizeof(g_debug_notify_request));
TEST_ASSERT_TRUE(g_debug_notify_request[0x2C] == 0);
TEST_ASSERT_STREQ("远程游玩配对已中止。",
(const char *)&g_debug_notify_request[0x2D]);
TEST_ASSERT_TRUE(g_debug_notify_request[0x42D] == '\0');
onion_notify_set_language(ONION_NOTIFY_LANG_EN);
onion_notify_debug(/*show_watermark=*/0, "notify.remote_play.paired");
TEST_ASSERT_TRUE(g_debug_notify_request[0x2C] == 0);
TEST_ASSERT_STREQ("Remote Play device paired.",
(const char *)&g_debug_notify_request[0x2D]);
onion_notify_debug(/*show_watermark=*/1, "notify.boot.thanks");
TEST_ASSERT_TRUE(g_debug_notify_request[0x2C] == 0);
TEST_ASSERT_STREQ("[OnionHEN] Special Thanks to: kvnhrt, Modmycon",
(const char *)&g_debug_notify_request[0x2D]);
TEST_ASSERT_TRUE(g_debug_notify_request[0x42D] == '\0');
onion_notify_set_send(NULL);
return 0;
}
static int test_notify_language_resolution(void) {
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_ZH_HANS,
onion_notify_resolve_language(0, 11));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_ZH_HANS,
onion_notify_resolve_language(1, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_EN,
onion_notify_resolve_language(2, 11));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_AR,
onion_notify_resolve_language(3, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_ZH_HANT,
onion_notify_resolve_language(4, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_JA,
onion_notify_resolve_language(5, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_FR,
onion_notify_resolve_language(6, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_DE,
onion_notify_resolve_language(7, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_KO,
onion_notify_resolve_language(8, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_ES,
onion_notify_resolve_language(9, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_PT_BR,
onion_notify_resolve_language(10, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_IT,
onion_notify_resolve_language(11, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_RU,
onion_notify_resolve_language(12, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_PL,
onion_notify_resolve_language(13, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_TH,
onion_notify_resolve_language(14, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_NL,
onion_notify_resolve_language(15, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_FI,
onion_notify_resolve_language(16, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_SV,
onion_notify_resolve_language(17, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_DA,
onion_notify_resolve_language(18, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_NO,
onion_notify_resolve_language(19, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_TR,
onion_notify_resolve_language(20, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_CS,
onion_notify_resolve_language(21, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_HU,
onion_notify_resolve_language(22, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_EL,
onion_notify_resolve_language(23, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_RO,
onion_notify_resolve_language(24, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_VI,
onion_notify_resolve_language(25, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_ID,
onion_notify_resolve_language(26, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_UK,
onion_notify_resolve_language(27, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_PT_PT,
onion_notify_resolve_language(28, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_AR,
onion_notify_resolve_language(0, 21));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_JA,
onion_notify_resolve_language(0, 0));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_FR,
onion_notify_resolve_language(0, 2));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_FR,
onion_notify_resolve_language(0, 22));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_DE,
onion_notify_resolve_language(0, 4));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_ZH_HANT,
onion_notify_resolve_language(0, 10));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_ZH_HANS,
onion_notify_resolve_language(0, 11));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_EN,
onion_notify_resolve_language(0, 1));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_KO,
onion_notify_resolve_language(0, 9));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_ES,
onion_notify_resolve_language(0, 3));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_ES,
onion_notify_resolve_language(0, 20));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_PT_BR,
onion_notify_resolve_language(0, 17));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_PT_PT,
onion_notify_resolve_language(0, 7));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_NL,
onion_notify_resolve_language(0, 6));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_FI,
onion_notify_resolve_language(0, 12));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_SV,
onion_notify_resolve_language(0, 13));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_DA,
onion_notify_resolve_language(0, 14));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_NO,
onion_notify_resolve_language(0, 15));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_TR,
onion_notify_resolve_language(0, 19));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_CS,
onion_notify_resolve_language(0, 23));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_HU,
onion_notify_resolve_language(0, 24));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_EL,
onion_notify_resolve_language(0, 25));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_RO,
onion_notify_resolve_language(0, 26));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_VI,
onion_notify_resolve_language(0, 28));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_ID,
onion_notify_resolve_language(0, 29));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_UK,
onion_notify_resolve_language(0, 30));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_IT,
onion_notify_resolve_language(0, 5));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_RU,
onion_notify_resolve_language(0, 8));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_PL,
onion_notify_resolve_language(0, 16));
TEST_ASSERT_EQ_INT(ONION_NOTIFY_LANG_TH,
onion_notify_resolve_language(0, 27));
return 0;
}
static int test_notify_format_localized(void) {
char out[128];
onion_notify_set_language(ONION_NOTIFY_LANG_ZH_HANS);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] 无法提升权限", out);
onion_notify_set_language(ONION_NOTIFY_LANG_EN);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Unable to raise privileges", out);
onion_notify_set_language(ONION_NOTIFY_LANG_AR);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] تعذّر رفع الامتيازات", out);
onion_notify_set_language(ONION_NOTIFY_LANG_ZH_HANT);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] 無法提升權限", out);
onion_notify_set_language(ONION_NOTIFY_LANG_JA);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] 権限を昇格できません", out);
onion_notify_set_language(ONION_NOTIFY_LANG_FR);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Impossible d’élever les privilèges", out);
onion_notify_set_language(ONION_NOTIFY_LANG_DE);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Berechtigungen konnten nicht erhöht werden",
out);
onion_notify_set_language(ONION_NOTIFY_LANG_KO);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] 권한을 올릴 수 없습니다", out);
onion_notify_set_language(ONION_NOTIFY_LANG_ES);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] No se pudieron elevar los privilegios", out);
onion_notify_set_language(ONION_NOTIFY_LANG_PT_BR);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Não foi possível elevar os privilégios", out);
onion_notify_set_language(ONION_NOTIFY_LANG_IT);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Impossibile elevare i privilegi", out);
onion_notify_set_language(ONION_NOTIFY_LANG_RU);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Не удалось повысить привилегии", out);
onion_notify_set_language(ONION_NOTIFY_LANG_PL);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Nie udało się podnieść uprawnień", out);
onion_notify_set_language(ONION_NOTIFY_LANG_TH);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] ไม่สามารถยกระดับสิทธิ์ได้", out);
onion_notify_set_language(ONION_NOTIFY_LANG_NL);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Rechten konden niet worden verhoogd", out);
onion_notify_set_language(ONION_NOTIFY_LANG_TR);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Yetkiler yükseltilemedi", out);
onion_notify_set_language(ONION_NOTIFY_LANG_VI);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Không nâng được quyền", out);
onion_notify_set_language(ONION_NOTIFY_LANG_UK);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Не вдалося підвищити права", out);
onion_notify_set_language(ONION_NOTIFY_LANG_PT_PT);
format_msg(out, sizeof(out), 1, "notify.priv.unable");
TEST_ASSERT_STREQ("[OnionHEN] Não foi possível elevar os privilégios", out);
return 0;
}
static int test_notify_payload_localized(void) {
char out[256];
onion_notify_set_language(ONION_NOTIFY_LANG_ZH_HANS);
format_msg(out, sizeof(out), 1, "notify.payload.loading", "demo.elf");
TEST_ASSERT_STREQ("[OnionHEN] 正在加载 Payload demo.elf...", out);
format_msg(out, sizeof(out), 1,
"notify.payload.launched", "/data/demo.elf",
"demo");
TEST_ASSERT_STREQ(
"[OnionHEN] Payload 已启动\n路径:/data/demo.elf\n标识:demo", out);
onion_notify_set_language(ONION_NOTIFY_LANG_EN);
return 0;
}
static char g_rich_payload[4096];
static int32_t capture_rich_notify(int32_t user_id, bool is_logged,
const char *payload) {
TEST_ASSERT_TRUE(user_id == 0xFE);
TEST_ASSERT_TRUE(is_logged);
snprintf(g_rich_payload, sizeof(g_rich_payload), "%s",
payload ? payload : "");
return 0;
}
static int test_notify_rich_formats_payload(void) {
g_rich_payload[0] = '\0';
onion_notify_set_rich_send(capture_rich_notify);
onion_notify_rich("Title", "Sub \"quoted\"", "/icon\"quoted.png",
"download", "42");
cJSON *root = cJSON_Parse(g_rich_payload);
cJSON *raw = cJSON_GetObjectItemCaseSensitive(root, "rawData");
cJSON *view = cJSON_GetObjectItemCaseSensitive(raw, "viewData");
cJSON *message = cJSON_GetObjectItemCaseSensitive(view, "message");
cJSON *sub_message = cJSON_GetObjectItemCaseSensitive(view, "subMessage");
cJSON *icon = cJSON_GetObjectItemCaseSensitive(view, "icon");
cJSON *icon_params = cJSON_GetObjectItemCaseSensitive(icon, "parameters");
TEST_ASSERT_TRUE(cJSON_IsObject(root));
TEST_ASSERT_STREQ("InteractiveToastTemplateB",
cJSON_GetObjectItemCaseSensitive(raw, "viewTemplateType")
->valuestring);
TEST_ASSERT_STREQ(
"Title", cJSON_GetObjectItemCaseSensitive(message, "body")->valuestring);
TEST_ASSERT_STREQ("Sub \"quoted\"",
cJSON_GetObjectItemCaseSensitive(sub_message, "body")
->valuestring);
TEST_ASSERT_STREQ("/icon\"quoted.png",
cJSON_GetObjectItemCaseSensitive(icon_params, "url")
->valuestring);
TEST_ASSERT_STREQ("42",
cJSON_GetObjectItemCaseSensitive(root,
"localNotificationId")
->valuestring);
cJSON_Delete(root);
return 0;
}
static int test_notify_rich_localizes_both_text_fields(void) {
g_rich_payload[0] = '\0';
onion_notify_set_language(ONION_NOTIFY_LANG_ZH_HANS);
onion_notify_set_rich_send(capture_rich_notify);
onion_notify_rich("notify.brand", "notify.boot.starting", "/icon.png",
"download", "43");
TEST_ASSERT_TRUE(strstr(g_rich_payload, "OnionHEN 正在启动...") != NULL);
onion_notify_set_language(ONION_NOTIFY_LANG_EN);
return 0;
}
static int g_plain_fallback_calls;
static char g_plain_fallback_message[1024];
static int g_rich_fail_calls;
static int32_t capture_plain_fallback(int32_t device, void *request, size_t size,
int32_t blocking) {
(void)device;
(void)blocking;
g_plain_fallback_calls++;
memset(g_plain_fallback_message, 0, sizeof(g_plain_fallback_message));
if (request && size >= 0x2D + 1) {
snprintf(g_plain_fallback_message, sizeof(g_plain_fallback_message), "%s",
(const char *)request + 0x2D);
}
return 0;
}
static int32_t fail_rich_notify(int32_t user_id, bool is_logged,
const char *payload) {
(void)user_id;
(void)is_logged;
(void)payload;
g_rich_fail_calls++;
return -1;
}
static int test_notify_rich_falls_back_when_send_fails(void) {
g_plain_fallback_calls = 0;
g_rich_fail_calls = 0;
g_plain_fallback_message[0] = '\0';
onion_notify_set_send(capture_plain_fallback);
onion_notify_set_rich_send(fail_rich_notify);
onion_notify_rich("Title", "Sub", "/icon.png", "download", "44");
TEST_ASSERT_EQ_INT(1, g_rich_fail_calls);
TEST_ASSERT_EQ_INT(1, g_plain_fallback_calls);
TEST_ASSERT_STREQ("Sub", g_plain_fallback_message);
onion_notify_set_send(NULL);
onion_notify_set_rich_send(NULL);
return 0;
}
static int test_notify_rich_falls_back_when_unregistered(void) {
g_plain_fallback_calls = 0;
g_plain_fallback_message[0] = '\0';
onion_notify_set_send(capture_plain_fallback);
onion_notify_set_rich_send(NULL);
onion_notify_rich("Title", "", "/icon.png", "download", "45");
TEST_ASSERT_EQ_INT(1, g_plain_fallback_calls);
TEST_ASSERT_STREQ("Title", g_plain_fallback_message);
onion_notify_set_send(NULL);
return 0;
}
static int test_notify_rich_skips_send_without_icon(void) {
g_plain_fallback_calls = 0;
g_rich_fail_calls = 0;
g_plain_fallback_message[0] = '\0';
onion_notify_set_send(capture_plain_fallback);
onion_notify_set_rich_send(fail_rich_notify);
onion_notify_rich("notify.brand", "notify.boot.starting", "", "download",
"46");
TEST_ASSERT_EQ_INT(0, g_rich_fail_calls);
TEST_ASSERT_EQ_INT(1, g_plain_fallback_calls);
TEST_ASSERT_STREQ("OnionHEN is starting...", g_plain_fallback_message);
onion_notify_set_send(NULL);
onion_notify_set_rich_send(NULL);
return 0;
}
static int test_notify_try_rich_falls_back_on_empty_payload(void) {
g_plain_fallback_calls = 0;
g_rich_fail_calls = 0;
g_plain_fallback_message[0] = '\0';
onion_notify_set_send(capture_plain_fallback);
onion_notify_set_rich_send(fail_rich_notify);
onion_notify_try_rich(NULL, "notify.boot.welcome");
TEST_ASSERT_EQ_INT(0, g_rich_fail_calls);
TEST_ASSERT_EQ_INT(1, g_plain_fallback_calls);
TEST_ASSERT_STREQ("Welcome to OnionHEN", g_plain_fallback_message);
onion_notify_set_send(NULL);
onion_notify_set_rich_send(NULL);
return 0;
}
int test_platform_notify_suite(void) {
int failures = 0;
failures += onion_test_run("notify_format_prefix", test_notify_format_prefix);
failures += onion_test_run("notify_format_truncates", test_notify_format_truncates);
failures +=
onion_test_run("notify_format_no_watermark", test_notify_format_no_watermark);
failures += onion_test_run("notify_send_noop", test_notify_send_noop);
failures += onion_test_run("notify_debug_localized",
test_notify_debug_localized);
failures += onion_test_run("notify_language_resolution",
test_notify_language_resolution);
failures += onion_test_run("notify_format_localized",
test_notify_format_localized);
failures += onion_test_run("notify_payload_localized",
test_notify_payload_localized);
failures +=
onion_test_run("notify_rich_formats_payload", test_notify_rich_formats_payload);
failures += onion_test_run("notify_rich_localizes_both_text_fields",
test_notify_rich_localizes_both_text_fields);
failures += onion_test_run("notify_rich_falls_back_when_send_fails",
test_notify_rich_falls_back_when_send_fails);
failures += onion_test_run("notify_rich_falls_back_when_unregistered",
test_notify_rich_falls_back_when_unregistered);
failures += onion_test_run("notify_rich_skips_send_without_icon",
test_notify_rich_skips_send_without_icon);
failures += onion_test_run("notify_try_rich_falls_back_on_empty_payload",
test_notify_try_rich_falls_back_on_empty_payload);
return failures;
}