/* * Copyright 2017 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.cloud.tools.eclipse.appengine.facets; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import com.google.cloud.tools.eclipse.test.util.project.ProjectUtils; import com.google.cloud.tools.eclipse.test.util.project.TestProjectCreator; import com.google.cloud.tools.eclipse.util.io.ResourceUtils; import com.google.common.io.ByteStreams; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.Path; import org.eclipse.jst.common.project.facet.core.JavaFacet; import org.eclipse.jst.j2ee.web.project.facet.WebFacetUtils; import org.eclipse.wst.common.componentcore.resources.IVirtualFile; import org.eclipse.wst.common.componentcore.resources.IVirtualFolder; import org.eclipse.wst.common.componentcore.resources.IVirtualResource; import org.junit.After; import org.junit.Rule; import org.junit.Test; public class WebProjectUtilTest { private static final Logger logger = Logger.getLogger(WebProjectUtilTest.class.getName()); @Rule public TestProjectCreator testProjectCreator = new TestProjectCreator(); private IProject importedProject; @After public void tearDown() { if (importedProject != null) { try { importedProject.delete(true, null); } catch (CoreException ex) { logger.log(Level.SEVERE, "Failure removing imported project", ex); } } } @Test public void testFindWebInfFile_plainEmpty() { IProject project = testProjectCreator.getProject(); IFile webXml = WebProjectUtil.findInWebInf(project, new Path("web.xml")); assertNull("Should not be found in empty plain project", webXml); } @Test public void testCreateWebInfFile_plainEmpty() throws CoreException, IOException { IProject project = testProjectCreator.getProject(); // default webapp location is src/main/webapp IFile fooTxt = WebProjectUtil.createFileInWebInf( project, new Path("foo.txt"), asInputStream("foo"), false /*overwrite*/, null); assertNotNull(fooTxt); assertTrue(fooTxt.exists()); assertEquals("src/main/webapp/WEB-INF/foo.txt", fooTxt.getProjectRelativePath().toString()); assertEquals("foo", readAsString(fooTxt)); } @Test public void testCreateWebInfFolder_plainEmpty() throws CoreException { IProject project = testProjectCreator.getProject(); // default webapp location is src/main/webapp IFolder libFolder = WebProjectUtil.createFolderInWebInf(project, new Path("lib"), null); assertNotNull(libFolder); assertTrue(libFolder.exists()); assertEquals("src/main/webapp/WEB-INF/lib", libFolder.getProjectRelativePath().toString()); } @Test public void testFindWebInfFile_plainMavenLikeProject() throws CoreException { IProject project = testProjectCreator.getProject(); createFile(project, new Path("src/main/webapp/WEB-INF/web.xml"), asInputStream("<web-app/>")); IFile webXml = WebProjectUtil.findInWebInf(project, new Path("web.xml")); assertNotNull(webXml); assertEquals("src/main/webapp/WEB-INF/web.xml", webXml.getProjectRelativePath().toString()); } @Test public void testCreateWebInfFile_plainMavenLikeProject() throws CoreException, IOException { IProject project = testProjectCreator.getProject(); IFile webXml = createFile(project, new Path("src/main/webapp/WEB-INF/web.xml"), asInputStream("<web-app/>")); assertNotNull(webXml); assertTrue(webXml.exists()); // should be found alongside the web.xml IFile fooTxt = WebProjectUtil.createFileInWebInf( project, new Path("foo.txt"), asInputStream("foo"), false /*overwrite*/, null); assertNotNull(fooTxt); assertTrue(fooTxt.exists()); assertEquals("src/main/webapp/WEB-INF/foo.txt", fooTxt.getProjectRelativePath().toString()); assertEquals("foo", readAsString(fooTxt)); } @Test public void testCreateWebInfFolder_plainMavenLikeProject() throws CoreException { IProject project = testProjectCreator.getProject(); IFile webXml = createFile(project, new Path("src/main/webapp/WEB-INF/web.xml"), asInputStream("<web-app/>")); assertNotNull(webXml); assertTrue(webXml.exists()); // should be found alongside the web.xml IFolder libFolder = WebProjectUtil.createFolderInWebInf(project, new Path("lib"), null); assertNotNull(libFolder); assertTrue(libFolder.exists()); assertEquals("src/main/webapp/WEB-INF/lib", libFolder.getProjectRelativePath().toString()); } @Test public void testFindWebInfFile_plainWtpLikeProject() throws CoreException { // WTP normally suggests putting the content in WebContent/ IProject project = testProjectCreator.getProject(); createFile(project, new Path("WebContent/WEB-INF/web.xml"), asInputStream("<web-app/>")); IFile webXml = WebProjectUtil.findInWebInf(project, new Path("web.xml")); assertNotNull(webXml); assertEquals("WebContent/WEB-INF/web.xml", webXml.getProjectRelativePath().toPortableString()); } @Test public void testCreateWebInfFile_plainWtpLikeProject() throws CoreException, IOException { IProject project = testProjectCreator.getProject(); IFile webXml = createFile(project, new Path("WebContent/WEB-INF/web.xml"), asInputStream("<web-app/>")); assertNotNull(webXml); assertTrue(webXml.exists()); // should be found alongside the web.xml IFile fooTxt = WebProjectUtil.createFileInWebInf( project, new Path("foo.txt"), asInputStream("foo"), false /*overwrite*/, null); assertNotNull(fooTxt); assertTrue(fooTxt.exists()); assertEquals("WebContent/WEB-INF/foo.txt", fooTxt.getProjectRelativePath().toString()); assertEquals("foo", readAsString(fooTxt)); } @Test public void testCreateWebInfFolder_plainWtpLikeProject() throws CoreException { IProject project = testProjectCreator.getProject(); IFile webXml = createFile(project, new Path("WebContent/WEB-INF/web.xml"), asInputStream("<web-app/>")); assertNotNull(webXml); assertTrue(webXml.exists()); // should be found alongside the web.xml IFolder libFolder = WebProjectUtil.createFolderInWebInf(project, new Path("lib"), null); assertNotNull(libFolder); assertTrue(libFolder.exists()); assertEquals("WebContent/WEB-INF/lib", libFolder.getProjectRelativePath().toString()); } @Test public void testFindWebInfFile_plainWebProject() throws CoreException { // WTP normally suggests putting the content in WebContent/ IProject project = testProjectCreator.getProject(); createFile(project, new Path("web/WEB-INF/web.xml"), asInputStream("<web-app/>")); IFile webXml = WebProjectUtil.findInWebInf(project, new Path("web.xml")); assertNotNull(webXml); assertEquals("web/WEB-INF/web.xml", webXml.getProjectRelativePath().toPortableString()); } @Test public void testCreateWebInfFile_plainWebProject() throws CoreException, IOException { IProject project = testProjectCreator.getProject(); IFile webXml = createFile(project, new Path("web/WEB-INF/web.xml"), asInputStream("<web-app/>")); assertNotNull(webXml); assertTrue(webXml.exists()); // should be found alongside the web.xml IFile fooTxt = WebProjectUtil.createFileInWebInf( project, new Path("foo.txt"), asInputStream("foo"), false /*overwrite*/, null); assertNotNull(fooTxt); assertTrue(fooTxt.exists()); assertEquals("web/WEB-INF/foo.txt", fooTxt.getProjectRelativePath().toString()); assertEquals("foo", readAsString(fooTxt)); } @Test public void testCreateWebInfFolder_plainWebProject() throws CoreException { IProject project = testProjectCreator.getProject(); IFile webXml = createFile(project, new Path("web/WEB-INF/web.xml"), asInputStream("<web-app/>")); assertNotNull(webXml); assertTrue(webXml.exists()); // should be found alongside the web.xml IFolder libFolder = WebProjectUtil.createFolderInWebInf(project, new Path("lib"), null); assertNotNull(libFolder); assertTrue(libFolder.exists()); assertEquals("web/WEB-INF/lib", libFolder.getProjectRelativePath().toString()); } @Test public void testFindWebInfFile_dynamicWebProject() { // WTP's Dynamic Web Project should create a web.xml IProject project = testProjectCreator .withFacets(JavaFacet.VERSION_1_7, WebFacetUtils.WEB_25).getProject(); IFile webXml = WebProjectUtil.findInWebInf(project, new Path("web.xml")); assertNotNull(webXml); assertTrue(webXml.exists()); } @Test public void testCreateWebInfFile_dynamicWebProject() throws CoreException, IOException { IProject project = testProjectCreator .withFacets(JavaFacet.VERSION_1_7, WebFacetUtils.WEB_25).getProject(); IFile webXml = WebProjectUtil.findInWebInf(project, new Path("web.xml")); assertNotNull(webXml); assertTrue(webXml.exists()); IFolder webInfDir = (IFolder) webXml.getParent(); // should be found alongside the web.xml IFile fooTxt = WebProjectUtil.createFileInWebInf( project, new Path("foo.txt"), asInputStream("foo"), false /*overwrite*/, null); assertNotNull(fooTxt); assertTrue(fooTxt.exists()); assertEquals(webInfDir.getProjectRelativePath().append("foo.txt").toString(), fooTxt.getProjectRelativePath().toString()); assertEquals("foo", readAsString(fooTxt)); } @Test public void testCreateWebInfFolder_dynamicWebProject() throws CoreException { IProject project = testProjectCreator .withFacets(JavaFacet.VERSION_1_7, WebFacetUtils.WEB_25).getProject(); IFile webXml = WebProjectUtil.findInWebInf(project, new Path("web.xml")); assertNotNull(webXml); assertTrue(webXml.exists()); IFolder webInfDir = (IFolder) webXml.getParent(); // should be found alongside the web.xml IFolder libFolder = WebProjectUtil.createFolderInWebInf(project, new Path("lib"), null); assertNotNull(libFolder); assertTrue(libFolder.exists()); assertEquals(webInfDir.getProjectRelativePath().append("lib").toString(), libFolder.getProjectRelativePath().toString()); } @Test public void testFindWebInfFile_dynamicWebProject_defaultRootSourceProject() throws CoreException, IOException { // WTP's Dynamic Web Project with multiple <wb-resource> elements // - looking up a file should look in the wb-resource in order Map<String, IProject> projects = ProjectUtils.importProjects( getClass(), "projects/test-dynamic-web-project-dynamicrootsource.zip", true, null); assertEquals(1, projects.size()); importedProject = projects.values().iterator().next(); createFile(importedProject, new Path("target/m2e-wtp/web-resources/WEB-INF/foo.txt"), asInputStream("m2e-wtp")); createFile(importedProject, new Path("src/main/webapp/WEB-INF/foo.txt"), asInputStream("webapp")); createFile(importedProject, new Path("src/main/webapp/WEB-INF/bar.txt"), asInputStream("webapp")); // foo.txt should be resolved to the first <wb-resource> IFile fooTxt = WebProjectUtil.findInWebInf(importedProject, new Path("foo.txt")); assertNotNull(fooTxt); assertTrue(fooTxt.exists()); assertEquals("target/m2e-wtp/web-resources/WEB-INF/foo.txt", fooTxt.getProjectRelativePath().toString()); assertEquals("m2e-wtp", readAsString(fooTxt)); // bar.txt should be resolved to the second <wb-resource> IFile barTxt = WebProjectUtil.findInWebInf(importedProject, new Path("bar.txt")); assertNotNull(barTxt); assertTrue(barTxt.exists()); assertEquals("src/main/webapp/WEB-INF/bar.txt", barTxt.getProjectRelativePath().toString()); assertEquals("webapp", readAsString(barTxt)); } @Test public void testCreateWebInfFile_dynamicWebProject_defaultRootSourceProject() throws CoreException, IOException { // WTP's Dynamic Web Project with multiple <wb-resource> elements // - creating a file should be put in the defaultRootSource Map<String, IProject> projects = ProjectUtils.importProjects( getClass(), "projects/test-dynamic-web-project-dynamicrootsource.zip", true, null); assertEquals(1, projects.size()); importedProject = projects.values().iterator().next(); IFile fooTxt = WebProjectUtil.createFileInWebInf( importedProject, new Path("foo.txt"), asInputStream("foo"), false /*overwrite*/, null); // foo.txt should be created in the <wb-resource> tagged with `defaultRootSource` assertNotNull(fooTxt); assertTrue(fooTxt.exists()); assertEquals("src/main/webapp/WEB-INF/foo.txt", fooTxt.getProjectRelativePath().toString()); assertEquals("foo", readAsString(fooTxt)); } @Test public void testCreateWebInfFolder_dynamicWebProject_defaultRootSourceProject() throws CoreException, IOException { // WTP's Dynamic Web Project with multiple <wb-resource> elements // - creating a file should be put in the defaultRootSource Map<String, IProject> projects = ProjectUtils.importProjects( getClass(), "projects/test-dynamic-web-project-dynamicrootsource.zip", true, null); assertEquals(1, projects.size()); importedProject = projects.values().iterator().next(); IFolder libFolder = WebProjectUtil.createFolderInWebInf(importedProject, new Path("lib"), null); // lib should be created in the <wb-resource> tagged with `defaultRootSource` assertNotNull(libFolder); assertTrue(libFolder.exists()); assertEquals("src/main/webapp/WEB-INF/lib", libFolder.getProjectRelativePath().toString()); } @Test public void testHasJsps_noFiles() throws CoreException { IVirtualFolder root = mock(IVirtualFolder.class, "/"); IVirtualFile txt = mock(IVirtualFile.class, "/a.txt"); when(txt.getFileExtension()).thenReturn("txt"); IVirtualFolder folder = mock(IVirtualFolder.class, "/a"); when(root.members()).thenReturn(new IVirtualResource[] {txt, folder}); when(folder.members()).thenReturn(new IVirtualResource[] {}); assertFalse(WebProjectUtil.hasJsps(root)); // no jsps, should traverse all folders verify(root, times(2)).members(); verify(folder, times(2)).members(); verify(txt).getFileExtension(); verifyNoMoreInteractions(root, folder, txt); } @Test public void testHasJsps_jspInRoot() throws CoreException { IVirtualFolder root = mock(IVirtualFolder.class, "/"); IVirtualFile jsp = mock(IVirtualFile.class, "/a.jsp"); when(jsp.getFileExtension()).thenReturn("jsp"); IVirtualFolder folder = mock(IVirtualFolder.class, "/a"); when(root.members()).thenReturn(new IVirtualResource[] {jsp, folder}); when(folder.members()).thenReturn(new IVirtualResource[] {}); assertTrue(WebProjectUtil.hasJsps(root)); // jsp in root means no traversal to folder verify(root).members(); verify(jsp).getFileExtension(); verify(folder, times(0)).members(); verifyNoMoreInteractions(root, folder, jsp); } @Test public void testHasJsps_jspInSub() throws CoreException { IVirtualFolder root = mock(IVirtualFolder.class, "/"); IVirtualFolder folder = mock(IVirtualFolder.class, "/a"); IVirtualFile jsp = mock(IVirtualFile.class, "/a/a.jsp"); when(jsp.getFileExtension()).thenReturn("jsp"); when(root.members()).thenReturn(new IVirtualResource[] {folder}); when(folder.members()).thenReturn(new IVirtualResource[] {jsp}); assertTrue(WebProjectUtil.hasJsps(root)); verify(root, times(2)).members(); verify(folder).members(); verify(jsp).getFileExtension(); verifyNoMoreInteractions(root, folder, jsp); } /** Create a file at the specific location, ensuring all folders are created as required. */ private IFile createFile(IProject project, Path filePath, InputStream fileContents) throws CoreException { IFile file = project.getFile(filePath); ResourceUtils.createFolders(file.getParent(), null); assertTrue(file.getParent().exists()); if (file.exists()) { file.setContents(fileContents, IFile.FORCE, null); } else { file.create(fileContents, IFile.FORCE, null); } return file; } /** * Convert the content into a ByteArrayInputStream. */ private static InputStream asInputStream(String content) { return new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); } /** * Read the contents of the provided file as a UTF-8-encoded string. */ private Object readAsString(IFile file) throws IOException, CoreException { try (InputStream input = file.getContents()) { return new String(ByteStreams.toByteArray(input), StandardCharsets.UTF_8); } } }