All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xceptance.xlt.mastercontroller.BasicConsoleUI Maven / Gradle / Ivy

/*
 * Copyright (c) 2005-2024 Xceptance Software Technologies GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.xceptance.xlt.mastercontroller;

import java.util.Arrays;
import java.util.Calendar;
import java.util.Formatter;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;

import com.xceptance.common.util.ConsoleUiUtils;
import com.xceptance.common.util.ProductInformation;
import com.xceptance.xlt.agentcontroller.AgentController;
import com.xceptance.xlt.agentcontroller.AgentStatusInfo;
import com.xceptance.xlt.agentcontroller.ScenarioStatus;
import com.xceptance.xlt.agentcontroller.TestResultAmount;
import com.xceptance.xlt.agentcontroller.TestUserStatus;
import com.xceptance.xlt.util.AgentControllerException;
import com.xceptance.xlt.util.AgentControllerInfo;
import com.xceptance.xlt.util.FailedAgentControllerCollection;
import com.xceptance.xlt.util.StatusUtils;

/**
 * The BasicConsoleUI is the base class for all master controller user interfaces.
 */
public abstract class BasicConsoleUI implements MasterControllerUI
{
    private static final String COPYRIGHT = "Copyright (c) 2005-%s %s. All rights reserved.";

    private static final List TEST_RESULT_AMOUNT_DISPLAY_NAMES = Arrays.asList(TestResultAmount.displayNames());

    private static final List TEST_RESULT_AMOUNTS = Arrays.asList(TestResultAmount.values());

    private static final List TEST_RESULT_SHORTCUTS = Arrays.asList(TestResultAmount.shortcuts());

    private static final List REPORT_CREATION_TYPE_SHORTCUTS = Arrays.asList(ReportCreationType.shortcuts());

    private static final List REPORT_CREATION_TYPE_DISPLAY_NAMES = Arrays.asList(ReportCreationType.displayNames());

    private static final List REPORT_CREATION_TYPES = Arrays.asList(ReportCreationType.values());

    private static final String TIME_TOTALS = "/";

    private static final String SETTING_NOT_AVAILABLE = "n/a";

    private static final String OVERFLOW_ERROR = "#OUT OF RANGE#";

    /**
     * The master controller this user interface interacts with.
     */
    protected final MasterController masterController;

    /**
     * Interval in seconds to update the agent status list.
     */
    private int statusListUpdateInterval;

    /**
     * Creates a new BasicConsoleUI object.
     *
     * @param masterController
     *            the master controller to use
     */
    public BasicConsoleUI(final MasterController masterController)
    {
        this.masterController = masterController;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void agentFilesUploaded()
    {
        print();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void agentsStarted()
    {
        print();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void agentsStopped()
    {
        print();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void agentStatusReceived(final FailedAgentControllerCollection results)
    {
        if (!results.isEmpty())
        {
            for (final Map.Entry result : results.getMap().entrySet())
            {
                final Exception ex = result.getValue();
                if (ex != null)
                {
                    final String msg = getUserFriendlyExceptionMessage(ex);
                    System.out.println("Failed to get agent status from " + result.getKey() + " -> " + msg);
                }
            }
            System.out.println();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void downloadingTestResults()
    {
        System.out.print(" -> Downloading test results");
    }

    /**
     * Triggers the master controller to download the test results from all known agent controllers.
     *
     * @param testResultAmount
     *            the amount of test result data to download
     * @return true if the operation was successful for all agent controllers; false otherwise
     */
    public boolean downloadTestResults(TestResultAmount testResultAmount)
    {
        if (testResultAmount == null)
        {
            testResultAmount = ConsoleUiUtils.selectItem("Select the data to be downloaded:", TEST_RESULT_SHORTCUTS,
                                                         TEST_RESULT_AMOUNT_DISPLAY_NAMES, TEST_RESULT_AMOUNTS);
            System.out.println();
        }

        boolean ok = !TestResultAmount.CANCEL.equals(testResultAmount);

        if (ok)
        {
            System.out.println("Downloading test results... Please be patient, it might take some time...");
            ok = masterController.downloadTestResults(testResultAmount);
            System.out.println();

            if (ok)
            {
                handleTestComment();
                System.out.println("\nResults have been downloaded to: " + masterController.getCurrentTestResultsDirectory());
            }
        }

        return ok;
    }

    /**
     * Triggers the master controller to generate the test report from the downloaded results and queries the user for
     * time range option.
     *
     * @return true if the operation was successful; false otherwise
     */
    public boolean generateReport()
    {
        return generateReport(null);
    }

    /**
     * Triggers the master controller to generate the test report from the downloaded results.
     *
     * @param reportCreationType
     *            time range option to generate the report from or null to query the user
     * @return true if the operation was successful; false otherwise
     */
    public boolean generateReport(ReportCreationType reportCreationType)
    {
        boolean result = false;

        if (reportCreationType == null)
        {
            reportCreationType = ConsoleUiUtils.selectItem("Would you like to include the ramp-up period in the test report?",
                                                           REPORT_CREATION_TYPE_SHORTCUTS, REPORT_CREATION_TYPE_DISPLAY_NAMES,
                                                           REPORT_CREATION_TYPES);
            System.out.println();
        }

        if (!reportCreationType.equals(ReportCreationType.ABORT))
        {
            System.out.println("Generating load test report based on latest download...");
            result = masterController.generateReport(reportCreationType);

            if (!result)
            {
                System.out.println(" -> Failed");
            }

            System.out.println();
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getStatusListUpdateInterval()
    {
        return statusListUpdateInterval;
    }

    /**
     * Checks whether there is a running load test.
     *
     * @return true if there is a running load test; false otherwise
     */
    public boolean isLoadTestRunning()
    {
        // check whether there are still running agents
        try
        {
            if (masterController.isAnyAgentRunning())
            {
                System.out.println("-> WARNING: Unable to execute command because a test is currently running.");
                return true;
            }
        }
        catch (final AgentControllerException e)
        {
            // potentially there are running agents even if we can not communicate with the agent controller.
            System.out.println("-> WARNING: Unable to execute command because at least one agent controller cannot be reached.");
            return true;
        }

        return false;
    }

    /**
     * Prints the status of all agents to the console in a tabular format. The output contains one line per simulated
     * user type.
     */
    public void printAgentStatusList()
    {
        final List agentControllerStatusList = masterController.getAgentControllerStatusList();

        final Map> failedAgentsByHost = StatusUtils.getFailedAgentsByHost(agentControllerStatusList);
        final List scenarioStatusList = StatusUtils.aggregateScenarioStatusLists(agentControllerStatusList);

        if (!scenarioStatusList.isEmpty())
        {
            // determine longest user name
            int maxNameLength = 9;
            for (final ScenarioStatus scenarioStatus : scenarioStatusList)
            {
                maxNameLength = Math.max(maxNameLength, scenarioStatus.getUserName().length());
            }

            // format and append the header line
            final StringBuilder buf = new StringBuilder();
            final Formatter formatter = new Formatter(buf);

            final String headerFormat = "%-" + maxNameLength +
                                        "s   State         Running Users   Iterations   Last Time   Avg. Time   Elapsed Time      Events          Errors" +
                                        "   Progress\n";
            formatter.format(headerFormat, "Test Case");

            final String separatorLineFormat = "%s   --------   ----------------   ----------   ---------   ---------   ------------   ---------   -------------" +
                                               "   --------\n";
            formatter.format(separatorLineFormat, StringUtils.repeat("-", maxNameLength));

            // format and append the user type lines
            final String format = "%-" + maxNameLength + "s   %-8s   %,6d of %,6d   %,10d   %9s   %9s     %10s   %,9d   %,6d %6s" +
                                  "   %7d%%\n";
            final String timeFormat = "%,7.2f s";
            final String failedFormat = "%-" + maxNameLength + "s   %-8s   %s\n";

            for (final ScenarioStatus scenarioStatus : scenarioStatusList)
            {
                if (scenarioStatus.getState() == TestUserStatus.State.Failed)
                {
                    formatter.format(failedFormat, scenarioStatus.getUserName(), scenarioStatus.getState(), scenarioStatus.getException());
                }
                else
                {
                    final String elapsedTime = formatTime(scenarioStatus.getElapsedTime());
                    int errorRate = 0;
                    if (scenarioStatus.getIterations() > 0)
                    {
                        errorRate = Math.round(scenarioStatus.getErrors() * 100.0f / scenarioStatus.getIterations());
                    }

                    final double lastRuntime = scenarioStatus.getLastRuntime() / 1000.0;
                    final double avgRuntime = scenarioStatus.getAverageRuntime() / 1000.0;

                    formatter.format(format, scenarioStatus.getUserName(), scenarioStatus.getState(), scenarioStatus.getRunningUsers(),
                                     scenarioStatus.getTotalUsers(), scenarioStatus.getIterations(), String.format(timeFormat, lastRuntime),
                                     String.format(timeFormat, avgRuntime), elapsedTime, scenarioStatus.getEvents(),
                                     scenarioStatus.getErrors(), "(" + errorRate + "%)", scenarioStatus.getPercentageComplete());
                }
            }

            // format and append the total status line
            if (scenarioStatusList.size() > 1)
            {
                final ScenarioStatus summaryStatus = StatusUtils.getTotalScenarioStatus(scenarioStatusList);

                final String elapsedTime = formatTime(summaryStatus.getElapsedTime());
                final int iterations = summaryStatus.getIterations();
                final int errors = summaryStatus.getErrors();
                final int errorRate = (iterations > 0) ? Math.round(errors * 100.0f / iterations) : 0;

                formatter.format(separatorLineFormat, StringUtils.repeat("-", maxNameLength));
                formatter.format(format, summaryStatus.getUserName(), summaryStatus.getState(), summaryStatus.getRunningUsers(),
                                 summaryStatus.getTotalUsers(), iterations, TIME_TOTALS, TIME_TOTALS, elapsedTime,
                                 summaryStatus.getEvents(), errors, "(" + errorRate + "%)", summaryStatus.getPercentageComplete());
            }

            // print the status
            formatter.close();
            System.out.println(buf);
        }

        if (!failedAgentsByHost.isEmpty())
        {
            final StringBuilder sb = new StringBuilder();
            int count = 0;
            sb.append("->  Agent(s) exited unexpectedly:\n");
            for (final Entry> failedAgentsEntry : failedAgentsByHost.entrySet())
            {
                sb.append("  - ").append(failedAgentsEntry.getKey()).append("\n");
                final List failedAgents = failedAgentsEntry.getValue();
                for (final AgentStatusInfo failedAgent : failedAgents)
                {
                    sb.append("        Agent '").append(failedAgent.getAgentID()).append("' returned with exit code '")
                      .append(failedAgent.getExitCode()).append("'\n");
                }

                count += failedAgents.size();
            }
            sb.insert(3, Integer.toString(count));
            System.out.println(sb.toString());
        }
    }

    public void printLoadTestSettings()
    {
        // Get the current load profile.
        final String output = getLoadTestSettings(masterController.getCurrentLoadProfile());
        if (!StringUtils.isBlank(output))
        {
            System.out.println();
            System.out.println("Load Profile");
            System.out.println(output);
        }
    }

    /**
     * This method returns a summary of the current load test settings.
     *
     * @param loadTestSettings
     *            The load test settings that should be analyzed.
     * @return A String containing the settings for all configured load profiles. In case no load profile is configured
     *         an empty String ("") will be returned.
     */
    String getLoadTestSettings(final TestLoadProfileConfiguration loadTestSettings)
    {
        // If the mastercontroller has not parsed the configuration yet, we quit quietly.
        final StringBuilder configLine = new StringBuilder();
        if (loadTestSettings != null)
        {
            final Formatter formatter = new Formatter(configLine);

            // Prepare the table header
            final String testCaseColumnHeading = "Test Case";
            final String arrivalRateColumnHeading = "Arrival Rate [eff]";
            final String usersColumnHeading = "Users [eff]";
            final String loadFactorColumnHeading = "Load Factor";
            final String measurementPeriodColumnHeading = "Measurement Period";
            final String colSep = " | ";
            final Map maxLengthMap = getColumnBoundaries(loadTestSettings);

            // Check what's longer: column heading or cell-content.
            final int maxNameLength = Math.max(maxLengthMap.get("testCases"), testCaseColumnHeading.length());
            final int arrivalLength = Math.max(maxLengthMap.get("arrivalRate"), arrivalRateColumnHeading.length());
            final int maxUserLength = Math.max(maxLengthMap.get("users"), usersColumnHeading.length());
            final int maxLoadFactor = Math.max(maxLengthMap.get("loadFactor"), loadFactorColumnHeading.length());
            final int measureLength = measurementPeriodColumnHeading.length();

            final int totalLength = maxNameLength + arrivalLength + maxUserLength + maxLoadFactor + measureLength + 4 * colSep.length();

            final String lineFormat = StringUtils.joinWith(colSep, "%s", "%s", "%s", "%s", "%s\n");
            final String dashLine = StringUtils.repeat("-", totalLength);
            formatter.format("%s\n", dashLine);

            formatter.format(lineFormat, StringUtils.center(testCaseColumnHeading, maxNameLength),
                             StringUtils.center(arrivalRateColumnHeading, arrivalLength),
                             StringUtils.center(usersColumnHeading, maxUserLength),
                             StringUtils.center(loadFactorColumnHeading, maxLoadFactor),
                             StringUtils.center(measurementPeriodColumnHeading, measureLength));

            formatter.format("%s\n", dashLine);

            int arrivalRateSum = 0, numberOfUsersSum = 0, measurementPeriodMaximum = 0;
            String previousLoadFactor = "";
            boolean sameLoadFactor = true, arrivalRateOverflow = false, userOverflow = false;

            for (final TestCaseLoadProfileConfiguration settings : loadTestSettings.getLoadTestConfiguration())
            {
                // Arrival Rate
                Pair boundaries = getMinMaxValue(settings.getArrivalRate());
                if (!arrivalRateOverflow && boundaries != null)
                {
                    try
                    {
                        arrivalRateSum = Math.addExact(arrivalRateSum, boundaries.getRight());
                    }
                    catch (final ArithmeticException e)
                    {
                        // We exceeded the maximum integer boundaries
                        arrivalRateOverflow = true;
                    }
                }

                final String arrivalRate = getIntRangeAsString(boundaries);

                // Number of Users
                boundaries = getMinMaxValue(settings.getNumberOfUsers());
                if (!userOverflow && boundaries != null)
                {
                    try
                    {
                        numberOfUsersSum = Math.addExact(numberOfUsersSum, boundaries.getRight());
                    }
                    catch (final ArithmeticException e)
                    {
                        userOverflow = true;
                    }
                }
                final String users = getIntRangeAsString(boundaries);

                // Load Factor
                boundaries = getMinMaxValue(settings.getLoadFactor());
                final String loadFactor;
                if (boundaries != null)
                {
                    final double minLoadFactorFraction = boundaries.getLeft() / 1000.0;
                    final double maxLoadFactorFraction = boundaries.getRight() / 1000.0;

                    loadFactor = getDoubleRangeAsString(new MutablePair<>(minLoadFactorFraction, maxLoadFactorFraction));
                }
                else
                {
                    loadFactor = getDoubleRangeAsString(null);
                }

                if (sameLoadFactor)
                {
                    // At the beginning we need to initialize previousLoadFactor
                    if (StringUtils.isEmpty(previousLoadFactor))
                    {
                        previousLoadFactor = loadFactor;
                    }
                    else
                    {
                        sameLoadFactor = previousLoadFactor.equals(loadFactor);
                    }
                }

                final int measurementPeriod = settings.getMeasurementPeriod();
                measurementPeriodMaximum = Math.max(measurementPeriod, measurementPeriodMaximum);

                formatter.format(lineFormat, StringUtils.rightPad(settings.getUserName(), maxNameLength),
                                 StringUtils.leftPad(arrivalRate, arrivalLength), StringUtils.leftPad(users, maxUserLength),
                                 StringUtils.leftPad(loadFactor, maxLoadFactor),
                                 StringUtils.leftPad(convertSecondsToTime(measurementPeriod), measureLength));
            }

            // print summary line
            formatter.format("%s\n", StringUtils.repeat("-", totalLength));
            final int firstColWidth = arrivalLength + maxNameLength + colSep.length();

            if (!arrivalRateOverflow)
            {
                formatter.format("%," + firstColWidth + "d" + colSep, arrivalRateSum);
            }
            else
            {
                formatter.format("%" + Math.max(firstColWidth, OVERFLOW_ERROR.length()) + "s" + colSep, OVERFLOW_ERROR);
            }
            if (!userOverflow)
            {
                formatter.format("%," + (maxUserLength) + "d" + colSep, numberOfUsersSum);
            }
            else
            {
                formatter.format("%" + Math.max((maxUserLength), OVERFLOW_ERROR.length()) + "s" + colSep, OVERFLOW_ERROR);
            }
            formatter.format("%" + (maxLoadFactor) + "s" + colSep, (sameLoadFactor) ? previousLoadFactor : " ");
            formatter.format("%" + (measureLength) + "s\n", convertSecondsToTime(measurementPeriodMaximum));

            formatter.close();
        }

        return configLine.toString();
    }

    /**
     * Converts seconds to a more human readable time format. If the amount of seconds is negative, it will be first
     * negated.
     *
     * @param seconds
     *            The number of seconds which should be converted.
     * @return The returned time format follows the pattern h:mm:ss.
     */
    private String convertSecondsToTime(long seconds)
    {
        // Alternative format: "%d h %02d min %02d sec"
        seconds = Math.abs(seconds);
        if (seconds > 0)
        {
            final long rem = seconds % 3600;
            return String.format("%d:%02d:%02d", seconds / 3600, rem / 60, rem % 60);
        }
        return "0:00:00";
    }

    /**
     * This method searches the lowest and highest value in a 2D-array and returns them as a pair.
     *
     * @param pairs
     *            2D-array which follows the pattern {[timestamp_1, value_1], [timestamp_2, value_2] ...}.
     * @return Returns a pair, containing the lowest value in Left and the highest value in
     *         Right. 
* If pairs is null, null will be returned as well. */ private Pair getMinMaxValue(final int[][] pairs) { // Handle null objects. For example, arrivalRate and loadFactor might be null. // Also make sure that the array contains at least one value (pairs[0][1]). if (pairs == null || pairs.length == 0 || pairs[0].length == 1) { return null; } int min = pairs[0][1]; int max = pairs[0][1]; for (int i = 1; i < pairs.length; i++) { final int val = pairs[i][1]; min = Math.min(min, val); max = Math.max(max, val); } return new ImmutablePair<>(min, max); } /** * Calculates and stores the maximum String size for test case names, arrival rate, number of users and load factor. * Numerical values will be first converted to a String representation before the maximum String length is * determined. Numerical ranges will be also converted into String representations following the pattern * minValue..maxValue.
* The maximum lengths will be stored in a map and can be accessed by referencing to the keys testCases, * arrivalRate, users and loadFactor. * * @param loadTestSettings * The configured load test settings that should be analyzed. * @return A map which contains the String size of the longest test case name, arrival rate, number of users and * load factor. */ private Map getColumnBoundaries(final TestLoadProfileConfiguration loadTestSettings) { int maxTestCaseLength = 0; int maxArrivalRateLength = 0; int maxUsersLength = 0; int maxLoadFactor = 0; final Map map = new HashMap<>(); for (final TestCaseLoadProfileConfiguration profile : loadTestSettings.getLoadTestConfiguration()) { maxTestCaseLength = Math.max(maxTestCaseLength, profile.getUserName().length()); maxArrivalRateLength = Math.max(maxArrivalRateLength, getIntRangeAsString(getMinMaxValue(profile.getArrivalRate())).length()); maxUsersLength = Math.max(maxUsersLength, getIntRangeAsString(getMinMaxValue(profile.getNumberOfUsers())).length()); final Pair loadFactorPair = getMinMaxValue(profile.getLoadFactor()); if (loadFactorPair != null) { final double min = loadFactorPair.getLeft() / 1000.0; final double max = loadFactorPair.getRight() / 1000.0; maxLoadFactor = Math.max(maxLoadFactor, getDoubleRangeAsString(new ImmutablePair<>(min, max)).length()); } else { maxLoadFactor = Math.max(maxLoadFactor, getDoubleRangeAsString(null).length()); } } map.put("testCases", maxTestCaseLength); map.put("arrivalRate", maxArrivalRateLength); map.put("users", maxUsersLength); map.put("loadFactor", maxLoadFactor); return map; } /** * Converts the passed pair of Integers to a String. * * @param pair * The pair that should be converted to a String. * @return Depending on the content, one of following Strings will be returned:
* pair(null) --> {@value #SETTING_NOT_AVAILABLE}
* pair(n, n) ---> "n"
* pair(m, n) ---> "m..n" */ private String getIntRangeAsString(final Pair pair) { if (pair == null) { return SETTING_NOT_AVAILABLE; } final int min = pair.getLeft(); final int max = pair.getRight(); return (min == max) ? String.format("%,d", max) : String.format("%,d..%,d", min, max); } /** * Converts the passed pair of Floats to a String. Floats will be displayed with a precision of 3. * * @param pair * The pair that should be converted to a String. * @return Depending on the content, one of following Strings will be returned:
* pair(null) --> {@value #SETTING_NOT_AVAILABLE}
* pair(n, n) ---> "n"
* pair(m, n) ---> "m..n" */ private String getDoubleRangeAsString(final Pair pair) { if (pair == null) { return SETTING_NOT_AVAILABLE; } final double min = pair.getLeft(); final double max = pair.getRight(); return (min == max) ? String.format("%,.3f", max) : String.format("%,.3f..%,.3f", min, max); } /** * Prints the status of all agents to the console in a tabular format. */ public void printXltInfo() { final ProductInformation info = ProductInformation.getProductInformation(); // get the "to" year for the copyright final GregorianCalendar cal = new GregorianCalendar(); cal.setTime(info.getBuildDate()); final int year = cal.get(Calendar.YEAR); // format the info final String productInfo = info.getProductIdentifier(); final String copyright = String.format(COPYRIGHT, year, info.getVendorName()); final String licenseInfo = "XLT is Open Source and available under the Apache License 2.0."; // put it all together System.out.printf("\n%s\n%s\n%s\n\n", productInfo, copyright, licenseInfo); } /** * Prints some basic agent controller precheck information like accessibility or XLT conflict. */ public void printAgentControllerPreCheckInformation() { System.out.print("\nChecking for agent controller reachability and XLT version conflicts ... "); final AgentControllersInformation agentControllerInfos = masterController.getAgentControllerInformation(); final StringBuilder sb = new StringBuilder(); // check for errors if (agentControllerInfos.hasErrors()) { sb.append("\n\n"); sb.append("WARNING: At least one agent controller is unreachable.\n\n"); for (final AgentControllerInfo agentControllerInfo : agentControllerInfos.getAgentControllerInformation()) { final Exception ex = agentControllerInfo.getException(); if (ex != null) { sb.append(" -> ").append(agentControllerInfo.getName()).append(": ") .append(getUserFriendlyExceptionMessage(agentControllerInfo.getException())).append('\n'); } } } // check for different XLT version else if (agentControllerInfos.hasXltVersionConflict()) { sb.append("\n\n"); sb.append("WARNING: Master controller and agent controllers run different XLT versions.\n"); sb.append(" Master controller version: ") .append(ProductInformation.getProductInformation().getCondensedProductIdentifier()).append('\n'); } else { sb.append("OK\n"); } System.out.println(sb.toString()); } /** * {@inheritDoc} */ @Override public void receivingAgentStatus() { } /** * Runs the user interface. */ public abstract void run(); /** * Sets the number of seconds to wait before the status list is updated again. * * @param statusListUpdateInterval * the update interval */ public void setStatusListUpdateInterval(final int statusListUpdateInterval) { this.statusListUpdateInterval = statusListUpdateInterval; } /** * Triggers the master controller to start the agent on all known agent controllers. * * @param testCaseName * the name of the test case to start the agents for, or null if all active test cases * should be started * @param checkTestSuiteUploaded * whether to check if the test suite was successfully uploaded before * @return true if the operation was successful for all agent controllers; false otherwise */ public boolean startAgents(final String testCaseName, final boolean checkTestSuiteUploaded) { if (!checkTestSuiteUploaded || masterController.areAgentsInSync()) { System.out.println("Starting agents... "); boolean result = false; try { result = masterController.startAgents(testCaseName); } catch (final AgentControllerException e) { print(e.getFailed()); } catch (final Exception e) { print(e); } System.out.println(); return result; } else { System.out.println("The test suite has to be uploaded before a test can be started.\n"); return false; } } /** * {@inheritDoc} */ @Override public void startingAgents() { System.out.print("Starting agents... "); } /** * Triggers the master controller to stop the agent on all known agent controllers. * * @return true if the operation was successful for all agent controllers; false otherwise */ public boolean stopAgents() { System.out.println("Aborting agents... "); boolean result = false; try { result = masterController.stopAgents(); } catch (final AgentControllerException e) { print(e.getFailed()); } System.out.println(); return result; } /** * {@inheritDoc} */ @Override public void stoppingAgents() { System.out.print("Stopping agents... "); } /** * {@inheritDoc} */ @Override public void testResultsDownloaded(final FailedAgentControllerCollection results) { print(results); } /** * Triggers the master controller to upload the agent files to all known agent controllers. * * @return true if uploading was successful, false otherwise */ public boolean uploadAgentFiles() { boolean result = false; System.out.println("Uploading test suite... "); try { masterController.updateAgentFiles(); result = true; } catch (final AgentControllerException e) { print(e.getFailed()); } catch (final Exception t) { print(t); } System.out.println(); return result; } /** * {@inheritDoc} */ @Override public void uploadingAgentFiles() { System.out.print(" -> Uploading agent files... "); } /** * {@inheritDoc} */ @Override public void skipAgentControllerConnections(final FailedAgentControllerCollection unconnectedAgentControllers) { if (!unconnectedAgentControllers.isEmpty()) { System.out.println("\n-> WARNING: Skipped unreachable agent controllers:"); for (final AgentController agentcontroller : unconnectedAgentControllers.getAgentControllers()) { System.out.println(" SKIPPED " + agentcontroller); } } } /** * Handles the test comment. */ protected void handleTestComment() { masterController.setTestComment4DownloadedResults(); } /** * Returns the given time (difference) value in the format "hours:mins:secs". Negative values are prefixed with a * negative sign. The number of hours is not limited to 24. * * @param time * the time to convert * @return the formatted time */ private String formatTime(long time) { String sign = ""; if (time < 0) { sign = "-"; time = -time; } final long timeInSecs = time / 1000; final long timeInMins = timeInSecs / 60; final long timeInHours = timeInMins / 60; final long secs = timeInSecs % 60; final long mins = timeInMins % 60; final long hours = timeInHours; return String.format("%s%d:%02d:%02d", sign, hours, mins, secs); } /** * Builds the list of failures ready for printing. * * @param results * the list of errors * @return the formatted failures */ private String buildFailureList(final FailedAgentControllerCollection results) { final StringBuilder failed = new StringBuilder(); for (final Map.Entry result : results.getMap().entrySet()) { failed.append(" -> " + result.getKey()); final Exception ex = result.getValue(); if (ex != null) { final String msg = getUserFriendlyExceptionMessage(ex); failed.append(" - " + msg); } failed.append("\n"); } return failed.toString(); } /** * Returns a more user-friendly message for the given exception. * * @param ex * the exception * @return the user-friendly message */ protected String getUserFriendlyExceptionMessage(final Exception ex) { String msg = StringUtils.defaultString(ex.getMessage()); if (msg.startsWith("401:")) { msg = "Authentication failed. The agent controller rejected the master controller's password."; } return msg; } /** * Prints the result of an operation at the given agent controller. If the operation was successful, the passed * exception is null, otherwise it may hold more information. * * @param ex * the exception */ private void print(final FailedAgentControllerCollection results) { final StringBuilder failed = new StringBuilder(); if (results != null && results.getMap().size() > 0) { failed.append("- FAILED!\n\n"); failed.append(buildFailureList(results)); System.out.println(failed); } else { System.out.println("- OK"); } } private void print(final Exception e) { System.out.println("- FAILED: " + e.getMessage()); } private void print() { print((FailedAgentControllerCollection) null); } protected boolean checkAlive() { // check if agent controllers are alive. // if we can not connect to an agent controller it has a running test potentially try { masterController.checkAlive(); return true; } catch (final AgentControllerException e) { final StringBuilder sb = new StringBuilder(); sb.append("WARNING: Unable to execute this command because at least one agent controller is unreachable:\n\n"); sb.append(buildFailureList(e.getFailed())); System.out.println(sb); } catch (final IllegalStateException e) { System.out.println("WARNING: Unable to execute this command: " + e.getMessage()); } return false; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy