001    /*
002     * Cobertura - http://cobertura.sourceforge.net/
003     *
004     * Copyright (C) 2003 jcoverage ltd.
005     * Copyright (C) 2005 Mark Doliner
006     * Copyright (C) 2005 Nathan Wilson
007     * Copyright (C) 2009 Charlie Squires
008     *
009     * Cobertura is free software; you can redistribute it and/or modify
010     * it under the terms of the GNU General Public License as published
011     * by the Free Software Foundation; either version 2 of the License,
012     * or (at your option) any later version.
013     *
014     * Cobertura is distributed in the hope that it will be useful, but
015     * WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     * General Public License for more details.
018     *
019     * You should have received a copy of the GNU General Public License
020     * along with Cobertura; if not, write to the Free Software
021     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
022     * USA
023     */
024    
025    package net.sourceforge.cobertura.check;
026    
027    import java.io.File;
028    import java.math.BigDecimal;
029    import java.util.HashMap;
030    import java.util.Iterator;
031    import java.util.Map;
032    import java.util.StringTokenizer;
033    
034    import net.sourceforge.cobertura.coveragedata.ClassData;
035    import net.sourceforge.cobertura.coveragedata.CoverageDataFileHandler;
036    import net.sourceforge.cobertura.coveragedata.ProjectData;
037    import net.sourceforge.cobertura.util.Header;
038    
039    import org.apache.log4j.Logger;
040    import org.apache.oro.text.regex.MalformedPatternException;
041    import org.apache.oro.text.regex.Pattern;
042    import org.apache.oro.text.regex.Perl5Compiler;
043    import org.apache.oro.text.regex.Perl5Matcher;
044    
045    public class Main
046    {
047    
048            private static final Logger logger = Logger.getLogger(Main.class);
049    
050            final Perl5Matcher pm = new Perl5Matcher();
051    
052            final Perl5Compiler pc = new Perl5Compiler();
053    
054            /**
055             * The default CoverageRate needed for a class to pass the check.
056             */
057            CoverageRate minimumCoverageRate;
058    
059            /**
060             * The keys of this map contain regular expression Patterns that
061             * match against classes.  The values of this map contain
062             * CoverageRate objects that specify the minimum coverage rates
063             * needed for a class that matches the pattern.
064             */
065            Map minimumCoverageRates = new HashMap();
066    
067            /**
068             * The keys of this map contain package names. The values of this 
069             * map contain PackageCoverage objects that track the line and
070             * branch coverage values for a package.
071             */
072            Map packageCoverageMap = new HashMap();
073    
074            double inRangeAndDivideByOneHundred(String coverageRateAsPercentage)
075            {
076                    return inRangeAndDivideByOneHundred(Integer.valueOf(
077                                    coverageRateAsPercentage).intValue());
078            }
079    
080            double inRangeAndDivideByOneHundred(int coverageRateAsPercentage)
081            {
082                    if ((coverageRateAsPercentage >= 0)
083                                    && (coverageRateAsPercentage <= 100))
084                    {
085                            return (double)coverageRateAsPercentage / 100;
086                    }
087                    throw new IllegalArgumentException("The value "
088                                    + coverageRateAsPercentage
089                                    + "% is invalid.  Percentages must be between 0 and 100.");
090            }
091    
092            void setMinimumCoverageRate(String minimumCoverageRate)
093                            throws MalformedPatternException
094            {
095                    StringTokenizer tokenizer = new StringTokenizer(minimumCoverageRate,
096                                    ":");
097                    this.minimumCoverageRates.put(pc.compile(tokenizer.nextToken()),
098                                    new CoverageRate(inRangeAndDivideByOneHundred(tokenizer
099                                                    .nextToken()), inRangeAndDivideByOneHundred(tokenizer
100                                                    .nextToken())));
101            }
102    
103            /**
104             * This method returns the CoverageRate object that
105             * applies to the given class.  If checks if there is a
106             * pattern that matches the class name, and returns that
107             * if it finds one.  Otherwise it uses the global minimum
108             * rates that were passed in.
109             */
110            CoverageRate findMinimumCoverageRate(String classname)
111            {
112                    Iterator iter = this.minimumCoverageRates.entrySet().iterator();
113                    while (iter.hasNext())
114                    {
115                            Map.Entry entry = (Map.Entry)iter.next();
116    
117                            if (pm.matches(classname, (Pattern)entry.getKey()))
118                            {
119                                    return (CoverageRate)entry.getValue();
120                            }
121                    }
122                    return this.minimumCoverageRate;
123            }
124    
125            public Main(String[] args) throws MalformedPatternException
126            {
127                    int exitStatus = 0;
128    
129                    Header.print(System.out);
130    
131                    File dataFile = CoverageDataFileHandler.getDefaultDataFile();
132                    double branchCoverageRate = -1.0;
133                    double lineCoverageRate = -1.0;
134                    double packageBranchCoverageRate = -1.0;
135                    double packageLineCoverageRate = -1.0;
136                    double totalBranchCoverageRate = -1.0;
137                    double totalLineCoverageRate = -1.0;
138    
139                    for (int i = 0; i < args.length; i++)
140                    {
141                            if (args[i].equals("--branch"))
142                            {
143                                    branchCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
144                            }
145                            else if (args[i].equals("--datafile"))
146                            {
147                                    dataFile = new File(args[++i]);
148                            }
149                            else if (args[i].equals("--line"))
150                            {
151                                    lineCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
152                            }
153                            else if (args[i].equals("--regex"))
154                            {
155                                    setMinimumCoverageRate(args[++i]);
156                            }
157                            else if (args[i].equals("--packagebranch"))
158                            {
159                                    packageBranchCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
160                            }
161                            else if (args[i].equals("--packageline"))
162                            {
163                                    packageLineCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
164                            }
165                            else if (args[i].equals("--totalbranch"))
166                            {
167                                    totalBranchCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
168                            }
169                            else if (args[i].equals("--totalline"))
170                            {
171                                    totalLineCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
172                            }
173                    }
174    
175                    ProjectData projectData = CoverageDataFileHandler
176                                    .loadCoverageData(dataFile);
177    
178                    if (projectData == null)
179                    {
180                            System.err.println("Error: Unable to read from data file "
181                                            + dataFile.getAbsolutePath());
182                            System.exit(1);
183                    }
184    
185                    // If they didn't specify any thresholds, then use some defaults
186                    if ((branchCoverageRate == -1.0) && (lineCoverageRate == -1.0)
187                                    && (packageLineCoverageRate == -1.0)
188                                    && (packageBranchCoverageRate == -1.0)
189                                    && (totalLineCoverageRate == -1.0)
190                                    && (totalBranchCoverageRate == -1.0)
191                                    && (this.minimumCoverageRates.size() == 0))
192                    {
193                            branchCoverageRate = 0.5;
194                            lineCoverageRate = 0.5;
195                            packageBranchCoverageRate = 0.5;
196                            packageLineCoverageRate = 0.5;
197                            totalBranchCoverageRate = 0.5;
198                            totalLineCoverageRate = 0.5;
199                    }
200                    // If they specified one or more thresholds, default everything else to 0
201                    else
202                    {
203                            if (branchCoverageRate == -1.0)
204                                    branchCoverageRate = 0.0;
205                            if (lineCoverageRate == -1.0)
206                                    lineCoverageRate = 0.0;
207                            if (packageLineCoverageRate == -1.0)
208                                    packageLineCoverageRate = 0.0;
209                            if (packageBranchCoverageRate == -1.0)
210                                    packageBranchCoverageRate = 0.0;
211                            if (totalLineCoverageRate == -1.0)
212                                    totalLineCoverageRate = 0.0;
213                            if (totalBranchCoverageRate == -1.0)
214                                    totalBranchCoverageRate = 0.0;
215                    }
216    
217                    this.minimumCoverageRate = new CoverageRate(lineCoverageRate,
218                                    branchCoverageRate);
219    
220                    double totalLines = 0;
221                    double totalLinesCovered = 0;
222                    double totalBranches = 0;
223                    double totalBranchesCovered = 0;
224    
225                    Iterator iter = projectData.getClasses().iterator();
226                    while (iter.hasNext())
227                    {
228                            ClassData classData = (ClassData)iter.next();
229                            CoverageRate coverageRate = findMinimumCoverageRate(classData
230                                            .getName());
231    
232                            if (totalBranchCoverageRate > 0.0)
233                            {
234                                    totalBranches += classData.getNumberOfValidBranches();
235                                    totalBranchesCovered += classData.getNumberOfCoveredBranches();
236                            }
237    
238                            if (totalLineCoverageRate > 0.0)
239                            {
240                                    totalLines += classData.getNumberOfValidLines();
241                                    totalLinesCovered += classData.getNumberOfCoveredLines();
242                            }
243    
244                            PackageCoverage packageCoverage = getPackageCoverage(classData
245                                            .getPackageName());
246                            if (packageBranchCoverageRate > 0.0)
247                            {
248                                    packageCoverage.addBranchCount(classData
249                                                    .getNumberOfValidBranches());
250                                    packageCoverage.addBranchCoverage(classData
251                                                    .getNumberOfCoveredBranches());
252                            }
253    
254                            if (packageLineCoverageRate > 0.0)
255                            {
256                                    packageCoverage.addLineCount(classData.getNumberOfValidLines());
257                                    packageCoverage.addLineCoverage(classData
258                                                    .getNumberOfCoveredLines());
259                            }
260    
261                            logger.debug("Class " + classData.getName()
262                                            + ", line coverage rate: "
263                                            + percentage(classData.getLineCoverageRate())
264                                            + "%, branch coverage rate: "
265                                            + percentage(classData.getBranchCoverageRate()) + "%");
266    
267                            if (classData.getBranchCoverageRate() < coverageRate
268                                            .getBranchCoverageRate())
269                            {
270                                    System.err.println(classData.getName()
271                                                    + " failed check. Branch coverage rate of "
272                                                    + percentage(classData.getBranchCoverageRate())
273                                                    + "% is below "
274                                                    + percentage(coverageRate.getBranchCoverageRate())
275                                                    + "%");
276                                    exitStatus |= 2;
277                            }
278    
279                            if (classData.getLineCoverageRate() < coverageRate
280                                            .getLineCoverageRate())
281                            {
282                                    System.err.println(classData.getName()
283                                                    + " failed check. Line coverage rate of "
284                                                    + percentage(classData.getLineCoverageRate())
285                                                    + "% is below "
286                                                    + percentage(coverageRate.getLineCoverageRate()) + "%");
287                                    exitStatus |= 4;
288                            }
289                    }
290    
291                    exitStatus |= checkPackageCoverageLevels(packageBranchCoverageRate,
292                                    packageLineCoverageRate);
293    
294                    // Check the rates for the overall project
295                    if ((totalBranches > 0)
296                                    && (totalBranchCoverageRate > (totalBranchesCovered / totalBranches)))
297                    {
298                            System.err
299                                            .println("Project failed check. "
300                                                            + "Total branch coverage rate of "
301                                                            + percentage(totalBranchesCovered / totalBranches)
302                                                            + "% is below "
303                                                            + percentage(totalBranchCoverageRate) + "%");
304                            exitStatus |= 8;
305                    }
306    
307                    if ((totalLines > 0)
308                                    && (totalLineCoverageRate > (totalLinesCovered / totalLines)))
309                    {
310                            System.err.println("Project failed check. "
311                                            + "Total line coverage rate of "
312                                            + percentage(totalLinesCovered / totalLines)
313                                            + "% is below " + percentage(totalLineCoverageRate) + "%");
314                            exitStatus |= 16;
315                    }
316    
317                    System.exit(exitStatus);
318            }
319    
320            private PackageCoverage getPackageCoverage(String packageName)
321            {
322                    PackageCoverage packageCoverage = (PackageCoverage)packageCoverageMap
323                                    .get(packageName);
324                    if (packageCoverage == null)
325                    {
326                            packageCoverage = new PackageCoverage();
327                            packageCoverageMap.put(packageName, packageCoverage);
328                    }
329                    return packageCoverage;
330            }
331    
332            private int checkPackageCoverageLevels(double packageBranchCoverageRate,
333                            double packageLineCoverageRate)
334            {
335                    int exitStatus = 0;
336                    Iterator iter = packageCoverageMap.entrySet().iterator();
337                    while (iter.hasNext())
338                    {
339                            Map.Entry entry = (Map.Entry)iter.next();
340                            String packageName = (String)entry.getKey();
341                            PackageCoverage packageCoverage = (PackageCoverage)entry.getValue();
342    
343                            exitStatus |= checkPackageCoverage(packageBranchCoverageRate,
344                                            packageLineCoverageRate, packageName, packageCoverage);
345                    }
346                    return exitStatus;
347            }
348    
349            private int checkPackageCoverage(double packageBranchCoverageRate,
350                            double packageLineCoverageRate, String packageName,
351                            PackageCoverage packageCoverage)
352            {
353                    int exitStatus = 0;
354                    double branchCoverage = packageCoverage.getBranchCoverage()
355                                    / packageCoverage.getBranchCount();
356                    if ((packageCoverage.getBranchCount() > 0)
357                                    && (packageBranchCoverageRate > branchCoverage))
358                    {
359                            System.err.println("Package " + packageName
360                                            + " failed check. Package branch coverage rate of "
361                                            + percentage(branchCoverage) + "% is below "
362                                            + percentage(packageBranchCoverageRate) + "%");
363                            exitStatus |= 32;
364                    }
365    
366                    double lineCoverage = packageCoverage.getLineCoverage()
367                                    / packageCoverage.getLineCount();
368                    if ((packageCoverage.getLineCount() > 0)
369                                    && (packageLineCoverageRate > lineCoverage))
370                    {
371                            System.err.println("Package " + packageName
372                                            + " failed check. Package line coverage rate of "
373                                            + percentage(lineCoverage) + "% is below "
374                                            + percentage(packageLineCoverageRate) + "%");
375                            exitStatus |= 64;
376                    }
377    
378                    return exitStatus;
379            }
380    
381            private String percentage(double coverateRate)
382            {
383                    BigDecimal decimal = new BigDecimal(coverateRate * 100);
384                    return decimal.setScale(1, BigDecimal.ROUND_DOWN).toString();
385            }
386    
387            public static void main(String[] args) throws MalformedPatternException
388            {
389                    new Main(args);
390            }
391    
392    }