commit 5fe893ed4eb29f1518a228223032c28806721e2c Author: Damian Johnson atagar@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)