From 9b2e33902a14c1e63e982ac2a2d08317b1998ca4 Mon Sep 17 00:00:00 2001 From: Krishnan Mahadevan Date: Mon, 22 Apr 2024 18:34:50 +0530 Subject: [PATCH 1/3] Allow users to access factory method params info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #3111 TestNG now exposes the IParameterInfo interface Via which you can extract the following details Pertaining to a factory powered test. * the index - which would match with what was Specified in the “indices” attribute of the “Factory” Annotation. If nothing was specified, then this Would be equal to a running count on the total instances. * current index - which represents a running count On the total instances. * The parameters of the factory method * The instance that was produced. --- CHANGES.txt | 1 + .../main/java/org/testng/IParameterInfo.java | 36 +++++++++++++++ .../org/testng/internal/IParameterInfo.java | 26 +++-------- .../org/testng/internal/BaseTestMethod.java | 2 +- .../java/org/testng/internal/ClassImpl.java | 2 +- .../org/testng/internal/FactoryMethod.java | 18 +++++--- .../org/testng/internal/ParameterInfo.java | 13 ++++-- .../testng/internal/TestNGClassFinder.java | 6 +-- .../test/factory/FactoryIntegrationTest.java | 45 ++++++++++++++++++- .../SimpleFactoryPoweredTestSample.java | 26 +++++++++++ ...leFactoryPoweredTestWithIndicesSample.java | 28 ++++++++++++ ...yPoweredTestWithoutDataProviderSample.java | 28 ++++++++++++ ...tWithoutDataProviderWithIndicesSample.java | 28 ++++++++++++ .../java/org/testng/internal/TestResult.java | 2 +- 14 files changed, 226 insertions(+), 35 deletions(-) create mode 100644 testng-core-api/src/main/java/org/testng/IParameterInfo.java create mode 100644 testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestSample.java create mode 100644 testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithIndicesSample.java create mode 100644 testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithoutDataProviderSample.java create mode 100644 testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample.java diff --git a/CHANGES.txt b/CHANGES.txt index 9fed42440d..590c36fed9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -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) diff --git a/testng-core-api/src/main/java/org/testng/IParameterInfo.java b/testng-core-api/src/main/java/org/testng/IParameterInfo.java new file mode 100644 index 0000000000..7a29d240ae --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IParameterInfo.java @@ -0,0 +1,36 @@ +package org.testng; + +/** Represents the ability to retrieve the parameters associated with a factory method. */ +public interface IParameterInfo { + + /** @return - The actual instance associated with a factory method */ + Object 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 indices attribute of the + * @Factory 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 @Factory annotated constructor or static + * method. For e.g., lets say your @Factory method returned 4 instances, then + * each of the invocations to this method would return a value from 0 to 3 + * + */ + int currentIndex(); + + /** @return - The parameters associated with the factory method as an array. */ + Object[] getParameters(); + + static Object embeddedInstance(Object original) { + if (original instanceof IParameterInfo) { + return ((IParameterInfo) original).getInstance(); + } + return original; + } +} diff --git a/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java b/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java index 6ccf1b041c..3446078ec8 100644 --- a/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java +++ b/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java @@ -1,21 +1,9 @@ package org.testng.internal; -/** Represents the ability to retrieve the parameters associated with a factory method. */ -public interface IParameterInfo { - - /** @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. */ - Object[] getParameters(); - - static Object embeddedInstance(Object original) { - if (original instanceof IParameterInfo) { - return ((IParameterInfo) original).getInstance(); - } - return original; - } -} +/** + * Represents the ability to retrieve the parameters associated with a factory method. + * + * @deprecated - This interface stands deprecated as of TestNG 7.11.0. + */ +@Deprecated +public interface IParameterInfo extends org.testng.IParameterInfo {} 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 c0ad3379f9..fbee5f4c6c 100644 --- a/testng-core/src/main/java/org/testng/internal/BaseTestMethod.java +++ b/testng-core/src/main/java/org/testng/internal/BaseTestMethod.java @@ -153,7 +153,7 @@ public String getMethodName() { public Object getInstance() { return Optional.ofNullable(m_instance) .map(IObject.IdentifiableObject::getInstance) - .map(IParameterInfo::embeddedInstance) + .map(org.testng.IParameterInfo::embeddedInstance) .orElse(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 f893b60086..098e7f191d 100644 --- a/testng-core/src/main/java/org/testng/internal/ClassImpl.java +++ b/testng-core/src/main/java/org/testng/internal/ClassImpl.java @@ -162,7 +162,7 @@ public void addInstance(Object instance) { } private static int computeHashCode(Object instance) { - return IParameterInfo.embeddedInstance(instance).hashCode(); + return org.testng.IParameterInfo.embeddedInstance(instance).hashCode(); } private DetailedAttributes newDetailedAttributes(boolean create, String errMsgPrefix) { diff --git a/testng-core/src/main/java/org/testng/internal/FactoryMethod.java b/testng-core/src/main/java/org/testng/internal/FactoryMethod.java index c5bf4de1fe..901ad7cb7e 100644 --- a/testng-core/src/main/java/org/testng/internal/FactoryMethod.java +++ b/testng-core/src/main/java/org/testng/internal/FactoryMethod.java @@ -7,6 +7,7 @@ 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; @@ -144,8 +145,8 @@ private static String[] getAllGroups( return groups.toArray(new String[0]); } - public IParameterInfo[] invoke() { - List result = Lists.newArrayList(); + public org.testng.IParameterInfo[] invoke() { + List result = Lists.newArrayList(); Map allParameterNames = Maps.newHashMap(); Parameters.MethodParameters methodParameters = @@ -174,6 +175,7 @@ public IParameterInfo[] invoke() { try { List indices = factoryAnnotation.getIndices(); int position = 0; + AtomicInteger counter = new AtomicInteger(0); while (parameterIterator.hasNext()) { Object[] parameters = parameterIterator.next(); if (parameters == null) { @@ -196,13 +198,18 @@ 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, counter.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, counter.getAndIncrement())); } } } @@ -210,7 +217,8 @@ public IParameterInfo[] invoke() { } 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, counter.getAndIncrement())); } position++; } diff --git a/testng-core/src/main/java/org/testng/internal/ParameterInfo.java b/testng-core/src/main/java/org/testng/internal/ParameterInfo.java index 7b514d1da4..82e427caa4 100644 --- a/testng-core/src/main/java/org/testng/internal/ParameterInfo.java +++ b/testng-core/src/main/java/org/testng/internal/ParameterInfo.java @@ -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 currentIndex; - public ParameterInfo(Object instance, int index, Object[] parameters) { + public ParameterInfo(Object instance, int index, Object[] parameters, int currentIndex) { this.instance = instance; this.index = index; this.parameters = parameters; + this.currentIndex = currentIndex; } @Override @@ -21,6 +23,11 @@ public int getIndex() { return index; } + @Override + public int currentIndex() { + return currentIndex; + } + @Override public Object[] getParameters() { return parameters; diff --git a/testng-core/src/main/java/org/testng/internal/TestNGClassFinder.java b/testng-core/src/main/java/org/testng/internal/TestNGClassFinder.java index b8d00d8889..aa2d39efea 100644 --- a/testng-core/src/main/java/org/testng/internal/TestNGClassFinder.java +++ b/testng-core/src/main/java/org/testng/internal/TestNGClassFinder.java @@ -178,7 +178,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 (org.testng.IParameterInfo o : fm.invoke()) { if (o == null) { throw new TestNGException( "The factory " + fm + " returned a null instance" + "at index " + i); @@ -329,8 +329,8 @@ private void addInstance(IInstanceInfo 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 org.testng.IParameterInfo) { + key = ((org.testng.IParameterInfo) o.getInstance()).getInstance().getClass(); } addInstance(key, o); } diff --git a/testng-core/src/test/java/test/factory/FactoryIntegrationTest.java b/testng-core/src/test/java/test/factory/FactoryIntegrationTest.java index f18c8289d0..21476eb896 100644 --- a/testng-core/src/test/java/test/factory/FactoryIntegrationTest.java +++ b/testng-core/src/test/java/test/factory/FactoryIntegrationTest.java @@ -3,13 +3,24 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; import org.testng.Assert; +import org.testng.IParameterInfo; +import org.testng.ITestListener; +import org.testng.ITestResult; import org.testng.TestListenerAdapter; import org.testng.TestNG; import org.testng.TestNGException; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import test.InvokedMethodNameListener; import test.SimpleBaseTest; +import test.factory.issue3111.SimpleFactoryPoweredTestSample; +import test.factory.issue3111.SimpleFactoryPoweredTestWithIndicesSample; +import test.factory.issue3111.SimpleFactoryPoweredTestWithoutDataProviderSample; +import test.factory.issue3111.SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample; public class FactoryIntegrationTest extends SimpleBaseTest { @@ -22,7 +33,9 @@ public void testExceptionWithNonStaticFactoryMethod() { } catch (TestNGException e) { assertThat(e) .hasMessage( - "\nCan't invoke public java.lang.Object[] test.factory.GitHub876Sample.createInstances(): either make it static or add a no-args constructor to your class"); + "\nCan't invoke public java.lang.Object[] test.factory.GitHub876Sample" + + ".createInstances(): either make it static or add a no-args constructor to " + + "your class"); } } @@ -46,7 +59,8 @@ public void testExceptionWithBadFactoryMethodReturnType() { } catch (TestNGException e) { assertThat(e) .hasMessage( - "\ntest.factory.BadMethodReturnTypeFactory.createInstances MUST return [ java.lang.Object[] or org.testng.IInstanceInfo[] ] but returns java.lang.Object"); + "\ntest.factory.BadMethodReturnTypeFactory.createInstances MUST return [ java.lang" + + ".Object[] or org.testng.IInstanceInfo[] ] but returns java.lang.Object"); } } @@ -65,4 +79,31 @@ public void doubleFactoryMethodShouldWork() { "FactoryBaseSample{1}#f", "FactoryBaseSample{2}#f", "FactoryBaseSample{3}#f", "FactoryBaseSample{4}#f"); } + + @Test(dataProvider = "testdata", description = "GITHUB-3111") + public void ensureCurrentIndexWorksForFactoryPoweredTests(Class klass, Integer[] expected) { + List params = new ArrayList<>(); + TestNG testng = create(klass); + testng.addListener( + new ITestListener() { + @Override + public void onTestSuccess(ITestResult result) { + params.add(result.getMethod().getFactoryMethodParamsInfo()); + } + }); + testng.run(); + List actualIndices = + params.stream().map(IParameterInfo::currentIndex).sorted().collect(Collectors.toList()); + assertThat(actualIndices).containsExactly(expected); + } + + @DataProvider(name = "testdata") + public Object[][] testdata() { + return new Object[][] { + {SimpleFactoryPoweredTestSample.class, new Integer[] {0, 1, 2}}, + {SimpleFactoryPoweredTestWithIndicesSample.class, new Integer[] {0}}, + {SimpleFactoryPoweredTestWithoutDataProviderSample.class, new Integer[] {0, 1, 2}}, + {SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample.class, new Integer[] {0}}, + }; + } } diff --git a/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestSample.java b/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestSample.java new file mode 100644 index 0000000000..a85ba3e2b0 --- /dev/null +++ b/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestSample.java @@ -0,0 +1,26 @@ +package test.factory.issue3111; + +import org.testng.Reporter; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +public class SimpleFactoryPoweredTestSample { + + private final int i; + + @Factory(dataProvider = "data") + public SimpleFactoryPoweredTestSample(int i) { + this.i = i; + } + + @Test + public void test() { + Reporter.log(Integer.toString(i)); + } + + @DataProvider + public static Object[][] data() { + return new Object[][] {{1}, {2}, {3}}; + } +} diff --git a/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithIndicesSample.java b/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithIndicesSample.java new file mode 100644 index 0000000000..3a745ac1bc --- /dev/null +++ b/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithIndicesSample.java @@ -0,0 +1,28 @@ +package test.factory.issue3111; + +import org.testng.Reporter; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +public class SimpleFactoryPoweredTestWithIndicesSample { + + private final int i; + + @Factory( + dataProvider = "data", + indices = {1}) + public SimpleFactoryPoweredTestWithIndicesSample(int i) { + this.i = i; + } + + @Test + public void test() { + Reporter.log(Integer.toString(i)); + } + + @DataProvider + public static Object[][] data() { + return new Object[][] {{1}, {2}, {3}}; + } +} diff --git a/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithoutDataProviderSample.java b/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithoutDataProviderSample.java new file mode 100644 index 0000000000..fb8bb9dac3 --- /dev/null +++ b/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithoutDataProviderSample.java @@ -0,0 +1,28 @@ +package test.factory.issue3111; + +import org.testng.Reporter; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +public class SimpleFactoryPoweredTestWithoutDataProviderSample { + + private final int i; + + public SimpleFactoryPoweredTestWithoutDataProviderSample(int i) { + this.i = i; + } + + @Test + public void test() { + Reporter.log(Integer.toString(i)); + } + + @Factory + public static Object[] data() { + return new Object[] { + new SimpleFactoryPoweredTestWithoutDataProviderSample(1), + new SimpleFactoryPoweredTestWithoutDataProviderSample(2), + new SimpleFactoryPoweredTestWithoutDataProviderSample(3), + }; + } +} diff --git a/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample.java b/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample.java new file mode 100644 index 0000000000..be564b3022 --- /dev/null +++ b/testng-core/src/test/java/test/factory/issue3111/SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample.java @@ -0,0 +1,28 @@ +package test.factory.issue3111; + +import org.testng.Reporter; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +public class SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample { + + private final int i; + + public SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample(int i) { + this.i = i; + } + + @Test + public void test() { + Reporter.log(Integer.toString(i)); + } + + @Factory(indices = {1}) + public static Object[] data() { + return new Object[] { + new SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample(1), + new SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample(2), + new SimpleFactoryPoweredTestWithoutDataProviderWithIndicesSample(3), + }; + } +} diff --git a/testng-runner-api/src/main/java/org/testng/internal/TestResult.java b/testng-runner-api/src/main/java/org/testng/internal/TestResult.java index e48352d8c0..2e730047ce 100644 --- a/testng-runner-api/src/main/java/org/testng/internal/TestResult.java +++ b/testng-runner-api/src/main/java/org/testng/internal/TestResult.java @@ -298,7 +298,7 @@ public void setParameters(Object[] parameters) { @Override public Object getInstance() { - return IParameterInfo.embeddedInstance(this.m_method.getInstance()); + return org.testng.IParameterInfo.embeddedInstance(this.m_method.getInstance()); } @Override From ac7ab962fad660ea4060bd6cc5147802963a2e6e Mon Sep 17 00:00:00 2001 From: Krishnan Mahadevan Date: Wed, 24 Apr 2024 10:04:14 +0530 Subject: [PATCH 2/3] Fixing review comments --- ...meterInfo.java => ITestClassInstance.java} | 8 ++++---- .../org/testng/internal/IParameterInfo.java | 4 +++- .../src/main/java/org/testng/TestClass.java | 4 ++-- .../org/testng/internal/BaseTestMethod.java | 3 ++- .../java/org/testng/internal/ClassImpl.java | 3 ++- .../org/testng/internal/FactoryMethod.java | 20 +++++++++++++------ .../org/testng/internal/ParameterInfo.java | 10 +++++----- .../testng/internal/TestNGClassFinder.java | 7 ++++--- .../test/factory/FactoryIntegrationTest.java | 9 ++++++--- .../java/org/testng/internal/TestResult.java | 3 ++- 10 files changed, 44 insertions(+), 27 deletions(-) rename testng-core-api/src/main/java/org/testng/{IParameterInfo.java => ITestClassInstance.java} (87%) diff --git a/testng-core-api/src/main/java/org/testng/IParameterInfo.java b/testng-core-api/src/main/java/org/testng/ITestClassInstance.java similarity index 87% rename from testng-core-api/src/main/java/org/testng/IParameterInfo.java rename to testng-core-api/src/main/java/org/testng/ITestClassInstance.java index 7a29d240ae..9972ee3e7a 100644 --- a/testng-core-api/src/main/java/org/testng/IParameterInfo.java +++ b/testng-core-api/src/main/java/org/testng/ITestClassInstance.java @@ -1,7 +1,7 @@ package org.testng; /** Represents the ability to retrieve the parameters associated with a factory method. */ -public interface IParameterInfo { +public interface ITestClassInstance { /** @return - The actual instance associated with a factory method */ Object getInstance(); @@ -22,14 +22,14 @@ public interface IParameterInfo { * each of the invocations to this method would return a value from 0 to 3 * */ - int currentIndex(); + int getInvocationIndex(); /** @return - The parameters associated with the factory method as an array. */ Object[] getParameters(); static Object embeddedInstance(Object original) { - if (original instanceof IParameterInfo) { - return ((IParameterInfo) original).getInstance(); + if (original instanceof ITestClassInstance) { + return ((ITestClassInstance) original).getInstance(); } return original; } diff --git a/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java b/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java index 3446078ec8..0a6f49c418 100644 --- a/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java +++ b/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java @@ -1,9 +1,11 @@ package org.testng.internal; +import org.testng.ITestClassInstance; + /** * Represents the ability to retrieve the parameters associated with a factory method. * * @deprecated - This interface stands deprecated as of TestNG 7.11.0. */ @Deprecated -public interface IParameterInfo extends org.testng.IParameterInfo {} +public interface IParameterInfo extends ITestClassInstance {} diff --git a/testng-core/src/main/java/org/testng/TestClass.java b/testng-core/src/main/java/org/testng/TestClass.java index 7e9dea897d..518121c49c 100644 --- a/testng-core/src/main/java/org/testng/TestClass.java +++ b/testng-core/src/main/java/org/testng/TestClass.java @@ -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()); @@ -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( 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 fbee5f4c6c..6a342c8e72 100644 --- a/testng-core/src/main/java/org/testng/internal/BaseTestMethod.java +++ b/testng-core/src/main/java/org/testng/internal/BaseTestMethod.java @@ -19,6 +19,7 @@ import org.testng.IClass; import org.testng.IRetryAnalyzer; import org.testng.ITestClass; +import org.testng.ITestClassInstance; import org.testng.ITestNGMethod; import org.testng.ITestObjectFactory; import org.testng.ITestResult; @@ -153,7 +154,7 @@ public String getMethodName() { public Object getInstance() { return Optional.ofNullable(m_instance) .map(IObject.IdentifiableObject::getInstance) - .map(org.testng.IParameterInfo::embeddedInstance) + .map(ITestClassInstance::embeddedInstance) .orElse(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 098e7f191d..2cbc2add19 100644 --- a/testng-core/src/main/java/org/testng/internal/ClassImpl.java +++ b/testng-core/src/main/java/org/testng/internal/ClassImpl.java @@ -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; @@ -162,7 +163,7 @@ public void addInstance(Object instance) { } private static int computeHashCode(Object instance) { - return org.testng.IParameterInfo.embeddedInstance(instance).hashCode(); + return ITestClassInstance.embeddedInstance(instance).hashCode(); } private DetailedAttributes newDetailedAttributes(boolean create, String errMsgPrefix) { diff --git a/testng-core/src/main/java/org/testng/internal/FactoryMethod.java b/testng-core/src/main/java/org/testng/internal/FactoryMethod.java index 901ad7cb7e..97c851d388 100644 --- a/testng-core/src/main/java/org/testng/internal/FactoryMethod.java +++ b/testng-core/src/main/java/org/testng/internal/FactoryMethod.java @@ -13,6 +13,7 @@ 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; @@ -145,8 +146,8 @@ private static String[] getAllGroups( return groups.toArray(new String[0]); } - public org.testng.IParameterInfo[] invoke() { - List result = Lists.newArrayList(); + public ITestClassInstance[] invoke() { + List result = Lists.newArrayList(); Map allParameterNames = Maps.newHashMap(); Parameters.MethodParameters methodParameters = @@ -175,7 +176,7 @@ public org.testng.IParameterInfo[] invoke() { try { List indices = factoryAnnotation.getIndices(); int position = 0; - AtomicInteger counter = new AtomicInteger(0); + AtomicInteger invocationCounter = new AtomicInteger(0); while (parameterIterator.hasNext()) { Object[] parameters = parameterIterator.next(); if (parameters == null) { @@ -201,7 +202,10 @@ public org.testng.IParameterInfo[] invoke() { .map( instance -> new ParameterInfo( - instance, instancePosition, parameters, counter.getAndIncrement())) + instance, + instancePosition, + parameters, + invocationCounter.getAndIncrement())) .collect(Collectors.toList())); } else { for (Integer index : indices) { @@ -209,7 +213,10 @@ public org.testng.IParameterInfo[] invoke() { if (i >= 0 && i < testInstances.length) { result.add( new ParameterInfo( - testInstances[i], position, parameters, counter.getAndIncrement())); + testInstances[i], + position, + parameters, + invocationCounter.getAndIncrement())); } } } @@ -218,7 +225,8 @@ public org.testng.IParameterInfo[] invoke() { if (indices == null || indices.isEmpty() || indices.contains(position)) { Object instance = m_objectFactory.newInstance(com.getConstructor(), parameters); result.add( - new ParameterInfo(instance, position, parameters, counter.getAndIncrement())); + new ParameterInfo( + instance, position, parameters, invocationCounter.getAndIncrement())); } position++; } diff --git a/testng-core/src/main/java/org/testng/internal/ParameterInfo.java b/testng-core/src/main/java/org/testng/internal/ParameterInfo.java index 82e427caa4..9a10491dea 100644 --- a/testng-core/src/main/java/org/testng/internal/ParameterInfo.java +++ b/testng-core/src/main/java/org/testng/internal/ParameterInfo.java @@ -4,13 +4,13 @@ public class ParameterInfo implements IParameterInfo { private final Object instance; private final int index; private final Object[] parameters; - private final int currentIndex; + private final int invocationIndex; - public ParameterInfo(Object instance, int index, Object[] parameters, int currentIndex) { + public ParameterInfo(Object instance, int index, Object[] parameters, int invocationIndex) { this.instance = instance; this.index = index; this.parameters = parameters; - this.currentIndex = currentIndex; + this.invocationIndex = invocationIndex; } @Override @@ -24,8 +24,8 @@ public int getIndex() { } @Override - public int currentIndex() { - return currentIndex; + public int getInvocationIndex() { + return invocationIndex; } @Override diff --git a/testng-core/src/main/java/org/testng/internal/TestNGClassFinder.java b/testng-core/src/main/java/org/testng/internal/TestNGClassFinder.java index aa2d39efea..e8b9dfb09d 100644 --- a/testng-core/src/main/java/org/testng/internal/TestNGClassFinder.java +++ b/testng-core/src/main/java/org/testng/internal/TestNGClassFinder.java @@ -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; @@ -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 (org.testng.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); @@ -329,8 +330,8 @@ private void addInstance(IInstanceInfo ii) { private void addInstance(IObject.IdentifiableObject o) { Class key = o.getInstance().getClass(); - if (o.getInstance() instanceof org.testng.IParameterInfo) { - key = ((org.testng.IParameterInfo) o.getInstance()).getInstance().getClass(); + if (o.getInstance() instanceof ITestClassInstance) { + key = ((ITestClassInstance) o.getInstance()).getInstance().getClass(); } addInstance(key, o); } diff --git a/testng-core/src/test/java/test/factory/FactoryIntegrationTest.java b/testng-core/src/test/java/test/factory/FactoryIntegrationTest.java index 21476eb896..76ff2ed763 100644 --- a/testng-core/src/test/java/test/factory/FactoryIntegrationTest.java +++ b/testng-core/src/test/java/test/factory/FactoryIntegrationTest.java @@ -7,7 +7,7 @@ import java.util.List; import java.util.stream.Collectors; import org.testng.Assert; -import org.testng.IParameterInfo; +import org.testng.ITestClassInstance; import org.testng.ITestListener; import org.testng.ITestResult; import org.testng.TestListenerAdapter; @@ -82,7 +82,7 @@ public void doubleFactoryMethodShouldWork() { @Test(dataProvider = "testdata", description = "GITHUB-3111") public void ensureCurrentIndexWorksForFactoryPoweredTests(Class klass, Integer[] expected) { - List params = new ArrayList<>(); + List params = new ArrayList<>(); TestNG testng = create(klass); testng.addListener( new ITestListener() { @@ -93,7 +93,10 @@ public void onTestSuccess(ITestResult result) { }); testng.run(); List actualIndices = - params.stream().map(IParameterInfo::currentIndex).sorted().collect(Collectors.toList()); + params.stream() + .map(ITestClassInstance::getInvocationIndex) + .sorted() + .collect(Collectors.toList()); assertThat(actualIndices).containsExactly(expected); } diff --git a/testng-runner-api/src/main/java/org/testng/internal/TestResult.java b/testng-runner-api/src/main/java/org/testng/internal/TestResult.java index 2e730047ce..89b5020aac 100644 --- a/testng-runner-api/src/main/java/org/testng/internal/TestResult.java +++ b/testng-runner-api/src/main/java/org/testng/internal/TestResult.java @@ -13,6 +13,7 @@ import org.testng.IAttributes; import org.testng.IClass; import org.testng.ITest; +import org.testng.ITestClassInstance; import org.testng.ITestContext; import org.testng.ITestNGMethod; import org.testng.ITestResult; @@ -298,7 +299,7 @@ public void setParameters(Object[] parameters) { @Override public Object getInstance() { - return org.testng.IParameterInfo.embeddedInstance(this.m_method.getInstance()); + return ITestClassInstance.embeddedInstance(this.m_method.getInstance()); } @Override From f11c4fc56158ca0807b6c4278fc69d36b676c962 Mon Sep 17 00:00:00 2001 From: Krishnan Mahadevan Date: Sat, 27 Apr 2024 11:38:54 +0530 Subject: [PATCH 3/3] Fixing review comments --- .../main/java/org/testng/IFactoryMethod.java | 12 +++++++++ .../java/org/testng/ITestClassInstance.java | 7 ++--- .../main/java/org/testng/ITestNGMethod.java | 9 +++++++ .../org/testng/internal/IParameterInfo.java | 11 +++++++- .../main/java/org/testng/DependencyMap.java | 18 ++++++++----- .../org/testng/internal/BaseTestMethod.java | 27 +++++++++++++++---- .../org/testng/internal/MethodSorting.java | 7 +++-- .../issue2426/MyMethodListener.java | 7 ++++- .../java/org/testng/internal/TestResult.java | 10 +++---- 9 files changed, 83 insertions(+), 25 deletions(-) create mode 100644 testng-core-api/src/main/java/org/testng/IFactoryMethod.java diff --git a/testng-core-api/src/main/java/org/testng/IFactoryMethod.java b/testng-core-api/src/main/java/org/testng/IFactoryMethod.java new file mode 100644 index 0000000000..6b11bef48c --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IFactoryMethod.java @@ -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 getParameters(); +} diff --git a/testng-core-api/src/main/java/org/testng/ITestClassInstance.java b/testng-core-api/src/main/java/org/testng/ITestClassInstance.java index 9972ee3e7a..509b609599 100644 --- a/testng-core-api/src/main/java/org/testng/ITestClassInstance.java +++ b/testng-core-api/src/main/java/org/testng/ITestClassInstance.java @@ -1,10 +1,10 @@ package org.testng; /** Represents the ability to retrieve the parameters associated with a factory method. */ -public interface ITestClassInstance { +public interface ITestClassInstance { /** @return - The actual instance associated with a factory method */ - Object getInstance(); + T getInstance(); /** * @return - The actual index of instance associated with a factory method. This index has a 1:1 @@ -24,9 +24,6 @@ public interface ITestClassInstance { */ int getInvocationIndex(); - /** @return - The parameters associated with the factory method as an array. */ - Object[] getParameters(); - static Object embeddedInstance(Object original) { if (original instanceof ITestClassInstance) { return ((ITestClassInstance) original).getInstance(); 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 bff0385de1..add31af349 100644 --- a/testng-core-api/src/main/java/org/testng/ITestNGMethod.java +++ b/testng-core-api/src/main/java/org/testng/ITestNGMethod.java @@ -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; @@ -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 getFactoryMethod() { + return Optional.empty(); + } + /** * @return - An array of {@link CustomAttribute} that represents the custom attributes associated * with a test. diff --git a/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java b/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java index 0a6f49c418..99ed97cf5f 100644 --- a/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java +++ b/testng-core-api/src/main/java/org/testng/internal/IParameterInfo.java @@ -1,6 +1,7 @@ package org.testng.internal; import org.testng.ITestClassInstance; +import org.testng.ITestNGMethod; /** * Represents the ability to retrieve the parameters associated with a factory method. @@ -8,4 +9,12 @@ * @deprecated - This interface stands deprecated as of TestNG 7.11.0. */ @Deprecated -public interface IParameterInfo extends ITestClassInstance {} +public interface IParameterInfo extends ITestClassInstance { + /** + * @return - The parameters associated with the factory method as an array. + * @deprecated - This method stands deprecated as of TestNG 7.11.0 Please use {@link + * ITestNGMethod#getFactoryMethod()} to retrieve the parameters. + */ + @Deprecated + Object[] getParameters(); +} diff --git a/testng-core/src/main/java/org/testng/DependencyMap.java b/testng-core/src/main/java/org/testng/DependencyMap.java index 7d834ff7d2..d998f73d54 100644 --- a/testng-core/src/main/java/org/testng/DependencyMap.java +++ b/testng-core/src/main/java/org/testng/DependencyMap.java @@ -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); @@ -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 first = baseClassMethod.getFactoryMethod(); + Optional second = derivedClassMethod.getFactoryMethod(); + if (first.isPresent() && second.isPresent()) { + Optional firstParams = first.get().getParameters(); + Optional 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( 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 6a342c8e72..28765c8350 100644 --- a/testng-core/src/main/java/org/testng/internal/BaseTestMethod.java +++ b/testng-core/src/main/java/org/testng/internal/BaseTestMethod.java @@ -17,6 +17,7 @@ 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; @@ -104,6 +105,10 @@ public BaseTestMethod( m_instance = instance; } + protected final IObject.IdentifiableObject identifiableObject() { + return m_instance; + } + /** {@inheritDoc} */ @Override public boolean isAlwaysRun() { @@ -300,6 +305,19 @@ public void setTimeOut(long timeOut) { m_timeOut = timeOut; } + @Override + public Optional 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} * @@ -539,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() { diff --git a/testng-core/src/main/java/org/testng/internal/MethodSorting.java b/testng-core/src/main/java/org/testng/internal/MethodSorting.java index c2e455922e..b7e6e2fe13 100644 --- a/testng-core/src/main/java/org/testng/internal/MethodSorting.java +++ b/testng-core/src/main/java/org/testng/internal/MethodSorting.java @@ -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 { @@ -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); diff --git a/testng-core/src/test/java/test/configuration/issue2426/MyMethodListener.java b/testng-core/src/test/java/test/configuration/issue2426/MyMethodListener.java index 8d32e31518..a123f8405c 100644 --- a/testng-core/src/test/java/test/configuration/issue2426/MyMethodListener.java +++ b/testng-core/src/test/java/test/configuration/issue2426/MyMethodListener.java @@ -3,6 +3,7 @@ import java.util.HashMap; import java.util.Map; import org.testng.IConfigurationListener; +import org.testng.IFactoryMethod; import org.testng.ITestResult; import org.testng.annotations.*; @@ -12,7 +13,11 @@ public class MyMethodListener implements IConfigurationListener { @Override public void onConfigurationSuccess(ITestResult tr) { - Object[] values = tr.getMethod().getFactoryMethodParamsInfo().getParameters(); + Object[] values = + tr.getMethod() + .getFactoryMethod() + .flatMap(IFactoryMethod::getParameters) + .orElse(new Object[0]); if (tr.getMethod().isBeforeSuiteConfiguration()) { contents.put(BeforeSuite.class, values); } diff --git a/testng-runner-api/src/main/java/org/testng/internal/TestResult.java b/testng-runner-api/src/main/java/org/testng/internal/TestResult.java index 89b5020aac..0817dd11eb 100644 --- a/testng-runner-api/src/main/java/org/testng/internal/TestResult.java +++ b/testng-runner-api/src/main/java/org/testng/internal/TestResult.java @@ -12,6 +12,7 @@ import javax.annotation.Nonnull; import org.testng.IAttributes; import org.testng.IClass; +import org.testng.IFactoryMethod; import org.testng.ITest; import org.testng.ITestClassInstance; import org.testng.ITestContext; @@ -304,11 +305,10 @@ public Object getInstance() { @Override public Object[] getFactoryParameters() { - IParameterInfo instance = this.m_method.getFactoryMethodParamsInfo(); - if (instance != null) { - return instance.getParameters(); - } - return new Object[0]; + return this.m_method + .getFactoryMethod() + .flatMap(IFactoryMethod::getParameters) + .orElse(new Object[0]); } @Override