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

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

/*
 * Copyright 2001-2013 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


/**
 * Base trait for a family of style traits that can pass a fixture object into tests.
 *
 * @author Bill Venners
 */
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
  /*
   * A Set of test names. If this fixture.Suite contains no tests, this method returns an empty Set.
   *
   * 

* This trait's implementation of this method uses Java reflection to discover all public methods whose name starts with "test", * which take either nothing, a single Informer, a single FixtureParam or two parameters of type FixtureParam * and Informer. For each discovered test method, it assigns a test name * comprised of just the method name if the method takes no parameters, or the method name plus (Informer) or FixtureParam * if the method takes a Informer or FixtureParam. * Here are a few method signatures and the names that this trait's implementation assigns them: *

* *
   * def testCat() {}         // test name: "testCat"
   * def testCat(Informer) {} // test name: "testCat(Informer)"
   * def testCat(FixtureParam) {} // test name: "testCat(FixtureParam)"
   * def testCat(FixtureParam, Informer) {} // test name: "testCat(FixtureParam, Informer)"
   * def testDog() {}         // test name: "testDog"
   * def testDog(Informer) {} // test name: "testDog(Informer)"
   * def testDog(FixtureParam) {} // test name: "testDog(FixtureParam)"
   * def testDog(FixtureParam, Informer) {} // test name: "testDog(FixtureParam, Informer)"
   * def test() {}            // test name: "test"
   * def test(Informer) {}    // test name: "test(Informer)"
   * def test(FixtureParam) {}    // test name: "test(FixtureParam)"
   * def test(FixtureParam, Informer) {}    // test name: "test(FixtureParam, Informer)"
   * 
* *

* This trait's implementation of this method returns an immutable Set of all such names, excluding the name * testNames. The iterator obtained by invoking elements on this * returned Set will produce the test names in their natural order, as determined by String's * compareTo method. *

* *

* This trait's implementation of runTests invokes this method * and calls runTest for each test name in the order they appear in the returned Set's iterator. * Although this trait's implementation of this method returns a Set whose iterator produces String * test names in a well-defined order, the contract of this method does not required a defined order. Subclasses are free to * override this method and return test names in an undefined order, or in a defined order that's different from String's * natural order. *

* *

* Subclasses may override this method to produce test names in a custom manner. One potential reason to override testNames is * to run tests in a different order, for example, to ensure that tests that depend on other tests are run after those other tests. * Another potential reason to override is allow tests to be defined in a different manner, such as methods annotated @Test annotations * (as is done in JUnitSuite and TestNGSuite) or test functions registered during construction (as is * done in FunSuite and FunSpec). *

* *

* In ScalaTest's event model, a test may be surrounded by “scopes.” Each test and scope is associated with string of text. * A test's name is concatenation of the text of any surrounding scopes followed by the text provided with the test * itself, after each text element has been trimmed and one space inserted between each component. Here's an example: *

* *
   * package org.scalatest.examples.fixture.freespec
   *
   * import org.scalatest._
   *
   * class SetSpec extends fixture.FreeSpec with fixture.UnitFixture {
   *
   *   "A Set" - {
   *     "when empty" - {
   *       "should have size 0" in {
   *         assert(Set.empty.size === 0)
   *       }
   *
   *       "should produce NoSuchElementException when head is invoked" in {
   *         assertThrows[NoSuchElementException] {
   *           Set.empty.head
   *         }
   *       }
   *     }
   *   }
   * }
   * 
* *

* The above FreeSpec contains two tests, both nested inside the same two scopes. The outermost scope names * the subject, A Set. The nested scope qualifies the subject with when empty. Inside that * scope are the two tests. The text of the tests are: *

* *

    *
  • should have size 0
  • *
  • should produce NoSuchElementException when head is invoked
  • *
* *

* Therefore, the names of these two tests are: *

* *
    *
  • A Stack when empty should have size 0
  • *
  • A Stack when empty should produce NoSuchElementException when head is invoked
  • *
* *

* Note that because the component scope and test text strings are trimmed, any leading or trailing space will be dropped * before they are strung together to form the test name, with each trimmed component separated by a space. If the scopes * in the above example had text " A Set " and " when empty ", and the first test had text * " should have size 0 ", its test name would still be the same, "A Set when empty should have size 0". *

*/ // Can just inherit the supertrait implementation of testnames that returns an empty set /* 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 } */ /** * Run a test. * *

* This trait's implementation uses Java reflection to invoke on this object the test method identified by the passed testName. *

* *

* Implementations of this method are responsible for ensuring a TestStarting event * is fired to the Reporter before executing any test, and either TestSucceeded, * TestFailed, TestPending or TestCanceled after executing any nested * Suite. (If a test is marked with the org.scalatest.Ignore tag, the * runTests method is responsible for ensuring a TestIgnored event is fired and that * this runTest method is not invoked for that ignored test.) *

* * @param testName the name of one test to run. * @param args the Args for this run * @return a Status object that indicates when the test started by this method has completed, and whether or not it failed . * * @throws NullArgumentException if any of testName or args is null. * @throws IllegalArgumentException if testName is defined, but no test with the specified test name * exists in this Suite */ // Can just inheirt the supertrait implementation of runTest that /* 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(thisSuite, stopper, reporter, testName) reportTestStarting(thisSuite, report, tracker, testName, testName, thisSuite.rerunner, Some(getTopOfMethod(thisSuite, 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)).toUnit } 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)).toUnit } val duration = System.currentTimeMillis - testStartTime reportTestSucceeded(thisSuite, report, tracker, testName, testName, messageRecorderForThisTest.recordedEvents(false, false), duration, formatter, thisSuite.rerunner, Some(getTopOfMethod(thisSuite, 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(thisSuite, 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(thisSuite, method)), thisSuite.rerunner)) SucceededStatus case e if !anExceptionThatShouldCauseAnAbort(e) => val duration = System.currentTimeMillis - testStartTime handleFailedTest(thisSuite, t, testName, messageRecorderForThisTest.recordedEvents(false, false), report, tracker, getEscapedIndentedTextForTest(testName, 1, true), duration) FailedStatus case e => throw e } case e if !anExceptionThatShouldCauseAnAbort(e) => val duration = System.currentTimeMillis - testStartTime handleFailedTest(thisSuite, 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(theSuite: org.scalatest.Suite, testName: String): Method = { val candidateMethods = theSuite.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. * * @return org.scalatest.fixture.Suite */ 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