Package org.openiaml.model.tests

Source Code of org.openiaml.model.tests.ModelTestCase

/**
*
*/
package org.openiaml.model.tests;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.xml.xpath.XPathExpressionException;

import junit.framework.AssertionFailedError;
import net.sourceforge.jwebunit.api.IElement;
import net.sourceforge.jwebunit.api.ITestingEngine;
import net.sourceforge.jwebunit.htmlunit.HtmlUnitTestingEngineImpl;
import net.sourceforge.jwebunit.junit.WebTestCase;
import net.sourceforge.jwebunit.junit.WebTester;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.ecore.EObject;
import org.openiaml.model.codegen.DefaultRuntimeProperties;
import org.openiaml.model.codegen.ICodeGeneratorInMemory;
import org.openiaml.model.codegen.php.CustomOAWLog;
import org.openiaml.model.codegen.php.IACleanerBeautifier;
import org.openiaml.model.codegen.php.OawCodeGeneratorWithRuntime;
import org.openiaml.model.tests.xpath.DefaultXpathTestCase;
import org.openiaml.model.xpath.IXpath;
import org.openiaml.model.xpath.IterableElementList;
import org.openiaml.modeltesting.EclipseProject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
import com.gargoylesoftware.htmlunit.WebClient;

/**
* The JET/Junit test framework now manages everything for you. It will
* automatically create empty projects to be compiled to.
*
* @see ModelTestCaseWithProperties
* @author jmwright
*
*/
public abstract class ModelTestCase extends WebTestCase implements IXpath {

  public ModelTestCase() {
    // call super with a custom tester
    super(getCustomTester());
  }

  /**
   * @return
   */
  private static WebTester getCustomTester() {
    return new WebTester() {

      /* (non-Javadoc)
       * @see net.sourceforge.jwebunit.junit.WebTester#initializeDialog()
       */
      @Override
      protected ITestingEngine initializeDialog() {
        HtmlUnitTestingEngineImpl engine = new HtmlUnitTestingEngineImpl() {
          /* (non-Javadoc)
           * @see net.sourceforge.jwebunit.htmlunit.HtmlUnitTestingEngineImpl#createWebClient()
           */
          @Override
          protected WebClient createWebClient() {
            WebClient wc = super.createWebClient();
            wc.setAjaxController(new NicelyResynchronizingAjaxController());
            return wc;
          }
         
        };
        return engine;
      }
     
    };
  }

  private EclipseProject project = new EclipseProject(getProjectName());
 
  public static final String PLUGIN_ID = "org.openiaml.model.tests";
  public static final String ROOT = "src/org/openiaml/model/tests/";
 
  // TODO issue 149: remove this field
  protected IProgressMonitor monitor;
 
  public static final String BASE_URL = "http://localhost:8080/junit-workspace/";
  // from ./junit-workspace/project_name/output/ to target
  public static final String CONFIG_RUNTIME = "../../../workspace-iaml/org.openiaml.model.runtime/src/include/";
  public static final String BASE_URL_RUNTIME = "http://localhost:8080/iaml-runtime/";
  public static final String CONFIG_WEB = "/iaml-runtime/";
 
  /**
   * The permissible delta in double tests.
   */
  public static final double TEST_DELTA = 0.0001;
 
  /**
   * TODO a temporary variable to make sure we don't set up
   * the same project twice.
   */
  protected boolean isSetup = false;
 
  /**
   * @throws java.lang.Exception
   */
  @Override
  protected void setUp() throws Exception {
    // parent setup
    super.setUp();
   
    logTimed("model", "setUp", "", "");
   
    monitor = new NullProgressMonitor();
   
    project.createProject();
    isSetup = true;

    // set test context
    getTestContext().setBaseUrl(BASE_URL + project.getProjectName() + "/");
   
    // do we need to set a proxy?
    if (System.getenv("http.proxyHost") != null) {
      getTestContext().setProxyAuthorization(null, null, System.getenv("http.proxyHost"), Integer.parseInt(System.getenv("http.proxyPort")));
    }
  }

  /**
   * @throws java.lang.Exception
   */
  @Override
  protected void tearDown() throws Exception {
    project.close();
   
    monitor = null;
    transformStatus = null;
   
    super.tearDown();
  }

  /**
   * A counter of projects created, so we don't run into a case
   * where the same project is created in the same second.
   *
   * @see #getProjectName()
   */
  public static int projectCounter = 0;
 
  /**
   * Get the project name which will be created,
   * by default "testing-" + the className
   *
   * @return
   */
  public String getProjectName() {
    return "test." + this.getClass().getSimpleName() + "." + new SimpleDateFormat("yyMMddHHmmss").format(new Date()) + projectCounter++;
  }

  protected IStatus transformStatus = null;
 
  /**
   * Store the last IStatus of doTransform().
   * @see #doTransform(String)
   */
  public IStatus getTransformStatus() {
    return transformStatus;
  }
 
  /**
   * Reset the codegen cache.
   */
  public void resetCodegenCache() {
    codegenCache.clear();
  }
 
  /**
   * Remove the given class from the codegen cache. Returns the
   * Strings that were in the cache (if it was present) or <code>null</code>.
   *
   * @param class1 The class to remove from the codegen cache.
   */
  public List<String> removeCodegenCache(Class<?> class1) {
    return codegenCache.remove(class1);
  }
 
  /**
   * This cache attempts to store the results of previous code generations, allowing us
   * to perform multiple tests faster.
   *
   * It stores a map of (input class, list of relative paths) of generated files.
   */
  private static Map<Class<?>, List<String>> codegenCache = new HashMap<Class<?>, List<String>>();

  /**
   * A map of project classes to generated project paths.
   */
  private static Map<Class<?>, String> projectCache = new HashMap<Class<?>, String>();

  /**
   * Do the transformation and make sure it succeeded.
   * The result of the transformation is stored in {@link #getTransformStatus()}.
   *
   * @param inputFile
   * @throws Throwable if it did not succeed
   */
  protected void doTransform(Class<?> cacheClass, EObject model) throws Exception {
    if (codegenCache.containsKey(cacheClass) && projectCache.containsKey(cacheClass)) {
      logTimed("codegen", "loading from cache", "", "");
     
      // load from cache
      loadFromCache(cacheClass);
      logTimed("codegen", "load from cache complete", "", "");
    } else {
      // codegen manually
      logTimed("codegen", "doing actual codegen", "", "");
      doTransformActual(model);
     
      // update cache
      logTimed("codegen", "updating cache", "", "");
      updateCache(cacheClass);
     
      logTimed("codegen", "saving to cache complete", "", "");
    }
  }
 
  /**
   * The code generation has been cached previously; copy the
   * cached code-generated files from this old project into our new project.
   *
   * @param inputFile
   * @throws Exception
   */
  private void loadFromCache(Class<?> inputFile) throws Exception {
    String root = projectCache.get(inputFile);
    for (String file : codegenCache.get(inputFile)) {
      // copy this file to the local project
      File source = new File(root + file);
      IFile target = getProject().getProject().getFile(file);
     
      // check that we can copy OK
      System.out.println("Copying cache file '" + file + "'...");
      assertTrue("The source file '" + source + "' should exist from our codegen cache.", source.exists());
      assertFalse("The target file '" + target + "' should not exist yet.", target.exists());

      copyFileIntoWorkspace(source, target);
    }
  }

  /**
   * Take the code generation results and load them into a cache
   * for future reference.
   *
   * @param inputFile
   */
  protected void updateCache(Class<?> inputFile) {
    // do nothing if generation was unsuccessful
    if (lastTracingCache == null)
      return;
   
    List<String> result = new ArrayList<String>();
    for (File f : lastTracingCache) {
      // get the relative path
      String root = project.getProject().getLocation().toOSString();
      result.add( f.getAbsolutePath().replace(root, "") );
    }
   
    codegenCache.put(inputFile, result);
    projectCache.put(inputFile, project.getProject().getLocation().toOSString());
  }

  /**
   * Do the transformation and make sure it succeeded.
   * The result of the transformation is stored in {@link #getTransformStatus()}.
   * TODO refactor this out into plugins
   *
   * @param inputFile
   * @throws Throwable if it did not succeed
   */
  protected void doTransformActual(EObject model) throws Exception {
    transformStatus = doTransform(model, monitor);
    if (!transformStatus.isOK()) {
      String firstMessage = null;
      for (IStatus s : transformStatus.getChildren()) {
        System.err.println(s);    // print out the status errors
        if (s.getException() != null)
          throw new TransformationException("Transformation failed: " + s.getException().getMessage(), s.getException())// we will only crash on the first one
        if (s.getMessage() != null && !s.getMessage().trim().isEmpty()) {
          firstMessage = s.getMessage();
        }
      }
     
      if (transformStatus.getException() != null) {
        throw new TransformationException("Transformation failed: " + transformStatus.getException().getMessage(), transformStatus.getException())// force an exception
      }
      // force a fail
      throw new TransformationException("Transformation did not pass successfully: " + transformStatus.getMessage() + (firstMessage == null ? "" : ", first message: " + firstMessage), transformStatus.getChildren());
    }
  }
 
  /**
   * Transform a model file into some code generated output. This currently contains the logic on
   * which code generation solution to use.
   */
  protected IStatus doTransform(EObject model, IProgressMonitor monitor) throws IOException, URISyntaxException, CoreException {
    // which transform to use?
    // return doTransformJET(filename, outputDir, monitor);
    return doTransformOAWWorkflow(model, monitor);
  }

  /**
   * The results of getTracingCache() for the last code generation run.
   */
  private List<File> lastTracingCache;
 
  /**
   * <p>
   * Transform a filename using OpenArchitectureWare. It also forces
   * a filesystem refresh of the particular project directory,
   * and does not stop until the refresh is complete.
   * </p>
   *
   * <p>
   * Sadly, we cannot refactor out the initialisation of the WorkflowRunner
   * in OAW, because the WorkflowRunners are not designed to be run more than
   * once per invoke() method:
   * <pre>
   * org.openarchitectureware.workflow.ConfigurationException: A default outlet is allready registered!
   * at org.openarchitectureware.xpand2.output.OutputImpl.addOutlet(OutputImpl.java:49)
   * </pre>
   * </p>
   *
   * TODO refactor into CodegenTestCase
   */
  protected IStatus doTransformOAWWorkflow(EObject model,
      IProgressMonitor monitor) throws CoreException {
   
    // enable tracing cache
    IACleanerBeautifier.setTracingEnabled(true);
   
    // set last cache to null
    lastTracingCache = null;
   
    try {
      // create some properties
      Map<String,String> runtimeProperties = getRuntimeProperties();
     
      ICodeGeneratorInMemory runner = new OawCodeGeneratorWithRuntime();
      IStatus status = runner.generateCode(model, project.getProject(), monitor, runtimeProperties);
     
      if (!status.isOK()) {
        // bail early
        return status;
      }

      lastTracingCache = IACleanerBeautifier.getTracingCache();

      // once generated, we need to refresh the workspace before
      // we can carry on testing (normally, we would just let Eclipse automatically
      // refresh the resources)
     
      // we need to *force* refresh the workspace
      return project.refreshProject();
    } finally {
      // disable tracing cache
      IACleanerBeautifier.setTracingEnabled(false);
    }
  }

  /**
   * Generate the properties required for ICodeGenerator.
   *
   * <p>This method will look for a local file <code>proxy.properties</code>;
   * if this file exists, this Properties file will be loaded and the
   * {@link DefaultRuntimeProperties#getDefaultProperties() default properties}
   * will be overridden with these.
   *
   * @see org.openiaml.model.codegen.ICodeGenerator#generateCode(IFile, IProgressMonitor, Map)
   * @return
   */
  protected Map<String, String> getRuntimeProperties() {
    // first, get the default properties
    Map<String,String> properties = new DefaultRuntimeProperties().getDefaultProperties();
   
    // then overwrite
    properties.put("include_runtime", "false");
    properties.put("config_runtime", CONFIG_RUNTIME);
    properties.put("config_web", CONFIG_WEB);
    properties.put("debug", "true");
   
    // we might want to get instance-specific information
    File f = new File("proxy.properties");
    if (f.exists()) {
      Properties p = new Properties();
      try {
        p.load(new FileReader(f));
      } catch (IOException e) {
        throw new RuntimeException("Problem loading file '" + f + "': " + e.getMessage(), e);
      }
      for (Object k : p.keySet()) {
        if (k instanceof String) {
          String ks = (String) k;
          properties.put(ks, p.getProperty(ks));
        }
      }
    }

    return properties;
  }

  /**
   * @return the project
   */
  public EclipseProject getProject() {
    return project;
  }
 
  /**
   * Copy a local file into the Eclipse workspace. Makes sure it doesn't
   * already exist, and that it does exist once this method is completed.
   *
   * @param sourceName
   * @param target
   * @return the target file
   * @throws Exception
   */
  protected IFile copyFileIntoWorkspace(String sourceName, IFile target) throws Exception {
    return copyFileIntoWorkspace(new File(sourceName), target);
  }

  /**
   * Copy a local file into the Eclipse workspace. Makes sure it doesn't
   * already exist, and that it does exist once this method is completed.
   *
   * This method also creates any necessary sub-folders recursively.
   *
   * @param source
   * @param target
   * @return the target file
   * @throws CoreException
   * @throws FileNotFoundException
   */
  protected IFile copyFileIntoWorkspace(File sourceFile, IFile target) throws CoreException, FileNotFoundException {
    return project.copyFileIntoWorkspace(sourceFile, target, monitor)
  }

  /**
   * Helper method: assert A >= B.
   *
   * @param expected expected value (B)
   * @param actual actual value (A)
   */
  public void assertGreaterEq(int expected, int actual) {
    assertTrue("expected >= than " + expected + ", but actually had " + actual, actual >= expected);
  }
 
  /**
   * Helper method: assert A > B.
   *
   * @param expected expected value (B)
   * @param actual actual value (A)
   */
  public void assertGreater(int expected, int actual) {
    assertTrue("expected > than " + expected + ", but actually had " + actual, actual > expected);
  }
 
  /**
   * Helper method: assert A != B
   *
   * @param expected expected value (B)
   * @param actual actual value (A)
   */
  public void assertNotEqual(String message, int expected, int actual) {
    assertFalse(message, expected == actual);
  }
 
  /**
   * Helper method: assert A != B
   *
   * @param expected expected value (B)
   * @param actual actual value (A)
   */
  public void assertNotEqual(String message, String expected, String actual) {
    if (expected == null)
      assertNotNull(message, actual);
    else
      assertFalse(message, expected.equals(actual));
  }
 
  /**
   * Helper method: assert A != B
   *
   * @param expected expected value (B)
   * @param actual actual value (A)
   */
  public void assertNotEqual(String a, String b) {
    if (a == null)
      assertNotNull("null should not equal null", b);
    else
      assertFalse("'" + a + "' should not equal '" + b + "'", a.equals(b));
  }
 
  /**
   * Assert that the given string is equal to one of the
   * expected strings.
   *
   * @param expected
   * @param actual
   */
  public void assertEqualsOneOf(String[] expected, String actual) {
    for (String a : expected) {
      if (actual.equals(a))
        return;
    }
    fail("String '" + actual + "' was not equal to any of the strings in '" + Arrays.toString(expected) + "'");
  }
 
  /**
   * Helper method: assert A != B
   *
   * @param expected expected value
   * @param actual actual value
   */
  public void assertNotEqual(int expected, int actual) {
    assertFalse("Expected '" + expected + "', actually '" + actual + "'", expected == actual);
  }
 
  /**
   * Assert that the given exception contains the given message.
   *
   * @param e
   * @param string
   */
  public void checkExceptionContains(Exception e,
      String string) {
    if (!e.getMessage().contains(string)) {
      // throw another exception, so we can still get the trace
      throw new RuntimeException("Exception did not contain '" + string + "'", e);
    }
  }
 
  /**
   * Override the method to provide more information.
   * TODO move this implementation into JWebUnit
   */
  @Override
  public void assertTitleMatch(String s) {
    try {
      super.assertTitleMatch(s);
    } catch (AssertionFailedError e) {
      throw new RuntimeException("Could not match '" + s + "' in title '" + getPageTitle() + "'");
    }
  }

  /**
   * TODO move this implementation into JWebUnit
   *
   * @return the page title
   */
  public String getPageTitle() {
    IElement title = getElementByXPath("//title");
    if (title == null)
      return "null";
    return title.getTextContent();
  }

  /**
   * Assert an instance of a class is in a given list.
   *
   * @param class1 class to check
   * @param used list of class instances
   */
  public void assertClassIn(Class<?> class1,
      List<?> used) {
    for (Object m : used) {
      if (m.getClass().equals(class1))
        return;
    }
    fail("Migrator '" + class1.getSimpleName() + "' should be found in list of used migrators: " + used);
  }

  /**
   * Assert an instance of a class is not in a given list.
   *
   * @param class1 class to check
   * @param used list of class instances
   */
  public void assertClassNotIn(Class<?> class1,
      List<?> used) {
    for (Object m : used) {
      if (m.getClass().equals(class1))
        fail("Migrator '" + class1.getSimpleName() + "' should not be found in list of used migrators: " + used);
    }
  }
 
  /**
   * Assert that the given object is of the given class or higher.
   *
   * @param class1 the expected class
   * @param object the object to check
   */
  public static void assertInstanceOf(Class<?> class1, Object object) {
    if (class1.isInstance(object)) {
      // ok
    } else {
      fail("Expected object instance '" + class1 + "', got '" + object.getClass() + "': " + object);
    }
  }
 
  /**
   * Assert that the given list of objects are of the given class or higher.
   *
   * @param class1 the expected class
   * @param objects the list of objects to check
   */
  public static void assertInstanceOf(Class<?> class1, Collection<?> objects) {
    for (Object object : objects) {
      if (class1.isInstance(object)) {
        // ok
      } else {
        fail("Expected object instance '" + class1 + "', got '" + object.getClass() + "': " + object);
      }
    }
  }
 
  /**
   * An exception has occured while transforming the model.
   *
   * @author jmwright
   *
   */
  public class TransformationException extends Exception {

    private static final long serialVersionUID = 1L;
   
    public TransformationException(String message, Throwable e) {
      super(message, e);
    }
    public TransformationException(Throwable e) {
      super(e.getMessage(), e);
    }
    public TransformationException(String message) {
      super(message);
    }
   
    private IStatus[] children;
   
    public TransformationException(String message, IStatus[] children) {
      this(message);
      this.children = children;
    }
   
    public IStatus[] getChildren() {
      return children;
    }
   
  }
 
  /**
   * Does the given element ID exist?
   *
   * TODO move into JWebUnit.
   */
  public boolean hasElementById(String id) {
    try {
      getElementById(id);
      return true;
    } catch (AssertionFailedError e) {
      return false;
    }
  }

  /**
   * Select all of the elements of the given list that are
   * instances of the given class.
   *
   * @param children
   * @param class1
   * @return
   */
  public static List<?> typeSelect(List<?> children,
      Class<?> class1) {
    List<Object> result = new ArrayList<Object>();
    for (Object o : children) {
      if (class1.isInstance(o))
        result.add(o);
    }
    return result;
  }

  /**
   * Assert that the given haystack String contains the given needle String.
   *
   * @param needle
   * @param haystack
   */
  public void assertContains(String needle, String haystack) {
    assertTrue("String '" + haystack + "' does not contain needle '" + needle + "'", haystack.contains(needle));
  }
 
  /**
   * XPath helper methods.
   * @see org.openiaml.model.tests.xpath.IXpath
   */
  private static IXpath xpath = new DefaultXpathTestCase();

  /**
   * Get the first node result from an XPath query. Returns the
   * node found, or null if none or more than one is found.
   *
   * @returns the found node, or null if none is found (or more than one is found)
   */
  @Override
  public Element hasXpathFirst(Element e, String query)
      throws XPathExpressionException {
    return xpath.hasXpathFirst(e, query);
  }

  /**
   * Get the first node result from an XPath query. Returns the
   * node found, or null if none or more than one is found.
   *
   * @returns the found node, or null if none is found (or more than one is found)
   */
  @Override
  public Element hasXpathFirst(Document e, String query)
      throws XPathExpressionException {
    return xpath.hasXpathFirst(e, query);
  }


  /* (non-Javadoc)
   * @see org.openiaml.model.tests.xpath.XpathTestCase#xpath(org.w3c.dom.Node, java.lang.String)
   */
  @Override
  public IterableElementList xpath(Node doc, String query)
      throws XPathExpressionException {
    return xpath.xpath(doc, query);
  }


  /* (non-Javadoc)
   * @see org.openiaml.model.tests.xpath.XpathTestCase#xpathFirst(org.w3c.dom.Document, java.lang.String)
   */
  @Override
  public Element xpathFirst(Document doc, String query)
      throws XPathExpressionException {
    return xpath.xpathFirst(doc, query);
  }


  /* (non-Javadoc)
   * @see org.openiaml.model.tests.xpath.XpathTestCase#xpathFirst(org.w3c.dom.Element, java.lang.String)
   */
  @Override
  public Element xpathFirst(Element e, String query)
      throws XPathExpressionException {
    return xpath.xpathFirst(e, query);
  }

  /**
   * Should we log the results to TIMED_LOG_FILE?
   */
  public static final boolean ENABLE_TIMED_LOG = false;
  private static final String TIMED_LOG_FILE = "timed.log";
 
  /**
   * Extends the CustomOAWLog to allow us to also register the time it
   * takes for various operations.
   */
  static {
    if (ENABLE_TIMED_LOG)
      initaliseLogExtender();
  }
 
  private static TimedOAWLogExtender extender;
  public static void initaliseLogExtender() {
   
    extender = new TimedOAWLogExtender(TIMED_LOG_FILE);
   
    // register to CustomOAWLog
    CustomOAWLog.setLogExtender(extender);
   
  }
 
  protected static void logTimed(String key1, String key2, String key3, String key4) {
    if (ENABLE_TIMED_LOG) {
      extender.logDirect(key1, key2, key3, key4);
    }
  }
 
  /**
   * @see XmlTestCase#readFile(File)
   */
  public static String readFile(File sourceFile) throws IOException {
    return XmlTestCase.readFile(sourceFile);
  }
 
  /**
   * @see XmlTestCase#copyFile(File, File)
   */
  public static void copyFile(File source, File target) throws IOException {
    XmlTestCase.copyFile(source, target);
  }
 
  /**
   * @see XmlTestCase#writeFile(File, String)
   */
  public static void writeFile(File sourceFile, String data) throws IOException {
    XmlTestCase.writeFile(sourceFile, data);
  }
 
  /**
   * Write the given string to the target IFile.
   * Assumed that the given string is in UTF-8 encoding.
   *
   * @param target
   * @param data
   * @throws UnsupportedEncodingException
   * @throws CoreException
   */
  public static void writeFile(IFile target, String data) throws UnsupportedEncodingException, CoreException {
    InputStream source = new ByteArrayInputStream(data.getBytes("UTF-8"));
    target.create(source, true, new NullProgressMonitor());
  }
 
}
TOP

Related Classes of org.openiaml.model.tests.ModelTestCase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.