This is the mail archive of the mauve-patches@sourceware.org mailing list for the Mauve project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

FYI: added tool for test coverage


I added that to estimate test coverage :

2005-10-16 Fabien DUMINY <fduminy@jnode.org>
* gnu/testlet/runner/TestletToAPI.java : added
* README.TestletToAPI : added
Index: README.TestletToAPI
===================================================================
RCS file: README.TestletToAPI
diff -N README.TestletToAPI
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ README.TestletToAPI	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,13 @@
+TestletToAPI can be used with Mauve but is in fact independant of it.
+
+TestletToAPI generates 2 japi files that can be used to show the coverage 
+(in term of methods, not of lines of code) of mauve testlets.
+
+That's a command line tool that takes 4 parameters :
+	inTestlets.japi                  : the output of japize on mauve testlets");
+	inTested.japi                    : the output of japize on tested api (Classpath, jdk, ...)
+	outTestlets.japi, outTested.japi : 2 japi files to be used as input of japicompat
+
+Example of usage (in a "native" command line style) :
+ TestletToAPI W:\mauve\mauve.japi W:\mauve\jdk15.japi W:\mauve\mauve.japi.post W:\mauve\jdk15.japi.post
+	
Index: TestletToAPI.java
===================================================================
RCS file: TestletToAPI.java
diff -N TestletToAPI.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ TestletToAPI.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,583 @@
+// Copyright (C) 2005 by <fduminy@jnode.org>
+// Written by Fabien DUMINY (fduminy@jnode.org)
+
+// This file is part of Mauve Reporter.
+
+// Mauve Reporter is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+
+// Mauve Reporter is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Mauve Reporter; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+package gnu.testlet.runner;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Generate 2 japi files that can be used to show the coverage 
+ * (in term of methods, not of lines of code) of mauve testlets
+ */
+public class TestletToAPI
+{
+    private static final String TESTLET_SERIALIZATION = "serialization";
+    private static final String TESTLET_CONSTRUCTORS = "constructors";
+    private static final String TESTLET_CONSTANTS = "constants";
+    private static final String TESTLET_CLASS_SUFFIX = "Class!"; // don't remove the "!" at the end !
+    
+    public static void main(String[] args) throws IOException
+    {                
+        if(args.length < 4)
+        {
+            usage();
+            return;
+        }
+        
+        final String inTestletsFile = args[0];
+        final String inTestedFile = args[1];
+        final String outTestletsFile = args[2];
+        final String outTestedFile = args[3];
+        
+        // just insure that all files are differents
+        // that's especially usefull to avoid erasing input files !
+        for(int i = 0 ; i < 4 ; i++)
+        {
+            for(int j = 0 ; j < 4 ; j++)
+            {
+                if((j != i) && args[i].equals(args[j]))
+                    throw new IllegalArgumentException("argument #"+i+" and argument #"+j+" are the same");
+            }            
+        }
+        
+        final Reader inTestlets = new FileReader(inTestletsFile);
+        final Reader inTested = new FileReader(inTestedFile);
+        final Writer outTestlets = new FileWriter(outTestletsFile);
+        final Writer outTested = new FileWriter(outTestedFile);
+        convert(inTestlets, inTested, outTestlets, outTested);
+    }
+
+    public static void usage()
+    {
+        System.out.println("usage: TestletToAPI inTestlets.japi inTested.japi outTestlets.japi outTested.japi");
+        System.out.println("  inTestlets.japi: the output of japize on mauve testlets");
+        System.out.println("  inTested.japi: the output of japize on tested api (Classpath, jdk, ...)");
+        System.out.println("  outTestlets.japi, outTested.japi: 2 japi files to be used as input of japicompat");
+    }
+    
+    public static void convert(Reader inTestlets, Reader inTested, Writer outTestlets, Writer outTested) throws IOException
+    {        
+        final Stats stats = new Stats();
+        final Map methodToFullLine = processTestedAPI(inTested, outTested, stats);
+        processMauveAPI(inTestlets, outTestlets, methodToFullLine, stats);
+        
+        System.out.println("=== Statistics ===");
+        System.out.println("NbTestlets:"+stats.getNbTestlets());
+        System.out.println("NbMethods:"+stats.getNbMethods());
+        System.out.println("NbMethodsNotFound:"+stats.getNbMethodsNotFound());
+        
+        if(stats.getNbMethods() == 0)
+            System.out.println("Estimated coverage : #ERROR#");
+        else
+        {
+            double ratio = (double) stats.getNbTestlets() / (double) stats.getNbMethods();
+            double percent = ((int)(10000 * ratio)) / 100.0d;
+            System.out.println("Estimated coverage : "+percent+" %");
+        }
+    }
+    
+    public static void processMauveAPI(Reader in, Writer out, Map methodToFullLine,
+            Stats stats) throws IOException
+    {
+        BufferedReader reader = null;
+        BufferedWriter writer = null;
+        try
+        {
+            reader = new BufferedReader(in);
+            writer = new BufferedWriter(out);
+            String l;
+            StringBuffer line = new StringBuffer(128);
+            Set testedMethods = new HashSet();
+            boolean firstLine = true;
+            while((l = reader.readLine()) != null)
+            {
+                line.setLength(0); // clear the buffer
+                line.append(l);
+
+                if(firstLine)
+                {
+                    writer.write(l);
+                    writer.write('\n');
+                    firstLine = false;
+                    continue;
+                }                
+                
+                if(!acceptAPILine(line.toString())) continue;
+                
+                Line mauveAPI = parseMauveAPILine(line);
+                
+                final String methodName = mauveAPI.getMethodName();
+                if(testedMethods.contains(methodName)) continue;
+                testedMethods.add(methodName);                
+                
+                Line testedAPI = (Line) methodToFullLine.get(methodName);
+                if((testedAPI == null) || (testedAPI == Line.NULL))
+                {
+                    System.err.println("method not found in API : "+methodName);
+                    stats.incNbMethodsNotFound();
+                    continue;
+                }
+                
+                String className = testedAPI.getClassName();
+                if(!testedMethods.contains(className))
+                {
+                    // first method of the class, add the class declaration line
+                    testedMethods.add(className);
+                    
+                    Line classDecl =  (Line) methodToFullLine.get(className);
+                    if((classDecl == null) || (classDecl == Line.NULL))
+                        throw new IllegalStateException(className + " has no declaration");
+                    
+                    writer.write(classDecl.getContent());                            
+                    writer.write('\n');
+                }
+                
+                // write the method (or constructors, or constants, or ...) declaration
+                writer.write(testedAPI.getContent());                            
+                writer.write('\n');
+                stats.inNbTestlets();
+            }
+        }
+        finally
+        {
+            if(reader != null)
+            {
+                reader.close();
+            }
+            if(writer != null)
+            {
+                writer.close();
+            }
+            
+            in.close();
+            out.close();
+        }
+    }
+    
+    public static Map processTestedAPI(Reader inTested, Writer outTested,
+                Stats stats) throws IOException
+    {
+        Map methodToFullLine = new HashMap();
+        BufferedReader reader = null;
+        BufferedWriter writer = null;
+        try
+        {
+            reader = new BufferedReader(inTested);
+            writer = new BufferedWriter(outTested);
+            String l;
+            StringBuffer line = new StringBuffer(128); 
+            boolean firstLine = true;
+            while((l = reader.readLine()) != null)
+            {
+                line.setLength(0); // clear the buffer
+                line.append(l);
+
+                if(firstLine)
+                {
+                    firstLine = false;
+                }
+                else if(acceptAPILine(l))
+                {
+                    Line parsedLine = parseTestedAPILine(line);
+                    String methodName = parsedLine.getMethodName();
+                    
+                    switch(parsedLine.getType())
+                    {
+                    case Line.CONSTANTS:
+                    case Line.CONSTRUCTORS:
+                        Line constLine = (Line) methodToFullLine.get(methodName);  
+                        if((constLine == Line.NULL) || (constLine == null))
+                        {
+                            methodToFullLine.put(methodName, parsedLine);
+                        }
+                        else
+                        {
+                            constLine.append(parsedLine.getContent());
+                        }
+                        break;
+                        
+                    case Line.CLASS_DECL:
+                        if(!methodToFullLine.containsKey(methodName))
+                        {
+                            methodToFullLine.put(parsedLine.getClassName(), parsedLine);
+                            
+                            if(parsedLine.isSerializable())
+                            {
+                                // this a serializable class, add a pseudo method
+                                String serialMethod = parsedLine.getClassName()+'!'+TESTLET_SERIALIZATION;
+                                String serialLine = serialMethod + "() Pcifu V";
+                                Line sl = new Line(Line.SERIALIZATION, serialMethod, serialLine, true);
+                                methodToFullLine.put(serialMethod, sl);
+                                
+                                // will write 2 lines on output :
+                                // 1 for the declaration and 1 for the pseudo method
+                                l += "\n" ;
+                                if(l.charAt(0) == '+')
+                                {
+                                    if(l.charAt(1) == '+')
+                                        l += "++";
+                                    else
+                                        l += "+";
+                                }
+                                l += serialLine;
+                                stats.incNbMethods(); // 1 extra method (a pseudo method)
+                            }
+                        }
+                        break;
+                      
+                    case Line.SERIALIZATION:
+                        throw new IllegalStateException("type SERIALIZATION unexpected here");
+                        
+                    case Line.METHOD:
+                        if(!methodToFullLine.containsKey(methodName))
+                        {
+                            methodToFullLine.put(methodName, parsedLine);
+                        }
+                        break;
+
+                    default:
+                        if(parsedLine != Line.NULL)
+                        {
+                            throw new IllegalStateException("unknown type of line");
+                        }
+                    } // switch
+                } // if
+                
+                writer.write(l);
+                writer.write('\n');
+                stats.incNbMethods();
+            } // while
+        }
+        finally
+        {
+            if(reader != null)
+            {
+                reader.close();
+            }
+            inTested.close();
+            
+            if(writer != null)
+            {
+                writer.close();
+            }
+            outTested.close();
+        }
+        
+        return methodToFullLine;
+    }
+        
+    public static Line parseTestedAPILine(StringBuffer japizeLine)
+    {
+        final String line = japizeLine.toString();
+        boolean isClassDecl = preprocessAPILine(japizeLine);
+        boolean isSerialClassDecl = false;
+        // tested API (Classpath, jdk or something compatible)
+
+        if(!isClassDecl && (japizeLine.indexOf("(") < 0))
+        {
+            // class declared as Serializable ?
+            isSerialClassDecl = (japizeLine.indexOf("class#") >= 0) && 
+                                        (japizeLine.lastIndexOf("java.io.Serializable") >= 0);
+            
+            boolean isNonSerialClassDecl = (japizeLine.indexOf("class:") >= 0);            
+            boolean isInterfaceDecl = (japizeLine.indexOf("interface") >= 0);
+            isClassDecl = (isNonSerialClassDecl || isSerialClassDecl || isInterfaceDecl);
+        }
+        if(isClassDecl)
+        {
+            // special case for class declaration (Serializable or not) and
+            // interface declarations
+            int idx = japizeLine.indexOf("!");
+            if(idx < 0) return Line.NULL;
+          
+            japizeLine.setLength(idx+1);            
+            return new Line(Line.CLASS_DECL, japizeLine.toString(), line, isSerialClassDecl);            
+        }
+        
+        int idx = japizeLine.indexOf("(");
+        if(idx < 0)
+        {
+            // constant ?
+            idx = japizeLine.indexOf("!#");
+            if(idx < 0) return Line.NULL;
+                
+            // special case for constants
+            japizeLine.setLength(idx+1);
+            japizeLine.append(TESTLET_CONSTANTS);
+            return new Line(Line.CONSTANTS, japizeLine.toString(), line);                
+        }
+        
+        boolean isConstructor = (japizeLine.indexOf("constructor") >= 0);
+        if(isConstructor)
+        {
+            // special case for constructors
+            japizeLine.setLength(idx);                    
+            japizeLine.append(TESTLET_CONSTRUCTORS);
+            return new Line(Line.CONSTRUCTORS, japizeLine.toString(), line);                
+        }
+        
+        if(idx >= 0)
+        {
+            // simple method
+            japizeLine.setLength(idx);                    
+            return new Line(Line.METHOD, japizeLine.toString(), line);
+        }
+        
+        return Line.NULL;
+    }
+
+    public static Line parseMauveAPILine(StringBuffer japizeLine)
+    {
+        int type;
+        final String line = japizeLine.toString();        
+        preprocessAPILine(japizeLine);
+        
+        // mauve test API
+
+        japizeLine.delete(0, "gnu.testlet.".length());
+        
+        int idx = japizeLine.indexOf("!");
+        if(idx < 0) return Line.NULL;
+        japizeLine.setLength(idx);
+        
+        idx = japizeLine.indexOf(",");
+        if(idx < 0) return Line.NULL;
+        japizeLine.setCharAt(idx, '!');
+        
+        idx = japizeLine.lastIndexOf("."); 
+        if(idx < 0) return Line.NULL;
+        japizeLine.setCharAt(idx, ',');
+        
+        // special case for some classes 
+        // (due to case conflicts in filenames under Windows)
+        // example : ColorClass->Color, ...
+        idx = japizeLine.indexOf(TESTLET_CLASS_SUFFIX);
+        if(idx >= 0)
+        {
+            japizeLine.delete(idx, idx+TESTLET_CLASS_SUFFIX.length()-1);
+        }
+        
+        if(japizeLine.indexOf(TESTLET_CONSTANTS) >= 0)
+            type = Line.CONSTANTS;
+        else if(japizeLine.indexOf(TESTLET_CONSTRUCTORS) >= 0)
+            type = Line.CONSTRUCTORS;
+        else if(japizeLine.indexOf(TESTLET_SERIALIZATION) >= 0)
+            type = Line.SERIALIZATION;
+        else
+            type = Line.METHOD;
+                
+        return new Line(type, japizeLine.toString(), line);
+    }
+    
+    public static boolean acceptAPILine(String line)
+    {
+//        final String filter = "java.awt.AWTKeyStroke";
+//        return line.startsWith("gnu.testlet."+filter) ||
+//               line.startsWith(filter);
+        
+        boolean isMauveFramework = line.toString().startsWith("gnu.testlet,");        
+//        return !isMauveFramework && 
+//               (line.contains("java.awt.BasicStroke") ||
+//                line.contains("java.awt,BasicStroke") );
+        return !isMauveFramework;
+    }
+    
+    public static class Stats
+    {
+        private int nbTestlets;
+        private int nbMethods; // include special methods (serialization, constructors, constants, ...)
+        private int nbMethodsNotFound;
+        
+        public Stats()
+        {
+            this.nbTestlets = 0;
+            this.nbMethods = 0;
+            this.nbMethodsNotFound = 0;
+        }
+
+        /**
+         * @return Returns the nbMethods.
+         */
+        final int getNbMethods()
+        {
+            return nbMethods;
+        }
+
+        final void incNbMethods()
+        {
+            this.nbMethods++;
+        }
+
+        /**
+         * @return Returns the nbTestlets.
+         */
+        final int getNbTestlets()
+        {
+            return nbTestlets;
+        }
+
+        final void inNbTestlets()
+        {
+            this.nbTestlets++;
+        }
+
+        /**
+         * @return Returns the nbMethodsNotFound.
+         */
+        final int getNbMethodsNotFound()
+        {
+            return nbMethodsNotFound;
+        }
+
+        /**
+         * @param nbMethodsNotFound The nbMethodsNotFound to set.
+         */
+        final void incNbMethodsNotFound()
+        {
+            this.nbMethodsNotFound++;
+        }
+        
+
+    }
+    
+    public static class Line
+    {
+        public static final Line NULL = new Line(); 
+        
+        private static final int METHOD        = 0;
+        private static final int CONSTRUCTORS  = 1;
+        private static final int CONSTANTS     = 2;
+        private static final int SERIALIZATION = 3;
+        private static final int CLASS_DECL    = 4;
+        
+        final private int type;
+        final private String className;
+        final private String methodName; // in fact: classname + '!' + methodName
+        final private StringBuffer lines;
+        final private boolean serializable;
+
+        public Line(int type, String methodName, String line)
+        {
+            this(type, methodName, line, false);
+        }
+        
+        public Line(int type, String methodName, String line, boolean serializable)
+        {
+            this.type = type;
+            this.methodName = methodName;
+            this.lines = new StringBuffer(line);
+            this.serializable = serializable;
+            
+            int idx = methodName.indexOf('!');
+            if(idx < 0) throw new IllegalArgumentException("methodName must contains the '!' character");                
+            this.className = methodName.substring(0, idx);
+        }
+        
+        public int getType()
+        {
+            return type;
+        }
+        
+        public boolean isSerializable()
+        {
+            return serializable;
+        }
+
+        public String getContent()
+        {
+            return lines.toString();
+        }
+
+        private Line()
+        {
+            this.type = -1;
+            this.className = "";
+            this.methodName = "";
+            this.lines = null;      
+            this.serializable = false;
+        }
+        
+        public void append(String nextLine)
+        {
+            if(!isConstructor() && !isConstant()) 
+                throw new UnsupportedOperationException("reserved to constructor and constant lines");
+            
+            lines.append('\n');
+            lines.append(nextLine);
+        }
+        
+        public boolean isConstructor()
+        {
+            return type == CONSTRUCTORS;
+        }
+        
+        public boolean isConstant()
+        {
+            return type == CONSTANTS;
+        }
+
+        /**
+         * @return Returns the methodName.
+         */
+        final public String getMethodName()
+        {
+            return methodName;
+        }
+        
+        public String getClassName()
+        {
+            return className;
+        }
+        
+        public String toString()
+        {
+            return "["+methodName+"]->"+lines.toString();
+        }
+    }
+
+    public static boolean preprocessAPILine(StringBuffer japizeLine)
+    {
+        boolean isClassDecl = false;
+        if(japizeLine.charAt(0) == '+')
+        {
+            if(japizeLine.charAt(1) == '+')
+            {
+                japizeLine.delete(0, 2);
+                
+                // declaration of java.lang.Object ?
+                isClassDecl = (japizeLine.toString().endsWith("class"));
+            }
+            else
+            {
+                japizeLine.delete(0, 1);
+            }
+        }
+        
+        return isClassDecl;
+    }
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]