diff --git a/cyclonedx/model/__init__.py b/cyclonedx/model/__init__.py index 648c5b3e..cb68ec75 100644 --- a/cyclonedx/model/__init__.py +++ b/cyclonedx/model/__init__.py @@ -21,7 +21,7 @@ import warnings from datetime import datetime from enum import Enum -from typing import Any, Iterable, Optional +from typing import Any, Iterable, Optional, Tuple, TypeVar from sortedcontainers import SortedSet @@ -59,7 +59,10 @@ def sha1sum(filename: str) -> str: return h.hexdigest() -class ComparableTuple(tuple): +_T = TypeVar('_T') + + +class ComparableTuple(Tuple[_T, ...]): """ Allows comparison of tuples, allowing for None values. """ @@ -244,6 +247,12 @@ def __eq__(self, other: object) -> bool: return hash(other) == hash(self) return False + def __lt__(self, other: Any) -> bool: + if isinstance(other, AttachedText): + return ComparableTuple((self.content_type, self.content, self.encoding)) < \ + ComparableTuple((other.content_type, other.content, other.encoding)) + return NotImplemented + def __hash__(self) -> int: return hash((self.content, self.content_type, self.encoding)) diff --git a/tests/test_model_component.py b/tests/test_model_component.py index 51582f27..fb695d53 100644 --- a/tests/test_model_component.py +++ b/tests/test_model_component.py @@ -37,6 +37,7 @@ from cyclonedx.model import ( AttachedText, Copyright, + Encoding, ExternalReference, ExternalReferenceType, IdentifiableAction, @@ -364,14 +365,17 @@ def test_not_same(self) -> None: self.assertFalse(diff_1 == diff_2) def test_sort(self) -> None: + text_a = AttachedText(content='a') + text_b = AttachedText(content='b') + # expected sort order: ([url], [text]) expected_order = [1, 0, 5, 2, 3, 4] diffs = [ - Diff(url=XsUri('a'), text='b'), - Diff(url=XsUri('a'), text='a'), - Diff(url=XsUri('b'), text='a'), - Diff(text='a'), - Diff(text='b'), + Diff(url=XsUri('a'), text=text_b), + Diff(url=XsUri('a'), text=text_a), + Diff(url=XsUri('b'), text=text_a), + Diff(text=text_a), + Diff(text=text_b), Diff(url=XsUri('a')), ] sorted_diffs = sorted(diffs) @@ -379,6 +383,23 @@ def test_sort(self) -> None: self.assertListEqual(sorted_diffs, expected_diffs) +class TestModelAttachedText(TestCase): + + def test_sort(self) -> None: + # expected sort order: (content_type, content, encoding) + expected_order = [0, 4, 2, 1, 3] + text = [ + AttachedText(content='a', content_type='a', encoding=Encoding.BASE_64), + AttachedText(content='a', content_type='b', encoding=Encoding.BASE_64), + AttachedText(content='b', content_type='a', encoding=Encoding.BASE_64), + AttachedText(content='b', content_type='b', encoding=Encoding.BASE_64), + AttachedText(content='a', content_type='a'), + ] + sorted_text = sorted(text) + expected_text = reorder(text, expected_order) + self.assertListEqual(sorted_text, expected_text) + + class TestModelPatch(TestCase): def test_same_1(self) -> None: @@ -425,8 +446,8 @@ def test_not_same_1(self) -> None: self.assertFalse(p1 == p2) def test_sort(self) -> None: - diff_a = Diff(text='a') - diff_b = Diff(text='b') + diff_a = Diff(text=AttachedText(content='a')) + diff_b = Diff(text=AttachedText(content='b')) resolves_a = [ IssueType(classification=IssueClassification.DEFECT),