Port over all manifest.yml editr tests (still failing)
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>language-server-test-harness</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,5 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
@@ -0,0 +1,4 @@
|
||||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
||||
@@ -0,0 +1,33 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>language-server-test-harness</artifactId>
|
||||
<name>language-server-test-harness</name>
|
||||
<description>Test harness for testing language server functionaltity implemented in Java</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-commons</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj-version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -128,4 +128,16 @@ public class Editor {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public void assertCompletions(String... specs) {
|
||||
throw new UnsupportedOperationException("Not implemented yet!");
|
||||
}
|
||||
|
||||
public void assertIsHoverRegion(String string) {
|
||||
throw new UnsupportedOperationException("Not implemented yet!");
|
||||
}
|
||||
|
||||
public void assertHoverContains(String string, String string2) {
|
||||
throw new UnsupportedOperationException("Not implemented yet!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,12 +7,18 @@
|
||||
<artifactId>commons-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>Parent pom for headless services written in Java</name>
|
||||
<name>commons-parent</name>
|
||||
|
||||
<modules>
|
||||
<module>language-server-commons</module>
|
||||
<module>language-server-test-harness</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<junit-version>4.11</junit-version>
|
||||
<assertj-version>3.5.2</assertj-version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
@@ -32,13 +38,13 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<version>${junit-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>3.5.2</version>
|
||||
<version>${assertj-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -58,6 +58,13 @@
|
||||
<artifactId>guava</artifactId>
|
||||
<version>18.0</version>
|
||||
</dependency>
|
||||
<!-- Test harness -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-test-harness</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-commons</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- Yaml -->
|
||||
<dependency>
|
||||
@@ -48,6 +48,13 @@
|
||||
<artifactId>guava</artifactId>
|
||||
<version>18.0</version>
|
||||
</dependency>
|
||||
<!-- Test harness -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-test-harness</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
package org.springframework.ide.vscode.testharness;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.text.BadLocationException;
|
||||
|
||||
import io.typefox.lsapi.Diagnostic;
|
||||
import io.typefox.lsapi.Position;
|
||||
import io.typefox.lsapi.PublishDiagnosticsParams;
|
||||
import io.typefox.lsapi.Range;
|
||||
|
||||
public class Editor {
|
||||
|
||||
private static final Comparator<Diagnostic> PROBLEM_COMPARATOR = new Comparator<Diagnostic>() {
|
||||
@Override
|
||||
public int compare(Diagnostic o1, Diagnostic o2) {
|
||||
int diff = compare(o1.getRange().getStart(), o2.getRange().getStart());
|
||||
if (diff!=0) return diff;
|
||||
return compare(o1.getRange().getEnd(), o2.getRange().getEnd());
|
||||
}
|
||||
|
||||
private int compare(Position p1, Position p2) {
|
||||
int d = p1.getLine() - p2.getLine();
|
||||
if (d!=0) return d;
|
||||
return p1.getCharacter() - p2.getCharacter();
|
||||
}
|
||||
};
|
||||
|
||||
private LanguageServerHarness harness;
|
||||
private TextDocumentInfo document;
|
||||
|
||||
public Editor(LanguageServerHarness harness, String contents) throws Exception {
|
||||
this.harness = harness;
|
||||
this.document = harness.openDocument(harness.createWorkingCopy(contents));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a 'expectedProblems' are found by the reconciler. Expected problems are
|
||||
* specified by string of the form "${badSnippet}|${messageSnippet}". The badSnippet
|
||||
* is the text expected to be covered by the marker's region and the message snippet must
|
||||
* be found in the error marker's message.
|
||||
* <p>
|
||||
* The expected problems are matched one-to-one in the order given (so markers in the
|
||||
* editor must appear in the expected order for the assert to pass).
|
||||
*
|
||||
* @param editor
|
||||
* @param expectedProblems
|
||||
* @throws BadLocationException
|
||||
*/
|
||||
public void assertProblems(String... expectedProblems) throws Exception {
|
||||
Editor editor = this;
|
||||
List<Diagnostic> actualProblems = new ArrayList<>(editor.reconcile());
|
||||
Collections.sort(actualProblems, PROBLEM_COMPARATOR);
|
||||
String bad = null;
|
||||
if (actualProblems.size()!=expectedProblems.length) {
|
||||
bad = "Wrong number of problems (expecting "+expectedProblems.length+" but found "+actualProblems.size()+")";
|
||||
} else {
|
||||
for (int i = 0; i < expectedProblems.length; i++) {
|
||||
if (!matchProblem(editor, actualProblems.get(i), expectedProblems[i])) {
|
||||
bad = "First mismatch at index "+i+": "+expectedProblems[i]+"\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bad!=null) {
|
||||
fail(bad+problemSumary(editor, actualProblems));
|
||||
}
|
||||
}
|
||||
|
||||
private String problemSumary(Editor editor, List<Diagnostic> actualProblems) throws Exception {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (Diagnostic p : actualProblems) {
|
||||
buf.append("\n----------------------\n");
|
||||
|
||||
String snippet = editor.getText(p.getRange());
|
||||
buf.append("("+p.getRange().getStart().getLine()+", "+p.getRange().getStart().getCharacter()+")["+snippet+"]:\n");
|
||||
buf.append(" "+p.getMessage());
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String getText(Range range) {
|
||||
return document.getText(range);
|
||||
}
|
||||
|
||||
public void setText(String newContent) throws Exception {
|
||||
document = harness.changeDocument(document.getUri(), newContent);
|
||||
}
|
||||
|
||||
private boolean matchProblem(Editor editor, Diagnostic problem, String expect) {
|
||||
String[] parts = expect.split("\\|");
|
||||
assertEquals(2, parts.length);
|
||||
String badSnippet = parts[0];
|
||||
String messageSnippet = parts[1];
|
||||
boolean spaceSensitive = badSnippet.trim().length()<badSnippet.length();
|
||||
boolean emptyRange = problem.getRange().getStart().equals(problem.getRange().getEnd());
|
||||
String actualBadSnippet = emptyRange
|
||||
? editor.getCharAt(problem.getRange().getStart())
|
||||
: editor.getText(problem.getRange());
|
||||
if (!spaceSensitive) {
|
||||
actualBadSnippet = actualBadSnippet.trim();
|
||||
}
|
||||
return actualBadSnippet.equals(badSnippet)
|
||||
&& problem.getMessage().contains(messageSnippet);
|
||||
}
|
||||
|
||||
private String getCharAt(Position start) {
|
||||
int offset = document.toOffset(start);
|
||||
return document.getText().substring(offset, offset+1);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Diagnostic> reconcile() {
|
||||
// We assume the language server works synchronously for now and it does an immediate reconcile
|
||||
// when the document changes. In the future this is probably not going to be the case though and then this
|
||||
// method will need to somehow ensure the linter is done working before retrieving the problems from the
|
||||
// test harness.
|
||||
PublishDiagnosticsParams diagnostics = harness.getDiagnostics(document);
|
||||
if (diagnostics!=null) {
|
||||
return (List<Diagnostic>) diagnostics.getDiagnostics();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
package org.springframework.ide.vscode.testharness;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.assertj.core.api.Condition;
|
||||
|
||||
import io.typefox.lsapi.ClientCapabilitiesImpl;
|
||||
import io.typefox.lsapi.CompletionItem;
|
||||
import io.typefox.lsapi.CompletionList;
|
||||
import io.typefox.lsapi.Diagnostic;
|
||||
import io.typefox.lsapi.DidChangeTextDocumentParamsImpl;
|
||||
import io.typefox.lsapi.DidOpenTextDocumentParamsImpl;
|
||||
import io.typefox.lsapi.InitializeParamsImpl;
|
||||
import io.typefox.lsapi.InitializeResult;
|
||||
import io.typefox.lsapi.Position;
|
||||
import io.typefox.lsapi.PositionImpl;
|
||||
import io.typefox.lsapi.PublishDiagnosticsParams;
|
||||
import io.typefox.lsapi.Range;
|
||||
import io.typefox.lsapi.ServerCapabilities;
|
||||
import io.typefox.lsapi.TextDocumentContentChangeEventImpl;
|
||||
import io.typefox.lsapi.TextDocumentItemImpl;
|
||||
import io.typefox.lsapi.TextDocumentPositionParamsImpl;
|
||||
import io.typefox.lsapi.VersionedTextDocumentIdentifierImpl;
|
||||
import io.typefox.lsapi.services.LanguageServer;
|
||||
|
||||
public class LanguageServerHarness {
|
||||
|
||||
//Warning this 'harness' is not very good yet. It just implements bare minimum to
|
||||
// be able to test the MyLanguageServer example.
|
||||
|
||||
private Random random = new Random();
|
||||
|
||||
private Callable<? extends LanguageServer> factory;
|
||||
|
||||
private LanguageServer server;
|
||||
|
||||
private InitializeResult initResult;
|
||||
|
||||
private Map<String,TextDocumentInfo> documents = new HashMap<>();
|
||||
private Map<String, PublishDiagnosticsParams> diagnostics = new HashMap<>();
|
||||
|
||||
public LanguageServerHarness(Callable<? extends LanguageServer> factory) throws Exception {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public synchronized TextDocumentInfo getOrReadFile(File file) throws Exception {
|
||||
String uri = file.toURI().toString();
|
||||
TextDocumentInfo d = documents.get(uri);
|
||||
if (d==null) {
|
||||
documents.put(uri, d = readFile(file));
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public TextDocumentInfo readFile(File file) throws Exception {
|
||||
byte[] encoded = Files.readAllBytes(file.toPath());
|
||||
String content = new String(encoded, getEncoding());
|
||||
TextDocumentItemImpl document = new TextDocumentItemImpl();
|
||||
document.setText(content);
|
||||
document.setUri(file.toURI().toString());
|
||||
document.setVersion(getFirstVersion());
|
||||
document.setLanguageId(getLanguageId());
|
||||
return new TextDocumentInfo(document);
|
||||
}
|
||||
|
||||
private synchronized TextDocumentItemImpl setDocumentContent(String uri, String newContent) {
|
||||
TextDocumentInfo o = documents.get(uri);
|
||||
TextDocumentItemImpl n = new TextDocumentItemImpl();
|
||||
n.setLanguageId(o.getLanguageId());
|
||||
n.setText(newContent);
|
||||
n.setVersion(o.getVersion()+1);
|
||||
n.setUri(o.getUri());
|
||||
documents.put(uri, new TextDocumentInfo(n));
|
||||
return n;
|
||||
}
|
||||
|
||||
protected Charset getEncoding() {
|
||||
return Charset.forName("utf8");
|
||||
}
|
||||
|
||||
protected String getLanguageId() {
|
||||
return "plaintext";
|
||||
}
|
||||
|
||||
protected String getFileExtension() {
|
||||
return ".txt";
|
||||
}
|
||||
|
||||
private synchronized void receiveDiagnostics(PublishDiagnosticsParams diags) {
|
||||
this.diagnostics.put(diags.getUri(), diags);
|
||||
}
|
||||
|
||||
public InitializeResult intialize(File workspaceRoot) throws Exception {
|
||||
server = factory.call();
|
||||
int parentPid = random.nextInt(40000)+1000;
|
||||
InitializeParamsImpl initParams = new InitializeParamsImpl();
|
||||
initParams.setRootPath(workspaceRoot== null?null:workspaceRoot.toString());
|
||||
initParams.setProcessId(parentPid);
|
||||
ClientCapabilitiesImpl clientCap = new ClientCapabilitiesImpl();
|
||||
initParams.setCapabilities(clientCap);
|
||||
initResult = server.initialize(initParams).get();
|
||||
|
||||
server.getTextDocumentService().onPublishDiagnostics(this::receiveDiagnostics);
|
||||
return initResult;
|
||||
}
|
||||
|
||||
public TextDocumentInfo openDocument(TextDocumentInfo documentInfo) throws Exception {
|
||||
DidOpenTextDocumentParamsImpl didOpen = new DidOpenTextDocumentParamsImpl();
|
||||
didOpen.setTextDocument(documentInfo.getDocument());
|
||||
didOpen.setText(documentInfo.getText());
|
||||
didOpen.setUri(documentInfo.getUri());
|
||||
server.getTextDocumentService().didOpen(didOpen);
|
||||
return documentInfo;
|
||||
}
|
||||
|
||||
public TextDocumentInfo openDocument(File file) throws Exception {
|
||||
return openDocument(getOrReadFile(file));
|
||||
}
|
||||
|
||||
public TextDocumentInfo changeDocument(String uri, String newContent) throws Exception {
|
||||
TextDocumentItemImpl textDocument = setDocumentContent(uri, newContent);
|
||||
DidChangeTextDocumentParamsImpl didChange = new DidChangeTextDocumentParamsImpl();
|
||||
VersionedTextDocumentIdentifierImpl version = new VersionedTextDocumentIdentifierImpl();
|
||||
version.setUri(uri);
|
||||
version.setVersion(textDocument.getVersion());
|
||||
didChange.setTextDocument(version);
|
||||
switch (getDocumentSyncMode()) {
|
||||
case ServerCapabilities.SYNC_NONE:
|
||||
break; //nothing todo
|
||||
case ServerCapabilities.SYNC_INCREMENTAL:
|
||||
throw new IllegalStateException("Incremental sync not yet supported by this test harness");
|
||||
case ServerCapabilities.SYNC_FULL:
|
||||
TextDocumentContentChangeEventImpl change = new TextDocumentContentChangeEventImpl();
|
||||
change.setText(newContent);
|
||||
didChange.setContentChanges(Collections.singletonList(change));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unkown SYNC mode: "+getDocumentSyncMode());
|
||||
}
|
||||
server.getTextDocumentService().didChange(didChange);
|
||||
return documents.get(uri);
|
||||
}
|
||||
|
||||
private int getDocumentSyncMode() {
|
||||
Integer mode = initResult.getCapabilities().getTextDocumentSync();
|
||||
return mode==null ? ServerCapabilities.SYNC_NONE : mode;
|
||||
}
|
||||
|
||||
public PublishDiagnosticsParams getDiagnostics(TextDocumentInfo doc) {
|
||||
return diagnostics.get(doc.getUri());
|
||||
}
|
||||
|
||||
public static Condition<Diagnostic> isDiagnosticWithSeverity(int severity) {
|
||||
return new Condition<>(
|
||||
(d) -> d.getSeverity()==severity,
|
||||
"Diagnostic with severity '"+severity+"'"
|
||||
);
|
||||
}
|
||||
|
||||
public static Condition<Diagnostic> isDiagnosticCovering(TextDocumentInfo doc, String string) {
|
||||
return new Condition<>(
|
||||
(d) -> isDiagnosticCovering(d, doc, string),
|
||||
"Diagnostic covering '"+string+"'"
|
||||
);
|
||||
}
|
||||
|
||||
public static final Condition<Diagnostic> isWarning = isDiagnosticWithSeverity(Diagnostic.SEVERITY_WARNING);
|
||||
|
||||
public static boolean isDiagnosticCovering(Diagnostic diag, TextDocumentInfo doc, String string) {
|
||||
Range rng = diag.getRange();
|
||||
String actualText = doc.getText(rng);
|
||||
return string.equals(actualText);
|
||||
}
|
||||
|
||||
public static Condition<Diagnostic> isDiagnosticOnLine(int line) {
|
||||
return new Condition<>(
|
||||
(d) -> d.getRange().getStart().getLine()==line,
|
||||
"Diagnostic on line "+line
|
||||
);
|
||||
}
|
||||
|
||||
public CompletionList getCompletions(TextDocumentInfo doc, Position cursor) throws Exception {
|
||||
TextDocumentPositionParamsImpl params = new TextDocumentPositionParamsImpl();
|
||||
params.setPosition(toImpl(cursor));
|
||||
params.setTextDocument(doc.getId());
|
||||
return server.getTextDocumentService().completion(params).get();
|
||||
}
|
||||
|
||||
private PositionImpl toImpl(Position pos) {
|
||||
if (pos instanceof PositionImpl) {
|
||||
return (PositionImpl) pos;
|
||||
} else {
|
||||
PositionImpl imp = new PositionImpl();
|
||||
imp.setCharacter(pos.getCharacter());
|
||||
imp.setLine(pos.getLine());
|
||||
return imp;
|
||||
}
|
||||
}
|
||||
|
||||
private CompletionItem resolveCompletionItem(CompletionItem unresolved) {
|
||||
try {
|
||||
return server.getTextDocumentService().resolveCompletionItem(unresolved).get();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<CompletionItem> resolveCompletions(CompletionList completions) {
|
||||
return completions.getItems().stream()
|
||||
.map(this::resolveCompletionItem)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Editor newEditor(String contents) throws Exception {
|
||||
return new Editor(this, contents);
|
||||
}
|
||||
|
||||
public synchronized TextDocumentInfo createWorkingCopy(String contents) throws Exception {
|
||||
TextDocumentItemImpl doc = new TextDocumentItemImpl();
|
||||
doc.setLanguageId(getLanguageId());
|
||||
doc.setText(contents);
|
||||
doc.setUri(createTempUri());
|
||||
doc.setVersion(getFirstVersion());
|
||||
TextDocumentInfo docinfo = new TextDocumentInfo(doc);
|
||||
documents.put(docinfo.getUri(), docinfo);
|
||||
return docinfo;
|
||||
}
|
||||
|
||||
protected int getFirstVersion() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected String createTempUri() throws Exception {
|
||||
return File.createTempFile("workingcopy", getFileExtension()).toURI().toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package org.springframework.ide.vscode.testharness;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import io.typefox.lsapi.Position;
|
||||
import io.typefox.lsapi.PositionImpl;
|
||||
import io.typefox.lsapi.Range;
|
||||
import io.typefox.lsapi.TextDocumentIdentifierImpl;
|
||||
import io.typefox.lsapi.TextDocumentItemImpl;
|
||||
|
||||
public class TextDocumentInfo {
|
||||
|
||||
Pattern NEWLINE = Pattern.compile("\\r|\\n|\\r\\n|\\n\\r");
|
||||
|
||||
private final TextDocumentItemImpl document;
|
||||
|
||||
private int[] _lineStarts;
|
||||
|
||||
public TextDocumentInfo(TextDocumentItemImpl document) {
|
||||
this.document = document;
|
||||
}
|
||||
|
||||
public String getLanguageId() {
|
||||
return getDocument().getLanguageId();
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return getDocument().getVersion();
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return getDocument().getText();
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return getDocument().getUri();
|
||||
}
|
||||
|
||||
public TextDocumentItemImpl getDocument() {
|
||||
return document;
|
||||
}
|
||||
|
||||
public String getText(Range rng) {
|
||||
int start = toOffset(rng.getStart());
|
||||
int end = toOffset(rng.getEnd());
|
||||
return getText().substring(start, end);
|
||||
}
|
||||
|
||||
public int toOffset(Position p) {
|
||||
int startOfLine = startOfLine(p.getLine());
|
||||
return startOfLine+p.getCharacter();
|
||||
}
|
||||
|
||||
private int startOfLine(int line) {
|
||||
return lineStarts()[line];
|
||||
}
|
||||
|
||||
private int[] lineStarts() {
|
||||
if (_lineStarts==null) {
|
||||
_lineStarts = parseLines();
|
||||
}
|
||||
return _lineStarts;
|
||||
}
|
||||
|
||||
private int[] parseLines() {
|
||||
List<Integer> lineStarts = new ArrayList<>();
|
||||
lineStarts.add(0);
|
||||
Matcher matcher = NEWLINE.matcher(getText());
|
||||
int pos = 0;
|
||||
while (matcher.find(pos)) {
|
||||
lineStarts.add(pos = matcher.end());
|
||||
}
|
||||
int[] array = new int[lineStarts.size()];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
array[i] = lineStarts.get(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the (first) position of a given text snippet in the
|
||||
* document.
|
||||
*
|
||||
* @return The position, or null if the snippet can't be found.
|
||||
*/
|
||||
public Position positionOf(String snippet) {
|
||||
int offset = getText().indexOf(snippet);
|
||||
if (offset>=0) {
|
||||
return toPosition(offset);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Position toPosition(int offset) {
|
||||
int line = lineNumber(offset);
|
||||
int startOfLine = startOfLine(line);
|
||||
int column = offset - startOfLine;
|
||||
PositionImpl pos = new PositionImpl();
|
||||
pos.setCharacter(column);
|
||||
pos.setLine(line);
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the line-number a given offset (i.e. what line is the offset inside of?)
|
||||
*/
|
||||
private int lineNumber(int offset) {
|
||||
int[] lineStarts = lineStarts();
|
||||
// TODO Could use binary search which is faster
|
||||
int lineNumber = 0;
|
||||
for (int i = 0; i < lineStarts.length; i++) {
|
||||
if (lineStarts[i]<=offset) {
|
||||
lineNumber = i;
|
||||
} else {
|
||||
return lineNumber;
|
||||
}
|
||||
}
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public TextDocumentIdentifierImpl getId() {
|
||||
TextDocumentIdentifierImpl id = new TextDocumentIdentifierImpl();
|
||||
id.setUri(getUri());
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package org.springframework.ide.vscode.yaml;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.testharness.Editor;
|
||||
import org.springframework.ide.vscode.testharness.LanguageServerHarness;
|
||||
import org.springframework.ide.vscode.yaml.YamlLanguageServer;
|
||||
|
||||
/**
|
||||
* This class is a placeholder where we will attempt to copy and port
|
||||
* as many tests a possible from
|
||||
* org.springframework.ide.eclipse.boot.properties.editor.test.YamlEditorTests
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ApplicationYamlEditorTest {
|
||||
|
||||
@Test public void testReconcileCatchesParseError() throws Exception {
|
||||
LanguageServerHarness harness = new LanguageServerHarness(YamlLanguageServer::new);
|
||||
harness.intialize(null);
|
||||
|
||||
Editor editor = harness.newEditor(
|
||||
"somemap: val\n"+
|
||||
"- sequence"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"-|expected <block end>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void linterRunsOnDocumentOpenAndChange() throws Exception {
|
||||
LanguageServerHarness harness = new LanguageServerHarness(YamlLanguageServer::new);
|
||||
harness.intialize(null);
|
||||
|
||||
Editor editor = harness.newEditor(
|
||||
"somemap: val\n"+
|
||||
"- sequence"
|
||||
);
|
||||
|
||||
editor.assertProblems(
|
||||
"-|expected <block end>"
|
||||
);
|
||||
|
||||
editor.setText(
|
||||
"- sequence\n" +
|
||||
"zomemap: val"
|
||||
);
|
||||
|
||||
editor.assertProblems(
|
||||
"z|expected <block end>"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.yaml;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.testharness.Editor;
|
||||
import org.springframework.ide.vscode.testharness.LanguageServerHarness;
|
||||
|
||||
public class ManifestYamlEditorTest {
|
||||
|
||||
LanguageServerHarness harness;
|
||||
|
||||
@Before public void setup() throws Exception {
|
||||
harness = new LanguageServerHarness(YamlLanguageServer::new);
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@Test public void testReconcileCatchesParseError() throws Exception {
|
||||
|
||||
Editor editor = harness.newEditor(
|
||||
"somemap: val\n"+
|
||||
"- sequence"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"-|expected <block end>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void linterRunsOnDocumentOpenAndChange() throws Exception {
|
||||
LanguageServerHarness harness = new LanguageServerHarness(YamlLanguageServer::new);
|
||||
harness.intialize(null);
|
||||
|
||||
Editor editor = harness.newEditor(
|
||||
"somemap: val\n"+
|
||||
"- sequence"
|
||||
);
|
||||
|
||||
editor.assertProblems(
|
||||
"-|expected <block end>"
|
||||
);
|
||||
|
||||
editor.setText(
|
||||
"- sequence\n" +
|
||||
"zomemap: val"
|
||||
);
|
||||
|
||||
editor.assertProblems(
|
||||
"z|expected <block end>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toplevelCompletions() throws Exception {
|
||||
Editor editor;
|
||||
editor = harness.newEditor("<*>");
|
||||
editor.assertCompletions(
|
||||
"applications:\n"+
|
||||
" - <*>",
|
||||
// ---------------
|
||||
"buildpack: <*>",
|
||||
// ---------------
|
||||
"command: <*>",
|
||||
// ---------------
|
||||
"disk_quota: <*>",
|
||||
// ---------------
|
||||
"domain: <*>",
|
||||
// ---------------
|
||||
"domains:\n"+
|
||||
" - <*>",
|
||||
// ---------------
|
||||
"env:\n"+
|
||||
" <*>",
|
||||
// ---------------
|
||||
// "host: <*>",
|
||||
// ---------------
|
||||
// "hosts: \n"+
|
||||
// " - <*>",
|
||||
// ---------------
|
||||
"inherit: <*>",
|
||||
// ---------------
|
||||
"instances: <*>",
|
||||
// ---------------
|
||||
"memory: <*>",
|
||||
// ---------------
|
||||
// "name: <*>",
|
||||
// ---------------
|
||||
"no-hostname: <*>",
|
||||
// ---------------
|
||||
"no-route: <*>",
|
||||
// ---------------
|
||||
"path: <*>",
|
||||
// ---------------
|
||||
"random-route: <*>",
|
||||
// ---------------
|
||||
"services:\n"+
|
||||
" - <*>",
|
||||
// ---------------
|
||||
"stack: <*>",
|
||||
// ---------------
|
||||
"timeout: <*>"
|
||||
);
|
||||
|
||||
editor = harness.newEditor("ranro<*>");
|
||||
editor.assertCompletions(
|
||||
"random-route: <*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedCompletions() throws Exception {
|
||||
Editor editor;
|
||||
editor = harness.newEditor(
|
||||
"applications:\n" +
|
||||
" - <*>"
|
||||
);
|
||||
editor.assertCompletions(
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - buildpack: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - command: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - disk_quota: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - domain: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - domains:\n"+
|
||||
" - <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - env:\n"+
|
||||
" <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - host: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - hosts:\n"+
|
||||
" - <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - instances: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - memory: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - name: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - no-hostname: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - no-route: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - path: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - random-route: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - services:\n"+
|
||||
" - <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - stack: <*>",
|
||||
// ---------------
|
||||
"applications:\n" +
|
||||
" - timeout: <*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valueCompletions() throws Exception {
|
||||
assertCompletions("disk_quota: <*>",
|
||||
"disk_quota: 1024M<*>",
|
||||
"disk_quota: 256M<*>",
|
||||
"disk_quota: 512M<*>"
|
||||
);
|
||||
assertCompletions("memory: <*>",
|
||||
"memory: 1024M<*>",
|
||||
"memory: 256M<*>",
|
||||
"memory: 512M<*>"
|
||||
);
|
||||
assertCompletions("no-hostname: <*>",
|
||||
"no-hostname: false<*>",
|
||||
"no-hostname: true<*>"
|
||||
);
|
||||
assertCompletions("no-route: <*>",
|
||||
"no-route: false<*>",
|
||||
"no-route: true<*>"
|
||||
);
|
||||
assertCompletions("random-route: <*>",
|
||||
"random-route: false<*>",
|
||||
"random-route: true<*>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hoverInfos() throws Exception {
|
||||
Editor editor = harness.newEditor(
|
||||
"memory: 1G\n" +
|
||||
"applications:\n" +
|
||||
" - buildpack: zbuildpack\n" +
|
||||
" domain: zdomain\n" +
|
||||
" name: foo"
|
||||
);
|
||||
editor.assertIsHoverRegion("memory");
|
||||
editor.assertIsHoverRegion("applications");
|
||||
editor.assertIsHoverRegion("buildpack");
|
||||
editor.assertIsHoverRegion("domain");
|
||||
editor.assertIsHoverRegion("name");
|
||||
|
||||
editor.assertHoverContains("memory", "Use the <code>memory</code> attribute to specify the memory limit");
|
||||
editor.assertHoverContains("1G", "Use the <code>memory</code> attribute to specify the memory limit");
|
||||
editor.assertHoverContains("buildpack", "use the <code>buildpack</code> attribute to specify its URL or name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reconcileMisSpelledPropertyNames() throws Exception {
|
||||
Editor editor;
|
||||
|
||||
editor = harness.newEditor(
|
||||
"memory: 1G\n" +
|
||||
"aplications:\n" +
|
||||
" - buildpack: zbuildpack\n" +
|
||||
" domain: zdomain\n" +
|
||||
" name: foo"
|
||||
);
|
||||
editor.assertProblems("aplications|Unknown property");
|
||||
|
||||
//mispelled or not allowed at toplevel
|
||||
editor = harness.newEditor(
|
||||
"name: foo\n" +
|
||||
"buildpeck: yahah\n" +
|
||||
"memory: 1G\n" +
|
||||
"memori: 1G\n"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"name|Unknown property",
|
||||
"buildpeck|Unknown property",
|
||||
"memori|Unknown property"
|
||||
);
|
||||
|
||||
//mispelled or not allowed as nested
|
||||
editor = harness.newEditor(
|
||||
"applications:\n" +
|
||||
"- name: fine\n" +
|
||||
" buildpeck: yahah\n" +
|
||||
" memory: 1G\n" +
|
||||
" memori: 1G\n" +
|
||||
" applications: bad\n"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"buildpeck|Unknown property",
|
||||
"memori|Unknown property",
|
||||
"applications|Unknown property"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reconcileStructuralProblems() throws Exception {
|
||||
Editor editor;
|
||||
|
||||
//forgot the 'applications:' heading
|
||||
editor = harness.newEditor(
|
||||
"- name: foo"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"- name: foo|Expecting a 'Map' but found a 'Sequence'"
|
||||
);
|
||||
|
||||
//forgot to make the '-' after applications
|
||||
editor = harness.newEditor(
|
||||
"applications:\n" +
|
||||
" name: foo"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"name: foo|Expecting a 'Sequence' but found a 'Map'"
|
||||
);
|
||||
|
||||
//Using a 'composite' element where a scalar type is expected
|
||||
editor = harness.newEditor(
|
||||
"memory:\n"+
|
||||
"- bad sequence\n" +
|
||||
"buildpack:\n" +
|
||||
" bad: map\n"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"- bad sequence|Expecting a 'Memory' but found a 'Sequence'",
|
||||
"bad: map|Expecting a 'Buildpack' but found a 'Map'"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reconcileSimpleTypes() throws Exception {
|
||||
Editor editor;
|
||||
|
||||
//check for 'format' errors:
|
||||
editor = harness.newEditor(
|
||||
"applications:\n" +
|
||||
"- name: foo\n" +
|
||||
" instances: not a number\n" +
|
||||
" no-route: notBool\n"+
|
||||
" memory: 1024\n" +
|
||||
" disk_quota: 2048\n"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"not a number|Positive Integer",
|
||||
"notBool|boolean",
|
||||
"1024|Memory",
|
||||
"2048|Memory"
|
||||
);
|
||||
|
||||
//check for 'range' errors:
|
||||
editor = harness.newEditor(
|
||||
"applications:\n" +
|
||||
"- name: foo\n" +
|
||||
" instances: -3\n" +
|
||||
" memory: -1024M\n" +
|
||||
" disk_quota: -2048M\n"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"-3|Positive Integer",
|
||||
"-1024M|Memory",
|
||||
"-2048M|Memory"
|
||||
);
|
||||
|
||||
//check that correct values are indeed accepted
|
||||
editor = harness.newEditor(
|
||||
"applications:\n" +
|
||||
"- name: foo\n" +
|
||||
" instances: 2\n" +
|
||||
" no-route: true\n"+
|
||||
" memory: 1024M\n" +
|
||||
" disk_quota: 2048MB\n"
|
||||
);
|
||||
editor.assertProblems(/*none*/);
|
||||
|
||||
//check that correct values are indeed accepted
|
||||
editor = harness.newEditor(
|
||||
"applications:\n" +
|
||||
"- name: foo\n" +
|
||||
" instances: 2\n" +
|
||||
" no-route: false\n" +
|
||||
" memory: 1024m\n" +
|
||||
" disk_quota: 2048mb\n"
|
||||
);
|
||||
editor.assertProblems(/*none*/);
|
||||
|
||||
editor = harness.newEditor(
|
||||
"applications:\n" +
|
||||
"- name: foo\n" +
|
||||
" instances: 2\n" +
|
||||
" memory: 1G\n" +
|
||||
" disk_quota: 2g\n"
|
||||
);
|
||||
editor.assertProblems(/*none*/);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void assertCompletions(String textBefore, String... textAfter) throws Exception {
|
||||
Editor editor = harness.newEditor(textBefore);
|
||||
editor.assertCompletions(textAfter);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user