From 1c71fa1131d28121655f9f96cfd0fee1403d1e4b Mon Sep 17 00:00:00 2001 From: Baubak Gandomi Date: Wed, 4 Dec 2024 11:30:10 +0100 Subject: [PATCH] Integrate new chaos (#221) * Adapting new order to #197 * Solved issue with NIE event failure * Solved issue with failing test * Allowing for synchronous targetted execution in mutational * Fixed #223: We now also run Mutational tests with single run * Adding documentation on events --- README.md | 31 +- diagrams/Murational-eventWrappings.png | Bin 0 -> 93323 bytes diagrams/PhasedDiagrams.drawio | 62 ++-- .../tests/integro/phased/MethodMapping.java | 8 +- .../integro/phased/MutationListener.java | 23 +- .../tests/integro/phased/MutationManager.java | 39 ++- .../integro/phased/NonInterruptiveEvent.java | 21 +- .../integro/phased/PhasedDataProvider.java | 14 +- .../integro/phased/PhasedEventManager.java | 67 +++- .../integro/phased/PhasedTestManager.java | 27 +- .../PhasedTestingEventException.java | 4 + .../integro/phased/MutationManagerTests.java | 107 ++++++- .../tests/integro/phased/MutationalTests.java | 250 ++++++++++++++- .../phased/PhasedTestManagerTests.java | 4 +- .../tests/integro/phased/TestPhased.java | 5 - .../phased/TestPhasedNonInterruptive.java | 294 ++++++++++++++++++ .../events/NIEMutationalSynchronousEvent.java | 64 ++++ ...tationalSynchronousEventWithException.java | 77 +++++ .../data/events/NIESynchronousEvent.java | 63 ++++ .../NIESynchronousEventWithException.java | 76 +++++ .../data/events/TestNIE_Synchroneous.java | 51 +++ .../TestMutationalNIE_Synchroneous.java | 51 +++ .../data/ie/MutationalTestSingleRun.java | 30 ++ 23 files changed, 1266 insertions(+), 102 deletions(-) create mode 100644 diagrams/Murational-eventWrappings.png create mode 100644 src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIEMutationalSynchronousEvent.java create mode 100644 src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIEMutationalSynchronousEventWithException.java create mode 100644 src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIESynchronousEvent.java create mode 100644 src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIESynchronousEventWithException.java create mode 100644 src/test/java/com/adobe/campaign/tests/integro/phased/data/events/TestNIE_Synchroneous.java create mode 100644 src/test/java/com/adobe/campaign/tests/integro/phased/data/mutational/TestMutationalNIE_Synchroneous.java create mode 100644 src/test/java/com/adobe/campaign/tests/integro/phased/mutational/data/ie/MutationalTestSingleRun.java diff --git a/README.md b/README.md index a743300..679db7f 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,13 @@ The mutational test methods help solve problems such as: * [Installation](#installation) * [Maven](#maven) * [Demo](#demo) - * [Test Execution Modes](#test-execution-modes) - * [Default Mode](#default-mode) - * [Single Execution Mode](#single-execution-mode) - * [Shuffled Execution Mode](#shuffled-execution-mode) + * [Wrapping a Secnario around an Event](#wrapping-a-secnario-around-an-event) + * [Default Mode](#default-mode) + * [Single Execution Mode](#single-execution-mode) + * [Shuffled Execution Mode](#shuffled-execution-mode) * [Shuffled - Interruptive](#shuffled---interruptive) * [Shuffled - Non-Interruptive](#shuffled---non-interruptive) + * [Event Management and Execution](#event-management-and-execution) * [Writing a Phased Test](#writing-a-phased-test) * [Setting Execution Modes](#setting-execution-modes) * [Shuffled Mode](#shuffled-mode) @@ -50,7 +51,8 @@ The mutational test methods help solve problems such as: * [NON-INTERRUPTIVE execution mode](#non-interruptive-execution-mode) * [PERMUATIONAL Execution Mode](#permuational-execution-mode) * [Run Time Properties](#run-time-properties) - * [PHASED.TESTS.PHASE](#phasedtestsphase) + * [MUTATIONAL.EXECUTION.MODE](#mutationalexecutionmode) + * [PHASED.TESTS.PHASE (DEPRECATED)](#phasedtestsphase--deprecated-) * [PHASED.EVENTS.NONINTERRUPTIVE](#phasedeventsnoninterruptive) * [PHASED.TESTS.DATABROKER](#phasedtestsdatabroker) * [PHASED.TESTS.STORAGE.PATH](#phasedtestsstoragepath) @@ -262,6 +264,18 @@ Example: ![Asynchronous Execution Mode](diagrams/PhasedDiagrams-Parallel-Non-Interruptive-Event.drawio.png) +## Event Management and Execution +Events are an important topic, and have to be correctly covered. An event in Mutational Testing contains three parts: +* StartUp - the event is initiated. +* waitTillFinished - the event has finished executing +* tearDown - the system is set to a stable state + +These parts of an event allow us to pilot the event injection around the scenario. + +For now we identify two different event wrappings: +![Event Wrappings](diagrams/Murational-eventWrappings.png) + +There are other wrappings, and we will eventually publish them at a later time. ## Writing a Phased Test The Phased Testing is activated using two annotations: @@ -544,6 +558,11 @@ This execution mode is a good way of performing chaos testing. This mode is activated by setting the environment variable "MUTATIONAL.EXECUTION.MODE" to "NON-INTERRUPTIVE". +The event can be piloted with the following behaviors: + + + + #### PERMUATIONAL Execution Mode We have now introduced the PERMUATIONAL execution mode. This execution mode executes a scenario with all possible permutations it can have. This is done by identifying the dependencies between each step, and creating the possible orders of that scenario. @@ -740,9 +759,11 @@ For now, we have not come around to deciding how retry should work in the case o ### 9.0.0 - In-Progress * **(new feature)** [#204 Introduction of the Execution Mode replacing Phases](https://github.com/adobe/phased-testing/issues/204). We have revised the way we execute scenarios, as we no longer only cater to Upgrade tests. The means you should revise the way you execute Phased Tests by using Execution Modes. For more information please refer to the chapter [Execution Modes](#execution-modes). * **(new feature)** [#35 Adding the Permutation Execution Mode](https://github.com/adobe/phased-testing/issues/35). We have introduced the Permutation Execution Mode. This mode executes a scenario with all possible permutations it can have. This is done by identifying the dependencies between each step, and creating the possible orders of that scenario. For more information please refer to the chapters [Permutation Execution Mode](#permutation-execution-mode). +* **(new feature)** [#197 Adding the event wrappings to a step](https://github.com/adobe/phased-testing/issues/197). We have now introduced the different wrappings an event can have around a test step. Wrappings can be set in different ways: Execution Mode, Event annotation (in progress), The Phased Test Annotation. * **New Environment Variables** * MUTATIONAL.EXECUTION.MODE : This property is used to set the execution mode of the Mutational Tests. The value can be one of the following: STANDARD, INTERRUPTIVE(PRODUCER), INTERRUPTIVE(CONSUMER), NON-INTERRUPTIVE, PERMUATIONAL. This will replace the PHASED.TESTS.PHASE property which will be removed in 9.X.3. +* MUTATIONAL.EVENTS.TARGET : This property allows us to run a single event on a specific step of a scenario. The notation is either the standard method reference, or that of Surefire. ### 8.11.2 * **(new feature)** [#178 Allowing the injection in any step of a scenario](https://github.com/adobe/phased-testing/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. diff --git a/diagrams/Murational-eventWrappings.png b/diagrams/Murational-eventWrappings.png new file mode 100644 index 0000000000000000000000000000000000000000..091b358b697468422f1b32c664076e3ee246a107 GIT binary patch literal 93323 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

3$byTALjh4o6Wg2MiNnyW_FXui8S)b8Q>Q?KJh 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 literal 0 HcmV?d00001 diff --git a/diagrams/PhasedDiagrams.drawio b/diagrams/PhasedDiagrams.drawio index d0fbd8b..bd2dace 100644 --- a/diagrams/PhasedDiagrams.drawio +++ b/diagrams/PhasedDiagrams.drawio @@ -1,4 +1,4 @@ - + @@ -24,44 +24,44 @@ - + - + - + - + - + - + - + - + - + - + - + - + @@ -135,7 +135,7 @@ - + @@ -1717,7 +1717,7 @@ - + @@ -1909,52 +1909,52 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/src/main/java/com/adobe/campaign/tests/integro/phased/MethodMapping.java b/src/main/java/com/adobe/campaign/tests/integro/phased/MethodMapping.java index 6694e97..e1f0473 100644 --- a/src/main/java/com/adobe/campaign/tests/integro/phased/MethodMapping.java +++ b/src/main/java/com/adobe/campaign/tests/integro/phased/MethodMapping.java @@ -8,18 +8,22 @@ */ package com.adobe.campaign.tests.integro.phased; +import java.lang.annotation.Annotation; + class MethodMapping { - + Class declaredClass; int nrOfProviders; int totalClassMethods; int methodOrderInExecution; - + Annotation[] annotations; + MethodMapping(Class in_declaredClass, int in_nrOfProviders, int in_nrOfStepsInTest, int in_executionOrder) { nrOfProviders=in_nrOfProviders; totalClassMethods=in_nrOfStepsInTest; declaredClass=in_declaredClass; methodOrderInExecution=in_executionOrder; + annotations = new Annotation[]{}; } } \ No newline at end of file diff --git a/src/main/java/com/adobe/campaign/tests/integro/phased/MutationListener.java b/src/main/java/com/adobe/campaign/tests/integro/phased/MutationListener.java index 768afd3..b769cdf 100644 --- a/src/main/java/com/adobe/campaign/tests/integro/phased/MutationListener.java +++ b/src/main/java/com/adobe/campaign/tests/integro/phased/MutationListener.java @@ -129,7 +129,7 @@ public void transform(ITestAnnotation annotation, Class testClass, Constructor t } else { annotation.setDataProvider(PhasedTestManager.isPhasedTestShuffledMode( - l_currentClass) ? PhasedDataProvider.MUTATIONAL : PhasedDataProvider.SINGLE); + l_currentClass) ? PhasedDataProvider.MUTATIONAL : PhasedDataProvider.MUTATIONAL_SINGLE); } annotation.setDataProviderClass(PhasedDataProvider.class); @@ -192,30 +192,18 @@ public List intercept(List list, ITestContext //Prepare data for shuffle calculation //NIA Cases 1 & 5 - if (PhasedTestManager.isPhasedTestShuffledMode(lt_method)) { + //if (PhasedTestManager.isPhasedTestShuffledMode(lt_method)) { log.debug("{} In Shuffled mode : current test {}", PhasedTestManager.PHASED_TEST_LOG_PREFIX , ClassPathParser.fetchFullName(lt_method)); - if (!l_classMethodMap.containsKey(lt_method.getDeclaringClass())) { - l_classMethodMap.put(lt_method.getDeclaringClass(), new ArrayList<>()); - } + + l_classMethodMap.putIfAbsent(lt_method.getDeclaringClass(), new ArrayList<>()); l_classMethodMap.get(lt_method.getDeclaringClass()) .add(ClassPathParser.fetchFullName(lt_method)); - } + //} } //If the property PHASED.TESTS.DETECT.ORDER not set, we follow the standard TestNG order - /* - if (ConfigValueHandlerPhased.PHASED_TEST_DETECT_ORDER.is("false")) { - log.info("{} Generating Phased Providers", PhasedTestManager.PHASED_TEST_LOG_PREFIX); - //NIA - PhasedTestManager.generatePhasedProviders(l_classMethodMap, Phases.getCurrentPhase()); - //return list; - return list.stream().filter(l -> l.getMethod().getRealClass().equals(PhasedParent.class)).collect( - Collectors.toList()); - } else { - - */ //Generate scenario step dependencies PhasedTestManager.setStepDependencies(l_phasedClasses.stream().filter(pc -> !pc.equals(Mutational.class)) @@ -245,6 +233,7 @@ public List intercept(List list, ITestContext throw new PhasedTestDefinitionException("The scenario " + lt_sd.getScenarioName() + " is not executable. This is probably due to steps that consume a resource that is not produced."); } + for (StepDependencies lt_methodName : lt_sd.fetchExecutionOrderList()) { l_phasedDependencyMethods.stream() .filter(m -> m.getMethod().getConstructorOrMethod().getName() diff --git a/src/main/java/com/adobe/campaign/tests/integro/phased/MutationManager.java b/src/main/java/com/adobe/campaign/tests/integro/phased/MutationManager.java index 09cb239..644b24b 100644 --- a/src/main/java/com/adobe/campaign/tests/integro/phased/MutationManager.java +++ b/src/main/java/com/adobe/campaign/tests/integro/phased/MutationManager.java @@ -9,9 +9,12 @@ package com.adobe.campaign.tests.integro.phased; import com.adobe.campaign.tests.integro.phased.utils.ClassPathParser; +import org.testng.ITestNGMethod; import org.testng.ITestResult; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; import java.util.stream.Collectors; public class MutationManager { @@ -74,12 +77,24 @@ public static Integer[] fetchExecutionIndex(String in_className, String in_phase Integer[] lr_result = new Integer[2]; //FetchNr Of Steps - int l_nrOfMethods = PhasedTestManager.getMethodMap().keySet().stream().filter(m -> m.startsWith(in_className)) - .collect(Collectors.toList()).size(); - - Integer[] l_boundaries = in_runValues.getExecutionMode().equals(ExecutionMode.INTERRUPTIVE)? PhasedTestManager.fetchShuffledStepCount( - in_phaseGroup) : new Integer[] { - 0, l_nrOfMethods }; + var l_relevantMethodMaps = PhasedTestManager.getMethodMap().keySet().stream().filter(m -> m.startsWith(in_className)) + .collect(Collectors.toList()); + int l_nrOfMethods = l_relevantMethodMaps.size(); + + //By default run everything + Integer[] l_boundaries = new Integer[] {0, l_nrOfMethods }; + + if (in_runValues.getExecutionMode().equals(ExecutionMode.INTERRUPTIVE)) { + if (in_phaseGroup.equals(PhasedTestManager.STD_PHASED_GROUP_SINGLE)) { + l_boundaries = new Integer[] { l_relevantMethodMaps.stream() + .filter(m -> Arrays.stream(PhasedTestManager.methodMap.get(m).annotations) + .anyMatch(a -> a.annotationType().equals(PhaseEvent.class))).findFirst() + .map(mf -> PhasedTestManager.methodMap.get(mf).methodOrderInExecution).orElse(l_nrOfMethods) + - 1, l_nrOfMethods }; + } else { + l_boundaries = PhasedTestManager.fetchShuffledStepCount(in_phaseGroup); + } + } switch (in_runValues.getBehavior()) { case "PRODUCER": @@ -119,4 +134,16 @@ public static boolean isSingleMode(Class in_class) { return isMutationalTest(in_class) && (PhasedTestManager.isPhasedTestWithEvent(in_class) || PhasedTestManager.isPhasedTestTargetOfEvent(in_class)); } + + /** + * Returns the data provider for a single phase + *

+ * Author : gandomi + * + * @param in_method The step/method for which we want to fond out the data provider + * @return An array containing the data providers for the method. Otherwise an empty array + */ + public static Object[][] fetchProvidersSingle(ITestNGMethod in_method) { + return new Object[][] {{ PhasedTestManager.STD_PHASED_GROUP_SINGLE }}; + } } diff --git a/src/main/java/com/adobe/campaign/tests/integro/phased/NonInterruptiveEvent.java b/src/main/java/com/adobe/campaign/tests/integro/phased/NonInterruptiveEvent.java index 727c9c3..60f13fd 100644 --- a/src/main/java/com/adobe/campaign/tests/integro/phased/NonInterruptiveEvent.java +++ b/src/main/java/com/adobe/campaign/tests/integro/phased/NonInterruptiveEvent.java @@ -10,8 +10,12 @@ import com.adobe.campaign.tests.integro.phased.exceptions.PhasedTestingEventException; +import java.util.concurrent.Future; + public abstract class NonInterruptiveEvent implements Runnable { + Future threadFuture = null; + /** * Starts the non-interruptive event * returns true if the event was successfully started @@ -36,19 +40,14 @@ public enum states {DEFINED , STARTED, FAILURE, FINISHED}; @Override public void run() { - state = startEvent() ? states.STARTED : states.FAILURE; - - if (state.equals(states.FAILURE)) { - throw new PhasedTestingEventException("There was a problem starting this event."); + try { + startEvent(); + state = states.STARTED; + } catch (Exception e) { + state = states.FAILURE; + throw new PhasedTestingEventException("There was a problem starting this event.", e); } - waitTillFinished(); - - if (!isFinished()) { - throw new PhasedTestingEventException("This event did not finish as expected."); - } - state=states.FINISHED; - Thread.currentThread().interrupt(); return; } diff --git a/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedDataProvider.java b/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedDataProvider.java index 3510033..77348d3 100644 --- a/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedDataProvider.java +++ b/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedDataProvider.java @@ -12,7 +12,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.testng.ITestContext; import org.testng.ITestNGMethod; import org.testng.annotations.DataProvider; @@ -21,6 +20,8 @@ public class PhasedDataProvider { public static final String SINGLE = "phased-data-provider-single"; public static final String DEFAULT = "phased-default"; public static final String MUTATIONAL = "MUTATIONAL"; + public static final String MUTATIONAL_SINGLE = "MUTATIONAL_SINGLE"; + @DataProvider(name = SHUFFLED) public Object[][] shuffledMode(Method m) { @@ -28,16 +29,19 @@ public Object[][] shuffledMode(Method m) { } protected static Logger log = LogManager.getLogger(); - @DataProvider(name = "MUTATIONAL") + @DataProvider(name = PhasedDataProvider.MUTATIONAL) public Object[][] shuffleGroups(ITestNGMethod tm) { - //PhasedTestManager.fetchProvidersShuffled(m); - log.info(tm.getTestClass().getRealClass().getTypeName()); return PhasedTestManager.fetchProvidersShuffled(tm); } - + + @DataProvider(name = MUTATIONAL_SINGLE) + public Object[][] singleRunMode(ITestNGMethod tm) { + return MutationManager.fetchProvidersSingle(tm); + } + @DataProvider(name = SINGLE) public Object[] singleRunMode(Method m) { return PhasedTestManager.fetchProvidersSingle(m); diff --git a/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedEventManager.java b/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedEventManager.java index 53030d3..948451d 100644 --- a/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedEventManager.java +++ b/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedEventManager.java @@ -10,6 +10,7 @@ import com.adobe.campaign.tests.integro.phased.exceptions.PhasedTestConfigurationException; import com.adobe.campaign.tests.integro.phased.exceptions.PhasedTestException; +import com.adobe.campaign.tests.integro.phased.exceptions.PhasedTestingEventException; import com.adobe.campaign.tests.integro.phased.utils.ClassPathParser; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -20,6 +21,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -116,7 +118,7 @@ protected static NonInterruptiveEvent startEvent(String in_event, String in_onAc NonInterruptiveEvent nie = instantiateClassFromString(in_event); logEvent(EventMode.START, in_event, in_onAccountOfStep); events.put(in_onAccountOfStep, nie); - eventExecutor.submit(nie); + nie.threadFuture = eventExecutor.submit(nie); while (nie.getState().equals(NonInterruptiveEvent.states.DEFINED)) { try { Thread.sleep(1); @@ -125,6 +127,21 @@ protected static NonInterruptiveEvent startEvent(String in_event, String in_onAc throw new RuntimeException(e); } } + + if (nie.getState().equals(NonInterruptiveEvent.states.FAILURE)) { + log.error("Event Exception : The event {} for step {} caused an exception during start.", in_event, in_onAccountOfStep); + try { + nie.threadFuture.get(); + } catch (InterruptedException | ExecutionException ex) { + ex.getCause().printStackTrace(); + } + } + + //NON_INTERRUPTIVE 23 + if (ExecutionMode.NON_INTERRUPTIVE.fetchBehavior().startsWith("2")) { + log.info("Forcing Event End {} BEFORE step {} has started.", in_event, in_onAccountOfStep); + performWaitTilFinish(in_event, in_onAccountOfStep, nie); + } return nie; } @@ -169,13 +186,50 @@ protected static NonInterruptiveEvent finishEvent(String in_event, String in_onA throw new PhasedTestConfigurationException("Class "+in_event+" not found.",e); } - l_activeEvent.waitTillFinished(); + //if (Phases.NON_INTERRUPTIVE.fetchType().startsWith("3")) { + // log.info("Forcing Event End {} AFTER step {} has finished.", in_event, in_onAccountOfStep); + performWaitTilFinish(in_event, in_onAccountOfStep, l_activeEvent); + //} + + if (!l_activeEvent.isFinished()) { + throw new PhasedTestingEventException("This event did not finish as expected."); + } + + l_activeEvent.state = NonInterruptiveEvent.states.FINISHED; + log.info("Event {} for step {} has finished.", in_event, in_onAccountOfStep); + + if (!l_activeEvent.threadFuture.isDone()) { + log.error("The event {} for step {} did not finish as expected. Cancelling the event.", in_event, in_onAccountOfStep); + l_activeEvent.threadFuture.cancel(true); + } logEvent(EventMode.END, in_event, in_onAccountOfStep); - l_activeEvent.tearDownEvent(); + performTearDown(in_event, in_onAccountOfStep, l_activeEvent); return l_activeEvent; } + private static void performWaitTilFinish(String in_event, String in_onAccountOfStep, NonInterruptiveEvent nie) { + try { + nie.waitTillFinished(); + } catch (Exception e) { + log.error("The waitTillFinished method for event {} caused an exception in the context of step {}.", + in_event, in_onAccountOfStep); + e.printStackTrace(); + nie.threadFuture.cancel(true); + } + } + + private static void performTearDown(String in_event, String in_onAccountOfStep, NonInterruptiveEvent l_activeEvent) { + try { + l_activeEvent.tearDownEvent(); + } catch (Exception e) { + log.error("The tearDownEvent method for event {} caused an exception of type {} in the context of step {}.", + in_event, e.getCause(), in_onAccountOfStep); + e.printStackTrace(); + l_activeEvent.threadFuture.cancel(true); + } + } + public static List getEventLogs() { return eventLogs; } @@ -183,7 +237,7 @@ public static List getEventLogs() { /** * resets the events. Mostly used for testing */ - public static void resetEvents() { + static void resetEvents() { events = new HashMap<>(); eventLogs = new ArrayList(); } @@ -235,10 +289,11 @@ public static ExecutorService getEventExecutor() { } public static void stopEventExecutor() { - if (eventExecutor != null) + if (eventExecutor != null) { eventExecutor.shutdown(); - + } eventExecutor = null; + } diff --git a/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedTestManager.java b/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedTestManager.java index 4094336..3e700d6 100644 --- a/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedTestManager.java +++ b/src/main/java/com/adobe/campaign/tests/integro/phased/PhasedTestManager.java @@ -725,13 +725,13 @@ public static Map generatePhasedProviders(Map, L Map in_scenarioDependencies, RunValues in_runValues) { methodMap = new HashMap<>(); - for (Entry, List> entry : in_classMethodMap.entrySet().stream().filter(e -> !Modifier.isAbstract(e.getKey().getModifiers())).collect( + for (Entry, List> lt_entry : in_classMethodMap.entrySet().stream().filter(e -> !Modifier.isAbstract(e.getKey().getModifiers())).collect( Collectors.toList())) { List lt_methodList = - in_scenarioDependencies == null ? entry.getValue() : in_scenarioDependencies.get( - entry.getKey().getTypeName()).fetchExecutionOrderList().stream() - .map(ol -> entry.getKey().getTypeName() + "." + ol.getStepName()).collect( + in_scenarioDependencies == null ? lt_entry.getValue() : in_scenarioDependencies.get( + lt_entry.getKey().getTypeName()).fetchExecutionOrderList().stream() + .map(ol -> lt_entry.getKey().getTypeName() + "." + ol.getStepName()).collect( Collectors.toList()); if (in_runValues.getExecutionMode().equals(ExecutionMode.INTERRUPTIVE)) { @@ -740,20 +740,26 @@ public static Map generatePhasedProviders(Map, L Collections.reverse(lt_methodList); } - for (int i = 0; i < entry.getValue().size(); i++) { + for (int i = 0; i < lt_entry.getValue().size(); i++) { methodMap.put(lt_methodList.get(i), - new MethodMapping(entry.getKey(), entry.getValue().size() - i, - entry.getValue().size(), i + 1)); + new MethodMapping(lt_entry.getKey(), lt_entry.getValue().size() - i, + lt_entry.getValue().size(), i + 1)); } } else { - for (int i = 0; i < entry.getValue().size(); i++) { + for (int i = 0; i < lt_entry.getValue().size(); i++) { methodMap.put(lt_methodList.get(i), - new MethodMapping(entry.getKey(), entry.getValue().size(), - entry.getValue().size(), i + 1)); + new MethodMapping(lt_entry.getKey(), lt_entry.getValue().size(), + lt_entry.getValue().size(), i + 1)); } } + + //Add annotations + for (Method method : lt_entry.getKey().getMethods()) { + lt_methodList.stream().filter(mli -> mli.equals(ClassPathParser.fetchFullName(method))).findFirst().ifPresent( + m -> methodMap.get(m).annotations = method.getDeclaredAnnotations()); + } } return methodMap; } @@ -1519,6 +1525,7 @@ public static Integer[] fetchShuffledStepCount(String in_phaseGroup) { throw new PhasedTestException("The phase group of this test does not seem correct: " + in_phaseGroup); } + String l_numberPart = in_phaseGroup.substring(STD_PHASED_GROUP_PREFIX.length()); return Arrays.stream(l_numberPart.split("_")).mapToInt(Integer::parseInt).boxed().toArray(Integer[]::new); diff --git a/src/main/java/com/adobe/campaign/tests/integro/phased/exceptions/PhasedTestingEventException.java b/src/main/java/com/adobe/campaign/tests/integro/phased/exceptions/PhasedTestingEventException.java index 8633405..443276f 100644 --- a/src/main/java/com/adobe/campaign/tests/integro/phased/exceptions/PhasedTestingEventException.java +++ b/src/main/java/com/adobe/campaign/tests/integro/phased/exceptions/PhasedTestingEventException.java @@ -12,4 +12,8 @@ public class PhasedTestingEventException extends RuntimeException { public PhasedTestingEventException(String message) { super(message); } + + public PhasedTestingEventException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/MutationManagerTests.java b/src/test/java/com/adobe/campaign/tests/integro/phased/MutationManagerTests.java index beae081..265eeae 100644 --- a/src/test/java/com/adobe/campaign/tests/integro/phased/MutationManagerTests.java +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/MutationManagerTests.java @@ -9,10 +9,13 @@ package com.adobe.campaign.tests.integro.phased; import com.adobe.campaign.tests.integro.phased.data.NormalSeries_A; +import com.adobe.campaign.tests.integro.phased.data.PhasedSeries_F_Shuffle; import com.adobe.campaign.tests.integro.phased.data.PhasedSeries_H_ShuffledClassWithError; +import com.adobe.campaign.tests.integro.phased.mutational.data.ie.MutationalTestSingleRun; import com.adobe.campaign.tests.integro.phased.mutational.data.nested.MutationalTestParent; import com.adobe.campaign.tests.integro.phased.mutational.data.simple1.PhasedChild1; import com.adobe.campaign.tests.integro.phased.mutational.data.simple1.PhasedChild2; +import com.adobe.campaign.tests.integro.phased.utils.ClassPathParser; import com.adobe.campaign.tests.integro.phased.utils.GeneralTestUtils; import com.adobe.campaign.tests.integro.phased.utils.MockTestTools; import org.hamcrest.Matchers; @@ -24,6 +27,10 @@ import java.io.File; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -110,7 +117,7 @@ public void testIsMutational_negativeNonMutational() throws NoSuchMethodExceptio } @Test - public void testExecutionIndex_InterruptiveProducer() { + public void testExecutionIndex_ShufflingProducer() { //PRODUCER //Three steps //PG 2_1 @@ -151,6 +158,59 @@ public void testExecutionIndex_InterruptiveProducer() { Matchers.arrayContaining(0, 3)); } + @Test + public void testExecutionIndex_SingleRunProducer() throws NoSuchMethodException { + //PRODUCER + //Three steps + //PG 2_1 + + Class testClass = MutationalTestSingleRun.class; + + Map, List> l_myMap = new HashMap<>(); + Method method1 = testClass.getMethod("step1", String.class); + Method method2 = testClass.getMethod("step2", String.class); + Method method3 = testClass.getMethod("step3", String.class); + + l_myMap.put(testClass, + Arrays.asList(ClassPathParser.fetchFullName(method1), ClassPathParser.fetchFullName(method2), + ClassPathParser.fetchFullName(method3))); + + Map l_result = PhasedTestManager.generatePhasedProviders(l_myMap, + Phases.getCurrentPhase().fetchRunValues()); + + String l_phaseGroup = PhasedTestManager.STD_PHASED_GROUP_SINGLE; + + //MutationManager. + //String l_scenarioName = MutationManager.fetchScenarioName(testClass.getTypeName(), l_phaseGroup); + + assertThat("We should have two steps to execute in Producer", + MutationManager.fetchExecutionIndex(testClass.getTypeName(), l_phaseGroup, + new RunValues(ExecutionMode.INTERRUPTIVE, "PRODUCER")), + Matchers.arrayContaining(0, 2)); + + assertThat("We should have one steps to executed in Consumer", + MutationManager.fetchExecutionIndex(testClass.getTypeName(), l_phaseGroup, + new RunValues(ExecutionMode.INTERRUPTIVE, "CONSUMER")), + Matchers.arrayContaining(2, 3)); + + assertThat("We should have one steps to executed by default", + MutationManager.fetchExecutionIndex(testClass.getTypeName(), l_phaseGroup, + new RunValues(ExecutionMode.STANDARD, "")), + Matchers.arrayContaining(0, 3)); + + assertThat("We should have one steps to executed in Asynchronous", + MutationManager.fetchExecutionIndex(testClass.getTypeName(), l_phaseGroup, + new RunValues(ExecutionMode.NON_INTERRUPTIVE, "23")), + Matchers.arrayContaining(0, 3)); + + assertThat("We should have one steps to executed in permutational", + MutationManager.fetchExecutionIndex(testClass.getTypeName(), l_phaseGroup, + new RunValues(ExecutionMode.PERMUTATIONAL, "23")), + Matchers.arrayContaining(0, 3)); + } + + + @Test public void testIfTestIsMutationalSimple() { Class l_testClass = PhasedChild1.class; @@ -208,4 +268,49 @@ public void testIsShuffled_Mutational() throws SecurityException { } + @Test + public void testCreateDataProviderData_singleRun() throws NoSuchMethodException { + Phases.PRODUCER.activate(); + + Map, List> l_myMap = new HashMap<>(); + Method method1 = MutationalTestSingleRun.class.getMethod("step1", String.class); + Method method2 = MutationalTestSingleRun.class.getMethod("step2", String.class); + Method method3 = MutationalTestSingleRun.class.getMethod("step3", String.class); + + l_myMap.put(MutationalTestSingleRun.class, + Arrays.asList(ClassPathParser.fetchFullName(method1), ClassPathParser.fetchFullName(method2), + ClassPathParser.fetchFullName(method3))); + + Map l_result = PhasedTestManager.generatePhasedProviders(l_myMap, + Phases.getCurrentPhase().fetchRunValues()); + + assertThat("we need to have the expected key", l_result.containsKey(ClassPathParser.fetchFullName(method1))); + assertThat("The first method should have three entries", l_result.get(ClassPathParser.fetchFullName(method1)).nrOfProviders, equalTo(3)); + + assertThat("The first method should have two entries", l_result.get(ClassPathParser.fetchFullName(method2)).nrOfProviders, equalTo(2)); + + assertThat("The first method should have one entry", l_result.get(ClassPathParser.fetchFullName(method3)).nrOfProviders, equalTo(1)); + + + assertThat("We should have the same amount of total sizes", l_result.get(ClassPathParser.fetchFullName(method1)).totalClassMethods, + equalTo(l_result.get(ClassPathParser.fetchFullName(method2)).totalClassMethods)); + assertThat("We should have the same amount of total sizes", l_result.get(ClassPathParser.fetchFullName(method1)).totalClassMethods, + equalTo(l_result.get(ClassPathParser.fetchFullName(method3)).totalClassMethods)); + + assertThat("The first method should have three entries", l_result.get(ClassPathParser.fetchFullName(method1)).methodOrderInExecution, equalTo(1)); + + assertThat("The first method should have two entries", l_result.get(ClassPathParser.fetchFullName(method2)).methodOrderInExecution, equalTo(2)); + + assertThat("The first method should have one entry", l_result.get(ClassPathParser.fetchFullName(method3)).methodOrderInExecution, equalTo(3)); + + assertThat("We should have the correct number of annotations", l_result.get(ClassPathParser.fetchFullName(method3)).annotations.length, + Matchers.equalTo(1)); + + assertThat("We should have the correct annotations", l_result.get(ClassPathParser.fetchFullName(method3)).annotations[0], + Matchers.instanceOf(PhaseEvent.class)); + + + } + + } diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/MutationalTests.java b/src/test/java/com/adobe/campaign/tests/integro/phased/MutationalTests.java index 4cec4ff..f9f0578 100644 --- a/src/test/java/com/adobe/campaign/tests/integro/phased/MutationalTests.java +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/MutationalTests.java @@ -9,8 +9,12 @@ package com.adobe.campaign.tests.integro.phased; import com.adobe.campaign.tests.integro.phased.data.events.MyNonInterruptiveEvent; +import com.adobe.campaign.tests.integro.phased.data.events.NIEMutationalSynchronousEvent; +import com.adobe.campaign.tests.integro.phased.data.events.NIEMutationalSynchronousEventWithException; +import com.adobe.campaign.tests.integro.phased.data.mutational.TestMutationalNIE_Synchroneous; import com.adobe.campaign.tests.integro.phased.mutational.data.erroneous.IE_Shuffled_ErrorAssertion1; import com.adobe.campaign.tests.integro.phased.mutational.data.erroneous.IE_Shuffled_ErrorOtherNonAssertive1; +import com.adobe.campaign.tests.integro.phased.mutational.data.ie.MutationalTestSingleRun; import com.adobe.campaign.tests.integro.phased.mutational.data.nie.TestMutationalShuffled_eventPassedAsExecutionVariable; import com.adobe.campaign.tests.integro.phased.mutational.data.permutational.MultipleProducerConsumer; import com.adobe.campaign.tests.integro.phased.mutational.data.permutational.ShoppingCartDemo; @@ -295,6 +299,68 @@ public void testInterruptiveEvent() { Matchers.equalTo(3)); } + @Test + public void testInterruptiveSingleRunEvent() { + + // ***** PRODUCER **** + TestNG myTestNG = TestTools.createTestNG(); + TestListenerAdapter tla = TestTools.fetchTestResultsHandler(myTestNG); + + // Define suites + XmlSuite mySuite = TestTools.addSuitToTestNGTest(myTestNG, "Automated Suite Phased Testing"); + + // Add listeners + mySuite.addListener(MutationListener.class.getTypeName()); + + // Create an instance of XmlTest and assign a name for it. + XmlTest myTest = TestTools.attachTestToSuite(mySuite, "Test Phased Tests"); + + Class l_testClass = MutationalTestSingleRun.class; + myTest.setXmlClasses(Collections.singletonList(new XmlClass(l_testClass))); + + Phases.PRODUCER.activate(); + myTestNG.run(); + + assertThat("We should have 2 successful methods of phased Tests", + (int) tla.getPassedTests().stream() + .filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), + is(equalTo(1))); + + assertThat("We should have no unsuccesful methods of phased Tests", + tla.getFailedTests().size() + tla.getSkippedTests().size(), is(equalTo(0))); + + // ***** COSNUMER **** + + //Clear data + PhasedTestManager.clearCache(); + Phases.CONSUMER.activate(); + + TestNG myTestNG2 = TestTools.createTestNG(); + TestListenerAdapter tla2 = TestTools.fetchTestResultsHandler(myTestNG2); + + // Define suites + XmlSuite mySuite2 = TestTools.addSuitToTestNGTest(myTestNG2, "Automated Suite Phased Testing"); + + // Add listeners + mySuite2.addListener(MutationListener.class.getTypeName()); + + // Create an instance of XmlTest and assign a name for it. + XmlTest myTest2 = TestTools.attachTestToSuite(mySuite2, "Test Phased Tests"); + + myTest2.setXmlClasses(Collections.singletonList(new XmlClass(l_testClass))); + + myTestNG2.run(); + + assertThat("We should have 1 successful methods of phased Tests", + (int) tla2.getPassedTests().stream() + .filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), + is(equalTo(1))); + + assertThat("We should have no unsuccesful methods of phased Tests", + tla.getFailedTests().size() + tla.getSkippedTests().size(), is(equalTo(0))); + + } + @Test public void testPermutational() { //Activate Merge @@ -398,8 +464,190 @@ public void testNonInterruptive_ParellelConfiguredAsExecutionVariable_Shuffled_O assertThat("We should have no failed tests", tla.getFailedTests().size(), equalTo(0)); assertThat("We should have no skipped tests", tla.getSkippedTests().size(), equalTo(0)); - assertThat("We should have the correct number of events in the logs (1 x phase groups)", + assertThat("We should have the correct number of events in the logs (2 x phase groups)", PhasedEventManager.getEventLogs().size(), Matchers.equalTo(6)); } + + + /** + * This is a test for non-intyerruptive events in shuffled classes. Using the legacy annotations + */ + @Test + public void testNonInterruptive_23_Targeted() { + NIEMutationalSynchronousEvent.START_STEP_VALUE = 2; + NIEMutationalSynchronousEvent.WTF_STEP_VALUE = 7; + NIEMutationalSynchronousEvent.TDE_STEP_VALUE = 23; + + //Reset + TestMutationalNIE_Synchroneous.testElement = 3; + + //The WTF should be finished before the start of step 2 + TestMutationalNIE_Synchroneous.expectedStep2Value = NIEMutationalSynchronousEvent.WTF_STEP_VALUE; + + TestMutationalNIE_Synchroneous.expectedStep3Value = NIEMutationalSynchronousEvent.TDE_STEP_VALUE; + TestMutationalNIE_Synchroneous.expectedStep3EndResult = NIEMutationalSynchronousEvent.WTF_STEP_VALUE + TestMutationalNIE_Synchroneous.testElement; + + + // Rampup + TestNG myTestNG = TestTools.createTestNG(); + TestListenerAdapter tla = TestTools.fetchTestResultsHandler(myTestNG); + + // Define suites + XmlSuite mySuite = TestTools.addSuitToTestNGTest(myTestNG, "Automated Suite Phased Testing"); + + // Add listeners + mySuite.addListener(MutationListener.class.getTypeName()); + + // Create an instance of XmlTest and assign a name for it. + XmlTest myTest = TestTools.attachTestToSuite(mySuite, "Test Shuffled Phased Tests"); + + final Class l_testClass = TestMutationalNIE_Synchroneous.class; + myTest.setXmlClasses(Collections.singletonList(new XmlClass(l_testClass))); + + ExecutionMode.NON_INTERRUPTIVE.activate("23"); + ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.activate(NIEMutationalSynchronousEvent.class.getTypeName()); + ConfigValueHandlerPhased.EVENT_TARGET.activate(l_testClass.getTypeName() + "#step2"); + + myTestNG.run(); + + assertThat("We should be in non-interruptive mode shuffled", + !PhasedTestManager.isPhasedTestShuffledMode(l_testClass)); + + tla.getFailedTests().forEach(t -> System.out.println(t.getThrowable().getMessage())); + + assertThat("We should have 1 successful scenarios of mutational Tests", + (int) tla.getPassedTests().stream().filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), + is(equalTo(1))); + + //Global + assertThat("We should have no failed tests", tla.getFailedTests().size(), equalTo(0)); + assertThat("We should have no skipped tests", tla.getSkippedTests().size(), equalTo(0)); + + assertThat("We should have the correct number of events in the logs (1 x phase groups)", + PhasedEventManager.getEventLogs().size(), + Matchers.equalTo(2)); + } + + /** + * This is a test for non-interruptive events in shuffled classes. Using the legacy annotations + */ + @Test + public void testNonInterruptive_33_Targetted() { + NIEMutationalSynchronousEvent.START_STEP_VALUE = 2; + NIEMutationalSynchronousEvent.WTF_STEP_VALUE = 7; + NIEMutationalSynchronousEvent.TDE_STEP_VALUE = 23; + + //Reset + TestMutationalNIE_Synchroneous.testElement = 3; + + //The WTF should be finished before the start of step 2 + TestMutationalNIE_Synchroneous.expectedStep2Value = NIEMutationalSynchronousEvent.START_STEP_VALUE; + + TestMutationalNIE_Synchroneous.expectedStep3Value = NIEMutationalSynchronousEvent.TDE_STEP_VALUE; + TestMutationalNIE_Synchroneous.expectedStep3EndResult = TestMutationalNIE_Synchroneous.expectedStep2Value + TestMutationalNIE_Synchroneous.testElement; + + // Rampup + TestNG myTestNG = TestTools.createTestNG(); + TestListenerAdapter tla = TestTools.fetchTestResultsHandler(myTestNG); + + // Define suites + XmlSuite mySuite = TestTools.addSuitToTestNGTest(myTestNG, "Automated Suite Phased Testing"); + + // Add listeners + mySuite.addListener(MutationListener.class.getTypeName()); + + // Create an instance of XmlTest and assign a name for it. + XmlTest myTest = TestTools.attachTestToSuite(mySuite, "Test Shuffled Phased Tests"); + + final Class l_testClass = TestMutationalNIE_Synchroneous.class; + myTest.setXmlClasses(Collections.singletonList(new XmlClass(l_testClass))); + + ExecutionMode.NON_INTERRUPTIVE.activate("33"); + ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.activate(NIEMutationalSynchronousEvent.class.getTypeName()); + ConfigValueHandlerPhased.EVENT_TARGET.activate(TestMutationalNIE_Synchroneous.class.getTypeName() + "#step2"); + + myTestNG.run(); + + assertThat("We should not be in non-interruptive mode shuffled", + !PhasedTestManager.isPhasedTestShuffledMode(l_testClass)); + + tla.getFailedTests().forEach(t -> System.out.println(t.getThrowable().getMessage())); + + assertThat("We should have 1 successful methods of phased Tests", + (int) tla.getPassedTests().stream().filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), + is(equalTo(1))); + + //Global + assertThat("We should have no failed tests", tla.getFailedTests().size(), equalTo(0)); + assertThat("We should have no skipped tests", tla.getSkippedTests().size(), equalTo(0)); + + assertThat("We should have the correct number of events in the logs (1 x phase groups)", + PhasedEventManager.getEventLogs().size(), + Matchers.equalTo(2)); + } + + + /** + * This is a test for non-interruptive events in shuffled classes. Using the legacy annotations + */ + + @Test + public void testNonInterruptive_33_Targetted_ExceptionInStartEvent() { + NIEMutationalSynchronousEventWithException.START_STEP_VALUE = 2; + NIEMutationalSynchronousEventWithException.WTF_STEP_VALUE = 7; + NIEMutationalSynchronousEventWithException.TDE_STEP_VALUE = 23; + NIEMutationalSynchronousEventWithException.exceptionPlace = 1; + + //Reset + TestMutationalNIE_Synchroneous.testElement = 3; + + //The WTF should be finished before the start of step 2 + TestMutationalNIE_Synchroneous.expectedStep2Value = NIEMutationalSynchronousEventWithException.START_STEP_VALUE; + + TestMutationalNIE_Synchroneous.expectedStep3Value = NIEMutationalSynchronousEventWithException.TDE_STEP_VALUE; + TestMutationalNIE_Synchroneous.expectedStep3EndResult = TestMutationalNIE_Synchroneous.expectedStep2Value + TestMutationalNIE_Synchroneous.testElement; + + // Rampup + TestNG myTestNG = TestTools.createTestNG(); + TestListenerAdapter tla = TestTools.fetchTestResultsHandler(myTestNG); + + // Define suites + XmlSuite mySuite = TestTools.addSuitToTestNGTest(myTestNG, "Automated Suite Phased Testing"); + + // Add listeners + mySuite.addListener(MutationListener.class.getTypeName()); + + // Create an instance of XmlTest and assign a name for it. + XmlTest myTest = TestTools.attachTestToSuite(mySuite, "Test Shuffled Phased Tests"); + + final Class l_testClass = TestMutationalNIE_Synchroneous.class; + myTest.setXmlClasses(Collections.singletonList(new XmlClass(l_testClass))); + + ExecutionMode.NON_INTERRUPTIVE.activate("33"); + ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.activate(NIEMutationalSynchronousEventWithException.class.getTypeName()); + ConfigValueHandlerPhased.EVENT_TARGET.activate(TestMutationalNIE_Synchroneous.class.getTypeName() + "#step2"); + + myTestNG.run(); + + assertThat("We should be in non-interruptive mode shuffled", + !PhasedTestManager.isPhasedTestShuffledMode(l_testClass)); + + tla.getFailedTests().forEach(t -> System.out.println(t.getThrowable().getMessage())); + + assertThat("Only step 1 should have succeeded", + (int) tla.getPassedTests().stream().filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), + is(equalTo(1))); + + //Global + assertThat("We should have no failed tests", tla.getFailedTests().size(), equalTo(0)); + assertThat("We should have no skipped tests", tla.getSkippedTests().size(), equalTo(0)); + + assertThat("We should have the correct number of events in the logs (1 x phase groups)", + PhasedEventManager.getEventLogs().size(), + Matchers.equalTo(2)); + } + + + } diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/PhasedTestManagerTests.java b/src/test/java/com/adobe/campaign/tests/integro/phased/PhasedTestManagerTests.java index d630bbb..01eb561 100644 --- a/src/test/java/com/adobe/campaign/tests/integro/phased/PhasedTestManagerTests.java +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/PhasedTestManagerTests.java @@ -274,7 +274,7 @@ public void exportingDataTwice() throws IOException { } /** - * Testing {@code PROP_PHASED_DATA_PATH} that when the property {@value ConfigValueHandlerPhased#PROP_PHASED_DATA_PATH.systemName} is set, that path is + * Testing {@code PROP_PHASED_DATA_PATH} that when the property "PHASED.TESTS.STORAGE.PATH" is set, that path is * used. * * Author : gandomi @@ -612,6 +612,7 @@ public void importingDataSTD_UsingSystemValues() { @Test public void testCreateDataProviderData() throws NoSuchMethodException { + Phases.PRODUCER.activate(); Map, List> l_myMap = new HashMap<>(); @@ -666,7 +667,6 @@ public void testCreateDataProviderData() throws NoSuchMethodException { assertThat(l_providerC[0].length, equalTo(1)); assertThat(l_providerC[0][0], equalTo(PhasedTestManager.STD_PHASED_GROUP_PREFIX + "3_0")); - } @Test diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/TestPhased.java b/src/test/java/com/adobe/campaign/tests/integro/phased/TestPhased.java index 0d7a350..9891f0b 100644 --- a/src/test/java/com/adobe/campaign/tests/integro/phased/TestPhased.java +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/TestPhased.java @@ -537,11 +537,6 @@ public void testFullMonty_SingleRun_newModel() { Phases.PRODUCER.activate(); myTestNG.run(); - assertThat("We should have 2 successful methods of phased Tests", - (int) tla.getPassedTests().stream() - .filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), - is(equalTo(2))); - assertThat("We should have 2 successful methods of phased Tests", (int) tla.getPassedTests().stream() .filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/TestPhasedNonInterruptive.java b/src/test/java/com/adobe/campaign/tests/integro/phased/TestPhasedNonInterruptive.java index abe5b21..8991830 100644 --- a/src/test/java/com/adobe/campaign/tests/integro/phased/TestPhasedNonInterruptive.java +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/TestPhasedNonInterruptive.java @@ -745,6 +745,300 @@ public void testNonInterruptive_ParellelConfiguredAsExecutionVariableTargetted_S } + /** + * This is a test for non-interruptive events in shuffled classes. Using the legacy annotations + */ + @Test + public void testNonInterruptive_33_Targetted() { + NIESynchronousEvent.START_STEP_VALUE = 2; + NIESynchronousEvent.WTF_STEP_VALUE = 7; + NIESynchronousEvent.TDE_STEP_VALUE = 23; + + //Reset + TestNIE_Synchroneous.testElement = 3; + + //The WTF should be finished before the start of step 2 + TestNIE_Synchroneous.expectedStep2Value = NIESynchronousEvent.START_STEP_VALUE; + + TestNIE_Synchroneous.expectedStep3Value = NIESynchronousEvent.TDE_STEP_VALUE; + TestNIE_Synchroneous.expectedStep3EndResult = TestNIE_Synchroneous.expectedStep2Value + TestNIE_Synchroneous.testElement; + + // Rampup + TestNG myTestNG = TestTools.createTestNG(); + TestListenerAdapter tla = TestTools.fetchTestResultsHandler(myTestNG); + + // Define suites + XmlSuite mySuite = TestTools.addSuitToTestNGTest(myTestNG, "Automated Suite Phased Testing"); + + // Add listeners + mySuite.addListener("com.adobe.campaign.tests.integro.phased.PhasedTestListener"); + + // Create an instance of XmlTest and assign a name for it. + XmlTest myTest = TestTools.attachTestToSuite(mySuite, "Test Shuffled Phased Tests"); + + final Class l_testClass = TestNIE_Synchroneous.class; + myTest.setXmlClasses(Collections.singletonList(new XmlClass(l_testClass))); + + Phases.ASYNCHRONOUS.activate(); + ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.activate(NIESynchronousEvent.class.getTypeName()); + ConfigValueHandlerPhased.EVENT_TARGET.activate(TestNIE_Synchroneous.class.getTypeName() + "#step2"); + + myTestNG.run(); + + assertThat("We should be in non-interruptive mode shuffled", + !PhasedTestManager.isPhasedTestShuffledMode(l_testClass)); + + tla.getFailedTests().forEach(t -> System.out.println(t.getThrowable().getMessage())); + + assertThat("We should have 3 successful methods of phased Tests", + (int) tla.getPassedTests().stream().filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), + is(equalTo(3))); + + //Global + assertThat("We should have no failed tests", tla.getFailedTests().size(), equalTo(0)); + assertThat("We should have no skipped tests", tla.getSkippedTests().size(), equalTo(0)); + + assertThat("We should have the correct number of events in the logs (1 x phase groups)", + PhasedEventManager.getEventLogs().size(), + Matchers.equalTo(2)); + } + + + /** + * This is a test for non-interruptive events in shuffled classes. Using the legacy annotations + */ + @Test + public void testNonInterruptive_33_Targetted_ExceptionInStartEvent() { + NIESynchronousEventWithException.START_STEP_VALUE = 2; + NIESynchronousEventWithException.WTF_STEP_VALUE = 7; + NIESynchronousEventWithException.TDE_STEP_VALUE = 23; + NIESynchronousEventWithException.exceptionPlace = 1; + + //Reset + TestNIE_Synchroneous.testElement = 3; + + //The WTF should be finished before the start of step 2 + TestNIE_Synchroneous.expectedStep2Value = NIESynchronousEventWithException.START_STEP_VALUE; + + TestNIE_Synchroneous.expectedStep3Value = NIESynchronousEventWithException.TDE_STEP_VALUE; + TestNIE_Synchroneous.expectedStep3EndResult = TestNIE_Synchroneous.expectedStep2Value + TestNIE_Synchroneous.testElement; + + // Rampup + TestNG myTestNG = TestTools.createTestNG(); + TestListenerAdapter tla = TestTools.fetchTestResultsHandler(myTestNG); + + // Define suites + XmlSuite mySuite = TestTools.addSuitToTestNGTest(myTestNG, "Automated Suite Phased Testing"); + + // Add listeners + mySuite.addListener("com.adobe.campaign.tests.integro.phased.PhasedTestListener"); + + // Create an instance of XmlTest and assign a name for it. + XmlTest myTest = TestTools.attachTestToSuite(mySuite, "Test Shuffled Phased Tests"); + + final Class l_testClass = TestNIE_Synchroneous.class; + myTest.setXmlClasses(Collections.singletonList(new XmlClass(l_testClass))); + + Phases.ASYNCHRONOUS.activate(); + ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.activate(NIESynchronousEventWithException.class.getTypeName()); + ConfigValueHandlerPhased.EVENT_TARGET.activate(TestNIE_Synchroneous.class.getTypeName() + "#step2"); + + myTestNG.run(); + + assertThat("We should be in non-interruptive mode shuffled", + !PhasedTestManager.isPhasedTestShuffledMode(l_testClass)); + + tla.getFailedTests().forEach(t -> System.out.println(t.getThrowable().getMessage())); + + assertThat("Only step 1 should have succeeded", + (int) tla.getPassedTests().stream().filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), + is(equalTo(3))); + + //Global + assertThat("We should have no failed tests", tla.getFailedTests().size(), equalTo(0)); + assertThat("We should have no skipped tests", tla.getSkippedTests().size(), equalTo(0)); + + assertThat("We should have the correct number of events in the logs (1 x phase groups)", + PhasedEventManager.getEventLogs().size(), + Matchers.equalTo(2)); + } + + /** + * This is a test for non-interruptive events in shuffled classes. Using the legacy annotations + */ + @Test + public void testNonInterruptive_33_Targetted_ExceptionInWaitForEventToFinish() { + NIESynchronousEventWithException.START_STEP_VALUE = 2; + NIESynchronousEventWithException.WTF_STEP_VALUE = 7; + NIESynchronousEventWithException.TDE_STEP_VALUE = 23; + NIESynchronousEventWithException.exceptionPlace = 2; + + //Reset + TestNIE_Synchroneous.testElement = 3; + + //The WTF should be finished before the start of step 2 + TestNIE_Synchroneous.expectedStep2Value = NIESynchronousEventWithException.START_STEP_VALUE; + + TestNIE_Synchroneous.expectedStep3Value = NIESynchronousEventWithException.TDE_STEP_VALUE; + TestNIE_Synchroneous.expectedStep3EndResult = TestNIE_Synchroneous.expectedStep2Value + TestNIE_Synchroneous.testElement; + + // Rampup + TestNG myTestNG = TestTools.createTestNG(); + TestListenerAdapter tla = TestTools.fetchTestResultsHandler(myTestNG); + + // Define suites + XmlSuite mySuite = TestTools.addSuitToTestNGTest(myTestNG, "Automated Suite Phased Testing"); + + // Add listeners + mySuite.addListener("com.adobe.campaign.tests.integro.phased.PhasedTestListener"); + + // Create an instance of XmlTest and assign a name for it. + XmlTest myTest = TestTools.attachTestToSuite(mySuite, "Test Shuffled Phased Tests"); + + final Class l_testClass = TestNIE_Synchroneous.class; + myTest.setXmlClasses(Collections.singletonList(new XmlClass(l_testClass))); + + Phases.ASYNCHRONOUS.activate(); + ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.activate(NIESynchronousEventWithException.class.getTypeName()); + ConfigValueHandlerPhased.EVENT_TARGET.activate(TestNIE_Synchroneous.class.getTypeName() + "#step2"); + + myTestNG.run(); + + assertThat("We should be in non-interruptive mode shuffled", + !PhasedTestManager.isPhasedTestShuffledMode(l_testClass)); + + tla.getFailedTests().forEach(t -> System.out.println(t.getThrowable().getMessage())); + + assertThat("Only step 1 should have succeeded", + (int) tla.getPassedTests().stream().filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), + is(equalTo(3))); + + //Global + assertThat("We should have no failed tests", tla.getFailedTests().size(), equalTo(0)); + assertThat("We should have no skipped tests", tla.getSkippedTests().size(), equalTo(0)); + + assertThat("We should have the correct number of events in the logs (1 x phase groups)", + PhasedEventManager.getEventLogs().size(), + Matchers.equalTo(2)); + } + + @Test + public void testNonInterruptive_33_Targetted_ExceptionInTearDown() { + NIESynchronousEventWithException.START_STEP_VALUE = 2; + NIESynchronousEventWithException.WTF_STEP_VALUE = 7; + NIESynchronousEventWithException.TDE_STEP_VALUE = 23; + NIESynchronousEventWithException.exceptionPlace = 3; + + //Reset + TestNIE_Synchroneous.testElement = 3; + + //The WTF should be finished before the start of step 2 + TestNIE_Synchroneous.expectedStep2Value = NIESynchronousEventWithException.START_STEP_VALUE; + + TestNIE_Synchroneous.expectedStep3Value = NIESynchronousEventWithException.TDE_STEP_VALUE; + TestNIE_Synchroneous.expectedStep3EndResult = TestNIE_Synchroneous.expectedStep2Value + TestNIE_Synchroneous.testElement; + + // Rampup + TestNG myTestNG = TestTools.createTestNG(); + TestListenerAdapter tla = TestTools.fetchTestResultsHandler(myTestNG); + + // Define suites + XmlSuite mySuite = TestTools.addSuitToTestNGTest(myTestNG, "Automated Suite Phased Testing"); + + // Add listeners + mySuite.addListener("com.adobe.campaign.tests.integro.phased.PhasedTestListener"); + + // Create an instance of XmlTest and assign a name for it. + XmlTest myTest = TestTools.attachTestToSuite(mySuite, "Test Shuffled Phased Tests"); + + final Class l_testClass = TestNIE_Synchroneous.class; + myTest.setXmlClasses(Collections.singletonList(new XmlClass(l_testClass))); + + Phases.ASYNCHRONOUS.activate(); + ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.activate(NIESynchronousEventWithException.class.getTypeName()); + ConfigValueHandlerPhased.EVENT_TARGET.activate(TestNIE_Synchroneous.class.getTypeName() + "#step2"); + + myTestNG.run(); + + assertThat("We should be in non-interruptive mode shuffled", + !PhasedTestManager.isPhasedTestShuffledMode(l_testClass)); + + tla.getFailedTests().forEach(t -> System.out.println(t.getThrowable().getMessage())); + + assertThat("Only step 1 should have succeeded", + (int) tla.getPassedTests().stream().filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), + is(equalTo(3))); + + //Global + assertThat("We should have no failed tests", tla.getFailedTests().size(), equalTo(0)); + assertThat("We should have no skipped tests", tla.getSkippedTests().size(), equalTo(0)); + + assertThat("We should have the correct number of events in the logs (1 x phase groups)", + PhasedEventManager.getEventLogs().size(), + Matchers.equalTo(2)); + } + + + /** + * This is a test for non-intyerruptive events in shuffled classes. Using the legacy annotations + */ + @Test + public void testNonInterruptive_23_Targeted() { + NIESynchronousEvent.START_STEP_VALUE = 2; + NIESynchronousEvent.WTF_STEP_VALUE = 7; + NIESynchronousEvent.TDE_STEP_VALUE = 23; + + //Reset + TestNIE_Synchroneous.testElement = 3; + + //The WTF should be finished before the start of step 2 + TestNIE_Synchroneous.expectedStep2Value = NIESynchronousEvent.WTF_STEP_VALUE; + + TestNIE_Synchroneous.expectedStep3Value = NIESynchronousEvent.TDE_STEP_VALUE; + TestNIE_Synchroneous.expectedStep3EndResult = NIESynchronousEvent.WTF_STEP_VALUE + TestNIE_Synchroneous.testElement; + + + // Rampup + TestNG myTestNG = TestTools.createTestNG(); + TestListenerAdapter tla = TestTools.fetchTestResultsHandler(myTestNG); + + // Define suites + XmlSuite mySuite = TestTools.addSuitToTestNGTest(myTestNG, "Automated Suite Phased Testing"); + + // Add listeners + mySuite.addListener("com.adobe.campaign.tests.integro.phased.PhasedTestListener"); + + // Create an instance of XmlTest and assign a name for it. + XmlTest myTest = TestTools.attachTestToSuite(mySuite, "Test Shuffled Phased Tests"); + + final Class l_testClass = TestNIE_Synchroneous.class; + myTest.setXmlClasses(Collections.singletonList(new XmlClass(l_testClass))); + + ExecutionMode.NON_INTERRUPTIVE.activate("23"); + ConfigValueHandlerPhased.EVENTS_NONINTERRUPTIVE.activate(NIESynchronousEvent.class.getTypeName()); + ConfigValueHandlerPhased.EVENT_TARGET.activate(TestNIE_Synchroneous.class.getTypeName() + "#step2"); + + myTestNG.run(); + + assertThat("We should be in non-interruptive mode shuffled", + !PhasedTestManager.isPhasedTestShuffledMode(l_testClass)); + + tla.getFailedTests().forEach(t -> System.out.println(t.getThrowable().getMessage())); + + assertThat("We should have 3 successful methods of phased Tests", + (int) tla.getPassedTests().stream().filter(m -> m.getInstance().getClass().equals(l_testClass)).count(), + is(equalTo(3))); + + //Global + assertThat("We should have no failed tests", tla.getFailedTests().size(), equalTo(0)); + assertThat("We should have no skipped tests", tla.getSkippedTests().size(), equalTo(0)); + + assertThat("We should have the correct number of events in the logs (1 x phase groups)", + PhasedEventManager.getEventLogs().size(), + Matchers.equalTo(2)); + } + + /** * This is a test for non-intyerruptive events in shuffled classes */ diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIEMutationalSynchronousEvent.java b/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIEMutationalSynchronousEvent.java new file mode 100644 index 0000000..83037da --- /dev/null +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIEMutationalSynchronousEvent.java @@ -0,0 +1,64 @@ +/* + * MIT License + * + * © Copyright 2020 Adobe. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.adobe.campaign.tests.integro.phased.data.events; + +import com.adobe.campaign.tests.integro.phased.NonInterruptiveEvent; +import com.adobe.campaign.tests.integro.phased.data.mutational.TestMutationalNIE_Synchroneous; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class NIEMutationalSynchronousEvent extends NonInterruptiveEvent { + public static int WAIT_TIME_MS = 1; + public static int START_STEP_VALUE = 3; + public static int WTF_STEP_VALUE = 13; + public static int TDE_STEP_VALUE = 11; + + private static final Logger log = LogManager.getLogger(); + + public NIEMutationalSynchronousEvent() { + } + + @Override + public boolean startEvent() { + try { + log.info("before sleep"); + Thread.sleep(WAIT_TIME_MS); + TestMutationalNIE_Synchroneous.testElement = START_STEP_VALUE; + } catch (InterruptedException e) { + e.printStackTrace(); + } + + log.info("started"); + return true; + } + + @Override + public boolean isFinished() { + return true; + } + + @Override + public boolean waitTillFinished() { + log.info("In WTF Setting synchronous value to {}", WTF_STEP_VALUE); + TestMutationalNIE_Synchroneous.testElement = WTF_STEP_VALUE; + return isFinished(); + } + + @Override + public boolean tearDownEvent() { + log.info("In TDE Setting synchronous value to {}", TDE_STEP_VALUE); + + TestMutationalNIE_Synchroneous.testElement = TDE_STEP_VALUE; + return true; + } + +} diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIEMutationalSynchronousEventWithException.java b/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIEMutationalSynchronousEventWithException.java new file mode 100644 index 0000000..080320e --- /dev/null +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIEMutationalSynchronousEventWithException.java @@ -0,0 +1,77 @@ +/* + * MIT License + * + * © Copyright 2020 Adobe. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.adobe.campaign.tests.integro.phased.data.events; + +import com.adobe.campaign.tests.integro.phased.NonInterruptiveEvent; +import com.adobe.campaign.tests.integro.phased.data.mutational.TestMutationalNIE_Synchroneous; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class NIEMutationalSynchronousEventWithException extends NonInterruptiveEvent { + public static int WAIT_TIME_MS = 1; + public static int START_STEP_VALUE = 3; + public static int WTF_STEP_VALUE = 13; + public static int TDE_STEP_VALUE = 11; + public static int exceptionPlace = 0; + + private static final Logger log = LogManager.getLogger(); + + public NIEMutationalSynchronousEventWithException() { + } + + @Override + public boolean startEvent() { + try { + log.info("before Exceptions"); + Thread.sleep(WAIT_TIME_MS); + TestMutationalNIE_Synchroneous.testElement = START_STEP_VALUE; + + if (exceptionPlace == 1) { + throw new RuntimeException("Exception in startEvent"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + log.info("started"); + return true; + } + + @Override + public boolean isFinished() { + return true; + } + + @Override + public boolean waitTillFinished() { + log.info("In WTF Setting synchronous value to {}", WTF_STEP_VALUE); + + if (exceptionPlace == 2) { + throw new RuntimeException("Exception in WTF"); + } + + TestMutationalNIE_Synchroneous.testElement = WTF_STEP_VALUE; + return isFinished(); + } + + @Override + public boolean tearDownEvent() { + TestMutationalNIE_Synchroneous.testElement = TDE_STEP_VALUE; + + if (exceptionPlace == 3) { + throw new RuntimeException("Exception in TDE"); + } + + return true; + } + +} diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIESynchronousEvent.java b/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIESynchronousEvent.java new file mode 100644 index 0000000..cf66366 --- /dev/null +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIESynchronousEvent.java @@ -0,0 +1,63 @@ +/* + * MIT License + * + * © Copyright 2020 Adobe. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.adobe.campaign.tests.integro.phased.data.events; + +import com.adobe.campaign.tests.integro.phased.NonInterruptiveEvent; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class NIESynchronousEvent extends NonInterruptiveEvent { + public static int WAIT_TIME_MS = 1; + public static int START_STEP_VALUE = 3; + public static int WTF_STEP_VALUE = 13; + public static int TDE_STEP_VALUE = 11; + + private static final Logger log = LogManager.getLogger(); + + public NIESynchronousEvent() { + } + + @Override + public boolean startEvent() { + try { + log.info("before sleep"); + Thread.sleep(WAIT_TIME_MS); + TestNIE_Synchroneous.testElement = START_STEP_VALUE; + } catch (InterruptedException e) { + e.printStackTrace(); + } + + log.info("started"); + return true; + } + + @Override + public boolean isFinished() { + return true; + } + + @Override + public boolean waitTillFinished() { + log.info("In WTF Setting synchronous value to {}", WTF_STEP_VALUE); + TestNIE_Synchroneous.testElement = WTF_STEP_VALUE; + return isFinished(); + } + + @Override + public boolean tearDownEvent() { + log.info("In TDE Setting synchronous value to {}", TDE_STEP_VALUE); + + TestNIE_Synchroneous.testElement = TDE_STEP_VALUE; + return true; + } + +} diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIESynchronousEventWithException.java b/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIESynchronousEventWithException.java new file mode 100644 index 0000000..1aa29d1 --- /dev/null +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/NIESynchronousEventWithException.java @@ -0,0 +1,76 @@ +/* + * MIT License + * + * © Copyright 2020 Adobe. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.adobe.campaign.tests.integro.phased.data.events; + +import com.adobe.campaign.tests.integro.phased.NonInterruptiveEvent; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class NIESynchronousEventWithException extends NonInterruptiveEvent { + public static int WAIT_TIME_MS = 1; + public static int START_STEP_VALUE = 3; + public static int WTF_STEP_VALUE = 13; + public static int TDE_STEP_VALUE = 11; + public static int exceptionPlace = 0; + + private static final Logger log = LogManager.getLogger(); + + public NIESynchronousEventWithException() { + } + + @Override + public boolean startEvent() { + try { + log.info("before Exceptions"); + Thread.sleep(WAIT_TIME_MS); + TestNIE_Synchroneous.testElement = START_STEP_VALUE; + + if (exceptionPlace == 1) { + throw new RuntimeException("Exception in startEvent"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + log.info("started"); + return true; + } + + @Override + public boolean isFinished() { + return true; + } + + @Override + public boolean waitTillFinished() { + log.info("In WTF Setting synchronous value to {}", WTF_STEP_VALUE); + + if (exceptionPlace == 2) { + throw new RuntimeException("Exception in WTF"); + } + + TestNIE_Synchroneous.testElement = WTF_STEP_VALUE; + return isFinished(); + } + + @Override + public boolean tearDownEvent() { + TestNIE_Synchroneous.testElement = TDE_STEP_VALUE; + + if (exceptionPlace == 3) { + throw new RuntimeException("Exception in TDE"); + } + + return true; + } + +} diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/TestNIE_Synchroneous.java b/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/TestNIE_Synchroneous.java new file mode 100644 index 0000000..2f5ec1f --- /dev/null +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/data/events/TestNIE_Synchroneous.java @@ -0,0 +1,51 @@ +/* + * MIT License + * + * © Copyright 2020 Adobe. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.adobe.campaign.tests.integro.phased.data.events; + +import com.adobe.campaign.tests.integro.phased.PhasedTest; +import com.adobe.campaign.tests.integro.phased.PhasedTestManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +@PhasedTest +@Test +public class TestNIE_Synchroneous { + public static int testElement = 3; + private static final Logger log = LogManager.getLogger(); + public static int expectedStep2Value = 3; + public static int expectedStep3Value = 3; + public static int expectedStep3EndResult = 3; + + public void step1(String val) { + log.info("step1 " + val); + PhasedTestManager.produceInStep(String.valueOf(testElement)); + } + + public void step2(String val) { + assertEquals(testElement, expectedStep2Value, "Step 2 assertion"); + log.info("step2 " + val+" - synchronous value is "+ testElement); + int l_fetchedValue = Integer.valueOf(PhasedTestManager.consumeFromStep("step1")); + PhasedTestManager.produceInStep(String.valueOf(l_fetchedValue + testElement)); + } + + public void step3(String val) { + assertEquals(testElement, expectedStep3Value, "Step 3 assertion"); + log.info("step3 " + val+" - synchronous value is "+ testElement); + String l_fetchedValue = PhasedTestManager.consumeFromStep("step2"); + + assertEquals(Integer.valueOf(l_fetchedValue), expectedStep3EndResult, "Step 3 assertion FULL"); + } + +} diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/data/mutational/TestMutationalNIE_Synchroneous.java b/src/test/java/com/adobe/campaign/tests/integro/phased/data/mutational/TestMutationalNIE_Synchroneous.java new file mode 100644 index 0000000..b9f1191 --- /dev/null +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/data/mutational/TestMutationalNIE_Synchroneous.java @@ -0,0 +1,51 @@ +/* + * MIT License + * + * © Copyright 2020 Adobe. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.adobe.campaign.tests.integro.phased.data.mutational; + +import com.adobe.campaign.tests.integro.phased.Mutational; +import com.adobe.campaign.tests.integro.phased.PhasedTest; +import com.adobe.campaign.tests.integro.phased.PhasedTestManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +@Test +public class TestMutationalNIE_Synchroneous extends Mutational { + public static int testElement = 3; + private static final Logger log = LogManager.getLogger(); + public static int expectedStep2Value = 3; + public static int expectedStep3Value = 3; + public static int expectedStep3EndResult = 3; + + public void step1(String val) { + log.info("step1 " + val); + PhasedTestManager.produceInStep(String.valueOf(testElement)); + } + + public void step2(String val) { + assertEquals(testElement, expectedStep2Value, "Step 2 assertion"); + log.info("step2 " + val+" - synchronous value is "+ testElement); + int l_fetchedValue = Integer.valueOf(PhasedTestManager.consumeFromStep("step1")); + PhasedTestManager.produceInStep(String.valueOf(l_fetchedValue + testElement)); + } + + public void step3(String val) { + assertEquals(testElement, expectedStep3Value, "Step 3 assertion"); + log.info("step3 " + val+" - synchronous value is "+ testElement); + String l_fetchedValue = PhasedTestManager.consumeFromStep("step2"); + + assertEquals(Integer.valueOf(l_fetchedValue), expectedStep3EndResult, "Step 3 assertion FULL"); + } + +} diff --git a/src/test/java/com/adobe/campaign/tests/integro/phased/mutational/data/ie/MutationalTestSingleRun.java b/src/test/java/com/adobe/campaign/tests/integro/phased/mutational/data/ie/MutationalTestSingleRun.java new file mode 100644 index 0000000..7f05916 --- /dev/null +++ b/src/test/java/com/adobe/campaign/tests/integro/phased/mutational/data/ie/MutationalTestSingleRun.java @@ -0,0 +1,30 @@ +/* + * Copyright 2022 Adobe + * All Rights Reserved. + * + * NOTICE: Adobe permits you to use, modify, and distribute this file in + * accordance with the terms of the Adobe license agreement accompanying + * it. + */ +package com.adobe.campaign.tests.integro.phased.mutational.data.ie; + +import com.adobe.campaign.tests.integro.phased.Mutational; +import com.adobe.campaign.tests.integro.phased.PhaseEvent; +import org.testng.annotations.Test; + +@Test +public class MutationalTestSingleRun extends Mutational { + public void step1(String a) { + + } + + + public void step2(String a) { + + } + + @PhaseEvent + public void step3(String a) { + + } +}