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

			RemoteMethod refreshEvents = conn.getMethod("refresh_events");
			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();

				TraceObject events = waitForObject("VMs[].Events");
				refreshEvents.invoke(Map.of("container", events));
				waitTxDone();

				List<TraceObjectValue> eventVals = waitForValues("VMs[].Events[]");
				assertEquals(1, eventVals.size());
				assertTrue(eventVals.get(0).getEntryKey().contains("step request"));

				conn.execute("VirtualMachine vm = manager.getCurrentVM(); ");
				conn.execute(
					"MethodEntryRequest brkReq = vm.eventRequestManager().createMethodEntryRequest();");

				eventVals = waitForPass(() -> {
					refreshEvents.invoke(Map.of("container", events));
					List<TraceObjectValue> evs = waitForValues("VMs[].Events[]");
					assertEquals(2, evs.size());
					return evs;
				});
				assertTrue(eventVals.get(0).getEntryKey().contains("method entry request"));

			}
		}
	}
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