Skip to content

Commit

Permalink
unified cli for karate core somewhat #827
Browse files Browse the repository at this point in the history
in the future, the netty picocli has to be merged and we also would have native html parallel reports
but for now, we can start testing the vs code plugin for this
parallel execution will now generate native html report and continuously update the results-json.txt
which the plugin can read and update etc
  • Loading branch information
ptrthomas committed Aug 20, 2019
1 parent baf0cf9 commit ae2d9e4
Show file tree
Hide file tree
Showing 21 changed files with 227 additions and 50 deletions.
17 changes: 10 additions & 7 deletions karate-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
<th>Cookies</th>
<td>
<a href="#cookie"><code>cookie()</code></a>
| <a href="#drivercookie"><code>driver.cookie</code></a>
<a href="#cookieset"><code>cookie(set)</code></a>
| <a href="#drivercookies"><code>driver.cookies</code></a>
| <a href="#deletecookie"><code>deleteCookie()</code></a>
| <a href="#clearcookies"><code>clearCookies()</code></a>
Expand Down Expand Up @@ -1091,17 +1091,20 @@ Normal page reload, does *not* clear cache.
## `fullscreen()`
## `driver.cookie`
Set a cookie:
## `cookie(set)`
Set a cookie. The method argument is JSON, so that you can pass more data in addition to the `value` such as `domain` and `url`. Most servers expect the `domain` to be set correctly like this:
```cucumber
Given def cookie2 = { name: 'hello', value: 'world' }
When driver.cookie = cookie2
Then match driver.cookies contains '#(^cookie2)'
Given def myCookie = { name: 'hello', value: 'world', domain: '.mycompany.com' }
When cookie(myCookie)
Then match driver.cookies contains '#(^myCookie)'
```
> Note that you can do the above as a one-liner like this: `* cookie({ name: 'hello', value: 'world' })`, just keep in mind here that then it would follow the rules of [Enclosed JavaScript](https://github.com/intuit/karate#enclosed-javascript) (not [Embedded Expressions](https://github.com/intuit/karate#embedded-expressions))
## `cookie()`
Get a cookie by name:
Get a cookie by name. Note how Karate's [`match`](https://github.com/intuit/karate#match) syntax comes in handy.

```cucumber
* def cookie1 = { name: 'foo', value: 'bar' }
And match driver.cookies contains '#(^cookie1)'
Expand Down
23 changes: 19 additions & 4 deletions karate-core/src/main/java/com/intuit/karate/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ public Builder path(String... paths) {
this.paths.addAll(Arrays.asList(paths));
return this;
}

public Builder path(List<String> paths) {
this.paths.addAll(paths);
return this;
}

public Builder tags(List<String> tags) {
this.tags.addAll(tags);
return this;
}

public Builder tags(String... tags) {
this.tags.addAll(Arrays.asList(tags));
Expand Down Expand Up @@ -108,7 +118,7 @@ List<Resource> resources() {
return resources;
}

Results parallel(int threadCount) {
public Results parallel(int threadCount) {
this.threadCount = threadCount;
return Runner.parallel(this);
}
Expand All @@ -118,7 +128,12 @@ Results parallel(int threadCount) {
public static Builder path(String... paths) {
Builder builder = new Builder();
return builder.path(paths);
}
}

public static Builder path(List<String> paths) {
Builder builder = new Builder();
return builder.path(paths);
}

//==========================================================================
//
Expand Down Expand Up @@ -200,7 +215,7 @@ public static Results parallel(Builder options) {
feature.setCallLine(resource.getLine());
FeatureContext featureContext = new FeatureContext(null, feature, options.tagSelector());
CallContext callContext = CallContext.forAsync(feature, options.hooks, null, false);
ExecutionContext execContext = new ExecutionContext(results.getStartTime(), featureContext, callContext, reportDir,
ExecutionContext execContext = new ExecutionContext(results, results.getStartTime(), featureContext, callContext, reportDir,
r -> featureExecutor.submit(r), scenarioExecutor, Thread.currentThread().getContextClassLoader());
featureResults.add(execContext.result);
FeatureExecutionUnit unit = new FeatureExecutionUnit(execContext);
Expand Down Expand Up @@ -284,7 +299,7 @@ public static void callAsync(String path, Map<String, Object> arg, ExecutionHook
Feature feature = FileUtils.parseFeatureAndCallTag(path);
FeatureContext featureContext = new FeatureContext(null, feature, null);
CallContext callContext = CallContext.forAsync(feature, Collections.singletonList(hook), arg, true);
ExecutionContext executionContext = new ExecutionContext(System.currentTimeMillis(), featureContext, callContext, null, system, null);
ExecutionContext executionContext = new ExecutionContext(null, System.currentTimeMillis(), featureContext, callContext, null, system, null);
FeatureExecutionUnit exec = new FeatureExecutionUnit(executionContext);
exec.setNext(next);
system.accept(exec);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ public class RunnerOptions {

@CommandLine.Option(names = {"-t", "--tags"}, description = "tags")
List<String> tags;

@CommandLine.Option(names = {"-T", "--threads"}, description = "threads")
int threads = 1;

@CommandLine.Option(names = {"-", "--plugin"}, description = "plugin (not supported)")
List<String> plugins;
Expand All @@ -81,6 +84,10 @@ public List<String> getFeatures() {
return features;
}

public int getThreads() {
return threads;
}

public static RunnerOptions parseStringArgs(String[] args) {
RunnerOptions options = CommandLine.populateCommand(new RunnerOptions(), args);
List<String> featuresTemp = new ArrayList();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* The MIT License
*
* Copyright 2019 Intuit 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.cli;

import com.intuit.karate.core.Engine;
import com.intuit.karate.core.ExecutionHook;
import com.intuit.karate.core.Feature;
import com.intuit.karate.core.FeatureResult;
import com.intuit.karate.core.PerfEvent;
import com.intuit.karate.core.Scenario;
import com.intuit.karate.core.ScenarioContext;
import com.intuit.karate.core.ScenarioResult;
import com.intuit.karate.http.HttpRequestBuilder;
import java.util.concurrent.locks.ReentrantLock;

/**
*
* @author pthomas3
*/
public class CliExecutionHook implements ExecutionHook {

private final String targetDir;
private final boolean intellij;
private final ReentrantLock LOCK = new ReentrantLock();

public CliExecutionHook(String targetDir, boolean intellij) {
this.targetDir = targetDir;
this.intellij = intellij;
}

@Override
public boolean beforeScenario(Scenario scenario, ScenarioContext context) {
return true;
}

@Override
public void afterScenario(ScenarioResult result, ScenarioContext context) {

}

@Override
public boolean beforeFeature(Feature feature) {
return true;
}

@Override
public void afterFeature(FeatureResult result) {
if (intellij) {
Main.log(result);
}
Engine.saveResultHtml(targetDir, result, null);
if (LOCK.tryLock()) {
Engine.saveStatsJson(targetDir, result.getResults(), null);
LOCK.unlock();
}
}

@Override
public String getPerfEventName(HttpRequestBuilder req, ScenarioContext context) {
return null;
}

@Override
public void reportPerfEvent(PerfEvent event) {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.intuit.karate;
package com.intuit.karate.cli;

import com.intuit.karate.FileUtils;
import com.intuit.karate.Resource;
import com.intuit.karate.Results;
import com.intuit.karate.Runner;
import com.intuit.karate.RunnerOptions;
import com.intuit.karate.StringUtils;
import com.intuit.karate.core.Engine;
import com.intuit.karate.core.Feature;
import com.intuit.karate.core.FeatureParser;
Expand All @@ -43,30 +49,49 @@
*
* @author pthomas3
*/
public class IdeUtils {
public class Main {

private static final Pattern COMMAND_NAME = Pattern.compile("--name (.+?\\$)");

public static void exec(String[] args) {
String command = System.getProperty("sun.java.command");
public static void main(String[] args) {
String command;
if (args.length > 0) {
command = StringUtils.join(args, ' ');
} else {
command = System.getProperty("sun.java.command");
}
System.out.println("command: " + command);
boolean isIntellij = command.contains("org.jetbrains");
RunnerOptions options = RunnerOptions.parseCommandLine(command);
String name = options.getName();
List<String> features = options.getFeatures();
ClassLoader cl = Thread.currentThread().getContextClassLoader();
List<Resource> resources = FileUtils.scanForFeatureFiles(features, cl);
String targetDir = FileUtils.getBuildDir() + File.separator + "surefire-reports";
if (options.getThreads() == 1) {
runNormal(options, targetDir, isIntellij);
} else {
runParallel(options, targetDir, isIntellij);
}
}

private static void runNormal(RunnerOptions options, String targetDir, boolean isIntellij) {
String tagSelector = Tags.fromKarateOptionsTags(options.getTags());
ClassLoader cl = Thread.currentThread().getContextClassLoader();
List<Resource> resources = FileUtils.scanForFeatureFiles(options.getFeatures(), cl);
for (Resource resource : resources) {
Feature feature = FeatureParser.parse(resource);
feature.setCallName(name);
feature.setCallName(options.getName());
feature.setCallLine(resource.getLine());
FeatureResult result = Engine.executeFeatureSync(null, feature, tagSelector, null);
if (isIntellij) {
log(result);
}
Engine.saveResultHtml(FileUtils.getBuildDir() + File.separator + "surefire-reports", result, null);
}
Engine.saveResultHtml(targetDir, result, null);
}
}

private static void runParallel(RunnerOptions ro, String targetDir, boolean isIntellij) {
CliExecutionHook hook = new CliExecutionHook(targetDir, isIntellij);
Runner.path(ro.getFeatures())
.tags(ro.getTags()).scenarioName(ro.getName())
.hook(hook).parallel(ro.getThreads());
}

public static StringUtils.Pair parseCommandLine(String commandLine, String cwd) {
Expand Down Expand Up @@ -149,7 +174,7 @@ private static StringUtils.Pair details(Throwable error) {
}
}

private static void log(FeatureResult fr) {
public static void log(FeatureResult fr) {
Feature f = fr.getFeature();
String uri = fr.getDisplayUri();
String featureName = Feature.KEYWORD + ": " + escape(f.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public static FeatureResult executeFeatureSync(String env, Feature feature, Stri
if (callContext == null) {
callContext = new CallContext(null, true);
}
ExecutionContext exec = new ExecutionContext(System.currentTimeMillis(), featureContext, callContext, null, null, null);
ExecutionContext exec = new ExecutionContext(null, System.currentTimeMillis(), featureContext, callContext, null, null, null);
FeatureExecutionUnit unit = new FeatureExecutionUnit(exec);
unit.run();
return exec.result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import com.intuit.karate.CallContext;
import com.intuit.karate.FileUtils;
import com.intuit.karate.Results;
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
Expand All @@ -35,6 +36,7 @@
*/
public class ExecutionContext {

public final Results results;
public final long startTime;
public final FeatureContext featureContext;
public final CallContext callContext;
Expand All @@ -45,16 +47,17 @@ public class ExecutionContext {

private final File reportDir;

public ExecutionContext(long startTime, FeatureContext featureContext, CallContext callContext, String reportDirString,
public ExecutionContext(Results results, long startTime, FeatureContext featureContext, CallContext callContext, String reportDirString,
Consumer<Runnable> system, ExecutorService scenarioExecutor) {
this(startTime, featureContext, callContext, reportDirString, system, scenarioExecutor, null);
this(results, startTime, featureContext, callContext, reportDirString, system, scenarioExecutor, null);
}

public ExecutionContext(long startTime, FeatureContext featureContext, CallContext callContext, String reportDirString,
public ExecutionContext(Results results, long startTime, FeatureContext featureContext, CallContext callContext, String reportDirString,
Consumer<Runnable> system, ExecutorService scenarioExecutor, ClassLoader classLoader) {
this.results = results;
this.scenarioExecutor = scenarioExecutor;
this.startTime = startTime;
result = new FeatureResult(featureContext.feature);
result = new FeatureResult(results, featureContext.feature);
this.featureContext = featureContext;
this.callContext = callContext;
this.classLoader = classLoader;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import com.intuit.karate.FileUtils;
import com.intuit.karate.JsonUtils;
import com.intuit.karate.Results;
import com.intuit.karate.ScriptValueMap;
import com.intuit.karate.StringUtils;
import com.intuit.karate.exception.KarateException;
Expand All @@ -41,6 +42,7 @@
*/
public class FeatureResult {

private final Results results;
private final Feature feature;
private final String displayName;
private final List<ScenarioResult> scenarioResults = new ArrayList();
Expand Down Expand Up @@ -101,7 +103,12 @@ public List<StepResult> getStepResults() {
return list;
}

public FeatureResult(Feature feature) {
public Results getResults() {
return results;
}

public FeatureResult(Results results, Feature feature) {
this.results = results;
this.feature = feature;
displayName = FileUtils.removePrefix(feature.getRelativePath());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ public Map<String, Object> cookie(String name) {
}

@Override
public void setCookie(Map<String, Object> cookie) {
public void cookie(Map<String, Object> cookie) {
if (cookie.get("url") == null && cookie.get("domain") == null) {
cookie = new HashMap(cookie); // don't mutate test
cookie.put("url", currentUrl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ default byte[] screenshot() {
}

Map<String, Object> cookie(String name);

void cookie(Map<String, Object> cookie);

void deleteCookie(String name);

void clearCookies();

List<Map> getCookies(); // getter

void setCookie(Map<String, Object> cookie); // setter
List<Map> getCookies(); // getter

void dialog(boolean accept);

Expand Down

0 comments on commit ae2d9e4

Please sign in to comment.