From f0256ac8fdb3c395994cb3ab69287178ca8e034d Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Fri, 7 Oct 2022 17:11:10 -0400 Subject: [PATCH 01/11] Remove unused, untested methods from ofrak.Resource --- ofrak_core/Makefile | 2 +- ofrak_core/ofrak/resource.py | 119 +++-------------------------------- 2 files changed, 10 insertions(+), 111 deletions(-) diff --git a/ofrak_core/Makefile b/ofrak_core/Makefile index 942d2586c..a7aa05166 100644 --- a/ofrak_core/Makefile +++ b/ofrak_core/Makefile @@ -16,7 +16,7 @@ inspect: .PHONY: test test: inspect $(PYTHON) -m pytest test_ofrak --cov=ofrak --cov-report=term-missing - fun-coverage --cov-fail-under=79 + fun-coverage --cov-fail-under=80 .PHONY: dependencies dependencies: diff --git a/ofrak_core/ofrak/resource.py b/ofrak_core/ofrak/resource.py index ca364e27d..caa8cba59 100644 --- a/ofrak_core/ofrak/resource.py +++ b/ofrak_core/ofrak/resource.py @@ -6,7 +6,7 @@ from ofrak.component.interface import ComponentInterface from ofrak.model.component_model import ComponentContext, CC, ComponentRunResult -from ofrak.model.data_model import DataPatch, DataMove +from ofrak.model.data_model import DataPatch from ofrak.model.job_model import ( JobRunContext, ) @@ -184,21 +184,6 @@ async def get_data_length(self) -> int: ) return await self._data_service.get_data_length(self._resource.data_id) - async def get_data_index_within_parent(self) -> int: - """ - Data is stored as a tree structure. Each data ID corresponds to a node; nodes's children - are sorted by offset. The index of a node in their parent's list of children indicates - the relative ordering of the child resources which correspond to those child nodes. - - :return: The relative position of this resource's data node in the parent - """ - if self._resource.data_id is None: - raise ValueError( - "Resource does not have a data_id. Cannot get data index from a " - "resource with no data." - ) - return await self._data_service.get_index_within_parent(self._resource.data_id) - async def get_data_range_within_parent(self) -> Range: """ If this resource is "mapped," i.e. its underlying data is defined as a range of its parent's @@ -239,44 +224,6 @@ async def get_offset_within_root(self) -> int: root_range = await self.get_data_range_within_root() return root_range.start - async def get_data_unmapped_range(self, offset: int) -> Range: - """ - This resource may have children mapped in at particular ranges of this resource's - underlying binary data. This method gets a range starting at an ``offset`` and ending at - the start of the next range mapped by a child. - - :param offset: An offset from the start of this resource's binary data where the unmapped - range should start - - :raises OutOfBoundError: If the provided offset is not a valid offset within the resource - :raises AmbiguousOrderError: If there is unmapped data directly before the given offset - - :return: A range starting at ``offset`` and ending at the the offset of the start of the - next range mapped by a child or, if the ``offset`` is within a mapped range, - ending at ``offset`` to create a 0-length range - """ - if self._resource.data_id is None: - raise ValueError( - "Resource does not have a data_id. Cannot get data range from a " - "resource with no data." - ) - return await self._data_service.get_unmapped_range(self._resource.data_id, offset) - - async def set_data_alignment(self, alignment: int): - """ - Set the alignment constraint for the data node associated with this resource. This method - does not modify the resource's data, but sets an alignment value that can be used to - ensure that unpackers and modifiers do not make changes that violate the set alignment. - - :param alignment: The new alignment value - """ - if self._resource.data_id is None: - raise ValueError( - "Resource does not have a data_id. Cannot set data alignment for a " - "resource with no data." - ) - return await self._data_service.set_alignment(self._resource.data_id, alignment) - async def set_data_overlaps_enabled(self, enable_overlaps: bool): """ Enable or disable allowing overlaps for the data node associated with this resource. If @@ -323,6 +270,14 @@ async def save(self): return async def _fetch(self, resource: MutableResourceModel): + """ + # Update the local model with the latest version from the resource service. This will fail + # if this resource has been modified. + # + # :raises InvalidStateError: If the local resource model has been modified + # :raises NotFoundError: If the resource service does not have a model for this resource's ID + # + """ if resource.is_modified and not resource.is_deleted: raise InvalidStateError( f"Cannot fetch dirty resource {resource.id.hex()} (resource " @@ -360,16 +315,6 @@ async def _update_views(self, component_result: ComponentRunResult): for view in views_in_context.values(): view.set_deleted() - async def fetch(self): - """ - Update the local model with the latest version from the resource service. This will fail - if this resource has been modified. - - :raises InvalidStateError: If the local resource model has been modified - :raises NotFoundError: If the resource service does not have a model for this resource's ID - """ - return await self._fetch(self._resource) - async def run( self, component_type: Type[ComponentInterface[CC]], @@ -841,12 +786,6 @@ def get_tags(self, inherit: bool = True) -> Iterable[ResourceTag]: """ return self._resource.get_tags(inherit) - def get_related_tags(self, tag: RT) -> List[RT]: - """ - Get all tags associated with the resource which inherit from the given tag (if any). - """ - return self._resource.get_specific_tags(tag) - def has_tag(self, tag: ResourceTag, inherit: bool = True) -> bool: """ Determine if the resource is associated with the provided tag. @@ -934,25 +873,6 @@ def get_attributes(self, attributes_type: Type[RA]) -> RA: ) return attributes - def get_all_attributes(self) -> Iterable[ResourceAttributes]: - """ - Get values for all the attributes this resource has. - :return: - """ - return list(self._resource.attributes.values()) - - def remove_attributes(self, attributes_type: Type[ResourceAttributes]): - """ - Remove the value of a given attributes type from this resource, if there is such a value. - If the resource does not have a value for the given attributes type, do nothing. - :param attributes_type: - :return: - """ - if not self._resource.has_attributes(attributes_type): - return - self._set_modified() - self._resource.remove_attributes(attributes_type) - def add_component( self, component_id: bytes, @@ -1017,27 +937,6 @@ def has_component_run(self, component_id: bytes, desired_version: Optional[int] return True return version == desired_version - def move( - self, - range: Range, - after: Optional["Resource"] = None, - before: Optional["Resource"] = None, - ): - if not self._component_context: - raise InvalidStateError( - f"Cannot remap resource {self._resource.id.hex()} outside of a modifier component" - ) - if self._resource.data_id is None: - raise ValueError("Cannot create a data move for a resource with no data") - self._component_context.modification_trackers[self._resource.id].data_moves.append( - DataMove( - range, - self._resource.data_id, - after_data_id=after.get_data_id() if after is not None else None, - before_data_id=before.get_data_id() if before is not None else None, - ) - ) - def queue_patch( self, patch_range: Range, From f1c04c2b4437b57f4be44966179ee25f44909a0e Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Fri, 7 Oct 2022 19:25:49 -0400 Subject: [PATCH 02/11] Fix formatting issue --- ofrak_core/ofrak/resource.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ofrak_core/ofrak/resource.py b/ofrak_core/ofrak/resource.py index caa8cba59..2f46d86cf 100644 --- a/ofrak_core/ofrak/resource.py +++ b/ofrak_core/ofrak/resource.py @@ -271,12 +271,11 @@ async def save(self): async def _fetch(self, resource: MutableResourceModel): """ - # Update the local model with the latest version from the resource service. This will fail - # if this resource has been modified. - # - # :raises InvalidStateError: If the local resource model has been modified - # :raises NotFoundError: If the resource service does not have a model for this resource's ID - # + Update the local model with the latest version from the resource service. This will fail + if this resource has been modified. + + :raises InvalidStateError: If the local resource model has been modified + :raises NotFoundError: If the resource service does not have a model for this resource's ID """ if resource.is_modified and not resource.is_deleted: raise InvalidStateError( From 199b535be2eef6b21cc1ed5e93310e13a695e796 Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Fri, 7 Oct 2022 19:26:35 -0400 Subject: [PATCH 03/11] Add tests for Resource.{is_modified, summarize, get_data_range_within_parent} --- ofrak_core/test_ofrak/unit/test_resource.py | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/ofrak_core/test_ofrak/unit/test_resource.py b/ofrak_core/test_ofrak/unit/test_resource.py index 9b1ad1f96..2bf0eecdf 100644 --- a/ofrak_core/test_ofrak/unit/test_resource.py +++ b/ofrak_core/test_ofrak/unit/test_resource.py @@ -216,3 +216,39 @@ async def test_flush_to_disk_pack(ofrak_context: OFRAKContext): await root_resource.flush_to_disk(t.name) await root_resource.flush_to_disk(t.name, pack=False) + + +async def test_is_modified(resource: Resource): + """ + Test Resource.is_modified raises true if the local resource is "dirty". + """ + assert resource.is_modified() is False + + resource.add_tag(Elf) + + assert resource.is_modified() is True + + +async def test_summarize(resource: Resource): + """ + Test that the resource string summary returns a string + """ + summary = await resource.summarize() + assert isinstance(summary, str) + + +async def test_get_range_within_parent(resource: Resource): + """ + Test that Resource.get_data_range_within_parent returns the correctly-mapped range. + """ + child_range = Range(1, 3) + child = await resource.create_child(data_range=child_range) + data_range_within_parent = await child.get_data_range_within_parent() + assert data_range_within_parent == child_range + + +async def test_get_range_within_parent_for_root(resource: Resource): + """ + Resource.get_data_range_within_parent returns Range(0, 0) if the resource is not mapped. + """ + assert await resource.get_data_range_within_parent() == Range(0, 0) From bc00f5bfaf6dc75abc9f80b18361461598af883a Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Fri, 7 Oct 2022 19:48:35 -0400 Subject: [PATCH 04/11] Remove Resource.get_offset_within_root; add test for Resource.identify --- examples/ex2_simple_code_modification.py | 4 ++-- ofrak_core/ofrak/resource.py | 10 ---------- ofrak_core/test_ofrak/unit/test_resource.py | 6 ++++++ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/examples/ex2_simple_code_modification.py b/examples/ex2_simple_code_modification.py index 2967fff66..82c4b24a7 100644 --- a/examples/ex2_simple_code_modification.py +++ b/examples/ex2_simple_code_modification.py @@ -68,9 +68,9 @@ async def main(ofrak_context: OFRAKContext, file_path: str, output_file_name: st ) # Patch in the modified bytes - ret_instruction_offset = await ret_instruction.resource.get_offset_within_root() + range_in_parent = await ret_instruction.resource.get_data_range_within_parent() binary_injector_config = BinaryPatchConfig( - ret_instruction_offset, + range_in_parent.start, new_instruction_bytes, ) await binary_resource.run(BinaryPatchModifier, binary_injector_config) diff --git a/ofrak_core/ofrak/resource.py b/ofrak_core/ofrak/resource.py index 2f46d86cf..3d65985ba 100644 --- a/ofrak_core/ofrak/resource.py +++ b/ofrak_core/ofrak/resource.py @@ -214,16 +214,6 @@ async def get_data_range_within_root(self) -> Range: ) return await self._data_service.get_data_range_within_root(self._resource.data_id) - async def get_offset_within_root(self) -> int: - """ - Does the same thing as `get_data_range_within_root`, except it returns the start offset of - the relative range to the root. - - :return: The start offset of the root node's data which this resource represents - """ - root_range = await self.get_data_range_within_root() - return root_range.start - async def set_data_overlaps_enabled(self, enable_overlaps: bool): """ Enable or disable allowing overlaps for the data node associated with this resource. If diff --git a/ofrak_core/test_ofrak/unit/test_resource.py b/ofrak_core/test_ofrak/unit/test_resource.py index 2bf0eecdf..b0f6ffa4c 100644 --- a/ofrak_core/test_ofrak/unit/test_resource.py +++ b/ofrak_core/test_ofrak/unit/test_resource.py @@ -252,3 +252,9 @@ async def test_get_range_within_parent_for_root(resource: Resource): Resource.get_data_range_within_parent returns Range(0, 0) if the resource is not mapped. """ assert await resource.get_data_range_within_parent() == Range(0, 0) + + +async def test_identify(resource: Resource): + await resource.identify() + assert resource.has_tag(GenericBinary) is True + assert resource.has_tag(Elf) is False From 2e06b60294238e6c7d85d1236c82a185fc8211bf Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Fri, 7 Oct 2022 20:18:23 -0400 Subject: [PATCH 05/11] Fix bug introduced during refactoring --- examples/ex2_simple_code_modification.py | 4 ++-- .../notebooks_with_outputs/4_simple_code_modification.ipynb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/ex2_simple_code_modification.py b/examples/ex2_simple_code_modification.py index 82c4b24a7..94b28d09c 100644 --- a/examples/ex2_simple_code_modification.py +++ b/examples/ex2_simple_code_modification.py @@ -68,9 +68,9 @@ async def main(ofrak_context: OFRAKContext, file_path: str, output_file_name: st ) # Patch in the modified bytes - range_in_parent = await ret_instruction.resource.get_data_range_within_parent() + range_in_root = await ret_instruction.resource.get_data_range_within_root() binary_injector_config = BinaryPatchConfig( - range_in_parent.start, + range_in_root.start, new_instruction_bytes, ) await binary_resource.run(BinaryPatchModifier, binary_injector_config) diff --git a/ofrak_tutorial/notebooks_with_outputs/4_simple_code_modification.ipynb b/ofrak_tutorial/notebooks_with_outputs/4_simple_code_modification.ipynb index 583f1217d..8267a7d9f 100644 --- a/ofrak_tutorial/notebooks_with_outputs/4_simple_code_modification.ipynb +++ b/ofrak_tutorial/notebooks_with_outputs/4_simple_code_modification.ipynb @@ -321,11 +321,11 @@ " program_attributes = await root_resource.analyze(ProgramAttributes)\n", " looping_instruction = await get_looping_instruction(main_cb, ret_instruction, program_attributes)\n", "\n", - " ret_instruction_offset = await ret_instruction.resource.get_offset_within_root()\n", + " range_in_root = await ret_instruction.resource.get_data_range_within_root()\n", " await root_resource.run(\n", " BinaryPatchModifier,\n", " BinaryPatchConfig(\n", - " offset=ret_instruction_offset,\n", + " offset=range_in_root.start,\n", " patch_bytes=looping_instruction,\n", " )\n", " )\n", From 53a5cc54b55d91fd14d4850fa7057cb6420b0c86 Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Fri, 7 Oct 2022 21:10:49 -0400 Subject: [PATCH 06/11] Remove singe-line type variables --- ofrak_core/ofrak/resource.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ofrak_core/ofrak/resource.py b/ofrak_core/ofrak/resource.py index 3d65985ba..b48718340 100644 --- a/ofrak_core/ofrak/resource.py +++ b/ofrak_core/ofrak/resource.py @@ -598,7 +598,6 @@ async def create_child( :param data_before: The sibling resource whose data is sequentially before the new resource :return: """ - data_model_id: Optional[bytes] if data_range is not None: if self._resource.data_id is None: raise ValueError( @@ -739,8 +738,7 @@ def add_view(self, view: ResourceViewInterface): :param view: An instance of a view """ - attributes: ResourceAttributes - for attributes in view.get_attributes_instances().values(): + for attributes in view.get_attributes_instances().values(): # type: ignore self.add_attributes(attributes) self.add_tag(type(view)) From f05c5673ef5aa44e56a9acebcbb87da64808ab85 Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Fri, 7 Oct 2022 21:45:08 -0400 Subject: [PATCH 07/11] Remove Resource.get_siblings_as_view and Resource.get_siblings - Resource.get_siblings is never used or tested - Resource.get_siblings_as_view is only used in one contrived documentation example --- docs/user-guide/resource_view.md | 43 +--------------------------- ofrak_core/ofrak/resource.py | 49 -------------------------------- 2 files changed, 1 insertion(+), 91 deletions(-) diff --git a/docs/user-guide/resource_view.md b/docs/user-guide/resource_view.md index 58932d3db..654e2b335 100644 --- a/docs/user-guide/resource_view.md +++ b/docs/user-guide/resource_view.md @@ -76,48 +76,7 @@ print(my_sym.name) # >> "foo" print(hex(my_sym.vaddr)) # >> "0x1000200" ``` -## Advanced ResourceView Usage - -Now that we have the basics out of the way, let's jump forward a few steps: - -```python -@dataclass -class Symbol(ResourceView): - name: str - vaddr: int - - @index - def Vaddr(self) -> int: - return self.vaddr - - async def get_alternative_names(self) -> Iterable[Symbol]: - """ - This symbol is one possible name for a particular address. This method finds other names for - the same address. It assumes all symbols are children of one parent resource (all symbols are - siblings) - """ - return self.resource.get_siblings_as_view( - Symbol, - r_filter=ResourceFilter( - tags=(Symbol,), - attribute_filters=( - ResourceAttributeValueFilter(Symbol.Vaddr, self.vaddr), - ) - ) - ) -``` - -Now `Symbol` has been expanded to show off some of the other uses of a view: - -* Indexes can be added - to views just like they can be added to `ResourceAttributes` classes. Adding an index allows us - to filter and sort resources with these attributes by some value. - -* Views can have methods, and these methods have direct access to the fields of the view (e.g. -`self.vaddr`). - -* Views provide a way to access the underlying resource (if it exists). The `get_alternative_names` -method accesses `self.resource` to make a query to fetch more resources. `.resource` returns a +ResourceViews provide a way to access the underlying resource (if it exists). `.resource` returns a `Resource` and is how you should access the resource when you need it. If the view does not have an underlying resource, a `ValueError` is raised: diff --git a/ofrak_core/ofrak/resource.py b/ofrak_core/ofrak/resource.py index b48718340..98f372f5e 100644 --- a/ofrak_core/ofrak/resource.py +++ b/ofrak_core/ofrak/resource.py @@ -1159,55 +1159,6 @@ async def get_only_descendant( ) return await self._create_resource(models[0]) - async def get_siblings_as_view( - self, - v_type: Type[RV], - r_filter: ResourceFilter = None, - r_sort: ResourceSort = None, - ) -> Iterable[RV]: - """ - Get all the siblings (resources which share a parent) of this resource. May optionally - filter the siblings so only those matching certain parameters are returned. May optionally - sort the siblings by an indexable attribute value key. The siblings - will be returned as an instance of the given - [viewable tag][ofrak.model.viewable_tag_model.ViewableResourceTag]. - - :param v_type: The type of [view][ofrak.resource] to get the siblings as - :param r_filter: Contains parameters which resources must match to be returned, including - any tags it must have and/or values of indexable attributes - :param r_sort: Specifies which indexable attribute to use as the key to sort and the - direction to sort - :return: - - :raises NotFoundError: If a filter was provided and no resources match the provided filter - """ - siblings = await self.get_siblings(r_filter, r_sort) - view_tasks = [r.view_as(v_type) for r in siblings] - return await asyncio.gather(*view_tasks) - - async def get_siblings( - self, - r_filter: ResourceFilter = None, - r_sort: ResourceSort = None, - ) -> Iterable["Resource"]: - """ - Get all the siblings (resources which share a parent) of this resource. May optionally - sort the siblings by an indexable attribute value key. May optionally - filter the siblings so only those matching certain parameters are returned. - - :param r_filter: Contains parameters which resources must match to be returned, including - any tags it must have and/or values of indexable attributes - :param r_sort: Specifies which indexable attribute to use as the key to sort and the - direction to sort - :return: - - :raises NotFoundError: If a filter was provided and no resources match the provided filter - """ - models = await self._resource_service.get_siblings_by_id( - self._resource.id, r_filter=r_filter, r_sort=r_sort - ) - return await self._create_resources(models) - async def get_only_sibling_as_view( self, v_type: Type[RV], From 7f8dbfebc0cbd22efb0c1756f8bdde53db581eda Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Fri, 7 Oct 2022 21:48:04 -0400 Subject: [PATCH 08/11] Add tests for uncovered functions --- ofrak_core/test_ofrak/unit/test_resource.py | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/ofrak_core/test_ofrak/unit/test_resource.py b/ofrak_core/test_ofrak/unit/test_resource.py index b0f6ffa4c..fa8f50fd0 100644 --- a/ofrak_core/test_ofrak/unit/test_resource.py +++ b/ofrak_core/test_ofrak/unit/test_resource.py @@ -237,6 +237,11 @@ async def test_summarize(resource: Resource): assert isinstance(summary, str) +async def test_summarize_tree(resource: Resource): + summary = await resource.summarize_tree() + assert isinstance(summary, str) + + async def test_get_range_within_parent(resource: Resource): """ Test that Resource.get_data_range_within_parent returns the correctly-mapped range. @@ -258,3 +263,20 @@ async def test_identify(resource: Resource): await resource.identify() assert resource.has_tag(GenericBinary) is True assert resource.has_tag(Elf) is False + + +async def test_get_tags(resource: Resource): + tags = resource.get_tags() + assert GenericBinary in tags + assert Elf not in tags + + resource.add_tag(Elf) + updated_tags = resource.get_tags() + assert Elf in updated_tags + + +async def test_repr(resource: Resource, capsys): + print(resource) + captured = capsys.readouterr() + assert captured.out.startswith("Resource(resource_id=") + assert "GenericBinary" in captured.out From 8b418dbcaa0eb318106b9ec5a646ad60d6929fd8 Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Tue, 11 Oct 2022 16:43:11 -0400 Subject: [PATCH 09/11] Test Resource.__repr__ directly --- ofrak_core/test_ofrak/unit/test_resource.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ofrak_core/test_ofrak/unit/test_resource.py b/ofrak_core/test_ofrak/unit/test_resource.py index fa8f50fd0..867e12992 100644 --- a/ofrak_core/test_ofrak/unit/test_resource.py +++ b/ofrak_core/test_ofrak/unit/test_resource.py @@ -275,8 +275,7 @@ async def test_get_tags(resource: Resource): assert Elf in updated_tags -async def test_repr(resource: Resource, capsys): - print(resource) - captured = capsys.readouterr() - assert captured.out.startswith("Resource(resource_id=") - assert "GenericBinary" in captured.out +async def test_repr(resource: Resource): + result = resource.__repr__() + assert result.startswith("Resource(resource_id=") + assert "GenericBinary" in result From 1c20c3cd47edab9df655832fe2b5fcb87385664a Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Tue, 11 Oct 2022 17:11:55 -0400 Subject: [PATCH 10/11] Add test for Resource.remove_attributes. --- ofrak_core/ofrak/resource.py | 12 ++++++++++++ ofrak_core/test_ofrak/unit/test_resource.py | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/ofrak_core/ofrak/resource.py b/ofrak_core/ofrak/resource.py index 98f372f5e..8cd385187 100644 --- a/ofrak_core/ofrak/resource.py +++ b/ofrak_core/ofrak/resource.py @@ -860,6 +860,18 @@ def get_attributes(self, attributes_type: Type[RA]) -> RA: ) return attributes + def remove_attributes(self, attributes_type: Type[ResourceAttributes]): + """ + Remove the value of a given attributes type from this resource, if there is such a value. + If the resource does not have a value for the given attributes type, do nothing. + :param attributes_type: + :return: + """ + if not self._resource.has_attributes(attributes_type): + return + self._set_modified() + self._resource.remove_attributes(attributes_type) + def add_component( self, component_id: bytes, diff --git a/ofrak_core/test_ofrak/unit/test_resource.py b/ofrak_core/test_ofrak/unit/test_resource.py index 867e12992..87a05598a 100644 --- a/ofrak_core/test_ofrak/unit/test_resource.py +++ b/ofrak_core/test_ofrak/unit/test_resource.py @@ -11,6 +11,7 @@ from ofrak.core.filesystem import FilesystemRoot from ofrak.core.patch_maker.linkable_binary import LinkableBinary from ofrak.core.program import Program +from ofrak.model.resource_model import ResourceAttributes from ofrak.resource import Resource from ofrak.resource_view import ResourceView from ofrak.service.resource_service_i import ResourceFilter @@ -279,3 +280,22 @@ async def test_repr(resource: Resource): result = resource.__repr__() assert result.startswith("Resource(resource_id=") assert "GenericBinary" in result + + +async def test_attributes(resource: Resource): + """ + Test Resource.{has_attributes, add_attributes, remove_attributes} + """ + + @dataclass(**ResourceAttributes.DATACLASS_PARAMS) + class DummyAttributes(ResourceAttributes): + name: str + + dummy_attributes = DummyAttributes("dummy") + assert resource.has_attributes(DummyAttributes) is False + + resource.add_attributes(dummy_attributes) + assert resource.has_attributes(DummyAttributes) is True + + resource.remove_attributes(DummyAttributes) + assert resource.has_attributes(DummyAttributes) is False From e542cfa01d3f7239588ff2896c88f98f51b96291 Mon Sep 17 00:00:00 2001 From: Wyatt <53830972+whyitfor@users.noreply.github.com> Date: Tue, 11 Oct 2022 17:33:19 -0400 Subject: [PATCH 11/11] Bump function coverage to 83 --- ofrak_core/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ofrak_core/Makefile b/ofrak_core/Makefile index a7aa05166..d22d3f3bc 100644 --- a/ofrak_core/Makefile +++ b/ofrak_core/Makefile @@ -16,7 +16,7 @@ inspect: .PHONY: test test: inspect $(PYTHON) -m pytest test_ofrak --cov=ofrak --cov-report=term-missing - fun-coverage --cov-fail-under=80 + fun-coverage --cov-fail-under=83 .PHONY: dependencies dependencies: