View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.meta;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static org.mockito.ArgumentMatchers.any;
24  import static org.mockito.ArgumentMatchers.anyString;
25  import static org.mockito.Mockito.mockStatic;
26  
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.util.LinkedHashSet;
30  import java.util.Set;
31  import java.util.stream.Collectors;
32  import java.util.stream.Stream;
33  
34  import javax.xml.transform.TransformerException;
35  
36  import org.apache.maven.doxia.macro.MacroExecutionException;
37  import org.itsallcode.io.Capturable;
38  import org.itsallcode.junit.sysextensions.SystemOutGuard;
39  import org.junit.jupiter.api.Test;
40  import org.junit.jupiter.api.extension.ExtendWith;
41  import org.mockito.MockedStatic;
42  import org.mockito.Mockito;
43  
44  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
45  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
46  import com.puppycrawl.tools.checkstyle.internal.utils.CheckUtil;
47  import com.puppycrawl.tools.checkstyle.site.SiteUtil;
48  
49  @ExtendWith(SystemOutGuard.class)
50  public final class MetadataGeneratorUtilTest extends AbstractModuleTestSupport {
51  
52      private static final Set<String> MODULES_CONTAINING_NO_METADATA_FILE = Set.of(
53              "Checker",
54              "TreeWalker",
55              "ClassAndPropertiesSettersJavadocScraper"
56      );
57  
58      @Override
59      public String getPackageLocation() {
60          return null;
61      }
62  
63      /**
64       * Generates metadata for checkstyle modules and verifies number of
65       * generated metadata modules match the number of checkstyle modules.
66       * Also verifies whether every checkstyle module contains description.
67       *
68       * @param systemOut wrapper for {@code System.out}
69       * @throws Exception if exception occurs during generating metadata or
70       *                   if an I/O error is thrown when accessing the starting f
71       */
72      @Test
73      public void testMetadataFilesGenerationAllFiles(@SystemOutGuard.SysOut Capturable systemOut)
74              throws Exception {
75          systemOut.captureMuted();
76  
77          MetadataGeneratorUtil.generate(System.getProperty("user.dir")
78                          + "/src/main/java/com/puppycrawl/tools/checkstyle",
79                  "checks", "filters", "filefilters");
80  
81          final Set<String> metaFiles;
82          try (Stream<Path> fileStream = Files.walk(
83                  Path.of(System.getProperty("user.dir") + "/src/main/resources/com/puppycrawl"
84                          + "/tools/checkstyle/meta"))) {
85              metaFiles = fileStream
86                      .filter(Files::isRegularFile)
87                      .filter(path -> !path.toString().endsWith(".properties"))
88                      .map(MetadataGeneratorUtilTest::getMetaFileName)
89                      .sorted()
90                      .collect(Collectors.toCollection(LinkedHashSet::new));
91          }
92          final Set<String> checkstyleModules =
93                  CheckUtil.getSimpleNames(CheckUtil.getCheckstyleModules())
94                          .stream()
95                          .sorted()
96                          .collect(Collectors.toCollection(LinkedHashSet::new));
97          checkstyleModules.removeAll(MODULES_CONTAINING_NO_METADATA_FILE);
98          assertWithMessage("Number of generated metadata files dont match with "
99                  + "number of checkstyle module")
100                 .that(metaFiles)
101                 .isEqualTo(checkstyleModules);
102     }
103 
104     /**
105      * Verifies that generate() catches MacroExecutionException
106      * it wrapped in a CheckstyleException.
107      *
108      * @throws Exception if an unexpected error occurs
109      */
110     @Test
111     public void testGenerateRethrowsMacroExecutionExceptionAsCheckstyleException()
112             throws Exception {
113         try (MockedStatic<SiteUtil> mocked = mockStatic(SiteUtil.class,
114                 Mockito.CALLS_REAL_METHODS)) {
115             mocked.when(() -> SiteUtil.getModuleInstance(anyString()))
116                     .thenThrow(new MacroExecutionException("simulated"));
117 
118             try {
119                 MetadataGeneratorUtil.generate(
120                         System.getProperty("user.dir")
121                                 + "/src/main/java/com/puppycrawl/tools/checkstyle",
122                         "checks");
123                 assertWithMessage("CheckstyleException should have been thrown").fail();
124             }
125             catch (CheckstyleException exception) {
126                 assertWithMessage("Cause must be MacroExecutionException")
127                         .that(exception.getCause())
128                         .isInstanceOf(MacroExecutionException.class);
129                 assertWithMessage("Message should mention macro failure")
130                         .that(exception.getMessage())
131                         .contains("Failed to execute macro");
132             }
133         }
134     }
135 
136     /**
137      * Verifies that writeMetadataFile() catches TransformerException
138      * it wrapped in a CheckstyleException.
139      *
140      * @throws Exception if an unexpected error occurs
141      */
142     @Test
143     public void testWriteMetadataFileRethrowsAsCheckstyleException() throws Exception {
144         try (MockedStatic<XmlMetaWriter> mocked = mockStatic(XmlMetaWriter.class)) {
145             mocked.when(() -> XmlMetaWriter.write(any(ModuleDetails.class)))
146                     .thenThrow(new TransformerException("simulated"));
147 
148             try {
149                 MetadataGeneratorUtil.generate(
150                         System.getProperty("user.dir")
151                                 + "/src/main/java/com/puppycrawl/tools/checkstyle",
152                         "checks");
153                 assertWithMessage("CheckstyleException should have been thrown").fail();
154             }
155             catch (CheckstyleException exception) {
156                 assertWithMessage("Message should mention module name")
157                         .that(exception.getMessage())
158                         .contains("Failed to write metadata into XML file for module");
159             }
160         }
161     }
162 
163     /**
164      * Get meta file name from full file name.
165      *
166      * @param file file to process
167      * @return meta file name
168      */
169     private static String getMetaFileName(Path file) {
170         final String fileName = file.getFileName().toString();
171         final int lengthToOmit;
172         if (fileName.contains("Check")) {
173             lengthToOmit = "Check.xml".length();
174         }
175         else {
176             lengthToOmit = ".xml".length();
177         }
178         return fileName.substring(0, fileName.length() - lengthToOmit);
179     }
180 }