From 7954fa2bb5872db4665e4d7bafdce9820bed210f Mon Sep 17 00:00:00 2001 From: Kris De Volder Date: Mon, 17 Oct 2016 11:16:43 -0700 Subject: [PATCH] Porting all the tests from YamlEditorTests in old STS Moved into ApplicationPropertiesTests suite. All tests running but most are not yet passing. Tests that are expected to fail w.r.t. already implemented functionlaity are marked with @Ignore annotations. --- .../application-properties-metadata/pom.xml | 12 +- .../metadata/CachingValueProvider.java | 129 + .../metadata/SpringPropertyIndexProvider.java | 21 + .../ide/vscode/testharness/Editor.java | 52 +- .../ide/vscode/testharness/IType.java | 9 + .../testharness/LanguageServerHarness.java | 28 + .../ide/vscode/testharness/TestProject.java | 11 + vscode-extensions/commons/pom.xml | 1 + .../vscode-application-yaml/pom.xml | 8 +- .../vscode/yaml/AbstractPropsEditorTest.java | 182 + .../yaml/ApplicationYamlEditorTest.java | 3500 ++++++++++++++++- .../ide/vscode/yaml/PropertyIndexHarness.java | 547 +++ .../ide/vscode/yaml/StyledStringMatcher.java | 74 + ...Test.java => YamlLanguageServerTests.java} | 32 +- 14 files changed, 4552 insertions(+), 54 deletions(-) create mode 100644 vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/CachingValueProvider.java create mode 100644 vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/SpringPropertyIndexProvider.java create mode 100644 vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/IType.java create mode 100644 vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/TestProject.java create mode 100644 vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/AbstractPropsEditorTest.java create mode 100644 vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/PropertyIndexHarness.java create mode 100644 vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/StyledStringMatcher.java rename vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/{YamlLanguageServerTest.java => YamlLanguageServerTests.java} (56%) diff --git a/vscode-extensions/commons/application-properties-metadata/pom.xml b/vscode-extensions/commons/application-properties-metadata/pom.xml index f0ede3136..14ec55193 100644 --- a/vscode-extensions/commons/application-properties-metadata/pom.xml +++ b/vscode-extensions/commons/application-properties-metadata/pom.xml @@ -33,20 +33,22 @@ - - + org.springframework.ide.vscode commons-util ${project.version} - + + org.springframework.ide.vscode + commons-language-server + ${project.version} + \ No newline at end of file diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/CachingValueProvider.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/CachingValueProvider.java new file mode 100644 index 000000000..b3ecc5caa --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/CachingValueProvider.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * 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.boot.properties.metadata; + +import java.time.Duration; + +import org.springframework.ide.vscode.boot.properties.metadata.ValueProviderRegistry.ValueProviderStrategy; + +/** + * A abstract {@link ValueProviderStrategy} that is mean to help speedup successive invocations of + * content assist with a similar 'query' string. + *

+ * This implementation is meant to be used for providers that use potentially lenghty/expensive searches + * to determine hints. Since content assist hints are requested by Eclipse CA framework directly on + * the UI thread, they can not simply perform a lengthy search and block UI thread until it finished. + *

+ * This implementation therefore does the following: + *

+ * + * TODO: rather than an abstract class this should really be 'Wrapper' class that delegates to another + * {@link ValueProviderStrategy} and adds a cache in front of it. + * + * @author Kris De Volder + */ +public abstract class CachingValueProvider implements ValueProviderStrategy { + + private static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(1000); + + /** + * Content assist is called inside UI thread and so doing something lenghty things + * like a JavaSearch will block the UI thread completely freezing the UI. So, we + * only return as many results as can be obtained within this hard TIMEOUT limit. + */ + public static Duration TIMEOUT = DEFAULT_TIMEOUT; + + /** + * The maximum number of results returned for a single request. Used to limit the + * values that are cached per entry. + */ + private int MAX_RESULTS = 500; + +// private Cache, CacheEntry> cache = createCache(); +// +// private class CacheEntry { +// boolean isComplete = false; +// int count = 0; +// Flux values; +// +// public CacheEntry(String query, Flux producer) { +// values = producer +// .take(MAX_RESULTS) +// .cache(MAX_RESULTS); +// values.subscribe(); // create infinite demand so that we actually force cache entries to be fetched upto the max. +// } +// +// @Override +// public String toString() { +// return "CacheEntry [isComplete=" + isComplete + ", count=" + count + "]"; +// } +// +// } +// +// @Override +// public final Flux getValues(IJavaProject javaProject, String query) { +//// debug("CA query: "+query); +// Tuple2 key = key(javaProject, query); +// CacheEntry cached = cache.get(key); +// if (cached==null) { +// cache.put(key, cached = new CacheEntry(query, getValuesIncremental(javaProject, query))); +// } +// return cached.values; +// } +// +// /** +// * Tries to use an already cached, complete result for a query that is a prefix of the current query to speed things up. +// *

+// * Falls back on doing a full-blown search if there's no usable 'prefix-query' in the cache. +// */ +// private Flux getValuesIncremental(IJavaProject javaProject, String query) { +//// debug("trying to solve "+query+" incrementally"); +// String subquery = query; +// while (subquery.length()>=1) { +// subquery = subquery.substring(0, subquery.length()-1); +// CacheEntry cached = cache.get(key(javaProject, subquery)); +// if (cached!=null) { +// System.out.println("cached "+subquery+": "+cached); +// if (cached.isComplete) { +//// debug("filtering "+subquery+" -> "+query); +// return cached.values +//// .doOnNext((hint) -> debug("filter["+query+"]: "+hint.getValue())) +// .filter((hint) -> 0!=FuzzyMatcher.matchScore(query, hint.getValue().toString())); +// } else { +//// debug("subquery "+subquery+" cached but is incomplete"); +// } +// } +// } +//// debug("full search for: "+query); +// return getValuesAsycn(javaProject, query); +// } +// +// protected abstract Flux getValuesAsycn(IJavaProject javaProject, String query); +// +// private Tuple2 key(IJavaProject javaProject, String query) { +// return Tuples.of(javaProject==null?null:javaProject.getElementName(), query); +// } +// +// protected Cache createCache() { +// return new LimitedTimeCache<>(Duration.ofMinutes(1)); +// } + + public static void restoreDefaults() { + TIMEOUT = DEFAULT_TIMEOUT; + } + +} diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/SpringPropertyIndexProvider.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/SpringPropertyIndexProvider.java new file mode 100644 index 000000000..6229380ca --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/boot/properties/metadata/SpringPropertyIndexProvider.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2015 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.boot.properties.metadata; + +import org.springframework.ide.vscode.util.IDocument; +import org.springframework.ide.vscode.boot.properties.util.FuzzyMap; + + +public abstract class SpringPropertyIndexProvider { + + public abstract FuzzyMap getIndex(IDocument doc); + +} diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/Editor.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/Editor.java index 55e030de3..50c5b8e25 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/Editor.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/Editor.java @@ -218,7 +218,7 @@ public class Editor { assertEquals(expect.toString(), actual.toString()); } - private void apply(CompletionItem completion) throws Exception { + public void apply(CompletionItem completion) throws Exception { TextEdit edit = completion.getTextEdit(); String docText = document.getText(); if (edit!=null) { @@ -269,7 +269,7 @@ public class Editor { } } - private List getCompletions() throws Exception { + public List getCompletions() throws Exception { CompletionList cl = harness.getCompletions(this.document, this.getCursor()); ArrayList items = new ArrayList<>(cl.getItems()); Collections.sort(items, new Comparator() { @@ -290,6 +290,10 @@ public class Editor { return items; } + public CompletionItem getFirstCompletion() throws Exception { + return getCompletions().get(0); + } + private Position getCursor() { return document.toPosition(selectionStart); } @@ -302,6 +306,10 @@ public class Editor { throw new UnsupportedOperationException("Not implemented yet!"); } + public void assertNoHover(String string) { + throw new UnsupportedOperationException("Not implemented yet!"); + } + public void setSelection(int start, int end) { Assert.assertTrue(start>=0); Assert.assertTrue(end>=start); @@ -315,4 +323,44 @@ public class Editor { return "Editor(\n"+getText()+"\n)"; } + public void assertLinkTargets(String hoverOver, String... expecteds) { + throw new UnsupportedOperationException("Not implemented yet!"); +// Editor editor = this; +// int pos = editor.middleOf(hoverOver); +// assertTrue("Not found in editor: '"+hoverOver+"'", pos>=0); +// +// List targets = getLinkTargets(editor, pos); +// assertEquals(expecteds.length, targets.size()); +// for (int i = 0; i < expecteds.length; i++) { +// assertEquals(expecteds[i], JavaElementLabels.getElementLabel(targets.get(i), JavaElementLabels.DEFAULT_QUALIFIED | JavaElementLabels.M_PARAMETER_TYPES)); +// } + } + + /** + * Get a problem that covers the given text in the editor. Throws exception + * if no matching problem is found. + */ + public Diagnostic assertProblem(String coveredText) throws Exception { + Editor editor = this; + List problems = editor.reconcile(); + for (Diagnostic p : problems) { + String c = editor.getText(p.getRange()); + if (c.equals(coveredText)) { + return p; + } + } + fail("No problem found covering the text '"+coveredText+"' in: \n" + + problemSumary(editor, problems) + ); + return null; //unreachable but compiler doesn't know + } + + public CompletionItem assertFirstQuickfix(Diagnostic problem, String expectLabel) { + throw new UnsupportedOperationException("Not implemented yet!"); + } + + public void assertText(String expected) { + assertEquals(expected, getText()); + } + } diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/IType.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/IType.java new file mode 100644 index 000000000..6ed7f2d34 --- /dev/null +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/IType.java @@ -0,0 +1,9 @@ +package org.springframework.ide.vscode.testharness; + +/** + * Replaces eclipse JDT IType. Maybe we keep this maybe not, if keep, it should move to some + * other place, not stay here in the testharness. + */ +public interface IType { + +} diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/LanguageServerHarness.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/LanguageServerHarness.java index 39f31ea73..a705116fd 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/LanguageServerHarness.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/LanguageServerHarness.java @@ -1,5 +1,7 @@ package org.springframework.ide.vscode.testharness; +import static org.junit.Assert.assertEquals; + import java.io.File; import java.nio.charset.Charset; import java.nio.file.Files; @@ -252,4 +254,30 @@ public class LanguageServerHarness { return File.createTempFile("workingcopy", getFileExtension()).toURI().toString(); } + public void assertCompletion(String textBefore, String expectTextAfter) throws Exception { + Editor editor = newEditor(textBefore); + CompletionItem completion = editor.getFirstCompletion(); + editor.apply(completion); + assertEquals(expectTextAfter, editor.getText()); + } + + public void assertCompletions(String textBefore, String... expectTextAfter) throws Exception { + Editor editor = newEditor(textBefore); + StringBuilder expect = new StringBuilder(); + StringBuilder actual = new StringBuilder(); + for (String after : expectTextAfter) { + expect.append(after); + expect.append("\n-------------------\n"); + } + + List completions = editor.getCompletions(); + for (CompletionItem ci : completions) { + editor = newEditor(textBefore); + editor.apply(ci); + actual.append(editor.getText()); + actual.append("\n-------------------\n"); + } + assertEquals(expect.toString(), actual.toString()); + } + } diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/TestProject.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/TestProject.java new file mode 100644 index 000000000..1c06ceb61 --- /dev/null +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/testharness/TestProject.java @@ -0,0 +1,11 @@ +package org.springframework.ide.vscode.testharness; + +import java.nio.file.Path; + +public interface TestProject { + + Path getPath(); + + IType findType(String string); + +} diff --git a/vscode-extensions/commons/pom.xml b/vscode-extensions/commons/pom.xml index fcd31ef6e..e41b048b6 100644 --- a/vscode-extensions/commons/pom.xml +++ b/vscode-extensions/commons/pom.xml @@ -26,6 +26,7 @@ 2.5.0 2.10 0.3.0 + 3.0.2.RELEASE diff --git a/vscode-extensions/vscode-application-yaml/pom.xml b/vscode-extensions/vscode-application-yaml/pom.xml index 4cb086205..65011bbe7 100644 --- a/vscode-extensions/vscode-application-yaml/pom.xml +++ b/vscode-extensions/vscode-application-yaml/pom.xml @@ -44,7 +44,13 @@ org.springframework.ide.vscode commons-language-server - 0.0.1-SNAPSHOT + ${project.version} + + + + org.springframework.ide.vscode + application-properties-metadata + ${project.version} diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/AbstractPropsEditorTest.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/AbstractPropsEditorTest.java new file mode 100644 index 000000000..6500c1b29 --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/AbstractPropsEditorTest.java @@ -0,0 +1,182 @@ +package org.springframework.ide.vscode.yaml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Before; +import org.springframework.ide.vscode.boot.properties.metadata.SpringPropertyIndexProvider; +import org.springframework.ide.vscode.testharness.Editor; +import org.springframework.ide.vscode.testharness.LanguageServerHarness; +import org.springframework.ide.vscode.testharness.TestProject; +import org.springframework.ide.vscode.yaml.PropertyIndexHarness.ItemConfigurer; + +import io.typefox.lsapi.CompletionItem; + +public class AbstractPropsEditorTest { + + private PropertyIndexHarness md; + private LanguageServerHarness harness; + + public Editor newEditor(String contents) throws Exception { + return harness.newEditor(contents); + } + + @Before + public void setup() throws Exception { + md = new PropertyIndexHarness(); + harness = new LanguageServerHarness(YamlLanguageServer::new); + harness.intialize(null); + } + + public ItemConfigurer data(String id, String type, Object deflt, String description, String... sources) { + return md.data(id, type, deflt, description, sources); + } + + public void defaultTestData() { + md.defaultTestData(); + } + + public TestProject createPredefinedMavenProject(String string) { + notImplemented(); + return null; + } + + public void useProject(TestProject p) { + notImplemented(); //only tests that don't require project context / classpath work for now + } + + /** + * Simulates applying the first completion to a text buffer and checks the result. + */ + public void assertCompletion(String textBefore, String expectTextAfter) throws Exception { + harness.assertCompletion(textBefore, expectTextAfter); + } + + private void notImplemented() { + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Checks that completions contains a completion with a given display string and that that completion + * has a info hover that contains a given snippet of text. + */ + public void assertCompletionWithInfoHover(String editorText, String expectLabel, String expectInfoSnippet) throws Exception { + notImplemented(); + } + + public boolean isEmptyMetadata() { + return md.isEmpty(); + } + + /** + * Checks that applying completions to a given 'textBefore' editor content produces the + * expected results. + */ + public void assertCompletions(String textBefore, String... expectTextAfter) throws Exception { + harness.assertCompletions(textBefore, expectTextAfter); + } + + public void assertNoCompletions(String text) throws Exception { + assertCompletions(text /*NONE*/); + } + + /** + * Checks that completions contains a completion with a given display string (and check that + * it applies as expected). + */ + public void assertCompletionWithLabel(String textBefore, String expectLabel, String expectTextAfter) throws Exception { + Editor editor = newEditor(textBefore); + List completions = editor.getCompletions(); + CompletionItem completion = assertCompletionWithLabel(expectLabel, completions); + editor.apply(completion); + assertEquals(expectTextAfter, editor.getText()); + } + + private CompletionItem assertCompletionWithLabel(String expectLabel, List completions) { + StringBuilder found = new StringBuilder(); + for (CompletionItem c : completions) { + String actualLabel = c.getLabel(); + found.append(actualLabel+"\n"); + if (actualLabel.equals(expectLabel)) { + return c; + } + } + fail("No completion found with label '"+expectLabel+"' in:\n"+found); + return null; //unreachable, but compiler doesn't know that. + } + + public void assertCompletionCount(int expected, String editorText) throws Exception { + Editor editor = newEditor(editorText); + assertEquals(expected, editor.getCompletions().size()); + } + + public void assertCompletionsDisplayString(String editorText, String... completionsLabels) throws Exception { + Editor editor = newEditor(editorText); + List completions = editor.getCompletions(); + String[] actualLabels = new String[completions.size()]; + for (int i = 0; i < actualLabels.length; i++) { + actualLabels[i] = completions.get(i).getLabel(); + } + assertElements(actualLabels, completionsLabels); + } + + public SpringPropertyIndexProvider getIndexProvider() { + return md.getIndexProvider(); + } + + @SafeVarargs + public static void assertElements(T[] actual, T... expect) { + assertElements(Arrays.asList(actual), expect); + } + + @SafeVarargs + public static void assertElements(Collection actual, T... expect) { + Set expectedSet = new HashSet(Arrays.asList(expect)); + + for (T propVal : actual) { + if (!expectedSet.remove(propVal)) { + fail("Unexpected element: "+propVal); + } + } + + if (!expectedSet.isEmpty()) { + StringBuilder missing = new StringBuilder(); + for (T propVal : expectedSet) { + missing.append(propVal+"\n"); + } + fail("Missing elements: \n"+missing); + } + } + + public void assertStyledCompletions(String editorText, StyledStringMatcher... expectStyles) throws Exception { + Editor editor = newEditor(editorText); + List completions = editor.getCompletions(); + assertEquals("Wrong number of elements", expectStyles.length, completions.size()); + for (int i = 0; i < expectStyles.length; i++) { + CompletionItem completion = completions.get(i); + throw new UnsupportedOperationException("Suport for styled labels not implemented"); +// StyledString actualLabel = getStyledDisplayString(completion); +// expectStyles[i].match(actualLabel); + } + } + + + public void deprecate(String key, String replacedBy, String reason) { + md.deprecate(key, replacedBy, reason); + } + + public void keyHints(String id, String... hintValues) { + md.keyHints(id, hintValues); + } + + public void valueHints(String id, String... hintValues) { + md.valueHints(id, hintValues); + } + +} diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/ApplicationYamlEditorTest.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/ApplicationYamlEditorTest.java index 11c33e3ca..086bef0a4 100644 --- a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/ApplicationYamlEditorTest.java +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/ApplicationYamlEditorTest.java @@ -1,9 +1,32 @@ +/******************************************************************************* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.time.Duration; + +import org.junit.Ignore; import org.junit.Test; +import org.springframework.ide.vscode.boot.properties.metadata.CachingValueProvider; +import org.springframework.ide.vscode.boot.properties.metadata.PropertyInfo; import org.springframework.ide.vscode.testharness.Editor; import org.springframework.ide.vscode.testharness.LanguageServerHarness; -import org.springframework.ide.vscode.yaml.YamlLanguageServer; +import org.springframework.ide.vscode.testharness.TestProject; +import org.springframework.ide.vscode.util.StringUtil; + +import io.typefox.lsapi.CompletionItem; +import io.typefox.lsapi.Diagnostic; /** * This class is a placeholder where we will attempt to copy and port @@ -12,26 +35,15 @@ import org.springframework.ide.vscode.yaml.YamlLanguageServer; * * @author Kris De Volder */ -public class ApplicationYamlEditorTest { +public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { + + //////////////////////////////////////////////////////////////////////////////////////// - @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 " - ); - } - @Test public void linterRunsOnDocumentOpenAndChange() throws Exception { LanguageServerHarness harness = new LanguageServerHarness(YamlLanguageServer::new); harness.intialize(null); - Editor editor = harness.newEditor( + Editor editor = newEditor( "somemap: val\n"+ "- sequence" ); @@ -49,4 +61,3460 @@ public class ApplicationYamlEditorTest { "z|expected " ); } + + ///////////////////// ported tests from old STS code base //////////////////////////////////////////////// + + @Ignore @Test public void testHovers() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "spring:\n" + + " application:\n" + + " name: foofoo\n" + + " beyond:\n" + + " the-valid: range\n" + + " \n" + + "server:\n" + + " port: 8888" + ); + + editor.assertIsHoverRegion("spring"); + editor.assertIsHoverRegion("application"); + editor.assertIsHoverRegion("name"); + + editor.assertIsHoverRegion("server"); + editor.assertIsHoverRegion("port"); + + editor.assertHoverContains("name", "spring.application.name"); + editor.assertHoverContains("port", "server.port"); + editor.assertHoverContains("8888", "server.port"); // hover over value show info about corresponding key. Is this logical? + + editor.assertNoHover("beyond"); + editor.assertNoHover("the-valid"); + editor.assertNoHover("range"); + + //Note currently, these provide no hovers, but maybe (some of them) should if we index proprty sources and not just the + // properties themselves. + editor.assertNoHover("spring"); + editor.assertNoHover("application"); + editor.assertNoHover("server"); + + + //Test for the case where we can't produce an AST for editor text + editor = newEditor( + "- syntax\n" + + "error:\n" + ); + editor.assertNoHover("syntax"); + editor.assertNoHover("error"); + } + + @Ignore @Test public void testHoverInfoForEnumValueInMapKey() throws Exception { + Editor editor; + TestProject project = createPredefinedMavenProject("boot13"); + useProject(project); + + //This test will fail if source jars haven't been downloaded. + //Probably that is why it fails in CI build? + //Let's check: +// IType type = project.findType("com.fasterxml.jackson.databind.SerializationFeature"); +// System.out.println(">>> source for: "+type.getFullyQualifiedName()); +// System.out.println(downloadSources(type)); +// System.out.println("<<< source for: "+type.getFullyQualifiedName()); + + editor = newEditor( + "spring:\n" + + " jackson:\n" + + " serialization:\n" + + " indent-output: true" + ); + editor.assertIsHoverRegion("indent-output"); + editor.assertHoverContains("indent-output", "allows enabling (or disabling) indentation"); + + //Also try that it works when spelled in all upper-case + editor = newEditor( + "spring:\n" + + " jackson:\n" + + " serialization:\n" + + " INDENT_OUTPUT: true" + ); + editor.assertIsHoverRegion("INDENT_OUTPUT"); + editor.assertHoverContains("INDENT_OUTPUT", "allows enabling (or disabling) indentation"); + } + + @Ignore @Test public void testHoverInfoForEnumValueInMapKeyCompletion() throws Exception { + TestProject project = createPredefinedMavenProject("boot13"); + useProject(project); + + //This test will fail if source jars haven't been downloaded. + //Probably that is why it fails in CI build? + //Let's check: +// IType type = project.findType("com.fasterxml.jackson.databind.SerializationFeature"); +// System.out.println(">>> source for: "+type.getFullyQualifiedName()); +// System.out.println(downloadSources(type)); +// System.out.println("<<< source for: "+type.getFullyQualifiedName()); + + assertCompletionWithInfoHover( + "spring:\n" + + " jackson:\n" + + " serialization:\n" + + " ind<*>" + , // ========== + "indent-output : boolean" + , // ==> + "allows enabling (or disabling) indentation" + ); + } + + @Ignore @Test public void testHoverInfoForValueHintCompletion() throws Exception { + data("my.bonus", "java.lang.String", null, "Bonus type") + .valueHint("small", "A small bonus. For a little extra incentive.") + .valueHint("large", "An large bonus. For the ones who deserve it.") + .valueHint("exorbitant", "Truly outrageous. Who deserves a bonus like that?"); + + assertCompletionWithInfoHover( + "my:\n" + + " bonus: l<*>" + , // ========== + "large" + , // ==> + "For the ones who deserve it" + ); + } + + @Ignore @Test public void testHoverInfoForValueHint() throws Exception { + data("my.bonus", "java.lang.String", null, "Bonus type") + .valueHint("small", "A small bonus. For a little extra incentive.") + .valueHint("large", "An large bonus. For the ones who deserve it.") + .valueHint("exorbitant", "Truly outrageous. Who deserves a bonus like that?"); + + Editor editor = newEditor( + "#comment here\n" + + "my:\n" + + " bonus: large\n" + ); + + editor.assertIsHoverRegion("large"); + editor.assertHoverContains("large", "For the ones who deserve it"); + } + + + @Ignore @Test public void testUserDefinedHoversandLinkTargets() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("foo.link-tester", "demo.LinkTestSubject", null, "for testing different Pojo link cases"); + Editor editor = newEditor( + "#A comment at the start\n" + + "foo:\n" + + " data:\n" + + " wavelen: 666\n" + + " name: foo\n" + + " next: green\n" + + " link-tester:\n" + + " has-it-all: nice\n" + + " strange: weird\n" + + " getter-only: getme\n" + ); + + editor.assertHoverContains("data", "Pojo"); // description from json metadata + editor.assertHoverContains("wavelen", "JavaDoc from field"); // javadoc from field + editor.assertHoverContains("name", "Set the name"); // javadoc from setter + editor.assertHoverContains("next", "Get the next"); // javadoc from getter + + editor.assertLinkTargets("data", "demo.FooProperties.setdata(ColorData)"); + editor.assertLinkTargets("wavelen", "demo.ColorData.setWavelen(double)"); + + } + + @Ignore @Test public void testHyperlinkTargets() throws Exception { + System.out.println(">>> testHyperlinkTargets"); + TestProject p = createPredefinedMavenProject("demo"); + useProject(p); + + Editor editor = newEditor( + "server:\n"+ + " port: 888\n" + + "spring:\n" + + " datasource:\n" + + " login-timeout: 1000\n" + + "flyway:\n" + + " init-sqls: a,b,c\n" + ); + + editor.assertLinkTargets("port", + "org.springframework.boot.autoconfigure.web.ServerProperties.setPort(Integer)" + ); + editor.assertLinkTargets("login-", + "org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.hikariDataSource()", + "org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.tomcatDataSource()", + "org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.dbcpDataSource()" + ); + editor.assertLinkTargets("init-sql", + "org.springframework.boot.autoconfigure.flyway.FlywayProperties.setInitSqls(List)"); + System.out.println("<<< testHyperlinkTargets"); + } + + @Ignore @Test public void testReconcile() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server:\n" + + " port: \n" + + " extracrap: 8080\n" + + "logging:\n"+ + " level:\n" + + " com.acme: INFO\n" + + " snuggem: what?\n" + + "bogus:\n" + + " no: \n" + + " good: true\n" + ); + editor.assertProblems( + "extracrap: 8080|Expecting a 'int' but got a 'Mapping' node", + "snuggem|Unknown property", + "bogus|Unknown property" + ); + } + + @Ignore @Test public void test_STS_4140_StringArrayReconciling() throws Exception { + defaultTestData(); + + Editor editor; + + //Case 0: very focussed test for easy debugging + editor = newEditor( + "flyway:\n" + + " schemas: [MEMBER_VERSION, MEMBER]" + ); + editor.assertProblems(/*NONE*/); + + //Case 1: String[] as a flow sequence + editor = newEditor( + "something-bad: wrong\n"+ + "flyway:\n" + + " url: jdbc:h2:file:~/localdb;IGNORECASE=TRUE;mv_store=false\n" + + " user: admin\n" + + " password: admin\n" + + " schemas: [MEMBER_VERSION, MEMBER]" + ); + editor.assertProblems( + "something-bad|Unknown property" + //No other problems should be reported + ); + + //Case 1: String[] as a block sequence + editor = newEditor( + "something-bad: wrong\n"+ + "flyway:\n" + + " url: jdbc:h2:file:~/localdb;IGNORECASE=TRUE;mv_store=false\n" + + " user: admin\n" + + " password: admin\n" + + " schemas:\n" + + " - MEMBER_VERSION\n" + + " - MEMBER" + ); + editor.assertProblems( + "something-bad|Unknown property" + //No other problems should be reported + ); + } + + @Ignore @Test public void test_STS_4140_StringArrayCompletion() throws Exception { + defaultTestData(); + + assertCompletion( + "#some comment\n" + + "flyway:\n" + + " sch<*>\n" + , //=> + "#some comment\n" + + "flyway:\n" + + " schemas:\n" + + " - <*>\n" + ); + } + + @Ignore @Test public void testReconcileIntegerScalar() throws Exception { + data("server.port", "java.lang.Integer", null, "Port of server"); + data("server.threads", "java.lang.Integer", null, "Number of threads for server threadpool"); + Editor editor = newEditor( + "server:\n" + + " port: \n" + + " 8888\n" + + " threads:\n" + + " not-a-number\n" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'" + ); + } + + @Ignore @Test public void testReconcileExpectMapping() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server:\n" + + " - a\n" + + " - b\n" + ); + editor.assertProblems( + "- a\n - b|Expecting a 'Mapping' node but got a 'Sequence' node" + ); + } + + @Ignore @Test public void testReconcileExpectScalar() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server:\n" + + " ? - a\n" + + " - b\n" + + " : c" + ); + editor.assertProblems( + "- a\n - b|Expecting a 'Scalar' node but got a 'Sequence' node" + ); + } + + @Ignore @Test public void testReconcileCamelCaseBasic() throws Exception { + Editor editor; + data("something.with-many-parts", "java.lang.Integer", "For testing tolerance of camelCase", null); + data("something.with-parts.and-more", "java.lang.Integer", "For testing tolerance of camelCase", null); + + //Nothing special... not using camel case + editor = newEditor( + "#Comment for good measure\n" + + "something:\n" + + " with-many-parts: not-a-number" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'" + ); + + //Now check that reconcile also tolerates camel case and reports no error for it + editor = newEditor( + "#Comment for good measure\n" + + "something:\n" + + " withManyParts: 123" + ); + editor.assertProblems(/*NONE*/); + + //Now check that reconciler traverses camelCase and reports errors assigning to camelCase names + editor = newEditor( + "#Comment for good measure\n" + + "something:\n" + + " withManyParts: not-a-number" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'" + ); + + //Now check also that camelcase tolerance works if its not in the end of the path + editor = newEditor( + "#Comment for good measure\n" + + "something:\n" + + " withParts:\n" + + " andMore: not-a-number\n" + + " bad: wrong\n" + + " something-bad: also wrong\n" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'", + "bad|Unknown property", + "something-bad|Unknown property" + ); + } + + @Ignore @Test public void testContentAssistCamelCaseBasic() throws Exception { + data("something.with-many-parts", "java.lang.Integer", "For testing tolerance of camelCase", null); + data("something.with-parts.and-more", "java.lang.Integer", "For testing tolerance of camelCase", null); + + assertCompletion( + "something:\n" + + " withParts:\n" + + " <*>", + // => + "something:\n" + + " withParts:\n" + + " and-more: <*>" + ); + } + + @Ignore @Test public void testReconcileCamelCaseBeanProp() throws Exception { + Editor editor; + + TestProject p = createPredefinedMavenProject("demo"); + useProject(p); + + data("demo.bean", "demo.CamelCaser", "For testing tolerance of camelCase", null); + + //Nothing special... not using camel case + editor = newEditor( + "#Comment for good measure\n" + + "demo:\n" + + " bean:\n"+ + " the-answer-to-everything: not-a-number\n" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'" + ); + + //Now check that reconcile also tolerates camel case and reports no error for it + editor = newEditor( + "#Comment for good measure\n" + + "demo:\n" + + " bean:\n"+ + " theAnswerToEverything: 42\n" + ); + editor.assertProblems(/*NONE*/); + + //Now check that reconciler traverses camelCase and reports errors assigning to camelCase names + editor = newEditor( + "#Comment for good measure\n" + + "demo:\n" + + " bean:\n"+ + " theAnswerToEverything: not-a-number\n" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'" + ); + + //Now check also that camelcase tolerance works if its not in the end of the path + editor = newEditor( + "#Comment for good measure\n" + + "demo:\n" + + " bean:\n"+ + " theAnswerToEverything: not-a-number\n"+ + " theLeft:\n"+ + " bad: wrong\n"+ + " theAnswerToEverything: not-this\n" + ); + editor.assertProblems( + "not-a-number|Expecting a 'int'", + "bad|Unknown property", + "not-this|Expecting a 'int'" + ); + } + + @Ignore @Test public void testContentAssistCamelCaseBeanProp() throws Exception { + TestProject p = createPredefinedMavenProject("demo"); + useProject(p); + + data("demo.bean", "demo.CamelCaser", "For testing tolerance of camelCase", null); + + assertCompletion( + "demo:\n" + + " bean:\n" + + " theLeft:\n" + + " answer<*>\n", + // => + "demo:\n" + + " bean:\n" + + " theLeft:\n" + + " the-answer-to-everything: <*>\n" + ); + } + + @Ignore @Test public void testContentAssistCamelCaseInsertInExistingContext() throws Exception { + data("demo-bean.first", "java.lang.String", "For testing tolerance of camelCase", null); + data("demo-bean.second", "java.lang.String", "For testing tolerance of camelCase", null); + + //Confirm first it works correctly without camelizing complications +// assertCompletion( +// "demo-bean:\n" + +// " first: numero uno\n" + +// "something-else: separator\n"+ +// "sec<*>", +// // => +// "demo-bean:\n" + +// " first: numero uno\n" + +// " second: <*>\n" + +// "something-else: separator\n" +// ); + + //Now with camels + assertCompletion( + "demoBean:\n" + + " first: numero uno\n" + + "something-else: separator\n"+ + "sec<*>", + // => + "demoBean:\n" + + " first: numero uno\n" + + " second: <*>\n" + + "something-else: separator\n" + ); + } + + + @Ignore @Test public void testReconcileBeanPropName() throws Exception { + TestProject p = createPredefinedMavenProject("demo-list-of-pojo"); + useProject(p); + assertNotNull(p.findType("demo.Foo")); + data("some-foo", "demo.Foo", null, "some Foo pojo property"); + Editor editor = newEditor( + "some-foo:\n" + + " name: Good\n" + + " bogus: Bad\n" + + " ? - a\n"+ + " - b\n"+ + " : Weird\n" + ); + editor.assertProblems( + "bogus|Unknown property 'bogus' for type 'demo.Foo'", + "- a\n - b|Expecting a bean-property name for object of type 'demo.Foo' " + + "but got a 'Sequence' node" + ); + } + + @Ignore @Test public void testIgnoreSpringExpression() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server:\n" + + " port: ${random.int}\n" + //should not be an error + " bad: wrong\n" + ); + editor.assertProblems( + "bad|Unknown property" + ); + } + + @Ignore @Test public void testReconcilePojoArray() throws Exception { + TestProject p = createPredefinedMavenProject("demo-list-of-pojo"); + useProject(p); + assertNotNull(p.findType("demo.Foo")); + + { + Editor editor = newEditor( + "token-bad-guy: problem\n"+ + "volder:\n" + + " foo:\n" + + " list:\n"+ + " - name: Kris\n" + + " description: Kris\n" + + " roles:\n" + + " - Developer\n" + + " - Admin\n" + + " bogus: Bad\n" + ); + + editor.assertProblems( + "token-bad-guy|Unknown property", + //'name' is ok + //'description' is ok + "bogus|Unknown property 'bogus' for type 'demo.Foo'" + ); + } + + { //Pojo array can also be entered as a map with integer keys + + Editor editor = newEditor( + "token-bad-guy: problem\n"+ + "volder:\n" + + " foo:\n" + + " list:\n"+ + " 0:\n"+ + " name: Kris\n" + + " description: Kris\n" + + " roles:\n" + + " 0: Developer\n" + + " one: Admin\n" + + " bogus: Bad\n" + ); + + editor.assertProblems( + "token-bad-guy|Unknown property", + "one|Expecting a 'int' but got 'one'", + "bogus|Unknown property 'bogus' for type 'demo.Foo'" + ); + + } + + } + + @Ignore @Test public void testReconcileSequenceGotAtomicType() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "liquibase:\n" + + " enabled:\n" + + " - element\n" + ); + editor.assertProblems( + "- element|Expecting a 'boolean' but got a 'Sequence' node" + ); + } + + @Ignore @Test public void testReconcileSequenceGotMapType() throws Exception { + data("the-map", "java.util.Map", null, "Nice mappy"); + Editor editor = newEditor( + "the-map:\n" + + " - a\n" + + " - b\n" + ); + editor.assertProblems( + "- a\n - b|Expecting a 'Map' but got a 'Sequence' node" + ); + } + + @Ignore @Test public void testEnumPropertyReconciling() throws Exception { + TestProject p = createPredefinedMavenProject("demo-enum"); + useProject(p); + assertNotNull(p.findType("demo.Color")); + + data("foo.color", "demo.Color", null, "A foonky colour"); + Editor editor = newEditor( + "foo:\n"+ + " color: BLUE\n" + + "---\n" + + "foo:\n" + + " color: RED\n" + + "---\n" + + "foo:\n" + + " color: GREEN\n" + + "---\n" + + "foo:\n" + + " color:\n" + + " bad: BLUE\n" + + "---\n" + + "foo:\n" + + " color: Bogus\n" + ); + + editor.assertProblems( + "bad: BLUE|Expecting a 'demo.Color[RED, GREEN, BLUE]' but got a 'Mapping' node", + "Bogus|Expecting a 'demo.Color[RED, GREEN, BLUE]' but got 'Bogus'" + ); + } + + @Test public void testReconcileSkipIfNoMetadata() throws Exception { + Editor editor = newEditor( + "foo:\n"+ + " color: BLUE\n" + + " color: RED\n" + //technically not allowed to bind same key twice but we don' check this + " color: GREEN\n" + + " color:\n" + + " bad: BLUE\n" + + " color: Bogus\n" + ); + assertTrue(isEmptyMetadata()); + editor.assertProblems(/*NONE*/); + } + + @Test public void testReconcileCatchesParseError() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "somemap: val\n"+ + "- sequence" + ); + editor.assertProblems( + "-|expected " + ); + } + + @Test public void testReconcileCatchesScannerError() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "somemap: \"quotes not closed\n" + ); + editor.assertProblems( + "|unexpected end of stream" + ); + } + + @Ignore @Test public void testContentAssistSimple() throws Exception { + defaultTestData(); + assertCompletion("port<*>", + "server:\n"+ + " port: <*>"); + assertCompletion( + "#A comment\n" + + "port<*>", + "#A comment\n" + + "server:\n"+ + " port: <*>"); + assertCompletions( + "server:\n" + + " nonsense<*>\n" + //=> nothing + ); + } + + @Ignore @Test public void testContentAssistNullContext() throws Exception { + defaultTestData(); + assertCompletions( + "#A comment\n" + + "foo:\n" + + " data:\n" + + " bogus:\n" + + " <*>" + // => nothing + ); + } + + @Ignore @Test public void testContentAssistNested() throws Exception { + data("server.port", "java.lang.Integer", null, "Server http port"); + data("server.address", "java.lang.String", "localhost", "Server host address"); + + assertCompletion( + "server:\n"+ + " port: 8888\n" + + " <*>" + , + "server:\n"+ + " port: 8888\n" + + " address: <*>" + ); + + assertCompletion( + "server:\n"+ + " <*>" + , + "server:\n"+ + " address: <*>" + ); + + assertCompletion( + "server:\n"+ + " a<*>" + , + "server:\n"+ + " address: <*>" + ); + + assertCompletion( + "server:\n"+ + " <*>\n" + + " port: 8888" + , + "server:\n"+ + " address: <*>\n" + + " port: 8888" + ); + + assertCompletion( + "server:\n"+ + " a<*>\n" + + " port: 8888" + , + "server:\n"+ + " address: <*>\n" + + " port: 8888" + ); + + } + + @Ignore @Test public void testContentAssistNestedSameLine() throws Exception { + data("server.port", "java.lang.Integer", null, "Server http port"); + + assertCompletion( + "server: <*>" + , + "server: \n" + + " port: <*>" + ); + + assertCompletion( + "#something before this stuff\n" + + "server: <*>" + , + "#something before this stuff\n" + + "server: \n" + + " port: <*>" + ); + } + + @Ignore @Test public void testContentAssistInsertCompletionElsewhere() throws Exception { + defaultTestData(); + + assertCompletion( + "server:\n" + + " port: 8888\n" + + " address: \n" + + " servlet-path: \n" + + "spring:\n" + + " activemq:\n" + + "something-else: great\n" + + "aopauto<*>" + , + "server:\n" + + " port: 8888\n" + + " address: \n" + + " servlet-path: \n" + + "spring:\n" + + " activemq:\n" + + " aop:\n" + + " auto: <*>\n" + + "something-else: great\n" + ); + + assertCompletion( + "server:\n"+ + " address: localhost\n"+ + "something: nice\n"+ + "po<*>" + , + "server:\n"+ + " address: localhost\n"+ + " port: <*>\n" + + "something: nice\n" + ); + } + + @Ignore @Test public void testContentAssistInsertCompletionElsewhereInEmptyParent() throws Exception { + data("server.port", "java.lang.Integer", null, "Server http port"); + data("server.address", "String", "localhost", "Server host address"); + + assertCompletion( + "#comment\n" + + "server:\n" + + "something:\n" + + " more\n" + + "po<*>" + , + "#comment\n" + + "server:\n" + + " port: <*>\n" + + "something:\n" + + " more\n" + ); + } + + @Ignore @Test public void testContentAssistInetAddress() throws Exception { + //Test that InetAddress is treated as atomic w.r.t indentation + + defaultTestData(); + assertCompletion( + "#set address of server\n" + + "servadd<*>" + , //=> + "#set address of server\n" + + "server:\n"+ + " address: <*>" + ); + + assertCompletion( + "#set address of server\n" + + "server:\n"+ + " port: 888\n" + + "more: stuff\n" + + "servadd<*>" + , //=> + "#set address of server\n" + + "server:\n"+ + " port: 888\n" + + " address: <*>\n" + + "more: stuff\n" + ); + + } + + @Ignore @Test public void testContentAssistInsertCompletionElsewhereThatAlreadyExists() throws Exception { + data("server.port", "java.lang.Integer", null, "Server http port"); + data("server.address", "String", "localhost", "Server host address"); + + //inserting something that already exists should just move the cursor to existing node + + assertCompletion( + "server:\n"+ + " port:\n" + + " 8888\n"+ + " address: localhost\n"+ + "something: nice\n"+ + "po<*>" + , + "server:\n"+ + " port:\n"+ + " <*>8888\n" + + " address: localhost\n"+ + "something: nice\n" + ); + + assertCompletion( + "server:\n"+ + " port: 8888\n" + + " address: localhost\n"+ + "something: nice\n"+ + "po<*>" + , + "server:\n"+ + " port: <*>8888\n" + + " address: localhost\n"+ + "something: nice\n" + ); + + assertCompletion( + "server:\n"+ + " port:\n"+ + " address: localhost\n"+ + "something: nice\n"+ + "po<*>" + , + "server:\n"+ + " port:<*>\n" + + " address: localhost\n"+ + "something: nice\n" + ); + } + + + @Ignore @Test public void testContentAssistPropertyWithMapType() throws Exception { + data("foo.mapping", "java.util.Map", null, "Nice little map"); + + //Try in-place completion + assertCompletion( + "map<*>" + , + "foo:\n"+ + " mapping:\n"+ + " <*>" + ); + + //Try 'elswhere' completion + assertCompletion( + "foo:\n" + + " something:\n" + + "more: stuff\n" + + "map<*>" + , + "foo:\n" + + " something:\n" + + " mapping:\n" + + " <*>\n" + + "more: stuff\n" + ); + } + + @Ignore @Test public void testContentAssistPropertyWithArrayType() throws Exception { + data("foo.list", "java.util.List", null, "Nice little list"); + + //Try in-place completion + assertCompletion( + "lis<*>" + , + "foo:\n"+ + " list:\n"+ + " - <*>" + ); + + //Try 'elsewhere' completion + assertCompletion( + "foo:\n" + + " something:\n" + + "more: stuff\n" + + "lis<*>" + , + "foo:\n" + + " something:\n" + + " list:\n" + + " - <*>\n" + + "more: stuff\n" + ); + } + + @Ignore @Test public void testContentAssistPropertyWithPojoType() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + //Try in-place completion + assertCompletion( + "foo.d<*>" + , + "foo:\n" + + " data:\n" + + " <*>" + ); + + //Try 'elsewhere' completion + assertCompletion( + "foo:\n" + + " something:\n" + + "more: stuff\n" + + "foo.d<*>" + , + "foo:\n" + + " something:\n" + + " data:\n" + + " <*>\n" + + "more: stuff\n" + ); + } + + @Ignore @Test public void testContentAssistPropertyWithEnumType() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + //Try in-place completion + assertCompletion( + "foo.co<*>" + , + "foo:\n" + + " color: <*>" + ); + + //Try 'elsewhere' completion + assertCompletion( + "foo:\n" + + " something:\n" + + "more: stuff\n" + + "foo.co<*>" + , + "foo:\n" + + " something:\n" + + " color: <*>\n" + + "more: stuff\n" + ); + } + + @Ignore @Test public void testCompletionForExistingGlobalPropertiesAreDemoted() throws Exception { + data("foo.bar", "java.lang.String", null, null); + data("foo.buttar", "java.lang.String", null, null); + data("foo.baracks", "java.lang.String", null, null); + data("foo.zamfir", "java.lang.String", null, null); + assertCompletions( + "foo:\n" + + " bar: nice\n" + + " baracks: full\n" + + "something:\n" + + " in: between\n"+ + "bar<*>", + //=> + // buttar + "foo:\n" + + " bar: nice\n" + + " baracks: full\n" + + " buttar: <*>\n" + + "something:\n" + + " in: between", + // bar (already existed so becomes navigation) + "foo:\n" + + " bar: <*>nice\n" + + " baracks: full\n" + + "something:\n" + + " in: between", + // baracks (already existed so becomes navigation) + "foo:\n" + + " bar: nice\n" + + " baracks: <*>full\n" + + "something:\n" + + " in: between" + ); + } + + @Ignore @Test public void testCompletionForExistingBeanPropertiesAreDemoted() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + assertCompletions( + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " <*>", + ///// non-existing /// + //color-children + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " color-children:\n"+ + " <*>", + //funky + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " funky: <*>", + //mapped-children + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " mapped-children:\n"+ + " <*>", + //nested + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " nested:\n"+ + " <*>", + //next + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " next: <*>", + //tags + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " tags:\n" + + " - <*>", + //wavelen + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: foo\n" + + " wavelen: <*>", + ///// existing //// + //children + "foo:\n" + + " data:\n" + + " children:\n" + + " <*>-\n" + + " name: foo", + //name + "foo:\n" + + " data:\n" + + " children:\n" + + " -\n" + + " name: <*>foo" + ); + } + + @Ignore @Test public void testNoCompletionsInsideComments() throws Exception { + defaultTestData(); + + //Ensure this test is not trivially passing because of missing test data + assertCompletion( + "po<*>" + , + "server:\n"+ + " port: <*>" + ); + + assertNoCompletions( + "#po<*>" + ); + } + + @Ignore @Test public void testCompletionsFromDeeplyNestedNode() throws Exception { + String[] names = {"foo", "nested", "bar"}; + int levels = 4; + generateNestedProperties(levels, names, ""); + + assertCompletionCount(81, // 3^4 + "<*>" + ); + + assertCompletionCount(27, // 3^3 + "#comment\n" + + "foo:\n" + + " <*>" + ); + + assertCompletionCount( 9, // 3^2 + "#comment\n" + + "foo:\n" + + " bar: <*>" + ); + + assertCompletionCount( 3, + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + ); + + assertCompletionCount( 9, + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + ); + + assertCompletionCount(27, + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + ); + + assertCompletionCount(81, + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + "<*>" + ); + + + assertCompletion( + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + , + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " bar: <*>" + ); + + assertCompletion( + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + , + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " bar:\n" + + " bar: <*>" + ); + + assertCompletion( + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " <*>" + , + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + " bar:\n" + + " bar: <*>\n" + + " " + ); + + assertCompletion( + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + "<*>" + , + "#comment\n" + + "foo:\n" + + " bar:\n"+ + " nested:\n" + + "bar:\n" + + " bar:\n" + + " bar:\n" + + " bar: <*>" + ); + } + + @Ignore @Test public void testInsertCompletionIntoDeeplyNestedNode() throws Exception { + String[] names = {"foo", "nested", "bar"}; + int levels = 4; + generateNestedProperties(levels, names, ""); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "bar.foo.nested.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "bar:\n" + + " foo:\n" + + " nested:\n" + + " bar: <*>" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "other:\n" + + "foo.foo.nested.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " foo:\n" + + " nested:\n" + + " bar: <*>\n" + + "other:\n" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "foo.foo.nested.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " foo:\n" + + " nested:\n" + + " bar: <*>" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "other:\n" + + "foo.nested.nested.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " nested:\n" + + " bar: <*>\n"+ + "other:\n" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "foo.nested.nested.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " nested:\n" + + " bar: <*>\n" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "other:\n" + + "foo.nested.bar.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " bar: <*>\n" + + "other:\n" + ); + + assertCompletion( + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + "foo.nested.bar.b<*>" + , + "foo:\n" + + " nested:\n" + + " bar:\n" + + " foo:\n" + + " bar: <*>\n" + ); + + } + + @Ignore @Test public void testBooleanValueCompletion() throws Exception { + defaultTestData(); + assertCompletions( + "liquibase:\n" + + " enabled: <*>", + "liquibase:\n" + + " enabled: false<*>", + "liquibase:\n" + + " enabled: true<*>" + ); + + assertCompletions( + "liquibase:\n" + + " enabled:\n" + + " <*>", + "liquibase:\n" + + " enabled:\n" + + " false<*>", + "liquibase:\n" + + " enabled:\n" + + " true<*>" + ); + + assertCompletions( + "liquibase:\n" + + " enabled: f<*>\n", + "liquibase:\n" + + " enabled: false<*>\n" + ); + + assertCompletions( + "liquibase:\n" + + " enabled: t<*>\n", + "liquibase:\n" + + " enabled: true<*>\n" + ); + + assertCompletions( + "liquibase:\n" + + " enabled:\n" + + " f<*>\n", + "liquibase:\n" + + " enabled:\n"+ + " false<*>\n" + ); + + assertCompletions( + "liquibase:\n" + + " enabled:\n" + + " t<*>\n", + "liquibase:\n" + + " enabled:\n"+ + " true<*>\n" + ); + + // booleans can also be completed in upper case? + assertCompletions( + "liquibase:\n" + + " enabled:\n" + + " T<*>\n", + "liquibase:\n" + + " enabled:\n" + + " TRUE<*>\n" + ); + + assertCompletions( + "liquibase:\n" + + " enabled:\n" + + " F<*>\n", + "liquibase:\n" + + " enabled:\n" + + " FALSE<*>\n" + ); + + //Dont suggest completion for something that's already complete. Causes odd + // and annoying behavior, like a completion popup after you stopped typing 'true' + assertNoCompletions( + "liquibase:\n" + + " enabled:\n" + + " true<*>" + ); + + //one more... for special char like '-' in the name + + assertCompletions( + "liquibase:\n" + + " check-change-log-location: t<*>", + "liquibase:\n" + + " check-change-log-location: true<*>" + ); + + assertCompletions( + "liquibase:\n" + + " enabled:<*>", + "liquibase:\n" + + " enabled: false<*>", + "liquibase:\n" + + " enabled: true<*>" + ); + + + } + + @Ignore @Test public void testEnumValueCompletion() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("foo.color", "demo.Color", null, "A foonky colour"); + + assertCompletion("foo.c<*>", + "foo:\n" + + " color: <*>" //Should complete on same line because enums are 'simple' values. + ); + + assertCompletion("foo:\n color: R<*>", "foo:\n color: RED<*>"); + assertCompletion("foo:\n color: G<*>", "foo:\n color: GREEN<*>"); + assertCompletion("foo:\n color: B<*>", "foo:\n color: BLUE<*>"); + + assertCompletion("foo:\n color: r<*>", "foo:\n color: red<*>"); + assertCompletion("foo:\n color: g<*>", "foo:\n color: green<*>"); + assertCompletion("foo:\n color: b<*>", "foo:\n color: blue<*>"); + + assertCompletionsDisplayString("foo:\n color: <*>", + "red", "green", "blue" + ); + } + + @Ignore @Test public void testEnumMapValueCompletion() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + assertCompletions( + "foo:\n" + + " nam<*>", + //==> + "foo:\n" + + " name-colors:\n"+ + " <*>", + // or + "foo:\n" + + " color-names:\n"+ + " <*>" + ); + assertCompletionsDisplayString( + "foo:\n"+ + " name-colors:\n" + + " something: <*>", + //=> + "red", "green", "blue" + ); + assertCompletions( + "foo:\n"+ + " name-colors:\n" + + " something: G<*>", + // => + "foo:\n"+ + " name-colors:\n" + + " something: GREEN<*>" + ); + } + + @Ignore @Test public void testEnumMapValueReconciling() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("foo.name-colors", "java.util.Map", null, "Map with colors in its values"); + + Editor editor; + + editor = newEditor( + "foo:\n"+ + " name-colors:\n" + + " jacket: BLUE\n" + + " hat: RED\n" + + " pants: GREEN\n" + + " wrong: NOT_A_COLOR\n" + ); + editor.assertProblems( + "NOT_A_COLOR|Color" + ); + + //lowercase enums should work too + editor = newEditor( + "foo:\n"+ + " name-colors:\n" + + " jacket: blue\n" + + " hat: red\n" + + " pants: green\n" + + " wrong: NOT_A_COLOR\n" + ); + editor.assertProblems( + "NOT_A_COLOR|Color" + ); + } + + @Ignore @Test public void testEnumMapKeyCompletion() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + data("foo.color-names", "java.util.Map", null, "Map with colors in its keys"); + data("foo.color-data", "java.util.Map", null, "Map with colors in its keys, and pojo in values"); + + //Map Enum -> String: + assertCompletions("foo:\n colnam<*>", + "foo:\n" + + " color-names:\n" + + " <*>"); + assertCompletions( + "foo:\n" + + " color-names:\n" + + " <*>", + //=> + "foo:\n" + + " color-names:\n" + + " blue: <*>", + "foo:\n" + + " color-names:\n" + + " green: <*>", + "foo:\n" + + " color-names:\n" + + " red: <*>" + ); + + assertCompletionsDisplayString( + "foo:\n" + + " color-names:\n" + + " <*>", + //=> + "blue : String", "green : String", "red : String" + ); + + assertCompletions( + "foo:\n" + + " color-names:\n" + + " B<*>", + "foo:\n" + + " color-names:\n" + + " BLUE: <*>" + ); + + //Map Enum -> Pojo: + assertCompletions("foo.coldat<*>", + "foo:\n" + + " color-data:\n" + + " <*>"); + assertCompletions( + "foo:\n" + + " color-data:\n" + + " <*>", + // => + "foo:\n" + + " color-data:\n" + + " blue:\n" + + " <*>", + "foo:\n" + + " color-data:\n" + + " green:\n" + + " <*>", + "foo:\n" + + " color-data:\n" + + " red:\n" + + " <*>" + ); + assertCompletions( + "foo:\n" + + " color-data:\n" + + " B<*>", + //=> + "foo:\n" + + " color-data:\n" + + " BLUE:\n" + + " <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " b<*>", + //=> + "foo:\n" + + " color-data:\n" + + " blue:\n" + + " <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data: b<*>", + //=> + "foo:\n" + + " color-data: \n" + + " blue:\n" + + " <*>" + ); + + assertCompletionsDisplayString( + "foo:\n" + + " color-data:\n" + + " <*>", + "red : demo.ColorData", "green : demo.ColorData", "blue : demo.ColorData" + ); + + assertCompletionsDisplayString( + "foo:\n" + + " color-data: <*>\n", + "red : demo.ColorData", "green : demo.ColorData", "blue : demo.ColorData" + ); + + } + + @Ignore @Test public void testPojoReconciling() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + Editor editor = newEditor( + "foo:\n" + + " data:\n" + + " bogus: Something\n" + + " wavelen: 3.0\n" + + " wavelen: not a double\n" + + " wavelen:\n"+ + " more: 3.0\n"+ + " wavelen:\n" + + " - 3.0\n" + ); + editor.assertProblems( + "bogus|Unknown property", + "wavelen|Duplicate", + "wavelen|Duplicate", + "not a double|'double'", + "wavelen|Duplicate", + "more: 3.0|Expecting a 'double' but got a 'Mapping' node", + "wavelen|Duplicate", + "- 3.0|Expecting a 'double' but got a 'Sequence' node" + ); + } + + + @Ignore @Test public void testListOfAtomicCompletions() throws Exception { + data("foo.slist", "java.util.List", null, "list of strings"); + data("foo.ulist", "java.util.List", null, "list of strings"); + data("foo.dlist", "java.util.List", null, "list of doubles"); + assertCompletions("foo:\n u<*>", + "foo:\n" + + " ulist:\n" + + " - <*>"); + assertCompletions("foo:\n d<*>", + "foo:\n" + + " dlist:\n" + + " - <*>"); + assertCompletions("foo:\n sl<*>", + "foo:\n"+ + " slist:\n" + + " - <*>"); + } + + @Ignore @Test public void testEnumsInLowerCaseReconciling() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); + + Editor editor = newEditor( + "simple:\n" + + " pants:\n"+ + " size: NOT_A_SIZE\n"+ + " size: EXTRA_SMALL\n"+ + " size: extra-small\n"+ + " size: small\n"+ + " size: SMALL\n" + ); + editor.assertProblems( + "size|Duplicate", + "NOT_A_SIZE|ClothingSize", + "size|Duplicate", + "size|Duplicate", + "size|Duplicate", + "size|Duplicate" + ); + + editor = newEditor( + "foo:\n" + + " color-names:\n"+ + " red: Rood\n"+ + " green: Groen\n"+ + " blue: Blauw\n" + + " not-a-color: Wrong\n" + + " blue.bad: Blauw\n" + + " blue:\n" + + " bad: Blauw" + ); + editor.assertProblems( + "blue|Duplicate", + "not-a-color|Color", + "blue.bad|Color", + "blue|Duplicate", + "bad: Blauw|Expecting a 'String' but got a 'Mapping'" + ); + + editor = newEditor( + "foo:\n" + + " color-data:\n"+ + " red:\n" + + " next: green\n" + + " next: not a color\n" + + " bogus: green\n" + + " name: Rood\n" + ); + editor.assertProblems( + "next|Duplicate", + "next|Duplicate", + "not a color|Color", + "bogus|Unknown property" + ); + } + + @Ignore @Test public void testEnumsInLowerCaseContentAssist() throws Exception { + TestProject p = createPredefinedMavenProject("demo-enum"); + useProject(p); + assertNotNull(p.findType("demo.ClothingSize")); + + data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); + + assertCompletions( + "simple:\n" + + " pants:\n"+ + " size: S<*>", + "simple:\n" + + " pants:\n"+ + " size: SMALL<*>", + "simple:\n" + + " pants:\n"+ + " size: EXTRA_SMALL<*>" + ); + + assertCompletions( + "simple:\n" + + " pants:\n"+ + " size: s<*>", + "simple:\n" + + " pants:\n"+ + " size: small<*>", + "simple:\n" + + " pants:\n"+ + " size: extra-small<*>" + ); + + assertCompletions( + "simple:\n" + + " pants:\n"+ + " size: ex<*>", + // => + "simple:\n" + + " pants:\n"+ + " size: extra-large<*>", + // or + "simple:\n" + + " pants:\n"+ + " size: extra-small<*>" + ); + + assertCompletions( + "simple:\n" + + " pants:\n"+ + " size: EX<*>", + // => + "simple:\n" + + " pants:\n"+ + " size: EXTRA_LARGE<*>", + // or + "simple:\n" + + " pants:\n"+ + " size: EXTRA_SMALL<*>" + ); + + assertCompletionsDisplayString("foo:\n color: <*>", "red", "green", "blue"); + + assertCompletions("foo:\n color-data: B<*>", "foo:\n color-data: \n BLUE:\n <*>"); + assertCompletions("foo:\n color-data: b<*>", "foo:\n color-data: \n blue:\n <*>"); + assertCompletions("foo:\n color-data: <*>", + "foo:\n color-data: \n blue:\n <*>", + "foo:\n color-data: \n green:\n <*>", + "foo:\n color-data: \n red:\n <*>" + ); + + assertCompletions( + "foo:\n"+ + " color-data:\n"+ + " red: na<*>", + "foo:\n"+ + " color-data:\n"+ + " red: \n"+ + " name: <*>" + ); + } + + @Ignore @Test public void testPojoInListCompletion() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + + assertCompletion( + "foo:\n" + + " color-data:\n" + + " red: chi<*>" + ,// => + "foo:\n" + + " color-data:\n" + + " red: \n" + + " children:\n" + + " - <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nex<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nes<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nested:\n"+ + " <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nex<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nes<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - nested:\n"+ + " <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: RED\n" + + " wav<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: RED\n" + + " wavelen: <*>" + ); + + assertCompletions( + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: RED\n" + + " wav<*>", + // => + "foo:\n" + + " color-data:\n" + + " red:\n" + + " children:\n" + + " - next: RED\n" + + " wavelen: <*>" + ); + } + + @Ignore @Test public void test_STS4111_NoEmptyLinesGapBeforeInsertedCompletion() throws Exception { + data("spring.application.name", "java.lang.String", null, "The name of the application"); + data("spring.application.index", "java.lang.Integer", true, "App instance index"); + data("spring.considerable.fun", "java.lang.Boolean", true, "Whether the spring fun is considerable"); + + assertCompletions( + "spring:\n" + + " application:\n" + + " index: 12\n" + + "\n" + + "server:\n" + + " port: 8888\n" + + "appname<*>" + , //=> + "spring:\n" + + " application:\n" + + " index: 12\n" + + " name: <*>\n" + + "\n" + + "server:\n" + + " port: 8888" + + ); + + //Also test that: + // - Fully commented lines also count as gaps + // - Gaps of more than one line are also handled correctly + assertCompletions( + "# spring stuff\n" + + "spring:\n" + + " application:\n" + + " index: 12\n" + + "\n" + + "#server stuff\n" + + "server:\n" + + " port: 8888\n" + + "cfun<*>" + , //=> + "# spring stuff\n" + + "spring:\n" + + " application:\n" + + " index: 12\n" + + " considerable:\n" + + " fun: <*>\n" + + "\n" + + "#server stuff\n" + + "server:\n" + + " port: 8888" + + ); + + } + + @Ignore @Test public void testDocumentSeparator() throws Exception { + defaultTestData(); + + assertCompletion( + "flyway:\n" + + " encoding: utf8\n" + + "---\n" + + "flyena<*>", + // => + "flyway:\n" + + " encoding: utf8\n" + + "---\n" + + "flyway:\n" + + " enabled: <*>" + ); + } + + @Ignore @Test public void testMultiProfileYamlReconcile() throws Exception { + Editor editor; + //See https://issuetracker.springsource.com/browse/STS-4144 + defaultTestData(); + + //Narrowly focussed test-case for easier debugging + editor = newEditor( + "spring:\n" + + " profiles: seven\n" + ); + editor.assertProblems(/*NONE*/); + + //More complete test + editor = newEditor( + "spring:\n" + + " profiles: seven\n" + + "server:\n" + + " port: 7777\n" + + "---\n" + + "spring:\n" + + " profiles: eight\n" + + "server:\n" + + " port: 8888\n"+ + "bogus: bad" + ); + editor.assertProblems( + "bogus|Unknown property" + ); + } + + @Ignore @Test public void testReconcileArrayOfPrimitiveType() throws Exception { + data("my.array", "int[]", null, "A primitive array"); + data("my.boxarray", "int[]", null, "An array of boxed primitives"); + data("my.list", "java.util.List", null, "A list of boxed types"); + + Editor editor = newEditor( + "my:\n" + + " array:\n" + + " - 777\n" + + " - bad\n" + + " - 888" + ); + editor.assertProblems( + "bad|Expecting a 'int'" + ); + + editor = newEditor( + "my:\n" + + " boxarray:\n" + + " - 777\n" + + " - bad\n" + + " - 888" + ); + editor.assertProblems( + "bad|Expecting a 'int'" + ); + + editor = newEditor( + "my:\n" + + " list:\n" + + " - 777\n" + + " - bad\n" + + " - 888" + ); + editor.assertProblems( + "bad|Expecting a 'int'" + ); + } + + @Ignore @Test public void test_STS4231() throws Exception { + //Should the 'predefined' project need to be recreated... use the commented code below: +// BootProjectTestHarness projectHarness = new BootProjectTestHarness(ResourcesPlugin.getWorkspace()); +// TestProject project = projectHarness.createBootProject("sts-4231", +// bootVersionAtLeast("1.3.0"), +// withStarters("web", "cloud-config-server") +// ); + + //For more robust test use predefined project which is not so much a moving target: + TestProject project = createPredefinedMavenProject("sts-4231"); + useProject(project); + + assertCompletionsDisplayString( + "info:\n" + + " component: Config Server\n" + + "spring:\n" + + " application:\n" + + " name: configserver\n" + + " jmx:\n" + + " default-domain: cloud.config.server\n" + + " cloud:\n" + + " config:\n" + + " server:\n" + + " git:\n" + + " uri: https://github.com/spring-cloud-samples/config-repo\n" + + " repos:\n" + + " my-repo:\n" + + " <*>\n", + // ==> + "name : String", + "pattern : String[]" + ); + + assertCompletion( + "info:\n" + + " component: Config Server\n" + + "spring:\n" + + " application:\n" + + " name: configserver\n" + + " jmx:\n" + + " default-domain: cloud.config.server\n" + + " cloud:\n" + + " config:\n" + + " server:\n" + + " git:\n" + + " uri: https://github.com/spring-cloud-samples/config-repo\n" + + " repos:\n" + + " my-repo:\n" + + " p<*>\n", + // ==> + "info:\n" + + " component: Config Server\n" + + "spring:\n" + + " application:\n" + + " name: configserver\n" + + " jmx:\n" + + " default-domain: cloud.config.server\n" + + " cloud:\n" + + " config:\n" + + " server:\n" + + " git:\n" + + " uri: https://github.com/spring-cloud-samples/config-repo\n" + + " repos:\n" + + " my-repo:\n" + + " pattern:\n" + + " - <*>\n" + ); + + } + + @Ignore @Test public void test_STS_4254_MapStringObjectReconciling() throws Exception { + Editor editor; + data("info", "java.util.Map", null, "Info for the actuator's info endpoint."); + + editor = newEditor( + "info: not-a-map\n" + ); + editor.assertProblems( + "not-a-map|Expecting a 'Map'" + ); + + editor= newEditor( + "info:\n" + + " build: \n" + + " artifact: foo-bar\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "info:\n" + + " more: \n" + + " deeply:\n" + + " nested: foo-bar\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "booger: Bad\n" + + "info:\n" + + " akey: avalue\n" + ); + editor.assertProblems( + "booger|Unknown property" + ); + } + + @Ignore @Test public void test_STS_4254_MapStringStringReconciling() throws Exception { + Editor editor; + data("info", "java.util.Map", null, "Info for the actuator's info endpoint."); + + editor= newEditor( + "info:\n" + + " build: \n" + + " artifact: foo-bar\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "info:\n" + + " more: \n" + + " deeply:\n" + + " nested: foo-bar\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "booger: Bad\n" + + "info:\n" + + " akey: avalue\n" + ); + editor.assertProblems( + "booger|Unknown property" + ); + + } + + @Ignore @Test public void test_STS_4254_MapStringIntegerReconciling() throws Exception { + Editor editor; + data("info", "java.util.Map", null, "Info for the actuator's info endpoint."); + + editor= newEditor( + "info:\n" + + " build: \n" + + " foo-bar\n" + ); + editor.assertProblems( + "foo-bar|Expecting a 'int'" + ); + + editor= newEditor( + "info:\n" + + " build: 123\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "info:\n" + + " build: \n" + + " number: 123\n" + ); + editor.assertProblems(/*NONE*/); + + editor= newEditor( + "info:\n" + + " build: \n" + + " artifact: abc\n" + ); + editor.assertProblems( + "abc|Expecting a 'int'" + ); + + //A more complex example for good measure + editor= newEditor( + "info:\n" + + " some: \n" + + " nested: foo\n" + + " and: bar\n" + + " or: 444\n" + + " also: bad\n" + ); + editor.assertProblems( + "foo|Expecting a 'int'", + "bar|Expecting a 'int'", + "bad|Expecting a 'int'" + ); + } + + @Ignore @Test public void testDeprecatedReconcileProperty() throws Exception { + data("error.path", "java.lang.String", null, "Path of the error controller."); + Editor editor = newEditor( + "# a comment\n"+ + "error:\n"+ + " path: foo\n" + ); + + deprecate("error.path", "server.error.path", null); + editor.assertProblems( + "path|'error.path' is Deprecated: Use 'server.error.path'" + //no other problems + ); + + deprecate("error.path", "server.error.path", "This is old."); + editor.assertProblems( + "path|'error.path' is Deprecated: Use 'server.error.path' instead. Reason: This is old." + //no other problems + ); + + deprecate("error.path", null, "This is old."); + editor.assertProblems( + "path|'error.path' is Deprecated: This is old." + //no other problems + ); + + deprecate("error.path", null, null); + editor.assertProblems( + "path|'error.path' is Deprecated!" + //no other problems + ); + } + + @Ignore @Test public void testDeprecatedPropertyCompletion() throws Exception { + data("error.path", "java.lang.String", null, "Path of the error controller."); + data("server.error.path", "java.lang.String", null, "Path of the error controller."); + deprecate("error.path", "server.error.path", "This is old."); + assertCompletionsDisplayString( + "errorpa<*>" + , // => + "server.error.path : String", // should be first because it is not deprecated, even though it is not as good a pattern match + "error.path : String" + ); + assertStyledCompletions("error.pa<*>", + StyledStringMatcher.plainFont("server.error.path : String"), + StyledStringMatcher.strikeout("error.path") + ); + } + + @Ignore @Test public void testDeprecatedPropertyHoverInfo() throws Exception { + data("error.path", "java.lang.String", null, "Path of the error controller."); + Editor editor = newEditor( + "# a comment\n"+ + "error:\n" + + " path: foo\n" + ); + + deprecate("error.path", "server.error.path", null); + editor.assertHoverContains("path", "error.path -> server.error.path"); + editor.assertHoverContains("path", "Deprecated!"); + + deprecate("error.path", "server.error.path", "This is old."); + editor.assertHoverContains("path", "error.path -> server.error.path"); + editor.assertHoverContains("path", "Deprecated: This is old"); + + deprecate("error.path", null, "This is old."); + editor.assertHoverContains("path", "Deprecated: This is old"); + + deprecate("error.path", null, null); + editor.assertHoverContains("path", "Deprecated!"); + } + + @Ignore @Test public void testDeprecatedBeanPropertyHoverInfo() throws Exception { + TestProject jp = createPredefinedMavenProject("demo"); + useProject(jp); + data("foo", "demo.Deprecater", null, "A bean with deprecated property."); + Editor editor = newEditor( + "# a comment\n"+ + "foo:\n" + + " name: foo\n" + ); + + editor.assertHoverContains("name", "Deprecated!"); + } + + @Ignore @Test public void testDeprecatedBeanPropertyReconcile() throws Exception { + TestProject jp = createPredefinedMavenProject("demo"); + useProject(jp); + data("foo", "demo.Deprecater", null, "A Bean with deprecated properties"); + + Editor editor = newEditor( + "# comment\n" + + "foo:\n" + + " name: Old faithfull\n" + + " new-name: New and fancy\n" + + " alt-name: alternate\n" + ); + editor.assertProblems( + "name|Property 'name' of type 'demo.Deprecater' is Deprecated!", + "alt-name|Deprecated" + ); + + editor = newEditor( + "# comment\n" + + "foo:\n" + + " alt-name: alternate\n" + ); + //check that message also contains reason and replacement infos. + editor.assertProblems( + "alt-name|Use 'something.else' instead" + ); + editor.assertProblems( + "alt-name|No good anymore" + ); + + + } + + @Ignore @Test public void testDeprecatedBeanPropertyCompletions() throws Exception { + TestProject jp = createPredefinedMavenProject("demo"); + useProject(jp); + data("foo", "demo.Deprecater", null, "A Bean with deprecated properties"); + + assertStyledCompletions( + "foo:\n" + + " nam<*>" + , // => + StyledStringMatcher.plainFont("new-name : String"), + StyledStringMatcher.strikeout("name"), + StyledStringMatcher.strikeout("alt-name") + ); + } + + @Ignore @Test public void testDeprecatedPropertyQuickfixSimple() throws Exception { + //A simple case for starters. The path edits aren't too complicated since there's + //just the one property in the file and only the last part of the 'path' changes. + //So this is a simple 'in-place' edit. + + data("my.old-name", "java.lang.String", null, "Old and deprecated name"); + deprecate("my.old-name", "my.new-name", null); + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " old-name: foo\n" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n"+ + " new-name: foo\n" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " old-name: foo\n" + + "your: stuff" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n"+ + " new-name: foo\n"+ + "your: stuff" + ); + } + + { // Don't move prop if it doesn't need to be moved + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " old-name: foo\n" + + " other: bar\n" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n"+ + " new-name: foo\n" + + " other: bar\n" + ); + } + } + + @Ignore @Test public void testDeprecatedPropertyQuickfixMovingValue() throws Exception { + data("my.old-name", "java.lang.String", null, "Old and deprecated name"); + deprecate("my.old-name", "your.new-name", null); + + data("my.stuff", "java.lang.String", null, "Old and deprecated name"); + deprecate("my.stuff", "my.for-sale.stuff", null); + + data("my.long.path.with.many.pieces", "java.lang.String", null, "Old"); + deprecate("my.long.path.with.many.pieces", "my.path.with.many.pieces", null); + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " pieces: foo\n" + ); + + Diagnostic problem = editor.assertProblem("pieces"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.path.with.many.pieces'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n" + + " path:\n"+ + " with:\n"+ + " many:\n"+ + " pieces: foo" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " cannot: remove this\n"+ + " pieces: foo\n" + ); + + Diagnostic problem = editor.assertProblem("pieces"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.path.with.many.pieces'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " cannot: remove this\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " pieces: foo" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " stuff: foo\n" + ); + + Diagnostic problem = editor.assertProblem("stuff"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.for-sale.stuff'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n" + + " for-sale:\n"+ + " stuff: foo" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " old-name: foo\n" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'your.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "your:\n"+ + " new-name: foo" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " old-name: foo\n" + + "your:\n" + + " goodies: nice" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'your.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "your:\n"+ + " goodies: nice\n"+ + " new-name: foo" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "your:\n" + + " goodies: nice\n" + + "my:\n" + + " other: stuff\n" + + " old-name: foo\n" + ); + + Diagnostic problem = editor.assertProblem("old-name"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'your.new-name'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "your:\n"+ + " goodies: nice\n"+ + " new-name: foo\n" + + "my:\n" + + " other: stuff" + ); + } + } + + @Ignore @Test public void testDeprecatedPropertyQuickfixMovingIndentedValue() throws Exception { + data("my.old-name", "java.lang.String", null, "Old and deprecated name"); + deprecate("my.old-name", "your.new-name", null); //same indent level + + data("my.stuff", "java.lang.String", null, "Old and deprecated name"); + deprecate("my.stuff", "my.long.path.with.many.pieces", null); // deeper indent level + + data("my.long.path.with.many.pieces", "java.lang.String", null, "Old"); + deprecate("my.long.path.with.many.pieces", "short.path", null); //shallower level + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " pieces: >\n"+ + " foo spread over\n"+ + " several lines\n" + + " of text\n"+ + "short:\n"+ + " stuff: goes here\n" + ); + Diagnostic problem = editor.assertProblem("pieces"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'short.path'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "short:\n"+ + " stuff: goes here\n" + + " path: >\n" + + " foo spread over\n"+ + " several lines\n" + + " of text\n" + ); + } + + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " bits: go here\n"+ + " stuff: >\n" + + " foo spread over\n"+ + " several lines\n" + + " of text\n" + ); + Diagnostic problem = editor.assertProblem("stuff"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'my.long.path.with.many.pieces'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " bits: go here\n"+ + " path:\n" + + " with:\n" + + " many:\n"+ + " pieces: >\n" + + " foo spread over\n"+ + " several lines\n" + + " of text" + ); + } + { + Editor editor = newEditor( + "# a comment\n"+ + "my:\n" + + " long:\n"+ + " path:\n"+ + " with:\n"+ + " many:\n"+ + " pieces:\n"+ + "# some\n" + + " - foo spread over\n"+ + " #confusing\n" + + " - several lines\n" + + " #comments\n" + + " - of text\n"+ + "short:\n"+ + " stuff: goes here\n" + ); + Diagnostic problem = editor.assertProblem("pieces"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'short.path'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "short:\n"+ + " stuff: goes here\n" + + " path:\n" + + " # some\n" + + " - foo spread over\n"+ + " #confusing\n" + + " - several lines\n" + + " #comments\n" + + " - of text\n" + ); + } + } + + @Ignore @Test public void testReconcileDuplicateProperties() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "spring:\n" + + " profiles: cloudfoundry\n" + + "spring: \n" + + " application:\n" + + " name: eureka" + ); + editor.assertProblems( + "spring|Duplicate", + "spring|Duplicate" + ); + } + + @Ignore @Test public void testReconcileDuplicatePropertiesNested() throws Exception { + data("foo.person.name", "String", null, "Name of person"); + data("foo.person.family", "String", null, "Family name of person"); + Editor editor = newEditor( + "foo:\n" + + " person:\n" + + " name: Hohohoh\n" + + " person:\n" + + " family:\n" + ); + editor.assertProblems( + "person|Duplicate", + "person|Duplicate" + ); + } + + @Ignore @Test public void testReconcileDuplicatePropertiesInBean() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("some.color", "demo.ColorData", null, "Some info about a color."); + Editor editor = newEditor( + "some:\n" + + " color:\n" + + " name: RED\n" + + " name: GREEN\n" + ); + editor.assertProblems( + "name|Duplicate", + "name|Duplicate" + ); + } + + @Ignore @Test public void testCharSetCompletions() throws Exception { + data("foobar.encoding", "java.nio.charset.Charset", null, "The charset-encoding to use for foobars"); + + assertCompletions( + "foobar:\n" + + " enco<*>" + , // ==> + "foobar:\n" + + " encoding: <*>" + ); + + assertCompletionWithLabel( + "foobar:\n" + + " encoding: UT<*>" + , + "UTF-8" + , + "foobar:\n" + + " encoding: UTF-8<*>" + ); + } + + @Ignore @Test public void testLocaleCompletions() throws Exception { + data("foobar.locale", "java.util.Locale", null, "The locale for foobars"); + + assertCompletions( + "foobar:\n" + + " loca<*>" + , // ==> + "foobar:\n" + + " locale: <*>" + ); + + assertCompletionWithLabel( + "foobar:\n" + + " locale: en<*>" + , + "en_CA" + , + "foobar:\n" + + " locale: en_CA<*>" + ); + } + + @Ignore @Test public void testMimeTypeCompletions() throws Exception { + data("foobar.mime", "org.springframework.util.MimeType", null, "The mimetype for foobars"); + + assertCompletions( + "foobar:\n" + + " mi<*>" + , // ==> + "foobar:\n" + + " mime: <*>" + ); + + assertCompletionWithLabel( + "foobar:\n" + + " mime: json<*>" + , + "application/json; charset=utf-8" + , + "foobar:\n" + + " mime: application/json; charset=utf-8<*>" + ); + } + + @Ignore @Test public void testPropertyValueHintCompletions() throws Exception { + //Test that 'value hints' work when property name is associated with 'value' hints. + // via boot metadata. + useProject(createPredefinedMavenProject("boot13")); + + assertCompletionsDisplayString( + "spring:\n" + + " http:\n" + + " converters:\n" + + " preferred-json-mapper: <*>\n" + , //=> + "gson", + "jackson" + ); + } + + @Ignore @Test public void testPropertyListHintCompletions() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + + assertCompletion( + "management:\n" + + " health:\n" + + " status:\n" + + " ord<*>" + , //=> + "management:\n" + + " health:\n" + + " status:\n" + + " order:\n"+ + " - <*>" + ); + + assertCompletionsDisplayString( + "management:\n" + + " health:\n" + + " status:\n" + + " order:\n" + + " - <*>" + , //=> + "DOWN", + "OUT_OF_SERVICE", + "UNKNOWN", + "UP" + ); + } + + @Ignore @Test public void testPropertyMapValueCompletions() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + + assertCompletionsDisplayString( + "logging:\n" + + " level:\n" + + " some.package: <*>" + , // => + "trace", + "debug", + "info", + "warn", + "error", + "fatal", + "off" + ); + } + + @Ignore @Test public void testPropertyMapKeyCompletions() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + assertCompletionWithLabel( + "logging:\n" + + " level:\n" + + " <*>" + , // => + "root : String" + , + "logging:\n" + + " level:\n" + + " root: <*>" + ); + } + + @Ignore @Test public void testEscapeStringValueStartingWithStar() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + assertCompletions( + "endpoints:\n"+ + " cors:\n"+ + " allowed-headers: \n" + + " - <*>" + , // => + "endpoints:\n"+ + " cors:\n"+ + " allowed-headers: \n" + + " - '*'<*>" + ); + } + + @Ignore @Test public void testEscapeStringValueWithAQuote() throws Exception { + data("foo.quote", "java.lang.String", null, "Character to used to surround quotes"); + valueHints("foo.quote", "\"", "'", "`"); + + assertCompletions( + "foo:\n" + + " quote: <*>" + , // => + "foo:\n" + + " quote: '\"'<*>" + , + "foo:\n" + + " quote: ''''<*>" + , + "foo:\n" + + " quote: '`'<*>" + ); + } + + @Ignore @Test public void testEscapeStringKeyWithAQuote() throws Exception { + data("foo.quote", "java.util.Map", null, "Name of quote characters"); + keyHints("foo.quote", "\"", "'", "`"); + + assertCompletions( + "foo:\n" + + " quote: <*>" + , // => + "foo:\n" + + " quote: \n"+ + " '\"': <*>" + , + "foo:\n" + + " quote: \n"+ + " '''': <*>" + , + "foo:\n" + + " quote: \n"+ + " '`': <*>" + ); + } + + @Ignore @Test public void testLoggerNameCompletion() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); // the provider can't be reliably tested if its not allowed to + // fetch all its values (even though in 'production' you + // wouldn't want it to block the UI thread for this long. + useProject(createPredefinedMavenProject("boot13")); + //Finds a package: + assertCompletionWithLabel( + "logging:\n" + + " level:\n" + + " boot.auto<*>" + , //----------------- + "org.springframework.boot.autoconfigure : String" + , // => + "logging:\n" + + " level:\n" + + " org.springframework.boot.autoconfigure: <*>" + ); + + //Finds a type: + assertCompletionWithLabel( + "logging:\n" + + " level:\n" + + " MesgSource<*>" + , //----------------- + "org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration : String" + , // => + "logging:\n" + + " level:\n" + + " org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration: <*>" + ); + } + + @Ignore @Test public void testSimpleResourceCompletion() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); + + useProject(createPredefinedMavenProject("boot13")); + + data("my.nice.resource", "org.springframework.core.io.Resource", null, "A very nice resource."); + + assertCompletion( + "my:\n" + + " nice:\n" + + " <*>\n" + ,// => + "my:\n" + + " nice:\n" + + " resource: <*>\n" + ); + + assertCompletionsDisplayString( + "my:\n" + + " nice:\n" + + " resource: <*>\n" + , // => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + + assertCompletionsDisplayString( + "my:\n" + + " nice:\n" + + " resource:\n" + + " <*>\n" + , // => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + + } + + @Ignore @Test public void testClasspathResourceCompletion() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); + + useProject(createPredefinedMavenProject("boot13")); + + data("my.nice.resource", "org.springframework.core.io.Resource", null, "A very nice resource."); + data("my.nice.list", "java.util.List", null, "A nice list of resources."); + + //Test 'simple key context' + + assertCompletionsDisplayString( + "my:\n" + + " nice:\n" + + " resource: classpath:app<*>\n" + ,// => + "classpath:application.properties", + "classpath:application.yml" + ); + + //Test 'list item' context: + + assertCompletionsDisplayString( + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:app<*>\n" + ,// => + "classpath:application.properties", + "classpath:application.yml" + ); + + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:app<*>\n" + ,// ========== + "classpath:application.yml" + , // => + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:application.yml<*>\n" + ); + + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:<*>\n" + ,// ========== + "classpath:application.yml" + , // => + "my:\n" + + " nice:\n" + + " list:\n"+ + " - classpath:application.yml<*>\n" + ); + + //Test 'raw node' context + + assertCompletionsDisplayString( + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:app<*>\n" + ,// => + "classpath:application.properties", + "classpath:application.yml" + ); + + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:app<*>\n" + ,//=============== + "classpath:application.properties" + ,// => + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:application.properties<*>\n" + ); + + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:<*>\n" + ,//=============== + "classpath:application.properties" + ,// => + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:application.properties<*>\n" + ); + + // do we find resources in sub-folders too? + assertCompletionWithLabel( + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:word<*>\n" + ,//=============== + "classpath:stuff/wordlist.txt" + ,// => + "my:\n" + + " nice:\n" + + " resource:\n"+ + " classpath:stuff/wordlist.txt<*>\n" + ); + } + + @Ignore @Test public void testClassReferenceCompletion() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); + + useProject(createPredefinedMavenProject("boot13_with_mongo")); + + assertCompletion( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-na<*>" + , // => + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: <*>" + ); + + assertCompletionsDisplayString( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: <*>" + , // => + "org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy", + "org.springframework.data.mapping.model.CamelCaseSplittingFieldNamingStrategy", + "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", + "org.springframework.data.mapping.model.SnakeCaseFieldNamingStrategy" + ); + + assertCompletionWithLabel( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: <*>" + , //===== + "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy" + , //=> + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy<*>" + ); + + //Test what happens when 'target' type isn't on the classpath: + useProject(createPredefinedMavenProject("boot13")); + assertCompletionsDisplayString( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: <*>" + // => + /*NONE*/ + ); + } + + @Ignore @Test public void testClassReferenceInValueLink() throws Exception { + Editor editor; + useProject(createPredefinedMavenProject("boot13_with_mongo")); + + editor = newEditor( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n" + ); + editor.assertLinkTargets("org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy"); + + editor = newEditor( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy:\n" + + " org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n" + ); + editor.assertLinkTargets("org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy", "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy"); + + //Linking should also work for types that aren't valid based on the constraints + editor = newEditor( + "spring:\n" + + " data:\n" + + " mongodb:\n" + + " field-naming-strategy: java.lang.String\n" + + "#more stuff" + ); + editor.assertLinkTargets("java.lang.String", "java.lang.String"); + } + + @Ignore @Test public void test_STS_3335_reconcile_list_nested_in_Map_of_String() throws Exception { + Editor editor; + useProject(createPredefinedMavenProject("demo-sts-4335")); + + editor = newEditor( + "test-map:\n" + + " test-list-object:\n" + + " color-list:\n" + + " - not-a-color\n"+ + " - RED\n" + + " - GREEN\n" + ); + editor.assertProblems( + "not-a-color|Expecting a 'com.wellsfargo.lendingplatform.web.config.Color" + ); + + editor = newEditor( + "test-map:\n" + + " test-list-object:\n" + + " string-list:\n" + + " - abc\n" + + " - def\n" + ); + editor.assertProblems(/*NONE*/); + + } + + + @Ignore @Test public void test_STS_3335_completions_list_nested_in_Map_of_String() throws Exception { + useProject(createPredefinedMavenProject("demo-sts-4335")); + + assertCompletions( + "test-map:\n" + + " some-string-key:\n" + + " col<*>" + , // => + "test-map:\n" + + " some-string-key:\n" + + " color-list:\n" + + " - <*>" + ); + + assertCompletionsDisplayString( + "test-map:\n" + + " some-string-key:\n" + + " color-list:\n" + + " - <*>" + , // => + "red", "green", "blue" + ); + } + + @Ignore @Test public void testHandleAsResourceContentAssist() throws Exception { + //"name": "my.terms-and-conditions", + // "providers": [ + // { + // "name": "handle-as", + // "parameters": { + // "target": "org.springframework.core.io.Resource" + // } + // } + // ] + data("my.terms-and-conditions", "java.lang.String", null, "Terms and Conditions text file") + .provider("handle-as", "target", "org.springframework.core.io.Resource"); + + assertCompletionsDisplayString( + "my:\n" + + " terms-and-conditions: <*>" + , // => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + } + + @Ignore @Test public void testBootBug5905() throws Exception { + useProject(createPredefinedMavenProject("demo-with-resource-prop")); + + //Check the metadata reflects the 'handle-as': + PropertyInfo metadata = getIndexProvider().getIndex(null).get("my.welcome.path"); + assertEquals("org.springframework.core.io.Resource", metadata.getType()); + + //Check the content assist based on it works too: + assertCompletionsDisplayString( + "my:\n"+ + " welcome:\n" + + " path: <*>" + , // => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + } + + @Ignore @Test public void testEnumJavaDocShownInValueContentAssist() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("my.background", "demo.Color", null, "Color to use as default background."); + + assertCompletionWithInfoHover( + "my:\n" + + " background: <*>" + , // ========== + "red" + , // ==> + "Hot and delicious" + ); + } + + @Ignore @Test public void testEnumJavaDocShownInValueHover() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("my.background", "demo.Color", null, "Color to use as default background."); + + Editor editor; + + editor = newEditor( + "my:\n" + + " background: red" + ); + editor.assertHoverContains("red", "Hot and delicious"); + + editor = newEditor( + "my:\n" + + " background: RED" + ); + editor.assertHoverContains("RED", "Hot and delicious"); + } + + @Ignore @Test public void testHyperLinkEnumValue() throws Exception { + Editor editor; + useProject(createPredefinedMavenProject("demo-enum")); + data("my.background", "demo.Color", null, "Color to use as default background."); + + editor = newEditor( + "my:\n" + + " background: RED" + ); + editor.assertLinkTargets("RED", "demo.Color.RED"); + + editor = newEditor( + "my:\n" + + " background: red" + ); + editor.assertLinkTargets("red", "demo.Color.RED"); + } + + @Ignore @Test public void testHyperLinkEnumValueInMapKey() throws Exception { + Editor editor; + useProject(createPredefinedMavenProject("demo-enum")); + data("my.color.map", "java.util.Map", null, "Pretty names for the colors."); + + editor = newEditor( + "my:\n" + + " color:\n" + + " map:\n" + + " RED: Rood\n" + + " green: Groen\n" + ); + editor.assertLinkTargets("RED", "demo.Color.RED"); + editor.assertLinkTargets("green", "demo.Color.GREEN"); + + editor = newEditor( + "spring:\n" + + " jackson:\n" + + " serialization:\n" + + " INDENT_OUTPUT: true" + ); + editor.assertLinkTargets("INDENT_OUTPUT", + "com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT" + ); + + editor = newEditor( + "spring:\n" + + " jackson:\n" + + " serialization:\n" + + " indent-output: true" + ); + editor.assertLinkTargets("indent-output", + "com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT" + ); + } + + ///////////////// cruft //////////////////////////////////////////////////////// + + private void generateNestedProperties(int levels, String[] names, String prefix) { + if (levels==0) { + data(prefix, "java.lang.String", null, "Property "+prefix); + } else if (levels > 0) { + for (int i = 0; i < names.length; i++) { + generateNestedProperties(levels-1, names, join(prefix, names[i])); + } + } + } + + private String join(String prefix, String string) { + if (StringUtil.hasText(prefix)) { + return prefix +"." + string; + } + return string; + } + } diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/PropertyIndexHarness.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/PropertyIndexHarness.java new file mode 100644 index 000000000..aee4bc943 --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/PropertyIndexHarness.java @@ -0,0 +1,547 @@ +package org.springframework.ide.vscode.yaml; + +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; +import org.springframework.boot.configurationmetadata.Deprecation; +import org.springframework.boot.configurationmetadata.ValueHint; +import org.springframework.boot.configurationmetadata.ValueProvider; +import org.springframework.ide.vscode.boot.properties.metadata.PropertyInfo; +import org.springframework.ide.vscode.boot.properties.metadata.SpringPropertyIndex; +import org.springframework.ide.vscode.boot.properties.metadata.SpringPropertyIndexProvider; +import org.springframework.ide.vscode.boot.properties.metadata.ValueProviderRegistry; +import org.springframework.ide.vscode.boot.properties.util.FuzzyMap; +import org.springframework.ide.vscode.testharness.TestProject; +import org.springframework.ide.vscode.util.IDocument; + +/** + * Provides some convenience apis for test code to create / use test data for a SpringPropertyIndex. + */ +public class PropertyIndexHarness { + + private Map datas = new LinkedHashMap<>(); + private ValueProviderRegistry valueProviders = ValueProviderRegistry.getDefault(); + private SpringPropertyIndex index = null; + private TestProject testProject = null; + + protected SpringPropertyIndexProvider indexProvider = new SpringPropertyIndexProvider() { + public FuzzyMap getIndex(IDocument doc) { + if (index==null) { + Path path = testProject == null ? null : testProject.getPath(); + index = new SpringPropertyIndex(valueProviders, path); + for (ConfigurationMetadataProperty propertyInfo : datas.values()) { + index.add(propertyInfo); + } + } + return index; + } + }; + + public class ItemConfigurer { + + private ConfigurationMetadataProperty item; + + public ItemConfigurer(ConfigurationMetadataProperty item) { + this.item = item; + } + + /** + * Add a provider with a single parameter. + * @return + */ + public ItemConfigurer provider(String name, String paramName, Object paramValue) { + ValueProvider provider = new ValueProvider(); + provider.setName(name); + provider.getParameters().put(paramName, paramValue); + item.getHints().getValueProviders().add(provider); + return this; + } + + /** + * Add a value hint. If description contains a '.' the dot is used + * to break description into a short and long description. + * @return + */ + public ItemConfigurer valueHint(Object value, String description) { + ValueHint hint = new ValueHint(); + hint.setValue(value); + if (description!=null) { + int dotPos = description.indexOf('.'); + if (dotPos>=0) { + hint.setShortDescription( description.substring(0, dotPos)); + } + hint.setDescription(description); + } + item.getHints().getValueHints().add(hint); + return this; + } + } + + + public ItemConfigurer data(String id, String type, Object deflt, String description, + String... source + ) { + ConfigurationMetadataProperty item = new ConfigurationMetadataProperty(); + item.setId(id); + item.setDescription(description); + item.setType(type); + item.setDefaultValue(deflt); + index = null; + datas.put(item.getId(), item); + return new ItemConfigurer(item); + } + + public void keyHints(String id, String... hintValues) { + index = null; + List hints = datas.get(id).getHints().getKeyHints(); + for (String value : hintValues) { + ValueHint hint = new ValueHint(); + hint.setValue(value); + hints.add(hint); + } + } + + public void valueHints(String id, String... hintValues) { + index = null; + List hints = datas.get(id).getHints().getValueHints(); + for (String value : hintValues) { + ValueHint hint = new ValueHint(); + hint.setValue(value); + hints.add(hint); + } + } + + public void deprecate(String key, String replacedBy, String reason) { + index = null; + ConfigurationMetadataProperty info = datas.get(key); + Deprecation d = new Deprecation(); + d.setReplacement(replacedBy); + d.setReason(reason); + info.setDeprecation(d); + } + + /** + * Call this method to add some default test data to the Completion engine's index. + * Note that this data is not added automatically, some test may want to use smaller + * test data sets. + */ + public void defaultTestData() { + data("banner.charset", "java.nio.charset.Charset", "UTF-8", "Banner file encoding."); + data("banner.location", "java.lang.String", "classpath:banner.txt", "Banner file location."); + data("debug", "java.lang.Boolean", "false", "Enable debug logs."); + data("flyway.check-location", "java.lang.Boolean", "false", "Check that migration scripts location exists."); + data("flyway.clean-on-validation-error", "java.lang.Boolean", null, null); + data("flyway.enabled", "java.lang.Boolean", "true", "Enable flyway."); + data("flyway.encoding", "java.lang.String", null, null); + data("flyway.ignore-failed-future-migration", "java.lang.Boolean", null, null); + data("flyway.init-description", "java.lang.String", null, null); + data("flyway.init-on-migrate", "java.lang.Boolean", null, null); + data("flyway.init-sqls", "java.util.List", null, "SQL statements to execute to initialize a connection immediately after obtaining\n it."); + data("flyway.init-version", "org.flywaydb.core.api.MigrationVersion", null, null); + data("flyway.locations", "java.util.List", null, "Locations of migrations scripts."); + data("flyway.out-of-order", "java.lang.Boolean", null, null); + data("flyway.password", "java.lang.String", null, "Login password of the database to migrate."); + data("flyway.placeholder-prefix", "java.lang.String", null, null); + data("flyway.placeholders", "java.util.Map", null, null); + data("flyway.placeholder-suffix", "java.lang.String", null, null); + data("flyway.schemas", "java.lang.String[]", null, null); + data("flyway.sql-migration-prefix", "java.lang.String", null, null); + data("flyway.sql-migration-separator", "java.lang.String", null, null); + data("flyway.sql-migration-suffix", "java.lang.String", null, null); + data("flyway.table", "java.lang.String", null, null); + data("flyway.target", "org.flywaydb.core.api.MigrationVersion", null, null); + data("flyway.url", "java.lang.String", null, "JDBC url of the database to migrate. If not set, the primary configured data source\n is used."); + data("flyway.user", "java.lang.String", null, "Login user of the database to migrate."); + data("flyway.validate-on-migrate", "java.lang.Boolean", null, null); + data("http.mappers.json-pretty-print", "java.lang.Boolean", null, "Enable json pretty print."); + data("http.mappers.json-sort-keys", "java.lang.Boolean", null, "Enable key sorting."); + data("liquibase.change-log", "java.lang.String", "classpath:/db/changelog/db.changelog-master.yaml", "Change log configuration path."); + data("liquibase.check-change-log-location", "java.lang.Boolean", "true", "Check the change log location exists."); + data("liquibase.contexts", "java.lang.String", null, "Comma-separated list of runtime contexts to use."); + data("liquibase.default-schema", "java.lang.String", null, "Default database schema."); + data("liquibase.drop-first", "java.lang.Boolean", "false", "Drop the database schema first."); + data("liquibase.enabled", "java.lang.Boolean", "true", "Enable liquibase support."); + data("liquibase.password", "java.lang.String", null, "Login password of the database to migrate."); + data("liquibase.url", "java.lang.String", null, "JDBC url of the database to migrate. If not set, the primary configured data source\n is used."); + data("liquibase.user", "java.lang.String", null, "Login user of the database to migrate."); + data("logging.config", "java.lang.String", null, "Location of the logging configuration file."); + data("logging.file", "java.lang.String", null, "Log file name."); + data("logging.level", "java.util.Map", null, "Log levels severity mapping. Use 'root' for the root logger."); + data("logging.path", "java.lang.String", null, "Location of the log file."); + data("multipart.file-size-threshold", "java.lang.String", "0", "Threshold after which files will be written to disk. Values can use the suffixed\n \"MB\" or \"KB\" to indicate a Megabyte or Kilobyte size."); + data("multipart.location", "java.lang.String", null, "Intermediate location of uploaded files."); + data("multipart.max-file-size", "java.lang.String", "1Mb", "Max file size. Values can use the suffixed \"MB\" or \"KB\" to indicate a Megabyte or\n Kilobyte size."); + data("multipart.max-request-size", "java.lang.String", "10Mb", "Max request size. Values can use the suffixed \"MB\" or \"KB\" to indicate a Megabyte\n or Kilobyte size."); + data("security.basic.enabled", "java.lang.Boolean", "true", "Enable basic authentication."); + data("security.basic.path", "java.lang.String[]", "[Ljava.lang.Object;@7abd0056", "Comma-separated list of paths to secure."); + data("security.basic.realm", "java.lang.String", "Spring", "HTTP basic realm name."); + data("security.enable-csrf", "java.lang.Boolean", "false", "Enable Cross Site Request Forgery support."); + data("security.filter-order", "java.lang.Integer", "0", "Security filter chain order."); + data("security.headers.cache", "java.lang.Boolean", "false", "Enable cache control HTTP headers."); + data("security.headers.content-type", "java.lang.Boolean", "false", "Enable \"X-Content-Type-Options\" header."); + data("security.headers.frame", "java.lang.Boolean", "false", "Enable \"X-Frame-Options\" header."); + data("security.headers.hsts", "org.springframework.boot.autoconfigure.security.SecurityProperties$Headers$HSTS", null, "HTTP Strict Transport Security (HSTS) mode (none, domain, all)."); + data("security.headers.xss", "java.lang.Boolean", "false", "Enable cross site scripting (XSS) protection."); + data("security.ignored", "java.util.List", null, "Comma-separated list of paths to exclude from the default secured paths."); + data("security.require-ssl", "java.lang.Boolean", "false", "Enable secure channel for all requests."); + data("security.sessions", "org.springframework.security.config.http.SessionCreationPolicy", null, "Session creation policy (always, never, if_required, stateless)."); + data("security.user.name", "java.lang.String", "user", "Default user name."); + data("security.user.password", "java.lang.String", null, "Password for the default user name."); + data("security.user.role", "java.util.List", null, "Granted roles for the default user name."); + data("server.address", "java.net.InetAddress", null, "Network address to which the server should bind to."); + data("server.context-parameters", "java.util.Map", null, "ServletContext parameters."); + data("server.context-path", "java.lang.String", null, "Context path of the application."); + data("server.port", "java.lang.Integer", null, "Server HTTP port."); + data("server.servlet-path", "java.lang.String", "/", "Path of the main dispatcher servlet."); + data("server.session-timeout", "java.lang.Integer", null, "Session timeout in seconds."); + data("server.ssl.ciphers", "java.lang.String[]", null, null); + data("server.ssl.client-auth", "org.springframework.boot.context.embedded.Ssl$ClientAuth", null, null); + data("server.ssl.key-alias", "java.lang.String", null, null); + data("server.ssl.key-password", "java.lang.String", null, null); + data("server.ssl.key-store", "java.lang.String", null, null); + data("server.ssl.key-store-password", "java.lang.String", null, null); + data("server.ssl.key-store-provider", "java.lang.String", null, null); + data("server.ssl.key-store-type", "java.lang.String", null, null); + data("server.ssl.protocol", "java.lang.String", null, null); + data("server.ssl.trust-store", "java.lang.String", null, null); + data("server.ssl.trust-store-password", "java.lang.String", null, null); + data("server.ssl.trust-store-provider", "java.lang.String", null, null); + data("server.ssl.trust-store-type", "java.lang.String", null, null); + data("server.tomcat.access-log-enabled", "java.lang.Boolean", "false", "Enable access log."); + data("server.tomcat.access-log-pattern", "java.lang.String", null, "Format pattern for access logs."); + data("server.tomcat.background-processor-delay", "java.lang.Integer", "30", "Delay in seconds between the invocation of backgroundProcess methods."); + data("server.tomcat.basedir", "java.io.File", null, "Tomcat base directory. If not specified a temporary directory will be used."); + data("server.tomcat.internal-proxies", "java.lang.String", "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|192\\.168\\.\\d{1,3}\\.\\d{1,3}|169\\.254\\.\\d{1,3}\\.\\d{1,3}|127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", "Regular expression that matches proxies that are to be trusted."); + data("server.tomcat.max-http-header-size", "java.lang.Integer", "0", "Maximum size in bytes of the HTTP message header."); + data("server.tomcat.max-threads", "java.lang.Integer", "0", "Maximum amount of worker threads."); + data("server.tomcat.port-header", "java.lang.String", null, "Name of the HTTP header used to override the original port value."); + data("server.tomcat.protocol-header", "java.lang.String", null, "Header that holds the incoming protocol, usually named \"X-Forwarded-Proto\".\n Configured as a RemoteIpValve only if remoteIpHeader is also set."); + data("server.tomcat.remote-ip-header", "java.lang.String", null, "Name of the http header from which the remote ip is extracted. Configured as a\n RemoteIpValve only if remoteIpHeader is also set."); + data("server.tomcat.uri-encoding", "java.lang.String", null, "Character encoding to use to decode the URI."); + data("server.undertow.buffer-size", "java.lang.Integer", null, "Size of each buffer in bytes."); + data("server.undertow.buffers-per-region", "java.lang.Integer", null, "Number of buffer per region."); + data("server.undertow.direct-buffers", "java.lang.Boolean", null, null); + data("server.undertow.io-threads", "java.lang.Integer", null, "Number of I/O threads to create for the worker."); + data("server.undertow.worker-threads", "java.lang.Integer", null, "Number of worker threads."); + data("spring.activemq.broker-url", "java.lang.String", null, "URL of the ActiveMQ broker. Auto-generated by default."); + data("spring.activemq.in-memory", "java.lang.Boolean", "true", "Specify if the default broker URL should be in memory. Ignored if an explicit\n broker has been specified."); + data("spring.activemq.password", "java.lang.String", null, "Login password of the broker."); + data("spring.activemq.pooled", "java.lang.Boolean", "false", "Specify if a PooledConnectionFactory should be created instead of a regular\n ConnectionFactory."); + data("spring.activemq.user", "java.lang.String", null, "Login user of the broker."); + data("spring.aop.auto", "java.lang.Boolean", "true", "Add @EnableAspectJAutoProxy."); + data("spring.aop.proxy-target-class", "java.lang.Boolean", "false", "Whether subclass-based (CGLIB) proxies are to be created (true) as opposed to standard Java interface-based proxies (false)."); + data("spring.application.index", "java.lang.Integer", null, "Application index."); + data("spring.application.name", "java.lang.String", null, "Application name."); + data("spring.batch.initializer.enabled", "java.lang.Boolean", "true", "Create the required batch tables on startup if necessary."); + data("spring.batch.job.enabled", "java.lang.Boolean", "true", "Execute all Spring Batch jobs in the context on startup."); + data("spring.batch.job.names", "java.lang.String", "", "Comma-separated list of job names to execute on startup. By default, all Jobs\n found in the context are executed."); + data("spring.batch.schema", "java.lang.String", "classpath:org/springframework/batch/core/schema-@@platform@@.sql", "Path to the SQL file to use to initialize the database schema."); + data("spring.config.location", "java.lang.String", null, "Config file locations."); + data("spring.config.name", "java.lang.String", "application", "Config file name."); + data("spring.dao.exceptiontranslation.enabled", "java.lang.Boolean", "true", "Enable the PersistenceExceptionTranslationPostProcessor."); + data("spring.data.elasticsearch.cluster-name", "java.lang.String", "elasticsearch", "Elasticsearch cluster name."); + data("spring.data.elasticsearch.cluster-nodes", "java.lang.String", null, "Comma-separated list of cluster node addresses. If not specified, starts a client\n node."); + data("spring.data.elasticsearch.repositories.enabled", "java.lang.Boolean", "true", "Enable Elasticsearch repositories."); + data("spring.data.jpa.repositories.enabled", "java.lang.Boolean", "true", "Enable JPA repositories."); + data("spring.data.mongodb.authentication-database", "java.lang.String", null, "Authentication database name."); + data("spring.data.mongodb.database", "java.lang.String", null, "Database name."); + data("spring.data.mongodb.grid-fs-database", "java.lang.String", null, "GridFS database name."); + data("spring.data.mongodb.host", "java.lang.String", null, "Mongo server host."); + data("spring.data.mongodb.password", "char[]", null, "Login password of the mongo server."); + data("spring.data.mongodb.port", "java.lang.Integer", null, "Mongo server port."); + data("spring.data.mongodb.repositories.enabled", "java.lang.Boolean", "true", "Enable Mongo repositories."); + data("spring.data.mongodb.uri", "java.lang.String", "mongodb://localhost/test", "Mmongo database URI. When set, host and port are ignored."); + data("spring.data.mongodb.username", "java.lang.String", null, "Login user of the mongo server."); + data("spring.data.rest.base-uri", "java.net.URI", null, null); + data("spring.data.rest.default-page-size", "java.lang.Integer", null, null); + data("spring.data.rest.limit-param-name", "java.lang.String", null, null); + data("spring.data.rest.max-page-size", "java.lang.Integer", null, null); + data("spring.data.rest.page-param-name", "java.lang.String", null, null); + data("spring.data.rest.return-body-on-create", "java.lang.Boolean", null, null); + data("spring.data.rest.return-body-on-update", "java.lang.Boolean", null, null); + data("spring.data.rest.sort-param-name", "java.lang.String", null, null); + data("spring.data.solr.host", "java.lang.String", "http://127.0.0.1:8983/solr", "Solr host. Ignored if \"zk-host\" is set."); + data("spring.data.solr.repositories.enabled", "java.lang.Boolean", "true", "Enable Solr repositories."); + data("spring.data.solr.zk-host", "java.lang.String", null, "ZooKeeper host address in the form HOST:PORT."); + data("spring.datasource.abandon-when-percentage-full", "java.lang.Integer", null, null); + data("spring.datasource.access-to-underlying-connection-allowed", "java.lang.Boolean", null, null); + data("spring.datasource.alternate-username-allowed", "java.lang.Boolean", null, null); + data("spring.datasource.auto-commit", "java.lang.Boolean", null, null); + data("spring.datasource.catalog", "java.lang.String", null, null); + data("spring.datasource.commit-on-return", "java.lang.Boolean", null, null); + data("spring.datasource.connection-customizer-class-name", "java.lang.String", null, null); + data("spring.datasource.connection-init-sql", "java.lang.String", null, null); + data("spring.datasource.connection-init-sqls", "java.util.Collection", null, null); + data("spring.datasource.connection-properties", "java.lang.String", null, null); + data("spring.datasource.connection-test-query", "java.lang.String", null, null); + data("spring.datasource.connection-timeout", "java.lang.Long", null, null); + data("spring.datasource.continue-on-error", "java.lang.Boolean", "false", "Do not stop if an error occurs while initializing the database."); + data("spring.datasource.data", "java.lang.String", null, "Data (DML) script resource reference."); + data("spring.datasource.data-source-class-name", "java.lang.String", null, null); + data("spring.datasource.data-source", "java.lang.Object", null, null); + data("spring.datasource.data-source-j-n-d-i", "java.lang.String", null, null); + data("spring.datasource.data-source-properties", "java.util.Properties", null, null); + data("spring.datasource.db-properties", "java.util.Properties", null, null); + data("spring.datasource.default-auto-commit", "java.lang.Boolean", null, null); + data("spring.datasource.default-catalog", "java.lang.String", null, null); + data("spring.datasource.default-read-only", "java.lang.Boolean", null, null); + data("spring.datasource.default-transaction-isolation", "java.lang.Integer", null, null); + data("spring.datasource.driver-class-name", "java.lang.String", null, "Fully qualified name of the JDBC driver. Auto-detected based on the URL by default."); + data("spring.datasource.fair-queue", "java.lang.Boolean", null, null); + data("spring.datasource.idle-timeout", "java.lang.Long", null, null); + data("spring.datasource.ignore-exception-on-pre-load", "java.lang.Boolean", null, null); + data("spring.datasource.initialization-fail-fast", "java.lang.Boolean", null, null); + data("spring.datasource.initialize", "java.lang.Boolean", "true", "Populate the database using 'data.sql'."); + data("spring.datasource.initial-size", "java.lang.Integer", null, null); + data("spring.datasource.init-s-q-l", "java.lang.String", null, null); + data("spring.datasource.isolate-internal-queries", "java.lang.Boolean", null, null); + data("spring.datasource.jdbc4-connection-test", "java.lang.Boolean", null, null); + data("spring.datasource.jdbc-interceptors", "java.lang.String", null, null); + data("spring.datasource.jdbc-url", "java.lang.String", null, null); + data("spring.datasource.jmx-enabled", "java.lang.Boolean", "false", "Enable JMX support (if provided by the underlying pool)."); + data("spring.datasource.jndi-name", "java.lang.String", null, "JNDI location of the datasource. Class, url, username & password are ignored when\n set."); + data("spring.datasource.leak-detection-threshold", "java.lang.Long", null, null); + data("spring.datasource.log-abandoned", "java.lang.Boolean", null, null); + data("spring.datasource.login-timeout", "java.lang.Integer", null, null); + data("spring.datasource.log-validation-errors", "java.lang.Boolean", null, null); + data("spring.datasource.max-active", "java.lang.Integer", null, null); + data("spring.datasource.max-age", "java.lang.Long", null, null); + data("spring.datasource.max-idle", "java.lang.Integer", null, null); + data("spring.datasource.maximum-pool-size", "java.lang.Integer", null, null); + data("spring.datasource.max-lifetime", "java.lang.Long", null, null); + data("spring.datasource.max-open-prepared-statements", "java.lang.Integer", null, null); + data("spring.datasource.max-wait", "java.lang.Integer", null, null); + data("spring.datasource.metric-registry", "java.lang.Object", null, null); + data("spring.datasource.min-evictable-idle-time-millis", "java.lang.Integer", null, null); + data("spring.datasource.min-idle", "java.lang.Integer", null, null); + data("spring.datasource.minimum-idle", "java.lang.Integer", null, null); + data("spring.datasource.name", "java.lang.String", null, null); + data("spring.datasource.num-tests-per-eviction-run", "java.lang.Integer", null, null); + data("spring.datasource.password", "java.lang.String", null, "Login password of the database."); + data("spring.datasource.platform", "java.lang.String", "all", "Platform to use in the schema resource (schema-${platform}.sql)."); + data("spring.datasource.pool-name", "java.lang.String", null, null); + data("spring.datasource.pool-prepared-statements", "java.lang.Boolean", null, null); + data("spring.datasource.propagate-interrupt-state", "java.lang.Boolean", null, null); + data("spring.datasource.read-only", "java.lang.Boolean", null, null); + data("spring.datasource.register-mbeans", "java.lang.Boolean", null, null); + data("spring.datasource.remove-abandoned", "java.lang.Boolean", null, null); + data("spring.datasource.remove-abandoned-timeout", "java.lang.Integer", null, null); + data("spring.datasource.rollback-on-return", "java.lang.Boolean", null, null); + data("spring.datasource.schema", "java.lang.String", null, "Schema (DDL) script resource reference."); + data("spring.datasource.separator", "java.lang.String", ";", "Statement separator in SQL initialization scripts."); + data("spring.datasource.sql-script-encoding", "java.lang.String", null, "SQL scripts encoding."); + data("spring.datasource.suspect-timeout", "java.lang.Integer", null, null); + data("spring.datasource.test-on-borrow", "java.lang.Boolean", null, null); + data("spring.datasource.test-on-connect", "java.lang.Boolean", null, null); + data("spring.datasource.test-on-return", "java.lang.Boolean", null, null); + data("spring.datasource.test-while-idle", "java.lang.Boolean", null, null); + data("spring.datasource.time-between-eviction-runs-millis", "java.lang.Integer", null, null); + data("spring.datasource.transaction-isolation", "java.lang.String", null, null); + data("spring.datasource.url", "java.lang.String", null, "JDBC url of the database."); + data("spring.datasource.use-disposable-connection-facade", "java.lang.Boolean", null, null); + data("spring.datasource.use-equals", "java.lang.Boolean", null, null); + data("spring.datasource.use-lock", "java.lang.Boolean", null, null); + data("spring.datasource.username", "java.lang.String", null, "Login user of the database."); + data("spring.datasource.validation-interval", "java.lang.Long", null, null); + data("spring.datasource.validation-query", "java.lang.String", null, null); + data("spring.datasource.validation-query-timeout", "java.lang.Integer", null, null); + data("spring.datasource.validator-class-name", "java.lang.String", null, null); + data("spring.datasource.xa.data-source-class-name", "java.lang.String", null, "XA datasource fully qualified name."); + data("spring.datasource.xa.properties", "java.util.Map", null, "Properties to pass to the XA data source."); + data("spring.freemarker.allow-request-override", "java.lang.Boolean", null, "Set whether HttpServletRequest attributes are allowed to override (hide) controller\n generated model attributes of the same name."); + data("spring.freemarker.cache", "java.lang.Boolean", null, "Enable template caching."); + data("spring.freemarker.char-set", "java.lang.String", null, null); + data("spring.freemarker.charset", "java.lang.String", null, "Template encoding."); + data("spring.freemarker.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists."); + data("spring.freemarker.content-type", "java.lang.String", null, "Content-Type value."); + data("spring.freemarker.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology."); + data("spring.freemarker.expose-request-attributes", "java.lang.Boolean", null, "Set whether all request attributes should be added to the model prior to merging\n with the template."); + data("spring.freemarker.expose-session-attributes", "java.lang.Boolean", null, "Set whether all HttpSession attributes should be added to the model prior to\n merging with the template."); + data("spring.freemarker.expose-spring-macro-helpers", "java.lang.Boolean", null, "Set whether to expose a RequestContext for use by Spring's macro library, under the\n name \"springMacroRequestContext\"."); + data("spring.freemarker.prefix", "java.lang.String", null, "Prefix that gets prepended to view names when building a URL."); + data("spring.freemarker.request-context-attribute", "java.lang.String", null, "Name of the RequestContext attribute for all views."); + data("spring.freemarker.settings", "java.util.Map", null, "Well-known FreeMarker keys which will be passed to FreeMarker's Configuration."); + data("spring.freemarker.suffix", "java.lang.String", null, "Suffix that gets appended to view names when building a URL."); + data("spring.freemarker.template-loader-path", "java.lang.String[]", new String[] {"snuzzle" ,"buggles"}, "Comma-separated list of template paths."); + data("spring.freemarker.view-names", "java.lang.String[]", null, "White list of view names that can be resolved."); + data("spring.groovy.template.cache", "java.lang.Boolean", null, "Enable template caching."); + data("spring.groovy.template.char-set", "java.lang.String", null, null); + data("spring.groovy.template.charset", "java.lang.String", null, "Template encoding."); + data("spring.groovy.template.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists."); + data("spring.groovy.template.configuration.auto-escape", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.auto-indent", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.auto-indent-string", "java.lang.String", null, null); + data("spring.groovy.template.configuration.auto-new-line", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.base-template-class", "java.lang.Class", null, null); + data("spring.groovy.template.configuration.cache-templates", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.declaration-encoding", "java.lang.String", null, null); + data("spring.groovy.template.configuration.expand-empty-elements", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration", "java.util.Map", null, "Configuration to pass to TemplateConfiguration."); + data("spring.groovy.template.configuration.locale", "java.util.Locale", null, null); + data("spring.groovy.template.configuration.new-line-string", "java.lang.String", null, null); + data("spring.groovy.template.configuration.resource-loader-path", "java.lang.String", null, null); + data("spring.groovy.template.configuration.use-double-quotes", "java.lang.Boolean", null, null); + data("spring.groovy.template.content-type", "java.lang.String", null, "Content-Type value."); + data("spring.groovy.template.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology."); + data("spring.groovy.template.prefix", "java.lang.String", "classpath:/templates/", "Prefix that gets prepended to view names when building a URL."); + data("spring.groovy.template.suffix", "java.lang.String", ".tpl", "Suffix that gets appended to view names when building a URL."); + data("spring.groovy.template.view-names", "java.lang.String[]", null, "White list of view names that can be resolved."); + data("spring.hornetq.embedded.cluster-password", "java.lang.String", null, "Cluster password. Randomly generated on startup by default"); + data("spring.hornetq.embedded.data-directory", "java.lang.String", null, "Journal file directory. Not necessary if persistence is turned off."); + data("spring.hornetq.embedded.enabled", "java.lang.Boolean", "true", "Enable embedded mode if the HornetQ server APIs are available."); + data("spring.hornetq.embedded.persistent", "java.lang.Boolean", "false", "Enable persistent store."); + data("spring.hornetq.embedded.queues", "java.lang.String[]", "[Ljava.lang.Object;@2f5ce114", "Comma-separate list of queues to create on startup."); + data("spring.hornetq.embedded.server-id", "java.lang.Integer", "0", "Server id. By default, an auto-incremented counter is used."); + data("spring.hornetq.embedded.topics", "java.lang.String[]", "[Ljava.lang.Object;@6272137a", "Comma-separate list of topics to create on startup."); + data("spring.hornetq.host", "java.lang.String", "localhost", "HornetQ broker host."); + data("spring.hornetq.mode", "org.springframework.boot.autoconfigure.jms.hornetq.HornetQMode", null, "HornetQ deployment mode, auto-detected by default. Can be explicitly set to\n \"native\" or \"embedded\"."); + data("spring.hornetq.port", "java.lang.Integer", "5445", "HornetQ broker port."); + data("spring.http.encoding.charset", "java.nio.charset.Charset", null, "Charset of HTTP requests and responses. Added to the \"Content-Type\" header if not\n set explicitly."); + data("spring.http.encoding.enabled", "java.lang.Boolean", "true", "Enable http encoding support."); + data("spring.http.encoding.force", "java.lang.Boolean", "true", "Force the encoding to the configured charset on HTTP requests and responses."); + data("spring.jackson.date-format", "java.lang.String", null, "Date format string (yyyy-MM-dd HH:mm:ss), or a fully-qualified date format class\n name."); + data("spring.jackson.deserialization", "java.util.Map", null, "Jackson on/off features that affect the way Java objects are deserialized."); + data("spring.jackson.generator", "java.util.Map", null, "Jackson on/off features for generators."); + data("spring.jackson.mapper", "java.util.Map", null, "Jackson general purpose on/off features."); + data("spring.jackson.parser", "java.util.Map", null, "Jackson on/off features for parsers."); + data("spring.jackson.property-naming-strategy", "java.lang.String", null, "One of the constants on Jackson's PropertyNamingStrategy\n (CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES). Can also be a fully-qualified class\n name of a PropertyNamingStrategy subclass."); + data("spring.jackson.serialization", "java.util.Map", null, "Jackson on/off features that affect the way Java objects are serialized."); + data("spring.jersey.filter.order", "java.lang.Integer", "0", "Jersey filter chain order."); + data("spring.jersey.init", "java.util.Map", null, "Init parameters to pass to Jersey."); + data("spring.jersey.type", "org.springframework.boot.autoconfigure.jersey.JerseyProperties$Type", null, "Jersey integration type. Can be either \"servlet\" or \"filter\"."); + data("spring.jms.jndi-name", "java.lang.String", null, "Connection factory JNDI name. When set, takes precedence to others connection\n factory auto-configurations."); + data("spring.jms.pub-sub-domain", "java.lang.Boolean", "false", "Specify if the default destination type is topic."); + data("spring.jmx.enabled", "java.lang.Boolean", "true", "Expose management beans to the JMX domain."); + data("spring.jpa.database", "org.springframework.orm.jpa.vendor.Database", null, "Target database to operate on, auto-detected by default. Can be alternatively set\n using the \"databasePlatform\" property."); + data("spring.jpa.database-platform", "java.lang.String", null, "Name of the target database to operate on, auto-detected by default. Can be\n alternatively set using the \"Database\" enum."); + data("spring.jpa.generate-ddl", "java.lang.Boolean", "false", "Initialize the schema on startup."); + data("spring.jpa.hibernate.ddl-auto", "java.lang.String", null, "DDL mode (\"none\", \"validate\", \"update\", \"create\", \"create-drop\"). This is\n actually a shortcut for the \"hibernate.hbm2ddl.auto\" property. Default to\n \"create-drop\" when using an embedded database, \"none\" otherwise."); + data("spring.jpa.hibernate.naming-strategy", "java.lang.Class", null, "Naming strategy fully qualified name."); + data("spring.jpa.open-in-view", "java.lang.Boolean", "true", "Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request."); + data("spring.jpa.properties", "java.util.Map", null, "Additional native properties to set on the JPA provider."); + data("spring.jpa.show-sql", "java.lang.Boolean", "false", "Enable logging of SQL statements."); + data("spring.jta.allow-multiple-lrc", "java.lang.Boolean", null, null); + data("spring.jta.asynchronous2-pc", "java.lang.Boolean", null, null); + data("spring.jta.background-recovery-interval", "java.lang.Integer", null, null); + data("spring.jta.background-recovery-interval-seconds", "java.lang.Integer", null, null); + data("spring.jta.current-node-only-recovery", "java.lang.Boolean", null, null); + data("spring.jta.debug-zero-resource-transaction", "java.lang.Boolean", null, null); + data("spring.jta.default-transaction-timeout", "java.lang.Integer", null, null); + data("spring.jta.disable-jmx", "java.lang.Boolean", null, null); + data("spring.jta.enabled", "java.lang.Boolean", "true", "Enable JTA support."); + data("spring.jta.exception-analyzer", "java.lang.String", null, null); + data("spring.jta.filter-log-status", "java.lang.Boolean", null, null); + data("spring.jta.force-batching-enabled", "java.lang.Boolean", null, null); + data("spring.jta.forced-write-enabled", "java.lang.Boolean", null, null); + data("spring.jta.graceful-shutdown-interval", "java.lang.Integer", null, null); + data("spring.jta.jndi-transaction-synchronization-registry-name", "java.lang.String", null, null); + data("spring.jta.jndi-user-transaction-name", "java.lang.String", null, null); + data("spring.jta.journal", "java.lang.String", null, null); + data("spring.jta.log-dir", "java.lang.String", null, "Transaction logs directory."); + data("spring.jta.log-part1-filename", "java.lang.String", null, null); + data("spring.jta.log-part2-filename", "java.lang.String", null, null); + data("spring.jta.max-log-size-in-mb", "java.lang.Integer", null, null); + data("spring.jta.resource-configuration-filename", "java.lang.String", null, null); + data("spring.jta.server-id", "java.lang.String", null, null); + data("spring.jta.skip-corrupted-logs", "java.lang.Boolean", null, null); + data("spring.jta.transaction-manager-id", "java.lang.String", null, "Transaction manager unique identifier."); + data("spring.jta.warn-about-zero-resource-transaction", "java.lang.Boolean", null, null); + data("spring.mail.default-encoding", "java.lang.String", "UTF-8", "Default MimeMessage encoding."); + data("spring.mail.host", "java.lang.String", null, "SMTP server host."); + data("spring.mail.password", "java.lang.String", null, "Login password of the SMTP server."); + data("spring.mail.port", "java.lang.Integer", null, "SMTP server port."); + data("spring.mail.properties", "java.util.Map", null, "Additional JavaMail session properties."); + data("spring.mail.username", "java.lang.String", null, "Login user of the SMTP server."); + data("spring.main.show-banner", "java.lang.Boolean", "true", "Display the banner when the application runs."); + data("spring.main.sources", "java.util.Set", null, "Sources (class name, package name or XML resource location) used to create the ApplicationContext."); + data("spring.main.web-environment", "java.lang.Boolean", null, "Run the application in a web environment (auto-detected by default)."); + data("spring.mandatory-file-encoding", "java.lang.String", null, "Expected character encoding the application must use."); + data("spring.messages.basename", "java.lang.String", "messages", "Comma-separated list of basenames, each following the ResourceBundle convention.\n Essentially a fully-qualified classpath location. If it doesn't contain a package\n qualifier (such as \"org.mypackage\"), it will be resolved from the classpath root."); + data("spring.messages.cache-seconds", "java.lang.Integer", "-1", "Loaded resource bundle files cache expiration, in seconds. When set to -1, bundles\n are cached forever."); + data("spring.messages.encoding", "java.lang.String", "utf-8", "Message bundles encoding."); + data("spring.mobile.devicedelegatingviewresolver.enabled", "java.lang.Boolean", "false", "Enable device view resolver."); + data("spring.mobile.devicedelegatingviewresolver.mobile-prefix", "java.lang.String", "mobile/", "Prefix that gets prepended to view names for mobile devices."); + data("spring.mobile.devicedelegatingviewresolver.mobile-suffix", "java.lang.String", "", "Suffix that gets appended to view names for mobile devices."); + data("spring.mobile.devicedelegatingviewresolver.normal-prefix", "java.lang.String", "", "Prefix that gets prepended to view names for normal devices."); + data("spring.mobile.devicedelegatingviewresolver.normal-suffix", "java.lang.String", "", "Suffix that gets appended to view names for normal devices."); + data("spring.mobile.devicedelegatingviewresolver.tablet-prefix", "java.lang.String", "tablet/", "Prefix that gets prepended to view names for tablet devices."); + data("spring.mobile.devicedelegatingviewresolver.tablet-suffix", "java.lang.String", "", "Suffix that gets appended to view names for tablet devices."); + data("spring.mobile.sitepreference.enabled", "java.lang.Boolean", "true", "Enable SitePreferenceHandler."); + data("spring.mvc.date-format", "java.lang.String", null, "Date format to use (e.g. dd/MM/yyyy)"); + data("spring.mvc.ignore-default-model-on-redirect", "java.lang.Boolean", "true", "If the the content of the \"default\" model should be ignored during redirect\n scenarios."); + data("spring.mvc.locale", "java.lang.String", null, "Locale to use."); + data("spring.mvc.message-codes-resolver-format", "org.springframework.validation.DefaultMessageCodesResolver$Format", null, "Formatting strategy for message codes (PREFIX_ERROR_CODE, POSTFIX_ERROR_CODE)."); + data("spring.profiles.active", "java.lang.String", null, "Comma-separated list of active profiles. Can be overridden by a command line switch."); + data("spring.profiles.include", "java.lang.String", null, "Unconditionally activate the specified comma separated profiles."); + data("spring.rabbitmq.addresses", "java.lang.String", null, "Comma-separated list of addresses to which the client should connect to."); + data("spring.rabbitmq.dynamic", "java.lang.Boolean", "true", "Create an AmqpAdmin bean."); + data("spring.rabbitmq.host", "java.lang.String", "localhost", "RabbitMQ host."); + data("spring.rabbitmq.password", "java.lang.String", null, "Login to authenticate against the broker."); + data("spring.rabbitmq.port", "java.lang.Integer", "5672", "RabbitMQ port."); + data("spring.rabbitmq.username", "java.lang.String", null, "Login user to authenticate to the broker."); + data("spring.rabbitmq.virtual-host", "java.lang.String", null, "Virtual host to use when connecting to the broker."); + data("spring.redis.database", "java.lang.Integer", "0", "Database index used by the connection factory."); + data("spring.redis.host", "java.lang.String", "localhost", "Redis server host."); + data("spring.redis.password", "java.lang.String", null, "Login password of the redis server."); + data("spring.redis.pool.max-active", "java.lang.Integer", "8", "Max number of connections that can be allocated by the pool at a given time.\n Use a negative value for no limit."); + data("spring.redis.pool.max-idle", "java.lang.Integer", "8", "Max number of \"idle\" connections in the pool. Use a negative value to indicate\n an unlimited number of idle connections."); + data("spring.redis.pool.max-wait", "java.lang.Integer", "-1", "Maximum amount of time (in milliseconds) a connection allocation should block\n before throwing an exception when the pool is exhausted. Use a negative value\n to block indefinitely."); + data("spring.redis.pool.min-idle", "java.lang.Integer", "0", "Target for the minimum number of idle connections to maintain in the pool. This\n setting only has an effect if it is positive."); + data("spring.redis.port", "java.lang.Integer", "6379", "Redis server port."); + data("spring.redis.sentinel.master", "java.lang.String", null, "Name of Redis server."); + data("spring.redis.sentinel.nodes", "java.lang.String", null, "Comma-separated list of host:port pairs."); + data("spring.resources.add-mappings", "java.lang.Boolean", "true", "Enable default resource handling."); + data("spring.resources.cache-period", "java.lang.Integer", null, "Cache period for the resources served by the resource handler, in seconds."); + data("spring.social.auto-connection-views", "java.lang.Boolean", "false", "Enable the connection status view for supported providers."); + data("spring.social.facebook.app-id", "java.lang.String", null, "Application id."); + data("spring.social.facebook.app-secret", "java.lang.String", null, "Application secret."); + data("spring.social.linkedin.app-id", "java.lang.String", null, "Application id."); + data("spring.social.linkedin.app-secret", "java.lang.String", null, "Application secret."); + data("spring.social.twitter.app-id", "java.lang.String", null, "Application id."); + data("spring.social.twitter.app-secret", "java.lang.String", null, "Application secret."); + data("spring.thymeleaf.cache", "java.lang.Boolean", "true", "Enable template caching."); + data("spring.thymeleaf.check-template-location", "java.lang.Boolean", "true", "Check that the templates location exists."); + data("spring.thymeleaf.content-type", "java.lang.String", "text/html", "Content-Type value."); + data("spring.thymeleaf.enabled", "java.lang.Boolean", "true", "Enable MVC Thymeleaf view resolution."); + data("spring.thymeleaf.encoding", "java.lang.String", "UTF-8", "Template encoding."); + data("spring.thymeleaf.excluded-view-names", "java.lang.String[]", null, "Comma-separated list of view names that should be excluded from resolution."); + data("spring.thymeleaf.mode", "java.lang.String", "HTML5", "Template mode to be applied to templates. See also StandardTemplateModeHandlers."); + data("spring.thymeleaf.prefix", "java.lang.String", "classpath:/templates/", "Prefix that gets prepended to view names when building a URL."); + data("spring.thymeleaf.suffix", "java.lang.String", ".html", "Suffix that gets appended to view names when building a URL."); + data("spring.thymeleaf.view-names", "java.lang.String[]", null, "Comma-separated list of view names that can be resolved."); + data("spring.velocity.allow-request-override", "java.lang.Boolean", null, "Set whether HttpServletRequest attributes are allowed to override (hide) controller\n generated model attributes of the same name."); + data("spring.velocity.cache", "java.lang.Boolean", null, "Enable template caching."); + data("spring.velocity.char-set", "java.lang.String", null, null); + data("spring.velocity.charset", "java.lang.String", null, "Template encoding."); + data("spring.velocity.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists."); + data("spring.velocity.content-type", "java.lang.String", null, "Content-Type value."); + data("spring.velocity.date-tool-attribute", "java.lang.String", null, "Name of the DateTool helper object to expose in the Velocity context of the view."); + data("spring.velocity.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology."); + data("spring.velocity.expose-request-attributes", "java.lang.Boolean", null, "Set whether all request attributes should be added to the model prior to merging\n with the template."); + data("spring.velocity.expose-session-attributes", "java.lang.Boolean", null, "Set whether all HttpSession attributes should be added to the model prior to\n merging with the template."); + data("spring.velocity.expose-spring-macro-helpers", "java.lang.Boolean", null, "Set whether to expose a RequestContext for use by Spring's macro library, under the\n name \"springMacroRequestContext\"."); + data("spring.velocity.number-tool-attribute", "java.lang.String", null, "Name of the NumberTool helper object to expose in the Velocity context of the view."); + data("spring.velocity.prefer-file-system-access", "java.lang.Boolean", "true", "Prefer file system access for template loading. File system access enables hot\n detection of template changes."); + data("spring.velocity.prefix", "java.lang.String", null, "Prefix that gets prepended to view names when building a URL."); + data("spring.velocity.properties", "java.util.Map", null, "Additional velocity properties."); + data("spring.velocity.request-context-attribute", "java.lang.String", null, "Name of the RequestContext attribute for all views."); + data("spring.velocity.resource-loader-path", "java.lang.String", "classpath:/templates/", "Template path."); + data("spring.velocity.suffix", "java.lang.String", null, "Suffix that gets appended to view names when building a URL."); + data("spring.velocity.toolbox-config-location", "java.lang.String", null, "Velocity Toolbox config location, for example \"/WEB-INF/toolbox.xml\". Automatically\n loads a Velocity Tools toolbox definition file and expose all defined tools in the\n specified scopes."); + data("spring.velocity.view-names", "java.lang.String[]", null, "White list of view names that can be resolved."); + data("spring.view.prefix", "java.lang.String", null, "Spring MVC view prefix."); + data("spring.view.suffix", "java.lang.String", null, "Spring MVC view suffix."); + } + + public boolean isEmpty() { + return datas == null || datas.isEmpty(); + } + + public SpringPropertyIndexProvider getIndexProvider() { + return indexProvider; + } + +} diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/StyledStringMatcher.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/StyledStringMatcher.java new file mode 100644 index 000000000..c04fa5e50 --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/StyledStringMatcher.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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.eclipse.jface.viewers.StyledString; +//import org.eclipse.swt.custom.StyleRange; + +public abstract class StyledStringMatcher { + +// public abstract void match(StyledString styledString) throws Exception; + + /** + * Creates a matcher that matches a given {@link StyledString} if a given String + * equals the concatenation of all the text in the {@link StyledString} formatted + * in 'plain' font (currently 'plain' just means that it is not using 'strikeout' + * as that is the only other style we currently care about). + */ + public static StyledStringMatcher plainFont(final String string) { + throw new UnsupportedOperationException("Not yet implemented"); +// return new StyledStringMatcher() { +// @Override +// public String toString() { +// return "plain("+string+")"; +// } +// +// @Override +// public void match(StyledString styledString) throws Exception { +// StringBuilder extractedText = new StringBuilder(); +// String string = styledString.getString(); +// for (StyleRange styleRange : styledString.getStyleRanges()) { +// if (!styleRange.strikeout) { +// extractedText.append(string.substring(styleRange.start, styleRange.start + styleRange.length)); +// } +// } +// assertEquals(string, extractedText.toString()); +// } +// }; + } + + /** + * Creates a matcher that matches a given {@link StyledString} if a given String + * equals the concatenation of all the text in the {@link StyledString} formatted + * in 'strikeout' font. + */ + public static StyledStringMatcher strikeout(final String expectedString) { + throw new UnsupportedOperationException("Not yet implemented"); +// return new StyledStringMatcher() { +// @Override +// public String toString() { +// return "strikeout("+expectedString+")"; +// } +// @Override +// public void match(StyledString styledString) throws Exception { +// StringBuilder extractedText = new StringBuilder(); +// String unstyledString = styledString.getString(); +// for (StyleRange styleRange : styledString.getStyleRanges()) { +// if (styleRange.strikeout) { +// extractedText.append(unstyledString.substring(styleRange.start, styleRange.start + styleRange.length)); +// } +// } +// assertEquals(expectedString, extractedText.toString()); +// } +// }; + } + +} diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTest.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTests.java similarity index 56% rename from vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTest.java rename to vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTests.java index bf65645f9..af694eaab 100644 --- a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTest.java +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/yaml/YamlLanguageServerTests.java @@ -18,10 +18,10 @@ import io.typefox.lsapi.InitializeResult; import io.typefox.lsapi.ServerCapabilities; import io.typefox.lsapi.TextDocumentSyncKind; -public class YamlLanguageServerTest { +public class YamlLanguageServerTests { public static File getTestResource(String name) throws URISyntaxException { - return Paths.get(YamlLanguageServerTest.class.getResource(name).toURI()).toFile(); + return Paths.get(YamlLanguageServerTests.class.getResource(name).toURI()).toFile(); } @Test @@ -39,34 +39,6 @@ public class YamlLanguageServerTest { } - @Test public void completions() throws Exception { - LanguageServerHarness harness = new LanguageServerHarness(YamlLanguageServer::new); - - File workspaceRoot = getTestResource("/workspace/"); - assertExpectedInitResult(harness.intialize(workspaceRoot)); - - TextDocumentInfo doc = harness.openDocument(getTestResource("/workspace/testfile.yml")); - - CompletionList completions = harness.getCompletions(doc, doc.positionOf("foo")); - assertThat(completions.isIncomplete()).isFalse(); - assertThat(completions.getItems()) - .extracting(CompletionItem::getLabel) - .containsExactly("TypeScript", "JavaScript"); - - List resolved = harness.resolveCompletions(completions); - assertThat(resolved) - .extracting(CompletionItem::getLabel) - .containsExactly("TypeScript", "JavaScript"); - - assertThat(resolved) - .extracting(CompletionItem::getDetail) - .containsExactly("TypeScript details", "JavaScript details"); - - assertThat(resolved) - .extracting(CompletionItem::getDocumentation) - .containsExactly("TypeScript docs", "JavaScript docs"); - } - private void assertExpectedInitResult(InitializeResult initResult) { assertThat(initResult.getCapabilities().getCompletionProvider().getResolveProvider()).isTrue(); assertThat(initResult.getCapabilities().getTextDocumentSync()).isEqualTo(TextDocumentSyncKind.Full);