Skip to main content
Glama
ProgramAnalyzerSearchTest.java22.3 kB
package com.ghidramcp.services; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; /** * Unit tests for search functionality in ProgramAnalyzer. * * These tests verify parameter validation and error message formatting * for the searchFunctionsByName and searchDataByName functionality. * * Note: Full integration tests with Ghidra Program objects would require * the Ghidra test framework and are beyond the scope of unit tests. */ class ProgramAnalyzerSearchTest { /** * Test that null/empty search term handling messages are correct */ @Test @DisplayName("Should validate null and empty search term inputs") void testNullEmptySearchTermValidation() { // Expected error for null/empty search terms String expectedError = "Search term is required"; assertTrue(expectedError.contains("required"), "Should indicate search term is required"); assertFalse(expectedError.isEmpty(), "Error message should not be empty"); } /** * Test that search terms are case-insensitive */ @ParameterizedTest @DisplayName("Should handle case-insensitive search terms") @ValueSource(strings = {"VMEM", "vmem", "VmEm", "vMeM"}) void testCaseInsensitiveSearch(String searchTerm) { // All these variations should match the same results assertNotNull(searchTerm, "Search term should not be null"); assertFalse(searchTerm.isEmpty(), "Search term should not be empty"); // Case normalization test String normalized = searchTerm.toLowerCase(); assertEquals(normalized, searchTerm.toLowerCase(), "Search should be case-insensitive"); } /** * Test function search result format */ @Test @DisplayName("Should format function search results correctly") void testFunctionResultFormat() { // Expected format: "FunctionName @ Address" String functionName = "malloc"; String address = "0x401000"; String expectedFormat = functionName + " @ " + address; assertTrue(expectedFormat.contains(" @ "), "Function result should contain ' @ ' separator"); assertTrue(expectedFormat.startsWith(functionName), "Function result should start with function name"); assertTrue(expectedFormat.endsWith(address), "Function result should end with address"); } /** * Test data search result format */ @Test @DisplayName("Should format data search results correctly") void testDataResultFormat() { // Expected format: "DataLabel @ Address (type: TypeName)" String dataLabel = "g_VMEM"; String address = "0x402000"; String typeName = "dword"; String expectedFormat = dataLabel + " @ " + address + " (type: " + typeName + ")"; assertTrue(expectedFormat.contains(" @ "), "Data result should contain ' @ ' separator"); assertTrue(expectedFormat.contains("(type: "), "Data result should contain type information"); assertTrue(expectedFormat.startsWith(dataLabel), "Data result should start with data label"); assertTrue(expectedFormat.contains(address), "Data result should contain address"); assertTrue(expectedFormat.endsWith(")"), "Data result should end with closing parenthesis"); } /** * Test pagination parameters */ @ParameterizedTest @DisplayName("Should accept valid pagination parameters") @ValueSource(ints = {0, 10, 50, 100, 1000}) void testValidPaginationParameters(int value) { // Valid offset and limit values assertTrue(value >= 0, "Pagination values should be non-negative"); } /** * Test default pagination values */ @Test @DisplayName("Should use correct default pagination values") void testDefaultPaginationValues() { int defaultOffset = 0; int defaultLimit = 100; assertEquals(0, defaultOffset, "Default offset should be 0"); assertEquals(100, defaultLimit, "Default limit should be 100"); } /** * Test no matches message for functions */ @Test @DisplayName("Should return appropriate message when no functions match") void testNoFunctionsMatchMessage() { String searchTerm = "nonexistent_function"; String expectedMessage = "No functions matching '" + searchTerm + "'"; assertTrue(expectedMessage.contains("No functions matching"), "Should indicate no functions were found"); assertTrue(expectedMessage.contains(searchTerm), "Should include the search term"); assertTrue(expectedMessage.contains("'"), "Search term should be quoted"); } /** * Test no matches message for data */ @Test @DisplayName("Should return appropriate message when no data matches") void testNoDataMatchMessage() { String searchTerm = "nonexistent_data"; String expectedMessage = "No data variables matching '" + searchTerm + "'"; assertTrue(expectedMessage.contains("No data variables matching"), "Should indicate no data variables were found"); assertTrue(expectedMessage.contains(searchTerm), "Should include the search term"); assertTrue(expectedMessage.contains("'"), "Search term should be quoted"); } /** * Test substring matching behavior */ @ParameterizedTest @DisplayName("Should perform substring matching") @ValueSource(strings = {"VMEM", "VME", "MEM", "g_V", "EM"}) void testSubstringMatching(String substring) { // All these substrings should match "g_VMEM" String fullName = "g_VMEM"; assertTrue(fullName.contains(substring) || fullName.toLowerCase().contains(substring.toLowerCase()), "Should match substring: " + substring); } /** * Test result sorting behavior */ @Test @DisplayName("Should sort results alphabetically") void testResultSorting() { // Results should be sorted String[] results = {"zebra @ 0x1000", "apple @ 0x2000", "banana @ 0x3000"}; String[] sorted = {"apple @ 0x2000", "banana @ 0x3000", "zebra @ 0x1000"}; // Verify sorting expectation assertTrue(sorted[0].compareTo(sorted[1]) < 0, "First result should come before second alphabetically"); assertTrue(sorted[1].compareTo(sorted[2]) < 0, "Second result should come before third alphabetically"); } /** * Test special character handling in search terms */ @Test @DisplayName("Should handle special characters in search terms") void testSpecialCharactersInSearchTerms() { // Common special characters that might appear in names String[] specialChars = {"_", ".", "$", "@"}; for (String specialChar : specialChars) { assertNotNull(specialChar, "Special character should be defined"); // These should be valid in search terms String searchTerm = "prefix" + specialChar + "suffix"; assertFalse(searchTerm.isEmpty(), "Search term with special char should be valid"); } } /** * Test data with undefined type handling */ @Test @DisplayName("Should handle data with undefined type") void testUndefinedDataType() { String undefinedType = "undefined"; String dataLabel = "unknown_data"; String address = "0x403000"; String expectedFormat = dataLabel + " @ " + address + " (type: " + undefinedType + ")"; assertTrue(expectedFormat.contains("type: undefined"), "Should show 'undefined' for data without type"); } /** * Test that results include all required components */ @Test @DisplayName("Should include all required components in results") void testResultComponents() { // Function result components String[] functionComponents = {"name", "@", "address"}; // Data result components String[] dataComponents = {"label", "@", "address", "(type:", ")"}; // Verify all components are defined for (String component : functionComponents) { assertNotNull(component, "Function component should be defined: " + component); } for (String component : dataComponents) { assertNotNull(component, "Data component should be defined: " + component); } } /** * Test pagination with empty results */ @Test @DisplayName("Should handle pagination with no results") void testPaginationWithNoResults() { // When there are no matches, pagination parameters are irrelevant String noMatchMessage = "No functions matching 'xyz'"; assertFalse(noMatchMessage.isEmpty(), "Should return message even with no results"); assertTrue(noMatchMessage.contains("No functions"), "Should indicate no matches were found"); } /** * Test that data labels can be null */ @Test @DisplayName("Should handle data with null labels") void testNullDataLabel() { // Data without labels should be skipped in search // Only data with non-null labels should be included // This is a behavioral test - data with null labels // should not cause exceptions and should be filtered out assertDoesNotThrow(() -> { String label = null; boolean shouldInclude = (label != null); assertFalse(shouldInclude, "Null labels should be filtered out"); }); } /** * Test search term validation */ @Test @DisplayName("Should validate search term is not null or empty") void testSearchTermValidation() { // null search term String nullTerm = null; boolean isNullInvalid = (nullTerm == null || nullTerm.isEmpty()); assertTrue(isNullInvalid, "Null search term should be invalid"); // empty search term String emptyTerm = ""; boolean isEmptyInvalid = (emptyTerm == null || emptyTerm.isEmpty()); assertTrue(isEmptyInvalid, "Empty search term should be invalid"); // valid search term String validTerm = "test"; boolean isValid = (validTerm != null && !validTerm.isEmpty()); assertTrue(isValid, "Valid search term should pass validation"); } /** * Test result format consistency */ @Test @DisplayName("Should maintain consistent result format") void testResultFormatConsistency() { // Function format: "name @ address" String functionPattern = "^.+ @ .+$"; String functionExample = "malloc @ 0x401000"; assertTrue(functionExample.matches(functionPattern), "Function result should match expected pattern"); // Data format: "label @ address (type: typename)" String dataPattern = "^.+ @ .+ \\(type: .+\\)$"; String dataExample = "g_VMEM @ 0x402000 (type: dword)"; assertTrue(dataExample.matches(dataPattern), "Data result should match expected pattern"); } // ========== Class Search Tests ========== /** * Test that class search is case-insensitive */ @ParameterizedTest @DisplayName("Should handle case-insensitive class search terms") @ValueSource(strings = {"GRAPHICS", "graphics", "Graphics", "gRaPhIcS"}) void testCaseInsensitiveClassSearch(String searchTerm) { // All these variations should match the same class names assertNotNull(searchTerm, "Search term should not be null"); assertFalse(searchTerm.isEmpty(), "Search term should not be empty"); // Case normalization test String normalized = searchTerm.toLowerCase(); assertEquals(normalized, searchTerm.toLowerCase(), "Class search should be case-insensitive"); } /** * Test class search substring matching */ @ParameterizedTest @DisplayName("Should perform substring matching for class names") @ValueSource(strings = {"Graph", "raphic", "ics", "Graphics", "phi"}) void testClassSubstringMatching(String substring) { // All these substrings should match "Graphics" String className = "Graphics"; assertTrue(className.toLowerCase().contains(substring.toLowerCase()), "Should match substring: " + substring); } /** * Test class search with null search term uses default behavior */ @Test @DisplayName("Should handle null search term for class search") void testNullClassSearchTerm() { String searchTerm = null; boolean shouldReturnAll = (searchTerm == null || searchTerm.isEmpty()); assertTrue(shouldReturnAll, "Null search term should return all classes"); } /** * Test class search with empty search term uses default behavior */ @Test @DisplayName("Should handle empty search term for class search") void testEmptyClassSearchTerm() { String searchTerm = ""; boolean shouldReturnAll = (searchTerm == null || searchTerm.isEmpty()); assertTrue(shouldReturnAll, "Empty search term should return all classes"); } /** * Test class search pagination parameters */ @ParameterizedTest @DisplayName("Should accept valid pagination parameters for class search") @ValueSource(ints = {0, 10, 50, 100, 500}) void testClassSearchPaginationParameters(int value) { assertTrue(value >= 0, "Class search pagination values should be non-negative"); } /** * Test class search result sorting */ @Test @DisplayName("Should sort class search results alphabetically") void testClassSearchResultSorting() { // Class results should be sorted String[] classes = {"Zebra", "Apple", "Banana"}; String[] sorted = {"Apple", "Banana", "Zebra"}; // Verify sorting expectation assertTrue(sorted[0].compareTo(sorted[1]) < 0, "First class should come before second alphabetically"); assertTrue(sorted[1].compareTo(sorted[2]) < 0, "Second class should come before third alphabetically"); } /** * Test class search with special characters */ @Test @DisplayName("Should handle special characters in class search terms") void testSpecialCharactersInClassSearchTerms() { // Common special characters that might appear in class names String[] specialChars = {"_", "$", "0", "1"}; for (String specialChar : specialChars) { assertNotNull(specialChar, "Special character should be defined"); // These should be valid in search terms String searchTerm = "Class" + specialChar + "Name"; assertFalse(searchTerm.isEmpty(), "Search term with special char should be valid"); } } /** * Test class search filtering behavior */ @Test @DisplayName("Should filter class names based on search term") void testClassSearchFilteringBehavior() { // Simulate filtering behavior String[] allClasses = {"Graphics", "GraphicsManager", "AudioManager", "NetworkManager"}; String searchTerm = "graph"; String searchLower = searchTerm.toLowerCase(); int matchCount = 0; for (String className : allClasses) { if (className.toLowerCase().contains(searchLower)) { matchCount++; } } assertEquals(2, matchCount, "Should match 'Graphics' and 'GraphicsManager'"); } /** * Test class search with no matches */ @Test @DisplayName("Should return empty results when no classes match search") void testClassSearchNoMatches() { String[] allClasses = {"Graphics", "Audio", "Network"}; String searchTerm = "nonexistent"; String searchLower = searchTerm.toLowerCase(); int matchCount = 0; for (String className : allClasses) { if (className.toLowerCase().contains(searchLower)) { matchCount++; } } assertEquals(0, matchCount, "Should find no matches for nonexistent search term"); } // ============================================================ // Tests for listDefinedStrings functionality // ============================================================ /** * Test string result format */ @Test @DisplayName("Should format string results correctly") void testStringResultFormat() { // Expected format: "address: \"value\"" String address = "0x00401000"; String value = "Hello World"; String expectedFormat = address + ": \"" + value + "\""; assertTrue(expectedFormat.contains(": \""), "String result should contain ': \"' separator"); assertTrue(expectedFormat.startsWith(address), "String result should start with address"); assertTrue(expectedFormat.endsWith("\""), "String result should end with closing quote"); assertTrue(expectedFormat.contains(value), "String result should contain the string value"); } /** * Test case-insensitive search for strings */ @ParameterizedTest @DisplayName("Should perform case-insensitive string search") @ValueSource(strings = {"ERROR", "error", "Error", "eRrOr"}) void testCaseInsensitiveStringSearch(String searchTerm) { String stringValue = "Error: file not found"; // Case-insensitive matching boolean matches = stringValue.toLowerCase().contains(searchTerm.toLowerCase()); assertTrue(matches, "String search should be case-insensitive"); } /** * Test string search with various substring patterns */ @ParameterizedTest @DisplayName("Should match substrings in string values") @ValueSource(strings = {"pass", "word", "123", "PASS", "password"}) void testStringSubstringMatching(String substring) { String fullString = "password123"; boolean matches = fullString.toLowerCase().contains(substring.toLowerCase()); assertTrue(matches, "Should match substring: " + substring); } /** * Test string result with escaped characters */ @Test @DisplayName("Should handle escaped characters in string values") void testEscapedCharactersInStrings() { // Common escape sequences that should be handled String[] escapeSequences = {"\\n", "\\t", "\\r", "\\\""}; for (String escape : escapeSequences) { assertNotNull(escape, "Escape sequence should be defined"); // Test that format works with escaped characters String address = "0x00401000"; String value = "test" + escape + "value"; String formatted = address + ": \"" + value + "\""; assertTrue(formatted.contains(value), "Should handle escape sequence: " + escape); } } /** * Test no program loaded message for strings */ @Test @DisplayName("Should return appropriate message when no program loaded for strings") void testNoProgramLoadedForStrings() { String expectedMessage = "No program loaded"; assertFalse(expectedMessage.isEmpty(), "No program message should not be empty"); assertTrue(expectedMessage.contains("No program"), "Should indicate no program is loaded"); } /** * Test null search parameter for strings */ @Test @DisplayName("Should handle null search parameter for strings") void testNullSearchParameterForStrings() { // When search is null, all strings should be returned String search = null; boolean shouldIncludeAll = (search == null); assertTrue(shouldIncludeAll, "Null search should include all strings without filtering"); } /** * Test empty string values */ @Test @DisplayName("Should handle empty string values") void testEmptyStringValue() { String address = "0x00401000"; String value = ""; String formatted = address + ": \"" + value + "\""; assertTrue(formatted.endsWith(": \"\""), "Should format empty string value correctly"); } /** * Test pagination defaults for strings */ @Test @DisplayName("Should use correct default pagination for strings") void testStringPaginationDefaults() { int defaultOffset = 0; int defaultLimit = 100; // Server-side default assertEquals(0, defaultOffset, "Default offset should be 0"); assertTrue(defaultLimit > 0, "Default limit should be positive"); } /** * Test string result pattern matching */ @Test @DisplayName("Should maintain consistent string result format") void testStringResultPattern() { // String format: "address: \"value\"" String stringPattern = "^0x[0-9a-fA-F]+: \".+\"$"; String stringExample = "0x00401000: \"Hello World\""; assertTrue(stringExample.matches(stringPattern), "String result should match expected pattern"); } /** * Test that string search filters correctly */ @Test @DisplayName("Should filter strings by search parameter") void testStringSearchFiltering() { String search = "error"; String[] testStrings = { "Error: file not found", "Success", "FATAL ERROR", "warning" }; int matchCount = 0; for (String str : testStrings) { if (str.toLowerCase().contains(search.toLowerCase())) { matchCount++; } } assertEquals(2, matchCount, "Should match exactly 2 strings containing 'error'"); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/HK47196/GhidraMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server