diff --git a/.travis.yml b/.travis.yml
index 93be8f9..aef1217 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
language: java
jdk:
-- openjdk7
+- openjdk8
script:
- mvn test -D-org.login.url="https://na14.salesforce.com" -D-org.username=$USERNAME -D-org.password=$PASSWORD -D-org.client.id=$CLIENT_ID -D-org.client.secret=$CLIENT_SECRET -D-org.wide.code.coverage.threshold=40 -D-team.code.coverage.threshold=40 -D-regex.for.selecting.source.classes.for.code.coverage.computation=HelloWorld -D-regex.for.selecting.test.classes.to.execute=HelloWord* -D-manifest.files.with.test.class.names.to.execute=ManifestFile_For_Unit_Test_Classes.txt -D-manifest.files.with.source.class.names.for.code.coverage.computation=ManifestFile.txt -D-max.test.execution.time.threshold=10
diff --git a/README.md b/README.md
index 1258332..e4501d2 100644
--- a/README.md
+++ b/README.md
@@ -86,6 +86,8 @@ Optional Parameters:
- -max.test.execution.time.threshold : Maximum execution time(in minutes) for a test before it gets aborted
- -proxy.host : Proxy host for external access
- -proxy.port : Proxy port for external access
+- -ignore.code.coverage.threshold : Build does not fail if the code coverage thresholds are not met
+- -ignore.test.failure : Build does not fail if there are tests failures
- -help : Displays options available for running this application
Note: You must provide either of the (-regex.for.selecting.source.classes.for.code.coverage.computation OR -manifest.files.with.source.class.names.for.code.coverage.computation) AND either of -(regex.for.selecting.test.classes.to.execute OR -manifest.files.with.test.class.names.to.execute)
diff --git a/SFDC_CLA.pdf b/SFDC_CLA.pdf
deleted file mode 100644
index 45fb9f8..0000000
Binary files a/SFDC_CLA.pdf and /dev/null differ
diff --git a/pom.xml b/pom.xml
index c354b0b..0fb553e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
com.sforce.cd.ApexUnit
ApexUnit-core
- 2.3.6
+ 4.0.0
ApexUnit
Apex Unit v 2.0 with enhanced metrics and advanced features
diff --git a/src/main/java/com/sforce/cd/apexUnit/ApexUnitRunner.java b/src/main/java/com/sforce/cd/apexUnit/ApexUnitRunner.java
index fba195b..0646f4f 100644
--- a/src/main/java/com/sforce/cd/apexUnit/ApexUnitRunner.java
+++ b/src/main/java/com/sforce/cd/apexUnit/ApexUnitRunner.java
@@ -22,6 +22,8 @@
package com.sforce.cd.apexUnit;
+import java.io.File;
+import java.time.*;
import java.util.Arrays;
import java.util.List;
@@ -101,21 +103,24 @@ public static void main(String[] args) {
}
Long end = System.currentTimeMillis();
LOG.debug("Total Time taken by ApexUnit tool in secs: " + (end - start) / 1000);
+ String reportDir = System.getProperty("user.dir") + System.getProperty("file.separator") + LocalDate.now() + "_Report";
+ File dir = new File(reportDir);
+ dir.mkdirs();
if (apexReportBeans != null && apexReportBeans.length > 0) {
LOG.info("Total test methods executed: " + apexReportBeans.length);
- String reportFile = "ApexUnitReport.xml";
+ String reportFile = reportDir + System.getProperty("file.separator") + "ApexUnitReport.xml";
ApexUnitTestReportGenerator.generateTestReport(apexReportBeans, reportFile);
} else {
ApexUnitUtils.shutDownWithErrMsg("Unable to generate test report. "
+ "Did not find any test results for the job id");
}
+ boolean teamCodeCoverageThresholdError = false;
+ boolean orgWideCodeCoverageThresholdError = false;
if (!skipCodeCoverageComputation) {
- ApexCodeCoverageReportGenerator.generateHTMLReport(apexClassCodeCoverageBeans);
+ ApexCodeCoverageReportGenerator.generateHTMLReport(apexClassCodeCoverageBeans, dir);
// validating the code coverage metrics against the thresholds
// provided by the user
- boolean teamCodeCoverageThresholdError = false;
- boolean orgWideCodeCoverageThresholdError = false;
if (ApexUnitCodeCoverageResults.teamCodeCoverage < CommandLineArguments.getTeamCodeCoverageThreshold()) {
if (ApexUnitCodeCoverageResults.teamCodeCoverage == -1) {
LOG.warn("No source class names provided. Team Code coverage not computed ");
@@ -123,19 +128,18 @@ public static void main(String[] args) {
teamCodeCoverageThresholdError = true;
}
}
- if (ApexUnitCodeCoverageResults.orgWideCodeCoverage < CommandLineArguments
- .getOrgWideCodeCoverageThreshold()) {
+ if (ApexUnitCodeCoverageResults.orgWideCodeCoverage < CommandLineArguments.getOrgWideCodeCoverageThreshold()) {
orgWideCodeCoverageThresholdError = true;
}
- if (teamCodeCoverageThresholdError) {
+ if (teamCodeCoverageThresholdError && !CommandLineArguments.getSkipCoverageEnforcement()) {
runTimeExceptionMessage += "Failed to meet the Team code coverage threshold : "
+ CommandLineArguments.getTeamCodeCoverageThreshold()
+ " The team code coverage for the given classes is: "
+ ApexUnitCodeCoverageResults.teamCodeCoverage + "%\n"
+ "Calibrate your threshold values if you are happy with the current code coverage\n";
}
- if (orgWideCodeCoverageThresholdError) {
+ if (orgWideCodeCoverageThresholdError && !CommandLineArguments.getSkipCoverageEnforcement()) {
runTimeExceptionMessage += "Failed to meet the Org code coverage threshold : "
+ CommandLineArguments.getOrgWideCodeCoverageThreshold()
+ " The org code coverage for the org is: " + ApexUnitCodeCoverageResults.orgWideCodeCoverage
@@ -144,7 +148,7 @@ public static void main(String[] args) {
}
// if there are test failures, concatenate error messages
- if (TestStatusPollerAndResultHandler.testFailures) {
+ if (TestStatusPollerAndResultHandler.testFailures && !CommandLineArguments.getIgnoreTestFailure()) {
runTimeExceptionMessage += "Test failures amongst the Apex tests executed. ";
if (TestStatusPollerAndResultHandler.failedTestMethods != null
&& TestStatusPollerAndResultHandler.failedTestMethods.size() > 0) {
@@ -158,7 +162,10 @@ public static void main(String[] args) {
if (!runTimeExceptionMessage.equals("")) {
ApexUnitUtils.shutDownWithErrMsg(runTimeExceptionMessage);
} else {
- LOG.info("Success!! No test failures and all code coverage thresholds are met!! Exiting ApexUnit.. Good bye..");
+ LOG.info("Build successful!!"
+ +(TestStatusPollerAndResultHandler.testFailures ? " - But "+TestStatusPollerAndResultHandler.failedTestMethods.size()+" Test failure(s) amongst the Apex tests executed : "+TestStatusPollerAndResultHandler.failedTestMethods.toString():" - No test failures!!")
+ +(teamCodeCoverageThresholdError || orgWideCodeCoverageThresholdError ? (teamCodeCoverageThresholdError ? " - Failed to meet the Team code coverage threshold : "+ApexUnitCodeCoverageResults.teamCodeCoverage+"% < "+CommandLineArguments.getTeamCodeCoverageThreshold()+"%":"")+(orgWideCodeCoverageThresholdError ? " - Failed to meet the Org code coverage threshold : "+ApexUnitCodeCoverageResults.orgWideCodeCoverage+"% < "+CommandLineArguments.getOrgWideCodeCoverageThreshold()+"%":""):" - All code coverage thresholds are met!!")
+ +" - Exiting ApexUnit.. Good bye..");
}
}
diff --git a/src/main/java/com/sforce/cd/apexUnit/arguments/CommandLineArguments.java b/src/main/java/com/sforce/cd/apexUnit/arguments/CommandLineArguments.java
index d16a0a4..f822769 100644
--- a/src/main/java/com/sforce/cd/apexUnit/arguments/CommandLineArguments.java
+++ b/src/main/java/com/sforce/cd/apexUnit/arguments/CommandLineArguments.java
@@ -35,7 +35,10 @@ public class CommandLineArguments {
public static final String ORG_CLIENT_SECRET = "-org.client.secret";
public static final String PROXY_HOST = "-proxy.host";
public static final String PROXY_PORT = "-proxy.port";
+ public static final String IGNORE_CODE_COVERAGE_THRESHOLD_ENFORCEMENT = "-ignore.code.coverage.threshold";
+ public static final String IGNORE_TEST_FAILURE = "-ignore.test.failure";
public static final String TEST_RELOAD = "-test.reload";
+
public static final String HELP = "-help";
@@ -75,6 +78,10 @@ public class CommandLineArguments {
static private String proxyHost;
@Parameter(names = PROXY_PORT, description = "Proxy port if required for access.", validateWith = PositiveIntegerValidator.class, required = false)
static private Integer proxyPort;
+ @Parameter(names = IGNORE_CODE_COVERAGE_THRESHOLD_ENFORCEMENT, description = "Build does not fail if the code coverage thresholds are not met.", required = false)
+ static private boolean skipCoverageEnforcement = false;
+ @Parameter(names = IGNORE_TEST_FAILURE, description = "Build does not fail if there are tests failures.", required = false)
+ static private boolean ignoreTestFailure = false;
@Parameter(names = HELP, help = true, description = "Displays options available for running this application")
static private boolean help;
@Parameter(names = TEST_RELOAD, description = "Want to reload test if same class changes submitted again.", arity=1)
@@ -145,6 +152,14 @@ public static void setClientSecret(String clientSecret) {
CommandLineArguments.clientSecret = clientSecret;
}
+ public static boolean getSkipCoverageEnforcement() {
+ return skipCoverageEnforcement;
+ }
+
+ public static boolean getIgnoreTestFailure() {
+ return ignoreTestFailure;
+ }
+
public static boolean isHelp() {
return help;
}
diff --git a/src/main/java/com/sforce/cd/apexUnit/client/QueryConstructor.java b/src/main/java/com/sforce/cd/apexUnit/client/QueryConstructor.java
index fc0240b..397ab1a 100644
--- a/src/main/java/com/sforce/cd/apexUnit/client/QueryConstructor.java
+++ b/src/main/java/com/sforce/cd/apexUnit/client/QueryConstructor.java
@@ -292,7 +292,7 @@ private static String processRegexForSoqlQueries(String apexClassNameRegex) {
}
/*
- * Escape single quotes in the user input during qyery execution
+ * Escape single quotes in the user input during query execution
*
* @param : userInput: String
*
diff --git a/src/main/java/com/sforce/cd/apexUnit/client/codeCoverage/CodeCoverageComputer.java b/src/main/java/com/sforce/cd/apexUnit/client/codeCoverage/CodeCoverageComputer.java
old mode 100644
new mode 100755
index db3e5c6..e8d3edc
--- a/src/main/java/com/sforce/cd/apexUnit/client/codeCoverage/CodeCoverageComputer.java
+++ b/src/main/java/com/sforce/cd/apexUnit/client/codeCoverage/CodeCoverageComputer.java
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2016, salesforce.com, inc.
* All rights reserved.
- *Licensed under the BSD 3-Clause license.
+ * Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
@@ -9,7 +9,8 @@
* Class to compute code coverage for the given class names and org wide code coverage
*
* @author adarsh.ramakrishna@salesforce.com
- */
+ */
+
package com.sforce.cd.apexUnit.client.codeCoverage;
@@ -92,13 +93,13 @@ public ApexClassCodeCoverageBean[] calculateAggregatedCodeCoverageUsingToolingAP
if (CommandLineArguments.getClassManifestFiles() != null) {
LOG.debug(" Fetching apex classes from location : " + CommandLineArguments.getClassManifestFiles());
classesAsArray = ApexClassFetcherUtils
- .fetchApexClassesFromManifestFiles(CommandLineArguments.getClassManifestFiles(),true);
+ .fetchApexClassesFromManifestFiles(CommandLineArguments.getClassManifestFiles(), true);
}
// fetch matching class names based on regex
if (CommandLineArguments.getSourceRegex() != null) {
LOG.debug(" Fetching apex classes with regex : " + CommandLineArguments.getSourceRegex());
classesAsArray = ApexClassFetcherUtils.fetchApexClassesBasedOnMultipleRegexes(connection, classesAsArray,
- CommandLineArguments.getSourceRegex(),true);
+ CommandLineArguments.getSourceRegex(), true);
}
// Do not proceed if no class names are returned from both manifest
// files and/or regexes
@@ -146,6 +147,7 @@ public ApexClassCodeCoverageBean[] calculateAggregatedCodeCoverageUsingToolingAP
}
+ // results are processed separately from thread submissions
for (int i = 0; i < numOfBatches; i++) {
try {
recordObject.addAll((JSONArray) pool.take().get().get("records"));
@@ -176,6 +178,8 @@ public ApexClassCodeCoverageBean[] calculateAggregatedCodeCoverageUsingToolingAP
LOG.debug("responseJsonObject says " + responseJsonObject + "\n relativeServiceURL is "
+ relativeServiceURL + "\n soqlcc is " + soqlcc);
if (responseJsonObject != null) {
+ String responseStr = responseJsonObject.toJSONString();
+ LOG.debug(responseStr);
apexClassCodeCoverageBeans = processJSONResponseAndConstructCodeCoverageBeans(connection,
(JSONArray) responseJsonObject.get("records"));
}
@@ -392,4 +396,4 @@ public int getOrgWideCodeCoverage() {
ApexUnitCodeCoverageResults.orgWideCodeCoverage = coverage;
return coverage;
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/sforce/cd/apexUnit/client/codeCoverage/WebServiceInvoker.java b/src/main/java/com/sforce/cd/apexUnit/client/codeCoverage/WebServiceInvoker.java
old mode 100644
new mode 100755
index 8739aac..3d40b77
--- a/src/main/java/com/sforce/cd/apexUnit/client/codeCoverage/WebServiceInvoker.java
+++ b/src/main/java/com/sforce/cd/apexUnit/client/codeCoverage/WebServiceInvoker.java
@@ -25,12 +25,15 @@
import java.util.Set;
import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
+import org.apache.commons.httpclient.params.HttpClientParams;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.slf4j.Logger;
@@ -64,12 +67,25 @@ public HashMap doPost(String relativeServiceURL) {
HttpClient httpclient = new HttpClient();
String requestString = "";
HashMap responseMap = new HashMap();
+
try {
// the client id and secret is applicable across all dev orgs
requestString = generateRequestString();
String authorizationServerURL = CommandLineArguments.getOrgUrl() + relativeServiceURL;
httpclient.getParams().setSoTimeout(0);
+ //solve Cookie rejected: "$Version=0; BrowserId=Su_SOgNxEeqrrjGlvnCIuA; $Path=/; $Domain=.salesforce.com". Domain attribute ".salesforce.com" violates RFC 2109: host minus domain may not contain any dots
+ httpclient.getParams().setParameter(HttpClientParams.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
+
+ // Set proxy if needed
+ if (CommandLineArguments.getProxyHost() != null && CommandLineArguments.getProxyPort() != null) {
+ LOG.debug("Setting proxy configuraiton to " + CommandLineArguments.getProxyHost() + " on port "
+ + CommandLineArguments.getProxyPort());
+ HostConfiguration hostConfiguration = httpclient.getHostConfiguration();
+ hostConfiguration.setProxy(CommandLineArguments.getProxyHost(),CommandLineArguments.getProxyPort());
+ httpclient.setHostConfiguration(hostConfiguration);
+ }
+
post = new PostMethod(authorizationServerURL);
post.addRequestHeader("Content-Type", "application/x-www-form-urlencoded");
post.addRequestHeader("X-PrettyPrint", "1");
@@ -140,6 +156,16 @@ public static JSONObject doGet(String relativeServiceURL, String accessToken) {
LOG.debug("relativeServiceURL in doGet method:" + relativeServiceURL);
HttpClient httpclient = new HttpClient();
+ //solve Cookie rejected: "$Version=0; BrowserId=Su_SOgNxEeqrrjGlvnCIuA; $Path=/; $Domain=.salesforce.com". Domain attribute ".salesforce.com" violates RFC 2109: host minus domain may not contain any dots
+ httpclient.getParams().setParameter(HttpClientParams.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
+ // Set proxy if needed
+ if (CommandLineArguments.getProxyHost() != null && CommandLineArguments.getProxyPort() != null) {
+ LOG.debug("Setting proxy configuraiton to " + CommandLineArguments.getProxyHost() + " on port "
+ + CommandLineArguments.getProxyPort());
+ HostConfiguration hostConfiguration = httpclient.getHostConfiguration();
+ hostConfiguration.setProxy(CommandLineArguments.getProxyHost(),CommandLineArguments.getProxyPort());
+ httpclient.setHostConfiguration(hostConfiguration);
+ }
GetMethod get = null;
String authorizationServerURL = CommandLineArguments.getOrgUrl() + relativeServiceURL;
diff --git a/src/main/java/com/sforce/cd/apexUnit/client/testEngine/TestExecutor.java b/src/main/java/com/sforce/cd/apexUnit/client/testEngine/TestExecutor.java
old mode 100644
new mode 100755
diff --git a/src/main/java/com/sforce/cd/apexUnit/client/testEngine/TestStatusPollerAndResultHandler.java b/src/main/java/com/sforce/cd/apexUnit/client/testEngine/TestStatusPollerAndResultHandler.java
index bb259ac..c4df6a8 100644
--- a/src/main/java/com/sforce/cd/apexUnit/client/testEngine/TestStatusPollerAndResultHandler.java
+++ b/src/main/java/com/sforce/cd/apexUnit/client/testEngine/TestStatusPollerAndResultHandler.java
@@ -67,7 +67,7 @@ public ApexReportBean[] fetchResultsFromParentJobId(String parentJobId, PartnerC
int index = 0;
SObject[] sObjects = queryResult.getRecords();
if (sObjects != null) {
- totalTestMethodsExecuted = sObjects.length;
+ totalTestMethodsExecuted += sObjects.length;
LOG.info("Total test methods executed: " + TestStatusPollerAndResultHandler.totalTestMethodsExecuted);
apexReportBeans = new ApexReportBean[sObjects.length];
for (SObject sobject : sObjects) {
@@ -141,7 +141,7 @@ public boolean waitForTestsToComplete(String parentJobId, PartnerConnection conn
if (sObjects != null) {
String status = "";
int totalTests = sObjects.length;
- totalTestClasses = totalTests;
+ totalTestClasses += totalTests;
int remainingTests = totalTests;
LOG.info("Total test classes to execute: " + totalTestClasses);
String testId = "";
diff --git a/src/main/java/com/sforce/cd/apexUnit/report/ApexCodeCoverageReportGenerator.java b/src/main/java/com/sforce/cd/apexUnit/report/ApexCodeCoverageReportGenerator.java
index 8d77dc5..700ba31 100644
--- a/src/main/java/com/sforce/cd/apexUnit/report/ApexCodeCoverageReportGenerator.java
+++ b/src/main/java/com/sforce/cd/apexUnit/report/ApexCodeCoverageReportGenerator.java
@@ -29,7 +29,7 @@
public class ApexCodeCoverageReportGenerator {
- public static void generateHTMLReport(ApexClassCodeCoverageBean[] apexClassCodeCoverageBeans) {
+ public static void generateHTMLReport(ApexClassCodeCoverageBean[] apexClassCodeCoverageBeans, File reportDir) {
// Preparing the table:
StringBuilder htmlBuilder = new StringBuilder();
htmlBuilder.append("");
@@ -112,17 +112,17 @@ public static void generateHTMLReport(ApexClassCodeCoverageBean[] apexClassCodeC
// provide link to the test report
appendTag(htmlBuilder, "header", "Apex Test Report: ");
appendLineSpaces(htmlBuilder, 2);
- String workingDir = System.getProperty("user.dir");
+ String workingDir = reportDir.getPath();
String apexUnitTestReportPath = "";
if (!workingDir.contains("jenkins")) {
- apexUnitTestReportPath = workingDir + System.getProperty("file.separator") + "ApexUnitReport.xml";
+ apexUnitTestReportPath = "." + System.getProperty("file.separator") + "ApexUnitReport.xml";
} else {
int lastIndexOfSlash = workingDir.lastIndexOf('/');
String jobName = workingDir.substring(lastIndexOfSlash + 1);
apexUnitTestReportPath = "https://jenkins.internal.salesforce.com/job/" + jobName
+ "/lastCompletedBuild/testReport/";
}
- appendTag(htmlBuilder, "a", "style=\"font-size:125%\"; href=" + apexUnitTestReportPath, "Detailed Test Report");
+ appendTag(htmlBuilder, "a", "style=\"font-size:125%\"; href=\"" + apexUnitTestReportPath + "\"", "Detailed Test Report");
appendLineSpaces(htmlBuilder, 2);
appendTag(htmlBuilder, "header", "Detailed code coverage report: ");
@@ -216,17 +216,14 @@ public static void generateHTMLReport(ApexClassCodeCoverageBean[] apexClassCodeC
htmlBuilder.append("");
htmlBuilder.append("