Skip to content

Commit aa992b0

Browse files
committed
Streamline native injection w.r.t cloning
Closes testng-team#1994 Currently as part of supporting native injection TestNG resorts to invoking the “clone()” method on the parameter being natively injected. This becomes a problem because XmlTest is the only internal TestNG object that implements the Cloneable interface wherein it resorts to something like a deep copy and adds the cloned instance to the XmlSuite. This causes a lot of phony XmlTest objects to be added up to the suite and causes issues as detailed in the bug. Fixed this by introducing a marker interface to indicate to TestNG to skip cloning and use the object as is. Annotated “XmlTest” with this annotation so that we dont clone xmltest but instead use it as is. Yes this will expose the XmlTest object, but its already exposed to the end user via various different means. So I guess we are ok here.
1 parent f1111e0 commit aa992b0

File tree

6 files changed

+73
-2
lines changed

6 files changed

+73
-2
lines changed

CHANGES.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Current
2+
Fixed: GITHUB-1994: Prevent duplication of XmlTest objects when its used as a parameter for Native Injection (Krishnan Mahadevan)
23
Fixed: GITHUB-165: @AfterGroups is not executed when group member fails or is skipped (Krishnan Mahadevan)
34
Fixed: GITHUB-118: @BeforeGroups only called if group is specified explicitly (Krishnan Mahadevan)
45
Fixed: GITHUB-182: Inherited test methods do not get expected group behavior (Krishnan Mahadevan)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.testng.annotations;
2+
3+
4+
import java.lang.annotation.ElementType;
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Marker interface which when used on a type will ensure that TestNG does not clone the object but
10+
* instead uses it as is when TestNG resorts to dependency injection.
11+
*/
12+
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
13+
@Target({ElementType.TYPE})
14+
public @interface SkipCloning {
15+
16+
}

src/main/java/org/testng/internal/TestResult.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.testng.Reporter;
1313
import org.testng.TestNGException;
1414
import org.testng.TestRunner;
15+
import org.testng.annotations.SkipCloning;
1516
import org.testng.collections.Lists;
1617
import org.testng.collections.Objects;
1718

@@ -276,7 +277,7 @@ public void setParameters(Object[] parameters) {
276277
m_parameters = new Object[parameters.length];
277278
for (int i = 0; i < parameters.length; i++) {
278279
// Copy parameter if possible because user may change it later
279-
if (parameters[i] instanceof Cloneable) {
280+
if (canAttemptCloning(parameters[i]) ) {
280281
try {
281282
Method clone = parameters[i].getClass().getDeclaredMethod("clone");
282283
m_parameters[i] = clone.invoke(parameters[i]);
@@ -292,6 +293,17 @@ public void setParameters(Object[] parameters) {
292293
}
293294
}
294295

296+
private static boolean canAttemptCloning(Object parameter) {
297+
if (parameter == null) {
298+
return false;
299+
}
300+
SkipCloning skipCloning = parameter.getClass().getAnnotation(SkipCloning.class);
301+
if (skipCloning != null) {
302+
return false;
303+
}
304+
return parameter instanceof Cloneable;
305+
}
306+
295307
@Override
296308
public Object getInstance() {
297309
return IParameterInfo.embeddedInstance(this.m_method.getInstance());

src/main/java/org/testng/xml/XmlTest.java

+2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99

1010
import org.testng.TestNG;
1111
import org.testng.TestNGException;
12+
import org.testng.annotations.SkipCloning;
1213
import org.testng.collections.Lists;
1314
import org.testng.collections.Maps;
1415
import org.testng.xml.dom.ParentSetter;
1516

1617
import static org.testng.xml.XmlSuite.ParallelMode.skipDeprecatedValues;
1718

1819
/** This class describes the tag &lt;test&gt; in testng.xml. */
20+
@SkipCloning
1921
public class XmlTest implements Cloneable {
2022

2123
public static final int DEFAULT_TIMEOUT_MS = Integer.MAX_VALUE;

src/test/java/test/parameters/ParameterTest.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package test.parameters;
22

3-
import org.testng.ITestNGListener;
43
import org.testng.ITestResult;
54
import org.testng.TestListenerAdapter;
65
import org.testng.TestNG;
@@ -15,6 +14,7 @@
1514
import java.util.HashMap;
1615
import java.util.List;
1716
import java.util.Map;
17+
import test.parameters.issue1994.TestclassSample;
1818

1919
import static org.assertj.core.api.Assertions.assertThat;
2020

@@ -130,4 +130,12 @@ public void testNativeInjectionAndParamsInjection() {
130130
testng.run();
131131
assertThat(listener.getPassedTests().isEmpty()).isFalse();
132132
}
133+
134+
@Test(description = "GITHUB-1994")
135+
public void testToEnsureNativeInjectionDoesnotResortToCloning() {
136+
XmlSuite xmlsuite = createXmlSuite("suite", "test", TestclassSample.class);
137+
TestNG testng = create(xmlsuite);
138+
testng.run();
139+
assertThat(TestclassSample.count).isEqualTo(1);
140+
}
133141
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package test.parameters.issue1994;
2+
3+
import org.testng.ISuite;
4+
import org.testng.ISuiteListener;
5+
import org.testng.annotations.BeforeClass;
6+
import org.testng.annotations.Listeners;
7+
import org.testng.annotations.Test;
8+
import org.testng.xml.XmlTest;
9+
10+
@Listeners(TestclassSample.class)
11+
public class TestclassSample implements ISuiteListener {
12+
13+
public static int count = 0;
14+
15+
@BeforeClass
16+
public void beforeClass(XmlTest xmlTest) {
17+
}
18+
19+
@Test
20+
public void testMethod() {
21+
}
22+
23+
@Override
24+
public void onFinish(ISuite suite) {
25+
setCount(suite.getXmlSuite().getTests().size());
26+
}
27+
28+
private static void setCount(int count) {
29+
TestclassSample.count = count;
30+
}
31+
32+
}

0 commit comments

Comments
 (0)