Skip to content

Commit 862b607

Browse files
committed
Merge branch 'feature-init'
2 parents d1d7c56 + 1430aa0 commit 862b607

18 files changed

+1754
-1
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
*.class
22
*.log
3+
target/
4+
.idea/
5+
test-output/

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
1-
# scalatestplus-testng
1+
# ScalaTest + TestNG
22
ScalaTest + TestNG provides integration support between ScalaTest and TestNG.
3+
4+
**Publishing**
5+
6+
Please use the following commands to publish to Sonatype:
7+
8+
```
9+
$ sbt +publishSigned
10+
```

build.sbt

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name := "scalatestplus-testng"
2+
3+
organization := "org.scalatestplus"
4+
5+
version := "1.0.0-SNAP3"
6+
7+
homepage := Some(url("https://github.com/scalatest/scalatestplus-testng"))
8+
9+
licenses := List("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0"))
10+
11+
developers := List(
12+
Developer(
13+
"bvenners",
14+
"Bill Venners",
15+
16+
url("https://github.com/bvenners")
17+
),
18+
Developer(
19+
"cheeseng",
20+
"Chua Chee Seng",
21+
22+
url("https://github.com/cheeseng")
23+
)
24+
)
25+
26+
crossScalaVersions := List("2.10.7", "2.11.12", "2.12.8", "2.13.0-M5")
27+
28+
libraryDependencies ++= Seq(
29+
"org.scalatest" %% "scalatest" % "3.1.0-SNAP8",
30+
"org.testng" % "testng" % "6.7",
31+
"commons-io" % "commons-io" % "1.3.2" % "test"
32+
)
33+
34+
testOptions in Test :=
35+
Seq(
36+
Tests.Argument(TestFrameworks.ScalaTest,
37+
"-l", "org.scalatest.tags.Slow",
38+
"-m", "org.scalatestplus.testng",
39+
))
40+
41+
enablePlugins(SbtOsgi)
42+
43+
osgiSettings
44+
45+
OsgiKeys.exportPackage := Seq(
46+
"org.scalatestplus.testng.*"
47+
)
48+
49+
OsgiKeys.importPackage := Seq(
50+
"org.scalatest.*",
51+
"org.scalactic.*",
52+
"scala.*;version=\"$<range;[==,=+);$<replace;"+scalaBinaryVersion.value+";-;.>>\"",
53+
"*;resolution:=optional"
54+
)
55+
56+
OsgiKeys.additionalHeaders:= Map(
57+
"Bundle-Name" -> "ScalaTestPlusTestNG",
58+
"Bundle-Description" -> "ScalaTest+TestNG is an open-source integration library between ScalaTest and TestNG for Scala projects.",
59+
"Bundle-DocURL" -> "http://www.scalatest.org/",
60+
"Bundle-Vendor" -> "Artima, Inc."
61+
)
62+
63+
publishTo := {
64+
val nexus = "https://oss.sonatype.org/"
65+
Some("publish-releases" at nexus + "service/local/staging/deploy/maven2")
66+
}
67+
68+
publishMavenStyle := true
69+
70+
publishArtifact in Test := false
71+
72+
pomIncludeRepository := { _ => false }
73+
74+
credentials += Credentials(Path.userHome / ".ivy2" / ".credentials")
75+
76+
pgpSecretRing := file((Path.userHome / ".gnupg" / "secring.gpg").getAbsolutePath)
77+
78+
pgpPassphrase := None

project/build.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version=1.2.8

project/plugins.sbt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.1")
2+
3+
addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.2.2")
4+
5+
addSbtPlugin("com.typesafe.sbt" % "sbt-osgi" % "0.9.4")
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2001-2018 Artima, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.scalatestplus.testng
17+
18+
import java.util.ResourceBundle
19+
import java.text.MessageFormat
20+
21+
private[testng] object Resources {
22+
23+
lazy val resourceBundle = ResourceBundle.getBundle("org.scalatestplus.testng.MessageBundle")
24+
25+
def makeString(resourceName: String, args: Array[Any]): String = {
26+
val raw = resourceBundle.getString(resourceName)
27+
formatString(raw, args)
28+
}
29+
30+
def formatString(rawString: String, args: Array[Any]): String = {
31+
val msgFmt = new MessageFormat(rawString)
32+
msgFmt.format(args.toArray)
33+
}
34+
35+
def testSucceededIconChar(): String = resourceBundle.getString("testSucceededIconChar")
36+
37+
def rawTestSucceededIconChar: String = resourceBundle.getString("testSucceededIconChar")
38+
39+
def iconPlusShortName(param0: Any, param1: Any): String = makeString("iconPlusShortName", Array(param0, param1))
40+
41+
def rawIconPlusShortName: String = resourceBundle.getString("iconPlusShortName")
42+
43+
def testNotFound(param0: Any): String = makeString("testNotFound", Array(param0))
44+
45+
def rawTestNotFound: String = resourceBundle.getString("testNotFound")
46+
47+
def testNGConfigFailed(): String = resourceBundle.getString("testNGConfigFailed")
48+
49+
def rawTestNGConfigFailed: String = resourceBundle.getString("testNGConfigFailed")
50+
51+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2001-2013 Artima, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.scalatestplus.testng
17+
18+
import org.testng.IAnnotationTransformer
19+
import org.testng.annotations.ITestAnnotation
20+
import java.lang.reflect.Method
21+
import java.lang.reflect.Constructor
22+
23+
// Making this private[scalatest] so that it is public to Java, to ensure the reflection thing works. But
24+
// doesn't show up in Scaladoc, and not part of the ScalaTest API. This is part of its implementation.
25+
// Probably might work as private[testng], but not sure and right before the release.
26+
private[testng] class SingleTestAnnotationTransformer(testName: String) extends IAnnotationTransformer {
27+
override def transform( annotation: ITestAnnotation, testClass: java.lang.Class[_], testConstructor: Constructor[_], testMethod: Method): Unit = {
28+
if (testName == testMethod.getName)
29+
annotation.setGroups(Array("org.scalatestplus.testng.singlemethodrun.methodname"))
30+
}
31+
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* Copyright 2001-2018 Artima, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.scalatestplus.testng
17+
18+
import java.lang.reflect.{Method, Modifier}
19+
import org.scalatest.{Resources => _, _}
20+
import org.scalatest.events.{Formatter, IndentedText}
21+
import org.scalatest.exceptions.NotAllowedException
22+
23+
import scala.collection.immutable.TreeSet
24+
import scala.reflect.NameTransformer.decode
25+
26+
private[testng] object TestNGHelper {
27+
28+
def formatterForSuiteAborted(suite: Suite, message: String): Option[Formatter] = {
29+
val suiteClass = suite.getClass
30+
val actualSuiteName =
31+
if (suiteClass.getName == "org.scalatest.DeferredAbortedSuite")
32+
suiteClass.getDeclaredField("suiteClassName").get(suite).asInstanceOf[String]
33+
else
34+
suiteClass.getName
35+
Some(IndentedText(actualSuiteName, message, 0))
36+
}
37+
38+
def getIndentedTextForTest(testText: String, level: Int, includeIcon: Boolean) = {
39+
val decodedTestText = scala.reflect.NameTransformer.decode(testText)
40+
val formattedText =
41+
if (includeIcon) {
42+
val testSucceededIcon = Resources.testSucceededIconChar
43+
(" " * (if (level == 0) 0 else (level - 1))) + Resources.iconPlusShortName(testSucceededIcon, decodedTestText)
44+
}
45+
else {
46+
(" " * level) + decodedTestText
47+
}
48+
IndentedText(formattedText, decodedTestText, level)
49+
}
50+
51+
def isTestMethodGoodies(m: Method) = {
52+
53+
val isInstanceMethod = !Modifier.isStatic(m.getModifiers())
54+
55+
// name must have at least 4 chars (minimum is "test")
56+
val simpleName = m.getName
57+
val firstFour = if (simpleName.length >= 4) simpleName.substring(0, 4) else ""
58+
59+
val paramTypes = m.getParameterTypes
60+
val hasNoParams = paramTypes.length == 0
61+
62+
// Discover testNames(Informer) because if we didn't it might be confusing when someone
63+
// actually wrote a testNames(Informer) method and it was silently ignored.
64+
val isTestNames = simpleName == "testNames"
65+
val isTestTags = simpleName == "testTags"
66+
val isTestDataFor = (simpleName == "testDataFor" && paramTypes.length == 2 && classOf[String].isAssignableFrom(paramTypes(0)) && classOf[ConfigMap].isAssignableFrom(paramTypes(1))) ||
67+
(simpleName == "testDataFor$default$2" && paramTypes.length == 0)
68+
69+
(isInstanceMethod, simpleName, firstFour, paramTypes, hasNoParams, isTestNames, isTestTags, isTestDataFor)
70+
}
71+
72+
def takesInformer(m: Method) = {
73+
val paramTypes = m.getParameterTypes
74+
paramTypes.length == 1 && classOf[Informer].isAssignableFrom(paramTypes(0))
75+
}
76+
77+
val InformerInParens = "(Informer)"
78+
val FixtureAndInformerInParens = "(FixtureParam, Informer)"
79+
val FixtureInParens = "(FixtureParam)"
80+
81+
object EncodedOrdering extends Ordering[String] {
82+
def compare(x: String, y: String): Int = {
83+
decode(x) compareTo decode(y)
84+
}
85+
}
86+
87+
def yeOldeTestNames(theSuite: Suite): Set[String] = {
88+
89+
def isTestMethod(m: Method) = {
90+
91+
// Factored out to share code with fixture.Suite.testNames
92+
val (isInstanceMethod, simpleName, firstFour, paramTypes, hasNoParams, isTestNames, isTestTags, isTestDataFor) = isTestMethodGoodies(m)
93+
94+
isInstanceMethod && (firstFour == "test") && !isTestDataFor && ((hasNoParams && !isTestNames && !isTestTags) || takesInformer(m))
95+
}
96+
97+
val testNameArray =
98+
for (m <- theSuite.getClass.getMethods; if isTestMethod(m))
99+
yield if (takesInformer(m)) m.getName + InformerInParens else m.getName
100+
101+
val result = TreeSet.empty[String](EncodedOrdering) ++ testNameArray
102+
if (result.size != testNameArray.length) {
103+
throw new NotAllowedException("Howdy", 0)
104+
}
105+
result
106+
}
107+
108+
def testMethodTakesAFixtureAndInformer(testName: String) = testName.endsWith(FixtureAndInformerInParens)
109+
110+
def testMethodTakesAnInformer(testName: String): Boolean = testName.endsWith(InformerInParens)
111+
112+
def testMethodTakesAFixture(testName: String) = testName.endsWith(FixtureInParens)
113+
114+
def simpleNameForTest(testName: String) =
115+
if (testName.endsWith(FixtureAndInformerInParens))
116+
testName.substring(0, testName.length - FixtureAndInformerInParens.length)
117+
else if (testName.endsWith(FixtureInParens))
118+
testName.substring(0, testName.length - FixtureInParens.length)
119+
else if (testName.endsWith(InformerInParens))
120+
testName.substring(0, testName.length - InformerInParens.length)
121+
else
122+
testName
123+
124+
def getMethodForTestName(theSuite: org.scalatest.Suite, testName: String): Method = {
125+
val candidateMethods = theSuite.getClass.getMethods.filter(_.getName == simpleNameForTest(testName))
126+
val found =
127+
if (testMethodTakesAFixtureAndInformer(testName))
128+
candidateMethods.find(
129+
candidateMethod => {
130+
val paramTypes = candidateMethod.getParameterTypes
131+
paramTypes.length == 2 && paramTypes(1) == classOf[Informer]
132+
}
133+
)
134+
else if (testMethodTakesAnInformer(testName))
135+
candidateMethods.find(
136+
candidateMethod => {
137+
val paramTypes = candidateMethod.getParameterTypes
138+
paramTypes.length == 1 && paramTypes(0) == classOf[Informer]
139+
}
140+
)
141+
else if (testMethodTakesAFixture(testName))
142+
candidateMethods.find(
143+
candidateMethod => {
144+
val paramTypes = candidateMethod.getParameterTypes
145+
paramTypes.length == 1
146+
}
147+
)
148+
else
149+
candidateMethods.find(_.getParameterTypes.length == 0)
150+
151+
found match {
152+
case Some(method) => method
153+
case None =>
154+
throw new IllegalArgumentException(Resources.testNotFound(testName))
155+
}
156+
}
157+
158+
def mergeMap[A, B](ms: List[Map[A, B]])(f: (B, B) => B): Map[A, B] =
159+
(Map[A, B]() /: (for (m <- ms; kv <- m) yield kv)) { (a, kv) =>
160+
a + (if (a.contains(kv._1)) kv._1 -> f(a(kv._1), kv._2) else kv)
161+
}
162+
163+
def autoTagClassAnnotations(tags: Map[String, Set[String]], theSuite: Suite) = {
164+
val suiteTags = for {
165+
a <- theSuite.getClass.getAnnotations
166+
annotationClass = a.annotationType
167+
if annotationClass.isAnnotationPresent(classOf[TagAnnotation])
168+
} yield annotationClass.getName
169+
170+
val autoTestTags =
171+
if (suiteTags.size > 0)
172+
Map() ++ theSuite.testNames.map(tn => (tn, suiteTags.toSet))
173+
else
174+
Map.empty[String, Set[String]]
175+
176+
mergeMap[String, Set[String]](List(tags, autoTestTags)) ( _ ++ _ )
177+
}
178+
179+
}

0 commit comments

Comments
 (0)