Thursday, November 11, 2010

Testing declarative Eclipse expressions

Our Eclipse-based product plugs into the platform debug support. And we have a large number of launch shortcuts which have enablement expressions specified in plugin.xml. And they are a huge pain in the ass to test manually. If a particular shortcut is disabled when it should be enabled, there isn't a good way (that I've seen) to trace the evaluation of the enablement expression at runtime. So it comes down to stepping through the deeply nested Expression.evaluate(..) calls, and you can easily get lost.

This is one of those times where I feel like the light bulb should have gone on for me years ago. It turns out there is a much easier way to test these things: by writing JUnit Plug-in Tests for them.

What follows is some sample code to do this. Since I'm testing the enablement of launch shortcuts, the configuration elements are specific to that scenario, but you should be able to do this for any expression that appears in a bundle's plugin.xml.

In a @Before method, I read the expression from the plugin.xml and convert it into an Expression object.

IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(IDebugUIConstants.PLUGIN_ID, IDebugUIConstants.EXTENSION_POINT_LAUNCH_SHORTCUTS);
IConfigurationElement[] infos = extensionPoint.getConfigurationElements();
for (IConfigurationElement elem : infos) {
  String id = elem.getAttribute("id");
  IConfigurationElement contextLaunch = null;
  IConfigurationElement enablement = null;
  if ("myShortcutId".equals(id)) {
    contextLaunch = elem.getChildren("contextualLaunch")[0];
    enablement = contextLaunch.getChildren("enablement")[0];
    expression = ExpressionConverter.getDefault().perform(enablement);
}


And in a @Test method, I set up the context I'm trying to mimic and evaluate the expression.

@Test
public void ui() throws Exception {
  List<object> ctxt = new ArrayList<object>();
  ctxt.add(selectedObject /*Some IResource, perhaps*/);
  IEvaluationContext context = new EvaluationContext(null, ctxt);
  context.addVariable("selection", ctxt); //$NON-NLS-1$
  assertEquals(EvaluationResult.valueOf(m_uiEnabled),  expression.evaluate(context));
}

Now I can muck around in those delicately constructed XML expressions and rest assured that I have a test suite watching my back.