Rev 6394: Provides MemoryStack to simplify configuration setup in tests in file:///home/vila/src/bzr/experimental/memory-stack/
Vincent Ladeuil
v.ladeuil+lp at free.fr
Wed Dec 21 14:25:27 UTC 2011
At file:///home/vila/src/bzr/experimental/memory-stack/
------------------------------------------------------------
revno: 6394
revision-id: v.ladeuil+lp at free.fr-20111221142526-pnwau0xnalimujts
parent: pqm at pqm.ubuntu.com-20111221000749-jnw2o645cdjr961f
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: memory-stack
timestamp: Wed 2011-12-21 15:25:26 +0100
message:
Provides MemoryStack to simplify configuration setup in tests
-------------- next part --------------
=== 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 14:25:26 +0000
@@ -22,6 +22,7 @@
import threading
+import testtools
from testtools import matchers
#import bzrlib specific imports here
@@ -3356,61 +3357,66 @@
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())
+ with testtools.ExpectedException(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 +3452,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 +3470,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 +3508,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 +3540,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 00:07:49 +0000
+++ b/doc/en/release-notes/bzr-2.5.txt 2011-12-21 14:25:26 +0000
@@ -142,6 +142,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