com.sforce.cd.apexUnit.client.codeCoverage.CodeCoverageComputer.java Source code

Java tutorial

Introduction

Here is the source code for com.sforce.cd.apexUnit.client.codeCoverage.CodeCoverageComputer.java

Source

/* 
 * Copyright (c) 2016, salesforce.com, inc.
 * All rights reserved.
 * 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 adarsh.ramakrishna@salesforce.com
 */

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

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sforce.cd.apexUnit.ApexUnitUtils;
import com.sforce.cd.apexUnit.arguments.CommandLineArguments;
import com.sforce.cd.apexUnit.client.QueryConstructor;
import com.sforce.cd.apexUnit.client.connection.ConnectionHandler;
import com.sforce.cd.apexUnit.client.utils.ApexClassFetcherUtils;
import com.sforce.cd.apexUnit.report.ApexClassCodeCoverageBean;
import com.sforce.cd.apexUnit.report.ApexUnitCodeCoverageResults;
import com.sforce.soap.partner.PartnerConnection;

public class CodeCoverageComputer {
    private static Logger LOG = LoggerFactory.getLogger(CodeCoverageComputer.class);
    Properties prop = new Properties();
    String propFileName = "config.properties";
    private String SUPPORTED_VERSION = System.getProperty("API_VERSION");

    /*
     * Constructor for CodeCoverageComputer Initialize SUPPORTED_VERSION
     * variable from property file TODO fetch SUPPORTED_VERSION from the org(by
     * querying?)
     */
    public CodeCoverageComputer() {
        // execute below code Only when System.getProperty() call doesn't
        // function
        if (SUPPORTED_VERSION == null) {
            InputStream inputStream = getClass().getClassLoader().getResourceAsStream(propFileName);
            if (inputStream != null) {
                try {
                    prop.load(inputStream);
                } catch (IOException e) {
                    ApexUnitUtils.shutDownWithErrMsg(
                            "IO exception encountered while reading from the file:" + propFileName);
                }
            }
            SUPPORTED_VERSION = prop.getProperty("API_VERSION");
        }
    }

    /**
     * Calculate Aggregated code coverage results for the Apex classes using
     * Tooling API's
     * 
     * @return code coverage result(beans) as array
     */
    public ApexClassCodeCoverageBean[] calculateAggregatedCodeCoverageUsingToolingAPI() {
        PartnerConnection connection = ConnectionHandler.getConnectionHandlerInstance().getConnection();

        ApexClassCodeCoverageBean[] apexClassCodeCoverageBeans = null;
        String[] classesAsArray = null;

        /*
         * Builder design pattern construct the test class array by building the
         * final array using simple objects(arrays) viz. array from Manifest
         * file and array from regex prefix
         */
        // read class names from manifest files
        if (CommandLineArguments.getClassManifestFiles() != null) {
            LOG.debug(" Fetching apex classes from location : " + CommandLineArguments.getClassManifestFiles());
            classesAsArray = ApexClassFetcherUtils
                    .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);
        }
        // Do not proceed if no class names are returned from both manifest
        // files and/or regexes
        if (classesAsArray != null && classesAsArray.length > 0) {
            String classArrayAsStringForQuery = processClassArrayForQuery(classesAsArray);
            String relativeServiceURL = "/services/data/v" + SUPPORTED_VERSION + "/tooling";
            // compute aggregated code coverage
            String soqlcc = QueryConstructor.getAggregatedCodeCoverage(classArrayAsStringForQuery);

            JSONObject responseJsonObject = null;
            responseJsonObject = WebServiceInvoker.doGet(relativeServiceURL, soqlcc,
                    OAuthTokenGenerator.getOrgToken());
            LOG.debug("responseJsonObject says " + responseJsonObject + "\n relativeServiceURL is "
                    + relativeServiceURL + "\n soqlcc is " + soqlcc);
            if (responseJsonObject != null) {
                apexClassCodeCoverageBeans = processJSONResponseAndConstructCodeCoverageBeans(connection,
                        responseJsonObject);
            }
            if (apexClassCodeCoverageBeans == null) {
                ApexUnitUtils.shutDownWithErrMsg(
                        "Code coverage metrics not computed. Null object returned while processing the JSON response from the Tooling API");
            }
        } else {
            ApexUnitUtils.shutDownWithErrMsg("No/Invalid Apex source classes mentioned in manifest file and/or "
                    + "regex pattern for ApexSourceClassPrefix didn't return any Apex source class names from the org");
        }
        return apexClassCodeCoverageBeans;
    }

    /*
     * convert string array into csv string to facilitate the querying
     * 
     * @param: class names as string array
     * 
     * @return csv class names as string
     */
    private String processClassArrayForQuery(String[] classesAsArray) {
        String queryString = "";
        for (int i = 0; i < classesAsArray.length; i++) {
            queryString += "'" + classesAsArray[i] + "'";
            queryString += ",";
        }
        if (queryString.length() > 1) {
            queryString = queryString.substring(0, queryString.length() - 1);
        }
        return queryString;
    }

    /*
     * Derive an array of code coverage beans from processing the JSON response
     * of Tooling API call
     * 
     * @param connection = partner connection
     * 
     * @param responseJsonObject - json response object that needs to be
     * processed
     * 
     * @return code coverage result(beans) as array
     */
    private ApexClassCodeCoverageBean[] processJSONResponseAndConstructCodeCoverageBeans(
            PartnerConnection connection, JSONObject responseJsonObject) {
        int classCounter = 0;
        int coveredLinesForTheTeam = 0;
        int unCoveredLinesForTheTeam = 0;
        String responseStr = responseJsonObject.toJSONString();
        LOG.debug(responseStr);
        JSONArray recordObject = (JSONArray) responseJsonObject.get("records");
        if (recordObject != null && recordObject.size() > 0) {
            ApexClassCodeCoverageBean[] apexClassCodeCoverageBeans = new ApexClassCodeCoverageBean[recordObject
                    .size()];
            for (int i = 0; i < recordObject.size(); ++i) {

                ApexClassCodeCoverageBean apexClassCodeCoverageBean = new ApexClassCodeCoverageBean();
                // The object below is one record from the ApexCodeCoverage
                // object
                JSONObject rec = (JSONObject) recordObject.get(i);

                // ApexClassOrTriggerId - The ID of the class or trigger under
                // test.
                String apexClassOrTriggerId = (String) rec.get("ApexClassOrTriggerId").toString();
                if (apexClassOrTriggerId != null) {
                    int coveredLines = 0;
                    if (rec.get("NumLinesCovered") != null) {
                        coveredLines = Integer.valueOf((String) rec.get("NumLinesCovered").toString());
                        coveredLinesForTheTeam += coveredLines;
                    } else {
                        LOG.debug(apexClassOrTriggerId + " has NumLinesCovered as NULL !!!!!!!!!");
                    }
                    int unCoveredLines = 0;
                    if (rec.get("NumLinesUncovered") != null) {
                        unCoveredLines = Integer.valueOf((String) rec.get("NumLinesUncovered").toString());
                        unCoveredLinesForTheTeam += unCoveredLines;
                    } else {
                        LOG.debug(apexClassOrTriggerId + " has NumLinesUncovered as NULL !!!!!!!!!");
                    }
                    if (rec.get("Coverage") != null) {
                        JSONObject codeCoverageLineLists = (JSONObject) rec.get("Coverage");
                        JSONArray coveredLinesJsonArray = (JSONArray) codeCoverageLineLists.get("coveredLines");
                        JSONArray uncoveredLinesJsonArray = (JSONArray) codeCoverageLineLists.get("uncoveredLines");
                        List<Long> coveredLinesList = new ArrayList<Long>();
                        for (int j = 0; j < coveredLinesJsonArray.size(); j++) {
                            coveredLinesList.add((Long) coveredLinesJsonArray.get(j));
                            LOG.debug("covered " + (Long) coveredLinesJsonArray.get(j));
                        }
                        if (coveredLinesList.size() > 0) {
                            apexClassCodeCoverageBean.setCoveredLinesList(coveredLinesList);
                        }

                        List<Long> uncoveredLinesList = new ArrayList<Long>();
                        for (int k = 0; k < uncoveredLinesJsonArray.size(); k++) {
                            uncoveredLinesList.add((Long) uncoveredLinesJsonArray.get(k));
                            LOG.debug("uncovered " + (Long) uncoveredLinesJsonArray.get(k));
                        }
                        if (uncoveredLinesList.size() > 0) {
                            apexClassCodeCoverageBean.setUncoveredLinesList(uncoveredLinesList);
                        }
                    }

                    apexClassCodeCoverageBean.setNumLinesCovered(coveredLines);
                    apexClassCodeCoverageBean.setNumLinesUncovered(unCoveredLines);
                    apexClassCodeCoverageBean.setApexClassorTriggerId(apexClassOrTriggerId);
                    HashMap<String, String> apexClassInfoMap = ApexClassFetcherUtils
                            .fetchApexClassInfoFromId(connection, apexClassOrTriggerId);
                    String apexClassName = apexClassInfoMap.get("Name");
                    String apiVersion = apexClassInfoMap.get("ApiVersion");
                    String lengthWithoutComments = apexClassInfoMap.get("LengthWithoutComments");
                    apexClassCodeCoverageBean.setApexClassName(apexClassName);
                    apexClassCodeCoverageBean.setApiVersion(apiVersion);
                    apexClassCodeCoverageBean.setLengthWithoutComments(lengthWithoutComments);
                    apexClassCodeCoverageBeans[classCounter++] = apexClassCodeCoverageBean;

                    LOG.info("Record number # " + classCounter + " : coveredLines : " + coveredLines
                            + " : unCoveredLines : " + unCoveredLines + " : code coverage % : "
                            + apexClassCodeCoverageBean.getCoveragePercentage() + " : apexClassOrTriggerId : "
                            + apexClassOrTriggerId + " : apexClassName : " + apexClassName + " : apiVersion : "
                            + apiVersion + " : lengthWithoutComments : " + lengthWithoutComments);
                }
            }
            double totalLines = coveredLinesForTheTeam + unCoveredLinesForTheTeam;
            if (totalLines > 0.0) {
                ApexUnitCodeCoverageResults.teamCodeCoverage = (coveredLinesForTheTeam / (totalLines)) * 100.0;
            } else {
                ApexUnitCodeCoverageResults.teamCodeCoverage = 100.0;
            }
            LOG.info(
                    "####################################   Summary of code coverage computation for the team..  #################################### ");
            LOG.info("Total Covered lines : " + coveredLinesForTheTeam + "\n Total Uncovered lines : "
                    + unCoveredLinesForTheTeam);

            LOG.info("Team code coverage is : " + ApexUnitCodeCoverageResults.teamCodeCoverage + "%");
            return apexClassCodeCoverageBeans;
        } else {
            // no code coverage record object found in the response. return null
            return null;
        }
    }

    /**
     * This method is not used currently Calculate code coverage results for the
     * Apex classes using Tooling API's This method is intended to provide code
     * coverage at method level for each class . This indicates which exact
     * method needs more coverage
     * 
     * @return
     */
    public void calculateCodeCoverageUsingToolingAPI(String classArrayAsStringForQuery) {
        int classCounter = 0;
        String relativeServiceURL = "/services/data/v" + SUPPORTED_VERSION + "/tooling";
        String soqlcc = QueryConstructor.getClassLevelCodeCoverage(classArrayAsStringForQuery);
        LOG.debug("OAuthTokenGenerator.getOrgToken() : " + OAuthTokenGenerator.getOrgToken());
        JSONObject responseJsonObject = null;
        responseJsonObject = WebServiceInvoker.doGet(relativeServiceURL, soqlcc, OAuthTokenGenerator.getOrgToken());

        if (responseJsonObject != null) {
            String responseStr = responseJsonObject.toJSONString();
            LOG.debug(responseStr);
            JSONArray recordObject = (JSONArray) responseJsonObject.get("records");
            for (int i = 0; i < recordObject.size(); ++i) {
                classCounter++;
                // The object below is one record from the ApexCodeCoverage
                // object
                JSONObject rec = (JSONObject) recordObject.get(i);

                int coveredLines = Integer.valueOf((String) rec.get("NumLinesCovered").toString());
                int unCoveredLines = Integer.valueOf((String) rec.get("NumLinesUncovered").toString());
                // ApexTestClassId - The ID of the test class.
                String apexTestClassID = (String) rec.get("ApexTestClassId").toString();
                // ApexClassOrTriggerId - The ID of the class or trigger under
                // test.
                String apexClassorTriggerId = (String) rec.get("ApexClassOrTriggerId").toString();
                String testMethodName = (String) rec.get("TestMethodName").toString();
                LOG.info("Record number # " + classCounter + " : coveredLines : " + coveredLines
                        + " : unCoveredLines : " + unCoveredLines + " : apexTestClassID : " + apexTestClassID
                        + " : apexClassorTriggerId : " + apexClassorTriggerId + " : testMethodName : "
                        + testMethodName);

            }

        }
    }

    /*
     * Calculate org wide code coverage results for all the Apex classes in the
     * org using Tooling API's
     * 
     * @return org wide code coverage result as integer
     */
    public int getOrgWideCodeCoverage() {
        String relativeServiceURL = "/services/data/v" + SUPPORTED_VERSION + "/tooling";
        String soql = QueryConstructor.getOrgWideCoverage();
        int coverage = 0;
        JSONObject responseJsonObject = null;
        responseJsonObject = WebServiceInvoker.doGet(relativeServiceURL, soql, OAuthTokenGenerator.getOrgToken());

        if (responseJsonObject != null) {
            String responseStr = responseJsonObject.toJSONString();
            LOG.debug("responseStr during org wide code coverage" + responseStr);
            JSONArray recordObject = (JSONArray) responseJsonObject.get("records");
            for (int i = 0; i < recordObject.size(); ++i) {

                JSONObject rec = (JSONObject) recordObject.get(i);

                coverage = Integer.valueOf((String) rec.get("PercentCovered").toString());
                LOG.info(
                        "####################################   Org wide code coverage result  #################################### ");
                LOG.info("Org wide code coverage : " + coverage + "%");
            }
        } else {
            ApexUnitUtils.shutDownWithErrMsg("Org wide code coverage not computed");
        }
        ApexUnitCodeCoverageResults.orgWideCodeCoverage = coverage;
        return coverage;
    }
}