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

guru.qas.martini.runtime.harness.MartiniCallable Maven / Gradle / Ivy

There is a newer version: 6.0-JDK12
Show newest version
/*
Copyright 2018 Penny Rohr Curich

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package guru.qas.martini.runtime.harness;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.core.convert.ConversionService;

import gherkin.ast.Examples;
import gherkin.ast.ScenarioDefinition;
import gherkin.ast.ScenarioOutline;
import gherkin.ast.Step;
import gherkin.ast.TableCell;
import gherkin.ast.TableRow;
import guru.qas.martini.Martini;
import guru.qas.martini.event.Status;
import guru.qas.martini.event.SuiteIdentifier;
import guru.qas.martini.gherkin.Recipe;
import guru.qas.martini.result.DefaultMartiniResult;
import guru.qas.martini.result.DefaultStepResult;
import guru.qas.martini.result.MartiniResult;
import guru.qas.martini.runtime.event.EventManager;
import guru.qas.martini.step.StepImplementation;
import guru.qas.martini.step.UnimplementedStepException;
import guru.qas.martini.tag.Categories;

import static com.google.common.base.Preconditions.*;

@SuppressWarnings({"WeakerAccess", "unused"})
@Configurable
public class MartiniCallable implements Callable {

	private static final Logger LOGGER = LoggerFactory.getLogger(MartiniCallable.class);
	private static final Pattern OUTLINE_PATTERN = Pattern.compile("^<(.*)>$");

	private BeanFactory beanFactory;
	private EventManager eventManager;
	private ConversionService conversionService;
	private Categories categories;

	@Autowired
	void set(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}

	@Autowired
	void set(EventManager eventManager) {
		this.eventManager = eventManager;
	}

	@Autowired
	void set(ConversionService conversionService) {
		this.conversionService = conversionService;
	}

	@Autowired
	void set(Categories categories) {
		this.categories = categories;
	}

	private final SuiteIdentifier suiteIdentifier;
	private final Martini martini;

	public MartiniCallable(
		SuiteIdentifier suiteIdentifier,
		Martini martini
	) {
		this.suiteIdentifier = checkNotNull(suiteIdentifier, "null SuiteIdentifier");
		this.martini = checkNotNull(martini, "null Martini");
	}

	@Override
	public MartiniResult call() {
		LOGGER.info("executing scenario {}", martini.getId());
		Thread thread = Thread.currentThread();
		Set categorizations = categories.getCategorizations(martini);

		DefaultMartiniResult result = DefaultMartiniResult.builder()
			.setThreadGroupName(thread.getThreadGroup().getName())
			.setThreadName(thread.getName())
			.setCategorizations(categorizations)
			.setMartini(martini)
			.setMartiniSuiteIdentifier(suiteIdentifier)
			.build();

		eventManager.publishBeforeScenario(this, result);

		try {
			Map stepIndex = martini.getStepIndex();
			result.setStartTimestamp(System.currentTimeMillis());

			DefaultStepResult lastResult = null;
			for (Map.Entry mapEntry : stepIndex.entrySet()) {

				Step step = mapEntry.getKey();
				eventManager.publishBeforeStep(this, result);

				StepImplementation implementation = mapEntry.getValue();
				if (null == lastResult || Status.PASSED == lastResult.getStatus()) {
					lastResult = execute(step, implementation);
				}
				else {
					lastResult = new DefaultStepResult(step, implementation);
					lastResult.setStatus(Status.SKIPPED);
				}
				result.add(lastResult);
				eventManager.publishAfterStep(this, result);
			}
		}
		finally {
			result.setEndTimestamp(System.currentTimeMillis());
			eventManager.publishAfterScenario(this, result);
		}

		return result;
	}

	protected DefaultStepResult execute(Step step, StepImplementation implementation) {
		LOGGER.info("executing @{} {}", step.getKeyword().trim(), step.getText().trim());

		DefaultStepResult result = new DefaultStepResult(step, implementation);
		result.setStartTimestamp(System.currentTimeMillis());
		try {
			Method method = implementation.getMethod();
			if (null == method) {
				Recipe recipe = martini.getRecipe();
				throw UnimplementedStepException.builder().setRecipe(recipe).setStep(step).build();
			}

			Object bean = getBean(method);
			Object[] arguments = getArguments(step, method, implementation);
			Object o = execute(method, bean, arguments);

			if (HttpEntity.class.isInstance(o)) {
				result.add(HttpEntity.class.cast(o));
			}
			result.setStatus(Status.PASSED);
		}
		catch (UnimplementedStepException e) {
			result.setException(e);
			result.setStatus(Status.SKIPPED);
		}
		catch (Exception e) {
			result.setException(e);
			result.setStatus(Status.FAILED);
		}
		finally {
			result.setEndTimestamp(System.currentTimeMillis());
		}
		return result;
	}

	protected Object[] getArguments(Step step, Method method, StepImplementation implementation) {
		Parameter[] parameters = method.getParameters();
		Object[] arguments = new Object[parameters.length];

		Map exampleValues = null;

		Recipe recipe = martini.getRecipe();
		ScenarioDefinition definition = recipe.getScenarioDefinition();
		if (ScenarioOutline.class.isInstance(definition)) {
			exampleValues = new HashMap<>();
			ScenarioOutline outline = ScenarioOutline.class.cast(definition);

			int exampleLine = recipe.getLocation().getLine();

			List examples = outline.getExamples();
			TableRow header = null;
			TableRow match = null;
			for (Iterator i = examples.iterator(); null == match && i.hasNext(); ) {
				Examples nextExamples = i.next();
				List rows = nextExamples.getTableBody();
				for (Iterator j = rows.iterator(); null == match && j.hasNext(); ) {
					TableRow row = j.next();
					if (row.getLocation().getLine() == exampleLine) {
						match = row;
						header = nextExamples.getTableHeader();
					}
				}
			}

			checkState(null != header, "unable to locate matching Examples table");
			List headerCells = header.getCells();
			List rowCells = match.getCells();
			checkState(headerCells.size() == rowCells.size(), "Examples header to row size mismatch");
			for (int i = 0; i < headerCells.size(); i++) {
				String headerValue = headerCells.get(i).getValue();
				String rowValue = rowCells.get(i).getValue();
				exampleValues.put(headerValue, rowValue);
			}
		}

		if (parameters.length > 0) {
			String text = step.getText();
			Pattern pattern = implementation.getPattern();
			Matcher matcher = pattern.matcher(text);
			checkState(matcher.find(),
				"unable to locate substitution parameters for pattern %s with input %s", pattern.pattern(), text);

			int groupCount = matcher.groupCount();
			for (int i = 0; i < groupCount; i++) {
				String parameterAsString = matcher.group(i + 1);
				Parameter parameter = parameters[i];
				Class parameterType = parameter.getType();

				Object converted;
				if (null == exampleValues) {
					converted = conversionService.convert(parameterAsString, parameterType);
				}
				else {
					Matcher tableMatcher = OUTLINE_PATTERN.matcher(parameterAsString);
					checkState(tableMatcher.find(), "Example table keys must be in the format ");
					String key = tableMatcher.group(1);
					String tableValue = exampleValues.get(key);
					converted = conversionService.convert(tableValue, parameterType);
				}

				arguments[i] = converted;
			}
		}

		return arguments;
	}

	protected Object getBean(Method method) {
		Class declaringClass = method.getDeclaringClass();
		return beanFactory.getBean(declaringClass);
	}

	protected Object execute(
		Method method,
		Object bean,
		Object[] arguments
	) throws InvocationTargetException, IllegalAccessException {
		return method.invoke(bean, arguments);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy