diff --git a/openerp/addons/base/tests/test_phantom.py b/openerp/addons/base/tests/test_phantom.py index e729b2e95cc..2e73a20c783 100644 --- a/openerp/addons/base/tests/test_phantom.py +++ b/openerp/addons/base/tests/test_phantom.py @@ -1,11 +1,14 @@ # -*- coding: utf-8 -*- +import os +import glob + import openerp -from openerp.tests import common -class test_phantom(common.HttpCase): - - def test_01_dummy(self): - self.phantomjs(openerp.modules.module.get_module_resource('base','tests','test_phantom_dummy.js')) +class test_phantom(openerp.tests.HttpCase): + def test_phantom(self): + fname, _ = os.path.splitext(__file__) + for i in glob.glob('%s*.js' % fname): + self.phantomjs(openerp.modules.module.get_module_resource('base','tests', i)) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/tests/test_phantom_dummy.js b/openerp/addons/base/tests/test_phantom_dummy.js deleted file mode 100644 index 7a1f2b362af..00000000000 --- a/openerp/addons/base/tests/test_phantom_dummy.js +++ /dev/null @@ -1,4 +0,0 @@ -console.log('{ "event": "success", "message": "Phantomjs success"}'); -// For a failure: -// console.log('{ "event": "failure", "message": "The test failed" }'); -phantom.exit(); diff --git a/openerp/addons/base/tests/test_phantom_hello.js b/openerp/addons/base/tests/test_phantom_hello.js new file mode 100644 index 00000000000..6a453e947aa --- /dev/null +++ b/openerp/addons/base/tests/test_phantom_hello.js @@ -0,0 +1,5 @@ +console.log("hello from test_phantom_hello.js"); +console.log("ok"); +// For a failure use: +//console.log("error"); +phantom.exit(); diff --git a/openerp/addons/base/tests/test_phantom_run.js b/openerp/addons/base/tests/test_phantom_run.js new file mode 100644 index 00000000000..77277a635ce --- /dev/null +++ b/openerp/addons/base/tests/test_phantom_run.js @@ -0,0 +1,6 @@ +// Load helper +phantom.injectJs(phantom.args[0]); + +pt = new PhantomTest(); +pt.run("/", "console.log('ok')", "console"); + diff --git a/openerp/addons/base/tests/test_phantom_run_admin.js b/openerp/addons/base/tests/test_phantom_run_admin.js new file mode 100644 index 00000000000..8e684eff66d --- /dev/null +++ b/openerp/addons/base/tests/test_phantom_run_admin.js @@ -0,0 +1,6 @@ +// Load helper +phantom.injectJs(phantom.args[0]); + +pt = new PhantomTest(); +pt.run_admin("/", "console.log('ok')", "console"); + diff --git a/openerp/service/server.py b/openerp/service/server.py index aaf469c5538..5372459cd41 100644 --- a/openerp/service/server.py +++ b/openerp/service/server.py @@ -845,7 +845,7 @@ def load_test_file_py(test_file): result = unittest2.TextTestRunner(verbosity=2, stream=openerp.modules.module.TestStream()).run(suite) if not result.wasSuccessful(): r = False - _logger.error('module %s: at least one error occurred in a test', module_name) + _logger.error('%s: at least one error occurred in a test', test_file) def preload_registries(dbnames): """ Preload a registries, possibly run a test file.""" diff --git a/openerp/tests/__init__.py b/openerp/tests/__init__.py new file mode 100644 index 00000000000..bdf699e6467 --- /dev/null +++ b/openerp/tests/__init__.py @@ -0,0 +1,2 @@ +import common +from common import * diff --git a/openerp/tests.py b/openerp/tests/common.py similarity index 75% rename from openerp/tests.py rename to openerp/tests/common.py index 9af002f5fcc..c91ef0134d4 100644 --- a/openerp/tests.py +++ b/openerp/tests/common.py @@ -5,20 +5,18 @@ helpers and classes to write tests. """ import json +import logging import os import select import subprocess +import sys import threading import time import unittest2 import uuid import xmlrpclib -import logging -import sys import openerp -# backward compatbility -common = sys.modules['openerp.tests.common'] = sys.modules['openerp.tests'] _logger = logging.getLogger(__name__) @@ -140,15 +138,12 @@ class HttpCase(SingleTransactionCase): def phantomjs(self, jsfile, timeout=30, options=None): """ Phantomjs Test protocol. - Use console.log in phantomjs to output test results evrey line must be - a one line JSON message using the following format: + Use console.log in phantomjs to output test results: - - for a success: { "event": "success", "message": "Log message" } - - for an error: { "event": "error", "message": "Short error description" } + - for a success: console.log("ok") + - for an error: console.log("error") - if a non json parsable line is received the helper will raise an - exception, the output buffer will be printed and phantom will be - killed + Other lines are relayed to the test log. """ self.timeout = timeout @@ -156,38 +151,35 @@ class HttpCase(SingleTransactionCase): 'timeout' : timeout, 'port': PORT, 'db': DB, - 'user': ADMIN_USER, + 'login': ADMIN_USER, 'password': ADMIN_PASSWORD, 'session_id': self.session_id, } if options: self.options.update(options) - self.ignore_filters = [ - # Ignore phantomjs warnings - "*** WARNING:", - # Fixes an issue with PhantomJS 1.9.2 on OS X 10.9 (Mavericks) - # cf. https://github.com/ariya/phantomjs/issues/11418 - "CoreText performance note", - ] + phantomtest = os.path.join(os.path.dirname(__file__), 'phantomtest.js') - cmd = ['phantomjs', jsfile, json.dumps(self.options)] + # phantom.args[0] == phantomtest path + # phantom.args[1] == options + cmd = ['phantomjs', jsfile, phantomtest, json.dumps(self.options)] _logger.info('executing %s', cmd) try: phantom = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except OSError: _logger.info("phantomjs not found, test %s skipped", jsfile) - try: t0 = time.time() buf = '' while 1: + # timeout if time.time() > t0 + self.timeout: - raise Exception("Phantom js timeout (%ss)" % self.timeout) + raise Exception("phantomjs test timeout (%ss)" % self.timeout) + # read a byte ready, _, _ = select.select([phantom.stdout], [], [], 0.5) if ready: - s = phantom.stdout.read(4096) + s = phantom.stdout.read(1) if s: buf += s else: @@ -196,20 +188,12 @@ class HttpCase(SingleTransactionCase): # process lines if '\n' in buf: line, buf = buf.split('\n', 1) - if line not in self.ignore_filters: - try: - line_json = json.loads(line) - if line_json.get('event') == 'success': - _logger.info(line_json.get('message','ok')) - continue - elif line_json.get('event') == 'error': - err = line_json.get('message','error') - _logger.info(err) - else: - err = line + buf - except ValueError: - err = line + buf - raise Exception(err) + _logger.info("phantomjs: %s", line) + if line == "ok": + _logger.info("phantomjs test successful") + return + if line == "error": + raise Exception("phantomjs test failed") finally: # kill phantomjs if phantom.exit() wasn't called in the test if phantom.poll() is None: diff --git a/openerp/tests/phantomtest.js b/openerp/tests/phantomtest.js new file mode 100644 index 00000000000..accc119f3d8 --- /dev/null +++ b/openerp/tests/phantomtest.js @@ -0,0 +1,118 @@ +// Phantomjs openerp helper + +function waitFor (ready, callback, timeout, timeoutMessageCallback) { + timeout = timeout || 10000; + var start = new Date().getTime(); + var condition = ready(); + var interval = setInterval(function() { + if ((new Date().getTime() - start < timeout) && !condition ) { + condition = ready(); + } else { + if(!condition) { + var message = timeoutMessageCallback ? timeoutMessageCallback() : "Timeout after "+timeout+" ms"; + console.log(message); + console.log("Waiting for " + ready); + console.log("error"); + phantom.exit(1); + } else { + clearInterval(interval); + callback(); + } + } + }, 250); +} + +function PhantomTest() { + var self = this; + this.options = JSON.parse(phantom.args[1]); + this.inject = []; + this.timeout = this.options.timeout ? Math.round(parseFloat(this.options.timeout)*1000 - 5000) : 10000; + this.origin = 'http://localhost'; + this.origin += this.options.port ? ':' + this.options.port : ''; + + // ---------------------------------------------------- + // test reporting + // ---------------------------------------------------- + this.error = function(message) { + console.log(message); + console.log("error"); + phantom.exit(1); + }; + + // ---------------------------------------------------- + // configure page + // ---------------------------------------------------- + this.page = require('webpage').create(); + this.page.viewportSize = { width: 1366, height: 768 }; + this.page.addCookie({ + 'domain': 'localhost', + 'name': 'session_id', + 'value': this.options.session_id, + }); + this.page.onError = function(message, trace) { + self.error(message + " " + trace); + }; + this.page.onAlert = function(message) { + self.error(message); + }; + this.page.onConsoleMessage = function(message) { + console.log(message); + }; + this.page.onLoadFinished = function(status) { + if (status === "success") { + for (var k in self.inject) { + if(!page.injectJs(self.inject[k])) { + self.error("Can't inject " + self.inject[k]); + } + } + } + }; + setTimeout(function () { + self.page.evaluate(function () { + var message = ("Timeout\nhref: " + window.location.href + + "\nreferrer: " + document.referrer + + "\n\n" + document.body.innerHTML).replace(/[^a-z0-9\s~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, "*"); + self.error(message); + }); + }, self.timeout); + + // ---------------------------------------------------- + // run test + // ---------------------------------------------------- + this.run = function(url_path, code, ready) { + var url = self.origin + url_path; + self.page.open(url, function(status) { + if (status !== 'success') { + self.error("failed to load " + url) + } else { + console.log('loaded', url, status); + // process ready + waitFor(function() { + return self.page.evaluate(function (ready) { + var r = false; + try { + r = !!eval(ready); + } catch(ex) { + console.log("waiting for page " + ready) + }; + return r; + }, ready); + // run test + }, function() { + self.page.evaluate(function (code) { return eval(code); }, code); + }); + } + }); + }; + this.run_admin = function(url_path, code, ready) { + qp = []; + qp.push('db=' + self.options.db); + qp.push('login=' + self.options.login); + qp.push('key=' + self.options.password); + qp.push('redirect=' + encodeURIComponent(url_path)); + var url_path2 = "/web/login?" + qp.join('&'); + return self.run(url_path2, code, ready); + }; +} + +// vim:et: