From 6933e55843200d436c92b03a950a96b10879dba8 Mon Sep 17 00:00:00 2001 From: Dan Bungert Date: Wed, 8 Jun 2022 15:27:31 -0600 Subject: [PATCH 1/3] gaps: test cleanup --- subiquity/common/filesystem/tests/test_gaps.py | 14 ++++++-------- subiquity/models/tests/test_filesystem.py | 4 +++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/subiquity/common/filesystem/tests/test_gaps.py b/subiquity/common/filesystem/tests/test_gaps.py index 2b3c0264..04fc5344 100644 --- a/subiquity/common/filesystem/tests/test_gaps.py +++ b/subiquity/common/filesystem/tests/test_gaps.py @@ -22,6 +22,7 @@ from subiquity.models.filesystem import ( MiB, ) from subiquity.models.tests.test_filesystem import ( + make_disk, make_model_and_disk, make_partition, ) @@ -31,26 +32,23 @@ from subiquity.common.filesystem import gaps class TestGaps(unittest.TestCase): def test_basic(self): - model, disk1 = make_model_and_disk() - - pg = gaps.parts_and_gaps(disk1) - self.assertEqual(1, len(pg)) - self.assertTrue(isinstance(pg[0], gaps.Gap)) - self.assertEqual(MiB, pg[0].offset) + [gap] = gaps.parts_and_gaps(make_disk()) + self.assertTrue(isinstance(gap, gaps.Gap)) + self.assertEqual(MiB, gap.offset) class TestDiskGaps(unittest.TestCase): def test_no_partition_gpt(self): size = 1 << 30 - m, d = make_model_and_disk(size=size, ptable='gpt') + d = make_disk(size=size, ptable='gpt') self.assertEqual( gaps.find_disk_gaps_v2(d), [gaps.Gap(d, MiB, size - 2*MiB, False)]) def test_no_partition_dos(self): size = 1 << 30 - m, d = make_model_and_disk(size=size, ptable='dos') + d = make_disk(size=size, ptable='dos') self.assertEqual( gaps.find_disk_gaps_v2(d), [gaps.Gap(d, MiB, size - MiB, False)]) diff --git a/subiquity/models/tests/test_filesystem.py b/subiquity/models/tests/test_filesystem.py index e495f985..1d29576e 100644 --- a/subiquity/models/tests/test_filesystem.py +++ b/subiquity/models/tests/test_filesystem.py @@ -143,7 +143,9 @@ def make_model(bootloader=None): return model -def make_disk(fs_model, **kw): +def make_disk(fs_model=None, **kw): + if fs_model is None: + fs_model = make_model() if 'serial' not in kw: kw['serial'] = 'serial%s' % len(fs_model._actions) if 'path' not in kw: From cb2d184ef2edf2ae114598289d4ce85368fead3b Mon Sep 17 00:00:00 2001 From: Dan Bungert Date: Fri, 10 Jun 2022 15:50:56 -0600 Subject: [PATCH 2/3] gaps: add gap.split --- subiquity/common/filesystem/gaps.py | 21 +++++++++++++++++- .../common/filesystem/tests/test_gaps.py | 22 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/subiquity/common/filesystem/gaps.py b/subiquity/common/filesystem/gaps.py index d212ae65..d4581724 100644 --- a/subiquity/common/filesystem/gaps.py +++ b/subiquity/common/filesystem/gaps.py @@ -28,7 +28,8 @@ from subiquity.models.filesystem import ( ) -@attr.s(auto_attribs=True) +# should also set on_setattr=None with attrs 20.1.0 +@attr.s(auto_attribs=True, frozen=True) class Gap: device: object offset: int @@ -41,6 +42,24 @@ class Gap: def id(self): return 'gap-' + self.device.id + def split(self, size): + """returns a tuple of two new gaps, split from the current gap based on + the supplied size. If size is equal to the gap size, the second gap is + None. The original gap is unmodified.""" + if size > self.size: + raise Exception('requested size larger than gap') + if size == self.size: + return (self, None) + first_gap = Gap(device=self.device, + offset=self.offset, + size=size, + in_extended=self.in_extended) + rest_gap = Gap(device=self.device, + offset=self.offset + size, + size=self.size - size, + in_extended=self.in_extended) + return (first_gap, rest_gap) + @functools.singledispatch def parts_and_gaps(device): diff --git a/subiquity/common/filesystem/tests/test_gaps.py b/subiquity/common/filesystem/tests/test_gaps.py index 04fc5344..c346b43e 100644 --- a/subiquity/common/filesystem/tests/test_gaps.py +++ b/subiquity/common/filesystem/tests/test_gaps.py @@ -37,6 +37,28 @@ class TestGaps(unittest.TestCase): self.assertEqual(MiB, gap.offset) +class TestSplitGap(unittest.TestCase): + def test_equal(self): + [gap] = gaps.parts_and_gaps(make_disk()) + actual = gap.split(gap.size) + self.assertEqual((gap, None), actual) + + def test_too_big(self): + [gap] = gaps.parts_and_gaps(make_disk()) + with self.assertRaises(Exception): + gap.split(gap.size + MiB) + + def test_split(self): + [gap] = gaps.parts_and_gaps(make_disk(size=100 << 30)) + size = 10 << 30 + new_gaps = gap.split(size) + self.assertEqual(2, len(new_gaps)) + self.assertEqual(size, new_gaps[0].size) + self.assertEqual(gap.size - size, new_gaps[1].size) + self.assertEqual(gap.offset, new_gaps[0].offset) + self.assertEqual(gap.offset + size, new_gaps[1].offset) + + class TestDiskGaps(unittest.TestCase): def test_no_partition_gpt(self): From 1aa1ee5cb01e6eabb9652eb22240ec6bde41cf05 Mon Sep 17 00:00:00 2001 From: Dan Bungert Date: Fri, 10 Jun 2022 12:46:31 -0600 Subject: [PATCH 3/3] gaps: add gaps.at_offset --- subiquity/common/filesystem/gaps.py | 8 +++++++ .../common/filesystem/tests/test_gaps.py | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/subiquity/common/filesystem/gaps.py b/subiquity/common/filesystem/gaps.py index d4581724..36f7f00f 100644 --- a/subiquity/common/filesystem/gaps.py +++ b/subiquity/common/filesystem/gaps.py @@ -205,3 +205,11 @@ def movable_trailing_partitions_and_gap_size(partition): else: return (trailing_partitions, 0) return (trailing_partitions, 0) + + +def at_offset(device, offset): + for pg in parts_and_gaps(device): + if isinstance(pg, Gap): + if pg.offset == offset: + return pg + return None diff --git a/subiquity/common/filesystem/tests/test_gaps.py b/subiquity/common/filesystem/tests/test_gaps.py index c346b43e..304feadf 100644 --- a/subiquity/common/filesystem/tests/test_gaps.py +++ b/subiquity/common/filesystem/tests/test_gaps.py @@ -59,6 +59,28 @@ class TestSplitGap(unittest.TestCase): self.assertEqual(gap.offset + size, new_gaps[1].offset) +class TestAtOffset(unittest.TestCase): + def test_zero(self): + self.assertIsNone(gaps.at_offset(make_disk(), 0)) + + def test_match(self): + [gap] = gaps.parts_and_gaps(make_disk()) + self.assertEqual(gap, gaps.at_offset(gap.device, gap.offset)) + + def test_not_match(self): + [gap] = gaps.parts_and_gaps(make_disk()) + self.assertIsNone(gaps.at_offset(gap.device, gap.offset + 1)) + + def test_two_gaps(self): + m, d = make_model_and_disk(size=100 << 20) + m.storage_version = 2 + make_partition(m, d, offset=0, size=20 << 20) + make_partition(m, d, offset=40 << 20, size=20 << 20) + [_, g1, _, g2] = gaps.parts_and_gaps(d) + self.assertEqual(g1, gaps.at_offset(d, 20 << 20)) + self.assertEqual(g2, gaps.at_offset(d, 60 << 20)) + + class TestDiskGaps(unittest.TestCase): def test_no_partition_gpt(self):