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