Port over all manifest.yml editr tests (still failing)

This commit is contained in:
Kris De Volder
2016-09-22 17:34:36 -07:00
parent be8c73adec
commit 5e666ef37d
16 changed files with 518 additions and 563 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@@ -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>

View File

@@ -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!");
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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>"
);
}
}

View File

@@ -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);
}
}