Skip to content

Commit

Permalink
Merge pull request #39 from ppi-ag/feature-persistence-matcher
Browse files Browse the repository at this point in the history
Feature persistence matcher + Post Processor
  • Loading branch information
rcschrg authored Feb 28, 2021
2 parents a346f52 + 83b3a8a commit f5b00bd
Show file tree
Hide file tree
Showing 17 changed files with 667 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2020 PPI AG (Hamburg, Germany)
* This program is made available under the terms of the MIT License.
*/

package de.ppi.deepsampler.core.api;

import de.ppi.deepsampler.core.model.ExecutionRepository;
import de.ppi.deepsampler.core.model.SampleRepository;

/**
* Provides functionality for influencing the execution phase of the stubbing done by deepsampler.
*
* @author Rico Schrage
*/
public class Execution {

private Execution() {
// static only
}

/**
* Makes deepsampler use a provided {@link SampleReturnProcessor} when returning arbitrary stubbed data.
*
* @param sampleReturnProcessor the sampleReturnProcessor
*/
public static void useGlobal(SampleReturnProcessor sampleReturnProcessor) {
ExecutionRepository.getInstance().addGlobalSampleReturnProcessor(sampleReturnProcessor);
}

/**
* Makes deepsampler use a provided {@link SampleReturnProcessor} when returning stubbed data defined by the last created sample.
*
* @param sampleReturnProcessor the sampleReturnProcessor
*/
public static void useForLastSample(SampleReturnProcessor sampleReturnProcessor) {
ExecutionRepository.getInstance().addSampleReturnProcessor(SampleRepository.getInstance().getLastSampleDefinition(), sampleReturnProcessor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2020 PPI AG (Hamburg, Germany)
* This program is made available under the terms of the MIT License.
*/

package de.ppi.deepsampler.core.api;

import de.ppi.deepsampler.core.model.SampleDefinition;
import de.ppi.deepsampler.core.model.StubMethodInvocation;

/**
* Hook-in processor for influencing the stubbing process.
*
* @author Rico Schrage
*/
@FunctionalInterface
public interface SampleReturnProcessor {
/**
* Will run after a stubbed method has been called.
*
* @param sampleDefinition the sampleDefinition which is responsible for the stubbing of the method call
* @param stubMethodInvocation the actual method call
* @param returnValue the real return value of the method call
*
* @return the new return value of the method call
*/
Object onReturn(final SampleDefinition sampleDefinition, final StubMethodInvocation stubMethodInvocation, final Object returnValue);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2020 PPI AG (Hamburg, Germany)
* This program is made available under the terms of the MIT License.
*/

package de.ppi.deepsampler.core.internal;

import de.ppi.deepsampler.core.model.ParameterMatcher;
import de.ppi.deepsampler.core.model.SampleDefinition;

import java.util.List;

public class SampleHandling {

private SampleHandling() {
//static only
}

@SuppressWarnings("unchecked")
public static boolean argumentsMatch(final SampleDefinition sampleDefinition, final Object[] arguments) {
final List<ParameterMatcher<?>> parameterMatchers = sampleDefinition.getParameterMatchers();

if (parameterMatchers.size() != arguments.length) {
return false;
}

for (int i = 0; i < arguments.length; i++) {
final ParameterMatcher<Object> parameterMatcher = (ParameterMatcher<Object>) parameterMatchers.get(i);
if (!parameterMatcher.matches(arguments[i])) {
return false;
}
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@

package de.ppi.deepsampler.core.internal.api;

import de.ppi.deepsampler.core.api.SampleReturnProcessor;
import de.ppi.deepsampler.core.model.*;

import java.util.ArrayList;
import java.util.List;

public class ExecutionManager {

private ExecutionManager() {
Expand All @@ -27,4 +31,28 @@ private static SampleExecutionInformation getSampleInformation(final SampleDefin
return executionInformation.getOrCreateBySample(sampleDefinition);
}

public static Object execute(final SampleDefinition sampleDefinition, final StubMethodInvocation stubMethodInvocation) throws Exception {
Object callReturnValue = null;
try {
callReturnValue = sampleDefinition.getAnswer().call(stubMethodInvocation);
} finally {
for (SampleReturnProcessor sampleReturnProcessor : getApplicableReturnProcessors(sampleDefinition)) {
callReturnValue = sampleReturnProcessor.onReturn(sampleDefinition, stubMethodInvocation, callReturnValue);
}
}
return callReturnValue;
}

private static List<SampleReturnProcessor> getApplicableReturnProcessors(final SampleDefinition sampleDefinition) {
final List<SampleReturnProcessor> allSampleReturnProcessors = new ArrayList<>();

final List<SampleReturnProcessor> globalSampleReturnProcessors = ExecutionRepository.getInstance().getGlobalProcessors();
final List<SampleReturnProcessor> localSampleReturnProcessors = ExecutionRepository.getInstance().getSampleReturnProcessorsFor(sampleDefinition);

allSampleReturnProcessors.addAll(globalSampleReturnProcessors);
allSampleReturnProcessors.addAll(localSampleReturnProcessors);

return allSampleReturnProcessors;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,55 @@

package de.ppi.deepsampler.core.model;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import de.ppi.deepsampler.core.api.SampleReturnProcessor;

import java.util.*;

public class ExecutionRepository {
private final ThreadLocal<Map<Class<?>, ExecutionInformation>> executionInformation = ThreadLocal.withInitial(HashMap::new);
private final Map<Class<?>, ExecutionInformation> executionInformation = new HashMap<>();
private final List<SampleReturnProcessor> globalProcessors = new ArrayList<>();
private final Map<SampleDefinition, List<SampleReturnProcessor>> sampleDefinitionSampleReturnProcessorMap = new HashMap<>();

private static ExecutionRepository myInstance;
private static final ThreadLocal<ExecutionRepository> myInstance = ThreadLocal.withInitial(ExecutionRepository::new);

/**
* Singleton Constructor.
*/
private ExecutionRepository() {}

public static synchronized ExecutionRepository getInstance() {
if (myInstance == null) {
myInstance = new ExecutionRepository();
}

return myInstance;
return myInstance.get();
}

public Map<Class<?>, ExecutionInformation> getAll() {
return Collections.unmodifiableMap(executionInformation.get());
return Collections.unmodifiableMap(executionInformation);
}

public ExecutionInformation getOrCreate(final Class<?> cls) {
return executionInformation.get().computeIfAbsent(cls, k -> new ExecutionInformation());
return executionInformation.computeIfAbsent(cls, k -> new ExecutionInformation());
}

public void addGlobalSampleReturnProcessor(SampleReturnProcessor sampleReturnProcessor) {
this.globalProcessors.add(sampleReturnProcessor);
}

public List<SampleReturnProcessor> getGlobalProcessors() {
return Collections.unmodifiableList(globalProcessors);
}

public void addSampleReturnProcessor(SampleDefinition sampleDefinition, SampleReturnProcessor sampleReturnProcessor) {
List<SampleReturnProcessor> sampleReturnProcessors = this.sampleDefinitionSampleReturnProcessorMap.computeIfAbsent(sampleDefinition, k -> new ArrayList<>());
sampleReturnProcessors.add(sampleReturnProcessor);
}

public List<SampleReturnProcessor> getSampleReturnProcessorsFor(SampleDefinition sampleDefinition) {
return sampleDefinitionSampleReturnProcessorMap.computeIfAbsent(sampleDefinition, k -> new ArrayList<>());
}

public void clear() {
executionInformation.get().clear();
executionInformation.remove();
executionInformation.clear();
globalProcessors.clear();
sampleDefinitionSampleReturnProcessorMap.clear();
myInstance.remove();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,15 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(sampledMethod.getMethod(), parameterValues, sampleId);
}

@Override
public String toString() {
return "SampleDefinition{" +
"sampledMethod=" + sampledMethod +
", parameterValues=" + parameterValues +
", parameterMatchers=" + parameterMatchers +
", answer=" + answer +
", sampleId='" + sampleId + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.util.List;
import java.util.Objects;

import static de.ppi.deepsampler.core.internal.SampleHandling.argumentsMatch;

public class SampleRepository {

private List<SampleDefinition> samples = new ArrayList<>();
Expand Down Expand Up @@ -105,25 +107,6 @@ && argumentsMatch(sampleDefinition, args)) {
return null;
}


@SuppressWarnings("unchecked")
private boolean argumentsMatch(final SampleDefinition sampleDefinition, final Object[] arguments) {
final List<ParameterMatcher<?>> parameterMatchers = sampleDefinition.getParameterMatchers();

if (parameterMatchers.size() != arguments.length) {
return false;
}

for (int i = 0; i < arguments.length; i++) {
final ParameterMatcher<Object> parameterMatcher = (ParameterMatcher<Object>) parameterMatchers.get(i);
if (!parameterMatcher.matches(arguments[i])) {
return false;
}
}

return true;
}

private void setCurrentSample(final SampleDefinition sampleDefinition) {
currentSample = sampleDefinition;
}
Expand Down Expand Up @@ -152,6 +135,14 @@ public void addCurrentParameterMatchers(ParameterMatcher<?> parameterMatcher) {
currentParameterMatchers.add(parameterMatcher);
}

public void setCurrentParameterMatchers(ParameterMatcher<?> parameterMatcher) {
currentParameterMatchers.set(currentParameterMatchers.size() - 1, parameterMatcher);
}

public ParameterMatcher<?> getLastParameterMatcher() {
return currentParameterMatchers.get(currentParameterMatchers.size() - 1);
}

public List<ParameterMatcher<?>> getCurrentParameterMatchers() {
return currentParameterMatchers;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package de.ppi.deepsampler.core.api;

import de.ppi.deepsampler.core.model.ExecutionRepository;
import de.ppi.deepsampler.core.model.SampleDefinition;
import de.ppi.deepsampler.core.model.SampleRepository;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class ExecutionTest {

@Test
void useGlobal() {
// GIVEN
SampleReturnProcessor sampleReturnProcessor = (a, b, c) -> null;

// WHEN
Execution.useGlobal(sampleReturnProcessor);

// THEN
assertEquals(sampleReturnProcessor, ExecutionRepository.getInstance().getGlobalProcessors().get(0));
}

@Test
void useForLastSample() {
// GIVEN
final SampleDefinition sdSampler = Sampler.prepare(SampleDefinition.class);
Sample.of(sdSampler.getSampleId()).is("");
final SampleReturnProcessor sampleReturnProcessor = (a, b, c) -> null;

// WHEN
Execution.useForLastSample(sampleReturnProcessor);

// THEN
assertEquals(sampleReturnProcessor, ExecutionRepository.getInstance().getSampleReturnProcessorsFor(SampleRepository.getInstance().getLastSampleDefinition()).get(0));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2020 PPI AG (Hamburg, Germany)
* This program is made available under the terms of the MIT License.
*/

package de.ppi.deepsampler.persistence.api;

import de.ppi.deepsampler.core.model.ParameterMatcher;

/**
* Special matcher to apply a {@link ParameterMatcher} <b>and</b> a {@link PersistentMatcher} to a single argument when defining a sample.
* <br>
* This class will basically behave exactly like the given {@link ParameterMatcher}, but additionally it will hold a {@link PersistentMatcher} to
* retrieve it later in the loading process of persistent samples.
* <br>
* <b>Never create {@link ComboMatcher} yourself! Always use {@link PersistentMatchers#combo(Object, PersistentMatcher)} for this.</b>
*
* @param <T> type to get matched in some sense
* @author Rico Schrage
*/
public class ComboMatcher<T> implements ParameterMatcher<T> {

private final PersistentMatcher<T> persistentMatcher;
private final ParameterMatcher<T> parameterMatcher;

/**
* Create a ComboMatcher with the parameterMatcher to be imitated and the persistentMatcher to hold for the later creating of a real matcher
* in the proces of loading persistent samples.
*
* @param parameterMatcher the {@link ParameterMatcher} to imitate
* @param persistentMatcher the {@link PersistentMatcher} to hold
*/
ComboMatcher(ParameterMatcher<T> parameterMatcher, PersistentMatcher<T> persistentMatcher) {
this.parameterMatcher = parameterMatcher;
this.persistentMatcher = persistentMatcher;
}

/**
* @return the hold {@link PersistentMatcher}
*/
public PersistentMatcher<T> getPersistentMatcher() {
return persistentMatcher;
}

/**
* @return the imitated {@link ParameterMatcher}
*/
public ParameterMatcher<T> getParameterMatcher() {
return parameterMatcher;
}

@Override
public boolean matches(T parameter) {
return parameterMatcher.matches(parameter);
}
}
Loading

0 comments on commit f5b00bd

Please sign in to comment.