Package org.springsource.loaded.testgen

Source Code of org.springsource.loaded.testgen.GenerativeTest

/*
* Copyright 2010-2012 VMware and contributors
*
* 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 org.springsource.loaded.testgen;

import org.junit.ComparisonFailure;
import org.junit.runner.RunWith;
import org.springsource.loaded.test.infra.IResult;
import org.springsource.loaded.test.infra.Result;
import org.springsource.loaded.test.infra.ResultException;


/**
* This class is intended to be subclassed to create 'generated' tests. It needs to be run with the {@link ExploreAllChoicesRunner}
* test runner, using the {@link RunWith} annotation.
* <p>
* To create a generative test two things come together:
*
* <ul>
* <li>A mechanism to create different test configurations based on 'random' choices. These random choices are made by the test's
* 'setup' method calling the provided 'choice' methods.
*
* <li>A mechanism to run the same test twice in two different execution contexts. It is the responsibility of the test subclass to
* setup the appropriate execution context. See {@link GenerativeSpringLoadedTest} for an example.
* </ul>
*
* The test runner is responsible for injecting implementations of the IChoiceGenerator interface.
* <p>
* On a first run, the test runner will provide a 'recording' choice generator. The test is run multiple times until all possible
* choices have been explored. For each test run the choices are recorded together with the observed test result for those choices.
* This is used to populate the test tree.
* <p>
* Then the tests are run again replaying the recorded choices. The result is compared with the result from the first run. The test
* fails if the results are not equal (using whatever implementation of equals is provided by the result objects.
*
* @author kdvolder
*/
public abstract class GenerativeTest {

  /**
   * Injected by the test runner. Use the ChoiceGenerator to implement some logic to choose test parameters. Either in your setup
   * method or your actual test method.
   */
  public IChoiceGenerator choiceGenerator = null;

  /**
   * This field is set by the test runner to indicate whether the test is currently in 'generative' mode, or 'replay/verify' mode.
   * This flag is mostly intended for the setup method so it can setup an appropriate execution context.
   */
  public boolean generative;

  /**
   * This method should setup the test, using the provided choice generator to construct/choose a test configuration.
   */
  public void setup() throws Exception, RejectedChoice {
  }

  public void teardown() throws Exception {
  }

  /**
   * There should be only one test method in this type of test, this is it!
   * <p>
   * The test method will be run twice by the runner, once in a 'generative' mode and once in 'verifying' mode.
   * <p>
   * In generative mode, it is ok to throw RejectedChoice exception, this will cause the test to be ignored. The test method
   * itself shouldn't need to know what mode it is running in. The test runner should inject the necessary context dependencies
   * for the code to be identical in both cases.
   * <p>
   * The only obligation the test method has is to ensure that, given a deterministic set of choices is made by the injected
   * ChoiceGenerator, the test method's behavior should also be deterministic.
   *
   * @throws ResultExeption if the test produces an expected exception as result.
   * @throws RejectedChoice (in generative mode only) if the generated test should be ignored.
   * @throws Exception any other exception should be treated as an unexpected error and make the test fail.
   * @return Result encapsulating the expected result of the test.
   */
  public abstract Result test() throws ResultException, Exception;

  /**
   * @return Use choice generator to pick an element from a bunch of Strings
   * @throws RejectedChoice
   */
  protected <T> T choice(T... options) throws RejectedChoice {
    return options[choice(options.length)];
  }

  /**
   * @return number in range 0 (inclusive) to 'hi' (exclusive)
   * @throws RejectedChoice if 'hi' is negative
   */
  protected int choice(int hi) throws RejectedChoice {
    return choice(0, hi);
  }

  /**
   * @return number in range 'lo' (inclusive) to 'hi' (exclusive)
   * @throws RejectedChoice if 'lo' >= 'hi'
   */
  protected int choice(int lo, int hi) throws RejectedChoice {
    if (lo >= hi) {
      throw new RejectedChoice(); //Nothing to choose from
    }
    if (hi - lo == 1) {
      //only one choice
      return lo;
    }

    //Use kind of 'binary search' for efficient choice making
    if (choice())
      return choice(lo, (lo + hi) / 2);
    else
      return choice((lo + hi) / 2, hi);
  }

  protected boolean choice() {
    boolean b = choiceGenerator.nextBoolean();
    return b;
  }

  /**
   * Override this and make it return something other than null to create a nicer name in the JUnit runner view. Beware that this
   * name must be unique or the Eclipse JUnit runner view will get confused displaying the results (though tests should still run
   * ok).
   * <p>
   * Typically, you should override this to return a string that describes the values for the configuration parameter values of
   * the test instance.
   * <p>
   * If not overridden, the choices 'bitString' will be displayed. This is unique, but not very informative.
   *
   * @return A String uniquely identifying the test or null.
   */
  public String getConfigDescription() {
    return null;
  }

  /**
   * This method is called by the test runner to compare predicted results against actual results.
   * <p>
   * Override this method to customise how you want these compared.
   *
   * @param expected Result from the 'generative' test run.
   * @param actual Result from the actual test run.
   */
  final protected void assertEqualIResults(IResult expected, IResult actual) {
    if (expected.equals(actual)) {
      return;
    }
    if (expected.getClass() != actual.getClass()) {
      //One is an Exception and the other one isn't. these's no way these should
      //ever be treated as equivalent!
      throw new ComparisonFailure(null, expected.toString(), actual.toString());
    }
    if (expected instanceof Result) {
      assertEqualResults((Result) expected, (Result) actual);
    } else if (expected instanceof ResultException) {
      assertEqualExceptions((ResultException) expected, (ResultException) actual);
    } else {
      //I don't what it is?? There are only two implementations of the interface
      throw new ComparisonFailure(null, expected.toString(), actual.toString());
    }
  }

  /**
   * This method gets called to compare two ResultExceptions, but only when the standard equals method returned false. Subclasses
   * may override this to relax the equality check.
   */
  protected void assertEqualExceptions(ResultException expected, ResultException actual) {
    throw new ComparisonFailure(null, expected.toString(), actual.toString());
  }

  /**
   * This method gets called to compare two Results, but only when the standard equals method returned false. Subclasses may
   * override this to relax the equality check.
   */
  protected void assertEqualResults(Result expected, Result actual) {
    throw new ComparisonFailure(null, expected.toString(), actual.toString());
  }

}
TOP

Related Classes of org.springsource.loaded.testgen.GenerativeTest

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.