Hello Patrick Georgi,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/coreboot/+/41046
to review the following change.
Change subject: tests: Add region-test for rdev API ......................................................................
tests: Add region-test for rdev API
This patch adds a basic test for the common region and region_device APIs, sanity checking the basic functions and things like overflow-handling. There is certainly more that could be added here, but it's a start.
Signed-off-by: Julius Werner jwerner@chromium.org Change-Id: I4932402f54768557e5b22b16e66220bd90ddebfd --- A tests/commonlib/Makefile.inc A tests/commonlib/region-test.c 2 files changed, 288 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/46/41046/1
diff --git a/tests/commonlib/Makefile.inc b/tests/commonlib/Makefile.inc new file mode 100644 index 0000000..128faf8 --- /dev/null +++ b/tests/commonlib/Makefile.inc @@ -0,0 +1,17 @@ +## +## This file is part of the coreboot project. +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; version 2 of the License. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +tests-y += region-test + +region-test-srcs += tests/commonlib/region-test.c +region-test-srcs += src/commonlib/region.c diff --git a/tests/commonlib/region-test.c b/tests/commonlib/region-test.c new file mode 100644 index 0000000..e009446 --- /dev/null +++ b/tests/commonlib/region-test.c @@ -0,0 +1,271 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* This file is part of the coreboot project. */ + +#include <commonlib/region.h> + +static void test_region(void **state) +{ + struct region outer = { .offset = 0x200000000, .size = 0x400000000 }; + assert_int_equal(region_offset(&outer), 0x200000000); + assert_int_equal(region_sz(&outer), 0x400000000); + assert_int_equal(region_end(&outer), 0x600000000); + + struct region inner = { .offset = 0x250000000, .size = 0x390000000 }; + assert_true(region_is_subregion(&outer, &inner)); + + struct region touching_bottom = { .offset = 0x200000000, .size = 0x100000000 }; + assert_true(region_is_subregion(&outer, &touching_bottom)); + + struct region touching_top = { .offset = 0x390000000, .size = 0x10000000 }; + assert_true(region_is_subregion(&outer, &touching_top)); + + struct region overlap_bottom = { .offset = 0x1f0000000, .size = 0x20000000 }; + assert_false(region_is_subregion(&outer, &overlap_bottom)); + + struct region overlap_top = { .offset = 0x5f0000000, .size = 0x20000000 }; + assert_false(region_is_subregion(&outer, &overlap_top)); + + struct region below = { .offset = 0x0, .size = 0x100000000 }; + assert_false(region_is_subregion(&outer, &below)); + + struct region above = { .offset = 0xffffffff00000000, .size = 0x100000000 }; + assert_false(region_is_subregion(&outer, &above)); +} + +static void *mock_mmap(const struct region_device *rdev, size_t offset, size_t size) +{ + check_expected_ptr(rdev); + check_expected(offset); + check_expected(size); + + return mock_ptr_type(void *); +} + +static int mock_unmap(const struct region_device *rdev, void * mapping) +{ + check_expected_ptr(rdev); + check_expected_ptr(mapping); + + return mock(); +} + +static ssize_t mock_readat(const struct region_device *rdev, void *buffer, + size_t offset, size_t size) +{ + check_expected_ptr(rdev); + check_expected_ptr(buffer); + check_expected(offset); + check_expected(size); + + ssize_t ret = mock(); + if (!ret) + return size; +} + +static ssize_t mock_writeat(const struct region_device *rdev, const void *buffer, + size_t offset, size_t size) +{ + check_expected_ptr(rdev); + check_expected_ptr(buffer); + check_expected(offset); + check_expected(size); + + ssize_t ret = mock(); + if (!ret) + return size; +} + +static ssize_t mock_eraseat(const struct region_device *rdev, size_t offset, size_t size) +{ + check_expected_ptr(rdev); + check_expected(offset); + check_expected(size); + + ssize_t ret = mock(); + if (!ret) + return size; +} + +struct region_device_ops mock_rdev_ops = { + .mmap = mock_mmap, + .munmap = mock_unmap, + .readat = mock_readat, + .writeat = mock_writeat, + .eraseat = mock_eraseat, +}; + +struct region_device mock_rdev = REGION_DEV_INIT(&mock_rdev_ops, 0, 0xffffffffffffffff); +void *mmap_result = (void *)0x12345678; +const size_t mock_size = 256; +u8 mock_buffer[256]; + +static void test_rdev_basics(void **state) +{ + assert_int_equal(region_device_offset(&mock_rdev), 0); + assert_int_equal(region_device_sz(&mock_rdev), 0xffffffffffffffff); + assert_int_equal(region_device_end(&mock_rdev), 0xffffffffffffffff); +} + +/* + * This function sets up defaults for the mock_rdev_ops functions so we don't have to explicitly + * mock every parameter every time. cmocka is kinda dumb for this sort of use case and doesn't + * let you override these anymore once they're set (because these are stored as queues, not + * stacks, and once you store an "infinite" element the test can never proceed behind it), so + * tests will always have to enqueue any custom values they may need for the rest of the test + * function before calling this. + */ +static void rdev_mock_defaults(void) +{ + will_return_maybe(mock_mmap, mmap_result); + will_return_maybe(mock_unmap, 0); + will_return_maybe(mock_readat, 0); + will_return_maybe(mock_writeat, 0); + will_return_maybe(mock_eraseat, 0); + + expect_value_count(mock_mmap, rdev, &mock_rdev, -2); + expect_value_count(mock_unmap, rdev, &mock_rdev, -2); + expect_value_count(mock_readat, rdev, &mock_rdev, -2); + expect_value_count(mock_writeat, rdev, &mock_rdev, -2); + expect_value_count(mock_eraseat, rdev, &mock_rdev, -2); + + expect_value_count(mock_readat, buffer, &mock_buffer, -2); + expect_value_count(mock_writeat, buffer, &mock_buffer, -2); + + expect_value_count(mock_mmap, offset, 0, -2); + expect_value_count(mock_readat, offset, 0, -2); + expect_value_count(mock_writeat, offset, 0, -2); + expect_value_count(mock_eraseat, offset, 0, -2); + + expect_value_count(mock_mmap, size, mock_size, -2); + expect_value_count(mock_readat, size, mock_size, -2); + expect_value_count(mock_writeat, size, mock_size, -2); + expect_value_count(mock_eraseat, size, mock_size, -2); + + expect_value_count(mock_unmap, mapping, mmap_result, -2); +} + +static void test_rdev_success(void **state) +{ + struct region_device child; + + expect_value(mock_mmap, size, region_device_sz(&mock_rdev)); + + rdev_mock_defaults(); + + assert_ptr_equal(rdev_mmap_full(&mock_rdev), mmap_result); + + assert_ptr_equal(rdev_mmap(&mock_rdev, 0, mock_size), mmap_result); + assert_int_equal(rdev_munmap(&mock_rdev, mmap_result), 0); + assert_int_equal(rdev_readat(&mock_rdev, mock_buffer, 0, mock_size), mock_size); + assert_int_equal(rdev_writeat(&mock_rdev, mock_buffer, 0, mock_size), mock_size); + assert_int_equal(rdev_eraseat(&mock_rdev, 0, mock_size), mock_size); +} + +static void test_rdev_failure(void **state) +{ + will_return(mock_mmap, NULL); + will_return(mock_unmap, -1); + will_return(mock_readat, -1); + will_return(mock_writeat, -1); + will_return(mock_eraseat, -1); + + rdev_mock_defaults(); + + assert_ptr_equal(rdev_mmap(&mock_rdev, 0, mock_size), NULL); + assert_int_equal(rdev_munmap(&mock_rdev, mmap_result), -1); + assert_int_equal(rdev_readat(&mock_rdev, mock_buffer, 0, mock_size), -1); + assert_int_equal(rdev_writeat(&mock_rdev, mock_buffer, 0, mock_size), -1); + assert_int_equal(rdev_eraseat(&mock_rdev, 0, mock_size), -1); +} + +static void test_rdev_wrap(void **state) +{ + struct region_device child; + const size_t wrap_offs = 0xffffffff00000000; + const size_t wrap_size = 0x200000000; + const size_t fit_size = 0xffffffff; + + /* For the 'wrap' cases, the underlying rdev_ops aren't even called, so only add + expectations for the 'fit' cases. */ + + expect_value(mock_mmap, offset, wrap_offs); + expect_value(mock_readat, offset, wrap_offs); + expect_value(mock_writeat, offset, wrap_offs); + expect_value(mock_eraseat, offset, wrap_offs); + + expect_value(mock_mmap, size, fit_size); + expect_value(mock_readat, size, fit_size); + expect_value(mock_writeat, size, fit_size); + expect_value(mock_eraseat, size, fit_size); + + rdev_mock_defaults(); + + assert_ptr_equal(rdev_mmap(&mock_rdev, wrap_offs, wrap_size), NULL); + assert_int_equal(rdev_readat(&mock_rdev, mock_buffer, wrap_offs, wrap_size), -1); + assert_int_equal(rdev_writeat(&mock_rdev, mock_buffer, wrap_offs, wrap_size), -1); + assert_int_equal(rdev_eraseat(&mock_rdev, wrap_offs, wrap_size), -1); + assert_int_equal(rdev_chain(&child, &mock_rdev, wrap_offs, wrap_size), -1); + + assert_ptr_equal(rdev_mmap(&mock_rdev, wrap_offs, fit_size), mmap_result); + assert_int_equal(rdev_readat(&mock_rdev, mock_buffer, wrap_offs, fit_size), fit_size); + assert_int_equal(rdev_writeat(&mock_rdev, mock_buffer, wrap_offs, fit_size), fit_size); + assert_int_equal(rdev_eraseat(&mock_rdev, wrap_offs, fit_size), fit_size); + assert_int_equal(rdev_chain(&child, &mock_rdev, wrap_offs, fit_size), 0); +} + +static void test_rdev_chain(void **state) +{ + struct region_device child; + const size_t child_offs = 0x200000000; + const size_t child_size = 0x100000000; + const size_t offs = 0x10000; + const size_t ovrflw_size = child_size - offs + 0x8000; + + expect_value(mock_mmap, offset, child_offs + offs); + expect_value(mock_readat, offset, child_offs + offs); + expect_value(mock_writeat, offset, child_offs + offs); + expect_value(mock_eraseat, offset, child_offs + offs); + + rdev_mock_defaults(); + + assert_int_equal(rdev_chain_full(&child, &mock_rdev), 0); + assert_int_equal(region_device_sz(&child), region_device_sz(&mock_rdev)); + assert_int_equal(region_device_offset(&child), region_device_offset(&mock_rdev)); + assert_int_equal(rdev_relative_offset(&mock_rdev, &child), 0); + + assert_int_equal(rdev_chain(&child, &mock_rdev, child_offs, child_size), 0); + assert_int_equal(region_device_sz(&child), child_size); + assert_int_equal(region_device_offset(&child), child_offs); + assert_int_equal(region_device_end(&child), child_offs + child_size); + assert_int_equal(rdev_relative_offset(&mock_rdev, &child), child_offs); + + assert_ptr_equal(rdev_mmap(&child, offs, mock_size), mmap_result); + assert_int_equal(rdev_munmap(&child, mmap_result), 0); + assert_int_equal(rdev_readat(&child, mock_buffer, offs, mock_size), mock_size); + assert_int_equal(rdev_writeat(&child, mock_buffer, offs, mock_size), mock_size); + assert_int_equal(rdev_eraseat(&child, offs, mock_size), mock_size); + + assert_ptr_equal(rdev_mmap(&child, offs, ovrflw_size), NULL); + assert_int_equal(rdev_readat(&child, mock_buffer, offs, ovrflw_size), -1); + assert_int_equal(rdev_writeat(&child, mock_buffer, offs, ovrflw_size), -1); + assert_int_equal(rdev_eraseat(&child, offs, ovrflw_size), -1); + + assert_ptr_equal(rdev_mmap(&child, child_size, mock_size), NULL); + assert_int_equal(rdev_readat(&child, mock_buffer, child_size, mock_size), -1); + assert_int_equal(rdev_writeat(&child, mock_buffer, child_size, mock_size), -1); + assert_int_equal(rdev_eraseat(&child, child_size, mock_size), -1); +} + +int main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_region), + cmocka_unit_test(test_rdev_basics), + cmocka_unit_test(test_rdev_success), + cmocka_unit_test(test_rdev_failure), + cmocka_unit_test(test_rdev_wrap), + cmocka_unit_test(test_rdev_chain), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +}