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.parser;
25  
26  import static org.junit.jupiter.api.Assertions.assertNull;
27  import static org.junit.jupiter.api.Assertions.assertTrue;
28  import static org.junit.jupiter.api.Assertions.fail;
29  import static org.mockito.Mockito.atLeast;
30  import static org.mockito.Mockito.lenient;
31  import static org.mockito.Mockito.verify;
32  
33  import java.io.File;
34  import java.io.IOException;
35  import java.security.NoSuchAlgorithmException;
36  import java.util.ArrayList;
37  import java.util.Collection;
38  import java.util.Collections;
39  import java.util.HashSet;
40  import java.util.List;
41  import java.util.Set;
42  
43  import org.eluder.coveralls.maven.plugin.CoverageFixture;
44  import org.eluder.coveralls.maven.plugin.CoverageParser;
45  import org.eluder.coveralls.maven.plugin.ProcessingException;
46  import org.eluder.coveralls.maven.plugin.domain.Branch;
47  import org.eluder.coveralls.maven.plugin.domain.Source;
48  import org.eluder.coveralls.maven.plugin.source.ChainingSourceCallback;
49  import org.eluder.coveralls.maven.plugin.source.SourceCallback;
50  import org.eluder.coveralls.maven.plugin.source.SourceLoader;
51  import org.eluder.coveralls.maven.plugin.source.UniqueSourceCallback;
52  import org.eluder.coveralls.maven.plugin.util.TestIoUtil;
53  import org.junit.jupiter.api.BeforeEach;
54  import org.junit.jupiter.api.Test;
55  import org.junit.jupiter.api.extension.ExtendWith;
56  import org.mockito.ArgumentCaptor;
57  import org.mockito.Mock;
58  import org.mockito.invocation.InvocationOnMock;
59  import org.mockito.junit.jupiter.MockitoExtension;
60  import org.mockito.stubbing.Answer;
61  
62  @ExtendWith(MockitoExtension.class)
63  public abstract class AbstractCoverageParserTest {
64  
65      @Mock
66      protected SourceLoader sourceLoaderMock;
67  
68      @Mock
69      protected SourceCallback sourceCallbackMock;
70  
71      @BeforeEach
72      public void init() throws IOException  {
73          for (String[] coverageFile : getCoverageFixture()) {
74              final String name = sourceName(coverageFile[0]);
75              final String content = TestIoUtil.readFileContent(TestIoUtil.getFile(name));
76              lenient().when(sourceLoaderMock.load(name)).then(sourceAnswer(name, content));
77          }
78      }
79  
80      protected String sourceName(final String coverageFile) {
81          return coverageFile;
82      }
83  
84      protected Answer<Source> sourceAnswer(final String name, final String content) {
85          return new Answer<Source>() {
86              @Override
87              public Source answer(final InvocationOnMock invocation) throws NoSuchAlgorithmException  {
88                  return new Source(name, content, TestIoUtil.getSha512DigestHex(content));
89              }
90          };
91      }
92  
93      @Test
94      public void testParseCoverage() throws ProcessingException, IOException  {
95          for (String coverageResource : getCoverageResources()) {
96              CoverageParser parser = createCoverageParser(TestIoUtil.getFile(coverageResource), sourceLoaderMock);
97              parser.parse(sourceCallbackMock);
98          }
99  
100         String[][] fixture = getCoverageFixture();
101 
102         ArgumentCaptor<Source> captor = ArgumentCaptor.forClass(Source.class);
103         verify(sourceCallbackMock, atLeast(CoverageFixture.getTotalFiles(fixture))).onSource(captor.capture());
104 
105         SourceCollector sourceCollector = new SourceCollector();
106         UniqueSourceCallback uniqueSourceCallback = new UniqueSourceCallback(sourceCollector);
107         ClassifierRemover classifierRemover = new ClassifierRemover(uniqueSourceCallback);
108         classifierRemover.onBegin();
109         for (Source source: captor.getAllValues()) {
110             classifierRemover.onSource(source);
111         }
112         classifierRemover.onComplete();
113 
114         for (String[] coverageFile : fixture) {
115             assertCoverage(sourceCollector.sources, coverageFile[0], Integer.parseInt(coverageFile[1]),
116                     toIntegerSet(coverageFile[2]), toIntegerSet(coverageFile[3]),
117                     toIntegerSet(coverageFile[4]), toIntegerSet(coverageFile[5]));
118         }
119     }
120 
121     protected abstract CoverageParser createCoverageParser(File coverageFile, SourceLoader sourceLoader);
122 
123     protected abstract List<String> getCoverageResources();
124 
125     protected abstract String[][] getCoverageFixture();
126 
127     private Set<Integer> toIntegerSet(final String commaSeparated) {
128         if (commaSeparated.isEmpty()) {
129             return Collections.emptySet();
130         }
131         String[] split = commaSeparated.split(",", -1);
132         Set<Integer> values = new HashSet<>();
133         for (String value : split) {
134             values.add(Integer.valueOf(value));
135         }
136         return values;
137     }
138 
139     private static class SourceCollector implements SourceCallback {
140 
141         private List<Source> sources = new ArrayList<>();
142 
143         @Override
144         public void onBegin() {
145             // Does nothing
146         }
147 
148         @Override
149         public void onSource(Source source) {
150             sources.add(source);
151         }
152 
153         @Override
154         public void onComplete() {
155             // Does Nothing
156         }
157     }
158 
159     private static class ClassifierRemover extends ChainingSourceCallback {
160 
161         public ClassifierRemover(SourceCallback chained) {
162             super(chained);
163         }
164 
165         @Override
166         protected void onSourceInternal(Source source) {
167             source.setClassifier(null);
168         }
169     }
170 
171     private static void assertCoverage(final Collection<Source> sources, final String name, final int lines,
172             final Set<Integer> coveredLines, final Set<Integer> missedLines,
173             final Set<Integer> coveredBranches, final Set<Integer> missedBranches) {
174 
175         Source tested = null;
176         for (Source source : sources) {
177             if (source.getName().endsWith(name)) {
178                 tested = source;
179                 break;
180             }
181         }
182         if (tested == null) {
183             fail("Expected source " + name + " not found from coverage report");
184         }
185         if (tested.getCoverage().length != lines) {
186             fail("Expected " + lines + " lines for " + name + " was " + tested.getCoverage().length);
187         }
188         for (int i = 0; i < tested.getCoverage().length; i++) {
189             Integer lineNumber = i + 1;
190             String message = name + " line " + lineNumber + " coverage";
191             if (coveredLines.contains(lineNumber)) {
192                 assertTrue(tested.getCoverage()[i] > 0, message);
193             } else if (missedLines.contains(lineNumber)) {
194                 assertTrue(tested.getCoverage()[i] == 0, message);
195             } else {
196                 assertNull(tested.getCoverage()[i], message);
197             }
198         }
199         for (final Branch b : tested.getBranchesList()) {
200             final String message = name + " branch " + b.getBranchNumber() + " coverage in line " + b.getLineNumber();
201             if (b.getHits() > 0) {
202                 assertTrue(coveredBranches.contains(b.getLineNumber()), message);
203             } else {
204                 assertTrue(missedBranches.contains(b.getLineNumber()), message);
205             }
206         }
207     }
208 }