/*******************************************************************************
* Copyright (c) 2015, 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.eclipse.boot.properties.editor.test;
import java.time.Duration;
import org.eclipse.core.resources.IProject;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.springframework.ide.eclipse.boot.properties.editor.metadata.CachingValueProvider;
import org.springframework.ide.eclipse.boot.properties.editor.metadata.PropertyInfo;
import org.springframework.ide.eclipse.editor.support.reconcile.ReconcileProblem;
import org.springframework.ide.eclipse.editor.support.util.StringUtil;
/**
* @author Kris De Volder
*/
public class YamlEditorTests extends ApplicationYamlEditorTestHarness {
public void testHovers() throws Exception {
defaultTestData();
MockEditor editor = new YamlEditor(
"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", "<b>spring.application.name</b>");
editor.assertHoverContains("port", "<b>server.port</b>");
editor.assertHoverContains("8888", "<b>server.port</b>"); // 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 = new YamlEditor(
"- syntax\n" +
"error:\n"
);
editor.assertNoHover("syntax");
editor.assertNoHover("error");
}
public void testHoverInfoForEnumValueInMapKey() throws Exception {
MockEditor editor;
IJavaProject project = JavaCore.create(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 = new YamlEditor(
"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 = new YamlEditor(
"spring:\n" +
" jackson:\n" +
" serialization:\n" +
" INDENT_OUTPUT: true"
);
editor.assertIsHoverRegion("INDENT_OUTPUT");
editor.assertHoverContains("INDENT_OUTPUT", "allows enabling (or disabling) indentation");
}
public void testHoverInfoForEnumValueInMapKeyCompletion() throws Exception {
IJavaProject project = JavaCore.create(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"
);
}
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"
);
}
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?");
YamlEditor editor = new YamlEditor(
"#comment here\n" +
"my:\n" +
" bonus: large\n"
);
editor.assertIsHoverRegion("large");
editor.assertHoverContains("large", "For the ones who deserve it");
}
public void testUserDefinedHoversandLinkTargets() throws Exception {
useProject(createPredefinedMavenProject("demo-enum"));
data("foo.link-tester", "demo.LinkTestSubject", null, "for testing different Pojo link cases");
MockEditor editor = new YamlEditor(
"#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
assertLinkTargets(editor, "data", "demo.FooProperties.setData(ColorData)");
assertLinkTargets(editor, "wavelen", "demo.ColorData.setWavelen(double)");
}
public void testHyperlinkTargets() throws Exception {
System.out.println(">>> testHyperlinkTargets");
IProject p = createPredefinedMavenProject("demo");
IJavaProject jp = JavaCore.create(p);
useProject(jp);
MockEditor editor = new YamlEditor(
"server:\n"+
" port: 888\n" +
"spring:\n" +
" datasource:\n" +
" login-timeout: 1000\n" +
"flyway:\n" +
" init-sqls: a,b,c\n"
);
assertLinkTargets(editor, "port",
"org.springframework.boot.autoconfigure.web.ServerProperties.setPort(Integer)"
);
assertLinkTargets(editor, "login-",
"org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.hikariDataSource()",
"org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.tomcatDataSource()",
"org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.dbcpDataSource()"
);
assertLinkTargets(editor, "init-sql",
"org.springframework.boot.autoconfigure.flyway.FlywayProperties.setInitSqls(List<String>)");
System.out.println("<<< testHyperlinkTargets");
}
public void testReconcile() throws Exception {
defaultTestData();
YamlEditor editor = new YamlEditor(
"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"
);
assertProblems(editor,
"extracrap: 8080|Expecting a 'int' but got a 'Mapping' node",
"snuggem|Unknown property",
"bogus|Unknown property"
);
}
public void test_STS_4140_StringArrayReconciling() throws Exception {
defaultTestData();
YamlEditor editor;
//Case 0: very focussed test for easy debugging
editor = new YamlEditor(
"flyway:\n" +
" schemas: [MEMBER_VERSION, MEMBER]"
);
assertProblems(editor /*none*/);
//Case 1: String[] as a flow sequence
editor = new YamlEditor(
"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]"
);
assertProblems(editor,
"something-bad|Unknown property"
//No other problems should be reported
);
//Case 1: String[] as a block sequence
editor = new YamlEditor(
"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"
);
assertProblems(editor,
"something-bad|Unknown property"
//No other problems should be reported
);
}
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"
);
}
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");
YamlEditor editor = new YamlEditor(
"server:\n" +
" port: \n" +
" 8888\n" +
" threads:\n" +
" not-a-number\n"
);
assertProblems(editor,
"not-a-number|Expecting a 'int'"
);
}
public void testReconcileExpectMapping() throws Exception {
defaultTestData();
YamlEditor editor = new YamlEditor(
"server:\n" +
" - a\n" +
" - b\n"
);
assertProblems(editor,
"- a\n - b|Expecting a 'Mapping' node but got a 'Sequence' node"
);
}
public void testReconcileExpectScalar() throws Exception {
defaultTestData();
YamlEditor editor = new YamlEditor(
"server:\n" +
" ? - a\n" +
" - b\n" +
" : c"
);
assertProblems(editor,
"- a\n - b|Expecting a 'Scalar' node but got a 'Sequence' node"
);
}
public void testReconcileCamelCaseBasic() throws Exception {
YamlEditor 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 = new YamlEditor(
"#Comment for good measure\n" +
"something:\n" +
" with-many-parts: not-a-number"
);
assertProblems(editor,
"not-a-number|Expecting a 'int'"
);
//Now check that reconcile also tolerates camel case and reports no error for it
editor = new YamlEditor(
"#Comment for good measure\n" +
"something:\n" +
" withManyParts: 123"
);
assertProblems(editor
/*no problems*/
);
//Now check that reconciler traverses camelCase and reports errors assigning to camelCase names
editor = new YamlEditor(
"#Comment for good measure\n" +
"something:\n" +
" withManyParts: not-a-number"
);
assertProblems(editor,
"not-a-number|Expecting a 'int'"
);
//Now check also that camelcase tolerance works if its not in the end of the path
editor = new YamlEditor(
"#Comment for good measure\n" +
"something:\n" +
" withParts:\n" +
" andMore: not-a-number\n" +
" bad: wrong\n" +
" something-bad: also wrong\n"
);
assertProblems(editor,
"not-a-number|Expecting a 'int'",
"bad|Unknown property",
"something-bad|Unknown property"
);
}
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: <*>"
);
}
public void testReconcileCamelCaseBeanProp() throws Exception {
YamlEditor editor;
IProject p = createPredefinedMavenProject("demo");
IJavaProject jp = JavaCore.create(p);
useProject(jp);
data("demo.bean", "demo.CamelCaser", "For testing tolerance of camelCase", null);
//Nothing special... not using camel case
editor = new YamlEditor(
"#Comment for good measure\n" +
"demo:\n" +
" bean:\n"+
" the-answer-to-everything: not-a-number\n"
);
assertProblems(editor,
"not-a-number|Expecting a 'int'"
);
//Now check that reconcile also tolerates camel case and reports no error for it
editor = new YamlEditor(
"#Comment for good measure\n" +
"demo:\n" +
" bean:\n"+
" theAnswerToEverything: 42\n"
);
assertProblems(editor
/*no problems*/
);
//Now check that reconciler traverses camelCase and reports errors assigning to camelCase names
editor = new YamlEditor(
"#Comment for good measure\n" +
"demo:\n" +
" bean:\n"+
" theAnswerToEverything: not-a-number\n"
);
assertProblems(editor,
"not-a-number|Expecting a 'int'"
);
//Now check also that camelcase tolerance works if its not in the end of the path
editor = new YamlEditor(
"#Comment for good measure\n" +
"demo:\n" +
" bean:\n"+
" theAnswerToEverything: not-a-number\n"+
" theLeft:\n"+
" bad: wrong\n"+
" theAnswerToEverything: not-this\n"
);
assertProblems(editor,
"not-a-number|Expecting a 'int'",
"bad|Unknown property",
"not-this|Expecting a 'int'"
);
}
public void testContentAssistCamelCaseBeanProp() throws Exception {
IProject p = createPredefinedMavenProject("demo");
IJavaProject jp = JavaCore.create(p);
useProject(jp);
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"
);
}
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"
);
}
public void testReconcileBeanPropName() throws Exception {
IProject p = createPredefinedMavenProject("demo-list-of-pojo");
IJavaProject jp = JavaCore.create(p);
useProject(jp);
assertNotNull(jp.findType("demo.Foo"));
data("some-foo", "demo.Foo", null, "some Foo pojo property");
YamlEditor editor = new YamlEditor(
"some-foo:\n" +
" name: Good\n" +
" bogus: Bad\n" +
" ? - a\n"+
" - b\n"+
" : Weird\n"
);
assertProblems(editor,
"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"
);
}
public void testIgnoreSpringExpression() throws Exception {
defaultTestData();
YamlEditor editor = new YamlEditor(
"server:\n" +
" port: ${random.int}\n" + //should not be an error
" bad: wrong\n"
);
assertProblems(editor,
"bad|Unknown property"
);
}
public void testReconcilePojoArray() throws Exception {
IProject p = createPredefinedMavenProject("demo-list-of-pojo");
IJavaProject jp = JavaCore.create(p);
useProject(jp);
assertNotNull(jp.findType("demo.Foo"));
{
YamlEditor editor = new YamlEditor(
"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"
);
assertProblems(editor,
"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
YamlEditor editor = new YamlEditor(
"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"
);
assertProblems(editor,
"token-bad-guy|Unknown property",
"one|Expecting a 'int' but got 'one'",
"bogus|Unknown property 'bogus' for type 'demo.Foo'"
);
}
}
public void testReconcileSequenceGotAtomicType() throws Exception {
defaultTestData();
YamlEditor editor = new YamlEditor(
"liquibase:\n" +
" enabled:\n" +
" - element\n"
);
assertProblems(editor,
"- element|Expecting a 'boolean' but got a 'Sequence' node"
);
}
public void testReconcileSequenceGotMapType() throws Exception {
data("the-map", "java.util.Map<java.lang.String,java.lang.String>", null, "Nice mappy");
YamlEditor editor = new YamlEditor(
"the-map:\n" +
" - a\n" +
" - b\n"
);
assertProblems(editor,
"- a\n - b|Expecting a 'Map<String, String>' but got a 'Sequence' node"
);
}
public void testEnumPropertyReconciling() throws Exception {
IProject p = createPredefinedMavenProject("demo-enum");
IJavaProject jp = JavaCore.create(p);
useProject(jp);
assertNotNull(jp.findType("demo.Color"));
data("foo.color", "demo.Color", null, "A foonky colour");
YamlEditor editor = new YamlEditor(
"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"
);
assertProblems(editor,
"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'"
);
}
public void testReconcileSkipIfNoMetadata() throws Exception {
YamlEditor editor = new YamlEditor(
"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(datas.isEmpty());
assertProblems(editor
//nothing
);
}
public void testReconcileCatchesParseError() throws Exception {
defaultTestData();
YamlEditor editor = new YamlEditor(
"somemap: val\n"+
"- sequence"
);
assertProblems(editor,
"-|expected <block end>"
);
}
public void testReconcileCatchesScannerError() throws Exception {
defaultTestData();
YamlEditor editor = new YamlEditor(
"somemap: \"quotes not closed\n"
);
assertProblems(editor,
"|unexpected end of stream"
);
}
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
);
}
public void testContentAssistNullContext() throws Exception {
defaultTestData();
assertCompletions(
"#A comment\n" +
"foo:\n" +
" data:\n" +
" bogus:\n" +
" <*>"
// => nothing
);
}
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"
);
}
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: <*>"
);
}
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"
);
}
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"
);
}
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"
);
}
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"
);
}
public void testContentAssistPropertyWithMapType() throws Exception {
data("foo.mapping", "java.util.Map<java.lang.String,java.lang.String>", 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"
);
}
public void testContentAssistPropertyWithArrayType() throws Exception {
data("foo.list", "java.util.List<java.lang.String>", 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"
);
}
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"
);
}
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"
);
}
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"
);
}
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"
);
}
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<*>"
);
}
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: <*>"
);
}
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"
);
}
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<*>"
);
}
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"
);
}
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<*>"
);
}
public void testEnumMapValueReconciling() throws Exception {
useProject(createPredefinedMavenProject("demo-enum"));
data("foo.name-colors", "java.util.Map<java.lang.String,demo.Color>", null, "Map with colors in its values");
YamlEditor editor;
editor = new YamlEditor(
"foo:\n"+
" name-colors:\n" +
" jacket: BLUE\n" +
" hat: RED\n" +
" pants: GREEN\n" +
" wrong: NOT_A_COLOR\n"
);
assertProblems(editor,
"NOT_A_COLOR|Color"
);
//lowercase enums should work too
editor = new YamlEditor(
"foo:\n"+
" name-colors:\n" +
" jacket: blue\n" +
" hat: red\n" +
" pants: green\n" +
" wrong: NOT_A_COLOR\n"
);
assertProblems(editor,
"NOT_A_COLOR|Color"
);
}
public void testEnumMapKeyCompletion() throws Exception {
useProject(createPredefinedMavenProject("demo-enum"));
data("foo.color-names", "java.util.Map<demo.Color,java.lang.String>", null, "Map with colors in its keys");
data("foo.color-data", "java.util.Map<demo.Color,demo.ColorData>", 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"
);
}
public void testPojoReconciling() throws Exception {
useProject(createPredefinedMavenProject("demo-enum"));
YamlEditor editor = new YamlEditor(
"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"
);
assertProblems(editor,
"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"
);
}
public void testListOfAtomicCompletions() throws Exception {
data("foo.slist", "java.util.List<java.lang.String>", null, "list of strings");
data("foo.ulist", "java.util.List<Unknown>", null, "list of strings");
data("foo.dlist", "java.util.List<java.lang.Double>", 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" +
" - <*>");
}
public void testEnumsInLowerCaseReconciling() throws Exception {
useProject(createPredefinedMavenProject("demo-enum"));
data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size");
YamlEditor editor = new YamlEditor(
"simple:\n" +
" pants:\n"+
" size: NOT_A_SIZE\n"+
" size: EXTRA_SMALL\n"+
" size: extra-small\n"+
" size: small\n"+
" size: SMALL\n"
);
assertProblems(editor,
"size|Duplicate",
"NOT_A_SIZE|ClothingSize",
"size|Duplicate",
"size|Duplicate",
"size|Duplicate",
"size|Duplicate"
);
editor = new YamlEditor(
"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"
);
assertProblems(editor,
"blue|Duplicate",
"not-a-color|Color",
"blue.bad|Color",
"blue|Duplicate",
"bad: Blauw|Expecting a 'String' but got a 'Mapping'"
);
editor = new YamlEditor(
"foo:\n" +
" color-data:\n"+
" red:\n" +
" next: green\n" +
" next: not a color\n" +
" bogus: green\n" +
" name: Rood\n"
);
assertProblems(editor,
"next|Duplicate",
"next|Duplicate",
"not a color|Color",
"bogus|Unknown property"
);
}
public void testEnumsInLowerCaseContentAssist() throws Exception {
IProject p = createPredefinedMavenProject("demo-enum");
IJavaProject jp = JavaCore.create(p);
useProject(jp);
assertNotNull(jp.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: <*>"
);
}
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: <*>"
);
}
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"
);
}
public void testDocumentSeparator() throws Exception {
defaultTestData();
assertCompletion(
"flyway:\n" +
" encoding: utf8\n" +
"---\n" +
"flyena<*>",
// =>
"flyway:\n" +
" encoding: utf8\n" +
"---\n" +
"flyway:\n" +
" enabled: <*>"
);
}
public void testMultiProfileYamlReconcile() throws Exception {
YamlEditor editor;
//See https://issuetracker.springsource.com/browse/STS-4144
defaultTestData();
//Narrowly focussed test-case for easier debugging
editor = new YamlEditor(
"spring:\n" +
" profiles: seven\n"
);
assertProblems(editor
/*none*/
);
//More complete test
editor = new YamlEditor(
"spring:\n" +
" profiles: seven\n" +
"server:\n" +
" port: 7777\n" +
"---\n" +
"spring:\n" +
" profiles: eight\n" +
"server:\n" +
" port: 8888\n"+
"bogus: bad"
);
assertProblems(editor,
"bogus|Unknown property"
);
}
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<java.lang.Integer>", null, "A list of boxed types");
YamlEditor editor = new YamlEditor(
"my:\n" +
" array:\n" +
" - 777\n" +
" - bad\n" +
" - 888"
);
assertProblems(editor,
"bad|Expecting a 'int'"
);
editor = new YamlEditor(
"my:\n" +
" boxarray:\n" +
" - 777\n" +
" - bad\n" +
" - 888"
);
assertProblems(editor,
"bad|Expecting a 'int'"
);
editor = new YamlEditor(
"my:\n" +
" list:\n" +
" - 777\n" +
" - bad\n" +
" - 888"
);
assertProblems(editor,
"bad|Expecting a 'int'"
);
}
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());
// IProject 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:
IProject 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"
);
}
public void test_STS_4254_MapStringObjectReconciling() throws Exception {
YamlEditor editor;
data("info", "java.util.Map<java.lang.String,java.lang.Object>", null, "Info for the actuator's info endpoint.");
editor = new YamlEditor(
"info: not-a-map\n"
);
assertProblems(editor,
"not-a-map|Expecting a 'Map<String, Object>'"
);
editor= new YamlEditor(
"info:\n" +
" build: \n" +
" artifact: foo-bar\n"
);
assertProblems(editor
/*nothing*/
);
editor= new YamlEditor(
"info:\n" +
" more: \n" +
" deeply:\n" +
" nested: foo-bar\n"
);
assertProblems(editor
/*nothing*/
);
editor= new YamlEditor(
"booger: Bad\n" +
"info:\n" +
" akey: avalue\n"
);
assertProblems(editor,
"booger|Unknown property"
);
}
public void test_STS_4254_MapStringStringReconciling() throws Exception {
YamlEditor editor;
data("info", "java.util.Map<java.lang.String,java.lang.String>", null, "Info for the actuator's info endpoint.");
editor= new YamlEditor(
"info:\n" +
" build: \n" +
" artifact: foo-bar\n"
);
assertProblems(editor
/*no problems*/
);
editor= new YamlEditor(
"info:\n" +
" more: \n" +
" deeply:\n" +
" nested: foo-bar\n"
);
assertProblems(editor
/*no problems*/
);
editor= new YamlEditor(
"booger: Bad\n" +
"info:\n" +
" akey: avalue\n"
);
assertProblems(editor,
"booger|Unknown property"
);
}
public void test_STS_4254_MapStringIntegerReconciling() throws Exception {
YamlEditor editor;
data("info", "java.util.Map<java.lang.String,java.lang.Integer>", null, "Info for the actuator's info endpoint.");
editor= new YamlEditor(
"info:\n" +
" build: \n" +
" foo-bar\n"
);
assertProblems(editor,
"foo-bar|Expecting a 'int'"
);
editor= new YamlEditor(
"info:\n" +
" build: 123\n"
);
assertProblems(editor
/*no problems*/
);
editor= new YamlEditor(
"info:\n" +
" build: \n" +
" number: 123\n"
);
assertProblems(editor
/*no problems*/
);
editor= new YamlEditor(
"info:\n" +
" build: \n" +
" artifact: abc\n"
);
assertProblems(editor,
"abc|Expecting a 'int'"
);
//A more complex example for good measure
editor= new YamlEditor(
"info:\n" +
" some: \n" +
" nested: foo\n" +
" and: bar\n" +
" or: 444\n" +
" also: bad\n"
);
assertProblems(editor,
"foo|Expecting a 'int'",
"bar|Expecting a 'int'",
"bad|Expecting a 'int'"
);
}
public void testDeprecatedReconcileProperty() throws Exception {
data("error.path", "java.lang.String", null, "Path of the error controller.");
YamlEditor editor = new YamlEditor(
"# a comment\n"+
"error:\n"+
" path: foo\n"
);
deprecate("error.path", "server.error.path", null);
assertProblems(editor,
"path|'error.path' is Deprecated: Use 'server.error.path'"
//no other problems
);
deprecate("error.path", "server.error.path", "This is old.");
assertProblems(editor,
"path|'error.path' is Deprecated: Use 'server.error.path' instead. Reason: This is old."
//no other problems
);
deprecate("error.path", null, "This is old.");
assertProblems(editor,
"path|'error.path' is Deprecated: This is old."
//no other problems
);
deprecate("error.path", null, null);
assertProblems(editor,
"path|'error.path' is Deprecated!"
//no other problems
);
}
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")
);
}
public void testDeprecatedPropertyHoverInfo() throws Exception {
data("error.path", "java.lang.String", null, "Path of the error controller.");
MockEditor editor = new YamlEditor(
"# a comment\n"+
"error:\n" +
" path: foo\n"
);
deprecate("error.path", "server.error.path", null);
editor.assertHoverContains("path", "<s>error.path</s> -> server.error.path");
editor.assertHoverContains("path", "<b>Deprecated!</b>");
deprecate("error.path", "server.error.path", "This is old.");
editor.assertHoverContains("path", "<s>error.path</s> -> server.error.path");
editor.assertHoverContains("path", "<b>Deprecated: </b>This is old");
deprecate("error.path", null, "This is old.");
editor.assertHoverContains("path", "<b>Deprecated: </b>This is old");
deprecate("error.path", null, null);
editor.assertHoverContains("path", "<b>Deprecated!</b>");
}
public void testDeprecatedBeanPropertyHoverInfo() throws Exception {
IProject jp = createPredefinedMavenProject("demo");
useProject(jp);
data("foo", "demo.Deprecater", null, "A bean with deprecated property.");
MockEditor editor = new YamlEditor(
"# a comment\n"+
"foo:\n" +
" name: foo\n"
);
editor.assertHoverContains("name", "<b>Deprecated!</b>");
}
public void testDeprecatedBeanPropertyReconcile() throws Exception {
IProject jp = createPredefinedMavenProject("demo");
useProject(jp);
data("foo", "demo.Deprecater", null, "A Bean with deprecated properties");
YamlEditor editor = new YamlEditor(
"# comment\n" +
"foo:\n" +
" name: Old faithfull\n" +
" new-name: New and fancy\n" +
" alt-name: alternate\n"
);
assertProblems(editor,
"name|Property 'name' of type 'demo.Deprecater' is Deprecated!",
"alt-name|Deprecated"
);
editor = new YamlEditor(
"# comment\n" +
"foo:\n" +
" alt-name: alternate\n"
);
//check that message also contains reason and replacement infos.
assertProblems(editor,
"alt-name|Use 'something.else' instead"
);
assertProblems(editor,
"alt-name|No good anymore"
);
}
public void testDeprecatedBeanPropertyCompletions() throws Exception {
IProject 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")
);
}
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);
{
YamlEditor editor = newEditor(
"# a comment\n"+
"my:\n" +
" old-name: foo\n"
);
ReconcileProblem problem = assertProblem(editor, "old-name");
ICompletionProposal fix = assertFirstQuickfix(editor, problem, "Change to 'my.new-name'");
editor.apply(fix);
editor.assertText(
"# a comment\n"+
"my:\n"+
" new-name: foo\n"
);
}
{
MockEditor editor = newEditor(
"# a comment\n"+
"my:\n" +
" old-name: foo\n" +
"your: stuff"
);
ReconcileProblem problem = assertProblem(editor, "old-name");
ICompletionProposal fix = assertFirstQuickfix(editor, 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
MockEditor editor = newEditor(
"# a comment\n"+
"my:\n" +
" old-name: foo\n" +
" other: bar\n"
);
ReconcileProblem problem = assertProblem(editor, "old-name");
ICompletionProposal fix = assertFirstQuickfix(editor, problem, "Change to 'my.new-name'");
editor.apply(fix);
editor.assertText(
"# a comment\n"+
"my:\n"+
" new-name: foo\n" +
" other: bar\n"
);
}
}
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);
{
MockEditor editor = newEditor(
"# a comment\n"+
"my:\n" +
" long:\n"+
" path:\n"+
" with:\n"+
" many:\n"+
" pieces: foo\n"
);
ReconcileProblem problem = assertProblem(editor, "pieces");
ICompletionProposal fix = assertFirstQuickfix(editor, 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"
);
}
{
MockEditor editor = newEditor(
"# a comment\n"+
"my:\n" +
" long:\n"+
" path:\n"+
" with:\n"+
" many:\n"+
" cannot: remove this\n"+
" pieces: foo\n"
);
ReconcileProblem problem = assertProblem(editor, "pieces");
ICompletionProposal fix = assertFirstQuickfix(editor, 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"
);
}
{
MockEditor editor = newEditor(
"# a comment\n"+
"my:\n" +
" stuff: foo\n"
);
ReconcileProblem problem = assertProblem(editor, "stuff");
ICompletionProposal fix = assertFirstQuickfix(editor, problem, "Change to 'my.for-sale.stuff'");
editor.apply(fix);
editor.assertText(
"# a comment\n"+
"my:\n" +
" for-sale:\n"+
" stuff: foo"
);
}
{
MockEditor editor = newEditor(
"# a comment\n"+
"my:\n" +
" old-name: foo\n"
);
ReconcileProblem problem = assertProblem(editor, "old-name");
ICompletionProposal fix = assertFirstQuickfix(editor, problem, "Change to 'your.new-name'");
editor.apply(fix);
editor.assertText(
"# a comment\n"+
"your:\n"+
" new-name: foo"
);
}
{
MockEditor editor = newEditor(
"# a comment\n"+
"my:\n" +
" old-name: foo\n" +
"your:\n" +
" goodies: nice"
);
ReconcileProblem problem = assertProblem(editor, "old-name");
ICompletionProposal fix = assertFirstQuickfix(editor, problem, "Change to 'your.new-name'");
editor.apply(fix);
editor.assertText(
"# a comment\n"+
"your:\n"+
" goodies: nice\n"+
" new-name: foo"
);
}
{
MockEditor editor = newEditor(
"# a comment\n"+
"your:\n" +
" goodies: nice\n" +
"my:\n" +
" other: stuff\n" +
" old-name: foo\n"
);
ReconcileProblem problem = assertProblem(editor, "old-name");
ICompletionProposal fix = assertFirstQuickfix(editor, 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"
);
}
}
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
{
MockEditor 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"
);
ReconcileProblem problem = assertProblem(editor, "pieces");
ICompletionProposal fix = assertFirstQuickfix(editor, 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"
);
}
{
MockEditor 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"
);
ReconcileProblem problem = assertProblem(editor, "stuff");
ICompletionProposal fix = assertFirstQuickfix(editor, 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"
);
}
{
MockEditor 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"
);
ReconcileProblem problem = assertProblem(editor, "pieces");
ICompletionProposal fix = assertFirstQuickfix(editor, 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"
);
}
}
public void testReconcileDuplicateProperties() throws Exception {
defaultTestData();
YamlEditor editor = new YamlEditor(
"spring:\n" +
" profiles: cloudfoundry\n" +
"spring: \n" +
" application:\n" +
" name: eureka"
);
assertProblems(editor,
"spring|Duplicate",
"spring|Duplicate"
);
}
public void testReconcileDuplicatePropertiesNested() throws Exception {
data("foo.person.name", "String", null, "Name of person");
data("foo.person.family", "String", null, "Family name of person");
YamlEditor editor = new YamlEditor(
"foo:\n" +
" person:\n" +
" name: Hohohoh\n" +
" person:\n" +
" family:\n"
);
assertProblems(editor,
"person|Duplicate",
"person|Duplicate"
);
}
public void testReconcileDuplicatePropertiesInBean() throws Exception {
useProject(createPredefinedMavenProject("demo-enum"));
data("some.color", "demo.ColorData", null, "Some info about a color.");
YamlEditor editor = new YamlEditor(
"some:\n" +
" color:\n" +
" name: RED\n" +
" name: GREEN\n"
);
assertProblems(editor,
"name|Duplicate",
"name|Duplicate"
);
}
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<*>"
);
}
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<*>"
);
}
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<*>"
);
}
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"
);
}
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"
);
}
public void testPropertyMapValueCompletions() throws Exception {
useProject(createPredefinedMavenProject("boot13"));
assertCompletionsDisplayString(
"logging:\n" +
" level:\n" +
" some.package: <*>"
, // =>
"trace",
"debug",
"info",
"warn",
"error",
"fatal",
"off"
);
}
public void testPropertyMapKeyCompletions() throws Exception {
useProject(createPredefinedMavenProject("boot13"));
assertCompletionWithLabel(
"logging:\n" +
" level:\n" +
" <*>"
, // =>
"root : String"
,
"logging:\n" +
" level:\n" +
" root: <*>"
);
}
public void testEscapeStringValueStartingWithStar() throws Exception {
useProject(createPredefinedMavenProject("boot13"));
assertCompletions(
"endpoints:\n"+
" cors:\n"+
" allowed-headers: \n" +
" - <*>"
, // =>
"endpoints:\n"+
" cors:\n"+
" allowed-headers: \n" +
" - '*'<*>"
);
}
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: '`'<*>"
);
}
public void testEscapeStringKeyWithAQuote() throws Exception {
data("foo.quote", "java.util.Map<java.lang.String,java.lang.String>", null, "Name of quote characters");
keyHints("foo.quote", "\"", "'", "`");
assertCompletions(
"foo:\n" +
" quote: <*>"
, // =>
"foo:\n" +
" quote: \n"+
" '\"': <*>"
,
"foo:\n" +
" quote: \n"+
" '''': <*>"
,
"foo:\n" +
" quote: \n"+
" '`': <*>"
);
}
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: <*>"
);
}
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://"
);
}
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<org.springframework.core.io.Resource>", 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"
);
}
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*/
);
}
public void testClassReferenceInValueLink() throws Exception {
MockEditor editor;
useProject(createPredefinedMavenProject("boot13_with_mongo"));
editor = newEditor(
"spring:\n" +
" data:\n" +
" mongodb:\n" +
" field-naming-strategy: org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n"
);
assertLinkTargets(editor, "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"
);
assertLinkTargets(editor, "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"
);
assertLinkTargets(editor, "java.lang.String", "java.lang.String");
}
public void test_STS_3335_reconcile_list_nested_in_Map_of_String() throws Exception {
YamlEditor editor;
useProject(createPredefinedMavenProject("demo-sts-4335"));
editor = new YamlEditor(
"test-map:\n" +
" test-list-object:\n" +
" color-list:\n" +
" - not-a-color\n"+
" - RED\n" +
" - GREEN\n"
);
assertProblems(editor,
"not-a-color|Expecting a 'com.wellsfargo.lendingplatform.web.config.Color"
);
editor = new YamlEditor(
"test-map:\n" +
" test-list-object:\n" +
" string-list:\n" +
" - abc\n" +
" - def\n"
);
assertProblems(editor /*NONE*/);
}
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"
);
}
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://"
);
}
public void testBootBug5905() throws Exception {
useProject(createPredefinedMavenProject("demo-with-resource-prop"));
//Check the metadata reflects the 'handle-as':
PropertyInfo metadata = indexProvider.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://"
);
}
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"
);
}
public void testEnumJavaDocShownInValueHover() throws Exception {
useProject(createPredefinedMavenProject("demo-enum"));
data("my.background", "demo.Color", null, "Color to use as default background.");
YamlEditor editor;
editor = new YamlEditor(
"my:\n" +
" background: red"
);
editor.assertHoverContains("red", "Hot and delicious");
editor = new YamlEditor(
"my:\n" +
" background: RED"
);
editor.assertHoverContains("RED", "Hot and delicious");
}
public void testHyperLinkEnumValue() throws Exception {
YamlEditor editor;
useProject(createPredefinedMavenProject("demo-enum"));
data("my.background", "demo.Color", null, "Color to use as default background.");
editor = new YamlEditor(
"my:\n" +
" background: RED"
);
assertLinkTargets(editor, "RED", "demo.Color.RED");
editor = new YamlEditor(
"my:\n" +
" background: red"
);
assertLinkTargets(editor, "red", "demo.Color.RED");
}
public void testHyperLinkEnumValueInMapKey() throws Exception {
YamlEditor editor;
useProject(createPredefinedMavenProject("demo-enum"));
data("my.color.map", "java.util.Map<demo.Color,java.lang.String>", null, "Pretty names for the colors.");
editor = new YamlEditor(
"my:\n" +
" color:\n" +
" map:\n" +
" RED: Rood\n" +
" green: Groen\n"
);
assertLinkTargets(editor, "RED", "demo.Color.RED");
assertLinkTargets(editor, "green", "demo.Color.GREEN");
editor = new YamlEditor(
"spring:\n" +
" jackson:\n" +
" serialization:\n" +
" INDENT_OUTPUT: true"
);
assertLinkTargets(editor, "INDENT_OUTPUT",
"com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT"
);
editor = new YamlEditor(
"spring:\n" +
" jackson:\n" +
" serialization:\n" +
" indent-output: true"
);
assertLinkTargets(editor, "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;
}
}