Server IP : 127.0.0.2 / Your IP : 13.59.234.246 Web Server : Apache/2.4.18 (Ubuntu) System : User : www-data ( ) PHP Version : 7.0.33-0ubuntu0.16.04.16 Disable Function : disk_free_space,disk_total_space,diskfreespace,dl,exec,fpaththru,getmyuid,getmypid,highlight_file,ignore_user_abord,leak,listen,link,opcache_get_configuration,opcache_get_status,passthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,php_uname,phpinfo,posix_ctermid,posix_getcwd,posix_getegid,posix_geteuid,posix_getgid,posix_getgrgid,posix_getgrnam,posix_getgroups,posix_getlogin,posix_getpgid,posix_getpgrp,posix_getpid,posix,_getppid,posix_getpwnam,posix_getpwuid,posix_getrlimit,posix_getsid,posix_getuid,posix_isatty,posix_kill,posix_mkfifo,posix_setegid,posix_seteuid,posix_setgid,posix_setpgid,posix_setsid,posix_setuid,posix_times,posix_ttyname,posix_uname,pclose,popen,proc_open,proc_close,proc_get_status,proc_nice,proc_terminate,shell_exec,source,show_source,system,virtual MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /opt/odoo/odoo/tools/ |
Upload File : |
# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. """ Helper functions for reports testing. Please /do not/ import this file by default, but only explicitly call it through the code of yaml tests. """ import odoo import odoo.report import odoo.tools as tools import logging from odoo.tools.safe_eval import safe_eval from subprocess import Popen, PIPE import os import tempfile _logger = logging.getLogger(__name__) _test_logger = logging.getLogger('odoo.tests') def try_report(cr, uid, rname, ids, data=None, context=None, our_module=None, report_type=None): """ Try to render a report <rname> with contents of ids This function should also check for common pitfalls of reports. """ if data is None: data = {} if context is None: context = {} if rname.startswith('report.'): rname_s = rname[7:] else: rname_s = rname _test_logger.info(" - Trying %s.create(%r)", rname, ids) res = odoo.report.render_report(cr, uid, ids, rname_s, data, context=context) if not isinstance(res, tuple): raise RuntimeError("Result of %s.create() should be a (data,format) tuple, now it is a %s" % \ (rname, type(res))) (res_data, res_format) = res if not res_data: raise ValueError("Report %s produced an empty result!" % rname) if tools.config['test_report_directory']: file(os.path.join(tools.config['test_report_directory'], rname+ '.'+res_format), 'wb+').write(res_data) _logger.debug("Have a %s report for %s, will examine it", res_format, rname) if res_format == 'pdf': if res_data[:5] != '%PDF-': raise ValueError("Report %s produced a non-pdf header, %r" % (rname, res_data[:10])) res_text = False try: fd, rfname = tempfile.mkstemp(suffix=res_format) os.write(fd, res_data) os.close(fd) proc = Popen(['pdftotext', '-enc', 'UTF-8', '-nopgbrk', rfname, '-'], shell=False, stdout=PIPE) stdout, stderr = proc.communicate() res_text = tools.ustr(stdout) os.unlink(rfname) except Exception: _logger.debug("Unable to parse PDF report: install pdftotext to perform automated tests.") if res_text is not False: for line in res_text.split('\n'): if ('[[' in line) or ('[ [' in line): _logger.error("Report %s may have bad expression near: \"%s\".", rname, line[80:]) # TODO more checks, what else can be a sign of a faulty report? elif res_format == 'html': pass else: _logger.warning("Report %s produced a \"%s\" chunk, cannot examine it", rname, res_format) return False _test_logger.info(" + Report %s produced correctly.", rname) return True def try_report_action(cr, uid, action_id, active_model=None, active_ids=None, wiz_data=None, wiz_buttons=None, context=None, our_module=None): """Take an ir.action.act_window and follow it until a report is produced :param action_id: the integer id of an action, or a reference to xml id of the act_window (can search [our_module.]+xml_id :param active_model, active_ids: call the action as if it had been launched from that model+ids (tree/form view action) :param wiz_data: a dictionary of values to use in the wizard, if needed. They will override (or complete) the default values of the wizard form. :param wiz_buttons: a list of button names, or button icon strings, which should be preferred to press during the wizard. Eg. 'OK' or 'fa-print' :param our_module: the name of the calling module (string), like 'account' """ if not our_module and isinstance(action_id, basestring): if '.' in action_id: our_module = action_id.split('.', 1)[0] context = dict(context or {}) # TODO context fill-up env = odoo.api.Environment(cr, uid, context) def log_test(msg, *args): _test_logger.info(" - " + msg, *args) datas = {} if active_model: datas['model'] = active_model if active_ids: datas['ids'] = active_ids if not wiz_buttons: wiz_buttons = [] if isinstance(action_id, basestring): if '.' in action_id: _, act_xmlid = action_id.split('.', 1) else: if not our_module: raise ValueError('You cannot only specify action_id "%s" without a module name' % action_id) act_xmlid = action_id action_id = '%s.%s' % (our_module, action_id) action = env.ref(action_id) act_model, act_id = action._name, action.id else: assert isinstance(action_id, (long, int)) act_model = 'ir.action.act_window' # assume that act_id = action_id act_xmlid = '<%s>' % act_id def _exec_action(action, datas, env): # taken from client/modules/action/main.py:84 _exec_action() if isinstance(action, bool) or 'type' not in action: return # Updating the context : Adding the context of action in order to use it on Views called from buttons context = dict(env.context) if datas.get('id',False): context.update( {'active_id': datas.get('id',False), 'active_ids': datas.get('ids',[]), 'active_model': datas.get('model',False)}) context1 = action.get('context', {}) if isinstance(context1, basestring): context1 = safe_eval(context1, dict(context)) context.update(context1) env = env(context=context) if action['type'] in ['ir.actions.act_window', 'ir.actions.submenu']: for key in ('res_id', 'res_model', 'view_type', 'view_mode', 'limit', 'search_view', 'auto_search', 'search_view_id'): datas[key] = action.get(key, datas.get(key, None)) view_id = False if action.get('views', []): if isinstance(action['views'],list): view_id = action['views'][0][0] datas['view_mode']= action['views'][0][1] else: if action.get('view_id', False): view_id = action['view_id'][0] elif action.get('view_id', False): view_id = action['view_id'][0] assert datas['res_model'], "Cannot use the view without a model" # Here, we have a view that we need to emulate log_test("will emulate a %s view: %s#%s", action['view_type'], datas['res_model'], view_id or '?') view_res = env[datas['res_model']].fields_view_get(view_id, action['view_type']) assert view_res and view_res.get('arch'), "Did not return any arch for the view" view_data = {} if view_res.get('fields'): view_data = env[datas['res_model']].default_get(list(view_res['fields'])) if datas.get('form'): view_data.update(datas.get('form')) if wiz_data: view_data.update(wiz_data) _logger.debug("View data is: %r", view_data) for fk, field in view_res.get('fields',{}).items(): # Default fields returns list of int, while at create() # we need to send a [(6,0,[int,..])] if field['type'] in ('one2many', 'many2many') \ and view_data.get(fk, False) \ and isinstance(view_data[fk], list) \ and not isinstance(view_data[fk][0], tuple) : view_data[fk] = [(6, 0, view_data[fk])] action_name = action.get('name') try: from xml.dom import minidom cancel_found = False buttons = [] dom_doc = minidom.parseString(view_res['arch']) if not action_name: action_name = dom_doc.documentElement.getAttribute('name') for button in dom_doc.getElementsByTagName('button'): button_weight = 0 if button.getAttribute('special') == 'cancel': cancel_found = True continue if button.getAttribute('icon') == 'fa-times-circle': cancel_found = True continue if button.getAttribute('default_focus') == '1': button_weight += 20 if button.getAttribute('string') in wiz_buttons: button_weight += 30 elif button.getAttribute('icon') in wiz_buttons: button_weight += 10 string = button.getAttribute('string') or '?%s' % len(buttons) buttons.append({ 'name': button.getAttribute('name'), 'string': string, 'type': button.getAttribute('type'), 'weight': button_weight, }) except Exception, e: _logger.warning("Cannot resolve the view arch and locate the buttons!", exc_info=True) raise AssertionError(e.args[0]) if not datas['res_id']: # it is probably an orm_memory object, we need to create # an instance datas['res_id'] = env[datas['res_model']].create(view_data).id if not buttons: raise AssertionError("view form doesn't have any buttons to press!") buttons.sort(key=lambda b: b['weight']) _logger.debug('Buttons are: %s', ', '.join([ '%s: %d' % (b['string'], b['weight']) for b in buttons])) res = None while buttons and not res: b = buttons.pop() log_test("in the \"%s\" form, I will press the \"%s\" button.", action_name, b['string']) if not b['type']: log_test("the \"%s\" button has no type, cannot use it", b['string']) continue if b['type'] == 'object': #there we are! press the button! rec = env[datas['res_model']].browse(datas['res_id']) func = getattr(rec, b['name'], None) if not func: _logger.error("The %s model doesn't have a %s attribute!", datas['res_model'], b['name']) continue res = func() break else: _logger.warning("in the \"%s\" form, the \"%s\" button has unknown type %s", action_name, b['string'], b['type']) return res elif action['type']=='ir.actions.report.xml': if 'window' in datas: del datas['window'] if not datas: datas = action.get('datas') if not datas: datas = action.get('data') datas = datas.copy() ids = datas.get('ids') if 'ids' in datas: del datas['ids'] res = try_report(cr, uid, 'report.'+action['report_name'], ids, datas, context, our_module=our_module) return res else: raise Exception("Cannot handle action of type %s" % act_model) log_test("will be using %s action %s #%d", act_model, act_xmlid, act_id) action = env[act_model].browse(act_id).read()[0] assert action, "Could not read action %s[%s]" % (act_model, act_id) loop = 0 while action: loop += 1 # This part tries to emulate the loop of the Gtk client if loop > 100: _logger.info("Passed %d loops, giving up", loop) raise Exception("Too many loops at action") log_test("it is an %s action at loop #%d", action.get('type', 'unknown'), loop) result = _exec_action(action, datas, env) if not isinstance(result, dict): break datas = result.get('datas', {}) if datas: del result['datas'] action = result return True