diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/src/com/google/cloud/tools/eclipse/appengine/libraries/PomFormatTest.java b/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/src/com/google/cloud/tools/eclipse/appengine/libraries/PomFormatTest.java new file mode 100644 index 0000000000..2ffb95114a --- /dev/null +++ b/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/src/com/google/cloud/tools/eclipse/appengine/libraries/PomFormatTest.java @@ -0,0 +1,139 @@ +/* + * Copyright 2019 Google LLC + * + * 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.libraries; + +import static org.junit.Assert.fail; + +import com.google.cloud.tools.eclipse.appengine.libraries.model.Library; +import com.google.cloud.tools.eclipse.appengine.libraries.model.LibraryFile; +import com.google.cloud.tools.eclipse.test.util.project.TestProjectCreator; +import com.google.cloud.tools.eclipse.util.ArtifactRetriever; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Platform; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.xml.sax.SAXException; + +public class PomFormatTest { + + @Rule + public final TestProjectCreator projectCreator = new TestProjectCreator(); + private Pom pom; + private IFile pomFile; + + @Before + public void setUp() throws SAXException, IOException, CoreException { + IProject project = projectCreator.getProject(); + pomFile = project.getFile("pom.xml"); + try (InputStream in = + Files.newInputStream(Paths.get("testdata/testPomEmpty.xml").toAbsolutePath())) { + pomFile.create(in, IFile.FORCE, null); + pom = Pom.parse(pomFile); + } + + Logger logger = Logger.getLogger(ArtifactRetriever.class.getName()); + logger.setLevel(Level.OFF); + } + + @After + public void tearDown() { + Logger logger = Logger.getLogger(ArtifactRetriever.class.getName()); + logger.setLevel(null); + } + + @Test + public void testAddDependencies() throws Exception { + Library library0 = PomTest.newLibrary("id0", + new LibraryFile(PomTest.coordinates("com.example.group0", "artifact0", "1.2.3"))); + Library library1 = PomTest.newLibrary("id1", + new LibraryFile(PomTest.coordinates("com.example.group1", "artifact1"))); + Library library2 = PomTest.newLibrary("id2", + new LibraryFile(PomTest.coordinates("com.example.group2", "artifact2")), + new LibraryFile(PomTest.coordinates("com.example.group3", "artifact3"))); + + List libraries = Arrays.asList(library0, library1, library2); + pom.addDependencies(libraries); + + Bundle bundle = Platform.getBundle("com.google.cloud.tools.eclipse.appengine.libraries.test"); + URL fileUrl = bundle.getEntry("/testdata/formatAdd.xml"); + File expected = new File(FileLocator.resolve(fileUrl).toURI()); + assertFileContentsEqual(expected, pomFile.getLocation().toFile()); + } + + @Test + public void testRemoveUnusedDependencies() throws Exception { + LibraryFile file1 = new LibraryFile(PomTest.coordinates("com.example.group1", "artifact1")); + LibraryFile file2 = new LibraryFile(PomTest.coordinates("com.example.group2", "artifact2")); + Library library1 = PomTest.newLibrary("id1", file1); + Library library2 = PomTest.newLibrary("id2", file1, file2); + + pom.addDependencies(Arrays.asList(library1, library2)); + + Bundle bundle = Platform.getBundle("com.google.cloud.tools.eclipse.appengine.libraries.test"); + URL fileUrl = bundle.getEntry("/testdata/formatRemove.xml"); + File expected = new File(FileLocator.resolve(fileUrl).toURI()); + assertFileContentsEqual(expected, pomFile.getLocation().toFile()); + } + + private static void assertFileContentsEqual(File expected, File actual) throws IOException { + final List expectedLines = + Files.readAllLines(expected.toPath(), Charset.forName("UTF-8")); + final List actualLines = Files.readAllLines(actual.toPath(), Charset.forName("UTF-8")); + + if (expectedLines == null || expectedLines.size() == 0) { + fail("Expected file shouldn't be empty"); + return; + } + if (expectedLines.size() != actualLines.size()) { + fail("Line sizes are differed, expected.length=" + expectedLines.size() + " actual.length=" + + actualLines.size()); + return; + } + + final List diff = new ArrayList<>(); + for (final String line : expectedLines) { + if (!actualLines.contains(line)) { + diff.add(("Line Number : " + expectedLines.indexOf(line) + 1) + " " + line + "\n"); + } + } + if (diff.size() > 0) { + String message = "The differences are:\n"; + for (String line : diff) { + message = message.concat(line); + } + fail(message); + } + } +} diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/src/com/google/cloud/tools/eclipse/appengine/libraries/PomTest.java b/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/src/com/google/cloud/tools/eclipse/appengine/libraries/PomTest.java index c0acc349cb..1cc1c3dc7c 100644 --- a/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/src/com/google/cloud/tools/eclipse/appengine/libraries/PomTest.java +++ b/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/src/com/google/cloud/tools/eclipse/appengine/libraries/PomTest.java @@ -373,17 +373,17 @@ private static Element getOnlyChild(Element element, String name) { return (Element) children.item(0); } - private static Library newLibrary(String libraryId, LibraryFile... libraryFiles) { + static Library newLibrary(String libraryId, LibraryFile... libraryFiles) { Library library = new Library(libraryId); library.setLibraryFiles(Arrays.asList(libraryFiles)); return library; } - private static MavenCoordinates coordinates(String groupId, String artifactId) { + static MavenCoordinates coordinates(String groupId, String artifactId) { return new MavenCoordinates.Builder().setGroupId(groupId).setArtifactId(artifactId).build(); } - private static MavenCoordinates coordinates(String groupId, String artifactId, String version) { + static MavenCoordinates coordinates(String groupId, String artifactId, String version) { return new MavenCoordinates.Builder().setGroupId(groupId).setArtifactId(artifactId) .setVersion(version).build(); } diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/testdata/formatAdd.xml b/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/testdata/formatAdd.xml new file mode 100644 index 0000000000..c126a31a2e --- /dev/null +++ b/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/testdata/formatAdd.xml @@ -0,0 +1,33 @@ + + + + + + com.google.cloud + google-cloud-bom + pom + import + 0.99.0-alpha + + + + + + com.example.group0 + artifact0 + 1.2.3 + + + com.example.group1 + artifact1 + + + com.example.group2 + artifact2 + + + com.example.group3 + artifact3 + + + diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/testdata/formatRemove.xml b/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/testdata/formatRemove.xml new file mode 100644 index 0000000000..372a77ef84 --- /dev/null +++ b/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/testdata/formatRemove.xml @@ -0,0 +1,24 @@ + + + + + + com.google.cloud + google-cloud-bom + pom + import + 0.99.0-alpha + + + + + + com.example.group1 + artifact1 + + + com.example.group2 + artifact2 + + + diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/testdata/testPomEmpty.xml b/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/testdata/testPomEmpty.xml new file mode 100644 index 0000000000..546e0fd8dd --- /dev/null +++ b/plugins/com.google.cloud.tools.eclipse.appengine.libraries.test/testdata/testPomEmpty.xml @@ -0,0 +1,6 @@ + + + diff --git a/plugins/com.google.cloud.tools.eclipse.appengine.libraries/src/com/google/cloud/tools/eclipse/appengine/libraries/Pom.java b/plugins/com.google.cloud.tools.eclipse.appengine.libraries/src/com/google/cloud/tools/eclipse/appengine/libraries/Pom.java index cb11ac82b3..2f98f7c53c 100644 --- a/plugins/com.google.cloud.tools.eclipse.appengine.libraries/src/com/google/cloud/tools/eclipse/appengine/libraries/Pom.java +++ b/plugins/com.google.cloud.tools.eclipse.appengine.libraries/src/com/google/cloud/tools/eclipse/appengine/libraries/Pom.java @@ -234,7 +234,7 @@ void updateDependencies(Collection selectedLibraries, dependency.appendChild(artifactIdElement); if (testComment == null) { - dependencies.appendChild(dependency); + addChild(dependencies, dependency); } else { dependencies.insertBefore(dependency, testComment); } @@ -279,7 +279,7 @@ private void handleDependencyManaged(LibraryFile artifact, Element dependency) { } } else { if (versionNode != null) { - dependency.removeChild(versionNode); + removeChild(dependency, versionNode); } } } @@ -305,8 +305,8 @@ private void createBOMIfNeeded(XPath xpath) throws CoreException { dependencyManagement = document.createElementNS("http://maven.apache.org/POM/4.0.0", "dependencyManagement"); } - dependencyManagement.appendChild(dependencies); - document.getDocumentElement().appendChild(dependencyManagement); + addChild(dependencyManagement, dependencies); + addChild(document.getDocumentElement(), dependencyManagement); } Element dependency = @@ -321,7 +321,7 @@ private void createBOMIfNeeded(XPath xpath) throws CoreException { if (bom != null) { boms.add(bom); } - dependencies.appendChild(dependency); + addChild(dependencies, dependency); } } catch (XPathExpressionException ex) { IStatus status = StatusUtil.error(Pom.class, ex.getMessage(), ex); @@ -444,7 +444,7 @@ static void removeUnusedDependencies(Element dependencies, } for (Node node : nodesToRemove) { - dependencies.removeChild(node); + removeChild(dependencies, node); } } @@ -482,10 +482,29 @@ private static String getValue(Element dependency, String childName) { private void writeDocument() throws CoreException, TransformerException { Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount","2"); //$NON-NLS-1$ //$NON-NLS-2$ ByteArrayOutputStream out = new ByteArrayOutputStream(); transformer.transform(new DOMSource(document), new StreamResult(out)); InputStream in = new ByteArrayInputStream(out.toByteArray()); pomFile.setContents(in, true, true, null); } + + private void addChild(Node parent, Node newChild) { + parent.appendChild(newChild); + removeWhiteSpaceBefore(newChild);// for child indentation + } + + private static void removeChild(Node parent, Node oldChild) { + removeWhiteSpaceBefore(oldChild); + parent.removeChild(oldChild); + } + + private static void removeWhiteSpaceBefore(Node node) { + Node prevElement = node.getPreviousSibling(); + if (prevElement != null && prevElement.getNodeType() == Node.TEXT_NODE + && prevElement.getNodeValue().trim().length() == 0) { + node.getParentNode().removeChild(prevElement); + } + } }