All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Latypov <dlatypov@google.com>
To: Brendan Higgins <brendanhiggins@google.com>,
	David Gow <davidgow@google.com>,
	Shuah Khan <skhan@linuxfoundation.org>
Cc: kunit-dev@googlegroups.com, linux-kselftest@vger.kernel.org,
	linux-kernel@vger.kernel.org, Kees Cook <keescook@chromium.org>,
	Luis Chamberlain <mcgrof@kernel.org>,
	Alan Maguire <alan.maguire@oracle.com>,
	Stephen Boyd <sboyd@kernel.org>,
	Daniel Latypov <dlatypov@google.com>
Subject: [RFC v1 11/12] kunit: mock: add struct param matcher
Date: Fri, 18 Sep 2020 11:31:13 -0700	[thread overview]
Message-ID: <20200918183114.2571146-12-dlatypov@google.com> (raw)
In-Reply-To: <20200918183114.2571146-1-dlatypov@google.com>

From: Brendan Higgins <brendanhiggins@google.com>

Add parameter matcher builder for matching struct values.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
---
 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[] = {&param0, &param1};
+
+	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


  parent reply	other threads:[~2020-09-18 18:32 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-18 18:31 [RFC v1 00/12] kunit: introduce class mocking support Daniel Latypov
2020-09-18 18:31 ` [RFC v1 01/12] Revert "kunit: move string-stream.h to lib/kunit" Daniel Latypov
2020-09-18 22:53   ` kernel test robot
2020-09-18 18:31 ` [RFC v1 02/12] kunit: test: add kunit_stream a std::stream like logger Daniel Latypov
2020-09-18 18:31 ` [RFC v1 03/12] kunit: test: add concept of post conditions Daniel Latypov
2020-09-18 18:31 ` [RFC v1 04/12] checkpatch: add support for struct MOCK(foo) syntax Daniel Latypov
2020-09-18 18:31 ` [RFC v1 05/12] kunit: mock: add parameter list manipulation macros Daniel Latypov
2020-09-18 18:31 ` [RFC v1 06/12] kunit: expose kunit_set_failure() for use by mocking Daniel Latypov
2020-09-18 18:31 ` [RFC v1 07/12] kunit: mock: add internal mock infrastructure Daniel Latypov
2020-09-18 18:31 ` [RFC v1 08/12] kunit: mock: add basic matchers and actions Daniel Latypov
2020-09-18 21:27   ` kernel test robot
2020-09-18 21:27   ` [RFC PATCH] kunit: mock: to_mock_u8_matcher can be static kernel test robot
2020-09-19  0:24   ` [RFC v1 08/12] kunit: mock: add basic matchers and actions kernel test robot
2020-09-18 18:31 ` [RFC v1 09/12] kunit: mock: add macro machinery to pick correct format args Daniel Latypov
2020-09-18 18:31 ` [RFC v1 10/12] kunit: mock: add class mocking support Daniel Latypov
2020-09-18 20:27   ` kernel test robot
2020-09-18 22:30   ` kernel test robot
2020-09-18 22:46   ` kernel test robot
2020-09-18 22:46   ` [RFC PATCH] kunit: mock: one_param can be static kernel test robot
2020-09-18 18:31 ` Daniel Latypov [this message]
2020-09-18 18:31 ` [RFC v1 12/12] kunit: mock: implement nice, strict and naggy mock distinctions Daniel Latypov
2020-09-23  0:24 ` [RFC v1 00/12] kunit: introduce class mocking support Daniel Latypov
2020-09-28 23:24   ` Brendan Higgins
2020-10-01 21:49     ` Daniel Latypov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200918183114.2571146-12-dlatypov@google.com \
    --to=dlatypov@google.com \
    --cc=alan.maguire@oracle.com \
    --cc=brendanhiggins@google.com \
    --cc=davidgow@google.com \
    --cc=keescook@chromium.org \
    --cc=kunit-dev@googlegroups.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=mcgrof@kernel.org \
    --cc=sboyd@kernel.org \
    --cc=skhan@linuxfoundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.