def test_invalid_graph_missing_states(): missing_states = textwrap.dedent("""\ name: example import: [stdlib] body: - component: name: fill_valve edges: edge1: nodes: [0, 1] states: open: edge1: 6 closed: edge1: closed - graph: name: main nodes: A: fixed_pressure: 500 components: - [fill_valve, 0] B: components: - [fill_valve, 1] """) missing_states_file = top.File(missing_states, 's') with pytest.raises(exceptions.BadInputError) as err: top.Package([missing_states_file]) assert "missing component" in str(err)
def test_bad_entry(): nonexistent = 'parameter' bad_entry = textwrap.dedent(f"""\ name: example import: [stdlib] body: - {nonexistent}: name: a - graph: name: main nodes: A: fixed_pressure: 500 B: components: - [fill_valve, 1] states: fill_valve: closed vent_valve: open three_way_valve: left """) with pytest.raises(exceptions.BadInputError) as err: _ = top.File(bad_entry, 's') assert "invalid input type" in str(err)
def test_invalid_undefined_component_param(): undefined_component_param = textwrap.dedent("""\ name: example import: [stdlib] body: - typedef: params: [edge1, open_teq, closed_teq] name: valve edges: edge1: nodes: [0, 1] states: open: edge1: open_teq closed: edge1: closed_teq - component: name: vent_valve type: valve params: edge1: fav_edge open_teq: 1 closed_teq: closed extra_param: 100 """) with pytest.raises(exceptions.BadInputError): _ = top.File(undefined_component_param, input_type='s')
def test_invalid_param(): not_param = 'nonexistent' invalid_param = textwrap.dedent(f"""\ name: example import: [stdlib] body: - typedef: params: [edge1, open_teq, closed_teq] name: valve edges: edge1: nodes: [0, 1] states: open: edge1: open_teq closed: edge1: closed_teq - component: name: vent_valve type: valve params: edge1: fav_edge open_teq: 1 {not_param}: closed """) with pytest.raises(exceptions.BadInputError) as err: _ = top.File(invalid_param, 's') assert "not found" in str(err)
def test_default_arg_inserted(): default_arg_inserted = textwrap.dedent("""\ name: example import: [stdlib] body: - typedef: params: [edge1=default, open_teq, closed_teq] name: valve edges: edge1: nodes: [0, 1] states: open: edge1: open_teq closed: edge1: closed_teq - component: name: vent_valve type: valve params: open_teq: 1 closed_teq: closed """) pack = top.Package([top.File(default_arg_inserted, 's')]) assert 'default' in pack.component_dict['example'][0]['edges']
def test_invalid_no_hoisting(): no_hoisting = textwrap.dedent("""\ name: example import: [stdlib] body: - component: name: vent_valve type: valve params: edge1: fav_edge open_teq: 1 closed_teq: closed - typedef: params: [edge1, open_teq, closed_teq] name: valve edges: edge1: nodes: [0, 1] states: open: edge1: open_teq closed: edge1: closed_teq """) with pytest.raises(exceptions.BadInputError): _ = top.File(no_hoisting, input_type='s')
def test_files_unchanged(): file = top.File(utils.example_path) top.Package([file]) assert len(file.typedefs) == 2 assert len(file.components) == 6 assert len(file.graphs) == 2
def test_valid_pdl(): file = top.File(utils.example_path) assert file.namespace == 'example' assert file.imports == ['stdlib'] assert len(file.typedefs) == 2 assert len(file.components) == 6 assert len(file.graphs) == 2
def test_package_shortcuts(): file = top.File(utils.example_path) namespace = file.namespace pack = top.Package([file]) for component in pack.component_dict[namespace]: assert 'states' in component for edges in component['states'].values(): for teq in edges.values(): assert 'fwd' in teq and 'back' in teq
def test_invalid_no_state_no_teq(): no_state_no_teq = textwrap.dedent("""\ name: example import: [stdlib] body: - component: name: vent_plug edges: edge1: nodes: [0, 1] """) with pytest.raises(exceptions.BadInputError): _ = top.File(no_state_no_teq, input_type='s')
def test_duplicate_names(): file_1 = textwrap.dedent("""\ name: name1 body: - component: name: fill_valve edges: edge1: nodes: [0, 1] states: open: edge1: 6 closed: edge1: closed """) file_2 = textwrap.dedent("""\ name: name2 body: - component: name: fill_valve edges: edge1: nodes: [0, 1] states: open: edge1: 6 closed: edge1: closed """) duplicate_pack = top.Package( [top.File(file_1, 's'), top.File(file_2, 's')]) assert duplicate_pack.component_dict["name1"][0][ 'name'] == "name1.fill_valve" assert duplicate_pack.component_dict["name2"][0][ 'name'] == "name2.fill_valve"
def test_invalid_nested_import(): nested_import = textwrap.dedent("""\ name: example import: [stdlib] body: - component: name: vent_valve type: stdlib.NEST.hole params: edge_name: fav_edge open_teq: 5 """) nested_import_file = top.File(nested_import, input_type='s') with pytest.raises(NotImplementedError) as err: top.Package([nested_import_file]) assert "nested imports" in str(err)
def test_invalid_no_component_name(): no_name = textwrap.dedent("""\ name: example import: [stdlib] body: - component: edges: edge1: nodes: [0, 1] states: open: edge1: 1 closed: edge1: closed """) with pytest.raises(exceptions.BadInputError): _ = top.File(no_name, input_type='s')
def test_package_storage(): file = top.File(utils.example_path) pack = top.Package([file]) namespace = file.namespace assert len(pack.typedefs) == 2 assert 'example' in pack.typedefs and 'stdlib' in pack.typedefs assert len(pack.component_dict[namespace]) == 6 assert len(pack.component_dict['stdlib']) == 0 assert len(pack.components()) == 6 assert len(pack.typedefs[namespace]) == 2 assert len(pack.typedefs['stdlib']) == 1 assert len(pack.graph_dict[namespace]) == 2 assert len(pack.graph_dict['stdlib']) == 0 assert len(pack.graphs()) == 2
def test_invalid_typedef(): no_params = textwrap.dedent("""\ name: example import: [stdlib] body: - typedef: params: name: valve edges: edge1: nodes: [0, 1] states: open: edge1: open_teq closed: edge1: closed_teq """) with pytest.raises(exceptions.BadInputError): _ = top.File(no_params, input_type='s')
def __init__(self, files, input_type='f', import_paths=None): """ Initialize a parser from one or more PDL files. A Parser contains a Package; most of its functionality lies in restructuring the data contained in its Package into output suitable for loading a plumbing engine. Parameters ---------- files: iterable files is the iterable (usually a list) of one or more paths of the PDL files we want parsed. Alternatively, it can also be a list of strings that are each a valid PDL file. Alternatively, a single string or file path is also acceptable. input_type: char input_type indicates whether the argument provided to "files" is a list of file paths (f) or a list of strings (s). """ self.import_paths = copy.deepcopy(import_paths) if import_paths is None: self.import_paths = utils.default_paths file_list = [] # if a single element, put into list for processing if isinstance(files, str): files = [files] for file in files: file_list.append(top.File(file, input_type)) self.package = top.Package(file_list, self.import_paths) self.components = {} self.mapping = {} self.initial_pressures = {} self.initial_states = {} self.parse_components() self.parse_graphs()
def test_invalid_bad_import_type(): # typedef not found errors having to do with imported files will only be caught at the package # level, not at the file one. We can look at changing this if we change the implementation of # how importable files are stored. bad_type = "NONEXISTENT_TYPE" bad_imported_type = textwrap.dedent(f"""\ name: example import: [stdlib] body: - component: name: vent_valve type: stdlib.{bad_type} params: edge_name: fav_edge open_teq: 5 closed_teq: closed """) bad_imported_type_file = top.File(bad_imported_type, input_type='s') with pytest.raises(exceptions.BadInputError) as err: top.Package([bad_imported_type_file]) assert "invalid component" in str(err)
def test_invalid_import(): bad_import = "NONEXISTENT" invalid_import = textwrap.dedent(f"""\ name: example import: [stdlib, {bad_import}] body: - typedef: params: [edge1, open_teq, closed_teq] name: valve edges: edge1: nodes: [0, 1] states: open: edge1: open_teq closed: edge1: closed_teq """) invalid_import_file = top.File(invalid_import, input_type='s') with pytest.raises(exceptions.BadInputError) as err: top.Package([invalid_import_file]) assert "invalid import" in str(err)
def test_no_import(): no_import = textwrap.dedent("""\ name: example body: - typedef: params: [edge_name, open_teq, closed_teq] name: valve edges: edge1: nodes: [0, 1] states: open: edge1: open_teq closed: edge1: closed_teq """) file = top.File(no_import, 's') assert file.namespace == 'example' assert file.imports == [] assert len(file.typedefs) == 1
def test_invalid_incomplete_node(): # A is missing its components incomplete_nodes = textwrap.dedent("""\ name: example import: [stdlib] body: - graph: name: main nodes: A: fixed_pressure: 500 B: components: - [fill_valve, 1] states: fill_valve: closed vent_valve: open three_way_valve: left """) with pytest.raises(exceptions.BadInputError): _ = top.File(incomplete_nodes, 's')
def test_package_typedefs(): file = top.File(utils.example_path) namespace = file.namespace pack = top.Package([file]) var_open = 'open_teq' var_closed = 'closed_teq' var_name = 'edge_name' for component in pack.component_dict[namespace]: # ensure typedef fulfillment occurred assert 'type' not in component and 'params' not in component assert 'edges' in component # ensure variables were replaced if component['name'] == 'vent_valve': assert var_name not in list(component['edges'].keys()) assert var_open not in component['states']['open'] assert var_closed not in component['states']['closed'] if component['name'] == 'hole': assert var_name not in list(component['edges'].keys()) assert var_open not in component['states']['open']
def __init__(self, files, import_paths=None): """ Initialize a Package from one or more Files. A Package should have all the components of a complete plumbing engine system; from here no additional information will make it into the PlumbingEngine. Once instantiated, a Package's PDL is cleaned and ready to use. Parameters ---------- files: iterable files is an iterable (usually a list) of one or more Files whose contents should go into the Package. """ self.import_paths = copy.deepcopy(import_paths) if import_paths is None: self.import_paths = utils.default_paths self.importable_files = dict() imports_folder = [] for import_path in self.import_paths: try: filenames = os.listdir(import_path) filenames = [ os.path.join(import_path, fname) for fname in filenames ] imports_folder.extend(filenames) except FileNotFoundError: imports_folder = [] warnings.warn( f"import directory {import_path} could not be found") for path in imports_folder: try: name = yaml.safe_load(open(path, 'r'))['name'] if name in self.importable_files: self.importable_files[name].add(path) else: self.importable_files[name] = {path} except KeyError: warnings.warn(path + " does not describe a pdl file") if len(list(files)) < 1: raise exceptions.BadInputError( "cannot instantiate a Package with no Files") self.imports = [] # dicts of {namespace: [entries]}, where entry is a PDL object. Organized like this to # reduce dict nesting; since this is a one time process it should be easy to keep # them synced. self.typedefs = {} self.component_dict = {} self.graph_dict = {} for file in files: # TODO(wendi): unused import detection self.imports.extend(copy.deepcopy(file.imports)) for imp in set(self.imports): if imp not in self.importable_files: raise exceptions.BadInputError(f"invalid import: {imp}") for path in self.importable_files[imp]: files.append(top.File(path)) # consolidate entry information from files for file in files: name = file.namespace if name not in self.typedefs: self.typedefs[name] = {} self.component_dict[name] = [] self.graph_dict[name] = [] self.typedefs[name].update(copy.deepcopy(file.typedefs)) self.component_dict[name].extend(copy.deepcopy(file.components)) self.graph_dict[name].extend(copy.deepcopy(file.graphs)) self.clean()
def test_bad_input_type(): bad_format = 'p' with pytest.raises(exceptions.BadInputError) as err: _ = top.File("", bad_format) assert "invalid input type" in str(err)
def test_import_modification(): file = top.File(utils.example_path) pack = top.Package([file]) assert len(pack.importable_files) != 0