/**
*
*/
package org.openiaml.model.tests.release;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.xpath.XPathExpressionException;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EPackage;
import org.openiaml.emf.SoftCache;
import org.openiaml.model.model.ModelPackage;
import org.openiaml.model.tests.XmlTestCase;
import org.openiaml.model.xpath.IterableElementList;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Tests .mf and plugin.xml files of the specified plugins
*
* @author jmwright
*
*/
public class GmfToolTestCase extends XmlTestCase {
private static SoftCache<String,Document> toolCache = new SoftCache<String,Document>() {
@Override
public Document retrieve(String input) {
try {
return loadDocument(input);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
};
public static SoftCache<String,Document> getToolCache() {
return toolCache;
}
private static Set<String> loadedTools = null;
/**
* Create a list of gmftools from {@link PluginsTestCase}.
*
* @return
*/
public static Set<String> getToolList() {
if (loadedTools == null) {
loadedTools = new HashSet<String>();
for (String gmfgen : PluginsTestCase.getAllGmfGens()) {
loadedTools.add(gmfgen.replace(".gmfgen", ".gmftool"));
}
}
return loadedTools;
}
/**
* There should be the same number of tools specified here
* as in {@link PluginsTestCase}.
*
* @throws Exception
*/
public void testGmfToolMatch() throws Exception {
assertEquals(getToolList().size(), PluginsTestCase.getAllGmfGens().size());
}
/**
* From a given root element (//palette/tools) [there may be
* many 'tools' in //palette), get ALL
* gmfTool elements within. Excluding tool groups, but recursing
* through the gool groups.
*
* @param root
* @throws XPathExpressionException
*/
private IterableElementList getAllGmfTools(IterableElementList roots, String prefix) throws XPathExpressionException {
assertNotEqual(prefix + ": There should be exactly one //tools entry", roots.getLength(), 0);
List<Element> result = new ArrayList<Element>();
for (Element root : roots) {
result.addAll(getAllGmfToolsRecursive(root, prefix));
}
return new IterableElementList(result);
}
/**
* From a given root element (//palette/tools) [there may be
* many 'tools' in //palette), get ALL
* gmfTool elements within. Excluding tool groups, but recursing
* through the gool groups.
*
* @param root
* @throws XPathExpressionException
*/
private List<Element> getAllGmfToolsRecursive(Element root, String prefix) throws XPathExpressionException {
List<Element> result = new ArrayList<Element>();
IterableElementList tools = xpath(root, "tools");
for (Element e : tools) {
if (e.getAttribute("xsi:type").equals("gmftool:ToolGroup")) {
// found a group
result.addAll(getAllGmfToolsRecursive(e, prefix));
} else if (e.getAttribute("xsi:type").equals("gmftool:CreationTool")) {
// found a tool
result.add(e);
} else {
// unknown tool type
fail("Unexpected GMF tool xsi:type: " + e.getAttribute("xsi:type"));
}
}
return result;
}
/**
* Check .gmftools for icons. If the .gmftool does not
* specify an icon, one is specified automatically, and the
* .gmftool is rewritten.
*
* (TODO The other alternative is to build this into a build.xml.)
*
* @throws Exception
*/
public void testGmfTools() throws Exception {
for (String file : getToolList()) {
boolean changed = false;
Document doc = getToolCache().get(file);
IterableElementList nl = xpath(doc, "//palette/tools");
IterableElementList tools = getAllGmfTools(nl, file + ": ");
assertNotSame(file + ": There should be at least one subtool entry", tools.getLength(), 0);
for (int i = 0; i < tools.getLength(); i++) {
String prefix = "[" + file + "#" + i + "] ";
Element tool = (Element) tools.item(i);
assertNotNull(prefix, tool);
Element smallIcon = (Element) xpath(tool, "smallIcon").item(0);
assertNotNull(prefix, smallIcon);
Element largeIcon = (Element) xpath(tool, "largeIcon").item(0);
assertNotNull(prefix, largeIcon);
String defaultIcon = tool.getAttribute("title") + ".gif";
// has it got an icon defined?
String smallPath = smallIcon.getAttribute("path");
if (smallPath == null || smallPath.isEmpty()) {
// set it
smallIcon.setAttribute("path", "icons/full/obj16/" + defaultIcon);
smallIcon.setAttribute("bundle", "org.openiaml.model.edit");
smallIcon.setAttribute("xsi:type", "gmftool:BundleImage");
changed = true;
}
// the icon should exist
File smallIconFile = new File( "../" + smallIcon.getAttribute("bundle") + "/" + smallIcon.getAttribute("path"));
assertFileExists(prefix, smallIconFile);
// has it got an icon defined?
String largePath = largeIcon.getAttribute("path");
if (largePath == null || largePath.isEmpty()) {
// set it
largeIcon.setAttribute("path", "icons/full/obj32/" + defaultIcon);
largeIcon.setAttribute("bundle", "org.openiaml.model.edit");
largeIcon.setAttribute("xsi:type", "gmftool:BundleImage");
changed = true;
}
// if the icon doesn't exist, but it exists in the smallIcon, copy it over
File largeIconFile = new File( "../" + largeIcon.getAttribute("bundle") + "/" + largeIcon.getAttribute("path") );
if (!largeIconFile.exists()) {
assertFileExists(prefix, smallIconFile); // sanity check
System.out.println("Copying " + smallIconFile + " to " + largeIconFile + "...");
copyFile(smallIconFile, largeIconFile); // copy file
}
// the icon should exist
assertFileExists(prefix, largeIconFile);
}
if (changed) {
System.out.println("Writing backup " + file + "...");
saveDocument(doc, new File(file + ".backup"));
System.out.println("Writing new " + file + "...");
saveDocument(doc, new File(file));
}
}
}
/**
* In each .gmftool, there should be exactly one mapping to
* a node in the .gmfmap.
*
* @throws Exception
*/
public void testCheckAllToolsMapped() throws Exception {
for (String filename : getToolList()) {
Document gmftool = getToolCache().get(filename);
Document gmfmap = loadGmfmap(filename);
IterableElementList roots = xpath(gmftool, "/ToolRegistry/palette/tools");
IterableElementList tools = getAllGmfTools(roots, filename);
for (Element tool : tools) {
// EventTrigger
String toolName = tool.getAttribute("title");
// remove any selectors
String selector = getSelector(toolName);
toolName = removeSelectors(toolName);
String nodeXpath, linkXpath;
if (selector == null) {
// a normal node
nodeXpath = "/Mapping/nodes/ownedChild/domainMetaElement[contains(@href, '" + toolName + "')]";
linkXpath = "/Mapping/links/domainMetaElement[contains(@href, '" + toolName + "')]";
} else {
nodeXpath = "/Mapping/nodes/ownedChild/domainMetaElement[contains(@href, '" + toolName + "') and contains(../../containmentFeature/@href, '" + selector + "')]";
linkXpath = "/Mapping/links/domainMetaElement[contains(@href, '" + toolName + "')]";
}
// should match either a node..
IterableElementList matches = xpath(gmfmap, nodeXpath);
boolean matched = false;
if (selector == null) {
// TODO not sure how to test this yet
continue;
}
for (Element match : matches) {
String href = match.getAttribute("href");
if (href.endsWith(toolName)) {
if (selector != null) {
assertFalse(filename + ": More than one mapping for tool '" + toolName + "': " + selector, matched);
matched = true;
}
if (selector == null) {
// TODO not sure how to test this yet
/*
assertEquals(filename + ": More than one non-selector mapping for tool '" + toolName + "': " + selector, 0, nullCount);
nullCount++;
*/
}
}
}
// or a link
IterableElementList matches2 = xpath(gmfmap, linkXpath);
for (Element match : matches2) {
String href = match.getAttribute("href");
if (href.endsWith(toolName)) {
if (selector != null) {
assertFalse(filename + ": More than one mapping for tool '" + toolName + "': " + selector, matched);
matched = true;
}
if (selector == null) {
// TODO not sure how to test this yet
/*
assertEquals(filename + ": More than one non-selector mapping for tool '" + toolName + "': " + selector, 0, nullCount);
nullCount++;
*/
}
}
}
assertTrue(filename + ": Tool '" + toolName + "' [" + selector + "] did not match any mappings.", matched);
}
}
}
/**
* Change 'Foo [bar]' into 'bar'. If there is no selector, returns null.
*
* @param toolName
* @return
*/
private String getSelector(String toolName) {
if (toolName.contains("[")) {
String s = toolName.substring(toolName.indexOf("[") + 1);
s = s.substring(0, s.indexOf("]"));
return s;
}
return null;
}
/**
* Change 'Foo [bar]' into 'Foo'.
*
* @param toolName
* @return
*/
private String removeSelectors(String toolName) {
if (toolName.contains("[")) {
return toolName.substring(0, toolName.indexOf("[")).trim();
}
return toolName;
}
public void testGetSelector() {
assertNull(getSelector("Hello"));
assertEquals("bar", getSelector("Hello [bar]"));
}
public void testRemoveSelectors() {
assertEquals("Hello", removeSelectors("Hello"));
assertEquals("Hello", removeSelectors("Hello [bar]"));
}
/**
* Load a corresponding .gmfmap for a .gmftool.
* Assumed to be the same filename as the .gmftool, except
* replacing the file extension.
*
* @param filename
* @return
*/
private Document loadGmfmap(String filename) throws Exception {
String f = filename.replace(".gmftool", ".gmfmap");
assertNotEqual("'" + filename + "' does not have gmftool extension", f, filename);
return loadDocument(f);
}
/**
* For each .gmftool creation node, the icons set to the
* creation tool should match the name of the element
* created.
*
* @throws Exception
*/
public void testIconsMatch() throws Exception {
for (String filename : getToolList()) {
Document gmftool = getToolCache().get(filename);
IterableElementList roots = xpath(gmftool, "/ToolRegistry/palette/tools");
IterableElementList tools = getAllGmfTools(roots, filename);
for (Element tool : tools) {
// EventTrigger
String toolName = tool.getAttribute("title");
String message = "[" + filename + "] " + toolName + ": ";
// remove selectors
toolName = removeSelectors(toolName);
// should have a smallIcon
Element smallIcon = xpathFirst(tool, "smallIcon");
assertEndsWith(message, "/" + toolName + ".gif", smallIcon.getAttribute("path"));
// should have a largeIcon
Element largeIcon = xpathFirst(tool, "smallIcon");
assertEndsWith(message, "/" + toolName + ".gif", largeIcon.getAttribute("path"));
}
}
}
/**
* Issue 141: Only one root tool group should have "collapsible"
* set to false.
*
* @throws Exception
*/
public void testOnlyOneStaticGroup() throws Exception {
for (String filename : getToolList()) {
Document gmftool = getToolCache().get(filename);
IterableElementList roots = xpath(gmftool, "/ToolRegistry/palette/tools");
assertNotEqual("No roots found for " + filename, roots.size(), 0);
Element previousStatic = null;
for (Element root : roots) {
if (!root.hasAttribute("collapsible") ||
root.getAttribute("collapsible").equals("false")) {
// found a static one
if (previousStatic != null) {
fail(filename + ": Found two static tool groups: " + root.getAttribute("title") + ", " + previousStatic.getAttribute("title"));
}
previousStatic = root;
}
}
assertNotNull("No static tool groups found in " + filename, previousStatic);
}
}
/**
* Search the icon folders, and find out if there are any images that
* do <em>not</em> match a model type.
*
* @throws Exception
*/
public void testFindAllUnusedIcons() throws Exception {
Set<String> classes = getAllEClassesNames(ModelPackage.eINSTANCE);
List<String> extras = new ArrayList<String>();
// first check /obj16
{
String[] files = new File("../org.openiaml.model.edit/icons/full/obj16").list();
for (String f : files) {
if (!f.endsWith(".gif"))
continue;
String name = f.substring(0, f.indexOf('.'));
if (classes.contains(name))
continue; // found it
extras.add("obj16/" + f); // found extra
}
}
// then check /obj32
{
String[] files = new File("../org.openiaml.model.edit/icons/full/obj32").list();
for (String f : files) {
if (!f.endsWith(".gif"))
continue;
String name = f.substring(0, f.indexOf('.'));
if (classes.contains(name))
continue; // found it
extras.add("obj32/" + f); // found extra
}
}
for (String f : extras) {
System.err.println(f);
}
assertEquals("Found some icons that do not represent any class in the model: " + extras, 0, extras.size());
}
/**
* Get all EClasses in the given package and sub-packages.
*
* @param pkg
* @return
*/
private Set<String> getAllEClassesNames(EPackage pkg) {
Set<String> r = new HashSet<String>();
for (EClassifier cf : pkg.getEClassifiers()) {
if (cf instanceof EClass) {
r.add(((EClass) cf).getName());
}
}
for (EPackage sub : pkg.getESubpackages()) {
r.addAll(getAllEClassesNames(sub));
}
return r;
}
/**
* Test all of the tool descriptions; they should match
* the titles. If they do not, set them manually.
* @throws Exception
*
*/
public void testAndSetToolDescriptions() throws Exception {
for (String filename : getToolList()) {
String f = new File(filename).getName();
Document gmftool = getToolCache().get(filename);
// get all tools in subgroups
IterableElementList roots = xpath(gmftool, "/ToolRegistry/palette/tools/tools");
boolean saveDocument = false;
for (Element tool : roots) {
// bar [baz]
String title = tool.getAttribute("title");
// check the description
String desc = tool.getAttribute("description");
String expected = "Create new " + title;
if (desc == null || !desc.equals(expected)) {
tool.setAttribute("description", expected);
saveDocument = true;
System.err.println(f + ": " + desc + " -> " + expected);
}
}
if (saveDocument) {
System.err.println("Writing " + f + "...");
saveDocument(gmftool, filename);
}
}
}
/**
* Test all of the tool icons; they should match
* the titles. If they do not, set them manually.
* @throws Exception
*
*/
public void testAndSetToolIcons() throws Exception {
for (String filename : getToolList()) {
String f = new File(filename).getName();
Document gmftool = getToolCache().get(filename);
// get all tools in subgroups
IterableElementList roots = xpath(gmftool, "/ToolRegistry/palette/tools/tools");
boolean saveDocument = false;
for (Element tool : roots) {
// bar [baz]
String title = tool.getAttribute("title");
// bar
title = removeSelectors(title);
// get the LargeIcon
{
String key = "largeIcon";
String expected = "icons/full/obj32/" + title + ".gif";
Element icon = xpathFirst(tool, key);
String actual = icon.getAttribute("path");
if (actual == null || !actual.equals(expected)) {
icon.setAttribute("path", expected);
saveDocument = true;
System.err.println(f + ": " + actual + " -> " + expected);
}
}
// get the LargeIcon
{
String key = "smallIcon";
String expected = "icons/full/obj16/" + title + ".gif";
Element icon = xpathFirst(tool, key);
String actual = icon.getAttribute("path");
if (actual == null || !actual.equals(expected)) {
icon.setAttribute("path", expected);
saveDocument = true;
System.err.println(f + ": " + actual + " -> " + expected);
}
}
}
if (saveDocument) {
System.err.println("Writing " + f + "...");
saveDocument(gmftool, filename);
}
}
}
}