From 07d4a7611ce4e522c496b4d4920885b4d95a2537 Mon Sep 17 00:00:00 2001 From: Lukasz Fundakowski Date: Thu, 20 Feb 2025 11:57:04 +0100 Subject: [PATCH] scripts: partition manager: split single test to multiple tests Refactored partition manager test to multiple tests to have better coverage for each functionality. Signed-off-by: Lukasz Fundakowski --- .github/workflows/scripts-test.yml | 4 +- CODEOWNERS | 1 + scripts/partition_manager.py | 24 +- scripts/requirements-test.txt | 1 + scripts/tests/partition_manager_test.py | 499 +++++++++++++++--------- 5 files changed, 323 insertions(+), 206 deletions(-) diff --git a/.github/workflows/scripts-test.yml b/.github/workflows/scripts-test.yml index 9e8e49cacd46..7b3c01d87350 100644 --- a/.github/workflows/scripts-test.yml +++ b/.github/workflows/scripts-test.yml @@ -5,7 +5,7 @@ on: branches: - main paths: - - 'scripts/**' + - 'scripts/**/*' jobs: test-scripts: @@ -25,4 +25,4 @@ jobs: - name: Install packages run: python -m pip install -r scripts/requirements-test.txt - name: Run tests - run: python -m pytest scripts/bootloader/tests scripts/tests + run: python -m pytest scripts diff --git a/CODEOWNERS b/CODEOWNERS index 31b49aa6bfaf..1e402ae16e24 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -691,6 +691,7 @@ /scripts/sdp/ @nrfconnect/ncs-ll-ursus /scripts/twister/alt/zephyr/tests/drivers/mspi/api/testcase.yaml @nrfconnect/ncs-ll-ursus /scripts/generate_psa_key_attributes.py @nrfconnect/ncs-aurora +/scripts/tests/ @nrfconnect/ncs-pluto @fundakol /scripts/docker/*.rst @nrfconnect/ncs-doc-leads /scripts/hid_configurator/*.rst @nrfconnect/ncs-si-bluebagel-doc diff --git a/scripts/partition_manager.py b/scripts/partition_manager.py index 29810396bae7..c911fedf166a 100644 --- a/scripts/partition_manager.py +++ b/scripts/partition_manager.py @@ -5,11 +5,11 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause import argparse -import yaml -from os import path import sys +from os import path from pprint import pformat -from io import StringIO + +import yaml PERMITTED_STR_KEYS = ['size', 'region'] END_TO_START = 'end_to_start' @@ -1050,23 +1050,5 @@ def main(): write_yaml_out_file(regions, args.output_regions) -def expect_addr_size(td, name, expected_address, expected_size): - if expected_size is not None: - assert td[name]['size'] == expected_size, \ - 'Size of {} was {}, expected {}.\ntd:{}'.format(name, td[name]['size'], expected_size, pformat(td)) - if expected_address is not None: - assert td[name]['address'] == expected_address, \ - 'Address of {} was {}, expected {}.\ntd:{}'.format(name, td[name]['address'], expected_address, pformat(td)) - if expected_size is not None and expected_address is not None: - assert td[name]['end_address'] == expected_address + expected_size, \ - 'End address of {} was {}, expected {}.\ntd:{}'.format(name, td[name]['end_address'], expected_address + expected_size, pformat(td)) - - -def expect_list(expected, actual): - expected_list = list(sorted(expected)) - actual_list = list(sorted(actual)) - assert sorted(expected_list) == sorted(actual_list), 'Expected list {}, was {}'.format(expected_list, actual_list) - - if __name__ == '__main__': main() diff --git a/scripts/requirements-test.txt b/scripts/requirements-test.txt index bbaf0df33b21..dc622a78fa46 100644 --- a/scripts/requirements-test.txt +++ b/scripts/requirements-test.txt @@ -2,3 +2,4 @@ cryptography intelhex pytest +pyyaml diff --git a/scripts/tests/partition_manager_test.py b/scripts/tests/partition_manager_test.py index dbbc442ddcfc..19bfb2a44b2c 100644 --- a/scripts/tests/partition_manager_test.py +++ b/scripts/tests/partition_manager_test.py @@ -1,34 +1,78 @@ -#!/usr/bin/env python3 # # Copyright (c) 2019 Nordic Semiconductor ASA # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -import yaml -from partition_manager import * +from io import StringIO +from pprint import pformat + +import pytest +import yaml -def test_partition_manager(): +from partition_manager import ( + COMPLEX, + END_TO_START, + START_TO_END, + PartitionError, + calculate_end_address, + fix_syntactic_sugar, + get_dynamic_area_start_and_size, + get_empty_part_to_move_dyn_part, + get_region_config, + get_required_offset, + remove_all_zero_sized_partitions, + remove_item_not_in_list, + resolve, + set_addresses_and_align, + set_sub_partition_address_and_size, + sort_regions, +) + + +def expect_addr_size(td, name, expected_address, expected_size): + if expected_size is not None: + assert td[name]['size'] == expected_size, \ + 'Size of {} was {}, expected {}.\ntd:{}'.format(name, td[name]['size'], expected_size, pformat(td)) + if expected_address is not None: + assert td[name]['address'] == expected_address, \ + 'Address of {} was {}, expected {}.\ntd:{}'.format(name, td[name]['address'], expected_address, pformat(td)) + if expected_size is not None and expected_address is not None: + assert td[name]['end_address'] == expected_address + expected_size, \ + 'End address of {} was {}, expected {}.\ntd:{}'.format(name, td[name]['end_address'], expected_address + expected_size, pformat(td)) + + +def expect_list(expected, actual): + expected_list = list(sorted(expected)) + actual_list = list(sorted(actual)) + assert sorted(expected_list) == sorted(actual_list), 'Expected list {}, was {}'.format(expected_list, actual_list) + + +def test_remove_item_not_in_list(): list_one = [1, 2, 3, 4] items_to_check = [4] remove_item_not_in_list(list_one, items_to_check, 'app') assert list_one[0] == 4 assert len(list_one) == 1 + +def test_get_dynamic_area_start_and_size_1(): test_config = { - 'first': {'address': 0, 'size': 10}, + 'first': {'address': 0, 'size': 10}, # Gap from deleted partition. - 'app': {'address': 20, 'size': 10}, + 'app': {'address': 20, 'size': 10}, # Gap from deleted partition. 'fourth': {'address': 40, 'size': 60}} start, size = get_dynamic_area_start_and_size(test_config, 0, 100, 'app') assert start == 10 - assert size == 40-10 + assert size == 40 - 10 + +def test_get_dynamic_area_start_and_size_2(): test_config = { - 'first': {'address': 0, 'size': 10}, + 'first': {'address': 0, 'size': 10}, 'second': {'address': 10, 'size': 10}, - 'app': {'address': 20, 'size': 80} + 'app': {'address': 20, 'size': 80} # Gap from deleted partition. } @@ -36,23 +80,29 @@ def test_partition_manager(): assert start == 20 assert size == 80 + +def test_get_dynamic_area_start_and_size_3(): test_config = { - 'app': {'address': 0, 'size': 10}, + 'app': {'address': 0, 'size': 10}, # Gap from deleted partition. 'second': {'address': 40, 'size': 60}} start, size = get_dynamic_area_start_and_size(test_config, 0, 100, 'app') assert start == 0 assert size == 40 + +def test_get_dynamic_area_start_and_size_4(): test_config = { - 'first': {'address': 0, 'size': 10}, + 'first': {'address': 0, 'size': 10}, # Gap from deleted partition. - 'app': {'address': 20, 'size': 10}} + 'app': {'address': 20, 'size': 10}} start, size = get_dynamic_area_start_and_size(test_config, 0, 100, 'app') assert start == 10 assert size == 100 - 10 - # Verify that base address can be larger than 0 (RAM vs FLASH) + +def test_that_base_address_can_be_larger_than_0(): + """Verify that base address can be larger than 0 (RAM vs FLASH)""" test_config = { 'first': {'address': 1000, 'size': 10}, # Gap from deleted partition. @@ -62,7 +112,9 @@ def test_partition_manager(): assert start == 1010 assert size == 400 - 10 - # Verify that all 'end' and 'start' are valid references in 'one_of' dicts + +def test_that_all_end_and_start_are_valid_references_in_one_of_dicts(): + """Verify that all 'end' and 'start' are valid references in 'one_of' dicts""" td = { 'a': {'placement': {'after': ['x0', 'x1', 'start']}, 'size': 100}, 'b': {'placement': {'before': ['x0', 'x1', 'end']}, 'size': 200}, @@ -76,7 +128,9 @@ def test_partition_manager(): expect_addr_size(td, 'app', 100, 700) expect_addr_size(td, 'b', 800, 200) - # Verify that 'app' spans the dynamic partition when a dynamic partition is set + +def test_that_app_spans_the_dynamic_partition_when_a_dynamic_partition_is_set(): + """Verify that 'app' spans the dynamic partition when a dynamic partition is set""" td = {'a': {'size': 100, 'region': 'flash', 'placement': {'after': 'start'}}} test_region = {'name': 'flash', 'size': 1000, @@ -87,7 +141,9 @@ def test_partition_manager(): get_region_config(td, test_region) assert td['app']['span'][0] == 'the_dynamic_partition' - # Verify that START_TO_END region configuration is correct + +def test_that_START_TO_END_region_configuration_is_correct(): + """Verify that START_TO_END region configuration is correct""" td = {'b': {'size': 100, 'region': 'extflash'}} test_region = {'name': 'extflash', 'size': 1000, @@ -99,7 +155,9 @@ def test_partition_manager(): assert td['extflash']['address'] == 2100 assert td['extflash']['size'] == 900 - # Verify that SRAM configuration is correct + +def test_that_SRAM_configuration_is_correct(): + """Verify that SRAM configuration is correct""" td = {'b': {'size': 100, 'region': 'sram'}} test_region = {'name': 'sram', 'size': 1000, @@ -111,7 +169,9 @@ def test_partition_manager(): assert td['sram']['address'] == 2000 assert td['sram']['size'] == 900 - # Verify that sram configuration is correct + +def test_that_sram_configuration_is_correct(): + """Verify that SRAM configuration is correct""" td = { 'b': {'size': 100, 'region': 'sram'}, 'c': {'size': 200, 'region': 'sram'}, @@ -130,7 +190,9 @@ def test_partition_manager(): assert td['c']['size'] == 200 assert td['d']['size'] == 300 - # Verify that SRAM configuration with given static configuration is correct + +def test_that_SRAM_configuration_with_given_static_configuration_is_correct(): + """Verify that SRAM configuration with given static configuration is correct""" test_region = {'name': 'sram', 'size': 1000, 'base_address': 2000, @@ -144,10 +206,10 @@ def test_partition_manager(): get_region_config(td, test_region, static_conf={'s1': {'size': 100, - 'address': (1000+2000)-100, + 'address': (1000 + 2000) - 100, 'region': 'sram'}, 's2': {'size': 200, - 'address': (1000+2000)-100-200, + 'address': (1000 + 2000) - 100 - 200, 'region': 'sram'}}) assert td['sram']['address'] == 2000 assert td['sram']['size'] == 100 @@ -156,14 +218,18 @@ def test_partition_manager(): assert td['c']['size'] == 200 assert td['d']['size'] == 300 - # Verify that SRAM configuration with given static configuration fails if static SRAM partitions are not - # packed at the end of flash, here there is a space between the two regions + +def test_that_SRAM_configuration_fails_if_static_SRAM_are_not_at_the_end_of_flash(): + """ + Verify that SRAM configuration with given static configuration fails + if static SRAM partitions are not packed at the end of flash, + here there is a space between the two regions + """ test_region = {'name': 'sram', 'size': 1000, 'base_address': 2000, 'placement_strategy': END_TO_START, 'device': None} - failed = False td = { 'a': {'placement': {'after': 'start'}, 'size': 100}, 'b': {'size': 100, 'region': 'sram'}, @@ -171,23 +237,23 @@ def test_partition_manager(): 'd': {'size': 300, 'region': 'sram'}, 'app': {} } - try: + with pytest.raises(PartitionError): get_region_config(td, test_region, static_conf={'s1': {'size': 100, - 'address': (1000+2000)-100, + 'address': (1000 + 2000) - 100, 'region': 'sram'}, 's2': {'size': 200, - 'address': (1000+2000)-100-300, + 'address': (1000 + 2000) - 100 - 300, 'region': 'sram'}}) # Note 300 not 200 - except PartitionError: - failed = True - assert failed - # Verify that SRAM configuration with given static configuration fails if static SRAM partitions are not - # packed at the end of flash, here the partitions are packed, but does not go to the end of SRAM - failed = False +def test_that_SRAM_configuration_fails_if_static_SRAM_partitions_are_not_at_the_end_of_flash(): + """ + Verify that SRAM configuration with given static configuration fails + if static SRAM partitions are not packed at the end of flash, + here the partitions are packed, but does not go to the end of SRAM + """ test_region = {'name': 'sram', 'size': 1000, 'base_address': 2000, @@ -200,21 +266,19 @@ def test_partition_manager(): 'd': {'size': 300, 'region': 'sram'}, 'app': {} } - try: + with pytest.raises(PartitionError): get_region_config(td, test_region, static_conf={'s1': {'size': 100, - 'address': (1000+2000-50)-100, + 'address': (1000 + 2000 - 50) - 100, 'region': 'sram'}, # Note - 50 's2': {'size': 200, - 'address': (1000+2000-50)-100-200, + 'address': (1000 + 2000 - 50) - 100 - 200, 'region': 'sram'}}) # Note - 50 - except PartitionError: - failed = True - assert failed - # Verify that all 'one_of' dicts are replaced with the first entry which corresponds to an existing partition +def test_that_all_one_of_dicts_are_replaced_with_the_first_entry_which_corresponds_to_an_existing_partition(): + """Verify that all 'one_of' dicts are replaced with the first entry which corresponds to an existing partition""" td = { 'a': {'placement': {'after': 'start'}, 'size': 100}, 'b': {'placement': {'after': ['x0', 'x1', 'a', 'x2']}, 'size': 200}, @@ -238,7 +302,8 @@ def test_partition_manager(): expect_addr_size(td, 'app', 600, 200) # s spans a, b and c expect_addr_size(td, 'e', 800, 200) # s spans a, b and c - # + +def test_that_all_one_of_dicts_are_replaced_with_the_first_entry_which_corresponds_to_an_existing_partition_2(): td = { 'a': {'placement': {'after': 'start'}, 'size': 100}, 'b': {'placement': {'after': ['x0', 'x1', 'a', 'x2']}, 'size': 200}, @@ -254,7 +319,7 @@ def test_partition_manager(): s_reqs = td.copy() s_reqs['p_ext'] = {'region': 'ext', 'size': 250} s, sub_partitions = resolve(td, 'app', s_reqs) - set_addresses_and_align(td, sub_partitions, s, 1250, 'app', system_reqs = s_reqs) + set_addresses_and_align(td, sub_partitions, s, 1250, 'app', system_reqs=s_reqs) set_sub_partition_address_and_size(td, sub_partitions) calculate_end_address(td) expect_addr_size(td, 'a', 0, 100) # b is after a @@ -264,9 +329,14 @@ def test_partition_manager(): expect_addr_size(td, 's', 0, 500) # s spans a, b and c expect_addr_size(td, 'app', 600, 200) # s spans a, b and c expect_addr_size(td, 'e', 800, 200) # s spans a, b and c - expect_addr_size(td, 'f', 1000, 250) # Shares size with 'p_ext' from a different region + expect_addr_size(td, 'f', 1000, 250) # Shares size with 'p_ext' from a different region + - # Verify that all 'share_size' with value partition that has size 0 is compatible with 'one_of' dicts +def test_that_all_share_size_with_value_partition_that_has_size_0_is_compatible_with_one_of_dicts(): + """ + Verify that all 'share_size' with value partition + that has size 0 is compatible with 'one_of' dicts + """ td = { 'a': {'placement': {'after': 'start'}, 'size': 0}, 'b': {'placement': {'after': ['a', 'start']}, @@ -284,7 +354,12 @@ def test_partition_manager(): assert 'c' not in td assert 'd' not in td - # Verify that all 'share_size' with value partition that has size 0 is compatible withe 'one_of' dicts. + +def test_that_all_share_size_with_value_partition_that_has_size_0_is_compatible_withe_one_of_dicts(): + """ + Verify that all 'share_size' with value partition + that has size 0 is compatible withe 'one_of' dicts. + """ td = { 'a': {'placement': {'after': 'start'}, 'size': 0}, 'b': {'placement': {'after': ['a', 'start']}, @@ -310,59 +385,63 @@ def test_partition_manager(): assert 'e' not in td expect_addr_size(td, 'f', 0, 100) - # Verify that an error is raised when no partition inside 'one_of' dicts exist as list item - failed = False + +def test_that_an_error_is_raised_when_no_partition_inside_one_of_dicts_exist_as_list_item(): + """Verify that an error is raised when no partition inside 'one_of' dicts exist as list item""" td = { 'app': {}, 'a': {'placement': {'after': 'app'}, 'size': 100}, 's': {'span': ['a', {'one_of': ['x0', 'x1']}]}, } - try: + with pytest.raises(PartitionError): resolve(td, 'app') - except PartitionError: - failed = True - assert failed - # Verify that empty placement property throws error + +def test_that_empty_placement_property_throws_error(): + """Verify that empty placement property throws error""" td = {'spm': {'placement': {'before': ['app']}, 'size': 100, 'inside': ['mcuboot_slot0']}, 'mcuboot': {'placement': {'before': ['spm', 'app']}, 'size': 200}, 'mcuboot_slot0': {'span': ['app']}, 'invalid': {'placement': {}}, 'app': {}} - failed = False - try: - s, sub_partitions = resolve(td, 'app') - except PartitionError: - failed = True - assert failed - - # Verify that offset is correct when aligning partition not at address 0 + + with pytest.raises(PartitionError): + resolve(td, 'app') + + +def test_that_offset_is_correct_when_aligning_partition_not_at_address_0(): + """Verify that offset is correct when aligning partition not at address 0""" offset = get_required_offset(align={'end': 800}, start=1400, size=100, move_up=False) assert offset == 700 - # Verify that offset is correct when aligning partition at address 0 + +def test_that_offset_is_correct_when_aligning_partition_at_address_0(): + """Verify that offset is correct when aligning partition at address 0""" offset = get_required_offset(align={'end': 800}, start=0, size=100, move_up=False) assert offset == 700 - # Verify that offset is correct when aligning partition at address 0 - # and end of first partition is larger than the required alignment. + +def test_that_offset_and_end_of_first_partition_are_correct_when_aligning_partition_at_address_0(): + """ + Verify that offset is correct when aligning partition at address 0 + and end of first partition is larger than the required alignment. + """ offset = get_required_offset(align={'end': 800}, start=0, size=1000, move_up=False) assert offset == 600 for l in [ - lambda : get_required_offset(align={'end': ['CONFIG_VAR']}, start=0, size=1000, move_up=False), - lambda : get_required_offset(align={'start': ['CONFIG_VAR']}, start=0, size=1000, move_up=False), - lambda : get_required_offset(align={'start': [[2]]},start=0, size=1000, move_up=False) - ]: - failed = False - try: + lambda: get_required_offset(align={'end': ['CONFIG_VAR']}, start=0, size=1000, move_up=False), + lambda: get_required_offset(align={'start': ['CONFIG_VAR']}, start=0, size=1000, move_up=False), + lambda: get_required_offset(align={'start': [[2]]}, start=0, size=1000, move_up=False) + ]: + with pytest.raises(TypeError): l() - except TypeError: - failed = True - assert failed, 'Should have received a TypeError.' - # Verify that the first partition can be aligned, and that the inserted empty partition is placed behind it. + + +def test_that_the_first_partition_can_be_aligned(): + """Verify that the first partition can be aligned, and that the inserted empty partition is placed behind it.""" td = {'first': {'placement': {'before': 'app', 'align': {'end': 800}}, 'size': 100}, - 'app': {'region': 'flash_primary',}} + 'app': {'region': 'flash_primary', }} s, sub_partitions = resolve(td, 'app') set_addresses_and_align(td, sub_partitions, s, 1000, 'app') set_sub_partition_address_and_size(td, sub_partitions) @@ -370,7 +449,9 @@ def test_partition_manager(): expect_addr_size(td, 'EMPTY_0', 100, 700) expect_addr_size(td, 'app', 800, 200) - # Verify that providing a static configuration with nothing unresolved gives a valid configuration with 'app'. + +def test_that_providing_a_static_configuration_with_nothing_unresolved_gives_a_valid_configuration_with_app(): + """Verify that providing a static configuration with nothing unresolved gives a valid configuration with 'app'""" static_config = {'spm': {'address': 0, 'placement': None, 'before': ['app'], 'size': 400}} test_config = {'app': dict()} flash_region = { @@ -387,7 +468,9 @@ def test_partition_manager(): assert 'spm' in test_config assert test_config['spm']['address'] == 0 - # Verify that mixing one static partition and a dynamic in a START_TO_END region configuration is allowed + +def test_that_mixing_one_static_partition_and_a_dynamic_in_a_START_TO_END_region_configuration_is_allowed(): + """Verify that mixing one static partition and a dynamic in a START_TO_END region configuration is allowed""" static_config = {'secondary': {'size': 200, 'address': 2000, 'region': 'extflash'}} td = {'b': {'size': 100, 'region': 'extflash'}} test_region = {'name': 'extflash', @@ -402,10 +485,12 @@ def test_partition_manager(): assert td['extflash']['address'] == 2300 assert td['extflash']['size'] == 700 - # Test a single partition with alignment where the address is smaller than the alignment value. + +def test_a_single_partition_with_alignment_where_the_address_is_smaller_than_the_alignment_value(): + """Test a single partition with alignment where the address is smaller than the alignment value.""" td = {'without_alignment': {'placement': {'before': 'with_alignment'}, 'size': 100}, 'with_alignment': {'placement': {'before': 'app', 'align': {'start': 200}}, 'size': 100}, - 'app': {'region': 'flash_primary',}, + 'app': {'region': 'flash_primary', }, 'span_with_alignment': {'span': ['without_alignment', 'with_alignment', 'app']}} s, sub_partitions = resolve(td, 'app') set_addresses_and_align(td, sub_partitions, s, 1000, 'app') @@ -415,10 +500,12 @@ def test_partition_manager(): expect_addr_size(td, 'with_alignment', 200, 100) expect_addr_size(td, 'span_with_alignment', 0, 1000) - # Test alignment after 'app' + +def test_alignment_after_app(): + """Test alignment after 'app'""" td = {'without_alignment': {'placement': {'after': 'app'}, 'size': 100}, 'with_alignment': {'placement': {'after': 'without_alignment', 'align': {'start': 400}}, 'size': 100}, - 'app': {'region': 'flash_primary',}} + 'app': {'region': 'flash_primary', }} s, sub_partitions = resolve(td, 'app') set_addresses_and_align(td, sub_partitions, s, 1000, 'app') set_sub_partition_address_and_size(td, sub_partitions) @@ -427,7 +514,9 @@ def test_partition_manager(): expect_addr_size(td, 'with_alignment', 800, 100) expect_addr_size(td, 'EMPTY_0', 900, 100) - # Test two partitions with alignment where the address is smaller than the alignment value. + +def test_two_partitions_with_alignment_where_the_address_is_smaller_than_the_alignment_value(): + """Test two partitions with alignment where the address is smaller than the alignment value""" td = {'without_alignment': {'placement': {'before': 'with_alignment'}, 'size': 100}, 'with_alignment': {'placement': {'before': 'with_alignment_2', 'align': {'end': 400}}, 'size': 100}, 'with_alignment_2': {'placement': {'before': 'app', 'align': {'start': 1000}}, 'size': 100}, @@ -441,7 +530,9 @@ def test_partition_manager(): expect_addr_size(td, 'EMPTY_1', 400, 600) expect_addr_size(td, 'with_alignment_2', 1000, 100) - # Test three partitions with alignment where the address is BIGGER than the alignment value. + +def test_three_partitions_with_alignment_where_the_address_is_BIGGER_than_the_alignment_value(): + """Test three partitions with alignment where the address is BIGGER than the alignment value.""" td = {'without_alignment': {'placement': {'before': 'with_alignment'}, 'size': 10000}, 'with_alignment': {'placement': {'before': 'with_alignment_2', 'align': {'end': 400}}, 'size': 100}, 'with_alignment_2': {'placement': {'before': 'app', 'align': {'start': 1000}}, 'size': 100}, @@ -455,15 +546,19 @@ def test_partition_manager(): expect_addr_size(td, 'EMPTY_1', 10400, 600) expect_addr_size(td, 'with_alignment_2', 11000, 100) - # Test 'align_next' - # 'second' will align 'third', but 'third' already has a bigger alignment. - # 'third' will align 'fourth' on 6000 instead of 2000. - # 'app' will align 'fifth' on 4000. - # 'fifth' has an 'align_next' directive that is ignored since 'fifth' is the last partition. + +def test_align_next(): + """ + Test 'align_next' + 'second' will align 'third', but 'third' already has a bigger alignment. + 'third' will align 'fourth' on 6000 instead of 2000. + 'app' will align 'fifth' on 4000. + 'fifth' has an 'align_next' directive that is ignored since 'fifth' is the last partition. + """ td = {'first': {'placement': {'after': 'start'}, 'size': 10000}, 'second': {'placement': {'after': 'first', 'align': {'end': 2000}, 'align_next': 4000}, 'size': 1000}, - 'third': {'placement': {'after':'second', 'align': {'start': 8000}, 'align_next': 6000}, 'size': 3000}, - 'fourth': {'placement': {'before': 'app', 'after':'third', 'align': {'start': 2000}}, 'size': 2000}, + 'third': {'placement': {'after': 'second', 'align': {'start': 8000}, 'align_next': 6000}, 'size': 3000}, + 'fourth': {'placement': {'before': 'app', 'after': 'third', 'align': {'start': 2000}}, 'size': 2000}, 'app': {'region': 'flash_primary', 'placement': {'align_next': 4000}}, 'fifth': {'placement': {'after': 'app', 'align_next': 10000}, 'size': 2000}} s, sub_partitions = resolve(td, 'app') @@ -480,80 +575,79 @@ def test_partition_manager(): expect_addr_size(td, 'fifth', 96000, 2000) expect_addr_size(td, 'EMPTY_3', 98000, 2000) - # Test 'align' (negative) - # Wrong place + +def test_align_next_wrong_place(): + """ + Test 'align' (negative) + Wrong place + """ td = {'first': {'placement': {'after': 'start'}, 'size': 10000}, 'second': {'placement': {'after': 'first'}, 'align': {'start': 4000}, 'size': 1000}, 'app': {'region': 'flash_primary'}} s, sub_partitions = resolve(td, 'app') - try: + with pytest.raises(PartitionError): set_addresses_and_align(td, sub_partitions, s, 100000, 'app') - except PartitionError: - pass - else: - assert False, "Should have raised an error." - # Test 'align_next' (negative) - # Wrong place + +def test_align_next_wrong_place_2(): + """ + Test 'align_next' (negative) + Wrong place + """ td = {'first': {'placement': {'after': 'start'}, 'align_next': 4000, 'size': 10000}, 'second': {'placement': {'after': 'first'}, 'size': 1000}, 'app': {'region': 'flash_primary'}} s, sub_partitions = resolve(td, 'app') - try: + with pytest.raises(PartitionError): set_addresses_and_align(td, sub_partitions, s, 100000, 'app') - except PartitionError: - pass - else: - assert False, "Should have raised an error." - # 'align' already exists (end) + +def test_align_already_exists_end(): + """'align' already exists (end)""" td = {'first': {'placement': {'after': 'start', 'align_next': 4000}, 'size': 10000}, 'second': {'placement': {'after': 'first', 'align': {'end': 2000}}, 'size': 1000}, 'app': {'region': 'flash_primary'}} s, sub_partitions = resolve(td, 'app') - try: + with pytest.raises(PartitionError): set_addresses_and_align(td, sub_partitions, s, 100000, 'app') - except PartitionError: - pass - else: - assert False, "Should have raised an error." - # Test 'align_next' (negative) - # 'align' already exists (start - not divisible) + +def test_align_already_exists_start_not_divisible(): + """ + Test 'align_next' (negative) + 'align' already exists (start - not divisible) + """ td = {'first': {'placement': {'after': 'start', 'align_next': 4000}, 'size': 10000}, 'second': {'placement': {'after': 'first', 'align': {'start': 3000}}, 'size': 1000}, 'app': {'region': 'flash_primary'}} s, sub_partitions = resolve(td, 'app') - try: + with pytest.raises(PartitionError): set_addresses_and_align(td, sub_partitions, s, 100000, 'app') - except PartitionError: - pass - else: - assert False, "Should have raised an error." - - -# FLASH (0x100000): -# +------------------------------------------+ -# | 0x0: b0 (0x8000) | -# +---0x8000: s0 (0xc200)--------------------+ -# | 0x8000: s0_pad (0x200) | -# +---0x8200: s0_image (0xc000)--------------+ -# | 0x8200: mcuboot (0xc000) | -# | 0x14200: EMPTY_0 (0xe00) | -# +---0x15000: s1 (0xc200)-------------------+ -# | 0x15000: s1_pad (0x200) | -# | 0x15200: s1_image (0xc000) | -# | 0x21200: EMPTY_1 (0xe00) | -# +---0x22000: mcuboot_primary (0x5d000)-----+ -# | 0x22000: mcuboot_pad (0x200) | -# +---0x22200: mcuboot_primary_app (0x5ce00)-+ -# | 0x22200: app (0x5ce00) | -# | 0x7f000: mcuboot_secondary (0x5d000) | -# | 0xdc000: EMPTY_2 (0x1000) | -# | 0xdd000: mcuboot_scratch (0x1e000) | -# | 0xfb000: mcuboot_storage (0x4000) | -# | 0xff000: provision (0x1000) | -# +------------------------------------------+ + + +def test_that_alignment_works_with_partition_which_shares_size_with_app(): + # FLASH (0x100000): + # +------------------------------------------+ + # | 0x0: b0 (0x8000) | + # +---0x8000: s0 (0xc200)--------------------+ + # | 0x8000: s0_pad (0x200) | + # +---0x8200: s0_image (0xc000)--------------+ + # | 0x8200: mcuboot (0xc000) | + # | 0x14200: EMPTY_0 (0xe00) | + # +---0x15000: s1 (0xc200)-------------------+ + # | 0x15000: s1_pad (0x200) | + # | 0x15200: s1_image (0xc000) | + # | 0x21200: EMPTY_1 (0xe00) | + # +---0x22000: mcuboot_primary (0x5d000)-----+ + # | 0x22000: mcuboot_pad (0x200) | + # +---0x22200: mcuboot_primary_app (0x5ce00)-+ + # | 0x22200: app (0x5ce00) | + # | 0x7f000: mcuboot_secondary (0x5d000) | + # | 0xdc000: EMPTY_2 (0x1000) | + # | 0xdd000: mcuboot_scratch (0x1e000) | + # | 0xfb000: mcuboot_storage (0x4000) | + # | 0xff000: provision (0x1000) | + # +------------------------------------------+ # Verify that alignment works with partition which shares size with app. td = {'b0': {'placement': {'after': 'start'}, 'size': 0x8000}, @@ -581,11 +675,15 @@ def test_partition_manager(): expect_addr_size(td, 'EMPTY_0', 0x14200, 0xe00) expect_addr_size(td, 'EMPTY_1', 0x21200, 0xe00) expect_addr_size(td, 'EMPTY_2', 0xdc000, 0x1000) - expect_addr_size(td, 's0_and_s1', 0x8000, 0xc200 + 0xc200 + 0xe00) # Check that size includes EMPTY_0 + expect_addr_size(td, 's0_and_s1', 0x8000, 0xc200 + 0xc200 + 0xe00) # Check that size includes EMPTY_0 assert td['mcuboot_secondary']['size'] == td['mcuboot_primary']['size'] - # Verify that if a partition X uses 'share_size' with a non-existing partition, then partition X is given size 0, - # and is hence not created. + +def test_that_if_partition_X_uses_share_size_with_non_existing_partition_size_is_0(): + """ + Verify that if a partition X uses 'share_size' with a non-existing partition, + then partition X is given size 0, and is hence not created. + """ td = {'should_not_exist': {'placement': {'before': 'exists'}, 'share_size': 'does_not_exist'}, 'exists': {'placement': {'before': 'app'}, 'size': 100}, 'app': {}} @@ -594,8 +692,12 @@ def test_partition_manager(): set_sub_partition_address_and_size(td, sub_partitions) assert 'should_not_exist' not in td - # Verify that if a partition X uses 'share_size' with a non-existing partition, but has set a default size, - # then partition X is created with the default size. + +def test_that_if_partition_X_uses_share_size_with_non_existing_partition_size_is_default(): + """ + Verify that if a partition X uses 'share_size' with a non-existing partition, + but has set a default size, then partition X is created with the default size. + """ td = {'should_exist': {'placement': {'before': 'exists'}, 'share_size': 'does_not_exist', 'size': 200}, 'exists': {'placement': {'before': 'app'}, 'size': 100}, 'app': {}} @@ -605,6 +707,8 @@ def test_partition_manager(): calculate_end_address(td) expect_addr_size(td, 'should_exist', 0, 200) + +def test_that_if_partition_X_uses_share_size_with_non_existing_partition_2(): td = {'spm': {'placement': {'before': ['app']}, 'size': 100}, 'mcuboot': {'placement': {'before': ['spm', 'app']}, 'size': 200}, 'app': {}} @@ -616,6 +720,8 @@ def test_partition_manager(): expect_addr_size(td, 'spm', 200, None) expect_addr_size(td, 'app', 300, 700) + +def test_calculate_end_address_1(): td = {'spm': {'placement': {'before': ['app']}, 'size': 100, 'inside': ['mcuboot_slot0']}, 'mcuboot': {'placement': {'before': ['spm', 'app']}, 'size': 200}, 'mcuboot_slot0': {'span': ['app']}, @@ -629,6 +735,8 @@ def test_partition_manager(): expect_addr_size(td, 'app', 300, 700) expect_addr_size(td, 'mcuboot_slot0', 200, 800) + +def test_calculate_end_address_2(): td = {'spm': {'placement': {'before': 'app'}, 'size': 100, 'inside': 'mcuboot_slot0'}, 'mcuboot': {'placement': {'before': 'app'}, 'size': 200}, 'mcuboot_pad': {'placement': {'after': 'mcuboot'}, 'inside': 'mcuboot_slot0', 'size': 10}, @@ -651,6 +759,8 @@ def test_partition_manager(): expect_addr_size(td, 'mcuboot_pad', 200, 10) expect_addr_size(td, 'mcuboot_data', 400, 200) + +def test_calculate_end_address_3(): td = {'spm': {'placement': {'before': ['app']}, 'size': 100, 'inside': ['mcuboot_slot0']}, 'mcuboot': {'placement': {'before': ['app']}, 'size': 200}, 'mcuboot_pad': {'placement': {'after': ['mcuboot']}, 'inside': ['mcuboot_slot0'], 'size': 10}, @@ -673,6 +783,8 @@ def test_partition_manager(): expect_addr_size(td, 'mcuboot_pad', 200, 10) expect_addr_size(td, 'mcuboot_data', 400, 200) + +def test_calculate_end_address_4(): td = { 'e': {'placement': {'before': ['app']}, 'size': 100}, 'a': {'placement': {'before': ['b']}, 'size': 100}, @@ -704,6 +816,8 @@ def test_partition_manager(): expect_addr_size(td, 'j', 980, None) expect_addr_size(td, 'k', 960, 40) + +def test_set_addresses_and_align_1(): td = {'mcuboot': {'placement': {'before': ['app', 'spu']}, 'size': 200}, 'b0': {'placement': {'before': ['mcuboot', 'app']}, 'size': 100}, 'app': {}} @@ -714,6 +828,8 @@ def test_partition_manager(): expect_addr_size(td, 'mcuboot', 100, None) expect_addr_size(td, 'app', 300, 700) + +def test_set_addresses_and_align_2(): td = {'b0': {'placement': {'before': ['mcuboot', 'app']}, 'size': 100}, 'app': {}} s, _ = resolve(td, 'app') set_addresses_and_align(td, {}, s, 1000, 'app') @@ -721,6 +837,8 @@ def test_partition_manager(): expect_addr_size(td, 'b0', 0, None) expect_addr_size(td, 'app', 100, 900) + +def test_set_addresses_and_align_3(): td = {'spu': {'placement': {'before': ['app']}, 'size': 100}, 'mcuboot': {'placement': {'before': ['spu', 'app']}, 'size': 200}, 'app': {}} @@ -731,6 +849,8 @@ def test_partition_manager(): expect_addr_size(td, 'spu', 200, None) expect_addr_size(td, 'app', 300, 700) + +def test_set_addresses_and_align_4(): td = {'provision': {'placement': {'before': ['end']}, 'size': 100}, 'mcuboot': {'placement': {'before': ['spu', 'app']}, 'size': 100}, 'b0': {'placement': {'before': ['mcuboot', 'app']}, 'size': 50}, @@ -745,7 +865,9 @@ def test_partition_manager(): expect_addr_size(td, 'app', 250, 650) expect_addr_size(td, 'provision', 900, None) - # Test #1 for removal of empty container partitions. + +def test_removal_of_empty_container_partitions_1(): + """Test #1 for removal of empty container partitions.""" td = {'a': {'share_size': 'does_not_exist'}, # a should be removed 'b': {'span': 'a'}, # b through d should be removed because a is removed 'c': {'span': 'b'}, @@ -755,17 +877,21 @@ def test_partition_manager(): expect_list(['e', 'app'], s) expect_list([], sub) - # Test #2 for removal of empty container partitions. - td = {'a': {'share_size': 'does_not_exist'}, # a should be removed - 'b': {'span': 'a'}, # b should not be removed, since d is placed inside it. + +def test_removal_of_empty_container_partitions_2(): + """Test #2 for removal of empty container partitions.""" + td = {'a': {'share_size': 'does_not_exist'}, # a should be removed + 'b': {'span': 'a'}, # b should not be removed, since d is placed inside it. 'c': {'placement': {'after': ['start']}}, 'd': {'inside': ['does_not_exist', 'b'], 'placement': {'after': ['c']}}} s, sub = resolve(td, 'app') expect_list(['c', 'd', 'app'], s) expect_list(['b'], sub) - expect_list(['d'], sub['b']['orig_span']) # Backup must contain edits. + expect_list(['d'], sub['b']['orig_span']) # Backup must contain edits. - # Verify that ambiguous requirements are resolved + +def test_that_ambiguous_requirements_are_resolved(): + """Verify that ambiguous requirements are resolved""" td = { '2': {'placement': {'before': ['end']}}, '1': {'placement': {'before': ['end']}}, @@ -774,32 +900,30 @@ def test_partition_manager(): s, _ = resolve(td, 'app') expect_list(['app', '1', '2'], s) - # Verify that partitions cannot be placed after end. + +def test_verify_that_partitions_cannot_be_placed_after_end(): + """Verify that partitions cannot be placed after end.""" td = { '2': {'placement': {'before': ['end']}}, '1': {'placement': {'after': ['end']}}, 'app': {'region': 'flash_primary'} } - try: + with pytest.raises(PartitionError): resolve(td, 'app') - except PartitionError: - failed = True - assert failed - # Verify that partitions cannot be placed before start. +def test_that_partitions_cannot_be_placed_before_start(): + """Verify that partitions cannot be placed before start.""" td = { '2': {'placement': {'before': ['start']}}, '1': {'placement': {'before': ['end']}}, 'app': {'region': 'flash_primary'} } - try: + with pytest.raises(PartitionError): resolve(td, 'app') - except PartitionError: - failed = True - assert failed +def test_that_partitions_cannot_be_placed_before_start_2(): td = { '6': {'placement': {'after': ['3']}}, '4': {'placement': {'after': ['3']}}, @@ -812,7 +936,9 @@ def test_partition_manager(): s, _ = resolve(td, 'app') expect_list(['1', '2', '3', '4', '5', '6', 'app'], s) - # Verify aligning dynamic partitions + +def test_aligning_dynamic_partitions_align_end(): + """Verify aligning dynamic partitions""" # Align end td = {'b0': {'address': 0, 'size': 1000}, @@ -825,6 +951,8 @@ def test_partition_manager(): assert empty_partition_address == 1600 assert empty_partition_size == 400 + +def test_aligning_dynamic_partitions_align_start(): # Align start td = {'b0': {'address': 0, 'size': 1000}, 'app': {'address': 1000, 'size': 500}, @@ -836,6 +964,8 @@ def test_partition_manager(): assert empty_partition_address == 1200 assert empty_partition_size == 800 + +def test_aligning_dynamic_partitions_align_start_greater_then_2_dynamic_partitions(): # Align start, > 2 dynamic partitions td = {'b0': {'address': 0, 'size': 1000}, 'app': {'address': 1000, 'size': 500}, @@ -851,7 +981,9 @@ def test_partition_manager(): assert empty_partition_address == 2600 assert empty_partition_size == 400 - # Align end, > 2 dynamic partitions + +def test_align_end_greater_then_2_dynamic_partitions(): + """Align end, > 2 dynamic partitions""" td = {'b0': {'address': 0, 'size': 1000}, 'app': {'address': 1000, 'size': 500}, 'share1': {'address': 1500, 'size': 500}, @@ -866,34 +998,33 @@ def test_partition_manager(): assert empty_partition_address == 2600 assert empty_partition_size == 400 - # Invalid alignment attempt, move size is too big. (move size is 600 and - # size of app is 500) + +def test_invalid_alignment_attempt_move_size_is_too_big(): + """ + Invalid alignment attempt, move size is too big. + (move size is 600 and size of app is 500) + """ td = {'b0': {'address': 0, 'size': 1000}, 'app': {'address': 1000, 'size': 500}, 'share1': {'address': 1500, 'size': 500}} - failed = False - try: + with pytest.raises(PartitionError): get_empty_part_to_move_dyn_part(['app', 'share1'], 'share1', td, 600, move_end=False, solution=['app', 'share1']) - except PartitionError: - failed = True - assert failed - # Invalid alignment attempt, reduction size is not word aligned. - failed = False +def test_Invalid_alignment_attempt_reduction_size_is_not_word_aligned(): + """Invalid alignment attempt, reduction size is not word aligned.""" td = {'b0': {'address': 0, 'size': 1000}, 'app': {'address': 1000, 'size': 500}, 'share1': {'address': 1500, 'size': 500}} - try: + with pytest.raises(PartitionError): get_empty_part_to_move_dyn_part(['app', 'share1'], 'share1', td, 333, move_end=False, solution=['app', 'share1']) - except PartitionError: - failed = True - assert failed + +def test_sort_regions_1(): td = {'first': {'region': 'region1'}, 'second': {'region': 'region1', 'share_size': 'first'}, 'third': {'region': 'region2', 'share_size': 'second'}, @@ -904,8 +1035,9 @@ def test_partition_manager(): sorted_regions = sort_regions(td, regions) assert list(sorted_regions.keys()) == ['region1', 'region3', 'region2'] + +def test_sort_regions_2(): # dependency loop - failed = False td = {'first': {'region': 'region1'}, 'second': {'region': 'region1', 'share_size': 'third'}, 'third': {'region': 'region2', 'share_size': 'second'}, @@ -913,12 +1045,12 @@ def test_partition_manager(): regions = {'region1': None, 'region2': None, 'region3': None} - try: - sorted_regions = sort_regions(td, regions) - except PartitionError: - failed = True - assert failed + with pytest.raises(PartitionError): + sort_regions(td, regions) + + +def test_sort_regions_3(): td = {'first': {'region': 'region1', 'share_size': 'third'}, 'second': {'region': 'region1', 'share_size': 'fourth'}, 'third': {'region': 'region2'}, @@ -935,7 +1067,8 @@ def test_partition_manager(): sorted_regions = sort_regions(td, regions) assert list(sorted_regions.keys()) == ['region2', 'region5', 'region4', 'region3', 'region1'] - # Regression test for alignment spec being deleted (NCSIDB-1032) + +def test_alignment_spec_being_deleted(): # (NCSIDB-1032) td = yaml.safe_load(StringIO(""" tfm: placement: {before: [app]}