Rev 6397: (vila) Provides config.MemoryStack to simplify configuration setup in tests in file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/

Patch Queue Manager pqm at pqm.ubuntu.com
Wed Dec 21 17:42:35 UTC 2011


At file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 6397 [merge]
revision-id: pqm at pqm.ubuntu.com-20111221174234-06h0xlw6qqb03al2
parent: pqm at pqm.ubuntu.com-20111221165239-5jak0hiyb0857m74
parent: v.ladeuil+lp at free.fr-20111221151821-gdtgtnk1ppd3r09w
committer: Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2011-12-21 17:42:34 +0000
message:
  (vila) Provides config.MemoryStack to simplify configuration setup in tests
   (Vincent Ladeuil)
modified:
  bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
  bzrlib/tests/test_bundle.py    test.py-20050630184834-092aa401ab9f039c
  bzrlib/tests/test_commit.py    test_commit.py-20050914060732-279f057f8c295434
  bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
  bzrlib/tests/test_gpg.py       testgpg.py-20051017042228-9276cd40a784c93c
  bzrlib/tests/test_smtp_connection.py test_smtp_connection-20070618204509-wuyxc0r0ztrecv7e-1
  doc/developers/configuration.txt configuration.txt-20110408142435-korjxxnskvq44sta-1
  doc/en/release-notes/bzr-2.5.txt bzr2.5.txt-20110708125756-587p0hpw7oke4h05-1
=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py	2011-12-20 18:47:35 +0000
+++ b/bzrlib/config.py	2011-12-21 14:25:26 +0000
@@ -3495,6 +3495,29 @@
         return []
 
 
+class MemoryStack(Stack):
+    """A configuration stack defined from a string.
+
+    This is mainly intended for tests and requires no disk resources.
+    """
+
+    def __init__(self, content=None):
+        """Create an in-memory stack from a given content.
+
+        It uses a single store based on configobj and support reading and
+        writing options.
+
+        :param content: The initial content of the store. If None, the store is
+            not loaded and ``_load_from_string`` can and should be used if
+            needed.
+        """
+        store = IniFileStore()
+        if content is not None:
+            store._load_from_string(content)
+        super(MemoryStack, self).__init__(
+            [store.get_sections], store)
+
+
 class _CompatibleStack(Stack):
     """Place holder for compatibility with previous design.
 

=== modified file 'bzrlib/tests/test_bundle.py'
--- a/bzrlib/tests/test_bundle.py	2011-12-09 13:00:30 +0000
+++ b/bzrlib/tests/test_bundle.py	2011-12-21 14:25:26 +0000
@@ -1418,7 +1418,7 @@
             from bzrlib.testament import Testament
             # monkey patch gpg signing mechanism
             bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
-            new_config = test_commit.MustSignConfig(branch)
+            new_config = test_commit.MustSignConfig()
             commit.Commit(config_stack=new_config).commit(message="base",
                                                     allow_pointless=True,
                                                     rev_id='B',

=== modified file 'bzrlib/tests/test_commit.py'
--- a/bzrlib/tests/test_commit.py	2011-12-14 10:28:24 +0000
+++ b/bzrlib/tests/test_commit.py	2011-12-21 14:25:26 +0000
@@ -44,27 +44,13 @@
 
 # TODO: Test commit with some added, and added-but-missing files
 
-class MustSignConfig(config.Stack):
+class MustSignConfig(config.MemoryStack):
 
-    def __init__(self, branch):
-        store = config.IniFileStore()
-        store._load_from_string('''
+    def __init__(self):
+        super(MustSignConfig, self).__init__('''
 gpg_signing_command=cat -
 create_signatures=always
 ''')
-        super(MustSignConfig, self).__init__([store.get_sections])
-        # FIXME: Strictly speaking we should fallback to the no-name section in
-        # branch.conf but no tests need that so far -- vila 2011-12-14
-
-
-class BranchWithHooks(config.Stack):
-
-    def __init__(self, branch):
-        store = config.IniFileStore()
-        store._load_from_string('post_commit=bzrlib.ahook bzrlib.ahook')
-        super(BranchWithHooks, self).__init__([store.get_sections])
-        # FIXME: Strictly speaking we should fallback to the no-name section in
-        # branch.conf but no tests need that so far -- vila 2011-12-14
 
 
 class CapturingReporter(NullCommitReporter):
@@ -439,9 +425,13 @@
             from bzrlib.testament import Testament
             # monkey patch gpg signing mechanism
             bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
-            commit.Commit(config_stack=MustSignConfig(branch)
-                          ).commit(message="base", allow_pointless=True,
-                                   rev_id='B', working_tree=wt)
+            conf = config.MemoryStack('''
+gpg_signing_command=cat -
+create_signatures=always
+''')
+            commit.Commit(config_stack=conf).commit(
+                message="base", allow_pointless=True, rev_id='B',
+                working_tree=wt)
             def sign(text):
                 return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
             self.assertEqual(sign(Testament.from_revision(branch.repository,
@@ -461,9 +451,12 @@
         try:
             # monkey patch gpg signing mechanism
             bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
-            config = MustSignConfig(branch)
+            conf = config.MemoryStack('''
+gpg_signing_command=cat -
+create_signatures=always
+''')
             self.assertRaises(SigningFailed,
-                              commit.Commit(config_stack=config).commit,
+                              commit.Commit(config_stack=conf).commit,
                               message="base",
                               allow_pointless=True,
                               rev_id='B',
@@ -483,11 +476,10 @@
             calls.append('called')
         bzrlib.ahook = called
         try:
-            config = BranchWithHooks(branch)
-            commit.Commit(config_stack=config).commit(
-                            message = "base",
-                            allow_pointless=True,
-                            rev_id='A', working_tree = wt)
+            conf = config.MemoryStack('post_commit=bzrlib.ahook bzrlib.ahook')
+            commit.Commit(config_stack=conf).commit(
+                message = "base", allow_pointless=True, rev_id='A',
+                working_tree = wt)
             self.assertEqual(['called', 'called'], calls)
         finally:
             del bzrlib.ahook

=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py	2011-12-16 16:49:57 +0000
+++ b/bzrlib/tests/test_config.py	2011-12-21 15:18:21 +0000
@@ -22,6 +22,7 @@
 import threading
 
 
+import testtools
 from testtools import matchers
 
 #import bzrlib specific imports here
@@ -3356,61 +3357,65 @@
         self.assertLength(0, sections)
 
 
-class TestStackGet(tests.TestCase):
-
-    # FIXME: This should be parametrized for all known Stack or dedicated
-    # paramerized tests created to avoid bloating -- vila 2011-03-31
-
-    def overrideOptionRegistry(self):
+class TestBaseStackGet(tests.TestCase):
+
+    def setUp(self):
+        super(TestBaseStackGet, self).setUp()
         self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
-    def test_single_config_get(self):
-        conf = dict(foo='bar')
-        conf_stack = config.Stack([conf])
-        self.assertEquals('bar', conf_stack.get('foo'))
+    def test_get_first_definition(self):
+        store1 = config.IniFileStore()
+        store1._load_from_string('foo=bar')
+        store2 = config.IniFileStore()
+        store2._load_from_string('foo=baz')
+        conf = config.Stack([store1.get_sections, store2.get_sections])
+        self.assertEquals('bar', conf.get('foo'))
 
     def test_get_with_registered_default_value(self):
-        conf_stack = config.Stack([dict()])
-        opt = config.Option('foo', default='bar')
-        self.overrideOptionRegistry()
-        config.option_registry.register('foo', opt)
+        config.option_registry.register(config.Option('foo', default='bar'))
+        conf_stack = config.Stack([])
         self.assertEquals('bar', conf_stack.get('foo'))
 
     def test_get_without_registered_default_value(self):
-        conf_stack = config.Stack([dict()])
-        opt = config.Option('foo')
-        self.overrideOptionRegistry()
-        config.option_registry.register('foo', opt)
+        config.option_registry.register(config.Option('foo'))
+        conf_stack = config.Stack([])
         self.assertEquals(None, conf_stack.get('foo'))
 
     def test_get_without_default_value_for_not_registered(self):
-        conf_stack = config.Stack([dict()])
-        opt = config.Option('foo')
-        self.overrideOptionRegistry()
+        conf_stack = config.Stack([])
         self.assertEquals(None, conf_stack.get('foo'))
 
-    def test_get_first_definition(self):
-        conf1 = dict(foo='bar')
-        conf2 = dict(foo='baz')
-        conf_stack = config.Stack([conf1, conf2])
-        self.assertEquals('bar', conf_stack.get('foo'))
-
-    def test_get_embedded_definition(self):
-        conf1 = dict(yy='12')
-        conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
-        conf_stack = config.Stack([conf1, conf2])
-        self.assertEquals('baz', conf_stack.get('foo'))
-
     def test_get_for_empty_section_callable(self):
         conf_stack = config.Stack([lambda : []])
         self.assertEquals(None, conf_stack.get('foo'))
 
     def test_get_for_broken_callable(self):
         # Trying to use and invalid callable raises an exception on first use
-        conf_stack = config.Stack([lambda : object()])
+        conf_stack = config.Stack([object])
         self.assertRaises(TypeError, conf_stack.get, 'foo')
 
 
+class TestMemoryStack(tests.TestCase):
+
+    def test_get(self):
+        conf = config.MemoryStack('foo=bar')
+        self.assertEquals('bar', conf.get('foo'))
+
+    def test_set(self):
+        conf = config.MemoryStack('foo=bar')
+        conf.set('foo', 'baz')
+        self.assertEquals('baz', conf.get('foo'))
+
+    def test_no_content(self):
+        conf = config.MemoryStack()
+        # No content means no loading
+        self.assertFalse(conf.store.is_loaded())
+        self.assertRaises(NotImplementedError, conf.get, 'foo')
+        # But a content can still be provided
+        conf.store._load_from_string('foo=bar')
+        self.assertEquals('bar', conf.get('foo'))
+
+
 class TestStackWithTransport(tests.TestCaseWithTransport):
 
     scenarios = [(key, {'get_stack': builder}) for key, builder
@@ -3446,17 +3451,15 @@
         self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
 
 
-class TestStackGetWithConverter(tests.TestCaseWithTransport):
+class TestStackGetWithConverter(tests.TestCase):
 
     def setUp(self):
         super(TestStackGetWithConverter, self).setUp()
         self.overrideAttr(config, 'option_registry', config.OptionRegistry())
         self.registry = config.option_registry
-        # We just want a simple stack with a simple store so we can inject
-        # whatever content the tests need without caring about what section
-        # names are valid for a given store/stack.
-        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
-        self.conf = config.Stack([store.get_sections], store)
+
+    def get_conf(self, content=None):
+        return config.MemoryStack(content)
 
     def register_bool_option(self, name, default=None, default_from_env=None):
         b = config.Option(name, help='A boolean.',
@@ -3466,29 +3469,34 @@
 
     def test_get_default_bool_None(self):
         self.register_bool_option('foo')
-        self.assertEquals(None, self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals(None, conf.get('foo'))
 
     def test_get_default_bool_True(self):
         self.register_bool_option('foo', u'True')
-        self.assertEquals(True, self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals(True, conf.get('foo'))
 
     def test_get_default_bool_False(self):
         self.register_bool_option('foo', False)
-        self.assertEquals(False, self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals(False, conf.get('foo'))
 
     def test_get_default_bool_False_as_string(self):
         self.register_bool_option('foo', u'False')
-        self.assertEquals(False, self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals(False, conf.get('foo'))
 
     def test_get_default_bool_from_env_converted(self):
         self.register_bool_option('foo', u'True', default_from_env=['FOO'])
         self.overrideEnv('FOO', 'False')
-        self.assertEquals(False, self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals(False, conf.get('foo'))
 
     def test_get_default_bool_when_conversion_fails(self):
         self.register_bool_option('foo', default='True')
-        self.conf.store._load_from_string('foo=invalid boolean')
-        self.assertEquals(True, self.conf.get('foo'))
+        conf = self.get_conf('foo=invalid boolean')
+        self.assertEquals(True, conf.get('foo'))
 
     def register_integer_option(self, name,
                                 default=None, default_from_env=None):
@@ -3499,25 +3507,29 @@
 
     def test_get_default_integer_None(self):
         self.register_integer_option('foo')
-        self.assertEquals(None, self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals(None, conf.get('foo'))
 
     def test_get_default_integer(self):
         self.register_integer_option('foo', 42)
-        self.assertEquals(42, self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals(42, conf.get('foo'))
 
     def test_get_default_integer_as_string(self):
         self.register_integer_option('foo', u'42')
-        self.assertEquals(42, self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals(42, conf.get('foo'))
 
     def test_get_default_integer_from_env(self):
         self.register_integer_option('foo', default_from_env=['FOO'])
         self.overrideEnv('FOO', '18')
-        self.assertEquals(18, self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals(18, conf.get('foo'))
 
     def test_get_default_integer_when_conversion_fails(self):
         self.register_integer_option('foo', default='12')
-        self.conf.store._load_from_string('foo=invalid integer')
-        self.assertEquals(12, self.conf.get('foo'))
+        conf = self.get_conf('foo=invalid integer')
+        self.assertEquals(12, conf.get('foo'))
 
     def register_list_option(self, name, default=None, default_from_env=None):
         l = config.Option(name, help='A list.',
@@ -3527,36 +3539,39 @@
 
     def test_get_default_list_None(self):
         self.register_list_option('foo')
-        self.assertEquals(None, self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals(None, conf.get('foo'))
 
     def test_get_default_list_empty(self):
         self.register_list_option('foo', '')
-        self.assertEquals([], self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals([], conf.get('foo'))
 
     def test_get_default_list_from_env(self):
         self.register_list_option('foo', default_from_env=['FOO'])
         self.overrideEnv('FOO', '')
-        self.assertEquals([], self.conf.get('foo'))
+        conf = self.get_conf('')
+        self.assertEquals([], conf.get('foo'))
 
     def test_get_with_list_converter_no_item(self):
         self.register_list_option('foo', None)
-        self.conf.store._load_from_string('foo=,')
-        self.assertEquals([], self.conf.get('foo'))
+        conf = self.get_conf('foo=,')
+        self.assertEquals([], conf.get('foo'))
 
     def test_get_with_list_converter_many_items(self):
         self.register_list_option('foo', None)
-        self.conf.store._load_from_string('foo=m,o,r,e')
-        self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
+        conf = self.get_conf('foo=m,o,r,e')
+        self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
 
     def test_get_with_list_converter_embedded_spaces_many_items(self):
         self.register_list_option('foo', None)
-        self.conf.store._load_from_string('foo=" bar", "baz "')
-        self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
+        conf = self.get_conf('foo=" bar", "baz "')
+        self.assertEquals([' bar', 'baz '], conf.get('foo'))
 
     def test_get_with_list_converter_stripped_spaces_many_items(self):
         self.register_list_option('foo', None)
-        self.conf.store._load_from_string('foo= bar ,  baz ')
-        self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
+        conf = self.get_conf('foo= bar ,  baz ')
+        self.assertEquals(['bar', 'baz'], conf.get('foo'))
 
 
 class TestIterOptionRefs(tests.TestCase):

=== modified file 'bzrlib/tests/test_gpg.py'
--- a/bzrlib/tests/test_gpg.py	2011-12-15 08:23:05 +0000
+++ b/bzrlib/tests/test_gpg.py	2011-12-21 14:25:26 +0000
@@ -34,16 +34,14 @@
     )
 
 
-class FakeConfig(config.Stack):
+class FakeConfig(config.MemoryStack):
 
     def __init__(self, content=None):
-        store = config.IniFileStore()
         if content is None:
             content = '''
 gpg_signing_key=amy at example.com
 gpg_signing_command=false'''
-        store._load_from_string(content)
-        super(FakeConfig, self).__init__([store.get_sections])
+        super(FakeConfig, self).__init__(content)
 
 
 class TestCommandLine(tests.TestCase):
@@ -118,6 +116,7 @@
         self.assertRaises(errors.BzrBadParameterUnicode,
                           self.assertProduces, u'foo')
 
+
 class TestVerify(TestCase):
 
     def import_keys(self):

=== modified file 'bzrlib/tests/test_smtp_connection.py'
--- a/bzrlib/tests/test_smtp_connection.py	2011-12-16 16:40:10 +0000
+++ b/bzrlib/tests/test_smtp_connection.py	2011-12-21 14:25:26 +0000
@@ -87,18 +87,10 @@
         self._calls.append(('login', user, password))
 
 
-class StringStack(config.Stack):
-
-    def __init__(self, text):
-        store = config.IniFileStore()
-        store._load_from_string(text)
-        super(StringStack, self).__init__([store.get_sections])
-
-
 class TestSMTPConnection(tests.TestCaseInTempDir):
 
     def get_connection(self, text, smtp_factory=None):
-        my_config = StringStack(text)
+        my_config = config.MemoryStack(text)
         return smtp_connection.SMTPConnection(
             my_config, _smtp_factory=smtp_factory)
 
@@ -109,13 +101,13 @@
         self.assertEqual(None, conn._smtp_password)
 
     def test_smtp_server(self):
-        conn = self.get_connection('[DEFAULT]\nsmtp_server=host:10\n')
+        conn = self.get_connection('smtp_server=host:10')
         self.assertEqual('host:10', conn._smtp_server)
 
     def test_missing_server(self):
         conn = self.get_connection('', smtp_factory=connection_refuser)
         self.assertRaises(errors.DefaultSMTPConnectionRefused, conn._connect)
-        conn = self.get_connection('[DEFAULT]\nsmtp_server=smtp.example.com\n',
+        conn = self.get_connection('smtp_server=smtp.example.com',
                                    smtp_factory=connection_refuser)
         self.assertRaises(errors.SMTPConnectionRefused, conn._connect)
 
@@ -123,14 +115,14 @@
         conn = self.get_connection('')
         self.assertIs(None, conn._smtp_username)
 
-        conn = self.get_connection('[DEFAULT]\nsmtp_username=joebody\n')
+        conn = self.get_connection('smtp_username=joebody')
         self.assertEqual(u'joebody', conn._smtp_username)
 
     def test_smtp_password_from_config(self):
         conn = self.get_connection('')
         self.assertIs(None, conn._smtp_password)
 
-        conn = self.get_connection('[DEFAULT]\nsmtp_password=mypass\n')
+        conn = self.get_connection('smtp_password=mypass')
         self.assertEqual(u'mypass', conn._smtp_password)
 
     def test_smtp_password_from_user(self):
@@ -266,14 +258,17 @@
         msg['From'] = '"J. Random Developer" <jrandom at example.com>'
         self.assertRaises(
             errors.NoDestinationAddress,
-            smtp_connection.SMTPConnection(StringStack("")).send_email, msg)
+            smtp_connection.SMTPConnection(config.MemoryStack("")
+                                           ).send_email, msg)
 
         msg = email_message.EmailMessage('from at from.com', '', 'subject')
         self.assertRaises(
             errors.NoDestinationAddress,
-            smtp_connection.SMTPConnection(StringStack("")).send_email, msg)
+            smtp_connection.SMTPConnection(config.MemoryStack("")
+                                           ).send_email, msg)
 
         msg = email_message.EmailMessage('from at from.com', [], 'subject')
         self.assertRaises(
             errors.NoDestinationAddress,
-            smtp_connection.SMTPConnection(StringStack("")).send_email, msg)
+            smtp_connection.SMTPConnection(config.MemoryStack("")
+                                           ).send_email, msg)

=== modified file 'doc/developers/configuration.txt'
--- a/doc/developers/configuration.txt	2011-12-14 16:30:09 +0000
+++ b/doc/developers/configuration.txt	2011-12-21 14:25:26 +0000
@@ -128,8 +128,6 @@
 Adding a new store
 ------------------
 
-
-
 The following stores are used by ``bzr`` in ways that illustrate various
 uses of sections.
 
@@ -327,3 +325,11 @@
 ``Stores`` can be used to build them but shouldn't be used otherwise, ditto
 for sections. Again, the associated tests could and should be used against the
 created stacks.
+
+Also note that ``MemoryStack`` can be used without any disk resources which
+allows for simpler and faster tests. A common pattern is to accept a
+``config`` parameter related to a given feature and test it with predefined
+configurations without involving the whole
+stack. ``bzrlib.tests.test_commit``, ``bzrlib.tests.test_gpg`` and
+``bzrlib.tests.test_smtp_connection`` are good examples.
+

=== modified file 'doc/en/release-notes/bzr-2.5.txt'
--- a/doc/en/release-notes/bzr-2.5.txt	2011-12-21 16:52:39 +0000
+++ b/doc/en/release-notes/bzr-2.5.txt	2011-12-21 17:42:34 +0000
@@ -150,6 +150,10 @@
 * New matcher ``ContainsNoVfsCalls`` which filters a list of HPSS
   calls for VFS requests. (Jelmer Vernooij)
 
+* New ``MemoryStack`` class allows for diskless tests and locally injected
+  configuration stacks.  Lower level tests for predefined set of options can
+  be written without setting up configuration files. (Vincent Ladeuil)
+
 
 bzr 2.5b4
 #########




More information about the bazaar-commits mailing list