def test_buffer(self): event = Event() block = EventBuffer(event) block._backup = MagicMock() self.configure_block(block, {"interval": {"milliseconds": 200}}) block.start() block.process_signals([Signal(), Signal(), Signal(), Signal()]) self.assert_num_signals_notified(0, block) event.wait(.3) self.assert_num_signals_notified(4, block) block.stop()
def test_debounce(self): block = Debouncer() block._backup = MagicMock() self.configure_block(block, {"interval": {"milliseconds": 200}}) block.start() block.process_signals([Signal(), Signal()]) block.process_signals([Signal()]) self.assert_num_signals_notified(1, block) sleep(.3) block.process_signals([Signal()]) self.assert_num_signals_notified(2, block) block.stop()
def test_interval_duration(self): event = Event() block = EventBuffer(event) block._backup = MagicMock() self.configure_block( block, { "interval": { "milliseconds": 1000 }, "interval_duration": { "milliseconds": 2000 } }) block.start() # process 4 signals (first group) block.process_signals([Signal(), Signal(), Signal(), Signal()]) self.assert_num_signals_notified(0, block) event.wait(1.3) # first emit notifies first group self.assert_num_signals_notified(4, block) # process 2 more signals (second group) block.process_signals([Signal(), Signal()]) event.wait(1.3) # second emit notifies first group and second group self.assert_num_signals_notified(10, block) # process 2 more signals (thrid group) block.process_signals([Signal(), Signal()]) event.wait(1.3) # third emit notifies second group and third group self.assert_num_signals_notified(14, block) block.stop()
def test_one_value(self): signals = [{ 'key': 'cherry', 'value': 'S' }, { 'key': 'cherry', 'value': 'M' }, { 'key': 'cherry', 'value': 'L' }, { 'key': 'banana', 'value': 'S' }, { 'key': 'apple', 'value': 'S' }, { 'flavor': 'bad' }] blk = HashTable() config = {'one_value': True} self.configure_block(blk, config) blk.start() blk.process_signals([Signal(s) for s in signals]) self.assert_num_signals_notified(1, blk) self.assertEqual('L', self.notified_signals[0].cherry) self.assertEqual('S', self.notified_signals[0].banana) self.assertEqual('S', self.notified_signals[0].apple) # Make sure the bad one didn't make its way into the output signal self.assertFalse(hasattr(self.notified_signals[0], 'flavor')) blk.stop()
def test_defaults(self): signals = [{ 'key': 'cherry', 'value': 'S' }, { 'key': 'cherry', 'value': 'M' }, { 'key': 'cherry', 'value': 'L' }, { 'key': 'banana', 'value': 'S' }, { 'key': 'apple', 'value': 'S' }, { 'flavor': 'bad' }] blk = HashTable() config = {} self.configure_block(blk, config) blk.start() blk.process_signals([Signal(s) for s in signals]) self.assert_num_signals_notified(1, blk) self.assertEqual(['S', 'M', 'L'], self.notified_signals[0].cherry) self.assertEqual(['S'], self.notified_signals[0].banana) self.assertEqual(['S'], self.notified_signals[0].apple)
def test_hash(self): signals = [{ 'flavor': 'cherry' }, { 'flavor': 'cherry', 'size': 'S' }, { 'flavor': 'cherry', 'size': 'M' }, { 'flavor': 'cherry', 'size': 'L' }, { 'flavor': 'banana', 'size': 'S' }, { 'flavor': 'apple', 'size': 'S' }] blk = HashTable() config = { "key": "{{$flavor}}", "value": "{{$size}}", } self.configure_block(blk, config) blk.start() blk.process_signals([Signal(s) for s in signals]) self.assert_num_signals_notified(1, blk) self.assertEqual(['S', 'M', 'L'], self.notified_signals[0].cherry) self.assertEqual(['S'], self.notified_signals[0].banana) self.assertEqual(['S'], self.notified_signals[0].apple) blk.stop()
def _perform_hash(self, signals): hash_dict = defaultdict(None) if self.one_value else defaultdict(list) for signal in signals: try: sig_key = self.key(signal) # Signal keys need to be strings sig_key = str(sig_key) sig_value = self.value(signal) except AttributeError: # If we don't have the value on the signal, don't add it to the # hash table continue except Exception as e: self._logger.warning("Failed to evaluate props: {}".format(e)) # Add sig_value to the proper hash key try: if sig_key is not None: if self.one_value: hash_dict[sig_key] = sig_value else: hash_dict[sig_key].append(sig_value) else: self._logger.debug("Skipping key: {}".format(sig_key)) except Exception as e: self._logger.warning( "Failed to add value {} to key {}: {}".format( sig_value, sig_key, e)) if len(hash_dict): return Signal(hash_dict)
def test_process_signals(self): blk = Example() self.configure_block(blk, {}) blk.start() blk.process_signals([Signal()]) blk.stop() self.assert_num_signals_notified(1) self.assertDictEqual(self.last_notified['default'][0].to_dict(), {})
def test_invalid_command(self, mock_serial, mock_xbee): blk = XBeeRemoteAT() self.configure_block(blk, {"command": "{{ 1 }}"}) blk.start() blk.process_signals([Signal({'iama': 'signal'})]) # send is never called because of the command not being ascii encodable # It needs to be a two ascii characters self.assertFalse(blk._xbee.send.called) blk.stop()
def test_invalid_dest_addr(self, mock_serial, mock_xbee): blk = XBeeRemoteAT() self.configure_block(blk, {"dest_addr": "0"}) blk.start() blk.process_signals([Signal({'iama': 'signal'})]) # send is never called because of the odd length dest_addr # It needs to be a byte, represented as two ascii chars self.assertFalse(blk._xbee.send.called) blk.stop()
def test_debounce_group(self): block = Debouncer() block._backup = MagicMock() self.configure_block(block, { "interval": { "milliseconds": 200 }, "group_by": "{{$foo}}" }) block.start() block.process_signals([ Signal({'foo': 'bar'}), ]) self.assert_num_signals_notified(1, block) block.process_signals([Signal({'foo': 'bar'}), Signal({'foo': 'qux'})]) self.assert_num_signals_notified(2, block) sleep(.3) block.process_signals([Signal({'foo': 'bar'})]) self.assert_num_signals_notified(3, block) block.stop()
def test_defaults(self, mock_serial, mock_xbee): blk = XBeeRemoteAT() self.configure_block(blk, {}) blk.start() blk.process_signals([Signal({'iama': 'signal'})]) blk._xbee.send.assert_called_once_with('remote_at', frame_id=b'\x01', dest_addr=b'\xFF\xFF', command=b'ID', parameter=b'') self.assertFalse(len(self.signals['default'])) blk.stop()
def test_grouping(self): signals = [{ 'group': 'fruit', 'key': 'cherry', 'value': 'S' }, { 'group': 'fruit', 'key': 'cherry', 'value': 'M' }, { 'group': 'fruit', 'key': 'cherry', 'value': 'L' }, { 'group': 'pie', 'key': 'banana', 'value': 'S' }, { 'group': 'pie', 'key': 'cherry', 'value': 'M' }, { 'group': 'pie', 'key': 'cherry', 'value': 'L' }, { 'group': 'fruit', 'key': 'banana', 'value': 'S' }] blk = HashTable() config = { 'group_attr': 'my_group', 'group_by': '{{$group}}', 'log_level': 'DEBUG' } self.configure_block(blk, config) blk.process_signals([Signal(s) for s in signals]) self.assert_num_signals_notified(2, blk) for sig_out in self.notified_signals: # Make sure the group got assigned to the right attr self.assertIn(sig_out.my_group, ['fruit', 'pie']) # Assert the right values went to the right groups if sig_out.my_group == 'fruit': self.assertEqual(len(sig_out.cherry), 3) self.assertEqual(len(sig_out.banana), 1) elif sig_out.my_group == 'pie': self.assertEqual(len(sig_out.cherry), 2) self.assertEqual(len(sig_out.banana), 1)
def process_signals(self, signals): return_signals = [] for signal in signals: try: values = self.list(signal) except Exception as e: values = [None] values = [None] if not values else values for value in values: sig = Signal(signal.to_dict()) setattr(sig, self.title, value) return_signals.append(sig) if return_signals: self.notify_signals(return_signals)
def test_expression_props(self, mock_serial, mock_xbee): blk = XBeeRemoteAT() self.configure_block(blk, { "dest_addr": "00 42", "command": "D0", "parameter": "05" }) blk.start() blk.process_signals([Signal({'iama': 'signal'})]) blk._xbee.send.assert_called_once_with('remote_at', frame_id=b'\x01', dest_addr=b'\x00\x42', command=b'D0', parameter=b'\x05') self.assertFalse(len(self.signals['default'])) blk.stop()
def test_non_string_attributes(self): now = datetime.datetime.utcnow() signals = [ { 'name': 123, 'value': 456 }, { 'name': 'str', 'value': 'string' }, { 'name': {}, 'value': {} }, { 'name': None, 'value': None }, { 'name': now, 'value': now }, ] blk = HashTable() self.configure_block(blk, { "key": "{{ $name }}", "value": "{{ $value }}", "one_value": True }) blk.start() blk.process_signals([Signal(s) for s in signals]) self.assert_num_signals_notified(1, blk) self.assertDictEqual( self.notified_signals[0].to_dict(), { "group": "null", "123": 456, "str": "string", "{}": {}, "None": None, str(now): now }) blk.stop()
def emit(self, reset=False): self._logger.debug('Emitting signals') if reset: self._emission_job.cancel() self._emission_job = Job( self.emit, self.interval, True ) self._last_emission = datetime.utcnow() signals = self._get_emit_signals() if signals: self._logger.debug('Notifying {} signals'.format(len(signals))) self.notify_signals(signals) elif self.timeout: self._logger.debug('Notifying timeout signal') self.notify_signals([Signal({self.timeout_attr: True})]) else: self._logger.debug('No signals to notify') if self.use_persistence: self.persistence.store('last_emission', self._last_emission) self._backup()
def _callback(self, response): try: self.notify_signals([Signal(response)]) except: self._logger.exception( 'Response is not valid: {}'.format(response))