Files
ghidra-cli/tests/symbol_tests.rs
T
Alexander KiselevandClaude Opus 4.6 ad01eaf930 fix: share bridge across mutation tests via OnceLock to prevent macOS data loss
On macOS, Ghidra stores project data in mmap pages that aren't flushed
to disk while the bridge runs. Per-test DaemonTestHarness stop/restart
cycles cause data loss. Use OnceLock<DaemonTestHarness> to share a
single bridge per test binary, matching the readonly_tests pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 15:57:50 -08:00

166 lines
3.9 KiB
Rust

//! Tests for symbol operations.
use assert_cmd::Command;
use predicates::prelude::*;
use serial_test::serial;
use std::sync::OnceLock;
#[macro_use]
mod common;
use common::{
ensure_test_project, get_function_address, get_function_addresses, DaemonTestHarness,
};
const TEST_PROJECT: &str = "ci-test";
const TEST_PROGRAM: &str = "sample_binary";
static HARNESS: OnceLock<DaemonTestHarness> = OnceLock::new();
fn harness() -> &'static DaemonTestHarness {
HARNESS.get_or_init(|| {
ensure_test_project(TEST_PROJECT, TEST_PROGRAM);
DaemonTestHarness::new(TEST_PROJECT, TEST_PROGRAM).expect("Failed to start daemon")
})
}
#[test]
#[serial]
fn test_symbol_list() {
require_ghidra!();
let harness = harness();
let output = Command::cargo_bin("ghidra")
.unwrap()
.arg("symbol")
.arg("list")
.arg("--project")
.arg(TEST_PROJECT)
.arg("--program")
.arg(TEST_PROGRAM)
.output()
.expect("Failed to run command");
assert!(output.status.success(), "symbol list should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
// Known functions should appear as symbols
// On macOS, names may have underscore prefix
assert!(
stdout.contains("main") || stdout.contains("_main"),
"symbol list should contain main. Output: {}",
stdout
);
drop(harness);
}
#[test]
#[serial]
fn test_symbol_create_and_get() {
require_ghidra!();
let harness = harness();
let addr = get_function_address(harness, TEST_PROJECT, TEST_PROGRAM, "main");
Command::cargo_bin("ghidra")
.unwrap()
.arg("symbol")
.arg("create")
.arg(&addr)
.arg("test_symbol")
.arg("--project")
.arg(TEST_PROJECT)
.arg("--program")
.arg(TEST_PROGRAM)
.assert()
.success();
Command::cargo_bin("ghidra")
.unwrap()
.arg("symbol")
.arg("get")
.arg("test_symbol")
.arg("--project")
.arg(TEST_PROJECT)
.arg("--program")
.arg(TEST_PROGRAM)
.assert()
.success()
.stdout(predicate::str::contains("test_symbol"));
}
#[test]
#[serial]
fn test_symbol_rename() {
require_ghidra!();
let harness = harness();
let addrs = get_function_addresses(harness, TEST_PROJECT, TEST_PROGRAM, 2);
let addr = &addrs[1];
// Use unique names to avoid collisions with cached project state
let old_name = format!("old_sym_{}", std::process::id());
let new_name = format!("new_sym_{}", std::process::id());
Command::cargo_bin("ghidra")
.unwrap()
.arg("symbol")
.arg("create")
.arg(addr)
.arg(&old_name)
.arg("--project")
.arg(TEST_PROJECT)
.arg("--program")
.arg(TEST_PROGRAM)
.assert()
.success();
Command::cargo_bin("ghidra")
.unwrap()
.arg("symbol")
.arg("rename")
.arg(&old_name)
.arg(&new_name)
.arg("--project")
.arg(TEST_PROJECT)
.arg("--program")
.arg(TEST_PROGRAM)
.assert()
.success();
// Verify new symbol exists
Command::cargo_bin("ghidra")
.unwrap()
.arg("symbol")
.arg("get")
.arg(&new_name)
.arg("--project")
.arg(TEST_PROJECT)
.arg("--program")
.arg(TEST_PROGRAM)
.assert()
.success()
.stdout(predicate::str::contains(&*new_name));
}
#[test]
#[serial]
fn test_symbol_get_nonexistent() {
require_ghidra!();
let harness = harness();
Command::cargo_bin("ghidra")
.unwrap()
.arg("symbol")
.arg("get")
.arg("nonexistent_symbol_12345")
.arg("--project")
.arg(TEST_PROJECT)
.arg("--program")
.arg(TEST_PROGRAM)
.assert()
.failure();
drop(harness);
}