All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gw.test.TestClassWrapper Maven / Gradle / Ivy

There is a newer version: 1.18.2
Show newest version
/*
 * Copyright 2014 Guidewire Software, Inc.
 */

package gw.test;

import gw.lang.reflect.java.JavaTypes;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestResult;
import junit.framework.TestSuite;
import junit.framework.AssertionFailedError;
import gw.lang.reflect.IType;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.IAnnotationInfo;
import gw.lang.reflect.ITypeInfo;
import gw.lang.reflect.IConstructorInfo;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.gs.IGosuClass;
import gw.testharness.IncludeInTestResults;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.Set;
import java.util.HashSet;
import java.lang.annotation.Annotation;

public class TestClassWrapper extends TestSuite {
  private IType _type;
  private TestExecutionManager _executionManager;

  public TestClassWrapper(TestExecutionManager executionManager, IType type, String... methods) {
    _executionManager = executionManager;
    _type = type;
    for (String method : methods) {
      addTest(makeTest(_type, method));
    }
  }

  @Override
  public void runTest(Test test, TestResult result) {
    if (_executionManager.hasTimedOut()) {
      result.addFailure(test, new AssertionFailedError(String.format("tests timed out")));
    } else if (_executionManager.hasTimeOut()) {
      long timeout = _executionManager.getTimeoutForCurrentTest();
      runWithTimeout(test, result, timeout);
    } else {
      super.runTest(test, result);
    }
  }

  private void runWithTimeout(final Test test, final TestResult result, long timeout) {
    ExecutorService service = Executors.newSingleThreadExecutor();
    Callable callable = new Callable() {
      public Object call() throws Exception {
        test.run(result);
        return null;
      }
    };
    Future future = service.submit(callable);
    service.shutdown();
    try {
      boolean terminated = service.awaitTermination(timeout, TimeUnit.MILLISECONDS);
      if (!terminated){
        service.shutdownNow();
      }
      future.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation
    } catch (TimeoutException e) {
      result.addFailure(test, new AssertionFailedError(String.format("test timed out after %d milliseconds", timeout)));
      result.endTest(test);
      _executionManager.markTimedOut();
    } catch (Throwable e) {
      if (e instanceof AssertionFailedError) {
        result.addFailure(test, (AssertionFailedError) e);
      } else {
        result.addFailure(test, new AssertionFailedError(e.getMessage()));
      }
    }
  }


  @Override
  public String getName() {
    return _type.getName();
  }

  private TestCase makeTest(final IType type, String method) {
    try {
      TestClass test;
      if (type.isValid()) {
        ITypeInfo typeInfo = type.getTypeInfo();
        IConstructorInfo noArgCons = typeInfo.getConstructor();
        if (noArgCons != null) {
          test = (TestClass) noArgCons.getConstructor().newInstance();
        } else {
          IConstructorInfo oneArgCons = typeInfo.getConstructor(JavaTypes.STRING());
          if (oneArgCons != null) {
            test = (TestClass) oneArgCons.getConstructor().newInstance(method);
          } else {
            throw new IllegalStateException("Test type " + type + " does not have either a no-arg constructor or a one-arg constructor taking a String");
          }
        }
      } else {
        test = new InvalidTestClass(_type);
      }
      test.setExecutionManager( _executionManager );
      test.setName(method);
      test.setGosuTest(_type instanceof IGosuClass);
      test.initMetadata( method );
      return test;
    } catch (final Exception e) {
      e.printStackTrace();
      return new ExceptionTestClass(type, e.getMessage());
    }
  }

//  static private Set getTestMethodMetadata(TestClass test, String method) {
//    Set set = new HashSet();
//    IMethodInfo testMethod = test.getType().getTypeInfo().getMethod(method);
//    if(testMethod == null) {
//      throw new IllegalStateException( "Method not found: " + test.getName() + "." + method);
//    }
//    for (IAnnotationInfo ai : testMethod.getAnnotations()) {
//      if (isMetaAnnotationInfo(ai)) {
//        set.add(new TestMetadata((Annotation) ai.getInstance()));
//      }
//    }
//    return set;
//  }

//  static private Set getTestClassMetadata(TestClass test) {
//    Set set = new HashSet();
//    for (IAnnotationInfo ai : test.getType().getTypeInfo().getAnnotations()) {
//      if (isMetaAnnotationInfo(ai)) {
//        set.add(new TestMetadata((Annotation) ai.getInstance()));
//      }
//    }
//    return set;
//  }

  private static boolean isMetaAnnotationInfo(IAnnotationInfo ai) {
    boolean isMetadata = false;
    for (IAnnotationInfo a : ai.getType().getTypeInfo().getAnnotations()) {
      if (a.getName().equals(IncludeInTestResults.class.getName())) {
        isMetadata = true;
        break;
      }
    }
    return isMetadata;
  }

  public IType getBackingType() {
    return _type;
  }

  private static class InvalidTestClass extends TestClass {
    private IType _type;

    private InvalidTestClass(IType type) {
      super(false);
      _type = type;
      initInternalData();
    }

    @Override
    public void run(TestResult result) {
      result.addError(this, getCompileError(_type));
    }

    @Override
    public IType getType() {
      return _type;
    }

    private Throwable getCompileError(IType type) {
      if (type instanceof IGosuClass) {
        type.isValid(); // just in case there has been a typesystem refresh, to ensure there is a PRE
        return ((IGosuClass)type).getParseResultsException();
      } else {
        return new IllegalStateException("Test type " + type + " is not valid.");
      }
    }

    @Override
    protected String getFullClassNameInternal() {
      return _type.getName();
    }
  };

  private static class ExceptionTestClass extends TestClass {
    private IType _type;
    private String _message;

    private ExceptionTestClass(IType type, String message) {
      super(false);
      _type = type;
      _message = message;
      initInternalData();
    }

    @Override
    public void run(TestResult result) {
      result.addError(this, new RuntimeException("Could not construct test " + _type.getName() + ". Reason : " + _message) );
    }

    @Override
    public IType getType() {
      return _type;
    }

    @Override
    protected String getFullClassNameInternal() {
      return _type.getName();
    }
  };
}