mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GP-5620 - Fixed a bug that introduced duplicate data when renaming
functions with a filter
This commit is contained in:
parent
71e7f65d3f
commit
8d2c94e28d
4 changed files with 171 additions and 34 deletions
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -18,25 +18,32 @@ package ghidra.app.plugin.core.functionwindow;
|
|||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.table.*;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.ActionContext;
|
||||
import docking.DefaultActionContext;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.dialogs.SettingsDialog;
|
||||
import docking.widgets.table.GTable;
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||
import ghidra.app.cmd.label.RenameLabelCmd;
|
||||
import ghidra.app.plugin.core.clear.ClearCmd;
|
||||
import ghidra.app.plugin.core.clear.ClearOptions;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.cmd.CompoundCmd;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.test.*;
|
||||
|
||||
public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
@ -45,7 +52,7 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
|
|||
private Program program;
|
||||
private FunctionWindowPlugin plugin;
|
||||
private GTable functionTable;
|
||||
private ComponentProvider provider;
|
||||
private FunctionWindowProvider provider;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
@ -57,7 +64,7 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
|
|||
|
||||
plugin.showFunctions();
|
||||
waitForSwing();
|
||||
provider = tool.getComponentProvider("Functions Window");
|
||||
provider = (FunctionWindowProvider) tool.getComponentProvider("Functions Window");
|
||||
functionTable = (GTable) findComponentByName(provider.getComponent(), "Functions Table");
|
||||
}
|
||||
|
||||
|
@ -76,7 +83,7 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
|
|||
ProgramManager pm = tool.getService(ProgramManager.class);
|
||||
pm.closeProgram(program, true);
|
||||
waitForSwing();
|
||||
waitForNotBusy(functionTable);
|
||||
waitForTable();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -91,12 +98,12 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
|
|||
cmd.add(new ClearCmd(f.getBody(), new ClearOptions()));
|
||||
}
|
||||
applyCmd(program, cmd);
|
||||
waitForNotBusy(functionTable);
|
||||
waitForTable();
|
||||
|
||||
assertEquals(0, functionTable.getRowCount());
|
||||
|
||||
undo(program);
|
||||
waitForNotBusy(functionTable);
|
||||
waitForTable();
|
||||
|
||||
assertEquals(numData, functionTable.getRowCount());
|
||||
}
|
||||
|
@ -104,7 +111,7 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
|
|||
@Test
|
||||
public void testProgramClose() throws Exception {
|
||||
closeProgram();
|
||||
waitForNotBusy(functionTable);
|
||||
waitForTable();
|
||||
assertEquals(functionTable.getRowCount(), 0);
|
||||
}
|
||||
|
||||
|
@ -165,6 +172,117 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
|
|||
assertThat(copyText, containsString(signatureText));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChange_WithFitler() throws Exception {
|
||||
|
||||
//
|
||||
// This tests a regression with changed items. Normally a changed item is handled by a
|
||||
// remove of the existing row object, with a re-add of that object. This allows us to avoid
|
||||
// duplicates and to sort the item. We had a bug that prevented the item from being
|
||||
// removed.
|
||||
//
|
||||
|
||||
// the bug was only present when sorted on the name column, since the sort was no longer
|
||||
// correct when the name had changed
|
||||
sort("Name");
|
||||
int startRowCount = functionTable.getRowCount();
|
||||
|
||||
// verify the function we will rename is in the table
|
||||
assertFunctionInTable("FUN_010058b8");
|
||||
|
||||
// apply a filter that will hide an item we will rename
|
||||
filter("entry");
|
||||
assertEquals(1, functionTable.getRowCount());
|
||||
assertFunctionInTable("entry");
|
||||
|
||||
// rename a function not showing, using a name that will pass the filter
|
||||
// FUN_010058b8 -> entry2
|
||||
renameFunction(addr("010058b8"), "entry2");
|
||||
|
||||
// verify the new item appears
|
||||
assertEquals(2, functionTable.getRowCount());
|
||||
assertFunctionInTable("entry2");
|
||||
|
||||
// remove the filter
|
||||
filter("");
|
||||
|
||||
// verify the old item is gone and the new item is still there
|
||||
assertFunctionInTable("entry2");
|
||||
assertFunctionNotInTable("FUN_010058b8");
|
||||
assertEquals("Table row count should not have changed for a function rename", startRowCount,
|
||||
functionTable.getRowCount());
|
||||
}
|
||||
|
||||
private void sort(String columnName) {
|
||||
|
||||
int column = getColumn(columnName);
|
||||
TableSortState descendingSortState = TableSortState.createDefaultSortState(column, false);
|
||||
FunctionTableModel model = (FunctionTableModel) functionTable.getModel();
|
||||
runSwing(() -> model.setTableSortState(descendingSortState));
|
||||
waitForTable();
|
||||
}
|
||||
|
||||
private int getColumn(String columnName) {
|
||||
int n = functionTable.getColumnCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
String name = functionTable.getColumnName(i);
|
||||
if (name.equals(columnName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Could not find column '%s'".formatted(columnName));
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void assertFunctionNotInTable(String expectedName) {
|
||||
FunctionTableModel model = (FunctionTableModel) functionTable.getModel();
|
||||
List<FunctionRowObject> data = model.getModelData();
|
||||
for (FunctionRowObject rowObject : data) {
|
||||
Function f = rowObject.getFunction();
|
||||
String name = f.getName();
|
||||
if (name.equals(expectedName)) {
|
||||
fail("The table should not have a function by name '%s'".formatted(expectedName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFunctionInTable(String expectedName) {
|
||||
FunctionTableModel model = (FunctionTableModel) functionTable.getModel();
|
||||
List<FunctionRowObject> data = model.getModelData();
|
||||
for (FunctionRowObject rowObject : data) {
|
||||
Function f = rowObject.getFunction();
|
||||
String name = f.getName();
|
||||
if (name.equals(expectedName)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("The table should have a function by name '%s'".formatted(expectedName));
|
||||
}
|
||||
|
||||
private void renameFunction(Address entry, String newName) {
|
||||
|
||||
FunctionManager fm = program.getFunctionManager();
|
||||
Function f = fm.getFunctionAt(entry);
|
||||
Symbol symbol = f.getSymbol();
|
||||
Namespace namespace = f.getParentNamespace();
|
||||
RenameLabelCmd cmd =
|
||||
new RenameLabelCmd(symbol, newName, namespace, SourceType.USER_DEFINED);
|
||||
applyCmd(program, cmd);
|
||||
waitForTable();
|
||||
}
|
||||
|
||||
private Address addr(String s) {
|
||||
AddressFactory af = program.getAddressFactory();
|
||||
return af.getAddress(s);
|
||||
}
|
||||
|
||||
private void filter(String text) {
|
||||
GTableFilterPanel<?> filterPanel = functionTable.getTableFilterPanel();
|
||||
runSwing(() -> filterPanel.setFilterText(text));
|
||||
waitForTable();
|
||||
}
|
||||
|
||||
private String getCopyText(int row, int column) {
|
||||
Object value = runSwing(() -> functionTable.getValueAt(row, column));
|
||||
assertNotNull(value);
|
||||
|
@ -227,8 +345,8 @@ public class FunctionWindowPluginTest extends AbstractGhidraHeadedIntegrationTes
|
|||
runSwing(() -> table.editingStopped(new ChangeEvent(table)));
|
||||
}
|
||||
|
||||
private void waitForNotBusy(GTable table) {
|
||||
waitForTableModel((ThreadedTableModel<?, ?>) table.getModel());
|
||||
private void waitForTable() {
|
||||
waitForTableModel((ThreadedTableModel<?, ?>) functionTable.getModel());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue