Merge lp:~zyga/snapcraft/plainbox-app into lp:~snappy-dev/snapcraft/core

Proposed by Zygmunt Krynicki
Status: Needs review
Proposed branch: lp:~zyga/snapcraft/plainbox-app
Merge into: lp:~snappy-dev/snapcraft/core
Diff against target: 219 lines (+215/-0)
1 file modified
runtests.py (+215/-0)
To merge this branch: bzr merge lp:~zyga/snapcraft/plainbox-app
Reviewer Review Type Date Requested Status
Federico Gimenez (community) Needs Information
Review via email: mp+272720@code.launchpad.net

Commit message

Add runtests.py

To post a comment you must log in.
lp:~zyga/snapcraft/plainbox-app updated
213. By Zygmunt Krynicki

Add runtests.py

This patch adds a new tool for running tests, runtests.py, intending to
eventually replace the runtests.sh shell script.

This tool uses Plainbox APIs that are only available on python3-plainbox
0.23 (which pulls in python3-guacamole 0.9). The script is feature
complete as far as integration tests go but doesn't (yet) handle unit
tests. I'm looking at how to support that separately.

This patch makes runtests.py take optional advantage of
python3-argcomplete. When installed and properly activated (see below),
runtests.py will complete the partial identifiers of the tests to run.
This makes it easier to run one or a few tests quickly.

To enable that in your active shell, run:

eval "$(register-python-argcomplete3 runtests.py)"

Signed-off-by: Zygmunt Krynicki <email address hidden>

Revision history for this message
Federico Gimenez (fgimenez) wrote :

Looks great, the only thing missing that I see is the ability to run different test plans, now it's fixed to "normal". In the runtests.sh script you can specify the kind of tests to be run (unit or plainbox) and, in the case of plainbox tests, the test plan to be run:

$ ./runtests.sh
$ ./runtests.sh unit
$ ./runtests.sh plainbox examples

Are you planning to add this feature to the script?

Perhaps it would be also nice to get a more verbose output through an option, what do you think?

Thanks,

review: Needs Information
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

I'm open to ideas, this MR was exactly to provoke feedback and
conversation. The second upside is that this is so trivial to modify
that you don't have to be a plainbox expert to tweak it. As for your
questions:

I'd like to integrate unit tests but there are two options: as a
plainbox job or natively from within the tool. I'll experiment which
works better.

Test plans is that something that the current script ran, we can
default to normal and switch to --examples with an option (it's
trivial as you see). If we integrate unit tests into test plans then
it would just be a selection of a test plan that matters (and we could
have a .* test plan that runs everything).

What kind of verbose output would you like to have, to show live
output as we run (not only when we fail?)

Thanks
ZK

On Wed, Sep 30, 2015 at 10:29 AM, Federico Gimenez
<email address hidden> wrote:
> Review: Needs Information
>
> Looks great, the only thing missing that I see is the ability to run different test plans, now it's fixed to "normal". In the runtests.sh script you can specify the kind of tests to be run (unit or plainbox) and, in the case of plainbox tests, the test plan to be run:
>
> $ ./runtests.sh
> $ ./runtests.sh unit
> $ ./runtests.sh plainbox examples
>
> Are you planning to add this feature to the script?
>
> Perhaps it would be also nice to get a more verbose output through an option, what do you think?
>
> Thanks,
> --
> https://code.launchpad.net/~zyga/snapcraft/plainbox-app/+merge/272720
> You are the owner of lp:~zyga/snapcraft/plainbox-app.

Revision history for this message
Federico Gimenez (fgimenez) wrote :

> Test plans is that something that the current script ran, we can
> default to normal and switch to --examples with an option (it's
> trivial as you see).

There's another thing here, for running the examples test plan we had to make some adjustments in the runtests.sh script [1], because plainbox was not able to reach the files out of $PLAINBOX_PROVIDER_DATA. Are you aware of that, will this patch take that into account?

[1]http://bazaar.launchpad.net/~snappy-dev/snapcraft/core/view/head:/integration-tests/runtests.sh#L37

> If we integrate unit tests into test plans then
> it would just be a selection of a test plan that matters (and we could
> have a .* test plan that runs everything).
>

Yes, that would be great

> What kind of verbose output would you like to have, to show live
> output as we run (not only when we fail?)
>

I think that it would be useful for debugging to have the same output as we do now with the plainbox run call used in runtests.sh, which shows all the commands executed.

Also there are some changes required for autopkgtest to work, the new dependencies should be added in [2] (including required version numbers if needed) and the calls at [3] and [4] should be adapted to the new script.

[2] http://bazaar.launchpad.net/~snappy-dev/snapcraft/core/view/head:/debian/tests/control
[3] http://bazaar.launchpad.net/~snappy-dev/snapcraft/core/view/head:/debian/tests/runexamples
[4] http://bazaar.launchpad.net/~snappy-dev/snapcraft/core/view/head:/debian/tests/runtests

Thanks!

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

I'm not aware of any issues with $PLAINBOX_PROVIDER_DATA. I'll rebase
this (there's a 2nd patch I didn't push that's a pain to touch as I
changed the IDs of each job) and see if I can reproduce it.

I get your point for other comments. I'll iterate on this. Thanks for
the feedback. :-)

On Wed, Sep 30, 2015 at 3:55 PM, Federico Gimenez
<email address hidden> wrote:
>> Test plans is that something that the current script ran, we can
>> default to normal and switch to --examples with an option (it's
>> trivial as you see).
>
> There's another thing here, for running the examples test plan we had to make some adjustments in the runtests.sh script [1], because plainbox was not able to reach the files out of $PLAINBOX_PROVIDER_DATA. Are you aware of that, will this patch take that into account?
>
> [1]http://bazaar.launchpad.net/~snappy-dev/snapcraft/core/view/head:/integration-tests/runtests.sh#L37
>
>> If we integrate unit tests into test plans then
>> it would just be a selection of a test plan that matters (and we could
>> have a .* test plan that runs everything).
>>
>
> Yes, that would be great
>
>> What kind of verbose output would you like to have, to show live
>> output as we run (not only when we fail?)
>>
>
> I think that it would be useful for debugging to have the same output as we do now with the plainbox run call used in runtests.sh, which shows all the commands executed.
>
> Also there are some changes required for autopkgtest to work, the new dependencies should be added in [2] (including required version numbers if needed) and the calls at [3] and [4] should be adapted to the new script.
>
> [2] http://bazaar.launchpad.net/~snappy-dev/snapcraft/core/view/head:/debian/tests/control
> [3] http://bazaar.launchpad.net/~snappy-dev/snapcraft/core/view/head:/debian/tests/runexamples
> [4] http://bazaar.launchpad.net/~snappy-dev/snapcraft/core/view/head:/debian/tests/runtests
>
> Thanks!
>
> --
> https://code.launchpad.net/~zyga/snapcraft/plainbox-app/+merge/272720
> You are the owner of lp:~zyga/snapcraft/plainbox-app.

Unmerged revisions

213. By Zygmunt Krynicki

Add runtests.py

This patch adds a new tool for running tests, runtests.py, intending to
eventually replace the runtests.sh shell script.

This tool uses Plainbox APIs that are only available on python3-plainbox
0.23 (which pulls in python3-guacamole 0.9). The script is feature
complete as far as integration tests go but doesn't (yet) handle unit
tests. I'm looking at how to support that separately.

This patch makes runtests.py take optional advantage of
python3-argcomplete. When installed and properly activated (see below),
runtests.py will complete the partial identifiers of the tests to run.
This makes it easier to run one or a few tests quickly.

To enable that in your active shell, run:

eval "$(register-python-argcomplete3 runtests.py)"

Signed-off-by: Zygmunt Krynicki <email address hidden>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'runtests.py'
2--- runtests.py 1970-01-01 00:00:00 +0000
3+++ runtests.py 2015-09-29 11:47:32 +0000
4@@ -0,0 +1,215 @@
5+#!/usr/bin/env python3
6+# PYTHON_ARGCOMPLETE_OK
7+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
8+#
9+# Copyright (C) 2015 Canonical Ltd
10+#
11+# This program is free software: you can redistribute it and/or modify
12+# it under the terms of the GNU General Public License version 3 as
13+# published by the Free Software Foundation.
14+#
15+# This program is distributed in the hope that it will be useful,
16+# but WITHOUT ANY WARRANTY; without even the implied warranty of
17+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+# GNU General Public License for more details.
19+#
20+# You should have received a copy of the GNU General Public License
21+# along with this program. If not, see <http://www.gnu.org/licenses/>.
22+
23+
24+"""Snapcraft Test Runner"""
25+
26+import os
27+
28+from guacamole import Command
29+from plainbox.abc import IJobRunnerUI
30+from plainbox.impl.secure.providers.v1 import Provider1
31+from plainbox.impl.secure.providers.v1 import Provider1Definition
32+from plainbox.impl.secure.qualifiers import select_jobs
33+from plainbox.impl.session.assistant import SessionAssistant
34+
35+
36+class runtests(Command):
37+
38+ """
39+ Tool for running snapcraft tests
40+
41+ Run snapcraft unit and integration tests
42+
43+ @EPILOG@
44+
45+ Without any arguments all tests are run in sequence. Specifying any job
46+ identifier runs only the jobs listed.
47+ """
48+
49+ #: Identifier of the application, used by guacamole
50+ app_id = 'snapcraft-runtests'
51+
52+ #: Plainbox namespace used by the snapcraft provider
53+ ns = '2015.com.canonical.snapcraft'
54+
55+ # Plainbox test plan that is started
56+ test_plan_id = '{}::normal'.format(ns)
57+
58+ def register_arguments(self, parser):
59+ parser.add_argument(
60+ 'job_ids', metavar='JOB', nargs="*",
61+ help="ID of the job to run"
62+ ).completer = JobsInTestPlanCompleter(
63+ self.test_plan_id, self._get_snapcraft_provider_def()
64+ )
65+
66+ def invoked(self, ctx):
67+ """Method called when the command is invoked."""
68+ self._setup_environment()
69+ summary = self._run_integration_tests(ctx)
70+ # Come up with an useful exit status.
71+ #
72+ # Ignoring the passing tests. All other outcomes are something we want
73+ # to flag on (crash, fail, etc). Perhaps we want to ignore 'skip'
74+ # later on but now nothing should trigger that.
75+ if 'pass' in summary:
76+ del summary['pass']
77+ return 1 if summary else 0
78+
79+ def _run_integration_tests(self, ctx):
80+ sa = SessionAssistant(self.app_id)
81+ sa.select_providers(
82+ "{}:tests".format(self.ns), additional_providers=[
83+ self._get_snapcraft_provider()
84+ ])
85+ sa.start_new_session("Snapcraft Integration Tests")
86+ sa.select_test_plan("{}::normal".format(self.ns))
87+ sa.bootstrap()
88+ if ctx.args.job_ids:
89+ sa.use_alternate_selection([
90+ '{}::{}'.format(self.ns, job_id)
91+ for job_id in ctx.args.job_ids])
92+ ui = _SnapcraftUI(ctx)
93+ for job_id in sa.get_static_todo_list():
94+ builder = sa.run_job(job_id, ui, native=False)
95+ result = builder.get_result()
96+ sa.use_job_result(job_id, result)
97+ sa._metadata.flags.add('submitted') # LP: https://pad.lv/1500799
98+ sa.finalize_session()
99+ return sa.get_summary()
100+
101+ def _setup_environment(self):
102+ cwd = os.getcwd()
103+ os.environ['PATH'] = os.path.expandvars("{}/bin:$PATH".format(cwd))
104+ os.environ['PYTHONPATH'] = os.path.expandvars(
105+ "{}:$PYTHONPATH".format(cwd))
106+ os.environ['SNAPCRAFT'] = 'snapcraft'
107+
108+ def _get_snapcraft_provider_def(self):
109+ definition = Provider1Definition()
110+ definition.name = "{}:tests".format(self.ns)
111+ definition.description = "Plainbox test provider for Snapcraft"
112+ definition.relocatable = True
113+ definition.version = "1.0"
114+ definition.location = os.path.abspath('integration-tests')
115+ return definition
116+
117+ def _get_snapcraft_provider(self):
118+ return Provider1.from_definition(
119+ self._get_snapcraft_provider_def(), secure=False)
120+
121+
122+class _SnapcraftUI(IJobRunnerUI):
123+
124+ def __init__(self, ctx):
125+ self.ctx = ctx
126+
127+ def considering_job(self, job, job_state):
128+ pass
129+
130+ def about_to_start_running(self, job, job_state):
131+ self.ctx.aprint(
132+ "{} ...".format(job.partial_id), end='', flush=True, bold=True)
133+
134+ def wait_for_interaction_prompt(self, job):
135+ pass
136+
137+ def started_running(self, job, job_state):
138+ pass
139+
140+ def about_to_execute_program(self, args, kwargs):
141+ pass
142+
143+ def finished_executing_program(self, returncode):
144+ pass
145+
146+ def got_program_output(self, stream_name, line):
147+ pass
148+
149+ def finished_running(self, job, job_state, job_result):
150+ if job_result.outcome == 'fail':
151+ self.ctx.aprint("\r{:50} {}".format(
152+ job.partial_id, job_result.outcome_meta().tr_label),
153+ flush=True, fg='red', bold=True)
154+ for io_log_record in job_result.get_io_log():
155+ self.ctx.aprint(
156+ io_log_record.data.decode("UTF-8", 'replace'),
157+ end='', fg='red')
158+ elif job_result.outcome == 'pass':
159+ self.ctx.aprint("\r{:50} {}".format(
160+ job.partial_id, job_result.outcome_meta().tr_label),
161+ flush=True, fg='green')
162+ else:
163+ self.ctx.aprint("\r{:50} {}".format(
164+ job.partial_id, job_result.outcome_meta().tr_label),
165+ flush=True, fg='yellow')
166+
167+ def notify_about_description(self, job):
168+ pass
169+
170+ def notify_about_purpose(self, job):
171+ pass
172+
173+ def notify_about_steps(self, job):
174+ pass
175+
176+ def notify_about_verification(self, job):
177+ pass
178+
179+ def job_cannot_start(self, job, job_state, job_result):
180+ pass
181+
182+ def finished(self, job, job_state, job_result):
183+ pass
184+
185+ def pick_action_cmd(self, action_list, prompt=None):
186+ pass
187+
188+ def noreturn_job(self):
189+ pass
190+
191+
192+class JobsInTestPlanCompleter:
193+
194+ def __init__(self, test_plan_id, provider_def):
195+ self._test_plan_id = test_plan_id
196+ self._provider_def = provider_def
197+ self._choices = None
198+
199+ def __call__(self, prefix, **kwargs):
200+ if self. _choices is None:
201+ self._choices = self._compute_choices()
202+ return (c for c in self._choices if c.startswith(prefix))
203+
204+ def _compute_choices(self):
205+ try:
206+ provider = Provider1.from_definition(
207+ self._provider_def, secure=False, check=False)
208+ test_plan = provider.id_map[self._test_plan_id][0]
209+ qualifier_list = [test_plan.get_qualifier()]
210+ return [job.partial_id for job in select_jobs(
211+ provider.job_list, qualifier_list)]
212+ except Exception:
213+ # If stuff blows up (maybe the provider is invalid), just don't
214+ # suggest anything.
215+ return []
216+
217+
218+if __name__ == '__main__':
219+ runtests().main()

Subscribers

People subscribed via source and target branches