Browse Source

Suppress module, no tests ready.

avkonst 11 years ago
parent
commit
5f5339673b

+ 2 - 0
mainline/core/db/loader.py

@@ -694,6 +694,8 @@ class Loader(object):
         self.db = core.db.sqlite.Database()
         if os.path.exists(dbfile):
             logging.warn("Removing existing file: " + dbfile)
+            # TODO can reuse existing db file to speed up the processing?
+            # TODO add option to choose to remove or to overwrite?
             os.unlink(dbfile)
         if previous_db != None and os.path.exists(previous_db) == False:
             raise core.api.ExitError(None, "Database file '" + previous_db + "'  does not exist")

+ 6 - 4
mainline/core/db/sqlite.py

@@ -23,6 +23,7 @@ import os.path
 import logging
 import itertools 
 import shutil
+import traceback
 
 class Database(object):
     
@@ -122,7 +123,7 @@ class Database(object):
             sql = "SELECT __columns__.name AS column_name, __tables__.name AS table_name, __columns__.id AS column_id FROM __columns__, __tables__ WHERE (__columns__.confirmed = 0 AND __columns__.table_id = __tables__.id)"
             db_loader.log(sql)
             for column in db_loader.conn.execute(sql).fetchall():
-                logging.warn("New database file inherits useless column: '" + column['table_name'] + "'.'" + column['column_name'] + "'")
+                logging.info("New database file inherits useless column: '" + column['table_name'] + "'.'" + column['column_name'] + "'")
                 sql = "DELETE FROM __columns__ WHERE id = '" + str(column['column_id']) + "'"
                 db_loader.log(sql)
                 db_loader.conn.execute(sql)
@@ -671,6 +672,7 @@ class Database(object):
         return data
 
     def log(self, sql):
-        #import traceback
-        #traceback.print_stack()
-        logging.debug("[" + str(self.id) + "] Executing query: " + sql)
+        if logging.getLogger().getEffectiveLevel() <= logging.DEBUG:
+            logging.debug("[" + str(self.id) + "] Executing query: " + sql)
+            traceback.print_stack()
+        

+ 1 - 2
mainline/core/dir.py

@@ -101,7 +101,7 @@ class DirectoryReader():
                             f.close()
                             checksum = binascii.crc32(text) & 0xffffffff # to match python 3
     
-                            (data, is_updated) = plugin.get_plugin_loader().get_database_loader().create_file_data(full_path, checksum, text)
+                            (data, is_updated) = plugin.get_plugin_loader().get_database_loader().create_file_data(norm_path, checksum, text)
                             procerrors = parser.process(plugin, data, is_updated)
                             if plugin.is_proctime_enabled == True:
                                 data.set_data('std.general', 'proctime', time.time() - ts)
@@ -114,7 +114,6 @@ class DirectoryReader():
                             exit_code += procerrors
                 else:
                     logging.info("Excluding: " + norm_path)
-                    logging.debug("-" * 60)
             return exit_code
         
         total_errors = run_recursively(plugin, directory)

+ 0 - 2
mainline/ext/std/code/cpp.py

@@ -172,8 +172,6 @@ class CppCodeParser(object):
             # Comment
             if text[m.start()] == '/':
                 data.add_marker(m.start(), m.end(), data.get_marker_types().COMMENT)
-                if text[m.start():m.end()].startswith("//\n"):
-                    print text[m.start():m.end()]
             
             # String
             elif text[m.start()] == '"' or text[m.start()] == '\'':

+ 0 - 2
mainline/ext/std/code/cs.py

@@ -188,8 +188,6 @@ class CsCodeParser(object):
             # Comment
             if text[m.start()] == '/':
                 data.add_marker(m.start(), m.end(), data.get_marker_types().COMMENT)
-                if text[m.start():m.end()].startswith("//\n"):
-                    print text[m.start():m.end()]
             
             # String
             elif text[m.start()] == '"' or text[m.start()] == '\'':

+ 0 - 2
mainline/ext/std/code/java.py

@@ -164,8 +164,6 @@ class JavaCodeParser(object):
             # Comment
             if text[m.start()] == '/':
                 data.add_marker(m.start(), m.end(), data.get_marker_types().COMMENT)
-                if text[m.start():m.end()].startswith("//\n"):
-                    print text[m.start():m.end()]
             
             # String
             elif text[m.start()] == '"' or text[m.start()] == '\'':

+ 4 - 3
mainline/ext/std/code/test.py

@@ -18,7 +18,7 @@
 #
 
 import core.api
-
+import logging
 
 # used for testing and development purposes
 class Plugin(core.api.Plugin, core.api.Child):
@@ -32,6 +32,7 @@ class Plugin(core.api.Plugin, core.api.Child):
 
         text = data.get_content()
         for region in data.iterate_regions():
-            print "Region:", region.get_name(), region.get_cursor()
+            logging.warn(region.get_name() + " " + region.get_cursor())
             for marker in data.iterate_markers(region_id=region.get_id(), exclude_children = True):
-                print "\tMarker:", data.get_marker_types()().to_str(marker.get_type()), text[marker.get_offset_begin():marker.get_offset_end()]
+                logging.warn("\tMarker: " + data.get_marker_types()().to_str(marker.get_type()) +
+                             " " + text[marker.get_offset_begin():marker.get_offset_end()])

+ 26 - 0
mainline/ext/std/suppress.ini

@@ -0,0 +1,26 @@
+;
+;    Metrix++, Copyright 2009-2013, Metrix++ Project
+;    Link: http://metrixplusplus.sourceforge.net
+;    
+;    This file is a part of Metrix++ Tool.
+;    
+;    Metrix++ 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, version 3 of the License.
+;    
+;    Metrix++ 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 Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+;
+
+[Plugin]
+version: 1.0
+package: std
+module:  suppress
+class:   Plugin
+depends: None
+enabled: True

+ 89 - 0
mainline/ext/std/suppress.py

@@ -0,0 +1,89 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ 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, version 3 of the License.
+#    
+#    Metrix++ 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 Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import core.api
+import core.export.cout
+
+import re
+
+class Plugin(core.api.Plugin, core.api.Child, core.api.IConfigurable):
+    
+    def declare_configuration(self, parser):
+        parser.add_option("--std.suppress.code", "--ssc", action="store_true", default=False,
+                         help="If set (True), suppression markers are collected from comments in code. "
+                              "Suppressions are used by post-processing tools, like limit, to remove false-positive warnings. "
+                              "Suppressions should be in the first comment block of a region (function/class/interface). "
+                              "Format of suppressions: 'metrix++: suppress metric-name'. "
+                              "For example: 'metrix++: suppress std.code.complexity:cyclomatic'. "
+                              " [default: %default]")
+    
+    def configure(self, options):
+        self.is_active = options.__dict__['std.suppress.code']
+        
+    def initialize(self):
+        if self.is_active == True:
+            # trigger version property set
+            core.api.Plugin.initialize(self)
+            namespace = self.get_plugin_loader().get_database_loader().create_namespace(self.get_name(), support_regions = True)
+            namespace.add_field('count', int, non_zero=True)
+            namespace.add_field('list', str)
+            core.api.subscribe_by_parents_interface(core.api.ICode, self, 'callback')
+
+    # suppress pattern
+    pattern = re.compile(r'''metrix[+][+][:][ \t]+suppress[ \t]+([^ \t\r\n\*]+)''')
+
+    def callback(self, parent, data, is_updated):
+        is_updated = is_updated or self.is_updated
+        if is_updated == True:
+            text = data.get_content()
+            for region in data.iterate_regions():
+                count = 0
+                list_text = ""
+                last_comment_end = None
+                for marker in data.iterate_markers(
+                                filter_group = data.get_marker_types().COMMENT,
+                                region_id = region.get_id(),
+                                exclude_children = True):
+                    
+                    if last_comment_end != None and marker.get_offset_begin() > last_comment_end + 2:
+                        # check continues comment blocks
+                        # stop searching, if this comment block is far from the last
+                        break
+                    last_comment_end = marker.get_offset_end()
+                    
+                    matches = self.pattern.findall(text, marker.get_offset_begin(), marker.get_offset_end())
+                    for m in matches:
+                        namespace_name, field = m.split(':')
+                        namespace = self.get_plugin_loader().get_database_loader().get_namespace(namespace_name)
+                        if namespace == None or namespace.get_field_packager(field) == None:
+                            core.export.cout.cout(data.get_path(), region.get_cursor(),
+                                                  core.export.cout.SEVERITY_WARNING,
+                                                  "Suppressed metric '" + namespace_name + "/" + field +
+                                                    "' is not being collected",
+                                                  [("Metric name", namespace_name + "/" + field),
+                                                   ("Region name", region.get_name())])
+                            continue 
+                        count += 1
+                        list_text += "[" + m +"]"
+
+                    if count > 0:
+                        region.set_data(self.get_name(), 'count', count)
+                        region.set_data(self.get_name(), 'list', list_text)
+
+

+ 46 - 0
mainline/tests/general/test_std_suppress.py

@@ -0,0 +1,46 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ 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, version 3 of the License.
+#    
+#    Metrix++ 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 Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+import unittest
+import os
+
+import tests.common
+
+class Test(tests.common.TestCase):
+
+    def test_parser(self):
+        
+        runner = tests.common.ToolRunner('collect', ['--std.code.complexity.cyclomatic'])
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('view')
+        self.assertExec(runner.run())
+        
+        dirs_list = [os.path.join('.', each) for each in os.listdir(self.get_content_paths().cwd)]
+        runner = tests.common.ToolRunner('view', opts_list=['--format=txt'], dirs_list=dirs_list, prefix='files')
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('limit',
+                                         ['--max-limit=std.code.complexity:cyclomatic:0'],
+                                         exit_code=12)
+        self.assertExec(runner.run())
+
+if __name__ == '__main__':
+    unittest.main()

+ 159 - 0
mainline/tests/general/test_std_suppress/sources/test.c

@@ -0,0 +1,159 @@
+/* comment here per global region
+ *
+ *	metrix++:	suppress std.general:size
+ */
+
+
+/*metrix++: suppress	std.general:size*/
+int func()
+{
+	/* comment here */
+}
+
+int func2()
+{
+	/*metrix++: suppress std.general:size*/
+}
+
+/* metrix++: suppress std.general:size */
+int func3()
+{
+	/* metrix++: suppress std.general:size*/
+}
+
+
+/* bla-bla */
+/* metrix++: suppress std.general:size */
+/* bla-bla */
+int func4()
+{
+	/* metrix++: suppress std.general:size*/
+}
+
+/* metrix++: suppress std.general:size */
+struct cl1
+{
+
+};
+
+struct cl2
+{
+	/* metrix++: suppress std.general:size*/
+
+};
+
+// bla-bla
+//metrix++: suppress std.general:size
+// bla-bla
+struct cl3
+{
+
+};
+
+/* bla-bla-bla */
+struct no_suppress_cl
+{
+
+};
+
+/* bla-bla-bla */
+int nu_suppress_func()
+{
+	/* bla-bla-bla */
+}
+
+struct cl2
+{
+	/* metrix++: suppress std.general:size per class */
+
+	/* metrix++: suppress std.general:size per function */
+	int func4()
+	{
+
+	}
+
+	int func_no_suppress_within_struct()
+	{
+
+	}
+};
+
+/* metrix++: suppress invalid:metric */
+struct suppresed_for_invalid_metric
+{
+
+};
+
+/* metrix++: suppress std.general:invlaid_metric */
+struct suppresed_for_invalid_metric
+{
+
+};
+
+/* metrix++: suppress invalid:metric */
+/* metrix++: suppress std.general:size */
+struct suppressed_for_size_and_invalid_metric
+{
+
+};
+
+/* metrix++: suppress invalid:metric */
+/* metrix++: suppress std.general:size */
+/* metrix++: suppress std.code.complexity:cyclomatic */
+int suppressed_for_size_and_complexity_and_invalid_metric()
+{
+	if (1) return;
+}
+
+// metrix++: suppress std.general:size asdas
+// metrix++: suppress std.code.complexity:cyclomatic adsad
+int func7()
+{
+	if (1) return;
+}
+
+// metrix++: suppress std.code.complexity:cyclomatic adsad
+int nu_suppress_for_size()
+{
+	if (1) return;
+}
+
+// metrix++: suppress std.general:size adsad
+int no_suppress_for_cyclomatic_complexity()
+{
+	if (1) return;
+}
+
+// metrix++: suppress std.general:size long-long
+// description why it was suppressed
+// metrix++: suppress std.code.complexity:cyclomatic
+int func8()
+{
+	if (1) return;
+}
+
+/* metrix++: suppress std.general:size long-long */
+/* description why it was suppressed */
+/* metrix++: suppress std.code.complexity:cyclomatic */
+int func9()
+{
+	if (1) return;
+}
+
+// metrix++: suppress std.code.complexity:cyclomatic adsad
+int bad_suppress_for_size()
+{
+	// metrix++: suppress std.general:size
+
+	if (1) return;
+}
+
+/* metrix++: suppress std.general:size long-long
+ * description why it was suppressed
+ * metrix++: suppress std.code.complexity:cyclomatic */
+int func10()
+{
+	if (1) return;
+}
+
+

+ 0 - 3
mainline/tools/collect.py

@@ -18,7 +18,6 @@
 #
 
 
-import logging
 import os.path
 
 import core.loader
@@ -33,8 +32,6 @@ def main(tool_args):
     loader = core.loader.Loader()
     parser =core.cmdparser.MultiOptionParser(usage="Usage: %prog collect [options] -- [path 1] ... [path N]")
     args = loader.load(os.path.join(os.environ['METRIXPLUSPLUS_INSTALL_DIR'], 'ext'), parser, tool_args)
-    logging.debug("Registered plugins:")
-    logging.debug(loader)
     exit_code = loader.run(args)
     loader.unload()
     return exit_code