diff --git a/CHANGES.txt b/CHANGES.txt
index 67e82fb0a..815e3192d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
Current (7.10.0)
Fixed: GITHUB:3084: Document project's PGP artifact signing keys (Krishnan Mahadevan)
+Fixed: GITHUB-3079: Associate a unique id with every test class object instantiated by TestNG (Krishnan Mahadevan)
Fixed: GITHUB:3040: replace the usages of synchronized with ReentrantLock (Krishnan Mahadevan)
Fixed: GITHUB-3041: TestNG 7.x DataProvider works in opposite to TestNG 6.x when retrying tests. (Krishnan Mahadevan)
Fixed: GITHUB-3066: How to dynamically adjust the number of TestNG threads after IExecutorFactory is deprecated? (Krishnan Mahadevan)
diff --git a/testng-core-api/src/main/java/org/testng/IClass.java b/testng-core-api/src/main/java/org/testng/IClass.java
index adf1df903..2c94a9aea 100644
--- a/testng-core-api/src/main/java/org/testng/IClass.java
+++ b/testng-core-api/src/main/java/org/testng/IClass.java
@@ -27,14 +27,30 @@ public interface IClass {
*
* @param create flag if a new set of instances must be returned (if set to false
)
* @return All the instances the methods will be invoked upon.
+ * @deprecated - As of TestNG v7.10.0
*/
+ @Deprecated
Object[] getInstances(boolean create);
+ /**
+ * Returns all the instances the methods will be invoked upon. This will typically be an array of
+ * one object in the absence of a @Factory annotation.
+ *
+ * @param create flag if a new set of instances must be returned (if set to false
)
+ * @param errorMsgPrefix - Text that should be prefixed to the error message when there are
+ * issues. Can be empty.
+ * @return All the instances the methods will be invoked upon.
+ * @deprecated - As of TestNG v7.10.0
+ */
+ @Deprecated
default Object[] getInstances(boolean create, String errorMsgPrefix) {
return getInstances(create);
}
- long[] getInstanceHashCodes();
-
+ /**
+ * @param instance - The instance to be added.
+ * @deprecated - As of TestNG v7.10.0
+ */
+ @Deprecated
void addInstance(Object instance);
}
diff --git a/testng-core-api/src/main/java/org/testng/ITestNGMethod.java b/testng-core-api/src/main/java/org/testng/ITestNGMethod.java
index 0a5ec017f..bff0385de 100644
--- a/testng-core-api/src/main/java/org/testng/ITestNGMethod.java
+++ b/testng-core-api/src/main/java/org/testng/ITestNGMethod.java
@@ -279,7 +279,7 @@ default CustomAttribute[] getAttributes() {
/**
* @return - An {@link IDataProviderMethod} for a data provider powered test method and null
- *
otherwise.
+ * otherwise.
*/
default IDataProviderMethod getDataProviderMethod() {
return null;
diff --git a/testng-core/src/main/java/org/testng/TestClass.java b/testng-core/src/main/java/org/testng/TestClass.java
index 4bc150f2e..1ce6add95 100644
--- a/testng-core/src/main/java/org/testng/TestClass.java
+++ b/testng-core/src/main/java/org/testng/TestClass.java
@@ -16,7 +16,7 @@
* This class represents a test class: - The test methods - The configuration methods (test and
* method) - The class file
*/
-class TestClass extends NoOpTestClass implements ITestClass, ITestClassConfigInfo {
+class TestClass extends NoOpTestClass implements ITestClass, ITestClassConfigInfo, IObject {
private IAnnotationFinder annotationFinder = null;
// The Strategy used to locate test methods (TestNG, JUnit, etc...)
@@ -106,14 +106,13 @@ private void initTestClassesAndInstances() {
//
// TestClasses and instances
//
- Object[] instances = getInstances(true, this.m_errorMsgPrefix);
- for (Object instance : instances) {
- instance = IParameterInfo.embeddedInstance(instance);
- if (instance instanceof ITest) {
- testName = ((ITest) instance).getTestName();
- break;
- }
- }
+ IObject.IdentifiableObject[] instances = getObjects(true, this.m_errorMsgPrefix);
+ Arrays.stream(instances)
+ .map(IdentifiableObject::getInstance)
+ .map(IParameterInfo::embeddedInstance)
+ .filter(it -> it instanceof ITest)
+ .findFirst()
+ .ifPresent(it -> testName = ((ITest) it).getTestName());
if (testName == null) {
testName = iClass.getTestName();
}
@@ -129,9 +128,14 @@ public Object[] getInstances(boolean create, String errorMsgPrefix) {
return iClass.getInstances(create, this.m_errorMsgPrefix);
}
+ @Override
+ public IObject.IdentifiableObject[] getObjects(boolean create, String errorMsgPrefix) {
+ return IObject.objects(iClass, create, errorMsgPrefix);
+ }
+
@Override
public long[] getInstanceHashCodes() {
- return iClass.getInstanceHashCodes();
+ return IObject.instanceHashCodes(iClass);
}
@Override
@@ -139,11 +143,16 @@ public void addInstance(Object instance) {
iClass.addInstance(instance);
}
+ @Override
+ public void addObject(IObject.IdentifiableObject instance) {
+ IObject.cast(iClass).ifPresent(it -> it.addObject(instance));
+ }
+
private void initMethods() {
ITestNGMethod[] methods = testMethodFinder.getTestMethods(m_testClass, xmlTest);
m_testMethods = createTestMethods(methods);
- for (Object eachInstance : iClass.getInstances(false)) {
+ for (IdentifiableObject eachInstance : IObject.objects(iClass, false)) {
m_beforeSuiteMethods =
ConfigurationMethod.createSuiteConfigurationMethods(
objectFactory,
@@ -182,7 +191,7 @@ private void initMethods() {
true,
xmlTest,
eachInstance);
- Object instance = IParameterInfo.embeddedInstance(eachInstance);
+ Object instance = IParameterInfo.embeddedInstance(eachInstance.getInstance());
beforeClassConfig.put(instance, Arrays.asList(m_beforeClassMethods));
m_afterClassMethods =
ConfigurationMethod.createClassConfigurationMethods(
@@ -234,7 +243,7 @@ private ITestNGMethod[] createTestMethods(ITestNGMethod[] methods) {
for (ITestNGMethod tm : methods) {
ConstructorOrMethod m = tm.getConstructorOrMethod();
if (m.getDeclaringClass().isAssignableFrom(m_testClass)) {
- for (Object o : iClass.getInstances(false)) {
+ for (IdentifiableObject o : IObject.objects(iClass, false)) {
log(4, "Adding method " + tm + " on TestClass " + m_testClass);
vResult.add(new TestNGMethod(objectFactory, m.getMethod(), annotationFinder, xmlTest, o));
}
diff --git a/testng-core/src/main/java/org/testng/internal/BaseClassFinder.java b/testng-core/src/main/java/org/testng/internal/BaseClassFinder.java
index 5fbd2b526..75af20b88 100644
--- a/testng-core/src/main/java/org/testng/internal/BaseClassFinder.java
+++ b/testng-core/src/main/java/org/testng/internal/BaseClassFinder.java
@@ -32,7 +32,7 @@ protected IClass findOrCreateIClass(
ITestContext context,
Class> cls,
XmlClass xmlClass,
- Object instance,
+ IObject.IdentifiableObject instance,
IAnnotationFinder annotationFinder,
ITestObjectFactory objectFactory) {
diff --git a/testng-core/src/main/java/org/testng/internal/BaseTestMethod.java b/testng-core/src/main/java/org/testng/internal/BaseTestMethod.java
index 976f8b855..503e58a99 100644
--- a/testng-core/src/main/java/org/testng/internal/BaseTestMethod.java
+++ b/testng-core/src/main/java/org/testng/internal/BaseTestMethod.java
@@ -9,6 +9,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
@@ -34,7 +35,8 @@
import org.testng.xml.XmlTest;
/** Superclass to represent both @Test and @Configuration methods. */
-public abstract class BaseTestMethod implements ITestNGMethod, IInvocationStatus {
+public abstract class BaseTestMethod
+ implements ITestNGMethod, IInvocationStatus, IInstanceIdentity {
private static final Pattern SPACE_SEPARATOR_PATTERN = Pattern.compile(" +");
@@ -81,7 +83,7 @@ public abstract class BaseTestMethod implements ITestNGMethod, IInvocationStatus
private int m_interceptedPriority;
private XmlTest m_xmlTest;
- private final Object m_instance;
+ private final IObject.IdentifiableObject m_instance;
private final Map m_testMethodToRetryAnalyzer = Maps.newConcurrentMap();
protected final ITestObjectFactory m_objectFactory;
@@ -91,7 +93,7 @@ public BaseTestMethod(
String methodName,
ConstructorOrMethod com,
IAnnotationFinder annotationFinder,
- Object instance) {
+ IObject.IdentifiableObject instance) {
m_objectFactory = objectFactory;
m_methodClass = com.getDeclaringClass();
m_method = com;
@@ -148,13 +150,23 @@ public String getMethodName() {
@Override
public Object getInstance() {
- return IParameterInfo.embeddedInstance(m_instance);
+ return Optional.ofNullable(m_instance)
+ .map(IObject.IdentifiableObject::getInstance)
+ .map(IParameterInfo::embeddedInstance)
+ .orElse(null);
+ }
+
+ @Override
+ public UUID getInstanceId() {
+ return Optional.ofNullable(m_instance)
+ .map(IObject.IdentifiableObject::getInstanceId)
+ .orElse(null);
}
/** {@inheritDoc} */
@Override
public long[] getInstanceHashCodes() {
- return m_testClass.getInstanceHashCodes();
+ return IObject.instanceHashCodes(m_testClass);
}
/**
@@ -379,8 +391,8 @@ public boolean equals(Object obj) {
@Override
public int hashCode() {
int hash = m_method.hashCode();
- if (m_instance != null) {
- hash = hash * 31 + System.identityHashCode(m_instance);
+ if (getInstance() != null) {
+ hash = hash * 31 + System.identityHashCode(getInstance());
}
return hash;
}
@@ -790,8 +802,11 @@ public String getQualifiedName() {
@Override
public IParameterInfo getFactoryMethodParamsInfo() {
- if (m_instance instanceof IParameterInfo) {
- return (IParameterInfo) m_instance;
+ if (m_instance == null) {
+ return null;
+ }
+ if (m_instance.getInstance() instanceof IParameterInfo) {
+ return (IParameterInfo) m_instance.getInstance();
}
return null;
}
diff --git a/testng-core/src/main/java/org/testng/internal/ClassImpl.java b/testng-core/src/main/java/org/testng/internal/ClassImpl.java
index 262f40eb6..f893b6008 100644
--- a/testng-core/src/main/java/org/testng/internal/ClassImpl.java
+++ b/testng-core/src/main/java/org/testng/internal/ClassImpl.java
@@ -1,5 +1,6 @@
package org.testng.internal;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.testng.IClass;
@@ -20,15 +21,15 @@
import org.testng.xml.XmlTest;
/** Implementation of an IClass. */
-public class ClassImpl implements IClass {
+public class ClassImpl implements IClass, IObject {
private final Class> m_class;
- private Object m_defaultInstance = null;
+ private IObject.IdentifiableObject m_defaultInstance = null;
private final IAnnotationFinder m_annotationFinder;
- private final List