changeset 36:b7fab35801d1

7902143: Add an ANC filter that takes method list as input Reviewed-by: afedorch
author shurailine
date Wed, 11 Apr 2018 01:44:12 -0700
parents 391790f13cfd
children 0b9e41807b07 2e8d8c4675db
files build/build.xml src/classes/com/sun/tdk/jcov/RepGen.java src/classes/com/sun/tdk/jcov/report/ParameterizedAncFilter.java src/classes/com/sun/tdk/jcov/report/ancfilters/ListANCFilter.java test/unit/com/sun/tdk/jcov/report/ancfilters/ListANCFilterTest.java
diffstat 5 files changed, 381 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/build/build.xml	Wed Apr 04 15:29:11 2018 -0700
+++ b/build/build.xml	Wed Apr 11 01:44:12 2018 -0700
@@ -201,6 +201,7 @@
             com.sun.tdk.jcov.report.ancfilters.SyntheticANCFilter
             com.sun.tdk.jcov.report.ancfilters.GetterANCFilter
             com.sun.tdk.jcov.report.ancfilters.SetterANCFilter
+            com.sun.tdk.jcov.report.ancfilters.ListANCFilter
         </echo>
 
         <jar destfile="${build.dir}/jcov.jar" basedir="${jcov.classes}" index="false">
--- a/src/classes/com/sun/tdk/jcov/RepGen.java	Wed Apr 04 15:29:11 2018 -0700
+++ b/src/classes/com/sun/tdk/jcov/RepGen.java	Wed Apr 11 01:44:12 2018 -0700
@@ -25,10 +25,11 @@
 package com.sun.tdk.jcov;
 
 import com.sun.tdk.jcov.instrument.DataMethod;
+import com.sun.tdk.jcov.instrument.DataClass;
 import com.sun.tdk.jcov.instrument.DataField;
-import com.sun.tdk.jcov.instrument.DataClass;
 import com.sun.tdk.jcov.processing.DataProcessorSPI;
 import com.sun.tdk.jcov.report.AncFilter;
+import com.sun.tdk.jcov.report.ParameterizedAncFilter;
 import com.sun.tdk.jcov.report.ancfilters.DefaultAncFilter;
 import com.sun.tdk.jcov.util.Utils;
 import com.sun.tdk.jcov.data.FileFormatException;
@@ -78,6 +79,7 @@
 
     final static String CUSTOM_REPORT_GENERATOR_SPI = "customreport.spi";
     final static String DATA_PROCESSOR_SPI = "dataprocessor.spi";
+    private static final String ANC_FILTER_PARAMETER_SEPARATOR = ":";
 
     // logger initialization
     static {
@@ -254,21 +256,44 @@
             }
             if (ancdeffilters.length == 1 && ancdeffilters[0].equals("all")){
                 for (DefaultAncFilter filter : loader) {
-                    defaultANCFiltersList.add(filter);
+                    if(!(filter instanceof ParameterizedAncFilter))
+                        defaultANCFiltersList.add(filter);
                 }
             }
             else {
                 for (String defaulAncFilter : ancdeffilters) {
                     boolean found = false;
                     for (DefaultAncFilter filter : loader) {
-                        if (defaulAncFilter.equals(filter.getFilterName())) {
+                        String filterName, filterParameters;
+                        int separatorPosition = defaulAncFilter.indexOf(ANC_FILTER_PARAMETER_SEPARATOR);
+                        if(separatorPosition > -1) {
+                            filterName = defaulAncFilter.substring(0, separatorPosition);
+                            filterParameters = defaulAncFilter.substring(separatorPosition +
+                                    ANC_FILTER_PARAMETER_SEPARATOR.length());
+                        } else {
+                            filterName = defaulAncFilter;
+                            filterParameters = null;
+                        }
+                        if (filterName.equals(filter.getFilterName())) {
+                            if(filterParameters != null) {
+                                if (filter instanceof ParameterizedAncFilter) {
+                                    try {
+                                        ((ParameterizedAncFilter) filter).setParameter(filterParameters);
+                                    } catch (Exception e) {
+                                        throw new RuntimeException("Unable to set parameter for filter " +
+                                                filterName + ":" + e.getMessage());
+                                    }
+                                } else {
+                                    throw new RuntimeException(filterName + " filter does not accept parameters: " + filter);
+                                }
+                            }
+                            found = true;
                             defaultANCFiltersList.add(filter);
-                            found = true;
                             break;
                         }
                     }
                     if (!found) {
-                        logger.log(Level.SEVERE, "There is no default ANC filter for \"{0}\" value", defaulAncFilter);
+                        throw new RuntimeException("There is no default ANC filter for \""+defaulAncFilter+"\" value");
                     }
                 }
             }
@@ -844,7 +869,7 @@
                     "");
 
     final static OptionDescr DSC_ANC_DEFAULT_FILTERS =
-            new OptionDescr("ancdeffilters", new String[]{"ancdf"}, "Default ANC filters names to use in report", OptionDescr.VAL_MULTI,
+            new OptionDescr("ancdeffilters", new String[]{"ancdf"}, "Default ANC filter name to use in report", OptionDescr.VAL_MULTI,
                     "");
 
     /**
@@ -879,4 +904,4 @@
             new OptionDescr("overviewReportTitle", new String[]{"overviewReportTitle", "ortitle"}, "The overview list report title", OptionDescr.VAL_SINGLE, "");
     public final static OptionDescr DSC_REPORT_TITLE_ENTITIES =
             new OptionDescr("entitiesReportTitle", new String[]{"entitiesReportTitle", "ertitle"}, "Entities report title (for modules, packages, subpackages)", OptionDescr.VAL_SINGLE, "");
-}
\ No newline at end of file
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/report/ParameterizedAncFilter.java	Wed Apr 11 01:44:12 2018 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov.report;
+
+/**
+ * This interface represents an ability to pass a string parameter. Syntax of using default parameterized filters:
+ * <pre>java ... RepGen -ancdf &lt;filter&gt;:&lt;parameter&gt;</pre>
+ */
+public interface ParameterizedAncFilter extends AncFilter {
+    /**
+     * Sets filter parameter specified in the command line.
+     * @param parameter
+     * @throws Exception
+     */
+    void setParameter(String parameter) throws Exception;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/report/ancfilters/ListANCFilter.java	Wed Apr 11 01:44:12 2018 -0700
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov.report.ancfilters;
+
+import com.sun.tdk.jcov.instrument.DataBlock;
+import com.sun.tdk.jcov.instrument.DataClass;
+import com.sun.tdk.jcov.instrument.DataMethod;
+import com.sun.tdk.jcov.report.ParameterizedAncFilter;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * This class provides an ability to use externally generated lists of methods to be used
+ * as an ANC filter. There are certain assumptions on the file format:
+ * <ul>
+ *     <li>Lines containing comments must start with "#" symbol.</li>
+ *     <li>First line in the file should be a comment and must contain the "ANC reason"</li>
+ *     <li>Every non-comment line should be empty or be in a form of
+ *     &lt;class-name#&lt&gt;&lt;method-name-andsignature&gt;. Example:  java/lang/String#indexOf(I)I</li>
+ * </ul>
+ */
+public class ListANCFilter implements DefaultAncFilter, ParameterizedAncFilter {
+
+    private static final String COMMENT_PREFIX = "#";
+    private static final String CLASS_METHOD_SEPARATOR = "#";
+
+    private Map<String, Set<String>> excludes;
+    private String ancReason;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean accept(DataClass dc) {
+        assertInitialized();
+        return false;
+    }
+
+    private void assertInitialized() {
+        if(excludes == null) {
+            throw new IllegalStateException("No ANC list was provided");
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean accept(DataClass dc, DataMethod dm) {
+        assertInitialized();
+        String className = dc.getFullname();
+        String methodName = dm.getName();
+        int dot = methodName.indexOf(".");
+        if(dot > -1) {
+            className = className + methodName.substring(0, dot);
+            methodName = methodName.substring(dot + 1);
+        }
+        Set<String> methods = excludes.get(className);
+        return methods != null && methods.contains(methodName + dm.getVmSignature());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean accept(DataMethod dm, DataBlock db) {
+        assertInitialized();
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getAncReason() {
+        assertInitialized();
+        return ancReason;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getFilterName() {
+        return "list";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setParameter(String parameter) throws IOException {
+        if(parameter == null)
+            throw new IllegalArgumentException("File must not be null for list filter.");
+        excludes = new HashMap<>();
+        try(BufferedReader in =
+                    new BufferedReader(new FileReader(parameter))) {
+            String line = in.readLine();
+            if(line != null && line.startsWith(COMMENT_PREFIX)) {
+                ancReason = line.substring(COMMENT_PREFIX.length());
+            } else {
+                throw new IllegalStateException("No ANC reason was provided.");
+            }
+            while((line = in.readLine()) != null) {
+                if(line.startsWith(COMMENT_PREFIX)) {
+                    continue;
+                }
+                int separator = line.indexOf(CLASS_METHOD_SEPARATOR);
+                if (separator > -1) {
+                    String clss = line.substring(0, separator);
+                    Set<String> mthds = excludes.get(clss);
+                    if (mthds == null) {
+                        mthds = new HashSet<>();
+                        excludes.put(clss, mthds);
+                    }
+                    mthds.add(line.substring(separator + 1));
+                } else {
+                    if (line.length() > 0) {
+                        throw new IllegalStateException("Unidentifiable method " + line);
+                    }
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unit/com/sun/tdk/jcov/report/ancfilters/ListANCFilterTest.java	Wed Apr 11 01:44:12 2018 -0700
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tdk.jcov.report.ancfilters;
+
+import com.sun.tdk.jcov.instrument.DataClass;
+import com.sun.tdk.jcov.instrument.DataMethodEntryOnly;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.AssertJUnit.assertFalse;
+
+public class ListANCFilterTest {
+    private String createListFile(String[] lines) throws IOException {
+        Path file = Files.createTempFile("ListANCFilterTest", ".lst");
+        BufferedWriter out = Files.newBufferedWriter(file, Charset.defaultCharset());
+        for(String ln : lines) {
+            out.write(ln);out.newLine();
+        }
+        out.close();
+        file.toFile().deleteOnExit();
+        return file.toAbsolutePath().toString();
+    }
+
+    @Test
+    public void testNormal() throws IOException {
+        ListANCFilter filter = new ListANCFilter();
+        String[] data = {
+                "#normal",
+                //a method
+                "java/lang/String#indexOf(I)I",
+                //a constructor
+                "java/lang/Math#<init>()V",
+        };
+        filter.setParameter(createListFile(data));
+        assertEquals(filter.getAncReason(), "normal");
+        DataClass stringDataClass = new DataClass(0,
+                "java/lang/String", "java.base", 0, false);
+        assertFalse(filter.accept(stringDataClass));
+        assertTrue(filter.accept(stringDataClass,
+                new DataMethodEntryOnly(stringDataClass, 0, "indexOf", "(I)I", "", new String[0], 0)));
+        DataClass mathDataClass = new DataClass(2,
+                "java/lang/Math", "java.base", 0, false);
+        assertTrue(filter.accept(mathDataClass,
+                new DataMethodEntryOnly(mathDataClass, 0, "<init>", "()V", "", new String[0], 0)));
+    }
+
+    @Test
+    public void testNested() throws IOException {
+        ListANCFilter filter = new ListANCFilter();
+        String[] data = {
+                "#nested",
+                //a nested class
+                "java/lang/System$LoggerFinder#checkPermission()Ljava/lang/Void;"
+        };
+        filter.setParameter(createListFile(data));
+        assertEquals(filter.getAncReason(), "nested");
+        DataClass systemDataClass = new DataClass(2,
+                "java/lang/System", "java.base", 0, false);
+        assertTrue(filter.accept(systemDataClass,
+                new DataMethodEntryOnly(systemDataClass, 0, "$LoggerFinder.checkPermission",
+                        "()Ljava/lang/Void;", "", new String[0], 0)));
+    }
+
+    @DataProvider(name="unreadable")
+    public Object[][] unreadableLists() {
+        return new Object[][] {
+                {new String[] {"java/lang/String#indexOf(I)I"}},
+                {new String[] {"data", "java/lang/String#indexOf(I)I"}},
+                {new String[] {"#data", "java/lang/String/indexOf(I)I"}},
+                {null}
+        };
+    }
+
+    @Test(dataProvider = "unreadable", expectedExceptions = {IllegalStateException.class, IllegalArgumentException.class})
+    public void testNotRead(String[] data) throws IllegalStateException, IOException {
+        String file;
+        if(data == null)
+            file = null;
+        else
+            file = createListFile(data);
+        new ListANCFilter().setParameter(file);
+    }
+
+    @DataProvider(name="readable")
+    public Object[][] readableLists() {
+        String indexOfLine = "java/lang/String#indexOf(I)I";
+        String[] indexOfElements = {"java/lang/String", "indexOf", "(I)I"};
+        String constructorLine = "java/lang/Math#<init>()V";
+        String[] constructorElements = {"java/lang/Math", "<init>", "()V"};
+        return new Object[][] {
+                {new String[] {"#data0", indexOfLine}, "data0", new String[][] {indexOfElements}},
+                {new String[] {"#data1", "", constructorLine}, "data1", new String[][] {constructorElements}},
+                {new String[] {"#data2", indexOfLine, constructorLine, ""}, "data2", new String[][] {indexOfElements, constructorElements}},
+                {new String[] {"#data3"}, "data3", new String[][] {}}
+        };
+    }
+
+    @Test(dataProvider = "readable")
+    public void testRead(String[] data, String reason, String[][] elements) throws IllegalStateException, IOException {
+        ListANCFilter filter = new ListANCFilter();
+        filter.setParameter(createListFile(data));
+        assertEquals(filter.getAncReason(), reason);
+        for(String[] el : elements) {
+            DataClass dataClass = new DataClass(2,
+                    el[0], "java.base", 0, false);
+            assertTrue(filter.accept(dataClass,
+                    new DataMethodEntryOnly(dataClass, 0, el[1], el[2], "", new String[0], 0)));        }
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class)
+    public void testUninitiated() {
+        new ListANCFilter().accept(new DataClass(0,
+                "java/lang/String", "java.base", 0, false));
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class)
+    public void testReasonUninitiated() {
+        new ListANCFilter().getAncReason();
+    }
+
+    @Test
+    public void testGetFilterName() {
+        assertEquals(new ListANCFilter().getFilterName(), "list");
+    }
+}