[tor-commits] [arm/master] Checking pyflakes as part of our tests

atagar at torproject.org atagar at torproject.org
Tue Dec 31 17:48:09 UTC 2013


commit 5fe893ed4eb29f1518a228223032c28806721e2c
Author: Damian Johnson <atagar at torproject.org>
Date:   Tue Dec 31 09:26:55 2013 -0800

    Checking pyflakes as part of our tests
    
    Sadly this isn't something stem is (or should) vend, so it's largely copied
    from its test utils...
    
      https://gitweb.torproject.org/stem.git/blob/HEAD:/test/util.py#l301
    
    Presently we're not taking care of the test configurability but we'll
    definitely soon need it.
---
 run_tests.py                  |   89 +++++++++++++++++++++++++++++++++++++++++
 test/starter/load_settings.py |   31 ++++++++++++++
 2 files changed, 120 insertions(+)

diff --git a/run_tests.py b/run_tests.py
index 894bc09..fb326f8 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -8,10 +8,27 @@ the test coverage we can achieve, but exercising what we can.
 """
 
 import os
+import re
 import unittest
 
+import stem.util.conf
+import stem.util.system
+
 from arm.util import load_settings
 
+CONFIG = stem.util.conf.config_dict("test", {
+  "pep8.ignore": [],
+  "pyflakes.ignore": [],
+})
+
+ARM_BASE = os.path.dirname(__file__)
+
+SRC_PATHS = [os.path.join(ARM_BASE, path) for path in (
+  'arm',
+  'test',
+)]
+
+
 def clean_orphaned_pyc():
   for root, _, files in os.walk(os.path.dirname(__file__)):
     for filename in files:
@@ -26,6 +43,60 @@ def clean_orphaned_pyc():
           os.remove(pyc_path)
 
 
+def get_pyflakes_issues(paths):
+  """
+  Performs static checks via pyflakes.
+
+  :param list paths: paths to search for problems
+
+  :returns: dict of the form ``path => [(line_number, message)...]``
+  """
+
+  pyflakes_ignore = {}
+
+  for line in CONFIG["pyflakes.ignore"]:
+    path, issue = line.split("=>")
+    pyflakes_ignore.setdefault(path.strip(), []).append(issue.strip())
+
+  def is_ignored(path, issue):
+    # Paths in pyflakes_ignore are relative, so we need to check to see if our
+    # path ends with any of them.
+
+    for ignore_path in pyflakes_ignore:
+      if path.endswith(ignore_path) and issue in pyflakes_ignore[ignore_path]:
+        return True
+
+    return False
+
+  # Pyflakes issues are of the form...
+  #
+  #   FILE:LINE: ISSUE
+  #
+  # ... for instance...
+  #
+  #   stem/prereq.py:73: 'long_to_bytes' imported but unused
+  #   stem/control.py:957: undefined name 'entry'
+
+  issues = {}
+
+  for path in paths:
+    pyflakes_output = stem.util.system.call(
+      "pyflakes %s" % path,
+      ignore_exit_status = True,
+    )
+
+    for line in pyflakes_output:
+      line_match = re.match("^(.*):(\d+): (.*)$", line)
+
+      if line_match:
+        path, line, issue = line_match.groups()
+
+        if not is_ignored(path, issue):
+          issues.setdefault(path, []).append((int(line), issue))
+
+  return issues
+
+
 def main():
   load_settings()
 
@@ -35,6 +106,24 @@ def main():
   test_runner = unittest.TextTestRunner()
   test_runner.run(tests)
 
+  print
+
+  static_check_issues = {}
+
+  if stem.util.system.is_available("pyflakes"):
+    static_check_issues.update(get_pyflakes_issues(SRC_PATHS))
+
+  if static_check_issues:
+    print "STATIC CHECKS"
+
+    for file_path in static_check_issues:
+      print "* %s" % file_path
+
+      for line_number, msg in static_check_issues[file_path]:
+        line_count = "%-4s" % line_number
+        print "  line %s - %s" % (line_count, msg)
+
+      print
 
 if __name__ == '__main__':
   main()
diff --git a/test/starter/load_settings.py b/test/starter/load_settings.py
new file mode 100644
index 0000000..5c9cfd8
--- /dev/null
+++ b/test/starter/load_settings.py
@@ -0,0 +1,31 @@
+import io
+import unittest
+
+from mock import patch
+
+from arm.starter import _load_settings
+
+
+class TestLoadSettings(unittest.TestCase):
+  def test_we_can_load_the_settings(self):
+    config = _load_settings(self.id())
+    self.assertEqual(config.get('settings_loaded'), 'true')
+
+  @patch('stem.util.conf.open', create = True)
+  def test_when_file_doesnt_exist(self, open_mock):
+    open_mock.side_effect = IOError("No such file or directory")
+
+    try:
+      _load_settings(self.id())
+      self.fail("We didn't raise an exception for a missing settings.cfg")
+    except ValueError as exc:
+      self.assertTrue("Unable to load arm's internal configuration" in str(exc))
+
+  @patch('stem.util.conf.open', create = True)
+  def test_that_repeated_calls_are_ignored(self, open_mock):
+    open_mock.return_value = io.BytesIO("settings_loaded true")
+
+    _load_settings(self.id())
+    _load_settings(self.id())
+    _load_settings(self.id())
+    self.assertEqual(1, open_mock.call_count)





More information about the tor-commits mailing list