/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.client.alm.mylyn.core.bp; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jubula.client.alm.mylyn.core.Activator; import org.eclipse.jubula.client.alm.mylyn.core.i18n.Messages; import org.eclipse.jubula.client.alm.mylyn.core.model.ALMChange; import org.eclipse.jubula.client.alm.mylyn.core.model.CommentEntry; import org.eclipse.jubula.client.alm.mylyn.core.model.FieldUpdate; import org.eclipse.jubula.client.alm.mylyn.core.utils.ALMAccess; import org.eclipse.jubula.client.core.businessprocess.TestResultBP; import org.eclipse.jubula.client.core.businessprocess.TestresultSummaryBP; import org.eclipse.jubula.client.core.events.DataEventDispatcher; import org.eclipse.jubula.client.core.events.DataEventDispatcher.DataState; import org.eclipse.jubula.client.core.events.DataEventDispatcher.ITestresultSummaryEventListener; import org.eclipse.jubula.client.core.model.IALMReportingProperties; import org.eclipse.jubula.client.core.model.IALMReportingRulePO; import org.eclipse.jubula.client.core.model.IProjectPO; import org.eclipse.jubula.client.core.model.ITestResultSummaryPO; import org.eclipse.jubula.client.core.model.ITestResultSummaryPO.AlmReportStatus; import org.eclipse.jubula.client.core.model.TestResult; import org.eclipse.jubula.client.core.model.TestResultNode; import org.eclipse.jubula.client.core.persistence.GeneralStorage; import org.eclipse.jubula.client.core.progress.IProgressConsole; import org.eclipse.jubula.client.core.utils.ITreeNodeOperation; import org.eclipse.jubula.client.core.utils.ITreeTraverserContext; import org.eclipse.jubula.client.core.utils.ReportRuleType; import org.eclipse.jubula.client.core.utils.TestResultNodeTraverser; import org.eclipse.jubula.mylyn.utils.MylynAccess; import org.eclipse.osgi.util.NLS; /** * @author BREDEX GmbH */ public class CommentReporter implements ITestresultSummaryEventListener { /** instance of this class */ private static CommentReporter instance; /** the progress console to use */ private IProgressConsole m_console; /** the report properties to use */ private IALMReportingProperties m_reportProps = null; /** * @author BREDEX GmbH */ private static class ALMChangeCreationOperation implements ITreeNodeOperation<TestResultNode> { /** the taskIdToComment mapping */ private Map<String, List<ALMChange>> m_taskIdToALMChange; /** report failure */ private final boolean m_reportFailure; /** report success */ private final boolean m_reportSuccess; /** reporting rules */ private final List<IALMReportingRulePO> m_reportingRules; /** dashboard URL */ private String m_dashboardURL; /** the summary id */ private ITestResultSummaryPO m_summary; /** test result node counter */ private long m_nodeCount = 0; /** * Constructor * * @param taskIdToALMChange * the mapping to fill with entries * @param reportSuccess * reportSuccess * @param reportFailure * reportFailure * @param reportingRules * reportingRules * @param dashboardURL * dashboardURL * @param summary */ public ALMChangeCreationOperation( Map<String, List<ALMChange>> taskIdToALMChange, boolean reportFailure, boolean reportSuccess, List<IALMReportingRulePO> reportingRules, String dashboardURL, ITestResultSummaryPO summary) { m_taskIdToALMChange = taskIdToALMChange; m_reportFailure = reportFailure; m_reportSuccess = reportSuccess; m_reportingRules = reportingRules; m_dashboardURL = dashboardURL; m_summary = summary; } /** {@inheritDoc} */ public boolean operate(ITreeTraverserContext<TestResultNode> ctx, TestResultNode parent, TestResultNode resultNode, boolean alreadyVisited) { m_nodeCount++; boolean didNodePass = CommentEntry .hasPassed(resultNode.getStatus()); String taskIdforNode = resultNode.getTaskId(); boolean hasTaskId = taskIdforNode != null; boolean writeCommentForNode = hasTaskId && ((m_reportSuccess && didNodePass) || (m_reportFailure && !didNodePass)); boolean writeFieldUpdateForNode = hasTaskId && !getApplicableRules(didNodePass).isEmpty(); if (writeCommentForNode) { CommentEntry c = new CommentEntry(resultNode, m_dashboardURL, m_summary, m_nodeCount); addALMChangeToNode(taskIdforNode, c); } if (writeFieldUpdateForNode) { FieldUpdate f = new FieldUpdate(resultNode, m_dashboardURL, m_summary, m_nodeCount, getApplicableRules(didNodePass)); addALMChangeToNode(taskIdforNode, f); } return true; } /** * Processes an ALM change * @param taskIdforNode task ID for the node * @param change the change */ private void addALMChangeToNode(String taskIdforNode, ALMChange change) { List<ALMChange> changes = m_taskIdToALMChange .get(taskIdforNode); if (changes != null) { changes.add(change); } else { List<ALMChange> cs = new LinkedList<ALMChange>(); cs.add(change); m_taskIdToALMChange.put(taskIdforNode, cs); } } /** * Returns the applicable reporting rules * @param didNodePass whether node passed * @return the applicable reporting rules */ private List<IALMReportingRulePO> getApplicableRules( boolean didNodePass) { List<IALMReportingRulePO> applicableRules = new ArrayList<IALMReportingRulePO>(); ReportRuleType type = didNodePass ? ReportRuleType.ONSUCCESS : ReportRuleType.ONFAILURE; for (IALMReportingRulePO rule : m_reportingRules) { if (rule.getType() == type) { applicableRules.add(rule); } } return applicableRules; } /** {@inheritDoc} */ public void postOperate(ITreeTraverserContext<TestResultNode> ctx, TestResultNode parent, TestResultNode node, boolean alreadyVisited) { // currently unused } } /** Constructor */ private CommentReporter() { DataEventDispatcher.getInstance() .addTestresultSummaryEventListener(this); } /** * @return Returns the instance. */ public static CommentReporter getInstance() { if (instance == null) { instance = new CommentReporter(); } return instance; } /** * process the result tree * * @param reportFailure * reportFailure * @param reportSuccess * reportSuccess * @param monitor * monitor * @param reportingRules * reportingRules * @param summary * the summary the result tree belongs to * @param rootResultNode * the result node to report for * @return status */ private IStatus processResultTree(IProgressMonitor monitor, boolean reportSuccess, boolean reportFailure, List<IALMReportingRulePO> reportingRules, ITestResultSummaryPO summary, TestResultNode rootResultNode) { Map<String, List<ALMChange>> taskIdToALMChange = new HashMap<String, List<ALMChange>>(); ITreeNodeOperation<TestResultNode> operation = new ALMChangeCreationOperation( taskIdToALMChange, reportFailure, reportSuccess, reportingRules, m_reportProps.getDashboardURL(), summary); TestResultNodeTraverser traverser = new TestResultNodeTraverser( rootResultNode, operation); traverser.traverse(); final IStatus reportStatus = reportToALM(monitor, taskIdToALMChange); if (reportStatus.isOK()) { TestresultSummaryBP.getInstance().setALMReportStatus(summary, AlmReportStatus.REPORTED); } return reportStatus; } /** * @param monitor * the monitor to use * @param taskIdToALMChange * the comment mapping * @return status */ private IStatus reportToALM(IProgressMonitor monitor, Map<String, List<ALMChange>> taskIdToALMChange) { String repoLabel = m_reportProps.getALMRepositoryName(); boolean failed = false; Set<String> taskIds = taskIdToALMChange.keySet(); int taskAmount = taskIds.size(); IProgressConsole c = getConsole(); int successCount = 0; if (taskAmount > 0) { String out = NLS.bind(Messages.ReportToALMJob, taskAmount, repoLabel); monitor.beginTask(out, taskAmount); c.writeLine(out); int overallCommentCount = 0; int overallFieldUpdateCount = 0; for (String taskId : taskIds) { List<ALMChange> changes = taskIdToALMChange.get(taskId); List<CommentEntry> comments = new LinkedList<CommentEntry>(); List<FieldUpdate> fieldUpdates = new LinkedList<FieldUpdate>(); split(changes, comments, fieldUpdates); boolean commentingSucceeded = true; IStatus fieldUpdateStatus = Status.OK_STATUS; int commentAmount = comments.size(); if (commentAmount > 0) { writeStatus(c, taskId, commentAmount, Messages. ReportingComment, Messages.ReportingComments); commentingSucceeded = ALMAccess.createComment( repoLabel, taskId, comments, monitor); if (!commentingSucceeded) { failed = true; c.writeErrorLine( NLS.bind(Messages.ReportingTaskFailed, taskId)); } else { overallCommentCount += commentAmount; } } int fieldUpdateAmount = fieldUpdates.size(); if (fieldUpdateAmount > 0) { writeStatus(c, taskId, fieldUpdateAmount, Messages.ReportingFieldUpdate, Messages.ReportingFieldUpdates); fieldUpdateStatus = ALMAccess.updateFields( repoLabel, taskId, fieldUpdates, monitor); if (!fieldUpdateStatus.isOK()) { failed = true; writeErrorStatus(c, taskId, fieldUpdateStatus); if (fieldUpdateStatus.getSeverity() == IStatus.CANCEL) { break; } } else { overallFieldUpdateCount += fieldUpdateAmount; } } if (fieldUpdateStatus.isOK() && commentingSucceeded) { successCount++; } monitor.worked(1); } c.writeLine(NLS.bind(Messages.ReportToALMJobDone, new Integer[] { overallCommentCount, overallFieldUpdateCount, successCount, taskAmount })); monitor.done(); } else { c.writeLine(Messages.NothingToReport); } if (!failed || successCount > 0) { return Status.OK_STATUS; } return new Status(IStatus.ERROR, Activator.ID, "Reporting comments performed with errors..."); //$NON-NLS-1$ } /** * write error lines to the console for the status and its children * @param c the {@link IProgressConsole} * @param taskId the id of the task * @param fieldUpdateStatus the update status of a field after the executin */ private void writeErrorStatus(IProgressConsole c, String taskId, IStatus fieldUpdateStatus) { c.writeErrorLine(fieldUpdateStatus.getMessage()); IStatus[] children = fieldUpdateStatus.getChildren(); if (children != null && children.length > 0) { for (int i = 0; i < children.length; i++) { c.writeErrorLine(children[i].getMessage()); } } c.writeErrorLine( NLS.bind(Messages.ReportingTaskFailed, taskId)); } /** * writes the status of the commenting to a task to the console * @param c console * @param taskId task * @param changeAmount amount of changes * @param one output if only one change * @param mult output if multiple changes */ private void writeStatus(IProgressConsole c, String taskId, int changeAmount, String one, String mult) { if (changeAmount > 1) { c.writeWarningLine(NLS.bind(mult, changeAmount, taskId)); } else { c.writeLine(NLS.bind(one, taskId)); } } /** * Splits a list of ALM changes into comments and field updates * @param changes the changes * @param comments the comments * @param fieldUpdates the field updates */ private void split(List<ALMChange> changes, List<CommentEntry> comments, List<FieldUpdate> fieldUpdates) { for (ALMChange change : changes) { if (change instanceof CommentEntry) { comments.add((CommentEntry)change); } else if (change instanceof FieldUpdate) { fieldUpdates.add((FieldUpdate)change); } } } /** * @return the console */ public IProgressConsole getConsole() { return m_console; } /** * @param console the console to set */ public void setConsole(IProgressConsole console) { m_console = console; } /** {@inheritDoc} */ public void handleTestresultSummaryChanged( final ITestResultSummaryPO summary, DataState state) { if (state != DataState.Added) { return; } IProjectPO project = GeneralStorage.getInstance().getProject(); TestResult resultTestModel = TestResultBP.getInstance() .getResultTestModel(); final TestResultNode rootResultNode = resultTestModel .getRootResultNode(); Job job = gatherInformationAndCreateReportToALMJob(summary, project.getProjectProperties(), rootResultNode); if (job != null) { job.schedule(); } } /** * @param summary the summary * @param properties the properties * @param rootResultNode the root result node * @return the job for reporting */ public Job gatherInformationAndCreateReportToALMJob( final ITestResultSummaryPO summary, IALMReportingProperties properties, final TestResultNode rootResultNode) { m_reportProps = properties; final boolean reportSuccess = properties.getIsReportOnSuccess(); final boolean reportFailure = properties.getIsReportOnFailure(); final List<IALMReportingRulePO> reportingRules = properties.getALMReportingRules(); final String almRepositoryName = properties.getALMRepositoryName(); if (!StringUtils.isBlank(almRepositoryName) && (reportSuccess || reportFailure || !reportingRules.isEmpty()) && summary.isTestsuiteRelevant()) { Job reportToALMOperation = new Job(NLS.bind( Messages.ReportToALMJobName, almRepositoryName)) { protected IStatus run(IProgressMonitor monitor) { getConsole().writeLine( NLS.bind(Messages.TaskRepositoryConnectionTest, almRepositoryName)); IStatus connectionStatus = MylynAccess .testConnection(almRepositoryName); if (connectionStatus.isOK()) { getConsole().writeLine( NLS.bind( Messages.TaskRepositoryConnectionTestSucceeded, almRepositoryName)); return processResultTree(monitor, reportSuccess, reportFailure, reportingRules, summary, rootResultNode); } getConsole().writeErrorLine( NLS.bind(Messages.TaskRepositoryConnectionTestFailed, connectionStatus.getMessage())); return connectionStatus; } }; return reportToALMOperation; } return null; } }