com.sforce.cd.apexUnit.client.testEngine.TestStatusPollerAndResultHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.sforce.cd.apexUnit.client.testEngine.TestStatusPollerAndResultHandler.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 poll and fetch the results for the ApexUnit test executions
 * 
 * @author adarsh.ramakrishna@salesforce.com
 */

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

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.time.StopWatch;
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.ApexReportBean;
import com.sforce.soap.partner.PartnerConnection;
import com.sforce.soap.partner.QueryResult;
import com.sforce.soap.partner.SaveResult;
import com.sforce.soap.partner.sobject.SObject;
import com.sforce.ws.ConnectionException;

public class TestStatusPollerAndResultHandler {

    public static boolean testFailures = false;
    public static int totalTestMethodsExecuted = 0;
    public static int totalTestClasses = 0;
    public static int totalTestClassesAborted = 0;
    public static List<String> failedTestMethods = new ArrayList<String>();

    private static Logger LOG = LoggerFactory.getLogger(TestStatusPollerAndResultHandler.class);

    public ApexReportBean[] fetchResultsFromParentJobId(String parentJobId, PartnerConnection conn) {
        waitForTestsToComplete(parentJobId, conn);
        LOG.info("All tests have now completed executing!!");
        // Each test method execution is represented by a single ApexTestResult
        // record.
        // For example, if an Apex test class contains six test methods,
        // six ApexTestResult records are created.
        // These records are in addition to the ApexTestQueueItem record that
        // represents the Apex class.
        String soql = QueryConstructor.fetchResultFromApexTestQueueItem(parentJobId);

        LOG.debug(soql);
        ApexReportBean[] apexReportBeans = null;
        QueryResult queryResult = null;
        try {
            queryResult = conn.query(soql);
        } catch (ConnectionException e) {
            ApexUnitUtils.shutDownWithDebugLog(e, ConnectionHandler.logConnectionException(e, conn, soql));
        }
        if (queryResult.getDone()) {
            int index = 0;
            SObject[] sObjects = queryResult.getRecords();
            if (sObjects != null) {
                totalTestMethodsExecuted = sObjects.length;
                apexReportBeans = new ApexReportBean[sObjects.length];
                for (SObject sobject : sObjects) {
                    ApexReportBean apexReportBean = populateReportBean(conn, sobject);
                    apexReportBeans[index++] = apexReportBean;
                }
            }
        }
        return apexReportBeans;
    }

    private ApexReportBean populateReportBean(PartnerConnection conn, SObject sobject) {

        String apexClassId = sobject.getField("ApexClassId").toString();
        ApexReportBean apexReportBean = null;
        if (apexClassId != null) {
            apexReportBean = new ApexReportBean();
            apexReportBean.setApexClassId(sobject.getField("ApexClassId").toString());
            if (ApexClassFetcherUtils.apexClassMap.get(apexReportBean.getApexClassId()) != null) {
                apexReportBean
                        .setApexClassName(ApexClassFetcherUtils.apexClassMap.get(apexReportBean.getApexClassId()));
            } else {
                apexReportBean.setApexClassName(
                        ApexClassFetcherUtils.fetchApexTestClassNameFromId(conn, apexReportBean.getApexClassId()));
            }
            if (sobject.getField("MethodName") != null) {
                apexReportBean.setMethodName(sobject.getField("MethodName").toString());
            }
            if (sobject.getField("Message") != null) {
                apexReportBean.setMessage(sobject.getField("Message").toString());
            }
            if (sobject.getField("Outcome") != null) {
                String outcome = sobject.getField("Outcome").toString();
                apexReportBean.setOutcome(outcome);
                if (outcome.equalsIgnoreCase("fail") || outcome.equalsIgnoreCase("compilefail")) {
                    testFailures = true;
                    failedTestMethods.add(apexReportBean.getApexClassName() + "." + apexReportBean.getMethodName());
                }
            }
            if (sobject.getField("RunTime") != null) {
                apexReportBean.setTimeElapsed(Long.parseLong(sobject.getField("RunTime").toString()));
            }
            if (sobject.getField("StackTrace") != null) {
                apexReportBean.setStackTrace(sobject.getField("StackTrace").toString());
            }
            // LOG.info("SystemModstamp,TestTimestamp"+sobject.getField("SystemModstamp")+
            // sobject.getField("TestTimestamp"));
            // LOG.info("ApexLog.DurationMilliseconds,ApexLog.Operation,ApexLog.Request,ApexLog.Status,ApexClass.Name"+
            // sobject.getField("ApexLog.DurationMilliseconds")+","+sobject.getField("ApexLog.DurationMilliseconds")
            // +","+sobject.getField("ApexLog.Request")+","+sobject.getField("ApexLog.Status")+","+sobject.getField("ApexClass.Name")+"...");
        }
        return apexReportBean;
    }

    public boolean waitForTestsToComplete(String parentJobId, PartnerConnection conn) {
        String soql = QueryConstructor.getTestExecutionStatus(parentJobId);
        // String soql =
        // QueryConstructor.getTestExecutionStatusAndTransactionTime(parentJobId);

        QueryResult queryResult;
        boolean testsCompleted = false;

        try {
            LOG.debug(soql);
            int index = 0;
            queryResult = conn.query(soql);

            if (queryResult.getDone()) {
                SObject[] sObjects = queryResult.getRecords();

                if (sObjects != null) {
                    String status = "";
                    int totalTests = sObjects.length;
                    totalTestClasses = totalTests;
                    int remainingTests = totalTests;
                    LOG.info("Total test classes to execute: " + totalTestClasses);
                    String testId = "";
                    String testName = "";
                    String id = "";
                    StopWatch stopWatch = new StopWatch();
                    long startTime = 0;
                    long endTime = 0;
                    for (SObject sobject : sObjects) {
                        sobject.setType("ApexTestQueueItem");
                        status = sobject.getField("Status").toString();
                        testId = sobject.getField("ApexClassId").toString();
                        id = sobject.getField("Id").toString();
                        LOG.debug("ID for ApexTestQueueItem: " + id);
                        testName = ApexClassFetcherUtils.apexClassMap.get(testId);
                        LOG.info("Now executing the test class: " + testName + " ("
                                + CommandLineArguments.getOrgUrl() + "/" + testId + " ) " + "Status : " + status);
                        stopWatch.reset();
                        stopWatch.start();
                        startTime = stopWatch.getTime();
                        LOG.debug("Start time: " + startTime);

                        while (status.equals("Processing") || status.equals("Queued") || status.equals("Preparing")
                                || !status.equals("Completed")) {

                            // break out of the loop if the test failed
                            if (status.equals("Failed")) {
                                LOG.info("Test class failure for : " + testName + " ("
                                        + CommandLineArguments.getOrgUrl() + "/" + testId + " ) ");
                                break;
                            } else if (status.equals("Aborted")) {
                                LOG.info("Test : " + testName + " (" + CommandLineArguments.getOrgUrl() + "/"
                                        + testId + " ) has been aborted.");
                                totalTestClassesAborted++;
                                break;
                            }
                            // Abort the long running tests based on user
                            // input(default: 10 minutes)
                            // stopWatch.getTime() will be in milliseconds,
                            // hence divide by 1000 to convert to seconds
                            // maxTestExecTimeThreshold will be in minutes,
                            // hence multiply by 60 to convert to seconds

                            if (CommandLineArguments.getMaxTestExecTimeThreshold() != null
                                    && stopWatch.getTime()
                                            / 1000.0 > CommandLineArguments.getMaxTestExecTimeThreshold() * 60
                                    && status.equals("Processing")) {
                                LOG.info("Oops! This test is a long running test. "
                                        + CommandLineArguments.getMaxTestExecTimeThreshold()
                                        + " minutes elapsed; aborting the test: " + testName);

                                // create new sobject for updating the record
                                SObject newSObject = new SObject();
                                newSObject.setType("ApexTestQueueItem");
                                newSObject.setField("Id", id);
                                // abort the test using DML, set status to
                                // "Aborted"
                                newSObject.setField("Status", "Aborted");
                                totalTestClassesAborted++;
                                // logging the status and id fields to compare
                                // them for pre and post update call

                                try {
                                    // TODO : up to 10 records can be updated at
                                    // a time by update() call.
                                    // add the logic to leverage this feature.
                                    // Currently only one record is being
                                    // updated(aborted)

                                    // Challenge: By the time we wait for 10
                                    // records that needs to be aborted, the
                                    // 'to-be-aborted' test might continue to
                                    // run and might get completed

                                    // update() call- analogous to UPDATE
                                    // Statement in SQL
                                    SaveResult[] saveResults = conn.update(new SObject[] { newSObject });

                                    LOG.debug("Stop time: " + stopWatch.getTime());
                                    stopWatch.stop();

                                    for (int i = 0; i < saveResults.length; i++) {
                                        if (saveResults[i].isSuccess()) {
                                            LOG.debug("The record " + saveResults[i].getId()
                                                    + " was updated successfully");
                                            LOG.info("Aborted test case: " + testName
                                                    + " since the test took more time than the threshold execution time of "
                                                    + CommandLineArguments.getMaxTestExecTimeThreshold() + " mins");
                                        } else {
                                            // There were errors during the
                                            // update call, so loop through and
                                            // print them out

                                            StringBuffer errorMsg = new StringBuffer();
                                            errorMsg.append("Record " + saveResults[i].getId() + " failed to save");
                                            for (int j = 0; j < saveResults[i].getErrors().length; j++) {
                                                com.sforce.soap.partner.Error err = saveResults[i].getErrors()[j];
                                                errorMsg.append("error code: " + err.getStatusCode().toString());
                                                errorMsg.append("error message: " + err.getMessage());
                                            }
                                            ApexUnitUtils.shutDownWithErrMsg(errorMsg.toString());
                                        }
                                    }
                                    LOG.debug("After update--" + newSObject.getField("Status").toString());
                                    break;
                                } catch (ConnectionException e) {
                                    ApexUnitUtils.shutDownWithDebugLog(e,
                                            ConnectionHandler.logConnectionException(e, conn, soql));
                                }

                            }

                            LOG.debug("Status of the test class: " + testName + " ("
                                    + CommandLineArguments.getOrgUrl() + "/" + testId + " ) " + " is : " + status);

                            while (stopWatch.getTime() % 1000 != 0) {
                                // wait, till 1 second elapses
                            }
                            LOG.debug("Firing polling query at " + stopWatch.getTime());
                            queryResult = conn.query(soql);
                            sObjects = queryResult.getRecords();
                            status = sObjects[index].getField("Status").toString();
                        }
                        endTime = stopWatch.getTime();
                        // get and log extended status for the test
                        if (sObjects[index] != null && sObjects[index].getField("ExtendedStatus") != null) {
                            String extendedStatus = sObjects[index].getField("ExtendedStatus").toString();
                            LOG.info("Test status for " + testName + ":" + extendedStatus);
                        }
                        LOG.info("Completed executing the test class: " + testName + ". Time taken by the test: "
                                + endTime / 1000 / 60 + " minutes," + (endTime / 1000) % 60 + " seconds");
                        index++;
                        remainingTests = totalTests - index;
                        LOG.info("Total tests executed " + index + " , Remaining tests " + remainingTests);
                        if (remainingTests == 0) {
                            testsCompleted = true;
                        }
                    }
                }
            }
        } catch (ConnectionException e) {
            ApexUnitUtils.shutDownWithDebugLog(e, ConnectionHandler.logConnectionException(e, conn, soql));
        }
        return testsCompleted;

    }

}