1#!/usr/bin/env python
2# Software License Agreement (BSD License)
3#
4# Copyright (c) 2009, Willow Garage, Inc.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10#
11#  * Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13#  * Redistributions in binary form must reproduce the above
14#    copyright notice, this list of conditions and the following
15#    disclaimer in the documentation and/or other materials provided
16#    with the distribution.
17#  * Neither the name of Willow Garage, Inc. nor the names of its
18#    contributors may be used to endorse or promote products derived
19#    from this software without specific prior written permission.
20#
21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32# POSSIBILITY OF SUCH DAMAGE.
33
34import os
35import subprocess
36import shutil
37import rosinstall.rosws_cli
38from test.scm_test_base import AbstractFakeRosBasedTest, _create_yaml_file, _create_config_elt_dict
39from wstool.config_yaml import PathSpec
40import wstool.multiproject_cmd
41from rosinstall.rosws_cli import RoswsCLI
42from rosinstall.helpers import ROSInstallException
43
44
45class FakeConfig():
46    def __init__(self, elts=[], celts=[], basepath=''):
47        self.elts = elts
48        self.celts = celts
49        self.basepath = basepath
50
51    def get_config_elements(self):
52        return self.celts
53
54    def get_source(self):
55        return self.elts
56
57    def get_base_path(self):
58        return self.basepath
59
60
61class MockConfigElement():
62    def __init__(self, local_name='', scmtype=None, path=None, uri=None, spec=None):
63        self.scmtype = scmtype
64        self.path = path
65        self.uri = uri
66        self.local_name = local_name
67        self.spec = spec
68
69    def get_path(self):
70        return self.path
71
72    def get_local_name(self):
73        return self.local_name
74
75    def get_path_spec(self):
76        return self.spec
77
78    def is_vcs_element(self):
79        return True if self.scmtype else False
80
81
82class RosinstallCommandlineTest(AbstractFakeRosBasedTest):
83
84    def test_require_bootstrap(self):
85        config = FakeConfig()
86        self.assertFalse(rosinstall.rosinstall_cmd._ros_requires_boostrap(config))
87        config = FakeConfig([PathSpec(self.ros_path, path=self.ros_path)])
88        self.assertFalse(rosinstall.rosinstall_cmd._ros_requires_boostrap(config))
89        config = FakeConfig([PathSpec(self.ros_path, 'git', 'gituri', path=self.ros_path)])
90        self.assertTrue(rosinstall.rosinstall_cmd._ros_requires_boostrap(config))
91
92
93class RosinstallCommandLineGenerationTest(AbstractFakeRosBasedTest):
94
95    def test_cmd_generate_ros_files_simple(self):
96        self.local_path = os.path.join(self.test_root_path, "ws")
97        os.makedirs(self.local_path)
98
99        config = FakeConfig(celts=[MockConfigElement(path=self.ros_path)], basepath=self.local_path)
100        rosinstall.rosinstall_cmd.cmd_generate_ros_files(config, self.local_path, nobuild=True, rosdep_yes=False, catkin=False, catkinpp=None)
101        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
102        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
103        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
104
105    def test_cmd_generate_ros_files_vcs(self):
106        self.local_path = os.path.join(self.test_root_path, "ws2")
107        os.makedirs(self.local_path)
108
109        config = FakeConfig(celts=[MockConfigElement(path=self.ros_path), MockConfigElement(path='gitrepo', scmtype='git', uri=self.git_path), MockConfigElement(path='hgrepo', scmtype='hg', uri=self.hg_path)], basepath=self.local_path)
110        rosinstall.rosinstall_cmd.cmd_generate_ros_files(config, self.local_path, nobuild=True, rosdep_yes=False, catkin=False, catkinpp=None)
111        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
112        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
113        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
114
115    def test_cmd_generate_ros_files_catkin(self):
116        self.local_path = os.path.join(self.test_root_path, "ws3")
117        os.makedirs(self.local_path)
118
119        config = FakeConfig([PathSpec(self.ros_path), PathSpec('gitrepo', 'git', uri=self.git_path)], self.local_path)
120        rosinstall.rosinstall_cmd.cmd_generate_ros_files(config, self.local_path, nobuild=True, rosdep_yes=False, catkin=True, catkinpp=False)
121        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
122        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
123        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
124        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'CMakeLists.txt')))
125
126    def test_cmd_generate_ros_files_catkinpp(self):
127        self.local_path = os.path.join(self.test_root_path, "ws4")
128        os.makedirs(self.local_path)
129
130        config = FakeConfig([PathSpec(self.ros_path), PathSpec('gitrepo', 'git', uri=self.git_path)], self.local_path)
131        rosinstall.rosinstall_cmd.cmd_generate_ros_files(config, self.local_path, nobuild=True, rosdep_yes=False, catkin=True, catkinpp=True)
132        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
133        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
134        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
135        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'CMakeLists.txt')))
136        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'workspace-config.cmake')))
137
138    def test_cmd_generate_ros_files_build(self):
139        self.local_path = os.path.join(self.test_root_path, "ws2b")
140        os.makedirs(self.local_path)
141        local_rosinstall = os.path.join(self.test_root_path, "local.rosinstall")
142        _create_yaml_file([_create_config_elt_dict("git", 'ros_comm', self.git_path),
143                           _create_config_elt_dict("git", 'ros', self.ros_path),
144                           _create_config_elt_dict("hg", 'hgrepo', self.hg_path)], local_rosinstall)
145        cli = RoswsCLI()
146        self.assertEqual(0, cli.cmd_init([self.local_path, local_rosinstall]))
147        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
148        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
149        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
150
151    def test_cmd_init(self):
152        self.local_path = os.path.join(self.test_root_path, "ws5")
153        os.makedirs(self.local_path)
154
155        cli = RoswsCLI()
156        self.assertEqual(0, cli.cmd_init([self.local_path, self.ros_path]))
157        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
158        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
159        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
160        self.assertTrue(os.path.exists(os.path.join(self.local_path, '.rosinstall')))
161        self.assertEqual(0, subprocess.call(". %s" % os.path.join(self.local_path, 'setup.sh'), shell=True, env=self.new_environ))
162        self.assertEqual(0, subprocess.call(". %s" % os.path.join(self.local_path, 'setup.bash'), shell=True, env=self.new_environ, executable='/bin/bash'))
163
164        self.assertEqual(0, cli.cmd_merge(self.local_path, [self.ros_path, "-y"]))
165        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
166        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
167        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
168        self.assertTrue(os.path.exists(os.path.join(self.local_path, '.rosinstall')))
169        self.assertEqual(0, subprocess.call(". %s" % os.path.join(self.local_path, 'setup.sh'), shell=True, env=self.new_environ))
170        self.assertEqual(0, subprocess.call(". %s" % os.path.join(self.local_path, 'setup.bash'), shell=True, env=self.new_environ, executable='/bin/bash'))
171
172    def test_cmd_init_catkin(self):
173        self.local_path = os.path.join(self.test_root_path, "ws6")
174        os.makedirs(self.local_path)
175
176        cli = RoswsCLI()
177        self.assertEqual(0, cli.cmd_init([self.local_path, self.ros_path, "-c"]))
178        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
179        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
180        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
181        self.assertTrue(os.path.exists(os.path.join(self.local_path, '.rosinstall')))
182        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'CMakeLists.txt')))
183
184    def test_cmd_init_catkin2(self):
185        self.local_path = os.path.join(self.test_root_path, "ws7")
186        os.makedirs(self.local_path)
187
188        cli = RoswsCLI()
189        self.assertEqual(0, cli.cmd_init([self.local_path, self.ros_path, "--catkin"]))
190        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
191        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
192        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
193        self.assertTrue(os.path.exists(os.path.join(self.local_path, '.rosinstall')))
194        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'CMakeLists.txt')))
195
196    def test_cmd_init_catkinpp(self):
197        self.local_path = os.path.join(self.test_root_path, "ws8")
198        os.makedirs(self.local_path)
199
200        cli = RoswsCLI()
201        self.assertEqual(0, cli.cmd_init([self.local_path, self.ros_path, "--catkin", "--cmake-prefix-path=foo"]))
202        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
203        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
204        self.assertFalse(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
205        self.assertTrue(os.path.exists(os.path.join(self.local_path, '.rosinstall')))
206        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'CMakeLists.txt')))
207        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'workspace-config.cmake')))
208
209    def test_cmd_init_makedir(self):
210        # rosinstall to create dir
211        self.local_path = os.path.join(self.test_root_path, "ws9")
212        cli = RoswsCLI()
213        self.assertEqual(0, cli.cmd_init([self.local_path, self.ros_path]))
214        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
215        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
216        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
217
218    def test_cmd_init_no_ros(self):
219        self.local_path = os.path.join(self.test_root_path, "ws10")
220
221        ros_root_existed = False
222        if 'ROS_ROOT' in os.environ:
223            ros_root_existed = True
224            oldros = os.environ.pop('ROS_ROOT')
225        cli = RoswsCLI()
226        self.assertEqual(0, cli.cmd_init([self.local_path]))
227
228        shutil.rmtree(self.local_path)
229        os.environ['ROS_ROOT'] = self.ros_path
230
231        self.assertEqual(0, cli.cmd_init([self.local_path]))
232        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
233        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
234        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
235        if ros_root_existed:
236            os.environ['ROS_ROOT'] = oldros
237        else:
238            os.environ.pop('ROS_ROOT')
239
240    def test_cmd_init_main(self):
241        # rosinstall to create dir
242        ros_root_existed = False
243
244        if 'ROS_ROOT' in os.environ:
245            ros_root_existed = True
246            oldros = os.environ.pop('ROS_ROOT')
247        os.environ['ROS_ROOT'] = self.ros_path
248        self.local_path = os.path.join(self.test_root_path, "ws11")
249        self.assertEqual(0, rosinstall.rosws_cli.rosws_main(['rosws', 'help']))
250        self.assertEqual(0, rosinstall.rosws_cli.rosws_main(['rosws', 'init', self.local_path]))
251        self.assertEqual(0, rosinstall.rosws_cli.rosws_main(['rosws', 'info', '-t', self.local_path]))
252        self.assertEqual(0, rosinstall.rosws_cli.rosws_main(['rosws', 'info', '--pkg-path-only', '-t', self.local_path]))
253        self.assertEqual(0, rosinstall.rosws_cli.rosws_main(['rosws', 'info', '--no-pkg-path', '-t', self.local_path]))
254        self.assertEqual(0, rosinstall.rosws_cli.rosws_main(['rosws', 'info', '--data-only', '-t', self.local_path]))
255        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.sh')))
256        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.bash')))
257        self.assertTrue(os.path.exists(os.path.join(self.local_path, 'setup.zsh')))
258        if ros_root_existed:
259            os.environ['ROS_ROOT'] = oldros
260        else:
261            os.environ.pop('ROS_ROOT')
262
263    def test_cmd_remove(self):
264        # rosinstall to create dir
265        self.local_path = os.path.join(self.test_root_path, "ws12")
266        cli = RoswsCLI()
267        self.assertEqual(0, cli.cmd_init([self.local_path, self.ros_path]))
268        self.assertEqual(0, cli.cmd_merge(self.local_path, [self.git_path, "-y"]))
269        self.assertEqual(0, cli.cmd_merge(self.local_path, [self.hg_path, "-y"]))
270        config = wstool.multiproject_cmd.get_config(basepath=self.local_path,
271                                                        config_filename='.rosinstall')
272        self.assertEqual(len(config.get_config_elements()), 3)
273        self.assertEqual(0, cli.cmd_remove(self.local_path, [self.git_path]))
274        config = wstool.multiproject_cmd.get_config(basepath=self.local_path,
275                                                        config_filename='.rosinstall')
276        self.assertEqual(len(config.get_config_elements()), 2)
277
278    def execute_check_result_allshells(self, command, path, cwd=None, expect=''):
279        """sources in turn each setup.*sh and runs command"""
280        # zsh has issues with SCRIPT_PATH
281        for shell in ['sh', 'bash']:
282            cmd = ". %s ; %s" % (os.path.join(path, 'setup.%s' % shell), command)
283            p = subprocess.Popen(cmd, shell=True, cwd=cwd, stdout=subprocess.PIPE, executable='/bin/%s' % shell)
284            output = p.communicate()[0]
285            self.assertEqual(expect.encode('UTF-8'), output.strip(), ("'%s' != '%s', cmd: %s, cwd: %s" % (expect, output, cmd, cwd)))
286            self.assertEqual(0, p.returncode)
287
288    def test_init_parallel(self):
289        self.local_path = os.path.join(self.test_root_path, "ws13a")
290        cli = RoswsCLI()
291        self.assertEqual(0, cli.cmd_init([self.local_path, self.simple_rosinstall, "--parallel=5"]))
292
293        command = "echo $ROS_WORKSPACE"
294        self.execute_check_result_allshells(command, self.local_path, expect=self.local_path)
295        self.execute_check_result_allshells(command, '.', cwd=self.local_path, expect=self.local_path)
296        self.execute_check_result_allshells(command, 'ws13a', cwd=self.test_root_path, expect=self.local_path)
297
298        local_ros_path = os.path.join(self.local_path, "ros")
299        local_git_path = os.path.join(self.local_path, "gitrepo")
300        package_path = "%s:%s" % (local_git_path, local_ros_path)
301        command = "echo $ROS_PACKAGE_PATH"
302        self.execute_check_result_allshells(command, self.local_path, expect=package_path)
303        self.execute_check_result_allshells(command, '.', cwd=self.local_path, expect=package_path)
304        self.execute_check_result_allshells(command, 'ws13a', cwd=self.test_root_path, expect=package_path)
305
306    def test_setup_sh(self):
307        self.local_path = os.path.join(self.test_root_path, "ws13")
308        cli = RoswsCLI()
309        self.assertEqual(0, cli.cmd_init([self.local_path, self.simple_rosinstall]))
310
311        command = "echo $ROS_WORKSPACE"
312        self.execute_check_result_allshells(command, self.local_path, expect=self.local_path)
313        self.execute_check_result_allshells(command, '.', cwd=self.local_path, expect=self.local_path)
314        self.execute_check_result_allshells(command, 'ws13', cwd=self.test_root_path, expect=self.local_path)
315
316        local_ros_path = os.path.join(self.local_path, "ros")
317        local_git_path = os.path.join(self.local_path, "gitrepo")
318        package_path = "%s:%s" % (local_git_path, local_ros_path)
319        command = "echo $ROS_PACKAGE_PATH"
320        self.execute_check_result_allshells(command, self.local_path, expect=package_path)
321        self.execute_check_result_allshells(command, '.', cwd=self.local_path, expect=package_path)
322        self.execute_check_result_allshells(command, 'ws13', cwd=self.test_root_path, expect=package_path)
323
324    def test_setup_sh_relros(self):
325        self.local_path = os.path.join(self.test_root_path, "ws14")
326        cli = RoswsCLI()
327        simple_rel_rosinstall = os.path.join(self.test_root_path, "simple_rel.rosinstall")
328        _create_yaml_file([_create_config_elt_dict("git", "ros", "../ros")],
329                          simple_rel_rosinstall)
330        self.assertEqual(0, cli.cmd_init([self.local_path, simple_rel_rosinstall]))
331
332        command = "echo $ROS_WORKSPACE"
333        self.execute_check_result_allshells(command, self.local_path, expect=self.local_path)
334        self.execute_check_result_allshells(command, '.', cwd=self.local_path, expect=self.local_path)
335        self.execute_check_result_allshells(command, 'ws14', cwd=self.test_root_path, expect=self.local_path)
336
337        package_path = os.path.join(self.local_path, "ros")
338        command = "echo $ROS_PACKAGE_PATH"
339        self.execute_check_result_allshells(command, self.local_path, expect=package_path)
340        self.execute_check_result_allshells(command, '.', cwd=self.local_path, expect=package_path)
341        self.execute_check_result_allshells(command, 'ws14', cwd=self.test_root_path, expect=package_path)
342
343
344    def test_setup_sh_relother(self):
345        self.local_path = os.path.join(self.test_root_path, "ws15")
346        cli = RoswsCLI()
347        simple_rel_rosinstall = os.path.join(self.test_root_path, "simple_rel2.rosinstall")
348        _create_yaml_file([_create_config_elt_dict("git", "ros", "../ros"),
349                           _create_config_elt_dict("other", "../gitrepo")],
350                          simple_rel_rosinstall)
351        self.assertEqual(0, cli.cmd_init([self.local_path, simple_rel_rosinstall]))
352
353        command = "echo $ROS_WORKSPACE"
354        self.execute_check_result_allshells(command, self.local_path, expect=self.local_path)
355        self.execute_check_result_allshells(command, '.', cwd=self.local_path, expect=self.local_path)
356        self.execute_check_result_allshells(command, 'ws15', cwd=self.test_root_path, expect=self.local_path)
357
358        local_ros_path = os.path.join(self.local_path, "ros")
359        package_path = "%s:%s" % (self.git_path, local_ros_path)
360        command = "echo $ROS_PACKAGE_PATH"
361        self.execute_check_result_allshells(command, self.local_path, expect=package_path)
362        self.execute_check_result_allshells(command, '.', cwd=self.local_path, expect=package_path)
363        self.execute_check_result_allshells(command, 'ws15', cwd=self.test_root_path, expect=package_path)
364