From: <cr...@us...> - 2009-01-18 13:23:25
|
Revision: 4887 http://jnode.svn.sourceforge.net/jnode/?rev=4887&view=rev Author: crawley Date: 2009-01-18 13:23:15 +0000 (Sun, 18 Jan 2009) Log Message: ----------- First checkin of a test harness for testing JNode commands and scripts. (This entails moving most of Emu into the 'shell' project.) Modified Paths: -------------- trunk/shell/.classpath trunk/shell/build.xml trunk/shell/descriptors/org.jnode.test.shell.xml trunk/shell/src/test/org/jnode/test/shell/Cassowary.java Added Paths: ----------- trunk/shell/src/emu/ trunk/shell/src/emu/org/ trunk/shell/src/emu/org/jnode/ trunk/shell/src/emu/org/jnode/emu/ trunk/shell/src/emu/org/jnode/emu/DeviceManager.java trunk/shell/src/emu/org/jnode/emu/Emu.java trunk/shell/src/emu/org/jnode/emu/EmuException.java trunk/shell/src/test/org/jnode/test/shell/command/ trunk/shell/src/test/org/jnode/test/shell/command/posix/ trunk/shell/src/test/org/jnode/test/shell/command/posix/TrueTest.xml trunk/shell/src/test/org/jnode/test/shell/harness/ trunk/shell/src/test/org/jnode/test/shell/harness/ClassTestRunner.java trunk/shell/src/test/org/jnode/test/shell/harness/CommandTestRunner.java trunk/shell/src/test/org/jnode/test/shell/harness/Test.java trunk/shell/src/test/org/jnode/test/shell/harness/TestHarness.java trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnable.java trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecification.java trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecificationException.java Removed Paths: ------------- trunk/distr/src/emu/org/jnode/emu/DeviceManager.java trunk/distr/src/emu/org/jnode/emu/Emu.java trunk/distr/src/emu/org/jnode/emu/EmuException.java Deleted: trunk/distr/src/emu/org/jnode/emu/DeviceManager.java =================================================================== --- trunk/distr/src/emu/org/jnode/emu/DeviceManager.java 2009-01-18 11:04:39 UTC (rev 4886) +++ trunk/distr/src/emu/org/jnode/emu/DeviceManager.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -1,105 +0,0 @@ -/* - * $Id$ - */ -package org.jnode.emu; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import org.apache.log4j.Logger; -import org.jnode.driver.AbstractDeviceManager; -import org.jnode.driver.Device; -import org.jnode.driver.DeviceFinder; -import org.jnode.driver.DeviceToDriverMapper; -import org.jnode.driver.DriverException; - -/** - * @author Levente S\u00e1ntha - */ -public class DeviceManager extends AbstractDeviceManager { - public static final Logger log = Logger.getLogger(DeviceManager.class); - - public static final DeviceManager INSTANCE = new DeviceManager(); - - private List<DeviceFinder> finders = new ArrayList<DeviceFinder>(); - - private List<DeviceToDriverMapper> mappers = new ArrayList<DeviceToDriverMapper>(); - - private DeviceManager() { - } - - public void removeAll() { - finders.clear(); - mappers.clear(); - - for (Device device : getDevices()) { - try { - unregister(device); - } catch (DriverException e) { - log.error("can't unregister " + device); - } - } - } - - public void add(DeviceFinder finder, DeviceToDriverMapper mapper) { - boolean doStart = false; - - if (!finders.contains(finder)) { - finders.add(finder); - doStart = true; - } - - if (!mappers.contains(mapper)) { - mappers.add(mapper); - doStart = true; - } - - if (doStart) { - start(); - } - } - - /** - * Start this manager - */ - public final void start() { - // Thread thread = new Thread() - // { - // public void run() - // { - log.debug("Loading extensions ..."); - loadExtensions(); - log.debug("Extensions loaded !"); - // } - // }; - // thread.start(); - - try { - // must be called before findDeviceDrivers - log.debug("findDevices ..."); - findDevices(); - - log.debug("findDeviceDrivers ..."); - findDeviceDrivers(); - - log.debug("StubDeviceManager initialized !"); - } catch (InterruptedException e) { - log.fatal("can't find devices", e); - } - } - - protected final void refreshFinders(List<DeviceFinder> finders) { - log.info("refreshFinders"); - finders.clear(); - finders.addAll(this.finders); - } - - protected final void refreshMappers(List<DeviceToDriverMapper> mappers) { - log.info("refreshMappers"); - mappers.clear(); - mappers.addAll(this.mappers); - - // Now sort them - Collections.sort(mappers, MapperComparator.INSTANCE); - } -} Deleted: trunk/distr/src/emu/org/jnode/emu/Emu.java =================================================================== --- trunk/distr/src/emu/org/jnode/emu/Emu.java 2009-01-18 11:04:39 UTC (rev 4886) +++ trunk/distr/src/emu/org/jnode/emu/Emu.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -1,228 +0,0 @@ -/* - * $Id: NameSpace.java 4564 2008-09-18 22:01:10Z fduminy $ - * - * JNode.org - * Copyright (C) 2003-2006 JNode.org - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; If not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -package org.jnode.emu; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; - -import javax.naming.NamingException; - -import org.jnode.emu.naming.BasicNameSpace; -import org.jnode.emu.plugin.model.DummyExtensionPoint; -import org.jnode.naming.InitialNaming; -import org.jnode.nanoxml.XMLElement; -import org.jnode.shell.ShellManager; -import org.jnode.shell.alias.AliasManager; -import org.jnode.shell.alias.def.DefaultAliasManager; -import org.jnode.shell.def.DefaultShellManager; -import org.jnode.shell.help.HelpFactory; -import org.jnode.shell.help.def.DefaultHelpFactory; -import org.jnode.shell.syntax.DefaultSyntaxManager; -import org.jnode.shell.syntax.SyntaxBundle; -import org.jnode.shell.syntax.SyntaxManager; -import org.jnode.shell.syntax.SyntaxSpecAdapter; -import org.jnode.shell.syntax.SyntaxSpecLoader; -import org.jnode.shell.syntax.XMLSyntaxSpecAdapter; - -/** - * This class is the core of a light-weight JNode emulator that allows (some) JNode - * applications to be run using a classic JVM in the context of a JNode development sandbox. - * - * @author Levente S\u00e1ntha - * @author Stephen Crawley - */ -public abstract class Emu { - private static final String[] ALL_PROJECTS = new String[]{ - "core", "distr", "fs", "gui", "net", "shell", "sound", "textui" - }; - - private static final String[] PLUGIN_NAMES = new String[]{ - "org.jnode.shell.command", - "org.jnode.shell.command.driver.console", - "org.jnode.apps.editor", - "org.jnode.apps.edit", - "org.jnode.apps.console", - }; - - /** - * Initialize a minimal subset of JNode services to allow us to run JNode commands. - * - * @param root the notional JNode sandbox root directory or <code>null</code>. - * @throws EmuException - */ - public static void initEnv(File root) throws EmuException { - if (true) { - if (root == null) { - root = new File("").getAbsoluteFile(); - System.err.println("Assuming that the JNode root is '" + root + "'"); - } - InitialNaming.setNameSpace(new BasicNameSpace()); - - try { - InitialNaming.bind(DeviceManager.NAME, DeviceManager.INSTANCE); - AliasManager aliasMgr = - new DefaultAliasManager(new DummyExtensionPoint()).createAliasManager(); - SyntaxManager syntaxMgr = - new DefaultSyntaxManager(new DummyExtensionPoint()).createSyntaxManager(); - for (String pluginName : PLUGIN_NAMES) { - configurePluginCommands(root, pluginName, aliasMgr, syntaxMgr); - } - System.setProperty("jnode.invoker", "thread"); - System.setProperty("jnode.interpreter", "redirecting"); - System.setProperty("jnode.debug", "true"); - InitialNaming.bind(AliasManager.NAME, aliasMgr); - InitialNaming.bind(ShellManager.NAME, new DefaultShellManager()); - InitialNaming.bind(SyntaxManager.NAME, syntaxMgr); - InitialNaming.bind(HelpFactory.NAME, new DefaultHelpFactory()); - } catch (NamingException ex) { - throw new EmuException("Problem setting up InitialNaming bindings", ex); - } - } - } - - /** - * Configure any command classes specified by a given plugin's descriptor - * - * @param root the root directory for the JNode sandbox. - * @param pluginName the plugin to be processed - * @param aliasMgr the alias manager to be populated - * @param syntaxMgr the syntax manager to be populated - * @throws EmuException - */ - private static void configurePluginCommands(File root, String pluginName, AliasManager aliasMgr, - SyntaxManager syntaxMgr) throws EmuException { - XMLElement pluginDescriptor = loadPluginDescriptor(root, pluginName); - extractAliases(pluginDescriptor, aliasMgr); - extractSyntaxBundles(pluginDescriptor, syntaxMgr); - } - - /** - * Populate the supplied syntax manager with syntax entries from a plugin descriptor. - * - * @param pluginDescriptor the plugin descriptor's root XML element - * @param syntaxMgr the syntax manager to be populated. - * @throws EmuException - */ - private static void extractSyntaxBundles(XMLElement pluginDescriptor, SyntaxManager syntaxMgr) - throws EmuException { - XMLElement syntaxesDescriptor = findExtension(pluginDescriptor, SyntaxManager.SYNTAXES_EP_NAME); - if (syntaxesDescriptor == null) { - return; - } - SyntaxSpecLoader loader = new SyntaxSpecLoader(); - for (XMLElement syntaxDescriptor : syntaxesDescriptor.getChildren()) { - if (!syntaxDescriptor.getName().equals("syntax")) { - continue; - } - SyntaxSpecAdapter adaptedElement = new XMLSyntaxSpecAdapter(syntaxDescriptor); - try { - SyntaxBundle bundle = loader.loadSyntax(adaptedElement); - if (bundle != null) { - syntaxMgr.add(bundle); - } - } catch (Exception ex) { - throw new EmuException("problem in syntax", ex); - } - } - } - - /** - * Populate the supplied alias manager with aliases from a plugin descriptor. - * - * @param pluginDescriptor the plugin descriptor's root XML element - * @param aliasMgr the alias manager to be populated. - * @throws EmuException - */ - private static void extractAliases(XMLElement pluginDescriptor, AliasManager aliasMgr) { - XMLElement aliasesDescriptor = findExtension(pluginDescriptor, AliasManager.ALIASES_EP_NAME); - if (aliasesDescriptor == null) { - return; - } - for (XMLElement aliasDescriptor : aliasesDescriptor.getChildren()) { - if (aliasDescriptor.getName().equals("alias")) { - String alias = aliasDescriptor.getStringAttribute("name"); - String className = aliasDescriptor.getStringAttribute("class"); - aliasMgr.add(alias, className); - } - } - } - - /** - * Locate the descriptor for a given extension point. - * - * @param pluginDescriptor the plugin descriptor - * @param epName the extension point's name - * @return - */ - private static XMLElement findExtension(XMLElement pluginDescriptor, String epName) { - for (XMLElement child : pluginDescriptor.getChildren()) { - if (child.getName().equals("extension") && - epName.equals(child.getStringAttribute("point"))) { - return child; - } - } - return null; - } - - /** - * Locate and load a plugin descriptor. We search the "descriptors" directory of - * each of the projects listed in ALL_PROJECTS - * - * @param root the notional root directory for the user's JNode sandbox. - * @param pluginName the name of the plugin we're trying to locate - * @return the loaded plugin descriptor or <code>null</code> - * @throws EmuException - */ - private static XMLElement loadPluginDescriptor(File root, String pluginName) - throws EmuException { - File file = null; - for (String projectName : ALL_PROJECTS) { - file = new File(new File(new File(root, projectName), "descriptors"), pluginName + ".xml"); - if (file.exists()) { - break; - } - } - if (!file.exists()) { - throw new EmuException("Cannot find plugin descriptor file for '" + - pluginName + "': " + file.getAbsolutePath()); - } - BufferedReader br = null; - try { - XMLElement elem = new XMLElement(); - br = new BufferedReader(new FileReader(file)); - elem.parseFromReader(br); - if (!elem.getName().equals("plugin")) { - throw new EmuException("File does not contain a 'plugin' descriptor: " + file); - } - return elem; - } catch (IOException ex) { - throw new EmuException("Problem reading / parsing plugin descriptor file " + file, ex); - } finally { - try { - br.close(); - } catch (IOException ex) { - // ignore - } - } - } -} Deleted: trunk/distr/src/emu/org/jnode/emu/EmuException.java =================================================================== --- trunk/distr/src/emu/org/jnode/emu/EmuException.java 2009-01-18 11:04:39 UTC (rev 4886) +++ trunk/distr/src/emu/org/jnode/emu/EmuException.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -1,12 +0,0 @@ -package org.jnode.emu; - -public class EmuException extends Exception { - - public EmuException(String message) { - super(message); - } - - public EmuException(String message, Throwable cause) { - super(message, cause); - } -} Modified: trunk/shell/.classpath =================================================================== --- trunk/shell/.classpath 2009-01-18 11:04:39 UTC (rev 4886) +++ trunk/shell/.classpath 2009-01-18 13:23:15 UTC (rev 4887) @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src/shell"/> + <classpathentry kind="src" path="src/emu"/> <classpathentry kind="src" path="src/test"/> <classpathentry kind="src" path="/JNode-Core"/> <classpathentry kind="lib" path="/JNode-Core/lib/log4j-1.2.8.jar"/> @@ -9,5 +10,6 @@ <classpathentry kind="lib" path="/JNode-Core/lib/ant-launcher.jar"/> <classpathentry exported="true" kind="lib" path="lib/bsh-2.0b5.jar"/> <classpathentry kind="lib" path="lib/rhino1.6r5-jsr223.jar"/> + <classpathentry kind="lib" path="/JNode-Builder/lib/nanoxml-2.2.3.jar"/> <classpathentry kind="output" path="build/classes"/> </classpath> Modified: trunk/shell/build.xml =================================================================== --- trunk/shell/build.xml 2009-01-18 11:04:39 UTC (rev 4886) +++ trunk/shell/build.xml 2009-01-18 13:23:15 UTC (rev 4887) @@ -28,6 +28,7 @@ <jnode.compile> <src path="${my-src.dir}/shell"/> <src path="${my-src.dir}/test"/> + <src path="${my-src.dir}/emu"/> <classpath refid="my-cp"/> </jnode.compile> </target> Modified: trunk/shell/descriptors/org.jnode.test.shell.xml =================================================================== --- trunk/shell/descriptors/org.jnode.test.shell.xml 2009-01-18 11:04:39 UTC (rev 4886) +++ trunk/shell/descriptors/org.jnode.test.shell.xml 2009-01-18 13:23:15 UTC (rev 4887) @@ -20,9 +20,13 @@ <runtime> <library name="jnode-shell.jar"> <export name="org.jnode.test.shell.*"/> + <export name="org.jnode.test.shell.bjorne.*"/> + <export name="org.jnode.test.shell.harness.*"/> <export name="org.jnode.test.shell.help.*"/> + <export name="org.jnode.test.shell.proclet.*"/> + <export name="org.jnode.test.shell.io.*"/> + <export name="org.jnode.test.shell.proclet.*"/> <export name="org.jnode.test.shell.syntax.*"/> - <export name="org.jnode.test.shell.proclet.*"/> </library> </runtime> @@ -34,5 +38,6 @@ <extension point="org.jnode.security.permissions"> <permission class="java.lang.RuntimePermission" name="setIO"/> + <permission class="java.lang.RuntimePermission" name="exitVM"/> </extension> </plugin> Added: trunk/shell/src/emu/org/jnode/emu/DeviceManager.java =================================================================== --- trunk/shell/src/emu/org/jnode/emu/DeviceManager.java (rev 0) +++ trunk/shell/src/emu/org/jnode/emu/DeviceManager.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,105 @@ +/* + * $Id$ + */ +package org.jnode.emu; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.apache.log4j.Logger; +import org.jnode.driver.AbstractDeviceManager; +import org.jnode.driver.Device; +import org.jnode.driver.DeviceFinder; +import org.jnode.driver.DeviceToDriverMapper; +import org.jnode.driver.DriverException; + +/** + * @author Levente S\u00e1ntha + */ +public class DeviceManager extends AbstractDeviceManager { + public static final Logger log = Logger.getLogger(DeviceManager.class); + + public static final DeviceManager INSTANCE = new DeviceManager(); + + private List<DeviceFinder> finders = new ArrayList<DeviceFinder>(); + + private List<DeviceToDriverMapper> mappers = new ArrayList<DeviceToDriverMapper>(); + + private DeviceManager() { + } + + public void removeAll() { + finders.clear(); + mappers.clear(); + + for (Device device : getDevices()) { + try { + unregister(device); + } catch (DriverException e) { + log.error("can't unregister " + device); + } + } + } + + public void add(DeviceFinder finder, DeviceToDriverMapper mapper) { + boolean doStart = false; + + if (!finders.contains(finder)) { + finders.add(finder); + doStart = true; + } + + if (!mappers.contains(mapper)) { + mappers.add(mapper); + doStart = true; + } + + if (doStart) { + start(); + } + } + + /** + * Start this manager + */ + public final void start() { + // Thread thread = new Thread() + // { + // public void run() + // { + log.debug("Loading extensions ..."); + loadExtensions(); + log.debug("Extensions loaded !"); + // } + // }; + // thread.start(); + + try { + // must be called before findDeviceDrivers + log.debug("findDevices ..."); + findDevices(); + + log.debug("findDeviceDrivers ..."); + findDeviceDrivers(); + + log.debug("StubDeviceManager initialized !"); + } catch (InterruptedException e) { + log.fatal("can't find devices", e); + } + } + + protected final void refreshFinders(List<DeviceFinder> finders) { + log.info("refreshFinders"); + finders.clear(); + finders.addAll(this.finders); + } + + protected final void refreshMappers(List<DeviceToDriverMapper> mappers) { + log.info("refreshMappers"); + mappers.clear(); + mappers.addAll(this.mappers); + + // Now sort them + Collections.sort(mappers, MapperComparator.INSTANCE); + } +} Added: trunk/shell/src/emu/org/jnode/emu/Emu.java =================================================================== --- trunk/shell/src/emu/org/jnode/emu/Emu.java (rev 0) +++ trunk/shell/src/emu/org/jnode/emu/Emu.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,232 @@ +/* + * $Id: NameSpace.java 4564 2008-09-18 22:01:10Z fduminy $ + * + * JNode.org + * Copyright (C) 2003-2006 JNode.org + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; If not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +package org.jnode.emu; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import javax.naming.NamingException; + +import org.jnode.emu.naming.BasicNameSpace; +import org.jnode.emu.plugin.model.DummyExtensionPoint; +import org.jnode.naming.InitialNaming; +import org.jnode.nanoxml.XMLElement; +import org.jnode.shell.ShellManager; +import org.jnode.shell.alias.AliasManager; +import org.jnode.shell.alias.def.DefaultAliasManager; +import org.jnode.shell.def.DefaultShellManager; +import org.jnode.shell.help.HelpFactory; +import org.jnode.shell.help.def.DefaultHelpFactory; +import org.jnode.shell.syntax.DefaultSyntaxManager; +import org.jnode.shell.syntax.SyntaxBundle; +import org.jnode.shell.syntax.SyntaxManager; +import org.jnode.shell.syntax.SyntaxSpecAdapter; +import org.jnode.shell.syntax.SyntaxSpecLoader; +import org.jnode.shell.syntax.XMLSyntaxSpecAdapter; + +/** + * Emu is the core of a light-weight JNode emulator that allows (some) JNode + * applications to be run using a classic JVM in the context of a JNode development sandbox. + * <p> + * An Emu is also a large flightless bird ... which seems kind of appropriate. + * + * @author Levente S\u00e1ntha + * @author Stephen Crawley + */ +public abstract class Emu { + private static final String[] ALL_PROJECTS = new String[]{ + "core", "distr", "fs", "gui", "net", "shell", "sound", "textui" + }; + + // FIXME configuring a hard-coded list of command plugins is a bad idea. + private static final String[] PLUGIN_NAMES = new String[]{ + "org.jnode.shell.command", + "org.jnode.shell.command.posix", + "org.jnode.shell.command.driver.console", + "org.jnode.apps.editor", + "org.jnode.apps.edit", + "org.jnode.apps.console", + }; + + /** + * Initialize a minimal subset of JNode services to allow us to run JNode commands. + * + * @param root the notional JNode sandbox root directory or <code>null</code>. + * @throws EmuException + */ + public static void initEnv(File root) throws EmuException { + if (true) { + if (root == null) { + root = new File("").getAbsoluteFile(); + System.err.println("Assuming that the JNode root is '" + root + "'"); + } + InitialNaming.setNameSpace(new BasicNameSpace()); + + try { + InitialNaming.bind(DeviceManager.NAME, DeviceManager.INSTANCE); + AliasManager aliasMgr = + new DefaultAliasManager(new DummyExtensionPoint()).createAliasManager(); + SyntaxManager syntaxMgr = + new DefaultSyntaxManager(new DummyExtensionPoint()).createSyntaxManager(); + for (String pluginName : PLUGIN_NAMES) { + configurePluginCommands(root, pluginName, aliasMgr, syntaxMgr); + } + System.setProperty("jnode.invoker", "thread"); + System.setProperty("jnode.interpreter", "redirecting"); + System.setProperty("jnode.debug", "true"); + InitialNaming.bind(AliasManager.NAME, aliasMgr); + InitialNaming.bind(ShellManager.NAME, new DefaultShellManager()); + InitialNaming.bind(SyntaxManager.NAME, syntaxMgr); + InitialNaming.bind(HelpFactory.NAME, new DefaultHelpFactory()); + } catch (NamingException ex) { + throw new EmuException("Problem setting up InitialNaming bindings", ex); + } + } + } + + /** + * Configure any command classes specified by a given plugin's descriptor + * + * @param root the root directory for the JNode sandbox. + * @param pluginName the plugin to be processed + * @param aliasMgr the alias manager to be populated + * @param syntaxMgr the syntax manager to be populated + * @throws EmuException + */ + private static void configurePluginCommands(File root, String pluginName, AliasManager aliasMgr, + SyntaxManager syntaxMgr) throws EmuException { + XMLElement pluginDescriptor = loadPluginDescriptor(root, pluginName); + extractAliases(pluginDescriptor, aliasMgr); + extractSyntaxBundles(pluginDescriptor, syntaxMgr); + } + + /** + * Populate the supplied syntax manager with syntax entries from a plugin descriptor. + * + * @param pluginDescriptor the plugin descriptor's root XML element + * @param syntaxMgr the syntax manager to be populated. + * @throws EmuException + */ + private static void extractSyntaxBundles(XMLElement pluginDescriptor, SyntaxManager syntaxMgr) + throws EmuException { + XMLElement syntaxesDescriptor = findExtension(pluginDescriptor, SyntaxManager.SYNTAXES_EP_NAME); + if (syntaxesDescriptor == null) { + return; + } + SyntaxSpecLoader loader = new SyntaxSpecLoader(); + for (XMLElement syntaxDescriptor : syntaxesDescriptor.getChildren()) { + if (!syntaxDescriptor.getName().equals("syntax")) { + continue; + } + SyntaxSpecAdapter adaptedElement = new XMLSyntaxSpecAdapter(syntaxDescriptor); + try { + SyntaxBundle bundle = loader.loadSyntax(adaptedElement); + if (bundle != null) { + syntaxMgr.add(bundle); + } + } catch (Exception ex) { + throw new EmuException("problem in syntax", ex); + } + } + } + + /** + * Populate the supplied alias manager with aliases from a plugin descriptor. + * + * @param pluginDescriptor the plugin descriptor's root XML element + * @param aliasMgr the alias manager to be populated. + * @throws EmuException + */ + private static void extractAliases(XMLElement pluginDescriptor, AliasManager aliasMgr) { + XMLElement aliasesDescriptor = findExtension(pluginDescriptor, AliasManager.ALIASES_EP_NAME); + if (aliasesDescriptor == null) { + return; + } + for (XMLElement aliasDescriptor : aliasesDescriptor.getChildren()) { + if (aliasDescriptor.getName().equals("alias")) { + String alias = aliasDescriptor.getStringAttribute("name"); + String className = aliasDescriptor.getStringAttribute("class"); + aliasMgr.add(alias, className); + } + } + } + + /** + * Locate the descriptor for a given extension point. + * + * @param pluginDescriptor the plugin descriptor + * @param epName the extension point's name + * @return + */ + private static XMLElement findExtension(XMLElement pluginDescriptor, String epName) { + for (XMLElement child : pluginDescriptor.getChildren()) { + if (child.getName().equals("extension") && + epName.equals(child.getStringAttribute("point"))) { + return child; + } + } + return null; + } + + /** + * Locate and load a plugin descriptor. We search the "descriptors" directory of + * each of the projects listed in ALL_PROJECTS + * + * @param root the notional root directory for the user's JNode sandbox. + * @param pluginName the name of the plugin we're trying to locate + * @return the loaded plugin descriptor or <code>null</code> + * @throws EmuException + */ + private static XMLElement loadPluginDescriptor(File root, String pluginName) + throws EmuException { + File file = null; + for (String projectName : ALL_PROJECTS) { + file = new File(new File(new File(root, projectName), "descriptors"), pluginName + ".xml"); + if (file.exists()) { + break; + } + } + if (!file.exists()) { + throw new EmuException("Cannot find plugin descriptor file for '" + + pluginName + "': " + file.getAbsolutePath()); + } + BufferedReader br = null; + try { + XMLElement elem = new XMLElement(); + br = new BufferedReader(new FileReader(file)); + elem.parseFromReader(br); + if (!elem.getName().equals("plugin")) { + throw new EmuException("File does not contain a 'plugin' descriptor: " + file); + } + return elem; + } catch (IOException ex) { + throw new EmuException("Problem reading / parsing plugin descriptor file " + file, ex); + } finally { + try { + br.close(); + } catch (IOException ex) { + // ignore + } + } + } +} Added: trunk/shell/src/emu/org/jnode/emu/EmuException.java =================================================================== --- trunk/shell/src/emu/org/jnode/emu/EmuException.java (rev 0) +++ trunk/shell/src/emu/org/jnode/emu/EmuException.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,12 @@ +package org.jnode.emu; + +public class EmuException extends Exception { + + public EmuException(String message) { + super(message); + } + + public EmuException(String message, Throwable cause) { + super(message, cause); + } +} Modified: trunk/shell/src/test/org/jnode/test/shell/Cassowary.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/Cassowary.java 2009-01-18 11:04:39 UTC (rev 4886) +++ trunk/shell/src/test/org/jnode/test/shell/Cassowary.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -34,9 +34,11 @@ import org.jnode.shell.help.def.DefaultHelpFactory; /** - * A Cassowary is another large Australian bird ... + * This class assembles a minimal set of JNode services to support unit + * testing some shell and syntax infrastructure. + * <p> + * A Cassowary is another large flightless bird ... like an Emu but different. * - * @author Levente S\u00e1ntha * @author cr...@jn... */ public class Cassowary { Added: trunk/shell/src/test/org/jnode/test/shell/command/posix/TrueTest.xml =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/command/posix/TrueTest.xml (rev 0) +++ trunk/shell/src/test/org/jnode/test/shell/command/posix/TrueTest.xml 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,6 @@ +<testSpec> +<title>Test 'true'</title> +<command>org.jnode.shell.command.posix.TrueCommand</command> +<runMode>AS_ALIAS</runMode> +<rc>0</rc> +</testSpec> \ No newline at end of file Added: trunk/shell/src/test/org/jnode/test/shell/harness/ClassTestRunner.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/ClassTestRunner.java (rev 0) +++ trunk/shell/src/test/org/jnode/test/shell/harness/ClassTestRunner.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,59 @@ +/** + * + */ +package org.jnode.test.shell.harness; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Method; + +/** + * This TestRunner runs a a class by calling its 'static void main(Sting[])' entry + * point. Note that classes that call System.exit(status) are problematic. + * + * @author cr...@jn... + */ +class ClassTestRunner implements TestRunnable { + + private ByteArrayOutputStream outBucket; + private ByteArrayOutputStream errBucket; + + private final TestSpecification spec; + private final TestHarness harness; + + public ClassTestRunner(TestSpecification spec, TestHarness harness) { + this.spec = spec; + this.harness = harness; + } + + @Override + public int run() throws Exception { + Class<?> commandClass = Class.forName(spec.getCommand()); + Method method = commandClass.getMethod("main", String[].class); + String[] args = spec.getArgs().toArray(new String[0]); + method.invoke(null, (Object) args); + return check() ? 0 : 1; + } + + private boolean check() { + // When a class is run this way we cannot capture the RC. + return + harness.expect(outBucket.toString(), spec.getOutputContent(), "output content") && + harness.expect(errBucket.toString(), spec.getErrorContent(), "err content"); + } + + @Override + public void cleanup() { + } + + @Override + public void setup() { + System.setIn(new ByteArrayInputStream(spec.getInputContent().getBytes())); + outBucket = new ByteArrayOutputStream(); + errBucket = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outBucket)); + System.setErr(new PrintStream(errBucket)); + } + +} \ No newline at end of file Added: trunk/shell/src/test/org/jnode/test/shell/harness/CommandTestRunner.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/CommandTestRunner.java (rev 0) +++ trunk/shell/src/test/org/jnode/test/shell/harness/CommandTestRunner.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,121 @@ +/** + * + */ +package org.jnode.test.shell.harness; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.lang.reflect.Method; + +import org.jnode.shell.CommandInfo; +import org.jnode.shell.CommandInvoker; +import org.jnode.shell.CommandLine; +import org.jnode.shell.CommandShell; +import org.jnode.shell.ShellUtils; +import org.jnode.shell.ThreadCommandInvoker; +import org.jnode.shell.alias.AliasManager; +import org.jnode.shell.alias.NoSuchAliasException; + + +/** + * This TestRunner runs a a class by calling its 'static void main(Sting[])' entry + * point. Note that classes that call System.exit(status) are problematic. + * + * @author cr...@jn... + */ +class CommandTestRunner implements TestRunnable { + + private ByteArrayOutputStream outBucket; + private ByteArrayOutputStream errBucket; + + private final TestSpecification spec; + private final TestHarness harness; + + @SuppressWarnings("unused") + private final boolean usingEmu; + + private static boolean emuInitialized; + private static boolean emuAvailable; + private static CommandShell shell; + + public CommandTestRunner(TestSpecification spec, TestHarness harness) { + this.spec = spec; + this.harness = harness; + this.usingEmu = initEmu(harness.getRoot()); + } + + private static synchronized boolean initEmu(File root) { + if (!emuInitialized) { + // This is a bit of a hack. We don't want class loader dependencies + // on the Emu code because that won't work when we run on JNode. But + // we need to use Emu if we are running tests on the dev't platform. + // The following infers that we are running on the dev't platform if + // the 'Emu' class is not loadable. + try { + Class<?> cls = Class.forName("org.jnode.emu.Emu"); + Method initMethod = cls.getMethod("initEnv", File.class); + initMethod.invoke(null, root); + emuAvailable = true; + } catch (Throwable ex) { + // debug ... + ex.printStackTrace(System.err); + emuAvailable = false; + } + try { + if (emuAvailable) { + shell = new CommandShell(); + } else { + shell = (CommandShell) ShellUtils.getCurrentShell(); + } + } catch (Exception ex) { + // debug ... + ex.printStackTrace(System.err); + throw new RuntimeException(ex); + } + emuInitialized = true; + } + return emuAvailable; + } + + @Override + public int run() throws Exception { + String[] args = spec.getArgs().toArray(new String[0]); + AliasManager aliasMgr = ShellUtils.getAliasManager(); + CommandInvoker invoker = new ThreadCommandInvoker(shell); + CommandLine cmdLine = new CommandLine(spec.getCommand(), args); + CommandInfo cmdInfo; + try { + Class<?> cls = aliasMgr.getAliasClass(spec.getCommand()); + cmdInfo = new CommandInfo(cls, aliasMgr.isInternal(spec.getCommand())); + } catch (NoSuchAliasException ex) { + final ClassLoader cl = + Thread.currentThread().getContextClassLoader(); + cmdInfo = new CommandInfo(cl.loadClass(spec.getCommand()), false); + } + invoker.invoke(cmdLine, cmdInfo); + return check() ? 0 : 1; + } + + private boolean check() { + // When a class is run this way we cannot capture the RC. + return + harness.expect(outBucket.toString(), spec.getOutputContent(), "output content") && + harness.expect(errBucket.toString(), spec.getErrorContent(), "err content"); + } + + @Override + public void cleanup() { + } + + @Override + public void setup() { + System.setIn(new ByteArrayInputStream(spec.getInputContent().getBytes())); + outBucket = new ByteArrayOutputStream(); + errBucket = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outBucket)); + System.setErr(new PrintStream(errBucket)); + } + +} \ No newline at end of file Added: trunk/shell/src/test/org/jnode/test/shell/harness/Test.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/Test.java (rev 0) +++ trunk/shell/src/test/org/jnode/test/shell/harness/Test.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,16 @@ +package org.jnode.test.shell.harness; + +public class Test { + + /** + * @param args + */ + public static void main(String[] args) { + if (args.length == 0) { + System.out.println("Hi mum"); + } else if (args[0].equals("System.exit")) { + System.exit(1); + } + } + +} Added: trunk/shell/src/test/org/jnode/test/shell/harness/TestHarness.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/TestHarness.java (rev 0) +++ trunk/shell/src/test/org/jnode/test/shell/harness/TestHarness.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,219 @@ +package org.jnode.test.shell.harness; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.PrintWriter; + +import net.n3.nanoxml.XMLException; + +/** + * This is the entry point class for the command test harness. Its + * purpose is to run 'black box' tests on commands and the like. + * + * @author crawley@jnode + */ +public class TestHarness { + + private final String commandName = this.getClass().getCanonicalName(); + + String[] TESTS = new String[] { + "<testSpec>" + + "<title>Hi mum</title>" + + "<command>org.jnode.test.shell.harness.Test</command>" + + "<output>Hi mum\n</output>" + + "</testSpec>", +// "<testSpec>" + +// "<title>exit</title>" + +// "<command>org.jnode.test.shell.harness.Test</command>" + +// "<args><arg>System.exit</arg></args>" + +// "</testSpec>" + }; + + private final String[] args; + + private PrintWriter reportWriter; + private int testCount; + private int errorCount; + private int failureCount; + private TestSpecification spec = null; + private InputStream savedIn; + private PrintStream savedOut; + private PrintStream savedErr; + + private boolean verbose; + + public TestHarness(String[] args) { + this.args = args; + this.reportWriter = new PrintWriter(System.err); + } + + /** + * @param args + */ + public static void main(String[] args) throws Exception { + new TestHarness(args).run(); + } + + private void run() throws Exception { + if (args.length == 0) { + for (String test : TESTS) { + try { + InputStream is = new ByteArrayInputStream(test.getBytes()); + spec = new TestSpecification().load(is); + execute(spec); + } catch (Exception ex) { + diagnose(ex, "<built-in-tests>"); + } + } + } else if (args[0].equals("--package")) { + if (args.length != 2) { + usage(); + } + InputStream is = null; + try { + is = this.getClass().getResourceAsStream(args[1]); + spec = new TestSpecification().load(is); + execute(spec); + } catch (Exception ex) { + diagnose(ex, args[1]); + } + finally { + if (is != null) { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + } + } else if (args[0].startsWith("-")) { + System.err.println("Unrecognized option '" + args[0] + "'"); + usage(); + return; + } else { + for (String arg : args) { + File file = new File(arg); + InputStream is = null; + try { + is = new FileInputStream(file); + spec = new TestSpecification().load(is); + execute(spec); + } catch (Exception ex) { + diagnose(ex, arg); + } + finally { + if (is != null) { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + } + } + } + report("Ran " + testCount + " tests with " + errorCount + + " errors and " + failureCount + " failures"); + } + + private void usage() { + System.err.println(commandName + " // run tests from builtin specs"); + System.err.println(commandName + " --package <java-package> // run tests from specs on classpath"); + System.err.println(commandName + " <spec-file> ... // run tests from specs read from file system"); + } + + private void execute(TestSpecification spec) { + reportVerbose("Running test '" + spec.getTitle() + "'"); + testCount++; + try { + TestRunnable runner; + switch (spec.getRunMode()) { + case AS_CLASS: + runner = new ClassTestRunner(spec, this); + break; + case AS_ALIAS: + runner = new CommandTestRunner(spec, this); + break; + default: + reportVerbose("Run mode '" + spec.getRunMode() + "' not implemented"); + return; + } + try { + setup(); + runner.setup(); + errorCount += runner.run(); + } finally { + runner.cleanup(); + cleanup(); + } + } catch (Throwable ex) { + ex.printStackTrace(reportWriter); + failureCount++; + } + reportVerbose("Completed test '" + spec.getTitle() + "'"); + } + + private void diagnose(Exception ex, String fileName) throws Exception { + if (ex instanceof IOException) { + report("IO error while reading test specification: " + ex.getMessage()); + } else if (ex instanceof XMLException) { + String msg = ex.getMessage(); + if (msg.equals("Nested Exception")) { + msg = ((XMLException) ex).getException().getMessage(); + } + report("XML error in test specification '" + fileName + "' : " + msg); + } else if (ex instanceof TestSpecificationException) { + report("Invalid test specification '" + fileName + ": " + ex.getMessage()); + } else { + throw ex; + } + } + + /** + * Restore the system system streams + */ + private void cleanup() { + System.setIn(savedIn); + System.setOut(savedOut); + System.setErr(savedErr); + } + + /** + * Save the System streams so that they can be restored. + */ + private void setup() { + savedIn = System.in; + savedOut = System.out; + savedErr = System.err; + } + + public void report(String message) { + reportWriter.println(message); + reportWriter.flush(); + } + + public void reportVerbose(String message) { + if (verbose) { + report(message); + } + } + + public boolean expect(Object expected, Object actual, String desc) { + if (expected.equals(actual)) { + return true; + } + report("Incorrect test result for '" + desc + "' in test '" + spec.getTitle() + "'"); + report(" expected '" + expected + "': got '" + actual + "'"); + return false; + } + + public File getRoot() { + // FIXME ... this should be the workspace root. + return new File(".."); + } + +} Added: trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnable.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnable.java (rev 0) +++ trunk/shell/src/test/org/jnode/test/shell/harness/TestRunnable.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,18 @@ +package org.jnode.test.shell.harness; + +/** + * This is the API implemented by the command test runners. We cannot + * use / extend Runnable because we need to propagate any exceptions + * in the {@link TestRunnable.run()} method. + * + * @author stephen + */ +public interface TestRunnable { + + public int run() throws Exception; + + public void setup(); + + public void cleanup(); + +} Added: trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecification.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecification.java (rev 0) +++ trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecification.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,157 @@ +package org.jnode.test.shell.harness; + +import java.io.File; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.n3.nanoxml.IXMLElement; +import net.n3.nanoxml.IXMLParser; +import net.n3.nanoxml.StdXMLReader; +import net.n3.nanoxml.XMLParserFactory; + +/** + * This represents a command test specification for a command or script. + * + * @author crawley@jnode + */ +public class TestSpecification { + + public static enum RunMode { + AS_SCRIPT, + AS_CLASS, + AS_ALIAS + } + + private RunMode runMode; + private String command; + private List<String> args = new ArrayList<String>(); + private String inputContent; + private String outputContent; + private String errorContent; + private String title; + private int rc; + private Map<File, String> fileMap = new HashMap<File, String>(); + + private IXMLElement root; + + public TestSpecification() { + super(); + } + + public TestSpecification load(InputStream in) throws Exception { + StdXMLReader xr = new StdXMLReader(in); + IXMLParser parser = XMLParserFactory.createDefaultXMLParser(); + parser.setReader(xr); + root = (IXMLElement) parser.parse(); + if (!root.getName().equals("testSpec")) { + throw new TestSpecificationException( + "The root element should be 'testSpec' not '" + root.getName() + "'"); + } + runMode = RunMode.valueOf(extractElementValue("runMode", "AS_CLASS")); + title = extractElementValue("title"); + command = extractElementValue("command"); + inputContent = extractElementValue("input", ""); + outputContent = extractElementValue("output", ""); + errorContent = extractElementValue("error", ""); + try { + rc = Integer.parseInt(extractElementValue("error", "0").trim()); + } catch (NumberFormatException ex) { + throw new TestSpecificationException("'rc' is not an integer"); + } + IXMLElement elem = root.getFirstChildNamed("args"); + if (elem != null) { + for (Object obj : elem.getChildren()) { + if (obj instanceof IXMLElement) { + IXMLElement child = (IXMLElement) obj; + if (!child.getName().equals("arg")) { + throw new TestSpecificationException( + "Child elements of 'args' should be 'arg' not '" + root.getName() + "'"); + } + args.add(child.getContent()); + } + } + } + elem = root.getFirstChildNamed("files"); + if (elem != null) { + for (Object obj : elem.getChildren()) { + if (obj instanceof IXMLElement) { + IXMLElement child = (IXMLElement) obj; + if (!child.getName().equals("file")) { + throw new TestSpecificationException( + "Child elements of 'files' should be 'file' not '" + root.getName() + "'"); + } + String fileName = extractElementValue(child, "name"); + String content = extractElementValue(child, "content", ""); + fileMap.put(new File(fileName), content); + } + } + } + return this; + } + + private String extractElementValue(IXMLElement parent, String name) throws TestSpecificationException { + IXMLElement elem = parent.getFirstChildNamed(name); + if (elem == null) { + throw new TestSpecificationException( + "Element '" + name + "' not found in '" + parent.getName() + "'"); + } else { + return elem.getContent(); + } + } + + private String extractElementValue(IXMLElement parent, String name, String dflt) { + IXMLElement elem = parent.getFirstChildNamed(name); + return elem == null ? dflt : elem.getContent(); + } + + private String extractElementValue(String name) throws TestSpecificationException { + return extractElementValue(root, name); + } + + private String extractElementValue(String name, String dflt) { + return extractElementValue(root, name, dflt); + } + + public String getOutputContent() { + return outputContent; + } + + public String getErrorContent() { + return errorContent; + } + + public int getRc() { + return rc; + } + + public void addFile(File file, String content) { + fileMap.put(file, content); + } + + public Map<File, String> getFileMap() { + return fileMap; + } + + public RunMode getRunMode() { + return runMode; + } + + public String getCommand() { + return command; + } + + public List<String> getArgs() { + return args; + } + + public String getInputContent() { + return inputContent; + } + + public String getTitle() { + return title; + } +} Added: trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecificationException.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecificationException.java (rev 0) +++ trunk/shell/src/test/org/jnode/test/shell/harness/TestSpecificationException.java 2009-01-18 13:23:15 UTC (rev 4887) @@ -0,0 +1,11 @@ +package org.jnode.test.shell.harness; + +public class TestSpecificationException extends Exception { + + private static final long serialVersionUID = -6276556804636413362L; + + public TestSpecificationException(String message) { + super(message); + } + +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |