1 # -*- coding: utf-8 -*-
3 import openerp.modules.registry
6 from openerp.tests import common
9 class CreatorCase(common.TransactionCase):
12 def __init__(self, *args, **kwargs):
13 super(CreatorCase, self).__init__(*args, **kwargs)
17 super(CreatorCase, self).setUp()
18 self.model = self.registry(self.model_name)
19 def make(self, value):
20 id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': value})
21 return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0]
22 def export(self, value, fields=('value',), context=None):
23 record = self.make(value)
24 return self.model._BaseModel__export_row(
25 self.cr, openerp.SUPERUSER_ID, record,
26 [f.split('/') for f in fields],
29 class test_boolean_field(CreatorCase):
30 model_name = 'export.boolean'
37 """ ``False`` value to boolean fields is unique in being exported as a
38 (unicode) string, not a boolean
44 class test_integer_field(CreatorCase):
45 model_name = 'export.integer'
48 self.assertEqual(self.model.search(self.cr, openerp.SUPERUSER_ID, []), [],
49 "Test model should have no records")
55 def test_basic_value(self):
60 def test_negative(self):
70 class test_float_field(CreatorCase):
71 model_name = 'export.float'
78 def test_epsilon(self):
80 self.export(0.000000000027),
83 def test_negative(self):
88 def test_positive(self):
95 self.export(87654321.4678),
98 class test_decimal_field(CreatorCase):
99 model_name = 'export.decimal'
106 def test_epsilon(self):
107 """ epsilon gets sliced to 0 due to precision
110 self.export(0.000000000027),
113 def test_negative(self):
118 def test_positive(self):
125 self.export(87654321.4678), [[u'87654321.468']])
127 class test_string_field(CreatorCase):
128 model_name = 'export.string.bounded'
130 def test_empty(self):
134 def test_within_bounds(self):
136 self.export("foobar"),
138 def test_out_of_bounds(self):
140 self.export("C for Sinking, "
141 "Java for Drinking, "
142 "Smalltalk for Thinking. "
143 "...and Power to the Penguin!"),
144 [[u"C for Sinking, J"]])
146 class test_unbound_string_field(CreatorCase):
147 model_name = 'export.string'
149 def test_empty(self):
153 def test_small(self):
155 self.export("foobar"),
159 self.export("We flew down weekly to meet with IBM, but they "
160 "thought the way to measure software was the amount "
161 "of code we wrote, when really the better the "
162 "software, the fewer lines of code."),
163 [[u"We flew down weekly to meet with IBM, but they thought the "
164 u"way to measure software was the amount of code we wrote, "
165 u"when really the better the software, the fewer lines of "
168 class test_text(CreatorCase):
169 model_name = 'export.text'
171 def test_empty(self):
175 def test_small(self):
177 self.export("foobar"),
181 self.export("So, `bind' is `let' and monadic programming is"
182 " equivalent to programming in the A-normal form. That"
183 " is indeed all there is to monads"),
184 [[u"So, `bind' is `let' and monadic programming is equivalent to"
185 u" programming in the A-normal form. That is indeed all there"
188 class test_date(CreatorCase):
189 model_name = 'export.date'
191 def test_empty(self):
195 def test_basic(self):
197 self.export('2011-11-07'),
200 class test_datetime(CreatorCase):
201 model_name = 'export.datetime'
203 def test_empty(self):
207 def test_basic(self):
209 self.export('2011-11-07 21:05:48'),
210 [[u'2011-11-07 21:05:48']])
212 """ Export ignores the timezone and always exports to UTC
214 .. note:: on the other hand, export uses user lang for name_get
216 # NOTE: ignores user timezone, always exports to UTC
218 self.export('2011-11-07 21:05:48', context={'tz': 'Pacific/Norfolk'}),
219 [[u'2011-11-07 21:05:48']])
221 class test_selection(CreatorCase):
222 model_name = 'export.selection'
229 def test_empty(self):
234 def test_value(self):
235 """ selections export the *label* for their value
241 def test_localized_export(self):
242 self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, {
243 'name': u'Français',
245 'translatable': True,
246 'date_format': '%d.%m.%Y',
247 'decimal_point': ',',
248 'thousands_sep': ' ',
250 Translations = self.registry('ir.translation')
251 for source, value in self.translations_fr:
252 Translations.create(self.cr, openerp.SUPERUSER_ID, {
253 'name': 'export.selection,value',
260 self.export(2, context={'lang': 'fr_FR'}),
263 class test_selection_function(CreatorCase):
264 model_name = 'export.selection.function'
266 def test_empty(self):
271 def test_value(self):
272 # FIXME: selection functions export the *value* itself
284 class test_m2o(CreatorCase):
285 model_name = 'export.many2one'
287 def test_empty(self):
291 def test_basic(self):
292 """ Exported value is the name_get of the related object
294 integer_id = self.registry('export.integer').create(
295 self.cr, openerp.SUPERUSER_ID, {'value': 42})
296 name = dict(self.registry('export.integer').name_get(
297 self.cr, openerp.SUPERUSER_ID,[integer_id]))[integer_id]
299 self.export(integer_id),
302 """ Can recursively export fields of m2o via path
304 integer_id = self.registry('export.integer').create(
305 self.cr, openerp.SUPERUSER_ID, {'value': 42})
307 self.export(integer_id, fields=['value/.id', 'value/value']),
308 [[unicode(integer_id), u'42']])
309 def test_external_id(self):
310 integer_id = self.registry('export.integer').create(
311 self.cr, openerp.SUPERUSER_ID, {'value': 42})
312 # __export__.$class.$id
313 external_id = u'__export__.export_many2one_%d' % integer_id
315 self.export(integer_id, fields=['value/id']),
318 class test_o2m(CreatorCase):
319 model_name = 'export.one2many'
321 (0, False, {'value': 4, 'str': 'record1'}),
322 (0, False, {'value': 42, 'str': 'record2'}),
323 (0, False, {'value': 36, 'str': 'record3'}),
324 (0, False, {'value': 4, 'str': 'record4'}),
325 (0, False, {'value': 13, 'str': 'record5'}),
328 u'export.one2many.child:%d' % d['value']
329 for c, _, d in commands
332 def test_empty(self):
337 def test_single(self):
339 self.export([(0, False, {'value': 42})]),
341 [[u'export.one2many.child:42']])
343 def test_single_subfield(self):
345 self.export([(0, False, {'value': 42})],
346 fields=['value', 'value/value']),
347 [[u'export.one2many.child:42', u'42']])
349 def test_integrate_one_in_parent(self):
351 self.export([(0, False, {'value': 42})],
352 fields=['const', 'value/value']),
355 def test_multiple_records(self):
357 self.export(self.commands, fields=['const', 'value/value']),
366 def test_multiple_records_name(self):
368 self.export(self.commands, fields=['const', 'value']),
370 u'4', u','.join(self.names)
373 def test_multiple_records_id(self):
374 export = self.export(self.commands, fields=['const', 'value/.id'])
375 O2M_c = self.registry('export.one2many.child')
376 ids = O2M_c.browse(self.cr, openerp.SUPERUSER_ID,
377 O2M_c.search(self.cr, openerp.SUPERUSER_ID, []))
381 ['4', str(ids[0].id)],
382 ['', str(ids[1].id)],
383 ['', str(ids[2].id)],
384 ['', str(ids[3].id)],
385 ['', str(ids[4].id)],
388 def test_multiple_records_with_name_before(self):
390 self.export(self.commands, fields=['const', 'value', 'value/value']),
391 [[ # exports sub-fields of very first o2m
392 u'4', u','.join(self.names), u'4'
395 def test_multiple_records_with_name_after(self):
397 self.export(self.commands, fields=['const', 'value/value', 'value']),
398 [ # completely ignores name_get request
406 def test_multiple_subfields_neighbour(self):
408 self.export(self.commands, fields=['const', 'value/str','value/value']),
410 [u'4', u'record1', u'4'],
411 ['', u'record2', u'42'],
412 ['', u'record3', u'36'],
413 ['', u'record4', u'4'],
414 ['', u'record5', u'13'],
417 def test_multiple_subfields_separated(self):
419 self.export(self.commands, fields=['value/str', 'const', 'value/value']),
421 [u'record1', u'4', u'4'],
422 [u'record2', '', u'42'],
423 [u'record3', '', u'36'],
424 [u'record4', '', u'4'],
425 [u'record5', '', u'13'],
428 class test_o2m_multiple(CreatorCase):
429 model_name = 'export.one2many.multiple'
431 def make(self, value=None, **values):
432 if value is not None: values['value'] = value
433 id = self.model.create(self.cr, openerp.SUPERUSER_ID, values)
434 return self.model.browse(self.cr, openerp.SUPERUSER_ID, [id])[0]
435 def export(self, value=None, fields=('child1', 'child2',), context=None, **values):
436 record = self.make(value, **values)
437 return self.model._BaseModel__export_row(
438 self.cr, openerp.SUPERUSER_ID, record,
439 [f.split('/') for f in fields],
442 def test_empty(self):
444 self.export(child1=False, child2=False),
447 def test_single_per_side(self):
449 self.export(child1=False, child2=[(0, False, {'value': 42})]),
450 [[False, u'export.one2many.child.2:42']])
453 self.export(child1=[(0, False, {'value': 43})], child2=False),
454 [[u'export.one2many.child.1:43', False]])
457 self.export(child1=[(0, False, {'value': 43})],
458 child2=[(0, False, {'value': 42})]),
459 [[u'export.one2many.child.1:43', u'export.one2many.child.2:42']])
461 def test_single_integrate_subfield(self):
462 fields = ['const', 'child1/value', 'child2/value']
464 self.export(child1=False, child2=[(0, False, {'value': 42})],
466 [[u'36', False, u'42']])
469 self.export(child1=[(0, False, {'value': 43})], child2=False,
471 [[u'36', u'43', False]])
474 self.export(child1=[(0, False, {'value': 43})],
475 child2=[(0, False, {'value': 42})],
477 [[u'36', u'43', u'42']])
479 def test_multiple(self):
480 """ With two "concurrent" o2ms, exports the first line combined, then
481 exports the rows for the first o2m, then the rows for the second o2m.
483 fields = ['const', 'child1/value', 'child2/value']
484 child1 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
485 for index, v in zip(itertools.count(), [4, 42, 36, 4, 13])]
486 child2 = [(0, False, {'value': v, 'str': 'record%.02d' % index})
487 for index, v in zip(itertools.count(10), [8, 12, 8, 55, 33, 13])]
490 self.export(child1=child1, child2=False, fields=fields),
492 [u'36', u'4', False],
499 self.export(child1=False, child2=child2, fields=fields),
501 [u'36', False, u'8'],
509 self.export(child1=child1, child2=child2, fields=fields),
523 class test_m2m(CreatorCase):
524 model_name = 'export.many2many'
526 (0, False, {'value': 4, 'str': 'record000'}),
527 (0, False, {'value': 42, 'str': 'record001'}),
528 (0, False, {'value': 36, 'str': 'record010'}),
529 (0, False, {'value': 4, 'str': 'record011'}),
530 (0, False, {'value': 13, 'str': 'record100'}),
533 u'export.many2many.other:%d' % d['value']
534 for c, _, d in commands
537 def test_empty(self):
542 def test_single(self):
544 self.export([(0, False, {'value': 42})]),
546 [[u'export.many2many.other:42']])
548 def test_single_subfield(self):
550 self.export([(0, False, {'value': 42})],
551 fields=['value', 'value/value']),
552 [[u'export.many2many.other:42', u'42']])
554 def test_integrate_one_in_parent(self):
556 self.export([(0, False, {'value': 42})],
557 fields=['const', 'value/value']),
560 def test_multiple_records(self):
562 self.export(self.commands, fields=['const', 'value/value']),
571 def test_multiple_records_name(self):
573 self.export(self.commands, fields=['const', 'value']),
574 [[ # FIXME: hardcoded comma, import uses config.csv_internal_sep
575 # resolution: remove configurable csv_internal_sep
576 u'4', u','.join(self.names)
579 # essentially same as o2m, so boring
581 class test_function(CreatorCase):
582 model_name = 'export.function'
584 def test_value(self):
585 """ Exports value normally returned by accessing the function field