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

net.amygdalum.testrecorder.MockedInteractions Maven / Gradle / Ivy

package net.amygdalum.testrecorder;

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import static net.amygdalum.testrecorder.deserializers.Templates.assignLocalVariableStatement;
import static net.amygdalum.testrecorder.deserializers.Templates.callLocalMethod;
import static net.amygdalum.testrecorder.deserializers.Templates.callMethod;
import static net.amygdalum.testrecorder.deserializers.Templates.callMethodChainExpression;
import static net.amygdalum.testrecorder.deserializers.Templates.callMethodStatement;
import static net.amygdalum.testrecorder.deserializers.Templates.methodDeclaration;
import static net.amygdalum.testrecorder.deserializers.Templates.newAnonymousClassInstance;
import static net.amygdalum.testrecorder.deserializers.Templates.returnStatement;
import static net.amygdalum.testrecorder.types.Computation.variable;

import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import net.amygdalum.testrecorder.deserializers.Templates;
import net.amygdalum.testrecorder.fakeio.FakeIO;
import net.amygdalum.testrecorder.runtime.Aspect;
import net.amygdalum.testrecorder.types.Computation;
import net.amygdalum.testrecorder.types.DeserializationException;
import net.amygdalum.testrecorder.types.RoleVisitor;
import net.amygdalum.testrecorder.types.DeserializerContext;
import net.amygdalum.testrecorder.types.LocalVariableNameGenerator;
import net.amygdalum.testrecorder.types.SerializedInput;
import net.amygdalum.testrecorder.types.SerializedInteraction;
import net.amygdalum.testrecorder.types.SerializedOutput;
import net.amygdalum.testrecorder.types.SerializedValue;
import net.amygdalum.testrecorder.types.TypeManager;
import net.amygdalum.testrecorder.util.Literals;
import net.amygdalum.testrecorder.util.Types;

public class MockedInteractions {

	public static final MockedInteractions NONE = new MockedInteractions(null, null, emptyList(), emptyList());

	private RoleVisitor setup;
	private RoleVisitor matcher;

	private Collection input;
	private Collection output;

	private List fakeClassVariables;

	public MockedInteractions(RoleVisitor setup, RoleVisitor matcher, Collection setupInput, Collection expectOutput) {
		this.setup = setup;
		this.matcher = matcher;
		this.input = setupInput;
		this.output = expectOutput;

		this.fakeClassVariables = new ArrayList<>();
	}

	public List prepare(DeserializerContext context) {
		if (setup == null || matcher == null) {
			return emptyList();
		} else if (input.isEmpty() && output.isEmpty()) {
			return emptyList();
		}
		LocalVariableNameGenerator locals = context.getLocals();
		TypeManager types = context.getTypes();

		types.registerTypes(FakeIO.class, Aspect.class);

		Map, List> ioByClass = Stream.concat(input.stream(), output.stream())
			.collect(groupingBy(SerializedInteraction::getDeclaringClass));

		List statements = new ArrayList<>();

		String fakeIOType = types.getRawTypeName(FakeIO.class);
		for (Map.Entry, List> entry : ioByClass.entrySet()) {
			Class clazz = entry.getKey();
			List interactions = entry.getValue();

			String[] fakeArgs = new String[] { types.getRawClass(clazz) };
			if (isProxy(clazz)) {
				fakeArgs = interactions.stream()
					.map(interaction -> context.resolve(interaction.getId()))
					.filter(Optional::isPresent)
					.map(value -> value.get().accept(setup))
					.peek(computation -> statements.addAll(computation.getStatements()))
					.map(computation -> computation.getValue())
					.toArray(String[]::new);
			}
			String val = callMethod(fakeIOType, "fake", fakeArgs);

			Map> interactionsByAspect = new LinkedHashMap<>();
			for (SerializedInteraction interaction : interactions) {
				LocalVariableNameGenerator methodParams = new LocalVariableNameGenerator();
				String dummyBody = interaction.getResultType() == void.class
					? ""
					: returnStatement(nullValue(interaction.getResultType()));
				String method = methodDeclaration("public",
					types.getRawTypeName(interaction.getResultType()),
					interaction.getMethodName(),
					Arrays.stream(interaction.getArgumentTypes())
						.map(type -> Templates.param(types.getRawTypeName(type), methodParams.fetchName(type)))
						.collect(toList()),
					dummyBody);

				String aspect = newAnonymousClassInstance(types.getRawTypeName(Aspect.class), emptyList(), method);

				String fake = callLocalMethod(fakeMethod(interaction), aspect);

				List aspectInteractions = interactionsByAspect.computeIfAbsent(fake, key -> new ArrayList<>());
				aspectInteractions.add(interaction);
			}

			List methods = new ArrayList<>();
			for (Map.Entry> aspectInteractions : interactionsByAspect.entrySet()) {
				String aspect = aspectInteractions.getKey();
				methods.add(aspect);
				for (SerializedInteraction interaction : aspectInteractions.getValue()) {
					RoleVisitor deserializer = deserializerFor(interaction, setup, matcher);
					List arguments = new ArrayList<>();

					if (!interaction.isStatic()) {
						Optional value = context.resolve(interaction.getId());
						if (value.isPresent()) {
							Computation selfComputation = value.get().accept(setup);
							statements.addAll(selfComputation.getStatements());
							arguments.add(selfComputation.getValue());
						} else {
							arguments.add(Literals.asLiteral(interaction.getId()));
						}
					}

					if (interaction.hasResult()) {
						Computation resultComputation = interaction.getResult().accept(setup);
						statements.addAll(resultComputation.getStatements());
						arguments.add(resultComputation.getValue());
					} else {
						Computation resultComputation = variable("null", interaction.getResultType());
						arguments.add(resultComputation.getValue());
					}

					List argumentsComputation = Arrays.stream(interaction.getArguments())
						.map(argument -> argument.accept(deserializer))
						.collect(toList());
					argumentsComputation.forEach(argumentComputation -> {
						statements.addAll(argumentComputation.getStatements());
						arguments.add(argumentComputation.getValue());
					});

					if (interaction.isStatic()) {
						methods.add(callLocalMethod("addStatic", arguments));
					} else if (context.resolve(interaction.getId()).isPresent()) {
						methods.add(callLocalMethod("addVirtual", arguments));
					} else {
						methods.add(callLocalMethod("addFreeVirtual", arguments));
					}
				}
			}
			val = callMethodChainExpression(val, methods);

			String fakeClassVariable = locals.fetchName(clazz);
			fakeClassVariables.add(fakeClassVariable);
			statements.add(assignLocalVariableStatement(fakeIOType, fakeClassVariable, callMethod(val, "setup")));
		}

		return statements;
	}

	private boolean isProxy(Class clazz) {
		return Proxy.isProxyClass(clazz)
			|| clazz == Proxy.class;
	}

	private RoleVisitor deserializerFor(SerializedInteraction interaction, RoleVisitor setup, RoleVisitor matcher) {
		if (interaction instanceof SerializedInput) {
			return setup;
		} else if (interaction instanceof SerializedOutput) {
			return matcher;
		} else {
			throw new DeserializationException("unknown faking: " + interaction.getClass());
		}
	}

	private String fakeMethod(SerializedInteraction interaction) {
		if (interaction instanceof SerializedInput) {
			return "fakeInput";
		} else if (interaction instanceof SerializedOutput) {
			return "fakeOutput";
		} else {
			throw new DeserializationException("unknown faking: " + interaction.getClass());
		}
	}

	private String nullValue(Type type) {
		if (!Types.isPrimitive(type)) {
			return "null";
		} else if (type == boolean.class) {
			return "false";
		} else if (type == char.class) {
			return "(char) 0";
		} else if (type == byte.class || type == short.class || type == int.class || type == long.class) {
			return "0";
		} else if (type == float.class || type == double.class) {
			return "0.0f";
		} else {
			return "null";
		}
	}

	public List verify(LocalVariableNameGenerator locals, TypeManager types, DeserializerContext context) {
		List statements = new ArrayList<>();
		for (String fakeClassVariable : fakeClassVariables) {
			statements.add(callMethodStatement(fakeClassVariable, "verify"));
		}
		return statements;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy