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

com.liferay.jenkins.results.parser.PortalTestSuiteUpstreamControllerBuildRunner Maven / Gradle / Ivy

The newest version!
/**
 * SPDX-FileCopyrightText: (c) 2000 Liferay, Inc. https://liferay.com
 * SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
 */

package com.liferay.jenkins.results.parser;

import java.io.IOException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.JSONObject;

/**
 * @author Michael Hashimoto
 */
public class PortalTestSuiteUpstreamControllerBuildRunner
	
		extends BaseBuildRunner {

	@Override
	public Workspace getWorkspace() {
		if (_workspace != null) {
			return _workspace;
		}

		_workspace = WorkspaceFactory.newWorkspace();

		return _workspace;
	}

	@Override
	public void run() {
		invokeTestSuiteBuilds();
	}

	@Override
	public void tearDown() {
	}

	protected PortalTestSuiteUpstreamControllerBuildRunner(S buildData) {
		super(buildData);
	}

	protected String getInvocationCohortName() {
		String invocationCohortName = System.getenv("INVOCATION_COHORT_NAME");

		if ((invocationCohortName != null) && !invocationCohortName.isEmpty()) {
			return invocationCohortName;
		}

		BuildData buildData = getBuildData();

		return buildData.getCohortName();
	}

	protected String getJobURL() {
		String mostAvailableMasterURL =
			JenkinsResultsParserUtil.getMostAvailableMasterURL(
				JenkinsResultsParserUtil.combine(
					"http://" + getInvocationCohortName() + ".liferay.com"),
				null, 1, 24, 2);

		S buildData = getBuildData();

		return JenkinsResultsParserUtil.combine(
			mostAvailableMasterURL, "/job/test-portal-testsuite-upstream(",
			buildData.getPortalUpstreamBranchName(), ")");
	}

	protected String getTestPortalBuildProfile(String testSuite) {
		return _getTestSuiteBuildProperty(
			"portal.testsuite.upstream.test.portal.build.profile", testSuite);
	}

	protected String getTestraySlackChannels(String testSuite) {
		return _getTestSuiteBuildProperty(
			"portal.testsuite.upstream.testray.slack.channels", testSuite);
	}

	protected String getTestraySlackIconEmoji(String testSuite) {
		return _getTestSuiteBuildProperty(
			"portal.testsuite.upstream.testray.slack.icon.emoji", testSuite);
	}

	protected String getTestraySlackUsername(String testSuite) {
		return _getTestSuiteBuildProperty(
			"portal.testsuite.upstream.testray.slack.username", testSuite);
	}

	protected void invokeTestSuiteBuilds() {
		List testSuiteNames = _getSelectedTestSuiteNames();

		if (testSuiteNames.isEmpty()) {
			System.out.println("There are no test suites to run at this time.");

			keepJenkinsBuild(false);

			return;
		}

		String jenkinsAuthenticationToken = null;

		try {
			Properties buildProperties =
				JenkinsResultsParserUtil.getBuildProperties();

			jenkinsAuthenticationToken = buildProperties.getProperty(
				"jenkins.authentication.token");
		}
		catch (IOException ioException) {
			throw new RuntimeException(ioException);
		}

		S buildData = getBuildData();

		String portalBranchSHA = buildData.getPortalBranchSHA();

		for (String testSuiteName : testSuiteNames) {
			JSONObject previousBuildJSONObject =
				_getPreviousTestSuiteBuildJSONObject(testSuiteName);

			if ((previousBuildJSONObject != null) &&
				_previousBuildHasCurrentSHA(
					previousBuildJSONObject, portalBranchSHA)) {

				System.out.println(
					testSuiteName + " was invoked on this SHA already: " +
						portalBranchSHA);

				continue;
			}

			String jobURL = getJobURL();

			StringBuilder sb = new StringBuilder();

			sb.append(jobURL);
			sb.append("/buildWithParameters?");
			sb.append("token=");
			sb.append(jenkinsAuthenticationToken);

			Map invocationParameters = new HashMap<>();

			invocationParameters.put("CI_TEST_SUITE", testSuiteName);
			invocationParameters.put(
				"JENKINS_GITHUB_BRANCH_NAME",
				buildData.getJenkinsGitHubBranchName());
			invocationParameters.put(
				"JENKINS_GITHUB_BRANCH_USERNAME",
				buildData.getJenkinsGitHubUsername());
			invocationParameters.put("PORTAL_GIT_COMMIT", portalBranchSHA);
			invocationParameters.put(
				"PORTAL_GITHUB_URL", buildData.getPortalGitHubURL());
			invocationParameters.put(
				"TEST_PORTAL_BUILD_PROFILE",
				getTestPortalBuildProfile(testSuiteName));

			String testrayProjectName = _getTestrayProjectName(testSuiteName);

			if (testrayProjectName != null) {
				String testrayRoutineName = JenkinsResultsParserUtil.combine(
					"[", buildData.getPortalUpstreamBranchName(), "] ci:test:",
					testSuiteName);

				String testrayBuildName = JenkinsResultsParserUtil.combine(
					testrayRoutineName, " - ",
					String.valueOf(buildData.getBuildNumber()), " - ",
					JenkinsResultsParserUtil.toDateString(
						new Date(buildData.getStartTime()),
						"yyyy-MM-dd[HH:mm:ss]", "America/Los_Angeles"));

				if (_getTestrayRoutineName(testSuiteName) != null) {
					testrayRoutineName = _getTestrayRoutineName(testSuiteName);
				}

				invocationParameters.put(
					"TESTRAY_BUILD_NAME", testrayBuildName);
				invocationParameters.put(
					"TESTRAY_PROJECT_NAME", testrayProjectName);
				invocationParameters.put(
					"TESTRAY_ROUTINE_NAME", testrayRoutineName);
			}

			invocationParameters.put(
				"TESTRAY_SLACK_CHANNELS",
				getTestraySlackChannels(testSuiteName));
			invocationParameters.put(
				"TESTRAY_SLACK_ICON_EMOJI",
				getTestraySlackIconEmoji(testSuiteName));
			invocationParameters.put(
				"TESTRAY_SLACK_USERNAME",
				getTestraySlackUsername(testSuiteName));

			invocationParameters.putAll(buildData.getBuildParameters());

			for (Map.Entry invocationParameter :
					invocationParameters.entrySet()) {

				String invocationParameterValue =
					invocationParameter.getValue();

				if (JenkinsResultsParserUtil.isNullOrEmpty(
						invocationParameterValue)) {

					continue;
				}

				sb.append("&");
				sb.append(invocationParameter.getKey());
				sb.append("=");
				sb.append(invocationParameterValue);
			}

			try {
				JenkinsResultsParserUtil.toString(sb.toString());

				System.out.println(
					"Job for '" + testSuiteName + "' was invoked at " + jobURL);

				_invokedTestSuiteNames.add(testSuiteName);
			}
			catch (IOException ioException) {
				System.out.println(
					JenkinsResultsParserUtil.combine(
						"Unable to invoke a new build for test suite, '",
						testSuiteName, "'"));

				ioException.printStackTrace();
			}
		}

		boolean keepLogs = true;

		if (_invokedTestSuiteNames.isEmpty()) {
			keepLogs = false;
		}

		keepJenkinsBuild(keepLogs);

		StringBuilder sb = new StringBuilder();

		sb.append(JenkinsResultsParserUtil.join(", ", _invokedTestSuiteNames));
		sb.append(",");
		sb.append(" GIT ID - ");
		sb.append("");

		sb.append(_getPortalBranchAbbreviatedSHA());

		sb.append("");

		buildData.setBuildDescription(sb.toString());

		updateBuildDescription();
	}

	private List _getBuildHistory() {
		S buildData = getBuildData();

		Build build = BuildFactory.newBuild(buildData.getBuildURL(), null);

		Job job = JobFactory.newJob(buildData.getJobName());

		return job.getBuildHistory(build.getJenkinsMaster());
	}

	private List _getBuildTestSuiteNames(Build build) {
		String buildDescription = build.getBuildDescription();

		if ((buildDescription == null) || buildDescription.isEmpty()) {
			return Collections.emptyList();
		}

		return Arrays.asList(buildDescription.split("\\s*,\\s*"));
	}

	private Map _getCandidateTestSuiteStaleDurations() {
		Properties buildProperties;

		try {
			buildProperties = JenkinsResultsParserUtil.getBuildProperties();
		}
		catch (IOException ioException) {
			throw new RuntimeException(ioException);
		}

		S buildData = getBuildData();

		String upstreamBranchName = buildData.getPortalUpstreamBranchName();

		Map candidateTestSuiteStaleDurations =
			new LinkedHashMap<>();

		for (String testSuiteName : _getTestSuiteNames()) {
			String suiteStaleDuration = buildProperties.getProperty(
				JenkinsResultsParserUtil.combine(
					"portal.testsuite.upstream.stale.duration[",
					upstreamBranchName, "][", testSuiteName, "]"));

			if (suiteStaleDuration == null) {
				continue;
			}

			candidateTestSuiteStaleDurations.put(
				testSuiteName, Long.parseLong(suiteStaleDuration) * 60 * 1000);
		}

		return candidateTestSuiteStaleDurations;
	}

	private Map _getLatestTestSuiteStartTimes() {
		Map latestTestSuiteStartTimes = new LinkedHashMap<>();

		List builds = _getBuildHistory();

		BuildData buildData = getBuildData();

		Build currentBuild = BuildFactory.newBuild(
			buildData.getBuildURL(), null);

		builds.remove(currentBuild);

		for (String testSuiteName : _getTestSuiteNames()) {
			for (Build build : builds) {
				List buildTestSuiteNames = _getBuildTestSuiteNames(
					build);

				if (buildTestSuiteNames.contains(testSuiteName)) {
					latestTestSuiteStartTimes.put(
						testSuiteName, build.getStartTime());

					break;
				}
			}
		}

		return latestTestSuiteStartTimes;
	}

	private String _getPortalBranchAbbreviatedSHA() {
		S buildData = getBuildData();

		String portalBranchSHA = buildData.getPortalBranchSHA();

		return portalBranchSHA.substring(0, 7);
	}

	private JSONObject _getPreviousTestSuiteBuildJSONObject(
		String testSuiteName) {

		for (JSONObject previousBuildJSONObject :
				getPreviousBuildJSONObjects()) {

			String description = previousBuildJSONObject.optString(
				"description", "");

			if (description.contains("EXPIRE") ||
				description.contains("SKIPPED")) {

				continue;
			}

			if (description.contains(testSuiteName)) {
				return previousBuildJSONObject;
			}
		}

		return null;
	}

	private List _getSelectedTestSuiteNames() {
		if (_selectedTestSuiteNames != null) {
			return _selectedTestSuiteNames;
		}

		_selectedTestSuiteNames = new ArrayList<>();

		Map candidateTestSuiteStaleDurations =
			_getCandidateTestSuiteStaleDurations();

		Map latestTestSuiteStartTimes =
			_getLatestTestSuiteStartTimes();

		S buildData = getBuildData();

		Long startTime = buildData.getStartTime();

		for (Map.Entry entry :
				candidateTestSuiteStaleDurations.entrySet()) {

			String testSuiteName = entry.getKey();

			if (!latestTestSuiteStartTimes.containsKey(testSuiteName)) {
				_selectedTestSuiteNames.add(testSuiteName);

				continue;
			}

			Long testSuiteIdleDuration =
				startTime - latestTestSuiteStartTimes.get(testSuiteName);

			if (testSuiteIdleDuration > entry.getValue()) {
				_selectedTestSuiteNames.add(testSuiteName);
			}
		}

		return _selectedTestSuiteNames;
	}

	private String _getTestrayProjectName(String testSuite) {
		return _getTestSuiteBuildProperty(
			"portal.testsuite.upstream.testray.project.name", testSuite);
	}

	private String _getTestrayRoutineName(String testSuite) {
		String testrayRoutineName = _getTestSuiteBuildProperty(
			"portal.testsuite.upstream.testray.routine.name", testSuite);

		if (JenkinsResultsParserUtil.isNullOrEmpty(testrayRoutineName)) {
			testrayRoutineName = _getTestSuiteBuildProperty(
				"portal.testsuite.upstream.testray.build.type", testSuite);
		}

		return testrayRoutineName;
	}

	private String _getTestSuiteBuildProperty(
		String propertyName, String testSuite) {

		S buildData = getBuildData();

		try {
			return JenkinsResultsParserUtil.getProperty(
				JenkinsResultsParserUtil.getBuildProperties(), propertyName,
				buildData.getPortalUpstreamBranchName(), testSuite);
		}
		catch (IOException ioException) {
			return null;
		}
	}

	private List _getTestSuiteNames() {
		S buildData = getBuildData();

		try {
			return JenkinsResultsParserUtil.getBuildPropertyAsList(
				true,
				JenkinsResultsParserUtil.combine(
					"portal.testsuite.upstream.suites[",
					buildData.getPortalUpstreamBranchName(), "]"));
		}
		catch (IOException ioException) {
			throw new RuntimeException(ioException);
		}
	}

	private boolean _previousBuildHasCurrentSHA(
		JSONObject previousBuildJSONObject, String portalBranchSHA) {

		if (previousBuildJSONObject == null) {
			return false;
		}

		String description = previousBuildJSONObject.optString(
			"description", "");

		Matcher matcher = _portalBranchSHAPattern.matcher(description);

		if (!matcher.find()) {
			return false;
		}

		String previousPortalBranchSHA = matcher.group("branchSHA");

		if (portalBranchSHA.equals(previousPortalBranchSHA)) {
			return true;
		}

		return false;
	}

	private static final Pattern _portalBranchSHAPattern = Pattern.compile(
		"GIT ID - [0-9a-f]{40})\">[0-9a-f]{7}");

	private final List _invokedTestSuiteNames = new ArrayList<>();
	private List _selectedTestSuiteNames;
	private Workspace _workspace;

}