--- a/_apycotlib/atest.py Thu May 02 12:40:58 2013 +0200
+++ b/_apycotlib/atest.py Fri Apr 25 15:56:11 2014 +0200
@@ -57,12 +57,13 @@
sys.path.insert(0, clean_path(path))
+from narvalbot.engine import options_dict
class Test(object):
"""the single source unit test class"""
def __init__(self, texec, writer):
- options = texec.options_dict()
+ options = options_dict(texec['options'])
# directory where the test environment will be built
self.tmpdir = tempfile.mkdtemp(dir=options.get('test_dir'))
# notify some subprocesses they're executed by apycot through an
@@ -70,16 +71,16 @@
os.environ['APYCOT_ROOT'] = self.tmpdir
# test config / tested project environment
self.texec = texec
- self.tconfig = texec.configuration
- self.environment = texec.environment
+ self.tconfig = texec['configuration']
+ self.environment = texec['environment']
# IWriter object
self.writer = writer
# local caches
self._configs = {}
self._repositories = {}
# environment variables as a dictionary
- self.environ = self.tconfig.apycot_process_environment()
- self.environ.update(self.environment.apycot_process_environment())
+ self.environ = self.tconfig['apycot_process_environment']
+ self.environ.update(self.environment['apycot_process_environment'])
self.environ.setdefault('LC_ALL', 'fr_FR.UTF-8') # XXX force utf-8
self._substitute(self.environment, self.environ)
# Track environment change to be able to restore it later.
@@ -100,7 +101,7 @@
def _substitute(self, pe, configdict):
substitute_dict(configdict,
- {'NAME': pe.name, 'TESTDIR': self.tmpdir,
+ {'NAME': pe['name'], 'TESTDIR': self.tmpdir,
'SRCDIR': self.apycot_repository(pe).co_path})
# resource accessors #######################################################
@@ -109,10 +110,12 @@
if pe is None:
pe = self.environment
try:
- return self._configs[pe.eid]
+ return self._configs[pe['eid']]
except KeyError:
- config = self.tconfig.apycot_configuration(pe)
- self._configs[pe.eid] = config
+ config = self.writer._cnxh.http_post(self.tconfig['cwuri'],
+ vid='apycot.get_configuration',
+ environment=pe['eid'])[0]
+ self._configs[pe['eid']] = config
self._substitute(pe, config)
return config
@@ -120,22 +123,22 @@
if pe is None:
pe = self.environment
try:
- return self._repositories[pe.eid]
+ return self._repositories[pe['eid']]
except KeyError:
from apycotlib.repositories import get_repository
- if not pe.repository:
- raise Exception('Project environment %s has no repository' % pe.dc_title())
- repdef = {'repository': pe.repository,
- 'path': pe.vcs_path,
- 'branch': self.texec.branch}
+ if not pe['repository']:
+ raise Exception('Project environment %s has no repository' % pe['title'])
+ repdef = {'repository': pe['repository'],
+ 'path': pe['vcs_path'],
+ 'branch': self.texec['branch']}
# don't overwrite branch hardcoded on the environment: have to be
# done here, not only in when starting plan (eg in entities.py)
# since project env may not be the tested project env
- pecfg = pe.apycot_configuration()
+ pecfg = pe['apycot_configuration']
if pecfg.get('branch'):
repdef['branch'] = pecfg['branch']
apyrep = get_repository(repdef)
- self._repositories[pe.eid] = apyrep
+ self._repositories[pe['eid']] = apyrep
return apyrep
def project_path(self, subpath=False):
@@ -153,7 +156,7 @@
# setup environment variables
if self.environ:
for key, val in self.environ.iteritems():
- self.update_env(self.tconfig.name, key, val)
+ self.update_env(self.tconfig['name'], key, val)
def clean(self):
"""clean the test environment"""
@@ -233,7 +236,7 @@
Command(self.writer, movebranchcmd, shell=True,
cwd=self.tmpdir).run()
self.writer.link_to_revision(pe, vcsrepo)
- self.writer.refresh_log(True)
+ self.writer.refresh_log()
def call_preprocessor(self, pptype, penv):
cfg = self.apycot_config(penv)
@@ -263,7 +266,7 @@
def run_checker(self, id, nonexecuted=False, **kwargs):
"""run all checks in the test environment"""
- options = self.texec.options_dict()
+ options = options_dict(self.texec['options'])
options.update(kwargs)
self._substitute(self.environment, options)
check_writer = self.writer.make_check_writer()
@@ -302,6 +305,5 @@
check_writer.end(status)
#globstatus = min(globstatus, status)
self.writer.execution_info('%s [%s]', checker.id, status)
- with self.writer._lock:
- self.global_status = min(self.global_status, status)
+ self.global_status = min(self.global_status, status)
return checker, status
--- a/_apycotlib/narvalactions.py Thu May 02 12:40:58 2013 +0200
+++ b/_apycotlib/narvalactions.py Fri Apr 25 15:56:11 2014 +0200
@@ -7,8 +7,8 @@
self.plan = plan
def __enter__(self):
- w = writer.TestDataWriter(self.plan.cnxh, self.plan.cwplan.eid)
- test = atest.Test(self.plan.cwplan, w)
+ w = writer.TestDataWriter(self.plan.cnxh, self.plan.plandata['cwuri'])
+ test = atest.Test(self.plan.plandata, w)
test.setup()
self.test = test
return test
@@ -20,8 +20,7 @@
def install_environment(test):
- tconfig = test.tconfig
- environment = test.environment
- for dep in tconfig.dependencies(environment) + [environment]:
+ data = test.writer._cnxh.http_post(test.texec['cwuri'], vid='apycot.get_dependencies')
+ for dep in data[0]:
test.checkout(dep)
test.call_preprocessor('install', dep)
--- a/_apycotlib/repositories.py Thu May 02 12:40:58 2013 +0200
+++ b/_apycotlib/repositories.py Fri Apr 25 15:56:11 2014 +0200
@@ -22,7 +22,7 @@
"""factory method: return a repository implementation according to
<attrs> (a dictionary)
"""
- repo_type = attrs['repository'].type
+ repo_type = attrs['repository']['type']
assert repo_type in SUPPORTED_REPO_TYPES, repo_type
return get_registered('repository', repo_type)(attrs)
@@ -63,7 +63,7 @@
def __repr__(self):
"""get a string synthetizing the location"""
- myrepr = '%s:%s' % (self.id, self.repository.source_url or self.repository.path)
+ myrepr = '%s:%s' % (self.id, self.repository['source_url'] or self.repository['path'])
if self.path:
myrepr += '/' + self.path
if self.branch:
@@ -111,7 +111,7 @@
id = 'subversion'
def _ref_repo(self):
- return self.repository.source_url
+ return self.repository['source_url']
def _co_path(self):
"""return the path where the project will be located in the test
@@ -148,15 +148,15 @@
default_branch = "default"
def _ref_repo(self):
- if self.repository.local_cache:
+ if False and self.repository['local_cache']:
cnx = self.repository._cw.cnx
cacheroot = cnx.get_option_value('local-repo-cache-root')
path = osp.join(cacheroot, self.repository.local_cache)
if osp.exists(path):
return path
- if self.repository.path and osp.exists(self.repository.path):
- return self.repository.path
- return self.repository.source_url
+ if self.repository['path'] and osp.exists(self.repository['path']):
+ return self.repository['path']
+ return self.repository['source_url']
def _co_path(self):
"""return the path where the project will be located in the test
--- a/_apycotlib/writer.py Thu May 02 12:40:58 2013 +0200
+++ b/_apycotlib/writer.py Fri Apr 25 15:56:11 2014 +0200
@@ -104,13 +104,12 @@
a CubicWeb instance (using the apycot cube)
"""
- def __init__(self, cnxh, target_eid):
+ def __init__(self, cnxh, target_url):
self._cnxh = cnxh
# eid of the execution entity
- self._eid = target_eid
+ self._url = target_url
self._logs = []
self._logs_sent = 0
- self._lock = RLock()
def start(self):
pass
@@ -119,11 +118,7 @@
pass
def set_exec_status(self, status):
- with self._lock:
- self._cnxh.execute(
- 'SET X status %(status)s WHERE X eid %(x)s',
- {'status': status, 'x': self._eid})
- self._cnxh.commit()
+ self._cnxh.http_post(self._url, vid='set_attributes', status=status)
def execution_info(self, *args, **kwargs):
msg = self._msg_info(*args, **kwargs)[-1]
@@ -137,26 +132,21 @@
xml_escape(msg))
self._logs.append(encodedmsg)
- def raw(self, name, value, type=None, commit=True):
+ def raw(self, name, value, type=None):
"""give some raw data"""
- with self._lock:
- self._cnxh.cw.create_entity(
- 'CheckResultInfo', label=self._unicode(name),
- value=self._unicode(value), type=type and unicode(type),
- for_check=self._cnxh.cw.entity_from_eid(self._eid))
- if commit:
- self._cnxh.commit()
+ self._cnxh.http_post(self._url, vid='create_subentity',
+ __cwetype__='CheckResultInfo',
+ __cwrel__='for_check',
+ label=self._unicode(name),
+ value=self._unicode(value),
+ type=type and unicode(type))
- def refresh_log(self, flush=True):
+ def refresh_log(self):
log = self._logs
- with self._lock:
- if self._logs_sent < len(log):
- self._cnxh.execute(
- 'SET X log %(log)s WHERE X eid %(x)s',
- {'log': u'\n'.join(log), 'x': self._eid})
- self._log_sent = len(log)
- if flush:
- self._cnxh.commit()
+ if self._logs_sent < len(log):
+ self._cnxh.http_post(self._url, vid='set_attributes',
+ log=u'\n'.join(log))
+ self._log_sent = len(log)
class CheckDataWriter(BaseDataWriter):
@@ -164,31 +154,26 @@
def start(self, checker):
"""Register the given checker as started"""
- with self._lock:
- crname = getattr(checker, 'id', checker) # may be the checked id
- self._eid = self._cnxh.cw.create_entity(
- 'CheckResult', name=self._unicode(crname), status=u'processing',
- starttime=datetime.now(),
- during_execution=self._cnxh.cw.entity_from_eid(self._eid)).eid
- if hasattr(checker, 'options'):
- options = ['%s=%s' % (k, v) for k, v in checker.options.iteritems()
- if k in checker.options_def
- and v != checker.options_def[k].get('default')]
- if options:
- self.info('\n'.join(options))
- self.refresh_log(flush=False)
- self._cnxh.commit()
+ crname = getattr(checker, 'id', checker) # may be the checked id
+ data = self._cnxh.http_post(self._url, vid='create_subentity',
+ __cwetype__='CheckResult',
+ __cwrel__='during_execution',
+ name=self._unicode(crname), status=u'processing',
+ starttime=datetime.now())
+ self._url = data[0]['cwuri']
+ if hasattr(checker, 'options'):
+ options = ['%s=%s' % (k, v) for k, v in checker.options.iteritems()
+ if k in checker.options_def
+ and v != checker.options_def[k].get('default')]
+ if options:
+ self.info('\n'.join(options))
+ self.refresh_log()
def end(self, status):
"""Register the given checker as closed with status <status>"""
- with self._lock:
- """end of the latest started check"""
- self._cnxh.execute(
- 'SET X status %(status)s, X endtime %(endtime)s, X log %(log)s '
- 'WHERE X eid %(x)s',
- {'status': self._unicode(status), 'endtime': datetime.now(),
- 'log': u'\n'.join(self._logs), 'x': self._eid})
- self._cnxh.commit()
+ self._cnxh.http_post(self._url, vid='set_attributes',
+ status=self._unicode(status), endtime=datetime.now(),
+ log=u'\n'.join(self._logs),)
class TestDataWriter(BaseDataWriter):
@@ -197,52 +182,36 @@
def make_check_writer(self):
"""Return a CheckDataWriter suitable to write checker log and result within this test"""
self.refresh_log()
- return CheckDataWriter(self._cnxh, self._eid)
+ return CheckDataWriter(self._cnxh, self._url)
def link_to_revision(self, environment, vcsrepo):
changeset = vcsrepo.changeset()
if changeset is not None:
- if not self._cnxh.execute(
- 'SET X using_revision REV '
- 'WHERE X eid %(x)s, REV changeset %(cs)s, '
- 'REV from_repository R, R eid %(r)s, '
- 'NOT X using_revision REV',
- {'x': self._eid, 'cs': changeset,
- 'r': environment.repository.eid}):
- self.raw(repr(vcsrepo), changeset, 'revision')
+ self.raw(repr(vcsrepo), changeset, 'revision')
def start(self):
self.set_exec_status(u'set up')
def end(self, status, archivedir=None):
"""mark the current test as closed (with status <status>) and archive if requested."""
- with self._lock:
- """end of the test execution"""
- if self._logs_sent < len(self._logs):
- self._cnxh.execute('SET X status %(status)s, X log %(log)s WHERE X eid %(x)s',
- {'log': u'\n'.join(self._logs),
- 'status': self._unicode(status),
- 'x': self._eid})
- else:
- self._cnxh.execute('SET X status %(status)s WHERE X eid %(x)s',
- {'status': self._unicode(status),
- 'x': self._eid})
- self._cnxh.commit()
- if archivedir:
- archive = make_archive_name(self._cnxh.cwinstid, self._eid)
- archivefpath = os.path.join(tempfile.gettempdir(), archive)
- tarball = tarfile.open(archivefpath, ARCHIVE_MODE)
- try:
- tarball.add(archivedir)
- tarball.close()
- self._cnxh.cw.create_entity(
- 'File', data=Binary(open(archivefpath, 'rb').read()),
- data_format=u'application/x-bzip2',
- data_name=unicode(archive),
- reverse_log_file=self._cnxh.cw.entity_from_eid(self._eid))
- except:
- self.error('while archiving execution directory', tb=True)
- finally:
- os.unlink(archivefpath)
- self._cnxh.commit()
+ self.refresh_log()
+ self._cnxh.http_post(self._url, vid='set_attributes',
+ status = self._unicode(status))
+ if False and archivedir: # XXX this should be refactored! (mostly) useless as is
+ archive = make_archive_name(self._cnxh.cwinstid, self._url)
+ archivefpath = os.path.join(tempfile.gettempdir(), archive)
+ tarball = tarfile.open(archivefpath, ARCHIVE_MODE)
+ try:
+ tarball.add(archivedir)
+ tarball.close()
+ self._cnxh.http_post(self._url, vid='create_subentity',
+ __cwetype__='File',
+ __cwrel__='reverse_log_file',
+ data=Binary(open(archivefpath, 'rb').read()),
+ data_format=u'application/x-bzip2',
+ data_name=unicode(archive))
+ except:
+ self.error('while archiving execution directory', tb=True)
+ finally:
+ os.unlink(archivefpath)
--- a/entities.py Thu May 02 12:40:58 2013 +0200
+++ b/entities.py Fri Apr 25 15:56:11 2014 +0200
@@ -201,6 +201,13 @@
return tc
return
+ def __json_encode__(self):
+ data = super(ProjectEnvironment, self).__json_encode__()
+ data['apycot_process_environment'] = self.apycot_process_environment()
+ data['repository'] = self.repository
+ data['apycot_configuration'] = self.apycot_configuration()
+ return data
+
# Test configuration ###########################################################
@@ -328,6 +335,11 @@
priority=priority)
return texec
+ def __json_encode__(self):
+ data = super(TestConfig, self).__json_encode__()
+ data['apycot_process_environment'] = self.apycot_process_environment()
+ return data
+
class TestExecution(Plan, ExecutionRSSMixin):
__regid__ = 'TestExecution'
@@ -452,6 +464,11 @@
if rev.repository.eid == repository.eid:
return rev
+ def __json_encode__(self):
+ data = super(TestExecution, self).__json_encode__()
+ data['environment'] = self.environment
+ data['configuration'] = self.configuration
+ return data
class CheckResult(AnyEntity):
__regid__ = 'CheckResult'
--- a/hooks.py Thu May 02 12:40:58 2013 +0200
+++ b/hooks.py Fri Apr 25 15:56:11 2014 +0200
@@ -193,6 +193,22 @@
StartTestOp.get_instance(self._cw).add_data(revision)
+class AutolinkToRevision(hook.Hook):
+ __regid__ = 'apycot.auto_link_to_revision'
+ __select__ = hook.Hook.__select__ & hook.match_rtype('for_check')
+ events = ('after_add_relation',)
+
+ def __call__(self):
+ cri = self._cw.entity_from_eid(self.eidfrom)
+ if cri.type != 'revision':
+ return
+ cr = self.eidto
+ if self._cw.execute(
+ 'SET X using_revision R WHERE X eid %(cr)s, R changeset %(cs)s, NOT X using_revision R',
+ {'cr': cr, 'cs': cri.value}):
+ cri.cw_delete()
+
+
# notifications ################################################################
class ExecStatusChangeView(notifviews.NotificationView):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/views/rest.py Fri Apr 25 15:56:11 2014 +0200
@@ -0,0 +1,47 @@
+# copyright 2010-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+
+__docformat__ = "restructuredtext en"
+_ = unicode
+
+from cubicweb.web.views import json
+from cubicweb.predicates import is_instance, match_form_params
+
+
+class GetDependencies(json.JsonEntityView):
+ __regid__ = 'apycot.get_dependencies'
+ __select__ = is_instance('TestExecution')
+
+ def call(self):
+ data = []
+ for entity in self.cw_rset.entities():
+ tconfig = entity.configuration
+ env = entity.environment
+ data.append(tconfig.dependencies(env) + [env])
+ self.wdata(data)
+
+class GetConfiguration(json.JsonEntityView):
+ __regid__ = 'apycot.get_configuration'
+ __select__ = is_instance('TestConfig') & match_form_params('environment')
+
+ def call(self):
+ data = []
+ env = self._cw.entity_from_eid(self._cw.form['environment'])
+ for tconfig in self.cw_rset.entities():
+ data.append(tconfig.apycot_configuration(env))
+ self.wdata(data)
+
+