View Javadoc
1   package org.apache.rat.mp;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  import org.apache.commons.io.IOUtils;
21  import org.apache.maven.model.Build;
22  import org.apache.maven.plugin.testing.AbstractMojoTestCase;
23  import org.apache.maven.plugin.testing.stubs.MavenProjectStub;
24  import org.apache.rat.config.AddLicenseHeaders;
25  
26  import java.io.BufferedReader;
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.io.IOException;
30  import java.io.InputStreamReader;
31  import java.lang.reflect.Field;
32  import java.nio.charset.Charset;
33  import javax.xml.parsers.DocumentBuilder;
34  import javax.xml.parsers.DocumentBuilderFactory;
35  import static junit.framework.TestCase.assertTrue;
36  import org.apache.rat.document.impl.guesser.BinaryGuesser;
37  
38  import static org.apache.rat.mp.RatTestHelpers.ensureRatReportIsCorrect;
39  import static org.apache.rat.mp.RatTestHelpers.getSourceDirectory;
40  import static org.apache.rat.mp.RatTestHelpers.newArtifactFactory;
41  import static org.apache.rat.mp.RatTestHelpers.newArtifactRepository;
42  import static org.apache.rat.mp.RatTestHelpers.newArtifactResolver;
43  import static org.apache.rat.mp.RatTestHelpers.newSiteRenderer;
44  import org.w3c.dom.Document;
45  import org.w3c.dom.NodeList;
46  
47  /**
48   * Test case for the {@link RatCheckMojo} and {@link RatReportMojo}.
49   */
50  public class RatCheckMojoTest extends AbstractMojoTestCase {
51  
52      /**
53       * Creates a new instance of {@link RatCheckMojo}.
54       *
55       * @param pDir The directory, where to look for a pom.xml file.
56       * @return The configured Mojo.
57       * @throws Exception An error occurred while creating the Mojo.
58       */
59      private RatCheckMojo newRatCheckMojo(String pDir) throws Exception {
60          return (RatCheckMojo) newRatMojo(pDir, "check", false);
61      }
62  
63      /**
64       * Creates a new instance of {@link AbstractRatMojo}.
65       *
66       * @param pDir  The directory, where to look for a pom.xml file.
67       * @param pGoal The goal, which the Mojo must implement.
68       * @return The configured Mojo.
69       * @throws Exception An error occurred while creating the Mojo.
70       */
71      private AbstractRatMojo newRatMojo(String pDir, String pGoal,
72                                         boolean pCreateCopy) throws Exception {
73          final File baseDir = new File(getBasedir());
74          final File testBaseDir = getSourceDirectory(getBasedir(), pDir,
75                  pCreateCopy, baseDir);
76          File testPom = new File(testBaseDir, "pom.xml");
77          AbstractRatMojo mojo = (AbstractRatMojo) lookupMojo(pGoal, testPom);
78          assertNotNull(mojo);
79          final File buildDirectory = new File(new File(baseDir, "target/test"),
80                  pDir);
81          setVariableValueToObject(mojo, "basedir", testBaseDir);
82          setVariableValueToObject(mojo, "addDefaultLicenseMatchers",
83                  Boolean.TRUE);
84          setVariableValueToObject(mojo, "useDefaultExcludes", Boolean.TRUE);
85          setVariableValueToObject(mojo, "useMavenDefaultExcludes", Boolean.TRUE);
86          setVariableValueToObject(mojo, "useEclipseDefaultExcludes",
87                  Boolean.TRUE);
88          setVariableValueToObject(mojo, "addLicenseHeaders", AddLicenseHeaders.FALSE.name());
89          final Build build = new Build();
90          build.setDirectory(buildDirectory.getPath());
91          final MavenProjectStub project = new MavenProjectStub() {
92              @Override
93              public Build getBuild() {
94                  return build;
95              }
96          };
97          setVariableValueToObject(mojo, "project", project);
98          assertNotNull(
99                  "Problem in test setup - you are missing a project in your mojo.",
100                 project);
101         assertNotNull(
102                 "The mojo is missing its MavenProject, which will result in an NPE during rat runs.",
103                 mojo.getProject());
104         assertNotNull(
105                 "No artifactRepos found, which will result in an NPE during rat runs.",
106                 project.getRemoteArtifactRepositories());
107 
108         if (mojo instanceof RatReportMojo) {
109             setVariableValueToObject(mojo, "localRepository",
110                     newArtifactRepository(container));
111             setVariableValueToObject(mojo, "resolver", newArtifactResolver());
112             setVariableValueToObject(mojo, "factory", newArtifactFactory());
113             setVariableValueToObject(mojo, "siteRenderer",
114                     newSiteRenderer(container));
115         } else if (mojo instanceof RatCheckMojo) {
116             final File ratTxtFile = new File(buildDirectory, "rat.txt");
117             setVariableValueToObject(mojo, "reportFile", ratTxtFile);
118         }
119         return mojo;
120     }
121 
122     /**
123      * Reads the location of the rat text file from the Mojo.
124      *
125      * @param pMojo The configured Mojo.
126      * @return Value of the "reportFile" property.
127      * @throws Exception An error occurred while reading the property.
128      */
129     private File getRatTxtFile(RatCheckMojo pMojo) throws Exception {
130         return (File) getVariableValueFromObject(pMojo, "reportFile");
131     }
132 
133     /**
134      * Runs a check, which should expose no problems.
135      *
136      * @throws Exception The test failed.
137      */
138     public void testIt1() throws Exception {
139         final RatCheckMojo mojo = newRatCheckMojo("it1");
140         final File ratTxtFile = getRatTxtFile(mojo);
141         mojo.execute();
142         ensureRatReportIsCorrect(ratTxtFile, 1, 0);
143     }
144 
145     /**
146      * Runs a check, which should detect a problem.
147      *
148      * @throws Exception The test failed.
149      */
150     public void testIt2() throws Exception {
151         final RatCheckMojo mojo = newRatCheckMojo("it2");
152         final File ratTxtFile = getRatTxtFile(mojo);
153         try {
154             mojo.execute();
155             fail("Expected RatCheckException");
156         } catch (RatCheckException e) {
157             final String msg = e.getMessage();
158             // default value is "${project.build.directory}/rat.txt"
159             final String REPORTFILE = "rat.txt";
160 
161             assertTrue("report filename was not contained in '" + msg + "'",
162                     msg.contains(REPORTFILE));
163             assertFalse("no null allowed in '" + msg + "'", (msg.toUpperCase()
164                     .contains("NULL")));
165         }
166         ensureRatReportIsCorrect(ratTxtFile, 1, 1);
167     }
168 
169     private String getFirstLine(File pFile) throws IOException {
170         FileInputStream fis = null;
171         InputStreamReader reader = null;
172         BufferedReader breader = null;
173         try {
174             fis = new FileInputStream(pFile);
175             reader = new InputStreamReader(fis, "UTF8");
176             breader = new BufferedReader(reader);
177             final String result = breader.readLine();
178             breader.close();
179             return result;
180         } finally {
181             IOUtils.closeQuietly(fis);
182             IOUtils.closeQuietly(reader);
183             IOUtils.closeQuietly(breader);
184         }
185     }
186 
187     /**
188      * Tests adding license headers.
189      */
190     public void testIt3() throws Exception {
191         final RatCheckMojo mojo = (RatCheckMojo) newRatMojo("it3", "check",
192                 true);
193         setVariableValueToObject(mojo, "addLicenseHeaders", AddLicenseHeaders.TRUE.name());
194         setVariableValueToObject(mojo, "numUnapprovedLicenses",
195                 1);
196         mojo.execute();
197         final File ratTxtFile = getRatTxtFile(mojo);
198         ensureRatReportIsCorrect(ratTxtFile, 1, 1);
199 
200         final File baseDir = new File(getBasedir());
201         final File sourcesDir = new File(new File(baseDir, "target/it-source"),
202                 "it3");
203         final String firstLineOrig = getFirstLine(new File(sourcesDir,
204                 "src.apt"));
205         assertTrue(firstLineOrig.contains("--"));
206         assertFalse(firstLineOrig.contains("~~"));
207         final String firstLineModified = getFirstLine(new File(sourcesDir,
208                 "src.apt.new"));
209         assertTrue(firstLineModified.contains("~~"));
210         assertFalse(firstLineModified.contains("--"));
211     }
212 
213     /**
214      * Test correct generation of XML file if non-UTF8 file.encoding is set.
215      *
216      * @throws Exception The test failed.
217      */
218     public void testIt4() throws Exception {
219         final RatCheckMojo mojo = newRatCheckMojo("it4");
220         final File ratTxtFile = getRatTxtFile(mojo);
221         try {
222             setVariableValueToObject(mojo, "reportStyle", "xml");
223             String origEncoding = overrideFileEncoding("ISO-8859-1");
224             mojo.execute();
225             overrideFileEncoding(origEncoding);
226             fail("Expected RatCheckException");
227         } catch (RatCheckException e) {
228             final String msg = e.getMessage();
229             // default value is "${project.build.directory}/rat.txt"
230             final String REPORTFILE = "rat.txt";
231 
232             assertTrue("report filename was not contained in '" + msg + "'",
233                     msg.contains(REPORTFILE));
234             assertFalse("no null allowed in '" + msg + "'", (msg.toUpperCase()
235                     .contains("NULL")));
236         }
237         assertTrue(ratTxtFile.exists());
238         DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
239         FileInputStream fis = new FileInputStream(ratTxtFile);
240         try {
241             Document doc = db.parse(fis);
242             NodeList headerSample = doc.getElementsByTagName("header-sample");
243             String textContent = headerSample.item(0).getTextContent();
244             if (textContent.length() == 0) { // can be the pom since this test will parse 2 files but the pom is "ok"
245                 textContent = headerSample.item(1).getTextContent();
246             }
247             boolean byteSequencePresent = textContent.contains("\u00E4\u00F6\u00FC\u00C4\u00D6\u00DC\u00DF");
248             assertTrue("Report should contain test umlauts, got '" + textContent + "'", byteSequencePresent);
249         } catch (Exception ex) {
250             fail("Report file could not be parsed as XML: " + ex.getMessage());
251         } finally {
252             fis.close();
253         }
254     }
255 
256 
257     private String overrideFileEncoding(String newEncoding) {
258         String current = System.getProperty("file.encoding");
259         System.setProperty("file.encoding", newEncoding);
260         setBinaryGuesserCharset(newEncoding);
261         clearDefaultCharset();
262         return current;
263     }
264 
265     private void clearDefaultCharset() {
266         try {
267             Field f = Charset.class.getDeclaredField("defaultCharset");
268             f.setAccessible(true);
269             f.set(null, null);
270         } catch (Exception ex) {
271             // This is for unittesting - there is no good reason not to rethrow
272             // it. This could be happening in JDK 9, where the unittests need
273             // run with the java.base module opened
274             throw new RuntimeException(ex);
275         }
276     }
277 
278     private void setBinaryGuesserCharset(String charset) {
279         try {
280             Field f = BinaryGuesser.class.getDeclaredField("CHARSET_FROM_FILE_ENCODING_OR_UTF8");
281             f.setAccessible(true);
282             f.set(null, Charset.forName(charset));
283         } catch (Exception ex) {
284             // This is for unittesting - there is no good reason not to rethrow
285             // it. This could be happening in JDK 9, where the unittests need
286             // run with the java.base module opened
287             throw new RuntimeException(ex);
288         }
289     }
290 }