GP-2168 - removed JMockit usage from some tests

This commit is contained in:
dragonmacher 2022-06-14 14:52:29 -04:00
parent f02ecf21ca
commit 33ed42dcb7
13 changed files with 92 additions and 116 deletions

View file

@ -309,6 +309,12 @@ public class DecompilerController {
//@formatter:on //@formatter:on
} }
// for testing
void setCache(Cache<Function, DecompileResults> cache) {
this.decompilerCache.invalidateAll();
this.decompilerCache = cache;
}
public void clearCache() { public void clearCache() {
decompilerCache.invalidateAll(); decompilerCache.invalidateAll();
} }

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.app.decompiler.component; package ghidra.app.decompiler.component;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -43,14 +42,11 @@ import ghidra.program.model.listing.Function;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.test.*; import ghidra.test.*;
import mockit.Mock;
import mockit.MockUp;
public class DecompilerCachingTest extends AbstractGhidraHeadedIntegrationTest { public class DecompilerCachingTest extends AbstractGhidraHeadedIntegrationTest {
private TestEnv env; private TestEnv env;
private PluginTool tool; private PluginTool tool;
private DecompilePlugin decompilePlugin;
private CodeBrowserPlugin codeBrowser; private CodeBrowserPlugin codeBrowser;
private ProgramDB program; private ProgramDB program;
private List<Address> functionAddrs = new ArrayList<>(); private List<Address> functionAddrs = new ArrayList<>();
@ -58,34 +54,26 @@ public class DecompilerCachingTest extends AbstractGhidraHeadedIntegrationTest {
public Cache<Function, DecompileResults> cache; public Cache<Function, DecompileResults> cache;
private ToyProgramBuilder builder; private ToyProgramBuilder builder;
// partial fake of DecompilerController to take control of the buildCache() method.
public class FakeDecompilerController extends MockUp<DecompilerController> {
@Mock
public Cache<Function, DecompileResults> buildCache() {
//@formatter:off
cache = CacheBuilder
.newBuilder()
.maximumSize(3)
.recordStats()
.build()
;
//@formatter:on
return cache;
}
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
// the magic of JMockit will cause our FakeDecompilerController to get used instead
// of the real one, regardless of where it gets constructed.
new FakeDecompilerController();
setErrorGUIEnabled(false); setErrorGUIEnabled(false);
env = new TestEnv(); env = new TestEnv();
tool = env.getTool(); tool = env.getTool();
initializeTool(); initializeTool();
//@formatter:off
cache = CacheBuilder
.newBuilder()
.maximumSize(3)
.recordStats()
.build()
;
//@formatter:on
DecompilerController controller = decompilerProvider.getController();
controller.setCache(cache);
} }
@After @After
@ -258,8 +246,6 @@ public class DecompilerCachingTest extends AbstractGhidraHeadedIntegrationTest {
private void installPlugins() throws PluginException { private void installPlugins() throws PluginException {
tool.addPlugin(CodeBrowserPlugin.class.getName()); tool.addPlugin(CodeBrowserPlugin.class.getName());
tool.addPlugin(DecompilePlugin.class.getName()); tool.addPlugin(DecompilePlugin.class.getName());
decompilePlugin = env.getPlugin(DecompilePlugin.class);
codeBrowser = env.getPlugin(CodeBrowserPlugin.class); codeBrowser = env.getPlugin(CodeBrowserPlugin.class);
} }

View file

@ -21,6 +21,7 @@ import java.awt.geom.Point2D;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
@ -85,6 +86,9 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
string -> provider.setClipboardStringContent(string); string -> provider.setClipboardStringContent(string);
private Cache<Function, FGData> cache; private Cache<Function, FGData> cache;
private BiConsumer<FGData, Boolean> fgDataDisposeListener = (data, evicted) -> {
// dummy
};
public FGController(FGProvider provider, FunctionGraphPlugin plugin) { public FGController(FGProvider provider, FunctionGraphPlugin plugin) {
this.provider = provider; this.provider = provider;
@ -110,6 +114,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
} }
data.dispose(); data.dispose();
fgDataDisposeListener.accept(data, true);
return true; return true;
} }
@ -288,7 +293,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
/** /**
* Sets the message that will appear in the lower part of the graph. * Sets the message that will appear in the lower part of the graph.
* *
* @param message the message to display * @param message the message to display
*/ */
public void setStatusMessage(String message) { public void setStatusMessage(String message) {
@ -355,7 +360,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
//================================================================================================== //==================================================================================================
// Grouping Methods (to be moved in a later refactoring) // Grouping Methods (to be moved in a later refactoring)
//================================================================================================== //==================================================================================================
public void groupSelectedVertices() { public void groupSelectedVertices() {
groupSelectedVertices(null); groupSelectedVertices(null);
@ -413,7 +418,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
//================================================================================================== //==================================================================================================
// End Group Methods // End Group Methods
//================================================================================================== //==================================================================================================
//================================================================================================== //==================================================================================================
// Methods call by the providers // Methods call by the providers
@ -795,7 +800,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
* BLEH!: I don't like clearing the cache this way...another options is to mark all cached * BLEH!: I don't like clearing the cache this way...another options is to mark all cached
* values as stale, somehow. If we did this, then when the view reuses the cached data, it could * values as stale, somehow. If we did this, then when the view reuses the cached data, it could
* signal to the user that the graph is out-of-date. * signal to the user that the graph is out-of-date.
* *
* @param program the program * @param program the program
*/ */
public void invalidateAllCacheForProgram(Program program) { public void invalidateAllCacheForProgram(Program program) {
@ -882,6 +887,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
Function function = data.getFunction(); Function function = data.getFunction();
if (function != null && cache.getIfPresent(function) != data) { if (function != null && cache.getIfPresent(function) != data) {
data.dispose(); data.dispose();
fgDataDisposeListener.accept(data, false);
return true; return true;
} }
return false; return false;
@ -1012,7 +1018,7 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
/** /**
* Will broadcast the given vertex location to the external system * Will broadcast the given vertex location to the external system
* *
* @param location the location coming from the vertex * @param location the location coming from the vertex
*/ */
public void synchronizeProgramLocationToVertex(ProgramLocation location) { public void synchronizeProgramLocationToVertex(ProgramLocation location) {
@ -1033,17 +1039,30 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
.newBuilder() .newBuilder()
.maximumSize(5) .maximumSize(5)
.removalListener(listener) .removalListener(listener)
// Note: using soft values means that sometimes our data is reclaimed by the // Note: using soft values means that sometimes our data is reclaimed by the
// Garbage Collector. We don't want that, we wish to call dispose() on the data // Garbage Collector. We don't want that, we wish to call dispose() on the data
//.softValues() //.softValues()
.build(); .build();
//@formatter:on //@formatter:on
} }
private void cacheValueRemoved(RemovalNotification<Function, FGData> notification) { // for testing
void setCache(Cache<Function, FGData> cache) {
this.cache.invalidateAll();
this.cache = cache;
}
// open for testing
void cacheValueRemoved(RemovalNotification<Function, FGData> notification) {
disposeGraphDataIfNotInUse(notification.getValue()); disposeGraphDataIfNotInUse(notification.getValue());
} }
void setFGDataDisposedListener(BiConsumer<FGData, Boolean> listener) {
this.fgDataDisposeListener = listener != null ? listener : (data, evicted) -> {
// dummy
};
}
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================

View file

@ -101,6 +101,6 @@ public class FGData {
@Override @Override
public String toString() { public String toString() {
return "FunctionGraphData[" + function.getName() + "]"; return "FunctionGraphData[" + function.getName() + "@" + function.getEntryPoint() + "]";
} }
} }

View file

@ -33,7 +33,6 @@ import org.junit.*;
import docking.*; import docking.*;
import docking.action.*; import docking.action.*;
import docking.menu.ActionState;
import docking.menu.MultiStateDockingAction; import docking.menu.MultiStateDockingAction;
import docking.test.AbstractDockingTest; import docking.test.AbstractDockingTest;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
@ -75,7 +74,6 @@ import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.test.*; import ghidra.test.*;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.task.RunManager; import ghidra.util.task.RunManager;
public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedIntegrationTest { public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedIntegrationTest {
@ -541,13 +539,13 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
protected void waitForBusyRunManager(FGController controller) { protected void waitForBusyRunManager(FGController controller) {
FGModel model = controller.getModel(); FGModel model = controller.getModel();
long start = System.nanoTime(); // long start = System.nanoTime();
waitForSwing(); waitForSwing();
RunManager runManager = (RunManager) TestUtils.getInstanceField("runManager", model); RunManager runManager = (RunManager) TestUtils.getInstanceField("runManager", model);
waitForCondition(() -> !runManager.isInProgress()); waitForCondition(() -> !runManager.isInProgress());
long end = System.nanoTime(); // long end = System.nanoTime();
long total = end - start; // long total = end - start;
// Msg.debug(this, // Msg.debug(this,
// "Run manager wait time: " + TimeUnit.MILLISECONDS.convert(total, TimeUnit.NANOSECONDS)); // "Run manager wait time: " + TimeUnit.MILLISECONDS.convert(total, TimeUnit.NANOSECONDS));
} }
@ -1999,26 +1997,6 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte
waitForSwing(); waitForSwing();
} }
private void setMinCrossLayout() {
Object actionManager = getInstanceField("actionManager", graphProvider);
@SuppressWarnings("unchecked")
final MultiStateDockingAction<FGLayoutProvider> action =
(MultiStateDockingAction<FGLayoutProvider>) getInstanceField("layoutAction",
actionManager);
runSwing(() -> {
List<ActionState<FGLayoutProvider>> states = action.getAllActionStates();
for (ActionState<FGLayoutProvider> state : states) {
FGLayoutProvider layoutProvider = state.getUserData();
if (layoutProvider.getLayoutName().contains("MinCross")) {
action.setCurrentActionState(state);
return;
}
}
throw new AssertException("Unable to find MinCross layout");
});
}
protected FGData triggerPersistenceAndReload(String functionAddress) { protected FGData triggerPersistenceAndReload(String functionAddress) {
// //
// Ideally, we would like to save, close and re-open the program so that we can get // Ideally, we would like to save, close and re-open the program so that we can get

View file

@ -19,6 +19,8 @@ import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses; import org.junit.runners.Suite.SuiteClasses;
import ghidra.app.plugin.core.functiongraph.mvc.FunctionGraphCacheTest;
//@formatter:off //@formatter:off
@RunWith(Suite.class) @RunWith(Suite.class)
@SuiteClasses({ @SuiteClasses({

View file

@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.plugin.core.functiongraph; package ghidra.app.plugin.core.functiongraph.mvc;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import java.util.*; import java.util.*;
@ -25,69 +24,53 @@ import org.junit.Test;
import com.google.common.cache.*; import com.google.common.cache.*;
import ghidra.app.plugin.core.functiongraph.mvc.FGController; import ghidra.app.plugin.core.functiongraph.AbstractFunctionGraphTest;
import ghidra.app.plugin.core.functiongraph.mvc.FGData;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import mockit.*;
public class FunctionGraphCacheTest extends AbstractFunctionGraphTest { public class FunctionGraphCacheTest extends AbstractFunctionGraphTest {
private Cache<Function, FGData> cache; private Cache<Function, FGData> cache;
private List<Address> disposedFunctionData = Collections.synchronizedList(new ArrayList<>()); private List<Address> disposedFunctionData = Collections.synchronizedList(new ArrayList<>());
private List<Address> evictedFromCache = Collections.synchronizedList(new ArrayList<>()); private List<Address> evictedFromCache = Collections.synchronizedList(new ArrayList<>());
// partial fake of FGController to take control of the buildCache() method and spy
// on the two methods that might dispose a FunctionGrahpData object.
public class FakeFunctionGraphController extends MockUp<FGController> {
@Mock
public Cache<Function, FGData> buildCache(RemovalListener<Function, FGData> listener) {
//@formatter:off
cache = CacheBuilder
.newBuilder()
.maximumSize(3)
.removalListener(listener)
.recordStats()
.build()
;
//@formatter:on
return cache;
}
@Mock
boolean disposeIfNotInCache(Invocation invocation, FGData data) {
boolean result = invocation.proceed(data);
Function function = data.getFunction();
if (function == null) {
return result;
}
Address address = data.getFunction().getEntryPoint();
if (result) {
disposedFunctionData.add(address);
}
return result;
}
@Mock
boolean disposeGraphDataIfNotInUse(Invocation invocation, FGData data) {
boolean result = invocation.proceed(data);
evictedFromCache.add(data.getFunction().getEntryPoint());
if (result) {
disposedFunctionData.add(data.getFunction().getEntryPoint());
}
return result;
}
}
@Override @Override
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
// the magic of JMockit will cause our FakeDecompilerController to get used instead
// of the real one, regardless of where it gets constructed.
new FakeFunctionGraphController();
super.setUp(); super.setUp();
FGController controller = getFunctionGraphController();
// go to an function address that is not used by this test (all tests use functions 0-2)
goToAddress(functionAddrs.get(3));
controller.clear();
waitForSwing();
RemovalListener<Function, FGData> listener = controller::cacheValueRemoved;
//@formatter:off
cache = CacheBuilder
.newBuilder()
.maximumSize(3)
.removalListener(listener)
.recordStats()
.build()
;
//@formatter:on
controller.setCache(cache);
controller.setFGDataDisposedListener((data, evicted) -> {
Function function = data.getFunction();
if (function == null) {
return;
}
Address address = data.getFunction().getEntryPoint();
disposedFunctionData.add(address);
if (evicted) {
evictedFromCache.add(address);
}
});
goToAddress(getStartingAddress());
} }
@Test @Test
@ -103,6 +86,7 @@ public class FunctionGraphCacheTest extends AbstractFunctionGraphTest {
@Test @Test
public void testBackToOldFunctionIsCacheHit() { public void testBackToOldFunctionIsCacheHit() {
goToAddress(functionAddrs.get(0)); goToAddress(functionAddrs.get(0));
goToAddress(functionAddrs.get(1)); goToAddress(functionAddrs.get(1));
CacheStats stats1 = cache.stats(); CacheStats stats1 = cache.stats();
@ -141,10 +125,11 @@ public class FunctionGraphCacheTest extends AbstractFunctionGraphTest {
goToAddress(functionAddrs.get(0)); goToAddress(functionAddrs.get(0));
goToAddress(functionAddrs.get(1)); goToAddress(functionAddrs.get(1));
goToAddress(functionAddrs.get(2)); goToAddress(functionAddrs.get(2));
assertEquals(3, cache.size());
cache.invalidateAll(); cache.invalidateAll();
assertEquals(3, evictedFromCache.size()); assertEquals(2, evictedFromCache.size());
assertEquals(2, disposedFunctionData.size()); assertEquals(2, disposedFunctionData.size());
assertTrue(disposedFunctionData.contains(getAddress(functionAddrs.get(0)))); assertTrue(disposedFunctionData.contains(getAddress(functionAddrs.get(0))));
assertTrue(disposedFunctionData.contains(getAddress(functionAddrs.get(1)))); assertTrue(disposedFunctionData.contains(getAddress(functionAddrs.get(1))));