From 28fed6ffcbb52e3302686506a8af3676584e5927 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Thu, 27 Oct 2016 20:22:48 -0400 Subject: [PATCH] Port SpringPropertiesEditorTests --- .../properties/metadata/PropertiesLoader.java | 268 +-- .../metadata/PropertiesIndexTest.java | 26 +- .../properties/metadata/TypeUtilTest.java | 4 +- ...rojectWithClasspathFileFinderStrategy.java | 4 +- .../java/MavenProjectFinderStrategy.java | 4 +- .../commons/commons-maven/pom.xml | 8 - .../ide/vscode/commons/maven/MavenCore.java | 22 + .../commons/maven/java/MavenJavaProject.java | 139 +- .../vscode/commons/maven/java/Projects.java | 36 - .../commons/maven/DependencyTreeTest.java | 11 +- .../languageserver/testharness/Editor.java | 22 +- .../testharness/LanguageServerHarness.java | 6 + .../commons/project-test-harness/pom.xml | 58 +- .../project/harness/ProjectsHarness.java | 63 +- .../vscode/project/harness/TestAsserts.java | 13 + .../properties-editor-test-harness/pom.xml | 5 + .../test/harness/AbstractPropsEditorTest.java | 17 +- .../vscode-application-properties/pom.xml | 12 + .../ApplicationPropertiesProblemType.java | 78 + .../test/ApplicationPropertiesEditorTest.java | 1547 ++++++++++++++++- .../ApplicationYamlASTReconciler.java | 24 +- .../reconcile/ApplicationYamlProblemType.java | 80 + .../SpringPropertiesProblemType.java | 129 -- .../yaml/reconcile/SpringPropertyProblem.java | 2 +- 24 files changed, 2116 insertions(+), 462 deletions(-) delete mode 100644 vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/Projects.java create mode 100644 vscode-extensions/commons/project-test-harness/src/main/java/org/springframework/ide/vscode/project/harness/TestAsserts.java create mode 100644 vscode-extensions/vscode-application-properties/src/main/java/org/springframework/ide/vscode/application/properties/reconcile/ApplicationPropertiesProblemType.java create mode 100644 vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlProblemType.java delete mode 100644 vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/SpringPropertiesProblemType.java diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/PropertiesLoader.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/PropertiesLoader.java index 01495e8e0..e6495f60c 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/PropertiesLoader.java +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/PropertiesLoader.java @@ -1,132 +1,136 @@ -package org.springframework.ide.vscode.application.properties.metadata; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.jar.JarFile; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.ZipEntry; - -import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; -import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder; -import org.springframework.ide.vscode.commons.java.IClasspath; - -public class PropertiesLoader { - - private static final String MAIN_SPRING_CONFIGURATION_METADATA_JSON = "META-INF/spring-configuration-metadata.json"; - - public static final String ADDITIONAL_SPRING_CONFIGURATION_METADATA_JSON = "META-INF/additional-spring-configuration-metadata.json"; - - /** - * The default classpath location for config metadata loaded when scanning .jar files on the classpath. - */ - public static final String[] JAR_META_DATA_LOCATIONS = { - MAIN_SPRING_CONFIGURATION_METADATA_JSON - //Not scanning 'additional' metadata because it integrated already in the main data. - }; - - /** - * The default classpath location for config metadata loaded when scanning project output folders. - */ - public static final String[] PROJECT_META_DATA_LOCATIONS = { - MAIN_SPRING_CONFIGURATION_METADATA_JSON, - ADDITIONAL_SPRING_CONFIGURATION_METADATA_JSON - }; - - private static final Logger LOG = Logger.getLogger(PropertiesLoader.class.getName()); - - private ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder.create(); - - public ConfigurationMetadataRepository load(IClasspath classPath) { - try { - classPath.getClasspathEntries().forEach(entry -> { - if (entry.toFile().isDirectory()) { - loadFromOutputFolder(entry); - } else { - loadFromJar(entry); - } - }); - } catch (Exception e) { - LOG.log(Level.SEVERE, "Failed to retrieve classpath", e); - } - ConfigurationMetadataRepository repository = builder.build(); - return repository; - } - - private void loadFromOutputFolder(Path outputFolderPath) { - if (outputFolderPath != null && Files.exists(outputFolderPath)) { - Arrays.stream(PROJECT_META_DATA_LOCATIONS).forEach(mdLoc -> { - loadFromJsonFile(outputFolderPath.resolve(mdLoc)); - }); - } - } - - private void loadFromJsonFile(Path mdf) { - if (Files.exists(mdf)) { - InputStream is = null; - try { - is = Files.newInputStream(mdf); - loadFromInputStream(mdf, is); - } catch (Exception e) { - LOG.log(Level.SEVERE, "Error loading file '" + mdf + "'", e); - } finally { - if (is!=null) { - try { - is.close(); - } catch (IOException e) { - //ignore - } - } - } - } - } - - private void loadFromJar(Path f) { - JarFile jarFile = null; - try { - jarFile = new JarFile(f.toFile()); - //jarDump(jarFile); - for (String loc : JAR_META_DATA_LOCATIONS) { - ZipEntry e = jarFile.getEntry(loc); - if (e!=null) { - loadFrom(jarFile, e); - } - } - } catch (Throwable e) { - LOG.log(Level.SEVERE, "Error loading JAR file", e); - } finally { - if (jarFile!=null) { - try { - jarFile.close(); - } catch (IOException e) { - } - } - } - } - - - private void loadFrom(JarFile jarFile, ZipEntry ze) { - InputStream is = null; - try { - is = jarFile.getInputStream(ze); - loadFromInputStream(jarFile.getName()+"["+ze.getName()+"]", is); - } catch (Throwable e) { - LOG.log(Level.SEVERE, "Error loading JAR file", e); - } finally { - if (is!=null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - } - - private void loadFromInputStream(Object origin, InputStream is) throws IOException { - builder.withJsonResource(origin, is); - } - -} +package org.springframework.ide.vscode.application.properties.metadata; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; + +import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder; +import org.springframework.ide.vscode.commons.java.IClasspath; + +public class PropertiesLoader { + + private static final String MAIN_SPRING_CONFIGURATION_METADATA_JSON = "META-INF/spring-configuration-metadata.json"; + + public static final String ADDITIONAL_SPRING_CONFIGURATION_METADATA_JSON = "META-INF/additional-spring-configuration-metadata.json"; + + /** + * The default classpath location for config metadata loaded when scanning .jar files on the classpath. + */ + public static final String[] JAR_META_DATA_LOCATIONS = { + MAIN_SPRING_CONFIGURATION_METADATA_JSON + //Not scanning 'additional' metadata because it integrated already in the main data. + }; + + /** + * The default classpath location for config metadata loaded when scanning project output folders. + */ + public static final String[] PROJECT_META_DATA_LOCATIONS = { + MAIN_SPRING_CONFIGURATION_METADATA_JSON, + ADDITIONAL_SPRING_CONFIGURATION_METADATA_JSON + }; + + private static final Logger LOG = Logger.getLogger(PropertiesLoader.class.getName()); + + private ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder.create(); + + public ConfigurationMetadataRepository load(IClasspath classPath) { + try { + classPath.getClasspathEntries().forEach(entry -> { + File fileEntry = entry.toFile(); + if (fileEntry.exists()) { + if (fileEntry.isDirectory()) { + loadFromOutputFolder(entry); + } else { + loadFromJar(entry); + } + } + }); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Failed to retrieve classpath", e); + } + ConfigurationMetadataRepository repository = builder.build(); + return repository; + } + + private void loadFromOutputFolder(Path outputFolderPath) { + if (outputFolderPath != null && Files.exists(outputFolderPath)) { + Arrays.stream(PROJECT_META_DATA_LOCATIONS).forEach(mdLoc -> { + loadFromJsonFile(outputFolderPath.resolve(mdLoc)); + }); + } + } + + private void loadFromJsonFile(Path mdf) { + if (Files.exists(mdf)) { + InputStream is = null; + try { + is = Files.newInputStream(mdf); + loadFromInputStream(mdf, is); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Error loading file '" + mdf + "'", e); + } finally { + if (is!=null) { + try { + is.close(); + } catch (IOException e) { + //ignore + } + } + } + } + } + + private void loadFromJar(Path f) { + JarFile jarFile = null; + try { + jarFile = new JarFile(f.toFile()); + //jarDump(jarFile); + for (String loc : JAR_META_DATA_LOCATIONS) { + ZipEntry e = jarFile.getEntry(loc); + if (e!=null) { + loadFrom(jarFile, e); + } + } + } catch (Throwable e) { + LOG.log(Level.SEVERE, "Error loading JAR file", e); + } finally { + if (jarFile!=null) { + try { + jarFile.close(); + } catch (IOException e) { + } + } + } + } + + + private void loadFrom(JarFile jarFile, ZipEntry ze) { + InputStream is = null; + try { + is = jarFile.getInputStream(ze); + loadFromInputStream(jarFile.getName()+"["+ze.getName()+"]", is); + } catch (Throwable e) { + LOG.log(Level.SEVERE, "Error loading JAR file", e); + } finally { + if (is!=null) { + try { + is.close(); + } catch (IOException e) { + } + } + } + } + + private void loadFromInputStream(Object origin, InputStream is) throws IOException { + builder.withJsonResource(origin, is); + } + +} diff --git a/vscode-extensions/commons/application-properties-metadata/src/test/java/org/springframework/ide/vscode/boot/properties/metadata/PropertiesIndexTest.java b/vscode-extensions/commons/application-properties-metadata/src/test/java/org/springframework/ide/vscode/boot/properties/metadata/PropertiesIndexTest.java index 1dae0b1bc..1e631ca6c 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/test/java/org/springframework/ide/vscode/boot/properties/metadata/PropertiesIndexTest.java +++ b/vscode-extensions/commons/application-properties-metadata/src/test/java/org/springframework/ide/vscode/boot/properties/metadata/PropertiesIndexTest.java @@ -19,9 +19,7 @@ import org.springframework.ide.vscode.application.properties.metadata.PropertyIn import org.springframework.ide.vscode.application.properties.metadata.SpringPropertiesIndexManager; import org.springframework.ide.vscode.application.properties.metadata.ValueProviderRegistry; import org.springframework.ide.vscode.application.properties.metadata.util.FuzzyMap; -import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; -import org.springframework.ide.vscode.commons.maven.java.Projects; -import org.springframework.ide.vscode.commons.maven.java.classpathfile.JavaProjectWithClasspathFile; +import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.project.harness.ProjectsHarness; /** @@ -33,13 +31,14 @@ import org.springframework.ide.vscode.project.harness.ProjectsHarness; public class PropertiesIndexTest { private static final String CUSTOM_PROPERTIES_PROJECT = "custom-properties-boot-project"; - + + private ProjectsHarness projects = ProjectsHarness.INSTANCE; + @Test public void springStandardPropertyPresent_Maven() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( ValueProviderRegistry.getDefault()); - MavenJavaProject mavenProject = Projects - .createMavenJavaProject(ProjectsHarness.buildMavenProject(CUSTOM_PROPERTIES_PROJECT)); + IJavaProject mavenProject = projects.mavenProject(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(mavenProject); PropertyInfo propertyInfo = index.get("server.port"); assertNotNull(propertyInfo); @@ -51,8 +50,7 @@ public class PropertiesIndexTest { public void customPropertyPresent_Maven() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( ValueProviderRegistry.getDefault()); - MavenJavaProject mavenProject = Projects - .createMavenJavaProject(ProjectsHarness.buildMavenProject(CUSTOM_PROPERTIES_PROJECT)); + IJavaProject mavenProject = projects.mavenProject(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(mavenProject); PropertyInfo propertyInfo = index.get("demo.settings.user"); assertNotNull(propertyInfo); @@ -64,8 +62,7 @@ public class PropertiesIndexTest { public void propertyNotPresent_Maven() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( ValueProviderRegistry.getDefault()); - MavenJavaProject mavenProject = Projects - .createMavenJavaProject(ProjectsHarness.buildMavenProject(CUSTOM_PROPERTIES_PROJECT)); + IJavaProject mavenProject = projects.mavenProject(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(mavenProject); PropertyInfo propertyInfo = index.get("my.server.port"); assertNull(propertyInfo); @@ -75,8 +72,7 @@ public class PropertiesIndexTest { public void springStandardPropertyPresent_ClasspathFile() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( ValueProviderRegistry.getDefault()); - JavaProjectWithClasspathFile classpathFileProject = Projects - .createJavaProjectWithClasspathFile(ProjectsHarness.buildMavenProject(CUSTOM_PROPERTIES_PROJECT)); + IJavaProject classpathFileProject = projects.javaProjectWithClasspathFile(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(classpathFileProject); PropertyInfo propertyInfo = index.get("server.port"); assertNotNull(propertyInfo); @@ -88,8 +84,7 @@ public class PropertiesIndexTest { public void customPropertyPresent_ClasspathFile() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( ValueProviderRegistry.getDefault()); - JavaProjectWithClasspathFile classpathFileProject = Projects - .createJavaProjectWithClasspathFile(ProjectsHarness.buildMavenProject(CUSTOM_PROPERTIES_PROJECT)); + IJavaProject classpathFileProject = projects.javaProjectWithClasspathFile(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(classpathFileProject); PropertyInfo propertyInfo = index.get("demo.settings.user"); assertNotNull(propertyInfo); @@ -101,8 +96,7 @@ public class PropertiesIndexTest { public void propertyNotPresent_ClasspathFile() throws Exception { SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager( ValueProviderRegistry.getDefault()); - JavaProjectWithClasspathFile classpathFileProject = Projects - .createJavaProjectWithClasspathFile(ProjectsHarness.buildMavenProject(CUSTOM_PROPERTIES_PROJECT)); + IJavaProject classpathFileProject = projects.javaProjectWithClasspathFile(CUSTOM_PROPERTIES_PROJECT); FuzzyMap index = indexManager.get(classpathFileProject); PropertyInfo propertyInfo = index.get("my.server.port"); assertNull(propertyInfo); diff --git a/vscode-extensions/commons/application-properties-metadata/src/test/java/org/springframework/ide/vscode/boot/properties/metadata/TypeUtilTest.java b/vscode-extensions/commons/application-properties-metadata/src/test/java/org/springframework/ide/vscode/boot/properties/metadata/TypeUtilTest.java index 40731f0f0..f08109299 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/test/java/org/springframework/ide/vscode/boot/properties/metadata/TypeUtilTest.java +++ b/vscode-extensions/commons/application-properties-metadata/src/test/java/org/springframework/ide/vscode/boot/properties/metadata/TypeUtilTest.java @@ -26,7 +26,6 @@ import org.springframework.ide.vscode.application.properties.metadata.types.Type import org.springframework.ide.vscode.application.properties.metadata.types.TypeUtil.EnumCaseMode; import org.springframework.ide.vscode.application.properties.metadata.types.TypedProperty; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.maven.java.Projects; import org.springframework.ide.vscode.project.harness.ProjectsHarness; /** @@ -41,6 +40,7 @@ import org.springframework.ide.vscode.project.harness.ProjectsHarness; @Ignore public class TypeUtilTest { + private ProjectsHarness projects = ProjectsHarness.INSTANCE; private IJavaProject project; private TypeUtil typeUtil; @@ -142,7 +142,7 @@ public class TypeUtilTest { } private void useProject(String name) throws Exception { - project = Projects.createMavenJavaProject(ProjectsHarness.buildMavenProject(name));; + project = projects.mavenProject(name);; typeUtil = new TypeUtil(project); } diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectWithClasspathFileFinderStrategy.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectWithClasspathFileFinderStrategy.java index ce42cf3a6..89fa77d8d 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectWithClasspathFileFinderStrategy.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/JavaProjectWithClasspathFileFinderStrategy.java @@ -15,7 +15,7 @@ import java.net.URI; import java.net.URISyntaxException; import org.springframework.ide.vscode.commons.languageserver.util.IDocument; -import org.springframework.ide.vscode.commons.maven.java.Projects; +import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.maven.java.classpathfile.JavaProjectWithClasspathFile; import org.springframework.ide.vscode.commons.util.FileUtils; import org.springframework.ide.vscode.commons.util.StringUtil; @@ -30,7 +30,7 @@ public class JavaProjectWithClasspathFileFinderStrategy implements IJavaProjectF URI uri = new URI(uriStr); //TODO: This only work with File uri. Should it work with others too? File file = new File(uri).getAbsoluteFile(); - File cpFile = FileUtils.findFile(file, Projects.CLASSPATH_TXT); + File cpFile = FileUtils.findFile(file, MavenCore.CLASSPATH_TXT); if (cpFile!=null) { return new JavaProjectWithClasspathFile(cpFile); } diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/MavenProjectFinderStrategy.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/MavenProjectFinderStrategy.java index 07404bd15..3822d369c 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/MavenProjectFinderStrategy.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/MavenProjectFinderStrategy.java @@ -15,8 +15,8 @@ import java.net.URI; import java.net.URISyntaxException; import org.springframework.ide.vscode.commons.languageserver.util.IDocument; +import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; -import org.springframework.ide.vscode.commons.maven.java.Projects; import org.springframework.ide.vscode.commons.util.FileUtils; import org.springframework.ide.vscode.commons.util.StringUtil; @@ -36,7 +36,7 @@ public class MavenProjectFinderStrategy implements IJavaProjectFinderStrategy { URI uri = new URI(uriStr); //TODO: This only work with File uri. Should it work with others too? File file = new File(uri).getAbsoluteFile(); - File pomFile = FileUtils.findFile(file, Projects.POM_XML); + File pomFile = FileUtils.findFile(file, MavenCore.POM_XML); if (pomFile!=null) { return new MavenJavaProject(pomFile); } diff --git a/vscode-extensions/commons/commons-maven/pom.xml b/vscode-extensions/commons/commons-maven/pom.xml index 3cdda749a..3a97454f6 100644 --- a/vscode-extensions/commons/commons-maven/pom.xml +++ b/vscode-extensions/commons/commons-maven/pom.xml @@ -41,14 +41,6 @@ ${project.version} - - - org.springframework.ide.vscode - project-test-harness - ${project.version} - test - - diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java index 66d18752d..adcbd1fb5 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java @@ -46,6 +46,8 @@ import org.eclipse.aether.util.graph.transformer.NearestVersionSelector; import org.eclipse.aether.util.graph.transformer.SimpleOptionalitySelector; import org.eclipse.aether.util.graph.visitor.CloningDependencyVisitor; import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor; +import org.springframework.ide.vscode.commons.util.ExternalCommand; +import org.springframework.ide.vscode.commons.util.ExternalProcess; /** * Maven Core functionality @@ -55,6 +57,9 @@ import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor; */ public class MavenCore { + public static final String CLASSPATH_TXT = "classpath.txt"; + public static final String POM_XML = "pom.xml"; + private static MavenCore instance = null; private MavenBridge maven = new MavenBridge(); @@ -79,6 +84,23 @@ public class MavenCore { Path dir = classPathFilePath.getParent(); return Arrays.stream(text.split(File.pathSeparator)).map(dir::resolve).collect(Collectors.toSet()); } + + /** + * Builds maven project + * + * @param Path of the project + * @throws Exception + */ + public static void buildMavenProject(Path testProjectPath) throws Exception { + Path mvnwPath = System.getProperty("os.name").toLowerCase().startsWith("win") + ? testProjectPath.resolve("mvnw.cmd") : testProjectPath.resolve("mvnw"); + mvnwPath.toFile().setExecutable(true); + ExternalProcess process = new ExternalProcess(testProjectPath.toFile(), + new ExternalCommand(mvnwPath.toAbsolutePath().toString(), "clean", "package"), true); + if (process.getExitValue() != 0) { + throw new RuntimeException("Failed to build test project"); + } + } /** * Creates Maven Project descriptor based on the pom file. diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java index dce65e05f..98005bf58 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java @@ -1,66 +1,73 @@ -/******************************************************************************* - * 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.commons.maven.java; - -import java.io.File; - -import org.apache.maven.project.MavenProject; -import org.springframework.ide.vscode.commons.java.IClasspath; -import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.java.IType; -import org.springframework.ide.vscode.commons.maven.MavenCore; -import org.springframework.ide.vscode.commons.util.HtmlSnippet; - -/** - * Wrapper for Maven Core project - * - * @author Alex Boyko - * - */ -public class MavenJavaProject implements IJavaProject { - - private MavenProject mavenProject; - private MavenProjectClasspath classpath; - private MavenCore maven; - - public MavenJavaProject(File pom) throws Exception { - this.maven = MavenCore.getInstance(); - this.mavenProject = maven.readProject(pom); - this.classpath = new MavenProjectClasspath(mavenProject, maven); - } - - @Override - public String getElementName() { - return mavenProject.getName(); - } - - @Override - public HtmlSnippet getJavaDoc() { - return null; - } - - @Override - public boolean exists() { - return mavenProject != null; - } - - @Override - public IType findType(String fqName) { - // TODO Auto-generated method stub - return null; - } - - @Override - public IClasspath getClasspath() { - return classpath; - } - -} +/******************************************************************************* + * 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.commons.maven.java; + +import java.io.File; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.maven.project.MavenProject; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.util.HtmlSnippet; + +/** + * Wrapper for Maven Core project + * + * @author Alex Boyko + * + */ +public class MavenJavaProject implements IJavaProject { + + private MavenProject mavenProject; + private MavenProjectClasspath classpath; + private MavenCore maven; + + public MavenJavaProject(File pom) throws Exception { + this.maven = MavenCore.getInstance(); + this.mavenProject = maven.readProject(pom); + this.classpath = new MavenProjectClasspath(mavenProject, maven); + } + + @Override + public String getElementName() { + return mavenProject.getName(); + } + + @Override + public HtmlSnippet getJavaDoc() { + return null; + } + + @Override + public boolean exists() { + return mavenProject != null; + } + + @Override + public IType findType(String fqName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public IClasspath getClasspath() { + return classpath; + } + + public Path getOutputFolder() { + return Paths.get(URI.create(mavenProject.getBuild().getOutputDirectory())); + } + +} diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/Projects.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/Projects.java deleted file mode 100644 index 005a7a5ad..000000000 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/Projects.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * 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.commons.maven.java; - -import java.nio.file.Path; - -import org.springframework.ide.vscode.commons.maven.java.classpathfile.JavaProjectWithClasspathFile; - -/** - * Maven projects methods - * - * @author Alex Boyko - * - */ -public class Projects { - - public static final String CLASSPATH_TXT = "classpath.txt"; - public static final String POM_XML = "pom.xml"; - - public static MavenJavaProject createMavenJavaProject(Path projectPath) throws Exception { - return new MavenJavaProject(projectPath.resolve(Projects.POM_XML).toFile()); - } - - public static JavaProjectWithClasspathFile createJavaProjectWithClasspathFile(Path projectPath) throws Exception { - return new JavaProjectWithClasspathFile(projectPath.resolve(Projects.CLASSPATH_TXT).toFile()); - } - -} diff --git a/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/DependencyTreeTest.java b/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/DependencyTreeTest.java index 4c5618b0a..fb84ca4e7 100644 --- a/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/DependencyTreeTest.java +++ b/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/DependencyTreeTest.java @@ -19,8 +19,6 @@ import java.util.stream.Collectors; import org.apache.maven.project.MavenProject; import org.junit.Test; -import org.springframework.ide.vscode.commons.maven.java.Projects; -import org.springframework.ide.vscode.project.harness.ProjectsHarness; /** * Tests for comparing maven calculated dependencies with ours @@ -32,14 +30,15 @@ public class DependencyTreeTest { @Test public void mavenTest() throws Exception { - Path testProjectPath = ProjectsHarness.buildMavenProject("empty-boot-project-with-classpath-file"); - - MavenProject project = MavenCore.getInstance().readProject(testProjectPath.resolve(Projects.POM_XML).toFile()); + Path testProjectPath = Paths.get(DependencyTreeTest.class.getResource("/empty-boot-project-with-classpath-file").toURI()); + MavenCore.buildMavenProject(testProjectPath); + + MavenProject project = MavenCore.getInstance().readProject(testProjectPath.resolve(MavenCore.POM_XML).toFile()); Set calculatedClassPath = MavenCore.getInstance().resolveDependencies(project, null).stream().map(artifact -> { return Paths.get(artifact.getFile().toURI()); }).collect(Collectors.toSet());; - Set expectedClasspath = MavenCore.readClassPathFile(testProjectPath.resolve(Projects.CLASSPATH_TXT)); + Set expectedClasspath = MavenCore.readClassPathFile(testProjectPath.resolve(MavenCore.CLASSPATH_TXT)); assertEquals(expectedClasspath, calculatedClassPath); } diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index a46e23215..27713dfaa 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -6,7 +6,10 @@ import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import javax.swing.text.BadLocationException; @@ -72,6 +75,7 @@ public class Editor { private int selectionEnd; private int selectionStart; + private Set ignoredTypes; public Editor(LanguageServerHarness harness, String contents) throws Exception { this.harness = harness; @@ -79,6 +83,7 @@ public class Editor { this.document = harness.openDocument(harness.createWorkingCopy(state.documentContents)); this.selectionStart = state.selectionStart; this.selectionEnd = state.selectionEnd; + this.ignoredTypes = new HashSet<>(); } /** @@ -96,7 +101,9 @@ public class Editor { */ public void assertProblems(String... expectedProblems) throws Exception { Editor editor = this; - List actualProblems = new ArrayList<>(editor.reconcile()); + List actualProblems = new ArrayList<>(editor.reconcile().stream().filter(d -> { + return !ignoredTypes.contains(d.getCode()); + }).collect(Collectors.toList())); Collections.sort(actualProblems, PROBLEM_COMPARATOR); String bad = null; if (actualProblems.size()!=expectedProblems.length) { @@ -313,6 +320,15 @@ public class Editor { throw new UnsupportedOperationException("Not implemented yet!"); } + /** + * Verifies an expected textSnippet is contained in the hover text that is + * computed when hovering mouse at position at the end of first occurrence of + * a given string in the editor. + */ + public void assertHoverText(String afterString, String expectSnippet) { + throw new UnsupportedOperationException("Not implemented yet!"); + } + public void setSelection(int start, int end) { Assert.assertTrue(start>=0); Assert.assertTrue(end>=start); @@ -366,4 +382,8 @@ public class Editor { assertEquals(expected, getText()); } + public void ignoreProblem(Object type) { + ignoredTypes.add(type.toString()); + } + } diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java index 306a1a91b..c7ca212e2 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java @@ -280,4 +280,10 @@ public class LanguageServerHarness { assertEquals(expect.toString(), actual.toString()); } + public void assertCompletionDisplayString(String editorContents, String expected) throws Exception { + Editor editor = newEditor(editorContents); + CompletionItem completion = editor.getFirstCompletion(); + assertEquals(expected, completion.getLabel()); + } + } diff --git a/vscode-extensions/commons/project-test-harness/pom.xml b/vscode-extensions/commons/project-test-harness/pom.xml index 7c30b0eba..75b41d4fd 100644 --- a/vscode-extensions/commons/project-test-harness/pom.xml +++ b/vscode-extensions/commons/project-test-harness/pom.xml @@ -1,20 +1,40 @@ - - 4.0.0 - - org.springframework.ide.vscode - commons-parent - 0.0.1-SNAPSHOT - ../pom.xml - - project-test-harness - project-test-harness - Test projects and utilities for test projects. Independent of any tooling projects. - - - org.springframework.ide.vscode - commons-util - ${project.version} - - + + 4.0.0 + + org.springframework.ide.vscode + commons-parent + 0.0.1-SNAPSHOT + ../pom.xml + + project-test-harness + project-test-harness + Test projects and utilities for test projects. Independent of any tooling projects. + + + org.springframework.ide.vscode + commons-util + ${project.version} + + + org.springframework.ide.vscode + commons-java + ${project.version} + + + org.springframework.ide.vscode + commons-maven + ${project.version} + + + com.google.guava + guava + ${guava-version} + + + junit + junit + ${junit-version} + + \ No newline at end of file diff --git a/vscode-extensions/commons/project-test-harness/src/main/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java b/vscode-extensions/commons/project-test-harness/src/main/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java index 8e8e4c2e5..8c52d3b94 100644 --- a/vscode-extensions/commons/project-test-harness/src/main/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java +++ b/vscode-extensions/commons/project-test-harness/src/main/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java @@ -10,41 +10,58 @@ *******************************************************************************/ package org.springframework.ide.vscode.project.harness; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import org.springframework.ide.vscode.commons.util.ExternalCommand; -import org.springframework.ide.vscode.commons.util.ExternalProcess; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; +import org.springframework.ide.vscode.commons.maven.java.classpathfile.JavaProjectWithClasspathFile; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; /** - * Test project harness utilities + * Test projects harness * * @author Alex Boyko * */ public class ProjectsHarness { - /** - * Builds maven project - * - * @param name - * @return - * @throws Exception - */ - public static Path buildMavenProject(String name) throws Exception { - Path testProjectPath = Paths.get(ProjectsHarness.class.getResource("/" + name).toURI()); - if (!Files.exists(testProjectPath.resolve("classpath.txt"))) { - Path mvnwPath = System.getProperty("os.name").toLowerCase().startsWith("win") - ? testProjectPath.resolve("mvnw.cmd") : testProjectPath.resolve("mvnw"); - mvnwPath.toFile().setExecutable(true); - ExternalProcess process = new ExternalProcess(testProjectPath.toFile(), - new ExternalCommand(mvnwPath.toAbsolutePath().toString(), "clean", "package"), true); - if (process.getExitValue() != 0) { - throw new RuntimeException("Failed to build test project"); + public static final ProjectsHarness INSTANCE = new ProjectsHarness();; + + public Cache cache = CacheBuilder.newBuilder().build(); + + private enum ProjectType { + MAVEN, + CLASSPATH_TXT + } + + private ProjectsHarness() { + } + + public IJavaProject project(ProjectType type, String name) throws Exception { + return cache.get(type + "/" + name, () -> { + Path testProjectPath = Paths.get(ProjectsHarness.class.getResource("/" + name).toURI()); + switch (type) { + case MAVEN: + return new MavenJavaProject(testProjectPath.resolve(MavenCore.POM_XML).toFile()); + case CLASSPATH_TXT: + MavenCore.buildMavenProject(testProjectPath); + return new JavaProjectWithClasspathFile(testProjectPath.resolve(MavenCore.CLASSPATH_TXT).toFile()); + default: + throw new IllegalStateException("Bug!!! Missing case"); } - } - return testProjectPath; + }); + } + + public MavenJavaProject mavenProject(String name) throws Exception { + return (MavenJavaProject) project(ProjectType.MAVEN, name); + } + + public IJavaProject javaProjectWithClasspathFile(String name) throws Exception { + return project(ProjectType.CLASSPATH_TXT, name); } } diff --git a/vscode-extensions/commons/project-test-harness/src/main/java/org/springframework/ide/vscode/project/harness/TestAsserts.java b/vscode-extensions/commons/project-test-harness/src/main/java/org/springframework/ide/vscode/project/harness/TestAsserts.java new file mode 100644 index 000000000..98b639bc0 --- /dev/null +++ b/vscode-extensions/commons/project-test-harness/src/main/java/org/springframework/ide/vscode/project/harness/TestAsserts.java @@ -0,0 +1,13 @@ +package org.springframework.ide.vscode.project.harness; + +import static org.junit.Assert.fail; + +public class TestAsserts { + + public static void assertContains(String needle, String haystack) { + if (haystack==null || !haystack.contains(needle)) { + fail("Not found: "+needle+"\n in \n"+haystack); + } + } + +} diff --git a/vscode-extensions/commons/properties-editor-test-harness/pom.xml b/vscode-extensions/commons/properties-editor-test-harness/pom.xml index 606b64188..5655ce0d3 100644 --- a/vscode-extensions/commons/properties-editor-test-harness/pom.xml +++ b/vscode-extensions/commons/properties-editor-test-harness/pom.xml @@ -33,5 +33,10 @@ junit ${junit-version} + + org.springframework.ide.vscode + project-test-harness + ${project.version} + \ No newline at end of file diff --git a/vscode-extensions/commons/properties-editor-test-harness/src/main/java/org/springframework/ide/vscode/properties/editor/test/harness/AbstractPropsEditorTest.java b/vscode-extensions/commons/properties-editor-test-harness/src/main/java/org/springframework/ide/vscode/properties/editor/test/harness/AbstractPropsEditorTest.java index 0dec319f4..6c1751821 100644 --- a/vscode-extensions/commons/properties-editor-test-harness/src/main/java/org/springframework/ide/vscode/properties/editor/test/harness/AbstractPropsEditorTest.java +++ b/vscode-extensions/commons/properties-editor-test-harness/src/main/java/org/springframework/ide/vscode/properties/editor/test/harness/AbstractPropsEditorTest.java @@ -1,6 +1,5 @@ package org.springframework.ide.vscode.properties.editor.test.harness; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -17,14 +16,21 @@ import org.springframework.ide.vscode.application.properties.metadata.types.Type import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.util.IDocument; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; import org.springframework.ide.vscode.languageserver.testharness.Editor; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; import org.springframework.ide.vscode.properties.editor.test.harness.PropertyIndexHarness.ItemConfigurer; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; import io.typefox.lsapi.CompletionItem; public abstract class AbstractPropsEditorTest { + public static final String INTEGER = Integer.class.getName(); + public static final String BOOLEAN = Boolean.class.getName(); + public static final String STRING = String.class.getName(); + + private ProjectsHarness projects = ProjectsHarness.INSTANCE; protected PropertyIndexHarness md; private LanguageServerHarness harness; private IJavaProject testProject; @@ -58,9 +64,8 @@ public abstract class AbstractPropsEditorTest { md.defaultTestData(); } - public IJavaProject createPredefinedMavenProject(String string) { - notImplemented(); - return null; + public MavenJavaProject createPredefinedMavenProject(String name) throws Exception { + return projects.mavenProject(name); } public void useProject(IJavaProject p) throws Exception { @@ -76,6 +81,10 @@ public abstract class AbstractPropsEditorTest { harness.assertCompletion(textBefore, expectTextAfter); } + public void assertCompletionDisplayString(String editorContents, String expected) throws Exception { + harness.assertCompletionDisplayString(editorContents, expected); + } + private void notImplemented() { throw new UnsupportedOperationException("Not yet implemented"); } diff --git a/vscode-extensions/vscode-application-properties/pom.xml b/vscode-extensions/vscode-application-properties/pom.xml index d79c872c1..6c1c36028 100644 --- a/vscode-extensions/vscode-application-properties/pom.xml +++ b/vscode-extensions/vscode-application-properties/pom.xml @@ -58,12 +58,24 @@ ${project.version} + + org.springframework.ide.vscode + project-test-harness + ${project.version} + test + org.springframework.ide.vscode language-server-test-harness ${project.version} test + + org.springframework.ide.vscode + properties-editor-test-harness + ${project.version} + test + diff --git a/vscode-extensions/vscode-application-properties/src/main/java/org/springframework/ide/vscode/application/properties/reconcile/ApplicationPropertiesProblemType.java b/vscode-extensions/vscode-application-properties/src/main/java/org/springframework/ide/vscode/application/properties/reconcile/ApplicationPropertiesProblemType.java new file mode 100644 index 000000000..a07535687 --- /dev/null +++ b/vscode-extensions/vscode-application-properties/src/main/java/org/springframework/ide/vscode/application/properties/reconcile/ApplicationPropertiesProblemType.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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.application.properties.reconcile; + +import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.ERROR; +import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.WARNING; + +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; + +/** + * @author Kris De Volder + */ +public enum ApplicationPropertiesProblemType implements ProblemType { + + PROP_INVALID_BEAN_NAVIGATION("Accessing a 'bean property' in a type that doesn't have properties (e.g. like String or Integer)"), + PROP_INVALID_INDEXED_NAVIGATION("Accessing a property using [] in a type that doesn't support that"), + PROP_EXPECTED_DOT_OR_LBRACK("Unexpected character found where a '.' or '[' was expected"), + PROP_NO_MATCHING_RBRACK("Found a '[' but no matching ']'"), + PROP_NON_INTEGER_IN_BRACKETS("Use of [..] navigation with non-integer value"), + PROP_VALUE_TYPE_MISMATCH("Expecting a value of a certain type, but value doesn't parse as such"), + PROP_INVALID_BEAN_PROPERTY("Accessing a named property in a type that doesn't provide a property accessor with that name"), + PROP_UNKNOWN_PROPERTY(WARNING, "Property-key not found in any configuration metadata on the project's classpath"), + PROP_DEPRECATED(WARNING, "Property is marked as Deprecated"), + PROP_DUPLICATE_KEY("Multiple assignments to the same property value"); + + private final ProblemSeverity defaultSeverity; + private String description; + private String label; + + private ApplicationPropertiesProblemType(ProblemSeverity defaultSeverity, String description, String label) { + this.description = description; + this.defaultSeverity = defaultSeverity; + this.label = label; + } + + private ApplicationPropertiesProblemType(ProblemSeverity defaultSeverity, String description) { + this(defaultSeverity, description, null); + } + + private ApplicationPropertiesProblemType(String description) { + this(ERROR, description); + } + + public ProblemSeverity getDefaultSeverity() { + return defaultSeverity; + } + + public String getLabel() { + if (label==null) { + label = createDefaultLabel(); + } + return label; + } + + public String getDescription() { + return description; + } + + private String createDefaultLabel() { + String label = this.toString().substring(5).toLowerCase().replace('_', ' '); + return Character.toUpperCase(label.charAt(0)) + label.substring(1); + } + + @Override + public String getCode() { + return name(); + } + +} diff --git a/vscode-extensions/vscode-application-properties/src/test/java/org/springframework/ide/vscode/application/properties/test/ApplicationPropertiesEditorTest.java b/vscode-extensions/vscode-application-properties/src/test/java/org/springframework/ide/vscode/application/properties/test/ApplicationPropertiesEditorTest.java index 8a00e7675..76e3eb462 100644 --- a/vscode-extensions/vscode-application-properties/src/test/java/org/springframework/ide/vscode/application/properties/test/ApplicationPropertiesEditorTest.java +++ b/vscode-extensions/vscode-application-properties/src/test/java/org/springframework/ide/vscode/application/properties/test/ApplicationPropertiesEditorTest.java @@ -10,10 +10,35 @@ *******************************************************************************/ package org.springframework.ide.vscode.application.properties.test; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.springframework.ide.vscode.application.properties.reconcile.ApplicationPropertiesProblemType.PROP_DUPLICATE_KEY; +import static org.springframework.ide.vscode.project.harness.TestAsserts.assertContains; + +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.time.Duration; +import java.util.List; + +import org.junit.Ignore; import org.junit.Test; import org.springframework.ide.vscode.application.properties.ApplicationPropertiesLanguageServer; +import org.springframework.ide.vscode.application.properties.metadata.CachingValueProvider; +import org.springframework.ide.vscode.application.properties.metadata.PropertiesLoader; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; import org.springframework.ide.vscode.languageserver.testharness.Editor; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; +import org.springframework.ide.vscode.properties.editor.test.harness.AbstractPropsEditorTest; +import org.springframework.ide.vscode.properties.editor.test.harness.StyledStringMatcher; + +import com.google.common.collect.ImmutableList; +import com.google.common.io.Files; + +import io.typefox.lsapi.CompletionItem; +import io.typefox.lsapi.Diagnostic; /** * Boot App Properties Editor tests @@ -21,13 +46,12 @@ import org.springframework.ide.vscode.languageserver.testharness.LanguageServerH * @author Alex Boyko * */ -public class ApplicationPropertiesEditorTest { +public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { private static final String SYNTAX_ERROR__UNEXPECTED_END_OF_INPUT = "Unexpected end of input, value identifier is expected"; private static final String SYNTAX_ERROR__UNEXPECTED_END_OF_LINE = "Unexpected end of line, value identifier is expected"; - @Test - public void testReconcileCatchesParseError() throws Exception { + @Test public void testReconcileCatchesParseError() throws Exception { LanguageServerHarness harness = new LanguageServerHarness(ApplicationPropertiesLanguageServer::new); harness.intialize(null); @@ -52,4 +76,1521 @@ public class ApplicationPropertiesEditorTest { editor.assertProblems("problem|" + SYNTAX_ERROR__UNEXPECTED_END_OF_LINE, "another|" + SYNTAX_ERROR__UNEXPECTED_END_OF_INPUT); } + + @Ignore @Test public void testServerPortCompletion() throws Exception { + data("server.port", INTEGER, 8080, "Port where server listens for http."); + assertCompletion("ser<*>", "server.port=<*>"); + assertCompletionDisplayString("ser<*>", "server.port : int"); + } + + @Ignore @Test public void testLoggingLevelCompletion() throws Exception { + data("logging.level", "java.util.Map", null, "Logging level per package."); + assertCompletion("lolev<*>","logging.level.<*>"); + } + + @Ignore @Test public void testListCompletion() throws Exception { + data("foo.bars", "java.util.List", null, "List of bars in foo."); + assertCompletion("foba<*>","foo.bars=<*>"); + } + + @Ignore @Test public void testInetAddresCompletion() throws Exception { + defaultTestData(); + assertCompletion("server.add<*>", "server.address=<*>"); + } + + @Ignore @Test public void testStringArrayCompletion() throws Exception { + data("spring.freemarker.view-names", "java.lang.String[]", null, "White list of view names that can be resolved."); + data("some.defaulted.array", "java.lang.String[]", new String[] {"a", "b", "c"} , "Stuff."); + + assertCompletion("spring.freemarker.vn<*>", "spring.freemarker.view-names=<*>"); + assertCompletion("some.d.a<*>", "some.defaulted.array=<*>"); + } + + @Ignore @Test public void testEmptyPrefixProposalsSortedAlpabetically() throws Exception { + defaultTestData(); + Editor editor = newEditor(""); + List completions = editor.getCompletions(); + assertTrue(completions.size()>100); //should be many proposals + String previous = null; + for (CompletionItem c : completions) { + String current = c.getLabel(); + if (previous!=null) { + assertTrue("Incorrect order: \n "+previous+"\n "+current, previous.compareTo(current)<=0); + } + previous = current; + } + } + + @Ignore @Test public void testValueCompletion() throws Exception { + defaultTestData(); + assertCompletionsVariations("liquibase.enabled=<*>", + "liquibase.enabled=false<*>", + "liquibase.enabled=true<*>" + ); + + assertCompletionsVariations("liquibase.enabled:<*>", + "liquibase.enabled:false<*>", + "liquibase.enabled:true<*>" + ); + + assertCompletionsVariations("liquibase.enabled = <*>", + "liquibase.enabled = false<*>", + "liquibase.enabled = true<*>" + ); + + assertCompletionsVariations("liquibase.enabled <*>", + "liquibase.enabled false<*>", + "liquibase.enabled true<*>" + ); + + assertCompletionsVariations("liquibase.enabled=f<*>", + "liquibase.enabled=false<*>" + ); + + assertCompletionsVariations("liquibase.enabled=t<*>", + "liquibase.enabled=true<*>" + ); + + assertCompletionsVariations("liquibase.enabled:f<*>", + "liquibase.enabled:false<*>" + ); + + assertCompletionsVariations("liquibase.enabled:t<*>", + "liquibase.enabled:true<*>" + ); + + assertCompletionsVariations("liquibase.enabled = f<*>", + "liquibase.enabled = false<*>" + ); + + assertCompletionsVariations("liquibase.enabled = t<*>", + "liquibase.enabled = true<*>" + ); + + assertCompletionsVariations("liquibase.enabled t<*>", + "liquibase.enabled true<*>" + ); + + //one more... for special char like '-' in the name + + assertCompletionsVariations("liquibase.check-change-log-location=t<*>", + "liquibase.check-change-log-location=true<*>" + ); + } + + + @Ignore @Test public void testHoverInfos() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "#foo\n" + + "# bar\n" + + "server.port=8080\n" + + "logging.level.com.acme=INFO\n" + ); + //Case 1: an 'exact' match of the property is in the hover region + editor.assertHoverText("server.", + "server.port" + ); + //Case 2: an object/map property has extra text after the property name + editor.assertHoverText("logging.", "logging.level"); + } + + @Ignore @Test public void testHoverInfosWithSpaces() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "#foo\n" + + "# bar\n"+ + "\n" + + " server.port = 8080\n" + + " logging.level.com.acme = INFO\n" + ); + //Case 1: an 'exact' match of the property is in the hover region + editor.assertHoverText("server.", + "server.port" + ); + //Case 2: an object/map property has extra text after the property name + editor.assertHoverText("logging.", "logging.level"); + } + + @Ignore @Test public void testHoverLongAndShort() throws Exception { + data("server.port", INTEGER, 8080, "Port where server listens for http."); + data("server.port.fancy", BOOLEAN, 8080, "Whether the port is fancy."); + Editor editor = newEditor( + "server.port=8080\n" + + "server.port.fancy=true\n" + ); + editor.assertHoverText("server.", "server.port"); + editor.assertHoverText("port.fa", "server.port.fancy"); + } + + + @Ignore @Test public void testPredefinedProject() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo"); + IType type = p.findType("demo.DemoApplication"); + assertNotNull(type); + } + + @Ignore @Test public void testEnableApt() throws Throwable { + MavenJavaProject p = createPredefinedMavenProject("demo-live-metadata"); + + //Check some assumptions about the initial state of the test project (if these checks fail then + // the test may be 'vacuous' since the things we are testing for already exist beforehand. + Path metadataFile = p.getOutputFolder().resolve(PropertiesLoader.PROJECT_META_DATA_LOCATIONS[0]); + assertTrue(metadataFile.toFile().isFile()); + assertContains("\"name\": \"foo.counter\"", Files.toString(metadataFile.toFile(), Charset.forName("UTF8"))); + } + + @Ignore @Test public void testHyperlinkTargets() throws Exception { + System.out.println(">>> testHyperlinkTargets"); + IJavaProject p = createPredefinedMavenProject("demo"); + useProject(p); + + Editor editor = newEditor( + "server.port=888\n" + + "spring.datasource.login-timeout=1000\n" + + "flyway.init-sqls=a,b,c\n" + ); + + editor.assertLinkTargets("server", + "org.springframework.boot.autoconfigure.web.ServerProperties.setPort(Integer)" + ); + editor.assertLinkTargets("data", + "org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.hikariDataSource()", + "org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.tomcatDataSource()", + "org.springframework.boot.autoconfigure.jdbc.DataSourceConfigMetadata.dbcpDataSource()" + ); + editor.assertLinkTargets("flyway", + "org.springframework.boot.autoconfigure.flyway.FlywayProperties.setInitSqls(List)"); + System.out.println("<<< testHyperlinkTargets"); + } + + @Ignore @Test public void testHyperlinkTargetsLoggingLevel() throws Exception { + System.out.println(">>> testHyperlinkTargetsLoggingLevel"); + IJavaProject p = createPredefinedMavenProject("demo"); + + useProject(p); + + Editor editor = newEditor( + "logging.level.com.acme=INFO\n" + ); + editor.assertLinkTargets("level", + "org.springframework.boot.logging.LoggingApplicationListener" + ); + System.out.println("<<< testHyperlinkTargetsLoggingLevel"); + } + + @Ignore @Test public void testReconcile() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server.port=8080\n" + + "server.port.extracrap=8080\n" + + "logging.level.com.acme=INFO\n" + + "logging.snuggem=what?\n" + + "bogus.no.good=true\n" + ); + editor.assertProblems( + ".extracrap|Can't use '.' navigation", + "snuggem|unknown property", + "ogus.no.good|unknown property" + ); + + } + + @Ignore @Test public void testReconcilePojoArray() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-list-of-pojo"); + + useProject(p); + assertNotNull(p.findType("demo.Foo")); + + Editor editor = newEditor( + "token.bad.guy=problem\n"+ + "volder.foo.list[0].name=Kris\n" + + "volder.foo.list[0].description=Kris\n" + + "volder.foo.list[0].roles[0]=Developer\n"+ + "volder.foo.list[0]garbage=Grable\n"+ + "volder.foo.list[0].bogus=Bad\n" + ); + + //This is the more ambitious requirement but it is not implemented yet. + editor.assertProblems( + "token.bad.guy|unknown property", + //'name' is ok + //'description' is ok + "garbage|'.' or '['", + "bogus|has no property" + ); + } + + @Ignore @Test public void testPojoArrayCompletions() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-list-of-pojo"); + + useProject(p); + assertNotNull(p.findType("demo.Foo")); + + assertCompletionsVariations("volder.foo.l<*>", "volder.foo.list[<*>"); + assertCompletionsDisplayString("volder.foo.list[0].<*>", + "name : String", + "description : String", + "roles : List"); + + assertCompletionsVariations("volder.foo.list[0].na<*>", + "volder.foo.list[0].name=<*>" + ); + assertCompletionsVariations("volder.foo.list[0].d<*>", + "volder.foo.list[0].description=<*>" + ); + assertCompletionsVariations("volder.foo.list[0].rl<*>", + "volder.foo.list[0].roles=<*>" + ); + } + + @Ignore @Test public void testReconcileArrayNotation() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "borked=bad+\n" + //token problem, to make sure reconciler is working + "security.user.role[0]=foo\n" + + "security.user.role[${one}]=foo" + ); + editor.assertProblems( + "orked|unknown property" + //no other problems + ); + } + + @Ignore @Test public void testReconcileArrayNotationError() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "security.user.role[bork]=foo\n" + + "security.user.role[1=foo\n" + + "security.user.role[1]crap=foo\n" + + "server.port[0]=8888\n" + + "spring.thymeleaf.view-names[1]=hello" //This is okay now. Boot handles this notation for arrays + ); + editor.assertProblems( + "bork|Integer", + "[|matching ']'", + "crap|'.' or '['", + "[0]|Can't use '[..]'" + //no other problems + ); + } + + @Ignore @Test public void testRelaxedNameReconciling() throws Exception { + data("connection.remote-host", "java.lang.String", "service.net", null); + data("foo-bar.name", "java.lang.String", null, null); + Editor editor = newEditor( + "bork=foo\n" + + "connection.remote-host=alternate.net\n" + + "connection.remoteHost=alternate.net\n" + + "foo-bar.name=Charlie\n" + + "fooBar.name=Charlie\n" + ); + editor.assertProblems( + "bork|unknown property" + //no other problems + ); + } + + @Ignore @Test public void testRelaxedNameReconcilingErrors() throws Exception { + //Tricky with relaxec names: the error positions have to be moved + // around because the relaxed names aren't same length as the + // canonical ids. + data("foo-bar-zor.enabled", "java.lang.Boolean", null, null); + Editor editor = newEditor( + "fooBarZor.enabled=notBoolean\n" + + "fooBarZor.enabled.subprop=true\n" + ); + editor.assertProblems( + "notBoolean|boolean", + ".subprop|Can't use '.' navigation" + ); + } + + @Ignore @Test public void testRelaxedNameContentAssist() throws Exception { + data("foo-bar-zor.enabled", "java.lang.Boolean", null, null); + assertCompletion("fooBar<*>", "foo-bar-zor.enabled=<*>"); + } + + @Ignore @Test public void testReconcileValues() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server.port=badPort\n" + + "liquibase.enabled=nuggels" + ); + editor.assertProblems( + "badPort|'int'", + "nuggels|'boolean'" + ); + } + + @Ignore @Test public void testNoReconcileInterpolatedValues() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server.port=${port}\n" + + "liquibase.enabled=nuggels" + ); + editor.assertProblems( + //no problem should be reported for ${port} + "nuggels|'boolean'" + ); + } + + @Ignore @Test public void testReconcileValuesWithSpaces() throws Exception { + defaultTestData(); + Editor editor = newEditor( + "server.port = badPort\n" + + "liquibase.enabled nuggels \n" + + "liquibase.enabled : snikkers" + ); + editor.ignoreProblem(PROP_DUPLICATE_KEY); //ignore deliberate abuse of dups + editor.assertProblems( + "badPort|'int'", + "nuggels|'boolean'", + "snikkers|'boolean'" + ); + } + + + @Ignore @Test public void testReconcileWithExtraSpaces() throws Exception { + defaultTestData(); + //Same test as previous but with extra spaces to make things more confusing + Editor editor = newEditor( + " server.port = 8080 \n" + + "\n" + + " server.port.extracrap = 8080\n" + + " logging.level.com.acme : INFO\n" + + "logging.snuggem = what?\n" + + "bogus.no.good= true\n" + ); + editor.assertProblems( + ".extracrap|Can't use '.' navigation", + "snuggem|unknown property", + "ogus.no.good|unknown property" + ); + } + + @Ignore @Test public void testEnumPropertyCompletionInsideCommaSeparateList() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.Color")); + + data("foo.colors", "java.util.List", null, "A foonky list"); + + //Completion requested right after '=' sign: + assertCompletionsDisplayString("foo.colors=<*>", "red", "green", "blue"); + assertCompletionWithLabel("foo.colors=<*>", "red", "foo.colors=red<*>"); + assertCompletion("foo.colors=R<*>", "foo.colors=RED<*>"); + assertCompletion("foo.colors=g<*>", "foo.colors=green<*>"); + assertCompletion("foo.colors=B<*>", "foo.colors=BLUE<*>"); + + //Completion requested after ',' + assertCompletionsDisplayString("foo.colors=red,<*>", "red", "green", "blue"); + assertCompletionWithLabel("foo.colors=red,<*>", "green", "foo.colors=red,green<*>"); + assertCompletion("foo.colors=RED,R<*>", "foo.colors=RED,RED<*>"); + assertCompletion("foo.colors=RED,G<*>", "foo.colors=RED,GREEN<*>"); + assertCompletion("foo.colors=RED,B<*>", "foo.colors=RED,BLUE<*>"); + } + + @Ignore @Test public void testEnumPropertyCompletion() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.Color")); + + data("foo.color", "demo.Color", null, "A foonky colour"); + + assertCompletion("foo.c<*>", "foo.color=<*>"); //Should add the '=' because enums are 'simple' values. + + assertCompletion("foo.color=R<*>", "foo.color=RED<*>"); + assertCompletion("foo.color=G<*>", "foo.color=GREEN<*>"); + assertCompletion("foo.color=B<*>", "foo.color=BLUE<*>"); + assertCompletionsDisplayString("foo.color=<*>", + "red", "green", "blue" + ); + } + + @Ignore @Test public void testEnumPropertyReconciling() throws Exception { + + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.Color")); + + data("foo.color", "demo.Color", null, "A foonky colour"); + Editor editor = newEditor( + "foo.color=BLUE\n"+ + "foo.color=RED\n"+ + "foo.color=GREEN\n"+ + "foo.color.bad=BLUE\n"+ + "foo.color=Bogus\n" + ); + + editor.ignoreProblem(PROP_DUPLICATE_KEY); //ignore deliberate abuse of dups + + editor.assertProblems( + ".bad|Can't use '.' navigation", + "Bogus|Color" + ); + } + + @Ignore @Test public void testEnumMapValueCompletion() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.Color")); + + assertCompletionsVariations("foo.nam<*>", + "foo.name-colors.<*>", + "foo.color-names.<*>" + ); + + assertCompletionsDisplayString("foo.name-colors.something=<*>", + "red", "green", "blue" + ); + assertCompletionsVariations("foo.name-colors.something=G<*>", "foo.name-colors.something=GREEN<*>"); + } + + @Ignore @Test public void testEnumMapValueReconciling() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + data("foo.name-colors", "java.util.Map", null, "Map with colors in its values"); + + assertNotNull(p.findType("demo.Color")); + + Editor editor = newEditor( + "foo.name-colors.jacket=BLUE\n" + + "foo.name-colors.hat=RED\n" + + "foo.name-colors.pants=GREEN\n" + + "foo.name-colors.wrong=NOT_A_COLOR\n" + ); + editor.assertProblems( + "NOT_A_COLOR|Color" + ); + } + + @Ignore @Test public void testEnumMapKeyCompletion() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + 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"); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); + + //Map Enum -> String: + assertCompletionsVariations("foo.colnam<*>", "foo.color-names.<*>"); + assertCompletionsVariations("foo.color-names.<*>", + "foo.color-names.blue=<*>", + "foo.color-names.green=<*>", + "foo.color-names.red=<*>" + ); + assertCompletionsDisplayString("foo.color-names.<*>", + "red : String", "green : String", "blue : String" + ); + assertCompletionsVariations("foo.color-names.B<*>", + "foo.color-names.BLUE=<*>" + ); + + //Map Enum -> Pojo: + assertCompletionsVariations("foo.coldat<*>", "foo.color-data.<*>"); + assertCompletionsVariations("foo.color-data.<*>", + "foo.color-data.blue.<*>", + "foo.color-data.green.<*>", + "foo.color-data.red.<*>" + ); + assertCompletionsVariations("foo.color-data.B<*>", + "foo.color-data.BLUE.<*>" + ); + assertCompletionsDisplayString("foo.color-data.<*>", + "blue : demo.ColorData", "green : demo.ColorData", "red : demo.ColorData" + ); + } + + @Ignore @Test public void testEnumMapKeyReconciling() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); + + Editor editor = newEditor( + "foo.color-names.RED=Rood\n"+ + "foo.color-names.GREEN=Groen\n"+ + "foo.color-names.BLUE=Blauw\n" + + "foo.color-names.NOT_A_COLOR=Wrong\n" + + "foo.color-names.BLUE.bad=Blauw\n" + ); + editor.assertProblems( + "NOT_A_COLOR|Color", + "BLUE.bad|Color" //because value type is not dotable the dots will be taken to be part of map key + ); + } + + @Ignore @Test public void testPojoCompletions() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); + + assertCompletion("foo.dat<*>", "foo.data.<*>"); + + assertCompletionsDisplayString("foo.data.", + "wavelen : double", + "name : String", + "next : demo.Color[RED, GREEN, BLUE]", + "nested : demo.ColorData", + "children : List", + "mapped-children : Map", + "color-children : Map", + "tags : List", + "funky : boolean" + ); + + assertCompletionsVariations("foo.data.wav<*>", "foo.data.wavelen=<*>"); + assertCompletionsVariations("foo.data.nam<*>", "foo.data.name=<*>"); + assertCompletionsVariations("foo.data.nex<*>", "foo.data.next=<*>"); + assertCompletionsVariations("foo.data.nes<*>", "foo.data.nested.<*>"); + assertCompletionsVariations("foo.data.chi<*>", + "foo.data.children[<*>", + "foo.data.color-children.<*>", //fuzzy + "foo.data.mapped-children.<*>" //fuzzy + ); + assertCompletionsVariations("foo.data.tag<*>", "foo.data.tags=<*>"); + assertCompletionsVariations("foo.data.map<*>", "foo.data.mapped-children.<*>"); + assertCompletionsVariations("foo.data.col<*>", "foo.data.color-children.<*>"); + } + + @Ignore @Test public void testPojoReconciling() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); + + Editor editor = newEditor( + "foo.data.bogus=Something\n" + + "foo.data.wavelen=3.0\n" + + "foo.data.wavelen=not a double\n" + + "foo.data.wavelen.more=3.0\n" + + "foo.data.wavelen[0]=3.0\n" + ); + editor.ignoreProblem(PROP_DUPLICATE_KEY); //ignore deliberate abuse of dups + editor.assertProblems( + "bogus|no property", + "not a double|'double'", + ".more|Can't use '.' navigation", + "[0]|Can't use '[..]' navigation" + ); + } + + @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"); + assertCompletionsVariations("foo.u<*>", "foo.ulist[<*>"); + assertCompletionsVariations("foo.d<*>", "foo.dlist=<*>"); + assertCompletionsVariations("foo.sl<*>", "foo.slist=<*>"); + } + + @Ignore @Test public void testMapKeyDotInterpretation() throws Exception { + //Interpretation of '.' changes depending on the domain type (i.e. when domain type is + //is a simple type got which '.' navigation is invalid then the '.' is 'eaten' by the key. + + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); + + data("atommap", "java.util.Map", null, "map of atomic data"); + data("objectmap", "java.util.Map", null, "map of atomic object (recursive map)"); + data("enummap", "java.util.Map", null, "map of enums"); + data("pojomap", "java.util.Map", null, "map of pojos"); + + Editor editor = newEditor( + "atommap.something.with.dots=Vaporize\n" + + "atommap.something.with.bracket[0]=Brackelate\n" + + "objectmap.other.with.dots=Objectify\n" + + "enummap.more.dots=Enumerate\n" + + "pojomap.do.some.dots=Pojodot\n" + + "pojomap.bracket.and.dots[1]=lala\n" + + "pojomap.zozo[2]=lala\n" + ); + editor.assertProblems( + "Vaporize|'int'", + "[0]|Can't use '[..]'", + //objectmap okay + "Enumerate|Color", + "some|no property", + "and|no property", + "[2]|Can't use '[..]'" + ); + + assertCompletionsVariations("enummap.more.dots=R<*>", + "enummap.more.dots=RED<*>", + "enummap.more.dots=GREEN<*>" //fuzzy match: G(R)EEN + ); + } + + @Ignore @Test public void testMapKeyDotInterpretationInPojo() throws Exception { + //Similar to testMapKeyDotInterpretation but this time maps are not attached to property + // directly but via a pojo property + + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); + + Editor editor = newEditor( + "foo.color-names.BLUE.dot=Blauw\n"+ + "foo.color-data.RED.name=Good\n"+ + "foo.color-data.GREEN.bad=Bad\n"+ + "foo.color-data.GREEN.wrong[1]=Wrong\n" + ); + editor.assertProblems( + "BLUE.dot|Color", //dot is eaten so this is an error + "bad|no property", //dot not eaten so '.bad' is accessing a property + "wrong|no property" + ); + + assertCompletionsVariations("foo.color-data.RED.ch<*>", + "foo.color-data.RED.children[<*>", + "foo.color-data.RED.color-children.<*>", + "foo.color-data.RED.mapped-children.<*>" + ); + } + + @Ignore @Test public void testEnumsInLowerCaseReconciling() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.ClothingSize")); + + data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); + + Editor editor = newEditor( + "simple.pants.size=NOT_A_SIZE\n"+ + "simple.pants.size=EXTRA_SMALL\n"+ + "simple.pants.size=extra-small\n"+ + "simple.pants.size=small\n"+ + "simple.pants.size=SMALL\n" + ); + editor.ignoreProblem(PROP_DUPLICATE_KEY); //ignore deliberate abuse of dups + editor.assertProblems( + "NOT_A_SIZE|ClothingSize" + ); + + editor = newEditor( + "foo.color-names.red=Rood\n"+ + "foo.color-names.green=Groen\n"+ + "foo.color-names.blue=Blauw\n" + + "foo.color-names.not-a-color=Wrong\n" + + "foo.color-names.blue.bad=Blauw\n" + ); + editor.assertProblems( + "not-a-color|Color", + "blue.bad|Color" //because value type is not dotable the dots will be taken to be part of map key + ); + + editor = newEditor( + "foo.color-data.red.next=green\n" + + "foo.color-data.red.next=not a color\n" + + "foo.color-data.red.bogus=green\n" + + "foo.color-data.red.name=Rood\n" + ); + editor.assertProblems( + "not a color|Color", + "bogus|no property" + ); + } + + @Ignore @Test public void testEnumsInLowerCaseContentAssist() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.ClothingSize")); + + data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); + + assertCompletionsVariations("simple.pants.size=S<*>", + "simple.pants.size=SMALL<*>", + "simple.pants.size=EXTRA_SMALL<*>" + ); + assertCompletionsVariations("simple.pants.size=s<*>", + "simple.pants.size=small<*>", + "simple.pants.size=extra-small<*>" + ); + assertCompletionsVariations("simple.pants.size=ex<*>", + "simple.pants.size=extra-large<*>", + "simple.pants.size=extra-small<*>" + ); + assertCompletionsVariations("simple.pants.size=EX<*>", + "simple.pants.size=EXTRA_LARGE<*>", + "simple.pants.size=EXTRA_SMALL<*>" + ); + assertCompletionsDisplayString("foo.color=<*>", "red", "green", "blue"); + + assertCompletionsVariations("foo.color-data.R<*>", + "foo.color-data.RED.<*>", + "foo.color-data.GREEN.<*>" + ); + assertCompletionsVariations("foo.color-data.r<*>", + "foo.color-data.red.<*>", + "foo.color-data.green.<*>" + ); + assertCompletionsVariations("foo.color-data.<*>", + "foo.color-data.blue.<*>", + "foo.color-data.green.<*>", + "foo.color-data.red.<*>" + ); + + assertCompletionsVariations("foo.color-data.red.na<*>", "foo.color-data.red.name=<*>"); + } + + @Ignore @Test public void testNavigationProposalAfterRelaxedPropertyName() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + + assertCompletionsVariations("foo.colorData.b<*>", "foo.colorData.blue.<*>"); + assertCompletionsVariations("foo.colorData.red.na<*>", "foo.colorData.red.name=<*>"); + } + + @Ignore @Test public void testValueProposalAssignedToRelaxedPropertyName() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + + data("relaxed-color", "demo.Color", null, "A soothing color"); + + assertCompletion("relaxed-color=b<*>", "relaxed-color=blue<*>"); + assertCompletion("relaxedColor=b<*>", "relaxedColor=blue<*>"); + } + + @Ignore @Test public void testReconcileDeprecatedProperty() throws Exception { + data("error.path", "java.lang.String", null, "Path of the error controller."); + Editor editor = newEditor( + "# a comment\n"+ + "error.path=foo\n" + ); + + deprecate("error.path", "server.error.path", null); + editor.assertProblems( + "error.path|Deprecated: Use 'server.error.path'" + //no other problems + ); + + deprecate("error.path", "server.error.path", "This is old."); + editor.assertProblems( + "error.path|Deprecated: Use 'server.error.path' instead. Reason: This is old." + //no other problems + ); + + deprecate("error.path", null, "This is old."); + editor.assertProblems( + "error.path|Deprecated: This is old." + //no other problems + ); + + deprecate("error.path", null, null); + editor.assertProblems( + "error.path|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("error.pa<*>", + "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" + ); + //TODO: could we check that 'deprecated' completions are formatted with 'strikethrough font? + 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.path=foo\n" + ); + + deprecate("error.path", "server.error.path", null); + editor.assertHoverText("path", "error.path -> server.error.path"); + editor.assertHoverText("path", "Deprecated!"); + + deprecate("error.path", "server.error.path", "This is old."); + editor.assertHoverText("path", "error.path -> server.error.path"); + editor.assertHoverText("path", "Deprecated: This is old"); + + deprecate("error.path", null, "This is old."); + editor.assertHoverText("path", "Deprecated: This is old"); + + deprecate("error.path", null, null); + editor.assertHoverText("path", "Deprecated!"); + } + + @Ignore @Test public void testDeprecatedPropertyQuickfix() throws Exception { + data("error.path", "java.lang.String", null, "Path of the error controller."); + deprecate("error.path", "server.error.path", null); + + Editor editor = newEditor( + "# a comment\n"+ + "error.path=foo\n" + ); + + Diagnostic problem = editor.assertProblem("error.path"); + CompletionItem fix = editor.assertFirstQuickfix(problem, "Change to 'server.error.path'"); + editor.apply(fix); + editor.assertText( + "# a comment\n"+ + "server.error.path<*>=foo\n" + ); + } + + @Ignore @Test public void testDeprecatedBeanPropertyReconcile() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo"); + useProject(p); + data("foo", "demo.Deprecater", null, "A Bean with deprecated properties"); + + Editor editor = newEditor( + "# comment\n" + + "foo.name=Old faithfull\n" + + "foo.new-name=New and fancy\n" + + "foo.alt-name=alternate\n" + ); + editor.assertProblems( + "name|Deprecated", + "alt-name|Property 'alt-name' of type 'demo.Deprecater' is Deprecated: Use 'something.else' instead. Reason: No good anymore" + ); + } + + @Ignore @Test public void testDeprecatedBeanPropertyCompletions() throws Exception { + IJavaProject p = createPredefinedMavenProject("demo"); + useProject(p); + data("foo", "demo.Deprecater", null, "A Bean with deprecated properties"); + + assertStyledCompletions("foo.nam<*>", + StyledStringMatcher.plainFont("new-name : String"), + StyledStringMatcher.strikeout("name"), + StyledStringMatcher.strikeout("alt-name") + ); + } + + @Ignore @Test public void testCharsetCompletions() throws Exception { + data("foobar.encoding", "java.nio.charset.Charset", null, "The charset-encoding to use for foobars"); + + assertCompletions( + "foobar.enco<*>" + , // ==> + "foobar.encoding=<*>" + ); + + assertCompletionWithLabel( + "foobar.encoding=UT<*>" + , + "UTF-8" + , + "foobar.encoding=UTF-8<*>" + ); + } + + @Ignore @Test public void testLocaleCompletions() throws Exception { + data("foobar.locale", "java.util.Locale", null, "Yada yada"); + + assertCompletions( + "foobar.loca<*>" + , // ==> + "foobar.locale=<*>" + ); + + assertCompletionWithLabel( + "foobar.locale=en<*>" + , + "en_CA" + , + "foobar.locale=en_CA<*>" + ); + } + + @Ignore @Test public void testPropertyValueHintCompletions() throws Exception { + //Test that 'value hints' work when property name is associated with 'value' hints. + // via boot metadata. + + //TODO: this should also work when hints associated with a + // map property key + // map property value + // list property value + + useProject(createPredefinedMavenProject("boot13")); + assertCompletionsDisplayString( + "spring.http.converters.preferred-json-mapper=<*>\n" + , //=> + "gson", + "jackson" + ); + } + + @Ignore @Test public void testPropertyListHintCompletions() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + + assertCompletion( + "management.health.status.ord<*>" + , //=> + "management.health.status.order=<*>" + ); + + assertCompletionsDisplayString( + "management.health.status.order=<*>" + , //=> + "DOWN", + "OUT_OF_SERVICE", + "UNKNOWN", + "UP" + ); + + assertCompletionsDisplayString( + "management.health.status.order=DOWN,<*>" + , //=> + "DOWN", + "OUT_OF_SERVICE", + "UNKNOWN", + "UP" + ); + } + + @Ignore @Test public void testPropertyMapValueCompletions() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + + assertCompletionsDisplayString( + "logging.level.some: <*>" + , // => + "trace", + "debug", + "info", + "warn", + "error", + "fatal", + "off" + ); + + assertCompletionsDisplayString( + "logging.level.some.package: <*>" + , // => + "trace", + "debug", + "info", + "warn", + "error", + "fatal", + "off" + ); + } + + @Ignore @Test public void testPropertyMapKeyCompletions() throws Exception { + useProject(createPredefinedMavenProject("boot13")); + assertCompletionWithLabel( + "logging.level.<*>" + , //============== + "root : String", + //=> + "logging.level.root=<*>" + ); + + assertCompletionWithLabel( + "logging.level.r<*>" + , //============== + "root : String", + //=> + "logging.level.root=<*>" + ); + + assertCompletionWithLabel( + "logging.level.ot<*>" + , //============== + "root : String", + //=> + "logging.level.root=<*>" + ); + } + + @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.terms-and-conditions=<*>" + , // => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + } + + @Ignore @Test public void testHandleAsListContentAssist() throws Exception { + data("my.tosses", "String[]", null, "A sequence of coin tosses") + .provider("handle-as", "target", "java.lang.Boolean[]"); + + assertCompletionsDisplayString( + "my.tosses[0]=<*>" + , // => + "true", + "false" + ); + } + + + @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.test-list-object.color-list[0]=not-a-color\n"+ + "test-map.test-list-object.color-list[1]=RED\n"+ + "test-map.test-list-object.color-list[2]=GREEN\n" + ); + editor.assertProblems( + "not-a-color|Expecting 'com.wellsfargo.lendingplatform.web.config.Color" + ); + + editor = newEditor( + "test-map.test-list-object.string-list[0]=not-a-color\n"+ + "test-map.test-list-object.string-list[1]=RED\n"+ + "test-map.test-list-object.string-list[2]=GREEN\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.some-string-key.col<*>" + , // => + "test-map.some-string-key.color-list=<*>" + ); + + assertCompletionsDisplayString( + "test-map.some-string-key.color-list[0]=<*>\n" + , // => + "red", "green", "blue" + ); + + assertCompletionsDisplayString( + "test-map.some-string-key.color-list[0]=<*>\n" + , // => + "red", "green", "blue" + ); + } + + @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( + "nicer<*>\n" + ,// => + "my.nice.resource=<*>\n" + ); + + assertCompletionsDisplayString( + "my.nice.resource=<*>\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."); + + assertCompletionsDisplayString( + "my.nice.resource=classpath:app<*>\n" + ,// => + "classpath:application.properties", + "classpath:application.yml" + ); + + //Test 'list item' context: + + assertCompletionsDisplayString( + "my.nice.list[0]=<*>" + ,// => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + + assertCompletionsDisplayString( + "my.nice.list[0]=classpath:app<*>\n" + ,// => + "classpath:application.properties", + "classpath:application.yml" + ); + + assertCompletionWithLabel( + "my.nice.list[0]=classpath:app<*>\n" + ,// ========== + "classpath:application.yml" + , // => + "my.nice.list[0]=classpath:application.yml<*>\n" + ); + + assertCompletionWithLabel( + "my.nice.list[0]= classpath:app<*>\n" + ,// ========== + "classpath:application.yml" + , // => + "my.nice.list[0]= classpath:application.yml<*>\n" + ); + + assertCompletionWithLabel( + "my.nice.list[0]=classpath:<*>\n" + ,// ========== + "classpath:application.yml" + , // => + "my.nice.list[0]=classpath:application.yml<*>\n" + ); + + //Test 'raw node' context + + // do we find resources in sub-folders too? + assertCompletionWithLabel( + "my.nice.resource=classpath:word<*>\n" + ,//=============== + "classpath:stuff/wordlist.txt" + ,// => + "my.nice.resource=classpath:stuff/wordlist.txt<*>\n" + ); + } + + @Ignore @Test public void testClasspathResourceCompletionInCommaList() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); + + useProject(createPredefinedMavenProject("boot13")); + data("my.nice.list", "java.util.List", null, "A nice list of resources."); + data("my.nice.array", "org.springframework.core.io.Resource[]", null, "A nice array of resources."); + + for (String kind : ImmutableList.of("list", "array")) { + assertCompletionWithLabel( + "my.nice."+kind+"=classpath:<*>" + ,//=========== + "classpath:stuff/wordlist.txt" + ,//=> + "my.nice."+kind+"=classpath:stuff/wordlist.txt<*>" + ); + + assertCompletionsDisplayString( + "my.nice."+kind+"=<*>" + ,// => + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + ); + + assertCompletionWithLabel( + "my.nice."+kind+"=classpath:stuff/wordlist.txt,classpath:app<*>" + ,//=========== + "classpath:application.yml" + ,//=> + "my.nice."+kind+"=classpath:stuff/wordlist.txt,classpath:application.yml<*>" + ); + } + } + + @Ignore @Test public void testClassReferenceCompletion() throws Exception { + CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); + + useProject(createPredefinedMavenProject("boot13_with_mongo")); + + assertCompletion( + "spring.data.mongodb.field-na<*>" + , // => + "spring.data.mongodb.field-naming-strategy=<*>" + ); + + assertCompletionsDisplayString( + "spring.data.mongodb.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.data.mongodb.field-naming-strategy=<*>" + , //===== + "org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy" + , //=> + "spring.data.mongodb.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.data.mongodb.field-naming-strategy=<*>" + // => + /*NONE*/ + ); + } + + @Ignore @Test public void testClassReferenceInValueLink() throws Exception { + Editor editor; + useProject(createPredefinedMavenProject("boot13_with_mongo")); + + editor = newEditor( + "#stuff\n" + + "spring.data.mongodb.field-naming-strategy=org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy\n" + + "#more stuff" + ); + 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( + "#stuff\n" + + "spring.data.mongodb.field-naming-strategy=java.lang.String\n" + + "#more stuff" + ); + editor.assertLinkTargets("java.lang.String", "java.lang.String"); + } + + @Ignore @Test public void testCommaListReconcile() throws Exception { + Editor editor; + IJavaProject p = createPredefinedMavenProject("demo-enum"); + + useProject(p); + assertNotNull(p.findType("demo.Color")); + + data("my.colors", "java.util.List", null, "Ooh! nice colors!"); + + editor = newEditor( + "#comment\n" + + "my.colors=RED, green, not-a-color , BLUE" + ); + editor.assertProblems( + "not-a-color|demo.Color" + ); + + editor = newEditor( + "my.colors=\\\n" + + " red,\\\n" + + " green,\\\n" + + " blue\n" + ); + editor.assertProblems(/*no problems*/); + + editor = newEditor( + "my.colors=\\\n" + + " bad,\\\n" + + " green,\\\n" + + " blue\n" + ); + editor.assertProblems( "bad|demo.Color"); + + editor = newEditor( + "my.colors=\\\n" + + " bad , \\\n" + + " green,\\\n" + + " blue\n" + ); + editor.assertProblems( "bad|demo.Color"); + + editor = newEditor( + "my.colors=\\\n" + + " red , \\\n" + + " green,\\\n" + + " bad\n" + ); + editor.assertProblems( "bad|demo.Color"); + + editor = newEditor( + "my.colors=\\\n" + + " red , \\\n" + + " green,\\\n" + + " bad \n" + ); + editor.assertProblems( "bad|demo.Color"); + + editor = newEditor( + "my.colors=red,\n" + ); + editor.assertProblems( ",|demo.Color"); + + + editor = newEditor( + "my.colors=red, \n" + ); + editor.assertProblems( " |demo.Color"); + } + + @Ignore @Test public void testReconcileDuplicateKey() throws Exception { + Editor editor; + data("some.property", "java.lang.String", null, "yada"); + data("some.other.property", "java.lang.String", null, "yada"); + + editor = newEditor( + "#comment\n" + + "some.property=stuff\n" + + "some.other.property=stuff\n" + + "some.property=different stuff\n" + ); + editor.assertProblems( + "some.property|Duplicate", + "some.property|Duplicate" + ); + + editor = newEditor( + "#comment\n" + + "some.property = stuff\n" + + "some.other.property=stuff\n" + + "some.property: different stuff\n" + + "some.other.property=stuff\n" + + "some.property: different stuff\n" + ); + editor.assertProblems( + "some.property|Duplicate", + "some.other.property|Duplicate", + "some.property|Duplicate", + "some.other.property|Duplicate", + "some.property|Duplicate" + ); + } + + @Ignore @Test public void test_PT_119352965() throws Exception { + data("some.property", "java.lang.String", null, "Some property to test stuff") + .valueHint("SOMETHING", "A value for something") + .valueHint("ALTERNATE", "An alternative value"); + data("some.other.property", "java.lang.String", null, "Another property to test stuff"); + + assertCompletionWithLabel( + "some.property=SOMETHING\n" + + "<*>" + , // =============== + "some.other.property : String" + , // => + "some.property=SOMETHING\n" + + "some.other.property=<*>" + ); + } + + @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.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.background: RED" + ); + editor.assertIsHoverRegion("RED"); + editor.assertHoverContains("RED", "Hot and delicious"); + + editor = newEditor( + "my.background=red" + ); + editor.assertHoverContains("red", "Hot and delicious"); + } + + + @Ignore @Test public void testEnumInValueLink() throws Exception { + useProject(createPredefinedMavenProject("demo-enum")); + data("my.background", "demo.Color", null, "Color to use as default background."); + + Editor editor; + + editor = newEditor( + "my.background: RED" + ); + editor.assertLinkTargets("RED", "demo.Color.RED"); + + editor = newEditor( + "my.background=red" + ); + editor.assertLinkTargets("red", "demo.Color.RED"); + } + + @Override + protected SimpleLanguageServer newLanguageServer() { + return new ApplicationPropertiesLanguageServer(); + } + + /** + * Like 'assertCompletionsBasic' but places the 'textBefore' in a context + * with other text around it... trying several different variations of + * text before and after the 'interesting' line. + */ + public void assertCompletionsVariations(String textBefore, String... expectTextAfter) throws Exception { + //Variation 1: by itself + assertCompletions(textBefore, expectTextAfter); + //Variation 2: comment text before and after + assertCompletions("#comment\n"+textBefore+"\n#comment", wrap("#comment\n", expectTextAfter, "\n#comment")); + //Variation 3: empty lines of text before and after + assertCompletions("\n"+textBefore+"\n\n", wrap("\n", expectTextAfter, "\n\n")); + //Variation 3.b: empty lines of text before and single newline after + assertCompletions("\n"+textBefore+"\n", wrap("\n", expectTextAfter, "\n")); + //Variation 4: property assignment before and after + assertCompletions("foo=bar\n"+textBefore+"\nnol=brol", wrap("foo=bar\n", expectTextAfter, "\nnol=brol")); + } + + private String[] wrap(String before, String[] middle, String after) { + //"\n"+expectTextAfter+"\n\n" + String[] result = new String[middle.length]; + for (int i = 0; i < result.length; i++) { + result[i] = before+middle[i]+after; + } + return result; + } + } diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlASTReconciler.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlASTReconciler.java index bed8f9e45..538d37783 100644 --- a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlASTReconciler.java +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlASTReconciler.java @@ -1,7 +1,7 @@ package org.springframework.ide.vscode.application.yaml.reconcile; -import static org.springframework.ide.vscode.application.yaml.reconcile.SpringPropertiesProblemType.YAML_DEPRECATED; -import static org.springframework.ide.vscode.application.yaml.reconcile.SpringPropertiesProblemType.YAML_DUPLICATE_KEY; +import static org.springframework.ide.vscode.application.yaml.reconcile.ApplicationYamlProblemType.YAML_DEPRECATED; +import static org.springframework.ide.vscode.application.yaml.reconcile.ApplicationYamlProblemType.YAML_DUPLICATE_KEY; import static org.springframework.ide.vscode.commons.yaml.ast.NodeUtil.asScalar; import static org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST.getChildren; @@ -273,19 +273,19 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler { } private void expectTypeFoundMapping(Type type, MappingNode node) { - expectType(SpringPropertiesProblemType.YAML_EXPECT_TYPE_FOUND_MAPPING, type, node); + expectType(ApplicationYamlProblemType.YAML_EXPECT_TYPE_FOUND_MAPPING, type, node); } private void expectTypeFoundSequence(Type type, SequenceNode seq) { - expectType(SpringPropertiesProblemType.YAML_EXPECT_TYPE_FOUND_SEQUENCE, type, seq); + expectType(ApplicationYamlProblemType.YAML_EXPECT_TYPE_FOUND_SEQUENCE, type, seq); } private void valueTypeMismatch(Type type, ScalarNode scalar) { - expectType(SpringPropertiesProblemType.YAML_VALUE_TYPE_MISMATCH, type, scalar); + expectType(ApplicationYamlProblemType.YAML_VALUE_TYPE_MISMATCH, type, scalar); } private void unkownProperty(Node node, String name, NodeTuple entry) { - SpringPropertyProblem p = problem(SpringPropertiesProblemType.YAML_UNKNOWN_PROPERTY, node, "Unknown property '"+name+"'"); + SpringPropertyProblem p = problem(ApplicationYamlProblemType.YAML_UNKNOWN_PROPERTY, node, "Unknown property '"+name+"'"); p.setPropertyName(extendForQuickfix(StringUtil.camelCaseToHyphens(name), entry.getValueNode())); problems.accept(p); } @@ -315,23 +315,23 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler { } private void expectScalar(Node node) { - problems.accept(problem(SpringPropertiesProblemType.YAML_EXPECT_SCALAR, node, "Expecting a 'Scalar' node but got "+describe(node))); + problems.accept(problem(ApplicationYamlProblemType.YAML_EXPECT_SCALAR, node, "Expecting a 'Scalar' node but got "+describe(node))); } protected void expectMapping(Node node) { - problems.accept(problem(SpringPropertiesProblemType.YAML_EXPECT_MAPPING, node, "Expecting a 'Mapping' node but got "+describe(node))); + problems.accept(problem(ApplicationYamlProblemType.YAML_EXPECT_MAPPING, node, "Expecting a 'Mapping' node but got "+describe(node))); } private void expectBeanPropertyName(Node keyNode, Type type) { - problems.accept(problem(SpringPropertiesProblemType.YAML_EXPECT_BEAN_PROPERTY_NAME, keyNode, "Expecting a bean-property name for object of type '"+typeUtil.niceTypeName(type)+"' " + problems.accept(problem(ApplicationYamlProblemType.YAML_EXPECT_BEAN_PROPERTY_NAME, keyNode, "Expecting a bean-property name for object of type '"+typeUtil.niceTypeName(type)+"' " + "but got "+describe(keyNode))); } private void unknownBeanProperty(Node keyNode, Type type, String name) { - problems.accept(problem(SpringPropertiesProblemType.YAML_INVALID_BEAN_PROPERTY, keyNode, "Unknown property '"+name+"' for type '"+typeUtil.niceTypeName(type)+"'")); + problems.accept(problem(ApplicationYamlProblemType.YAML_INVALID_BEAN_PROPERTY, keyNode, "Unknown property '"+name+"' for type '"+typeUtil.niceTypeName(type)+"'")); } - private void expectType(SpringPropertiesProblemType problemType, Type type, Node node) { + private void expectType(ApplicationYamlProblemType problemType, Type type, Node node) { problems.accept(problem(problemType, node, "Expecting a '"+typeUtil.niceTypeName(type)+"' but got "+describe(node))); } @@ -356,7 +356,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler { return problem; } - protected SpringPropertyProblem problem(SpringPropertiesProblemType type, Node node, String msg) { + protected SpringPropertyProblem problem(ApplicationYamlProblemType type, Node node, String msg) { int start = node.getStartMark().getIndex(); int end = node.getEndMark().getIndex(); return SpringPropertyProblem.problem(type, msg, start, end-start); diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlProblemType.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlProblemType.java new file mode 100644 index 000000000..9ad4276b3 --- /dev/null +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/ApplicationYamlProblemType.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * 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.application.yaml.reconcile; + +import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.ERROR; +import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.WARNING; + +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; + +/** + * @author Kris De Volder + */ +public enum ApplicationYamlProblemType implements ProblemType { + + YAML_SYNTAX_ERROR("Error parsing the input using snakeyaml"), + YAML_UNKNOWN_PROPERTY(WARNING, "Property-key not found in the configuration metadata on the project's classpath"), + YAML_VALUE_TYPE_MISMATCH("Expecting a value of a certain type, but value doesn't parse as such"), + YAML_EXPECT_SCALAR("Expecting a 'scalar' value but found something more complex."), + YAML_EXPECT_TYPE_FOUND_SEQUENCE("Found a 'sequence' node where a non 'list-like' type is expected"), + YAML_EXPECT_TYPE_FOUND_MAPPING("Found a 'mapping' node where a type that can't be treated as a 'property map' is expected"), + YAML_EXPECT_MAPPING("Expecting a 'mapping' node but found something else"), + YAML_EXPECT_BEAN_PROPERTY_NAME("Expecting a 'bean property' name but found something more complex"), + YAML_INVALID_BEAN_PROPERTY("Accessing a named property in a type that doesn't provide a property accessor with that name"), + YAML_DEPRECATED(WARNING, "Property is marked as Deprecated"), + YAML_DUPLICATE_KEY("A mapping node contains multiple entries for the same key"); + + private final ProblemSeverity defaultSeverity; + private String description; + private String label; + + private ApplicationYamlProblemType(ProblemSeverity defaultSeverity, String description, String label) { + this.description = description; + this.defaultSeverity = defaultSeverity; + this.label = label; + } + + private ApplicationYamlProblemType(ProblemSeverity defaultSeverity, String description) { + this(defaultSeverity, description, null); + } + + private ApplicationYamlProblemType(String description) { + this(ERROR, description); + } + + public ProblemSeverity getDefaultSeverity() { + return defaultSeverity; + } + + + public String getLabel() { + if (label==null) { + label = createDefaultLabel(); + } + return label; + } + + public String getDescription() { + return description; + } + + private String createDefaultLabel() { + String label = this.toString().substring(5).toLowerCase().replace('_', ' '); + return Character.toUpperCase(label.charAt(0)) + label.substring(1); + } + + @Override + public String getCode() { + return name(); + } + +} diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/SpringPropertiesProblemType.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/SpringPropertiesProblemType.java deleted file mode 100644 index e8ee3b2e6..000000000 --- a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/SpringPropertiesProblemType.java +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************* - * 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.application.yaml.reconcile; - -import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.*; - -import java.util.ArrayList; - -import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity; -import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType; - -/** - * @author Kris De Volder - */ -public enum SpringPropertiesProblemType implements ProblemType { - - // Naming: - // YAML_* for all problems in .yml files. - // PROP_* for all problems in .properties files. - // All enum values must start with one or the other (or some stuff will break!). - -// PROP_INVALID_BEAN_NAVIGATION("Accessing a 'bean property' in a type that doesn't have properties (e.g. like String or Integer)"), -// PROP_INVALID_INDEXED_NAVIGATION("Accessing a property using [] in a type that doesn't support that"), -// PROP_EXPECTED_DOT_OR_LBRACK("Unexpected character found where a '.' or '[' was expected"), -// PROP_NO_MATCHING_RBRACK("Found a '[' but no matching ']'"), -// PROP_NON_INTEGER_IN_BRACKETS("Use of [..] navigation with non-integer value"), -// PROP_VALUE_TYPE_MISMATCH("Expecting a value of a certain type, but value doesn't parse as such"), -// PROP_INVALID_BEAN_PROPERTY("Accessing a named property in a type that doesn't provide a property accessor with that name"), -// PROP_UNKNOWN_PROPERTY(WARNING, "Property-key not found in any configuration metadata on the project's classpath"), -// PROP_DEPRECATED(WARNING, "Property is marked as Deprecated"), -// PROP_DUPLICATE_KEY("Multiple assignments to the same property value"), - - YAML_SYNTAX_ERROR("Error parsing the input using snakeyaml"), - YAML_UNKNOWN_PROPERTY(WARNING, "Property-key not found in the configuration metadata on the project's classpath"), - YAML_VALUE_TYPE_MISMATCH("Expecting a value of a certain type, but value doesn't parse as such"), - YAML_EXPECT_SCALAR("Expecting a 'scalar' value but found something more complex."), - YAML_EXPECT_TYPE_FOUND_SEQUENCE("Found a 'sequence' node where a non 'list-like' type is expected"), - YAML_EXPECT_TYPE_FOUND_MAPPING("Found a 'mapping' node where a type that can't be treated as a 'property map' is expected"), - YAML_EXPECT_MAPPING("Expecting a 'mapping' node but found something else"), - YAML_EXPECT_BEAN_PROPERTY_NAME("Expecting a 'bean property' name but found something more complex"), - YAML_INVALID_BEAN_PROPERTY("Accessing a named property in a type that doesn't provide a property accessor with that name"), - YAML_DEPRECATED(WARNING, "Property is marked as Deprecated"), - YAML_DUPLICATE_KEY("A mapping node contains multiple entries for the same key"); - - private final ProblemSeverity defaultSeverity; - private String description; - private String label; - - private SpringPropertiesProblemType(ProblemSeverity defaultSeverity, String description, String label) { - this.description = description; - this.defaultSeverity = defaultSeverity; - this.label = label; - } - - private SpringPropertiesProblemType(ProblemSeverity defaultSeverity, String description) { - this(defaultSeverity, description, null); - } - - private SpringPropertiesProblemType(String description) { - this(ERROR, description); - } - - public ProblemSeverity getDefaultSeverity() { - return defaultSeverity; - } - - - public static SpringPropertiesProblemType[] forProperties() { - return withPrefix("PROP_"); - } - - - private static SpringPropertiesProblemType[] withPrefix(String prefix) { - SpringPropertiesProblemType[] allValues = values(); - ArrayList values = new ArrayList(allValues.length); - for (SpringPropertiesProblemType v : allValues) { - if (v.toString().startsWith(prefix)) { - values.add(v); - } - } - return values.toArray(new SpringPropertiesProblemType[values.size()]); - } - - public String getLabel() { - if (label==null) { - label = createDefaultLabel(); - } - return label; - } - - public String getDescription() { - return description; - } - - private String createDefaultLabel() { - String label = this.toString().substring(5).toLowerCase().replace('_', ' '); - return Character.toUpperCase(label.charAt(0)) + label.substring(1); - } - - @Override - public String getCode() { - return name(); - } - -// TODO: obsolete? We should simply keep the problemtype implementations for yaml / props editor totally separate -// public static final SpringPropertiesProblemType[] FOR_YAML = FOR(EditorType.YAML); -// public static final SpringPropertiesProblemType[] FOR_PROPERTIES = FOR(EditorType.PROP); -// public static SpringPropertiesProblemType[] FOR(EditorType et) { -// return withPrefix(et.getProblemTypePrefix()); -// } -// public EditorType getEditorType() { -// String string = this.toString(); -// for (EditorType et : EditorType.values()) { -// String prefix = et.getProblemTypePrefix(); -// if (string.startsWith(prefix)) { -// return et; -// } -// } -// throw new IllegalStateException("Bug: unknown editor type for "+this); -// } -} diff --git a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/SpringPropertyProblem.java b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/SpringPropertyProblem.java index 8c71402a3..55fa13c52 100644 --- a/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/SpringPropertyProblem.java +++ b/vscode-extensions/vscode-application-yaml/src/main/java/org/springframework/ide/vscode/application/yaml/reconcile/SpringPropertyProblem.java @@ -15,7 +15,7 @@ public class SpringPropertyProblem extends ReconcileProblemImpl { super(type, msg, offset, len); } - public static SpringPropertyProblem problem(SpringPropertiesProblemType type, String msg, int offset, int len) { + public static SpringPropertyProblem problem(ApplicationYamlProblemType type, String msg, int offset, int len) { return new SpringPropertyProblem(type, msg, offset, len); }