com.intuit.karate.Runner Maven / Gradle / Ivy
The newest version!
/*
* The MIT License
*
* Copyright 2022 Karate Labs Inc.
*
* 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.intuit.karate;
import com.intuit.karate.core.Feature;
import com.intuit.karate.core.FeatureCall;
import com.intuit.karate.core.FeatureResult;
import com.intuit.karate.core.FeatureRuntime;
import com.intuit.karate.core.RuntimeHookFactory;
import com.intuit.karate.core.ScenarioCall;
import com.intuit.karate.driver.DriverOptions;
import com.intuit.karate.driver.DriverRunner;
import com.intuit.karate.http.HttpClientFactory;
import com.intuit.karate.report.SuiteReports;
import com.intuit.karate.resource.ResourceUtils;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
import org.slf4j.LoggerFactory;
/**
*
* @author pthomas3
*/
public class Runner {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(Runner.class);
public static Map runFeature(FeatureCall feature, Map vars, boolean evalKarateConfig) {
Suite suite = new Suite();
FeatureRuntime featureRuntime = FeatureRuntime.of(suite, feature, vars);
featureRuntime.caller.setKarateConfigDisabled(!evalKarateConfig);
featureRuntime.run();
FeatureResult result = featureRuntime.result;
if (result.isFailed()) {
throw result.getErrorMessagesCombined();
}
return result.getVariables();
}
public static Map runFeature(File file, Map vars, boolean evalKarateConfig) {
Feature feature = Feature.read(file);
return runFeature(new FeatureCall(feature), vars, evalKarateConfig);
}
public static Map runFeature(Class relativeTo, String path, Map vars, boolean evalKarateConfig) {
File file = ResourceUtils.getFileRelativeTo(relativeTo, path);
return runFeature(file, vars, evalKarateConfig);
}
public static Map runFeature(String path, Map vars, boolean evalKarateConfig) {
FeatureCall feature = FileUtils.parseFeatureAndCallTag(path);
return runFeature(feature, vars, evalKarateConfig);
}
// this is called by karate-gatling !
public static void callAsync(Runner.Builder builder, String path, Map arg, PerfHook perfHook) {
builder.features = Collections.emptyList(); // will skip expensive feature resolution in builder.resolveAll()
Suite suite = new Suite(builder);
FeatureCall feature = FileUtils.parseFeatureAndCallTag(path);
FeatureRuntime featureRuntime = FeatureRuntime.of(suite, feature, arg, perfHook);
featureRuntime.setNext(() -> perfHook.afterFeature(featureRuntime.result));
perfHook.submit(featureRuntime);
}
//==========================================================================
//
public static class Builder {
ClassLoader classLoader;
Class optionsClass;
String env;
File workingDir;
String buildDir;
String configDir;
int threadCount = 1;
int timeoutMinutes;
String reportDir;
String scenarioName;
List tags;
List paths;
List features;
String relativeTo;
final Collection hooks = new ArrayList();
RuntimeHookFactory hookFactory;
HttpClientFactory clientFactory;
boolean forTempUse;
boolean backupReportDir = true;
boolean outputHtmlReport = true;
boolean outputJunitXml;
boolean outputCucumberJson;
boolean dryRun;
boolean debugMode;
Map systemProperties;
Map callSingleCache;
Map callOnceCache;
SuiteReports suiteReports;
Map drivers;
// synchronize because the main user is karate-gatling
public synchronized Builder copy() {
Builder b = new Builder();
b.classLoader = classLoader;
b.optionsClass = optionsClass;
b.env = env;
b.workingDir = workingDir;
b.buildDir = buildDir;
b.configDir = configDir;
b.threadCount = threadCount;
b.timeoutMinutes = timeoutMinutes;
b.reportDir = reportDir;
b.scenarioName = scenarioName;
b.tags = tags;
b.paths = paths;
b.features = features;
b.relativeTo = relativeTo;
b.hooks.addAll(hooks); // final
b.hookFactory = hookFactory;
b.clientFactory = clientFactory;
b.forTempUse = forTempUse;
b.backupReportDir = backupReportDir;
b.outputHtmlReport = outputHtmlReport;
b.outputJunitXml = outputJunitXml;
b.outputCucumberJson = outputCucumberJson;
b.dryRun = dryRun;
b.debugMode = debugMode;
b.systemProperties = systemProperties;
b.callSingleCache = callSingleCache;
b.callOnceCache = callOnceCache;
b.suiteReports = suiteReports;
b.drivers = drivers;
return b;
}
public List resolveAll() {
if (classLoader == null) {
classLoader = Thread.currentThread().getContextClassLoader();
}
if (clientFactory == null) {
clientFactory = HttpClientFactory.DEFAULT;
}
if (systemProperties == null) {
systemProperties = new HashMap(System.getProperties());
} else {
Map temp = new HashMap(System.getProperties());
temp.putAll(systemProperties); // make sure user-specified takes precedence
systemProperties = temp;
}
// env
String tempOptions = StringUtils.trimToNull(systemProperties.get(Constants.KARATE_OPTIONS));
if (tempOptions != null) {
LOGGER.info("using system property '{}': {}", Constants.KARATE_OPTIONS, tempOptions);
Main ko = Main.parseKarateOptions(tempOptions);
if (ko.tags != null) {
tags = ko.tags;
}
if (ko.paths != null) {
paths = ko.paths;
}
if (ko.threads != threadCount) { // 1 by default
threadCount = ko.threads;
}
dryRun = ko.dryRun || dryRun;
}
String tempEnv = StringUtils.trimToNull(systemProperties.get(Constants.KARATE_ENV));
if (tempEnv != null) {
LOGGER.info("using system property '{}': {}", Constants.KARATE_ENV, tempEnv);
env = tempEnv;
} else if (env != null) {
LOGGER.info("karate.env is: '{}'", env);
}
// config dir
String tempConfig = StringUtils.trimToNull(systemProperties.get(Constants.KARATE_CONFIG_DIR));
if (tempConfig != null) {
LOGGER.info("using system property '{}': {}", Constants.KARATE_CONFIG_DIR, tempConfig);
configDir = tempConfig;
}
if (workingDir == null) {
workingDir = FileUtils.WORKING_DIR;
}
if (configDir == null) {
try {
ResourceUtils.getResource(workingDir, "classpath:karate-config.js");
configDir = "classpath:"; // default mode
} catch (Exception e) {
configDir = workingDir.getPath();
}
}
if (configDir.startsWith("file:") || configDir.startsWith("classpath:")) {
// all good
} else {
configDir = "file:" + configDir;
}
if (configDir.endsWith(":") || configDir.endsWith("/") || configDir.endsWith("\\")) {
// all good
} else {
configDir = configDir + File.separator;
}
if (buildDir == null) {
buildDir = FileUtils.getBuildDir();
}
if (reportDir == null) {
reportDir = buildDir + File.separator + Constants.KARATE_REPORTS;
}
// hooks
if (hookFactory != null) {
hook(hookFactory.create());
}
// features
if (features == null) {
if (paths != null && !paths.isEmpty()) {
if (relativeTo != null) {
paths = paths.stream().map(p -> {
if (p.startsWith("classpath:")) {
return p;
}
if (!p.endsWith(".feature")) {
p = p + ".feature";
}
return relativeTo + "/" + p;
}).collect(Collectors.toList());
}
} else if (relativeTo != null) {
paths = new ArrayList();
paths.add(relativeTo);
}
features = ResourceUtils.findFeatureFiles(workingDir, paths, scenarioName);
}
if (callSingleCache == null) {
callSingleCache = new HashMap();
}
if (callOnceCache == null) {
callOnceCache = new HashMap();
}
if (suiteReports == null) {
suiteReports = SuiteReports.DEFAULT;
}
if (drivers != null) {
Map customDrivers = drivers;
drivers = DriverOptions.driverRunners();
drivers.putAll(customDrivers); // allows override of Karate drivers (e.g. custom 'chrome')
} else {
drivers = DriverOptions.driverRunners();
}
if (threadCount < 1) {
threadCount = 1;
}
return features;
}
public T forTempUse() {
forTempUse = true;
return (T) this;
}
//======================================================================
//
public T configDir(String dir) {
this.configDir = dir;
return (T) this;
}
public T karateEnv(String env) {
this.env = env;
return (T) this;
}
public T systemProperty(String key, String value) {
if (systemProperties == null) {
systemProperties = new HashMap();
}
systemProperties.put(key, value);
return (T) this;
}
public T workingDir(File value) {
if (value != null) {
this.workingDir = value;
}
return (T) this;
}
public T buildDir(String value) {
if (value != null) {
this.buildDir = value;
}
return (T) this;
}
public T classLoader(ClassLoader value) {
classLoader = value;
return (T) this;
}
public T relativeTo(Class clazz) {
relativeTo = "classpath:" + ResourceUtils.toPathFromClassPathRoot(clazz);
return (T) this;
}
/**
* @see com.intuit.karate.Runner#builder()
* @deprecated
*/
@Deprecated
public T fromKarateAnnotation(Class> clazz) {
KarateOptions ko = clazz.getAnnotation(KarateOptions.class);
if (ko != null) {
LOGGER.warn("the @KarateOptions annotation is deprecated, please use Runner.builder()");
if (ko.tags().length > 0) {
tags = Arrays.asList(ko.tags());
}
if (ko.features().length > 0) {
paths = Arrays.asList(ko.features());
}
}
return relativeTo(clazz);
}
public T path(String... value) {
path(Arrays.asList(value));
return (T) this;
}
public T path(List value) {
if (value != null) {
if (paths == null) {
paths = new ArrayList();
}
paths.addAll(value);
}
return (T) this;
}
public T tags(List value) {
if (value != null) {
if (tags == null) {
tags = new ArrayList();
}
tags.addAll(value);
}
return (T) this;
}
public T tags(String... tags) {
tags(Arrays.asList(tags));
return (T) this;
}
public T features(Collection value) {
if (value != null) {
if (features == null) {
features = new ArrayList();
}
features.addAll(value.stream().map(FeatureCall::new).collect(Collectors.toList()));
}
return (T) this;
}
public T features(Feature... value) {
return features(Arrays.asList(value));
}
public T reportDir(String value) {
if (value != null) {
this.reportDir = value;
}
return (T) this;
}
public T scenarioName(String name) {
this.scenarioName = name;
return (T) this;
}
public T timeoutMinutes(int timeoutMinutes) {
this.timeoutMinutes = timeoutMinutes;
return (T) this;
}
public T hook(RuntimeHook hook) {
if (hook != null) {
hooks.add(hook);
}
return (T) this;
}
public T hooks(Collection hooks) {
if (hooks != null) {
this.hooks.addAll(hooks);
}
return (T) this;
}
public T hookFactory(RuntimeHookFactory hookFactory) {
this.hookFactory = hookFactory;
return (T) this;
}
public T clientFactory(HttpClientFactory clientFactory) {
this.clientFactory = clientFactory;
return (T) this;
}
// don't allow junit 5 builder to run in parallel
public Builder threads(int value) {
threadCount = value;
return this;
}
public T outputHtmlReport(boolean value) {
outputHtmlReport = value;
return (T) this;
}
public T backupReportDir(boolean value) {
backupReportDir = value;
return (T) this;
}
public T outputCucumberJson(boolean value) {
outputCucumberJson = value;
return (T) this;
}
public T outputJunitXml(boolean value) {
outputJunitXml = value;
return (T) this;
}
public T dryRun(boolean value) {
dryRun = value;
return (T) this;
}
public T debugMode(boolean value) {
debugMode = value;
return (T) this;
}
public T callSingleCache(Map value) {
callSingleCache = value;
return (T) this;
}
public T callOnceCache(Map value) {
callOnceCache = value;
return (T) this;
}
public T suiteReports(SuiteReports value) {
suiteReports = value;
return (T) this;
}
public T customDrivers(Map customDrivers) {
drivers = customDrivers;
return (T) this;
}
public Results parallel(int threadCount) {
threads(threadCount);
Suite suite = new Suite(this);
suite.run();
return suite.buildResults();
}
@Override
public String toString() {
return paths + "";
}
}
public static Builder path(String... paths) {
Builder builder = new Builder();
return builder.path(paths);
}
public static Builder path(List paths) {
Builder builder = new Builder();
return builder.path(paths);
}
public static Builder builder() {
return new Runner.Builder();
}
}