Skip to content
This repository was archived by the owner on Feb 12, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e93f5d0
Batchify code coverage calculation to avoid "Request-URI Too Long" error
eSh3m4 Oct 22, 2017
c6a40f4
Merge with the simpler solution to the "Request-URI Too Long" error o…
eSh3m4 Oct 22, 2017
00d04cf
Group tests results reports files into a single report directory and …
eSh3m4 Oct 23, 2017
2c05d86
add a relative path to the detailed results in the html report to eas…
eSh3m4 Oct 23, 2017
4c175c4
Incremented pom version from 2.3.6 to 2.3.8 to match the actual versi…
eSh3m4 Oct 24, 2017
db14316
Fix the issue with the number of test classes and methods in the html…
eSh3m4 Oct 25, 2017
18a25eb
Feature added : the parameter "-ignore.code.coverage.threshold" prev…
eSh3m4 Oct 25, 2017
9bd78d3
Feature added : the parameter "-ignore.test.failure" prevent builds f…
eSh3m4 Oct 25, 2017
65a3b3e
Adjust the success messages to the added features "-ignore.test.failu…
eSh3m4 Oct 25, 2017
028dfb3
Detailed build success message
eSh3m4 Oct 25, 2017
2f5c519
Update .travis.yml
ashwin512 Jan 11, 2018
a140f48
Merge pull request #69 from dragon0556/master
ashwin512 Jan 13, 2018
dead2ea
Merge pull request #1 from dragon0556/apex-unit-url-too-long
dragon0556 Mar 31, 2018
f385dd8
Merge pull request #71 from dragon0556/master
code-sagar Apr 2, 2018
b08ab58
Fix typo in escapeSingleQuote method documentation
foviedoITBA May 4, 2018
b92216c
remove cla file
vamshi-sfdc Sep 26, 2018
671c314
Merge pull request #74 from forcedotcom/removeCLA
vamshi-sfdc Sep 26, 2018
e6387e6
Set proxy configuration on WebServiceInvoker
damiendurant Dec 4, 2018
fb2ee5c
Merge pull request #75 from damiendurant/master
dragon0556 Dec 20, 2018
4f87d27
Merge pull request #72 from foviedoITBA/typo-fix
dragon0556 Dec 21, 2018
04e2148
changing version to 4.0.0
Aug 1, 2019
7d044b1
Merge pull request #78 from RutujaChaudhari/master
dragon0556 Aug 1, 2019
02a544b
Merge remote-tracking branch 'upstream/master' adding proxy support
eSh3m4 Nov 3, 2019
ad5b870
Update .yml file to meet Travis CI build requirements
eSh3m4 Nov 6, 2019
1cf782f
solve Cookie domain rejected issue
eSh3m4 Nov 12, 2019
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
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Binary file removed SFDC_CLA.pdf
Binary file not shown.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<groupId>com.sforce.cd.ApexUnit</groupId>
<artifactId>ApexUnit-core</artifactId>
<version>2.3.6</version>
<version>4.0.0</version>
<name>ApexUnit</name>
<description>Apex Unit v 2.0 with enhanced metrics and advanced features</description>
<!-- Fail fast for older Maven versions -->
Expand Down
27 changes: 17 additions & 10 deletions src/main/java/com/sforce/cd/apexUnit/ApexUnitRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

package com.sforce.cd.apexUnit;

import java.io.File;
import java.time.*;
import java.util.Arrays;
import java.util.List;

Expand Down Expand Up @@ -101,41 +103,43 @@ 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 ");
} else {
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
Expand All @@ -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) {
Expand All @@ -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..");
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
14 changes: 9 additions & 5 deletions src/main/java/com/sforce/cd/apexUnit/client/codeCoverage/CodeCoverageComputer.java
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
/*
* 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
*/

/*
* Class to compute code coverage for the given class names and org wide code coverage
*
* @author [email protected]
*/
*/


package com.sforce.cd.apexUnit.client.codeCoverage;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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"));
}
Expand Down Expand Up @@ -392,4 +396,4 @@ public int getOrgWideCodeCoverage() {
ApexUnitCodeCoverageResults.orgWideCodeCoverage = coverage;
return coverage;
}
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/sforce/cd/apexUnit/client/codeCoverage/WebServiceInvoker.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -64,12 +67,25 @@ public HashMap<String, String> doPost(String relativeServiceURL) {
HttpClient httpclient = new HttpClient();
String requestString = "";
HashMap<String, String> responseMap = new HashMap<String, String>();


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");
Expand Down Expand Up @@ -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;
Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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("<html>");
Expand Down Expand Up @@ -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: ");
Expand Down Expand Up @@ -216,17 +216,14 @@ public static void generateHTMLReport(ApexClassCodeCoverageBean[] apexClassCodeC
htmlBuilder.append("</body>");
htmlBuilder.append("</html>");

createHTMLReport(htmlBuilder.toString());
createHTMLReport(htmlBuilder.toString(), reportDir);
}

private static void createHTMLReport(String htmlBuffer) {
private static void createHTMLReport(String htmlBuffer, File reportDir) {

File tmpFile = null;
FileOutputStream tmpOut = null;
String workingDir = System.getProperty("user.dir") + System.getProperty("file.separator") + "Report";
File dir = new File(workingDir);
dir.mkdirs();
tmpFile = new File(dir, "ApexUnitReport.html");
tmpFile = new File(reportDir, "ApexUnitReport.html");
byte[] reportAsBytes;
try {
tmpOut = new FileOutputStream(tmpFile);
Expand Down