Skip to content

Commit a3ec3a3

Browse files
committed
Add Unique Id for all test class instances
Closes #3079
1 parent 438674c commit a3ec3a3

27 files changed

+505
-109
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Current (7.10.0)
22
Fixed: GITHUB:3084: Document project's PGP artifact signing keys (Krishnan Mahadevan)
3+
Fixed: GITHUB-3079: Associate a unique id with every test class object instantiated by TestNG (Krishnan Mahadevan)
34
Fixed: GITHUB:3040: replace the usages of synchronized with ReentrantLock (Krishnan Mahadevan)
45
Fixed: GITHUB-3041: TestNG 7.x DataProvider works in opposite to TestNG 6.x when retrying tests. (Krishnan Mahadevan)
56
Fixed: GITHUB-3066: How to dynamically adjust the number of TestNG threads after IExecutorFactory is deprecated? (Krishnan Mahadevan)

testng-core-api/src/main/java/org/testng/IClass.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,30 @@ public interface IClass {
2727
*
2828
* @param create flag if a new set of instances must be returned (if set to <code>false</code>)
2929
* @return All the instances the methods will be invoked upon.
30+
* @deprecated - As of TestNG <code>v7.10.0</code>
3031
*/
32+
@Deprecated
3133
Object[] getInstances(boolean create);
3234

35+
/**
36+
* Returns all the instances the methods will be invoked upon. This will typically be an array of
37+
* one object in the absence of a @Factory annotation.
38+
*
39+
* @param create flag if a new set of instances must be returned (if set to <code>false</code>)
40+
* @param errorMsgPrefix - Text that should be prefixed to the error message when there are
41+
* issues. Can be empty.
42+
* @return All the instances the methods will be invoked upon.
43+
* @deprecated - As of TestNG <code>v7.10.0</code>
44+
*/
45+
@Deprecated
3346
default Object[] getInstances(boolean create, String errorMsgPrefix) {
3447
return getInstances(create);
3548
}
3649

37-
long[] getInstanceHashCodes();
38-
50+
/**
51+
* @param instance - The instance to be added.
52+
* @deprecated - As of TestNG <code>v7.10.0</code>
53+
*/
54+
@Deprecated
3955
void addInstance(Object instance);
4056
}

testng-core-api/src/main/java/org/testng/ITestNGMethod.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.List;
44
import java.util.Map;
55
import java.util.Set;
6+
import java.util.UUID;
67
import java.util.concurrent.Callable;
78
import org.testng.annotations.CustomAttribute;
89
import org.testng.internal.ConstructorOrMethod;
@@ -279,7 +280,7 @@ default CustomAttribute[] getAttributes() {
279280

280281
/**
281282
* @return - An {@link IDataProviderMethod} for a data provider powered test method and <code>null
282-
* </code> otherwise.
283+
* </code> otherwise.
283284
*/
284285
default IDataProviderMethod getDataProviderMethod() {
285286
return null;
@@ -296,4 +297,12 @@ default Class<?>[] getParameterTypes() {
296297
default boolean isIgnoreFailure() {
297298
return false;
298299
}
300+
301+
/**
302+
* @return - A <code>{@link UUID}</code> that represents a unique id which is associated with
303+
* every test class object.
304+
*/
305+
default UUID getInstanceId() {
306+
return null;
307+
}
299308
}

testng-core/src/main/java/org/testng/TestClass.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* This class represents a test class: - The test methods - The configuration methods (test and
1717
* method) - The class file
1818
*/
19-
class TestClass extends NoOpTestClass implements ITestClass, ITestClassConfigInfo {
19+
class TestClass extends NoOpTestClass implements ITestClass, ITestClassConfigInfo, IObject {
2020

2121
private IAnnotationFinder annotationFinder = null;
2222
// The Strategy used to locate test methods (TestNG, JUnit, etc...)
@@ -106,14 +106,13 @@ private void initTestClassesAndInstances() {
106106
//
107107
// TestClasses and instances
108108
//
109-
Object[] instances = getInstances(true, this.m_errorMsgPrefix);
110-
for (Object instance : instances) {
111-
instance = IParameterInfo.embeddedInstance(instance);
112-
if (instance instanceof ITest) {
113-
testName = ((ITest) instance).getTestName();
114-
break;
115-
}
116-
}
109+
IObject.IdentifiableObject[] instances = getObjects(true, this.m_errorMsgPrefix);
110+
Arrays.stream(instances)
111+
.map(IdentifiableObject::getInstance)
112+
.map(IParameterInfo::embeddedInstance)
113+
.filter(it -> it instanceof ITest)
114+
.findFirst()
115+
.ifPresent(it -> testName = ((ITest) it).getTestName());
117116
if (testName == null) {
118117
testName = iClass.getTestName();
119118
}
@@ -129,21 +128,31 @@ public Object[] getInstances(boolean create, String errorMsgPrefix) {
129128
return iClass.getInstances(create, this.m_errorMsgPrefix);
130129
}
131130

131+
@Override
132+
public IObject.IdentifiableObject[] getObjects(boolean create, String errorMsgPrefix) {
133+
return IObject.objects(iClass, create, errorMsgPrefix);
134+
}
135+
132136
@Override
133137
public long[] getInstanceHashCodes() {
134-
return iClass.getInstanceHashCodes();
138+
return IObject.instanceHashCodes(iClass);
135139
}
136140

137141
@Override
138142
public void addInstance(Object instance) {
139143
iClass.addInstance(instance);
140144
}
141145

146+
@Override
147+
public void addObject(IObject.IdentifiableObject instance) {
148+
IObject.cast(iClass).ifPresent(it -> it.addObject(instance));
149+
}
150+
142151
private void initMethods() {
143152
ITestNGMethod[] methods = testMethodFinder.getTestMethods(m_testClass, xmlTest);
144153
m_testMethods = createTestMethods(methods);
145154

146-
for (Object eachInstance : iClass.getInstances(false)) {
155+
for (IdentifiableObject eachInstance : IObject.objects(iClass, false)) {
147156
m_beforeSuiteMethods =
148157
ConfigurationMethod.createSuiteConfigurationMethods(
149158
objectFactory,
@@ -182,7 +191,7 @@ private void initMethods() {
182191
true,
183192
xmlTest,
184193
eachInstance);
185-
Object instance = IParameterInfo.embeddedInstance(eachInstance);
194+
Object instance = IParameterInfo.embeddedInstance(eachInstance.getInstance());
186195
beforeClassConfig.put(instance, Arrays.asList(m_beforeClassMethods));
187196
m_afterClassMethods =
188197
ConfigurationMethod.createClassConfigurationMethods(
@@ -234,7 +243,7 @@ private ITestNGMethod[] createTestMethods(ITestNGMethod[] methods) {
234243
for (ITestNGMethod tm : methods) {
235244
ConstructorOrMethod m = tm.getConstructorOrMethod();
236245
if (m.getDeclaringClass().isAssignableFrom(m_testClass)) {
237-
for (Object o : iClass.getInstances(false)) {
246+
for (IdentifiableObject o : IObject.objects(iClass, false)) {
238247
log(4, "Adding method " + tm + " on TestClass " + m_testClass);
239248
vResult.add(new TestNGMethod(objectFactory, m.getMethod(), annotationFinder, xmlTest, o));
240249
}

testng-core/src/main/java/org/testng/internal/BaseClassFinder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ protected IClass findOrCreateIClass(
3232
ITestContext context,
3333
Class<?> cls,
3434
XmlClass xmlClass,
35-
Object instance,
35+
IObject.IdentifiableObject instance,
3636
IAnnotationFinder annotationFinder,
3737
ITestObjectFactory objectFactory) {
3838

testng-core/src/main/java/org/testng/internal/BaseTestMethod.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.Objects;
1010
import java.util.Optional;
1111
import java.util.Set;
12+
import java.util.UUID;
1213
import java.util.concurrent.Callable;
1314
import java.util.concurrent.ConcurrentLinkedQueue;
1415
import java.util.concurrent.atomic.AtomicInteger;
@@ -81,7 +82,7 @@ public abstract class BaseTestMethod implements ITestNGMethod, IInvocationStatus
8182
private int m_interceptedPriority;
8283

8384
private XmlTest m_xmlTest;
84-
private final Object m_instance;
85+
private final IObject.IdentifiableObject m_instance;
8586

8687
private final Map<String, IRetryAnalyzer> m_testMethodToRetryAnalyzer = Maps.newConcurrentMap();
8788
protected final ITestObjectFactory m_objectFactory;
@@ -91,7 +92,7 @@ public BaseTestMethod(
9192
String methodName,
9293
ConstructorOrMethod com,
9394
IAnnotationFinder annotationFinder,
94-
Object instance) {
95+
IObject.IdentifiableObject instance) {
9596
m_objectFactory = objectFactory;
9697
m_methodClass = com.getDeclaringClass();
9798
m_method = com;
@@ -148,13 +149,21 @@ public String getMethodName() {
148149

149150
@Override
150151
public Object getInstance() {
151-
return IParameterInfo.embeddedInstance(m_instance);
152+
return Optional.ofNullable(m_instance)
153+
.map(IObject.IdentifiableObject::getInstance)
154+
.map(IParameterInfo::embeddedInstance)
155+
.orElse(null);
156+
}
157+
158+
@Override
159+
public UUID getInstanceId() {
160+
return m_instance.getInstanceId();
152161
}
153162

154163
/** {@inheritDoc} */
155164
@Override
156165
public long[] getInstanceHashCodes() {
157-
return m_testClass.getInstanceHashCodes();
166+
return IObject.instanceHashCodes(m_testClass);
158167
}
159168

160169
/**
@@ -379,8 +388,8 @@ public boolean equals(Object obj) {
379388
@Override
380389
public int hashCode() {
381390
int hash = m_method.hashCode();
382-
if (m_instance != null) {
383-
hash = hash * 31 + System.identityHashCode(m_instance);
391+
if (getInstance() != null) {
392+
hash = hash * 31 + System.identityHashCode(getInstance());
384393
}
385394
return hash;
386395
}
@@ -790,8 +799,11 @@ public String getQualifiedName() {
790799

791800
@Override
792801
public IParameterInfo getFactoryMethodParamsInfo() {
793-
if (m_instance instanceof IParameterInfo) {
794-
return (IParameterInfo) m_instance;
802+
if (m_instance == null) {
803+
return null;
804+
}
805+
if (m_instance.getInstance() instanceof IParameterInfo) {
806+
return (IParameterInfo) m_instance.getInstance();
795807
}
796808
return null;
797809
}

testng-core/src/main/java/org/testng/internal/ClassImpl.java

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.testng.internal;
22

3+
import java.util.Arrays;
34
import java.util.List;
45
import java.util.Map;
56
import org.testng.IClass;
@@ -20,15 +21,15 @@
2021
import org.testng.xml.XmlTest;
2122

2223
/** Implementation of an IClass. */
23-
public class ClassImpl implements IClass {
24+
public class ClassImpl implements IClass, IObject {
2425

2526
private final Class<?> m_class;
26-
private Object m_defaultInstance = null;
27+
private IObject.IdentifiableObject m_defaultInstance = null;
2728
private final IAnnotationFinder m_annotationFinder;
28-
private final List<Object> m_instances = Lists.newArrayList();
29+
private final List<IObject.IdentifiableObject> identifiableObjects = Lists.newArrayList();
2930
private final Map<Class<?>, IClass> m_classes;
3031
private long[] m_instanceHashCodes;
31-
private final Object m_instance;
32+
private final IObject.IdentifiableObject m_instance;
3233
private final ITestObjectFactory m_objectFactory;
3334
private String m_testName = null;
3435
private final XmlClass m_xmlClass;
@@ -38,7 +39,7 @@ public ClassImpl(
3839
ITestContext context,
3940
Class<?> cls,
4041
XmlClass xmlClass,
41-
Object instance,
42+
IObject.IdentifiableObject instance,
4243
Map<Class<?>, IClass> classes,
4344
IAnnotationFinder annotationFinder,
4445
ITestObjectFactory objectFactory) {
@@ -49,8 +50,8 @@ public ClassImpl(
4950
m_annotationFinder = annotationFinder;
5051
m_instance = instance;
5152
m_objectFactory = objectFactory;
52-
if (instance instanceof ITest) {
53-
m_testName = ((ITest) instance).getTestName();
53+
if (IObject.IdentifiableObject.unwrap(instance) instanceof ITest) {
54+
m_testName = ((ITest) instance.getInstance()).getTestName();
5455
}
5556
if (m_testName == null) {
5657
ITestAnnotation annotation = m_annotationFinder.findAnnotation(cls, ITestAnnotation.class);
@@ -90,7 +91,7 @@ public XmlClass getXmlClass() {
9091
return m_xmlClass;
9192
}
9293

93-
private Object getDefaultInstance(boolean create, String errMsgPrefix) {
94+
private IObject.IdentifiableObject getDefaultInstance(boolean create, String errMsgPrefix) {
9495
if (m_defaultInstance == null) {
9596
if (m_instance != null) {
9697
m_defaultInstance = m_instance;
@@ -103,10 +104,12 @@ private Object getDefaultInstance(boolean create, String errMsgPrefix) {
103104
BasicAttributes basic = new BasicAttributes(this, null);
104105
DetailedAttributes detailed = newDetailedAttributes(create, errMsgPrefix);
105106
CreationAttributes attributes = new CreationAttributes(m_testContext, basic, detailed);
106-
m_defaultInstance = dispenser.dispense(attributes);
107+
Object raw = dispenser.dispense(attributes);
108+
if (raw != null) {
109+
m_defaultInstance = new IObject.IdentifiableObject(raw);
110+
}
107111
}
108112
}
109-
110113
return m_defaultInstance;
111114
}
112115

@@ -117,21 +120,33 @@ public Object[] getInstances(boolean create) {
117120

118121
@Override
119122
public Object[] getInstances(boolean create, String errorMsgPrefix) {
120-
Object[] result = {};
123+
return Arrays.stream(getObjects(create, errorMsgPrefix))
124+
.map(IdentifiableObject::getInstance)
125+
.toArray(Object[]::new);
126+
}
127+
128+
@Override
129+
public void addObject(IdentifiableObject instance) {
130+
identifiableObjects.add(instance);
131+
}
132+
133+
@Override
134+
public IdentifiableObject[] getObjects(boolean create, String errorMsgPrefix) {
135+
IdentifiableObject[] result = {};
121136

122-
if (!m_instances.isEmpty()) {
123-
result = m_instances.toArray(new Object[0]);
137+
if (!identifiableObjects.isEmpty()) {
138+
result = identifiableObjects.toArray(new IdentifiableObject[0]);
124139
} else {
125-
Object defaultInstance = getDefaultInstance(create, errorMsgPrefix);
140+
IdentifiableObject defaultInstance = getDefaultInstance(create, errorMsgPrefix);
126141
if (defaultInstance != null) {
127-
result = new Object[] {defaultInstance};
142+
result = new IdentifiableObject[] {defaultInstance};
128143
}
129144
}
130145

131-
int m_instanceCount = m_instances.size();
146+
int m_instanceCount = identifiableObjects.size();
132147
m_instanceHashCodes = new long[m_instanceCount];
133148
for (int i = 0; i < m_instanceCount; i++) {
134-
m_instanceHashCodes[i] = computeHashCode(m_instances.get(i));
149+
m_instanceHashCodes[i] = computeHashCode(identifiableObjects.get(i).getInstance());
135150
}
136151
return result;
137152
}
@@ -143,7 +158,7 @@ public String toString() {
143158

144159
@Override
145160
public void addInstance(Object instance) {
146-
m_instances.add(instance);
161+
addObject(new IdentifiableObject(instance));
147162
}
148163

149164
private static int computeHashCode(Object instance) {

0 commit comments

Comments
 (0)