1#include <base/system.h>
2
3#include <gtest/gtest.h>
4
5bool is_letter(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
6
7bool IsValidEditorTooltip(const char *pTooltip, char *pErrorMsg, int ErrorMsgSize)
8{
9 pErrorMsg[0] = '\0';
10 char aHotkey[512];
11 aHotkey[0] = '\0';
12
13 if(pTooltip[0] == '[')
14 {
15 str_copy(dst&: aHotkey, src: pTooltip + 1);
16 const char *pHotkeyEnd = str_find(haystack: aHotkey, needle: "]");
17 if(!pHotkeyEnd)
18 {
19 str_copy(dst: pErrorMsg, src: "tooltip missing closing square bracket", dst_size: ErrorMsgSize);
20 return false;
21 }
22 aHotkey[pHotkeyEnd - aHotkey] = '\0';
23
24 for(int i = 0; aHotkey[i]; i++)
25 {
26 bool ExpectLowerCase = true;
27 if(i == 0)
28 {
29 ExpectLowerCase = false;
30 }
31 else if(!is_letter(c: aHotkey[i - 1]))
32 {
33 // the first character of a word should be uppercase
34 ExpectLowerCase = false;
35 }
36
37 bool IsLower = aHotkey[i] >= 'a' && aHotkey[i] <= 'z';
38 bool IsUpper = aHotkey[i] >= 'A' && aHotkey[i] <= 'Z';
39
40 if(ExpectLowerCase && IsUpper)
41 {
42 str_format(buffer: pErrorMsg, buffer_size: ErrorMsgSize, format: "expected character '%c' at index %d to be lower case", aHotkey[i], i + 1);
43 return false;
44 }
45 if(!ExpectLowerCase && IsLower)
46 {
47 str_format(buffer: pErrorMsg, buffer_size: ErrorMsgSize, format: "expected character '%c' at index %d to be upper case", aHotkey[i], i + 1);
48 return false;
49 }
50 }
51 }
52
53 const char *pParenthesis = str_find(haystack: pTooltip, needle: "(");
54 if(pParenthesis)
55 {
56 const char *pHotkey = str_find_nocase(haystack: pParenthesis, needle: "ctrl");
57 if(!pHotkey)
58 pHotkey = str_find_nocase(haystack: pParenthesis, needle: "shift");
59 if(!pHotkey)
60 pHotkey = str_find_nocase(haystack: pParenthesis, needle: "home");
61
62 if(pHotkey)
63 {
64 int Offset = pHotkey - pTooltip;
65 str_format(buffer: pErrorMsg, buffer_size: ErrorMsgSize, format: "found hotkey at offset %d. Hotkeys must be defined at the start.", Offset);
66 return false;
67 }
68 }
69
70 if(!str_endswith(str: pTooltip, suffix: "."))
71 {
72 str_copy(dst: pErrorMsg, src: "tooltip has to end with a dot", dst_size: ErrorMsgSize);
73 return false;
74 }
75 return true;
76}
77
78void AssertTooltip(const char *pTooltip)
79{
80 char aError[512];
81 bool IsValid = IsValidEditorTooltip(pTooltip, pErrorMsg: aError, ErrorMsgSize: sizeof(aError));
82 if(!IsValid)
83 {
84 dbg_msg(sys: "test", fmt: "Invalid tooltip: %s", pTooltip);
85 dbg_msg(sys: "test", fmt: "ERROR: %s", aError);
86 }
87 EXPECT_TRUE(IsValid);
88}
89
90TEST(Editor, QuickActionNames)
91{
92 char aError[512];
93 EXPECT_TRUE(IsValidEditorTooltip("hello world.", aError, sizeof(aError)));
94 EXPECT_TRUE(IsValidEditorTooltip("[Ctrl+H] hello world.", aError, sizeof(aError)));
95 EXPECT_FALSE(IsValidEditorTooltip("[Ctrl+H hello world.", aError, sizeof(aError)));
96 EXPECT_FALSE(IsValidEditorTooltip("[ctrl+h] hello world.", aError, sizeof(aError)));
97 EXPECT_FALSE(IsValidEditorTooltip("hello world", aError, sizeof(aError)));
98 EXPECT_FALSE(IsValidEditorTooltip("hello world (Ctrl+H).", aError, sizeof(aError)));
99
100#define REGISTER_QUICK_ACTION(name, text, callback, disabled, active, button_color, description) AssertTooltip(description);
101#include <game/editor/quick_actions.h>
102#undef REGISTER_QUICK_ACTION
103}
104