You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by kb...@apache.org on 2023/11/13 08:32:29 UTC
(incubator-kie-kogito-benchmarks) 02/38: Initial hierarchy, build and start methods, 3 REST clients, appended log measurements
This is an automated email from the ASF dual-hosted git repository.
kbowers pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-kogito-benchmarks.git
commit 34569dc753972c66647fe975d95b219daa886d8f
Author: Marián Macik <mm...@redhat.com>
AuthorDate: Fri Apr 30 13:34:19 2021 +0200
Initial hierarchy, build and start methods, 3 REST clients, appended log measurements
---
.gitignore | 23 +
framework/pom.xml | 107 ++++
.../org/kie/kogito/benchmarks/framework/App.java | 61 +++
.../kogito/benchmarks/framework/BuildResult.java | 28 +
.../kie/kogito/benchmarks/framework/Commands.java | 604 +++++++++++++++++++++
.../benchmarks/framework/HTTPRequestInfo.java | 93 ++++
.../kogito/benchmarks/framework/LogBuilder.java | 201 +++++++
.../org/kie/kogito/benchmarks/framework/Logs.java | 336 ++++++++++++
.../kie/kogito/benchmarks/framework/MvnCmds.java | 53 ++
.../kie/kogito/benchmarks/framework/RunInfo.java | 28 +
.../kogito/benchmarks/framework/URLContent.java | 33 ++
.../kogito/benchmarks/framework/WebpageTester.java | 78 +++
.../benchmarks/framework/WhitelistLogLines.java | 112 ++++
pom.xml | 30 +
sample-kogito-app/pom.xml | 115 ++++
.../src/main/java/mypackage/GreetingEndpoint.java | 47 ++
.../src/main/resources/LoanApplication.bpmn | 259 +++++++++
.../src/main/resources/MortgageApproval.dmn | 81 +++
.../src/main/resources/application.properties | 36 ++
sample-kogito-app/threshold.properties | 6 +
tests/pom.xml | 41 ++
.../kogito/benchmarks/AbstractTemplateTest.java | 367 +++++++++++++
.../kie/kogito/benchmarks/QuarkusLargeTest.java | 69 +++
.../kie/kogito/benchmarks/QuarkusSmallTest.java | 22 +
.../org/kie/kogito/benchmarks/QuarkusTest.java | 9 +
.../org/kie/kogito/benchmarks/StartStopTest.java | 214 ++++++++
tests/src/test/resources/log4j2.xml | 13 +
27 files changed, 3066 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96c49bd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+target/
+/local
+
+logs/
+
+**/.idea
+
+# Repository wide ignore mac DS_Store files
+.DS_Store
+
+# Eclipse, Netbeans and IntelliJ files
+!.gitignore
+!.github
+/nbproject
+/*.ipr
+/*.iws
+*.iml
+.settings/
+.project
+.classpath
+.factorypath
+.vscode
+.run/
\ No newline at end of file
diff --git a/framework/pom.xml b/framework/pom.xml
new file mode 100644
index 0000000..bf46da0
--- /dev/null
+++ b/framework/pom.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.kie.kogito</groupId>
+ <artifactId>kogito-benchmarks</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>framework</artifactId>
+
+ <name></name>
+ <description></description>
+
+ <properties>
+ <log4j.version>2.13.2</log4j.version>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <!-- Override test scope coming from parent -->
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>${version.org.junit.jupiter}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <version>${version.org.junit.jupiter}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>${version.org.junit.jupiter}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ <version>${version.org.junit.jupiter}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-engine</artifactId>
+ <version>${version.org.junit.platform}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.platform</groupId>
+ <artifactId>junit-platform-commons</artifactId>
+ <version>${version.org.junit.platform}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <version>${log4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <version>${log4j.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>3.4.1.Final</version> <!-- TODO find another logging library-->
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Logging -->
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/App.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/App.java
new file mode 100644
index 0000000..ab39a3a
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/App.java
@@ -0,0 +1,61 @@
+package org.kie.kogito.benchmarks.framework;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.lang3.StringUtils;
+
+import static org.kie.kogito.benchmarks.framework.Commands.BASE_DIR;
+
+public enum App {
+ SAMPLE_KOGITO_APP_QUARKUS_JVM("sample-kogito-app", MvnCmds.QUARKUS_JVM, URLContent.SAMPLE_KOGITO_APP, WhitelistLogLines.SAMPLE_KOGITO_APP),
+ SAMPLE_KOGITO_APP_SPRING_BOOT("sample-kogito-app", MvnCmds.SPRING_BOOT_JVM, URLContent.SAMPLE_KOGITO_APP, WhitelistLogLines.SAMPLE_KOGITO_APP);
+ // JAX_RS_MINIMAL("app-jax-rs-minimal", URLContent.JAX_RS_MINIMAL, WhitelistLogLines.JAX_RS_MINIMAL),
+ // FULL_MICROPROFILE("app-full-microprofile", URLContent.FULL_MICROPROFILE, WhitelistLogLines.FULL_MICROPROFILE),
+ // GENERATED_SKELETON("app-generated-skeleton", URLContent.GENERATED_SKELETON, WhitelistLogLines.GENERATED_SKELETON);
+
+ public final String dir;
+ public final MvnCmds mavenCommands;
+ public final URLContent urlContent;
+ public final WhitelistLogLines whitelistLogLines;
+ public final Map<String, Long> thresholdProperties = new HashMap<>();
+
+ App(String dir, MvnCmds mavenCommands, URLContent urlContent, WhitelistLogLines whitelistLogLines) {
+ this.dir = dir;
+ this.mavenCommands = mavenCommands;
+ this.urlContent = urlContent;
+ this.whitelistLogLines = whitelistLogLines;
+ File tpFile = new File(BASE_DIR + File.separator + dir + File.separator + "threshold.properties");
+ String appDirNormalized = dir.toUpperCase().replace('-', '_') + "_";
+ try (InputStream input = new FileInputStream(tpFile)) {
+ Properties props = new Properties();
+ props.load(input);
+ for (String pn : props.stringPropertyNames()) {
+ String normPn = pn.toUpperCase().replace('.', '_');
+ String env = System.getenv().get(appDirNormalized + normPn);
+ if (StringUtils.isNotBlank(env)) {
+ props.replace(pn, env);
+ }
+ String sys = System.getProperty(appDirNormalized + normPn);
+ if (StringUtils.isNotBlank(sys)) {
+ props.replace(pn, sys);
+ }
+ thresholdProperties.put(pn, Long.parseLong(props.getProperty(pn)));
+ }
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Check threshold.properties and Sys and Env variables (upper case, underscores instead of dots). " +
+ "All values are expected to be of type long.");
+ } catch (IOException e) {
+ throw new RuntimeException("Couldn't find " + tpFile.getAbsolutePath());
+ }
+ }
+
+ public File getAppDir(String parent) {
+ return new File(parent + File.separator + dir);
+ }
+}
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/BuildResult.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/BuildResult.java
new file mode 100644
index 0000000..19ab591
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/BuildResult.java
@@ -0,0 +1,28 @@
+package org.kie.kogito.benchmarks.framework;
+
+import java.io.File;
+
+public class BuildResult {
+
+ private final long buildTimeMs;
+ private final File buildLog;
+ private final int exitCode;
+
+ public BuildResult(long buildTimeMs, File buildLog, int exitCode) {
+ this.buildTimeMs = buildTimeMs;
+ this.buildLog = buildLog;
+ this.exitCode = exitCode;
+ }
+
+ public long getBuildTimeMs() {
+ return buildTimeMs;
+ }
+
+ public File getBuildLog() {
+ return buildLog;
+ }
+
+ public int getExitCode() {
+ return exitCode;
+ }
+}
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/Commands.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/Commands.java
new file mode 100644
index 0000000..de70e59
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/Commands.java
@@ -0,0 +1,604 @@
+package org.kie.kogito.benchmarks.framework;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Scanner;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jboss.logging.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import static org.kie.kogito.benchmarks.framework.Logs.appendln;
+import static org.kie.kogito.benchmarks.framework.Logs.appendlnSection;
+
+public class Commands {
+ private static final Logger LOGGER = Logger.getLogger(Commands.class.getName());
+
+ public static final String BASE_DIR = getBaseDir();
+ public static final String MVNW = Commands.isThisWindows ? "mvnw.cmd" : "./mvnw";
+ public static final boolean isThisWindows = System.getProperty("os.name").matches(".*[Ww]indows.*");
+ private static final Pattern numPattern = Pattern.compile("[ \t]*[0-9]+[ \t]*");
+ private static final Pattern quarkusVersionPattern = Pattern.compile("[ \t]*<quarkus.version>([^<]*)</quarkus.version>.*");
+ private static final Pattern trailingSlash = Pattern.compile("/+$");
+
+ public static String getArtifactGeneBaseDir() {
+ for (String p : new String[] { "ARTIFACT_GENERATOR_WORKSPACE", "artifact.generator.workspace" }) {
+ String env = System.getenv().get(p);
+ if (StringUtils.isNotBlank(env)) {
+ return env;
+ }
+ String sys = System.getProperty(p);
+ if (StringUtils.isNotBlank(sys)) {
+ return sys;
+ }
+ }
+ return System.getProperty("java.io.tmpdir");
+ }
+
+ public static String getLocalMavenRepoDir() {
+ for (String p : new String[] { "TESTS_MAVEN_REPO_LOCAL", "tests.maven.repo.local" }) {
+ String env = System.getenv().get(p);
+ if (StringUtils.isNotBlank(env)) {
+ return env;
+ }
+ String sys = System.getProperty(p);
+ if (StringUtils.isNotBlank(sys)) {
+ return sys;
+ }
+ }
+ return System.getProperty("user.home") + File.separator + ".m2" + File.separator + "repository";
+ }
+
+ /**
+ * Get system properties starting with `quarkus.native` prefix, for example quarkus.native.builder-image
+ *
+ * @return List of `-Dquarkus.native.xyz=foo` strings
+ */
+ public static List<String> getQuarkusNativeProperties() {
+ List<String> quarkusNativeProperties = System.getProperties().entrySet().stream()
+ .filter(e -> e.getKey().toString().contains("quarkus.native"))
+ .map(e -> "-D" + e.getKey() + "=" + e.getValue())
+ .collect(Collectors.toList());
+ return quarkusNativeProperties;
+ }
+
+ public static String getQuarkusPlatformVersion() {
+ for (String p : new String[] { "QUARKUS_PLATFORM_VERSION", "quarkus.platform.version" }) {
+ String env = System.getenv().get(p);
+ if (StringUtils.isNotBlank(env)) {
+ return env;
+ }
+ String sys = System.getProperty(p);
+ if (StringUtils.isNotBlank(sys)) {
+ return sys;
+ }
+ }
+ LOGGER.warn("Failed to detect quarkus.platform.version/QUARKUS_PLATFORM_VERSION, defaulting to getQuarkusVersion().");
+ return getQuarkusVersion();
+ }
+
+ public static String getQuarkusVersion() {
+ for (String p : new String[] { "QUARKUS_VERSION", "quarkus.version" }) {
+ String env = System.getenv().get(p);
+ if (StringUtils.isNotBlank(env)) {
+ return env;
+ }
+ String sys = System.getProperty(p);
+ if (StringUtils.isNotBlank(sys)) {
+ return sys;
+ }
+ }
+ String failure = "Failed to determine quarkus.version. Check pom.xm, check env and sys vars QUARKUS_VERSION";
+ try (Scanner sc = new Scanner(new File(getBaseDir() + File.separator + "pom.xml"), StandardCharsets.UTF_8)) {
+ while (sc.hasNextLine()) {
+ String line = sc.nextLine();
+ Matcher m = quarkusVersionPattern.matcher(line);
+ if (m.matches()) {
+ return m.group(1);
+ }
+ }
+ } catch (IOException e) {
+ throw new IllegalArgumentException(failure);
+ }
+ throw new IllegalArgumentException(failure);
+ }
+
+ public static String getBaseDir() {
+ String env = System.getenv().get("basedir");
+ String sys = System.getProperty("basedir");
+ if (StringUtils.isNotBlank(env)) {
+ return new File(env).getParent();
+ }
+ if (StringUtils.isBlank(sys)) {
+ throw new IllegalArgumentException("Unable to determine project.basedir.");
+ }
+ return new File(sys).getParent();
+ }
+
+ public static String getCodeQuarkusURL() {
+ return getCodeQuarkusURL("https://code.quarkus.io");
+ }
+
+ public static String getCodeQuarkusURL(String fallbackURL) {
+ String url = null;
+ for (String p : new String[] { "CODE_QUARKUS_URL", "code.quarkus.url" }) {
+ String env = System.getenv().get(p);
+ if (StringUtils.isNotBlank(env)) {
+ url = env;
+ break;
+ }
+ String sys = System.getProperty(p);
+ if (StringUtils.isNotBlank(sys)) {
+ url = sys;
+ break;
+ }
+ }
+ if (url == null) {
+ url = fallbackURL;
+ LOGGER.warn("Failed to detect code.quarkus.url/CODE_QUARKUS_URL env/sys props, defaulting to " + url);
+ return url;
+ }
+ Matcher m = trailingSlash.matcher(url);
+ if (m.find()) {
+ url = m.replaceAll("");
+ }
+ return url;
+ }
+
+ public static void cleanTarget(App app) {
+ String target = BASE_DIR + File.separator + app.dir + File.separator + "target";
+ String logs = BASE_DIR + File.separator + app.dir + File.separator + "logs";
+ cleanDirOrFile(target, logs);
+ }
+
+ public static BuildResult buildApp(App app, String methodName, String className, StringBuilder whatIDidReport) throws InterruptedException {
+ File appDir = app.getAppDir(BASE_DIR);
+ File buildLogA = new File(appDir.getAbsolutePath() + File.separator + "logs" + File.separator + app.mavenCommands.name().toLowerCase() + "-build.log");
+ ExecutorService buildService = Executors.newFixedThreadPool(1);
+
+ List<String> baseBuildCmd = new ArrayList<>(Arrays.asList(app.mavenCommands.mvnCmds[0]));
+ //baseBuildCmd.add("-Dquarkus.version=" + getQuarkusVersion());
+ List<String> cmd = getBuildCommand(baseBuildCmd.toArray(new String[0]));
+
+ buildService.submit(new Commands.ProcessRunner(appDir, buildLogA, cmd, 20)); // TODO exit code handling
+ appendln(whatIDidReport, "# " + className + ", " + methodName);
+ appendln(whatIDidReport, (new Date()).toString());
+ appendln(whatIDidReport, appDir.getAbsolutePath());
+ appendlnSection(whatIDidReport, String.join(" ", cmd));
+ long buildStarts = System.currentTimeMillis();
+ buildService.shutdown();
+ buildService.awaitTermination(30, TimeUnit.MINUTES);
+ long buildEnds = System.currentTimeMillis();
+ long buildTimeMs = buildEnds - buildStarts;
+
+ return new BuildResult(buildTimeMs, buildLogA, 0);
+ }
+
+ public static RunInfo startApp(App app, StringBuilder whatIDidReport) throws IOException, InterruptedException {
+ File appDir = app.getAppDir(BASE_DIR);
+ File runLogA = new File(appDir.getAbsolutePath() + File.separator + "logs" + File.separator + app.mavenCommands.name().toLowerCase() + "-run.log");
+ List<String> cmd = getRunCommand(app.mavenCommands.mvnCmds[1]);
+ appendln(whatIDidReport, appDir.getAbsolutePath());
+ appendlnSection(whatIDidReport, String.join(" ", cmd));
+ long runStarts = System.currentTimeMillis();
+ Process pA = runCommand(cmd, appDir, runLogA);
+ long runEnds = System.currentTimeMillis();
+ System.out.println("RunEnds (" + runEnds + ") - RunStarts (" + runStarts + ") : " + (runEnds - runStarts));
+ // Test web pages
+ long timeToFirstOKRequest = WebpageTester.testWeb(app.urlContent.urlContent[0][0], 10, app.urlContent.urlContent[0][1], true);
+ LOGGER.info("Testing web page content...");
+ for (String[] urlContent : app.urlContent.urlContent) {
+ WebpageTester.testWeb(urlContent[0], 5, urlContent[1], false);
+ }
+
+ return new RunInfo(pA, runLogA, timeToFirstOKRequest);
+ }
+
+ public static void cleanDirOrFile(String... path) {
+ for (String s : path) {
+ try {
+ Files.walk(Paths.get(s))
+ .sorted(Comparator.reverseOrder())
+ .map(Path::toFile)
+ .forEach(File::delete);
+ //FileUtils.forceDelete(new File(s));
+ } catch (IOException e) {
+ //Silence is golden
+ }
+ }
+ }
+
+ public static List<String> getRunCommand(String[] baseCommand) {
+ List<String> runCmd = new ArrayList<>();
+ if (isThisWindows) {
+ runCmd.add("cmd");
+ runCmd.add("/C");
+ }
+ runCmd.addAll(Arrays.asList(baseCommand));
+
+ return Collections.unmodifiableList(runCmd);
+ }
+
+ public static List<String> getBuildCommand(String[] baseCommand) {
+ List<String> buildCmd = new ArrayList<>();
+ if (isThisWindows) {
+ buildCmd.add("cmd");
+ buildCmd.add("/C");
+ }
+ buildCmd.addAll(Arrays.asList(baseCommand));
+ buildCmd.add("-Dmaven.repo.local=" + getLocalMavenRepoDir());
+
+ return Collections.unmodifiableList(buildCmd);
+ }
+
+ // public static List<String> getBuildCommand(String[] baseCommand, String repoDir) {
+ // List<String> buildCmd = new ArrayList<>();
+ // if (isThisWindows) {
+ // buildCmd.add("cmd");
+ // buildCmd.add("/C");
+ // }
+ // buildCmd.addAll(Arrays.asList(baseCommand));
+ // buildCmd.add("-Dmaven.repo.local=" + repoDir);
+ // buildCmd.add("--settings=" + BASE_DIR + File.separator + App.GENERATED_SKELETON.dir + File.separator + "settings.xml");
+ //
+ // return Collections.unmodifiableList(buildCmd);
+ // }
+
+ // public static List<String> getGeneratorCommand(Set<TestFlags> flags, String[] baseCommand, String[] extensions, String repoDir) {
+ // List<String> generatorCmd = new ArrayList<>();
+ // if (isThisWindows) {
+ // generatorCmd.add("cmd");
+ // generatorCmd.add("/C");
+ // }
+ // generatorCmd.addAll(Arrays.asList(baseCommand));
+ // if (flags.contains(TestFlags.PRODUCT_BOM)) {
+ // generatorCmd.add("-DplatformArtifactId=quarkus-product-bom");
+ // generatorCmd.add("-DplatformGroupId=com.redhat.quarkus");
+ // generatorCmd.add("-DplatformVersion=" + getQuarkusPlatformVersion());
+ // } else if (flags.contains(TestFlags.QUARKUS_BOM)) {
+ // generatorCmd.add("-DplatformArtifactId=quarkus-bom");
+ // generatorCmd.add("-DplatformVersion=" + getQuarkusVersion());
+ // } else if (flags.contains(TestFlags.UNIVERSE_BOM)) {
+ // generatorCmd.add("-DplatformArtifactId=quarkus-universe-bom");
+ // generatorCmd.add("-DplatformVersion=" + getQuarkusVersion());
+ // } else if (flags.contains(TestFlags.UNIVERSE_PRODUCT_BOM)) {
+ // generatorCmd.add("-DplatformArtifactId=quarkus-universe-bom");
+ // generatorCmd.add("-DplatformGroupId=com.redhat.quarkus");
+ // generatorCmd.add("-DplatformVersion=" + getQuarkusPlatformVersion());
+ // }
+ // generatorCmd.add("-Dextensions=" + String.join(",", extensions));
+ // generatorCmd.add("-Dmaven.repo.local=" + repoDir);
+ // generatorCmd.add("--settings=" + BASE_DIR + File.separator + App.GENERATED_SKELETON.dir + File.separator + "settings.xml");
+ //
+ // return Collections.unmodifiableList(generatorCmd);
+ // }
+
+ public static List<String> getGeneratorCommand(String[] baseCommand, String[] extensions) {
+ List<String> generatorCmd = new ArrayList<>();
+ if (isThisWindows) {
+ generatorCmd.add("cmd");
+ generatorCmd.add("/C");
+ }
+ generatorCmd.addAll(Arrays.asList(baseCommand));
+ generatorCmd.add("-DplatformVersion=" + getQuarkusVersion());
+ generatorCmd.add("-Dextensions=" + String.join(",", extensions));
+ generatorCmd.add("-Dmaven.repo.local=" + getLocalMavenRepoDir());
+
+ return Collections.unmodifiableList(generatorCmd);
+ }
+
+ // /**
+ // * Download a zip file with an example project
+ // *
+ // * @param extensions collection of extension codes, @See {@link io.quarkus.ts.startstop.utils.CodeQuarkusExtensions}
+ // * @param destinationZipFile path where the zip file will be written
+ // * @return the actual URL used for audit and logging purposes
+ // * @throws IOException
+ // */
+ // public static String download(Collection<CodeQuarkusExtensions> extensions, String destinationZipFile) throws IOException {
+ // String downloadURL = getCodeQuarkusURL() + "/api/download?s=" +
+ // extensions.stream().map(x -> x.shortId).collect(Collectors.joining("."));
+ // try (ReadableByteChannel readableByteChannel = Channels.newChannel(
+ // new URL(downloadURL).openStream());
+ // FileChannel fileChannel = new FileOutputStream(destinationZipFile).getChannel()) {
+ // fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
+ // }
+ // return downloadURL;
+ // }
+
+ public static File unzip(String zipFilePath, String destinationDir) throws InterruptedException, IOException {
+ ProcessBuilder pb;
+ if (isThisWindows) {
+ pb = new ProcessBuilder("powershell", "-c", "Expand-Archive", "-Path", zipFilePath, "-DestinationPath", destinationDir, "-Force");
+ } else {
+ pb = new ProcessBuilder("unzip", "-o", zipFilePath, "-d", destinationDir);
+ }
+ Map<String, String> env = pb.environment();
+ env.put("PATH", System.getenv("PATH"));
+ pb.directory(new File(destinationDir));
+ pb.redirectErrorStream(true);
+ File unzipLog = new File(zipFilePath + ".log");
+ unzipLog.delete();
+ pb.redirectOutput(ProcessBuilder.Redirect.to(unzipLog));
+ Process p = pb.start();
+ p.waitFor(3, TimeUnit.MINUTES);
+ return unzipLog;
+ }
+
+ public static void removeRepositoriesAndPluginRepositories(String pomFilePath) throws Exception {
+ File pomFile = new File(pomFilePath);
+ Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(pomFile);
+ NodeList repositories = doc.getElementsByTagName("repositories");
+ if (repositories.getLength() == 1) {
+ Node node = repositories.item(0);
+ node.getParentNode().removeChild(node);
+ }
+ NodeList pluginRepositories = doc.getElementsByTagName("pluginRepositories");
+ if (pluginRepositories.getLength() == 1) {
+ Node node = pluginRepositories.item(0);
+ node.getParentNode().removeChild(node);
+ }
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.transform(new DOMSource(doc), new StreamResult(pomFile));
+ }
+
+ public static boolean waitForTcpClosed(String host, int port, long loopTimeoutS) throws InterruptedException, UnknownHostException {
+ InetAddress address = InetAddress.getByName(host);
+ long now = System.currentTimeMillis();
+ long startTime = now;
+ InetSocketAddress socketAddr = new InetSocketAddress(address, port);
+ while (now - startTime < 1000 * loopTimeoutS) {
+ try (Socket socket = new Socket()) {
+ // If it let's you write something there, it is still ready.
+ socket.connect(socketAddr, 1000);
+ socket.setSendBufferSize(1);
+ socket.getOutputStream().write(1);
+ socket.shutdownInput();
+ socket.shutdownOutput();
+ LOGGER.info("Socket still available: " + host + ":" + port);
+ } catch (IOException e) {
+ // Exception thrown - socket is likely closed.
+ return true;
+ }
+ Thread.sleep(1000);
+ now = System.currentTimeMillis();
+ }
+ return false;
+ }
+
+ // // TODO we should get rid of it once Quarkus progresses with walid config per extension in generated examples
+ // public static void confAppPropsForSkeleton(String appDir) throws IOException {
+ // // Config, see app-generated-skeleton/README.md
+ // String appPropsSrc = BASE_DIR + File.separator + App.GENERATED_SKELETON.dir + File.separator + "application.properties";
+ // String appPropsDst = appDir + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "application.properties";
+ // Files.copy(Paths.get(appPropsSrc),
+ // Paths.get(appPropsDst), StandardCopyOption.REPLACE_EXISTING);
+ // }
+
+ public static void adjustPrettyPrintForJsonLogging(String appDir) throws IOException {
+ Path appProps = Paths.get(appDir + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "application.properties");
+ Path appYaml = Paths.get(appDir + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "application.yml");
+
+ adjustFileContent(appProps, "quarkus.log.console.json.pretty-print=true", "quarkus.log.console.json.pretty-print=false");
+ adjustFileContent(appYaml, "pretty-print: true", "pretty-print: fase");
+ }
+
+ private static void adjustFileContent(Path path, String regex, String replacement) throws IOException {
+ if (Files.exists(path)) {
+ String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
+ content = content.replaceAll(regex, replacement);
+ Files.write(path, content.getBytes(StandardCharsets.UTF_8));
+ }
+ }
+
+ public static int parsePort(String url) {
+ return Integer.parseInt(url.split(":")[2].split("/")[0]);
+ }
+
+ public static Process runCommand(List<String> command, File directory, File logFile) {
+ ProcessBuilder pa = new ProcessBuilder(command);
+ Map<String, String> envA = pa.environment();
+ envA.put("PATH", System.getenv("PATH"));
+ pa.directory(directory);
+ pa.redirectErrorStream(true);
+ pa.redirectOutput(ProcessBuilder.Redirect.to(logFile));
+ Process pA = null;
+ try {
+ pA = pa.start();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return pA;
+ }
+
+ public static void pidKiller(long pid, boolean force) {
+ try {
+ if (isThisWindows) {
+ if (!force) {
+ Process p = Runtime.getRuntime().exec(new String[] {
+ BASE_DIR + File.separator + "testsuite" + File.separator + "src" + File.separator + "it" + File.separator + "resources" + File.separator +
+ "CtrlC.exe ",
+ Long.toString(pid) });
+ p.waitFor(1, TimeUnit.MINUTES);
+ }
+ Runtime.getRuntime().exec(new String[] { "cmd", "/C", "taskkill", "/PID", Long.toString(pid), "/F", "/T" });
+ } else {
+ Runtime.getRuntime().exec(new String[] { "kill", force ? "-9" : "-15", Long.toString(pid) });
+ }
+ } catch (IOException | InterruptedException e) {
+ LOGGER.error(e.getMessage(), e);
+ }
+ }
+
+ public static long getRSSkB(long pid) throws IOException, InterruptedException {
+ ProcessBuilder pa;
+ if (isThisWindows) {
+ // Note that PeakWorkingSetSize might be better, but we would need to change it on Linux too...
+ // https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process
+ pa = new ProcessBuilder("wmic", "process", "where", "processid=" + pid, "get", "WorkingSetSize");
+ } else {
+ pa = new ProcessBuilder("ps", "-p", Long.toString(pid), "-o", "rss=");
+ }
+ Map<String, String> envA = pa.environment();
+ envA.put("PATH", System.getenv("PATH"));
+ pa.redirectErrorStream(true);
+ Process p = pa.start();
+ try (BufferedReader processOutputReader =
+ new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
+ String l;
+ while ((l = processOutputReader.readLine()) != null) {
+ if (numPattern.matcher(l).matches()) {
+ if (isThisWindows) {
+ // Qualifiers: DisplayName ("Working Set Size"), Units ("bytes")
+ return Long.parseLong(l.trim()) / 1024L;
+ } else {
+ return Long.parseLong(l.trim());
+ }
+ }
+ }
+ p.waitFor();
+ }
+ return -1L;
+ }
+
+ public static long getOpenedFDs(long pid) throws IOException, InterruptedException {
+ ProcessBuilder pa;
+ long count = 0;
+ if (isThisWindows) {
+ pa = new ProcessBuilder("wmic", "process", "where", "processid=" + pid, "get", "HandleCount");
+ } else {
+ pa = new ProcessBuilder("lsof", "-F0n", "-p", Long.toString(pid));
+ }
+ Map<String, String> envA = pa.environment();
+ envA.put("PATH", System.getenv("PATH"));
+ pa.redirectErrorStream(true);
+ Process p = pa.start();
+ try (BufferedReader processOutputReader =
+ new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) {
+ if (isThisWindows) {
+ String l;
+ // TODO: We just get a magical number with all FDs... Is it O.K.?
+ while ((l = processOutputReader.readLine()) != null) {
+ if (numPattern.matcher(l).matches()) {
+ return Long.parseLong(l.trim());
+ }
+ }
+ } else {
+ // TODO: For the time being we count apples and oranges; we might want to distinguish .so and .jar ?
+ while (processOutputReader.readLine() != null) {
+ count++;
+ }
+ }
+ p.waitFor();
+ }
+ return count;
+ }
+
+ /*
+ * TODO: CPU cycles used
+ *
+ * Pros: good data
+ * Cons: dependency on perf tool; will not translate to Windows data
+ *
+ * karm@local:~/workspaceRH/fooBar$ perf stat java -jar target/fooBar-1.0.0-SNAPSHOT-runner.jar
+ * 2020-02-25 16:07:00,870 INFO [io.quarkus] (main) fooBar 1.0.0-SNAPSHOT (running on Quarkus 999-SNAPSHOT) started in 0.776s.
+ * 2020-02-25 16:07:00,873 INFO [io.quarkus] (main) Profile prod activated.
+ * 2020-02-25 16:07:00,873 INFO [io.quarkus] (main) Installed features: [amazon-lambda, cdi, resteasy]
+ * 2020-02-25 16:07:03,360 INFO [io.quarkus] (main) fooBar stopped in 0.018s
+ *
+ * Performance counter stats for 'java -jar target/fooBar-1.0.0-SNAPSHOT-runner.jar':
+ *
+ * 1688.910052 task-clock:u (msec) # 0.486 CPUs utilized
+ * 0 context-switches:u # 0.000 K/sec
+ * 0 cpu-migrations:u # 0.000 K/sec
+ * 12,865 page-faults:u # 0.008 M/sec
+ * 4,274,799,448 cycles:u # 2.531 GHz
+ * 4,325,761,598 instructions:u # 1.01 insn per cycle
+ * 919,713,769 branches:u # 544.561 M/sec
+ * 29,310,015 branch-misses:u # 3.19% of all branches
+ *
+ * 3.473028811 seconds time elapsed
+ */
+
+ // Ask Karm about this
+ public static void processStopper(Process p, boolean force) throws InterruptedException, IOException {
+ p.children().forEach(child -> {
+ if (child.supportsNormalTermination()) {
+ child.destroy();
+ }
+ pidKiller(child.pid(), force);
+ });
+ if (p.supportsNormalTermination()) {
+ p.destroy();
+ p.waitFor(3, TimeUnit.MINUTES);
+ }
+ pidKiller(p.pid(), force);
+ }
+
+ public static class ProcessRunner implements Runnable {
+ final File directory;
+ final File log;
+ final List<String> command;
+ final long timeoutMinutes;
+
+ public ProcessRunner(File directory, File log, List<String> command, long timeoutMinutes) {
+ this.directory = directory;
+ this.log = log;
+ this.command = command;
+ this.timeoutMinutes = timeoutMinutes;
+ }
+
+ @Override
+ public void run() {
+ ProcessBuilder pb = new ProcessBuilder(command);
+ Map<String, String> env = pb.environment();
+ env.put("PATH", System.getenv("PATH"));
+ pb.directory(directory);
+ pb.redirectErrorStream(true);
+ pb.redirectOutput(ProcessBuilder.Redirect.to(log));
+ Process p = null;
+ try {
+ p = pb.start();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ try {
+ Objects.requireNonNull(p).waitFor(timeoutMinutes, TimeUnit.MINUTES);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/HTTPRequestInfo.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/HTTPRequestInfo.java
new file mode 100644
index 0000000..74d0ecc
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/HTTPRequestInfo.java
@@ -0,0 +1,93 @@
+package org.kie.kogito.benchmarks.framework;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class HTTPRequestInfo {
+
+ private String uri;
+ private String body;
+ private String method;
+ private Map<String, String> headers = new HashMap<>();
+ private int expectedResponseStatusCode;
+
+ private void setURI(String uri) {
+ this.uri = uri;
+ }
+
+ private void setBody(String body) {
+ this.body = body;
+ }
+
+ private void setMethod(String method) {
+ this.method = method;
+ }
+
+ private void addHeader(String name, String value) {
+ this.headers.put(name, value);
+ }
+
+ private void setExpectedResponseStatusCode(int expectedResponseStatusCode) {
+ this.expectedResponseStatusCode = expectedResponseStatusCode;
+ }
+
+ public String getURI() {
+ return uri;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public Map<String, String> getHeaders() {
+ return headers;
+ }
+
+ public int getExpectedResponseStatusCode() {
+ return expectedResponseStatusCode;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private final HTTPRequestInfo instance = new HTTPRequestInfo();
+
+ public Builder URI(String uri) {
+ instance.setURI(uri);
+ return this;
+ }
+
+ public Builder body(String body) {
+ instance.setBody(body);
+ return this;
+ }
+
+ public Builder method(String method) {
+ instance.setMethod(method);
+ return this;
+ }
+
+ public Builder header(String name, String value) {
+ instance.addHeader(name, value);
+ return this;
+ }
+
+ public Builder expectedResponseStatusCode(int statusCode) {
+ instance.setExpectedResponseStatusCode(statusCode);
+ return this;
+ }
+
+ public HTTPRequestInfo build() {
+ return instance;
+ }
+
+ }
+
+}
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/LogBuilder.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/LogBuilder.java
new file mode 100644
index 0000000..2294df5
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/LogBuilder.java
@@ -0,0 +1,201 @@
+package org.kie.kogito.benchmarks.framework;
+
+import java.util.Objects;
+
+public class LogBuilder {
+
+ public static class Log {
+ public final String headerCSV;
+ public final String headerMarkdown;
+ public final String lineCSV;
+ public final String lineMarkdown;
+
+ public Log(String headerCSV, String headerMarkdown, String lineCSV, String lineMarkdown) {
+ this.headerCSV = headerCSV;
+ this.headerMarkdown = headerMarkdown;
+ this.lineCSV = lineCSV;
+ this.lineMarkdown = lineMarkdown;
+ }
+ }
+
+ private static final String buildTimeMsHeader = "buildTimeMs";
+ private long buildTimeMs = -1L;
+ private static final String timeToFirstOKRequestMsHeader = "timeToFirstOKRequestMs";
+ private long timeToFirstOKRequestMs = -1L;
+ private static final String timeToReloadedOKRequestHeader = "timeToReloadMs";
+ private long timeToReloadedOKRequest = -1L;
+ private static final String startedInMsHeader = "startedInMs";
+ private long startedInMs = -1L;
+ private static final String stoppedInMsHeader = "stoppedInMs";
+ private long stoppedInMs = -1L;
+ private static final String rssKbHeader = "RSSKb";
+ private long rssKb = -1L;
+ private static final String rssKbFinalHeader = "RSSKbFinal";
+ private long rssKbFinal = -1L;
+ private static final String openedFilesHeader = "FDs";
+ private long openedFiles = -1L;
+ private static final String appHeader = "App";
+ private App app = null;
+ private static final String modeHeader = "Mode";
+ private MvnCmds mode = null;
+
+ public LogBuilder buildTimeMs(long buildTimeMs) {
+ if (buildTimeMs <= 0) {
+ throw new IllegalArgumentException("buildTimeMs must be a positive long, was: " + buildTimeMs);
+ }
+ this.buildTimeMs = buildTimeMs;
+ return this;
+ }
+
+ public LogBuilder timeToFirstOKRequestMs(long timeToFirstOKRequestMs) {
+ if (timeToFirstOKRequestMs <= 0) {
+ throw new IllegalArgumentException("timeToFirstOKRequestMs must be a positive long, was: " + timeToFirstOKRequestMs);
+ }
+ this.timeToFirstOKRequestMs = timeToFirstOKRequestMs;
+ return this;
+ }
+
+ public LogBuilder timeToReloadedOKRequest(long timeToReloadedOKRequest) {
+ if (timeToReloadedOKRequest <= 0) {
+ throw new IllegalArgumentException("timeToReloadedOKRequest must be a positive long, was: " + timeToFirstOKRequestMs);
+ }
+ this.timeToReloadedOKRequest = timeToReloadedOKRequest;
+ return this;
+ }
+
+ public LogBuilder startedInMs(long startedInMs) {
+ if (startedInMs <= 0) {
+ throw new IllegalArgumentException("startedInMs must be a positive long, was: " + startedInMs);
+ }
+ this.startedInMs = startedInMs;
+ return this;
+ }
+
+ public LogBuilder stoppedInMs(long stoppedInMs) {
+ if (stoppedInMs <= 0) {
+ throw new IllegalArgumentException("stoppedInMs must be a positive long, was: " + stoppedInMs);
+ }
+ this.stoppedInMs = stoppedInMs;
+ return this;
+ }
+
+ public LogBuilder rssKb(long rssKb) {
+ if (rssKb <= 0) {
+ throw new IllegalArgumentException("rssKb must be a positive long, was: " + rssKb);
+ }
+ this.rssKb = rssKb;
+ return this;
+ }
+
+ public LogBuilder rssKbFinal(long rssKbFinal) {
+ if (rssKbFinal <= 0) {
+ throw new IllegalArgumentException("rssKb must be a positive long, was: " + rssKb);
+ }
+ this.rssKbFinal = rssKbFinal;
+ return this;
+ }
+
+ public LogBuilder openedFiles(long openedFiles) {
+ if (openedFiles <= 0) {
+ throw new IllegalArgumentException("openedFiles must be a positive long, was: " + openedFiles);
+ }
+ this.openedFiles = openedFiles;
+ return this;
+ }
+
+ public LogBuilder app(App app) {
+ Objects.requireNonNull(app, "Valid app flavour must be provided");
+ this.app = app;
+ return this;
+ }
+
+ public LogBuilder mode(MvnCmds mode) {
+ Objects.requireNonNull(mode, "Valid app flavour must be provided");
+ this.mode = mode;
+ return this;
+ }
+
+ public Log build() {
+ StringBuilder h = new StringBuilder(512);
+ StringBuilder l = new StringBuilder(512);
+ int sections = 0;
+ if (app != null) {
+ h.append(appHeader);
+ h.append(',');
+ l.append(app);
+ l.append(',');
+ sections++;
+ }
+ if (mode != null) {
+ h.append(modeHeader);
+ h.append(',');
+ l.append(mode);
+ l.append(',');
+ sections++;
+ }
+ if (buildTimeMs != -1L) {
+ h.append(buildTimeMsHeader);
+ h.append(',');
+ l.append(buildTimeMs);
+ l.append(',');
+ sections++;
+ }
+ if (timeToFirstOKRequestMs != -1L) {
+ h.append(timeToFirstOKRequestMsHeader);
+ h.append(',');
+ l.append(timeToFirstOKRequestMs);
+ l.append(',');
+ sections++;
+ }
+ if (timeToReloadedOKRequest != -1L) {
+ h.append(timeToReloadedOKRequestHeader);
+ h.append(',');
+ l.append(timeToReloadedOKRequest);
+ l.append(',');
+ sections++;
+ }
+ if (startedInMs != -1L) {
+ h.append(startedInMsHeader);
+ h.append(',');
+ l.append(startedInMs);
+ l.append(',');
+ sections++;
+ }
+ if (stoppedInMs != -1L) {
+ h.append(stoppedInMsHeader);
+ h.append(',');
+ l.append(stoppedInMs);
+ l.append(',');
+ sections++;
+ }
+ if (rssKb != -1L) {
+ h.append(rssKbHeader);
+ h.append(',');
+ l.append(rssKb);
+ l.append(',');
+ sections++;
+ }
+ if (rssKbFinal != -1L) {
+ h.append(rssKbFinalHeader);
+ h.append(',');
+ l.append(rssKbFinal);
+ l.append(',');
+ sections++;
+ }
+ if (openedFiles != -1L) {
+ h.append(openedFilesHeader);
+ h.append(',');
+ l.append(openedFiles);
+ l.append(',');
+ sections++;
+ }
+ String header = h.toString();
+ // Strip trailing ',' for CSV
+ String headerCSV = header.substring(0, header.length() - 1);
+ String headerMarkdown = "|" + header.replaceAll(",", "|") + "\n|" + " --- |".repeat(sections);
+ String line = l.toString();
+ String lineCSV = line.substring(0, line.length() - 1);
+ String lineMarkdown = "|" + line.replaceAll(",", "|");
+ return new Log(headerCSV, headerMarkdown, lineCSV, lineMarkdown);
+ }
+}
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/Logs.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/Logs.java
new file mode 100644
index 0000000..dba7874
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/Logs.java
@@ -0,0 +1,336 @@
+package org.kie.kogito.benchmarks.framework;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jboss.logging.Logger;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.kie.kogito.benchmarks.framework.Commands.BASE_DIR;
+import static org.kie.kogito.benchmarks.framework.Commands.isThisWindows;
+
+public class Logs {
+ private static final Logger LOGGER = Logger.getLogger(Logs.class.getName());
+
+ public static final String jarSuffix = "redhat";
+ private static final Pattern jarNamePattern = Pattern.compile("^((?!" + jarSuffix + ").)*jar$");
+
+ private static final Pattern startedPattern = Pattern.compile(".* started in ([0-9\\.]+)s.*", Pattern.DOTALL);
+ private static final Pattern stoppedPattern = Pattern.compile(".* stopped in ([0-9\\.]+)s.*", Pattern.DOTALL);
+ /*
+ * Due to console colouring, Windows has control characters in the sequence.
+ * So "1.778s" in "started in 1.778s." becomes "[38;5;188m1.778".
+ * e.g.
+ * //started in [38;5;188m1.228[39ms.
+ * //stopped in [38;5;188m0.024[39ms[39m[38;5;203m[39m[38;5;227m
+ *
+ * Although when run from Jenkins service account; those symbols might not be present
+ * depending on whether you checked AllowInteractingWithDesktop.
+ * // TODO to make it smoother?
+ */
+ private static final Pattern startedPatternControlSymbols = Pattern.compile(".* started in .*188m([0-9\\.]+).*", Pattern.DOTALL);
+ private static final Pattern stoppedPatternControlSymbols = Pattern.compile(".* stopped in .*188m([0-9\\.]+).*", Pattern.DOTALL);
+
+ private static final Pattern warnErrorDetectionPattern = Pattern.compile("(?i:.*(ERROR|WARN|SLF4J:).*)");
+ private static final Pattern listeningOnDetectionPattern = Pattern.compile("(?i:.*Listening on:.*)");
+ private static final Pattern devExpectedHostPattern = Pattern.compile("(?i:.*localhost:.*)");
+ private static final Pattern defaultExpectedHostPattern = Pattern.compile("(?i:.*0.0.0.0:.*)");
+
+ public static final long SKIP = -1L;
+
+ public static void checkLog(String testClass, String testMethod, App app, MvnCmds cmd, File log) throws IOException {
+ try (Scanner sc = new Scanner(log, UTF_8)) {
+ Set<String> offendingLines = new HashSet<>();
+ while (sc.hasNextLine()) {
+ String line = sc.nextLine();
+ boolean error = warnErrorDetectionPattern.matcher(line).matches();
+ if (error) {
+ if (isWhiteListed(app.whitelistLogLines.errs, line)) {
+ LOGGER.info(cmd.name() + " log for " + testMethod + " contains whitelisted error: `" + line + "'");
+ } else if (isWhiteListed(app.whitelistLogLines.platformErrs(), line)) {
+ LOGGER.info(cmd.name() + " log for " + testMethod + " contains platform specific whitelisted error: `" + line + "'");
+ } else {
+ offendingLines.add(line);
+ }
+ }
+ }
+ assertTrue(offendingLines.isEmpty(),
+ cmd.name() + " log should not contain error or warning lines that are not whitelisted. " +
+ "See testsuite" + File.separator + "target" + File.separator + "archived-logs" +
+ File.separator + testClass + File.separator + testMethod + File.separator + log.getName() +
+ " and check these offending lines: \n" + String.join("\n", offendingLines));
+ }
+ }
+
+ public static void checkListeningHost(String testClass, String testMethod, MvnCmds cmd, File log) throws IOException {
+ boolean isOffending = true;
+ try (Scanner sc = new Scanner(log, UTF_8)) {
+ while (sc.hasNextLine()) {
+ String line = sc.nextLine();
+ if (listeningOnDetectionPattern.matcher(line).matches()) {
+ Pattern expectedHostPattern = defaultExpectedHostPattern;
+ if (cmd == MvnCmds.DEV || cmd == MvnCmds.MVNW_DEV) {
+ expectedHostPattern = devExpectedHostPattern;
+ }
+
+ isOffending = !expectedHostPattern.matcher(line).matches();
+ }
+ }
+ }
+
+ assertFalse(isOffending,
+ cmd.name() + " log should contain expected listening host. " +
+ "See testsuite" + File.separator + "target" + File.separator + "archived-logs" +
+ File.separator + testClass + File.separator + testMethod + File.separator + log.getName() +
+ " and check the listening host.");
+ }
+
+ private static boolean isWhiteListed(Pattern[] patterns, String line) {
+ for (Pattern p : patterns) {
+ if (p.matcher(line).matches()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // public static void checkJarSuffixes(Set<TestFlags> flags, File appDir) throws IOException {
+ // if (flags.contains(TestFlags.PRODUCT_BOM) || flags.contains(TestFlags.UNIVERSE_PRODUCT_BOM)) {
+ // List<Path> possiblyUnwantedArtifacts = Logs.listJarsFailingNameCheck(
+ // appDir.getAbsolutePath() + File.separator + "target" + File.separator + "lib");
+ // List<String> reportArtifacts = new ArrayList<>();
+ // boolean containsNotWhitelisted = false;
+ // for (Path p : possiblyUnwantedArtifacts) {
+ // boolean found = false;
+ // for (String w : WhitelistProductBomJars.PRODUCT_BOM.jarNames) {
+ // if (p.toString().contains(w)) {
+ // found = true;
+ // break;
+ // }
+ // }
+ // if (found) {
+ // reportArtifacts.add("WHITELISTED: " + p);
+ // } else {
+ // containsNotWhitelisted = true;
+ // reportArtifacts.add(p.toString());
+ // }
+ // }
+ // assertFalse(containsNotWhitelisted, "There are not-whitelisted artifacts without expected string " + jarSuffix + " suffix, see: \n"
+ // + String.join("\n", reportArtifacts));
+ // if (!reportArtifacts.isEmpty()) {
+ // LOGGER.warn("There are whitelisted artifacts without expected string " + jarSuffix + " suffix, see: \n"
+ // + String.join("\n", reportArtifacts));
+ // }
+ // }
+ // }
+
+ public static void checkThreshold(App app, MvnCmds cmd, long rssKb, long timeToFirstOKRequest, long timeToReloadedOKRequest) {
+ String propPrefix = isThisWindows ? "windows" : "linux";
+ if (cmd == MvnCmds.QUARKUS_JVM) {
+ propPrefix += ".jvm";
+ } else if (cmd == MvnCmds.NATIVE) {
+ propPrefix += ".native";
+ } else if (cmd == MvnCmds.DEV) {
+ propPrefix += ".dev";
+ // } else if (cmd == MvnCmds.GENERATOR) {
+ // propPrefix += ".generated.dev";
+ } else {
+ throw new IllegalArgumentException("Unexpected mode. Check MvnCmds.java.");
+ }
+ if (timeToFirstOKRequest != SKIP) {
+ long timeToFirstOKRequestThresholdMs = app.thresholdProperties.get(propPrefix + ".time.to.first.ok.request.threshold.ms");
+ assertTrue(timeToFirstOKRequest <= timeToFirstOKRequestThresholdMs,
+ "Application " + app + " in " + cmd + " mode took " + timeToFirstOKRequest
+ + " ms to get the first OK request, which is over " +
+ timeToFirstOKRequestThresholdMs + " ms threshold.");
+ }
+ if (rssKb != SKIP) {
+ long rssThresholdKb = app.thresholdProperties.get(propPrefix + ".RSS.threshold.kB");
+ assertTrue(rssKb <= rssThresholdKb,
+ "Application " + app + " in " + cmd + " consumed " + rssKb + " kB, which is over " +
+ rssThresholdKb + " kB threshold.");
+ }
+ if (timeToReloadedOKRequest != SKIP) {
+ long timeToReloadedOKRequestThresholdMs = app.thresholdProperties.get(propPrefix + ".time.to.reload.threshold.ms");
+ assertTrue(timeToReloadedOKRequest <= timeToReloadedOKRequestThresholdMs,
+ "Application " + app + " in " + cmd + " mode took " + timeToReloadedOKRequest
+ + " ms to get the first OK request after dev mode reload, which is over " +
+ timeToReloadedOKRequestThresholdMs + " ms threshold.");
+ }
+ }
+
+ public static void archiveLog(String testClass, String testMethod, File log) throws IOException {
+ if (log == null || !log.exists()) {
+ LOGGER.warn("log must be a valid, existing file. Skipping operation.");
+ return;
+ }
+ if (StringUtils.isBlank(testClass)) {
+ throw new IllegalArgumentException("testClass must not be blank");
+ }
+ if (StringUtils.isBlank(testMethod)) {
+ throw new IllegalArgumentException("testMethod must not be blank");
+ }
+ Path destDir = getLogsDir(testClass, testMethod);
+ Files.createDirectories(destDir);
+ String filename = log.getName();
+ Files.copy(log.toPath(), Paths.get(destDir.toString(), filename), REPLACE_EXISTING);
+ }
+
+ public static void writeReport(String testClass, String testMethod, String text) throws IOException {
+ Path destDir = getLogsDir(testClass, testMethod);
+ Files.createDirectories(destDir);
+ Files.write(Paths.get(destDir.toString(), "report.md"), text.getBytes(UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+ Path agregateReport = Paths.get(getLogsDir().toString(), "aggregated-report.md");
+ if (Files.notExists(agregateReport)) {
+ Files.write(agregateReport, ("# Aggregated Report\n\n").getBytes(UTF_8), StandardOpenOption.CREATE);
+ }
+ Files.write(agregateReport, text.getBytes(UTF_8), StandardOpenOption.APPEND);
+ }
+
+ /**
+ * Markdown needs two newlines to make a new paragraph.
+ */
+ public static void appendln(StringBuilder s, String text) {
+ s.append(text);
+ s.append("\n\n");
+ }
+
+ public static void appendlnSection(StringBuilder s, String text) {
+ s.append(text);
+ s.append("\n\n---\n");
+ }
+
+ public static Path getLogsDir(String testClass, String testMethod) throws IOException {
+ Path destDir = new File(getLogsDir(testClass).toString() + File.separator + testMethod).toPath();
+ Files.createDirectories(destDir);
+ return destDir;
+ }
+
+ public static Path getLogsDir(String testClass) throws IOException {
+ Path destDir = new File(getLogsDir().toString() + File.separator + testClass).toPath();
+ Files.createDirectories(destDir);
+ return destDir;
+ }
+
+ public static Path getLogsDir() throws IOException {
+ Path destDir = new File(BASE_DIR + File.separator + "testsuite" + File.separator + "target" +
+ File.separator + "archived-logs").toPath();
+ Files.createDirectories(destDir);
+ return destDir;
+ }
+
+ public static void logMeasurements(LogBuilder.Log log, Path path) throws IOException {
+ if (Files.notExists(path)) {
+ Files.write(path, (log.headerCSV + "\n").getBytes(UTF_8), StandardOpenOption.CREATE);
+ }
+ Files.write(path, (log.lineCSV + "\n").getBytes(UTF_8), StandardOpenOption.APPEND);
+ LOGGER.info("\n" + log.headerCSV + "\n" + log.lineCSV);
+ }
+
+ public static void logMeasurementsSummary(LogBuilder.Log log, Path path) throws IOException {
+ if (Files.notExists(path)) {
+ Files.write(path, (log.headerCSV + "\n").getBytes(UTF_8), StandardOpenOption.CREATE);
+ Files.write(path, (log.lineCSV + "\n").getBytes(UTF_8), StandardOpenOption.APPEND);
+ return;
+ }
+
+ List<String> lines = Files.lines(path).collect(Collectors.toCollection(ArrayList::new));
+ String currentHeader = lines.get(0);
+ String headerWithoutAppAndMode = Arrays.stream(log.headerCSV.split(",")).skip(2).collect(Collectors.joining(","));
+ if (!currentHeader.contains(headerWithoutAppAndMode)) {
+ lines.set(0, currentHeader + "," + headerWithoutAppAndMode);
+ currentHeader = lines.get(0);
+ }
+
+ String lastLine = lines.get(lines.size() - 1);
+
+ long headerLength = currentHeader.chars().filter(value -> value == ',').count();
+ long lastLineLength = lastLine.chars().filter(value -> value == ',').count();
+ if (lastLineLength < headerLength) {
+ String newDataWithoutAppAndMode = Arrays.stream(log.lineCSV.split(",")).skip(2).collect(Collectors.joining(","));
+ lines.set(lines.size() - 1, lastLine + "," + newDataWithoutAppAndMode);
+ } else {
+ lines.add(log.lineCSV);
+ }
+
+ Files.write(path, (lines.stream().collect(Collectors.joining(System.lineSeparator())) + System.lineSeparator()).getBytes());
+
+ LOGGER.info("\n" + log.headerCSV + "\n" + log.lineCSV);
+ }
+
+ /**
+ * List Jar file names failing regexp pattern check
+ *
+ * Note the pattern is hardcoded to look for jars not containing word 'redhat',
+ * but it could be easily generalized if needed.
+ *
+ * @param path to the root of directory tree
+ * @return list of offending jar paths
+ */
+ public static List<Path> listJarsFailingNameCheck(String path) throws IOException {
+ return Files.find(Paths.get(path),
+ 500, //if this is not enough, something is broken anyway
+ (filePath, fileAttr) -> fileAttr.isRegularFile() && jarNamePattern.matcher(filePath.getFileName().toString()).matches())
+ .collect(Collectors.toList());
+ }
+
+ public static float[] parseStartStopTimestamps(File log) throws IOException {
+ float[] startedStopped = new float[] { -1f, -1f };
+ try (Scanner sc = new Scanner(log, UTF_8)) {
+ while (sc.hasNextLine()) {
+ String line = sc.nextLine();
+
+ Matcher m = startedPatternControlSymbols.matcher(line);
+ if (startedStopped[0] == -1f && m.matches()) {
+ startedStopped[0] = Float.parseFloat(m.group(1));
+ continue;
+ }
+
+ m = startedPattern.matcher(line);
+ if (startedStopped[0] == -1f && m.matches()) {
+ startedStopped[0] = Float.parseFloat(m.group(1));
+ continue;
+ }
+
+ m = stoppedPatternControlSymbols.matcher(line);
+ if (startedStopped[1] == -1f && m.matches()) {
+ startedStopped[1] = Float.parseFloat(m.group(1));
+ continue;
+ }
+
+ m = stoppedPattern.matcher(line);
+ if (startedStopped[1] == -1f && m.matches()) {
+ startedStopped[1] = Float.parseFloat(m.group(1));
+ }
+ }
+ }
+ if (startedStopped[0] == -1f) {
+ LOGGER.error("Parsing start time from log failed. " +
+ "Might not be the right time to call this method. The process might have ben killed before it wrote to log." +
+ "Find " + log.getName() + " in your target dir.");
+ }
+ if (startedStopped[1] == -1f) {
+ LOGGER.error("Parsing stop time from log failed. " +
+ "Might not be the right time to call this method. The process might have been killed before it wrote to log." +
+ "Find " + log.getName() + " in your target dir.");
+ }
+ return startedStopped;
+ }
+}
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/MvnCmds.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/MvnCmds.java
new file mode 100644
index 0000000..e4bbeb4
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/MvnCmds.java
@@ -0,0 +1,53 @@
+package org.kie.kogito.benchmarks.framework;
+
+import java.util.stream.Stream;
+
+import static org.kie.kogito.benchmarks.framework.Commands.getLocalMavenRepoDir;
+import static org.kie.kogito.benchmarks.framework.Commands.getQuarkusNativeProperties;
+
+public enum MvnCmds {
+ QUARKUS_JVM(new String[][] {
+ new String[] { "mvn", "clean", "package", /* "quarkus:build", */"-Dquarkus.package.output-name=quarkus" },
+ new String[] { "java", "-jar", "target/quarkus-runner.jar" }
+ }),
+ SPRING_BOOT_JVM(new String[][] {
+ new String[] { "mvn", "clean", "package", /* "quarkus:build", */"-Dquarkus.package.output-name=quarkus" },
+ new String[] { "java", "-jar", "target/quarkus-runner.jar" }
+ }),
+ DEV(new String[][] {
+ new String[] { "mvn", "clean", "quarkus:dev", "-Dmaven.repo.local=" + getLocalMavenRepoDir() }
+ }),
+ NATIVE(new String[][] {
+ Stream.concat(Stream.of("mvn", "clean", "compile", "package", "-Pnative"),
+ getQuarkusNativeProperties().stream()).toArray(String[]::new),
+ new String[] { Commands.isThisWindows ? "target\\quarkus-runner" : "./target/quarkus-runner" }
+ }),
+ // GENERATOR(new String[][] {
+ // new String[] {
+ // "mvn",
+ // "io.quarkus:quarkus-maven-plugin:" + getQuarkusVersion() + ":create",
+ // "-DprojectGroupId=my-groupId",
+ // "-DprojectArtifactId=" + App.GENERATED_SKELETON.dir,
+ // "-DprojectVersion=1.0.0-SNAPSHOT",
+ // "-DpackageName=org.my.group"
+ // }
+ // }),
+ MVNW_DEV(new String[][] {
+ new String[] { Commands.MVNW, "quarkus:dev" }
+ }),
+ MVNW_JVM(new String[][] {
+ new String[] { Commands.MVNW, "clean", "compile", "quarkus:build", "-Dquarkus.package.output-name=quarkus" },
+ new String[] { "java", "-jar", "target/quarkus-app/quarkus-run.jar" }
+ }),
+ MVNW_NATIVE(new String[][] {
+ Stream.concat(Stream.of(Commands.MVNW, "clean", "compile", "package", "-Pnative", "-Dquarkus.package.output-name=quarkus"),
+ getQuarkusNativeProperties().stream()).toArray(String[]::new),
+ new String[] { Commands.isThisWindows ? "target\\quarkus-runner" : "./target/quarkus-runner" }
+ });
+
+ public final String[][] mvnCmds;
+
+ MvnCmds(String[][] mvnCmds) {
+ this.mvnCmds = mvnCmds;
+ }
+}
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/RunInfo.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/RunInfo.java
new file mode 100644
index 0000000..e7f4599
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/RunInfo.java
@@ -0,0 +1,28 @@
+package org.kie.kogito.benchmarks.framework;
+
+import java.io.File;
+
+public class RunInfo {
+
+ private final Process process;
+ private final File runLog;
+ private final long timeToFirstOKRequest;
+
+ public RunInfo(Process process, File runLog, long timeToFirstOKRequest) {
+ this.process = process;
+ this.runLog = runLog;
+ this.timeToFirstOKRequest = timeToFirstOKRequest;
+ }
+
+ public Process getProcess() {
+ return process;
+ }
+
+ public File getRunLog() {
+ return runLog;
+ }
+
+ public long getTimeToFirstOKRequest() {
+ return timeToFirstOKRequest;
+ }
+}
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/URLContent.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/URLContent.java
new file mode 100644
index 0000000..d44dc8e
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/URLContent.java
@@ -0,0 +1,33 @@
+package org.kie.kogito.benchmarks.framework;
+
+public enum URLContent {
+ SAMPLE_KOGITO_APP(new String[][] { new String[] { "http://localhost:8080/LoanApplication", "[]" },
+ new String[] { "http://localhost:8080/greeting", "1" } }),
+ JAX_RS_MINIMAL(new String[][] {
+ new String[] { "http://localhost:8080", "Hello from a simple JAX-RS app." },
+ new String[] { "http://localhost:8080/data/hello", "Hello World" }
+ }),
+ FULL_MICROPROFILE(new String[][] {
+ new String[] { "http://localhost:8080", "Hello from a full MicroProfile suite" },
+ new String[] { "http://localhost:8080/data/hello", "Hello World" },
+ new String[] { "http://localhost:8080/data/config/injected", "Config value as Injected by CDI Injected value" },
+ new String[] { "http://localhost:8080/data/config/lookup", "Config value from ConfigProvider lookup value" },
+ new String[] { "http://localhost:8080/data/resilience", "Fallback answer due to timeout" },
+ new String[] { "http://localhost:8080/health", "\"UP\"" },
+ new String[] { "http://localhost:8080/data/metric/timed", "Request is used in statistics, check with the Metrics call." },
+ new String[] { "http://localhost:8080/metrics", "ontroller_timed_request_seconds_count" },
+ new String[] { "http://localhost:8080/data/secured/test", "Jessie specific value" },
+ new String[] { "http://localhost:8080/openapi", "/resilience" },
+ new String[] { "http://localhost:8080/data/client/test/parameterValue=xxx", "Processed parameter value 'parameterValue=xxx'" }
+ }),
+ GENERATED_SKELETON(new String[][] {
+ new String[] { "http://localhost:8080", "Congratulations" },
+ new String[] { "http://localhost:8080/hello-spring", "Bye Spring" }
+ });
+
+ public final String[][] urlContent;
+
+ URLContent(String[][] urlContent) {
+ this.urlContent = urlContent;
+ }
+}
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/WebpageTester.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/WebpageTester.java
new file mode 100644
index 0000000..f88118e
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/WebpageTester.java
@@ -0,0 +1,78 @@
+package org.kie.kogito.benchmarks.framework;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.Scanner;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jboss.logging.Logger;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class WebpageTester {
+ private static final Logger LOGGER = Logger.getLogger(WebpageTester.class.getName());
+
+ /**
+ * Patiently try to wait for a web page and examine it
+ *
+ * @param url address
+ * @param timeoutS in seconds
+ * @param stringToLookFor string must be present on the page
+ */
+ public static long testWeb(String url, long timeoutS, String stringToLookFor, boolean measureTime) throws InterruptedException, IOException {
+ if (StringUtils.isBlank(url)) {
+ throw new IllegalArgumentException("url must not be empty");
+ }
+ if (timeoutS < 0) {
+ throw new IllegalArgumentException("timeoutS must be positive");
+ }
+ if (StringUtils.isBlank(stringToLookFor)) {
+ throw new IllegalArgumentException("stringToLookFor must contain a non-empty string");
+ }
+ String webPage = "";
+
+ long now = System.currentTimeMillis();
+ System.out.println("Now in the testWeb method: " + now);
+ final long startTime = now;
+ boolean found = false;
+ long foundTimestamp = -1L;
+ while (now - startTime < 1000 * timeoutS) {
+ URLConnection c = new URL(url).openConnection();
+ c.setRequestProperty("Accept", "*/*");
+ c.setConnectTimeout(500);
+ long requestStart = System.currentTimeMillis();
+ try (Scanner scanner = new Scanner(c.getInputStream(), StandardCharsets.UTF_8.toString())) {
+ scanner.useDelimiter("\\A");
+ webPage = scanner.hasNext() ? scanner.next() : "";
+ } catch (Exception e) {
+ LOGGER.debug("Waiting `" + stringToLookFor + "' to appear on " + url);
+ }
+ if (webPage.contains(stringToLookFor)) {
+ found = true;
+ if (measureTime) {
+ foundTimestamp = System.currentTimeMillis();
+ System.out.println("Found timestamp " + foundTimestamp);
+ System.out.println("Request took " + (foundTimestamp - requestStart));
+ }
+ break;
+ }
+ if (!measureTime) {
+ Thread.sleep(500);
+ } else {
+ Thread.sleep(0, 100000);
+ }
+ now = System.currentTimeMillis();
+ }
+
+ String failureMessage = "Timeout " + timeoutS + "s was reached. " +
+ (StringUtils.isNotBlank(webPage) ? webPage + " must contain string: " : "Empty webpage does not contain string: ") +
+ "`" + stringToLookFor + "'";
+ if (!found) {
+ LOGGER.info(failureMessage);
+ }
+ assertTrue(found, failureMessage);
+ return foundTimestamp - startTime;
+ }
+}
diff --git a/framework/src/main/java/org/kie/kogito/benchmarks/framework/WhitelistLogLines.java b/framework/src/main/java/org/kie/kogito/benchmarks/framework/WhitelistLogLines.java
new file mode 100644
index 0000000..41aa220
--- /dev/null
+++ b/framework/src/main/java/org/kie/kogito/benchmarks/framework/WhitelistLogLines.java
@@ -0,0 +1,112 @@
+package org.kie.kogito.benchmarks.framework;
+
+import java.util.regex.Pattern;
+
+/**
+ * Whitelists errors in log files.
+ *
+ * @author Michal Karm Babacek <ka...@redhat.com>
+ */
+public enum WhitelistLogLines {
+ SAMPLE_KOGITO_APP(new Pattern[] { Pattern.compile(".*") }),
+ JAX_RS_MINIMAL(new Pattern[] {
+ // Some artifacts names...
+ Pattern.compile(".*maven-error-diagnostics.*"),
+ Pattern.compile(".*errorprone.*"),
+ }),
+ FULL_MICROPROFILE(new Pattern[] {
+ // Some artifacts names...
+ Pattern.compile(".*maven-error-diagnostics.*"),
+ Pattern.compile(".*errorprone.*"),
+ // Needs fixing in the demo app?
+ Pattern.compile(".*TestSecureController.java.*"),
+ // Well, the RestClient demo probably should do some cleanup before shutdown...?
+ Pattern.compile(".*Closing a class org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient.*"),
+ }),
+ GENERATED_SKELETON(new Pattern[] {
+ // It so happens that the dummy skeleton tries to find Mongo. This is expected.
+ // See app-generated-skeleton/README.md for explanation of the scope.
+ Pattern.compile(".*The remote computer refused the network connection.*"),
+ // Harmless warning
+ Pattern.compile(".*The Agroal dependency is present but no JDBC datasources have been defined.*"),
+ // Due to our not exactly accurate application.properties, these expected warnings occur...
+ Pattern.compile(".*Unrecognized configuration key[ \\\\\"]*(" +
+ "quarkus.oidc.auth-server-url|" +
+ "quarkus.oidc.client-id|" +
+ "quarkus.oidc-client.auth-server-url|" +
+ "quarkus.oidc-client.client-id|" +
+ "quarkus.oidc-client.token-path|" +
+ "quarkus.oidc-client.discovery-enabled|" +
+ "quarkus.smallrye-jwt.enabled|" +
+ "quarkus.jaeger.service-name|" +
+ "quarkus.jaeger.sampler-param|" +
+ "quarkus.jaeger.endpoint|" +
+ "quarkus.jaeger.sampler-type" +
+ ")[ \\\\\"]*was provided.*"),
+ // Some artifacts names...
+ Pattern.compile(".*maven-error-diagnostics.*"),
+ Pattern.compile(".*errorprone.*"),
+ Pattern.compile(".*google-cloud-errorreporting-bom.*"),
+ // When GraalVM is used; unrelated to the test
+ Pattern.compile(".*forcing TieredStopAtLevel to full optimization because JVMCI is enabled.*"),
+ Pattern.compile(".*error_prone_annotations.*"),
+ Pattern.compile(".*SRGQL010000: Schema is null, or it has no operations. Not bootstrapping SmallRye GraphQL*"),
+ Pattern.compile(".*No WebJars were found in the project.*"),
+ Pattern.compile(
+ ".*This application uses the MP Metrics API. The micrometer extension currently provides a compatibility layer that supports the MP Metrics API, but metric names and recorded values will be different. Note that the MP Metrics compatibility layer will move to a different extension in the future.*"),
+ // kubernetes-client tries to configure client from service account
+ Pattern.compile(".*Error reading service account token from.*"),
+ // hibernate-orm issues this warning when default datasource is ambiguous
+ // (no explicit configuration, none or multiple JDBC driver extensions)
+ // Result of DevServices support https://github.com/quarkusio/quarkus/pull/14960
+ Pattern.compile(".*Unable to determine a database type for default datasource.*"),
+ });
+
+ public final Pattern[] errs;
+
+ WhitelistLogLines(Pattern[] errs) {
+ this.errs = errs;
+ }
+
+ public final Pattern[] platformErrs() {
+ switch (OS.current()) {
+ case MAC:
+ return new Pattern[] {
+ Pattern.compile(".*objcopy executable not found in PATH. Debug symbols will not be separated from executable.*"),
+ Pattern.compile(".*That will result in a larger native image with debug symbols embedded in it.*"),
+ };
+ }
+ return new Pattern[] {};
+ }
+
+ enum OS {
+ MAC,
+ LINUX,
+ WINDOWS,
+ UNKNOWN;
+
+ public static OS current() {
+ if (isMac()) {
+ return MAC;
+ } else if (isWindows()) {
+ return WINDOWS;
+ } else if (isLinux()) {
+ return LINUX;
+ } else {
+ return UNKNOWN;
+ }
+ }
+ }
+
+ private static boolean isMac() {
+ return System.getProperty("os.name").toLowerCase().contains("mac");
+ }
+
+ private static boolean isWindows() {
+ return System.getProperty("os.name").toLowerCase().contains("windows");
+ }
+
+ private static boolean isLinux() {
+ return System.getProperty("os.name").toLowerCase().contains("linux");
+ }
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..c37181a
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.kie.kogito</groupId>
+ <artifactId>kogito-build-parent</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>kogito-benchmarks</artifactId>
+ <packaging>pom</packaging>
+
+ <name></name>
+ <description></description>
+
+ <properties>
+ <maven.compiler.release>11</maven.compiler.release> <!-- Mainly because of the additions to the Process API introduced in Java 9 -->
+ </properties>
+
+ <modules>
+ <module>framework</module>
+ <module>sample-kogito-app</module>
+ <module>tests</module>
+ </modules>
+
+
+</project>
\ No newline at end of file
diff --git a/sample-kogito-app/pom.xml b/sample-kogito-app/pom.xml
new file mode 100644
index 0000000..e33c2b2
--- /dev/null
+++ b/sample-kogito-app/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.kie.kogito</groupId>
+ <artifactId>kogito-benchmarks</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>sample-kogito-app</artifactId>
+
+ <properties>
+ <!-- Skip tests by default -->
+ <skipTests>true</skipTests>
+ <quarkus.version>1.11.5.Final</quarkus.version>
+ <kogito.version>1.4.1.Final</kogito.version>
+ <surefire.version>2.22.2</surefire.version>
+ <compiler.version>3.8.1</compiler.version>
+
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.release>11</maven.compiler.release>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.kie.kogito</groupId>
+ <artifactId>kogito-quarkus-bom</artifactId>
+ <version>${kogito.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.kie.kogito</groupId>
+ <artifactId>kogito-quarkus</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-resteasy</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-resteasy-jackson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-smallrye-openapi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-smallrye-health</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-arc</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-junit5</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.rest-assured</groupId>
+ <artifactId>rest-assured</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>${compiler.version}</version>
+ <configuration>
+ <release>11</release>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>${surefire.version}</version>
+ <configuration>
+ <systemPropertyVariables>
+ <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+ <maven.home>${maven.home}</maven.home>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-maven-plugin</artifactId>
+ <version>${quarkus.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>build</goal>
+ <goal>generate-code</goal>
+ <goal>generate-code-tests</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/sample-kogito-app/src/main/java/mypackage/GreetingEndpoint.java b/sample-kogito-app/src/main/java/mypackage/GreetingEndpoint.java
new file mode 100644
index 0000000..812c1b5
--- /dev/null
+++ b/sample-kogito-app/src/main/java/mypackage/GreetingEndpoint.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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 mypackage;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.enterprise.event.Observes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import io.quarkus.runtime.StartupEvent;
+
+@Path("/")
+public class GreetingEndpoint {
+
+ private static final String template = "Hello, %s!";
+
+ @GET
+ @Path("/greeting")
+ @Produces(MediaType.APPLICATION_JSON)
+ public String greeting(@QueryParam("name") String name) {
+ System.out.println(new SimpleDateFormat("HH:mm:ss.SSS").format(new java.util.Date(System.currentTimeMillis())));
+ String suffix = name != null ? name : "World";
+ return String.valueOf(System.currentTimeMillis());
+ }
+
+ void onStart(@Observes StartupEvent startup) {
+ System.out.println(new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()));
+ }
+}
\ No newline at end of file
diff --git a/sample-kogito-app/src/main/resources/LoanApplication.bpmn b/sample-kogito-app/src/main/resources/LoanApplication.bpmn
new file mode 100644
index 0000000..df67fc3
--- /dev/null
+++ b/sample-kogito-app/src/main/resources/LoanApplication.bpmn
@@ -0,0 +1,259 @@
+<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:bpsim="http://www.bpsim.org/schemas/1.0" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:drools="http://www.jboss.org/drools" id="_an5RsHt5EDmPCqW-qXr3vw" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd http://www. [...]
+ <bpmn2:itemDefinition id="_amountItem" structureRef="Integer"/>
+ <bpmn2:itemDefinition id="_approvedItem" structureRef="Boolean"/>
+ <bpmn2:itemDefinition id="__A27A8002-A5DB-4448-9DC8-FC702E9AF322_namespaceInputXItem" structureRef="java.lang.String"/>
+ <bpmn2:itemDefinition id="__A27A8002-A5DB-4448-9DC8-FC702E9AF322_modelInputXItem" structureRef="java.lang.String"/>
+ <bpmn2:itemDefinition id="__A27A8002-A5DB-4448-9DC8-FC702E9AF322_decisionInputXItem" structureRef="java.lang.String"/>
+ <bpmn2:itemDefinition id="__A27A8002-A5DB-4448-9DC8-FC702E9AF322_amountInputXItem" structureRef="Integer"/>
+ <bpmn2:itemDefinition id="__A27A8002-A5DB-4448-9DC8-FC702E9AF322_mortgageApprovedOutputXItem" structureRef="Boolean"/>
+ <bpmn2:process id="LoanApplication" drools:packageName="com.example" drools:version="1.0" drools:adHoc="false" name="LoanApplication" isExecutable="true" processType="Public">
+ <bpmn2:property id="amount" itemSubjectRef="_amountItem" name="amount">
+ <bpmn2:extensionElements>
+ <drools:metaData name="customTags">
+ <drools:metaValue><![CDATA[tracked]]></drools:metaValue>
+ </drools:metaData>
+ </bpmn2:extensionElements>
+ </bpmn2:property>
+ <bpmn2:property id="approved" itemSubjectRef="_approvedItem" name="approved">
+ <bpmn2:extensionElements>
+ <drools:metaData name="customTags">
+ <drools:metaValue><![CDATA[tracked]]></drools:metaValue>
+ </drools:metaData>
+ </bpmn2:extensionElements>
+ </bpmn2:property>
+ <bpmn2:sequenceFlow id="_CA9E497D-E2DE-47B3-8CB6-7CEB6BCC302A" sourceRef="_293C116F-8607-4D9C-AFFE-9901DF5B9D33" targetRef="_E7D37FE2-7E1C-4AE2-8D5B-22D6AF4F0F6E"/>
+ <bpmn2:sequenceFlow id="_2C3AB5A8-9862-4AEB-8B74-7274A67CAB71" sourceRef="_7CA472D9-AB07-42B5-9D05-C2F6661F033C" targetRef="_C4FB5974-F764-42C7-95AA-DE6F6F14D167"/>
+ <bpmn2:sequenceFlow id="_33084556-2A2C-44ED-92D6-53C4957E6670" sourceRef="_7CA472D9-AB07-42B5-9D05-C2F6661F033C" targetRef="_293C116F-8607-4D9C-AFFE-9901DF5B9D33">
+ <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression" language="http://www.java.com/java"><![CDATA[return approved;]]></bpmn2:conditionExpression>
+ </bpmn2:sequenceFlow>
+ <bpmn2:sequenceFlow id="_CC99D2A0-69E2-4488-A0DB-F01FB1DBE2CD" sourceRef="_A27A8002-A5DB-4448-9DC8-FC702E9AF322" targetRef="_7CA472D9-AB07-42B5-9D05-C2F6661F033C"/>
+ <bpmn2:sequenceFlow id="_C7CF5D2E-E5B1-4240-8DA8-4B75CE6EB936" sourceRef="_C4FB5974-F764-42C7-95AA-DE6F6F14D167" targetRef="_C8496017-134C-4C70-9D16-9E67C7D3CEBF"/>
+ <bpmn2:sequenceFlow id="_6FC874CA-F759-4F96-AE0E-9C397789E1C2" sourceRef="_0002A37F-EAEE-447C-A9D2-11458967B7AE" targetRef="_A27A8002-A5DB-4448-9DC8-FC702E9AF322">
+ <bpmn2:extensionElements>
+ <drools:metaData name="isAutoConnection.source">
+ <drools:metaValue><![CDATA[true]]></drools:metaValue>
+ </drools:metaData>
+ <drools:metaData name="isAutoConnection.target">
+ <drools:metaValue><![CDATA[true]]></drools:metaValue>
+ </drools:metaData>
+ </bpmn2:extensionElements>
+ </bpmn2:sequenceFlow>
+ <bpmn2:scriptTask id="_C4FB5974-F764-42C7-95AA-DE6F6F14D167" name="Declined" scriptFormat="http://www.java.com/java">
+ <bpmn2:extensionElements>
+ <drools:metaData name="elementname">
+ <drools:metaValue><![CDATA[Declined]]></drools:metaValue>
+ </drools:metaData>
+ </bpmn2:extensionElements>
+ <bpmn2:incoming>_2C3AB5A8-9862-4AEB-8B74-7274A67CAB71</bpmn2:incoming>
+ <bpmn2:outgoing>_C7CF5D2E-E5B1-4240-8DA8-4B75CE6EB936</bpmn2:outgoing>
+ <bpmn2:script>System.out.println("Mortgage has been DECLINED");</bpmn2:script>
+ </bpmn2:scriptTask>
+ <bpmn2:scriptTask id="_293C116F-8607-4D9C-AFFE-9901DF5B9D33" name="Approved" scriptFormat="http://www.java.com/java">
+ <bpmn2:extensionElements>
+ <drools:metaData name="elementname">
+ <drools:metaValue><![CDATA[Approved]]></drools:metaValue>
+ </drools:metaData>
+ </bpmn2:extensionElements>
+ <bpmn2:incoming>_33084556-2A2C-44ED-92D6-53C4957E6670</bpmn2:incoming>
+ <bpmn2:outgoing>_CA9E497D-E2DE-47B3-8CB6-7CEB6BCC302A</bpmn2:outgoing>
+ <bpmn2:script>System.out.println("Mortgage has been APPROVED");</bpmn2:script>
+ </bpmn2:scriptTask>
+ <bpmn2:endEvent id="_E7D37FE2-7E1C-4AE2-8D5B-22D6AF4F0F6E">
+ <bpmn2:incoming>_CA9E497D-E2DE-47B3-8CB6-7CEB6BCC302A</bpmn2:incoming>
+ </bpmn2:endEvent>
+ <bpmn2:exclusiveGateway id="_7CA472D9-AB07-42B5-9D05-C2F6661F033C" drools:dg="_2C3AB5A8-9862-4AEB-8B74-7274A67CAB71" gatewayDirection="Diverging" default="_2C3AB5A8-9862-4AEB-8B74-7274A67CAB71">
+ <bpmn2:incoming>_CC99D2A0-69E2-4488-A0DB-F01FB1DBE2CD</bpmn2:incoming>
+ <bpmn2:outgoing>_33084556-2A2C-44ED-92D6-53C4957E6670</bpmn2:outgoing>
+ <bpmn2:outgoing>_2C3AB5A8-9862-4AEB-8B74-7274A67CAB71</bpmn2:outgoing>
+ </bpmn2:exclusiveGateway>
+ <bpmn2:businessRuleTask id="_A27A8002-A5DB-4448-9DC8-FC702E9AF322" name="Mortgage Approval" implementation="http://www.jboss.org/drools/dmn">
+ <bpmn2:extensionElements>
+ <drools:metaData name="elementname">
+ <drools:metaValue><![CDATA[Mortgage Approval]]></drools:metaValue>
+ </drools:metaData>
+ </bpmn2:extensionElements>
+ <bpmn2:incoming>_6FC874CA-F759-4F96-AE0E-9C397789E1C2</bpmn2:incoming>
+ <bpmn2:outgoing>_CC99D2A0-69E2-4488-A0DB-F01FB1DBE2CD</bpmn2:outgoing>
+ <bpmn2:ioSpecification>
+ <bpmn2:dataInput id="_A27A8002-A5DB-4448-9DC8-FC702E9AF322_namespaceInputX" drools:dtype="java.lang.String" itemSubjectRef="__A27A8002-A5DB-4448-9DC8-FC702E9AF322_namespaceInputXItem" name="namespace"/>
+ <bpmn2:dataInput id="_A27A8002-A5DB-4448-9DC8-FC702E9AF322_decisionInputX" drools:dtype="java.lang.String" itemSubjectRef="__A27A8002-A5DB-4448-9DC8-FC702E9AF322_decisionInputXItem" name="decision"/>
+ <bpmn2:dataInput id="_A27A8002-A5DB-4448-9DC8-FC702E9AF322_modelInputX" drools:dtype="java.lang.String" itemSubjectRef="__A27A8002-A5DB-4448-9DC8-FC702E9AF322_modelInputXItem" name="model"/>
+ <bpmn2:dataInput id="_A27A8002-A5DB-4448-9DC8-FC702E9AF322_amountInputX" drools:dtype="Integer" itemSubjectRef="__A27A8002-A5DB-4448-9DC8-FC702E9AF322_amountInputXItem" name="amount"/>
+ <bpmn2:dataOutput id="_A27A8002-A5DB-4448-9DC8-FC702E9AF322_mortgageApprovedOutputX" drools:dtype="Boolean" itemSubjectRef="__A27A8002-A5DB-4448-9DC8-FC702E9AF322_mortgageApprovedOutputXItem" name="mortgageApproved"/>
+ <bpmn2:inputSet>
+ <bpmn2:dataInputRefs>_A27A8002-A5DB-4448-9DC8-FC702E9AF322_namespaceInputX</bpmn2:dataInputRefs>
+ <bpmn2:dataInputRefs>_A27A8002-A5DB-4448-9DC8-FC702E9AF322_decisionInputX</bpmn2:dataInputRefs>
+ <bpmn2:dataInputRefs>_A27A8002-A5DB-4448-9DC8-FC702E9AF322_modelInputX</bpmn2:dataInputRefs>
+ <bpmn2:dataInputRefs>_A27A8002-A5DB-4448-9DC8-FC702E9AF322_amountInputX</bpmn2:dataInputRefs>
+ </bpmn2:inputSet>
+ <bpmn2:outputSet>
+ <bpmn2:dataOutputRefs>_A27A8002-A5DB-4448-9DC8-FC702E9AF322_mortgageApprovedOutputX</bpmn2:dataOutputRefs>
+ </bpmn2:outputSet>
+ </bpmn2:ioSpecification>
+ <bpmn2:dataInputAssociation>
+ <bpmn2:targetRef>_A27A8002-A5DB-4448-9DC8-FC702E9AF322_namespaceInputX</bpmn2:targetRef>
+ <bpmn2:assignment>
+ <bpmn2:from xsi:type="bpmn2:tFormalExpression"><![CDATA[https://kiegroup.org/dmn/_28B07541-BCAA-4D8D-B754-05ED035496BA]]></bpmn2:from>
+ <bpmn2:to xsi:type="bpmn2:tFormalExpression"><![CDATA[_A27A8002-A5DB-4448-9DC8-FC702E9AF322_namespaceInputX]]></bpmn2:to>
+ </bpmn2:assignment>
+ </bpmn2:dataInputAssociation>
+ <bpmn2:dataInputAssociation>
+ <bpmn2:targetRef>_A27A8002-A5DB-4448-9DC8-FC702E9AF322_decisionInputX</bpmn2:targetRef>
+ <bpmn2:assignment>
+ <bpmn2:from xsi:type="bpmn2:tFormalExpression"><![CDATA[MortgageApproved]]></bpmn2:from>
+ <bpmn2:to xsi:type="bpmn2:tFormalExpression"><![CDATA[_A27A8002-A5DB-4448-9DC8-FC702E9AF322_decisionInputX]]></bpmn2:to>
+ </bpmn2:assignment>
+ </bpmn2:dataInputAssociation>
+ <bpmn2:dataInputAssociation>
+ <bpmn2:targetRef>_A27A8002-A5DB-4448-9DC8-FC702E9AF322_modelInputX</bpmn2:targetRef>
+ <bpmn2:assignment>
+ <bpmn2:from xsi:type="bpmn2:tFormalExpression"><![CDATA[MortgageApproval]]></bpmn2:from>
+ <bpmn2:to xsi:type="bpmn2:tFormalExpression"><![CDATA[_A27A8002-A5DB-4448-9DC8-FC702E9AF322_modelInputX]]></bpmn2:to>
+ </bpmn2:assignment>
+ </bpmn2:dataInputAssociation>
+ <bpmn2:dataInputAssociation>
+ <bpmn2:sourceRef>amount</bpmn2:sourceRef>
+ <bpmn2:targetRef>_A27A8002-A5DB-4448-9DC8-FC702E9AF322_amountInputX</bpmn2:targetRef>
+ </bpmn2:dataInputAssociation>
+ <bpmn2:dataOutputAssociation>
+ <bpmn2:sourceRef>_A27A8002-A5DB-4448-9DC8-FC702E9AF322_mortgageApprovedOutputX</bpmn2:sourceRef>
+ <bpmn2:targetRef>approved</bpmn2:targetRef>
+ </bpmn2:dataOutputAssociation>
+ </bpmn2:businessRuleTask>
+ <bpmn2:endEvent id="_C8496017-134C-4C70-9D16-9E67C7D3CEBF">
+ <bpmn2:incoming>_C7CF5D2E-E5B1-4240-8DA8-4B75CE6EB936</bpmn2:incoming>
+ </bpmn2:endEvent>
+ <bpmn2:startEvent id="_0002A37F-EAEE-447C-A9D2-11458967B7AE">
+ <bpmn2:outgoing>_6FC874CA-F759-4F96-AE0E-9C397789E1C2</bpmn2:outgoing>
+ </bpmn2:startEvent>
+ </bpmn2:process>
+ <bpmndi:BPMNDiagram>
+ <bpmndi:BPMNPlane bpmnElement="LoanApplication">
+ <bpmndi:BPMNShape id="shape__0002A37F-EAEE-447C-A9D2-11458967B7AE" bpmnElement="_0002A37F-EAEE-447C-A9D2-11458967B7AE">
+ <dc:Bounds height="56" width="56" x="134" y="186"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape id="shape__C8496017-134C-4C70-9D16-9E67C7D3CEBF" bpmnElement="_C8496017-134C-4C70-9D16-9E67C7D3CEBF">
+ <dc:Bounds height="56" width="56" x="830" y="262"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape id="shape__A27A8002-A5DB-4448-9DC8-FC702E9AF322" bpmnElement="_A27A8002-A5DB-4448-9DC8-FC702E9AF322">
+ <dc:Bounds height="102" width="154" x="270" y="163"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape id="shape__7CA472D9-AB07-42B5-9D05-C2F6661F033C" bpmnElement="_7CA472D9-AB07-42B5-9D05-C2F6661F033C">
+ <dc:Bounds height="56" width="56" x="504" y="186"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape id="shape__E7D37FE2-7E1C-4AE2-8D5B-22D6AF4F0F6E" bpmnElement="_E7D37FE2-7E1C-4AE2-8D5B-22D6AF4F0F6E">
+ <dc:Bounds height="56" width="56" x="830" y="102"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape id="shape__293C116F-8607-4D9C-AFFE-9901DF5B9D33" bpmnElement="_293C116F-8607-4D9C-AFFE-9901DF5B9D33">
+ <dc:Bounds height="102" width="154" x="618" y="79"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape id="shape__C4FB5974-F764-42C7-95AA-DE6F6F14D167" bpmnElement="_C4FB5974-F764-42C7-95AA-DE6F6F14D167">
+ <dc:Bounds height="102" width="154" x="618" y="239"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNEdge id="edge_shape__0002A37F-EAEE-447C-A9D2-11458967B7AE_to_shape__A27A8002-A5DB-4448-9DC8-FC702E9AF322" bpmnElement="_6FC874CA-F759-4F96-AE0E-9C397789E1C2">
+ <di:waypoint x="190" y="214"/>
+ <di:waypoint x="270" y="214"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge id="edge_shape__C4FB5974-F764-42C7-95AA-DE6F6F14D167_to_shape__C8496017-134C-4C70-9D16-9E67C7D3CEBF" bpmnElement="_C7CF5D2E-E5B1-4240-8DA8-4B75CE6EB936">
+ <di:waypoint x="695" y="290"/>
+ <di:waypoint x="830" y="290"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge id="edge_shape__A27A8002-A5DB-4448-9DC8-FC702E9AF322_to_shape__7CA472D9-AB07-42B5-9D05-C2F6661F033C" bpmnElement="_CC99D2A0-69E2-4488-A0DB-F01FB1DBE2CD">
+ <di:waypoint x="424" y="214"/>
+ <di:waypoint x="504" y="214"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge id="edge_shape__7CA472D9-AB07-42B5-9D05-C2F6661F033C_to_shape__293C116F-8607-4D9C-AFFE-9901DF5B9D33" bpmnElement="_33084556-2A2C-44ED-92D6-53C4957E6670">
+ <di:waypoint x="532" y="186"/>
+ <di:waypoint x="532" y="130"/>
+ <di:waypoint x="618" y="130"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge id="edge_shape__7CA472D9-AB07-42B5-9D05-C2F6661F033C_to_shape__C4FB5974-F764-42C7-95AA-DE6F6F14D167" bpmnElement="_2C3AB5A8-9862-4AEB-8B74-7274A67CAB71">
+ <di:waypoint x="532" y="242"/>
+ <di:waypoint x="532.0000000000026" y="290"/>
+ <di:waypoint x="618" y="290"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge id="edge_shape__293C116F-8607-4D9C-AFFE-9901DF5B9D33_to_shape__E7D37FE2-7E1C-4AE2-8D5B-22D6AF4F0F6E" bpmnElement="_CA9E497D-E2DE-47B3-8CB6-7CEB6BCC302A">
+ <di:waypoint x="695" y="130"/>
+ <di:waypoint x="830" y="130"/>
+ </bpmndi:BPMNEdge>
+ </bpmndi:BPMNPlane>
+ </bpmndi:BPMNDiagram>
+ <bpmn2:relationship type="BPSimData">
+ <bpmn2:extensionElements>
+ <bpsim:BPSimData>
+ <bpsim:Scenario id="default" name="Simulationscenario">
+ <bpsim:ScenarioParameters/>
+ <bpsim:ElementParameters elementRef="_0002A37F-EAEE-447C-A9D2-11458967B7AE">
+ <bpsim:TimeParameters>
+ <bpsim:ProcessingTime>
+ <bpsim:NormalDistribution mean="0" standardDeviation="0"/>
+ </bpsim:ProcessingTime>
+ </bpsim:TimeParameters>
+ </bpsim:ElementParameters>
+ <bpsim:ElementParameters elementRef="_A27A8002-A5DB-4448-9DC8-FC702E9AF322">
+ <bpsim:TimeParameters>
+ <bpsim:ProcessingTime>
+ <bpsim:NormalDistribution mean="0" standardDeviation="0"/>
+ </bpsim:ProcessingTime>
+ </bpsim:TimeParameters>
+ <bpsim:ResourceParameters>
+ <bpsim:Availability>
+ <bpsim:FloatingParameter value="0"/>
+ </bpsim:Availability>
+ <bpsim:Quantity>
+ <bpsim:FloatingParameter value="0"/>
+ </bpsim:Quantity>
+ </bpsim:ResourceParameters>
+ <bpsim:CostParameters>
+ <bpsim:UnitCost>
+ <bpsim:FloatingParameter value="0"/>
+ </bpsim:UnitCost>
+ </bpsim:CostParameters>
+ </bpsim:ElementParameters>
+ <bpsim:ElementParameters elementRef="_293C116F-8607-4D9C-AFFE-9901DF5B9D33">
+ <bpsim:TimeParameters>
+ <bpsim:ProcessingTime>
+ <bpsim:NormalDistribution mean="0" standardDeviation="0"/>
+ </bpsim:ProcessingTime>
+ </bpsim:TimeParameters>
+ <bpsim:ResourceParameters>
+ <bpsim:Availability>
+ <bpsim:FloatingParameter value="0"/>
+ </bpsim:Availability>
+ <bpsim:Quantity>
+ <bpsim:FloatingParameter value="0"/>
+ </bpsim:Quantity>
+ </bpsim:ResourceParameters>
+ <bpsim:CostParameters>
+ <bpsim:UnitCost>
+ <bpsim:FloatingParameter value="0"/>
+ </bpsim:UnitCost>
+ </bpsim:CostParameters>
+ </bpsim:ElementParameters>
+ <bpsim:ElementParameters elementRef="_C4FB5974-F764-42C7-95AA-DE6F6F14D167">
+ <bpsim:TimeParameters>
+ <bpsim:ProcessingTime>
+ <bpsim:NormalDistribution mean="0" standardDeviation="0"/>
+ </bpsim:ProcessingTime>
+ </bpsim:TimeParameters>
+ <bpsim:ResourceParameters>
+ <bpsim:Availability>
+ <bpsim:FloatingParameter value="0"/>
+ </bpsim:Availability>
+ <bpsim:Quantity>
+ <bpsim:FloatingParameter value="0"/>
+ </bpsim:Quantity>
+ </bpsim:ResourceParameters>
+ <bpsim:CostParameters>
+ <bpsim:UnitCost>
+ <bpsim:FloatingParameter value="0"/>
+ </bpsim:UnitCost>
+ </bpsim:CostParameters>
+ </bpsim:ElementParameters>
+ </bpsim:Scenario>
+ </bpsim:BPSimData>
+ </bpmn2:extensionElements>
+ <bpmn2:source>_an5RsHt5EDmPCqW-qXr3vw</bpmn2:source>
+ <bpmn2:target>_an5RsHt5EDmPCqW-qXr3vw</bpmn2:target>
+ </bpmn2:relationship>
+</bpmn2:definitions>
\ No newline at end of file
diff --git a/sample-kogito-app/src/main/resources/MortgageApproval.dmn b/sample-kogito-app/src/main/resources/MortgageApproval.dmn
new file mode 100644
index 0000000..3ca0ed3
--- /dev/null
+++ b/sample-kogito-app/src/main/resources/MortgageApproval.dmn
@@ -0,0 +1,81 @@
+<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/" xmlns="https://kiegroup.org/dmn/_28B07541-BCAA-4D8D-B754-05ED035496BA" xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/" xmlns:kie="http://www.drools.org/kie/dmn/1.2" xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/" xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/" xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/" id="_905E3E6C-C346-4DA3-AA28-9ED076578E03" name="MortgageApproval" typeLanguage="htt [...]
+ <dmn:extensionElements/>
+ <dmn:inputData id="_23558138-A90D-4685-9B08-1ABD1DDD0A2A" name="amount">
+ <dmn:extensionElements/>
+ <dmn:variable id="_F42026F3-B207-41FF-8177-9954910B16A7" name="amount"/>
+ </dmn:inputData>
+ <dmn:decision id="_B0928930-EC40-4726-B8B1-4D9D5AEF1C82" name="mortgageApproved">
+ <dmn:extensionElements/>
+ <dmn:variable id="_C54E9DA3-878C-4713-B790-606CB9C01721" name="mortgageApproved" typeRef="boolean"/>
+ <dmn:informationRequirement id="_AF4E07ED-DE25-4E44-BB37-3FDF52056005">
+ <dmn:requiredInput href="#_23558138-A90D-4685-9B08-1ABD1DDD0A2A"/>
+ </dmn:informationRequirement>
+ <dmn:decisionTable id="_1D72E97A-473F-460A-A2F1-6602AF93FE3B" hitPolicy="FIRST" preferredOrientation="Rule-as-Row">
+ <dmn:input id="_3E5D7B3E-6C23-46B0-B385-2362EEB99ADB">
+ <dmn:inputExpression id="_D4F176DB-19F7-4B89-A1C6-57C126B9C4C3" typeRef="number">
+ <dmn:text>amount</dmn:text>
+ </dmn:inputExpression>
+ </dmn:input>
+ <dmn:output id="_2F517F3D-D449-4AAF-B150-B8DF82E3A1C3"/>
+ <dmn:annotation name="Description"/>
+ <dmn:rule id="_8C26AAB7-6CCE-4BD3-9F40-A0B7F0CC3848">
+ <dmn:inputEntry id="_7E7C0CAF-CDCA-4313-BDA9-EE13BCA8CB84">
+ <dmn:text><5000</dmn:text>
+ </dmn:inputEntry>
+ <dmn:outputEntry id="_67260421-5E3A-4131-BB54-8ACA4B36B2C3">
+ <dmn:text>true</dmn:text>
+ </dmn:outputEntry>
+ <dmn:annotationEntry>
+ <dmn:text>Less than 5000 is OK</dmn:text>
+ </dmn:annotationEntry>
+ </dmn:rule>
+ <dmn:rule id="_55C03D40-47FF-43D9-A15E-B08E5C53AE0E">
+ <dmn:inputEntry id="_07A49F5D-8932-441D-8AF7-59C93E7E5752">
+ <dmn:text>>=5000</dmn:text>
+ </dmn:inputEntry>
+ <dmn:outputEntry id="_ABECCAE3-5CEA-45CC-B111-E504B0947318">
+ <dmn:text>false</dmn:text>
+ </dmn:outputEntry>
+ <dmn:annotationEntry>
+ <dmn:text>5000 and more is too much</dmn:text>
+ </dmn:annotationEntry>
+ </dmn:rule>
+ </dmn:decisionTable>
+ </dmn:decision>
+ <dmndi:DMNDI>
+ <dmndi:DMNDiagram id="_18B53C8E-F1CA-40CC-B2FF-4310382BDF60" name="DRG">
+ <di:extension>
+ <kie:ComponentsWidthsExtension>
+ <kie:ComponentWidths dmnElementRef="_1D72E97A-473F-460A-A2F1-6602AF93FE3B">
+ <kie:width>50</kie:width>
+ <kie:width>100</kie:width>
+ <kie:width>190</kie:width>
+ <kie:width>355</kie:width>
+ </kie:ComponentWidths>
+ </kie:ComponentsWidthsExtension>
+ </di:extension>
+ <dmndi:DMNShape id="dmnshape-drg-_23558138-A90D-4685-9B08-1ABD1DDD0A2A" dmnElementRef="_23558138-A90D-4685-9B08-1ABD1DDD0A2A" isCollapsed="false">
+ <dmndi:DMNStyle>
+ <dmndi:FillColor red="255" green="255" blue="255"/>
+ <dmndi:StrokeColor red="0" green="0" blue="0"/>
+ <dmndi:FontColor red="0" green="0" blue="0"/>
+ </dmndi:DMNStyle>
+ <dc:Bounds x="281" y="266" width="100" height="50"/>
+ <dmndi:DMNLabel/>
+ </dmndi:DMNShape>
+ <dmndi:DMNShape id="dmnshape-drg-_B0928930-EC40-4726-B8B1-4D9D5AEF1C82" dmnElementRef="_B0928930-EC40-4726-B8B1-4D9D5AEF1C82" isCollapsed="false">
+ <dmndi:DMNStyle>
+ <dmndi:FillColor red="255" green="255" blue="255"/>
+ <dmndi:StrokeColor red="0" green="0" blue="0"/>
+ <dmndi:FontColor red="0" green="0" blue="0"/>
+ </dmndi:DMNStyle>
+ <dc:Bounds x="255" y="108" width="154" height="72"/>
+ <dmndi:DMNLabel/>
+ </dmndi:DMNShape>
+ <dmndi:DMNEdge id="dmnedge-drg-_AF4E07ED-DE25-4E44-BB37-3FDF52056005" dmnElementRef="_AF4E07ED-DE25-4E44-BB37-3FDF52056005">
+ <di:waypoint x="331" y="291"/>
+ <di:waypoint x="332" y="180"/>
+ </dmndi:DMNEdge>
+ </dmndi:DMNDiagram>
+ </dmndi:DMNDI>
+</dmn:definitions>
\ No newline at end of file
diff --git a/sample-kogito-app/src/main/resources/application.properties b/sample-kogito-app/src/main/resources/application.properties
new file mode 100644
index 0000000..a733bf3
--- /dev/null
+++ b/sample-kogito-app/src/main/resources/application.properties
@@ -0,0 +1,36 @@
+#
+# Copyright 2020 Red Hat, Inc. and/or its affiliates.
+#
+# 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.
+#
+
+#https://quarkus.io/guides/openapi-swaggerui
+quarkus.swagger-ui.always-include=true
+
+kogito.service.url=http://localhost:8080
+
+# events configuration
+
+kafka.bootstrap.servers=localhost:9092
+
+mp.messaging.outgoing.kogito-processinstances-events.connector=smallrye-kafka
+mp.messaging.outgoing.kogito-processinstances-events.topic=kogito-processinstances-events
+mp.messaging.outgoing.kogito-processinstances-events.value.serializer=org.apache.kafka.common.serialization.StringSerializer
+
+mp.messaging.outgoing.kogito-usertaskinstances-events.connector=smallrye-kafka
+mp.messaging.outgoing.kogito-usertaskinstances-events.topic=kogito-usertaskinstances-events
+mp.messaging.outgoing.kogito-usertaskinstances-events.value.serializer=org.apache.kafka.common.serialization.StringSerializer
+
+mp.messaging.outgoing.kogito-variables-events.connector=smallrye-kafka
+mp.messaging.outgoing.kogito-variables-events.topic=kogito-variables-events
+mp.messaging.outgoing.kogito-variables-events.value.serializer=org.apache.kafka.common.serialization.StringSerializer
\ No newline at end of file
diff --git a/sample-kogito-app/threshold.properties b/sample-kogito-app/threshold.properties
new file mode 100644
index 0000000..ea23577
--- /dev/null
+++ b/sample-kogito-app/threshold.properties
@@ -0,0 +1,6 @@
+linux.jvm.time.to.first.ok.request.threshold.ms=3000
+linux.jvm.RSS.threshold.kB=380000
+linux.native.time.to.first.ok.request.threshold.ms=35
+linux.native.RSS.threshold.kB=90000
+windows.jvm.time.to.first.ok.request.threshold.ms=2000
+windows.jvm.RSS.threshold.kB=4000
diff --git a/tests/pom.xml b/tests/pom.xml
new file mode 100644
index 0000000..38a9d04
--- /dev/null
+++ b/tests/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.kie.kogito</groupId>
+ <artifactId>kogito-benchmarks</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>tests</artifactId>
+
+ <name></name>
+ <description></description>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.kie.kogito</groupId>
+ <artifactId>framework</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.kie.kogito</groupId>
+ <artifactId>framework</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.5.13</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/tests/src/test/java/org/kie/kogito/benchmarks/AbstractTemplateTest.java b/tests/src/test/java/org/kie/kogito/benchmarks/AbstractTemplateTest.java
new file mode 100644
index 0000000..e9d93dd
--- /dev/null
+++ b/tests/src/test/java/org/kie/kogito/benchmarks/AbstractTemplateTest.java
@@ -0,0 +1,367 @@
+package org.kie.kogito.benchmarks;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Scanner;
+import java.util.stream.Collectors;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.assertj.core.api.Assertions;
+import org.jboss.logging.Logger;
+import org.junit.jupiter.api.TestInfo;
+import org.kie.kogito.benchmarks.framework.App;
+import org.kie.kogito.benchmarks.framework.BuildResult;
+import org.kie.kogito.benchmarks.framework.HTTPRequestInfo;
+import org.kie.kogito.benchmarks.framework.LogBuilder;
+import org.kie.kogito.benchmarks.framework.Logs;
+import org.kie.kogito.benchmarks.framework.MvnCmds;
+import org.kie.kogito.benchmarks.framework.RunInfo;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.kie.kogito.benchmarks.framework.Commands.BASE_DIR;
+import static org.kie.kogito.benchmarks.framework.Commands.buildApp;
+import static org.kie.kogito.benchmarks.framework.Commands.cleanTarget;
+import static org.kie.kogito.benchmarks.framework.Commands.getOpenedFDs;
+import static org.kie.kogito.benchmarks.framework.Commands.getRSSkB;
+import static org.kie.kogito.benchmarks.framework.Commands.parsePort;
+import static org.kie.kogito.benchmarks.framework.Commands.processStopper;
+import static org.kie.kogito.benchmarks.framework.Commands.startApp;
+import static org.kie.kogito.benchmarks.framework.Commands.waitForTcpClosed;
+import static org.kie.kogito.benchmarks.framework.Logs.SKIP;
+import static org.kie.kogito.benchmarks.framework.Logs.appendln;
+import static org.kie.kogito.benchmarks.framework.Logs.archiveLog;
+import static org.kie.kogito.benchmarks.framework.Logs.checkListeningHost;
+import static org.kie.kogito.benchmarks.framework.Logs.checkLog;
+import static org.kie.kogito.benchmarks.framework.Logs.checkThreshold;
+import static org.kie.kogito.benchmarks.framework.Logs.getLogsDir;
+import static org.kie.kogito.benchmarks.framework.Logs.parseStartStopTimestamps;
+import static org.kie.kogito.benchmarks.framework.Logs.writeReport;
+
+public abstract class AbstractTemplateTest {
+
+ private static final Logger LOGGER = Logger.getLogger(StartStopTest.class.getName());
+
+ public static final int START_STOP_ITERATIONS = 3;
+ public static final String LOCALHOST = "http://localhost:8080";
+
+ public void startStop(TestInfo testInfo, App app) throws IOException, InterruptedException {
+ LOGGER.info("Testing app startStop: " + app.toString() + ", mode: " + app.mavenCommands.toString());
+
+ Process pA = null;
+ File buildLogA = null;
+ File runLogA = null;
+ StringBuilder whatIDidReport = new StringBuilder();
+ File appDir = app.getAppDir(BASE_DIR);
+ MvnCmds mvnCmds = app.mavenCommands;
+ String cn = testInfo.getTestClass().get().getCanonicalName();
+ String mn = testInfo.getTestMethod().get().getName();
+ try {
+ // Cleanup
+ cleanTarget(app);
+ Files.createDirectories(Paths.get(appDir.getAbsolutePath() + File.separator + "logs"));
+
+ // Build
+ BuildResult buildResult = buildApp(app, mn, cn, whatIDidReport);
+ buildLogA = buildResult.getBuildLog();
+
+ assertTrue(buildLogA.exists());
+ checkLog(cn, mn, app, mvnCmds, buildLogA);
+
+ // Prepare for run
+ List<Long> rssKbValues = new ArrayList<>(START_STOP_ITERATIONS);
+ List<Long> timeToFirstOKRequestValues = new ArrayList<>(START_STOP_ITERATIONS);
+ List<Long> startedInMsValues = new ArrayList<>(START_STOP_ITERATIONS);
+ List<Long> stoppedInMsValues = new ArrayList<>(START_STOP_ITERATIONS);
+ List<Long> openedFilesValues = new ArrayList<>(START_STOP_ITERATIONS);
+
+ for (int i = 0; i < START_STOP_ITERATIONS; i++) {
+ // Run
+ LOGGER.info("Running... round " + i);
+ RunInfo runInfo = startApp(app, whatIDidReport);
+ pA = runInfo.getProcess();
+ runLogA = runInfo.getRunLog();
+
+ LOGGER.info("Terminate and scan logs...");
+ pA.getInputStream().available(); // TODO Ask Karm
+
+ long rssKb = getRSSkB(pA.pid());
+ long openedFiles = getOpenedFDs(pA.pid());
+
+ processStopper(pA, false);
+
+ LOGGER.info("Gonna wait for ports closed...");
+ // Release ports
+ assertTrue(waitForTcpClosed("localhost", parsePort(app.urlContent.urlContent[0][0]), 60),
+ "Main port is still open");
+ checkLog(cn, mn, app, mvnCmds, runLogA);
+ checkListeningHost(cn, mn, mvnCmds, runLogA);
+
+ float[] startedStopped = parseStartStopTimestamps(runLogA);
+ long startedInMs = (long) (startedStopped[0] * 1000);
+ long stoppedInMs = (long) (startedStopped[1] * 1000);
+
+ Path measurementsLog = Paths.get(getLogsDir(cn, mn).toString(), "measurements.csv");
+ LogBuilder.Log log = new LogBuilder()
+ .app(app)
+ .mode(mvnCmds)
+ .buildTimeMs(buildResult.getBuildTimeMs())
+ .timeToFirstOKRequestMs(runInfo.getTimeToFirstOKRequest())
+ .startedInMs(startedInMs)
+ .stoppedInMs(stoppedInMs)
+ .rssKb(rssKb)
+ .openedFiles(openedFiles)
+ .build();
+ Logs.logMeasurements(log, measurementsLog);
+ appendln(whatIDidReport, "Measurements:");
+ appendln(whatIDidReport, log.headerMarkdown + "\n" + log.lineMarkdown);
+
+ rssKbValues.add(rssKb);
+ openedFilesValues.add(openedFiles);
+ timeToFirstOKRequestValues.add(runInfo.getTimeToFirstOKRequest());
+ startedInMsValues.add(startedInMs);
+ stoppedInMsValues.add(stoppedInMs);
+ }
+
+ long rssKbAvgWithoutMinMax = getAvgWithoutMinMax(rssKbValues);
+ long openedFilesAvgWithoutMinMax = getAvgWithoutMinMax(openedFilesValues);
+ long timeToFirstOKRequestAvgWithoutMinMax = getAvgWithoutMinMax(timeToFirstOKRequestValues);
+ long startedInMsAvgWithoutMinMax = getAvgWithoutMinMax(startedInMsValues);
+ long stoppedInMsAvgWithoutMinMax = getAvgWithoutMinMax(stoppedInMsValues);
+
+ Path measurementsSummary = Paths.get(getLogsDir(cn).toString(), "measurementsSummary.csv");
+
+ LogBuilder.Log log = new LogBuilder()
+ .app(app)
+ .mode(mvnCmds)
+ .buildTimeMs(buildResult.getBuildTimeMs())
+ .timeToFirstOKRequestMs(timeToFirstOKRequestAvgWithoutMinMax)
+ .startedInMs(startedInMsAvgWithoutMinMax)
+ .stoppedInMs(stoppedInMsAvgWithoutMinMax)
+ .rssKb(rssKbAvgWithoutMinMax)
+ .openedFiles(openedFilesAvgWithoutMinMax)
+ .build();
+ Logs.logMeasurementsSummary(log, measurementsSummary);
+
+ LOGGER.info("AVG timeToFirstOKRequest without min and max values: " + timeToFirstOKRequestAvgWithoutMinMax);
+ LOGGER.info("AVG rssKb without min and max values: " + rssKbAvgWithoutMinMax);
+ checkThreshold(app, mvnCmds, rssKbAvgWithoutMinMax, timeToFirstOKRequestAvgWithoutMinMax, SKIP);
+ } finally {
+ // Make sure processes are down even if there was an exception / failure
+ if (pA != null) {
+ processStopper(pA, true);
+ }
+ // Archive logs no matter what
+ archiveLog(cn, mn, buildLogA);
+ archiveLog(cn, mn, runLogA);
+ writeReport(cn, mn, whatIDidReport.toString());
+ //cleanTarget(app);
+ }
+ }
+
+ private long getAvgWithoutMinMax(List<Long> listOfValues) { // TODO Median?
+ listOfValues.remove(Collections.min(listOfValues));
+ listOfValues.remove(Collections.max(listOfValues));
+ return (long) listOfValues.stream().mapToLong(val -> val).average().orElse(Long.MAX_VALUE);
+ }
+
+ public void loadTest(TestInfo testInfo, App app, HTTPRequestInfo requestInfo) throws IOException, InterruptedException {
+ LOGGER.info("Testing app startStop: " + app.toString() + ", mode: " + app.mavenCommands.toString());
+
+ Process pA = null;
+ File buildLogA = null;
+ File runLogA = null;
+ StringBuilder whatIDidReport = new StringBuilder();
+ File appDir = app.getAppDir(BASE_DIR);
+ MvnCmds mvnCmds = app.mavenCommands;
+ String cn = testInfo.getTestClass().get().getCanonicalName();
+ String mn = testInfo.getTestMethod().get().getName();
+ try {
+ // Cleanup
+ cleanTarget(app);
+ Files.createDirectories(Paths.get(appDir.getAbsolutePath() + File.separator + "logs"));
+
+ // Build
+ BuildResult buildResult = buildApp(app, mn, cn, whatIDidReport);
+ buildLogA = buildResult.getBuildLog();
+
+ assertTrue(buildLogA.exists());
+ checkLog(cn, mn, app, mvnCmds, buildLogA);
+
+ // Start the App
+ RunInfo runInfo = startApp(app, whatIDidReport);
+ pA = runInfo.getProcess();
+ runLogA = runInfo.getRunLog();
+
+ long rssKb = getRSSkB(pA.pid());
+
+ List<Long> values = new ArrayList<>(20000);
+
+
+ // Plain OLD Java "HTTP Client"
+// long startTime = System.currentTimeMillis();
+// for (int i = 0; i < 20000; i++) {
+// long requestStartTime = System.nanoTime();
+// HttpURLConnection c = (HttpURLConnection) new URL(requestInfo.getURI()).openConnection();
+// requestInfo.getHeaders().forEach(c::setRequestProperty);
+// c.setRequestMethod(requestInfo.getMethod());
+// c.setDoOutput(true);
+// c.setConnectTimeout(500);
+//
+// try (OutputStream os = c.getOutputStream()) {
+// os.write(requestInfo.getBody().getBytes());
+// }
+//
+// try (Scanner scanner = new Scanner(c.getInputStream(), StandardCharsets.UTF_8.toString())) {
+// Assertions.assertThat(c.getResponseCode()).isEqualTo(requestInfo.getExpectedResponseStatusCode());
+//// System.out.println("Response code: " + c.getResponseCode());
+// scanner.useDelimiter("\\A");
+// String webPage = scanner.hasNext() ? scanner.next() : "";
+//// System.out.println("Page is: " + webPage);
+// } catch (Exception e) {
+// LOGGER.info("Error when executing request on " + requestInfo.getURI(), e);
+// }
+//
+// long requestEndTime = System.nanoTime();
+// long duration = requestEndTime - requestStartTime;
+// values.add(duration);
+//// System.out.println(duration);
+// }
+// long endTime = System.currentTimeMillis();
+
+
+ // Java 11 HTTP Client
+
+// HttpClient httpClient = HttpClient.newHttpClient();
+// HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(URI.create(requestInfo.getURI()))
+// .POST(HttpRequest.BodyPublishers.ofString(requestInfo.getBody()));
+// requestInfo.getHeaders().forEach(requestBuilder::header);
+// HttpRequest request = requestBuilder.build();
+//
+//
+//
+//
+// long startTime = System.currentTimeMillis();
+// for (int i = 0; i < 20000; i++) {
+// long requestStartTime = System.nanoTime();
+// HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
+// Assertions.assertThat(response.statusCode()).isEqualTo(requestInfo.getExpectedResponseStatusCode());
+//// System.out.println("Response code: " + response.statusCode());
+//// System.out.println("Page is: " + response.body());
+// long requestEndTime = System.nanoTime();
+// long duration = requestEndTime - requestStartTime;
+// values.add(duration);
+//// System.out.println(duration);
+// }
+// long endTime = System.currentTimeMillis();
+
+
+
+ // Apache HTTP Client 4
+ long startTime;
+ try (CloseableHttpClient client = HttpClients.createDefault()){
+ HttpPost postRequest = new HttpPost(requestInfo.getURI());
+ postRequest.setEntity(new StringEntity(requestInfo.getBody()));
+ requestInfo.getHeaders().forEach(postRequest::setHeader);
+
+ startTime = System.currentTimeMillis();
+ for (int i = 0; i < 20000; i++) {
+ long requestStartTime = System.nanoTime();
+ try (CloseableHttpResponse response = client.execute(postRequest)) {
+ Assertions.assertThat(response.getStatusLine().getStatusCode()).isEqualTo(requestInfo.getExpectedResponseStatusCode());
+// System.out.println("Response code: " + response.getStatusLine().getStatusCode());
+// System.out.println("Page is: " + EntityUtils.toString(response.getEntity()));
+ EntityUtils.consume(response.getEntity());
+ }
+ long requestEndTime = System.nanoTime();
+ long duration = requestEndTime - requestStartTime;
+ values.add(duration);
+ }
+ }
+ long endTime = System.currentTimeMillis();
+
+ System.out.println("First response time: " + values.get(0));
+ System.out.println("Second response time: " + values.get(1));
+ System.out.println("Third response time: " + values.get(2));
+ System.out.println("Average response time: " + values.stream().mapToLong(Long::longValue).skip(1).average());
+ System.out.println("Total duration: " + (endTime - startTime));
+
+ long rssKbFinal = getRSSkB(pA.pid());
+ long openedFiles = getOpenedFDs(pA.pid()); // TODO also do before the "test" itself?
+
+ // Stop the App
+ processStopper(pA, false);
+
+ LOGGER.info("Gonna wait for ports closed...");
+ // Release ports
+ assertTrue(waitForTcpClosed("localhost", parsePort(app.urlContent.urlContent[0][0]), 60),
+ "Main port is still open");
+ checkLog(cn, mn, app, mvnCmds, runLogA);
+ checkListeningHost(cn, mn, mvnCmds, runLogA);
+
+ float[] startedStopped = parseStartStopTimestamps(runLogA);// Don't need this in the load test?
+ long startedInMs = (long) (startedStopped[0] * 1000);
+ long stoppedInMs = (long) (startedStopped[1] * 1000);
+
+ Path measurementsLog = Paths.get(getLogsDir(cn, mn).toString(), "measurements.csv");
+ Path measurementsSummaryLog = Paths.get(getLogsDir(cn).toString(), "measurementsSummary.csv");
+ LogBuilder.Log log = new LogBuilder()
+ .app(app)
+ .mode(mvnCmds)
+ .buildTimeMs(buildResult.getBuildTimeMs())
+ .timeToFirstOKRequestMs(runInfo.getTimeToFirstOKRequest())
+ .startedInMs(startedInMs)
+ .stoppedInMs(stoppedInMs)
+ .rssKb(rssKb)
+ .rssKbFinal(rssKbFinal)
+ .openedFiles(openedFiles)
+ .build();
+
+ Logs.logMeasurements(log, measurementsLog);
+
+ LogBuilder.Log summaryLog = new LogBuilder()
+ .app(app)
+ .mode(mvnCmds)
+ .rssKbFinal(rssKbFinal)
+ .build();
+ Logs.logMeasurementsSummary(summaryLog, measurementsSummaryLog);
+ appendln(whatIDidReport, "Measurements:");
+ appendln(whatIDidReport, log.headerMarkdown + "\n" + log.lineMarkdown);
+
+ //checkThreshold(app, mvnCmds, rssKbAvgWithoutMinMax, timeToFirstOKRequestAvgWithoutMinMax, SKIP);
+ } finally {
+ // Make sure processes are down even if there was an exception / failure
+ if (pA != null) {
+ processStopper(pA, true);
+ }
+ // Archive logs no matter what
+ archiveLog(cn, mn, buildLogA);
+ archiveLog(cn, mn, runLogA);
+ writeReport(cn, mn, whatIDidReport.toString());
+ //cleanTarget(app);
+
+ }
+ }
+
+}
diff --git a/tests/src/test/java/org/kie/kogito/benchmarks/QuarkusLargeTest.java b/tests/src/test/java/org/kie/kogito/benchmarks/QuarkusLargeTest.java
new file mode 100644
index 0000000..bce1104
--- /dev/null
+++ b/tests/src/test/java/org/kie/kogito/benchmarks/QuarkusLargeTest.java
@@ -0,0 +1,69 @@
+package org.kie.kogito.benchmarks;
+
+import static org.kie.kogito.benchmarks.framework.Logs.getLogsDir;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.kie.kogito.benchmarks.framework.App;
+import org.kie.kogito.benchmarks.framework.HTTPRequestInfo;
+import org.kie.kogito.benchmarks.framework.LogBuilder;
+import org.kie.kogito.benchmarks.framework.Logs;
+
+public class QuarkusLargeTest extends AbstractTemplateTest {
+
+ private static final App APP_TO_TEST = App.SAMPLE_KOGITO_APP_QUARKUS_JVM;
+
+ @Test
+ public void startStop(TestInfo testInfo) throws IOException, InterruptedException {
+ startStop(testInfo, APP_TO_TEST);
+ }
+
+ @Test
+ public void loadTest(TestInfo testInfo) throws IOException, InterruptedException {
+ HTTPRequestInfo requestInfo = HTTPRequestInfo.builder()
+ .URI(LOCALHOST + "/LoanApplication")
+ .body("{\"amount\":\"2000\"}")
+ .method("POST")
+ .header("Accept", "*/*")
+ .header("Content-Type", "application/json")
+ .expectedResponseStatusCode(201)
+ .build(); // This may be directly replaced for example by Apache-specific class, but this keeps
+ // it detached from any framework
+
+ loadTest(testInfo, APP_TO_TEST, requestInfo);
+
+// Path measurementLogSummary = Paths.get(getLogsDir(testInfo.getTestClass().get().getCanonicalName()).toString(), "measurementsSummary.csv");
+//
+// for (App app : new App[]{APP_TO_TEST, App.SAMPLE_KOGITO_APP_SPRING_BOOT}) {
+// LogBuilder.Log log = new LogBuilder()
+// .app(app)
+// .mode(app.mavenCommands)
+// .buildTimeMs(100)
+// .timeToFirstOKRequestMs(200)
+// .startedInMs(300)
+// .stoppedInMs(400)
+// .rssKb(500)
+// .openedFiles(700)
+// .build();
+//
+// LogBuilder.Log log2 = new LogBuilder()
+// .app(app)
+// .mode(app.mavenCommands)
+// .rssKbFinal(600)
+// .build();
+//
+// Logs.logMeasurementsSummary(log, measurementLogSummary);
+// Logs.logMeasurementsSummary(log2, measurementLogSummary);
+//
+//
+// Logs.logMeasurementsSummary(log, measurementLogSummary);
+// Logs.logMeasurementsSummary(log2, measurementLogSummary);
+// }
+
+
+ }
+}
diff --git a/tests/src/test/java/org/kie/kogito/benchmarks/QuarkusSmallTest.java b/tests/src/test/java/org/kie/kogito/benchmarks/QuarkusSmallTest.java
new file mode 100644
index 0000000..cbaf805
--- /dev/null
+++ b/tests/src/test/java/org/kie/kogito/benchmarks/QuarkusSmallTest.java
@@ -0,0 +1,22 @@
+package org.kie.kogito.benchmarks;
+
+import java.io.IOException;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.kie.kogito.benchmarks.framework.App;
+
+public class QuarkusSmallTest extends AbstractTemplateTest {
+
+ private static final App APP_TO_TEST = App.SAMPLE_KOGITO_APP_QUARKUS_JVM;
+
+ @Test
+ public void startStop(TestInfo testInfo) throws IOException, InterruptedException {
+ startStop(testInfo, APP_TO_TEST);
+ }
+
+ @Test
+ public void loadTest(TestInfo testInfo) {
+ // TODO
+ }
+}
diff --git a/tests/src/test/java/org/kie/kogito/benchmarks/QuarkusTest.java b/tests/src/test/java/org/kie/kogito/benchmarks/QuarkusTest.java
new file mode 100644
index 0000000..0a598e3
--- /dev/null
+++ b/tests/src/test/java/org/kie/kogito/benchmarks/QuarkusTest.java
@@ -0,0 +1,9 @@
+package org.kie.kogito.benchmarks;
+
+import org.kie.kogito.benchmarks.framework.MvnCmds;
+
+public abstract class QuarkusTest extends AbstractTemplateTest {
+
+ protected static final MvnCmds MavenCommands = MvnCmds.QUARKUS_JVM;
+
+}
diff --git a/tests/src/test/java/org/kie/kogito/benchmarks/StartStopTest.java b/tests/src/test/java/org/kie/kogito/benchmarks/StartStopTest.java
new file mode 100644
index 0000000..04c50e9
--- /dev/null
+++ b/tests/src/test/java/org/kie/kogito/benchmarks/StartStopTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2021 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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 org.kie.kogito.benchmarks;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.jboss.logging.Logger;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.kie.kogito.benchmarks.framework.App;
+import org.kie.kogito.benchmarks.framework.Commands;
+import org.kie.kogito.benchmarks.framework.LogBuilder;
+import org.kie.kogito.benchmarks.framework.Logs;
+import org.kie.kogito.benchmarks.framework.MvnCmds;
+import org.kie.kogito.benchmarks.framework.WebpageTester;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.kie.kogito.benchmarks.framework.Commands.cleanTarget;
+import static org.kie.kogito.benchmarks.framework.Commands.getBaseDir;
+import static org.kie.kogito.benchmarks.framework.Commands.getBuildCommand;
+import static org.kie.kogito.benchmarks.framework.Commands.getOpenedFDs;
+import static org.kie.kogito.benchmarks.framework.Commands.getRSSkB;
+import static org.kie.kogito.benchmarks.framework.Commands.getRunCommand;
+import static org.kie.kogito.benchmarks.framework.Commands.parsePort;
+import static org.kie.kogito.benchmarks.framework.Commands.processStopper;
+import static org.kie.kogito.benchmarks.framework.Commands.runCommand;
+import static org.kie.kogito.benchmarks.framework.Commands.waitForTcpClosed;
+import static org.kie.kogito.benchmarks.framework.Logs.SKIP;
+import static org.kie.kogito.benchmarks.framework.Logs.appendln;
+import static org.kie.kogito.benchmarks.framework.Logs.appendlnSection;
+import static org.kie.kogito.benchmarks.framework.Logs.archiveLog;
+import static org.kie.kogito.benchmarks.framework.Logs.checkListeningHost;
+import static org.kie.kogito.benchmarks.framework.Logs.checkLog;
+import static org.kie.kogito.benchmarks.framework.Logs.checkThreshold;
+import static org.kie.kogito.benchmarks.framework.Logs.getLogsDir;
+import static org.kie.kogito.benchmarks.framework.Logs.parseStartStopTimestamps;
+import static org.kie.kogito.benchmarks.framework.Logs.writeReport;
+
+@Tag("startstop")
+public class StartStopTest {
+
+ private static final Logger LOGGER = Logger.getLogger(StartStopTest.class.getName());
+
+ public static final String BASE_DIR = getBaseDir();
+
+ public void testRuntime(TestInfo testInfo, App app, MvnCmds mvnCmds) throws IOException, InterruptedException {
+ LOGGER.info("Testing app: " + app.toString() + ", mode: " + mvnCmds.toString());
+
+ Process pA = null;
+ File buildLogA = null;
+ File runLogA = null;
+ StringBuilder whatIDidReport = new StringBuilder();
+ File appDir = new File(BASE_DIR + File.separator + app.dir);
+ String cn = testInfo.getTestClass().get().getCanonicalName();
+ String mn = testInfo.getTestMethod().get().getName();
+ try {
+ // Cleanup
+ cleanTarget(app);
+ Files.createDirectories(Paths.get(appDir.getAbsolutePath() + File.separator + "logs"));
+
+ // Build
+ buildLogA = new File(appDir.getAbsolutePath() + File.separator + "logs" + File.separator + mvnCmds.name().toLowerCase() + "-build.log");
+ ExecutorService buildService = Executors.newFixedThreadPool(1);
+
+ List<String> baseBuildCmd = new ArrayList<>();
+ baseBuildCmd.addAll(Arrays.asList(mvnCmds.mvnCmds[0]));
+ //baseBuildCmd.add("-Dquarkus.version=" + getQuarkusVersion());
+ List<String> cmd = getBuildCommand(baseBuildCmd.toArray(new String[0]));
+
+ buildService.submit(new Commands.ProcessRunner(appDir, buildLogA, cmd, 20)); // TODO exit code handling
+ appendln(whatIDidReport, "# " + cn + ", " + mn);
+ appendln(whatIDidReport, (new Date()).toString());
+ appendln(whatIDidReport, appDir.getAbsolutePath());
+ appendlnSection(whatIDidReport, String.join(" ", cmd));
+ long buildStarts = System.currentTimeMillis();
+ buildService.shutdown();
+ buildService.awaitTermination(30, TimeUnit.MINUTES);
+ long buildEnds = System.currentTimeMillis();
+
+ assertTrue(buildLogA.exists());
+ checkLog(cn, mn, app, mvnCmds, buildLogA);
+
+ List<Long> rssKbList = new ArrayList<>(10);
+ List<Long> timeToFirstOKRequestList = new ArrayList<>(10);
+ for (int i = 0; i < 1; i++) {
+ // Run
+ LOGGER.info("Running... round " + i);
+ runLogA = new File(appDir.getAbsolutePath() + File.separator + "logs" + File.separator + mvnCmds.name().toLowerCase() + "-run.log");
+ cmd = getRunCommand(mvnCmds.mvnCmds[1]);
+ appendln(whatIDidReport, appDir.getAbsolutePath());
+ appendlnSection(whatIDidReport, String.join(" ", cmd));
+ long runStarts = System.currentTimeMillis();
+ pA = runCommand(cmd, appDir, runLogA);
+ long runEnds = System.currentTimeMillis();
+ System.out.println("RunEnds (" + runEnds + ") - RunStarts (" + runStarts + ") : " + (runEnds - runStarts));
+ // Test web pages
+ long timeToFirstOKRequest = WebpageTester.testWeb(app.urlContent.urlContent[0][0], 10, app.urlContent.urlContent[0][1], true);
+ LOGGER.info("Testing web page content...");
+ for (String[] urlContent : app.urlContent.urlContent) {
+ WebpageTester.testWeb(urlContent[0], 5, urlContent[1], false);
+ }
+
+ LOGGER.info("Terminate and scan logs...");
+ pA.getInputStream().available(); // TODO Ask Karm
+
+ long rssKb = getRSSkB(pA.pid());
+ long openedFiles = getOpenedFDs(pA.pid());
+
+ processStopper(pA, false);
+
+ LOGGER.info("Gonna wait for ports closed...");
+ // Release ports
+ assertTrue(waitForTcpClosed("localhost", parsePort(app.urlContent.urlContent[0][0]), 60),
+ "Main port is still open");
+ checkLog(cn, mn, app, mvnCmds, runLogA);
+ checkListeningHost(cn, mn, mvnCmds, runLogA);
+
+ float[] startedStopped = parseStartStopTimestamps(runLogA);
+
+ Path measurementsLog = Paths.get(getLogsDir(cn, mn).toString(), "measurements.csv");
+ LogBuilder.Log log = new LogBuilder()
+ .app(app)
+ .mode(mvnCmds)
+ .buildTimeMs(buildEnds - buildStarts)
+ .timeToFirstOKRequestMs(timeToFirstOKRequest)
+ .startedInMs((long) (startedStopped[0] * 1000))
+ .stoppedInMs((long) (startedStopped[1] * 1000))
+ .rssKb(rssKb)
+ .openedFiles(openedFiles)
+ .build();
+ Logs.logMeasurements(log, measurementsLog);
+ appendln(whatIDidReport, "Measurements:");
+ appendln(whatIDidReport, log.headerMarkdown + "\n" + log.lineMarkdown);
+
+ rssKbList.add(rssKb);
+ timeToFirstOKRequestList.add(timeToFirstOKRequest);
+ }
+
+ long rssKbAvgWithoutMinMax = getAvgWithoutMinMax(rssKbList);
+ long timeToFirstOKRequestAvgWithoutMinMax = getAvgWithoutMinMax(timeToFirstOKRequestList);
+ LOGGER.info("AVG timeToFirstOKRequest without min and max values: " + timeToFirstOKRequestAvgWithoutMinMax);
+ LOGGER.info("AVG rssKb without min and max values: " + rssKbAvgWithoutMinMax);
+ checkThreshold(app, mvnCmds, rssKbAvgWithoutMinMax, timeToFirstOKRequestAvgWithoutMinMax, SKIP);
+ } finally {
+ // Make sure processes are down even if there was an exception / failure
+ if (pA != null) {
+ processStopper(pA, true);
+ }
+ // Archive logs no matter what
+ archiveLog(cn, mn, buildLogA);
+ archiveLog(cn, mn, runLogA);
+ writeReport(cn, mn, whatIDidReport.toString());
+ //cleanTarget(app);
+ }
+ }
+
+ private long getAvgWithoutMinMax(List<Long> listOfValues) {
+ // listOfValues.remove(Collections.min(listOfValues));
+ // listOfValues.remove(Collections.max(listOfValues));
+ return (long) listOfValues.stream().mapToLong(val -> val).average().orElse(Long.MAX_VALUE);
+ }
+
+ @Test
+ public void kogito(TestInfo testInfo) throws IOException, InterruptedException {
+ testRuntime(testInfo, App.SAMPLE_KOGITO_APP_QUARKUS_JVM, MvnCmds.QUARKUS_JVM);
+ }
+
+ // @Test
+ // public void jaxRsMinimalJVM(TestInfo testInfo) throws IOException, InterruptedException {
+ // testRuntime(testInfo, App.JAX_RS_MINIMAL, MvnCmds.JVM);
+ // }
+ //
+ // @Test
+ // @Tag("native")
+ // public void jaxRsMinimalNative(TestInfo testInfo) throws IOException, InterruptedException {
+ // testRuntime(testInfo, App.JAX_RS_MINIMAL, MvnCmds.NATIVE);
+ // }
+ //
+ // @Test
+ // public void fullMicroProfileJVM(TestInfo testInfo) throws IOException, InterruptedException {
+ // testRuntime(testInfo, App.FULL_MICROPROFILE, MvnCmds.JVM);
+ // }
+ //
+ // @Test
+ // @Tag("native")
+ // public void fullMicroProfileNative(TestInfo testInfo) throws IOException, InterruptedException {
+ // testRuntime(testInfo, App.FULL_MICROPROFILE, MvnCmds.NATIVE);
+ // }
+}
diff --git a/tests/src/test/resources/log4j2.xml b/tests/src/test/resources/log4j2.xml
new file mode 100644
index 0000000..1faad0e
--- /dev/null
+++ b/tests/src/test/resources/log4j2.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN">
+ <Appenders>
+ <Console name="Console" target="SYSTEM_OUT">
+ <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%-5p}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=green, DEBUG=green bold, TRACE=blue} [%style{%C{1.}}{cyan}] (%style{%M}{magenta}) %m%n"/>
+ </Console>
+ </Appenders>
+ <Loggers>
+ <Root level="info">
+ <AppenderRef ref="Console"/>
+ </Root>
+ </Loggers>
+</Configuration>
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org