Skip to content

Commit

Permalink
First working solution with targetted events #178 (#191)
Browse files Browse the repository at this point in the history
* First working solution with targetted events #178
  • Loading branch information
baubakg authored Aug 30, 2024
1 parent ce4cb88 commit 764cd9a
Show file tree
Hide file tree
Showing 9 changed files with 373 additions and 25 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,26 @@ This works for both Shuffled and Single-Run tests. If we want to run all tests w

You can also add it as a property in your testng definition file.

#### Targeting an Event to a Specific Step
As of version 8.11.2, we can inject an event to a specific step of a Phased Scenario. This is done by:
* Declaring an event by setting the variable `PHASED.EVENTS.NONINTERRUPTIVE`.
* Identifying the step on which an event will occur. This is done by setting the variable `PHASED.EVENTS.TARGET`.

The step should point to a method. For method `step1` in the class `a.b.c.ScenarioA` you can set:
* `a.b.c.ScenarioA.step1`
* `ScenarioA#step1`
* `ScenarioA.step1`

In the case of nested tests, for method `step1` in the class `a.b.c.ScenarioA`, and sub-class `NestedClassB` you need to use the `$` notation. It will look like:
* `a.b.c.ScenarioA$NestedClassB.step1`
* `ScenarioA$NestedClassB#step1`
* `ScenarioA$NestedClassB.step1`

Here is an example of running a specific event for a specific test:

```mvn clean test -DPHASED.EVENTS.NONINTERRUPTIVE=com.adobe.campaign.tests.integro.phased.data.events.MyNonInterruptiveEvent -DPHASED.EVENTS.TARGET=ScenarioA$NestedClassB#step1 ```


### Before- and After-Phase Actions
We have introduced the possibility of defining Before and After Phases. This means that you can state if a method can be invoked before or after the phased tests are executed. These methods are only activated when we are in a Phase, and will not run when executed when we execute the scenarios in Non-Phased mode.

Expand Down Expand Up @@ -568,6 +588,10 @@ For now, we have not come around to deciding how retry should work in the case o

## Release Notes

### 8.11.2
* [#178 Allowing the injection in any step of a scenario](https://github.com/adobe/bridgeService/issues/178). We can now inject an event into a step in an arbitrary phased test. This is done by setting the syetm property PHASED.EVENTS.TARGET. This way you can inject the event into that step.


### 8.11.1
* Renaming ConfigValueHandler to ConfigValueHandlerPhased
* Migrating to Java 11
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public enum ConfigValueHandlerPhased {
PHASED_TEST_SOURCE_LOCATION("PHASED.TESTS.CODE.ROOT","/src/test/java", false),
PHASED_TEST_DETECT_ORDER("PHASED.TESTS.DETECT.ORDER", "false", false),
PHASED_TEST_NONPHASED_LEGACY( "PHASED.TESTS.NONPHASED.LEGACY", "false", false ),
PROP_SCENARIO_EXPORTED_PREFIX("PHASED.TESTS.STORAGE.SCENARIO.PREFIX", "[TC]", false);
PROP_SCENARIO_EXPORTED_PREFIX("PHASED.TESTS.STORAGE.SCENARIO.PREFIX", "[TC]", false),
EVENT_TARGET("PHASED.EVENTS.TARGET", null, false );

public final String systemName;
public final String defaultValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@
import org.testng.ITestResult;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

Expand All @@ -42,10 +39,43 @@ public class PhasedEventManager {
* @return The event that is declared on the method. Null if there is no event declared for the method
*/
public static String fetchApplicableEvent(Method in_method) {
if (in_method.isAnnotationPresent(PhaseEvent.class) && (in_method.getDeclaredAnnotation(PhaseEvent.class).eventClasses().length > 0)) {
if (!PhasedTestManager.isPhasedTest(in_method) ) {
return null;
}
/*
if (in_method.isAnnotationPresent(PhaseEvent.class) && (
in_method.getDeclaredAnnotation(PhaseEvent.class).eventClasses().length > 0)) {
return in_method.getDeclaredAnnotation(PhaseEvent.class).eventClasses()[0];
} else if (PhasedTestManager.isPhasedTest(in_method) && (in_method.getDeclaringClass().getDeclaredAnnotation(PhasedTest.class).eventClasses().length > 0)) {
}
*/
if (PhasedTestManager.isPhasedTestWithEvent(in_method.getDeclaringClass())) {
if (in_method.isAnnotationPresent(PhaseEvent.class)) {
//if the event is declared on the Event annotation it gets precedence
if (in_method.getDeclaredAnnotation(PhaseEvent.class).eventClasses().length > 0) {
return in_method.getDeclaredAnnotation(PhaseEvent.class).eventClasses()[0];
} else if (in_method.getDeclaringClass().getDeclaredAnnotation(PhasedTest.class).eventClasses().length
> 0) {
return in_method.getDeclaringClass().getDeclaredAnnotation(PhasedTest.class).eventClasses()[0];
} else {
return ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.fetchValue();
}
} else {
return null;
}
//Otherwise it is the event on the class
//otherwise it is the passed event
/*
return in_method.getDeclaredAnnotation(PhaseEvent.class).eventClasses().length > 0 ?
in_method.getDeclaredAnnotation(PhaseEvent.class).eventClasses()[0] :
ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.fetchValue();
*/
}
else if (in_method.getDeclaringClass().getDeclaredAnnotation(PhasedTest.class).eventClasses().length > 0) {
return in_method.getDeclaringClass().getDeclaredAnnotation(PhasedTest.class).eventClasses()[0];
} else if (ConfigValueHandlerPhased.EVENT_TARGET.isSet()) {
return PhasedTestManager.isPhasedTestTargetOfEvent(in_method) ? ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.fetchValue() : null;
} else if (ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.isSet()) {
return ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.fetchValue();
}
Expand Down Expand Up @@ -171,11 +201,15 @@ public static String fetchEvent(ITestResult in_testResult) {

if (PhasedTestManager.isPhasedTestSingleMode(l_currentMethod)) {
//Check if the current method is subject to event
if (l_currentMethod.isAnnotationPresent(PhaseEvent.class)) {
return fetchApplicableEvent(l_currentMethod);
/*
if (l_currentMethod.isAnnotationPresent(PhaseEvent.class)) {
return fetchApplicableEvent(l_currentMethod);
} else {
} else {
return null;
}
}
*/
} else {

//Use Phase Context instead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public void onTestStart(ITestResult result) {

//Check if there is an event declared
String lt_event = PhasedEventManager.fetchEvent(result);
if (PhasedEventManager.fetchEvent(result) != null) {
if (lt_event != null) {
//TODO use PhasedTestManager for fetching full name instead
PhasedEventManager.startEvent(lt_event, ClassPathParser.fetchFullName(result));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ private PhasedTestManager() {

public static final String STD_MERGE_STEP_ERROR_PREFIX = "Phased Error: Failure in step ";


/**
* The different states a step can assume in a scenario
* <p>
Expand Down Expand Up @@ -865,7 +864,7 @@ static boolean isPhasedTestSingleMode(Method in_method) {
static boolean isPhasedTestSingleMode(Class<?> in_class) {
//TODO in 8.11.3 to be removed - make public
//return isPhasedTest(in_class) && (isPhasedTestWithEvent(in_class)
return isPhasedTest(in_class) && (isPhasedTestWithEvent(in_class) || !in_class.getAnnotation(PhasedTest.class).canShuffle());
return isPhasedTest(in_class) && (isPhasedTestWithEvent(in_class) || !in_class.getAnnotation(PhasedTest.class).canShuffle()) || isPhasedTestTargetOfEvent(in_class);
}

/**
Expand Down Expand Up @@ -898,8 +897,8 @@ static boolean isPhasedTestShuffledMode(ITestResult in_testResult) {
* @return True if the given test scenario is a Shuffled Phased Test scenario
*/
static boolean isPhasedTestShuffledMode(Class<?> in_class) {
//return isPhasedTest(in_class) && in_class.getAnnotation(PhasedTest.class).canShuffle();
return isPhasedTest(in_class) && !isPhasedTestWithEvent(in_class) && in_class.getAnnotation(PhasedTest.class).canShuffle();
return isPhasedTest(in_class) && !isPhasedTestWithEvent(in_class) && in_class.getAnnotation(PhasedTest.class)
.canShuffle() && !isPhasedTestTargetOfEvent(in_class);
}

/**
Expand Down Expand Up @@ -1667,4 +1666,33 @@ public static int asynchronousExtractIndex(ITestResult in_testResult) {
return lr_index;
}

/**
* Lets us know if the given class is the target of an event
*
* @param in_class A class candidate for an event
* @return true if the property PHASED.EVENTS.NONINTERRUPTIVE.INJECTINTO points to a method in the class
*/
public static boolean isPhasedTestTargetOfEvent(Class in_class) {
if (!ConfigValueHandlerPhased.EVENT_TARGET.isSet()) {
return false;
}
return ClassPathParser.elementsCorrespond(in_class,
ConfigValueHandlerPhased.EVENT_TARGET.fetchValue());
}

/**
* Lets us know if the given method is the target of an event
*
* @param in_method A method candidate for an event
* @return true if the property PHASED.EVENTS.NONINTERRUPTIVE.INJECTINTO points to the method
*/
public static boolean isPhasedTestTargetOfEvent(Method in_method) {
if (!ConfigValueHandlerPhased.EVENT_TARGET.isSet()) {
return false;
}

return ClassPathParser.elementsCorrespond(in_method,
ConfigValueHandlerPhased.EVENT_TARGET.fetchValue());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,44 @@ public static File fetchClassFile(Class in_class) {
return (in_class.getDeclaringClass() == null) ? fetchClassFile(in_class.getTypeName()) : fetchClassFile(
in_class.getDeclaringClass().getTypeName());
}

/**
* Lets us know if the given class corresponds to the selected method/class
* @param in_class The current class to assess
* @param in_selectedElementName the selected element
* @return if the element corresponds to the class
*/
public static boolean elementsCorrespond(Class in_class, String in_selectedElementName) {
return in_class.getTypeName().endsWith(extractElements(in_selectedElementName)[0]);
}

/**
* Lets us know if the given method corresponds to the selected method/class
* @param in_method The current class to assess
* @param in_selectedElementName the selected element
* @return if the element corresponds to the class
*/
public static boolean elementsCorrespond(Method in_method, String in_selectedElementName) {
String[] l_elements = extractElements(in_selectedElementName);

return elementsCorrespond(in_method.getDeclaringClass(), in_selectedElementName) && in_method.getName().equals(l_elements[1]);
}

/**
* Given a string representing a class or a
* @param l_selectedMethodName
* @return
*/
public static String[] extractElements(String l_selectedMethodName) {
if (l_selectedMethodName.contains("#")) {
return l_selectedMethodName.split("#");
}

int lioDot = l_selectedMethodName.lastIndexOf('.');

if (lioDot == -1) {
throw new IllegalArgumentException("The selected method name is not valid, or is of a bad format. Please include a full reference to a step name");
}
return new String []{l_selectedMethodName.substring(0,lioDot),l_selectedMethodName.substring(lioDot+1)};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1282,17 +1282,77 @@ public void testIsInSingleMode_Shuffled_Negative3() throws NoSuchMethodException

}

@Test(enabled = false)
@Test(enabled = true)
public void testIsInSingleMode_NegativeAsynchronousMethod() throws NoSuchMethodException, SecurityException {
final Method l_myMethod = TestSINGLEWithEvent_eventAsExecProperty.class.getMethod("step3", String.class);

Phases.ASYNCHRONOUS.activate();
assertThat("We should be in Shuffled mode", !PhasedTestManager.isPhasedTestShuffledMode(l_myMethod));
assertThat("We should be in Shuffled mode", !PhasedTestManager.isPhasedTestSingleMode(l_myMethod));
assertThat("We should be in Shuffled mode", !PhasedTestManager.isPhasedTestSingleMode(l_myMethod));
assertThat("We should not be in Shuffled mode", !PhasedTestManager.isPhasedTestShuffledMode(l_myMethod));
assertThat("We should be in Shuffled mode", PhasedTestManager.isPhasedTestSingleMode(l_myMethod));
}


@Test
public void testIsShuffled_butIsSingleSinceAcynhcronousTargetted() throws NoSuchMethodException, SecurityException {
Class l_myClass = PhasedSeries_F_Shuffle.class;

assertThat("We should not be in Shuffled mode", PhasedTestManager.isPhasedTestShuffledMode(l_myClass));
assertThat("We should not be in Single mode", !PhasedTestManager.isPhasedTestSingleMode(l_myClass));

//Activate target event
ConfigValueHandlerPhased.EVENT_TARGET.activate(l_myClass.getTypeName()+".step1");
assertThat("We should not be in Shuffled mode", !PhasedTestManager.isPhasedTestShuffledMode(l_myClass));
assertThat("We should be in Single mode", PhasedTestManager.isPhasedTestSingleMode(l_myClass));


}

/////// Checking if element is the target of a phase event
@Test
public void testIsTargetOfEvent() throws NoSuchMethodException, SecurityException {
Class l_myClass = PhasedSeries_F_Shuffle.class;

assertThat("We should be the target of an event", !PhasedTestManager.isPhasedTestTargetOfEvent(l_myClass));

//Simple
ConfigValueHandlerPhased.EVENT_TARGET.activate(l_myClass.getTypeName()+".step1");
assertThat("We should be the target of an event", PhasedTestManager.isPhasedTestTargetOfEvent(l_myClass));

//Simple with #
ConfigValueHandlerPhased.EVENT_TARGET.activate(l_myClass.getSimpleName()+"#step1");
assertThat("We should be the target of an event", PhasedTestManager.isPhasedTestTargetOfEvent(l_myClass));

//Simple with .
ConfigValueHandlerPhased.EVENT_TARGET.activate(l_myClass.getSimpleName()+".step1");
assertThat("We should be the target of an event", PhasedTestManager.isPhasedTestTargetOfEvent(l_myClass));

}

@Test
public void testIsTargetOfEventMethodLevel() throws NoSuchMethodException, SecurityException {
Class l_myClass = PhasedSeries_F_Shuffle.class;

Method l_targetMethod = l_myClass.getMethod("step1", String.class);
Method l_nonTargetedMethod = l_myClass.getMethod("step2", String.class);


assertThat("We should be the target of an event", !PhasedTestManager.isPhasedTestTargetOfEvent(l_targetMethod));

//Simple
ConfigValueHandlerPhased.EVENT_TARGET.activate(l_myClass.getTypeName()+".step1");
assertThat("We should be the target of an event", PhasedTestManager.isPhasedTestTargetOfEvent(l_myClass));

//Simple with #
ConfigValueHandlerPhased.EVENT_TARGET.activate(l_myClass.getSimpleName()+"#step1");
assertThat("We should be the target of an event", PhasedTestManager.isPhasedTestTargetOfEvent(l_myClass));

//Simple with .
ConfigValueHandlerPhased.EVENT_TARGET.activate(l_myClass.getSimpleName()+".step1");
assertThat("We should be the target of an event", PhasedTestManager.isPhasedTestTargetOfEvent(l_myClass));

}


/****** Key Identity Methods *****/
@Test
public void testGenerateStepKeyIdentity() {
Expand Down
Loading

0 comments on commit 764cd9a

Please sign in to comment.