Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PHEE-687] Parallel execution of integration test #3

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,11 @@ jobs:
export CALLBACK_URL="https://$NGROK_PUBLIC_URL"
echo -n "Public url ."
echo $CALLBACK_URL
./gradlew test -Dcucumber.filter.tags="@gov"
./gradlew test -Dcucumber.tags="@gov"
echo -n "Test execution is completed, kill ngrok"
pkill ngrok
- store_test_results:
path: build/test-results/test/TEST-org.fynarfin.integrationtest.TestRunner.xml
path: build/cucumber.xml
- store_artifacts:
path: build/test-results
test-chart-ams:
Expand Down Expand Up @@ -264,11 +264,11 @@ jobs:
export CALLBACK_URL="https://$NGROK_PUBLIC_URL"
echo -n "Public url ."
echo $CALLBACK_URL
./gradlew test -Dcucumber.filter.tags="@amsIntegration"
./gradlew test -Dcucumber.tags="@amsIntegration"
echo -n "Test execution is completed, kill ngrok"
pkill ngrok
- store_test_results:
path: build/test-results/test/TEST-org.fynarfin.integrationtest.TestRunner.xml
path: build/cucumber.xml
- store_artifacts:
path: build/test-results
workflows:
Expand Down Expand Up @@ -317,17 +317,9 @@ workflows:
- AWS
- Helm
- slack
- test-chart-ams:
requires:
- deploying-bpmns
context:
- AWS
- Helm
- slack
- Ngrok
- test-chart-gov:
requires:
- test-chart-ams
- deploying-bpmns
context:
- AWS
- Helm
Expand Down
88 changes: 24 additions & 64 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ plugins {
id 'com.adarshr.test-logger' version '3.2.0'
}

apply plugin:'com.diffplug.spotless'
apply plugin: 'com.diffplug.spotless'

spotless {
format 'misc', {
target '**/*.md', '**/*.properties', '**/.gitignore', '**/.openapi-generator-ignore', '**/*.yml', '**/*.xml', '**/**.json', '**/*.sql'
target '**/*.md', '**/*.properties', '**/.gitignore', '**/.openapi-generator-ignore', '**/*.yml', '**/*.xml', '**/*.json', '**/*.sql'
targetExclude '**/build/**', '**/bin/**', '**/.settings/**', '**/.idea/**', '**/.gradle/**', '**/gradlew.bat', '**/licenses/**', '**/banner.txt', '.vscode/**'
indentWithSpaces(4)
endWithNewline()
Expand All @@ -31,15 +32,16 @@ spotless {
}

configure(this) {
// NOTE: order matters!
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'eclipse'
apply plugin: 'checkstyle'

configurations {
implementation.setCanBeResolved(true)
api.setCanBeResolved(true)
}

tasks.withType(JavaCompile) {
options.compilerArgs += [
"-Xlint:unchecked",
Expand All @@ -66,18 +68,14 @@ configure(this) {
"-Xlint:varargs",
"-Xlint:preview",
"-Xlint:static",
// -Werror needs to be disabled because EclipseLink's static weaving doesn't generate warning-free code
// and during an IntelliJ recompilation, it fails
//"-Werror",
"-Xmaxwarns",
1500,
"-Xmaxerrs",
1500
]
options.deprecation = true
}
// Configuration for the spotless plugin
// https://github.com/diffplug/spotless/tree/main/plugin-gradle

spotless {
java {
targetExclude '**/build/**', '**/bin/**', '**/out/**'
Expand All @@ -86,7 +84,6 @@ configure(this) {
eclipse().configFile "$rootDir/config/formatter.xml"
endWithNewline()
trimTrailingWhitespace()
// Enforce style modifier order
custom 'Modifier ordering', {
def modifierRanking = [
public : 1,
Expand All @@ -100,29 +97,22 @@ configure(this) {
volatile : 9,
synchronized: 10,
native : 11,
strictfp : 12]
// Find any instance of multiple modifiers. Lead with a non-word character to avoid
// accidental matching against for instance, "an alternative default value"
strictfp : 12
]
it.replaceAll(/\W(?:public |protected |private |abstract |default |static |final |transient |volatile |synchronized |native |strictfp ){2,}/, {
// Do not replace the leading non-word character. Identify the modifiers
it.replaceAll(/(?:public |protected |private |abstract |default |static |final |transient |volatile |synchronized |native |strictfp ){2,}/, {
// Sort the modifiers according to the ranking above
it.split().sort({ modifierRanking[it] }).join(' ') + ' '
}
)
}
)
})
})
}
}
lineEndings 'UNIX'
}
// If we are running Gradle within Eclipse to enhance classes,
// set the classes directory to point to Eclipse's default build directory

if (project.hasProperty('env') && project.getProperty('env') == 'eclipse') {
sourceSets.main.java.outputDir = file("$projectDir/bin/main")
}
// Configuration for the Checkstyle plugin
// https://docs.gradle.org/current/userguide/checkstyle_plugin.html

dependencies {
checkstyle 'com.puppycrawl.tools:checkstyle:10.3.1'
checkstyle 'com.github.sevntu-checkstyle:sevntu-checks:1.42.0'
Expand Down Expand Up @@ -154,13 +144,11 @@ dependencies {
testImplementation('io.rest-assured:rest-assured:5.1.1') {
exclude group: 'org.codehaus.groovy'
}

testCompileOnly 'org.projectlombok:lombok:1.18.24'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.24'
implementation 'io.cucumber:cucumber-java:7.8.1'
implementation 'io.cucumber:cucumber-spring:7.8.1'
testImplementation 'io.cucumber:cucumber-junit:7.8.1'

testRuntimeOnly("org.junit.vintage:junit-vintage-engine")
checkstyle 'com.puppycrawl.tools:checkstyle:10.9.3'
checkstyle 'com.github.sevntu-checkstyle:sevntu-checks:1.44.1'
Expand All @@ -172,50 +160,22 @@ dependencies {
testImplementation 'org.apache.commons:commons-csv:1.5'
testImplementation 'org.awaitility:awaitility:4.2.0'
implementation 'commons-validator:commons-validator:1.7'
implementation 'io.github.prashant-ramcharan:courgette-jvm:6.12.0'
}

tasks.named('test') {
useJUnitPlatform()
}


configurations {
cucumberRuntime {
extendsFrom testImplementation
tasks.withType(Test) {
systemProperties = System.getProperties()
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
showStandardStreams = true
}
testlogger {
// pick a theme - mocha, standard, plain, mocha-parallel, standard-parallel or plain-parallel
theme 'mocha'
showSkipped false
showStackTraces true
reports {
junitXml.destination = file("build/test-results/test")
html.destination = file("build/reports/tests/test")
}
}

test {
systemProperty "cucumber.filter.tags", System.getProperty("cucumber.filter.tags")
systemProperty "cucumber.filter.name", System.getProperty("cucumber.filter.name")
}
task cucumberCli() {
dependsOn assemble, testClasses
doLast {
javaexec {
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = [
'--plugin',
'html:cucumber-report',
'--plugin',
'json:cucumber.json',
'--plugin',
'pretty',
'--plugin',
'html:build/cucumber-report.html',
'--plugin',
'json:build/cucumber-report.json',
'--glue',
'org.mifos.integrationtest.cucumber',
'src/test/java/resources'
]
}
}
task parallelRun(type: Test) {
include '**/TestRunner.class'
outputs.upToDateWhen { false }
}
28 changes: 21 additions & 7 deletions src/test/java/org/mifos/integrationtest/TestRunner.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
package org.mifos.integrationtest;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import courgette.api.CourgetteOptions;
import courgette.api.CourgetteRunLevel;
import courgette.api.CourgetteTestOutput;
import courgette.api.CucumberOptions;
import courgette.api.junit.Courgette;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(features = { "src/test/java/resources" }, glue = { "org.mifos.integrationtest.cucumber" }, plugin = {
"html:cucumber-report", "json:cucumber.json", "pretty", "html:build/cucumber-report.html", "json:build/cucumber-report.json",
"junit:build/cucumber.xml" })
public class TestRunner {}
//@RunWith(Cucumber.class)
//@CucumberOptions(features = { "src/test/java/resources" }, glue = { "org.mifos.integrationtest.cucumber" }, plugin = {
// "html:cucumber-report", "json:cucumber.json", "pretty", "html:build/cucumber-report.html", "json:build/cucumber-report.json",
// "junit:build/cucumber.xml" })

@RunWith(Courgette.class)
@CourgetteOptions(threads = 3, runLevel = CourgetteRunLevel.FEATURE, rerunFailedScenarios = false, testOutput = CourgetteTestOutput.CONSOLE, generateCourgetteRunLog = true, reportTitle = "Paymenthub Test results", reportTargetDir = "build", cucumberOptions = @CucumberOptions(features = "src/test/java/resources", glue = "org.mifos.integrationtest.cucumber", publish = true, plugin = {
"pretty", // Pretty console output
"html:build/cucumber-report.html", // HTML report
"json:build/cucumber-report.json", // JSON report
"junit:build/cucumber.xml" // JUnit XML report
}))
@SuppressWarnings({ "FinalClass", "HideUtilityClassConstructor" })
public class TestRunner {

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public WireMockServer getMockServer() {

@Override
public String getBaseUri() {
return "http://localhost:" + getMockServer().port();
return "http://localhost:" + WireMockServerSingleton.getInstance().port();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.mifos.integrationtest.config;

import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.common.FatalStartupException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
@SuppressWarnings({ "FinalClass", "HideUtilityClassConstructor" })
public class WireMockServerSingleton {

static Logger logger = LoggerFactory.getLogger(WireMockServerSingleton.class);
private static final transient ThreadLocal<WireMockServer> threadLocalInstance = new ThreadLocal<>();

public static WireMockServer getInstance() {
WireMockServer instance = threadLocalInstance.get();
if (instance == null || !instance.isRunning()) {
synchronized (WireMockServerSingleton.class) {
instance = threadLocalInstance.get(); // Double-check idiom
if (instance == null || !instance.isRunning()) {
instance = startWireMockServerWithRetry(3); // Retry 3 times
threadLocalInstance.set(instance);
}
}
}
return instance;
}

private static WireMockServer startWireMockServerWithRetry(int maxRetries) {
WireMockServer server = null;
for (int attempt = 1; attempt <= maxRetries; attempt++) {
// int port = getRandomPort();
server = new WireMockServer(53013);
try {
server.start();
logger.info("WireMock started on port {}", server.port());
return server;
} catch (FatalStartupException e) {
logger.error("Failed to start WireMock on port {} (Attempt {}/{}). Retrying...", 53013, attempt, maxRetries, e);
// Optionally, add a short delay here if needed
}
}
throw new IllegalStateException("Failed to start WireMock server after " + maxRetries + " attempts.");
}

public static int getPort() {
WireMockServer instance = threadLocalInstance.get();
if (instance != null && instance.isRunning()) {
return instance.port();
} else {
throw new IllegalStateException("WireMock server is not running.");
}
}
}
Loading