1#!/usr/bin/env python3
2#
3#   Copyright 2016 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import copy
18import os
19import tempfile
20import shutil
21import unittest
22
23from acts.controllers.relay_lib import generic_relay_device
24from acts.controllers.relay_lib import relay
25from acts.controllers.relay_lib import relay_board
26from acts.controllers.relay_lib import relay_device
27from acts.controllers.relay_lib import relay_rig
28from acts.controllers.relay_lib import sain_smart_board
29
30import acts.controllers.relay_lib.errors as errors
31import acts.controllers.relay_lib.fugu_remote as fugu_remote
32
33# Shorthand versions of the long class names.
34GenericRelayDevice = generic_relay_device.GenericRelayDevice
35Relay = relay.Relay
36RelayDict = relay.RelayDict
37RelayState = relay.RelayState
38SynchronizeRelays = relay.SynchronizeRelays
39RelayBoard = relay_board.RelayBoard
40RelayDevice = relay_device.RelayDevice
41RelayRig = relay_rig.RelayRig
42SainSmartBoard = sain_smart_board.SainSmartBoard
43RelayDeviceConnectionError = errors.RelayDeviceConnectionError
44
45
46class MockBoard(RelayBoard):
47    def __init__(self, config):
48        self.relay_states = dict()
49        self.relay_previous_states = dict()
50        RelayBoard.__init__(self, config)
51
52    def get_relay_position_list(self):
53        return [0, 1]
54
55    def get_relay_status(self, relay_position):
56        if relay_position not in self.relay_states:
57            self.relay_states[relay_position] = RelayState.NO
58            self.relay_previous_states[relay_position] = RelayState.NO
59        return self.relay_states[relay_position]
60
61    def set(self, relay_position, state):
62        self.relay_previous_states[relay_position] = self.get_relay_status(
63            relay_position)
64        self.relay_states[relay_position] = state
65        return state
66
67
68class ActsRelayTest(unittest.TestCase):
69    def setUp(self):
70        Relay.transition_wait_time = 0
71        Relay.button_press_time = 0
72        self.config = {
73            'name': 'MockBoard',
74            'relays': [{
75                'name': 'Relay',
76                'relay_pos': 0
77            }]
78        }
79        self.board = MockBoard(self.config)
80        self.relay = Relay(self.board, 'Relay')
81        self.board.set(self.relay.position, RelayState.NO)
82
83    def tearDown(self):
84        Relay.transition_wait_time = .2
85        Relay.button_press_time = .25
86
87    def test_turn_on_from_off(self):
88        self.board.set(self.relay.position, RelayState.NO)
89        self.relay.set_nc()
90        self.assertEqual(
91            self.board.get_relay_status(self.relay.position), RelayState.NC)
92
93    def test_turn_on_from_on(self):
94        self.board.set(self.relay.position, RelayState.NC)
95        self.relay.set_nc()
96        self.assertEqual(
97            self.board.get_relay_status(self.relay.position), RelayState.NC)
98
99    def test_turn_off_from_on(self):
100        self.board.set(self.relay.position, RelayState.NC)
101        self.relay.set_no()
102        self.assertEqual(
103            self.board.get_relay_status(self.relay.position), RelayState.NO)
104
105    def test_turn_off_from_off(self):
106        self.board.set(self.relay.position, RelayState.NO)
107        self.relay.set_no()
108        self.assertEqual(
109            self.board.get_relay_status(self.relay.position), RelayState.NO)
110
111    def test_toggle_off_to_on(self):
112        self.board.set(self.relay.position, RelayState.NO)
113        self.relay.toggle()
114        self.assertEqual(
115            self.board.get_relay_status(self.relay.position), RelayState.NC)
116
117    def test_toggle_on_to_off(self):
118        self.board.set(self.relay.position, RelayState.NC)
119        self.relay.toggle()
120        self.assertEqual(
121            self.board.get_relay_status(self.relay.position), RelayState.NO)
122
123    def test_set_on(self):
124        self.board.set(self.relay.position, RelayState.NO)
125        self.relay.set(RelayState.NC)
126        self.assertEqual(
127            self.board.get_relay_status(self.relay.position), RelayState.NC)
128
129    def test_set_off(self):
130        self.board.set(self.relay.position, RelayState.NC)
131        self.relay.set(RelayState.NO)
132        self.assertEqual(
133            self.board.get_relay_status(self.relay.position), RelayState.NO)
134
135    def test_set_foo(self):
136        with self.assertRaises(ValueError):
137            self.relay.set('FOO')
138
139    def test_set_nc_for(self):
140        # Here we set twice so relay_previous_state will also be OFF
141        self.board.set(self.relay.position, RelayState.NO)
142        self.board.set(self.relay.position, RelayState.NO)
143
144        self.relay.set_nc_for(0)
145
146        self.assertEqual(
147            self.board.get_relay_status(self.relay.position), RelayState.NO)
148        self.assertEqual(self.board.relay_previous_states[self.relay.position],
149                         RelayState.NC)
150
151    def test_set_no_for(self):
152        # Here we set twice so relay_previous_state will also be OFF
153        self.board.set(self.relay.position, RelayState.NC)
154        self.board.set(self.relay.position, RelayState.NC)
155
156        self.relay.set_no_for(0)
157
158        self.assertEqual(
159            self.board.get_relay_status(self.relay.position), RelayState.NC)
160        self.assertEqual(self.board.relay_previous_states[self.relay.position],
161                         RelayState.NO)
162
163    def test_get_status_on(self):
164        self.board.set(self.relay.position, RelayState.NC)
165        self.assertEqual(self.relay.get_status(), RelayState.NC)
166
167    def test_get_status_off(self):
168        self.board.set(self.relay.position, RelayState.NO)
169        self.assertEqual(self.relay.get_status(), RelayState.NO)
170
171    def test_clean_up_default_on(self):
172        new_relay = Relay(self.board, 0)
173        new_relay._original_state = RelayState.NO
174        self.board.set(new_relay.position, RelayState.NO)
175        new_relay.clean_up()
176
177        self.assertEqual(
178            self.board.get_relay_status(new_relay.position), RelayState.NO)
179
180    def test_clean_up_default_off(self):
181        new_relay = Relay(self.board, 0)
182        new_relay._original_state = RelayState.NO
183        self.board.set(new_relay.position, RelayState.NC)
184        new_relay.clean_up()
185
186        self.assertEqual(
187            self.board.get_relay_status(new_relay.position), RelayState.NO)
188
189    def test_clean_up_original_state_none(self):
190        val = 'STAYS_THE_SAME'
191        new_relay = Relay(self.board, 0)
192        # _original_state is none by default
193        # The line below sets the dict to an impossible value.
194        self.board.set(new_relay.position, val)
195        new_relay.clean_up()
196        # If the impossible value is cleared, then the test should fail.
197        self.assertEqual(self.board.get_relay_status(new_relay.position), val)
198
199
200class ActsSainSmartBoardTest(unittest.TestCase):
201    STATUS_MSG = ('<small><a href="{}"></a>'
202                  '</small><a href="{}/{}TUX">{}TUX</a><p>')
203
204    RELAY_ON_PAGE_CONTENTS = 'relay_on page'
205    RELAY_OFF_PAGE_CONTENTS = 'relay_off page'
206
207    def setUp(self):
208        Relay.transition_wait_time = 0
209        Relay.button_press_time = 0
210        self.test_dir = 'file://' + tempfile.mkdtemp() + '/'
211
212        # Creates the files used for testing
213        self._set_status_page('0000000000000000')
214        with open(self.test_dir[7:] + '00', 'w+') as file:
215            file.write(self.RELAY_OFF_PAGE_CONTENTS)
216        with open(self.test_dir[7:] + '01', 'w+') as file:
217            file.write(self.RELAY_ON_PAGE_CONTENTS)
218
219        self.config = ({
220            'name': 'SSBoard',
221            'base_url': self.test_dir,
222            'relays': [{
223                'name': '0',
224                'relay_pos': 0
225            }, {
226                'name': '1',
227                'relay_pos': 1
228            }, {
229                'name': '2',
230                'relay_pos': 7
231            }]
232        })
233        self.ss_board = SainSmartBoard(self.config)
234        self.r0 = Relay(self.ss_board, 0)
235        self.r1 = Relay(self.ss_board, 1)
236        self.r7 = Relay(self.ss_board, 7)
237
238    def tearDown(self):
239        shutil.rmtree(self.test_dir[7:])
240        Relay.transition_wait_time = .2
241        Relay.button_press_time = .25
242
243    def test_get_url_code(self):
244        result = self.ss_board._get_relay_url_code(self.r0.position,
245                                                   RelayState.NO)
246        self.assertEqual(result, '00')
247
248        result = self.ss_board._get_relay_url_code(self.r0.position,
249                                                   RelayState.NC)
250        self.assertEqual(result, '01')
251
252        result = self.ss_board._get_relay_url_code(self.r7.position,
253                                                   RelayState.NO)
254        self.assertEqual(result, '14')
255
256        result = self.ss_board._get_relay_url_code(self.r7.position,
257                                                   RelayState.NC)
258        self.assertEqual(result, '15')
259
260    def test_load_page_status(self):
261        self._set_status_page('0000111100001111')
262        result = self.ss_board._load_page(SainSmartBoard.HIDDEN_STATUS_PAGE)
263        self.assertTrue(
264            result.endswith(
265                '0000111100001111TUX">0000111100001111TUX</a><p>'))
266
267    def test_load_page_relay(self):
268        result = self.ss_board._load_page('00')
269        self.assertEqual(result, self.RELAY_OFF_PAGE_CONTENTS)
270
271        result = self.ss_board._load_page('01')
272        self.assertEqual(result, self.RELAY_ON_PAGE_CONTENTS)
273
274    def test_load_page_no_connection(self):
275        with self.assertRaises(errors.RelayDeviceConnectionError):
276            self.ss_board._load_page('**')
277
278    def _set_status_page(self, status_16_chars):
279        with open(self.test_dir[7:] + '99', 'w+') as status_file:
280            status_file.write(
281                self.STATUS_MSG.format(self.test_dir[:-1], self.test_dir[:-1],
282                                       status_16_chars, status_16_chars))
283
284    def _test_sync_status_dict(self, status_16_chars):
285        self._set_status_page(status_16_chars)
286        expected_dict = dict()
287
288        for index, char in enumerate(status_16_chars):
289            expected_dict[
290                index] = RelayState.NC if char == '1' else RelayState.NO
291
292        self.ss_board._sync_status_dict()
293        self.assertDictEqual(expected_dict, self.ss_board.status_dict)
294
295    def test_sync_status_dict(self):
296        self._test_sync_status_dict('0000111100001111')
297        self._test_sync_status_dict('0000000000000000')
298        self._test_sync_status_dict('0101010101010101')
299        self._test_sync_status_dict('1010101010101010')
300        self._test_sync_status_dict('1111111111111111')
301
302    def test_get_relay_status_status_dict_none(self):
303        self._set_status_page('1111111111111111')
304        self.ss_board.status_dict = None
305        self.assertEqual(
306            self.ss_board.get_relay_status(self.r0.position), RelayState.NC)
307
308    def test_get_relay_status_status_dict_on(self):
309        self.r0.set(RelayState.NC)
310        self.assertEqual(
311            self.ss_board.get_relay_status(self.r0.position), RelayState.NC)
312
313    def test_get_relay_status_status_dict_off(self):
314        self.r0.set(RelayState.NO)
315        self.assertEqual(
316            self.ss_board.get_relay_status(self.r0.position), RelayState.NO)
317
318    def test_set_on(self):
319        os.utime(self.test_dir[7:] + '01', (0, 0))
320        self.ss_board.set(self.r0.position, RelayState.NC)
321        self.assertNotEqual(os.stat(self.test_dir[7:] + '01').st_atime, 0)
322
323    def test_set_off(self):
324        os.utime(self.test_dir[7:] + '00', (0, 0))
325        self.ss_board.set(self.r0.position, RelayState.NO)
326        self.assertNotEqual(os.stat(self.test_dir[7:] + '00').st_atime, 0)
327
328    def test_connection_error_no_tux(self):
329        default_status_msg = self.STATUS_MSG
330        self.STATUS_MSG = self.STATUS_MSG.replace('TUX', '')
331        try:
332            self._set_status_page('1111111111111111')
333            self.ss_board.get_relay_status(0)
334        except RelayDeviceConnectionError:
335            self.STATUS_MSG = default_status_msg
336            return
337
338        self.fail('Should have thrown an error without TUX appearing.')
339
340
341class ActsRelayRigTest(unittest.TestCase):
342    def setUp(self):
343        Relay.transition_wait_time = 0
344        Relay.button_press_time = 0
345        self.config = {
346            'boards': [{
347                'type': 'SainSmartBoard',
348                'name': 'ss_control',
349                'base_url': 'http://192.168.1.4/30000/'
350            }, {
351                'type': 'SainSmartBoard',
352                'name': 'ss_control_2',
353                'base_url': 'http://192.168.1.4/30000/'
354            }],
355            'devices': [{
356                'type': 'GenericRelayDevice',
357                'name': 'device',
358                'relays': {
359                    'Relay00': 'ss_control/0',
360                    'Relay10': 'ss_control/1'
361                }
362            }]
363        }
364
365    def tearDown(self):
366        Relay.transition_wait_time = .2
367        Relay.button_press_time = .25
368
369    def test_init_relay_rig_missing_boards(self):
370        flawed_config = copy.deepcopy(self.config)
371        del flawed_config['boards']
372        with self.assertRaises(errors.RelayConfigError):
373            RelayRig(flawed_config)
374
375    def test_init_relay_rig_is_not_list(self):
376        flawed_config = copy.deepcopy(self.config)
377        flawed_config['boards'] = self.config['boards'][0]
378        with self.assertRaises(errors.RelayConfigError):
379            RelayRig(flawed_config)
380
381    def test_init_relay_rig_duplicate_board_names(self):
382        flawed_config = copy.deepcopy(self.config)
383        flawed_config['boards'][1]['name'] = (self.config['boards'][0]['name'])
384        with self.assertRaises(errors.RelayConfigError):
385            RelayRigMock(flawed_config)
386
387    def test_init_relay_rig_device_gets_relays(self):
388        modded_config = copy.deepcopy(self.config)
389        del modded_config['devices'][0]['relays']['Relay00']
390        rig = RelayRigMock(modded_config)
391        self.assertEqual(len(rig.relays), 4)
392        self.assertEqual(len(rig.devices['device'].relays), 1)
393
394        rig = RelayRigMock(self.config)
395        self.assertEqual(len(rig.devices['device'].relays), 2)
396
397    def test_init_relay_rig_correct_device_type(self):
398        rig = RelayRigMock(self.config)
399        self.assertEqual(len(rig.devices), 1)
400        self.assertIsInstance(rig.devices['device'], GenericRelayDevice)
401
402    def test_init_relay_rig_missing_devices_creates_generic_device(self):
403        modded_config = copy.deepcopy(self.config)
404        del modded_config['devices']
405        rig = RelayRigMock(modded_config)
406        self.assertEqual(len(rig.devices), 1)
407        self.assertIsInstance(rig.devices['device'], GenericRelayDevice)
408        self.assertDictEqual(rig.devices['device'].relays, rig.relays)
409
410
411class RelayRigMock(RelayRig):
412    """A RelayRig that substitutes the MockBoard for any board."""
413
414    _board_constructors = {
415        'SainSmartBoard': lambda x: MockBoard(x),
416        'FuguMockBoard': lambda x: FuguMockBoard(x)
417    }
418
419    def __init__(self, config=None):
420        if not config:
421            config = {
422                "boards": [{
423                    'name': 'MockBoard',
424                    'type': 'SainSmartBoard'
425                }]
426            }
427
428        RelayRig.__init__(self, config)
429
430
431class ActsGenericRelayDeviceTest(unittest.TestCase):
432    def setUp(self):
433        Relay.transition_wait_time = 0
434        Relay.button_press_time = 0
435        self.board_config = {'name': 'MockBoard', 'type': 'SainSmartBoard'}
436
437        self.board = MockBoard(self.board_config)
438        self.r0 = self.board.relays[0]
439        self.r1 = self.board.relays[1]
440
441        self.device_config = {
442            'name': 'MockDevice',
443            'relays': {
444                'r0': 'MockBoard/0',
445                'r1': 'MockBoard/1'
446            }
447        }
448        config = {
449            'boards': [self.board_config],
450            'devices': [self.device_config]
451        }
452        self.rig = RelayRigMock(config)
453        self.rig.boards['MockBoard'] = self.board
454        self.rig.relays[self.r0.relay_id] = self.r0
455        self.rig.relays[self.r1.relay_id] = self.r1
456
457    def tearDown(self):
458        Relay.transition_wait_time = .2
459        Relay.button_press_time = .25
460
461    def test_setup_single_relay(self):
462        self.r0.set(RelayState.NC)
463        self.r1.set(RelayState.NC)
464
465        modified_config = copy.deepcopy(self.device_config)
466        del modified_config['relays']['r1']
467
468        grd = GenericRelayDevice(modified_config, self.rig)
469        grd.setup()
470
471        self.assertEqual(self.r0.get_status(), RelayState.NO)
472        self.assertEqual(self.r1.get_status(), RelayState.NC)
473
474    def test_setup_multiple_relays(self):
475        self.board.set(self.r0.position, RelayState.NC)
476        self.board.set(self.r1.position, RelayState.NC)
477
478        grd = GenericRelayDevice(self.device_config, self.rig)
479        grd.setup()
480
481        self.assertEqual(self.r0.get_status(), RelayState.NO)
482        self.assertEqual(self.r1.get_status(), RelayState.NO)
483
484    def test_cleanup_single_relay(self):
485        self.test_setup_single_relay()
486
487    def test_cleanup_multiple_relays(self):
488        self.test_setup_multiple_relays()
489
490    def change_state(self, begin_state, call, end_state, previous_state=None):
491        self.board.set(self.r0.position, begin_state)
492        grd = GenericRelayDevice(self.device_config, self.rig)
493        call(grd)
494        self.assertEqual(self.r0.get_status(), end_state)
495        if previous_state:
496            self.assertEqual(
497                self.board.relay_previous_states[self.r0.position],
498                previous_state)
499
500    def test_press_while_no(self):
501        self.change_state(RelayState.NO, lambda x: x.press('r0'),
502                          RelayState.NO, RelayState.NC)
503
504    def test_press_while_nc(self):
505        self.change_state(RelayState.NC, lambda x: x.press('r0'),
506                          RelayState.NO, RelayState.NC)
507
508    def test_hold_down_while_no(self):
509        self.change_state(RelayState.NO, lambda x: x.hold_down('r0'),
510                          RelayState.NC)
511
512    def test_hold_down_while_nc(self):
513        self.change_state(RelayState.NC, lambda x: x.hold_down('r0'),
514                          RelayState.NC)
515
516    def test_release_while_nc(self):
517        self.change_state(RelayState.NC, lambda x: x.release('r0'),
518                          RelayState.NO)
519
520
521class ActsRelayDeviceTest(unittest.TestCase):
522    def setUp(self):
523        Relay.transition_wait_time = 0
524        Relay.button_press_time = 0
525
526        self.board_config = {
527            'name': 'MockBoard',
528            'relays': [{
529                'id': 0,
530                'relay_pos': 0
531            }, {
532                'id': 1,
533                'relay_pos': 1
534            }]
535        }
536
537        self.board = MockBoard(self.board_config)
538        self.r0 = Relay(self.board, 0)
539        self.r1 = Relay(self.board, 1)
540        self.board.set(self.r0.position, RelayState.NO)
541        self.board.set(self.r1.position, RelayState.NO)
542
543        self.rig = RelayRigMock()
544        self.rig.boards['MockBoard'] = self.board
545        self.rig.relays[self.r0.relay_id] = self.r0
546        self.rig.relays[self.r1.relay_id] = self.r1
547
548        self.device_config = {
549            "type": "GenericRelayDevice",
550            "name": "device",
551            "relays": {
552                'r0': 'MockBoard/0',
553                'r1': 'MockBoard/1'
554            }
555        }
556
557    def tearDown(self):
558        Relay.transition_wait_time = .2
559        Relay.button_press_time = .25
560
561    def test_init_raise_on_name_missing(self):
562        flawed_config = copy.deepcopy(self.device_config)
563        del flawed_config['name']
564        with self.assertRaises(errors.RelayConfigError):
565            RelayDevice(flawed_config, self.rig)
566
567    def test_init_raise_on_name_wrong_type(self):
568        flawed_config = copy.deepcopy(self.device_config)
569        flawed_config['name'] = {}
570        with self.assertRaises(errors.RelayConfigError):
571            RelayDevice(flawed_config, self.rig)
572
573    def test_init_raise_on_relays_missing(self):
574        flawed_config = copy.deepcopy(self.device_config)
575        del flawed_config['relays']
576        with self.assertRaises(errors.RelayConfigError):
577            RelayDevice(flawed_config, self.rig)
578
579    def test_init_raise_on_relays_wrong_type(self):
580        flawed_config = copy.deepcopy(self.device_config)
581        flawed_config['relays'] = str
582        with self.assertRaises(errors.RelayConfigError):
583            RelayDevice(flawed_config, self.rig)
584
585    def test_init_raise_on_relays_is_empty(self):
586        flawed_config = copy.deepcopy(self.device_config)
587        flawed_config['relays'] = []
588        with self.assertRaises(errors.RelayConfigError):
589            RelayDevice(flawed_config, self.rig)
590
591    def test_init_raise_on_relays_are_dicts_without_names(self):
592        flawed_config = copy.deepcopy(self.device_config)
593        flawed_config['relays'] = [{'id': 0}, {'id': 1}]
594        with self.assertRaises(errors.RelayConfigError):
595            RelayDevice(flawed_config, self.rig)
596
597    def test_init_raise_on_relays_are_dicts_without_ids(self):
598        flawed_config = copy.deepcopy(self.device_config)
599        flawed_config['relays'] = [{'name': 'r0'}, {'name': 'r1'}]
600        with self.assertRaises(errors.RelayConfigError):
601            RelayDevice(flawed_config, self.rig)
602
603    def test_init_pass_relays_have_ids_and_names(self):
604        RelayDevice(self.device_config, self.rig)
605
606
607class TestRelayRigParser(unittest.TestCase):
608    def setUp(self):
609        Relay.transition_wait_time = 0
610        Relay.button_press_time = 0
611        self.board_config = {
612            'name': 'MockBoard',
613            'relays': [{
614                'id': 'r0',
615                'relay_pos': 0
616            }, {
617                'id': 'r1',
618                'relay_pos': 1
619            }]
620        }
621        self.r0 = self.board_config['relays'][0]
622        self.r1 = self.board_config['relays'][1]
623        self.board = MockBoard(self.board_config)
624
625    def tearDown(self):
626        Relay.transition_wait_time = .2
627        Relay.button_press_time = .25
628
629    def test_create_relay_board_raise_on_missing_type(self):
630        with self.assertRaises(errors.RelayConfigError):
631            RelayRigMock().create_relay_board(self.board_config)
632
633    def test_create_relay_board_valid_config(self):
634        config = copy.deepcopy(self.board_config)
635        config['type'] = 'SainSmartBoard'
636        RelayRigMock().create_relay_board(config)
637
638    def test_create_relay_board_raise_on_type_not_found(self):
639        flawed_config = copy.deepcopy(self.board_config)
640        flawed_config['type'] = 'NonExistentBoard'
641        with self.assertRaises(errors.RelayConfigError):
642            RelayRigMock().create_relay_board(flawed_config)
643
644    def test_create_relay_device_create_generic_on_missing_type(self):
645        rig = RelayRigMock()
646        rig.relays['r0'] = self.r0
647        rig.relays['r1'] = self.r1
648        config = {
649            'name': 'name',
650            'relays': {
651                'r0': 'MockBoard/0',
652                'r1': 'MockBoard/1'
653            }
654        }
655        device = rig.create_relay_device(config)
656        self.assertIsInstance(device, GenericRelayDevice)
657
658    def test_create_relay_device_config_with_type(self):
659        rig = RelayRigMock()
660        rig.relays['r0'] = self.r0
661        rig.relays['r1'] = self.r1
662        config = {
663            'type': 'GenericRelayDevice',
664            'name': '.',
665            'relays': {
666                'r0': 'MockBoard/0',
667                'r1': 'MockBoard/1'
668            }
669        }
670        device = rig.create_relay_device(config)
671        self.assertIsInstance(device, GenericRelayDevice)
672
673    def test_create_relay_device_raise_on_type_not_found(self):
674        rig = RelayRigMock()
675        rig.relays['r0'] = self.r0
676        rig.relays['r1'] = self.r1
677        config = {
678            'type': 'SomeInvalidType',
679            'name': '.',
680            'relays': [{
681                'name': 'r0',
682                'pos': 'MockBoard/0'
683            }, {
684                'name': 'r1',
685                'pos': 'MockBoard/1'
686            }]
687        }
688        with self.assertRaises(errors.RelayConfigError):
689            rig.create_relay_device(config)
690
691
692class TestSynchronizeRelays(unittest.TestCase):
693    def test_synchronize_relays(self):
694        Relay.transition_wait_time = .1
695        with SynchronizeRelays():
696            self.assertEqual(Relay.transition_wait_time, 0)
697        self.assertEqual(Relay.transition_wait_time, .1)
698
699
700class FuguMockBoard(MockBoard):
701    def get_relay_position_list(self):
702        return range(4)
703
704
705class TestFuguRemote(unittest.TestCase):
706    def setUp(self):
707        Relay.transition_wait_time = 0
708        self.mock_rig = RelayRigMock({
709            "boards": [{
710                'name': 'MockBoard',
711                'type': 'FuguMockBoard'
712            }]
713        })
714        self.mock_board = self.mock_rig.boards['MockBoard']
715        self.fugu_config = {
716            'type': 'FuguRemote',
717            'name': 'UniqueDeviceName',
718            'mac_address': '00:00:00:00:00:00',
719            'relays': {
720                'Power': 'MockBoard/0',
721                fugu_remote.Buttons.BACK.value: 'MockBoard/1',
722                fugu_remote.Buttons.HOME.value: 'MockBoard/2',
723                fugu_remote.Buttons.PLAY_PAUSE.value: 'MockBoard/3'
724            }
725        }
726        Relay.button_press_time = 0
727
728    def tearDown(self):
729        Relay.button_press_time = .25
730        Relay.transition_wait_time = .2
731
732    def test_config_missing_button(self):
733        """FuguRemote __init__ should throw an error if a relay is missing."""
734        flawed_config = copy.deepcopy(self.fugu_config)
735        del flawed_config['relays']['Power']
736        del flawed_config['relays'][fugu_remote.Buttons.BACK.value]
737        with self.assertRaises(errors.RelayConfigError):
738            fugu_remote.FuguRemote(flawed_config, self.mock_rig)
739
740    def test_config_missing_mac_address(self):
741        """FuguRemote __init__ should throw an error without a mac address."""
742        flawed_config = copy.deepcopy(self.fugu_config)
743        del flawed_config['mac_address']
744        with self.assertRaises(errors.RelayConfigError):
745            fugu_remote.FuguRemote(flawed_config, self.mock_rig)
746
747    def test_config_no_issues(self):
748        """FuguRemote __init__ should not throw errors for a correct config."""
749        fugu_remote.FuguRemote(self.fugu_config, self.mock_rig)
750
751    def test_power_nc_after_setup(self):
752        """Power should be NORMALLY_CLOSED after calling setup if it exists."""
753        fugu = fugu_remote.FuguRemote(self.fugu_config, self.mock_rig)
754        fugu.setup()
755        self.assertEqual(self.mock_board.get_relay_status(0), RelayState.NC)
756        pass
757
758    def press_button_success(self, relay_position):
759        self.assertEqual(self.mock_board.relay_states[relay_position],
760                         RelayState.NO)
761        self.assertEqual(self.mock_board.relay_previous_states[relay_position],
762                         RelayState.NC)
763
764    def test_press_play_pause(self):
765        fugu = fugu_remote.FuguRemote(self.fugu_config, self.mock_rig)
766        fugu.press_play_pause()
767        self.press_button_success(3)
768
769    def test_press_back(self):
770        fugu = fugu_remote.FuguRemote(self.fugu_config, self.mock_rig)
771        fugu.press_back()
772        self.press_button_success(1)
773
774    def test_press_home(self):
775        fugu = fugu_remote.FuguRemote(self.fugu_config, self.mock_rig)
776        fugu.press_home()
777        self.press_button_success(2)
778
779    def test_enter_pairing_mode(self):
780        fugu = fugu_remote.FuguRemote(self.fugu_config, self.mock_rig)
781        fugu_remote.PAIRING_MODE_WAIT_TIME = 0
782        fugu.enter_pairing_mode()
783        self.press_button_success(2)
784        self.press_button_success(1)
785
786
787class TestRelayDict(unittest.TestCase):
788    def test_init(self):
789        mock_device = object()
790        blank_dict = dict()
791        relay_dict = RelayDict(mock_device, blank_dict)
792        self.assertEqual(relay_dict._store, blank_dict)
793        self.assertEqual(relay_dict.relay_device, mock_device)
794
795    def test_get_item_valid_key(self):
796        mock_device = object()
797        blank_dict = {'key': 'value'}
798        relay_dict = RelayDict(mock_device, blank_dict)
799        self.assertEqual(relay_dict['key'], 'value')
800
801    def test_get_item_invalid_key(self):
802        # Create an object with a single attribute 'name'
803        mock_device = type('', (object, ), {'name': 'name'})()
804        blank_dict = {'key': 'value'}
805        relay_dict = RelayDict(mock_device, blank_dict)
806        with self.assertRaises(errors.RelayConfigError):
807            value = relay_dict['not_key']
808
809    def test_iter(self):
810        mock_device = type('', (object, ), {'name': 'name'})()
811        data_dict = {'a': '1', 'b': '2', 'c': '3'}
812        relay_dict = RelayDict(mock_device, data_dict)
813
814        rd_set = set()
815        for key in relay_dict:
816            rd_set.add(key)
817        dd_set = set()
818        for key in data_dict:
819            dd_set.add(key)
820
821        self.assertSetEqual(rd_set, dd_set)
822
823
824if __name__ == "__main__":
825    unittest.main()
826