JDomUtilsTest.java
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.maven.cling.invoker.mvnup.goals;
import java.io.StringReader;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Indentation.FOUR_SPACES;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Indentation.TAB;
import static org.apache.maven.cling.invoker.mvnup.goals.UpgradeConstants.Indentation.TWO_SPACES;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Unit tests for JDomUtils functionality including indentation detection and XML manipulation.
*/
class JDomUtilsTest {
private SAXBuilder saxBuilder;
@BeforeEach
void setUp() {
saxBuilder = new SAXBuilder();
}
@Test
void testDetectTwoSpaceIndentation() throws Exception {
String pomXml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0.0</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
</project>
""";
Document document = saxBuilder.build(new StringReader(pomXml));
Element root = document.getRootElement();
String baseIndent = JDomUtils.detectBaseIndentationUnit(root);
assertEquals(TWO_SPACES, baseIndent, "Should detect 2-space indentation");
}
@Test
void testDetectFourSpaceIndentation() throws Exception {
String pomXml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0.0</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
</project>
""";
Document document = saxBuilder.build(new StringReader(pomXml));
Element root = document.getRootElement();
String baseIndent = JDomUtils.detectBaseIndentationUnit(root);
assertEquals(FOUR_SPACES, baseIndent, "Should detect 4-space indentation");
}
@Test
void testDetectTabIndentation() throws Exception {
String pomXml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
\t<modelVersion>4.0.0</modelVersion>
\t<groupId>test</groupId>
\t<artifactId>test</artifactId>
\t<version>1.0.0</version>
\t<build>
\t\t<plugins>
\t\t\t<plugin>
\t\t\t\t<groupId>org.apache.maven.plugins</groupId>
\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>
\t\t\t\t<version>3.8.1</version>
\t\t\t</plugin>
\t\t</plugins>
\t</build>
</project>
""";
Document document = saxBuilder.build(new StringReader(pomXml));
Element root = document.getRootElement();
String baseIndent = JDomUtils.detectBaseIndentationUnit(root);
assertEquals(TAB, baseIndent, "Should detect tab indentation");
}
@Test
void testDetectIndentationWithMixedContent() throws Exception {
// POM with mostly 4-space indentation but some 2-space (should prefer 4-space)
String pomXml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>test</id>
</profile>
</profiles>
</project>
""";
Document document = saxBuilder.build(new StringReader(pomXml));
Element root = document.getRootElement();
String baseIndent = JDomUtils.detectBaseIndentationUnit(root);
assertEquals(FOUR_SPACES, baseIndent, "Should detect 4-space indentation as the most common pattern");
}
@Test
void testDetectIndentationFromBuildElement() throws Exception {
String pomXml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0.0</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
</project>
""";
Document document = saxBuilder.build(new StringReader(pomXml));
Element root = document.getRootElement();
Element buildElement = root.getChild("build", root.getNamespace());
String baseIndent = JDomUtils.detectBaseIndentationUnit(buildElement);
assertEquals(FOUR_SPACES, baseIndent, "Should detect 4-space indentation from build element");
}
@Test
void testDetectIndentationFallbackToDefault() throws Exception {
// Minimal POM with no clear indentation pattern
String pomXml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"><modelVersion>4.0.0</modelVersion><groupId>test</groupId><artifactId>test</artifactId><version>1.0.0</version></project>
""";
Document document = saxBuilder.build(new StringReader(pomXml));
Element root = document.getRootElement();
String baseIndent = JDomUtils.detectBaseIndentationUnit(root);
assertEquals(TWO_SPACES, baseIndent, "Should fallback to 2-space default when no pattern is detected");
}
@Test
void testDetectIndentationConsistency() throws Exception {
String pomXml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0.0</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
</project>
""";
Document document = saxBuilder.build(new StringReader(pomXml));
Element root = document.getRootElement();
Element buildElement = root.getChild("build", root.getNamespace());
Element pluginsElement = buildElement.getChild("plugins", buildElement.getNamespace());
// All elements should detect the same base indentation unit
String rootIndent = JDomUtils.detectBaseIndentationUnit(root);
String buildIndent = JDomUtils.detectBaseIndentationUnit(buildElement);
String pluginsIndent = JDomUtils.detectBaseIndentationUnit(pluginsElement);
assertEquals(FOUR_SPACES, rootIndent, "Root should detect 4-space indentation");
assertEquals(FOUR_SPACES, buildIndent, "Build should detect 4-space indentation");
assertEquals(FOUR_SPACES, pluginsIndent, "Plugins should detect 4-space indentation");
assertEquals(rootIndent, buildIndent, "All elements should detect the same indentation");
assertEquals(buildIndent, pluginsIndent, "All elements should detect the same indentation");
}
@Test
void testAddElementWithCorrectIndentation() throws Exception {
String pomXml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0.0</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
</project>
""";
Document document = saxBuilder.build(new StringReader(pomXml));
Element root = document.getRootElement();
Element buildElement = root.getChild("build", root.getNamespace());
// Add a new pluginManagement element using JDomUtils
JDomUtils.insertNewElement("pluginManagement", buildElement);
// Verify the element was added with correct indentation
XMLOutputter outputter = new XMLOutputter(Format.getRawFormat());
String pomString = outputter.outputString(document);
// The pluginManagement should be indented with 4 spaces (same as plugins)
assertTrue(pomString.contains(" <pluginManagement>"), "pluginManagement should be indented with 4 spaces");
assertTrue(
pomString.contains(" </pluginManagement>"),
"pluginManagement closing tag should be indented with 4 spaces");
}
@Test
void testRealWorldScenarioWithPluginManagementAddition() throws Exception {
// Real-world POM with 4-space indentation
String pomXml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-project</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
</plugin>
</plugins>
</build>
</project>
""";
Document document = saxBuilder.build(new StringReader(pomXml));
Element root = document.getRootElement();
Element buildElement = root.getChild("build", root.getNamespace());
// Verify the detected indentation is 4 spaces
String baseIndent = JDomUtils.detectBaseIndentationUnit(root);
assertEquals(FOUR_SPACES, baseIndent, "Should detect 4-space indentation from real-world POM");
// Add pluginManagement section using the detected indentation
Element pluginManagementElement = JDomUtils.insertNewElement("pluginManagement", buildElement);
Element managedPluginsElement = JDomUtils.insertNewElement("plugins", pluginManagementElement);
Element managedPluginElement = JDomUtils.insertNewElement("plugin", managedPluginsElement);
// Add plugin details
JDomUtils.insertContentElement(managedPluginElement, "groupId", "org.apache.maven.plugins");
JDomUtils.insertContentElement(managedPluginElement, "artifactId", "maven-exec-plugin");
JDomUtils.insertContentElement(managedPluginElement, "version", "3.2.0");
// Verify the output maintains consistent 4-space indentation
XMLOutputter outputter = new XMLOutputter(Format.getRawFormat());
String pomString = outputter.outputString(document);
// Check that pluginManagement is properly indented
assertTrue(pomString.contains(" <pluginManagement>"), "pluginManagement should be indented with 4 spaces");
assertTrue(
pomString.contains(" <plugins>"),
"plugins under pluginManagement should be indented with 8 spaces");
assertTrue(
pomString.contains(" <plugin>"),
"plugin under pluginManagement should be indented with 12 spaces");
assertTrue(
pomString.contains(" <groupId>org.apache.maven.plugins</groupId>"),
"plugin elements should be indented with 16 spaces");
}
@Test
void testProperClosingTagFormattingWithPluginManagement() throws Exception {
String pomXml =
"""
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0.0</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
</project>
""";
Document document = saxBuilder.build(new StringReader(pomXml));
Element root = document.getRootElement();
Element buildElement = root.getChild("build", root.getNamespace());
// Add pluginManagement section using JDomUtils
Element pluginManagementElement = JDomUtils.insertNewElement("pluginManagement", buildElement);
Element managedPluginsElement = JDomUtils.insertNewElement("plugins", pluginManagementElement);
Element managedPluginElement = JDomUtils.insertNewElement("plugin", managedPluginsElement);
// Add plugin details
JDomUtils.insertContentElement(managedPluginElement, "groupId", "org.apache.maven.plugins");
JDomUtils.insertContentElement(managedPluginElement, "artifactId", "maven-enforcer-plugin");
JDomUtils.insertContentElement(managedPluginElement, "version", "3.0.0");
// Verify the output has proper formatting without extra blank lines
XMLOutputter outputter = new XMLOutputter(Format.getRawFormat());
String pomString = outputter.outputString(document);
// Verify that the XML is well-formed and contains the expected elements
assertTrue(pomString.contains("<pluginManagement>"), "Should contain pluginManagement");
assertTrue(pomString.contains("</pluginManagement>"), "Should contain closing pluginManagement");
assertTrue(pomString.contains("<plugin>"), "Should contain plugin");
assertTrue(pomString.contains("</plugin>"), "Should contain closing plugin");
assertTrue(pomString.contains("maven-enforcer-plugin"), "Should contain the plugin artifact ID");
// Verify that there are no malformed closing tags (the main issue from Spotless)
assertFalse(pomString.contains("</plugin></plugins>"), "Should not have malformed closing tags");
assertFalse(pomString.contains("</plugins></pluginManagement>"), "Should not have malformed closing tags");
// Check for whitespace-only lines (lines with only spaces/tabs)
String[] lines = pomString.split("\n");
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
if (line.trim().isEmpty() && !line.isEmpty()) {
System.out.println("Found whitespace-only line at index " + i + ": '" + line + "' (length: "
+ line.length() + ")");
// This is what Spotless complains about - lines with only whitespace
assertFalse(
true, "Line " + (i + 1) + " contains only whitespace characters, should be completely empty");
}
}
}
private static void assertTrue(boolean condition, String message) {
if (!condition) {
throw new AssertionError(message);
}
}
private static void assertFalse(boolean condition, String message) {
if (condition) {
throw new AssertionError(message);
}
}
}