class TestUnitCommandInstrumentProtocol(MiUnitTestCase): """ Test cases for instrument protocol class. Functions in this class provide instrument protocol unit tests and provide a tutorial on use of the protocol interface. """ class TestState(BaseEnum): """ Protocol states for SBE37. Cherry picked from DriverProtocolState enum. """ TEST = "TEST" class TestEvent(BaseEnum): """ Protocol events for SBE37. Cherry picked from DriverEvent enum. """ ENTER = "ENTER" EXIT = "EXIT" TEST = "TEST" def setUp(self): """ """ self.prompts = [">"] self.newline = "\n" self.callback_result = None self._trigger_count = 0 self._events = [] self.protocol = CommandResponseInstrumentProtocol( self.prompts, self.newline, self.event_callback) self.protocol_fsm = ThreadSafeFSM(self.TestState, self.TestEvent, self.TestEvent.ENTER, self.TestEvent.EXIT) self.protocol_fsm.add_handler(self.TestState.TEST, self.TestEvent.TEST, lambda x: x) self.protocol._add_build_handler(self.TestEvent.TEST, self._build_simple_command) self.protocol._add_response_handler(self.TestEvent.TEST, self._parse_test_response) self.protocol._connection = Mock() self.protocol._connection.send = lambda x: self.protocol.add_to_buffer( "%s >->" % x) self.protocol.get_current_state = Mock( return_value=self.TestState.TEST) self.protocol._send_wakeup = lambda: self.protocol.add_to_buffer( "wakeup response >->") self.protocol._wakeup = functools.partial(self.protocol._wakeup, delay=0) def _build_simple_command(self, cmd): return "cmd...do it!" def _parse_test_response(self, resp, prompt): return "c=%s p=%s" % (resp, prompt) def event_callback(self, event, value=None): log.debug("Test event callback: %s" % event) self._events.append(event) self._trigger_count += 1 def test_cmd_response(self): """ Test getting a response from a command supplied with prompts and regexes. """ regex1 = re.compile(r'.*(do it).*') regex2 = re.compile(r'foobar') regex3 = re.compile(r'.*(do) (it).*') regex4 = re.compile(r'.*do it.*') # Normal case result = self.protocol._do_cmd_resp(self.TestEvent.TEST) self.assertEqual( result, self._parse_test_response( self._build_simple_command(None) + " >", ">")) # expected prompt cases result = self.protocol._do_cmd_resp(self.TestEvent.TEST, expected_prompt=">") self.assertEqual( result, self._parse_test_response( self._build_simple_command(None) + " >", ">")) result = self.protocol._do_cmd_resp(self.TestEvent.TEST, expected_prompt=">-") self.assertEqual( result, self._parse_test_response( self._build_simple_command(None) + " >-", ">-")) # Should time out looking for a bad prompt self.assertRaises(InstrumentTimeoutException, self.protocol._do_cmd_resp, self.TestEvent.TEST, expected_prompt="-->", timeout=.1) # regex cases result = self.protocol._do_cmd_resp(self.TestEvent.TEST, response_regex=regex1) self.assertEqual(result, self._parse_test_response("do it", "")) result = self.protocol._do_cmd_resp(self.TestEvent.TEST, response_regex=regex3) self.assertEqual(result, self._parse_test_response("doit", "")) result = self.protocol._do_cmd_resp(self.TestEvent.TEST, response_regex=regex4) self.assertEqual(result, self._parse_test_response("", "")) # Should time out looking for a bad regex self.assertRaises(InstrumentTimeoutException, self.protocol._do_cmd_resp, self.TestEvent.TEST, response_regex=regex2, timeout=.1) # combo case self.assertRaises(InstrumentProtocolException, self.protocol._do_cmd_resp, self.TestEvent.TEST, expected_prompt=">", response_regex=regex1)
class TestUnitCommandInstrumentProtocol(MiUnitTestCase): """ Test cases for instrument protocol class. Functions in this class provide instrument protocol unit tests and provide a tutorial on use of the protocol interface. """ class TestState(BaseEnum): """ Protocol states for SBE37. Cherry picked from DriverProtocolState enum. """ TEST = "TEST" class TestEvent(BaseEnum): """ Protocol events for SBE37. Cherry picked from DriverEvent enum. """ ENTER = "ENTER" EXIT = "EXIT" TEST = "TEST" def setUp(self): """ """ self.prompts = [">"] self.newline = "\n" self.callback_result = None self._trigger_count = 0 self._events = [] self.protocol = CommandResponseInstrumentProtocol(self.prompts, self.newline, self.event_callback) self.protocol_fsm = ThreadSafeFSM(self.TestState, self.TestEvent, self.TestEvent.ENTER, self.TestEvent.EXIT) self.protocol_fsm.add_handler(self.TestState.TEST, self.TestEvent.TEST, lambda x : x) self.protocol._add_build_handler(self.TestEvent.TEST, self._build_simple_command) self.protocol._add_response_handler(self.TestEvent.TEST, self._parse_test_response) self.protocol._connection = Mock() self.protocol._connection.send = lambda x : self.protocol.add_to_buffer("%s >->" % x) self.protocol.get_current_state = Mock(return_value=self.TestState.TEST) self.protocol._send_wakeup = lambda: self.protocol.add_to_buffer("wakeup response >->") def _build_simple_command(self, cmd): return "cmd...do it!" def _parse_test_response(self, resp, prompt): return "c=%s p=%s" % (resp, prompt) def event_callback(self, event, value=None): log.debug("Test event callback: %s" % event) self._events.append(event) self._trigger_count += 1 def test_cmd_response(self): """ Test getting a response from a command supplied with prompts and regexes. """ regex1 = re.compile(r'.*(do it).*') regex2 = re.compile(r'foobar') regex3 = re.compile(r'.*(do) (it).*') regex4 = re.compile(r'.*do it.*') # Normal case result = self.protocol._do_cmd_resp(self.TestEvent.TEST) self.assertEqual(result, self._parse_test_response(self._build_simple_command(None)+" >", ">")) # expected prompt cases result = self.protocol._do_cmd_resp(self.TestEvent.TEST, expected_prompt=">") self.assertEqual(result, self._parse_test_response(self._build_simple_command(None)+" >", ">")) result = self.protocol._do_cmd_resp(self.TestEvent.TEST, expected_prompt=">-") self.assertEqual(result, self._parse_test_response(self._build_simple_command(None)+" >-", ">-")) # Should time out looking for a bad prompt self.assertRaises(InstrumentTimeoutException, self.protocol._do_cmd_resp, self.TestEvent.TEST, expected_prompt="-->", timeout=5) # regex cases result = self.protocol._do_cmd_resp(self.TestEvent.TEST, response_regex=regex1) self.assertEqual(result, self._parse_test_response("do it", "")) result = self.protocol._do_cmd_resp(self.TestEvent.TEST, response_regex=regex3) self.assertEqual(result, self._parse_test_response("doit", "")) result = self.protocol._do_cmd_resp(self.TestEvent.TEST, response_regex=regex4) self.assertEqual(result, self._parse_test_response("", "")) # Should time out looking for a bad regex self.assertRaises(InstrumentTimeoutException, self.protocol._do_cmd_resp, self.TestEvent.TEST, response_regex=regex2) # combo case self.assertRaises(InstrumentProtocolException, self.protocol._do_cmd_resp, self.TestEvent.TEST, expected_prompt=">", response_regex=regex1)
class TestUnitInstrumentProtocol(MiUnitTestCase): """ Test cases for instrument protocol class. Functions in this class provide instrument protocol unit tests and provide a tutorial on use of the protocol interface. """ def setUp(self): """ """ self.callback_result = None self._trigger_count = 0 self._events = [] self.protocol = InstrumentProtocol(self.event_callback) def tearDown(self): if self.protocol._scheduler: self.protocol._scheduler._scheduler.shutdown() def event_callback(self, event, value=None): log.debug("Test event callback: %s" % event) self._events.append(event) self._trigger_count += 1 def _scheduler_callback(self): """ Callback to test the scheduler """ self._trigger_count += 1 def assert_scheduled_event_triggered(self, event_count=1): count = 0 for i in range(0, 40): count = self._trigger_count log.debug("check for triggered event, count %d" % self._trigger_count) if (count >= event_count): break time.sleep(0.3) self.assertGreater(count, 0) def test_get_prompts(self): """ ensure prompts are returned sorted by length """ prompts = ['aa', 'bbb', 'c', 'dddd'] expected = ['dddd', 'bbb', 'aa', 'c'] class Prompts(BaseEnum): A = 'aa' B = 'bbb' C = 'c' D = 'dddd' self.protocol = CommandResponseInstrumentProtocol( prompts, '\r\n', self.event_callback) self.assertEqual(self.protocol._get_prompts(), expected) self.protocol = CommandResponseInstrumentProtocol( Prompts, '\r\n', self.event_callback) self.assertEqual(self.protocol._get_prompts(), expected) def test_extraction(self): sample_line = "SATPAR0229,10.01,2206748544,234\r\n" ntptime = ntplib.system_to_ntp_time(time.time()) result = self.protocol._extract_sample(PARParticle, SAMPLE_REGEX, sample_line, ntptime, publish=False) log.debug("R: %s" % result) self.assertEqual(result['stream_name'], PARParticle(None, None).data_particle_type()) # Test the format of the result in the individual driver tests. Here, # just tests that the result is there. def test_get_param_list(self): """ verify get_param_list returns correct parameter lists. """ params = ['foo', 'bar', 'baz'] for key in params: self.protocol._param_dict.add(key, r'', None, None) # All can be passed in as a string self.assertEqual( sorted(params), sorted(self.protocol._get_param_list(DriverParameter.ALL))) # All can be passed in as a single element list self.assertEqual( sorted(params), sorted(self.protocol._get_param_list([DriverParameter.ALL]))) # All can be in a list anywhere, not just the first element self.assertEqual( sorted(params), sorted(self.protocol._get_param_list(['foo', DriverParameter.ALL]))) # Bad parameters raise exceptions event when ALL is specified with self.assertRaises(InstrumentParameterException): self.assertEqual( sorted(params), sorted( self.protocol._get_param_list( ['noparam', DriverParameter.ALL]))) # An exception is raised when the param is not a list or string with self.assertRaises(InstrumentParameterException): self.protocol._get_param_list({'other': 'struct'}) # when a subset is given, the same set is returned. subset = ['bar', 'baz'] self.assertEqual(sorted(subset), sorted(self.protocol._get_param_list(subset))) # verify we can accept a tuple subset = ['bar', 'baz'] self.assertEqual(sorted(subset), sorted(self.protocol._get_param_list(('bar', 'baz')))) # An exception is raised when the param is not known with self.assertRaises(InstrumentParameterException): self.protocol._get_param_list(subset + ['boom']) # Verify we can send in a single parameter as a string, not ALL self.assertEqual(['bar'], self.protocol._get_param_list('bar')) def test_ring_buffer(self): """ verify command thread ring buffer rolls as expected """ prompts = ['aa', 'bbb', 'c', 'dddd'] self.protocol = CommandResponseInstrumentProtocol( prompts, '\r\n', self.event_callback) self.protocol._max_buffer_size = Mock(return_value=5) self.protocol.add_to_buffer("a") self.protocol.add_to_buffer("b") self.protocol.add_to_buffer("c") self.protocol.add_to_buffer("d") self.protocol.add_to_buffer("e") self.assertEqual(len(self.protocol._linebuf), 5) self.assertEqual(len(self.protocol._promptbuf), 5) self.assertEqual(self.protocol._linebuf, "abcde") self.assertEqual(self.protocol._promptbuf, "abcde") self.protocol.add_to_buffer("f") self.assertEqual(len(self.protocol._linebuf), 5) self.assertEqual(len(self.protocol._promptbuf), 5) self.assertEqual(self.protocol._linebuf, "bcdef") self.assertEqual(self.protocol._promptbuf, "bcdef") self.protocol.add_to_buffer("gh") self.assertEqual(len(self.protocol._linebuf), 5) self.assertEqual(len(self.protocol._promptbuf), 5) self.assertEqual(self.protocol._linebuf, "defgh") self.assertEqual(self.protocol._promptbuf, "defgh") @unittest.skip('Not Written') def test_publish_raw(self): """ Tests to see if raw data is appropriately published back out to the InstrumentAgent via the event callback. """ # build a packet # have it published by the protocol (force state if needed) # delay? # catch it in the callback # confirm it came back # compare response to original packet self.assertTrue(False) @unittest.skip('Not Written') def test_publish_parsed_data(self): """ Tests to see if parsed data is appropriately published back to the InstrumentAgent via the event callback. """ # similar to above self.assertTrue(False) @unittest.skip('Not Written') def test_publish_engineering_data(self): """ Tests to see if engineering data is appropriately published back to the InstrumentAgent via the event callback. """ # similar to above self.assertTrue(False) def test_get_running_config(self): """ Checks to see that one can successfully get the running config from an instrument protocol. """ # set some values log.debug("First param_dict: %s", self.protocol._param_dict.get_config()) self.protocol._param_dict.add("foo", r'foo=(.*)', lambda match: int(match.group(1)), lambda x: str(x), direct_access=True, default_value=10) self.protocol._param_dict.set_default( "foo") # test hack to set w/o fetch self.protocol._param_dict.add("bar", r'bar=(.*)', lambda match: int(match.group(1)), lambda x: str(x), direct_access=False, default_value=15) self.protocol._param_dict.set_default("bar") self.assertEquals(self.protocol._param_dict.get("foo"), 10) self.assertEquals(self.protocol._param_dict.get("bar"), 15) result = self.protocol.get_cached_config() self.assertEquals(result['foo'], 10) self.assertEquals(result['bar'], 15) self.protocol._param_dict.update("bar=20") result = self.protocol.get_cached_config() self.assertEquals(result['foo'], 10) self.assertEquals(result['bar'], 20) self.assertEquals(self.protocol._param_dict.get("bar"), 20) # get and check the running config result = self.protocol.get_cached_config() self.assertTrue(isinstance(result, dict)) self.assertEquals(result['foo'], 10) self.assertEquals(result['bar'], 20) def test_init_values(self): """ Test getting and setting the initialization value for a parameter """ self.protocol._param_dict.add("foo", r'foo=(.*)', lambda match: int(match.group(1)), lambda x: str(x), direct_access=True, startup_param=True, default_value=10) self.protocol._param_dict.add("bar", r'bar=(.*)', lambda match: int(match.group(1)), lambda x: str(x), direct_access=False, startup_param=True, default_value=0) self.protocol._param_dict.add("baz", r'baz=(.*)', lambda match: int(match.group(1)), lambda x: str(x), direct_access=True, default_value=20) self.protocol._param_dict.add("bat", r'bat=(.*)', lambda match: int(match.group(1)), lambda x: str(x), startup_param=False, default_value=20) self.protocol._param_dict.add("qux", r'qux=(.*)', lambda match: int(match.group(1)), lambda x: str(x), startup_param=True) self.protocol._param_dict.add("rok", r'rok=(.*)', lambda match: int(match.group(1)), lambda x: str(x)) # set an additional value for test self.protocol._param_dict.update("qux=6666") # mark init params self.assertRaises(InstrumentParameterException, self.protocol.set_init_params, []) self.protocol.set_init_params( {DriverConfigKey.PARAMETERS: { "foo": 1111, "baz": 2222 }}) # get new startup config self.assertRaises(InstrumentProtocolException, self.protocol.get_startup_config) self.protocol.set_init_params({ DriverConfigKey.PARAMETERS: { "foo": 1111, "baz": 2222, "bat": 11, "qux": 22 } }) result = self.protocol.get_startup_config() self.assertEquals(len(result), 5) self.assertEquals(result["foo"], 1111) # init param self.assertEquals(result["bar"], 0) # init param with default value self.assertEquals(result["baz"], 2222) # non-init param, but value specified self.assertEquals(result["bat"], 11) # set param self.assertEquals(result["qux"], 22) # set param self.assertIsNone(result.get("rok")) # defined in paramdict, no config def test_apply_startup_params(self): """ Test that the apply startup parameters method exists and throws a "not implemented" exception for the base class """ self.assertRaises(NotImplementedException, self.protocol.apply_startup_params) def test_scheduler(self): """ Test to see that the scheduler can add and remove jobs properly Jobs are just queued for adding unit we call initialize_scheduler then the jobs are actually created. use an interval job to allow for testing for removal """ job_name = 'test_job' startup_config = { DriverConfigKey.SCHEDULER: { job_name: { DriverSchedulerConfigKey.TRIGGER: { DriverSchedulerConfigKey.TRIGGER_TYPE: TriggerType.INTERVAL, DriverSchedulerConfigKey.SECONDS: 1 }, } } } self.protocol.set_init_params(startup_config) # Verify we are initialized properly self.assertIsNone(self.protocol._scheduler) self.assertEqual(self.protocol._scheduler_config, {}) self.assertEqual(self.protocol._scheduler_callback, {}) # Verify the the scheduler is created self.protocol.initialize_scheduler() self.assertIsInstance(self.protocol._scheduler, DriverScheduler) self.assertEqual(self.protocol._scheduler_config, {}) self.assertEqual(self.protocol._scheduler_callback, {}) # Now lets see some magic happen. Lets add our schedulers. Generally # This would be done as part of the protocol init, but it can happen # anytime. If the scheduler has already been initialized the # job will be started right away self.protocol._add_scheduler(job_name, self._scheduler_callback) self.assertEqual(0, self._trigger_count) self.assert_scheduled_event_triggered(event_count=3) # now remove the job and see that no events are triggered self.protocol._remove_scheduler(job_name) self._trigger_count = 0 time.sleep(2) self.assertEqual(self._trigger_count, 0) # now check that it raises exception if the removal is re-attempted try: self.protocol._remove_scheduler(job_name) except Exception as e: return self.fail("a non-existent job was erroneous removed") def test_scheduler_event(self): """ Test if we can add and trigger jobs using events instead of callbacks We will create two event triggers, foo and bar. They should come in that order. """ self.protocol._protocol_fsm = Mock() #self.protocol._fsm.on_event = Mock() dt = datetime.datetime.now() + datetime.timedelta(0, 1) foo_scheduler = 'foo' bar_scheduler = 'bar' startup_config = { DriverConfigKey.SCHEDULER: { foo_scheduler: { DriverSchedulerConfigKey.TRIGGER: { DriverSchedulerConfigKey.TRIGGER_TYPE: TriggerType.INTERVAL, DriverSchedulerConfigKey.SECONDS: 1 } }, bar_scheduler: { DriverSchedulerConfigKey.TRIGGER: { DriverSchedulerConfigKey.TRIGGER_TYPE: TriggerType.INTERVAL, DriverSchedulerConfigKey.SECONDS: 2 } } } } self.protocol.set_init_params(startup_config) # Verify we are initialized properly self.assertIsNone(self.protocol._scheduler) self.assertEqual(self.protocol._scheduler_config, {}) self.assertEqual(self.protocol._scheduler_callback, {}) # Verify the the scheduler is created self.protocol.initialize_scheduler() self.assertIsInstance(self.protocol._scheduler, DriverScheduler) self.assertEqual(self.protocol._scheduler_config, {}) self.assertEqual(self.protocol._scheduler_callback, {}) # Now lets see some magic happen. Lets add our schedulers. Generally # This would be done as part of the protocol init, but it can happen # anytime. If the scheduler has already been initialized the # job will be started right away foo_event = 'foo' bar_event = 'bar' self.protocol._add_scheduler_event(foo_scheduler, foo_event) self.protocol._add_scheduler_event(bar_scheduler, bar_event) self.assertEqual(0, self._trigger_count) #self.assert_scheduled_event_triggered(2) ##### Integration tests for test_scheduler in the SBE37 integration suite def test_generate_config_metadata_json(self): """ Tests generate of the metadata structure """ self.protocol._param_dict.add("foo", r'foo=(.*)', lambda match: int(match.group(1)), lambda x: str(x), direct_access=True, default_value=10) self.protocol._param_dict.add("bar", r'bar=(.*)', lambda match: int(match.group(1)), lambda x: str(x), direct_access=False, default_value=15) self.protocol._cmd_dict.add( "cmd1", timeout=60, arguments=[CommandArgument("coeff"), CommandArgument("delay")]) # different way of creating things, possibly more clear in some cases # and allows for testing arg and command later cmd2_arg1 = CommandArgument("trigger") cmd2 = Command("cmd2", arguments=[cmd2_arg1]) self.protocol._cmd_dict.add_command(cmd2) self.protocol._driver_dict.add(DriverDictKey.VENDOR_SW_COMPATIBLE, True) # Now do the real testing result = self.protocol.get_config_metadata_dict() self.assert_(isinstance(result[ConfigMetadataKey.DRIVER], dict)) self.assert_(isinstance(result[ConfigMetadataKey.COMMANDS], dict)) self.assert_(isinstance(result[ConfigMetadataKey.PARAMETERS], dict)) self.assertEquals(result[ConfigMetadataKey.DRIVER], {DriverDictKey.VENDOR_SW_COMPATIBLE: True}) # Check a few in the cmd list...the leaves in the structure are # tested in the cmd dict test cases self.assert_("cmd1" in result[ConfigMetadataKey.COMMANDS].keys()) self.assert_("cmd2" in result[ConfigMetadataKey.COMMANDS].keys()) # Check a few in the param list...the leaves in the structure are # tested in the param dict test cases self.assert_("foo" in result[ConfigMetadataKey.PARAMETERS].keys()) self.assert_("bar" in result[ConfigMetadataKey.PARAMETERS].keys()) def test_verify_muttable(self): """ Verify the verify_not_read_only works as expected. """ self.protocol._param_dict.add( 'ro', r'', None, None, visibility=ParameterDictVisibility.READ_ONLY) self.protocol._param_dict.add( 'immutable', r'', None, None, visibility=ParameterDictVisibility.IMMUTABLE) self.protocol._param_dict.add( 'rw', r'', None, None, visibility=ParameterDictVisibility.READ_WRITE) # Happy Path self.protocol._verify_not_readonly({'rw': 1}) self.protocol._verify_not_readonly({ 'rw': 1, 'immutable': 2 }, startup=True) with self.assertRaises(InstrumentParameterException): self.protocol._verify_not_readonly({'rw': 1, 'immutable': 2}) with self.assertRaises(InstrumentParameterException): self.protocol._verify_not_readonly({'rw': 1, 'ro': 2}) with self.assertRaises(InstrumentParameterException): self.protocol._verify_not_readonly({ 'rw': 1, 'ro': 2 }, startup=True)
class TestUnitInstrumentProtocol(MiUnitTestCase): """ Test cases for instrument protocol class. Functions in this class provide instrument protocol unit tests and provide a tutorial on use of the protocol interface. """ def setUp(self): """ """ self.callback_result = None self._trigger_count = 0 self._events = [] self.protocol = InstrumentProtocol(self.event_callback) def tearDown(self): if self.protocol._scheduler: self.protocol._scheduler._scheduler.shutdown() def event_callback(self, event, value=None): log.debug("Test event callback: %s" % event) self._events.append(event) self._trigger_count += 1 def _scheduler_callback(self): """ Callback to test the scheduler """ self._trigger_count += 1 def assert_scheduled_event_triggered(self, event_count=1): count = 0 for i in range(0, 40): count = self._trigger_count log.debug("check for triggered event, count %d" % self._trigger_count) if(count >= event_count): break time.sleep(0.3) self.assertGreater(count, 0) def test_get_prompts(self): """ ensure prompts are returned sorted by length """ prompts = ['aa', 'bbb', 'c', 'dddd'] expected = ['dddd', 'bbb', 'aa', 'c'] class Prompts(BaseEnum): A = 'aa' B = 'bbb' C = 'c' D = 'dddd' self.protocol = CommandResponseInstrumentProtocol(prompts, '\r\n', self.event_callback) self.assertEqual(self.protocol._get_prompts(), expected) self.protocol = CommandResponseInstrumentProtocol(Prompts, '\r\n', self.event_callback) self.assertEqual(self.protocol._get_prompts(), expected) def test_extraction(self): sample_line = "SATPAR0229,10.01,2206748544,234\r\n" ntptime = ntplib.system_to_ntp_time(time.time()) result = self.protocol._extract_sample(SatlanticPARDataParticle, SAMPLE_REGEX, sample_line, ntptime, publish=False) log.debug("R: %s" % result) self.assertEqual(result['stream_name'], SatlanticPARDataParticle(None, None).data_particle_type()) # Test the format of the result in the individual driver tests. Here, # just tests that the result is there. def test_get_param_list(self): """ verify get_param_list returns correct parameter lists. """ params = ['foo', 'bar', 'baz'] for key in params: self.protocol._param_dict.add(key, r'', None, None) # All can be passed in as a string self.assertEqual(sorted(params), sorted(self.protocol._get_param_list(DriverParameter.ALL))) # All can be passed in as a single element list self.assertEqual(sorted(params), sorted(self.protocol._get_param_list([DriverParameter.ALL]))) # All can be in a list anywhere, not just the first element self.assertEqual(sorted(params), sorted(self.protocol._get_param_list(['foo', DriverParameter.ALL]))) # Bad parameters raise exceptions event when ALL is specified with self.assertRaises(InstrumentParameterException): self.assertEqual(sorted(params), sorted(self.protocol._get_param_list(['noparam', DriverParameter.ALL]))) # An exception is raised when the param is not a list or string with self.assertRaises(InstrumentParameterException): self.protocol._get_param_list({'other': 'struct'}) # when a subset is given, the same set is returned. subset = ['bar', 'baz'] self.assertEqual(sorted(subset), sorted(self.protocol._get_param_list(subset))) # verify we can accept a tuple subset = ['bar', 'baz'] self.assertEqual(sorted(subset), sorted(self.protocol._get_param_list(('bar', 'baz')))) # An exception is raised when the param is not known with self.assertRaises(InstrumentParameterException): self.protocol._get_param_list(subset + ['boom']) # Verify we can send in a single parameter as a string, not ALL self.assertEqual(['bar'], self.protocol._get_param_list('bar')) def test_ring_buffer(self): """ verify command thread ring buffer rolls as expected """ prompts = ['aa', 'bbb', 'c', 'dddd'] self.protocol = CommandResponseInstrumentProtocol(prompts, '\r\n', self.event_callback) self.protocol._max_buffer_size = Mock(return_value=5) self.protocol.add_to_buffer("a") self.protocol.add_to_buffer("b") self.protocol.add_to_buffer("c") self.protocol.add_to_buffer("d") self.protocol.add_to_buffer("e") self.assertEqual(len(self.protocol._linebuf), 5) self.assertEqual(len(self.protocol._promptbuf), 5) self.assertEqual(self.protocol._linebuf, "abcde") self.assertEqual(self.protocol._promptbuf, "abcde") self.protocol.add_to_buffer("f") self.assertEqual(len(self.protocol._linebuf), 5) self.assertEqual(len(self.protocol._promptbuf), 5) self.assertEqual(self.protocol._linebuf, "bcdef") self.assertEqual(self.protocol._promptbuf, "bcdef") self.protocol.add_to_buffer("gh") self.assertEqual(len(self.protocol._linebuf), 5) self.assertEqual(len(self.protocol._promptbuf), 5) self.assertEqual(self.protocol._linebuf, "defgh") self.assertEqual(self.protocol._promptbuf, "defgh") @unittest.skip('Not Written') def test_publish_raw(self): """ Tests to see if raw data is appropriately published back out to the InstrumentAgent via the event callback. """ # build a packet # have it published by the protocol (force state if needed) # delay? # catch it in the callback # confirm it came back # compare response to original packet self.assertTrue(False) @unittest.skip('Not Written') def test_publish_parsed_data(self): """ Tests to see if parsed data is appropriately published back to the InstrumentAgent via the event callback. """ # similar to above self.assertTrue(False) @unittest.skip('Not Written') def test_publish_engineering_data(self): """ Tests to see if engineering data is appropriately published back to the InstrumentAgent via the event callback. """ # similar to above self.assertTrue(False) def test_get_running_config(self): """ Checks to see that one can successfully get the running config from an instrument protocol. """ # set some values log.debug("First param_dict: %s", self.protocol._param_dict.get_config()) self.protocol._param_dict.add("foo", r'foo=(.*)', lambda match : int(match.group(1)), lambda x : str(x), direct_access=True, default_value=10) self.protocol._param_dict.set_default("foo") # test hack to set w/o fetch self.protocol._param_dict.add("bar", r'bar=(.*)', lambda match : int(match.group(1)), lambda x : str(x), direct_access=False, default_value=15) self.protocol._param_dict.set_default("bar") self.assertEquals(self.protocol._param_dict.get("foo"), 10) self.assertEquals(self.protocol._param_dict.get("bar"), 15) result = self.protocol.get_cached_config() self.assertEquals(result['foo'], 10) self.assertEquals(result['bar'], 15) self.protocol._param_dict.update("bar=20") result = self.protocol.get_cached_config() self.assertEquals(result['foo'], 10) self.assertEquals(result['bar'], 20) self.assertEquals(self.protocol._param_dict.get("bar"), 20) # get and check the running config result = self.protocol.get_cached_config() self.assertTrue(isinstance(result, dict)) self.assertEquals(result['foo'], 10) self.assertEquals(result['bar'], 20) def test_init_values(self): """ Test getting and setting the initialization value for a parameter """ self.protocol._param_dict.add("foo", r'foo=(.*)', lambda match : int(match.group(1)), lambda x : str(x), direct_access=True, startup_param=True, default_value=10) self.protocol._param_dict.add("bar", r'bar=(.*)', lambda match : int(match.group(1)), lambda x : str(x), direct_access=False, startup_param=True, default_value=0) self.protocol._param_dict.add("baz", r'baz=(.*)', lambda match : int(match.group(1)), lambda x : str(x), direct_access=True, default_value=20) self.protocol._param_dict.add("bat", r'bat=(.*)', lambda match : int(match.group(1)), lambda x : str(x), startup_param=False, default_value=20) self.protocol._param_dict.add("qux", r'qux=(.*)', lambda match : int(match.group(1)), lambda x : str(x), startup_param=True) self.protocol._param_dict.add("rok", r'rok=(.*)', lambda match : int(match.group(1)), lambda x : str(x)) # set an additional value for test self.protocol._param_dict.update("qux=6666") # mark init params self.assertRaises(InstrumentParameterException, self.protocol.set_init_params, []) self.protocol.set_init_params({DriverConfigKey.PARAMETERS: {"foo": 1111, "baz":2222}}) # get new startup config self.assertRaises(InstrumentProtocolException, self.protocol.get_startup_config) self.protocol.set_init_params({DriverConfigKey.PARAMETERS: {"foo": 1111, "baz":2222, "bat": 11, "qux": 22}}) result = self.protocol.get_startup_config() self.assertEquals(len(result), 5) self.assertEquals(result["foo"], 1111) # init param self.assertEquals(result["bar"], 0) # init param with default value self.assertEquals(result["baz"], 2222) # non-init param, but value specified self.assertEquals(result["bat"], 11) # set param self.assertEquals(result["qux"], 22) # set param self.assertIsNone(result.get("rok")) # defined in paramdict, no config def test_apply_startup_params(self): """ Test that the apply startup parameters method exists and throws a "not implemented" exception for the base class """ self.assertRaises(NotImplementedException, self.protocol.apply_startup_params) def test_scheduler(self): """ Test to see that the scheduler can add and remove jobs properly Jobs are just queued for adding unit we call initialize_scheduler then the jobs are actually created. use an interval job to allow for testing for removal """ job_name = 'test_job' startup_config = { DriverConfigKey.SCHEDULER: { job_name: { DriverSchedulerConfigKey.TRIGGER: { DriverSchedulerConfigKey.TRIGGER_TYPE: TriggerType.INTERVAL, DriverSchedulerConfigKey.SECONDS: 2 }, } } } self.protocol.set_init_params(startup_config) # Verify we are initialized properly self.assertIsNone(self.protocol._scheduler) self.assertEqual(self.protocol._scheduler_config, {}) self.assertEqual(self.protocol._scheduler_callback, {}) # Verify the the scheduler is created self.protocol.initialize_scheduler() self.assertIsInstance(self.protocol._scheduler, DriverScheduler) self.assertEqual(self.protocol._scheduler_config, {}) self.assertEqual(self.protocol._scheduler_callback, {}) # Now lets see some magic happen. Lets add our schedulers. Generally # This would be done as part of the protocol init, but it can happen # anytime. If the scheduler has already been initialized the # job will be started right away self.protocol._add_scheduler(job_name, self._scheduler_callback) self.assertEqual(0, self._trigger_count) self.assert_scheduled_event_triggered(event_count=3) # now remove the job and see that no events are triggered self.protocol._remove_scheduler(job_name) self._trigger_count = 0 time.sleep(4) self.assertEqual(self._trigger_count, 0) # now check that it raises exception if the removal is re-attempted try: self.protocol._remove_scheduler(job_name) except Exception as e: return self.fail("a non-existent job was erroneous removed") def test_scheduler_event(self): """ Test if we can add and trigger jobs using events instead of callbacks We will create two event triggers, foo and bar. They should come in that order. """ self.protocol._protocol_fsm = Mock() #self.protocol._fsm.on_event = Mock() dt = datetime.datetime.now() + datetime.timedelta(0,1) foo_scheduler = 'foo' bar_scheduler = 'bar' startup_config = { DriverConfigKey.SCHEDULER: { foo_scheduler: { DriverSchedulerConfigKey.TRIGGER: { DriverSchedulerConfigKey.TRIGGER_TYPE: TriggerType.INTERVAL, DriverSchedulerConfigKey.SECONDS: 1 } }, bar_scheduler: { DriverSchedulerConfigKey.TRIGGER: { DriverSchedulerConfigKey.TRIGGER_TYPE: TriggerType.INTERVAL, DriverSchedulerConfigKey.SECONDS: 2 } } } } self.protocol.set_init_params(startup_config) # Verify we are initialized properly self.assertIsNone(self.protocol._scheduler) self.assertEqual(self.protocol._scheduler_config, {}) self.assertEqual(self.protocol._scheduler_callback, {}) # Verify the the scheduler is created self.protocol.initialize_scheduler() self.assertIsInstance(self.protocol._scheduler, DriverScheduler) self.assertEqual(self.protocol._scheduler_config, {}) self.assertEqual(self.protocol._scheduler_callback, {}) # Now lets see some magic happen. Lets add our schedulers. Generally # This would be done as part of the protocol init, but it can happen # anytime. If the scheduler has already been initialized the # job will be started right away foo_event='foo' bar_event='bar' self.protocol._add_scheduler_event(foo_scheduler, foo_event) self.protocol._add_scheduler_event(bar_scheduler, bar_event) self.assertEqual(0, self._trigger_count) #self.assert_scheduled_event_triggered(2) ##### Integration tests for test_scheduler in the SBE37 integration suite def test_generate_config_metadata_json(self): """ Tests generate of the metadata structure """ self.protocol._param_dict.add("foo", r'foo=(.*)', lambda match : int(match.group(1)), lambda x : str(x), direct_access=True, default_value=10) self.protocol._param_dict.add("bar", r'bar=(.*)', lambda match : int(match.group(1)), lambda x : str(x), direct_access=False, default_value=15) self.protocol._cmd_dict.add("cmd1", timeout=60, arguments=[CommandArgument("coeff"), CommandArgument("delay") ] ) # different way of creating things, possibly more clear in some cases # and allows for testing arg and command later cmd2_arg1 = CommandArgument("trigger") cmd2 = Command("cmd2", arguments=[cmd2_arg1]) self.protocol._cmd_dict.add_command(cmd2) self.protocol._driver_dict.add(DriverDictKey.VENDOR_SW_COMPATIBLE, True) # Now do the real testing result = self.protocol.get_config_metadata_dict() self.assert_(isinstance(result[ConfigMetadataKey.DRIVER], dict)) self.assert_(isinstance(result[ConfigMetadataKey.COMMANDS], dict)) self.assert_(isinstance(result[ConfigMetadataKey.PARAMETERS], dict)) self.assertEquals(result[ConfigMetadataKey.DRIVER], {DriverDictKey.VENDOR_SW_COMPATIBLE:True}) # Check a few in the cmd list...the leaves in the structure are # tested in the cmd dict test cases self.assert_("cmd1" in result[ConfigMetadataKey.COMMANDS].keys()) self.assert_("cmd2" in result[ConfigMetadataKey.COMMANDS].keys()) # Check a few in the param list...the leaves in the structure are # tested in the param dict test cases self.assert_("foo" in result[ConfigMetadataKey.PARAMETERS].keys()) self.assert_("bar" in result[ConfigMetadataKey.PARAMETERS].keys()) def test_verify_muttable(self): """ Verify the verify_not_read_only works as expected. """ self.protocol._param_dict.add('ro', r'', None, None, visibility=ParameterDictVisibility.READ_ONLY) self.protocol._param_dict.add('immutable', r'', None, None, visibility=ParameterDictVisibility.IMMUTABLE) self.protocol._param_dict.add('rw', r'', None, None, visibility=ParameterDictVisibility.READ_WRITE) # Happy Path self.protocol._verify_not_readonly({'rw': 1}) self.protocol._verify_not_readonly({'rw': 1, 'immutable': 2}, startup=True) with self.assertRaises(InstrumentParameterException): self.protocol._verify_not_readonly({'rw': 1, 'immutable': 2}) with self.assertRaises(InstrumentParameterException): self.protocol._verify_not_readonly({'rw': 1, 'ro': 2}) with self.assertRaises(InstrumentParameterException): self.protocol._verify_not_readonly({'rw': 1, 'ro': 2}, startup=True)