rizin/test/unit/test_core_addr.c
IndAlok 666d0ba8a0
Implement unified API for describing addresses (name+offset format) (#5571)
* core: Add unified API for describing addresses (resolves rizinorg#4993)
* core: Fix pxW and prompt tests by adding backward-compatible delta limit

This commit fixes the test failures caused by behavioral changes in the
unified address description API:

1. pxW tests failing (cmd_pxw, helloworld-gcc-elf):
   - The old pxW code had a hardcoded 8192 limit for flag delta
   - Added max_flag_delta option to RzAddrDescribeOptions
   - rz_core_addr_get_flag_offset() now uses max_flag_delta=-1 (8192 limit)

2. prompt settings test failing (test/db/tools/rz):
   - The old prompt code used spaces around +/- (e.g., 'entry0 + 1')
   - Added use_spaces_around_delta option to RzAddrDescribeOptions
   - Added rz_core_addr_get_flag_offset_prompt() for prompt display

* core/caddr: Add proper error handling and API improvements

- Add RZ_NONNULL annotations to helper function parameters
- Add null pointer checks after rz_str_dup() to detect allocation failures
- Change helper functions to return bool for error propagation
- Handle memory allocation failures in rz_core_addr_describe() by
  checking helper return values and cleaning up on failure
- Use RZ_STR_ISEMPTY macro for consistent empty string checks
- Extract rz_core_addr_description_to_pj() as separate API for
  converting RzAddrDescription to JSON format

* core: Add unified API for describing addresses (resolves rizinorg#4993)
 core: Fix pxW and prompt tests by adding backward-compatible delta limit

This commit fixes the test failures caused by behavioral changes in the
unified address description API:

1. pxW tests failing (cmd_pxw, helloworld-gcc-elf):
   - The old pxW code had a hardcoded 8192 limit for flag delta
   - Added max_flag_delta option to RzAddrDescribeOptions
   - rz_core_addr_get_flag_offset() now uses max_flag_delta=-1 (8192 limit)

2. prompt settings test failing (test/db/tools/rz):
   - The old prompt code used spaces around +/- (e.g., 'entry0 + 1')
   - Added use_spaces_around_delta option to RzAddrDescribeOptions
   - Added rz_core_addr_get_flag_offset_prompt() for prompt display
2025-12-10 19:11:31 +08:00

258 lines
8.8 KiB
C

// SPDX-FileCopyrightText: 2024 RizinOrg <info@rizin.re>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_core.h>
#include "minunit.h"
bool test_rz_core_addr_options_new(void) {
RzCoreAddrOptions *opts = rz_core_addr_options_new();
mu_assert_notnull(opts, "options should be allocated");
mu_assert_true(opts->show_offset, "show_offset should default to true");
mu_assert_true(opts->prefer_function, "prefer_function should default to true");
mu_assert_true(opts->show_flag, "show_flag should default to true");
mu_assert_true(opts->use_decimal, "use_decimal should default to true");
mu_assert_false(opts->show_color, "show_color should default to false");
mu_assert_false(opts->show_source_info, "show_source_info should default to false");
mu_assert_false(opts->use_realnames, "use_realnames should default to false");
mu_assert_eq(opts->max_flag_delta, 0, "max_flag_delta should default to 0 (unlimited)");
mu_assert_false(opts->use_spaces_around_delta, "use_spaces_around_delta should default to false");
rz_core_addr_options_free(opts);
mu_end;
}
bool test_rz_core_addr_describe_basic(void) {
RzCore *core = rz_core_new();
mu_assert_notnull(core, "core should be allocated");
// Add a flag for testing
rz_flag_set(core->flags, "sym.main", 0x1000, 10);
// Test basic description
RzCoreAddr *desc = rz_core_addr_describe(core, 0x1000, NULL);
mu_assert_notnull(desc, "description should be allocated");
mu_assert_eq(desc->addr, 0x1000, "addr should match");
mu_assert_notnull(desc->flag_name, "flag_name should be set");
mu_assert_streq(desc->flag_name, "sym.main", "flag_name should match");
mu_assert_eq(desc->flag_delta, 0, "flag_delta should be 0");
rz_core_addr_free(desc);
// Test with offset
desc = rz_core_addr_describe(core, 0x1005, NULL);
mu_assert_notnull(desc, "description should be allocated");
mu_assert_eq(desc->addr, 0x1005, "addr should match");
mu_assert_notnull(desc->flag_name, "flag_name should be set");
mu_assert_streq(desc->flag_name, "sym.main", "flag_name should match");
mu_assert_eq(desc->flag_delta, 5, "flag_delta should be 5");
rz_core_addr_free(desc);
// Test with address BEFORE any flag (should have no flag)
desc = rz_core_addr_describe(core, 0x500, NULL);
mu_assert_notnull(desc, "description should be allocated");
mu_assert_eq(desc->addr, 0x500, "addr should match");
mu_assert_null(desc->flag_name, "flag_name should be NULL for address before any flag");
rz_core_addr_free(desc);
rz_core_free(core);
mu_end;
}
bool test_rz_core_addr_to_string(void) {
RzCoreAddr desc = {
.addr = 0x1005,
.flag_name = "sym.main",
.flag_offset = 0x1000,
.flag_delta = 5
};
// Test hex format
RzCoreAddrOptions opts = {
.show_offset = true,
.prefer_function = false,
.show_flag = true,
.use_decimal = false,
.show_color = false,
.show_source_info = false,
.use_realnames = false
};
char *str = rz_core_addr_to_string(&desc, &opts);
mu_assert_notnull(str, "string should be allocated");
mu_assert_streq(str, "sym.main+0x5", "string should be name+offset");
free(str);
// Test decimal format
opts.use_decimal = true;
str = rz_core_addr_to_string(&desc, &opts);
mu_assert_notnull(str, "string should be allocated");
mu_assert_streq(str, "sym.main+5", "string should be name+decimal offset");
free(str);
// Test with zero delta
desc.flag_delta = 0;
str = rz_core_addr_to_string(&desc, &opts);
mu_assert_notnull(str, "string should be allocated");
mu_assert_streq(str, "sym.main", "string should be just name");
free(str);
// Test with no name
RzCoreAddr desc2 = {
.addr = 0x2000,
.flag_name = NULL
};
opts.use_decimal = false;
str = rz_core_addr_to_string(&desc2, &opts);
mu_assert_notnull(str, "string should be allocated");
mu_assert_streq(str, "0x2000", "string should be hex address");
free(str);
mu_end;
}
bool test_rz_core_addr_get_name_delta(void) {
RzCore *core = rz_core_new();
mu_assert_notnull(core, "core should be allocated");
// Add flags for testing
rz_flag_set(core->flags, "sym.main", 0x1000, 10);
rz_flag_set(core->flags, "sym.helper", 0x2000, 20);
// Test exact match
char *str = rz_core_addr_get_name_delta(core, 0x1000);
mu_assert_notnull(str, "string should be allocated");
mu_assert_streq(str, "sym.main", "string should be flag name");
free(str);
// Test with offset (uses decimal format by default)
str = rz_core_addr_get_name_delta(core, 0x100a);
mu_assert_notnull(str, "string should be allocated");
mu_assert_streq(str, "sym.main+10", "string should be name+offset");
free(str);
// Test no match - returns hex address string when no flag found (hex for clarity)
str = rz_core_addr_get_name_delta(core, 0x500);
mu_assert_notnull(str, "string should be allocated for unknown address");
mu_assert_streq(str, "0x500", "string should be hex address when no flag found");
free(str);
rz_core_free(core);
mu_end;
}
bool test_rz_core_addr_with_function(void) {
RzCore *core = rz_core_new();
mu_assert_notnull(core, "core should be allocated");
// Create a function for testing
RzAnalysisFunction *fcn = rz_analysis_create_function(core->analysis, "main", 0x1000, RZ_ANALYSIS_FCN_TYPE_FCN);
mu_assert_notnull(fcn, "function should be created");
// Test function preference - test at function start address
// (rz_analysis_get_fcn_in requires basic blocks, so use rz_analysis_get_function_at)
RzCoreAddrOptions opts = {
.show_offset = true,
.prefer_function = true,
.show_flag = true,
.use_decimal = false,
.show_color = false,
.show_source_info = false,
.use_realnames = false
};
RzCoreAddr *desc = rz_core_addr_describe(core, 0x1000, &opts);
mu_assert_notnull(desc, "description should be allocated");
mu_assert_notnull(desc->fcn_name, "fcn_name should be set");
mu_assert_streq(desc->fcn_name, "main", "fcn_name should match");
mu_assert_eq(desc->fcn_delta, 0, "fcn_delta should be 0 at function start");
rz_core_addr_free(desc);
rz_core_free(core);
mu_end;
}
bool test_rz_core_addr_describe_pj(void) {
RzCore *core = rz_core_new();
mu_assert_notnull(core, "core should be allocated");
// Add a flag for testing
rz_flag_set(core->flags, "sym.test", 0x1000, 10);
PJ *pj = pj_new();
mu_assert_notnull(pj, "pj should be allocated");
rz_core_addr_describe_pj(core, pj, 0x1005, NULL);
char *json_str = pj_drain(pj);
mu_assert_notnull(json_str, "json string should be allocated");
// Should contain addr and flag info
mu_assert("should contain addr", strstr(json_str, "\"addr\"") != NULL);
mu_assert("should contain flag_name", strstr(json_str, "\"flag_name\"") != NULL);
free(json_str);
rz_core_free(core);
mu_end;
}
bool test_rz_core_addr_max_flag_delta(void) {
RzCore *core = rz_core_new();
mu_assert_notnull(core, "core should be allocated");
// Add a flag far away from test address
rz_flag_set(core->flags, "sym.distant", 0x1000, 10);
// Test with unlimited delta (0) - should find the flag
RzCoreAddrOptions opts_unlimited = {
.show_offset = true,
.show_flag = true,
.max_flag_delta = 0 // unlimited
};
RzCoreAddr *desc = rz_core_addr_describe(core, 0x10000, &opts_unlimited);
mu_assert_notnull(desc, "description should be allocated");
mu_assert_notnull(desc->flag_name, "flag_name should be set with unlimited delta");
mu_assert_streq(desc->flag_name, "sym.distant", "flag_name should match");
rz_core_addr_free(desc);
// Test with default 8192 limit (-1) - should NOT find the flag (delta too large)
RzCoreAddrOptions opts_default = {
.show_offset = true,
.show_flag = true,
.max_flag_delta = -1 // default 8192 limit
};
desc = rz_core_addr_describe(core, 0x10000, &opts_default);
mu_assert_notnull(desc, "description should be allocated");
mu_assert_null(desc->flag_name, "flag_name should be NULL with 8192 limit (delta too large)");
rz_core_addr_free(desc);
// Test with small delta within limit - should find the flag
desc = rz_core_addr_describe(core, 0x1100, &opts_default);
mu_assert_notnull(desc, "description should be allocated");
mu_assert_notnull(desc->flag_name, "flag_name should be set (delta within 8192)");
mu_assert_eq(desc->flag_delta, 0x100, "flag_delta should be 0x100");
rz_core_addr_free(desc);
// Test with custom limit
RzCoreAddrOptions opts_custom = {
.show_offset = true,
.show_flag = true,
.max_flag_delta = 100 // small custom limit
};
desc = rz_core_addr_describe(core, 0x1100, &opts_custom);
mu_assert_notnull(desc, "description should be allocated");
mu_assert_null(desc->flag_name, "flag_name should be NULL with 100 limit (delta 0x100 > 100)");
rz_core_addr_free(desc);
rz_core_free(core);
mu_end;
}
bool all_tests() {
mu_run_test(test_rz_core_addr_options_new);
mu_run_test(test_rz_core_addr_describe_basic);
mu_run_test(test_rz_core_addr_to_string);
mu_run_test(test_rz_core_addr_get_name_delta);
mu_run_test(test_rz_core_addr_with_function);
mu_run_test(test_rz_core_addr_describe_pj);
mu_run_test(test_rz_core_addr_max_flag_delta);
return tests_passed != tests_run;
}
mu_main(all_tests)