Merge lp:~milo/linaro-image-tools/bug704029 into lp:linaro-image-tools/11.11

Proposed by Milo Casagrande
Status: Merged
Approved by: Deepti B. Kalakeri
Approved revision: 533
Merge reported by: Milo Casagrande
Merged at revision: not available
Proposed branch: lp:~milo/linaro-image-tools/bug704029
Merge into: lp:linaro-image-tools/11.11
Diff against target: 177 lines (+77/-19)
3 files modified
linaro_image_tools/tests/fixtures.py (+5/-4)
linaro_image_tools/tests/test_utils.py (+37/-10)
linaro_image_tools/utils.py (+35/-5)
To merge this branch: bzr merge lp:~milo/linaro-image-tools/bug704029
Reviewer Review Type Date Requested Status
Deepti B. Kalakeri (community) Needs Fixing
Milo Casagrande (community) Needs Fixing
Данило Шеган Pending
Review via email: mp+108910@code.launchpad.net

Description of the change

Hello,

in the proposed branch I applied kiko patch in order to be able to ask user if they want to install the required packages.

In the branch I modified a couple of things, only in the relevant functions:
- in 'install_package_providing': modified the user messages, suppressed the output from the installation process since the user was seeing apt-get messages (could be interesting to add an additional message that packages have been installed), added an addition exception class ('PackageInstallationRefused'), to handle the case where the user doesn't want to install the package (in this case it will raises an exception, and the process will terminate, making it impossible to continue using l-i-t).
- in 'tests_utils': reworked the test case where package installation was being tested: with the new functionalities in 'install_package_providing' we need the output from the process in order to parse it and check which packages should be installed, the older mock object was redirecting everything to /dev/null.
- fixed small pep8 warnings.

Thanks for the review.

To post a comment you must log in.
Revision history for this message
Milo Casagrande (milo) wrote :

I was a little bit too confident, and looks like the change proposed here, at least for the test, are not working.
Even reverting back using a temp file is not working anymore now, I guess there was something else that made it work once earlier today.

Will look a little bit more into it later today.

review: Needs Fixing
525. By Milo Casagrande

First step into fixing the mock object.

 * Fixed the mock object in order to return back
   a predefined output string.
 * Fixed the test function accordingly.
 * Fixed PEP8 warnings.

526. By Milo Casagrande

Merged code from trunk.

527. By Milo Casagrande

Renamed test class: conflicting with another one

528. By Milo Casagrande

Fixed test regression.

 * Fixed the test regression due to the expected user input.

Revision history for this message
Milo Casagrande (milo) wrote :

Hi,

the latest commits on this branch now fix the problem.

For testing the output part: since with the new functionalities we
need to output of the command, in order to parse it, I needed the mock
object to return some kind of output to the callable function. I
reworked the method in the mock objects in order to take an optional
argument 'output_string' where it is defined the expected output the
function should receive in order to proceed without errors. The
default value in this case in an empty string as that was the defined
return value for the 'communicate()' function.

For the input part the solution was, obviously, dead simple: I needed
a new fixture for stdin.

I also fixed a couple of PEP8 warnings and renamed a class name since
there were two classes with the same name.

--
Milo Casagrande
Infrastructure Engineer
Linaro.org <www.linaro.org> │ Open source software for ARM SoCs

Revision history for this message
Deepti B. Kalakeri (deeptik) wrote :
Download full text (10.3 KiB)

Changes looks good. But needs small fix. Please see comments in line.

On Wed, Jun 6, 2012 at 3:50 PM, Milo Casagrande
<email address hidden>wrote:

> Milo Casagrande has proposed merging lp:~milo/linaro-image-tools/bug704029
> into lp:linaro-image-tools.
>
> Requested reviews:
> Данило Шеган (danilo)
> Related bugs:
> Bug #704029 in Linaro Image Tools: "l-m-c should not install packages on
> host system without user consent"
> https://bugs.launchpad.net/linaro-image-tools/+bug/704029
>
> For more details, see:
> https://code.launchpad.net/~milo/linaro-image-tools/bug704029/+merge/108910<https://code.launchpad.net/%7Emilo/linaro-image-tools/bug704029/+merge/108910>
>
> Hello,
>
> in the proposed branch I applied kiko patch in order to be able to ask
> user if they want to install the required packages.
>
> In the branch I modified a couple of things, only in the relevant
> functions:
> - in 'install_package_providing': modified the user messages, suppressed
> the output from the installation process since the user was seeing apt-get
> messages (could be interesting to add an additional message that packages
> have been installed), added an addition exception class
> ('PackageInstallationRefused'), to handle the case where the user doesn't
> want to install the package (in this case it will raises an exception, and
> the process will terminate, making it impossible to continue using l-i-t).
> - in 'tests_utils': reworked the test case where package installation was
> being tested: with the new functionalities in 'install_package_providing'
> we need the output from the process in order to parse it and check which
> packages should be installed, the older mock object was redirecting
> everything to /dev/null.
> - fixed small pep8 warnings.
>
> Thanks for the review.
> --
> https://code.launchpad.net/~milo/linaro-image-tools/bug704029/+merge/108910<https://code.launchpad.net/%7Emilo/linaro-image-tools/bug704029/+merge/108910>
> Your team Linaro Infrastructure is subscribed to branch
> lp:linaro-image-tools.
>
> === modified file 'linaro_image_tools/tests/test_utils.py'
> --- linaro_image_tools/tests/test_utils.py 2012-04-18 13:26:25 +0000
> +++ linaro_image_tools/tests/test_utils.py 2012-06-06 10:19:24 +0000
> @@ -24,6 +24,7 @@
> import logging
> import tempfile
> import tarfile
> +from StringIO import StringIO
>
> from linaro_image_tools import cmd_runner, utils
> from linaro_image_tools.testing import TestCaseWithFixtures
> @@ -85,7 +86,6 @@
> def wait(self):
> return self.returncode
>
> -
> class MockCmdRunnerPopen_sha1sum_fail(object):
> def __call__(self, cmd, *args, **kwargs):
> self.returncode = 0
> @@ -99,7 +99,6 @@
> def wait(self):
> return self.returncode
>
> -
> class MockCmdRunnerPopen_wait_fails(object):
> def __call__(self, cmd, *args, **kwargs):
> self.returncode = 0
> @@ -136,7 +135,7 @@
> signature_filename),
> 'sha1sum -c %s' % hash_filename],
> fixture.mock.commands_executed)
> -
> +
> def test_verify_files_returns_files(self):
> ...

Revision history for this message
Deepti B. Kalakeri (deeptik) :
review: Needs Fixing
Revision history for this message
Milo Casagrande (milo) wrote :

Hello Deepti,

thanks for the reivew!

On Sun, Jun 10, 2012 at 5:55 PM, Deepti B. Kalakeri
<email address hidden> wrote:
>
> The message does not indicate which service needs the package.
> We can improve the message to something like :
> "linaro-media-create needs missing package: %s to execute %s command.
> Install (Y/n)?" % (" ".join(to_install), command)

Agreed, will look into a better way to express that.

> or something better on the similar lines.
> Additionally it would good if we gave a user hint on installation
> completion through a message.

Before suppressing the command output, it showed apt-get running
output, we can re-introduce that. It is rather verbose in my opinion,
but that wouldn't add to much logic to handle percentage or completion
information here.

> Also, when I ran the l-m-c command and choose not to install we get the
> following traceback along
> with the message
>
> This does not look so clean. We should do a  graceful exit than showing all
> this trace messages.

I agree with you, I wasn't sure either to throw an exception there.
I'm not sure what is the best practice inside l-i-t, but I would
rather exit the application (via sys.exit(CODE)) then throwing the
exception that is not caught nor handled in a better way up the stack.

> I tried running the tests locally and I get the following traceback
> http://paste.ubuntu.com/1033938/.
> Though the tests changes looks good, I cannot validate if they run
> successful atleast on Natty.

I will try again and look what can be wrong: on my machine it works correctly.
Cheers.

--
Milo Casagrande
Infrastructure Engineer
Linaro.org <www.linaro.org> │ Open source software for ARM SoCs

529. By Milo Casagrande

Reenabled stdout and stderr on apt-get install.

530. By Milo Casagrande

User friendly messages.

531. By Milo Casagrande

Removed thrown exception, program exits instead.

532. By Milo Casagrande

Merged from trunk.

Revision history for this message
Milo Casagrande (milo) wrote :

Hello Deepti,

last pushes on the repository should address all your suggestion.
Unfortunately, I'm not able to reproduce the error you have.

Cheers.

--
Milo Casagrande
Infrastructure Engineer
Linaro.org <www.linaro.org> │ Open source software for ARM SoCs

Revision history for this message
Deepti B. Kalakeri (deeptik) wrote :

Overall looks good. Very minimal changes though that are needed.

202 print ("In order to use the '%s' command, the following packages have "
203 "to be installed: %s" % (command, " ".join(to_install)))
204 resp = raw_input("Install them? (Y/n) ")

Instead of displaying "command, the following packages have"
can we rewrite it as "command, the following package/s have" ?
Also, Instead of displaying
"Install them" can we rewrite it as "Install ?" this
would mean we can indicate it for one or many packages

For the message : Package installation is necessary to continue. Exiting.
Please remove the extra space between the . and Exiting.

We should add one more test to cover when the user denies installation of the
package ??

Revision history for this message
Deepti B. Kalakeri (deeptik) :
review: Needs Fixing
Revision history for this message
Milo Casagrande (milo) wrote :

Hello Deepti,

thanks for the review.
All the suggested changes have been applied, and I also added a test
for the refused package installation.

Cheers.

--
Milo Casagrande
Infrastructure Engineer
Linaro.org <www.linaro.org> │ Open source software for ARM SoCs

533. By Milo Casagrande

Fixed proposed review changes.

Revision history for this message
Deepti B. Kalakeri (deeptik) wrote :

Looks good. +1.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'linaro_image_tools/tests/fixtures.py'
2--- linaro_image_tools/tests/fixtures.py 2012-06-07 13:22:47 +0000
3+++ linaro_image_tools/tests/fixtures.py 2012-06-13 08:23:21 +0000
4@@ -69,8 +69,9 @@
5 # used in tests to make sure all callsites wait for their child.
6 child_finished = True
7
8- def __init__(self, assert_child_finished=True):
9+ def __init__(self, output_string='', assert_child_finished=True):
10 self.assert_child_finished = assert_child_finished
11+ self.output_string = output_string
12
13 def __call__(self, cmd, *args, **kwargs):
14 if self.assert_child_finished and not self.child_finished:
15@@ -91,7 +92,7 @@
16
17 def communicate(self, input=None):
18 self.wait()
19- return '', ''
20+ return self.output_string, ''
21
22 def wait(self):
23 self.child_finished = True
24@@ -112,9 +113,9 @@
25 If no mock is given, a MockCmdRunnerPopen instance is used.
26 """
27
28- def __init__(self, assert_child_finished=True):
29+ def __init__(self, output_string='', assert_child_finished=True):
30 super(MockCmdRunnerPopenFixture, self).__init__(
31- cmd_runner, 'Popen', MockCmdRunnerPopen(assert_child_finished))
32+ cmd_runner, 'Popen', MockCmdRunnerPopen(output_string, assert_child_finished))
33
34 def tearDown(self):
35 super(MockCmdRunnerPopenFixture, self).tearDown()
36
37=== modified file 'linaro_image_tools/tests/test_utils.py'
38--- linaro_image_tools/tests/test_utils.py 2012-06-06 13:59:32 +0000
39+++ linaro_image_tools/tests/test_utils.py 2012-06-13 08:23:21 +0000
40@@ -24,6 +24,7 @@
41 import logging
42 import tempfile
43 import tarfile
44+from StringIO import StringIO
45
46 from linaro_image_tools import cmd_runner, utils
47 from linaro_image_tools.testing import TestCaseWithFixtures
48@@ -249,19 +250,45 @@
49
50 class TestInstallPackageProviding(TestCaseWithFixtures):
51
52- def test_found_package(self):
53- self.useFixture(MockSomethingFixture(
54- sys, 'stdout', open('/dev/null', 'w')))
55- fixture = self.useFixture(MockCmdRunnerPopenFixture())
56+ # This is the package we need to fake the installation of, it is a
57+ # slightly changed version of 'apt-get -s install' output.
58+ # It is used as an argument to MockCmdRunnerPopenFixture in order to
59+ # pass a string that should be expected as output from the command that
60+ # is being executed.
61+ output_string = 'Inst dosfstools (3.0.12-1ubuntu1 Ubuntu:12.04)'
62+
63+ def test_package_installation_accepted(self):
64+ self.useFixture(MockSomethingFixture(sys,
65+ 'stdout',
66+ open('/dev/null', 'w')))
67+ # We need this since we are getting user input via raw_input
68+ # and we need a 'Y' to proceed with the operations.
69+ self.useFixture(MockSomethingFixture(sys,
70+ 'stdin',
71+ StringIO('Y')))
72+ fixture = self.useFixture(MockCmdRunnerPopenFixture(self.output_string))
73 install_package_providing('mkfs.vfat')
74 self.assertEqual(
75- ['%s apt-get --yes install dosfstools' % sudo_args],
76- fixture.mock.commands_executed)
77+ ['apt-get -s install dosfstools',
78+ '%s apt-get --yes install dosfstools' %
79+ sudo_args],
80+ fixture.mock.commands_executed)
81+
82+ def test_package_installation_refused(self):
83+ self.useFixture(MockSomethingFixture(sys,
84+ 'stdout',
85+ open('/dev/null', 'w')))
86+ # We need this since we are getting user input via raw_input
87+ # and we need a 'n' to mimic a refused package installation.
88+ self.useFixture(MockSomethingFixture(sys,
89+ 'stdin',
90+ StringIO('n')))
91+ self.useFixture(MockCmdRunnerPopenFixture(self.output_string))
92+ self.assertRaises(SystemExit, install_package_providing, 'mkfs.vfat')
93
94 def test_not_found_package(self):
95- self.assertRaises(
96- UnableToFindPackageProvidingCommand,
97- install_package_providing, 'mkfs.lean')
98+ self.assertRaises(UnableToFindPackageProvidingCommand,
99+ install_package_providing, 'mkfs.lean')
100
101
102 class Args():
103@@ -288,7 +315,7 @@
104 board="testboard")))
105
106
107-class TestPrepMediaPath(TestCaseWithFixtures):
108+class TestAdditionalOptionChecks(TestCaseWithFixtures):
109
110 def test_additional_option_checks(self):
111 self.useFixture(MockSomethingFixture(os.path, 'abspath', lambda x: x))
112
113=== modified file 'linaro_image_tools/utils.py'
114--- linaro_image_tools/utils.py 2012-06-06 13:59:32 +0000
115+++ linaro_image_tools/utils.py 2012-06-13 08:23:21 +0000
116@@ -171,7 +171,10 @@
117
118 If we can't find any package which provides it, raise
119 UnableToFindPackageProvidingCommand.
120+
121+ If the user denies installing the package, the program exits.
122 """
123+
124 if CommandNotFound is None:
125 raise UnableToFindPackageProvidingCommand(
126 "Cannot lookup a package which provides %s" % command)
127@@ -182,12 +185,35 @@
128 "Unable to find any package providing %s" % command)
129
130 # TODO: Ask the user to pick a package when there's more than one that
131- # provide the given command.
132+ # provides the given command.
133 package, _ = packages[0]
134- print ("Installing required command %s from package %s"
135- % (command, package))
136- cmd_runner.run(
137- ['apt-get', '--yes', 'install', package], as_root=True).wait()
138+ output, _ = cmd_runner.run(['apt-get', '-s', 'install', package],
139+ stdout=subprocess.PIPE).communicate()
140+ to_install = []
141+ for line in output.splitlines():
142+ if line.startswith("Inst"):
143+ to_install.append(line.split()[1])
144+ if not to_install:
145+ raise UnableToFindPackageProvidingCommand(
146+ "Unable to find any package to be installed.")
147+
148+ try:
149+ print ("In order to use the '%s' command, the following package/s have "
150+ "to be installed: %s" % (command, " ".join(to_install)))
151+ resp = raw_input("Install? (Y/n) ")
152+ if resp.lower() != 'y':
153+ print "Package installation is necessary to continue. Exiting."
154+ sys.exit(1)
155+ print ("Installing required command '%s' from package '%s'..."
156+ % (command, package))
157+ cmd_runner.run(['apt-get', '--yes', 'install', package],
158+ as_root=True).wait()
159+ except EOFError:
160+ raise PackageInstallationRefused(
161+ "Package installation interrupted: input error.")
162+ except KeyboardInterrupt:
163+ raise PackageInstallationRefused(
164+ "Package installation interrupted by the user.")
165
166
167 def has_command(command):
168@@ -272,6 +298,10 @@
169 """We can't find a package which provides the given command."""
170
171
172+class PackageInstallationRefused(Exception):
173+ """User has chosen not to install a package."""
174+
175+
176 class InvalidHwpackFile(Exception):
177 """The hwpack parameter is not a regular file."""
178

Subscribers

People subscribed via source and target branches