openMSX
SymbolManager_test.cc
Go to the documentation of this file.
1#include "catch.hpp"
2#include "SymbolManager.hh"
3
4using namespace openmsx;
5
6TEST_CASE("SymbolManager: isHexDigit")
7{
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
23TEST_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
35TEST_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
92TEST_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
100TEST_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
107TEST_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
117TEST_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.Map", buffer) == SymbolFile::Type::LINKMAP);
124 CHECK(SymbolManager::detectType("symbols.symbol", buffer) == SymbolFile::Type::GENERIC); // pasmo
125 CHECK(SymbolManager::detectType("symbols.publics", buffer) == SymbolFile::Type::GENERIC); // pasmo
126 CHECK(SymbolManager::detectType("symbols.sys", buffer) == SymbolFile::Type::GENERIC); // pasmo
127 CHECK(SymbolManager::detectType("symbols.unknown", buffer) == SymbolFile::Type::GENERIC); // unknown extension
128 }
129 SECTION(".sym extension") {
130 CHECK(SymbolManager::detectType("myfile.sym", "; Symbol table from myfile.asm") == SymbolFile::Type::ASMSX);
131 CHECK(SymbolManager::detectType("myfile.sym", "bla: %equ 123") == SymbolFile::Type::GENERIC);
132 CHECK(SymbolManager::detectType("myfile.sym", "bla: equ 123") == SymbolFile::Type::GENERIC);
133 CHECK(SymbolManager::detectType("myfile.sym", "bla equ #123") == SymbolFile::Type::GENERIC);
134 CHECK(SymbolManager::detectType("myfile.sym", "Sections:") == SymbolFile::Type::VASM);
135 CHECK(SymbolManager::detectType("myfile.sym", "anything else") == SymbolFile::Type::HTC);
136 }
137}
138
139TEST_CASE("SymbolManager: loadLines")
140{
141 auto dummyParser = [](std::span<std::string_view> tokens) -> std::optional<Symbol> {
142 if (tokens.size() == 1) return Symbol{std::string(tokens[0]), 123, {}, {}};
143 return {};
144 };
145
146 SECTION("empty file") {
147 std::string_view buffer;
148 auto file = SymbolManager::loadLines("file.sym", buffer, SymbolFile::Type::GENERIC, dummyParser);
149 CHECK(file.filename == "file.sym");
150 CHECK(file.type == SymbolFile::Type::GENERIC);
151 CHECK(file.symbols.empty());
152 }
153 SECTION("comment + symbol") {
154 std::string_view buffer =
155 "; This is a comment\n"
156 "bla\n" // ok for dummy parser
157 "foo bla\n" // ignored because 2 tokens
158 "bar ; comment on same line\n"; // only 1 token after removing comment
159 auto file = SymbolManager::loadLines("file2.sym", buffer, SymbolFile::Type::NOICE, dummyParser);
160 CHECK(file.filename == "file2.sym");
161 CHECK(file.type == SymbolFile::Type::NOICE);
162 REQUIRE(file.symbols.size() == 2);
163 CHECK(file.symbols[0].name == "bla");
164 CHECK(file.symbols[0].value == 123);
165 CHECK(file.symbols[1].name == "bar");
166 CHECK(file.symbols[1].value == 123);
167 }
168}
169
170TEST_CASE("SymbolManager: loadGeneric")
171{
172 std::string_view buffer =
173 "foo: equ 1\n"
174 "bar equ 2\n"
175 "qux: equ 3 ; comment\n"
176 "; only comment\n"
177 "error equ\n"
178 "error equ 123 extra stuff\n";
179 auto file = SymbolManager::loadGeneric("myfile.sym", buffer);
180 CHECK(file.filename == "myfile.sym");
181 CHECK(file.type == SymbolFile::Type::GENERIC);
182 REQUIRE(file.symbols.size() == 3);
183 CHECK(file.symbols[0].name == "foo");
184 CHECK(file.symbols[0].value == 1);
185 CHECK(file.symbols[1].name == "bar");
186 CHECK(file.symbols[1].value == 2);
187 CHECK(file.symbols[2].name == "qux");
188 CHECK(file.symbols[2].value == 3);
189}
190
191TEST_CASE("SymbolManager: loadNoICE without segments")
192{
193 std::string_view buffer =
194 "def foo 1\n"
195 "def bar 234h ; comment\n"
196 "error def 123\n"
197 "def error 99 extra stuff\n";
198 auto file = SymbolManager::loadNoICE("noice.sym", buffer);
199 CHECK(file.filename == "noice.sym");
200 CHECK(file.type == SymbolFile::Type::NOICE);
201 REQUIRE(file.symbols.size() == 2);
202 CHECK(file.symbols[0].name == "foo");
203 CHECK(file.symbols[0].value == 1);
204 CHECK(file.symbols[0].segment == std::nullopt);
205 CHECK(file.symbols[1].name == "bar");
206 CHECK(file.symbols[1].value == 0x234);
207 CHECK(file.symbols[1].segment == std::nullopt);
208}
209
210TEST_CASE("SymbolManager: loadNoICE with segments")
211{
212 std::string_view buffer =
213 "def foo 1\n"
214 "def bar 234h ; comment\n"
215 "error def 123\n"
216 "def error 99 extra stuff\n"
217 "def baz 123456h ; comment\n";
218 auto file = SymbolManager::loadNoICE("noice.sym", buffer);
219 CHECK(file.filename == "noice.sym");
220 CHECK(file.type == SymbolFile::Type::NOICE);
221 REQUIRE(file.symbols.size() == 3);
222 CHECK(file.symbols[0].name == "foo");
223 CHECK(file.symbols[0].value == 1);
224 CHECK(file.symbols[0].segment == 0);
225 CHECK(file.symbols[1].name == "bar");
226 CHECK(file.symbols[1].value == 0x234);
227 CHECK(file.symbols[1].segment == 0);
228 CHECK(file.symbols[2].name == "baz");
229 CHECK(file.symbols[2].value == 0x3456);
230 CHECK(file.symbols[2].segment == 0x12);
231}
232
233TEST_CASE("SymbolManager: loadHTC")
234{
235 // TODO verify with an actual HTC file
236 std::string_view buffer =
237 "foo 1234 bla\n"
238 "error 1234\n";
239 auto file = SymbolManager::loadHTC("htc.sym", buffer);
240 CHECK(file.filename == "htc.sym");
241 CHECK(file.type == SymbolFile::Type::HTC);
242 REQUIRE(file.symbols.size() == 1);
243 CHECK(file.symbols[0].name == "foo");
244 CHECK(file.symbols[0].value == 0x1234);
245}
246
247TEST_CASE("SymbolManager: loadVASM")
248{
249 std::string_view buffer =
250 "12AB ignore\n"
251 "Symbols by value:\n"
252 "12AB label\n"
253 "bla foo\n";
254 auto file = SymbolManager::loadVASM("vasm.sym", buffer);
255 CHECK(file.filename == "vasm.sym");
256 CHECK(file.type == SymbolFile::Type::VASM);
257 REQUIRE(file.symbols.size() == 1);
258 CHECK(file.symbols[0].name == "label");
259 CHECK(file.symbols[0].value == 0x12ab);
260}
261
262TEST_CASE("SymbolManager: loadASMSX")
263{
264 std::string_view buffer =
265 "1234h ignore1\n"
266 "12h:3456h ignore2\n"
267 "; global and local\n"
268 "1234h l1\n"
269 "12h:abcdh l2\n";
270 auto file = SymbolManager::loadASMSX("asmsx.sym", buffer);
271 CHECK(file.filename == "asmsx.sym");
272 CHECK(file.type == SymbolFile::Type::ASMSX);
273 REQUIRE(file.symbols.size() == 2);
274 CHECK(file.symbols[0].name == "l1");
275 CHECK(file.symbols[0].value == 0x1234);
276 CHECK(file.symbols[1].name == "l2");
277 CHECK(file.symbols[1].value == 0xabcd);
278}
279
280TEST_CASE("SymbolManager: loadLinkMap")
281{
282 std::string_view buffer =
283 "ignore1 text 2CE7 ignore2 0AEE\n"
284 "ignore3 2E58 ignore3 text 2E4C\n"
285 " Symbol Table\n"
286 "asllmod text 2CE7 asllsub 0AEE\n"
287 "cret 2E58 csv text 2E4C\n"
288 "single text 9876\n"
289 "last 8765\n";
290 auto file = SymbolManager::loadLinkMap("link.map", buffer);
291 CHECK(file.filename == "link.map");
292 CHECK(file.type == SymbolFile::Type::LINKMAP);
293 REQUIRE(file.symbols.size() == 6);
294 CHECK(file.symbols[0].name == "asllmod");
295 CHECK(file.symbols[0].value == 0x2ce7);
296 CHECK(file.symbols[1].name == "asllsub");
297 CHECK(file.symbols[1].value == 0x0aee);
298 CHECK(file.symbols[2].name == "cret");
299 CHECK(file.symbols[2].value == 0x2e58);
300 CHECK(file.symbols[3].name == "csv");
301 CHECK(file.symbols[3].value == 0x2e4c);
302 CHECK(file.symbols[4].name == "single");
303 CHECK(file.symbols[4].value == 0x9876);
304 CHECK(file.symbols[5].name == "last");
305 CHECK(file.symbols[5].value == 0x8765);
306}
TEST_CASE("SymbolManager: isHexDigit")
static std::optional< unsigned > isHexDigit(char c)
static SymbolFile loadLines(std::string_view filename, std::string_view buffer, SymbolFile::Type type, function_ref< std::optional< Symbol >(std::span< std::string_view >)> lineParser)
static std::optional< Symbol > checkLabelSegmentAndValue(std::string_view label, std::string_view value)
static SymbolFile::Type detectType(std::string_view filename, std::string_view buffer)
static SymbolFile loadNoICE(std::string_view filename, std::string_view buffer)
static SymbolFile loadHTC(std::string_view filename, std::string_view buffer)
static SymbolFile loadGeneric(std::string_view filename, std::string_view buffer)
static SymbolFile loadVASM(std::string_view filename, std::string_view buffer)
static std::optional< uint16_t > is4DigitHex(std::string_view s)
static std::optional< Symbol > checkLabel(std::string_view label, uint32_t value)
static SymbolFile loadASMSX(std::string_view filename, std::string_view buffer)
static SymbolFile loadLinkMap(std::string_view filename, std::string_view buffer)
static std::optional< Symbol > checkLabelAndValue(std::string_view label, std::string_view value)
CHECK(m3==m3)
This file implemented 3 utility functions:
Definition Autofire.cc:11