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

org.scalatest.fixture.Suite.scala Maven / Gradle / Ivy

Go to download

ScalaTest is a free, open-source testing toolkit for Scala and Java programmers.

There is a newer version: 2.0.M6-SNAP4
Show newest version
/*
 * Copyright 2001-2008 Artima, 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 org.scalatest.fixture

import org.scalatest._
import collection.immutable.TreeSet
import Suite._
import java.lang.reflect.{InvocationTargetException, Method, Modifier}
import org.scalatest.events._
import org.scalatest.Suite._
import exceptions.{TestCanceledException, TestPendingException}

/**
 * Base trait for a family of style traits that can pass a fixture object into tests.
 *
 * 

* Prior to ScalaTest 2.0.M4, trait fixture.Suite served two purposes: 1) It served as the base * class of ScalaTest's family of "fixture" style traits, and 2) It was itself a style trait in which tests are methods * that take a fixture parameter. Although it will continue to serve its first purpose, fixture.Suite has * been deprecated as a style trait. Pre-existing code that used fixture.Suite as a style trait to define * tests as methods will continue to work during the deprecation period, but will generate a deprecation warning. Please * change all such uses of fixture.Suite to use trait fixture.Spec instead. *

* * @author Bill Venners */ @Finders(Array("org.scalatest.finders.MethodFinder")) trait Suite extends org.scalatest.Suite { thisSuite => /** * The type of the fixture parameter that can be passed into tests in this suite. */ protected type FixtureParam /** * Trait whose instances encapsulate a test function that takes a fixture and config map. * *

* The fixture.Suite trait's implementation of runTest passes instances of this trait * to fixture.Suite's withFixture method, such as: *

* *
   * def testSomething(fixture: Fixture) {
   *   // ...
   * }
   * def testSomethingElse(fixture: Fixture, info: Informer) {
   *   // ...
   * }
   * 
* *

* For more detail and examples, see the * documentation for trait fixture.Suite. *

*/ protected trait OneArgTest extends (FixtureParam => Unit) with TestData { thisOneArgTest => /** * Run the test, using the passed FixtureParam. */ def apply(fixture: FixtureParam) /** * Convert this OneArgTest to a NoArgTest whose * name and configMap methods return the same values * as this OneArgTest, and whose apply method invokes * this OneArgTest's apply method, * passing in the given fixture. * *

* This method makes it easier to invoke the withFixture method * that takes a NoArgTest. For example, if a fixture.Suite * mixes in SeveredStackTraces, it will inherit an implementation * of withFixture(NoArgTest) provided by * SeveredStackTraces that implements the stack trace severing * behavior. If the fixture.Suite does not delegate to that * withFixture(NoArgTest) method, the stack trace severing behavior * will not happen. Here's how that might look in a fixture.Suite * whose FixtureParam is StringBuilder: *

* *
     * def withFixture(test: OneArgTest) {
     *   withFixture(test.toNoArgTest(new StringBuilder))
     * }
     * 
*/ def toNoArgTest(fixture: FixtureParam) = new NoArgTest { val name = thisOneArgTest.name val configMap = thisOneArgTest.configMap def apply() { thisOneArgTest(fixture) } val scopes = thisOneArgTest.scopes val text = thisOneArgTest.text val tags = thisOneArgTest.tags } } /** * Run the passed test function with a fixture created by this method. * *

* This method should create the fixture object needed by the tests of the * current suite, invoke the test function (passing in the fixture object), * and if needed, perform any clean up needed after the test completes. * For more detail and examples, see the main documentation for this trait. *

* * @param fun the OneArgTest to invoke, passing in a fixture */ protected def withFixture(test: OneArgTest) private[fixture] class TestFunAndConfigMap(val name: String, test: FixtureParam => Any, val configMap: Map[String, Any]) extends OneArgTest { def apply(fixture: FixtureParam) { test(fixture) } private val testData = testDataFor(name, configMap) val scopes = testData.scopes val text = testData.text val tags = testData.tags } private[fixture] class FixturelessTestFunAndConfigMap(override val name: String, test: () => Any, override val configMap: Map[String, Any]) extends NoArgTest { def apply() { test() } private val testData = testDataFor(name, configMap) val scopes = testData.scopes val text = testData.text val tags = testData.tags } // TODO: add documentation here, so people know they can pass an Informer as the second arg. override def testNames: Set[String] = { def takesTwoParamsOfTypesAnyAndInformer(m: Method) = { val paramTypes = m.getParameterTypes val hasTwoParams = paramTypes.length == 2 hasTwoParams && classOf[Informer].isAssignableFrom(paramTypes(1)) } def takesOneParamOfAnyType(m: Method) = m.getParameterTypes.length == 1 def isTestMethod(m: Method) = { // Factored out to share code with Suite.testNames val (isInstanceMethod, simpleName, firstFour, paramTypes, hasNoParams, isTestNames, isTestTags, isTestDataFor) = isTestMethodGoodies(m) // Also, will discover both // testNames(Object) and testNames(Object, Informer). Reason is if I didn't discover these // it would likely just be silently ignored, and that might waste users' time isInstanceMethod && (firstFour == "test") && !isTestDataFor && ((hasNoParams && !isTestNames && !isTestTags) || takesInformer(m) || takesOneParamOfAnyType(m) || takesTwoParamsOfTypesAnyAndInformer(m)) } val testNameArray = for (m <- getClass.getMethods; if isTestMethod(m)) yield if (takesInformer(m)) m.getName + InformerInParens else if (takesOneParamOfAnyType(m)) m.getName + FixtureInParens else if (takesTwoParamsOfTypesAnyAndInformer(m)) m.getName + FixtureAndInformerInParens else m.getName TreeSet[String]() ++ testNameArray } protected override def runTest(testName: String, args: Args): Status = { if (testName == null) throw new NullPointerException("testName was null") if (args == null) throw new NullPointerException("args was null") import args._ val (stopRequested, report, method, testStartTime) = getSuiteRunTestGoodies(stopper, reporter, testName) reportTestStarting(thisSuite, report, tracker, testName, testName, thisSuite.rerunner, Some(getTopOfMethod(testName))) val formatter = getEscapedIndentedTextForTest(testName, 1, true) val messageRecorderForThisTest = new MessageRecorder(report) val informerForThisTest = MessageRecordingInformer( messageRecorderForThisTest, (message, payload, isConstructingThread, testWasPending, testWasCanceled, location) => createInfoProvided(thisSuite, report, tracker, Some(testName), message, payload, 2, location, isConstructingThread, true) ) val documenterForThisTest = MessageRecordingDocumenter( messageRecorderForThisTest, (message, _, isConstructingThread, testWasPending, testWasCanceled, location) => createInfoProvided(thisSuite, report, tracker, Some(testName), message, None, 2, location, isConstructingThread, true) // TODO: Need a test that fails because testWasCanceleed isn't being passed ) // TODO: Use a message recorder in FixtureSuite. Maybe just allow the state and // use Engine in Suite, though then I'd have two Engines in everything. Or even three down here. // Nah, go ahead and use message recording informer here, and maybe find some other way to // reduce the duplication between Suite, FixtureSuite, and Engine. try { if (testMethodTakesAFixtureAndInformer(testName) || testMethodTakesAFixture(testName)) { val testFun: FixtureParam => Unit = { (fixture: FixtureParam) => { val anyRefFixture: AnyRef = fixture.asInstanceOf[AnyRef] // TODO zap this cast val args: Array[Object] = if (testMethodTakesAFixtureAndInformer(testName)) { Array(anyRefFixture, informerForThisTest) } else Array(anyRefFixture) method.invoke(thisSuite, args: _*) } } withFixture(new TestFunAndConfigMap(testName, testFun, configMap)) } else { // Test method does not take a fixture val testFun: () => Unit = { () => { val args: Array[Object] = if (testMethodTakesAnInformer(testName)) Array(informerForThisTest) else Array() method.invoke(this, args: _*) } } withFixture(new FixturelessTestFunAndConfigMap(testName, testFun, configMap)) } val duration = System.currentTimeMillis - testStartTime reportTestSucceeded(thisSuite, report, tracker, testName, testName, messageRecorderForThisTest.recordedEvents(false, false), duration, formatter, thisSuite.rerunner, Some(getTopOfMethod(method))) SucceededStatus } catch { case ite: InvocationTargetException => val t = ite.getTargetException t match { case _: TestPendingException => val duration = System.currentTimeMillis - testStartTime // testWasPending = true so info's printed out in the finally clause show up yellow reportTestPending(thisSuite, report, tracker, testName, testName, messageRecorderForThisTest.recordedEvents(true, false), duration, formatter, Some(getTopOfMethod(method))) SucceededStatus case e: TestCanceledException => val duration = System.currentTimeMillis - testStartTime val message = getMessageForException(e) val formatter = getEscapedIndentedTextForTest(testName, 1, true) // testWasCanceled = true so info's printed out in the finally clause show up yellow report(TestCanceled(tracker.nextOrdinal(), message, thisSuite.suiteName, thisSuite.suiteId, Some(thisSuite.getClass.getName), testName, testName, messageRecorderForThisTest.recordedEvents(false, true), Some(e), Some(duration), Some(formatter), Some(getTopOfMethod(method)), thisSuite.rerunner)) SucceededStatus case e if !anErrorThatShouldCauseAnAbort(e) => val duration = System.currentTimeMillis - testStartTime handleFailedTest(t, testName, messageRecorderForThisTest.recordedEvents(false, false), report, tracker, getEscapedIndentedTextForTest(testName, 1, true), duration) FailedStatus case e => throw e } case e if !anErrorThatShouldCauseAnAbort(e) => val duration = System.currentTimeMillis - testStartTime handleFailedTest(e, testName, messageRecorderForThisTest.recordedEvents(false, false), report, tracker, getEscapedIndentedTextForTest(testName, 1, true), duration) FailedStatus case e: Throwable => throw e } } // Overriding this in fixture.Suite to reduce duplication of tags method private[scalatest] override def getMethodForTestName(testName: String) = { val candidateMethods = getClass.getMethods.filter(_.getName == Suite.simpleNameForTest(testName)) val found = if (testMethodTakesAFixtureAndInformer(testName)) candidateMethods.find( candidateMethod => { val paramTypes = candidateMethod.getParameterTypes paramTypes.length == 2 && paramTypes(1) == classOf[Informer] } ) else if (testMethodTakesAnInformer(testName)) candidateMethods.find( candidateMethod => { val paramTypes = candidateMethod.getParameterTypes paramTypes.length == 1 && paramTypes(0) == classOf[Informer] } ) else if (testMethodTakesAFixture(testName)) candidateMethods.find( candidateMethod => { val paramTypes = candidateMethod.getParameterTypes paramTypes.length == 1 } ) else candidateMethods.find(_.getParameterTypes.length == 0) found match { case Some(method) => method case None => throw new IllegalArgumentException(Resources("testNotFound", testName)) } } /** * Suite style name. */ override val styleName: String = "org.scalatest.fixture.Suite" } private[scalatest] object Suite { val FixtureAndInformerInParens = "(FixtureParam, Informer)" val FixtureInParens = "(FixtureParam)" private def testMethodTakesAFixtureAndInformer(testName: String) = testName.endsWith(FixtureAndInformerInParens) private[scalatest] def testMethodTakesAFixture(testName: String) = testName.endsWith(FixtureInParens) private[scalatest] def simpleNameForTest(testName: String) = if (testName.endsWith(FixtureAndInformerInParens)) testName.substring(0, testName.length - FixtureAndInformerInParens.length) else if (testName.endsWith(FixtureInParens)) testName.substring(0, testName.length - FixtureInParens.length) else if (testName.endsWith(InformerInParens)) testName.substring(0, testName.length - InformerInParens.length) else testName private def argsArrayForTestName(testName: String): Array[Class[_]] = if (testMethodTakesAFixtureAndInformer(testName)) Array(classOf[Object], classOf[Informer]) else Array(classOf[Informer]) }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy