Skip to content

Commit ee595b7

Browse files
authored
Merge pull request #272 from eclipse-rcptt/merge/master/release/2.8
Merge release/2.8 into master
2 parents 7a1bb80 + 4f01f21 commit ee595b7

14 files changed

Lines changed: 457 additions & 104 deletions

File tree

ecl/plugins/org.eclipse.rcptt.ecl.platform.ui/src/org/eclipse/rcptt/ecl/platform/internal/ui/commands/ExecuteCommandService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public IStatus service(Command command, IProcess context) throws InterruptedExce
6060
}
6161
});
6262
try {
63-
return result.get(1, TimeUnit.SECONDS);
63+
// Garbage collection may take more than a second on Eclipse CI
64+
return result.get(2, TimeUnit.SECONDS);
6465
} catch (TimeoutException e) {
6566
return error("UI thread has timed out");
6667
} catch (java.util.concurrent.ExecutionException e) {

launching/org.eclipse.rcptt.launching.ext/src/org/eclipse/rcptt/internal/launching/ext/PDELocationUtils.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,12 @@
1313
*/
1414
package org.eclipse.rcptt.internal.launching.ext;
1515

16+
import static org.eclipse.core.runtime.Status.error;
1617
import static org.eclipse.rcptt.internal.launching.ext.Q7ExtLaunchingPlugin.PLUGIN_ID;
1718

1819
import java.io.File;
1920
import java.io.IOException;
2021
import java.nio.file.Files;
21-
import java.util.NoSuchElementException;
22-
import java.util.Optional;
2322

2423
import org.eclipse.core.filesystem.EFS;
2524
import org.eclipse.core.runtime.CoreException;
@@ -133,9 +132,9 @@ private static IPath getOSXProductE4Folder(final IPath path) throws CoreExceptio
133132
try {
134133
return Path.fromOSString(Files.list(root).filter(Files::isDirectory).filter(PDELocationUtils::endsWithApp).findFirst().map(appDir ->
135134
appDir. resolve("Contents").resolve("Eclipse")
136-
).filter(Files::isDirectory).orElseThrow(() -> new NoSuchElementException("Can't find Eclipse directory in " + path)).toString());
135+
).filter(Files::isDirectory).orElseThrow(() -> new CoreException(error("Can't find Eclipse directory in " + path))).toString());
137136
} catch (IOException e) {
138-
throw new CoreException(Status.error("Can't find Eclipse directory in " + path, e));
137+
throw new CoreException(error("Can't find Eclipse directory in " + path, e));
139138
}
140139
}
141140

launching/org.eclipse.rcptt.reporting.html/src/org/eclipse/rcptt/reporting/html/FullSingleTestHtmlRenderer.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2009, 2019 Xored Software Inc and others.
2+
* Copyright (c) 2009 Xored Software Inc and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v2.0
55
* which accompanies this distribution, and is available at
@@ -69,7 +69,7 @@
6969
public class FullSingleTestHtmlRenderer {
7070
private final PrintWriter writer;
7171
private final NumberFormat durationFormat;
72-
private final DateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm");
72+
private final DateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
7373
private final Function<Screenshot, String> imageStorage;
7474
private static final ILog LOG = Platform.getLog(FullSingleTestHtmlRenderer.class);
7575

@@ -123,8 +123,21 @@ public void render(Report report) {
123123
closeDetails();
124124
}
125125

126+
private void renderTime(long ms_since_epoch) {
127+
writer.print("<span class=\"time\" epoch=\"");
128+
writer.print(ms_since_epoch);
129+
writer.print("\">");
130+
writer.print(dateFormat.format(ms_since_epoch));
131+
writer.print("</span>");
132+
133+
}
126134
private void renderNode(Node node) {
135+
renderTime(node.getStartTime());
136+
writer.print(" - ");
137+
renderTime(node.getEndTime());
138+
writer.println("<p>");
127139
Q7Info info = ReportHelper.getInfo(node);
140+
128141
renderResult(info.getResult());
129142
EList<Node> children = node.getChildren();
130143
Q7WaitInfoRoot waitInfo = ReportHelper.getWaitInfo(node, false);
@@ -147,10 +160,11 @@ private void renderNode(Node node) {
147160
logs.append(logs2);
148161
}
149162
if (logs.length() > 2) {
150-
renderHeader(2, "Logs", "");
163+
openDetails(2, "Logs", "");
151164
writer.println("<pre>");
152165
writer.println(logs);
153166
writer.println("</pre>");
167+
closeDetails();
154168
}
155169
}
156170

@@ -247,7 +261,12 @@ private void renderEvent(EObject eObject) {
247261
public void renderAdvanced(AdvancedInformation info) {
248262
EList<InfoNode> nodes = info.getNodes();
249263
for (InfoNode infoNode : nodes) {
264+
if (infoNode.getChildren().isEmpty() && infoNode.getProperties().isEmpty()) {
265+
continue;
266+
}
267+
openDetails(5, infoNode.getName(), "");
250268
renderNode(infoNode);
269+
closeDetails();
251270
}
252271
// Append job information
253272
EList<JobEntry> jobs = info.getJobs();
@@ -266,8 +285,7 @@ public void renderAdvanced(AdvancedInformation info) {
266285
// Append thread information
267286
EList<StackTraceEntry> threads = info.getThreads();
268287
if (!threads.isEmpty()) {
269-
renderHeader(5, "Thread information", "");
270-
writer.println("<div class=\"childNode\">");
288+
openDetails(5, "Thread information", "");
271289
for (StackTraceEntry trace : threads) {
272290
if (trace.getThreadClass().equals(
273291
"org.eclipse.core.internal.jobs.Worker")
@@ -284,12 +302,11 @@ public void renderAdvanced(AdvancedInformation info) {
284302
.println("<br>");
285303
}
286304
}
287-
writer.println("</div>");
305+
closeDetails();
288306
}
289307
}
290308

291309
private void renderNode(InfoNode infoNode) {
292-
writer.println(infoNode.getName());
293310
EList<NodeProperty> list = infoNode.getProperties();
294311
EList<InfoNode> childs = infoNode.getChildren();
295312
if (!list.isEmpty() || !childs.isEmpty()) {
@@ -303,6 +320,7 @@ private void renderNode(InfoNode infoNode) {
303320
}
304321
if (childs.size() != 0) {
305322
for (InfoNode child : childs) {
323+
writer.println(child.getName());
306324
renderNode(child);
307325
}
308326
}

launching/org.eclipse.rcptt.reporting.util/src/org/eclipse/rcptt/reporting/util/IndexedExecutionReport.java

Lines changed: 157 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,63 +14,190 @@
1414
import static java.util.Objects.requireNonNull;
1515

1616
import java.io.Closeable;
17+
import java.io.EOFException;
18+
import java.io.FileNotFoundException;
1719
import java.io.IOException;
1820
import java.io.InputStream;
1921
import java.io.UncheckedIOException;
22+
import java.lang.ref.SoftReference;
23+
import java.nio.file.Files;
2024
import java.nio.file.Path;
2125
import java.util.HashMap;
2226
import java.util.Map;
27+
import java.util.Objects;
28+
import java.util.Optional;
29+
import java.util.concurrent.atomic.AtomicReference;
2330
import java.util.stream.Stream;
2431
import java.util.zip.ZipEntry;
2532
import java.util.zip.ZipException;
2633
import java.util.zip.ZipFile;
34+
import java.util.zip.ZipInputStream;
2735

2836
import org.eclipse.rcptt.sherlock.core.model.sherlock.report.Report;
2937
import org.eclipse.rcptt.sherlock.core.streams.SherlockReportFormat;
3038

31-
/** A collection of reports from one execution session grouped in a ZIP file **/
39+
/** A collection of reports from one execution session grouped in a ZIP file
40+
*
41+
* Lazy loading is implemented to prevent OOM on large collections.
42+
* Supports reading of incomplete archive when execution is still in progress or was aborted.
43+
*
44+
* **/
3245
public final class IndexedExecutionReport implements Closeable {
33-
private final ZipFile zipFile;
46+
private final Path zipPath;
3447
private final Map<String, Handle> idIndex = synchronizedMap(new HashMap<>());
3548
private final Map<String, Handle> entryIndex = synchronizedMap(new HashMap<>());
49+
private final AtomicReference<ZipFile> zipFile = new AtomicReference<>(null);
50+
3651
public final class Handle {
3752
private final ZipEntry zipEntry;
3853
private ReportEntry reportEntry;
54+
private SoftReference<Report> cachedReport = new SoftReference<Report>(null);
3955
private Handle(ZipEntry entry) {
4056
this.zipEntry = requireNonNull(entry);
4157
}
4258
public String getId() throws IOException {
4359
return getEntry().id;
4460
}
4561
public Report getReport() throws IOException {
46-
try (InputStream is = zipFile.getInputStream(zipEntry)) {
47-
Report report = SherlockReportFormat.loadReport(is, true, true);
48-
reportEntry = ReportEntry.create(report);
49-
idIndex.put(reportEntry.id, this);
50-
return report;
62+
var result = this.cachedReport.get();
63+
if (result != null) {
64+
return result;
65+
}
66+
try (var is = readEntry()) {
67+
result = populate(is);
68+
assert reportEntry != null;
69+
return result;
70+
}
71+
}
72+
private Report populate(InputStream is) throws IOException {
73+
Report report = cachedReport.get();
74+
if (report != null) {
75+
return report;
5176
}
77+
report = SherlockReportFormat.loadReport(is, false, true);
78+
this.cachedReport = new SoftReference<Report>(report);
79+
reportEntry = ReportEntry.create(report);
80+
idIndex.put(reportEntry.id, this);
81+
return report;
82+
}
83+
private InputStream readEntry() throws IOException {
84+
InputStream result;
85+
try {
86+
result = openZipFile().map(f -> {
87+
try {
88+
return f.getInputStream(zipEntry);
89+
} catch (IOException e) {
90+
throw new UncheckedIOException(e);
91+
}
92+
}).orElseGet(() -> {
93+
try {
94+
String name = zipEntry.getName();
95+
ZipInputStream zipInputStream = new ZipInputStream(Files.newInputStream(zipPath));
96+
try {
97+
for (var entry = zipInputStream.getNextEntry(); entry != null; entry = zipInputStream.getNextEntry()) {
98+
if (entry.getName().equals(name)) {
99+
return zipInputStream;
100+
}
101+
}
102+
zipInputStream.close();
103+
throw new FileNotFoundException(zipPath + ":" + name);
104+
} catch (Throwable e) {
105+
zipInputStream.close();
106+
throw e;
107+
}
108+
} catch (IOException e) {
109+
throw new UncheckedIOException(e);
110+
}
111+
});
112+
} catch (UncheckedIOException e) {
113+
throw e.getCause();
114+
}
115+
return result;
52116
}
117+
53118
public ReportEntry getEntry() throws IOException {
54119
if (reportEntry == null) {
55120
getReport();
56121
}
57122
assert reportEntry != null;
58123
return reportEntry;
59124
}
125+
126+
@Override
127+
public String toString() {
128+
return "" + zipEntry + (reportEntry == null ? "" : ", " + reportEntry.id);
129+
}
60130
}
61131
public IndexedExecutionReport(Path path) throws ZipException, IOException {
62-
zipFile = new ZipFile(path.toFile());
132+
zipPath = path;
63133
}
134+
64135
public Stream<Handle> read() {
65-
if (zipFile.size() == entryIndex.size()) {
66-
return entryIndex.values().stream();
136+
try {
137+
Stream<Handle> resultStream = openZipFile().map(this::streamZipFile).orElseGet(this::streamZipInputStream);
138+
return resultStream;
139+
} catch (IOException e) {
140+
throw new UncheckedIOException(e);
141+
}
142+
}
143+
144+
private Stream<Handle> streamZipFile(ZipFile file) {
145+
return file.stream().map(entry -> {
146+
var result = entryIndex.computeIfAbsent(entry.getName(), (ignored) -> new Handle(entry));
147+
if (result.reportEntry == null) {
148+
try (var is = file.getInputStream(entry)) {
149+
result.populate(is);
150+
} catch (IOException e) {
151+
throw new UncheckedIOException(e);
152+
}
153+
}
154+
return result;
155+
});
156+
}
157+
private Stream<Handle> streamZipInputStream() {
158+
ZipInputStream zipInputStream;
159+
try {
160+
zipInputStream = new ZipInputStream(Files.newInputStream(zipPath));
161+
} catch (IOException e) {
162+
throw new UncheckedIOException(e);
67163
}
68-
return zipFile.stream().map(e -> entryIndex.computeIfAbsent(e.getName(), (ignored) -> new Handle(e)) );
164+
Stream<Handle> resultStream = Stream.generate(() -> {
165+
try {
166+
synchronized(zipInputStream) {
167+
ZipEntry entry = zipInputStream.getNextEntry();
168+
if (entry == null) {
169+
return null;
170+
}
171+
var result = entryIndex.computeIfAbsent(entry.getName(), (ignored) -> new Handle(entry));
172+
if (result.reportEntry == null) {
173+
result.populate(zipInputStream);
174+
}
175+
return result;
176+
}
177+
} catch (EOFException e) {
178+
// Support unfinished reports from still active or aborted executions
179+
return null;
180+
} catch (IOException e) {
181+
throw new UncheckedIOException(e);
182+
}
183+
});
184+
resultStream.onClose(() -> {
185+
try {
186+
zipInputStream.close();
187+
} catch (IOException e) {
188+
throw new UncheckedIOException(e);
189+
}
190+
});
191+
resultStream = resultStream.takeWhile(Objects::nonNull);
192+
return resultStream;
69193
}
70194
@Override
71195
public void close() throws IOException {
72196
idIndex.clear();
73-
zipFile.close();
197+
entryIndex.clear();
198+
try (var close = zipFile.get()) {
199+
// closes if not null
200+
}
74201
}
75202
public Handle getById(String id) {
76203
Handle result = idIndex.get(id);
@@ -87,4 +214,22 @@ public Handle getById(String id) {
87214
}).findAny().get();
88215
}
89216
}
217+
218+
@SuppressWarnings("resource")
219+
private Optional<ZipFile> openZipFile() throws IOException {
220+
try {
221+
ZipFile f = zipFile.get();
222+
if (f == null) {
223+
f = new ZipFile(zipPath.toFile());
224+
if (!zipFile.compareAndSet(null, f)) {
225+
f.close();
226+
f = zipFile.get();
227+
assert f != null;
228+
}
229+
}
230+
return Optional.of(f);
231+
} catch (ZipException e) {
232+
return Optional.empty();
233+
}
234+
}
90235
}

launching/tests/org.eclipse.rcptt.reporting.html.tests/src/org/eclipse/rcptt/reporting/html/tests/HtmlReporterTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2009, 2019 Xored Software Inc and others.
2+
* Copyright (c) 2009 Xored Software Inc and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v2.0
55
* which accompanies this distribution, and is available at
@@ -55,15 +55,15 @@ public void testNoRoot() {
5555
}
5656

5757

58-
private Report createReport(String name, int severity) {
58+
public static Report createReport(String name, int severity) {
5959
Report report = ReportFactory.eINSTANCE.createReport();
6060
Node node = createNode(name, severity);
6161
ReportHelper.getInfo(node).setId(name);
6262
report.setRoot(node);
6363
return report;
6464
}
6565

66-
private Node createNode(String name, int severity) {
66+
private static Node createNode(String name, int severity) {
6767
Node node = ReportFactory.eINSTANCE.createNode();
6868
node.setName(name);
6969
ReportHelper.getInfo(node).setResult(RcpttPlugin.createProcessStatus(severity, "No message"));

0 commit comments

Comments
 (0)