[ADD] file field type in xml_convert, tests
authorXavier Morel <xmo@openerp.com>
Mon, 29 Apr 2013 11:24:59 +0000 (13:24 +0200)
committerXavier Morel <xmo@openerp.com>
Mon, 29 Apr 2013 11:24:59 +0000 (13:24 +0200)
bzr revid: xmo@openerp.com-20130429112459-ol1ayn2z34j32r54

doc/03_module_dev_01.rst
openerp/tests/__init__.py
openerp/tests/addons/test_convert/__init__.py [new file with mode: 0644]
openerp/tests/addons/test_convert/__openerp__.py [new file with mode: 0644]
openerp/tests/addons/test_convert/test_file.txt [new file with mode: 0644]
openerp/tests/test_convert.py [new file with mode: 0644]
openerp/tools/convert.py

index 0fbca9d..adc32c4 100644 (file)
@@ -172,40 +172,73 @@ is as follows:
      </data>
    </openerp>
 
-Record Tag
-//////////
+``<record>``
+////////////
 
-**Description**
+Defines a new record in a specified OpenERP model.
 
-The addition of new data is made with the record tag. This one takes a
-mandatory attribute : model. Model is the object name where the insertion has
-to be done. The tag record can also take an optional attribute: id. If this
-attribute is given, a variable of this name can be used later on, in the same
-file, to make reference to the newly created resource ID.
+``@model`` (required)
 
-A record tag may contain field tags. They indicate the record's fields value.
-If a field is not specified the default value will be used.
+    Name of the model in which this record will be created/inserted.
 
-The Record Field tag
-////////////////////
+``@id`` (optional)
 
-The attributes for the field tag are the following:
+    :term:`external ID` for the record, also allows referring to this record in
+    the rest of this file or in other files (through ``field/@ref`` or the
+    :py:func:`ref() <openerp.tools.convert._ref>` function)
 
-name : mandatory
-  the field name
+A record tag generally contains multiple ``field`` tags specifying the values
+set on the record's fields when creating it. Fields left out will be set to
+their default value unless required.
+
+``<field>``
+///////////
+
+In its most basic use, the ``field`` tag will set its body (as a string) as
+the value of the corresponding ``record``'s ``@name`` field.
+
+Extra attributes can either preprocess the body or replace its use entirely:
+
+``@name`` (mandatory)
+
+    Name of the field in the containing ``record``'s model
+
+``@type`` (optional)
+
+    One of ``char``, ``int``, ``float``, ``list``, ``tuple``, ``xml`` or
+    ``html`` or ``file``. Converts the ``field``'s body to the specified type
+    (or validates the body's content)
+
+    * ``xml`` will join multiple XML nodes under a single ``<data>`` root
+    * in ``xml`` and ``html``, external ids can be referenced using
+      ``%(id_name)s``
+    * ``list`` and ``tuple``'s element are specified using ``<value>``
+      sub-nodes with the same attributes as ``field``.
+    * ``file`` expects a module-local path and will save the path prefixed with
+      the current module's name, separated by a ``,`` (comma). For use with
+      :py:func:`~openerp.modules.module.get_module_resource`.
+
+``@model``
+
+    Model used for ``@search``'s search, or registry object put in context for
+    ``@eval``. Required if ``@search`` but optional if ``@eval``.
+
+``@eval`` (optional)
+
+    A Python expression evaluated to obtain the value to set on the record
+
+``@ref`` (optional)
 
-eval : optional
-  python expression that indicating the value to add
-  
-ref
-  reference to an id defined in this file
+    Links to an other record through its :term:`external id`. The module prefix
+    may be ommitted to link to a record defined in the same module.
 
-model
-  model to be looked up in the search
+``@search`` (optional)
 
-search
-  a query
+    Search domain (evaluated Python expression) into ``@model`` to get the
+    records to set on the field.
 
+    Sets all the matches found for m2m fields, the first id for other field
+    types.
 
 **Example**
 
index 3d129e0..5f26237 100644 (file)
@@ -21,6 +21,7 @@ import test_osv
 import test_translate
 import test_uninstall
 import test_view_validation
+import test_convert
 # This need a change in `oe run-tests` to only run fast_suite + checks by default.
 # import test_xmlrpc
 
@@ -41,6 +42,7 @@ checks = [
     test_misc,
     test_osv,
     test_translate,
+    test_convert,
 ]
  
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/openerp/tests/addons/test_convert/__init__.py b/openerp/tests/addons/test_convert/__init__.py
new file mode 100644 (file)
index 0000000..40a96af
--- /dev/null
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/openerp/tests/addons/test_convert/__openerp__.py b/openerp/tests/addons/test_convert/__openerp__.py
new file mode 100644 (file)
index 0000000..40d9eb3
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    'name': 'test_convert',
+    'description': "Data for xml conversion tests",
+    'version': '0.0.1',
+}
diff --git a/openerp/tests/addons/test_convert/test_file.txt b/openerp/tests/addons/test_convert/test_file.txt
new file mode 100644 (file)
index 0000000..e69b2e0
--- /dev/null
@@ -0,0 +1 @@
+nothing to see here, move along
diff --git a/openerp/tests/test_convert.py b/openerp/tests/test_convert.py
new file mode 100644 (file)
index 0000000..87c3000
--- /dev/null
@@ -0,0 +1,83 @@
+import collections
+import unittest2
+from lxml import etree as ET
+from lxml.builder import E
+
+from . import common
+
+from openerp.tools.convert import _eval_xml
+
+Field = E.field
+Value = E.value
+class TestEvalXML(common.TransactionCase):
+    def eval_xml(self, node, obj=None, idref=None):
+        return _eval_xml(obj, node, pool=None, cr=self.cr, uid=self.uid,
+                         idref=idref, context=None)
+
+    def test_char(self):
+        self.assertEqual(
+            self.eval_xml(Field("foo")),
+            "foo")
+        self.assertEqual(
+            self.eval_xml(Field("None")),
+            "None")
+
+    def test_int(self):
+        self.assertIsNone(
+            self.eval_xml(Field("None", type='int')),
+            "what the fuck?")
+        self.assertEqual(
+            self.eval_xml(Field(" 42  ", type="int")),
+            42)
+
+        with self.assertRaises(ValueError):
+            self.eval_xml(Field("4.82", type="int"))
+
+        with self.assertRaises(ValueError):
+            self.eval_xml(Field("Whelp", type="int"))
+
+    def test_float(self):
+        self.assertEqual(
+            self.eval_xml(Field("4.78", type="float")),
+            4.78)
+
+        with self.assertRaises(ValueError):
+            self.eval_xml(Field("None", type="float"))
+
+        with self.assertRaises(ValueError):
+            self.eval_xml(Field("Foo", type="float"))
+
+    def test_list(self):
+        self.assertEqual(
+            self.eval_xml(Field(type="list")),
+            [])
+
+        self.assertEqual(
+            self.eval_xml(Field(
+                Value("foo"),
+                Value("5", type="int"),
+                Value("4.76", type="float"),
+                Value("None", type="int"),
+                type="list"
+            )),
+            ["foo", 5, 4.76, None])
+
+    def test_file(self):
+        Obj = collections.namedtuple('Obj', 'module')
+        obj = Obj('test_convert')
+        self.assertEqual(
+            self.eval_xml(Field('test_file.txt', type='file'), obj),
+            'test_convert,test_file.txt')
+
+        with self.assertRaises(IOError):
+            self.eval_xml(Field('test_nofile.txt', type='file'), obj)
+
+    @unittest2.skip("not tested")
+    def test_xml(self):
+        pass
+
+    @unittest2.skip("not tested")
+    def test_html(self):
+        pass
+
+
index cdcaff3..8541550 100644 (file)
@@ -161,6 +161,12 @@ def _eval_xml(self, node, pool, cr, uid, idref, context=None):
         if t == 'html':
             return _process("".join([etree.tostring(n, encoding='utf-8')
                                    for n in node]), idref)
+        if t == 'file':
+            from ..modules import module
+            path = node.text.strip()
+            if not module.get_module_resource(self.module, path):
+                raise IOError("No such file or directory: '%s'" % path)
+            return '%s,%s' % (self.module, path)
         if t in ('char', 'int', 'float'):
             d = node.text
             if t == 'int':