yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
message_test.cc
Go to the documentation of this file.
1#include <gtest/gtest.h>
2#include <filesystem>
3
6#include "testing.h"
7
8namespace yaze {
9namespace test {
10
11class MessageRomTest : public ::testing::Test {
12 protected:
13 void SetUp() override {
14 // Skip tests if ROM is not available
15 if (getenv("YAZE_SKIP_ROM_TESTS")) {
16 GTEST_SKIP() << "ROM tests disabled";
17 }
18
19 // Check if ROM file exists
20 std::string rom_path = "zelda3.sfc";
21 if (!std::filesystem::exists(rom_path)) {
22 GTEST_SKIP() << "Test ROM not found: " << rom_path;
23 }
24
25 EXPECT_OK(rom_.LoadFromFile(rom_path));
27 }
28 void TearDown() override {}
29
32 std::vector<editor::DictionaryEntry> dictionary_;
33};
34
35TEST_F(MessageRomTest, ParseSingleMessage_CommandParsing) {
36 std::vector<uint8_t> mock_data = {0x6A, 0x7F, 0x00};
37 int pos = 0;
38
39 auto result = editor::ParseSingleMessage(mock_data, &pos);
40 EXPECT_TRUE(result.ok());
41 const auto message_data = result.value();
42
43 // Verify that the command was recognized and parsed
44 EXPECT_EQ(message_data.ContentsParsed, "[L]");
45 EXPECT_EQ(pos, 2);
46}
47
48TEST_F(MessageRomTest, ParseSingleMessage_BasicAscii) {
49 // A, B, C, terminator
50 std::vector<uint8_t> mock_data = {0x00, 0x01, 0x02, 0x7F, 0x00};
51 int pos = 0;
52
53 auto result = editor::ParseSingleMessage(mock_data, &pos);
54 ASSERT_TRUE(result.ok());
55 const auto message_data = result.value();
56 EXPECT_EQ(pos, 4); // consumed all 4 bytes
57
58 std::vector<editor::MessageData> message_data_vector = {message_data};
59 auto parsed = editor::ParseMessageData(message_data_vector, dictionary_);
60
61 EXPECT_THAT(parsed, ::testing::ElementsAre("ABC"));
62}
63
64TEST_F(MessageRomTest, FindMatchingCharacter_Success) {
65 EXPECT_EQ(editor::FindMatchingCharacter('A'), 0x00);
66 EXPECT_EQ(editor::FindMatchingCharacter('Z'), 0x19);
67 EXPECT_EQ(editor::FindMatchingCharacter('a'), 0x1A);
68 EXPECT_EQ(editor::FindMatchingCharacter('z'), 0x33);
69}
70
71TEST_F(MessageRomTest, FindMatchingCharacter_Failure) {
72 EXPECT_EQ(editor::FindMatchingCharacter('@'), 0xFF);
73 EXPECT_EQ(editor::FindMatchingCharacter('#'), 0xFF);
74}
75
76TEST_F(MessageRomTest, FindDictionaryEntry_Success) {
77 EXPECT_EQ(editor::FindDictionaryEntry(0x88), 0x00);
78 EXPECT_EQ(editor::FindDictionaryEntry(0x90), 0x08);
79}
80
81TEST_F(MessageRomTest, FindDictionaryEntry_Failure) {
82 EXPECT_EQ(editor::FindDictionaryEntry(0x00), -1);
83 EXPECT_EQ(editor::FindDictionaryEntry(0xFF), -1);
84}
85
86TEST_F(MessageRomTest, ParseMessageToData_Basic) {
87 std::string input = "[L][C:01]ABC";
88 auto result = editor::ParseMessageToData(input);
89 std::vector<uint8_t> expected = {0x6A, 0x77, 0x01, 0x00, 0x01, 0x02};
90 EXPECT_EQ(result, expected);
91}
92
93TEST_F(MessageRomTest, ReplaceAllDictionaryWords_Success) {
94 std::vector<editor::DictionaryEntry> mock_dict = {
95 editor::DictionaryEntry(0x00, "test"),
96 editor::DictionaryEntry(0x01, "message")};
97 std::string input = "This is a test message.";
98 auto result = editor::ReplaceAllDictionaryWords(input, mock_dict);
99 EXPECT_EQ(result, "This is a [D:00] [D:01].");
100}
101
102TEST_F(MessageRomTest, ReplaceAllDictionaryWords_NoMatch) {
103 std::vector<editor::DictionaryEntry> mock_dict = {
104 editor::DictionaryEntry(0x00, "hello")};
105 std::string input = "No matching words.";
106 auto result = editor::ReplaceAllDictionaryWords(input, mock_dict);
107 EXPECT_EQ(result, "No matching words.");
108}
109
110TEST_F(MessageRomTest, ParseTextDataByte_Success) {
111 EXPECT_EQ(editor::ParseTextDataByte(0x00), "A");
112 EXPECT_EQ(editor::ParseTextDataByte(0x74), "[1]");
113 EXPECT_EQ(editor::ParseTextDataByte(0x88), "[D:00]");
114}
115
116TEST_F(MessageRomTest, ParseTextDataByte_Failure) {
117 EXPECT_EQ(editor::ParseTextDataByte(0xFF), "");
118}
119
120TEST_F(MessageRomTest, ParseSingleMessage_SpecialCharacters) {
121 std::vector<uint8_t> mock_data = {0x4D, 0x4E, 0x4F, 0x50, 0x7F};
122 int pos = 0;
123
124 auto result = editor::ParseSingleMessage(mock_data, &pos);
125 ASSERT_TRUE(result.ok());
126 const auto message_data = result.value();
127
128 EXPECT_EQ(message_data.ContentsParsed, "[UP][DOWN][LEFT][RIGHT]");
129 EXPECT_EQ(pos, 5);
130}
131
132TEST_F(MessageRomTest, ParseSingleMessage_DictionaryReference) {
133 std::vector<uint8_t> mock_data = {0x88, 0x89, 0x7F};
134 int pos = 0;
135
136 auto result = editor::ParseSingleMessage(mock_data, &pos);
137 ASSERT_TRUE(result.ok());
138 const auto message_data = result.value();
139
140 EXPECT_EQ(message_data.ContentsParsed, "[D:00][D:01]");
141 EXPECT_EQ(pos, 3);
142}
143
144TEST_F(MessageRomTest, ParseSingleMessage_InvalidTerminator) {
145 std::vector<uint8_t> mock_data = {0x00, 0x01, 0x02}; // No terminator
146 int pos = 0;
147
148 auto result = editor::ParseSingleMessage(mock_data, &pos);
149 EXPECT_FALSE(result.ok());
150}
151
152TEST_F(MessageRomTest, ParseSingleMessage_EmptyData) {
153 std::vector<uint8_t> mock_data = {0x7F};
154 int pos = 0;
155
156 auto result = editor::ParseSingleMessage(mock_data, &pos);
157 ASSERT_TRUE(result.ok());
158 const auto message_data = result.value();
159
160 EXPECT_EQ(message_data.ContentsParsed, "");
161 EXPECT_EQ(pos, 1);
162}
163
164TEST_F(MessageRomTest, OptimizeMessageForDictionary_Basic) {
165 std::vector<editor::DictionaryEntry> mock_dict = {
166 editor::DictionaryEntry(0x00, "Link"),
167 editor::DictionaryEntry(0x01, "Zelda")};
168 std::string input = "[L] rescued Zelda from danger.";
169
170 editor::MessageData message_data;
171 std::string optimized =
172 message_data.OptimizeMessageForDictionary(input, mock_dict);
173
174 EXPECT_EQ(optimized, "[L] rescued [D:01] from danger.");
175}
176
177TEST_F(MessageRomTest, SetMessage_Success) {
178 std::vector<editor::DictionaryEntry> mock_dict = {
179 editor::DictionaryEntry(0x00, "item")};
180 editor::MessageData message_data;
181 std::string input = "You got an item!";
182
183 message_data.SetMessage(input, mock_dict);
184
185 EXPECT_EQ(message_data.RawString, "You got an item!");
186 EXPECT_EQ(message_data.ContentsParsed, "You got an [D:00]!");
187}
188
189TEST_F(MessageRomTest, FindMatchingElement_CommandWithArgument) {
190 std::string input = "[W:02]";
192
193 EXPECT_TRUE(result.Active);
194 EXPECT_EQ(result.Parent.Token, "W");
195 EXPECT_EQ(result.Value, 0x02);
196}
197
198TEST_F(MessageRomTest, FindMatchingElement_InvalidCommand) {
199 std::string input = "[INVALID]";
201
202 EXPECT_FALSE(result.Active);
203}
204
205TEST_F(MessageRomTest, BuildDictionaryEntries_CorrectSize) {
206 auto result = editor::BuildDictionaryEntries(&rom_);
207 EXPECT_EQ(result.size(), editor::kNumDictionaryEntries);
208 EXPECT_FALSE(result.empty());
209}
210
211TEST_F(MessageRomTest, ParseMessageData_CommandWithArgument_NoExtraCharacters) {
212 // This test specifically checks for the bug where command arguments
213 // were being incorrectly parsed as characters (e.g., capital 'A' after [W])
214 // The bug was caused by using a range-based for loop while also tracking position
215
216 // Message: [W:01]ABC
217 // Bytes: 0x6B (W command), 0x01 (argument), 0x00 (A), 0x01 (B), 0x02 (C)
218 std::vector<uint8_t> data = {0x6B, 0x01, 0x00, 0x01, 0x02};
219
220 editor::MessageData message;
221 message.ID = 0;
222 message.Address = 0;
223 message.Data = data;
224
225 std::vector<editor::MessageData> message_data_vector = {message};
226 auto parsed = editor::ParseMessageData(message_data_vector, dictionary_);
227
228 // Should be "[W:01]ABC" NOT "[W:01]BABC" or "[W:01]AABC"
229 EXPECT_EQ(parsed[0], "[W:01]ABC");
230
231 // The 'B' should not appear twice or be skipped
232 EXPECT_EQ(parsed[0].find("BABC"), std::string::npos);
233 EXPECT_EQ(parsed[0].find("AABC"), std::string::npos);
234}
235
236TEST_F(MessageRomTest, ParseMessageData_MultipleCommandsWithArguments) {
237 // Test multiple commands with arguments in sequence
238 // [W:01][C:02]AB
239 std::vector<uint8_t> data = {
240 0x6B, 0x01, // [W:01] - Window border command with arg
241 0x77, 0x02, // [C:02] - Color command with arg
242 0x00, 0x01 // AB - Regular characters
243 };
244
245 editor::MessageData message;
246 message.ID = 0;
247 message.Data = data;
248
249 std::vector<editor::MessageData> message_data_vector = {message};
250 auto parsed = editor::ParseMessageData(message_data_vector, dictionary_);
251
252 EXPECT_EQ(parsed[0], "[W:01][C:02]AB");
253
254 // Make sure argument bytes (0x01, 0x02) weren't parsed as characters
255 EXPECT_EQ(parsed[0].find("BAB"), std::string::npos);
256 EXPECT_EQ(parsed[0].find("CAB"), std::string::npos);
257}
258
259TEST_F(MessageRomTest, ParseMessageData_CommandWithoutArgument) {
260 // Test command without argument followed by text
261 // [K]ABC - Wait for key command (no arg) followed by ABC
262 std::vector<uint8_t> data = {
263 0x7E, // [K] - Wait for key (no argument)
264 0x00, 0x01, 0x02 // ABC
265 };
266
267 editor::MessageData message;
268 message.ID = 0;
269 message.Data = data;
270
271 std::vector<editor::MessageData> message_data_vector = {message};
272 auto parsed = editor::ParseMessageData(message_data_vector, dictionary_);
273
274 EXPECT_EQ(parsed[0], "[K]ABC");
275}
276
277TEST_F(MessageRomTest, ParseMessageData_MixedCommands) {
278 // Test mix of commands with and without arguments
279 // [W:01]A[K]B[C:02]C
280 std::vector<uint8_t> data = {
281 0x6B, 0x01, // [W:01] - with arg
282 0x00, // A
283 0x7E, // [K] - no arg
284 0x01, // B
285 0x77, 0x02, // [C:02] - with arg
286 0x02 // C
287 };
288
289 editor::MessageData message;
290 message.ID = 0;
291 message.Data = data;
292
293 std::vector<editor::MessageData> message_data_vector = {message};
294 auto parsed = editor::ParseMessageData(message_data_vector, dictionary_);
295
296 EXPECT_EQ(parsed[0], "[W:01]A[K]B[C:02]C");
297}
298
299} // namespace test
300} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:71
absl::Status LoadFromFile(const std::string &filename, bool z3_load=true)
Definition rom.cc:289
editor::MessageEditor message_editor_
std::vector< editor::DictionaryEntry > dictionary_
uint8_t FindMatchingCharacter(char value)
std::string ParseTextDataByte(uint8_t value)
std::string ReplaceAllDictionaryWords(std::string str, const std::vector< DictionaryEntry > &dictionary)
constexpr int kNumDictionaryEntries
absl::StatusOr< MessageData > ParseSingleMessage(const std::vector< uint8_t > &rom_data, int *current_pos)
std::vector< std::string > ParseMessageData(std::vector< MessageData > &message_data, const std::vector< DictionaryEntry > &dictionary_entries)
std::vector< DictionaryEntry > BuildDictionaryEntries(Rom *rom)
std::vector< uint8_t > ParseMessageToData(std::string str)
ParsedElement FindMatchingElement(const std::string &str)
int8_t FindDictionaryEntry(uint8_t value)
TEST_F(DungeonObjectRenderingE2ETests, RunAllTests)
Main namespace for the application.
std::vector< uint8_t > Data
std::string OptimizeMessageForDictionary(std::string_view message_string, const std::vector< DictionaryEntry > &dictionary)
void SetMessage(const std::string &message, const std::vector< DictionaryEntry > &dictionary)
#define EXPECT_OK(expr)
Definition testing.h:10