All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.ppi.deepsampler.persistence.api.PersistentSampleManager Maven / Gradle / Ivy

There is a newer version: 2.1.0
Show newest version
/*
 * Copyright 2022 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.api.Matchers;
import de.ppi.deepsampler.core.error.NoMatchingParametersFoundException;
import de.ppi.deepsampler.core.internal.SampleHandling;
import de.ppi.deepsampler.core.model.*;
import de.ppi.deepsampler.persistence.PersistentSamplerContext;
import de.ppi.deepsampler.persistence.bean.PolymorphicPersistentBean;
import de.ppi.deepsampler.persistence.bean.ReflectionTools;
import de.ppi.deepsampler.persistence.bean.ext.BeanConverterExtension;
import de.ppi.deepsampler.persistence.error.PersistenceException;
import de.ppi.deepsampler.persistence.error.NoMatchingSamplerFoundException;
import de.ppi.deepsampler.persistence.model.PersistentMethodCall;
import de.ppi.deepsampler.persistence.model.PersistentModel;
import de.ppi.deepsampler.persistence.model.PersistentSampleMethod;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

/**
 * The {@link PersistentSampleManager} is used to handle any provided {@link SourceManager} and to act
 * as a bridge between this manager and the DeepSampler repositories.
 */
public class PersistentSampleManager {
    private final List sourceManagerList = new ArrayList<>();
    private final PersistentSamplerContext persistentSamplerContext = new PersistentSamplerContext();

    public PersistentSampleManager(final SourceManager sourceManager) {
        addSourceProvider(sourceManager);
    }

    /**
     * Add another {@link SourceManager} to this builder.
     *
     * @param sourceManager the {@link SourceManager}
     * @return this
     */
    public PersistentSampleManager addSource(final SourceManager sourceManager) {
        addSourceProvider(sourceManager);
        return this;
    }

    /**
     * Add a {@link BeanConverterExtension} to the sample manager.
     *
     * @param beanConverterExtension {@link BeanConverterExtension}
     * @return this
     */
    public PersistentSampleManager addBeanExtension(final BeanConverterExtension beanConverterExtension) {
        persistentSamplerContext.addBeanConverterExtension(beanConverterExtension);
        return this;
    }

    /**
     * End of chain method: call {@link SourceManager#save(Map, PersistentSamplerContext)} on all added {@link SourceManager}s.
     */
    public void recordSamples() {
        for (final SourceManager sourceManager : sourceManagerList) {
            sourceManager.save(ExecutionRepository.getInstance().getAll(), persistentSamplerContext);
        }
    }

    /**
     * End of chain method: call {@link SourceManager#load()} on all {@link SourceManager}s and write
     * all loaded samples to the DeepSampler repositories.
     */
    public void load() {
        for (final SourceManager sourceManager : sourceManagerList) {
            final PersistentModel persistentModel = sourceManager.load();

            mergeSamplesFromPersistenceIntoSampleRepository(persistentModel);
        }

        if (SampleRepository.getInstance().isEmpty()) {
            throw new PersistenceException("No Samples from the file could be matched to predefined sampled methods. " +
                    "Did you define sampled methods using Sample.of() in your test?");
        }
    }

    /**
     * This method merges the samples from the persistence (e.g. JSON-File) into manually defined Samplers and Samples. The order of the
     * Samplers is defined by the Samplers in the test class or the compound. Samples from the file will be inserted in the list of Samples
     * at the position where the matching samplers have been defined.
     * 

* E.G. Someone could now first define a matcher that matches only on a particular parameter of the value * "Picard". The second matcher could then by anyString(). The first Sample would then be used only if the correct parameter is supplied and in all * other cases the second sampler would be used. * * @param persistentSamples Contains the Samples from the persistence i.e. JSON */ private void mergeSamplesFromPersistenceIntoSampleRepository(final PersistentModel persistentSamples) { SampleRepository sampleRepository = SampleRepository.getInstance(); List originallyDefinedSamplers = new ArrayList<>(sampleRepository.getSamples()); // This is a Set of all SampleIds from the persistence. Everytime, when a SampleId could be matched to a Sampler from the SampleRepository, // the SampleId will be removed from this Set. The remaining SampleIds are unmatched and will be reported by an Exception. Set unusedPersistentSampleIds = getPersistentSampleIds(persistentSamples); for (int i = 0; i < sampleRepository.size(); i++) { SampleDefinition sampler = sampleRepository.get(i); if (!sampler.isMarkedForPersistence()) { continue; } List mergedPersistentSamples = createSampleDefinitionForEachPersistentSample(persistentSamples, sampler); sampleRepository.replace(i, mergedPersistentSamples); i += mergedPersistentSamples.size() - 1; unusedPersistentSampleIds = filterUsedSampleIds(unusedPersistentSampleIds, mergedPersistentSamples); } if (!unusedPersistentSampleIds.isEmpty()) { throw new NoMatchingSamplerFoundException(unusedPersistentSampleIds, originallyDefinedSamplers); } } private Set filterUsedSampleIds(Set unusedPersistentSampleIds, List mergedPersistentCalls) { List mergedSampleIds = mergedPersistentCalls.stream().map(SampleDefinition::getSampleId).collect(Collectors.toList()); return unusedPersistentSampleIds.stream() .filter(s -> !mergedSampleIds.contains(s)) .collect(Collectors.toSet()); } private Set getPersistentSampleIds(PersistentModel persistentSamples) { return persistentSamples.getSampleMethodToSampleMap().keySet().stream() .map(PersistentSampleMethod::getSampleMethodId) .collect(Collectors.toSet()); } private List createSampleDefinitionForEachPersistentSample(PersistentModel persistentSamples, SampleDefinition sampler) { List usedPersistentCalls = new ArrayList<>(); List unusedPersistentCalls = new ArrayList<>(); for (PersistentSampleMethod persistentSampleMethod : persistentSamples.getSampleMethodToSampleMap().keySet()) { if (persistentSampleMethod.getSampleMethodId().equals(sampler.getSampleId())) { List calls = persistentSamples.getSampleMethodToSampleMap().get(persistentSampleMethod).getAllCalls(); for (PersistentMethodCall call : calls) { SampleDefinition combinedSampleDefinition = combinePersistentSampleAndDefinedSampler(sampler, persistentSampleMethod, call); // We use the parameter values from combinedSampleDefinition because combinePersistentSampleAndDefinedSampler() unwraps the // parameters from persistence containers. Object[] actualParameterValues = combinedSampleDefinition.getParameterValues().toArray(); unusedPersistentCalls.add(combinedSampleDefinition); if (SampleHandling.argumentsMatch(sampler, actualParameterValues)) { usedPersistentCalls.add(combinedSampleDefinition); unusedPersistentCalls.remove(combinedSampleDefinition); } } } } if (!unusedPersistentCalls.isEmpty()) { throw new NoMatchingParametersFoundException(unusedPersistentCalls); } return usedPersistentCalls; } private SampleDefinition combinePersistentSampleAndDefinedSampler(final SampleDefinition matchingSample, final PersistentSampleMethod persistentSampleMethod, final PersistentMethodCall call) { final List parameterEnvelopes = call.getPersistentParameter().getParameter(); final Object returnValueEnvelope = call.getPersistentReturnValue(); final Class returnClass; final SampledMethod sampledMethod = matchingSample.getSampledMethod(); if (returnValueEnvelope instanceof PolymorphicPersistentBean) { returnClass = ReflectionTools.getOriginalClassFromPolymorphicPersistentBean((PolymorphicPersistentBean) returnValueEnvelope); } else { returnClass = sampledMethod.getMethod().getReturnType(); } final Type[] parameterTypes = sampledMethod.getMethod().getGenericParameterTypes(); final Type genericReturnType = sampledMethod.getMethod().getGenericReturnType(); final ParameterizedType parameterizedReturnType = genericReturnType instanceof ParameterizedType ? (ParameterizedType) genericReturnType : null; final String joinPointId = persistentSampleMethod.getSampleMethodId(); final List parameterValues = unwrapValue(joinPointId, parameterTypes, parameterEnvelopes); final List> parameterMatchers = toMatcher(parameterValues, matchingSample.getParameterMatchers()); final SampleDefinition sample = new SampleDefinition(sampledMethod); sample.setSampleId(joinPointId); sample.setParameterMatchers(parameterMatchers); sample.setParameterValues(parameterValues); final Object returnValue = unwrapValue(returnClass, parameterizedReturnType, returnValueEnvelope); sample.setAnswer(invocation -> returnValue); return sample; } private List unwrapValue(final String id, final Type[] parameterTypes, final List parameterPersistentBeans) { final List params = new ArrayList<>(); if (parameterTypes.length != parameterPersistentBeans.size()) { throw new PersistenceException("The number of parameters from the method of %s does " + "not match the number of persistent parameters (%s:%s)!", id, parameterTypes, parameterPersistentBeans); } for (int i = 0; i < parameterPersistentBeans.size(); ++i) { final ParameterizedType parameterType = parameterTypes[i] instanceof ParameterizedType ? (ParameterizedType) parameterTypes[i] : null; final Class parameterClass = ReflectionTools.getClass(parameterTypes[i]); final Object persistentBean = parameterPersistentBeans.get(i); params.add(unwrapValue(parameterClass, parameterType, persistentBean)); } return params; } private Object unwrapValue(final Class targetClass, final ParameterizedType type, final Object persistentBean) { return persistentSamplerContext.getPersistentBeanConverter().revert(persistentBean, targetClass, type); } @SuppressWarnings("unchecked") private List> toMatcher(final List params, List> parameterMatchers) { List> resultingParameterMatcher = new ArrayList<>(); for (int i = 0; i < params.size(); ++i) { Object param = params.get(i); ParameterMatcher parameterMatcher = parameterMatchers.get(i); if (parameterMatcher instanceof ComboMatcher) { resultingParameterMatcher.add(s -> ((ComboMatcher) parameterMatcher).getPersistentMatcher().matches(s, param)); } else { resultingParameterMatcher.add(new Matchers.EqualsMatcher<>(param)); } } return resultingParameterMatcher; } private void addSourceProvider(final SourceManager sourceManager) { this.sourceManagerList.add(sourceManager); } }