1- ///////////////////////////////////////////////////////////////////////////////////////////////
2- // checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite.
3- // Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors
4- //
5- // Licensed under the Apache License, Version 2.0 (the "License");
6- // you may not use this file except in compliance with the License.
7- // You may obtain a copy of the License at
8- //
9- // http://www.apache.org/licenses/LICENSE-2.0
10- //
11- // Unless required by applicable law or agreed to in writing, software
12- // distributed under the License is distributed on an "AS IS" BASIS,
13- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14- // See the License for the specific language governing permissions and
15- // limitations under the License.
16- ///////////////////////////////////////////////////////////////////////////////////////////////
17-
181package org .checkstyle .autofix .recipe ;
192
203import static org .junit .jupiter .api .Assertions .assertDoesNotThrow ;
214import static org .openrewrite .java .Assertions .java ;
225
23- import java .io .IOException ;
6+ import java .io .ByteArrayOutputStream ;
7+ import java .io .File ;
248import java .nio .file .Files ;
25- import java .nio .file .Paths ;
9+ import java .nio .file .Path ;
10+ import java .util .ArrayList ;
11+ import java .util .Collections ;
12+ import java .util .List ;
2613
2714import org .checkstyle .autofix .InputClassRenamer ;
15+ import org .checkstyle .autofix .parser .CheckConfiguration ;
16+ import org .checkstyle .autofix .parser .CheckstyleReportParser ;
17+ import org .checkstyle .autofix .parser .CheckstyleViolation ;
18+ import org .checkstyle .autofix .parser .ConfigurationLoader ;
2819import org .openrewrite .Recipe ;
2920import org .openrewrite .test .RewriteTest ;
3021
31- public abstract class AbstractRecipeTest implements RewriteTest {
22+ import com .puppycrawl .tools .checkstyle .AbstractXmlTestSupport ;
23+ import com .puppycrawl .tools .checkstyle .Checker ;
24+ import com .puppycrawl .tools .checkstyle .XMLLogger ;
25+ import com .puppycrawl .tools .checkstyle .api .Configuration ;
26+ import com .puppycrawl .tools .checkstyle .bdd .InlineConfigParser ;
27+ import com .puppycrawl .tools .checkstyle .bdd .TestInputConfiguration ;
3228
33- private static final String BASE_TEST_RESOURCES_PATH = "src/test/resources/org"
34- + "/checkstyle/autofix/recipe/" ;
29+ /**
30+ * Simple base class for recipe tests that extends Checkstyle's AbstractXmlTestSupport.
31+ */
32+ public abstract class AbstractRecipeTest extends AbstractXmlTestSupport implements RewriteTest {
3533
36- private Recipe createPreprocessingRecipe () {
37- return new InputClassRenamer ();
38- }
34+ /**
35+ * Returns the subpackage name for test resources.
36+ */
37+ protected abstract String getSubpackage ();
3938
40- protected abstract Recipe getRecipe ();
39+ /**
40+ * Creates the recipe with violations and configs.
41+ */
42+ protected abstract Recipe createRecipe (List <CheckstyleViolation > violations ,
43+ List <CheckConfiguration > checkConfigs );
4144
42- protected void testRecipe (String recipePath , String testCaseName ) throws IOException {
43- final String testCaseDir = testCaseName .toLowerCase ();
45+ @ Override
46+ protected String getPackageLocation () {
47+ return "org/checkstyle/autofix/recipe/" + getSubpackage ();
48+ }
49+
50+ /**
51+ * Main verification method.
52+ */
53+ protected void verify (String testCaseName ) throws Exception {
4454 final String inputFileName = "Input" + testCaseName + ".java" ;
4555 final String outputFileName = "Output" + testCaseName + ".java" ;
56+ final String inputPath = testCaseName .toLowerCase () + "/" + inputFileName ;
57+ final String outputPath = testCaseName .toLowerCase () + "/" + outputFileName ;
58+
59+ final List <CheckstyleViolation > violations = runCheckstyleAndGetViolations (inputPath );
60+
61+ final List <CheckConfiguration > checkConfigs = getAllCheckConfigurations (inputPath );
62+
63+ final String beforeCode = readFile (getPath (inputPath ));
64+ final String expectedAfterCode = readFile (getPath (outputPath ));
65+
66+ final Recipe preprocessingRecipe = new InputClassRenamer ();
67+ final Recipe mainRecipe = createRecipe (violations , checkConfigs );
68+
69+ testRecipe (beforeCode , expectedAfterCode , preprocessingRecipe , mainRecipe );
70+ }
4671
47- final String beforeCode = Files . readString ( Paths . get ( BASE_TEST_RESOURCES_PATH
48- + recipePath + "/" + testCaseDir + "/" + inputFileName ));
72+ private List < CheckstyleViolation > runCheckstyleAndGetViolations ( String inputPath )
73+ throws Exception {
4974
50- final String afterCode = Files .readString (Paths .get (BASE_TEST_RESOURCES_PATH
51- + recipePath + "/" + testCaseDir + "/" + outputFileName ));
75+ final String configFilePath = getPath (inputPath );
76+ final TestInputConfiguration testInputConfiguration =
77+ InlineConfigParser .parse (configFilePath );
78+ final Configuration parsedConfig = testInputConfiguration .createConfiguration ();
5279
53- final Recipe preprocessing = createPreprocessingRecipe ();
54- final Recipe mainRecipe = getRecipe ();
80+ final Checker checker = createChecker (parsedConfig );
81+ final ByteArrayOutputStream xmlOutput = new ByteArrayOutputStream ();
82+ final XMLLogger logger = new XMLLogger (xmlOutput , XMLLogger .OutputStreamOptions .CLOSE );
83+ checker .addListener (logger );
84+
85+ final List <File > filesToCheck = Collections .singletonList (new File (configFilePath ));
86+ checker .process (filesToCheck );
87+
88+ final Path tempXmlPath = Files .createTempFile ("checkstyle-report" , ".xml" );
89+ try {
90+ Files .write (tempXmlPath , xmlOutput .toByteArray ());
91+ return CheckstyleReportParser .parse (tempXmlPath );
92+ } finally {
93+ Files .deleteIfExists (tempXmlPath );
94+ }
95+ }
96+
97+ private List <CheckConfiguration > getAllCheckConfigurations (String inputPath ) throws Exception {
98+ final String configFilePath = getPath (inputPath );
99+ final TestInputConfiguration testInputConfiguration =
100+ InlineConfigParser .parse (configFilePath );
101+ final Configuration parsedConfig = testInputConfiguration .createConfiguration ();
102+
103+ final List <CheckConfiguration > checkConfigs = new ArrayList <>();
104+
105+ for (Configuration child : parsedConfig .getChildren ()) {
106+ if ("TreeWalker" .equals (child .getName ())) {
107+ for (Configuration check : child .getChildren ()) {
108+ final CheckConfiguration checkConfig = ConfigurationLoader .mapConfiguration (check );
109+ checkConfigs .add (checkConfig );
110+ }
111+ }
112+ }
113+
114+ if (checkConfigs .isEmpty ()) {
115+ throw new IllegalStateException ("No check configurations found" );
116+ }
117+
118+ return checkConfigs ;
119+ }
55120
121+ /**
122+ * Helper method to test recipes using OpenRewrite testing framework.
123+ */
124+ private void testRecipe (String beforeCode , String expectedAfterCode ,
125+ Recipe ... recipes ) {
56126 assertDoesNotThrow (() -> {
57127 rewriteRun (
58- spec -> spec .recipes (preprocessing , mainRecipe ),
59- java (beforeCode , afterCode )
128+ spec -> spec .recipes (recipes ),
129+ java (beforeCode , expectedAfterCode )
60130 );
61131 });
62132 }
63- }
133+ }
0 commit comments