From: Brendan Higgins <brendanhiggins@xxxxxxxxxx> Add parameter matcher builder for matching struct values. Signed-off-by: Brendan Higgins <brendanhiggins@xxxxxxxxxx> Signed-off-by: Daniel Latypov <dlatypov@xxxxxxxxxx> --- include/kunit/mock.h | 58 +++++++++++++++++++ lib/kunit/common-mocks.c | 117 +++++++++++++++++++++++++++++++++++++++ lib/kunit/mock-test.c | 40 +++++++++++++ 3 files changed, 215 insertions(+) diff --git a/include/kunit/mock.h b/include/kunit/mock.h index 43f797c7e8bb..4a03f0627afa 100644 --- a/include/kunit/mock.h +++ b/include/kunit/mock.h @@ -758,8 +758,18 @@ struct mock_param_matcher *kunit_ptr_ge(struct kunit *test, void *expected); struct mock_param_matcher *kunit_memeq(struct kunit *test, const void *buf, size_t size); + struct mock_param_matcher *kunit_streq(struct kunit *test, const char *str); +struct mock_param_matcher *kunit_str_contains(struct kunit *test, + const char *needle); + +/* Matches var-arg arguments. */ +struct mock_param_matcher *kunit_va_format_cmp( + struct kunit *test, + struct mock_param_matcher *fmt_matcher, + struct mock_param_matcher *va_matcher); + struct mock_action *kunit_u8_return(struct kunit *test, u8 ret); struct mock_action *kunit_u16_return(struct kunit *test, u16 ret); struct mock_action *kunit_u32_return(struct kunit *test, u32 ret); @@ -778,4 +788,52 @@ struct mock_action *kunit_ulonglong_return(struct kunit *test, unsigned long long ret); struct mock_action *kunit_ptr_return(struct kunit *test, void *ret); +/** + * struct mock_struct_matcher_entry - composed with other &struct + * mock_struct_matcher_entry to make a + * &struct struct_matcher + * @member_offset: offset of this member + * @matcher: matcher for this particular member + * + * This is used for struct_cmp() matchers. + */ +struct mock_struct_matcher_entry { + size_t member_offset; + struct mock_param_matcher *matcher; +}; + +static inline void init_mock_struct_matcher_entry_internal( + struct mock_struct_matcher_entry *entry, + size_t offset, + struct mock_param_matcher *matcher) +{ + entry->member_offset = offset; + entry->matcher = matcher; +} + +/** + * INIT_MOCK_STRUCT_MATCHER_ENTRY() + * @entry: the &struct mock_struct_matcher_entry to initialize + * @type: the struct being matched + * @member: the member of the struct being matched, used to calculate the offset + * @matcher: matcher to match that member + * + * Initializes ``entry`` to match ``type->member`` with ``matcher``. + */ +#define INIT_MOCK_STRUCT_MATCHER_ENTRY(entry, type, member, matcher) \ + init_mock_struct_matcher_entry_internal(entry, \ + offsetof(type, member),\ + matcher) + +static inline void INIT_MOCK_STRUCT_MATCHER_ENTRY_LAST( + struct mock_struct_matcher_entry *entry) +{ + entry->matcher = NULL; +} + +struct mock_param_matcher *kunit_struct_cmp( + struct kunit *test, + const char *struct_name, + struct mock_struct_matcher_entry *entries); + #endif /* _KUNIT_MOCK_H */ diff --git a/lib/kunit/common-mocks.c b/lib/kunit/common-mocks.c index 1f755f26cf5f..202efd3b8cad 100644 --- a/lib/kunit/common-mocks.c +++ b/lib/kunit/common-mocks.c @@ -228,6 +228,123 @@ struct mock_param_matcher *kunit_streq(struct kunit *test, const char *str) return &matcher->matcher; } +struct mock_str_contains_matcher { + struct mock_param_matcher matcher; + const char *needle; +}; + +static bool match_str_contains(struct mock_param_matcher *pmatcher, + struct kunit_stream *stream, + const void *phaystack) +{ + struct mock_str_contains_matcher *matcher = + container_of(pmatcher, + struct mock_str_contains_matcher, + matcher); + const char *haystack = CONVERT_TO_ACTUAL_TYPE(const char *, phaystack); + bool matches = strstr(haystack, matcher->needle); + + if (matches) + kunit_stream_add(stream, + "'%s' found in '%s'", + matcher->needle, + haystack); + else + kunit_stream_add(stream, + "'%s' not found in '%s'", + matcher->needle, + haystack); + return matches; +} + +struct mock_param_matcher *kunit_str_contains(struct kunit *test, + const char *str) +{ + struct mock_str_contains_matcher *matcher; + + matcher = kunit_kzalloc(test, sizeof(*matcher), GFP_KERNEL); + if (!matcher) + return NULL; + + matcher->matcher.match = match_str_contains; + matcher->needle = str; + + return &matcher->matcher; +} + +struct mock_param_matcher *kunit_va_format_cmp( + struct kunit *test, + struct mock_param_matcher *fmt_matcher, + struct mock_param_matcher *va_matcher) +{ + struct mock_struct_matcher_entry *entries; + + entries = kunit_kzalloc(test, sizeof(*entries) * 3, GFP_KERNEL); + if (!entries) + return NULL; + + INIT_MOCK_STRUCT_MATCHER_ENTRY(&entries[0], + struct va_format, + fmt, + fmt_matcher); + INIT_MOCK_STRUCT_MATCHER_ENTRY(&entries[1], + struct va_format, + va, + va_matcher); + INIT_MOCK_STRUCT_MATCHER_ENTRY_LAST(&entries[2]); + + return kunit_struct_cmp(test, "va_format", entries); +} + +struct mock_struct_matcher { + struct mock_param_matcher matcher; + const char *struct_name; + struct mock_struct_matcher_entry *entries; +}; + +static bool match_struct(struct mock_param_matcher *pmatcher, + struct kunit_stream *stream, + const void *pactual) +{ + struct mock_struct_matcher *matcher = + container_of(pmatcher, + struct mock_struct_matcher, + matcher); + struct mock_struct_matcher_entry *entry; + const char *actual = CONVERT_TO_ACTUAL_TYPE(const char *, pactual); + const char *member_ptr; + bool matches = true, tmp; + + kunit_stream_add(stream, "struct %s {", matcher->struct_name); + for (entry = matcher->entries; entry->matcher; entry++) { + member_ptr = actual + entry->member_offset; + tmp = entry->matcher->match(entry->matcher, stream, member_ptr); + matches = matches && tmp; + kunit_stream_add(stream, ", "); + } + kunit_stream_add(stream, "}"); + + return matches; +} + +struct mock_param_matcher *kunit_struct_cmp( + struct kunit *test, + const char *struct_name, + struct mock_struct_matcher_entry *entries) +{ + struct mock_struct_matcher *matcher; + + matcher = kunit_kzalloc(test, sizeof(*matcher), GFP_KERNEL); + if (!matcher) + return NULL; + + matcher->matcher.match = match_struct; + matcher->struct_name = struct_name; + matcher->entries = entries; + + return &matcher->matcher; +} + #define DEFINE_RETURN_ACTION_STRUCT(type_name, type) \ struct mock_##type_name##_action { \ struct mock_action action; \ diff --git a/lib/kunit/mock-test.c b/lib/kunit/mock-test.c index 8a0fa33d087c..df0969b43ade 100644 --- a/lib/kunit/mock-test.c +++ b/lib/kunit/mock-test.c @@ -243,6 +243,45 @@ static void mock_test_do_expect_default_return(struct kunit *test) KUNIT_EXPECT_EQ(test, 0, expectation->times_called); } +/* + * Method called on naggy mock with no expectations will not fail, but will show + * a warning message + */ +static void mock_test_naggy_no_expectations_no_fail(struct kunit *test) +{ + struct mock_test_context *ctx = test->priv; + struct kunit *failing_test = ctx->failing_test; + struct mock *mock = ctx->mock; + + int param0 = 5, param1 = -5; + static const char * const two_param_types[] = {"int", "int"}; + const void *two_params[] = {¶m0, ¶m1}; + + mock_set_default_action(mock, + "add", + real_add, + kunit_int_return(failing_test, -4)); + + + KUNIT_EXPECT_CALL(mock_add( + mock, + kunit_any(failing_test), + kunit_va_format_cmp(failing_test, + kunit_str_contains(failing_test, + "Method was called with no expectations declared"), + kunit_any(failing_test)))); + + mock->do_expect(mock, + "add", + real_add, + two_param_types, + two_params, + ARRAY_SIZE(two_params)); + mock_validate_expectations(mock); + + KUNIT_EXPECT_FALSE(test, failing_test->success); +} + static void mock_test_mock_validate_expectations(struct kunit *test) { struct mock_test_context *ctx = test->priv; @@ -307,6 +346,7 @@ static struct kunit_case mock_test_cases[] = { KUNIT_CASE(mock_test_failed_expect_call_fails_test), KUNIT_CASE(mock_test_do_expect_default_return), KUNIT_CASE(mock_test_mock_validate_expectations), + KUNIT_CASE(mock_test_naggy_no_expectations_no_fail), {} }; -- 2.28.0.681.g6f77f65b4e-goog