Rev 2: Start of refactoring to make sure commands are called with appropriate parameters. in http://bzr.arbash-meinel.com/branches/bzr/extra/moin_graphviz

John Arbash Meinel john at arbash-meinel.com
Tue Apr 3 00:07:59 BST 2007


At http://bzr.arbash-meinel.com/branches/bzr/extra/moin_graphviz

------------------------------------------------------------
revno: 2
revision-id: john at arbash-meinel.com-20070402230756-hdm8969fymtxsn2z
parent: john at arbash-meinel.com-20070402222501-lxijfcuog91nqbj2
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: moin_graphviz
timestamp: Mon 2007-04-02 18:07:56 -0500
message:
  Start of refactoring to make sure commands are called with appropriate parameters.
added:
  test_graphviz.py               test_graphviz.py-20070402223544-kez8ooyxqh1wx5w0-1
modified:
  graphviz.py                    graphviz.py-20070402222439-3qloodrjtlsuaz1o-1
-------------- next part --------------
=== added file 'test_graphviz.py'
--- a/test_graphviz.py	1970-01-01 00:00:00 +0000
+++ b/test_graphviz.py	2007-04-02 23:07:56 +0000
@@ -0,0 +1,130 @@
+"""A test suite for the graphviz Moin plugin."""
+
+import unittest
+
+import graphviz
+
+
+class MockRequest(object):
+    """A Request object which conforms to what we need."""
+
+    def __init__(self):
+        self._actions = []
+        self.form = None
+
+    def write(self, txt):
+        self._actions.append(('write', txt))
+
+    def getText(self, txt): # Wrapper for i18n
+        self._actions.append(('getText', txt))
+        return txt
+
+
+class MockFormatter(object):
+    """A formatter which implements the basic functionality."""
+
+    def __init__(self):
+        self._actions = []
+
+    def escapedText(self, txt):
+        self._actions(('escapedText', txt))
+
+    def rawHTML(self, txt):
+        self._actions(('rawHTML', txt))
+
+
+class ParserWithCommand(graphviz.Parser):
+    """A class which overrides self._dot_command for easier testing."""
+
+    _command = ['dot', '-Tpng']
+
+    def _dot_command(self):
+        return self._command
+
+
+class TestCaseWithParser(unittest.TestCase):
+    """A simple class with convenience functions."""
+
+    def get_formatter(self):
+        return MockFormatter()
+
+    def get_request(self):
+        return MockRequest()
+
+    def get_parser(self, raw='', request=None):
+        if request is None:
+            request = self.get_request()
+        return graphviz.Parser(raw, request)
+
+
+class TestParser(TestCaseWithParser):
+
+    def test_can_get_parser(self):
+        self.get_parser()
+
+    def test__dot_command(self):
+        """We should be able to configure a custom dot program.
+
+        But we also don't want commands to accidentally have a side effect
+        of changing which dot program we use.
+        """
+        # Test that we can force a different dot command
+        orig_val = graphviz.Parser._dot
+        try:
+            graphviz.Parser._dot = '/path/to/dot'
+            p = self.get_parser()
+            self.assertEqual(['/path/to/dot', '-Tpng'], p._dot_command())
+
+            graphviz.Parser._dot = '/alt/path/to/dot'
+            self.assertEqual(['/alt/path/to/dot', '-Tpng'], p._dot_command())
+
+            # But it shouldn't be effected by Parser._dot
+            p._dot = '/hacked/dot/path'
+            self.assertEqual(['/alt/path/to/dot', '-Tpng'], p._dot_command())
+        finally:
+            graphviz.Parser._dot = orig_val
+
+    def test_ParserWithCommand(self):
+        p = ParserWithCommand('', self.get_request())
+        self.assertEqual(['dot', '-Tpng'], p._dot_command())
+        # You can force the command by setting p._command
+        p._command = ['custom', 'command']
+        self.assertEqual(['custom', 'command'], p._dot_command())
+
+    def test__spawn_dot_args(self):
+        """Test that we spawn a subprocess"""
+        orig_val = graphviz.Parser._dot
+        try:
+            graphviz.Parser._dot = 'echo'
+            p = self.get_parser()
+            sub = p._spawn_dot()
+            # Check the arguments
+            out, err = sub.communicate()
+            self.assertEqual('-Tpng\n', out)
+        finally:
+            graphviz.Parser._dot = orig_val
+
+    def test__spawn_dot_pipe(self):
+        """Test that when spawning we properly pipe everything"""
+        p = ParserWithCommand('', self.get_request())
+        p._command = ['cat']
+
+        sub = p._spawn_dot()
+        out, err = sub.communicate()
+        self.assertEqual('', out)
+        self.assertEqual(None, err)
+
+        sub = p._spawn_dot()
+        out, err = sub.communicate('some text\n')
+        self.assertEqual('some text\n', out)
+        self.assertEqual(None, err)
+
+    def test__spawn_bad_command(self):
+        """Check the error when 'dot' doesn't exist."""
+        orig_val = graphviz.Parser._dot
+        try:
+            graphviz.Parser._dot = '/this/command/doesnt/exist'
+            p = self.get_parser()
+            self.assertRaises(OSError, p._spawn_dot)
+        finally:
+            graphviz.Parser._dot = orig_val

=== modified file 'graphviz.py'
--- a/graphviz.py	2007-04-02 22:25:01 +0000
+++ b/graphviz.py	2007-04-02 23:07:56 +0000
@@ -1,35 +1,66 @@
 # -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - Plain Text Parser
+"""MoinMoin - Parse graphviz statements into PNG images.
 
-    @copyright: 2006, Jeff Bailey <jbailey at raspberryginger.com>
-    @license: GNU GPL, see COPYING for details.
+ at copyright: 2006, Jeff Bailey <jbailey at raspberryginger.com>
+            2007, John Arbash Meinel <john at arbash-meinel.com>
+ at license: GNU GPL, see COPYING for details.
 """
 
 from subprocess import Popen, PIPE
 from base64 import b64encode
 
+
 Dependencies = []
 
-class Parser:
-    """
-        Parse the output through graphviz to generate output.
-    """
-
+
+class Parser(object):
+    """Parse the output through graphviz to generate output."""
+
+    _dot = "/usr/bin/dot"
     extensions = 'graphviz'
     Dependencies = []
-    
+
     def __init__(self, raw, request, **kw):
         self.raw = raw
         self.request = request
         self.form = request.form
+        # Used for i18n
         self._ = request.getText
 
+    def _create_png(self):
+        """Create the PNG image from the raw text."""
+        fd = self._spawn_dot()
+        (myout, myerr) = fd.communicate(self.raw)
+        return myout
+
+    def _dot_command(self):
+        return [Parser._dot, "-Tpng"]
+
+    def _spawn_dot(self):
+        return Popen(self._dot_command(), stdin=PIPE, stdout=PIPE)
+
     def format(self, formatter):
         """ Send the text. """
-        fd = Popen(["/usr/bin/dot","-Tpng"], stdin=PIPE, stdout=PIPE)
-        (myout, myerr) = fd.communicate(self.raw)
+        myout = self._create_png()
         encoded = b64encode(myout)
         self.request.write('<img src="data:image/png;base64,')
         self.request.write(encoded)
         self.request.write('" alt="Graphviz Image" />')
+
+
+def test_suite():
+    import unittest
+    import test_graphviz
+
+    return unittest.TestLoader().loadTestsFromModule(test_graphviz)
+
+
+def _test():
+    import unittest
+
+    runner = unittest.TextTestRunner()
+    runner.run(test_suite())
+
+
+if __name__ == '__main__':
+    _test()



More information about the bazaar-commits mailing list