Finalize metrics for Hovers and CodeLenses. Add tests
This commit is contained in:
@@ -556,6 +556,26 @@ public class Editor {
|
||||
assertContains(snippet, hoverString(hover));
|
||||
}
|
||||
|
||||
public void assertLiveCodeLensContains(String codeLensOver, int occurrence, String snippet) throws Exception {
|
||||
int cmPosition = getHoverPosition(codeLensOver, occurrence);
|
||||
for (CodeLens cm : getLiveDataCodeLenses()) {
|
||||
if (cmPosition >= doc.toOffset(cm.getRange().getStart()) && cmPosition <= doc.toOffset(cm.getRange().getEnd())) {
|
||||
if (cm.getData() instanceof String) {
|
||||
if (((String) cm.getData()).contains(snippet)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
fail("Live Data CodeLens data field is not a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
fail("Cannot find '" + snippet + "' in Live Data Code Lenses");
|
||||
}
|
||||
|
||||
public void assertLiveCodeLensContains(String codeLensOver, String snippet) throws Exception {
|
||||
assertLiveCodeLensContains(codeLensOver, 1, snippet);
|
||||
}
|
||||
|
||||
public String hoverString(Hover hover) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
boolean first = true;
|
||||
@@ -619,6 +639,16 @@ public class Editor {
|
||||
Hover hover = harness.getHover(doc, doc.toPosition(hoverPosition));
|
||||
assertEquals(expectedHover.trim(), hoverString(hover).trim());
|
||||
}
|
||||
|
||||
public List<? extends CodeLens> getCodeLenses() throws Exception {
|
||||
return harness.getCodeLenses(doc);
|
||||
}
|
||||
|
||||
public List<? extends CodeLens> getLiveDataCodeLenses() throws Exception {
|
||||
HighlightParams highlights = this.highlightsFuture.get(HIGHLIGHTS_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
|
||||
return highlights != null ? highlights.getCodeLenses() : ImmutableList.of();
|
||||
}
|
||||
|
||||
|
||||
public void assertNoHover(String hoverOver, int occurence) throws Exception {
|
||||
int hoverPosition = getHoverPosition(hoverOver,occurence);
|
||||
|
||||
@@ -13,8 +13,6 @@ package org.springframework.ide.vscode.boot.java.livehover.v2;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.RuntimeMXBean;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
@@ -38,7 +36,6 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
@@ -129,13 +126,12 @@ public class SpringProcessLiveDataExtractorOverJMX {
|
||||
return new LiveMetricsModel() {
|
||||
|
||||
@Override
|
||||
public RequestMappingMetrics getRequestMappingMetrics(String[] uris, String[] requestMethods) {
|
||||
public RequestMappingMetrics getRequestMappingMetrics(String[] paths, String[] requestMethods) {
|
||||
try {
|
||||
List<Object> tags = new ArrayList<>();
|
||||
if (uris.length == 0) {
|
||||
if (paths.length == 0) {
|
||||
return null;
|
||||
}
|
||||
String[] paths = getPaths(uris);
|
||||
tags.add("uri:" + String.join(",", paths));
|
||||
if (requestMethods.length > 0) {
|
||||
tags.add("method:" + String.join(",", requestMethods));
|
||||
@@ -161,22 +157,6 @@ public class SpringProcessLiveDataExtractorOverJMX {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] getPaths(String[] uris) {
|
||||
Builder<String> builder = ImmutableList.builder();
|
||||
if (uris != null) {
|
||||
for (String val : uris) {
|
||||
try {
|
||||
URI uri = new URI(val);
|
||||
builder.add(uri.getPath());
|
||||
} catch (URISyntaxException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build().toArray(new String[0]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2019 Pivotal, Inc.
|
||||
* Copyright (c) 2017, 2020 Pivotal, Inc.
|
||||
* 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
|
||||
@@ -140,10 +140,18 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
int remaining = 0;
|
||||
|
||||
for (Tuple2<LiveRequestMapping, SpringProcessLiveData> dataEntry : data) {
|
||||
for (String url : getUrls(dataEntry)) {
|
||||
for (Tuple2<String, String> urlWithPath : getUrlsWithPath(dataEntry)) {
|
||||
if (lenses.size() <= CODE_LENS_LIMIT) {
|
||||
Set<String> requestMethods = dataEntry.getT1().getRequestMethods();
|
||||
RequestMappingMetrics metrics = dataEntry.getT2().getLiveMterics().getRequestMappingMetrics(new String[] { url }, requestMethods.toArray(new String[requestMethods.size()]));
|
||||
SpringProcessLiveData liveData = dataEntry.getT2();
|
||||
LiveRequestMapping requestMapping = dataEntry.getT1();
|
||||
String url = urlWithPath.getT1();
|
||||
String path = urlWithPath.getT2();
|
||||
Set<String> requestMethods = requestMapping.getRequestMethods();
|
||||
|
||||
RequestMappingMetrics metrics = liveData.getLiveMterics() == null ? null
|
||||
: liveData.getLiveMterics().getRequestMappingMetrics(new String[] { path },
|
||||
requestMethods.toArray(new String[requestMethods.size()]));
|
||||
|
||||
CodeLens codeLens = createCodeLensForRequestMapping(range, url, metrics);
|
||||
lenses.add(codeLens);
|
||||
} else {
|
||||
@@ -232,8 +240,8 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<String> getUrls(Tuple2<LiveRequestMapping, SpringProcessLiveData> mappingMethod) {
|
||||
List<String> urls = new ArrayList<>();
|
||||
private List<Tuple2<String, String>> getUrlsWithPath(Tuple2<LiveRequestMapping, SpringProcessLiveData> mappingMethod) {
|
||||
List<Tuple2<String, String>> urls = new ArrayList<>();
|
||||
SpringProcessLiveData liveData = mappingMethod.getT2();
|
||||
String contextPath = liveData.getContextPath();
|
||||
|
||||
@@ -241,7 +249,8 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
String port = liveData.getPort();
|
||||
String host = liveData.getHost();
|
||||
|
||||
String[] paths = mappingMethod.getT1().getSplitPath();
|
||||
LiveRequestMapping requestMapping = mappingMethod.getT1();
|
||||
String[] paths = requestMapping.getSplitPath();
|
||||
if (paths==null || paths.length==0) {
|
||||
//Technically, this means the path 'predicate' is unconstrained, meaning any path matches.
|
||||
//So this is not quite the same as the case where path=""... but...
|
||||
@@ -251,7 +260,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
}
|
||||
for (String path : paths) {
|
||||
String url = UrlUtil.createUrl(urlScheme, host, port, path, contextPath);
|
||||
urls.add(url);
|
||||
urls.add(Tuples.of(url, path));
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
@@ -286,10 +295,12 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
Renderable urlRenderables = Renderables.concat(renderableUrls);
|
||||
|
||||
Set<String> requestMethods = requestMapping.getRequestMethods();
|
||||
RequestMappingMetrics metrics = liveData.getLiveMterics().getRequestMappingMetrics(requestMapping.getSplitPath(), requestMethods.toArray(new String[requestMethods.size()]));
|
||||
RequestMappingMetrics metrics = liveData.getLiveMterics() == null ? null
|
||||
: liveData.getLiveMterics().getRequestMappingMetrics(requestMapping.getSplitPath(),
|
||||
requestMethods.toArray(new String[requestMethods.size()]));
|
||||
if (metrics != null) {
|
||||
Renderable metricsRenderable = Renderables.concat(
|
||||
Renderables.bold("Count: " + metrics.getCallsCount() + " | Total Time: " + metrics.getTotalTime() + " | Max Time: " + metrics.getMaxTime()),
|
||||
Renderables.bold(createHoverMetricsContent(metrics)),
|
||||
Renderables.text("\n\n"));
|
||||
urlRenderables = Renderables.concat(urlRenderables, Renderables.text("\n\n"), metricsRenderable);
|
||||
}
|
||||
@@ -313,6 +324,42 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
// being added between the content itself
|
||||
return new Hover(ImmutableList.of(Either.forLeft(contentVal.toString())));
|
||||
}
|
||||
|
||||
private String createHoverMetricsContent(RequestMappingMetrics metrics) {
|
||||
char timeUnitShort = metrics.getTimeUnit().name().toLowerCase().charAt(0);
|
||||
|
||||
StringBuilder metricsContent = new StringBuilder();
|
||||
metricsContent.append("Count: ");
|
||||
metricsContent.append(metrics.getCallsCount());
|
||||
metricsContent.append(" | Total Time: ");
|
||||
metricsContent.append(metrics.getTotalTime());
|
||||
metricsContent.append(timeUnitShort);
|
||||
metricsContent.append(" | Max Time: ");
|
||||
metricsContent.append(metrics.getMaxTime());
|
||||
metricsContent.append(timeUnitShort);
|
||||
|
||||
return metricsContent.toString();
|
||||
}
|
||||
|
||||
private String createCodeLensMetricsContent(RequestMappingMetrics metrics) {
|
||||
char timeUnitShort = metrics.getTimeUnit().name().toLowerCase().charAt(0);
|
||||
|
||||
StringBuilder metricsContent = new StringBuilder();
|
||||
|
||||
metricsContent.append("Count=");
|
||||
metricsContent.append(metrics.getCallsCount());
|
||||
metricsContent.append(' ');
|
||||
metricsContent.append("Total=");
|
||||
metricsContent.append(String.format("%.2f", metrics.getTotalTime()));
|
||||
metricsContent.append(timeUnitShort);
|
||||
metricsContent.append(' ');
|
||||
metricsContent.append("Max=");
|
||||
metricsContent.append(String.format("%.2f", metrics.getMaxTime()));
|
||||
metricsContent.append(timeUnitShort);
|
||||
metricsContent.append(')');
|
||||
|
||||
return metricsContent.toString();
|
||||
}
|
||||
|
||||
private CodeLens createCodeLensForRequestMapping(Range range, String content, RequestMappingMetrics metrics) {
|
||||
CodeLens codeLens = new CodeLens();
|
||||
@@ -322,24 +369,11 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
if (StringUtil.hasText(content)) {
|
||||
|
||||
if (metrics != null) {
|
||||
char timeUnitShort = metrics.getTimeUnit().name().charAt(0);
|
||||
|
||||
StringBuilder codeLensContent = new StringBuilder(content);
|
||||
|
||||
codeLensContent.append(' ');
|
||||
codeLensContent.append('(');
|
||||
codeLensContent.append("Count=");
|
||||
codeLensContent.append(metrics.getCallsCount());
|
||||
codeLensContent.append(' ');
|
||||
codeLensContent.append("Total=");
|
||||
codeLensContent.append(shorten(metrics.getTotalTime()));
|
||||
codeLensContent.append(timeUnitShort);
|
||||
codeLensContent.append(' ');
|
||||
codeLensContent.append("Max=");
|
||||
codeLensContent.append(shorten(metrics.getMaxTime()));
|
||||
codeLensContent.append(timeUnitShort);
|
||||
codeLensContent.append(createCodeLensMetricsContent(metrics));
|
||||
codeLensContent.append(')');
|
||||
|
||||
content = codeLensContent.toString();
|
||||
}
|
||||
|
||||
@@ -354,10 +388,6 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
return codeLens;
|
||||
}
|
||||
|
||||
private double shorten(double val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
private CodeLens createCodeLensForRemaining(Range range, int remaining) {
|
||||
CodeLens codeLens = new CodeLens();
|
||||
codeLens.setRange(range);
|
||||
@@ -373,5 +403,5 @@ public class RequestMappingHoverProvider implements HoverProvider {
|
||||
|
||||
return codeLens;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 Pivotal, Inc.
|
||||
* Copyright (c) 2017, 2020 Pivotal, Inc.
|
||||
* 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
|
||||
@@ -203,7 +203,7 @@ public class RequestMappingLiveHoverTest {
|
||||
|
||||
@Override
|
||||
public double getTotalTime() {
|
||||
return 3.0;
|
||||
return "/hello".equals(paths[0]) ? 3.034567 : 5.43673;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -213,12 +213,12 @@ public class RequestMappingLiveHoverTest {
|
||||
|
||||
@Override
|
||||
public double getMaxTime() {
|
||||
return 0.55;
|
||||
return "/hello".equals(paths[0]) ? 0.5535632 : 0.7437624;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCallsCount() {
|
||||
return 25;
|
||||
return "/hello".equals(paths[0]) ? 25 : 34;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -231,10 +231,72 @@ public class RequestMappingLiveHoverTest {
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHighlights("@RequestMapping(\"/hello\")", "@RequestMapping(\"/goodbye\")");
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(\"/hello\")", "[https://cfapps.io:999/hello](https://cfapps.io:999/hello) \n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
editor.assertHoverContains("@RequestMapping(\"/hello\")", "[https://cfapps.io:999/hello](https://cfapps.io:999/hello) \n");
|
||||
editor.assertHoverContains("@RequestMapping(\"/hello\")", "Count: 25 | Total Time: 3.034567s | Max Time: 0.5535632s");
|
||||
editor.assertHoverContains("@RequestMapping(\"/hello\")", "Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(\"/goodbye\")", "Count: 25 | Total Time: 3.0 | Max Time: 0.55");
|
||||
editor.assertHoverContains("@RequestMapping(\"/goodbye\")", "Count: 34 | Total Time: 5.43673s | Max Time: 0.7437624s");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiveCodeLensMetricsSection() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
SpringProcessLiveData liveData = new SpringProcessLiveDataBuilder()
|
||||
.port("999")
|
||||
.processID("76543")
|
||||
.urlScheme("https")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappingsJson(
|
||||
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
|
||||
.liveMetrics(new LiveMetricsModel() {
|
||||
|
||||
@Override
|
||||
public RequestMappingMetrics getRequestMappingMetrics(String[] paths, String[] requestMethods) {
|
||||
return new RequestMappingMetrics() {
|
||||
|
||||
@Override
|
||||
public double getTotalTime() {
|
||||
return "/hello".equals(paths[0]) ? 3.034567 : 5.43673;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeUnit getTimeUnit() {
|
||||
return TimeUnit.SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMaxTime() {
|
||||
return "/hello".equals(paths[0]) ? 0.5535632 : 0.7437624;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCallsCount() {
|
||||
return "/hello".equals(paths[0]) ? 25 : 34;
|
||||
}
|
||||
};
|
||||
}
|
||||
})
|
||||
.build();
|
||||
liveDataProvider.add("processkey", liveData);
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHighlights("@RequestMapping(\"/hello\")", "@RequestMapping(\"/goodbye\")");
|
||||
|
||||
editor.assertLiveCodeLensContains("@RequestMapping(\"/hello\")", "Count=25 Total=3.03s Max=0.55s");
|
||||
editor.assertLiveCodeLensContains("@RequestMapping(\"/goodbye\")", "Count=34 Total=5.44s Max=0.74s");
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user