summaryrefslogtreecommitdiff
path: root/src/tracetab_tests.cpp
diff options
context:
space:
mode:
authorOxore <oxore@protonmail.com>2025-01-03 17:07:00 +0300
committerOxore <oxore@protonmail.com>2025-01-07 14:39:01 +0300
commitcb96278e25140cfcc1afc22df2102bcf3b6ae38c (patch)
tree9e93bd8a5fb4d5fbc177924b6b25ca8cd04e7fd7 /src/tracetab_tests.cpp
parent810dc87cd5173f8cfc81c774fd49cf8f928a9ae8 (diff)
Impl extended trace table format parser
Diffstat (limited to 'src/tracetab_tests.cpp')
-rw-r--r--src/tracetab_tests.cpp317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/tracetab_tests.cpp b/src/tracetab_tests.cpp
new file mode 100644
index 0000000..e8d0710
--- /dev/null
+++ b/src/tracetab_tests.cpp
@@ -0,0 +1,317 @@
+/* SPDX-License-Identifier: Unlicense
+ */
+
+#include "tracetab.h"
+#include "doctest/doctest.h"
+#include "debug.h"
+
+#include <cstring>
+
+#define STRING_SLICE(s) StringSlice{ s, (sizeof s) - 1u }
+
+struct StringSlice {
+ const char *str;
+ size_t len;
+};
+
+TEST_CASE("PC trace") {
+ const auto data = STRING_SLICE("512\n0x202\n0b1000000100\n01006\n");
+ TraceTable tt{};
+ const bool ok = ParseTraceData(tt, data.str, data.len, stderr);
+ REQUIRE(ok);
+ CHECK_EQ(tt.TypesCount(), 0);
+ CHECK_EQ(tt.NodesCount(), 4);
+ CHECK_EQ(tt.Shstr(), nullptr);
+ CHECK_EQ(tt.Node(0), TraceNode::Pc(512));
+ CHECK_EQ(tt.Node(1), TraceNode::Pc(0x202));
+ CHECK_EQ(tt.Node(2), TraceNode::Pc(0b1000000100));
+ CHECK_EQ(tt.Node(3), TraceNode::Pc(01006));
+}
+
+TEST_CASE("pc, fn and mixed newlines") {
+ const auto data = STRING_SLICE(
+ "0x200 pc __start\r\n\r0x491ac fn 30 printf\n\n\n0x49c78 fn 100 fputs");
+ TraceTable tt{};
+ const bool ok = ParseTraceData(tt, data.str, data.len, stderr);
+ REQUIRE(ok);
+ CHECK_EQ(tt.TypesCount(), 0);
+ REQUIRE_EQ(tt.NodesCount(), 3);
+ CHECK_NE(tt.Shstr(), nullptr);
+ {
+ const auto n = tt.Node(0);
+ CHECK_EQ(n.kind, TraceNodeKind::kPc);
+ CHECK_EQ(n.address, 0x200);
+ CHECK_EQ(n.size, 0);
+ CHECK_NE(n.name, 0);
+ TRACE("tt.Shstr(n.name) = %s\n", tt.Shstr(n.name));
+ CHECK_EQ(0, strcmp("__start", tt.Shstr(n.name)));
+ }
+ {
+ const auto n = tt.Node(1);
+ CHECK_EQ(n.kind, TraceNodeKind::kFunction);
+ CHECK_EQ(n.address, 0x491ac);
+ CHECK_EQ(n.size, 30);
+ CHECK_NE(n.name, 0);
+ TRACE("tt.Shstr(n.name) = %s\n", tt.Shstr(n.name));
+ CHECK_EQ(0, strcmp("printf", tt.Shstr(n.name)));
+ }
+ {
+ const auto n = tt.Node(2);
+ CHECK_EQ(n.kind, TraceNodeKind::kFunction);
+ CHECK_EQ(n.address, 0x49c78);
+ CHECK_EQ(n.size, 100);
+ CHECK_NE(n.name, 0);
+ TRACE("tt.Shstr(n.name) = %s\n", tt.Shstr(n.name));
+ CHECK_EQ(0, strcmp("fputs", tt.Shstr(n.name)));
+ }
+}
+
+TEST_CASE("str, strz and blob") {
+ const auto data = STRING_SLICE(
+ "0x100 str 0x10 smd_header_sega_genesis\n"
+ "0xbc1b strz 13 windtrap_wsa\n"
+ "0x29bb0 blob 256 copy_of_smd_header\n");
+ TraceTable tt{};
+ const bool ok = ParseTraceData(tt, data.str, data.len, stderr);
+ REQUIRE(ok);
+ CHECK_EQ(tt.TypesCount(), 0);
+ REQUIRE_EQ(tt.NodesCount(), 3);
+ CHECK_NE(tt.Shstr(), nullptr);
+ {
+ const auto n = tt.Node(0);
+ CHECK_EQ(n.kind, TraceNodeKind::kData);
+ CHECK_EQ(n.address, 0x100);
+ CHECK_NE(n.name, 0);
+ CHECK_EQ(0, strcmp("smd_header_sega_genesis", tt.Shstr(n.name)));
+ CHECK_EQ(n.data_type.kind, DataTypeKind::kStr);
+ CHECK_EQ(n.data_type.count, 0x10);
+ CHECK_EQ(n.data_type.count * n.data_type.BaseSize(), n.size);
+ CHECK_EQ(0x10, n.size);
+ }
+ {
+ const auto n = tt.Node(1);
+ CHECK_EQ(n.kind, TraceNodeKind::kData);
+ CHECK_EQ(n.address, 0xbc1b);
+ CHECK_NE(n.name, 0);
+ CHECK_EQ(0, strcmp("windtrap_wsa", tt.Shstr(n.name)));
+ CHECK_EQ(n.data_type.kind, DataTypeKind::kStrz);
+ CHECK_EQ(n.data_type.count, 13);
+ CHECK_EQ(n.data_type.count * n.data_type.BaseSize(), n.size);
+ CHECK_EQ(13, n.size);
+ }
+ {
+ const auto n = tt.Node(2);
+ CHECK_EQ(n.kind, TraceNodeKind::kData);
+ CHECK_EQ(n.address, 0x29bb0);
+ CHECK_NE(n.name, 0);
+ CHECK_EQ(0, strcmp("copy_of_smd_header", tt.Shstr(n.name)));
+ CHECK_EQ(n.data_type.kind, DataTypeKind::kBlob);
+ CHECK_EQ(n.data_type.count, 256);
+ CHECK_EQ(n.data_type.count * n.data_type.BaseSize(), n.size);
+ CHECK_EQ(256, n.size);
+ }
+}
+
+TEST_CASE("tables") {
+ const auto data = STRING_SLICE(
+ "0x4 [ptr] 63\n"
+ "0x87f44 [ptr,u32,] 19 soundtest_music_name_ptrs\n"
+ "0x88124 [ptr,u32] passwords_ptrs\n");
+ TraceTable tt{};
+ const bool ok = ParseTraceData(tt, data.str, data.len, stderr);
+ REQUIRE(ok);
+ REQUIRE_NE(tt.TypesCount(), 0);
+ REQUIRE_EQ(tt.NodesCount(), 3);
+ CHECK_NE(tt.Shstr(), nullptr);
+ {
+ const auto n = tt.Node(0);
+ CHECK_EQ(n.kind, TraceNodeKind::kData);
+ CHECK_EQ(n.address, 0x4);
+ CHECK_EQ(n.name, 0);
+ CHECK_EQ(n.data_type.kind, DataTypeKind::kTable);
+ CHECK_EQ(n.data_type.count, 63);
+ REQUIRE_EQ(n.data_type.nested_num, 1);
+ const auto ptr_type = tt.Type(n.data_type.nested_idx);
+ CHECK_EQ(ptr_type.kind, DataTypeKind::kPtr);
+ CHECK_EQ(ptr_type.count, 1);
+ CHECK_EQ(n.data_type.count * (ptr_type.count * ptr_type.BaseSize()), n.size);
+ CHECK_NE(0, n.size);
+ }
+ {
+ const auto n = tt.Node(1);
+ CHECK_EQ(n.kind, TraceNodeKind::kData);
+ CHECK_EQ(n.address, 0x87f44);
+ CHECK_NE(n.name, 0);
+ CHECK_EQ(0, strcmp("soundtest_music_name_ptrs", tt.Shstr(n.name)));
+ CHECK_EQ(n.data_type.kind, DataTypeKind::kTable);
+ CHECK_EQ(n.data_type.count, 19);
+ REQUIRE_EQ(n.data_type.nested_num, 2);
+ const auto ptr_type = tt.Type(n.data_type.nested_idx);
+ CHECK_EQ(ptr_type.kind, DataTypeKind::kPtr);
+ CHECK_EQ(ptr_type.count, 1);
+ const auto u32_type = tt.Type(n.data_type.nested_idx + 1);
+ CHECK_EQ(u32_type.kind, DataTypeKind::kU32);
+ CHECK_EQ(u32_type.count, 1);
+ CHECK_EQ(
+ n.data_type.count * (ptr_type.count * ptr_type.BaseSize() +
+ u32_type.count * u32_type.BaseSize()),
+ n.size);
+ CHECK_NE(0, n.size);
+ }
+ {
+ const auto n = tt.Node(2);
+ CHECK_EQ(n.kind, TraceNodeKind::kData);
+ CHECK_EQ(n.address, 0x88124);
+ CHECK_NE(n.name, 0);
+ CHECK_EQ(0, strcmp("passwords_ptrs", tt.Shstr(n.name)));
+ CHECK_EQ(n.data_type.kind, DataTypeKind::kTable);
+ CHECK_EQ(n.data_type.count, 0);
+ REQUIRE_EQ(n.data_type.nested_num, 2);
+ const auto ptr_type = tt.Type(n.data_type.nested_idx);
+ CHECK_EQ(ptr_type.kind, DataTypeKind::kPtr);
+ CHECK_EQ(ptr_type.count, 1);
+ const auto u32_type = tt.Type(n.data_type.nested_idx + 1);
+ CHECK_EQ(u32_type.kind, DataTypeKind::kU32);
+ CHECK_EQ(u32_type.count, 1);
+ CHECK_EQ(
+ n.data_type.count * (ptr_type.count * ptr_type.BaseSize() +
+ u32_type.count * u32_type.BaseSize()),
+ 0);
+ CHECK_EQ(0, n.size);
+ }
+}
+
+TEST_CASE("table types with count specified") {
+ const auto data = STRING_SLICE(
+ "0x88 [ptr 4,u16 2, u8 4] 14 something_idk\n");
+ TraceTable tt{};
+ const bool ok = ParseTraceData(tt, data.str, data.len, stderr);
+ REQUIRE(ok);
+ REQUIRE_NE(tt.TypesCount(), 0);
+ REQUIRE_EQ(tt.NodesCount(), 1);
+ CHECK_NE(tt.Shstr(), nullptr);
+ {
+ const auto n = tt.Node(0);
+ CHECK_EQ(n.kind, TraceNodeKind::kData);
+ CHECK_EQ(n.address, 0x88);
+ CHECK_NE(n.name, 0);
+ CHECK_EQ(0, strcmp("something_idk", tt.Shstr(n.name)));
+ CHECK_EQ(n.data_type.kind, DataTypeKind::kTable);
+ CHECK_EQ(n.data_type.count, 14);
+ REQUIRE_EQ(n.data_type.nested_num, 3);
+ const auto ptr_type = tt.Type(n.data_type.nested_idx);
+ CHECK_EQ(ptr_type.kind, DataTypeKind::kPtr);
+ CHECK_EQ(ptr_type.count, 4);
+ const auto u16_type = tt.Type(n.data_type.nested_idx + 1);
+ CHECK_EQ(u16_type.kind, DataTypeKind::kU16);
+ CHECK_EQ(u16_type.count, 2);
+ const auto u8_type = tt.Type(n.data_type.nested_idx + 2);
+ CHECK_EQ(u8_type.kind, DataTypeKind::kU8);
+ CHECK_EQ(u8_type.count, 4);
+ CHECK_EQ(
+ n.data_type.count *
+ (ptr_type.count * ptr_type.BaseSize() +
+ u16_type.count * u16_type.BaseSize() +
+ u8_type.count * u8_type.BaseSize()),
+ n.size);
+ CHECK_NE(0, n.size);
+ }
+}
+
+TEST_CASE("multiline trace node with comments") {
+ const auto data = STRING_SLICE(
+ "0x100 \\\n"
+ " [\\# side comments are ignored\n"
+ " ptr \\\n"
+ " 4\\\n"
+ " ,\\\n"
+ " u32\\#another random comment\n"
+ " ] \\\n"
+ " 4\\# and another one\n"
+ " trace_node#proper trailing comment\n");
+ TraceTable tt{};
+ const bool ok = ParseTraceData(tt, data.str, data.len, stderr);
+ REQUIRE(ok);
+ REQUIRE_NE(tt.TypesCount(), 0);
+ REQUIRE_EQ(tt.NodesCount(), 1);
+ CHECK_NE(tt.Shstr(), nullptr);
+ {
+ const auto n = tt.Node(0);
+ CHECK_EQ(n.kind, TraceNodeKind::kData);
+ CHECK_EQ(n.address, 0x100);
+ CHECK_NE(n.name, 0);
+ CHECK_EQ(0, strcmp("trace_node", tt.Shstr(n.name)));
+ CHECK_EQ(n.data_type.kind, DataTypeKind::kTable);
+ CHECK_EQ(n.data_type.count, 4);
+ REQUIRE_EQ(n.data_type.nested_num, 2);
+ const auto ptr_type = tt.Type(n.data_type.nested_idx);
+ CHECK_EQ(ptr_type.kind, DataTypeKind::kPtr);
+ CHECK_EQ(ptr_type.count, 4);
+ const auto u32_type = tt.Type(n.data_type.nested_idx + 1);
+ CHECK_EQ(u32_type.kind, DataTypeKind::kU32);
+ CHECK_EQ(u32_type.count, 1);
+ CHECK_EQ(
+ n.data_type.count *
+ (ptr_type.count * ptr_type.BaseSize() +
+ u32_type.count * u32_type.BaseSize()),
+ n.size);
+ CHECK_NE(0, n.size);
+ }
+}
+
+TEST_CASE("multiline trace node with comments") {
+ const auto data = STRING_SLICE(
+ "# Heading comments are appended to the trace node.\r"
+ "# And they may be multiline!\n"
+ "# The newlines in the heading comment are preserved\r\n"
+ "# if there is more than one line.\n"
+ "0x100 str 8 trace_node #side comments are ignored\n"
+ "#Just a stray comment without trace node because empty line follows\n"
+ "\n"
+ "# Single line heading comment won't preserve trailing newline symbol\n"
+ "0x200 strz 4 trace_node2 # side comments are ignored\n"
+ "#Just a comment without trace node");
+ TraceTable tt{};
+ const bool ok = ParseTraceData(tt, data.str, data.len, stderr);
+ REQUIRE(ok);
+ REQUIRE_EQ(tt.TypesCount(), 0);
+ REQUIRE_EQ(tt.NodesCount(), 2);
+ CHECK_NE(tt.Shstr(), nullptr);
+ {
+ const auto n = tt.Node(0);
+ CHECK_EQ(n.kind, TraceNodeKind::kData);
+ CHECK_EQ(n.address, 0x100);
+ CHECK_NE(n.name, 0);
+ TRACE("name = %s", tt.Shstr(n.name));
+ CHECK_EQ(0, strcmp("trace_node", tt.Shstr(n.name)));
+ CHECK_NE(n.comment, 0);
+ TRACE("comment = %s", tt.Shstr(n.comment));
+ CHECK_EQ(0, strcmp(
+ " Heading comments are appended to the trace node.\n"
+ " And they may be multiline!\n"
+ " The newlines in the heading comment are preserved\n"
+ " if there is more than one line.",
+ tt.Shstr(n.comment)));
+ CHECK_EQ(n.data_type.kind, DataTypeKind::kStr);
+ CHECK_EQ(n.data_type.count, 8);
+ CHECK_EQ(n.data_type.count * n.data_type.BaseSize(), n.size);
+ CHECK_EQ(8, n.size);
+ }
+ {
+ const auto n = tt.Node(1);
+ CHECK_EQ(n.kind, TraceNodeKind::kData);
+ CHECK_EQ(n.address, 0x200);
+ CHECK_NE(n.name, 0);
+ TRACE("n.name = %s", tt.Shstr(n.name));
+ CHECK_EQ(0, strcmp("trace_node2", tt.Shstr(n.name)));
+ CHECK_NE(n.comment, 0);
+ TRACE("comment = %s", tt.Shstr(n.comment));
+ CHECK_EQ(0, strcmp(
+ " Single line heading comment won't preserve trailing newline symbol",
+ tt.Shstr(n.comment)));
+ CHECK_EQ(n.data_type.kind, DataTypeKind::kStrz);
+ CHECK_EQ(n.data_type.count, 4);
+ CHECK_EQ(n.data_type.count * n.data_type.BaseSize(), n.size);
+ CHECK_EQ(4, n.size);
+ }
+}