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

Subscribers

People subscribed via source and target branches