View Javadoc
1   /*
2    * The MIT License (MIT)
3    *
4    * Copyright (c) 2013 - 2023, Tapio Rautonen
5    *
6    * Permission is hereby granted, free of charge, to any person obtaining a copy
7    * of this software and associated documentation files (the "Software"), to deal
8    * in the Software without restriction, including without limitation the rights
9    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10   * copies of the Software, and to permit persons to whom the Software is
11   * furnished to do so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in
14   * all copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22   * THE SOFTWARE.
23   */
24  package org.eluder.coveralls.maven.plugin;
25  
26  import static org.junit.jupiter.api.Assertions.assertEquals;
27  import static org.junit.jupiter.api.Assertions.assertNotNull;
28  import static org.junit.jupiter.api.Assertions.assertThrows;
29  import static org.junit.jupiter.api.Assertions.assertTrue;
30  import static org.junit.jupiter.api.Assertions.fail;
31  import static org.mockito.ArgumentMatchers.any;
32  import static org.mockito.ArgumentMatchers.anyString;
33  import static org.mockito.Mockito.lenient;
34  import static org.mockito.Mockito.when;
35  import static org.mockito.Mockito.verify;
36  import static org.mockito.Mockito.verifyNoInteractions;
37  
38  import java.io.File;
39  import java.io.FileNotFoundException;
40  import java.io.IOException;
41  import java.nio.file.Files;
42  import java.nio.file.Path;
43  import java.util.ArrayList;
44  import java.util.Arrays;
45  import java.util.Collections;
46  import java.util.List;
47  
48  import org.apache.maven.model.Build;
49  import org.apache.maven.model.Model;
50  import org.apache.maven.model.Reporting;
51  import org.apache.maven.plugin.MojoExecutionException;
52  import org.apache.maven.plugin.MojoFailureException;
53  import org.apache.maven.plugin.logging.Log;
54  import org.apache.maven.project.MavenProject;
55  import org.apache.maven.settings.Settings;
56  import org.eluder.coveralls.maven.plugin.domain.CoverallsResponse;
57  import org.eluder.coveralls.maven.plugin.domain.Git;
58  import org.eluder.coveralls.maven.plugin.domain.Job;
59  import org.eluder.coveralls.maven.plugin.domain.Source;
60  import org.eluder.coveralls.maven.plugin.httpclient.CoverallsClient;
61  import org.eluder.coveralls.maven.plugin.json.JsonWriter;
62  import org.eluder.coveralls.maven.plugin.parser.CoberturaParser;
63  import org.eluder.coveralls.maven.plugin.service.ServiceSetup;
64  import org.eluder.coveralls.maven.plugin.source.SourceLoader;
65  import org.eluder.coveralls.maven.plugin.util.TestIoUtil;
66  import org.eluder.coveralls.maven.plugin.validation.ValidationErrors;
67  import org.junit.jupiter.api.BeforeEach;
68  import org.junit.jupiter.api.Test;
69  import org.junit.jupiter.api.extension.ExtendWith;
70  import org.junit.jupiter.api.io.CleanupMode;
71  import org.junit.jupiter.api.io.TempDir;
72  import org.mockito.Mock;
73  import org.mockito.Mockito;
74  import org.mockito.invocation.InvocationOnMock;
75  import org.mockito.junit.jupiter.MockitoExtension;
76  import org.mockito.stubbing.Answer;
77  
78  @ExtendWith(MockitoExtension.class)
79  public class CoverallsReportMojoTest {
80  
81      @TempDir(cleanup = CleanupMode.ON_SUCCESS)
82      public Path folder;
83  
84      public File coverallsFile;
85  
86      private CoverallsReportMojo mojo;
87  
88      @Mock
89      private CoverallsClient coverallsClientMock;
90  
91      @Mock
92      private SourceLoader sourceLoaderMock;
93  
94      @Mock
95      private Job jobMock;
96  
97      @Mock
98      private Log logMock;
99  
100     @Mock
101     private MavenProject projectMock;
102 
103     @Mock
104     private MavenProject collectedProjectMock;
105 
106     @Mock
107     private Model modelMock;
108 
109     @Mock
110     private Reporting reportingMock;
111 
112     @Mock
113     private Build buildMock;
114 
115     @Mock
116     private Settings settingsMock;
117 
118     @BeforeEach
119     void init() throws IOException  {
120         coverallsFile = Files.createFile(folder.resolve("coverallsFile.json")).toFile();
121 
122         lenient().when(sourceLoaderMock.load(anyString())).then(new Answer<Source>() {
123             @Override
124             public Source answer(final InvocationOnMock invocation) throws IOException {
125                 String sourceFile = invocation.getArguments()[0].toString();
126                 String content = readFileContent(sourceFile);
127                 return new Source(sourceFile, content, TestIoUtil.getSha512DigestHex(content));
128             }
129         });
130         lenient().when(logMock.isInfoEnabled()).thenReturn(true);
131         lenient().when(jobMock.validate()).thenReturn(new ValidationErrors());
132 
133 
134         mojo = new CoverallsReportMojo() {
135             @Override
136             protected SourceLoader createSourceLoader(final Job job) {
137                 return sourceLoaderMock;
138             }
139             @Override
140             protected List<CoverageParser> createCoverageParsers(SourceLoader sourceLoader) {
141                 List<CoverageParser> parsers = new ArrayList<CoverageParser>();
142                 parsers.add(new CoberturaParser(TestIoUtil.getFile("cobertura.xml"), sourceLoader));
143                 return parsers;
144             }
145             @Override
146             protected Environment createEnvironment() {
147                 return new Environment(this, Collections.<ServiceSetup>emptyList());
148             }
149             @Override
150             protected Job createJob() {
151                 return jobMock;
152             }
153             @Override
154             protected JsonWriter createJsonWriter(final Job job) throws IOException {
155                 return new JsonWriter(jobMock, CoverallsReportMojoTest.this.coverallsFile);
156             }
157             @Override
158             protected CoverallsClient createCoverallsClient() {
159                 return coverallsClientMock;
160             }
161             @Override
162             public Log getLog() {
163                 return logMock;
164             }
165         };
166         mojo.settings = settingsMock;
167         mojo.project = projectMock;
168         mojo.sourceEncoding = "UTF-8";
169         mojo.failOnServiceError = true;
170 
171         lenient().when(modelMock.getReporting()).thenReturn(reportingMock);
172         lenient().when(reportingMock.getOutputDirectory()).thenReturn(folder.toFile().getAbsolutePath());
173         lenient().when(buildMock.getDirectory()).thenReturn(folder.toFile().getAbsolutePath());
174 
175         List<MavenProject> projects = new ArrayList<MavenProject>();
176         projects.add(collectedProjectMock);
177         lenient().when(projectMock.getCollectedProjects()).thenReturn(projects);
178         lenient().when(projectMock.getBuild()).thenReturn(buildMock);
179         lenient().when(projectMock.getModel()).thenReturn(modelMock);
180         List<String> sourceRoots = new ArrayList<String>();
181         sourceRoots.add(folder.toFile().getAbsolutePath());
182         lenient().when(collectedProjectMock.getCompileSourceRoots()).thenReturn(sourceRoots);
183         lenient().when(collectedProjectMock.getBuild()).thenReturn(buildMock);
184         lenient().when(collectedProjectMock.getModel()).thenReturn(modelMock);
185     }
186 
187     @Test
188     void testCreateCoverageParsersWithoutCoverageReports() {
189         mojo = new CoverallsReportMojo();
190         mojo.settings = settingsMock;
191         mojo.project = projectMock;
192         assertThrows(IOException.class, () -> {
193             mojo.createCoverageParsers(sourceLoaderMock);
194         });
195     }
196 
197     @Test
198     void testCreateSourceLoader() throws IOException {
199         Git gitMock = Mockito.mock(Git.class);
200         Path git = Files.createDirectory(folder.resolve("git"));
201         when(gitMock.getBaseDir()).thenReturn(git.toFile());
202         when(jobMock.getGit()).thenReturn(gitMock);
203         TestIoUtil.writeFileContent("public interface Test { }", Files.createFile(git.resolve("source.java")).toFile()); 
204         mojo = new CoverallsReportMojo();
205         mojo.settings = settingsMock;
206         mojo.project = projectMock;
207         mojo.sourceEncoding = "UTF-8";
208         SourceLoader sourceLoader = mojo.createSourceLoader(jobMock);
209         Source source = sourceLoader.load("git/source.java");
210         assertNotNull(source);
211     }
212 
213     @Test
214     void testDefaultBehavior() throws IOException, MojoExecutionException, MojoFailureException {
215         mojo = new CoverallsReportMojo() {
216             @Override
217             protected SourceLoader createSourceLoader(final Job job) {
218                 return sourceLoaderMock;
219             }
220             @Override
221             protected List<CoverageParser> createCoverageParsers(SourceLoader sourceLoader) throws IOException {
222                 return Collections.emptyList();
223             }
224         };
225         mojo.sourceDirectories = Arrays.asList(TestIoUtil.getFile("/"));
226         mojo.sourceEncoding = "UTF-8";
227         mojo.settings = settingsMock;
228         mojo.project = projectMock;
229         mojo.repoToken = "asdfg";
230         mojo.coverallsFile = Files.createFile(folder.resolve("mojoCoverallsFile")).toFile(); 
231         mojo.dryRun = true;
232         mojo.skip = false;
233         mojo.basedir = TestIoUtil.getFile("/");
234 
235         mojo.execute();
236     }
237 
238     @Test
239     void testSuccessfullSubmission() throws ProcessingException, IOException, MojoExecutionException, MojoFailureException {
240         when(coverallsClientMock.submit(any(File.class))).thenReturn(new CoverallsResponse("success", false, null));
241         mojo.execute();
242         String json = TestIoUtil.readFileContent(coverallsFile);
243         assertNotNull(json);
244 
245         String[][] fixture = CoverageFixture.JAVA_FILES;
246         for (String[] coverageFile : fixture) {
247             assertTrue(json.contains(coverageFile[0]));
248         }
249 
250         verifySuccessfullSubmit(logMock, fixture);
251     }
252 
253     @Test
254     void testFailWithProcessingException() throws ProcessingException, IOException, MojoExecutionException {
255         when(coverallsClientMock.submit(any(File.class))).thenThrow(new ProcessingException());
256         try {
257             mojo.execute();
258             fail("Should have failed with MojoFailureException");
259         } catch (MojoFailureException ex) {
260             assertEquals(ex.getCause().getClass(), ProcessingException.class);
261         }
262     }
263 
264     @Test
265     void testProcessingExceptionWithAllowedServiceFailure() throws ProcessingException, IOException, MojoExecutionException {
266         mojo.failOnServiceError = false;
267         when(coverallsClientMock.submit(any(File.class))).thenThrow(new ProcessingException());
268         try {
269             mojo.execute();
270             fail("Should have failed with MojoFailureException");
271         } catch (MojoFailureException ex) {
272             assertEquals(ex.getCause().getClass(), ProcessingException.class);
273         }
274     }
275 
276     @Test
277     void testFailWithIOException() throws ProcessingException, IOException, MojoExecutionException {
278         when(coverallsClientMock.submit(any(File.class))).thenThrow(new IOException());
279         try {
280             mojo.execute();
281             fail("Should have failed with MojoFailureException");
282         } catch (MojoFailureException ex) {
283             assertEquals(ex.getCause().getClass(), IOException.class);
284         }
285     }
286 
287     @Test
288     void testIOExceptionWithAllowedServiceFailure() throws ProcessingException, IOException, MojoExecutionException, MojoFailureException {
289         mojo.failOnServiceError = false;
290         when(coverallsClientMock.submit(any(File.class))).thenThrow(new IOException());
291         mojo.execute();
292         verify(logMock).warn(anyString());
293     }
294 
295     @Test
296     void testFailWithNullPointerException() throws ProcessingException, IOException, MojoFailureException {
297         when(coverallsClientMock.submit(any(File.class))).thenThrow(new NullPointerException());
298         try {
299             mojo.execute();
300             fail("Should have failed with MojoFailureException");
301         } catch (MojoExecutionException ex) {
302             assertEquals(ex.getCause().getClass(), NullPointerException.class);
303         }
304     }
305 
306     @Test
307     void testSkipExecution() throws MojoExecutionException, MojoFailureException {
308         mojo.skip = true;
309         mojo.execute();
310 
311         verifyNoInteractions(jobMock);
312     }
313 
314     public static void verifySuccessfullSubmit(Log logMock, String[][] fixture) {
315         verify(logMock).info("Gathered code coverage metrics for " + CoverageFixture.getTotalFiles(fixture) + " source files with " + CoverageFixture.getTotalLines(fixture) + " lines of code:");
316         verify(logMock).info("*** It might take hours for Coveralls to update the actual coverage numbers for a job");
317     }
318 
319     protected String readFileContent(final String sourceFile) throws FileNotFoundException, IOException  {
320         return TestIoUtil.readFileContent(TestIoUtil.getFile(sourceFile));
321     }
322 }