Package com.google.gwt.junit.client.impl

Source Code of com.google.gwt.junit.client.impl.GWTRunner$JUnitHostListener

/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.junit.client.impl;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.UrlBuilder;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.junit.client.impl.JUnitHost.TestBlock;
import com.google.gwt.junit.client.impl.JUnitHost.TestInfo;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;

import java.util.HashMap;

/**
* The entry point class for GWTTestCases.
*
* This is the main test running logic. Each time a test completes, the results
* are reported back through {@link #junitHost}, and the next method to run is
* returned. This process repeats until the next method to run is null.
*/
public abstract class GWTRunner implements EntryPoint {

  /**
   * The RPC callback object for {@link GWTRunner#junitHost}. When
   * {@link #onSuccess} is called, it's time to run the next test case.
   */
  private final class JUnitHostListener implements AsyncCallback<TestBlock> {

    /**
     * The number of times we've failed to communicate with the server on the
     * current test batch.
     */
    private int curRetryCount = 0;

    /**
     * A call to junitHost failed.
     */
    public void onFailure(Throwable caught) {
      if (maxRetryCount < 0 || curRetryCount < maxRetryCount) {
        // Try the call again
        curRetryCount++;
        new Timer() {
          @Override
          public void run() {
            syncToServer();
          }
        }.schedule(1000);
      } else {
        // Give up and mark the test complete on the client side.
        markComplete();
      }
    }

    /**
     * A call to junitHost succeeded; run the next test case.
     */
    public void onSuccess(TestBlock nextTestBlock) {
      curRetryCount = 0;
      currentBlock = nextTestBlock;
      currentTestIndex = 0;
      currentResults.clear();
      if (currentBlock != null && currentBlock.getTests().length > 0) {
        doRunTest();
      } else {
        markComplete();
      }
    }

    /**
     * Set a global expando so the test infrastructure knows that the test is
     * complete.
     */
    private native void markComplete() /*-{
      $doc.title = "Completed Tests";
      $wnd._gwt_test_complete = true;
    }-*/;
  }

  /**
   * The singleton instance.
   */
  private static GWTRunner sInstance;

  /**
   * A query param specifying the test class to run, for serverless mode.
   */
  private static final String TESTCLASS_QUERY_PARAM = "gwt.junit.testclassname";

  /**
   * A query param specifying the test method to run, for serverless mode.
   */
  private static final String TESTFUNC_QUERY_PARAM = "gwt.junit.testfuncname";

  /**
   * A query param specifying the number of times to retry if the server fails
   * to respond.
   */
  private static final String RETRYCOUNT_QUERY_PARAM = "gwt.junit.retrycount";

  /**
   * A query param specifying the block index to start on.
   */
  private static final String BLOCKINDEX_QUERY_PARAM = "gwt.junit.blockindex";

  public static GWTRunner get() {
    return sInstance;
  }

  /**
   * The current block of tests to execute.
   */
  private TestBlock currentBlock;

  /**
   * Active test within current block of tests.
   */
  private int currentTestIndex = 0;

  /**
   * Results for all test cases in the current block.
   */
  private HashMap<TestInfo, JUnitResult> currentResults = new HashMap<TestInfo, JUnitResult>();

  /**
   * If set, all remaining tests will fail with the failure message.
   */
  private String failureMessage;

  /**
   * The remote service to communicate with.
   */
  private final JUnitHostAsync junitHost = (JUnitHostAsync) GWT.create(JUnitHost.class);

  /**
   * Handles all RPC responses.
   */
  private final JUnitHostListener junitHostListener = new JUnitHostListener();

  /**
   * The maximum number of times to retry communication with the server per
   * test batch.
   */
  private int maxRetryCount = -1;

  /**
   * If true, run a single test case with no RPC.
   */
  private boolean serverless = false;

  // TODO(FINDBUGS): can this be a private constructor to avoid multiple
  // instances?
  public GWTRunner() {
    sInstance = this;

    // Bind junitHost to the appropriate url.
    ServiceDefTarget endpoint = (ServiceDefTarget) junitHost;
    String url = GWT.getModuleBaseURL() + "junithost";
    endpoint.setServiceEntryPoint(url);

    // Null out the default uncaught exception handler since we will control it.
    GWT.setUncaughtExceptionHandler(null);
  }

  public void onModuleLoad() {
    maxRetryCount = parseQueryParamInteger(RETRYCOUNT_QUERY_PARAM, -1);
    currentBlock = checkForQueryParamTestToRun();
    if (currentBlock != null) {
      /*
       * Just run a single test with no server-side interaction.
       */
      serverless = true;
      runTest();
    } else {
      /*
       * Normal operation: Kick off the test running process by getting the
       * first method to run from the server.
       */
      syncToServer();
    }
  }

  public void reportResultsAndGetNextMethod(JUnitResult result) {
    if (serverless) {
      // That's it, we're done
      return;
    }
    if (result != null && failureMessage != null) {
      RuntimeException ex = new RuntimeException(failureMessage);
      result.setExceptionWrapper(new ExceptionWrapper(ex));
    }  
    TestInfo currentTest = getCurrentTest();
    currentResults.put(currentTest, result);
    ++currentTestIndex;
    if (currentTestIndex < currentBlock.getTests().length) {
      // Run the next test after a short delay.
      DeferredCommand.addCommand(new Command() {
        public void execute() {
          doRunTest();
        }
      });
    } else {
      syncToServer();
    }
  }

  /**
   * Implemented by the generated subclass. Creates an instance of the specified
   * test class by fully qualified name.
   */
  protected abstract GWTTestCase createNewTestCase(String testClass);

  /**
   * Implemented by the generated subclass. Get the value of the user agent
   * property.
   */
  protected abstract String getUserAgentProperty();

  private TestBlock checkForQueryParamTestToRun() {
    String testClass = Window.Location.getParameter(TESTCLASS_QUERY_PARAM);
    String testMethod = Window.Location.getParameter(TESTFUNC_QUERY_PARAM);
    if (testClass == null || testMethod == null) {
      return null;
    }
    // TODO: support blocks of tests?
    TestInfo[] tests = new TestInfo[] {new TestInfo(GWT.getModuleName(),
        testClass, testMethod)};
    return new TestBlock(tests, 0);
  }

  private void doRunTest() {
    // Make sure the module matches.
    String currentModule = GWT.getModuleName();
    String newModule = getCurrentTest().getTestModule();
    if (currentModule.equals(newModule)) {
      // The module is correct.
      runTest();
    } else {
      /*
       * We're being asked to run a test in a different module. We must navigate
       * the browser to a new URL which will run that other module.  We retain
       * the same path suffix (e.g., '/junit.html') as the current URL.
       */
      String currentPath = Window.Location.getPath();
      String pathSuffix = currentPath.substring(currentPath.lastIndexOf('/'));
     
      UrlBuilder builder = Window.Location.createUrlBuilder();
      builder.setParameter(BLOCKINDEX_QUERY_PARAM,
          Integer.toString(currentBlock.getIndex())).setPath(
          newModule + pathSuffix);
      Window.Location.replace(builder.buildString());
      currentBlock = null;
      currentTestIndex = 0;
    }
  }

  private TestInfo getCurrentTest() {
    return currentBlock.getTests()[currentTestIndex];
  }

  /**
   * Parse an integer from a query parameter, returning the default value if
   * the parameter cannot be found.
   *
   * @param paramName the parameter name
   * @param defaultValue the default value
   * @return the integer value of the parameter
   */
  private int parseQueryParamInteger(String paramName, int defaultValue) {
    String value = Window.Location.getParameter(paramName);
    if (value != null) {
      try {
        return Integer.parseInt(value);
      } catch (NumberFormatException e) {
        setFailureMessage("'" + value + "' is not a valid value for " +
            paramName + ".");
        return defaultValue;
      }
    }
    return defaultValue;
  }

  private void runTest() {
    // Dynamically create a new test case.
    TestInfo currentTest = getCurrentTest();
    GWTTestCase testCase = null;
    Throwable caught = null;
    try {
      testCase = createNewTestCase(currentTest.getTestClass());
    } catch (Throwable e) {
      caught = e;
    }
    if (testCase == null) {
      RuntimeException ex = new RuntimeException(currentTest
          + ": could not instantiate the requested class", caught);
      JUnitResult result = new JUnitResult();
      result.setExceptionWrapper(new ExceptionWrapper(ex));
      reportResultsAndGetNextMethod(result);
      return;
    }

    testCase.setName(currentTest.getTestMethod());
    testCase.__doRunTest();
  }

  /**
   * Fail all tests with the specified message.
   */
  private void setFailureMessage(String message) {
    failureMessage = message;
  }

  private void syncToServer() {
    if (currentBlock == null) {
      int firstBlockIndex = parseQueryParamInteger(BLOCKINDEX_QUERY_PARAM, 0);
      junitHost.getTestBlock(firstBlockIndex, getUserAgentProperty(),
          junitHostListener);
    } else {
      junitHost.reportResultsAndGetTestBlock(currentResults,
          currentBlock.getIndex() + 1, getUserAgentProperty(),
          junitHostListener);
    }
  }

}
TOP

Related Classes of com.google.gwt.junit.client.impl.GWTRunner$JUnitHostListener

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.