com.intuit.karate.cucumber.CucumberRunner.java Source code

Java tutorial

Introduction

Here is the source code for com.intuit.karate.cucumber.CucumberRunner.java

Source

/*
 * The MIT License
 *
 * Copyright 2017 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.cucumber;

import com.intuit.karate.ScriptEnv;
import cucumber.runtime.Backend;
import cucumber.runtime.Runtime;
import cucumber.runtime.RuntimeGlue;
import cucumber.runtime.RuntimeOptions;
import cucumber.runtime.RuntimeOptionsFactory;
import cucumber.runtime.StopWatch;
import cucumber.runtime.UndefinedStepsTracker;
import cucumber.runtime.io.MultiLoader;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.model.CucumberFeature;
import cucumber.runtime.xstream.LocalizedXStreams;
import gherkin.formatter.Formatter;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author pthomas3
 */
public class CucumberRunner {

    private static final Logger logger = LoggerFactory.getLogger(CucumberRunner.class);

    private final ClassLoader classLoader;
    private final RuntimeOptions runtimeOptions;
    private final ResourceLoader resourceLoader;
    private final List<FeatureFile> featureFiles;

    public CucumberRunner(Class clazz) {
        logger.debug("init test class: {}", clazz);
        classLoader = clazz.getClassLoader();
        RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(clazz);
        runtimeOptions = runtimeOptionsFactory.create();
        resourceLoader = new MultiLoader(classLoader);
        List<CucumberFeature> cfs = runtimeOptions.cucumberFeatures(resourceLoader);
        featureFiles = new ArrayList<>(cfs.size());
        for (CucumberFeature cf : cfs) {
            featureFiles.add(new FeatureFile(cf, new File(cf.getPath())));
        }
    }

    public CucumberRunner(File file) {
        logger.debug("init feature file: {}", file);
        classLoader = Thread.currentThread().getContextClassLoader();
        resourceLoader = new MultiLoader(classLoader);
        RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(getClass());
        runtimeOptions = runtimeOptionsFactory.create();
        FeatureWrapper wrapper = FeatureWrapper.fromFile(file, classLoader);
        CucumberFeature feature = wrapper.getFeature();
        FeatureFile featureFile = new FeatureFile(feature, file);
        featureFiles = Collections.singletonList(featureFile);
    }

    public List<CucumberFeature> getFeatures() {
        List<CucumberFeature> list = new ArrayList<>(featureFiles.size());
        for (FeatureFile featureFile : featureFiles) {
            list.add(featureFile.feature);
        }
        return list;
    }

    public List<FeatureFile> getFeatureFiles() {
        return featureFiles;
    }

    public RuntimeOptions getRuntimeOptions() {
        return runtimeOptions;
    }

    public ClassLoader getClassLoader() {
        return classLoader;
    }

    public Runtime getRuntime(CucumberFeature feature) {
        return getRuntime(new FeatureFile(feature, new File(feature.getPath())));
    }

    public Runtime getRuntime(FeatureFile featureFile) {
        File packageFile = featureFile.file;
        String featurePath;
        if (packageFile.exists()) { // loaded by karate
            featurePath = packageFile.getAbsolutePath();
        } else { // was loaded by cucumber-jvm, is relative to classpath
            String temp = packageFile.getPath().replace('\\', '/'); // fix for windows
            featurePath = classLoader.getResource(temp).getFile();
        }
        logger.debug("loading feature: {}", featurePath);
        File featureDir = new File(featurePath).getParentFile();
        ScriptEnv env = new ScriptEnv(false, null, featureDir, packageFile.getName(), classLoader);
        Backend backend = new KarateBackend(env, null, null);
        RuntimeGlue glue = new RuntimeGlue(new UndefinedStepsTracker(), new LocalizedXStreams(classLoader));
        return new Runtime(resourceLoader, classLoader, Collections.singletonList(backend), runtimeOptions,
                StopWatch.SYSTEM, glue);
    }

    // only called for TestNG ?
    public void finish() {
        Formatter formatter = runtimeOptions.formatter(classLoader);
        formatter.done();
        formatter.close();
    }

    public void run(FeatureFile featureFile, KarateJunitFormatter formatter) {
        Runtime runtime = getRuntime(featureFile);
        featureFile.feature.run(formatter, formatter, runtime);
    }

    public void run(KarateJunitFormatter formatter) {
        for (FeatureFile featureFile : getFeatureFiles()) {
            run(featureFile, formatter);
        }
    }

    private static KarateJunitFormatter getFormatter(String reportDirPath, FeatureFile featureFile) {
        File reportDir = new File(reportDirPath);
        try {
            FileUtils.forceMkdirParent(reportDir);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        String featurePath = featureFile.feature.getPath();
        if (featurePath == null) {
            featurePath = featureFile.file.getPath();
        }
        featurePath = new File(featurePath).getPath(); // fix for windows
        String featurePackagePath = featurePath.replace(File.separator, ".");
        if (featurePackagePath.endsWith(".feature")) {
            featurePackagePath = featurePackagePath.substring(0, featurePackagePath.length() - 8);
        }
        try {
            reportDirPath = reportDir.getPath() + File.separator;
            String reportPath = reportDirPath + "TEST-" + featurePackagePath + ".xml";
            return new KarateJunitFormatter(featurePackagePath, reportPath);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static KarateStats parallel(Class clazz, int threadCount) {
        return parallel(clazz, threadCount, "target/surefire-reports");
    }

    public static KarateStats parallel(Class clazz, int threadCount, String reportDir) {
        KarateStats stats = KarateStats.startTimer();
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        CucumberRunner runner = new CucumberRunner(clazz);
        List<FeatureFile> featureFiles = runner.getFeatureFiles();
        List<Callable<KarateJunitFormatter>> callables = new ArrayList<>(featureFiles.size());
        int count = featureFiles.size();
        for (int i = 0; i < count; i++) {
            int index = i + 1;
            FeatureFile featureFile = featureFiles.get(i);
            callables.add(() -> {
                String threadName = Thread.currentThread().getName();
                KarateJunitFormatter formatter = getFormatter(reportDir, featureFile);
                logger.info(">>>> feature {} of {} on thread {}: {}", index, count, threadName,
                        featureFile.feature.getPath());
                runner.run(featureFile, formatter);
                logger.info("<<<< feature {} of {} on thread {}: {}", index, count, threadName,
                        featureFile.feature.getPath());
                formatter.done();
                return formatter;
            });
        }
        try {
            List<Future<KarateJunitFormatter>> futures = executor.invokeAll(callables);
            stats.stopTimer();
            for (Future<KarateJunitFormatter> future : futures) {
                KarateJunitFormatter formatter = future.get();
                stats.addToTestCount(formatter.getTestCount());
                stats.addToFailCount(formatter.getFailCount());
                stats.addToSkipCount(formatter.getSkipCount());
                stats.addToTimeTaken(formatter.getTimeTaken());
                if (formatter.isFail()) {
                    stats.addToFailedList(formatter.getFeaturePath());
                }
            }
            stats.printStats(threadCount);
            return stats;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}