# Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.tools import cloc from odoo.tests import TransactionCase, tagged XML_TEST = """ Line Line cdata Line yes6]]> ]]> ]]> name not a comment but found as is After closed comment back to normal """ PY_TEST_NO_RETURN = '''line = 1 line = 2''' PY_TEST = ''' # comment 1 def func(): # eol comment 3 """ docstring """ pass def query(): long_query = """ SELECT * FROM table WHERE id = 1; """ return query print(i.lineno, i, getattr(i,'s',None), getattr(i,'value',None)) ''' JS_TEST = ''' /* comment */ function() { return 1+2; // comment } function() { hello = 4; /* comment */ console.log(hello); regex = /\/*h/; legit_code_counted = 1; regex2 = /.*/; } ''' CSS_TEST = ''' /* Comment */ p { text-align: center; color: red; text-overflow: ' /* '; } #content, #footer, #supplement { position: absolute; left: 510px; width: 200px; text-overflow: ' */ '; } ''' SCSS_TEST = ''' /* Comment */ // Standalone list views .o_content > .o_list_view > .table-responsive > .table { // List views always have the table-sm class, maybe we should remove // it (and consider it does not exist) and change the default table paddings @include o-list-view-full-width-padding($base-x: $table-cell-padding-x-sm, $base-y: $table-cell-padding-y-sm, $ratio: 2); &:not(.o_list_table_grouped) { @include media-breakpoint-up(xl) { @include o-list-view-full-width-padding($base-x: $table-cell-padding-x-sm, $base-y: $table-cell-padding-y-sm, $ratio: 2.5); } } .o_optional_columns_dropdown_toggle { padding: 8px 10px; } } #content, #footer, #supplement { text-overflow: '/*'; left: 510px; width: 200px; text-overflow: '*/'; } ''' class TestClocCustomization(TransactionCase): def create_xml_id(self, module, name, rec): self.env['ir.model.data'].create({ 'name': name, 'model': rec._name, 'res_id': rec.id, 'module': module, }) def create_field(self, name): field = self.env['ir.model.fields'].with_context(studio=True).create({ 'name': name, 'field_description': name, 'model': 'res.partner', 'model_id': self.env.ref('base.model_res_partner').id, 'ttype': 'integer', 'store': False, 'compute': "for rec in self: rec['x_invoice_count'] = 10", }) # Simulate the effect of https://github.com/odoo/odoo/commit/9afce4805fc8bac45fdba817488aa867fddff69b # Updating a module create xml_id of the module even for manual field if it's the original module # of the model self.create_xml_id('base', name, field) return field def create_server_action(self, name): return self.env['ir.actions.server'].create({ 'name': name, 'code': """ for rec in records: rec['name'] = test """, 'state': 'code', 'type': 'ir.actions.server', 'model_id': self.env.ref('base.model_res_partner').id, }) def test_ignore_auto_generated_computed_field(self): """ Check that we count custom fields with no module or studio not auto generated Having an xml_id but no existing module is consider as not belonging to a module """ f1 = self.create_field('x_invoice_count') self.create_xml_id('studio_customization', 'invoice_count', f1) cl = cloc.Cloc() cl.count_customization(self.env) self.assertEqual(cl.code.get('odoo/studio', 0), 0, 'Studio auto generated count field should not be counted in cloc') f2 = self.create_field('x_studio_custom_field') self.create_xml_id('studio_customization', 'studio_custom', f2) cl = cloc.Cloc() cl.count_customization(self.env) self.assertEqual(cl.code.get('odoo/studio', 0), 1, 'Count other studio computed field') self.create_field('x_custom_field') cl = cloc.Cloc() cl.count_customization(self.env) self.assertEqual(cl.code.get('odoo/studio', 0), 2, 'Count fields without xml_id') f4 = self.create_field('x_custom_field_export') self.create_xml_id('__export__', 'studio_custom', f4) cl = cloc.Cloc() cl.count_customization(self.env) self.assertEqual(cl.code.get('odoo/studio', 0), 3, 'Count fields with xml_id but without module') def test_several_xml_id(self): sa = self.create_server_action("Test double xml_id") self.create_xml_id("__export__", "first", sa) self.create_xml_id("base", "second", sa) cl = cloc.Cloc() cl.count_customization(self.env) self.assertEqual(cl.code.get('odoo/studio', 0), 2, 'Count Should count SA with a non standard xml_id') self.create_xml_id("__import__", "third", sa) cl = cloc.Cloc() cl.count_customization(self.env) self.assertEqual(cl.code.get('odoo/studio', 0), 2, 'SA with several xml_id should be counted only once') def test_cloc_exclude_xml_id(self): sa = self.create_server_action("Test double xml_id") self.create_xml_id("__cloc_exclude__", "sa_first", sa) self.create_xml_id("__upgrade__", "sa_second", sa) cl = cloc.Cloc() cl.count_customization(self.env) self.assertEqual(cl.code.get('odoo/studio', 0), 0, 'Should not count SA with cloc_exclude xml_id') f1 = self.create_field('x_invoice_count') self.create_xml_id("__cloc_exclude__", "field_first", f1) self.create_xml_id("__upgrade__", "field_second", f1) cl = cloc.Cloc() cl.count_customization(self.env) self.assertEqual(cl.code.get('odoo/studio', 0), 0, 'Should not count Field with cloc_exclude xml_id') def test_field_no_xml_id(self): self.env['ir.model.fields'].create({ 'name': "x_no_xml_id", 'field_description': "no_xml_id", 'model': 'res.partner', 'model_id': self.env.ref('base.model_res_partner').id, 'ttype': 'integer', 'store': False, 'compute': "for rec in self: rec['x_invoice_count'] = 10", }) cl = cloc.Cloc() cl.count_customization(self.env) self.assertEqual(cl.code.get('odoo/studio', 0), 1, 'Should count field with no xml_id at all') class TestClocParser(TransactionCase): def test_parser(self): cl = cloc.Cloc() xml_count = cl.parse_xml(XML_TEST) self.assertEqual(xml_count, (18, 31)) py_count = cl.parse_py(PY_TEST_NO_RETURN) self.assertEqual(py_count, (2, 2)) py_count = cl.parse_py(PY_TEST) if self._python_version >= (3, 8, 0): # Multi line str lineno return the begining of the str # in python 3.8, it result in a different count for # multi str used in expressions self.assertEqual(py_count, (7, 16)) else: self.assertEqual(py_count, (8, 16)) js_count = cl.parse_js(JS_TEST) self.assertEqual(js_count, (10, 17)) css_count = cl.parse_css(CSS_TEST) self.assertEqual(css_count, (11, 17)) scss_count = cl.parse_scss(SCSS_TEST) self.assertEqual(scss_count, (17, 26)) @tagged('post_install', '-at_install') class TestClocStdNoCusto(TransactionCase): def test_no_custo_install(self): """ Make sure after the installation of module no database customization is counted """ cl = cloc.Cloc() cl.count_customization(self.env) self.assertEqual(cl.code.get('odoo/studio', 0), 0, 'Module should not generate customization in database')