public void testRefreshMemory() throws Exception {
		try (JshellAndConnection conn = startAndConnectJshell()) {
			start(conn, "HelloWorld.class");

			RemoteMethod refreshMemory = conn.getMethod("refresh_memory");
			RemoteMethod stepInto = conn.getMethod("step_into");
			try (ManagedDomainObject mdo = openDomainObject("/New Traces/HelloWorld.class")) {
				tb = new ToyDBTraceBuilder((Trace) mdo.get());
				txPut(conn, "Threads");

				TraceObject thread = waitForObject("VMs[].Threads[main]");
				stepInto.invoke(Map.of("thread", thread));
				waitTxDone();

				List<TraceObjectValue> children = waitForValues("VMs[].Memory[]");
				assertTrue(children.size() < 100);

				TraceObject memory = waitForObject("VMs[].Memory");
				refreshMemory.invoke(Map.of("memory", memory));
				children = waitForValues("VMs[].Memory[]");
				assertTrue(children.size() > 100);
			}
		}
	}
Because we control the target artifact, we know the steps precisely. NOTE: This must be the
same at least for step_into and step_over, otherwise, we can't know for sure step_over
actually behaves any differently than step_into. By matching with step_into, we can assure
ourselves that step_over actually encountered an invoke* bytecode instruction, because if it
didn't then step_into ought to fail.
Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/java/rmi/JavaMethodsTest.java