OXYGENHUB
◆ main openmsx / src / unittest / SymbolManager_test.cc
13.5 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "catch.hpp"
2
#include "SymbolManager.hh"
3
4
using namespace openmsx;
5
6
TEST_CASE("SymbolManager: isHexDigit")
7
{
8
	CHECK(SymbolManager::isHexDigit('0') == 0);
9
	CHECK(SymbolManager::isHexDigit('4') == 4);
10
	CHECK(SymbolManager::isHexDigit('9') == 9);
11
	CHECK(SymbolManager::isHexDigit('a') == 10);
12
	CHECK(SymbolManager::isHexDigit('e') == 14);
13
	CHECK(SymbolManager::isHexDigit('f') == 15);
14
	CHECK(SymbolManager::isHexDigit('A') == 10);
15
	CHECK(SymbolManager::isHexDigit('C') == 12);
16
	CHECK(SymbolManager::isHexDigit('F') == 15);
17
	CHECK(SymbolManager::isHexDigit('-') == std::nullopt);
18
	CHECK(SymbolManager::isHexDigit('@') == std::nullopt);
19
	CHECK(SymbolManager::isHexDigit('G') == std::nullopt);
20
	CHECK(SymbolManager::isHexDigit('g') == std::nullopt);
21
}
22
23
TEST_CASE("SymbolManager: is4DigitHex")
24
{
25
	CHECK(SymbolManager::is4DigitHex("0000") == 0x0000);
26
	CHECK(SymbolManager::is4DigitHex("12ab") == 0x12ab);
27
	CHECK(SymbolManager::is4DigitHex("F000") == 0xf000);
28
	CHECK(SymbolManager::is4DigitHex("fFfF") == 0xffff);
29
	CHECK(SymbolManager::is4DigitHex("") == std::nullopt);
30
	CHECK(SymbolManager::is4DigitHex("123") == std::nullopt);
31
	CHECK(SymbolManager::is4DigitHex("12345") == std::nullopt);
32
	CHECK(SymbolManager::is4DigitHex("abg1") == std::nullopt);
33
}
34
35
TEST_CASE("SymbolManager: parseValue")
36
{
37
	SECTION("ok") {
38
		// hex
39
		CHECK(SymbolManager::parseValue<uint16_t>("0xa1") == 0xa1);
40
		CHECK(SymbolManager::parseValue<uint32_t>("0xa1") == 0xa1);
41
		CHECK(SymbolManager::parseValue<uint16_t>("0x1234") == 0x1234);
42
		CHECK(SymbolManager::parseValue<uint32_t>("0x1234") == 0x1234);
43
		CHECK(SymbolManager::parseValue<uint16_t>("0XABCD") == 0xabcd);
44
		CHECK(SymbolManager::parseValue<uint32_t>("0XABCD") == 0xabcd);
45
		CHECK(SymbolManager::parseValue<uint16_t>("0x00002345") == 0x2345);
46
		CHECK(SymbolManager::parseValue<uint32_t>("0x00002345") == 0x2345);
47
		CHECK(SymbolManager::parseValue<uint16_t>("$fedc") == 0xfedc);
48
		CHECK(SymbolManager::parseValue<uint32_t>("$fedc") == 0xfedc);
49
		CHECK(SymbolManager::parseValue<uint16_t>("#11aA") == 0x11aa);
50
		CHECK(SymbolManager::parseValue<uint32_t>("#11aA") == 0x11aa);
51
		CHECK(SymbolManager::parseValue<uint16_t>("bbffh") == 0xbbff);
52
		CHECK(SymbolManager::parseValue<uint32_t>("bbffh") == 0xbbff);
53
		CHECK(SymbolManager::parseValue<uint16_t>("3210H") == 0x3210);
54
		CHECK(SymbolManager::parseValue<uint32_t>("3210H") == 0x3210);
55
		CHECK(SymbolManager::parseValue<uint16_t>("01234h") == 0x1234);
56
		CHECK(SymbolManager::parseValue<uint32_t>("01234h") == 0x1234);
57
		// dec
58
		CHECK(SymbolManager::parseValue<uint16_t>("123") == 0x007b);
59
		CHECK(SymbolManager::parseValue<uint32_t>("123") == 0x007b);
60
		CHECK(SymbolManager::parseValue<uint16_t>("65535") == 0xffff);
61
		CHECK(SymbolManager::parseValue<uint32_t>("65535") == 0xffff);
62
		CHECK(SymbolManager::parseValue<uint16_t>("0020") == 0x0014); // NOT interpreted as octal
63
		CHECK(SymbolManager::parseValue<uint32_t>("0020") == 0x0014); // NOT interpreted as octal
64
		// bin
65
		CHECK(SymbolManager::parseValue<uint16_t>("%1100") == 0x000c);
66
		CHECK(SymbolManager::parseValue<uint32_t>("%1100") == 0x000c);
67
		CHECK(SymbolManager::parseValue<uint16_t>("0b000100100011") == 0x0123);
68
		CHECK(SymbolManager::parseValue<uint32_t>("0b000100100011") == 0x0123);
69
		CHECK(SymbolManager::parseValue<uint16_t>("0B1111000001011000") == 0xf058);
70
		CHECK(SymbolManager::parseValue<uint32_t>("0B1111000001011000") == 0xf058);
71
	}
72
	SECTION("error") {
73
		// wrong format
74
		CHECK(SymbolManager::parseValue<uint16_t>("0xFEDX") == std::nullopt);
75
		CHECK(SymbolManager::parseValue<uint32_t>("0xFEDGFF") == std::nullopt);
76
		CHECK(SymbolManager::parseValue<uint16_t>("1234a") == std::nullopt);
77
		CHECK(SymbolManager::parseValue<uint32_t>("1234567a") == std::nullopt);
78
		CHECK(SymbolManager::parseValue<uint16_t>("-3") == std::nullopt);
79
		CHECK(SymbolManager::parseValue<uint32_t>("-100000") == std::nullopt);
80
		CHECK(SymbolManager::parseValue<uint16_t>("0b00112110") == std::nullopt);
81
		CHECK(SymbolManager::parseValue<uint32_t>("0b00110113") == std::nullopt);
82
		// overflow
83
		CHECK(SymbolManager::parseValue<uint16_t>("0x10000") == std::nullopt);
84
		CHECK(SymbolManager::parseValue<uint16_t>("65536") == std::nullopt);
85
		CHECK(SymbolManager::parseValue<uint16_t>("%11110000111100001") == std::nullopt);
86
		CHECK(SymbolManager::parseValue<uint32_t>("0x100000000") == std::nullopt);
87
		CHECK(SymbolManager::parseValue<uint32_t>("4294967296") == std::nullopt);
88
		CHECK(SymbolManager::parseValue<uint32_t>("%111100001111000011110000111100001") == std::nullopt);
89
	}
90
}
91
92
TEST_CASE("SymbolManager: checkLabel")
93
{
94
	CHECK(SymbolManager::checkLabel("foo", 123) == Symbol("foo", 123, {}, {}));
95
	CHECK(SymbolManager::checkLabel("bar:", 234) == Symbol("bar", 234, {}, {}));
96
	CHECK(SymbolManager::checkLabel("", 345) == std::nullopt);
97
	CHECK(SymbolManager::checkLabel(":", 456) == std::nullopt);
98
}
99
100
TEST_CASE("SymbolManager: checkLabelAndValue")
101
{
102
	CHECK(SymbolManager::checkLabelAndValue("foo", "123") == Symbol("foo", 123, {}, {}));
103
	CHECK(SymbolManager::checkLabelAndValue("", "123") == std::nullopt);
104
	CHECK(SymbolManager::checkLabelAndValue("foo", "bla") == std::nullopt);
105
}
106
107
TEST_CASE("SymbolManager: checkLabelSegmentAndValue")
108
{
109
	CHECK(SymbolManager::checkLabelSegmentAndValue("foo", "123") == Symbol("foo", 123, {}, {}));
110
	CHECK(SymbolManager::checkLabelSegmentAndValue("", "123") == std::nullopt);
111
	CHECK(SymbolManager::checkLabelSegmentAndValue("", "123") == std::nullopt);
112
	CHECK(SymbolManager::checkLabelSegmentAndValue("foo", "0x123456") == Symbol("foo", 0x3456, {}, 0x12));
113
	CHECK(SymbolManager::checkLabelSegmentAndValue("foo", "2311527") == Symbol("foo", 0x4567, {}, 0x23));
114
	CHECK(SymbolManager::checkLabelSegmentAndValue("foo", "0x123456") == Symbol("foo", 0x3456, {}, 0x12));
115
}
116
117
TEST_CASE("SymbolManager: detectType")
118
{
119
	SECTION("on extension") {
120
		std::string_view buffer = "content doesn't matter";
121
		CHECK(SymbolManager::detectType("symbols.noi", buffer) == SymbolFile::Type::NOICE);
122
		CHECK(SymbolManager::detectType("symbols.NOI", buffer) == SymbolFile::Type::NOICE);
123
		CHECK(SymbolManager::detectType("symbols.symbol", buffer) == SymbolFile::Type::GENERIC); // pasmo
124
		CHECK(SymbolManager::detectType("symbols.publics", buffer) == SymbolFile::Type::GENERIC); // pasmo
125
		CHECK(SymbolManager::detectType("symbols.sys", buffer) == SymbolFile::Type::GENERIC); // pasmo
126
		CHECK(SymbolManager::detectType("symbols.unknown", buffer) == SymbolFile::Type::GENERIC); // unknown extension
127
	}
128
	SECTION(".map extension") {
129
		CHECK(SymbolManager::detectType("symbols.Map", "HI-TECH Software ZC Compiler V7.80PL2") == SymbolFile::Type::LINKMAP);
130
		CHECK(SymbolManager::detectType("symbols.Map", "CHGMOD                          = $005F ; const, local, , sample025, , sample025.asm:2") == SymbolFile::Type::GENERIC);
131
	}
132
	SECTION(".sym extension") {
133
		CHECK(SymbolManager::detectType("myfile.sym", "; Symbol table from myfile.asm") == SymbolFile::Type::ASMSX);
134
		CHECK(SymbolManager::detectType("myfile.sym", "bla: %equ 123") == SymbolFile::Type::GENERIC);
135
		CHECK(SymbolManager::detectType("myfile.sym", "bla: equ 123") == SymbolFile::Type::GENERIC);
136
		CHECK(SymbolManager::detectType("myfile.sym", "bla equ #123") == SymbolFile::Type::GENERIC);
137
		CHECK(SymbolManager::detectType("myfile.sym", "Sections:") == SymbolFile::Type::VASM);
138
		CHECK(SymbolManager::detectType("myfile.sym", "; this file was created with wlalink") == SymbolFile::Type::WLALINK_NOGMB);
139
		CHECK(SymbolManager::detectType("myfile.sym", "anything else") == SymbolFile::Type::HTC);
140
	}
141
}
142
143
TEST_CASE("SymbolManager: loadLines")
144
{
145
	auto dummyParser = [](std::span<std::string_view> tokens) -> std::optional<Symbol> {
146
		if (tokens.size() == 1) return Symbol{std::string(tokens[0]), 123, {}, {}};
147
		return {};
148
	};
149
150
	SECTION("empty file") {
151
		std::string_view buffer;
152
		auto file = SymbolManager::loadLines("file.sym", buffer, SymbolFile::Type::GENERIC, dummyParser);
153
		CHECK(file.filename == "file.sym");
154
		CHECK(file.type == SymbolFile::Type::GENERIC);
155
		CHECK(file.symbols.empty());
156
	}
157
	SECTION("comment + symbol") {
158
		std::string_view buffer =
159
			"; This is a comment\n"
160
			"bla\n" // ok for dummy parser
161
			"foo bla\n" // ignored because 2 tokens
162
			"bar ; comment on same line\n"; // only 1 token after removing comment
163
		auto file = SymbolManager::loadLines("file2.sym", buffer, SymbolFile::Type::NOICE, dummyParser);
164
		CHECK(file.filename == "file2.sym");
165
		CHECK(file.type == SymbolFile::Type::NOICE);
166
		REQUIRE(file.symbols.size() == 2);
167
		CHECK(file.symbols[0].name == "bla");
168
		CHECK(file.symbols[0].value == 123);
169
		CHECK(file.symbols[1].name == "bar");
170
		CHECK(file.symbols[1].value == 123);
171
	}
172
}
173
174
TEST_CASE("SymbolManager: loadGeneric")
175
{
176
	std::string_view buffer =
177
		"foo: equ 1\n"
178
		"bar %equ 2\n"
179
		"qux = 3 ; comment\n"
180
		"; only comment\n"
181
		"error equ\n"
182
		"error equ 123 extra stuff\n";
183
	auto file = SymbolManager::loadGeneric("myfile.sym", buffer);
184
	CHECK(file.filename == "myfile.sym");
185
	CHECK(file.type == SymbolFile::Type::GENERIC);
186
	REQUIRE(file.symbols.size() == 3);
187
	CHECK(file.symbols[0].name == "foo");
188
	CHECK(file.symbols[0].value == 1);
189
	CHECK(file.symbols[1].name == "bar");
190
	CHECK(file.symbols[1].value == 2);
191
	CHECK(file.symbols[2].name == "qux");
192
	CHECK(file.symbols[2].value == 3);
193
}
194
195
TEST_CASE("SymbolManager: loadNoICE without segments")
196
{
197
	std::string_view buffer =
198
		"def foo 1\n"
199
		"def bar 234h ; comment\n"
200
		"error def 123\n"
201
		"def error 99 extra stuff\n";
202
	auto file = SymbolManager::loadNoICE("noice.sym", buffer);
203
	CHECK(file.filename == "noice.sym");
204
	CHECK(file.type == SymbolFile::Type::NOICE);
205
	REQUIRE(file.symbols.size() == 2);
206
	CHECK(file.symbols[0].name == "foo");
207
	CHECK(file.symbols[0].value == 1);
208
	CHECK(file.symbols[0].segment == std::nullopt);
209
	CHECK(file.symbols[1].name == "bar");
210
	CHECK(file.symbols[1].value == 0x234);
211
	CHECK(file.symbols[1].segment == std::nullopt);
212
}
213
214
TEST_CASE("SymbolManager: loadNoICE with segments")
215
{
216
	std::string_view buffer =
217
		"def foo 1\n"
218
		"def bar 234h ; comment\n"
219
		"error def 123\n"
220
		"def error 99 extra stuff\n"
221
		"def baz 123456h ; comment\n";
222
	auto file = SymbolManager::loadNoICE("noice.sym", buffer);
223
	CHECK(file.filename == "noice.sym");
224
	CHECK(file.type == SymbolFile::Type::NOICE);
225
	REQUIRE(file.symbols.size() == 3);
226
	CHECK(file.symbols[0].name == "foo");
227
	CHECK(file.symbols[0].value == 1);
228
	CHECK(file.symbols[0].segment == 0);
229
	CHECK(file.symbols[1].name == "bar");
230
	CHECK(file.symbols[1].value == 0x234);
231
	CHECK(file.symbols[1].segment == 0);
232
	CHECK(file.symbols[2].name == "baz");
233
	CHECK(file.symbols[2].value == 0x3456);
234
	CHECK(file.symbols[2].segment == 0x12);
235
}
236
237
TEST_CASE("SymbolManager: loadHTC")
238
{
239
	// TODO verify with an actual HTC file
240
	std::string_view buffer =
241
		"foo 1234 bla\n"
242
		"error 1234\n";
243
	auto file = SymbolManager::loadHTC("htc.sym", buffer);
244
	CHECK(file.filename == "htc.sym");
245
	CHECK(file.type == SymbolFile::Type::HTC);
246
	REQUIRE(file.symbols.size() == 1);
247
	CHECK(file.symbols[0].name == "foo");
248
	CHECK(file.symbols[0].value == 0x1234);
249
}
250
251
TEST_CASE("SymbolManager: loadVASM")
252
{
253
	std::string_view buffer =
254
		"12AB ignore\n"
255
		"Symbols by value:\n"
256
		"12AB label\n"
257
		"bla foo\n";
258
	auto file = SymbolManager::loadVASM("vasm.sym", buffer);
259
	CHECK(file.filename == "vasm.sym");
260
	CHECK(file.type == SymbolFile::Type::VASM);
261
	REQUIRE(file.symbols.size() == 1);
262
	CHECK(file.symbols[0].name == "label");
263
	CHECK(file.symbols[0].value == 0x12ab);
264
}
265
266
TEST_CASE("SymbolManager: loadASMSX")
267
{
268
	std::string_view buffer =
269
		"1234h ignore1\n"
270
		"12h:3456h ignore2\n"
271
		"; global and local\n"
272
		"1234h l1\n"
273
		"12h:abcdh l2\n";
274
	auto file = SymbolManager::loadASMSX("asmsx.sym", buffer);
275
	CHECK(file.filename == "asmsx.sym");
276
	CHECK(file.type == SymbolFile::Type::ASMSX);
277
	REQUIRE(file.symbols.size() == 2);
278
	CHECK(file.symbols[0].name == "l1");
279
	CHECK(file.symbols[0].value == 0x1234);
280
	CHECK(file.symbols[1].name == "l2");
281
	CHECK(file.symbols[1].value == 0xabcd);
282
}
283
284
TEST_CASE("SymbolManager: loadNoGmb")
285
{
286
	std::string_view buffer =
287
		"; this file was created with wlalink\n"
288
		"; no$gmb symbolic information for \"test.rom\".\n"
289
		"00:4010 main\n"
290
		"00:402d main@main_loop\n"
291
		"00:404f Game_Initialize\n"
292
		"00:404e Game_Update\n";
293
	auto file = SymbolManager::loadNoGmb("myfile.sym", buffer);
294
	CHECK(file.filename == "myfile.sym");
295
	CHECK(file.type == SymbolFile::Type::WLALINK_NOGMB);
296
	REQUIRE(file.symbols.size() == 4);
297
	CHECK(file.symbols[0].name == "main");
298
	CHECK(file.symbols[0].value == 0x4010);
299
	CHECK(file.symbols[1].name == "main@main_loop");
300
	CHECK(file.symbols[1].value == 0x402d);
301
	CHECK(file.symbols[2].name == "Game_Initialize");
302
	CHECK(file.symbols[2].value == 0x404f);
303
	CHECK(file.symbols[3].name == "Game_Update");
304
	CHECK(file.symbols[3].value == 0x404e);
305
}
306
307
TEST_CASE("SymbolManager: loadLinkMap")
308
{
309
	std::string_view buffer =
310
		"ignore1             text    2CE7  ignore2                   0AEE\n"
311
		"ignore3                     2E58  ignore3           text    2E4C\n"
312
		"         Symbol Table\n"
313
		"asllmod            text    2CE7  asllsub                    0AEE\n"
314
		"cret                       2E58  csv                text    2E4C\n"
315
		"single             text    9876\n"
316
		"last                       8765\n";
317
	auto file = SymbolManager::loadLinkMap("link.map", buffer);
318
	CHECK(file.filename == "link.map");
319
	CHECK(file.type == SymbolFile::Type::LINKMAP);
320
	REQUIRE(file.symbols.size() == 6);
321
	CHECK(file.symbols[0].name == "asllmod");
322
	CHECK(file.symbols[0].value == 0x2ce7);
323
	CHECK(file.symbols[1].name == "asllsub");
324
	CHECK(file.symbols[1].value == 0x0aee);
325
	CHECK(file.symbols[2].name == "cret");
326
	CHECK(file.symbols[2].value == 0x2e58);
327
	CHECK(file.symbols[3].name == "csv");
328
	CHECK(file.symbols[3].value == 0x2e4c);
329
	CHECK(file.symbols[4].name == "single");
330
	CHECK(file.symbols[4].value == 0x9876);
331
	CHECK(file.symbols[5].name == "last");
332
	CHECK(file.symbols[5].value == 0x8765);
333
}