--- /dev/null
+# -*- coding: utf-8 -*-
+# Run with one of these commands:
+# > OPENERP_ADDONS_PATH='../../addons/trunk' OPENERP_PORT=8069 \
+# OPENERP_DATABASE=yy PYTHONPATH=. python tests/test_ir_sequence.py
+# > OPENERP_ADDONS_PATH='../../addons/trunk' OPENERP_PORT=8069 \
+# OPENERP_DATABASE=yy nosetests tests/test_ir_sequence.py
+# > OPENERP_ADDONS_PATH='../../../addons/trunk' OPENERP_PORT=8069 \
+# OPENERP_DATABASE=yy PYTHONPATH=../:. unit2 test_ir_sequence
+# This assume an existing database.
+import psycopg2
++import psycopg2.errorcodes
+import unittest2
+
+import openerp
+from openerp.tests import common
+
+DB = common.DB
+ADMIN_USER_ID = common.ADMIN_USER_ID
+
+def registry(model):
+ return openerp.modules.registry.RegistryManager.get(DB)[model]
+
+def cursor():
+ return openerp.modules.registry.RegistryManager.get(DB).db.cursor()
+
+
+def drop_sequence(code):
+ cr = cursor()
+ for model in ['ir.sequence', 'ir.sequence.type']:
+ s = registry(model)
+ ids = s.search(cr, ADMIN_USER_ID, [('code', '=', code)])
+ s.unlink(cr, ADMIN_USER_ID, ids)
+ cr.commit()
+ cr.close()
+
+class test_ir_sequence_standard(unittest2.TestCase):
+ """ A few tests for a 'Standard' (i.e. PostgreSQL) sequence. """
+
+ def test_ir_sequence_create(self):
+ """ Try to create a sequence object. """
+ cr = cursor()
+ d = dict(code='test_sequence_type', name='Test sequence type')
+ c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ d = dict(code='test_sequence_type', name='Test sequence')
+ c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ cr.commit()
+ cr.close()
+
+ def test_ir_sequence_search(self):
+ """ Try a search. """
+ cr = cursor()
+ ids = registry('ir.sequence').search(cr, ADMIN_USER_ID, [], {})
+ assert ids
+ cr.commit()
+ cr.close()
+
+ def test_ir_sequence_draw(self):
+ """ Try to draw a number. """
+ cr = cursor()
+ n = registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type', {})
+ assert n
+ cr.commit()
+ cr.close()
+
+ def test_ir_sequence_draw_twice(self):
+ """ Try to draw a number from two transactions. """
+ cr0 = cursor()
+ cr1 = cursor()
+ n0 = registry('ir.sequence').next_by_code(cr0, ADMIN_USER_ID, 'test_sequence_type', {})
+ assert n0
+ n1 = registry('ir.sequence').next_by_code(cr1, ADMIN_USER_ID, 'test_sequence_type', {})
+ assert n1
+ cr0.commit()
+ cr1.commit()
+ cr0.close()
+ cr1.close()
+
+ @classmethod
+ def tearDownClass(cls):
+ drop_sequence('test_sequence_type')
+
+class test_ir_sequence_no_gap(unittest2.TestCase):
+ """ Copy of the previous tests for a 'No gap' sequence. """
+
+ def test_ir_sequence_create_no_gap(self):
+ """ Try to create a sequence object. """
+ cr = cursor()
+ d = dict(code='test_sequence_type_2', name='Test sequence type')
+ c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ d = dict(code='test_sequence_type_2', name='Test sequence',
+ implementation='no_gap')
+ c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ cr.commit()
+ cr.close()
+
+ def test_ir_sequence_draw_no_gap(self):
+ """ Try to draw a number. """
+ cr = cursor()
+ n = registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_2', {})
+ assert n
+ cr.commit()
+ cr.close()
+
+ def test_ir_sequence_draw_twice_no_gap(self):
+ """ Try to draw a number from two transactions.
+ This is expected to not work.
+ """
+ cr0 = cursor()
+ cr1 = cursor()
+ cr1._default_log_exceptions = False # Prevent logging a traceback
- msg_re = '^could not obtain lock on row in relation "ir_sequence"$'
- with self.assertRaisesRegexp(psycopg2.OperationalError, msg_re):
++ with self.assertRaises(psycopg2.OperationalError) as e:
+ n0 = registry('ir.sequence').next_by_code(cr0, ADMIN_USER_ID, 'test_sequence_type_2', {})
+ assert n0
+ n1 = registry('ir.sequence').next_by_code(cr1, ADMIN_USER_ID, 'test_sequence_type_2', {})
++ self.assertEqual(e.exception.pgcode, psycopg2.errorcodes.LOCK_NOT_AVAILABLE, msg="postgresql returned an incorrect errcode")
+ cr0.close()
+ cr1.close()
+
+ @classmethod
+ def tearDownClass(cls):
+ drop_sequence('test_sequence_type_2')
+
+class test_ir_sequence_change_implementation(unittest2.TestCase):
+ """ Create sequence objects and change their ``implementation`` field. """
+
+ def test_ir_sequence_1_create(self):
+ """ Try to create a sequence object. """
+ cr = cursor()
+ d = dict(code='test_sequence_type_3', name='Test sequence type')
+ c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ d = dict(code='test_sequence_type_3', name='Test sequence')
+ c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ d = dict(code='test_sequence_type_4', name='Test sequence type')
+ c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ d = dict(code='test_sequence_type_4', name='Test sequence',
+ implementation='no_gap')
+ c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ cr.commit()
+ cr.close()
+
+ def test_ir_sequence_2_write(self):
+ cr = cursor()
+ ids = registry('ir.sequence').search(cr, ADMIN_USER_ID,
+ [('code', 'in', ['test_sequence_type_3', 'test_sequence_type_4'])], {})
+ registry('ir.sequence').write(cr, ADMIN_USER_ID, ids,
+ {'implementation': 'standard'}, {})
+ registry('ir.sequence').write(cr, ADMIN_USER_ID, ids,
+ {'implementation': 'no_gap'}, {})
+ cr.commit()
+ cr.close()
+
+ def test_ir_sequence_3_unlink(self):
+ cr = cursor()
+ ids = registry('ir.sequence').search(cr, ADMIN_USER_ID,
+ [('code', 'in', ['test_sequence_type_3', 'test_sequence_type_4'])], {})
+ registry('ir.sequence').unlink(cr, ADMIN_USER_ID, ids, {})
+ cr.commit()
+ cr.close()
+
+ @classmethod
+ def tearDownClass(cls):
+ drop_sequence('test_sequence_type_3')
+ drop_sequence('test_sequence_type_4')
+
+class test_ir_sequence_generate(unittest2.TestCase):
+ """ Create sequence objects and generate some values. """
+
+ def test_ir_sequence_create(self):
+ """ Try to create a sequence object. """
+ cr = cursor()
+ d = dict(code='test_sequence_type_5', name='Test sequence type')
+ c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ d = dict(code='test_sequence_type_5', name='Test sequence')
+ c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ cr.commit()
+ cr.close()
+
+ cr = cursor()
+ f = lambda *a: registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_5', {})
+ assert all(str(x) == f() for x in xrange(1,10))
+ cr.commit()
+ cr.close()
+
+ def test_ir_sequence_create_no_gap(self):
+ """ Try to create a sequence object. """
+ cr = cursor()
+ d = dict(code='test_sequence_type_6', name='Test sequence type')
+ c = registry('ir.sequence.type').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ d = dict(code='test_sequence_type_6', name='Test sequence')
+ c = registry('ir.sequence').create(cr, ADMIN_USER_ID, d, {})
+ assert c
+ cr.commit()
+ cr.close()
+
+ cr = cursor()
+ f = lambda *a: registry('ir.sequence').next_by_code(cr, ADMIN_USER_ID, 'test_sequence_type_6', {})
+ assert all(str(x) == f() for x in xrange(1,10))
+ cr.commit()
+ cr.close()
+
+ @classmethod
+ def tearDownClass(cls):
+ drop_sequence('test_sequence_type_5')
+ drop_sequence('test_sequence_type_6')
+
+
+if __name__ == '__main__':
+ unittest2.main()
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+# -*- coding: utf-8 -*-
+import itertools
+import openerp.modules.registry
+import openerp
+
+from openerp.tests import common
+
+
+class CreatorCase(common.TransactionCase):
+ model_name = False
+
+ def __init__(self, *args, **kwargs):
+ super(CreatorCase, self).__init__(*args, **kwargs)
+ self.model = None
+
+ def setUp(self):
+ super(CreatorCase, self).setUp()
+ self.model = self.registry(self.model_name)
+ def make(self, value):
+ id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': value})
+ return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0]
+ def export(self, value, fields=('value',), context=None):
+ record = self.make(value)
+ return self.model._BaseModel__export_row(
+ self.cr, openerp.SUPERUSER_ID, record,
+ [f.split('/') for f in fields],
+ context=context)
+
+class test_boolean_field(CreatorCase):
+ model_name = 'export.boolean'
+
+ def test_true(self):
+ self.assertEqual(
+ self.export(True),
+ [[u'True']])
+ def test_false(self):
+ """ ``False`` value to boolean fields is unique in being exported as a
+ (unicode) string, not a boolean
+ """
+ self.assertEqual(
+ self.export(False),
+ [[u'False']])
+
+class test_integer_field(CreatorCase):
+ model_name = 'export.integer'
+
+ def test_empty(self):
+ self.assertEqual(self.model.search(self.cr, openerp.SUPERUSER_ID, []), [],
+ "Test model should have no records")
+ def test_0(self):
+ self.assertEqual(
+ self.export(0),
+ [[False]])
+
+ def test_basic_value(self):
+ self.assertEqual(
+ self.export(42),
+ [[u'42']])
+
+ def test_negative(self):
+ self.assertEqual(
+ self.export(-32),
+ [[u'-32']])
+
+ def test_huge(self):
+ self.assertEqual(
+ self.export(2**31-1),
+ [[unicode(2**31-1)]])
+
+class test_float_field(CreatorCase):
+ model_name = 'export.float'
+
+ def test_0(self):
+ self.assertEqual(
+ self.export(0.0),
+ [[False]])
+
+ def test_epsilon(self):
+ self.assertEqual(
+ self.export(0.000000000027),
+ [[u'2.7e-11']])
+
+ def test_negative(self):
+ self.assertEqual(
+ self.export(-2.42),
+ [[u'-2.42']])
+
+ def test_positive(self):
+ self.assertEqual(
+ self.export(47.36),
+ [[u'47.36']])
+
+ def test_big(self):
+ self.assertEqual(
+ self.export(87654321.4678),
+ [[u'87654321.4678']])
+
+class test_decimal_field(CreatorCase):
+ model_name = 'export.decimal'
+
+ def test_0(self):
+ self.assertEqual(
+ self.export(0.0),
+ [[False]])
+
+ def test_epsilon(self):
+ """ epsilon gets sliced to 0 due to precision
+ """
+ self.assertEqual(
+ self.export(0.000000000027),
+ [[False]])
+
+ def test_negative(self):
+ self.assertEqual(
+ self.export(-2.42),
+ [[u'-2.42']])
+
+ def test_positive(self):
+ self.assertEqual(
+ self.export(47.36),
+ [[u'47.36']])
+
+ def test_big(self):
+ self.assertEqual(
+ self.export(87654321.4678), [[u'87654321.468']])
+
+class test_string_field(CreatorCase):
+ model_name = 'export.string.bounded'
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(""),
+ [[False]])
+ def test_within_bounds(self):
+ self.assertEqual(
+ self.export("foobar"),
+ [[u"foobar"]])
+ def test_out_of_bounds(self):
+ self.assertEqual(
+ self.export("C for Sinking, "
+ "Java for Drinking, "
+ "Smalltalk for Thinking. "
+ "...and Power to the Penguin!"),
+ [[u"C for Sinking, J"]])
+
+class test_unbound_string_field(CreatorCase):
+ model_name = 'export.string'
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(""),
+ [[False]])
+ def test_small(self):
+ self.assertEqual(
+ self.export("foobar"),
+ [[u"foobar"]])
+ def test_big(self):
+ self.assertEqual(
+ self.export("We flew down weekly to meet with IBM, but they "
+ "thought the way to measure software was the amount "
+ "of code we wrote, when really the better the "
+ "software, the fewer lines of code."),
+ [[u"We flew down weekly to meet with IBM, but they thought the "
+ u"way to measure software was the amount of code we wrote, "
+ u"when really the better the software, the fewer lines of "
+ u"code."]])
+
+class test_text(CreatorCase):
+ model_name = 'export.text'
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(""),
+ [[False]])
+ def test_small(self):
+ self.assertEqual(
+ self.export("foobar"),
+ [[u"foobar"]])
+ def test_big(self):
+ self.assertEqual(
+ self.export("So, `bind' is `let' and monadic programming is"
+ " equivalent to programming in the A-normal form. That"
+ " is indeed all there is to monads"),
+ [[u"So, `bind' is `let' and monadic programming is equivalent to"
+ u" programming in the A-normal form. That is indeed all there"
+ u" is to monads"]])
+
+class test_date(CreatorCase):
+ model_name = 'export.date'
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(False),
+ [[False]])
+ def test_basic(self):
+ self.assertEqual(
+ self.export('2011-11-07'),
+ [[u'2011-11-07']])
+
+class test_datetime(CreatorCase):
+ model_name = 'export.datetime'
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(False),
+ [[False]])
+ def test_basic(self):
+ self.assertEqual(
+ self.export('2011-11-07 21:05:48'),
+ [[u'2011-11-07 21:05:48']])
+ def test_tz(self):
+ """ Export ignores the timezone and always exports to UTC
+
+ .. note:: on the other hand, export uses user lang for name_get
+ """
+ # NOTE: ignores user timezone, always exports to UTC
+ self.assertEqual(
+ self.export('2011-11-07 21:05:48', context={'tz': 'Pacific/Norfolk'}),
+ [[u'2011-11-07 21:05:48']])
+
+class test_selection(CreatorCase):
+ model_name = 'export.selection'
+ translations_fr = [
+ ("Qux", "toto"),
+ ("Bar", "titi"),
+ ("Foo", "tete"),
+ ]
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(False),
+ [[False]])
+
+ def test_value(self):
+ """ selections export the *label* for their value
+ """
+ self.assertEqual(
+ self.export(2),
+ [[u"Bar"]])
+
+ def test_localized_export(self):
+ self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, {
+ 'name': u'Français',
+ 'code': 'fr_FR',
+ 'translatable': True,
+ 'date_format': '%d.%m.%Y',
+ 'decimal_point': ',',
+ 'thousands_sep': ' ',
+ })
+ Translations = self.registry('ir.translation')
+ for source, value in self.translations_fr:
+ Translations.create(self.cr, openerp.SUPERUSER_ID, {
+ 'name': 'export.selection,value',
+ 'lang': 'fr_FR',
+ 'type': 'selection',
+ 'src': source,
+ 'value': value
+ })
+ self.assertEqual(
+ self.export(2, context={'lang': 'fr_FR'}),
+ [[u'Bar']])
+
+class test_selection_function(CreatorCase):
+ model_name = 'export.selection.function'
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(False),
+ [[False]])
+
+ def test_value(self):
+ # FIXME: selection functions export the *value* itself
+ self.assertEqual(
+ self.export(1),
+ [[u'1']])
+ self.assertEqual(
+ self.export(3),
+ [[u'3']])
+ # fucking hell
+ self.assertEqual(
+ self.export(0),
+ [[False]])
+
+class test_m2o(CreatorCase):
+ model_name = 'export.many2one'
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(False),
+ [[False]])
+ def test_basic(self):
+ """ Exported value is the name_get of the related object
+ """
+ integer_id = self.registry('export.integer').create(
+ self.cr, openerp.SUPERUSER_ID, {'value': 42})
+ name = dict(self.registry('export.integer').name_get(
+ self.cr, openerp.SUPERUSER_ID,[integer_id]))[integer_id]
+ self.assertEqual(
+ self.export(integer_id),
+ [[name]])
+ def test_path(self):
+ """ Can recursively export fields of m2o via path
+ """
+ integer_id = self.registry('export.integer').create(
+ self.cr, openerp.SUPERUSER_ID, {'value': 42})
+ self.assertEqual(
+ self.export(integer_id, fields=['value/.id', 'value/value']),
+ [[unicode(integer_id), u'42']])
+ def test_external_id(self):
+ integer_id = self.registry('export.integer').create(
+ self.cr, openerp.SUPERUSER_ID, {'value': 42})
- # __export__.$class.$id
- external_id = u'__export__.export_many2one_%d' % integer_id
++ # Expecting the m2o target model name in the external id,
++ # not this model's name
++ external_id = u'__export__.export_integer_%d' % integer_id
+ self.assertEqual(
+ self.export(integer_id, fields=['value/id']),
+ [[external_id]])
+
+class test_o2m(CreatorCase):
+ model_name = 'export.one2many'
+ commands = [
+ (0, False, {'value': 4, 'str': 'record1'}),
+ (0, False, {'value': 42, 'str': 'record2'}),
+ (0, False, {'value': 36, 'str': 'record3'}),
+ (0, False, {'value': 4, 'str': 'record4'}),
+ (0, False, {'value': 13, 'str': 'record5'}),
+ ]
+ names = [
+ u'export.one2many.child:%d' % d['value']
+ for c, _, d in commands
+ ]
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(False),
+ [[False]])
+
+ def test_single(self):
+ self.assertEqual(
+ self.export([(0, False, {'value': 42})]),
+ # name_get result
+ [[u'export.one2many.child:42']])
+
+ def test_single_subfield(self):
+ self.assertEqual(
+ self.export([(0, False, {'value': 42})],
+ fields=['value', 'value/value']),
+ [[u'export.one2many.child:42', u'42']])
+
+ def test_integrate_one_in_parent(self):
+ self.assertEqual(
+ self.export([(0, False, {'value': 42})],
+ fields=['const', 'value/value']),
+ [[u'4', u'42']])
+
+ def test_multiple_records(self):
+ self.assertEqual(
+ self.export(self.commands, fields=['const', 'value/value']),
+ [
+ [u'4', u'4'],
+ [u'', u'42'],
+ [u'', u'36'],
+ [u'', u'4'],
+ [u'', u'13'],
+ ])
+
+ def test_multiple_records_name(self):
+ self.assertEqual(
+ self.export(self.commands, fields=['const', 'value']),
+ [[
+ u'4', u','.join(self.names)
+ ]])
+
+ def test_multiple_records_id(self):
+ export = self.export(self.commands, fields=['const', 'value/.id'])
+ O2M_c = self.registry('export.one2many.child')
+ ids = O2M_c.browse(self.cr, openerp.SUPERUSER_ID,
+ O2M_c.search(self.cr, openerp.SUPERUSER_ID, []))
+ self.assertEqual(
+ export,
+ [
+ ['4', str(ids[0].id)],
+ ['', str(ids[1].id)],
+ ['', str(ids[2].id)],
+ ['', str(ids[3].id)],
+ ['', str(ids[4].id)],
+ ])
+
+ def test_multiple_records_with_name_before(self):
+ self.assertEqual(
+ self.export(self.commands, fields=['const', 'value', 'value/value']),
+ [[ # exports sub-fields of very first o2m
+ u'4', u','.join(self.names), u'4'
+ ]])
+
+ def test_multiple_records_with_name_after(self):
+ self.assertEqual(
+ self.export(self.commands, fields=['const', 'value/value', 'value']),
+ [ # completely ignores name_get request
+ [u'4', u'4', ''],
+ ['', u'42', ''],
+ ['', u'36', ''],
+ ['', u'4', ''],
+ ['', u'13', ''],
+ ])
+
+ def test_multiple_subfields_neighbour(self):
+ self.assertEqual(
+ self.export(self.commands, fields=['const', 'value/str','value/value']),
+ [
+ [u'4', u'record1', u'4'],
+ ['', u'record2', u'42'],
+ ['', u'record3', u'36'],
+ ['', u'record4', u'4'],
+ ['', u'record5', u'13'],
+ ])
+
+ def test_multiple_subfields_separated(self):
+ self.assertEqual(
+ self.export(self.commands, fields=['value/str', 'const', 'value/value']),
+ [
+ [u'record1', u'4', u'4'],
+ [u'record2', '', u'42'],
+ [u'record3', '', u'36'],
+ [u'record4', '', u'4'],
+ [u'record5', '', u'13'],
+ ])
+
+class test_o2m_multiple(CreatorCase):
+ model_name = 'export.one2many.multiple'
+
+ def make(self, value=None, **values):
+ if value is not None: values['value'] = value
+ id = self.model.create(self.cr, openerp.SUPERUSER_ID, values)
+ return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0]
+ def export(self, value=None, fields=('child1', 'child2',), context=None, **values):
+ record = self.make(value, **values)
+ return self.model._BaseModel__export_row(
+ self.cr, openerp.SUPERUSER_ID, record,
+ [f.split('/') for f in fields],
+ context=context)
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(child1=False, child2=False),
+ [[False, False]])
+
+ def test_single_per_side(self):
+ self.assertEqual(
+ self.export(child1=False, child2=[(0, False, {'value': 42})]),
+ [[False, u'export.one2many.child.2:42']])
+
+ self.assertEqual(
+ self.export(child1=[(0, False, {'value': 43})], child2=False),
+ [[u'export.one2many.child.1:43', False]])
+
+ self.assertEqual(
+ self.export(child1=[(0, False, {'value': 43})],
+ child2=[(0, False, {'value': 42})]),
+ [[u'export.one2many.child.1:43', u'export.one2many.child.2:42']])
+
+ def test_single_integrate_subfield(self):
+ fields = ['const', 'child1/value', 'child2/value']
+ self.assertEqual(
+ self.export(child1=False, child2=[(0, False, {'value': 42})],
+ fields=fields),
+ [[u'36', False, u'42']])
+
+ self.assertEqual(
+ self.export(child1=[(0, False, {'value': 43})], child2=False,
+ fields=fields),
+ [[u'36', u'43', False]])
+
+ self.assertEqual(
+ self.export(child1=[(0, False, {'value': 43})],
+ child2=[(0, False, {'value': 42})],
+ fields=fields),
+ [[u'36', u'43', u'42']])
+
+ def test_multiple(self):
+ """ With two "concurrent" o2ms, exports the first line combined, then
+ exports the rows for the first o2m, then the rows for the second o2m.
+ """
+ fields = ['const', 'child1/value', 'child2/value']
+ child1 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
+ for index, v in zip(itertools.count(), [4, 42, 36, 4, 13])]
+ child2 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
+ for index, v in zip(itertools.count(10), [8, 12, 8, 55, 33, 13])]
+
+ self.assertEqual(
+ self.export(child1=child1, child2=False, fields=fields),
+ [
+ [u'36', u'4', False],
+ ['', u'42', ''],
+ ['', u'36', ''],
+ ['', u'4', ''],
+ ['', u'13', ''],
+ ])
+ self.assertEqual(
+ self.export(child1=False, child2=child2, fields=fields),
+ [
+ [u'36', False, u'8'],
+ ['', '', u'12'],
+ ['', '', u'8'],
+ ['', '', u'55'],
+ ['', '', u'33'],
+ ['', '', u'13'],
+ ])
+ self.assertEqual(
+ self.export(child1=child1, child2=child2, fields=fields),
+ [
+ [u'36', u'4', u'8'],
+ ['', u'42', ''],
+ ['', u'36', ''],
+ ['', u'4', ''],
+ ['', u'13', ''],
+ ['', '', u'12'],
+ ['', '', u'8'],
+ ['', '', u'55'],
+ ['', '', u'33'],
+ ['', '', u'13'],
+ ])
+
+class test_m2m(CreatorCase):
+ model_name = 'export.many2many'
+ commands = [
+ (0, False, {'value': 4, 'str': 'record000'}),
+ (0, False, {'value': 42, 'str': 'record001'}),
+ (0, False, {'value': 36, 'str': 'record010'}),
+ (0, False, {'value': 4, 'str': 'record011'}),
+ (0, False, {'value': 13, 'str': 'record100'}),
+ ]
+ names = [
+ u'export.many2many.other:%d' % d['value']
+ for c, _, d in commands
+ ]
+
+ def test_empty(self):
+ self.assertEqual(
+ self.export(False),
+ [[False]])
+
+ def test_single(self):
+ self.assertEqual(
+ self.export([(0, False, {'value': 42})]),
+ # name_get result
+ [[u'export.many2many.other:42']])
+
+ def test_single_subfield(self):
+ self.assertEqual(
+ self.export([(0, False, {'value': 42})],
+ fields=['value', 'value/value']),
+ [[u'export.many2many.other:42', u'42']])
+
+ def test_integrate_one_in_parent(self):
+ self.assertEqual(
+ self.export([(0, False, {'value': 42})],
+ fields=['const', 'value/value']),
+ [[u'4', u'42']])
+
+ def test_multiple_records(self):
+ self.assertEqual(
+ self.export(self.commands, fields=['const', 'value/value']),
+ [
+ [u'4', u'4'],
+ [u'', u'42'],
+ [u'', u'36'],
+ [u'', u'4'],
+ [u'', u'13'],
+ ])
+
+ def test_multiple_records_name(self):
+ self.assertEqual(
+ self.export(self.commands, fields=['const', 'value']),
+ [[ # FIXME: hardcoded comma, import uses config.csv_internal_sep
+ # resolution: remove configurable csv_internal_sep
+ u'4', u','.join(self.names)
+ ]])
+
+ # essentially same as o2m, so boring
+
+class test_function(CreatorCase):
+ model_name = 'export.function'
+
+ def test_value(self):
+ """ Exports value normally returned by accessing the function field
+ """
+ self.assertEqual(
+ self.export(42),
+ [[u'3']])