package org.myrobotlab.service; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.text.ParseException; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Test; import org.myrobotlab.framework.ServiceType; import org.myrobotlab.framework.repo.ServiceData; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.service.interfaces.ServiceInterface; import org.slf4j.Logger; public class ServiceInterfaceTest { public final static Logger log = LoggerFactory.getLogger(ServiceInterfaceTest.class); @Test public final void testInstallAllServices() throws ClassNotFoundException, ParseException, IOException { // TODO: this probably is going to take longer but it's worth while! ServiceData sd = ServiceData.getLocalInstance();// CodecUtils.fromJson(FileUtils.readFileToString(new // File("../repo/serviceData.json")), // ServiceData.class); for (ServiceType st : sd.getServiceTypes()) { if (!st.isAvailable()) { log.info("Installing Service:" + st.getName()); Runtime.install(st.getName()); } else { log.info("already installed."); } } } @Test public final void testAllServices() throws ClassNotFoundException { ArrayList<String> servicesWithoutWebPages = new ArrayList<String>(); ArrayList<String> servicesWithoutScripts = new ArrayList<String>(); ArrayList<String> servicesThatDontStartProperly = new ArrayList<String>(); ArrayList<String> servicesNotInServiceDataJson = new ArrayList<String>(); HashSet<String> whiteListServices = new HashSet<String>(); // CLI seems to mess up the console in the unit test so things // don't log well anymore. whiteListServices.add("Cli"); // gui service will probably blow up if you are running in a console. whiteListServices.add("GUIService"); // leap motion blows up because java.libary.path not having the leap deps. whiteListServices.add("LeapMotion"); // jna lib path stuff whiteListServices.add("OculusRift"); // plantoid gets a null pointer on tracking service start whiteListServices.add("Plantoid"); // Shoutbox gets an address in use/failed to bind to port 8888 whiteListServices.add("Shoutbox"); // jni class path error whiteListServices.add("SLAMBad"); // WebGUI gets an address in use/failed to bind to port 8888 whiteListServices.add("WebGui"); // dependencies missing in repo whiteListServices.add("MyoThalmic"); // NPE exception. whiteListServices.add("Tracking"); // NPE Exception in serial service? whiteListServices.add("EddieControlBoard"); // NPE in serial whiteListServices.add("GPS"); // NPE in regex whiteListServices.add("Lidar"); // starts a webgui and gets bind error whiteListServices.add("PickToLight"); // NPE in serial whiteListServices.add("Sabertooth"); // NPE in serial whiteListServices.add("VirtualDevice"); whiteListServices.add("OpenNI"); // start up python so we have it available to do some testing with. Python python = (Python) Runtime.createAndStart("python", "Python"); String testScriptDirectory = "./src/resource/Python/examples/"; List<String> servicesToTest = listAllServices(); // List<String> servicesToTest = new ArrayList<String>(); // servicesToTest.add("Cli"); // Load the service data file // TODO: read this from the repo at build time? ServiceData sd = ServiceData.getLocalInstance(); int numServices = servicesToTest.size(); int numServicePages = 0; int numScripts = 0; int numScriptsWorky = 0; int numStartable = 0; log.info("----------------------------------------------"); for (String service : servicesToTest) { System.out.println("SYSTEM TESTING " + service); System.out.flush(); if (whiteListServices.contains(service)) { log.info("White listed testing of service {}", service); continue; } log.info("Testing Service: {}", service); ServiceType st = sd.getServiceType("org.myrobotlab.service." + service); if (st == null) { System.out.println("NO SERVICE TYPE FOUND!"); servicesNotInServiceDataJson.add(service); } else { System.out.println("Service Type Found!"); } // System.out.flush(); // try { // System.in.read(); // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // if (serviceHasWebPage(service)) { log.info("Service {} has a web page..", service); numServicePages++; } else { log.warn("Service {} does not have a web page..", service); servicesWithoutWebPages.add(service); } if ("Gps".equals(service)) { log.info("here"); } if (serviceInterfaceTest(service)) { numStartable++; } else { servicesThatDontStartProperly.add(service); } // validate that a script exists File script = new File(testScriptDirectory + service + ".py"); if (script.exists()) { log.info("Service Has a Script: {}", service); numScripts++; } else { log.warn("Missing Script for Service {}", service); servicesWithoutScripts.add(service); } // if (testServiceScript(python, testScriptDirectory, service)) { // log.info("Default script for {} executes ok!", service); numScriptsWorky++; } else { // log.warn("Default script for {} blows up!", service); // servicesWithoutWorkyScript(service); } // log.info("SERVICE TESTED WAS :" + service); log.info("----------------------------------------------"); // System.out.println("Next?"); // try { // System.in.read(); // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } log.info("----------------------------------------------"); log.info("Service Report"); log.info("Number of Services: {}", numServices); log.info("Number of Startable Services: {}", numStartable); log.info("Number of Services Pages {}", numServicePages); log.info("Number of Scripts: {}", numScripts); log.info("Number of Scripts Worky: {}", numScriptsWorky); log.info("----------------------------------------------"); for (String s : servicesThatDontStartProperly) { log.info("FAILED ON START:" + s); } for (String s : servicesWithoutWebPages) { log.info("NO WEB PAGE :" + s); } for (String s : servicesWithoutScripts) { log.info("NO SCRIPT :" + s); } for (String s : servicesNotInServiceDataJson) { log.info("NOT IN SERVICE DATA :" + s); } log.info("Done..."); } private boolean serviceInterfaceTest(String service) { // see if we can start/stop and release the service. ServiceInterface foo = Runtime.create(service.toLowerCase(), service); if (foo == null) { log.warn("Runtime Create returned a null service for {}", service); return false; } System.out.println("Service Test:" + service); System.out.flush(); // Assert.assertNotNull(foo.getCategories()); Assert.assertNotNull(foo.getDescription()); Assert.assertNotNull(foo.getName()); Assert.assertNotNull(foo.getSimpleName()); Assert.assertNotNull(foo.getType()); // TODO: add a bunch more tests here! foo.startService(); foo.stopService(); foo.releaseService(); foo.startService(); foo.save(); foo.load(); foo.stopService(); return true; } private boolean testServiceScript(Python python, String testScriptDirectory, String service) { // TODO: this blows stuff up too much. String testScriptFile = testScriptDirectory + service + ".py"; File script = new File(testScriptFile); if (!script.exists()) { log.warn("No default script for Service {}", script); return false; } else { log.info("Default Script Exists for {}", service); } return false; // dead code // if (true) { // // Diabled testing of scripts currently. // return false; // } // try { // String test = FileUtils.readFileToString(script); // System.out.println(test); // // python.execAndWait(test); // } catch (IOException e) { // // TODO Auto-generated catch block // log.warn("Error Running script {}" , script); // e.printStackTrace(); // // return false; // } // return true; } private boolean serviceHasWebPage(String service) { String url = "http://www.myrobotlab.org/service/" + service; InputStream in = null; try { in = new URL(url).openStream(); } catch (MalformedURLException e) { // TODO Auto-generated catch block // e.printStackTrace(); return false; } catch (IOException e) { // TODO Auto-generated catch block // e.printStackTrace(); return false; } try { // read the page (we don't care about contents. (yet)) IOUtils.toString(in); } catch (IOException e) { // e.printStackTrace(); return false; } finally { IOUtils.closeQuietly(in); } return true; } public static List<String> listAllServices() throws ClassNotFoundException { // TODO: should this be replaced with a call to Runtime ? List<Class<?>> classes = ServiceInterfaceTest.getClassesForPackage("org.myrobotlab.service"); List<String> services = new ArrayList<String>(); for (Class<?> c : classes) { // System.out.println("CLASS:" + c.toString()); HashSet<String> superClasses = new HashSet<String>(); Class x = c; while (true) { if (x.getSuperclass() != null) { superClasses.add(x.getSuperclass().toString()); x = x.getSuperclass(); } else { break; } } if (superClasses.contains("class org.myrobotlab.framework.Service")) { // Get just the class name. String[] parts = c.toString().split(" ")[1].split("\\."); services.add(parts[parts.length - 1]); // System.out.println(parts[parts.length-1]); } } return services; } /** * Attempts to list all the classes in the specified package as determined by * the context class loader * * @param pckgname * the package name to search * @return a list of classes that exist within that package * @throws ClassNotFoundException * if something went wrong * * Ref: * http://stackoverflow.com/questions/1498122/java-loop-on-all-the- * classes-in-the-classpath */ private static List<Class<?>> getClassesForPackage(String pckgname) throws ClassNotFoundException { // This will hold a list of directories matching the pckgname. There may be // more than one if a package is split over multiple jars/paths ArrayList<File> directories = new ArrayList<File>(); try { ClassLoader cld = Thread.currentThread().getContextClassLoader(); if (cld == null) { throw new ClassNotFoundException("Can't get class loader."); } String path = pckgname.replace('.', '/'); // Ask for all resources for the path Enumeration<URL> resources = cld.getResources(path); while (resources.hasMoreElements()) { directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8"))); } } catch (NullPointerException x) { throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)"); } catch (UnsupportedEncodingException encex) { throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)"); } catch (IOException ioex) { throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname); } ArrayList<Class<?>> classes = new ArrayList<Class<?>>(); // For every directory identified capture all the .class files for (File directory : directories) { if (directory.exists()) { // Get the list of the files contained in the package String[] files = directory.list(); for (String file : files) { // we are only interested in .class files if (file.endsWith(".class")) { // removes the .class extension try { classes.add(Class.forName(pckgname + '.' + file.substring(0, file.length() - 6))); } catch (NoClassDefFoundError e) { // do nothing. this class hasn't been found by the loader, and we // don't care. } } } } else { throw new ClassNotFoundException(pckgname + " (" + directory.getPath() + ") does not appear to be a valid package"); } } return classes; } }