blob: ffb6e90132914377aa7da30e049b40dcba816537 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2016 Xored Software Inc and others.
* 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:
* Xored Software Inc - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.rcptt.reporting.util;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.rcptt.ecl.core.EclException;
import org.eclipse.rcptt.ecl.core.ProcessStatus;
import org.eclipse.rcptt.ecl.internal.core.ProcessStatusConverter;
import org.eclipse.rcptt.internal.core.RcpttPlugin;
import org.eclipse.rcptt.reporting.ItemKind;
import org.eclipse.rcptt.reporting.Q7Info;
import org.eclipse.rcptt.reporting.core.ImageEntry;
import org.eclipse.rcptt.reporting.core.ReportHelper;
import org.eclipse.rcptt.reporting.core.SimpleSeverity;
import org.eclipse.rcptt.reporting.core.TimeFormatHelper;
import org.eclipse.rcptt.sherlock.core.model.sherlock.EclipseStatus;
import org.eclipse.rcptt.sherlock.core.model.sherlock.JavaException;
import org.eclipse.rcptt.sherlock.core.model.sherlock.JavaStackTraceEntry;
import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Event;
import org.eclipse.rcptt.sherlock.core.model.sherlock.report.LoggingCategory;
import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Node;
import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Report;
import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Screenshot;
import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Snaphot;
import org.eclipse.rcptt.sherlock.core.reporting.ReportBuilder;
import org.eclipse.rcptt.sherlock.core.reporting.SimpleReportGenerator;
import org.eclipse.rcptt.tesla.core.TeslaFeatures;
import org.eclipse.rcptt.tesla.core.info.AdvancedInformation;
import org.eclipse.rcptt.tesla.core.info.Q7WaitInfo;
import org.eclipse.rcptt.tesla.core.info.Q7WaitInfoRoot;
import org.eclipse.rcptt.tesla.core.utils.AdvancedInformationGenerator;
import org.eclipse.rcptt.util.StringUtils;
public class RcpttReportGenerator {
private final SimpleReportGenerator simpleReportGenerator = new SimpleReportGenerator();
private final List<ImageEntry> images;
private final PrintWriter writer;
private long startTime = 0;
private final Map<String, Long> totalWaitTime = new HashMap<String, Long>();
private int maxMethodNameLength = 0;
private int maxTotalTimeLength = 0;
public RcpttReportGenerator(PrintWriter writer, List<ImageEntry> images) {
this.writer = writer;
this.images = images;
}
protected PrintWriter writeTabs(int tabs) {
return writeTabs(writer, tabs);
}
public void writeReport(Report report, int tabs) {
startTime = report.getRoot().getStartTime();
printNode(report.getRoot(), tabs);
printTotalWaitTime();
}
protected static <T extends Appendable> T writeTabs(T writer, int tabs) {
for (int i = 0; i < tabs; ++i) {
try {
writer.append(" ");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return writer;
}
// @Override
// public StringBuilder toString(StringBuilder builder,
// int tabs, org.eclipse.emf.ecore.EObject obj,
// String... ignores) {
// if (obj instanceof AdvancedInformation) {
// String content = new AdvancedInformationGenerator()
// .generateContent((AdvancedInformation) obj);
// builder.append(content);
// return builder;
// }
// return super.toString(builder, tabs, obj, ignores);
// };
//
public void printNode(Node infoNode, int tabs) {
writeQ7Info(tabs, infoNode);
writeQ7WaitInfo(tabs, infoNode);
writeLogsFromNode(tabs, infoNode);
try {
for (Event child : infoNode.getEvents()) {
writeEvent(child, tabs + 1);
}
for (Snaphot child : infoNode.getSnapshots()) {
writeSnapshot(child, tabs + 1);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
printChildren(tabs, infoNode);
}
private void writeEvent(Event event, int tabs) throws IOException {
if (event.getCount() == 1) {
writeTabs(tabs + 1)
.append("Event at ")
.println(TimeFormatHelper.format(event.getTime() - startTime));
} else {
writeTabs(tabs + 1).append("Event: ")
.append(String.valueOf(event.getCount()))
.append(" times, first at ")
.append(TimeFormatHelper.format(event.getTime() - startTime))
.println();
}
printObject(event.getData(), tabs + 2);
}
public void printObject(EObject object, int tabs) throws IOException {
if (object instanceof EclipseStatus) {
printStatus((EclipseStatus) object, tabs);
} else if (object instanceof Snaphot) {
writeSnapshot((Snaphot) object, tabs);
} else {
simpleReportGenerator.toString(writer, tabs, object);
}
}
private void writeSnapshot(Snaphot snapshot, int tabs) throws IOException {
if (snapshot.getData() instanceof Screenshot) {
Screenshot shot = (Screenshot) snapshot
.getData();
String description = shot.getMessage()
+ ": " //$NON-NLS-1$
+ TimeFormatHelper.format(snapshot
.getTime()
- startTime);
images.add(new ImageEntry(shot.getData(), description));
return;
} else if (snapshot.getData() instanceof AdvancedInformation) {
printAdvanced((AdvancedInformation) snapshot.getData(), tabs);
return;
}
writeTabs(tabs).println("Snapshot " + TimeFormatHelper.format(snapshot.getTime() - startTime));
printObject(snapshot.getData(), tabs + 1);
}
private void printAdvanced(AdvancedInformation data, int tabs) {
new AdvancedInformationGenerator(writer).writeAdvanced(data, tabs);
}
private PrintWriter w(int tabs) {
return writeTabs(tabs);
}
private void printStatus(EclipseStatus status, int tabs) throws IOException {
SimpleSeverity severity = SimpleSeverity.create(status.getSeverity());
w(tabs).append(severity.name());
writer.append(" in plugin: ").println(status.getPlugin());
w(tabs).append("message: ").println(status.getMessage());
if (status.getException() != null) {
w(tabs).println("exception: ");
printJavaException(status.getException(), tabs + 2);
}
for (EclipseStatus child : status.getChildren()) {
printStatus(child, tabs + 1);
}
}
private void printJavaException(JavaException e, int tabs) {
w(tabs).append(e.getClassName());
if (!StringUtils.isEmpty(e.getMessage())) {
writer.print(":" + e.getMessage());
}
writer.println();
for (JavaStackTraceEntry st : e.getStackTrace()) {
w(tabs + 2).append("at ")
.append(st.getClassName()).append(".")
.append(st.getMethodName()).append("(")
.append(st.getFileName()).append(":")
.append("" + st.getLineNumber()).append(")")
.println();
}
JavaException cause = e.getCause();
if (cause != null) {
w(tabs + 2).println("Caused by:");
printJavaException(cause, tabs + 1);
}
}
protected void printChildren(int tabs, Node infoNode) {
for (Node child : infoNode.getChildren()) {
printNode(child, tabs + 4);
}
}
private void writeQ7WaitInfo(int tabs, Node infoNode) {
Q7WaitInfoRoot waitInfo = ReportHelper.getWaitInfo(infoNode, false);
if (waitInfo != null) {
writeQ7WaitInfo(tabs, waitInfo); //$NON-NLS-1$
}
}
public static String getType(Q7WaitInfoRoot info, Q7WaitInfo q7WaitInfo) {
String type = info.getTypesNames().get(q7WaitInfo.getTypeId());
if (!TeslaFeatures.isIncludeIgnoredWaitDetails() && type.contains("(ignored)")) {
return null;
}
return type;
}
public void writeQ7WaitInfo(int tabs, Q7WaitInfoRoot info) {
List<Q7WaitInfo> infos = new ArrayList<Q7WaitInfo>(info.getInfos());
if (infos.size() == 0) {
return;
}
Comparator<Q7WaitInfo> comparator = new Comparator<Q7WaitInfo>() {
@Override
public int compare(Q7WaitInfo info1, Q7WaitInfo info2) {
return Long.compare(info1.getDuration(), info2.getDuration());
}
};
Collections.sort(infos, Collections.reverseOrder(comparator));
String classNameColumn = "Method name"; //$NON-NLS-1$
String totalTimeColumn = "Time"; //$NON-NLS-1$
int classNameLength = classNameColumn.length();
int totalTimeLength = totalTimeColumn.length();
boolean isEmpty = true;
for (Q7WaitInfo q7WaitInfo : infos) {
long totalTime = q7WaitInfo.getDuration();
String type = getType(info, q7WaitInfo);
String className = SimpleReportGenerator.getClassName(info, q7WaitInfo);
if (type == null) {
continue;
}
if (!TeslaFeatures.isIncludeEclipseMethodsWaitDetails()
&& className.startsWith("org.eclipse")) { //$NON-NLS-1$
continue;
}
if (totalTime == 0) {
continue;
}
// calculate column length
String methodName = String.format("%s: %s", type, className);
if (methodName.length() > classNameLength) {
classNameLength = methodName.length();
}
if (String.valueOf(totalTime).length() > totalTimeLength) {
totalTimeLength = String.valueOf(totalTime).length();
}
isEmpty = false;
}
if (isEmpty) {
return;
}
writeTabs(tabs + 4).println("--> Wait details <--");
writeTabs(tabs + 8)
.append(String.format("%" + -classNameLength + "s", classNameColumn))
.append(" ")
.append(String.format("%" + -totalTimeLength + "s", totalTimeColumn))
.println();
for (Q7WaitInfo i : infos) {
long totalTime = i.getDuration();
String type = getType(info, i);
String className = SimpleReportGenerator.getClassName(info, i);
if (type == null) {
continue;
}
if (!TeslaFeatures.isIncludeEclipseMethodsWaitDetails()
&& className.startsWith("org.eclipse")) { //$NON-NLS-1$
continue;
}
if (totalTime == 0) {
continue;
}
String methodName = String.format("%s: %s", type, className);
writeTabs(tabs + 8)
.append(String.format("%" + -classNameLength + "s", methodName))
.append(" ")
.append(String.format("%" + totalTimeLength + "s", totalTime))
.println();
addWaitTime(type, className, totalTime);
}
}
String kindToString(ItemKind kind, String name) {
switch (kind) {
case CONTEXT:
return "Context *" + name + "*";
case ECL_COMMAND:
return name;
case SCRIPT:
return "Script *" + name + "*";
case TESTCASE:
return "Test case *" + name + "*";
case TEST_SUITE:
return "Test suite *" + name + "*";
case VERIFICATION:
return "Verification *" + name + "*";
}
return name;
}
private void writeQ7Info(int tabs, Node infoNode) {
Q7Info q7Info = ReportHelper.getInfo(infoNode);
writeTabs(tabs);
if (q7Info != null) {
writer.append(kindToString(q7Info.getType(), infoNode.getName()));
writer.
append(" ")
.append("time: " +
TimeFormatHelper.format(infoNode.getDuration()))
.println();
writeResult(tabs + 1, q7Info.getResult());
}
}
public void writeResult(int tabs, ProcessStatus result) {
if (result == null)
result = RcpttPlugin.createProcessStatus(IStatus.ERROR, "Null result");
if (SimpleSeverity.create(result) == SimpleSeverity.OK)
return;
w(tabs).append("Result: " + SimpleSeverity.create(result).name() + ", message: ")
.println(result.getMessage());
writeException(writer, tabs + 1, result.getException());
for (ProcessStatus child : result.getChildren()) {
writeResult(tabs + 1, child);
}
}
private static void writeException(Writer writer, final int tabs, EclException exception) {
if (exception == null)
return;
IndentedWriter iwriter = new IndentedWriter(writer) {
@Override
public void writeIndent() {
writeTabs(this, tabs);
}
};
ProcessStatusConverter.getThrowable(exception).printStackTrace(iwriter);
iwriter.println();
iwriter.flush();
}
public void writeLogsFromNode(int tabs, Node infoNode) {
boolean haveEntries = false;
for (LoggingCategory logCategory : LoggingCategory.VALUES) {
String log = ReportBuilder.getLogs(infoNode, logCategory);
if (!StringUtils.isEmpty(log)) {
if (!haveEntries) {
haveEntries = true;
writeTabs(tabs)
.println("--------------Logs BEGIN-------------------"); //$NON-NLS-1$
}
for (String logLine : log.split("[\r\n]+")) {
writeTabs(tabs)
.append(logLine)
.println();
}
}
}
if (haveEntries) {
writeTabs(tabs)
.append("--------------Logs END-------------------") //$NON-NLS-1$
.println();
}
}
private void addWaitTime(String type, String className, long totalTime) {
if (!(type.equals("job") || type.equals("sync") || type.equals("async"))) { //$NON-NLS-1$
return;
}
if (!TeslaFeatures.isIncludeEclipseMethodsWaitDetails()
&& className.startsWith("org.eclipse")) { //$NON-NLS-1$
return;
}
String methodName = String.format("%s: %s", type, className);
if (totalWaitTime.containsKey(methodName)) {
totalTime += totalWaitTime.get(methodName);
}
totalWaitTime.put(methodName, totalTime);
int methodNameLength = methodName.length();
if (methodNameLength > maxMethodNameLength) {
maxMethodNameLength = methodNameLength;
}
int totalTimeLength = String.valueOf(totalTime).length();
if (totalTimeLength > maxTotalTimeLength) {
maxTotalTimeLength = totalTimeLength;
}
}
private void printTotalWaitTime() {
String totalWaitTimeTable = "Total wait time:"; //$NON-NLS-1$
String methodNameColumn = "Method name"; //$NON-NLS-1$
String totalTimeColumn = "Time"; //$NON-NLS-1$
String noWaitInfoMessage = TeslaFeatures.isIncludeEclipseMethodsWaitDetails()
? "There were no methods RCPTT was waiting for"
: "There were no third-party methods RCPTT was waiting for";
int methodNameLength = methodNameColumn.length();
if (methodNameLength > maxMethodNameLength) {
maxMethodNameLength = methodNameLength;
}
int totalTimeLength = totalTimeColumn.length();
if (totalTimeLength > maxTotalTimeLength) {
maxTotalTimeLength = totalTimeLength;
}
writer.println();
writer.append(totalWaitTimeTable).println();
if (totalWaitTime.isEmpty()) {
writer.append(" ")
.append(noWaitInfoMessage) // $NON-NLS-1$
.println();
return;
}
writer.append(" ")
.append(String.format("%" + -maxMethodNameLength + "s", methodNameColumn))
.append(" ")
.append(String.format("%" + -maxTotalTimeLength + "s", totalTimeColumn))
.println();
List<Map.Entry<String, Long>> sortedTable = getSortedTimeTable(totalWaitTime);
for (Map.Entry<String, Long> entry : sortedTable) {
writer.append(" ")
.append(String.format("%" + -maxMethodNameLength + "s", entry.getKey()))
.append(" ")
.append(String.format("%" + maxTotalTimeLength + "s", entry.getValue()))
.println();
}
}
private List<Map.Entry<String, Long>> getSortedTimeTable(Map<String, Long> unsortedMap) {
Comparator<Map.Entry<String, Long>> comparator = new Comparator<Map.Entry<String, Long>>() {
@Override
public int compare(Map.Entry<String, Long> entry1, Map.Entry<String, Long> entry2) {
return entry1.getValue().compareTo(entry2.getValue());
}
};
List<Map.Entry<String, Long>> sortedTable = new LinkedList<>(unsortedMap.entrySet());
Collections.sort(sortedTable, Collections.reverseOrder(comparator));
return sortedTable;
}
}