• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python2
2# Copyright 2009 Google Inc. Released under the GPL v2
3
4from __future__ import absolute_import
5from __future__ import division
6from __future__ import print_function
7
8import time, unittest
9import six
10
11import common
12from autotest_lib.client.common_lib import error
13from autotest_lib.client.common_lib.test_utils import mock
14from autotest_lib.server import subcommand
15from six.moves import range
16from six.moves import zip
17
18
19def _create_subcommand(func, args):
20    # to avoid __init__
21    class wrapper(subcommand.subcommand):
22        def __init__(self, func, args):
23            self.func = func
24            self.args = args
25            self.subdir = None
26            self.debug = None
27            self.pid = None
28            self.returncode = None
29
30    return wrapper(func, args)
31
32
33class subcommand_test(unittest.TestCase):
34    def setUp(self):
35        self.god = mock.mock_god()
36
37
38    def tearDown(self):
39        self.god.unstub_all()
40        # cleanup the hooks
41        subcommand.subcommand.fork_hooks = []
42        subcommand.subcommand.join_hooks = []
43
44
45    def test_create(self):
46        def check_attributes(cmd, func, args, subdir=None, debug=None,
47                             pid=None, returncode=None, fork_hooks=[],
48                             join_hooks=[]):
49            self.assertEquals(cmd.func, func)
50            self.assertEquals(cmd.args, args)
51            self.assertEquals(cmd.subdir, subdir)
52            self.assertEquals(cmd.debug, debug)
53            self.assertEquals(cmd.pid, pid)
54            self.assertEquals(cmd.returncode, returncode)
55            self.assertEquals(cmd.fork_hooks, fork_hooks)
56            self.assertEquals(cmd.join_hooks, join_hooks)
57
58        def func(arg1, arg2):
59            pass
60
61        cmd = subcommand.subcommand(func, (2, 3))
62        check_attributes(cmd, func, (2, 3))
63        self.god.check_playback()
64
65        self.god.stub_function(subcommand.os.path, 'abspath')
66        self.god.stub_function(subcommand.os.path, 'exists')
67        self.god.stub_function(subcommand.os, 'mkdir')
68
69        subcommand.os.path.abspath.expect_call('dir').and_return('/foo/dir')
70        subcommand.os.path.exists.expect_call('/foo/dir').and_return(False)
71        subcommand.os.mkdir.expect_call('/foo/dir')
72
73        (subcommand.os.path.exists.expect_call('/foo/dir/debug')
74                .and_return(False))
75        subcommand.os.mkdir.expect_call('/foo/dir/debug')
76
77        cmd = subcommand.subcommand(func, (2, 3), subdir='dir')
78        check_attributes(cmd, func, (2, 3), subdir='/foo/dir',
79                         debug='/foo/dir/debug')
80        self.god.check_playback()
81
82
83    def _setup_fork_start_parent(self):
84        self.god.stub_function(subcommand.os, 'fork')
85
86        subcommand.os.fork.expect_call().and_return(1000)
87        func = self.god.create_mock_function('func')
88        cmd = _create_subcommand(func, [])
89        cmd.fork_start()
90
91        return cmd
92
93
94    def test_fork_start_parent(self):
95        cmd = self._setup_fork_start_parent()
96
97        self.assertEquals(cmd.pid, 1000)
98        self.god.check_playback()
99
100
101    def _setup_fork_start_child(self):
102        self.god.stub_function(subcommand.os, 'pipe')
103        self.god.stub_function(subcommand.os, 'fork')
104        self.god.stub_function(subcommand.os, 'close')
105        self.god.stub_function(subcommand.os, 'write')
106        self.god.stub_function(six.moves.cPickle, 'dumps')
107        self.god.stub_function(subcommand.os, '_exit')
108
109
110    def test_fork_start_child(self):
111        self._setup_fork_start_child()
112
113        func = self.god.create_mock_function('func')
114        fork_hook = self.god.create_mock_function('fork_hook')
115        join_hook = self.god.create_mock_function('join_hook')
116
117        subcommand.subcommand.register_fork_hook(fork_hook)
118        subcommand.subcommand.register_join_hook(join_hook)
119        cmd = _create_subcommand(func, (1, 2))
120
121        subcommand.os.pipe.expect_call().and_return((10, 20))
122        subcommand.os.fork.expect_call().and_return(0)
123        subcommand.os.close.expect_call(10)
124        fork_hook.expect_call(cmd)
125        func.expect_call(1, 2).and_return(True)
126        six.moves.cPickle.dumps.expect_call(True,
127                six.moves.cPickle.HIGHEST_PROTOCOL).and_return('True')
128        subcommand.os.write.expect_call(20, 'True')
129        subcommand.os.close.expect_call(20)
130        join_hook.expect_call(cmd)
131        subcommand.os._exit.expect_call(0)
132
133        cmd.fork_start()
134        self.god.check_playback()
135
136
137    def test_fork_start_child_error(self):
138        self._setup_fork_start_child()
139        self.god.stub_function(subcommand.logging, 'exception')
140
141        func = self.god.create_mock_function('func')
142        cmd = _create_subcommand(func, (1, 2))
143        error = Exception('some error')
144
145        subcommand.os.pipe.expect_call().and_return((10, 20))
146        subcommand.os.fork.expect_call().and_return(0)
147        subcommand.os.close.expect_call(10)
148        func.expect_call(1, 2).and_raises(error)
149        subcommand.logging.exception.expect_call('function failed')
150        six.moves.cPickle.dumps.expect_call(error,
151                six.moves.cPickle.HIGHEST_PROTOCOL).and_return('error')
152        subcommand.os.write.expect_call(20, 'error')
153        subcommand.os.close.expect_call(20)
154        subcommand.os._exit.expect_call(1)
155
156        cmd.fork_start()
157        self.god.check_playback()
158
159
160    def _setup_poll(self):
161        cmd = self._setup_fork_start_parent()
162        self.god.stub_function(subcommand.os, 'waitpid')
163        return cmd
164
165
166    def test_poll_running(self):
167        cmd = self._setup_poll()
168
169        (subcommand.os.waitpid.expect_call(1000, subcommand.os.WNOHANG)
170                .and_raises(subcommand.os.error('waitpid')))
171        self.assertEquals(cmd.poll(), None)
172        self.god.check_playback()
173
174
175    def test_poll_finished_success(self):
176        cmd = self._setup_poll()
177
178        (subcommand.os.waitpid.expect_call(1000, subcommand.os.WNOHANG)
179                .and_return((1000, 0)))
180        self.assertEquals(cmd.poll(), 0)
181        self.god.check_playback()
182
183
184    def test_poll_finished_failure(self):
185        cmd = self._setup_poll()
186        self.god.stub_function(cmd, '_handle_exitstatus')
187
188        (subcommand.os.waitpid.expect_call(1000, subcommand.os.WNOHANG)
189                .and_return((1000, 10)))
190        cmd._handle_exitstatus.expect_call(10).and_raises(Exception('fail'))
191
192        self.assertRaises(Exception, cmd.poll)
193        self.god.check_playback()
194
195
196    def test_wait_success(self):
197        cmd = self._setup_poll()
198
199        (subcommand.os.waitpid.expect_call(1000, 0)
200                .and_return((1000, 0)))
201
202        self.assertEquals(cmd.wait(), 0)
203        self.god.check_playback()
204
205
206    def test_wait_failure(self):
207        cmd = self._setup_poll()
208        self.god.stub_function(cmd, '_handle_exitstatus')
209
210        (subcommand.os.waitpid.expect_call(1000, 0)
211                .and_return((1000, 10)))
212
213        cmd._handle_exitstatus.expect_call(10).and_raises(Exception('fail'))
214        self.assertRaises(Exception, cmd.wait)
215        self.god.check_playback()
216
217
218class real_subcommand_test(unittest.TestCase):
219    """Test actually running subcommands (without mocking)."""
220
221
222    def _setup_subcommand(self, func, *args):
223        cmd = subcommand.subcommand(func, args)
224        cmd.fork_start()
225        return cmd
226
227
228    def test_fork_waitfor_no_timeout(self):
229        """Test fork_waitfor success with no timeout."""
230        cmd = self._setup_subcommand(lambda: None)
231        self.assertEquals(cmd.fork_waitfor(), 0)
232
233
234    def test_fork_waitfor_timeout(self):
235        """Test fork_waitfor success with a timeout."""
236        cmd = self._setup_subcommand(lambda: None)
237        self.assertEquals(cmd.fork_waitfor(timeout=60), 0)
238
239
240    def test_fork_waitfor_exception(self):
241        """Test fork_waitfor failure with an exception."""
242        cmd = self._setup_subcommand(lambda: None, 'foo')
243        with self.assertRaises(error.AutoservSubcommandError):
244          cmd.fork_waitfor(timeout=60)
245
246
247    def test_fork_waitfor_timeout_fail(self):
248        """Test fork_waitfor timing out."""
249        cmd = self._setup_subcommand(lambda: time.sleep(60))
250        with self.assertRaises(error.AutoservSubcommandError):
251          cmd.fork_waitfor(timeout=1)
252
253
254class parallel_test(unittest.TestCase):
255    def setUp(self):
256        self.god = mock.mock_god()
257        self.god.stub_function(six.moves.cPickle, 'load')
258
259
260    def tearDown(self):
261        self.god.unstub_all()
262
263
264    def _get_cmd(self, func, args):
265        cmd = _create_subcommand(func, args)
266        cmd.result_pickle = self.god.create_mock_class(file, 'file')
267        return self.god.create_mock_class(cmd, 'subcommand')
268
269
270    def _get_tasklist(self):
271        return [self._get_cmd(lambda x: x * 2, (3,)),
272                self._get_cmd(lambda: None, [])]
273
274
275    def _setup_common(self):
276        tasklist = self._get_tasklist()
277
278        for task in tasklist:
279            task.fork_start.expect_call()
280
281        return tasklist
282
283
284    def test_success(self):
285        tasklist = self._setup_common()
286
287        for task in tasklist:
288            task.fork_waitfor.expect_call(timeout=None).and_return(0)
289            (six.moves.cPickle.load.expect_call(task.result_pickle)
290                    .and_return(6))
291            task.result_pickle.close.expect_call()
292
293        subcommand.parallel(tasklist)
294        self.god.check_playback()
295
296
297    def test_failure(self):
298        tasklist = self._setup_common()
299
300        for task in tasklist:
301            task.fork_waitfor.expect_call(timeout=None).and_return(1)
302            (six.moves.cPickle.load.expect_call(task.result_pickle)
303                    .and_return(6))
304            task.result_pickle.close.expect_call()
305
306        self.assertRaises(subcommand.error.AutoservError, subcommand.parallel,
307                          tasklist)
308        self.god.check_playback()
309
310
311    def test_timeout(self):
312        self.god.stub_function(subcommand.time, 'time')
313
314        tasklist = self._setup_common()
315        timeout = 10
316
317        subcommand.time.time.expect_call().and_return(1)
318
319        for task in tasklist:
320            subcommand.time.time.expect_call().and_return(1)
321            task.fork_waitfor.expect_call(timeout=timeout).and_return(None)
322            (six.moves.cPickle.load.expect_call(task.result_pickle)
323                    .and_return(6))
324            task.result_pickle.close.expect_call()
325
326        self.assertRaises(subcommand.error.AutoservError, subcommand.parallel,
327                          tasklist, timeout=timeout)
328        self.god.check_playback()
329
330
331    def test_return_results(self):
332        tasklist = self._setup_common()
333
334        tasklist[0].fork_waitfor.expect_call(timeout=None).and_return(0)
335        (six.moves.cPickle.load.expect_call(tasklist[0].result_pickle)
336                .and_return(6))
337        tasklist[0].result_pickle.close.expect_call()
338
339        error = Exception('fail')
340        tasklist[1].fork_waitfor.expect_call(timeout=None).and_return(1)
341        (six.moves.cPickle.load.expect_call(tasklist[1].result_pickle)
342                .and_return(error))
343        tasklist[1].result_pickle.close.expect_call()
344
345        self.assertEquals(subcommand.parallel(tasklist, return_results=True),
346                          [6, error])
347        self.god.check_playback()
348
349
350class test_parallel_simple(unittest.TestCase):
351    def setUp(self):
352        self.god = mock.mock_god()
353        self.god.stub_function(subcommand, 'parallel')
354        ctor = self.god.create_mock_function('subcommand')
355        self.god.stub_with(subcommand, 'subcommand', ctor)
356
357
358    def tearDown(self):
359        self.god.unstub_all()
360
361
362    def test_simple_success(self):
363        func = self.god.create_mock_function('func')
364
365        func.expect_call(3)
366
367        subcommand.parallel_simple(func, (3,))
368        self.god.check_playback()
369
370
371    def test_simple_failure(self):
372        func = self.god.create_mock_function('func')
373
374        error = Exception('fail')
375        func.expect_call(3).and_raises(error)
376
377        self.assertRaises(Exception, subcommand.parallel_simple, func, (3,))
378        self.god.check_playback()
379
380
381    def test_simple_return_value(self):
382        func = self.god.create_mock_function('func')
383
384        result = 1000
385        func.expect_call(3).and_return(result)
386
387        self.assertEquals(subcommand.parallel_simple(func, (3,),
388                                                     return_results=True),
389                          [result])
390        self.god.check_playback()
391
392
393    def test_default_subdirs_constructor(self):
394        func = self.god.create_mock_function('func')
395        args = list(range(4))
396        for arg in args:
397            subcommand.subcommand.expect_call(
398                    func, [arg], str(arg)).and_return(arg)
399        subcommand.parallel.expect_call(args, None, return_results=False)
400
401        subcommand.parallel_simple(func, args)
402        self.god.check_playback()
403
404
405    def test_nolog_skips_subdirs(self):
406        func = self.god.create_mock_function('func')
407        args = list(range(3))
408        for arg in args:
409            subcommand.subcommand.expect_call(
410                    func, [arg], None).and_return(arg)
411        subcommand.parallel.expect_call(args, None, return_results=False)
412
413        subcommand.parallel_simple(func, args, log=False)
414        self.god.check_playback()
415
416
417    def test_custom_subdirs_constructor(self):
418        func = self.god.create_mock_function('func')
419        args = list(range(7))
420        subdirs = ['subdir%s' % arg for arg in args]
421        for arg, subdir in zip(args, subdirs):
422            subcommand.subcommand.expect_call(
423                    func, [arg], subdir).and_return(arg)
424        subcommand.parallel.expect_call(args, None, return_results=False)
425
426        subcommand.parallel_simple(
427                func, args, subdir_name_constructor=lambda x: 'subdir%s' % x)
428        self.god.check_playback()
429
430
431if __name__ == '__main__':
432    unittest.main()
433