Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow users to access factory method params info #3115

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Current (7.11.0)
Fixed: GITHUB-3111: Replacement API for IClass.getInstanceHashCodes() and IClass.getInstances(boolean) (Krishnan Mahadevan)

7.10.2
Fixed: GITHUB-3117: ListenerComparator doesn't work (Krishnan Mahadevan)
Expand Down
12 changes: 12 additions & 0 deletions testng-core-api/src/main/java/org/testng/IFactoryMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.testng;

import java.util.Optional;

/** Represents a factory method */
public interface IFactoryMethod {

/**
* @return - Returns parameters associated with a factory method wrapped within a {@link Optional}
*/
Optional<Object[]> getParameters();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.testng;

/** Represents the ability to retrieve the parameters associated with a factory method. */
public interface ITestClassInstance<T> {

/** @return - The actual instance associated with a factory method */
T getInstance();

/**
* @return - The actual index of instance associated with a factory method. This index has a 1:1
* correspondence with what were specified via the <code>indices</code> attribute of the
* <code>@Factory</code> annotation. For e.g., lets say you specified the indices to the "1"
* and your factory returned 4 instances, then the instance on which this method is invoked
* would have the value as "1".
*/
int getIndex();

/**
* @return - returns an index which indicates the running position in the array of test class
* instances that were produced by a <code>@Factory</code> annotated constructor or static
* method. For e.g., lets say your <code>@Factory</code> method returned 4 instances, then
* each of the invocations to this method would return a value from <code>0</code> to <code>3
* </code>
*/
int getInvocationIndex();

static Object embeddedInstance(Object original) {
if (original instanceof ITestClassInstance) {
return ((ITestClassInstance) original).getInstance();
}
return original;
}
}
9 changes: 9 additions & 0 deletions testng-core-api/src/main/java/org/testng/ITestNGMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
import org.testng.annotations.CustomAttribute;
Expand Down Expand Up @@ -269,6 +270,14 @@ default IParameterInfo getFactoryMethodParamsInfo() {
return null;
}

/**
* @return - A {@link IFactoryMethod} implementation that contains attributes associated with a
* factory method, wrapped within an {@link Optional}.
*/
default Optional<IFactoryMethod> getFactoryMethod() {
return Optional.empty();
}

/**
* @return - An array of {@link CustomAttribute} that represents the custom attributes associated
* with a test.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
package org.testng.internal;

/** Represents the ability to retrieve the parameters associated with a factory method. */
public interface IParameterInfo {
import org.testng.ITestClassInstance;
import org.testng.ITestNGMethod;

/** @return - The actual instance associated with a factory method */
Object getInstance();

/** @return - The actual index of instance associated with a factory method */
int getIndex();

/** @return - The parameters associated with the factory method as an array. */
/**
* Represents the ability to retrieve the parameters associated with a factory method.
*
* @deprecated - This interface stands deprecated as of TestNG <code>7.11.0</code>.
*/
@Deprecated
public interface IParameterInfo extends ITestClassInstance {
/**
* @return - The parameters associated with the factory method as an array.
* @deprecated - This method stands deprecated as of TestNG <code>7.11.0</code> Please use {@link
* ITestNGMethod#getFactoryMethod()} to retrieve the parameters.
*/
@Deprecated
Object[] getParameters();

static Object embeddedInstance(Object original) {
if (original instanceof IParameterInfo) {
return ((IParameterInfo) original).getInstance();
}
return original;
}
}
18 changes: 12 additions & 6 deletions testng-core/src/main/java/org/testng/DependencyMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ private static boolean hasInstance(
Object derivedInstance = derivedClassMethod.getInstance();
boolean result = derivedInstance != null || baseInstance != null;
boolean params =
null != baseClassMethod.getFactoryMethodParamsInfo()
&& null != derivedClassMethod.getFactoryMethodParamsInfo().getParameters();
baseClassMethod.getFactoryMethod().flatMap(IFactoryMethod::getParameters).isPresent();

if (result && params && RuntimeBehavior.enforceThreadAffinity()) {
return hasSameParameters(baseClassMethod, derivedClassMethod);
Expand All @@ -118,10 +117,17 @@ private static boolean hasInstance(

private static boolean hasSameParameters(
ITestNGMethod baseClassMethod, ITestNGMethod derivedClassMethod) {
return baseClassMethod
.getFactoryMethodParamsInfo()
.getParameters()[0]
.equals(derivedClassMethod.getFactoryMethodParamsInfo().getParameters()[0]);
Optional<IFactoryMethod> first = baseClassMethod.getFactoryMethod();
Optional<IFactoryMethod> second = derivedClassMethod.getFactoryMethod();
if (first.isPresent() && second.isPresent()) {
Optional<Object[]> firstParams = first.get().getParameters();
Optional<Object[]> secondParams = second.get().getParameters();
if (firstParams.isPresent() && secondParams.isPresent()) {
return firstParams.get()[0].equals(secondParams.get()[0]);
}
return false;
}
return false;
}

private static boolean isSameInstance(
Expand Down
4 changes: 2 additions & 2 deletions testng-core/src/main/java/org/testng/TestClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private void initTestClassesAndInstances() {
IObject.IdentifiableObject[] instances = getObjects(true, this.m_errorMsgPrefix);
Arrays.stream(instances)
.map(IdentifiableObject::getInstance)
.map(IParameterInfo::embeddedInstance)
.map(ITestClassInstance::embeddedInstance)
.filter(it -> it instanceof ITest)
.findFirst()
.ifPresent(it -> testName = ((ITest) it).getTestName());
Expand Down Expand Up @@ -208,7 +208,7 @@ private void initMethods() {
true,
xmlTest,
eachInstance);
Object instance = IParameterInfo.embeddedInstance(eachInstance.getInstance());
Object instance = ITestClassInstance.embeddedInstance(eachInstance.getInstance());
beforeClassConfig.put(instance, m_beforeClassMethods);
m_afterClassMethods =
ConfigurationMethod.createClassConfigurationMethods(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.testng.IClass;
import org.testng.IFactoryMethod;
import org.testng.IRetryAnalyzer;
import org.testng.ITestClass;
import org.testng.ITestClassInstance;
import org.testng.ITestNGMethod;
import org.testng.ITestObjectFactory;
import org.testng.ITestResult;
Expand Down Expand Up @@ -103,6 +105,10 @@ public BaseTestMethod(
m_instance = instance;
}

protected final IObject.IdentifiableObject identifiableObject() {
return m_instance;
}

/** {@inheritDoc} */
@Override
public boolean isAlwaysRun() {
Expand Down Expand Up @@ -153,7 +159,7 @@ public String getMethodName() {
public Object getInstance() {
return Optional.ofNullable(m_instance)
.map(IObject.IdentifiableObject::getInstance)
.map(IParameterInfo::embeddedInstance)
.map(ITestClassInstance::embeddedInstance)
.orElse(null);
}

Expand Down Expand Up @@ -299,6 +305,19 @@ public void setTimeOut(long timeOut) {
m_timeOut = timeOut;
}

@Override
public Optional<IFactoryMethod> getFactoryMethod() {
IObject.IdentifiableObject identifiable = identifiableObject();
if (identifiable == null) {
return Optional.empty();
}
Object instance = identifiableObject().getInstance();
if (instance instanceof ParameterInfo) {
return Optional.of(() -> Optional.of(((ParameterInfo) instance).getParameters()));
}
return ITestNGMethod.super.getFactoryMethod();
}

/**
* {@inheritDoc}
*
Expand Down Expand Up @@ -538,11 +557,10 @@ public String getSimpleName() {
}

private String instanceParameters() {
IParameterInfo instance = getFactoryMethodParamsInfo();
if (instance != null) {
return ", instance params:" + Arrays.toString(instance.getParameters());
}
return "";
return getFactoryMethod()
.flatMap(IFactoryMethod::getParameters)
.map(it -> ", instance params:" + Arrays.toString(it))
.orElse("");
}

protected String getSignature() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Map;
import org.testng.IClass;
import org.testng.ITest;
import org.testng.ITestClassInstance;
import org.testng.ITestContext;
import org.testng.ITestObjectFactory;
import org.testng.annotations.ITestAnnotation;
Expand Down Expand Up @@ -162,7 +163,7 @@ public void addInstance(Object instance) {
}

private static int computeHashCode(Object instance) {
return IParameterInfo.embeddedInstance(instance).hashCode();
return ITestClassInstance.embeddedInstance(instance).hashCode();
}

private DetailedAttributes newDetailedAttributes(boolean create, String errMsgPrefix) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.testng.DataProviderHolder;
import org.testng.IDataProviderInterceptor;
import org.testng.IDataProviderListener;
import org.testng.IInstanceInfo;
import org.testng.ITestClassInstance;
import org.testng.ITestContext;
import org.testng.ITestMethodFinder;
import org.testng.ITestNGListener;
Expand Down Expand Up @@ -144,8 +146,8 @@ private static String[] getAllGroups(
return groups.toArray(new String[0]);
}

public IParameterInfo[] invoke() {
List<IParameterInfo> result = Lists.newArrayList();
public ITestClassInstance[] invoke() {
List<ITestClassInstance> result = Lists.newArrayList();

Map<String, String> allParameterNames = Maps.newHashMap();
Parameters.MethodParameters methodParameters =
Expand Down Expand Up @@ -174,6 +176,7 @@ public IParameterInfo[] invoke() {
try {
List<Integer> indices = factoryAnnotation.getIndices();
int position = 0;
AtomicInteger invocationCounter = new AtomicInteger(0);
while (parameterIterator.hasNext()) {
Object[] parameters = parameterIterator.next();
if (parameters == null) {
Expand All @@ -196,21 +199,34 @@ public IParameterInfo[] invoke() {
final int instancePosition = position;
result.addAll(
Arrays.stream(testInstances)
.map(instance -> new ParameterInfo(instance, instancePosition, parameters))
.map(
instance ->
new ParameterInfo(
instance,
instancePosition,
parameters,
invocationCounter.getAndIncrement()))
.collect(Collectors.toList()));
} else {
for (Integer index : indices) {
int i = index - position;
if (i >= 0 && i < testInstances.length) {
result.add(new ParameterInfo(testInstances[i], position, parameters));
result.add(
new ParameterInfo(
testInstances[i],
position,
parameters,
invocationCounter.getAndIncrement()));
}
}
}
position += testInstances.length;
} else {
if (indices == null || indices.isEmpty() || indices.contains(position)) {
Object instance = m_objectFactory.newInstance(com.getConstructor(), parameters);
result.add(new ParameterInfo(instance, position, parameters));
result.add(
new ParameterInfo(
instance, position, parameters, invocationCounter.getAndIncrement()));
}
position++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import org.testng.IFactoryMethod;
import org.testng.ITestNGMethod;

public enum MethodSorting implements Comparator<ITestNGMethod> {
Expand All @@ -31,8 +32,10 @@ public int compare(ITestNGMethod o1, ITestNGMethod o2) {
.thenComparing(Object::toString)
.thenComparing(
method ->
Optional.ofNullable(method.getFactoryMethodParamsInfo())
.map(it -> Arrays.toString(it.getParameters()))
method
.getFactoryMethod()
.flatMap(IFactoryMethod::getParameters)
.map(Arrays::toString)
.orElse(""))
.thenComparing(this::objectEquality);
return comparator.compare(o1, o2);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package org.testng.internal;

public class ParameterInfo implements IParameterInfo {
private Object instance;
private final Object instance;
private final int index;
private Object[] parameters;
private final Object[] parameters;
private final int invocationIndex;

public ParameterInfo(Object instance, int index, Object[] parameters) {
public ParameterInfo(Object instance, int index, Object[] parameters, int invocationIndex) {
this.instance = instance;
this.index = index;
this.parameters = parameters;
this.invocationIndex = invocationIndex;
}

@Override
Expand All @@ -21,6 +23,11 @@ public int getIndex() {
return index;
}

@Override
public int getInvocationIndex() {
return invocationIndex;
}

@Override
public Object[] getParameters() {
return parameters;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.testng.DataProviderHolder;
import org.testng.IClass;
import org.testng.IInstanceInfo;
import org.testng.ITestClassInstance;
import org.testng.ITestContext;
import org.testng.ITestObjectFactory;
import org.testng.TestNGException;
Expand Down Expand Up @@ -178,7 +179,7 @@ private ClassInfoMap processFactory(IClass ic, ConstructorOrMethod factoryMethod
// If the factory returned IInstanceInfo, get the class from it,
// otherwise, just call getClass() on the returned instances
int i = 0;
for (IParameterInfo o : fm.invoke()) {
for (ITestClassInstance o : fm.invoke()) {
if (o == null) {
throw new TestNGException(
"The factory " + fm + " returned a null instance" + "at index " + i);
Expand Down Expand Up @@ -329,8 +330,8 @@ private <T> void addInstance(IInstanceInfo<T> ii) {

private void addInstance(IObject.IdentifiableObject o) {
Class<?> key = o.getInstance().getClass();
if (o.getInstance() instanceof IParameterInfo) {
key = ((IParameterInfo) o.getInstance()).getInstance().getClass();
if (o.getInstance() instanceof ITestClassInstance) {
key = ((ITestClassInstance) o.getInstance()).getInstance().getClass();
}
addInstance(key, o);
}
Expand Down
Loading
Loading