Skip to content

Commit

Permalink
wip cleanup of runtime life cycle #1905
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrthomas committed Aug 6, 2022
1 parent aac1589 commit 251efee
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public void setMockEngine(ScenarioEngine mockEngine) {

public ScenarioEngine getMockEngine() {
return mockEngine;
}
}

public static FeatureRuntime forTempUse(HttpClientFactory hcf) {
Suite sr = Suite.forTempUse(hcf);
Expand Down
104 changes: 54 additions & 50 deletions karate-core/src/main/java/com/intuit/karate/core/MockHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,39 +91,22 @@ public MockHandler(List<Feature> features) {

public MockHandler(String prefix, List<Feature> features, Map<String, Object> args) {
this.prefix = "/".equals(prefix) ? null : prefix;
for (Feature feature : features) {
FeatureRuntime featureRuntime = FeatureRuntime.of(Suite.forTempUse(HttpClientFactory.DEFAULT), feature, args);
FeatureSection section = new FeatureSection();
section.setIndex(-1); // TODO util for creating dummy scenario
Scenario dummy = new Scenario(feature, section, -1);
section.setScenario(dummy);
ScenarioRuntime runtime = new ScenarioRuntime(featureRuntime, dummy);
initRuntime(runtime);
if (feature.isBackgroundPresent()) {
// if we are within a scenario already e.g. karate.start(), preserve context
ScenarioEngine prevEngine = ScenarioEngine.get();
try {
ScenarioEngine.set(runtime.engine);
for (Step step : feature.getBackground().getSteps()) {
Result result = StepRuntime.execute(step, runtime.actions);
if (result.isFailed()) {
String message = "mock-server background failed - " + feature + ":" + step.getLine();
runtime.logger.error(message);
throw new KarateException(message, result.getError());
}
}
} finally {
ScenarioEngine.set(prevEngine);
}
}
features.forEach(feature -> {
ScenarioRuntime runtime = initRuntime(feature, args);
corsEnabled = corsEnabled || runtime.engine.getConfig().isCorsEnabled();
globals.putAll(runtime.engine.shallowCloneVariables());
runtime.logger.info("mock server initialized: {}", feature);
featureRuntimes.put(feature, runtime);
}
featureRuntimes.put(feature, runtime);
});
}

private void initRuntime(ScenarioRuntime runtime) {
private ScenarioRuntime initRuntime(Feature feature, Map<String, Object> args) {
FeatureRuntime featureRuntime = FeatureRuntime.of(Suite.forTempUse(HttpClientFactory.DEFAULT), feature, args);
FeatureSection section = new FeatureSection();
section.setIndex(-1); // TODO util for creating dummy scenario
Scenario dummy = new Scenario(feature, section, -1);
section.setScenario(dummy);
ScenarioRuntime runtime = new ScenarioRuntime(featureRuntime, dummy);
runtime.engine.setVariable(PATH_MATCHES, (Function<String, Boolean>) this::pathMatches);
runtime.engine.setVariable(PARAM_EXISTS, (Function<String, Boolean>) this::paramExists);
runtime.engine.setVariable(PARAM_VALUE, (Function<String, String>) this::paramValue);
Expand All @@ -133,6 +116,24 @@ private void initRuntime(ScenarioRuntime runtime) {
runtime.engine.setVariable(HEADER_CONTAINS, (BiFunction<String, String, Boolean>) this::headerContains);
runtime.engine.setVariable(BODY_PATH, (Function<String, Object>) this::bodyPath);
runtime.engine.init();
if (feature.isBackgroundPresent()) {
// if we are within a scenario already e.g. karate.start(), preserve context
ScenarioEngine prevEngine = ScenarioEngine.get();
try {
ScenarioEngine.set(runtime.engine);
for (Step step : feature.getBackground().getSteps()) {
Result result = StepRuntime.execute(step, runtime.actions);
if (result.isFailed()) {
String message = "mock-server background failed - " + feature + ":" + step.getLine();
runtime.logger.error(message);
throw new KarateException(message, result.getError());
}
}
} finally {
ScenarioEngine.set(prevEngine);
}
}
return runtime;
}

private static final Result PASSED = Result.passed(0);
Expand Down Expand Up @@ -161,27 +162,10 @@ public synchronized Response handle(Request req) { // note the [synchronized]
Feature feature = entry.getKey();
ScenarioRuntime runtime = entry.getValue();
// important for graal to work properly
Thread.currentThread().setContextClassLoader(runtime.featureRuntime.suite.classLoader);
// begin init engine for this request
Thread.currentThread().setContextClassLoader(runtime.featureRuntime.suite.classLoader);
LOCAL_REQUEST.set(req);
req.processBody();
ScenarioEngine engine = new ScenarioEngine(runtime, new HashMap<>(globals));
engine.init();
engine.setVariable(ScenarioEngine.REQUEST_URL_BASE, req.getUrlBase());
engine.setVariable(ScenarioEngine.REQUEST_URI, req.getPath());
engine.setVariable(ScenarioEngine.REQUEST_METHOD, req.getMethod());
engine.setVariable(ScenarioEngine.REQUEST_HEADERS, req.getHeaders());
engine.setVariable(ScenarioEngine.REQUEST, req.getBodyConverted());
engine.setVariable(REQUEST_PARAMS, req.getParams());
engine.setVariable(REQUEST_BYTES, req.getBody());
engine.setRequest(req);
runtime.featureRuntime.setMockEngine(engine);
ScenarioEngine.set(engine);
Map<String, List<Map<String, Object>>> parts = req.getMultiParts();
if (parts != null) {
engine.setHiddenVariable(REQUEST_PARTS, parts);
}
// end init engine for this request
ScenarioEngine engine = initEngine(runtime, globals, req);
for (FeatureSection fs : feature.getSections()) {
if (fs.isOutline()) {
runtime.logger.warn("skipping scenario outline - {}:{}", feature, fs.getScenarioOutline().getLine());
Expand All @@ -192,8 +176,7 @@ public synchronized Response handle(Request req) { // note the [synchronized]
Map<String, Object> configureHeaders;
Variable response, responseStatus, responseHeaders, responseDelay;
ScenarioActions actions = new ScenarioActions(engine);
Result result = PASSED;
result = executeScenarioSteps(feature, runtime, scenario, actions, result);
Result result = executeScenarioSteps(feature, runtime, scenario, actions);
engine.mockAfterScenario();
configureHeaders = engine.mockConfigureHeaders();
response = engine.vars.remove(ScenarioEngine.RESPONSE);
Expand Down Expand Up @@ -242,8 +225,29 @@ public synchronized Response handle(Request req) { // note the [synchronized]
}
return new Response(404);
}

private static ScenarioEngine initEngine(ScenarioRuntime runtime, Map<String, Variable> globals, Request req) {
ScenarioEngine engine = new ScenarioEngine(runtime.engine.getConfig(), runtime, new HashMap(globals), runtime.logger);
engine.init();
engine.setVariable(ScenarioEngine.REQUEST_URL_BASE, req.getUrlBase());
engine.setVariable(ScenarioEngine.REQUEST_URI, req.getPath());
engine.setVariable(ScenarioEngine.REQUEST_METHOD, req.getMethod());
engine.setVariable(ScenarioEngine.REQUEST_HEADERS, req.getHeaders());
engine.setVariable(ScenarioEngine.REQUEST, req.getBodyConverted());
engine.setVariable(REQUEST_PARAMS, req.getParams());
engine.setVariable(REQUEST_BYTES, req.getBody());
engine.setRequest(req);
runtime.featureRuntime.setMockEngine(engine);
ScenarioEngine.set(engine);
Map<String, List<Map<String, Object>>> parts = req.getMultiParts();
if (parts != null) {
engine.setHiddenVariable(REQUEST_PARTS, parts);
}
return engine;
}

private Result executeScenarioSteps(Feature feature, ScenarioRuntime runtime, Scenario scenario, ScenarioActions actions, Result result) {
private Result executeScenarioSteps(Feature feature, ScenarioRuntime runtime, Scenario scenario, ScenarioActions actions) {
Result result = PASSED;
for (Step step : scenario.getSteps()) {
result = StepRuntime.execute(step, actions);
if (result.isAborted()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
*/
package com.intuit.karate.core;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -67,11 +66,8 @@ public Config getParentConfig(boolean copy) {
if (parentRuntime == null) {
return new Config();
}
if (copy) {
return new Config(parentRuntime.engine.getConfig());
} else {
return parentRuntime.engine.getConfig();
}
Config temp = parentRuntime.engine.getConfig();
return copy ? new Config(temp) : temp;
}

public boolean isNone() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,6 @@ public class ScenarioEngine {

protected JsEngine JS;

// only used by mock server
public ScenarioEngine(ScenarioRuntime runtime, Map<String, Variable> vars) {
this(runtime.engine.config, runtime, vars, runtime.logger);
}

public ScenarioEngine(Config config, ScenarioRuntime runtime, Map<String, Variable> vars, Logger logger) {
this.config = config;
this.runtime = runtime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
*/
package com.intuit.karate.core;

import com.intuit.karate.graal.JsEngine;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
Expand All @@ -47,6 +46,7 @@ public class ScenarioIterator implements Spliterator<ScenarioRuntime> {
private Scenario currentScenario;

// dynamic
private ScenarioRuntime dynamicRuntime;
private Variable expressionValue;
private int index;

Expand Down Expand Up @@ -90,33 +90,35 @@ public boolean tryAdvance(Consumer<? super ScenarioRuntime> action) {
if (currentScenario.isDynamic()) {
if (expressionValue == null) {
String expression = currentScenario.getDynamicExpression();
dynamicRuntime = new ScenarioRuntime(featureRuntime, currentScenario);
try {
expressionValue = new Variable(JsEngine.evalGlobal(expression));
ScenarioEngine.set(dynamicRuntime.engine);
dynamicRuntime.engine.init();
expressionValue = dynamicRuntime.engine.evalJs(expression);
if (expressionValue.isList() || expressionValue.isJsOrJavaFunction()) {
// all good
} else {
throw new RuntimeException("result is neither list nor function: " + expressionValue);
}
} catch (Exception e) {
String message = "dynamic expression evaluation failed: " + expression;
ScenarioRuntime dummy = new ScenarioRuntime(featureRuntime, currentScenario);
dummy.result.addFakeStepResult(message, e);
dynamicRuntime.result.addFakeStepResult(message, e);
currentScenario = null;
action.accept(dummy);
action.accept(dynamicRuntime);
return true; // exit early
}
}
final int rowIndex = index++;
Variable rowValue;
if (expressionValue.isJsOrJavaFunction()) {
try {
rowValue = ScenarioEngine.get().executeFunction(expressionValue, rowIndex);
ScenarioEngine.set(dynamicRuntime.engine);
rowValue = dynamicRuntime.engine.executeFunction(expressionValue, rowIndex);
} catch (Exception e) {
String message = "dynamic function expression evaluation failed at index " + rowIndex + ": " + e.getMessage();
ScenarioRuntime dummy = new ScenarioRuntime(featureRuntime, currentScenario);
dummy.result.addFakeStepResult(message, e);
dynamicRuntime.result.addFakeStepResult(message, e);
currentScenario = null;
action.accept(dummy);
action.accept(dynamicRuntime);
return true; // exit early
}
} else { // is list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ public List<Scenario> getScenarios(FeatureRuntime fr) {
if (selectedForExecution) {
Table table = examples.getTable();
if (table.isDynamic()) {
// technically row index 0 to denote an example (not -1)
Scenario scenario = toScenario(table.getDynamicExpression(), 0, table.getLineNumberForRow(0), examples.getTags());
Scenario scenario = toScenario(table.getDynamicExpression(), -1, table.getLineNumberForRow(0), examples.getTags());
list.add(scenario);
} else {
int rowCount = table.getRows().size();
Expand Down
Loading

0 comments on commit 251efee

Please sign in to comment.