class TestHDFMapMSI(ut.TestCase): """Test class for HDFMapMSI""" # What to test? # X 1. returned object is a dictionary # X 2. if input is not h5py.Group instance, then TypeError is # raised # X 3. existence of: # a. mappable_devices # - check it's a tuple # X 4. MSI group has no sub-groups # - dict is empty # X 5. MSI has unknown & known sub-groups # - only known group is added to dictionary # X 6. MSI has a known sub-group, but mapping fails # - failed map should not be added to to dict # X 7. all diagnostics are mappable, but not all mappable # diagnostics are included # X 8. MSI group has a diagnostic dataset # def setUp(self): self.f = FauxHDFBuilder() def tearDown(self): self.f.cleanup() @property def map(self): """Map of MSI Group""" return self.map_msi(self.msi_group) @property def msi_group(self): """MSI group""" return self.f['MSI'] @staticmethod def map_msi(group): """Mapping function""" return HDFMapMSI(group) def test_not_h5py_group(self): """Test error if object to map is not h5py.Group""" with self.assertRaises(TypeError): self.map_msi(None) def test_msi_scenarios(self): """ Test various scenarios of mappable and non-mappable diagnostics in the MSI group """ # the MSI group is empty ---- self.f.remove_all_modules() _map = self.map self.assertBasics(_map) self.assertEqual(_map, {}) # the MSI group has all mappable diagnostics ---- self.f.remove_all_modules() self.f.add_module('Discharge', {}) self.f.add_module('Heater', {}) self.f.add_module('Magnetic field', {}) _map = self.map self.assertBasics(_map) # check all diagnostics were mapped self.assertEqual(len(_map), 3) self.assertIn('Discharge', _map) self.assertIn('Heater', _map) self.assertIn('Magnetic field', _map) # the MSI group has mappable and unknown diagnostics ---- self.f.remove_all_modules() self.f.add_module('Discharge', {}) self.f.add_module('Heater', {}) self.f.add_module('Magnetic field', {}) self.f['MSI'].create_group('Not known') _map = self.map self.assertBasics(_map) # check correct diagnostics were mapped self.assertEqual(len(_map), 3) self.assertIn('Discharge', _map) self.assertIn('Heater', _map) self.assertIn('Magnetic field', _map) self.assertNotIn('Not known', _map) # delete unknown group del self.f['MSI/Not known'] # the MSI group has a dataset ---- self.f.remove_all_modules() self.f.add_module('Discharge', {}) self.f.add_module('Heater', {}) self.f.add_module('Magnetic field', {}) data = np.empty((2, 100), dtype=np.float32) self.f['MSI'].create_dataset('A dataset', data=data) _map = self.map self.assertBasics(_map) # check correct diagnostics were mapped self.assertEqual(len(_map), 3) self.assertIn('Discharge', _map) self.assertIn('Heater', _map) self.assertIn('Magnetic field', _map) self.assertNotIn('A dataset', _map) # delete dataset del self.f['MSI/A dataset'] # the MSI group has a mappable diagnostic ---- # but mapping fails ---- self.f.remove_all_modules() self.f.add_module('Discharge', {}) self.f.add_module('Heater', {}) # remove a dataset from 'Discharge' # - this will cause mapping of 'Discharge' to fail # del self.f['MSI/Discharge/Discharge current'] # check map _map = self.map self.assertBasics(_map) # check correct diagnostics were mapped self.assertEqual(len(_map), 1) self.assertIn('Heater', _map) self.assertNotIn('Discharge', _map) def assertBasics(self, msi_map: HDFMapMSI): # mapped object is a dictionary self.assertIsInstance(msi_map, dict) # all dict items are a mapping class for val in msi_map.values(): self.assertIsInstance(val, HDFMapMSITemplate) # look for map attributes self.assertTrue(hasattr(msi_map, 'mappable_devices')) # check attribute types self.assertIsInstance(msi_map.mappable_devices, tuple)
class TestControlTemplates(ut.TestCase): """ Test class for HDFMapControlTemplate and HDFMapControlCLTemplate. """ def setUp(self): # blank/temporary HDF5 file self.f = FauxHDFBuilder() # fill the MSI group for testing self.f['MSI'].create_group('g1') self.f['MSI'].create_group('g2') # correct 'command list' dataset dtype = np.dtype([('Shot number', np.int32), ('Command index', np.int8)]) data = np.empty((5, ), dtype=dtype) self.f['MSI'].create_dataset('d1', data=data) # dataset missing 'Command index' dtype = np.dtype([('Shot number', np.int32), ('Not command index', np.int8)]) data = np.empty((5, ), dtype=dtype) self.f['MSI'].create_dataset('d2', data=data) # dataset missing 'Command index' wrong shape and size dtype = np.dtype([('Shot number', np.int32), ('Command index', np.float16, (2, ))]) data = np.empty((5, ), dtype=dtype) self.f['MSI'].create_dataset('d3', data=data) def tearDown(self): """Cleanup temporary HDF5 file""" self.f.cleanup() def group(self): """HDF5 group for testing""" return self.f['MSI'] @staticmethod def dummy_map(template, group): """A template mapping of a HDF5 group""" # Note: Since the template classes contain abstract methods that # are intended to be overwritten, those methods must be # overwritten before instantiating an object. # # retrieve desired template class template_cls = DummyTemplates[template].value # override abstract methods # - all abstract methods will now return NotImplemented instead # of raising NotImplementedError # new_dict = template_cls.__dict__.copy() for abstractmethod in template_cls.__abstractmethods__: new_dict[abstractmethod] = lambda *args: NotImplemented # define new template class new_template_class = type("dummy_%s" % template_cls.__name__, (template_cls, ), new_dict) # return map return new_template_class(group) def test_not_h5py_group(self): """Test error if object to map is not h5py.Group""" with self.assertRaises(TypeError): self.dummy_map('default', None) self.dummy_map('cl', None) def test_structure(self): # test HDFMapControlTemplate self.assertControlTemplate(self.dummy_map('default', self.group()), self.group()) # test HDFMapControlCLTemplate self.assertControlCLTemplate(self.dummy_map('cl', self.group()), self.group()) def assertControlTemplate(self, _map, _group: h5py.Group): # check instance self.assertIsInstance(_map, HDFMapControlTemplate) # check attribute existence attrs = ('info', 'configs', 'contype', 'dataset_names', 'group', 'has_command_list', 'one_config_per_dset', 'subgroup_names', 'device_name', 'construct_dataset_name', '_build_configs') for attr in attrs: self.assertTrue(hasattr(_map, attr)) # -- check 'info' ---- # assert is a dict self.assertIsInstance(_map.info, dict) # assert required keys self.assertIn('group name', _map.info) self.assertIn('group path', _map.info) self.assertIn('contype', _map.info) # assert values self.assertEqual(_map.info['group name'], os.path.basename(_group.name)) self.assertEqual(_map.info['group path'], _group.name) self.assertEqual(_map.info['contype'], NotImplemented) # -- check 'configs' ---- self.assertIsInstance(_map.configs, dict) self.assertEqual(len(_map.configs), 0) # -- check 'one_config_per_dset' ---- # empty configs dict self.assertFalse(_map.one_config_per_dset) # one config for three datasets with mock.patch.dict(_map._configs, {'config1': {}}): self.assertFalse(_map.one_config_per_dset) # 3 configs for 3 datasets with mock.patch.dict(_map._configs, { 'config1': {}, 'config2': {}, 'config3': {} }): self.assertTrue(_map.one_config_per_dset) # -- check 'has_command_list' ---- # empty configs dict self.assertFalse(_map.has_command_list) # add artificial 'command list' with mock.patch.dict(_map.configs, { 'config1': {}, 'config2': { 'command list': ('start', ) } }): self.assertTrue(_map.has_command_list) # -- check other attributes ---- self.assertEqual(_map.contype, _map.info['contype']) self.assertEqual(_map.group, _group) self.assertEqual(_map.device_name, _map.info['group name']) self.assertEqual(sorted(_map.dataset_names), sorted(['d1', 'd2', 'd3'])) self.assertEqual(sorted(_map.subgroup_names), sorted(['g1', 'g2'])) # -- check abstract methods ---- # Note: `self.dummy_map` overrides all abstract methods to # return NotImplemented values # self.assertEqual(_map.construct_dataset_name(), NotImplemented) self.assertEqual(_map._build_configs(), NotImplemented) def assertControlCLTemplate(self, _map, _group: h5py.Group): # check instance self.assertIsInstance(_map, HDFMapControlCLTemplate) # re-assert HDFMapControlTemplate structure self.assertControlTemplate(_map, _group) # check attribute existence attrs = ( '_default_re_patterns', 'clparse', '_construct_state_values_dict', 'reset_state_values_config', 'set_state_values_config', '_default_state_values_dict', ) for attr in attrs: self.assertTrue(hasattr(_map, attr)) # -- check '_default_re_patterns' ---- self.assertIsInstance(_map._default_re_patterns, tuple) self.assertEqual(len(_map._default_re_patterns), 0) # -- check 'clparse' ---- with mock.patch.dict(_map._configs, {'config1': { 'command list': ('VOLT 25.0', ) }}): self.assertIsInstance(_map.clparse('config1'), CLParse) # -- check '_construct_state_values_dict' ---- cl = ( 'VOLT 20.0', 'VOLT 25.0', 'VOLT 30.0', ) config_name = 'config1' configs_dict = { config_name: { 'command list': cl, 'dset paths': ('/MSI/d1', ) } } clparse_dict = { 'VOLT': { 'command list': (20.0, 25.0, 30.0), 'cl str': cl, 'dtype': np.float64 } } pattern = \ r'(?P<VOLT>(\bVOLT\s)(?P<VAL>(\d+\.\d*|\.\d+|\d+\b)))' # run tests on a mock _map._configs with mock.patch.dict(_map._configs, configs_dict): # mock CLParse attribute 'apply_patterns' with mock.patch.object(CLParse, 'apply_patterns', ) as \ mock_apply_pat: # 'apply_patterns' is unsuccessful mock_apply_pat.return_value = (False, {}) sv_dict = _map._construct_state_values_dict( config_name, [pattern]) self.assertFalse(bool(sv_dict)) # 'apply_patterns' is successful mock_apply_pat.return_value = (True, clparse_dict) sv_dict = _map._construct_state_values_dict( config_name, [pattern]) self.assertTrue(bool(sv_dict)) self.assertIsInstance(sv_dict, dict) self.assertEqual(len(sv_dict), 1) self.assertEqual(sv_dict['VOLT']['dset paths'], configs_dict[config_name]['dset paths']) self.assertEqual(sv_dict['VOLT']['dset field'], ('Command index', )) self.assertEqual(sv_dict['VOLT']['shape'], ()) self.assertEqual(sv_dict['VOLT']['dtype'], clparse_dict['VOLT']['dtype']) self.assertEqual(sv_dict['VOLT']['command list'], clparse_dict['VOLT']['command list']) self.assertEqual(sv_dict['VOLT']['cl str'], clparse_dict['VOLT']['cl str']) # the 'dset_paths' dataset does NOT have 'Command index' configs_dict[config_name]['dset paths'] = ('/MSI/d2', ) with self.assertWarns(UserWarning): sv_dict = _map._construct_state_values_dict( config_name, [pattern]) self.assertFalse(bool(sv_dict)) # the 'dset_paths' dataset 'Command index' field does # NOT have correct shape or dtype configs_dict[config_name]['dset paths'] = ('/MSI/d3', ) with self.assertWarns(UserWarning): sv_dict = _map._construct_state_values_dict( config_name, [pattern]) self.assertFalse(bool(sv_dict)) # -- check 'reset_state_values_config' ---- cl = ('VOLT 20.0', 'VOLT 25.0', 'VOLT 30.0') pattern = \ r'(?P<VOLT>(\bVOLT\s)(?P<VAL>(\d+\.\d*|\.\d+|\d+\b)))' config_name = 'config1' configs_dict = { config_name: { 'command list': cl, 'dset paths': ('/MSI/d1', ), 'shotnum': {}, 'state values': {} } } dsv_dict = { 'command': { 'command list': cl, 'cl str': cl, 're pattern': None, 'dset paths': configs_dict[config_name]['dset paths'], 'dset field': ('Command index', ), 'shape': (), 'dtype': np.dtype((np.unicode_, 10)) } } sv_dict = { 'VOLT': { 'command list': (20.0, 25.0, 30.0), 'cl str': cl, 're pattern': re.compile(pattern), 'dset paths': configs_dict[config_name]['dset paths'], 'dset field': ('Command index', ), 'shape': (), 'dtype': np.float64 } } # mock _map._configs # mock the '_default_state_values_dict' method with mock.patch.dict(_map._configs, configs_dict), \ mock.patch.object(_map, '_default_state_values_dict') \ as mock_dsvdict: mock_dsvdict.return_value = dsv_dict # kwarg apply_patterns=False (default behavior) _map.reset_state_values_config(config_name, apply_patterns=False) self.assertEqual(_map._configs[config_name]['state values'], dsv_dict) # kwarg apply_patterns=True # mock '_construct_state_values_dict' with mock.patch.object( _map, '_construct_state_values_dict') as mock_csvd: # '_construct_state_values_dict' returns {} mock_csvd.return_value = {} _map._configs[config_name]['state values'] = {} _map.reset_state_values_config(config_name, apply_patterns=True) self.assertEqual(_map._configs[config_name]['state values'], dsv_dict) # '_construct_state_values_dict' returns sv_dict mock_csvd.return_value = sv_dict _map._configs[config_name]['state values'] = {} _map.reset_state_values_config(config_name, apply_patterns=True) self.assertEqual(_map._configs[config_name]['state values'], sv_dict) # -- check 'set_state_values_config' ---- cl = ('VOLT 20.0', 'VOLT 25.0', 'VOLT 30.0') pattern = \ r'(?P<VOLT>(\bVOLT\s)(?P<VAL>(\d+\.\d*|\.\d+|\d+\b)))' config_name = 'config1' configs_dict = { config_name: { 'command list': cl, 'dset paths': ('/MSI/d1', ), 'shotnum': {}, 'state values': {} } } sv_dict = { 'VOLT': { 'command list': (20.0, 25.0, 30.0), 'cl str': cl, 're pattern': re.compile(pattern), 'dset paths': configs_dict[config_name]['dset paths'], 'dset field': ('Command index', ), 'shape': (), 'dtype': np.float64 } } # mock _map._configs # mock the '_construct_state_values_dict' method with mock.patch.dict(_map._configs, configs_dict), \ mock.patch.object(_map, '_construct_state_values_dict') \ as mock_csvd: # '_construct_state_values_dict' fails and returns {} mock_csvd.return_value = {} with self.assertWarns(UserWarning): _map.set_state_values_config(config_name, [pattern]) self.assertEqual(_map._configs[config_name]['state values'], {}) # '_construct_state_values_dict' fails and returns sv_dict mock_csvd.return_value = sv_dict _map._configs[config_name]['state values'] = {} _map.set_state_values_config(config_name, [pattern]) self.assertEqual(_map._configs[config_name]['state values'], sv_dict) # -- check abstract methods ---- # Note: `self.dummy_map` overrides all abstract methods to # return NotImplemented values # self.assertEqual(_map._default_state_values_dict(''), NotImplemented)
class TestHDFMapControls(ut.TestCase): """Test class for HDFMapControls""" # What to test? # X 1. returned object is a dictionary # X 2. if input is not h5py.Group instance, then TypeError is # raised # X 3. existence of: # a. mappable_devices # - check it's a tuple # X 4. data group has no sub-groups # - dict is empty # X 5. data group has unknown & known control device sub-groups # - only known group is added to dictionary # 6. data group has a known control sub-group, but mapping fails # - failed map should not be added to to dict # X 7. all control devices are mappable, but not all mappable # controls are included # X 8. data group has a dataset # def setUp(self): self.f = FauxHDFBuilder() def tearDown(self): self.f.cleanup() @property def map(self): """Map of Control Group""" return self.map_control(self.data_group) @property def data_group(self): """MSI group""" return self.f['Raw data + config'] @staticmethod def map_control(group): """Mapping function""" return HDFMapControls(group) def test_not_h5py_group(self): """Test error if object to map is not h5py.Group""" with self.assertRaises(TypeError): self.map_control(None) def test_msi_scenarios(self): """ Test various scenarios of mappable and non-mappable control device groups. """ # the data group has NO control device groups ---- self.f.remove_all_modules() _map = self.map self.assertBasics(_map) self.assertEqual(_map, {}) # the control group has all mappable devices ---- self.f.remove_all_modules() self.f.add_module('6K Compumotor', {}) self.f.add_module('Waveform', {}) _map = self.map self.assertBasics(_map) # check all controls were mapped self.assertEqual(len(_map), 2) self.assertIn('6K Compumotor', _map) self.assertIn('Waveform', _map) # the data group has mappable and unknown controls ---- self.f.remove_all_modules() self.f.add_module('6K Compumotor', {}) self.f.add_module('Waveform', {}) self.f['Raw data + config'].create_group('Not known') _map = self.map self.assertBasics(_map) # check correct diagnostics were mapped self.assertEqual(len(_map), 2) self.assertIn('6K Compumotor', _map) self.assertIn('Waveform', _map) self.assertNotIn('Not known', _map) # delete unknown group del self.f['Raw data + config/Not known'] # the data group has a dataset ---- self.f.remove_all_modules() self.f.add_module('Waveform', {}) data = np.empty((2, 100), dtype=np.float32) self.f['Raw data + config'].create_dataset('A dataset', data=data) _map = self.map self.assertBasics(_map) # check correct diagnostics were mapped self.assertEqual(len(_map), 1) self.assertIn('Waveform', _map) self.assertNotIn('A dataset', _map) # delete dataset del self.f['Raw data + config/A dataset'] # the data group has a mappable control devices ---- # but mapping fails ---- self.f.remove_all_modules() self.f.add_module('6K Compumotor', {}) self.f.add_module('Waveform', {}) # remove a dataset from 'Waveform' # - this will cause mapping of 'Waveform' to fail # del self.f['Raw data + config/Waveform/config01'] # check map _map = self.map self.assertBasics(_map) # check correct controls were mapped self.assertEqual(len(_map), 1) self.assertIn('6K Compumotor', _map) self.assertNotIn('Waveform', _map) def assertBasics(self, _map: HDFMapControls): # mapped object is a dictionary self.assertIsInstance(_map, dict) # all dict items are a mapping class for val in _map.values(): self.assertIsInstance(val, (HDFMapControlTemplate, HDFMapControlCLTemplate)) # look for map attributes self.assertTrue( hasattr(_map, 'mappable_devices')) # check attribute types self.assertIsInstance(_map.mappable_devices, tuple)