linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC v2 00/12] kunit: introduce class mocking support.
@ 2020-10-12 22:20 Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 01/12] Revert "kunit: move string-stream.h to lib/kunit" Daniel Latypov
                   ` (12 more replies)
  0 siblings, 13 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

# Background
KUnit currently lacks any first-class support for mocking.
For an overview and discussion on the pros and cons, see
https://martinfowler.com/articles/mocksArentStubs.html

This patch set introduces the basic machinery needed for mocking:
setting and validating expectations, setting default actions, etc.

Using that basic infrastructure, we add macros for "class mocking", as
it's probably the easiest type of mocking to start with.

## Class mocking

By "class mocking", we're referring mocking out function pointers stored
in structs like:
  struct sender {
        int (*send)(struct sender *sender, int data);
  };
or in ops structs
  struct sender {
        struct send_ops *ops; // contains `send`
  };

After the necessary DEFINE_* macros, we can then write code like
  struct MOCK(sender) mock_sender = CONSTRUCT_MOCK(sender, test);

  /* Fake an error for a specific input. */
  handle = KUNIT_EXPECT_CALL(send(<omitted>, kunit_int_eq(42)));
  handle->action = kunit_int_return(test, -EINVAL);

  /* Pass the mocked object to some code under test. */
  KUNIT_EXPECT_EQ(test, -EINVAL, send_message(...));

I.e. the goal is to make it easier to test
1) with less dependencies (we don't need to setup a real `sender`)
2) unusual/error conditions more easily.

In the future, we hope to build upon this to support mocking in more
contexts, e.g. standalone funcs, etc.

# TODOs

## Naming
This introduces a number of new macros for dealing with mocks,
e.g:
  DEFINE_STRUCT_CLASS_MOCK(METHOD(foo), CLASS(example),
                           RETURNS(int),
                           PARAMS(struct example *, int));
  ...
  KUNIT_EXPECT_CALL(foo(mock_get_ctrl(mock_example), ...);
For consistency, we could prefix everything with KUNIT, e.g.
`KUNIT_DEFINE_STRUCT_CLASS_MOCK` and `kunit_mock_get_ctrl`, but it feels
like the names might be long enough that they would hinder readability.

## Usage
For now the only use of class mocking is in kunit-example-test.c
As part of changing this from an RFC to a real patch set, we're hoping
to include at least one example.

Pointers to bits of code where this would be useful that aren't too
hairy would be appreciated.
E.g. could easily add a test for tools/perf/ui/progress.h, e.g. that
ui_progress__init() calls ui_progress_ops.init(), but that likely isn't
useful to anyone.

---
v2: 
* Pass `struct kunit *` to mock init's to allow allocating ops structs.
* Update kunit-example-test.cc to do so as a more realistic example.
v1: https://lore.kernel.org/linux-kselftest/20200918183114.2571146-1-dlatypov@google.com/
---

Brendan Higgins (9):
  kunit: test: add kunit_stream a std::stream like logger
  kunit: test: add concept of post conditions
  checkpatch: add support for struct MOCK(foo) syntax
  kunit: mock: add parameter list manipulation macros
  kunit: mock: add internal mock infrastructure
  kunit: mock: add basic matchers and actions
  kunit: mock: add class mocking support
  kunit: mock: add struct param matcher
  kunit: mock: implement nice, strict and naggy mock distinctions

Daniel Latypov (2):
  Revert "kunit: move string-stream.h to lib/kunit"
  kunit: expose kunit_set_failure() for use by mocking

Marcelo Schmitt (1):
  kunit: mock: add macro machinery to pick correct format args

 include/kunit/assert.h                 |   3 +-
 include/kunit/kunit-stream.h           |  94 +++
 include/kunit/mock.h                   | 902 +++++++++++++++++++++++++
 include/kunit/params.h                 | 305 +++++++++
 {lib => include}/kunit/string-stream.h |   2 +
 include/kunit/test.h                   |   9 +
 lib/kunit/Makefile                     |   9 +-
 lib/kunit/assert.c                     |   2 -
 lib/kunit/common-mocks.c               | 409 +++++++++++
 lib/kunit/kunit-example-test.c         |  98 +++
 lib/kunit/kunit-stream.c               | 110 +++
 lib/kunit/mock-macro-test.c            | 241 +++++++
 lib/kunit/mock-test.c                  | 531 +++++++++++++++
 lib/kunit/mock.c                       | 370 ++++++++++
 lib/kunit/string-stream-test.c         |   3 +-
 lib/kunit/string-stream.c              |   5 +-
 lib/kunit/test.c                       |  15 +-
 scripts/checkpatch.pl                  |   4 +
 18 files changed, 3099 insertions(+), 13 deletions(-)
 create mode 100644 include/kunit/kunit-stream.h
 create mode 100644 include/kunit/mock.h
 create mode 100644 include/kunit/params.h
 rename {lib => include}/kunit/string-stream.h (95%)
 create mode 100644 lib/kunit/common-mocks.c
 create mode 100644 lib/kunit/kunit-stream.c
 create mode 100644 lib/kunit/mock-macro-test.c
 create mode 100644 lib/kunit/mock-test.c
 create mode 100644 lib/kunit/mock.c


base-commit: 10b82d5176488acee2820e5a2cf0f2ec5c3488b6
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC v2 01/12] Revert "kunit: move string-stream.h to lib/kunit"
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 02/12] kunit: test: add kunit_stream a std::stream like logger Daniel Latypov
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

This reverts commit 109fb06fdc6f6788df7dfbc235f7636a38e28fd4.

string-stream will be used by kunit mocking code to print messages about
mock expectations.
It makes the code signifcantly simpler if string-stream objects can be
part of structs declared in mocking headers.

Signed-off-by: Daniel Latypov <dlatypov@google.com>
---
 include/kunit/assert.h                 | 3 +--
 {lib => include}/kunit/string-stream.h | 0
 lib/kunit/assert.c                     | 2 --
 lib/kunit/string-stream-test.c         | 3 +--
 lib/kunit/string-stream.c              | 3 +--
 lib/kunit/test.c                       | 2 +-
 6 files changed, 4 insertions(+), 9 deletions(-)
 rename {lib => include}/kunit/string-stream.h (100%)

diff --git a/include/kunit/assert.h b/include/kunit/assert.h
index ad889b539ab3..db6a0fca09b4 100644
--- a/include/kunit/assert.h
+++ b/include/kunit/assert.h
@@ -9,11 +9,10 @@
 #ifndef _KUNIT_ASSERT_H
 #define _KUNIT_ASSERT_H
 
+#include <kunit/string-stream.h>
 #include <linux/err.h>
-#include <linux/kernel.h>
 
 struct kunit;
-struct string_stream;
 
 /**
  * enum kunit_assert_type - Type of expectation/assertion.
diff --git a/lib/kunit/string-stream.h b/include/kunit/string-stream.h
similarity index 100%
rename from lib/kunit/string-stream.h
rename to include/kunit/string-stream.h
diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c
index 33acdaa28a7d..9c12e30792ba 100644
--- a/lib/kunit/assert.c
+++ b/lib/kunit/assert.c
@@ -8,8 +8,6 @@
 #include <kunit/assert.h>
 #include <kunit/test.h>
 
-#include "string-stream.h"
-
 void kunit_base_assert_format(const struct kunit_assert *assert,
 			      struct string_stream *stream)
 {
diff --git a/lib/kunit/string-stream-test.c b/lib/kunit/string-stream-test.c
index 110f3a993250..121f9ab11501 100644
--- a/lib/kunit/string-stream-test.c
+++ b/lib/kunit/string-stream-test.c
@@ -6,11 +6,10 @@
  * Author: Brendan Higgins <brendanhiggins@google.com>
  */
 
+#include <kunit/string-stream.h>
 #include <kunit/test.h>
 #include <linux/slab.h>
 
-#include "string-stream.h"
-
 static void string_stream_test_empty_on_creation(struct kunit *test)
 {
 	struct string_stream *stream = alloc_string_stream(test, GFP_KERNEL);
diff --git a/lib/kunit/string-stream.c b/lib/kunit/string-stream.c
index 141789ca8949..151a0e7ac349 100644
--- a/lib/kunit/string-stream.c
+++ b/lib/kunit/string-stream.c
@@ -6,12 +6,11 @@
  * Author: Brendan Higgins <brendanhiggins@google.com>
  */
 
+#include <kunit/string-stream.h>
 #include <kunit/test.h>
 #include <linux/list.h>
 #include <linux/slab.h>
 
-#include "string-stream.h"
-
 struct string_stream_fragment_alloc_context {
 	struct kunit *test;
 	int len;
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index c36037200310..670d1cc9c105 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -7,12 +7,12 @@
  */
 
 #include <kunit/test.h>
+#include <kunit/string-stream.h>
 #include <linux/kernel.h>
 #include <linux/kref.h>
 #include <linux/sched/debug.h>
 
 #include "debugfs.h"
-#include "string-stream.h"
 #include "try-catch-impl.h"
 
 static void kunit_set_failure(struct kunit *test)
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 02/12] kunit: test: add kunit_stream a std::stream like logger
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 01/12] Revert "kunit: move string-stream.h to lib/kunit" Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 03/12] kunit: test: add concept of post conditions Daniel Latypov
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

From: Brendan Higgins <brendanhiggins@google.com>

A lot of the expectation and assertion infrastructure prints out fairly
complicated test failure messages, so add a C++ style log library for
for logging test results called `struct kunit_stream`.

kunit_stream allows us to construct a message before we know whether we
want to print it out; this can be extremely handy if there is
information you might need for a failure message that is easiest to
collect in the steps leading up to the actual check.

Note that kunit_stream is distinct from string_stream: whereas
string_stream is really just a string builder, kunit_stream is
specifically used for constructing expectation/assertion (introduced
later in this series) messages to a user of KUnit.

Patch was previously reviewed at
https://lore.kernel.org/linux-fsdevel/20190712081744.87097-5-brendanhiggins@google.com/
Primary differences are
* kunit/ => lib/kunit/
* re-expose string_stream_clear(), which was made static since then

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
Cc: Stephen Boyd <sboyd@kernel.org>
---
 include/kunit/kunit-stream.h  |  94 +++++++++++++++++++++++++++++
 include/kunit/string-stream.h |   2 +
 include/kunit/test.h          |   1 +
 lib/kunit/Makefile            |   1 +
 lib/kunit/kunit-stream.c      | 110 ++++++++++++++++++++++++++++++++++
 lib/kunit/string-stream.c     |   2 +-
 6 files changed, 209 insertions(+), 1 deletion(-)
 create mode 100644 include/kunit/kunit-stream.h
 create mode 100644 lib/kunit/kunit-stream.c

diff --git a/include/kunit/kunit-stream.h b/include/kunit/kunit-stream.h
new file mode 100644
index 000000000000..9a768732a04b
--- /dev/null
+++ b/include/kunit/kunit-stream.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * C++ stream style string formatter and printer used in KUnit for outputting
+ * KUnit messages.
+ *
+ * Copyright (C) 2020, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins@google.com>
+ */
+
+#ifndef _KUNIT_KUNIT_STREAM_H
+#define _KUNIT_KUNIT_STREAM_H
+
+#include <linux/types.h>
+#include <kunit/string-stream.h>
+
+struct kunit;
+
+/**
+ * struct kunit_stream - a std::stream style string builder.
+ *
+ * A std::stream style string builder. Allows messages to be built up and
+ * printed all at once. Note that the intention is to only use
+ * &struct kunit_stream to communicate with a user of KUnit, most often to
+ * communicate something about an expectation or an assertion to the user. If
+ * you want a similar interface, but aren't sure if this is the right class for
+ * you to use, you probably want to use the related string_stream class, which
+ * is allowed for generic string construction in a similar manner. This class is
+ * really only for the KUnit library to communicate certain kinds of information
+ * to KUnit users and should not be used by anyone else.
+ *
+ * A note on &struct kunit_stream's usage: a kunit_stream will generally
+ * accompany *one* expectation or assertion. Multiple expectations/assertions
+ * may be validated concurrently at any given time, even within a single test
+ * case, so sharing a kunit_stream between expectations/assertions may result in
+ * unintended consequences.
+ */
+struct kunit_stream {
+	/* private: internal use only. */
+	struct kunit *test;
+	struct string_stream *internal_stream;
+};
+
+/**
+ * alloc_kunit_stream() - constructs a new &struct kunit_stream.
+ * @test: The test context object.
+ * @gfp: The GFP flags to use for internal allocations.
+ *
+ * Constructs a new test managed &struct kunit_stream.
+ */
+struct kunit_stream *alloc_kunit_stream(struct kunit *test,
+					gfp_t gfp);
+
+/**
+ * kunit_stream_add(): adds the formatted input to the internal buffer.
+ * @kstream: the stream being operated on.
+ * @fmt: printf style format string to append to stream.
+ *
+ * Appends the formatted string, @fmt, to the internal buffer.
+ */
+void __printf(2, 3) kunit_stream_add(struct kunit_stream *kstream,
+				     const char *fmt, ...);
+
+/**
+ * kunit_stream_append(): appends the contents of @other to @kstream.
+ * @kstream: the stream to which @other is appended.
+ * @other: the stream whose contents are appended to @kstream.
+ *
+ * Appends the contents of @other to @kstream.
+ */
+void kunit_stream_append(struct kunit_stream *kstream,
+			 struct kunit_stream *other);
+
+/**
+ * kunit_stream_commit(): prints out the internal buffer to the user.
+ * @kstream: the stream being operated on.
+ *
+ * Outputs the contents of the internal buffer as a kunit_printk formatted
+ * output. KUNIT_STREAM ONLY OUTPUTS ITS BUFFER TO THE USER IF COMMIT IS
+ * CALLED!!! The reason for this is that it allows us to construct a message
+ * before we know whether we want to print it out; this can be extremely handy
+ * if there is information you might need for a failure message that is easiest
+ * to collect in the steps leading up to the actual check.
+ */
+void kunit_stream_commit(struct kunit_stream *kstream);
+
+/**
+ * kunit_stream_clear(): clears the internal buffer.
+ * @kstream: the stream being operated on.
+ *
+ * Clears the contents of the internal buffer.
+ */
+void kunit_stream_clear(struct kunit_stream *kstream);
+
+#endif /* _KUNIT_KUNIT_STREAM_H */
diff --git a/include/kunit/string-stream.h b/include/kunit/string-stream.h
index fe98a00b75a9..44677c386890 100644
--- a/include/kunit/string-stream.h
+++ b/include/kunit/string-stream.h
@@ -46,6 +46,8 @@ int string_stream_append(struct string_stream *stream,
 
 bool string_stream_is_empty(struct string_stream *stream);
 
+void string_stream_clear(struct string_stream *stream);
+
 int string_stream_destroy(struct string_stream *stream);
 
 #endif /* _KUNIT_STRING_STREAM_H */
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 59f3144f009a..687782fa44d9 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -10,6 +10,7 @@
 #define _KUNIT_TEST_H
 
 #include <kunit/assert.h>
+#include <kunit/kunit-stream.h>
 #include <kunit/try-catch.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 724b94311ca3..523a7b0f9783 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_KUNIT) +=			kunit.o
 kunit-objs +=				test.o \
 					string-stream.o \
 					assert.o \
+					kunit-stream.o \
 					try-catch.o
 
 ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
diff --git a/lib/kunit/kunit-stream.c b/lib/kunit/kunit-stream.c
new file mode 100644
index 000000000000..744798693f39
--- /dev/null
+++ b/lib/kunit/kunit-stream.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * C++ stream style string formatter and printer used in KUnit for outputting
+ * KUnit messages.
+ *
+ * Copyright (C) 2020, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins@google.com>
+ */
+
+#include <kunit/test.h>
+#include <kunit/kunit-stream.h>
+#include <kunit/string-stream.h>
+
+void kunit_stream_add(struct kunit_stream *kstream, const char *fmt, ...)
+{
+	va_list args;
+	struct string_stream *stream = kstream->internal_stream;
+
+	va_start(args, fmt);
+
+	if (string_stream_vadd(stream, fmt, args))
+		kunit_err(kstream->test,
+			  "Failed to allocate fragment: %s\n",
+			  fmt);
+
+	va_end(args);
+}
+
+void kunit_stream_append(struct kunit_stream *kstream,
+			 struct kunit_stream *other)
+{
+	int ret;
+
+	ret = string_stream_append(kstream->internal_stream,
+				   other->internal_stream);
+
+	if (ret)
+		kunit_err(kstream->test,
+			  "Failed to append other stream: %d\n", ret);
+}
+
+void kunit_stream_clear(struct kunit_stream *kstream)
+{
+	string_stream_clear(kstream->internal_stream);
+}
+
+void kunit_stream_commit(struct kunit_stream *kstream)
+{
+	struct string_stream *stream = kstream->internal_stream;
+	struct string_stream_fragment *fragment;
+	struct kunit *test = kstream->test;
+	char *buf;
+
+	buf = string_stream_get_string(stream);
+	if (!buf) {
+		kunit_err(test,
+			  "Could not allocate buffer, dumping stream:\n");
+		list_for_each_entry(fragment, &stream->fragments, node) {
+			kunit_err(test, "%s", fragment->fragment);
+		}
+		kunit_err(test, "\n");
+	} else {
+		kunit_err(test, "%s", buf);
+	}
+
+	kunit_stream_clear(kstream);
+}
+
+struct kunit_stream_alloc_context {
+	struct kunit *test;
+	gfp_t gfp;
+};
+
+static int kunit_stream_init(struct kunit_resource *res, void *context)
+{
+	struct kunit_stream_alloc_context *ctx = context;
+	struct kunit_stream *stream;
+
+	stream = kunit_kzalloc(ctx->test, sizeof(*stream), ctx->gfp);
+	if (!stream)
+		return -ENOMEM;
+
+	stream->test = ctx->test;
+	stream->internal_stream = alloc_string_stream(ctx->test, ctx->gfp);
+	if (!stream->internal_stream)
+		return -ENOMEM;
+
+	res->data = stream;
+	return 0;
+}
+
+static void kunit_stream_free(struct kunit_resource *res)
+{
+	/* Do nothing because cleanup is handled by KUnit managed resources */
+}
+
+struct kunit_stream *alloc_kunit_stream(struct kunit *test,
+					gfp_t gfp)
+{
+	struct kunit_stream_alloc_context ctx = {
+		.test = test,
+		.gfp = gfp
+	};
+
+	return kunit_alloc_resource(test,
+				    kunit_stream_init,
+				    kunit_stream_free,
+				    gfp,
+				    &ctx);
+}
diff --git a/lib/kunit/string-stream.c b/lib/kunit/string-stream.c
index 151a0e7ac349..a94004134928 100644
--- a/lib/kunit/string-stream.c
+++ b/lib/kunit/string-stream.c
@@ -112,7 +112,7 @@ int string_stream_add(struct string_stream *stream, const char *fmt, ...)
 	return result;
 }
 
-static void string_stream_clear(struct string_stream *stream)
+void string_stream_clear(struct string_stream *stream)
 {
 	struct string_stream_fragment *frag_container, *frag_container_safe;
 
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 03/12] kunit: test: add concept of post conditions
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 01/12] Revert "kunit: move string-stream.h to lib/kunit" Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 02/12] kunit: test: add kunit_stream a std::stream like logger Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 04/12] checkpatch: add support for struct MOCK(foo) syntax Daniel Latypov
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

From: Brendan Higgins <brendanhiggins@google.com>

Add a way to specify that certain conditions must be met at the end of a
test case.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
---
 include/kunit/test.h |  6 ++++++
 lib/kunit/test.c     | 11 +++++++++++
 2 files changed, 17 insertions(+)

diff --git a/include/kunit/test.h b/include/kunit/test.h
index 687782fa44d9..0eb3abb00da4 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -190,6 +190,11 @@ struct kunit_suite {
 	char *log;
 };
 
+struct kunit_post_condition {
+	struct list_head node;
+	void (*validate)(struct kunit_post_condition *condition);
+};
+
 /**
  * struct kunit - represents a running instance of a test.
  *
@@ -223,6 +228,7 @@ struct kunit {
 	 * protect it with some type of lock.
 	 */
 	struct list_head resources; /* Protected by lock. */
+	struct list_head post_conditions;
 };
 
 void kunit_init_test(struct kunit *test, const char *name, char *log);
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 670d1cc9c105..4e8c74c89073 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -228,6 +228,7 @@ void kunit_init_test(struct kunit *test, const char *name, char *log)
 {
 	spin_lock_init(&test->lock);
 	INIT_LIST_HEAD(&test->resources);
+	INIT_LIST_HEAD(&test->post_conditions);
 	test->name = name;
 	test->log = log;
 	if (test->log)
@@ -269,6 +270,16 @@ static void kunit_case_internal_cleanup(struct kunit *test)
 static void kunit_run_case_cleanup(struct kunit *test,
 				   struct kunit_suite *suite)
 {
+	struct kunit_post_condition *condition, *condition_safe;
+
+	list_for_each_entry_safe(condition,
+				 condition_safe,
+				 &test->post_conditions,
+				 node) {
+		condition->validate(condition);
+		list_del(&condition->node);
+	}
+
 	if (suite->exit)
 		suite->exit(test);
 
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 04/12] checkpatch: add support for struct MOCK(foo) syntax
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
                   ` (2 preceding siblings ...)
  2020-10-12 22:20 ` [RFC v2 03/12] kunit: test: add concept of post conditions Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 05/12] kunit: mock: add parameter list manipulation macros Daniel Latypov
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

From: Brendan Higgins <brendanhiggins@google.com>

KUnit will soon add macros for generating mocks from types, the
generated mock types are named like `struct MOCK(foo)` (where the base
type is struct foo).

Add `struct MOCK(foo)` as a NonptrType so that it is recognized
correctly in declarations.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
---
 scripts/checkpatch.pl | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 504d2e431c60..b40a68e7bc25 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -796,6 +796,10 @@ sub build_types {
 			(?:
 				(?:typeof|__typeof__)\s*\([^\)]*\)|
 				(?:$typeTypedefs\b)|
+				# Matching a \b breaks struct MOCK(foo) syntax,
+				# so we need to have it not lumped in with the
+				# types in @typeList.
+				(?:struct\s+MOCK\($Ident\))|
 				(?:${all}\b)
 			)
 			(?:\s+$Modifier|\s+const)*
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 05/12] kunit: mock: add parameter list manipulation macros
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
                   ` (3 preceding siblings ...)
  2020-10-12 22:20 ` [RFC v2 04/12] checkpatch: add support for struct MOCK(foo) syntax Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 06/12] kunit: expose kunit_set_failure() for use by mocking Daniel Latypov
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

From: Brendan Higgins <brendanhiggins@google.com>

Add macros for parsing and manipulating parameter lists needed for
generating mocks.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
---
 include/kunit/params.h      | 305 ++++++++++++++++++++++++++++++++++++
 lib/kunit/Makefile          |   3 +-
 lib/kunit/mock-macro-test.c | 150 ++++++++++++++++++
 3 files changed, 457 insertions(+), 1 deletion(-)
 create mode 100644 include/kunit/params.h
 create mode 100644 lib/kunit/mock-macro-test.c

diff --git a/include/kunit/params.h b/include/kunit/params.h
new file mode 100644
index 000000000000..06efcb58c2c0
--- /dev/null
+++ b/include/kunit/params.h
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Macros for parsing and manipulating parameter lists needed for generating
+ * mocks.
+ *
+ * Copyright (C) 2020, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins@google.com>
+ */
+
+#ifndef _KUNIT_PARAMS_H
+#define _KUNIT_PARAMS_H
+
+#define NUM_VA_ARGS_IMPL(__dummy,					       \
+			 __1,						       \
+			 __2,						       \
+			 __3,						       \
+			 __4,						       \
+			 __5,						       \
+			 __6,						       \
+			 __7,						       \
+			 __8,						       \
+			 __9,						       \
+			 __10,						       \
+			 __11,						       \
+			 __12,						       \
+			 __13,						       \
+			 __14,						       \
+			 __15,						       \
+			 __16,						       \
+			 __nargs, args...) __nargs
+
+#define NUM_VA_ARGS(args...) NUM_VA_ARGS_IMPL(__dummy, ##args,		       \
+					  16,				       \
+					  15,				       \
+					  14,				       \
+					  13,				       \
+					  12,				       \
+					  11,				       \
+					  10,				       \
+					  9,				       \
+					  8,				       \
+					  7,				       \
+					  6,				       \
+					  5,				       \
+					  4,				       \
+					  3,				       \
+					  2,				       \
+					  1,				       \
+					  0)
+
+#define CONCAT_INTERNAL(left, right) left##right
+#define CONCAT(left, right) CONCAT_INTERNAL(left, right)
+
+#define EMPTY()
+
+/*
+ * Takes the name of a function style macro such as FOO() and prevents it from
+ * being evaluated on the current pass.
+ *
+ * This is useful when you need to write a "recursive" macro since a macro name
+ * becomes painted after it is pasted. If a different macro is pasted, this
+ * different macro won't be pasted; if we then defer the evaluation of the this
+ * "indirection macro", we can prevent the original definition from getting
+ * painted.
+ *
+ * Example:
+ *   #define EXAMPLE EXPAND(FOO()) // FOO() is evaluated on 1st pass.
+ *   #define EXAMPLE EXPAND(DEFER(FOO)()) // FOO() is evaluated on the second
+ *					  // pass.
+ */
+#define DEFER(macro_id) macro_id EMPTY()
+
+/*
+ * Takes the name of a function style macro such as FOO() and prevents it from
+ * being evaluated on the current or following pass.
+ *
+ * This is useful when you need to DEFER inside an operation which causes an
+ * extra pass, like IF.
+ *
+ * Example:
+ *   #define EXAMPLE EXPAND(FOO()) // FOO() is evaluated on 1st pass.
+ *   #define EXAMPLE EXPAND(DEFER(FOO)()) // FOO() is evaluated on the second
+ *					  // pass.
+ *   #define EXAMPLE EXPAND(OBSTRUCT(FOO)()) // FOO() is evaluated on the third
+ *					     // pass.
+ */
+#define OBSTRUCT(macro_id) macro_id DEFER(EMPTY)()
+
+#define EXPAND_1(args...) args
+#define EXPAND_2(args...) EXPAND_1(EXPAND_1(args))
+#define EXPAND_4(args...) EXPAND_2(EXPAND_2(args))
+#define EXPAND_8(args...) EXPAND_4(EXPAND_4(args))
+#define EXPAND_16(args...) EXPAND_8(EXPAND_8(args))
+
+/*
+ * Causes multiple evaluation passes of a macro.
+ *
+ * CPP is implemented as a push down automaton. It consumes a stream of tokens
+ * and as it comes across macros, it either evaluates them and pastes the
+ * result, or if the macro is a function macro, it pushes the macro to a stack,
+ * it evaluates the input to the function macro, pops the state from the stack
+ * and continues.
+ *
+ * This macro serves the function of making the cursor return to the beginging
+ * of a macro that requires mulitple passes to evaluate. It is most useful when
+ * used with DEFER(...) and OBSTRUCT(...).
+ */
+#define EXPAND(args...) EXPAND_16(args)
+
+#define INC(id) INC_##id
+#define INC_0  1
+#define INC_1  2
+#define INC_2  3
+#define INC_3  4
+#define INC_4  5
+#define INC_5  6
+#define INC_6  7
+#define INC_7  8
+#define INC_8  9
+#define INC_9  10
+#define INC_10 11
+#define INC_11 12
+#define INC_12 13
+#define INC_13 14
+#define INC_14 15
+#define INC_15 16
+#define INC_16 17
+
+#define DEC(id) DEC_##id
+#define DEC_1  0
+#define DEC_2  1
+#define DEC_3  2
+#define DEC_4  3
+#define DEC_5  4
+#define DEC_6  5
+#define DEC_7  6
+#define DEC_8  7
+#define DEC_9  8
+#define DEC_10 9
+#define DEC_11 10
+#define DEC_12 11
+#define DEC_13 12
+#define DEC_14 13
+#define DEC_15 14
+#define DEC_16 15
+
+#define DROP_FIRST_ARG_INTERNAL(dropped, x, args...) x
+#define DROP_FIRST_ARG(args...) DROP_FIRST_ARG_INTERNAL(args)
+
+#define EQUAL(left, right) EQUAL_##left##_##right
+#define EQUAL_0_0 dropped, 1
+#define EQUAL_1_1 dropped, 1
+#define EQUAL_2_2 dropped, 1
+#define EQUAL_3_3 dropped, 1
+#define EQUAL_4_4 dropped, 1
+#define EQUAL_5_5 dropped, 1
+#define EQUAL_6_6 dropped, 1
+#define EQUAL_7_7 dropped, 1
+#define EQUAL_8_8 dropped, 1
+#define EQUAL_9_9 dropped, 1
+#define EQUAL_10_10 dropped, 1
+#define EQUAL_11_11 dropped, 1
+#define EQUAL_12_12 dropped, 1
+#define EQUAL_13_13 dropped, 1
+#define EQUAL_14_14 dropped, 1
+#define EQUAL_15_15 dropped, 1
+#define EQUAL_16_16 dropped, 1
+
+#define IS_EQUAL(left, right) DROP_FIRST_ARG(EQUAL(left, right), 0)
+
+#define NOT_INTERNAL(condition) NOT_##condition
+#define NOT(condition) NOT_INTERNAL(condition)
+#define NOT_0 1
+#define NOT_1 0
+
+#define IS_NOT_EQUAL(left, right) NOT(IS_EQUAL(left, right))
+
+#define EMPTY_IMPL(tokens) CONCAT(EMPTY_, tokens)
+#define IS_EMPTY(tokens)
+
+#define OR_INTERNAL(left, right) OR_##left##_##right
+#define OR(left, right) OR_INTERNAL(left, right)
+#define OR_0_0 0
+#define OR_0_1 1
+#define OR_1_0 1
+#define OR_1_1 1
+
+#define IF(condition) CONCAT(IF_, condition)
+#define IF_0(body)
+#define IF_1(body) body
+
+#define COMMA() ,
+
+#define APPLY_TOKENS_INTERNAL(tokens, yield_token, seen_token) \
+		IF(yield_token)(IF(seen_token)(COMMA()) tokens)
+#define APPLY_TOKENS(tokens, yield_token, seen_token) \
+		APPLY_TOKENS_INTERNAL(tokens, yield_token, seen_token)
+
+/*
+ * Provides the indirection to keep the PARAM_LIST_RECURSE_INTERNAL from getting
+ * pasted, only useful if used with DEFER(...) or OBSTRUCT(...).
+ */
+#define PARAM_LIST_RECURSE_INDIRECT() PARAM_LIST_RECURSE_INTERNAL
+
+/*
+ * Given a starting index, a number of args, a MACRO to apply, and a list of
+ * types (with at least one element) this will call MACRO with the first type in
+ * the list and index; it will then call itself again on all remaining types, if
+ * any, while incrementing index, and decrementing nargs.
+ *
+ * Assumes nargs is the number of types in the list.
+ */
+#define PARAM_LIST_RECURSE_INTERNAL(index,				       \
+				    nargs,				       \
+				    MACRO,				       \
+				    FILTER,				       \
+				    context,				       \
+				    seen_token,				       \
+				    type,				       \
+				    args...)				       \
+		APPLY_TOKENS(MACRO(context, type, index),		       \
+			     FILTER(context, type, index),		       \
+			     seen_token)				       \
+		IF(IS_NOT_EQUAL(nargs, 1))				       \
+			(OBSTRUCT(PARAM_LIST_RECURSE_INDIRECT)()	       \
+			 (INC(index), DEC(nargs),			       \
+			  MACRO, FILTER, context,			       \
+			  OR(seen_token, FILTER(context, type, index)),	       \
+			  args))
+
+#define PARAM_LIST_RECURSE(index, nargs, MACRO, FILTER, context, args...)      \
+		IF(IS_NOT_EQUAL(nargs, 0))				       \
+			(OBSTRUCT(PARAM_LIST_RECURSE_INTERNAL)(index,	       \
+							       nargs,	       \
+							       MACRO,	       \
+							       FILTER,	       \
+							       context,	       \
+							       0,	       \
+							       args))
+
+#define FILTER_NONE(context, type, index) 1
+
+#define FILTER_INDEX_INTERNAL(index_to_drop, type, index) \
+		IS_NOT_EQUAL(index, index_to_drop)
+#define FILTER_INDEX(index_to_drop, type, index) \
+		FILTER_INDEX_INTERNAL(index_to_drop, type, index)
+
+/*
+ * Applies a MACRO which takes a type and the index of the type and outputs a
+ * sequence of tokens to a list of types.
+ */
+#define FOR_EACH_PARAM(MACRO, FILTER, context, args...) \
+		EXPAND(PARAM_LIST_RECURSE(0,\
+					  NUM_VA_ARGS(args),\
+					  MACRO,\
+					  FILTER,\
+					  context,\
+					  args))
+
+#define PRODUCE_TYPE_AND_ARG(context, type, index) type arg##index
+#define PARAM_LIST_FROM_TYPES(args...)					       \
+		FOR_EACH_PARAM(PRODUCE_TYPE_AND_ARG,			       \
+			       FILTER_NONE,				       \
+			       not_used,				       \
+			       args)
+
+#define PRODUCE_TYPE_NAME(context, type, index) #type
+#define TYPE_NAMES_FROM_TYPES(handle_index, args...)			       \
+		FOR_EACH_PARAM(PRODUCE_TYPE_NAME,			       \
+			       FILTER_INDEX,				       \
+			       handle_index,				       \
+			       args)
+
+#define PRODUCE_PTR_TO_ARG(context, type, index) &arg##index
+#define PTR_TO_ARG_FROM_TYPES(handle_index, args...)			       \
+		FOR_EACH_PARAM(PRODUCE_PTR_TO_ARG,			       \
+			       FILTER_INDEX,				       \
+			       handle_index,				       \
+			       args)
+
+#define PRODUCE_MATCHER_AND_ARG(ctrl_index, type, index)		       \
+		IF(IS_EQUAL(index, ctrl_index))(struct mock *arg##ctrl_index)  \
+		IF(IS_NOT_EQUAL(index, ctrl_index))(			       \
+				struct mock_param_matcher *arg##index)
+#define MATCHER_PARAM_LIST_FROM_TYPES(ctrl_index, args...)		       \
+		FOR_EACH_PARAM(PRODUCE_MATCHER_AND_ARG,			       \
+			       FILTER_NONE,				       \
+			       ctrl_index,				       \
+			       args)
+
+#define PRODUCE_ARG(context, type, index) arg##index
+#define ARG_NAMES_FROM_TYPES(ctrl_index, args...)			       \
+		FOR_EACH_PARAM(PRODUCE_ARG,				       \
+			       FILTER_INDEX,				       \
+			       ctrl_index,				       \
+			       args)
+
+#define PRODUCE_ARRAY_ACCESSOR(context, type, index) *((type *) params[index])
+#define ARRAY_ACCESSORS_FROM_TYPES(args...)				       \
+		FOR_EACH_PARAM(PRODUCE_ARRAY_ACCESSOR,			       \
+			       FILTER_NONE,				       \
+			       not_used,				       \
+			       args)
+
+#endif /* _KUNIT_PARAMS_H */
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 523a7b0f9783..1707660c8b1c 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -10,7 +10,8 @@ ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
 kunit-objs +=				debugfs.o
 endif
 
-obj-$(CONFIG_KUNIT_TEST) +=		kunit-test.o
+obj-$(CONFIG_KUNIT_TEST) +=		kunit-test.o \
+					mock-macro-test.o
 
 # string-stream-test compiles built-in only.
 ifeq ($(CONFIG_KUNIT_TEST),y)
diff --git a/lib/kunit/mock-macro-test.c b/lib/kunit/mock-macro-test.c
new file mode 100644
index 000000000000..6c3dc2193edb
--- /dev/null
+++ b/lib/kunit/mock-macro-test.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for parameter list parsing macros.
+ *
+ * Copyright (C) 2020, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins@google.com>
+ */
+
+#include <kunit/test.h>
+#include <kunit/params.h>
+
+#define TO_STR_INTERNAL(...) #__VA_ARGS__
+#define TO_STR(...) TO_STR_INTERNAL(__VA_ARGS__)
+
+static void mock_macro_is_equal(struct kunit *test)
+{
+	KUNIT_EXPECT_STREQ(test, "dropped, 1", TO_STR(EQUAL(1, 1)));
+	KUNIT_EXPECT_EQ(test, 1, DROP_FIRST_ARG(dropped, 1, 0));
+	KUNIT_EXPECT_EQ(test, 0, DROP_FIRST_ARG(1, 0));
+	KUNIT_EXPECT_EQ(test, 0, DROP_FIRST_ARG(EQUAL(1, 0), 0));
+	KUNIT_EXPECT_EQ(test, 1, IS_EQUAL(1, 1));
+	KUNIT_EXPECT_EQ(test, 0, IS_EQUAL(1, 0));
+}
+
+static void mock_macro_if(struct kunit *test)
+{
+	KUNIT_EXPECT_STREQ(test, "body", ""IF(1)("body"));
+	KUNIT_EXPECT_STREQ(test, "", ""IF(0)("body"));
+	KUNIT_EXPECT_STREQ(test, "body", ""IF(IS_EQUAL(1, 1))("body"));
+	KUNIT_EXPECT_STREQ(test, "", ""IF(IS_EQUAL(0, 1))("body"));
+}
+
+static void mock_macro_apply_tokens(struct kunit *test)
+{
+	KUNIT_EXPECT_STREQ(test, "type", TO_STR(APPLY_TOKENS(type, 1, 0)));
+	KUNIT_EXPECT_STREQ(test, ", type", TO_STR(APPLY_TOKENS(type, 1, 1)));
+	KUNIT_EXPECT_STREQ(test, "", TO_STR(APPLY_TOKENS(type, 0, 1)));
+}
+
+#define IDENTITY(context, type, index) type
+
+static void mock_macro_param_list_recurse(struct kunit *test)
+{
+	KUNIT_EXPECT_STREQ(test, "", TO_STR(PARAM_LIST_RECURSE(0,
+							      0,
+							      IDENTITY,
+							      FILTER_NONE,
+							      not_used)));
+	KUNIT_EXPECT_STREQ(test,
+			  "type",
+			  TO_STR(EXPAND(PARAM_LIST_RECURSE(0,
+							   1,
+							   IDENTITY,
+							   FILTER_NONE,
+							   not_used,
+							   type))));
+	KUNIT_EXPECT_STREQ(test,
+			  "type0 , type1 , type2 , type3 , type4 , type5 , "
+			  "type6 , type7 , type8 , type9 , type10 , type11 , "
+			  "type12 , type13 , type14 , type15",
+			  TO_STR(EXPAND(PARAM_LIST_RECURSE(0, 16,
+							   IDENTITY,
+							   FILTER_NONE,
+							   not_used,
+							   type0, type1, type2,
+							   type3, type4, type5,
+							   type6, type7, type8,
+							   type9, type10,
+							   type11, type12,
+							   type13, type14,
+							   type15))));
+}
+
+static void mock_macro_for_each_param(struct kunit *test)
+{
+	KUNIT_EXPECT_STREQ(test,
+			  "type0 , type1",
+			  TO_STR(FOR_EACH_PARAM(IDENTITY,
+						FILTER_NONE,
+						not_used,
+						type0,
+						type1)));
+	KUNIT_EXPECT_STREQ(test,
+			  "type1",
+			  TO_STR(FOR_EACH_PARAM(IDENTITY,
+						FILTER_INDEX,
+						0,
+						type0,
+						type1)));
+}
+
+static void mock_macro_param_list_from_types_basic(struct kunit *test)
+{
+	KUNIT_EXPECT_STREQ(test, "", TO_STR(PARAM_LIST_FROM_TYPES()));
+	KUNIT_EXPECT_STREQ(test, "int arg0",
+			   TO_STR(PARAM_LIST_FROM_TYPES(int)));
+	KUNIT_EXPECT_STREQ(test, "struct kunit_struct * arg0 , int arg1",
+			  TO_STR(PARAM_LIST_FROM_TYPES(struct kunit_struct *,
+						       int)));
+	KUNIT_EXPECT_STREQ(test,
+			  "type0 arg0 , type1 arg1 , type2 arg2 , type3 arg3 , "
+			  "type4 arg4 , type5 arg5 , type6 arg6 , type7 arg7 , "
+			  "type8 arg8 , type9 arg9 , type10 arg10 , "
+			  "type11 arg11 , type12 arg12 , type13 arg13 , "
+			  "type14 arg14 , type15 arg15",
+			  TO_STR(PARAM_LIST_FROM_TYPES(type0, type1, type2,
+						       type3, type4, type5,
+						       type6, type7, type8,
+						       type9, type10, type11,
+						       type12, type13, type14,
+						       type15)));
+}
+
+static void mock_macro_arg_names_from_types(struct kunit *test)
+{
+	KUNIT_EXPECT_STREQ(test, "", TO_STR(ARG_NAMES_FROM_TYPES(0)));
+	KUNIT_EXPECT_STREQ(test, "", TO_STR(ARG_NAMES_FROM_TYPES(0, int)));
+	KUNIT_EXPECT_STREQ(test,
+			  "arg1",
+			  TO_STR(ARG_NAMES_FROM_TYPES(0,
+						      struct kunit_struct *,
+						      int)));
+	KUNIT_EXPECT_STREQ(test,
+			  "arg0 , arg1 , arg3 , arg4 , arg5 , arg6 , arg7 , "
+			  "arg8 , arg9 , arg10 , arg11 , arg12 , arg13 , "
+			  "arg14 , arg15",
+			  TO_STR(ARG_NAMES_FROM_TYPES(2, type0, type1, type2,
+						      type3, type4, type5,
+						      type6, type7, type8,
+						      type9, type10, type11,
+						      type12, type13, type14,
+						      type15)));
+}
+
+static struct kunit_case mock_macro_test_cases[] = {
+	KUNIT_CASE(mock_macro_is_equal),
+	KUNIT_CASE(mock_macro_if),
+	KUNIT_CASE(mock_macro_apply_tokens),
+	KUNIT_CASE(mock_macro_param_list_recurse),
+	KUNIT_CASE(mock_macro_for_each_param),
+	KUNIT_CASE(mock_macro_param_list_from_types_basic),
+	KUNIT_CASE(mock_macro_arg_names_from_types),
+	{}
+};
+
+static struct kunit_suite mock_macro_test_suite = {
+	.name = "mock-macro-test",
+	.test_cases = mock_macro_test_cases,
+};
+kunit_test_suite(mock_macro_test_suite);
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 06/12] kunit: expose kunit_set_failure() for use by mocking
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
                   ` (4 preceding siblings ...)
  2020-10-12 22:20 ` [RFC v2 05/12] kunit: mock: add parameter list manipulation macros Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 07/12] kunit: mock: add internal mock infrastructure Daniel Latypov
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

Being able to fail the test outside of expectations and assertions is a
requirement for new features, e.g. mocking, dynamic analysis, etc.

Signed-off-by: Daniel Latypov <dlatypov@google.com>
---
 include/kunit/test.h | 2 ++
 lib/kunit/test.c     | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/include/kunit/test.h b/include/kunit/test.h
index 0eb3abb00da4..05330593243d 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -233,6 +233,8 @@ struct kunit {
 
 void kunit_init_test(struct kunit *test, const char *name, char *log);
 
+void kunit_set_failure(struct kunit *test);
+
 int kunit_run_tests(struct kunit_suite *suite);
 
 size_t kunit_suite_num_test_cases(struct kunit_suite *suite);
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 4e8c74c89073..1ccf6dbecd73 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -15,7 +15,7 @@
 #include "debugfs.h"
 #include "try-catch-impl.h"
 
-static void kunit_set_failure(struct kunit *test)
+void kunit_set_failure(struct kunit *test)
 {
 	WRITE_ONCE(test->success, false);
 }
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 07/12] kunit: mock: add internal mock infrastructure
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
                   ` (5 preceding siblings ...)
  2020-10-12 22:20 ` [RFC v2 06/12] kunit: expose kunit_set_failure() for use by mocking Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 08/12] kunit: mock: add basic matchers and actions Daniel Latypov
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

From: Brendan Higgins <brendanhiggins@google.com>

Add the core internal mechanisms that mocks are implemented with; in
particular, this adds the mechanisms by which expectation on mocks are
validated and by which actions may be supplied and then executed when
mocks are called.

Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
---
 include/kunit/mock.h | 126 +++++++++++++++
 lib/kunit/Makefile   |   1 +
 lib/kunit/mock.c     | 364 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 491 insertions(+)
 create mode 100644 include/kunit/mock.h
 create mode 100644 lib/kunit/mock.c

diff --git a/include/kunit/mock.h b/include/kunit/mock.h
new file mode 100644
index 000000000000..299b423fdd51
--- /dev/null
+++ b/include/kunit/mock.h
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Mocking API for KUnit.
+ *
+ * Copyright (C) 2020, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins@google.com>
+ */
+
+#ifndef _KUNIT_MOCK_H
+#define _KUNIT_MOCK_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h> /* For PARAMS(...) */
+#include <kunit/test.h>
+#include <kunit/kunit-stream.h>
+#include <kunit/params.h>
+
+/**
+ * struct mock_param_matcher - represents a matcher used in a *call expectation*
+ * @match: the function that performs the matching
+ *
+ * The matching function takes a couple of parameters:
+ *
+ * - ``this``: refers to the parent struct
+ * - ``stream``: a &kunit_stream to which a detailed message should be added as
+ *   to why the parameter matches or not
+ * - ``param``: a pointer to the parameter to check for a match
+ *
+ * The matching function should return whether or not the passed parameter
+ * matches.
+ */
+struct mock_param_matcher {
+	bool (*match)(struct mock_param_matcher *this,
+		      struct kunit_stream *stream,
+		      const void *param);
+};
+
+#define MOCK_MAX_PARAMS 255
+
+struct mock_matcher {
+	struct mock_param_matcher *matchers[MOCK_MAX_PARAMS];
+	int num;
+};
+
+/**
+ * struct mock_action - Represents an action that a mock performs when
+ *                      expectation is matched
+ * @do_action: the action to perform
+ *
+ * The action function is given some parameters:
+ *
+ * - ``this``: refers to the parent struct
+ * - ``params``: an array of pointers to the params passed into the mocked
+ *   method or function. **The class argument is excluded for a mocked class
+ *   method.**
+ * - ``len``: size of ``params``
+ *
+ * The action function returns a pointer to the value that the mocked method
+ * or function should be returning.
+ */
+struct mock_action {
+	void *(*do_action)(struct mock_action *this,
+			   const void **params,
+			   int len);
+};
+
+/**
+ * struct mock_expectation - represents a *call expectation* on a function.
+ * @action: A &struct mock_action to perform when the function is called.
+ * @max_calls_expected: maximum number of times an expectation may be called.
+ * @min_calls_expected: minimum number of times an expectation may be called.
+ * @retire_on_saturation: no longer match once ``max_calls_expected`` is
+ *			  reached.
+ *
+ * Represents a *call expectation* on a function created with
+ * KUNIT_EXPECT_CALL().
+ */
+struct mock_expectation {
+	struct mock_action *action;
+	int max_calls_expected;
+	int min_calls_expected;
+	bool retire_on_saturation;
+	/* private: internal use only. */
+	const char *expectation_name;
+	struct list_head node;
+	struct mock_matcher *matcher;
+	int times_called;
+};
+
+struct mock_method {
+	struct list_head node;
+	const char *method_name;
+	const void *method_ptr;
+	struct mock_action *default_action;
+	struct list_head expectations;
+};
+
+struct mock {
+	struct kunit_post_condition parent;
+	struct kunit *test;
+	struct list_head methods;
+	/* TODO(brendanhiggins@google.com): add locking to do_expect. */
+	const void *(*do_expect)(struct mock *mock,
+				 const char *method_name,
+				 const void *method_ptr,
+				 const char * const *param_types,
+				 const void **params,
+				 int len);
+};
+
+void mock_init_ctrl(struct kunit *test, struct mock *mock);
+
+void mock_validate_expectations(struct mock *mock);
+
+int mock_set_default_action(struct mock *mock,
+			    const char *method_name,
+			    const void *method_ptr,
+			    struct mock_action *action);
+
+struct mock_expectation *mock_add_matcher(struct mock *mock,
+					  const char *method_name,
+					  const void *method_ptr,
+					  struct mock_param_matcher *matchers[],
+					  int len);
+
+#endif /* _KUNIT_MOCK_H */
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 1707660c8b1c..a51157970502 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -1,6 +1,7 @@
 obj-$(CONFIG_KUNIT) +=			kunit.o
 
 kunit-objs +=				test.o \
+					mock.o \
 					string-stream.o \
 					assert.o \
 					kunit-stream.o \
diff --git a/lib/kunit/mock.c b/lib/kunit/mock.c
new file mode 100644
index 000000000000..12fb88899451
--- /dev/null
+++ b/lib/kunit/mock.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mocking API for KUnit.
+ *
+ * Copyright (C) 2020, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins@google.com>
+ */
+
+#include <kunit/mock.h>
+
+static bool mock_match_params(struct mock_matcher *matcher,
+			      struct kunit_stream *stream,
+			      const void **params,
+			      int len)
+{
+	struct mock_param_matcher *param_matcher;
+	bool ret = true, tmp;
+	int i;
+
+	BUG_ON(matcher->num != len);
+
+	for (i = 0; i < matcher->num; i++) {
+		param_matcher = matcher->matchers[i];
+		kunit_stream_add(stream, "\t");
+		tmp = param_matcher->match(param_matcher, stream, params[i]);
+		ret = ret && tmp;
+		kunit_stream_add(stream, "\n");
+	}
+
+	return ret;
+}
+
+static const void *mock_do_expect(struct mock *mock,
+				  const char *method_name,
+				  const void *method_ptr,
+				  const char * const *type_names,
+				  const void **params,
+				  int len);
+
+static void fail_and_flush(struct kunit *test, struct kunit_stream *stream)
+{
+	kunit_set_failure(test);
+	kunit_stream_commit(stream);
+}
+
+void mock_validate_expectations(struct mock *mock)
+{
+	struct mock_expectation *expectation, *expectation_safe;
+	struct kunit_stream *stream;
+	struct mock_method *method;
+	int times_called;
+
+	stream = alloc_kunit_stream(mock->test, GFP_KERNEL);
+	list_for_each_entry(method, &mock->methods, node) {
+		list_for_each_entry_safe(expectation, expectation_safe,
+					 &method->expectations, node) {
+			times_called = expectation->times_called;
+			if (!(expectation->min_calls_expected <= times_called &&
+			      times_called <= expectation->max_calls_expected)
+			    ) {
+				kunit_stream_add(stream,
+						 "Expectation was not called the specified number of times:\n\t");
+				kunit_stream_add(stream,
+						 "Function: %s, min calls: %d, max calls: %d, actual calls: %d\n",
+						 method->method_name,
+						 expectation->min_calls_expected,
+						 expectation->max_calls_expected,
+						 times_called);
+				fail_and_flush(mock->test, stream);
+			}
+			list_del(&expectation->node);
+		}
+	}
+}
+
+static void mock_validate_wrapper(struct kunit_post_condition *condition)
+{
+	struct mock *mock = container_of(condition, struct mock, parent);
+
+	mock_validate_expectations(mock);
+}
+
+void mock_init_ctrl(struct kunit *test, struct mock *mock)
+{
+	mock->test = test;
+	INIT_LIST_HEAD(&mock->methods);
+	mock->do_expect = mock_do_expect;
+	mock->parent.validate = mock_validate_wrapper;
+	list_add_tail(&mock->parent.node, &test->post_conditions);
+}
+
+static struct mock_method *mock_lookup_method(struct mock *mock,
+					      const void *method_ptr)
+{
+	struct mock_method *ret;
+
+	list_for_each_entry(ret, &mock->methods, node) {
+		if (ret->method_ptr == method_ptr)
+			return ret;
+	}
+
+	return NULL;
+}
+
+static struct mock_method *mock_add_method(struct mock *mock,
+					   const char *method_name,
+					   const void *method_ptr)
+{
+	struct mock_method *method;
+
+	method = kunit_kzalloc(mock->test, sizeof(*method), GFP_KERNEL);
+	if (!method)
+		return NULL;
+
+	INIT_LIST_HEAD(&method->expectations);
+	method->method_name = method_name;
+	method->method_ptr = method_ptr;
+	list_add_tail(&method->node, &mock->methods);
+
+	return method;
+}
+
+static int mock_add_expectation(struct mock *mock,
+				const char *method_name,
+				const void *method_ptr,
+				struct mock_expectation *expectation)
+{
+	struct mock_method *method;
+
+	method = mock_lookup_method(mock, method_ptr);
+	if (!method) {
+		method = mock_add_method(mock, method_name, method_ptr);
+		if (!method)
+			return -ENOMEM;
+	}
+
+	list_add_tail(&expectation->node, &method->expectations);
+
+	return 0;
+}
+
+struct mock_expectation *mock_add_matcher(struct mock *mock,
+					  const char *method_name,
+					  const void *method_ptr,
+					  struct mock_param_matcher *matchers[],
+					  int len)
+{
+	struct mock_expectation *expectation;
+	struct mock_matcher *matcher;
+	int ret;
+
+	expectation = kunit_kzalloc(mock->test,
+				   sizeof(*expectation),
+				   GFP_KERNEL);
+	if (!expectation)
+		return NULL;
+
+	matcher = kunit_kmalloc(mock->test, sizeof(*matcher), GFP_KERNEL);
+	if (!matcher)
+		return NULL;
+
+	memcpy(&matcher->matchers, matchers, sizeof(*matchers) * len);
+	matcher->num = len;
+
+	expectation->matcher = matcher;
+	expectation->max_calls_expected = 1;
+	expectation->min_calls_expected = 1;
+
+	ret = mock_add_expectation(mock, method_name, method_ptr, expectation);
+	if (ret < 0)
+		return NULL;
+
+	return expectation;
+}
+
+int mock_set_default_action(struct mock *mock,
+			    const char *method_name,
+			    const void *method_ptr,
+			    struct mock_action *action)
+{
+	struct mock_method *method;
+
+	method = mock_lookup_method(mock, method_ptr);
+	if (!method) {
+		method = mock_add_method(mock, method_name, method_ptr);
+		if (!method)
+			return -ENOMEM;
+	}
+
+	method->default_action = action;
+
+	return 0;
+}
+
+static void mock_format_param(struct kunit_stream *stream,
+			      const char *type_name,
+			      const void *param)
+{
+	/*
+	 * Cannot find formatter, so just print the pointer of the
+	 * symbol.
+	 */
+	kunit_stream_add(stream, "<%pS>", param);
+}
+
+static void mock_add_method_declaration_to_stream(
+		struct kunit_stream *stream,
+		const char *function_name,
+		const char * const *type_names,
+		const void **params,
+		int len)
+{
+	int i;
+
+	kunit_stream_add(stream, "%s(", function_name);
+	for (i = 0; i < len; i++) {
+		mock_format_param(stream, type_names[i], params[i]);
+		if (i < len - 1)
+			kunit_stream_add(stream, ", ");
+	}
+	kunit_stream_add(stream, ")\n");
+}
+
+static struct kunit_stream *mock_initialize_failure_message(
+		struct kunit *test,
+		const char *function_name,
+		const char * const *type_names,
+		const void **params,
+		int len)
+{
+	struct kunit_stream *stream;
+
+	stream = alloc_kunit_stream(test, GFP_KERNEL);
+	if (!stream)
+		return NULL;
+
+	kunit_stream_add(stream,
+			 "EXPECTATION FAILED: no expectation for call: ");
+	mock_add_method_declaration_to_stream(stream,
+					      function_name,
+					      type_names,
+					      params,
+					      len);
+	return stream;
+}
+
+static bool mock_is_expectation_retired(struct mock_expectation *expectation)
+{
+	return expectation->retire_on_saturation &&
+			expectation->times_called ==
+			expectation->max_calls_expected;
+}
+
+static void mock_add_method_expectation_error(struct kunit *test,
+					      struct kunit_stream *stream,
+					      char *message,
+					      struct mock *mock,
+					      struct mock_method *method,
+					      const char * const *type_names,
+					      const void **params,
+					      int len)
+{
+	kunit_stream_clear(stream);
+	kunit_stream_add(stream, message);
+	mock_add_method_declaration_to_stream(stream,
+		method->method_name, type_names, params, len);
+}
+
+static struct mock_expectation *mock_apply_expectations(
+		struct mock *mock,
+		struct mock_method *method,
+		const char * const *type_names,
+		const void **params,
+		int len)
+{
+	struct kunit_stream *attempted_matching_stream;
+	bool expectations_all_saturated = true;
+	struct kunit *test = mock->test;
+	struct kunit_stream *stream = alloc_kunit_stream(test, GFP_KERNEL);
+	struct mock_expectation *ret;
+
+	if (list_empty(&method->expectations)) {
+		mock_add_method_expectation_error(test, stream,
+			"Method was called with no expectations declared: ",
+			mock, method, type_names, params, len);
+		kunit_stream_commit(stream);
+		return NULL;
+	}
+
+	attempted_matching_stream = mock_initialize_failure_message(
+			test,
+			method->method_name,
+			type_names,
+			params,
+			len);
+
+	list_for_each_entry(ret, &method->expectations, node) {
+		if (mock_is_expectation_retired(ret))
+			continue;
+		expectations_all_saturated = false;
+
+		kunit_stream_add(attempted_matching_stream,
+				 "Tried expectation: %s, but\n",
+				 ret->expectation_name);
+		if (mock_match_params(ret->matcher,
+			attempted_matching_stream, params, len)) {
+			/*
+			 * Matcher was found; we won't print, so clean up the
+			 * log.
+			 */
+			kunit_stream_clear(attempted_matching_stream);
+			return ret;
+		}
+	}
+
+	if (expectations_all_saturated) {
+		mock_add_method_expectation_error(test, stream,
+			"Method was called with fully saturated expectations: ",
+			mock, method, type_names, params, len);
+	} else {
+		mock_add_method_expectation_error(test, stream,
+			"Method called that did not match any expectations: ",
+			mock, method, type_names, params, len);
+		kunit_stream_append(stream, attempted_matching_stream);
+	}
+	fail_and_flush(test, stream);
+	kunit_stream_clear(attempted_matching_stream);
+	return NULL;
+}
+
+static const void *mock_do_expect(struct mock *mock,
+				  const char *method_name,
+				  const void *method_ptr,
+				  const char * const *param_types,
+				  const void **params,
+				  int len)
+{
+	struct mock_expectation *expectation;
+	struct mock_method *method;
+	struct mock_action *action;
+
+	method = mock_lookup_method(mock, method_ptr);
+	if (!method)
+		return NULL;
+
+	expectation = mock_apply_expectations(mock,
+					      method,
+					      param_types,
+					      params,
+					      len);
+	if (!expectation) {
+		action = method->default_action;
+	} else {
+		expectation->times_called++;
+		if (expectation->action)
+			action = expectation->action;
+		else
+			action = method->default_action;
+	}
+	if (!action)
+		return NULL;
+
+	return action->do_action(action, params, len);
+}
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 08/12] kunit: mock: add basic matchers and actions
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
                   ` (6 preceding siblings ...)
  2020-10-12 22:20 ` [RFC v2 07/12] kunit: mock: add internal mock infrastructure Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 09/12] kunit: mock: add macro machinery to pick correct format args Daniel Latypov
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

From: Brendan Higgins <brendanhiggins@google.com>

Add basic matchers and actions needed for any kind of mocking to be
useful; these matchers and actions are how expectations for mocks are
described: what calls the mocks are expected to receive, and what the
mock should do under those circumstances.

Co-developed-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
 include/kunit/mock.h     | 222 ++++++++++++++++++++++++++++++++
 lib/kunit/Makefile       |   1 +
 lib/kunit/common-mocks.c | 271 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 494 insertions(+)
 create mode 100644 lib/kunit/common-mocks.c

diff --git a/include/kunit/mock.h b/include/kunit/mock.h
index 299b423fdd51..13fdeb8730b5 100644
--- a/include/kunit/mock.h
+++ b/include/kunit/mock.h
@@ -123,4 +123,226 @@ struct mock_expectation *mock_add_matcher(struct mock *mock,
 					  struct mock_param_matcher *matchers[],
 					  int len);
 
+#define CONVERT_TO_ACTUAL_TYPE(type, ptr) (*((type *) ptr))
+
+/**
+ * DOC: Built In Matchers
+ *
+ * These are the matchers that can be used when matching arguments in
+ * :c:func:`KUNIT_EXPECT_CALL` (more can be defined manually).
+ *
+ * For example, there's a matcher that matches any arguments:
+ *
+ * .. code-block:: c
+ *
+ *    struct mock_param_matcher *any(struct kunit *test);
+ *
+ * There are matchers for integers based on the binary condition:
+ *
+ * * eq: equals to
+ * * ne: not equal to
+ * * lt: less than
+ * * le: less than or equal to
+ * * gt: greater than
+ * * ge: greater than or equal to
+ *
+ * .. code-block:: c
+ *
+ *    struct mock_param_matcher *kunit_int_eq(struct kunit *test, int expected);
+ *    struct mock_param_matcher *kunit_int_ne(struct kunit *test, int expected);
+ *    struct mock_param_matcher *kunit_int_lt(struct kunit *test, int expected);
+ *    struct mock_param_matcher *kunit_int_le(struct kunit *test, int expected);
+ *    struct mock_param_matcher *kunit_int_gt(struct kunit *test, int expected);
+ *    struct mock_param_matcher *kunit_int_ge(struct kunit *test, int expected);
+ *
+ * For a detailed list, please see
+ * ``include/linux/mock.h``.
+ */
+
+/* Matches any argument */
+struct mock_param_matcher *kunit_any(struct kunit *test);
+
+/*
+ * Matches different types of integers, the argument is compared to the
+ * `expected` field, based on the comparison defined.
+ */
+struct mock_param_matcher *kunit_u8_eq(struct kunit *test, u8 expected);
+struct mock_param_matcher *kunit_u8_ne(struct kunit *test, u8 expected);
+struct mock_param_matcher *kunit_u8_le(struct kunit *test, u8 expected);
+struct mock_param_matcher *kunit_u8_lt(struct kunit *test, u8 expected);
+struct mock_param_matcher *kunit_u8_ge(struct kunit *test, u8 expected);
+struct mock_param_matcher *kunit_u8_gt(struct kunit *test, u8 expected);
+
+struct mock_param_matcher *kunit_u16_eq(struct kunit *test, u16 expected);
+struct mock_param_matcher *kunit_u16_ne(struct kunit *test, u16 expected);
+struct mock_param_matcher *kunit_u16_le(struct kunit *test, u16 expected);
+struct mock_param_matcher *kunit_u16_lt(struct kunit *test, u16 expected);
+struct mock_param_matcher *kunit_u16_ge(struct kunit *test, u16 expected);
+struct mock_param_matcher *kunit_u16_gt(struct kunit *test, u16 expected);
+
+struct mock_param_matcher *kunit_u32_eq(struct kunit *test, u32 expected);
+struct mock_param_matcher *kunit_u32_ne(struct kunit *test, u32 expected);
+struct mock_param_matcher *kunit_u32_le(struct kunit *test, u32 expected);
+struct mock_param_matcher *kunit_u32_lt(struct kunit *test, u32 expected);
+struct mock_param_matcher *kunit_u32_ge(struct kunit *test, u32 expected);
+struct mock_param_matcher *kunit_u32_gt(struct kunit *test, u32 expected);
+
+struct mock_param_matcher *kunit_u64_eq(struct kunit *test, u64 expected);
+struct mock_param_matcher *kunit_u64_ne(struct kunit *test, u64 expected);
+struct mock_param_matcher *kunit_u64_le(struct kunit *test, u64 expected);
+struct mock_param_matcher *kunit_u64_lt(struct kunit *test, u64 expected);
+struct mock_param_matcher *kunit_u64_ge(struct kunit *test, u64 expected);
+struct mock_param_matcher *kunit_u64_gt(struct kunit *test, u64 expected);
+
+struct mock_param_matcher *kunit_char_eq(struct kunit *test, char expected);
+struct mock_param_matcher *kunit_char_ne(struct kunit *test, char expected);
+struct mock_param_matcher *kunit_char_le(struct kunit *test, char expected);
+struct mock_param_matcher *kunit_char_lt(struct kunit *test, char expected);
+struct mock_param_matcher *kunit_char_ge(struct kunit *test, char expected);
+struct mock_param_matcher *kunit_char_gt(struct kunit *test, char expected);
+
+struct mock_param_matcher *kunit_uchar_eq(struct kunit *test,
+					 unsigned char expected);
+struct mock_param_matcher *kunit_uchar_ne(struct kunit *test,
+					 unsigned char expected);
+struct mock_param_matcher *kunit_uchar_le(struct kunit *test,
+					 unsigned char expected);
+struct mock_param_matcher *kunit_uchar_lt(struct kunit *test,
+					 unsigned char expected);
+struct mock_param_matcher *kunit_uchar_ge(struct kunit *test,
+					 unsigned char expected);
+struct mock_param_matcher *kunit_uchar_gt(struct kunit *test,
+					 unsigned char expected);
+
+struct mock_param_matcher *kunit_schar_eq(struct kunit *test,
+					 signed char expected);
+struct mock_param_matcher *kunit_schar_ne(struct kunit *test,
+					 signed char expected);
+struct mock_param_matcher *kunit_schar_le(struct kunit *test,
+					 signed char expected);
+struct mock_param_matcher *kunit_schar_lt(struct kunit *test,
+					 signed char expected);
+struct mock_param_matcher *kunit_schar_ge(struct kunit *test,
+					 signed char expected);
+struct mock_param_matcher *kunit_schar_gt(struct kunit *test,
+					 signed char expected);
+
+struct mock_param_matcher *kunit_short_eq(struct kunit *test, short expected);
+struct mock_param_matcher *kunit_short_ne(struct kunit *test, short expected);
+struct mock_param_matcher *kunit_short_le(struct kunit *test, short expected);
+struct mock_param_matcher *kunit_short_lt(struct kunit *test, short expected);
+struct mock_param_matcher *kunit_short_ge(struct kunit *test, short expected);
+struct mock_param_matcher *kunit_short_gt(struct kunit *test, short expected);
+
+struct mock_param_matcher *kunit_ushort_eq(struct kunit *test,
+					  unsigned short expected);
+struct mock_param_matcher *kunit_ushort_ne(struct kunit *test,
+					  unsigned short expected);
+struct mock_param_matcher *kunit_ushort_le(struct kunit *test,
+					  unsigned short expected);
+struct mock_param_matcher *kunit_ushort_lt(struct kunit *test,
+					  unsigned short expected);
+struct mock_param_matcher *kunit_ushort_ge(struct kunit *test,
+					  unsigned short expected);
+struct mock_param_matcher *kunit_ushort_gt(struct kunit *test,
+					  unsigned short expected);
+
+struct mock_param_matcher *kunit_int_eq(struct kunit *test, int expected);
+struct mock_param_matcher *kunit_int_ne(struct kunit *test, int expected);
+struct mock_param_matcher *kunit_int_lt(struct kunit *test, int expected);
+struct mock_param_matcher *kunit_int_le(struct kunit *test, int expected);
+struct mock_param_matcher *kunit_int_gt(struct kunit *test, int expected);
+struct mock_param_matcher *kunit_int_ge(struct kunit *test, int expected);
+
+struct mock_param_matcher *kunit_uint_eq(struct kunit *test,
+					unsigned int expected);
+struct mock_param_matcher *kunit_uint_ne(struct kunit *test,
+					unsigned int expected);
+struct mock_param_matcher *kunit_uint_lt(struct kunit *test,
+					unsigned int expected);
+struct mock_param_matcher *kunit_uint_le(struct kunit *test,
+					unsigned int expected);
+struct mock_param_matcher *kunit_uint_gt(struct kunit *test,
+					unsigned int expected);
+struct mock_param_matcher *kunit_uint_ge(struct kunit *test,
+					unsigned int expected);
+
+struct mock_param_matcher *kunit_long_eq(struct kunit *test, long expected);
+struct mock_param_matcher *kunit_long_ne(struct kunit *test, long expected);
+struct mock_param_matcher *kunit_long_le(struct kunit *test, long expected);
+struct mock_param_matcher *kunit_long_lt(struct kunit *test, long expected);
+struct mock_param_matcher *kunit_long_ge(struct kunit *test, long expected);
+struct mock_param_matcher *kunit_long_gt(struct kunit *test, long expected);
+
+struct mock_param_matcher *kunit_ulong_eq(struct kunit *test,
+					 unsigned long expected);
+struct mock_param_matcher *kunit_ulong_ne(struct kunit *test,
+					 unsigned long expected);
+struct mock_param_matcher *kunit_ulong_le(struct kunit *test,
+					 unsigned long expected);
+struct mock_param_matcher *kunit_ulong_lt(struct kunit *test,
+					 unsigned long expected);
+struct mock_param_matcher *kunit_ulong_ge(struct kunit *test,
+					 unsigned long expected);
+struct mock_param_matcher *kunit_ulong_gt(struct kunit *test,
+					 unsigned long expected);
+
+struct mock_param_matcher *kunit_longlong_eq(struct kunit *test,
+					    long long expected);
+struct mock_param_matcher *kunit_longlong_ne(struct kunit *test,
+					    long long expected);
+struct mock_param_matcher *kunit_longlong_le(struct kunit *test,
+					    long long expected);
+struct mock_param_matcher *kunit_longlong_lt(struct kunit *test,
+					    long long expected);
+struct mock_param_matcher *kunit_longlong_ge(struct kunit *test,
+					    long long expected);
+struct mock_param_matcher *kunit_longlong_gt(struct kunit *test,
+					    long long expected);
+
+struct mock_param_matcher *kunit_ulonglong_eq(struct kunit *test,
+					     unsigned long long expected);
+struct mock_param_matcher *kunit_ulonglong_ne(struct kunit *test,
+					     unsigned long long expected);
+struct mock_param_matcher *kunit_ulonglong_le(struct kunit *test,
+					     unsigned long long expected);
+struct mock_param_matcher *kunit_ulonglong_lt(struct kunit *test,
+					     unsigned long long expected);
+struct mock_param_matcher *kunit_ulonglong_ge(struct kunit *test,
+					     unsigned long long expected);
+struct mock_param_matcher *kunit_ulonglong_gt(struct kunit *test,
+					     unsigned long long expected);
+
+/* Matches pointers. */
+struct mock_param_matcher *kunit_ptr_eq(struct kunit *test, void *expected);
+struct mock_param_matcher *kunit_ptr_ne(struct kunit *test, void *expected);
+struct mock_param_matcher *kunit_ptr_lt(struct kunit *test, void *expected);
+struct mock_param_matcher *kunit_ptr_le(struct kunit *test, void *expected);
+struct mock_param_matcher *kunit_ptr_gt(struct kunit *test, void *expected);
+struct mock_param_matcher *kunit_ptr_ge(struct kunit *test, void *expected);
+
+/* Matches memory sections and strings. */
+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_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);
+struct mock_action *kunit_u64_return(struct kunit *test, u64 ret);
+struct mock_action *kunit_char_return(struct kunit *test, char ret);
+struct mock_action *kunit_uchar_return(struct kunit *test, unsigned char ret);
+struct mock_action *kunit_schar_return(struct kunit *test, signed char ret);
+struct mock_action *kunit_short_return(struct kunit *test, short ret);
+struct mock_action *kunit_ushort_return(struct kunit *test, unsigned short ret);
+struct mock_action *kunit_int_return(struct kunit *test, int ret);
+struct mock_action *kunit_uint_return(struct kunit *test, unsigned int ret);
+struct mock_action *kunit_long_return(struct kunit *test, long ret);
+struct mock_action *kunit_ulong_return(struct kunit *test, unsigned long ret);
+struct mock_action *kunit_longlong_return(struct kunit *test, long long ret);
+struct mock_action *kunit_ulonglong_return(struct kunit *test,
+					  unsigned long long ret);
+struct mock_action *kunit_ptr_return(struct kunit *test, void *ret);
+
 #endif /* _KUNIT_MOCK_H */
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index a51157970502..a7a3c5e0a8bf 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_KUNIT) +=			kunit.o
 
 kunit-objs +=				test.o \
 					mock.o \
+					common-mocks.o \
 					string-stream.o \
 					assert.o \
 					kunit-stream.o \
diff --git a/lib/kunit/common-mocks.c b/lib/kunit/common-mocks.c
new file mode 100644
index 000000000000..4d8a3c9d5f0f
--- /dev/null
+++ b/lib/kunit/common-mocks.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common KUnit mock call arg matchers and formatters.
+ *
+ * Copyright (C) 2020, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins@google.com>
+ */
+
+#include <linux/kernel.h>
+#include <kunit/mock.h>
+
+static bool match_any(struct mock_param_matcher *pmatcher,
+		      struct kunit_stream *stream,
+		      const void *actual)
+{
+	kunit_stream_add(stream, "don't care");
+	return true;
+}
+
+static struct mock_param_matcher any_matcher = {
+	.match = match_any,
+};
+
+struct mock_param_matcher *kunit_any(struct kunit *test)
+{
+	return &any_matcher;
+}
+
+#define DEFINE_MATCHER_STRUCT(type_name, type)				       \
+		struct mock_##type_name##_matcher {			       \
+			struct mock_param_matcher matcher;		       \
+			type expected;					       \
+		}
+
+#define DEFINE_TO_MATCHER_STRUCT(type_name)				       \
+		struct mock_##type_name##_matcher *			       \
+		to_mock_##type_name##_matcher(				       \
+				struct mock_param_matcher *matcher)	       \
+		{							       \
+			return container_of(matcher,			       \
+					    struct mock_##type_name##_matcher, \
+					    matcher);			       \
+		}
+
+#define DEFINE_MATCH_FUNC(type_name, type, op_name, op)			       \
+		static bool match_##type_name##_##op_name(		       \
+				struct mock_param_matcher *pmatcher,	       \
+				struct kunit_stream *stream,		       \
+				const void *pactual)			       \
+		{							       \
+			struct mock_##type_name##_matcher *matcher =	       \
+				to_mock_##type_name##_matcher(pmatcher);       \
+			type actual = *((type *) pactual);		       \
+			bool matches = actual op matcher->expected;	       \
+									       \
+			if (matches)					       \
+				kunit_stream_add(stream,		       \
+						 "%d "#op" %d",		       \
+						 actual,		       \
+						 matcher->expected);	       \
+			else						       \
+				kunit_stream_add(stream,		       \
+						 "%d not "#op" %d",	       \
+						 actual,		       \
+						 matcher->expected);	       \
+									       \
+			return matches;					       \
+		}
+
+#define DEFINE_MATCH_FACTORY(type_name, type, op_name)			       \
+		struct mock_param_matcher *kunit_##type_name##_##op_name(      \
+				struct kunit *test, type expected)	       \
+		{							       \
+			struct mock_##type_name##_matcher *matcher;	       \
+									       \
+			matcher = kunit_kmalloc(test,			       \
+					       sizeof(*matcher),	       \
+					       GFP_KERNEL);		       \
+			if (!matcher)					       \
+				return NULL;				       \
+									       \
+			matcher->matcher.match = match_##type_name##_##op_name;\
+			matcher->expected = expected;			       \
+			return &matcher->matcher;			       \
+		}
+
+#define DEFINE_MATCHER_WITH_TYPENAME(type_name, type)			       \
+		DEFINE_MATCHER_STRUCT(type_name, type);			       \
+		DEFINE_TO_MATCHER_STRUCT(type_name)			       \
+		DEFINE_MATCH_FUNC(type_name, type, eq, ==)		       \
+		DEFINE_MATCH_FACTORY(type_name, type, eq)		       \
+		DEFINE_MATCH_FUNC(type_name, type, ne, !=)		       \
+		DEFINE_MATCH_FACTORY(type_name, type, ne)		       \
+		DEFINE_MATCH_FUNC(type_name, type, le, <=)		       \
+		DEFINE_MATCH_FACTORY(type_name, type, le)		       \
+		DEFINE_MATCH_FUNC(type_name, type, lt, <)		       \
+		DEFINE_MATCH_FACTORY(type_name, type, lt)		       \
+		DEFINE_MATCH_FUNC(type_name, type, ge, >=)		       \
+		DEFINE_MATCH_FACTORY(type_name, type, ge)		       \
+		DEFINE_MATCH_FUNC(type_name, type, gt, >)		       \
+		DEFINE_MATCH_FACTORY(type_name, type, gt)
+
+#define DEFINE_MATCHER(type) DEFINE_MATCHER_WITH_TYPENAME(type, type)
+
+DEFINE_MATCHER(u8);
+DEFINE_MATCHER(u16);
+DEFINE_MATCHER(u32);
+DEFINE_MATCHER(u64);
+DEFINE_MATCHER(char);
+DEFINE_MATCHER_WITH_TYPENAME(uchar, unsigned char);
+DEFINE_MATCHER_WITH_TYPENAME(schar, signed char);
+DEFINE_MATCHER(short);
+DEFINE_MATCHER_WITH_TYPENAME(ushort, unsigned short);
+DEFINE_MATCHER(int);
+DEFINE_MATCHER_WITH_TYPENAME(uint, unsigned int);
+DEFINE_MATCHER(long);
+DEFINE_MATCHER_WITH_TYPENAME(ulong, unsigned long);
+DEFINE_MATCHER_WITH_TYPENAME(longlong, long long);
+DEFINE_MATCHER_WITH_TYPENAME(ulonglong, unsigned long long);
+
+DEFINE_MATCHER_WITH_TYPENAME(ptr, void *);
+
+struct mock_memeq_matcher {
+	struct mock_param_matcher matcher;
+	const void *expected;
+	size_t size;
+};
+
+static bool match_memeq(struct mock_param_matcher *pmatcher,
+			struct kunit_stream *stream,
+			const void *pactual)
+{
+	struct mock_memeq_matcher *matcher =
+			container_of(pmatcher,
+				     struct mock_memeq_matcher,
+				     matcher);
+	const void *actual = CONVERT_TO_ACTUAL_TYPE(const void *, pactual);
+	bool matches = !memcmp(actual, matcher->expected, matcher->size);
+	int i;
+
+	for (i = 0; i < matcher->size; i++)
+		kunit_stream_add(stream, "%02x, ", ((const char *) actual)[i]);
+	if (matches)
+		kunit_stream_add(stream, "== ");
+	else
+		kunit_stream_add(stream, "!= ");
+	for (i = 0; i < matcher->size; i++)
+		kunit_stream_add(stream,
+			    "%02x, ",
+			    ((const char *) matcher->expected)[i]);
+
+	return matches;
+}
+
+struct mock_param_matcher *kunit_memeq(struct kunit *test,
+				      const void *buf,
+				      size_t size)
+{
+	struct mock_memeq_matcher *matcher;
+
+	matcher = kunit_kzalloc(test, sizeof(*matcher), GFP_KERNEL);
+	if (!matcher)
+		return NULL;
+
+	matcher->matcher.match = match_memeq;
+	matcher->expected = buf;
+	matcher->size = size;
+
+	return &matcher->matcher;
+}
+
+struct mock_streq_matcher {
+	struct mock_param_matcher matcher;
+	const char *expected;
+};
+
+static bool match_streq(struct mock_param_matcher *pmatcher,
+			struct kunit_stream *stream,
+			const void *pactual)
+{
+	struct mock_streq_matcher *matcher =
+			container_of(pmatcher,
+				     struct mock_streq_matcher,
+				     matcher);
+	const char *actual = CONVERT_TO_ACTUAL_TYPE(const char *, pactual);
+	bool matches = !strcmp(actual, matcher->expected);
+
+	if (matches)
+		kunit_stream_add(stream, "%s == %s", actual, matcher->expected);
+	else
+		kunit_stream_add(stream, "%s != %s", actual, matcher->expected);
+
+	return matches;
+}
+
+struct mock_param_matcher *kunit_streq(struct kunit *test, const char *str)
+{
+	struct mock_streq_matcher *matcher;
+
+	matcher = kunit_kzalloc(test, sizeof(*matcher), GFP_KERNEL);
+	if (!matcher)
+		return NULL;
+
+	matcher->matcher.match = match_streq;
+	matcher->expected = str;
+
+	return &matcher->matcher;
+}
+
+#define DEFINE_RETURN_ACTION_STRUCT(type_name, type)			       \
+		struct mock_##type_name##_action {			       \
+			struct mock_action action;			       \
+			type ret;					       \
+		}
+
+#define DEFINE_RETURN_ACTION_FUNC(type_name, type)			       \
+		void *do_##type_name##_return(struct mock_action *paction,     \
+					      const void **params,	       \
+					      int len)			       \
+		{							       \
+			struct mock_##type_name##_action *action =	       \
+					container_of(paction,		       \
+						     struct mock_##type_name##_action,\
+						     action);		       \
+									       \
+			return (void *) &action->ret;			       \
+		}
+
+#define DEFINE_RETURN_ACTION_FACTORY(type_name, type)			       \
+		struct mock_action *kunit_##type_name##_return(		       \
+				struct kunit *test,			       \
+				type ret)				       \
+		{							       \
+			struct mock_##type_name##_action *action;	       \
+									       \
+			action = kunit_kmalloc(test,			       \
+					      sizeof(*action),		       \
+					      GFP_KERNEL);		       \
+			if (!action)					       \
+				return NULL;				       \
+									       \
+			action->action.do_action = do_##type_name##_return;    \
+			action->ret = ret;				       \
+									       \
+			return &action->action;				       \
+		}
+
+#define DEFINE_RETURN_ACTION_WITH_TYPENAME(type_name, type)		       \
+		DEFINE_RETURN_ACTION_STRUCT(type_name, type);		       \
+		DEFINE_RETURN_ACTION_FUNC(type_name, type);		       \
+		DEFINE_RETURN_ACTION_FACTORY(type_name, type)
+
+#define DEFINE_RETURN_ACTION(type) \
+		DEFINE_RETURN_ACTION_WITH_TYPENAME(type, type)
+
+DEFINE_RETURN_ACTION(u8);
+DEFINE_RETURN_ACTION(u16);
+DEFINE_RETURN_ACTION(u32);
+DEFINE_RETURN_ACTION(u64);
+DEFINE_RETURN_ACTION(char);
+DEFINE_RETURN_ACTION_WITH_TYPENAME(uchar, unsigned char);
+DEFINE_RETURN_ACTION_WITH_TYPENAME(schar, signed char);
+DEFINE_RETURN_ACTION(short);
+DEFINE_RETURN_ACTION_WITH_TYPENAME(ushort, unsigned short);
+DEFINE_RETURN_ACTION(int);
+DEFINE_RETURN_ACTION_WITH_TYPENAME(uint, unsigned int);
+DEFINE_RETURN_ACTION(long);
+DEFINE_RETURN_ACTION_WITH_TYPENAME(ulong, unsigned long);
+DEFINE_RETURN_ACTION_WITH_TYPENAME(longlong, long long);
+DEFINE_RETURN_ACTION_WITH_TYPENAME(ulonglong, unsigned long long);
+DEFINE_RETURN_ACTION_WITH_TYPENAME(ptr, void *);
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 09/12] kunit: mock: add macro machinery to pick correct format args
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
                   ` (7 preceding siblings ...)
  2020-10-12 22:20 ` [RFC v2 08/12] kunit: mock: add basic matchers and actions Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 10/12] kunit: mock: add class mocking support Daniel Latypov
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan,
	Marcelo Schmitt

From: Marcelo Schmitt <marcelo.schmitt1@gmail.com>

Note: It was unclear if there was existing code that could be reused.

This is used by DEFINE_MATCHER to generate matching funcs for primitive
types that don't trigger compiler warnings.

After preprocessing, we now generate matcher func code like
  kunit_stream_add(stream, "%p not > "%p", actual, matcher->expected)
as opposed to the hoping %d will work for all types.

Signed-off-by: Marcelo Schmitt <marcelo.schmitt1@gmail.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
---
 lib/kunit/common-mocks.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/lib/kunit/common-mocks.c b/lib/kunit/common-mocks.c
index 4d8a3c9d5f0f..ce8929157ded 100644
--- a/lib/kunit/common-mocks.c
+++ b/lib/kunit/common-mocks.c
@@ -42,6 +42,27 @@ struct mock_param_matcher *kunit_any(struct kunit *test)
 					    matcher);			       \
 		}
 
+#define TYPE_FRMT(type_name) FORMAT_##type_name
+#define FORMAT_u8 "%hu"
+#define FORMAT_u16 "%hu"
+#define FORMAT_u32 "%u"
+#define FORMAT_u64 "%llu"
+#define FORMAT_char "%c"
+#define FORMAT_uchar "%c"
+#define FORMAT_schar "%c"
+#define FORMAT_short "%hd"
+#define FORMAT_ushort "%hu"
+#define FORMAT_int "%d"
+#define FORMAT_uint "%u"
+#define FORMAT_long "%ld"
+#define FORMAT_ulong "%lu"
+#define FORMAT_longlong "%lld"
+#define FORMAT_ulonglong "%llu"
+#define FORMAT_ptr "%p"
+
+#define CMP_FORMAT(type_name, msg, op)                                        \
+	       TYPE_FRMT(type_name) msg " " #op " " TYPE_FRMT(type_name)
+
 #define DEFINE_MATCH_FUNC(type_name, type, op_name, op)			       \
 		static bool match_##type_name##_##op_name(		       \
 				struct mock_param_matcher *pmatcher,	       \
@@ -55,12 +76,12 @@ struct mock_param_matcher *kunit_any(struct kunit *test)
 									       \
 			if (matches)					       \
 				kunit_stream_add(stream,		       \
-						 "%d "#op" %d",		       \
+						 CMP_FORMAT(type_name, "", op),\
 						 actual,		       \
 						 matcher->expected);	       \
 			else						       \
 				kunit_stream_add(stream,		       \
-						 "%d not "#op" %d",	       \
+						 CMP_FORMAT(type_name, " not", op), \
 						 actual,		       \
 						 matcher->expected);	       \
 									       \
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 10/12] kunit: mock: add class mocking support
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
                   ` (8 preceding siblings ...)
  2020-10-12 22:20 ` [RFC v2 09/12] kunit: mock: add macro machinery to pick correct format args Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 11/12] kunit: mock: add struct param matcher Daniel Latypov
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

From: Brendan Higgins <brendanhiggins@google.com>

Introduce basic class mocking, the ability to automatically generate a
Linux C-style class implementation whose behavior is controlled by test
cases, which can also set expectations on when and how mocks are called.

Co-developed-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
---
 include/kunit/mock.h           | 433 +++++++++++++++++++++++++++++++++
 lib/kunit/Makefile             |   5 +-
 lib/kunit/kunit-example-test.c |  98 ++++++++
 lib/kunit/mock-macro-test.c    |  91 +++++++
 lib/kunit/mock-test.c          | 320 ++++++++++++++++++++++++
 5 files changed, 945 insertions(+), 2 deletions(-)
 create mode 100644 lib/kunit/mock-test.c

diff --git a/include/kunit/mock.h b/include/kunit/mock.h
index 13fdeb8730b5..9252a0abd295 100644
--- a/include/kunit/mock.h
+++ b/include/kunit/mock.h
@@ -123,6 +123,439 @@ struct mock_expectation *mock_add_matcher(struct mock *mock,
 					  struct mock_param_matcher *matchers[],
 					  int len);
 
+#define MOCK(name) name##_mock
+
+/**
+ * KUNIT_EXPECT_CALL() - Declares a *call expectation* on a mock function.
+ * @expectation_call: a mocked method or function with parameters replaced with
+ *                    matchers.
+ *
+ * Example:
+ *
+ * .. code-block:: c
+ *
+ *	// Class to mock.
+ *	struct example {
+ *		int (*foo)(struct example *, int);
+ *	};
+ *
+ *	// Define the mock.
+ *	DECLARE_STRUCT_CLASS_MOCK_PREREQS(example);
+ *
+ *	DEFINE_STRUCT_CLASS_MOCK(METHOD(foo), CLASS(example),
+ *				 RETURNS(int),
+ *				 PARAMS(struct example *, int));
+ *
+ *	static int example_init(struct MOCK(example) *mock_example)
+ *	{
+ *		struct example *example = mock_get_trgt(mock_example);
+ *
+ *		example->foo = foo;
+ *		return 0;
+ *	}
+ *
+ *	DEFINE_STRUCT_CLASS_MOCK_INIT(example, example_init);
+ *
+ *	static void foo_example_test_success(struct kunit *test)
+ *	{
+ *		struct MOCK(example) *mock_example;
+ *		struct example *example = mock_get_trgt(mock_example);
+ *		struct mock_expectation *handle;
+ *
+ *		mock_example = CONSTRUCT_MOCK(example, test);
+ *
+ *		handle = KUNIT_EXPECT_CALL(foo(mock_get_ctrl(mock_example),
+ *					       kunit_int_eq(test, 5)));
+ *		handle->action = int_return(test, 2);
+ *
+ *		KUNIT_EXPECT_EQ(test, 2, example_bar(example, 5));
+ *	}
+ *
+ * Return:
+ * A &struct mock_expectation representing the call expectation.
+ * allowing additional conditions and actions to be specified.
+ */
+#define KUNIT_EXPECT_CALL(expectation_call) mock_master_##expectation_call
+
+#define mock_get_ctrl_internal(mock_object) (&(mock_object)->ctrl)
+#define mock_get_ctrl(mock_object) mock_get_ctrl_internal(mock_object)
+
+#define mock_get_trgt_internal(mock_object) (&(mock_object)->trgt)
+#define mock_get_trgt(mock_object) mock_get_trgt_internal(mock_object)
+
+#define mock_get_test(mock_object) (mock_get_ctrl(mock_object)->test)
+
+#define CLASS(struct_name) struct_name
+#define HANDLE_INDEX(index) index
+#define METHOD(method_name) method_name
+#define RETURNS(return_type) return_type
+/* #define PARAMS(...) __VA_ARGS__ included by linux/tracepoint.h */
+
+#define MOCK_INIT_ID(struct_name) struct_name##mock_init
+#define REAL_ID(func_name) __real__##func_name
+#define INVOKE_ID(func_name) __invoke__##func_name
+
+#define DECLARE_MOCK_CLIENT(name, return_type, param_types...) \
+		return_type name(PARAM_LIST_FROM_TYPES(param_types))
+
+#define DECLARE_MOCK_MASTER(name, ctrl_index, param_types...)		       \
+		struct mock_expectation *mock_master_##name(		       \
+				MATCHER_PARAM_LIST_FROM_TYPES(ctrl_index,      \
+							      param_types))
+
+#define DECLARE_MOCK_COMMON(name, handle_index, return_type, param_types...)   \
+		DECLARE_MOCK_CLIENT(name, return_type, param_types);	       \
+		DECLARE_MOCK_MASTER(name, handle_index, param_types)
+
+#define DECLARE_STRUCT_CLASS_MOCK_STRUCT(struct_name)			       \
+		struct MOCK(struct_name) {				       \
+			struct mock		ctrl;			       \
+			struct struct_name	trgt;			       \
+		}
+
+#define DECLARE_STRUCT_CLASS_MOCK_CONVERTER(struct_name)		       \
+		static inline struct mock *from_##struct_name##_to_mock(       \
+				const struct struct_name *trgt)		       \
+		{							       \
+			return mock_get_ctrl(				       \
+					container_of(trgt,		       \
+						     struct MOCK(struct_name), \
+						     trgt));		       \
+		}
+
+/**
+ * DECLARE_STRUCT_CLASS_MOCK_PREREQS() - Create a mock child class
+ * @struct_name: name of the class/struct to be mocked
+ *
+ * Creates a mock child class of ``struct_name`` named
+ * ``struct MOCK(struct_name)`` along with supporting internally used methods.
+ *
+ * See KUNIT_EXPECT_CALL() for example usages.
+ */
+#define DECLARE_STRUCT_CLASS_MOCK_PREREQS(struct_name)			       \
+		DECLARE_STRUCT_CLASS_MOCK_STRUCT(struct_name);		       \
+		DECLARE_STRUCT_CLASS_MOCK_CONVERTER(struct_name)
+
+#define DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name,		       \
+							struct_name,	       \
+							handle_index,	       \
+							return_type,	       \
+							param_types...)	       \
+		DECLARE_MOCK_COMMON(name,				       \
+				    handle_index,			       \
+				    return_type,			       \
+				    param_types)
+
+#define DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX(name,			       \
+					       struct_name,		       \
+					       handle_index,		       \
+					       return_type,		       \
+					       param_types...)		       \
+		DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name,	       \
+								struct_name,   \
+								handle_index,  \
+								return_type,   \
+								param_types)
+
+/**
+ * DECLARE_STRUCT_CLASS_MOCK()
+ * @name: method name
+ * @struct_name: name of the class/struct
+ * @return_type: return type of the method
+ * @param_types: parameters of the method
+ *
+ * Same as DEFINE_STRUCT_CLASS_MOCK(), but only makes header compatible
+ * declarations.
+ */
+#define DECLARE_STRUCT_CLASS_MOCK(name,					       \
+				  struct_name,				       \
+				  return_type,				       \
+				  param_types...)			       \
+		DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX(name,		       \
+						       struct_name,	       \
+						       0,		       \
+						       return_type,	       \
+						       param_types)
+
+/**
+ * DECLARE_STRUCT_CLASS_MOCK_VOID_RETURN()
+ * @name: method name
+ * @struct_name: name of the class/struct
+ * @param_types: parameters of the method
+ *
+ * Same as DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN(), but only makes header
+ * compatible declarations.
+ */
+#define DECLARE_STRUCT_CLASS_MOCK_VOID_RETURN(name,			       \
+					      struct_name,		       \
+					      param_types...)		       \
+		DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX(name,		       \
+						       struct_name,	       \
+						       0,		       \
+						       void,		       \
+						       param_types)
+
+/**
+ * DECLARE_STRUCT_CLASS_MOCK_INIT()
+ * @struct_name: name of the class/struct
+ *
+ * Same as DEFINE_STRUCT_CLASS_MOCK_INIT(), but only makes header compatible
+ * declarations.
+ */
+#define DECLARE_STRUCT_CLASS_MOCK_INIT(struct_name)			       \
+		struct MOCK(struct_name) *MOCK_INIT_ID(struct_name)(	       \
+				struct kunit *test)
+
+/**
+ * CONSTRUCT_MOCK()
+ * @struct_name: name of the class
+ * @test: associated test
+ *
+ * Constructs and allocates a test managed ``struct MOCK(struct_name)`` given
+ * the name of the class for which the mock is defined and a test object.
+ *
+ * See KUNIT_EXPECT_CALL() for example usage.
+ */
+#define CONSTRUCT_MOCK(struct_name, test) MOCK_INIT_ID(struct_name)(test)
+
+#define DEFINE_MOCK_CLIENT_COMMON(name,					       \
+				  handle_index,				       \
+				  MOCK_SOURCE,				       \
+				  mock_source_ctx,			       \
+				  return_type,				       \
+				  RETURN,				       \
+				  param_types...)			       \
+		return_type name(PARAM_LIST_FROM_TYPES(param_types))	       \
+		{							       \
+			struct mock *mock = MOCK_SOURCE(mock_source_ctx,       \
+							handle_index);	       \
+			static const char * const param_type_names[] = {       \
+				TYPE_NAMES_FROM_TYPES(handle_index,	       \
+						      param_types)	       \
+			};						       \
+			const void *params[] = {			       \
+				PTR_TO_ARG_FROM_TYPES(handle_index,	       \
+						      param_types)	       \
+			};						       \
+			const void *retval;				       \
+									       \
+			retval = mock->do_expect(mock,			       \
+						 #name,			       \
+						 name,			       \
+						 param_type_names,	       \
+						 params,		       \
+						 ARRAY_SIZE(params));	       \
+			KUNIT_ASSERT_NOT_ERR_OR_NULL(mock->test, retval);      \
+			if (!retval) {					       \
+				kunit_info(mock->test,			       \
+					   "no action installed for "#name"\n");\
+				BUG();					       \
+			}						       \
+			RETURN(return_type, retval);			       \
+		}
+
+#define CLASS_MOCK_CLIENT_SOURCE(ctx, handle_index) ctx(arg##handle_index)
+#define DEFINE_MOCK_METHOD_CLIENT_COMMON(name,				       \
+					 handle_index,			       \
+					 mock_converter,		       \
+					 return_type,			       \
+					 RETURN,			       \
+					 param_types...)		       \
+		DEFINE_MOCK_CLIENT_COMMON(name,				       \
+					  handle_index,			       \
+					  CLASS_MOCK_CLIENT_SOURCE,	       \
+					  mock_converter,		       \
+					  return_type,			       \
+					  RETURN,			       \
+					  param_types)
+
+#define CAST_AND_RETURN(return_type, retval) return *((return_type *) retval)
+#define NO_RETURN(return_type, retval)
+
+#define DEFINE_MOCK_METHOD_CLIENT(name,					       \
+				  handle_index,				       \
+				  mock_converter,			       \
+				  return_type,				       \
+				  param_types...)			       \
+		DEFINE_MOCK_METHOD_CLIENT_COMMON(name,			       \
+						 handle_index,		       \
+						 mock_converter,	       \
+						 return_type,		       \
+						 CAST_AND_RETURN,	       \
+						 param_types)
+
+#define DEFINE_MOCK_METHOD_CLIENT_VOID_RETURN(name,			       \
+					      handle_index,		       \
+					      mock_converter,		       \
+					      param_types...)		       \
+		DEFINE_MOCK_METHOD_CLIENT_COMMON(name,			       \
+						 handle_index,		       \
+						 mock_converter,	       \
+						 void,			       \
+						 NO_RETURN,		       \
+						 param_types)
+
+#define DEFINE_MOCK_MASTER_COMMON_INTERNAL(name,			       \
+					   ctrl_index,			       \
+					   MOCK_SOURCE,			       \
+					   param_types...)		       \
+		struct mock_expectation *mock_master_##name(		       \
+				MATCHER_PARAM_LIST_FROM_TYPES(ctrl_index,      \
+							      param_types))    \
+		{ \
+			struct mock_param_matcher *matchers[] = {	       \
+				ARG_NAMES_FROM_TYPES(ctrl_index, param_types)  \
+			};						       \
+									       \
+			return mock_add_matcher(MOCK_SOURCE(ctrl_index),       \
+						#name,			       \
+						(const void *) name,	       \
+						matchers,		       \
+						ARRAY_SIZE(matchers));	       \
+		}
+#define DEFINE_MOCK_MASTER_COMMON(name,					       \
+				  ctrl_index,				       \
+				  MOCK_SOURCE,				       \
+				  param_types...)			       \
+		DEFINE_MOCK_MASTER_COMMON_INTERNAL(name,		       \
+						   ctrl_index,		       \
+						   MOCK_SOURCE,		       \
+						   param_types)
+
+#define CLASS_MOCK_MASTER_SOURCE(ctrl_index) arg##ctrl_index
+#define DEFINE_MOCK_METHOD_MASTER(name, ctrl_index, param_types...)	       \
+		DEFINE_MOCK_MASTER_COMMON(name,				       \
+					  ctrl_index,			       \
+					  CLASS_MOCK_MASTER_SOURCE,	       \
+					  param_types)
+
+#define DEFINE_MOCK_COMMON(name,					       \
+			   handle_index,				       \
+			   mock_converter,				       \
+			   return_type,					       \
+			   param_types...)				       \
+		DEFINE_MOCK_METHOD_CLIENT(name,				       \
+					  handle_index,			       \
+					  mock_converter,		       \
+					  return_type,			       \
+					  param_types);			       \
+		DEFINE_MOCK_METHOD_MASTER(name, handle_index, param_types)
+
+#define DEFINE_MOCK_COMMON_VOID_RETURN(name,				       \
+				       handle_index,			       \
+				       mock_converter,			       \
+				       param_types...)			       \
+		DEFINE_MOCK_METHOD_CLIENT_VOID_RETURN(name,		       \
+						      handle_index,	       \
+						      mock_converter,	       \
+						      param_types);	       \
+		DEFINE_MOCK_METHOD_MASTER(name, handle_index, param_types)
+
+#define DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name,		       \
+						       struct_name,	       \
+						       handle_index,	       \
+						       return_type,	       \
+						       param_types...)	       \
+		DEFINE_MOCK_COMMON(name,				       \
+				   handle_index,			       \
+				   from_##struct_name##_to_mock,	       \
+				   return_type, param_types)
+#define DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX(name,			       \
+					      struct_name,		       \
+					      handle_index,		       \
+					      return_type,		       \
+					      param_types...)		       \
+		DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name,	       \
+							       struct_name,    \
+							       handle_index,   \
+							       return_type,    \
+							       param_types)
+
+#define DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_VOID_RETURN_INTERNAL(	       \
+		name,							       \
+		struct_name,						       \
+		handle_index,						       \
+		param_types...)						       \
+		DEFINE_MOCK_COMMON_VOID_RETURN(name,			       \
+					       handle_index,		       \
+					       from_##struct_name##_to_mock,   \
+					       param_types)
+#define DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_VOID_RETURN(name,		       \
+							  struct_name,	       \
+							  handle_index,	       \
+							  param_types...)      \
+		DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_VOID_RETURN_INTERNAL(    \
+				name,					       \
+				struct_name,				       \
+				handle_index,				       \
+				param_types)
+
+/**
+ * DEFINE_STRUCT_CLASS_MOCK()
+ * @name: name of the method
+ * @struct_name: name of the class of which the method belongs
+ * @return_type: return type of the method to be created. **Must not be void.**
+ * @param_types: parameters to method to be created.
+ *
+ * See KUNIT_EXPECT_CALL() for example usage.
+ */
+#define DEFINE_STRUCT_CLASS_MOCK(name,					       \
+				 struct_name,				       \
+				 return_type,				       \
+				 param_types...)			       \
+		DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX(name,		       \
+						      struct_name,	       \
+						      0,		       \
+						      return_type,	       \
+						      param_types)
+
+/**
+ * DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN()
+ * @name: name of the method
+ * @struct_name: name of the class of which the method belongs
+ * @param_types: parameters to method to be created.
+ *
+ * Same as DEFINE_STRUCT_CLASS_MOCK() except the method has a ``void`` return
+ * type.
+ */
+#define DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN(name, struct_name, param_types...)\
+		DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_VOID_RETURN(name,	       \
+								  struct_name, \
+								  0,	       \
+								  param_types)
+
+/**
+ * DEFINE_STRUCT_CLASS_MOCK_INIT()
+ * @struct_name: name of the class
+ * @init_func: a function of type ``int (*)(struct kunit *, struct MOCK(struct_name) *)``.
+ *             The function is passed a pointer to an allocated, *but not
+ *             initialized*, ``struct MOCK(struct_name)``. The job of this user
+ *             provided function is to perform remaining initialization. Usually
+ *             this entails assigning mock methods to the function pointers in
+ *             the parent struct.
+ *
+ * See KUNIT_EXPECT_CALL() for example usage.
+ */
+#define DEFINE_STRUCT_CLASS_MOCK_INIT(struct_name, init_func)		       \
+		struct MOCK(struct_name) *MOCK_INIT_ID(struct_name)(	       \
+				struct kunit *test)			       \
+		{							       \
+			struct MOCK(struct_name) *mock_obj;		       \
+									       \
+			mock_obj = kunit_kzalloc(test,			       \
+						sizeof(*mock_obj),	       \
+						GFP_KERNEL);		       \
+			if (!mock_obj)					       \
+				return NULL;				       \
+									       \
+			mock_init_ctrl(test, mock_get_ctrl(mock_obj));	       \
+									       \
+			if (init_func(test, mock_obj))			       \
+				return NULL;				       \
+									       \
+			return mock_obj;				       \
+		}
+
 #define CONVERT_TO_ACTUAL_TYPE(type, ptr) (*((type *) ptr))
 
 /**
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index a7a3c5e0a8bf..649e1c1f0d00 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -13,11 +13,12 @@ kunit-objs +=				debugfs.o
 endif
 
 obj-$(CONFIG_KUNIT_TEST) +=		kunit-test.o \
-					mock-macro-test.o
+					mock-macro-test.o \
+					mock-test.o
 
 # string-stream-test compiles built-in only.
 ifeq ($(CONFIG_KUNIT_TEST),y)
 obj-$(CONFIG_KUNIT_TEST) +=		string-stream-test.o
 endif
 
-obj-$(CONFIG_KUNIT_EXAMPLE_TEST) +=	kunit-example-test.o
+obj-$(CONFIG_KUNIT_EXAMPLE_TEST)	+= kunit-example-test.o
diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
index be1164ecc476..d6fff5a961ce 100644
--- a/lib/kunit/kunit-example-test.c
+++ b/lib/kunit/kunit-example-test.c
@@ -7,6 +7,7 @@
  */
 
 #include <kunit/test.h>
+#include <kunit/mock.h>
 
 /*
  * This is the most fundamental element of KUnit, the test case. A test case
@@ -29,6 +30,92 @@ static void example_simple_test(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, 1 + 1, 2);
 }
 
+struct example_ops;
+
+struct example {
+  struct example_ops *ops;
+};
+
+/*
+ * A lot of times, we embed "ops structs", which acts an abstraction over
+ * hardware, a file system implementation, or some other subsystem that you
+ * want to reason about in a generic way.
+ */
+struct example_ops {
+	int (*foo)(struct example *example, int num);
+};
+
+static int example_bar(struct example *example, int num)
+{
+	return example->ops->foo(example, num);
+}
+
+/*
+ * KUnit allows such a class to be "mocked out" with the following:
+ */
+
+/*
+ * This macro creates a mock subclass of the specified class.
+ */
+DECLARE_STRUCT_CLASS_MOCK_PREREQS(example);
+
+/*
+ * This macro creates a mock implementation of the specified method of the
+ * specified class.
+ */
+DEFINE_STRUCT_CLASS_MOCK(METHOD(foo), CLASS(example),
+			 RETURNS(int),
+			 PARAMS(struct example *, int));
+
+/*
+ * This tells KUnit how to initialize the parts of the mock that come from the
+ * parent. In this example, all we have to do is populate the member functions
+ * of the parent class with the mock versions we defined.
+ */
+static int example_init(struct kunit *test, struct MOCK(example) *mock_example)
+{
+	/* This is how you get a pointer to the parent class of a mock. */
+	struct example *example = mock_get_trgt(mock_example);
+
+	/*
+	 * Here we create an ops struct containing our mock method instead.
+	 */
+	example->ops = kunit_kzalloc(test, sizeof(*example->ops), GFP_KERNEL);
+	example->ops->foo = foo;
+
+	return 0;
+}
+
+/*
+ * This registers our parent init function above, allowing KUnit to create a
+ * constructor for the mock.
+ */
+DEFINE_STRUCT_CLASS_MOCK_INIT(example, example_init);
+
+/*
+ * This is a test case where we use our mock.
+ */
+static void example_mock_test(struct kunit *test)
+{
+	struct MOCK(example) *mock_example = test->priv;
+	struct example *example = mock_get_trgt(mock_example);
+	struct mock_expectation *handle;
+
+	/*
+	 * Here we make an expectation that our mock method will be called with
+	 * a parameter equal to 5 passed in.
+	 */
+	handle = KUNIT_EXPECT_CALL(foo(mock_get_ctrl(mock_example),
+				       kunit_int_eq(test, 5)));
+	/*
+	 * We specify that when our mock is called in this way, we want it to
+	 * return 2.
+	 */
+	handle->action = kunit_int_return(test, 2);
+
+	KUNIT_EXPECT_EQ(test, 2, example_bar(example, 5));
+}
+
 /*
  * This is run once before each test case, see the comment on
  * example_test_suite for more information.
@@ -37,6 +124,16 @@ static int example_test_init(struct kunit *test)
 {
 	kunit_info(test, "initializing\n");
 
+	/*
+	 * Here we construct the mock and store it in test's `priv` field; this
+	 * field is for KUnit users. You can put whatever you want here, but
+	 * most often it is a place that the init function can put stuff to be
+	 * used by test cases.
+	 */
+	test->priv = CONSTRUCT_MOCK(example, test);
+	if (!test->priv)
+		return -EINVAL;
+
 	return 0;
 }
 
@@ -52,6 +149,7 @@ static struct kunit_case example_test_cases[] = {
 	 * test suite.
 	 */
 	KUNIT_CASE(example_simple_test),
+	KUNIT_CASE(example_mock_test),
 	{}
 };
 
diff --git a/lib/kunit/mock-macro-test.c b/lib/kunit/mock-macro-test.c
index 6c3dc2193edb..d196dbee2407 100644
--- a/lib/kunit/mock-macro-test.c
+++ b/lib/kunit/mock-macro-test.c
@@ -8,6 +8,55 @@
 
 #include <kunit/test.h>
 #include <kunit/params.h>
+#include <kunit/mock.h>
+
+struct mock_macro_dummy_struct {
+	int (*one_param)(struct mock_macro_dummy_struct *test_struct);
+	int (*two_param)(struct mock_macro_dummy_struct *test_struct, int num);
+	int (*non_first_slot_param)(
+			int num,
+			struct mock_macro_dummy_struct *test_struct);
+	void *(*void_ptr_return)(struct mock_macro_dummy_struct *test_struct);
+};
+
+DECLARE_STRUCT_CLASS_MOCK_PREREQS(mock_macro_dummy_struct);
+
+DEFINE_STRUCT_CLASS_MOCK(METHOD(one_param), CLASS(mock_macro_dummy_struct),
+			 RETURNS(int),
+			 PARAMS(struct mock_macro_dummy_struct *));
+
+DEFINE_STRUCT_CLASS_MOCK(METHOD(two_param), CLASS(mock_macro_dummy_struct),
+			 RETURNS(int),
+			 PARAMS(struct mock_macro_dummy_struct *, int));
+
+DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX(METHOD(non_first_slot_param),
+			 CLASS(mock_macro_dummy_struct), HANDLE_INDEX(1),
+			 RETURNS(int),
+			 PARAMS(int, struct mock_macro_dummy_struct *));
+
+DEFINE_STRUCT_CLASS_MOCK(METHOD(void_ptr_return),
+			 CLASS(mock_macro_dummy_struct),
+			 RETURNS(void *),
+			 PARAMS(struct mock_macro_dummy_struct *));
+
+static int mock_macro_dummy_struct_init(
+		struct MOCK(mock_macro_dummy_struct) *mock_test_struct)
+{
+	struct mock_macro_dummy_struct *test_struct =
+			mock_get_trgt(mock_test_struct);
+
+	test_struct->one_param = one_param;
+	test_struct->two_param = two_param;
+	test_struct->non_first_slot_param = non_first_slot_param;
+	return 0;
+}
+
+DEFINE_STRUCT_CLASS_MOCK_INIT(mock_macro_dummy_struct,
+			      mock_macro_dummy_struct_init);
+
+struct mock_macro_context {
+	struct MOCK(mock_macro_dummy_struct) *mock_test_struct;
+};
 
 #define TO_STR_INTERNAL(...) #__VA_ARGS__
 #define TO_STR(...) TO_STR_INTERNAL(__VA_ARGS__)
@@ -132,6 +181,46 @@ static void mock_macro_arg_names_from_types(struct kunit *test)
 						      type15)));
 }
 
+static void mock_macro_test_generated_method_code_works(struct kunit *test)
+{
+	struct mock_macro_context *ctx = test->priv;
+	struct MOCK(mock_macro_dummy_struct) *mock_test_struct =
+			ctx->mock_test_struct;
+	struct mock_macro_dummy_struct *test_struct =
+			mock_get_trgt(mock_test_struct);
+	struct mock_expectation *handle;
+
+	handle = KUNIT_EXPECT_CALL(one_param(mock_get_ctrl(mock_test_struct)));
+	handle->action = kunit_int_return(test, 0);
+	handle = KUNIT_EXPECT_CALL(two_param(mock_get_ctrl(mock_test_struct),
+					     kunit_int_eq(test, 5)));
+	handle->action = kunit_int_return(test, 1);
+	handle = KUNIT_EXPECT_CALL(non_first_slot_param(
+			kunit_int_eq(test, 5),
+			mock_get_ctrl(mock_test_struct)));
+	handle->action = kunit_int_return(test, 1);
+
+	test_struct->one_param(test_struct);
+	test_struct->two_param(test_struct, 5);
+	test_struct->non_first_slot_param(5, test_struct);
+}
+
+static int mock_macro_test_init(struct kunit *test)
+{
+	struct mock_macro_context *ctx;
+
+	ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+	test->priv = ctx;
+
+	ctx->mock_test_struct = CONSTRUCT_MOCK(mock_macro_dummy_struct, test);
+	if (!ctx->mock_test_struct)
+		return -EINVAL;
+
+	return 0;
+}
+
 static struct kunit_case mock_macro_test_cases[] = {
 	KUNIT_CASE(mock_macro_is_equal),
 	KUNIT_CASE(mock_macro_if),
@@ -140,11 +229,13 @@ static struct kunit_case mock_macro_test_cases[] = {
 	KUNIT_CASE(mock_macro_for_each_param),
 	KUNIT_CASE(mock_macro_param_list_from_types_basic),
 	KUNIT_CASE(mock_macro_arg_names_from_types),
+	KUNIT_CASE(mock_macro_test_generated_method_code_works),
 	{}
 };
 
 static struct kunit_suite mock_macro_test_suite = {
 	.name = "mock-macro-test",
+	.init = mock_macro_test_init,
 	.test_cases = mock_macro_test_cases,
 };
 kunit_test_suite(mock_macro_test_suite);
diff --git a/lib/kunit/mock-test.c b/lib/kunit/mock-test.c
new file mode 100644
index 000000000000..8a0fa33d087c
--- /dev/null
+++ b/lib/kunit/mock-test.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for mock.h.
+ *
+ * Copyright (C) 2020, Google LLC.
+ * Author: Brendan Higgins <brendanhiggins@google.com>
+ */
+
+#include <kunit/test.h>
+#include <kunit/mock.h>
+
+// A simple class for unit-testing/example purposes.
+struct adder {
+	int (*add)(struct adder *adder, int x, int y);
+};
+
+static int real_add(struct adder *adder, int x, int y)
+{
+	return x + y;
+}
+
+static void adder_real_init(struct adder *adder)
+{
+	  adder->add = real_add;
+}
+
+DECLARE_STRUCT_CLASS_MOCK_PREREQS(adder);
+DEFINE_STRUCT_CLASS_MOCK(METHOD(mock_add), CLASS(adder), RETURNS(int),
+				     PARAMS(struct adder*, int, int));
+DECLARE_STRUCT_CLASS_MOCK_INIT(adder);
+
+// This would normally live in the .c file.
+static int adder_mock_init(struct MOCK(adder) *mock_adder)
+{
+	struct adder *real = mock_get_trgt(mock_adder);
+
+	adder_real_init(real);
+
+	real->add = mock_add;
+	mock_set_default_action(mock_get_ctrl(mock_adder),
+				"mock_add",
+				mock_add,
+				kunit_int_return(mock_get_test(mock_adder), 0));
+	return 0;
+}
+DEFINE_STRUCT_CLASS_MOCK_INIT(adder, adder_mock_init);
+
+
+/*
+ * Note: we create a new `failing_test` so we can validate that failed mock
+ * expectations mark tests as failed.
+ * Marking the real `test` as failed is obviously problematic.
+ *
+ * See mock_test_failed_expect_call_fails_test for an example.
+ */
+struct mock_test_context {
+	struct kunit *failing_test;
+	struct mock  *mock;
+};
+
+static void mock_test_do_expect_basic(struct kunit *test)
+{
+	struct mock_test_context *ctx = test->priv;
+	struct mock *mock = ctx->mock;
+	int param0 = 5, param1 = -4;
+	static const char * const two_param_types[] = {"int", "int"};
+	const void *two_params[] = {&param0, &param1};
+	struct mock_param_matcher *matchers_any_two[] = {
+		kunit_any(test), kunit_any(test)
+	};
+	struct mock_expectation *expectation;
+	const void *ret;
+
+	expectation = mock_add_matcher(mock,
+				       "",
+				       NULL,
+				       matchers_any_two,
+				       ARRAY_SIZE(matchers_any_two));
+	expectation->action = kunit_int_return(test, 5);
+	KUNIT_EXPECT_EQ(test, 0, expectation->times_called);
+
+	ret = mock->do_expect(mock,
+			      "",
+			      NULL,
+			      two_param_types,
+			      two_params,
+			      ARRAY_SIZE(two_params));
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ret);
+	KUNIT_EXPECT_EQ(test, 5, *((int *) ret));
+	KUNIT_EXPECT_EQ(test, 1, expectation->times_called);
+}
+
+static void mock_test_ptr_eq(struct kunit *test)
+{
+	struct mock_test_context *ctx = test->priv;
+	struct kunit *failing_test = ctx->failing_test;
+	struct mock *mock = ctx->mock;
+	void *param0 = ctx, *param1 = failing_test;
+	static const char * const two_param_types[] = {"void *", "void *"};
+	const void *two_params[] = {&param0, &param1};
+	struct mock_param_matcher *matchers_two_ptrs[] = {
+		kunit_ptr_eq(test, param0), kunit_ptr_eq(test, param1)
+	};
+	struct mock_expectation *expectation;
+	const void *ret;
+
+	expectation = mock_add_matcher(mock,
+				       "",
+				       NULL,
+				       matchers_two_ptrs,
+				       ARRAY_SIZE(matchers_two_ptrs));
+	expectation->action = kunit_int_return(test, 0);
+	KUNIT_EXPECT_EQ(test, 0, expectation->times_called);
+
+	ret = mock->do_expect(mock,
+			      "",
+			      NULL,
+			      two_param_types,
+			      two_params,
+			      ARRAY_SIZE(two_params));
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ret);
+	KUNIT_EXPECT_EQ(test, 1, expectation->times_called);
+}
+
+static void mock_test_ptr_eq_not_equal(struct kunit *test)
+{
+	struct mock_test_context *ctx = test->priv;
+	struct kunit *failing_test = ctx->failing_test;
+	struct mock *mock = ctx->mock;
+
+	/* Pick some two pointers, but pass in different values. */
+	void *param0 = test, *param1 = failing_test;
+	static const char * const two_param_types[] = {"void *", "void *"};
+	const void *two_params[] = {&param0, &param1};
+	struct mock_param_matcher *matchers_two_ptrs[] = {
+		kunit_ptr_eq(failing_test, param0),
+		kunit_ptr_eq(failing_test, param1 - 1)
+	};
+	struct mock_expectation *expectation;
+	const void *ret;
+
+	expectation = mock_add_matcher(mock,
+				       "",
+				       NULL,
+				       matchers_two_ptrs,
+				       ARRAY_SIZE(matchers_two_ptrs));
+	expectation->action = kunit_int_return(failing_test, 0);
+	KUNIT_EXPECT_EQ(test, 0, expectation->times_called);
+
+	ret = mock->do_expect(mock,
+			      "",
+			      NULL,
+			      two_param_types,
+			      two_params,
+			      ARRAY_SIZE(two_params));
+	KUNIT_EXPECT_FALSE(test, ret);
+	KUNIT_EXPECT_EQ(test, 0, expectation->times_called);
+
+	KUNIT_EXPECT_FALSE(test, failing_test->success);
+}
+
+/*
+ * In order for us to be able to rely on KUNIT_EXPECT_CALL to validate other
+ * behavior, we need to test that unsatisfied KUNIT_EXPECT_CALL causes a test
+ * failure.
+ *
+ * In order to understand what this test is testing we must first understand how
+ * KUNIT_EXPECT_CALL() works conceptually. In theory, a test specifies that it
+ * expects some function to be called some number of times (can be zero), with
+ * some particular arguments. Hence, KUNIT_EXPECT_CALL() must do two things:
+ *
+ * 1) Determine whether a function call matches the expectation.
+ *
+ * 2) Fail if there are too many or too few matches.
+ */
+static void mock_test_failed_expect_call_fails_test(struct kunit *test)
+{
+       /*
+	* We do not want to fail the real `test` object used to run this test.
+	* So we use a separate `failing_test` for KUNIT_EXPECT_CALL().
+	*/
+	struct mock_test_context *ctx = test->priv;
+	struct kunit *failing_test = ctx->failing_test;
+	struct mock *mock = ctx->mock;
+
+	/*
+	 * Put an expectation on mock, which we won't satisify.
+	 *
+	 * NOTE: it does not actually matter what function we expect here.
+	 * `mock` does not represent an actual mock on anything; we just need to
+	 * create some expectation, that we won't satisfy.
+	 */
+	KUNIT_EXPECT_CALL(mock_add(mock,
+			       kunit_any(failing_test),
+			       kunit_any(failing_test)));
+
+	/*
+	 * Validate the unsatisfied expectation that we just created. This
+	 * should cause `failing_test` to fail.
+	 */
+	mock_validate_expectations(mock);
+
+	/* Verify that `failing_test` has actually failed. */
+	KUNIT_EXPECT_FALSE(test, failing_test->success);
+}
+
+static void mock_test_do_expect_default_return(struct kunit *test)
+{
+	struct mock_test_context *ctx = test->priv;
+	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};
+	struct mock_param_matcher *matchers[] = {
+		kunit_int_eq(test, 5),
+		kunit_int_eq(test, -4)
+	};
+	struct mock_expectation *expectation;
+	const void *ret;
+
+	expectation = mock_add_matcher(mock,
+				       "add",
+				       mock_add,
+				       matchers,
+				       ARRAY_SIZE(matchers));
+	expectation->action = kunit_int_return(test, 5);
+	KUNIT_EXPECT_EQ(test, 0, expectation->times_called);
+
+	KUNIT_EXPECT_FALSE(test,
+			   mock_set_default_action(mock,
+						   "add",
+						   mock_add,
+						   kunit_int_return(test, -4)));
+
+	ret = mock->do_expect(mock,
+			      "add",
+			      mock_add,
+			      two_param_types,
+			      two_params,
+			      ARRAY_SIZE(two_params));
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ret);
+	KUNIT_EXPECT_EQ(test, -4, *((int *) ret));
+	KUNIT_EXPECT_EQ(test, 0, expectation->times_called);
+}
+
+static void mock_test_mock_validate_expectations(struct kunit *test)
+{
+	struct mock_test_context *ctx = test->priv;
+	struct kunit *failing_test = ctx->failing_test;
+	struct mock *mock = ctx->mock;
+
+	struct mock_param_matcher *matchers[] = {
+		kunit_int_eq(failing_test, 5),
+		kunit_int_eq(failing_test, -4)
+	};
+	struct mock_expectation *expectation;
+
+
+	expectation = mock_add_matcher(mock,
+				       "add",
+				       mock_add,
+				       matchers,
+				       ARRAY_SIZE(matchers));
+	expectation->times_called = 0;
+	expectation->min_calls_expected = 1;
+	expectation->max_calls_expected = 1;
+
+	mock_validate_expectations(mock);
+
+	KUNIT_EXPECT_FALSE(test, failing_test->success);
+}
+
+static int mock_test_init(struct kunit *test)
+{
+	struct mock_test_context *ctx;
+
+	ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+	test->priv = ctx;
+
+	ctx->failing_test = kunit_kzalloc(test, sizeof(*ctx->failing_test),
+					  GFP_KERNEL);
+	if (!ctx->failing_test)
+		return -EINVAL;
+	kunit_init_test(ctx->failing_test, NULL, NULL);
+
+	ctx->mock = kunit_kzalloc(test, sizeof(*ctx->mock), GFP_KERNEL);
+	if (!ctx->mock)
+		return -ENOMEM;
+	mock_init_ctrl(ctx->failing_test, ctx->mock);
+
+	return 0;
+}
+
+static void mock_test_exit(struct kunit *test)
+{
+	struct mock_test_context *ctx = test->priv;
+
+	kunit_cleanup(ctx->failing_test);
+}
+
+static struct kunit_case mock_test_cases[] = {
+	KUNIT_CASE(mock_test_do_expect_basic),
+	KUNIT_CASE(mock_test_ptr_eq),
+	KUNIT_CASE(mock_test_ptr_eq_not_equal),
+	KUNIT_CASE(mock_test_failed_expect_call_fails_test),
+	KUNIT_CASE(mock_test_do_expect_default_return),
+	KUNIT_CASE(mock_test_mock_validate_expectations),
+	{}
+};
+
+static struct kunit_suite mock_test_suite = {
+	.name = "mock-test",
+	.init = mock_test_init,
+	.exit = mock_test_exit,
+	.test_cases = mock_test_cases,
+};
+
+kunit_test_suite(mock_test_suite);
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 11/12] kunit: mock: add struct param matcher
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
                   ` (9 preceding siblings ...)
  2020-10-12 22:20 ` [RFC v2 10/12] kunit: mock: add class mocking support Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-10-12 22:20 ` [RFC v2 12/12] kunit: mock: implement nice, strict and naggy mock distinctions Daniel Latypov
  2020-11-02 18:00 ` [RFC v2 00/12] kunit: introduce class mocking support Brendan Higgins
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan

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 9252a0abd295..df99ae5ac721 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 ce8929157ded..6a4cc9c60427 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.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC v2 12/12] kunit: mock: implement nice, strict and naggy mock distinctions
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
                   ` (10 preceding siblings ...)
  2020-10-12 22:20 ` [RFC v2 11/12] kunit: mock: add struct param matcher Daniel Latypov
@ 2020-10-12 22:20 ` Daniel Latypov
  2020-11-02 18:00 ` [RFC v2 00/12] kunit: introduce class mocking support Brendan Higgins
  12 siblings, 0 replies; 14+ messages in thread
From: Daniel Latypov @ 2020-10-12 22:20 UTC (permalink / raw)
  To: dlatypov
  Cc: alan.maguire, brendanhiggins, davidgow, keescook, kunit-dev,
	linux-kernel, linux-kselftest, mcgrof, sboyd, skhan, Felix Guo

From: Brendan Higgins <brendanhiggins@google.com>

Nice mocks only fail when there is an expectation on a method, but none
match a given call. Strict mocks only pass when there is a matching
expectation for every call. Naggy mocks have the same pass/fail behavior
as nice, but report a warning in any case a strict mock would fail.

Signed-off-by: Felix Guo <felixguoxiuping@gmail.com>
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
---
 include/kunit/mock.h  |  63 ++++++++++++++++
 lib/kunit/mock-test.c | 171 ++++++++++++++++++++++++++++++++++++++++++
 lib/kunit/mock.c      |  10 ++-
 3 files changed, 242 insertions(+), 2 deletions(-)

diff --git a/include/kunit/mock.h b/include/kunit/mock.h
index df99ae5ac721..12e70a3f82c5 100644
--- a/include/kunit/mock.h
+++ b/include/kunit/mock.h
@@ -95,10 +95,17 @@ struct mock_method {
 	struct list_head expectations;
 };
 
+enum mock_type {
+	MOCK_TYPE_NICE,
+	MOCK_TYPE_NAGGY,
+	MOCK_TYPE_STRICT
+};
+
 struct mock {
 	struct kunit_post_condition parent;
 	struct kunit *test;
 	struct list_head methods;
+	enum mock_type type;
 	/* TODO(brendanhiggins@google.com): add locking to do_expect. */
 	const void *(*do_expect)(struct mock *mock,
 				 const char *method_name,
@@ -108,6 +115,8 @@ struct mock {
 				 int len);
 };
 
+#define DEFAULT_MOCK_TYPE MOCK_TYPE_NAGGY
+
 void mock_init_ctrl(struct kunit *test, struct mock *mock);
 
 void mock_validate_expectations(struct mock *mock);
@@ -125,6 +134,60 @@ struct mock_expectation *mock_add_matcher(struct mock *mock,
 
 #define MOCK(name) name##_mock
 
+/**
+ * STRICT_MOCK() - sets the mock to be strict and returns the mock
+ * @mock: the mock
+ *
+ * For an example, see ``The Nice, the Strict, and the Naggy`` under
+ * ``Using KUnit``.
+ */
+#define STRICT_MOCK(mock) \
+({ \
+	mock_get_ctrl(mock)->type = MOCK_TYPE_STRICT; \
+	mock; \
+})
+
+static inline bool is_strict_mock(struct mock *mock)
+{
+	return mock->type == MOCK_TYPE_STRICT;
+}
+
+/**
+ * NICE_MOCK() - sets the mock to be nice and returns the mock
+ * @mock: the mock
+ *
+ * For an example, see ``The Nice, the Strict, and the Naggy`` under
+ * ``Using KUnit``.
+ */
+#define NICE_MOCK(mock) \
+({ \
+	mock_get_ctrl(mock)->type = MOCK_TYPE_NICE; \
+	mock; \
+})
+
+static inline bool is_nice_mock(struct mock *mock)
+{
+	return mock->type == MOCK_TYPE_NICE;
+}
+
+/**
+ * NAGGY_MOCK() - sets the mock to be naggy and returns the mock
+ * @mock: the mock
+ *
+ * For an example, see ``The Nice, the Strict, and the Naggy`` under
+ * ``Using KUnit``.
+ */
+#define NAGGY_MOCK(mock) \
+({ \
+	mock_get_ctrl(mock)->type = MOCK_TYPE_NAGGY; \
+	mock; \
+})
+
+static inline bool is_naggy_mock(struct mock *mock)
+{
+	return mock->type == MOCK_TYPE_NAGGY;
+}
+
 /**
  * KUNIT_EXPECT_CALL() - Declares a *call expectation* on a mock function.
  * @expectation_call: a mocked method or function with parameters replaced with
diff --git a/lib/kunit/mock-test.c b/lib/kunit/mock-test.c
index df0969b43ade..1c2aa2aa9c1b 100644
--- a/lib/kunit/mock-test.c
+++ b/lib/kunit/mock-test.c
@@ -243,6 +243,50 @@ static void mock_test_do_expect_default_return(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, 0, expectation->times_called);
 }
 
+/**
+ * DOC: Testing the failure condition of different mock types.
+ *
+ * The following tests will test the behaviour of expectations under different
+ * conditions. For example, what happens when an expectation:
+ * - is not satisfied at the end of the test
+ * - is fulfilled but the expected function is called again
+ * - a function is called without expectations set on it
+ *
+ * For each of these conditions, there may be variations between the different
+ * types of mocks: nice mocks, naggy mocks (the default) and strict mocks.
+ *
+ * More information about these mocks can be found in the kernel documentation
+ * under Documentation/test/api/class-and-function-mocking
+ */
+
+/* Method called on strict mock with no expectations will fail */
+static void mock_test_strict_no_expectations_will_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->type = MOCK_TYPE_STRICT;
+
+	mock_set_default_action(mock,
+				"add",
+				mock_add,
+				kunit_int_return(failing_test, -4));
+
+	mock->do_expect(mock,
+			"add",
+			mock_add,
+			two_param_types,
+			two_params,
+			ARRAY_SIZE(two_params));
+	mock_validate_expectations(mock);
+
+	KUNIT_EXPECT_FALSE(test, failing_test->success);
+}
+
 /*
  * Method called on naggy mock with no expectations will not fail, but will show
  * a warning message
@@ -257,6 +301,8 @@ static void mock_test_naggy_no_expectations_no_fail(struct kunit *test)
 	static const char * const two_param_types[] = {"int", "int"};
 	const void *two_params[] = {&param0, &param1};
 
+	mock->type = MOCK_TYPE_NAGGY;
+
 	mock_set_default_action(mock,
 				"add",
 				real_add,
@@ -282,6 +328,77 @@ static void mock_test_naggy_no_expectations_no_fail(struct kunit *test)
 	KUNIT_EXPECT_FALSE(test, failing_test->success);
 }
 
+/* Method called on nice mock with no expectations will do nothing. */
+static void mock_test_nice_no_expectations_do_nothing(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->type = MOCK_TYPE_NICE;
+
+	mock->do_expect(mock,
+			"add",
+			mock_add,
+			two_param_types,
+			two_params,
+			ARRAY_SIZE(two_params));
+	mock_validate_expectations(mock);
+
+	KUNIT_EXPECT_TRUE(test, failing_test->success);
+}
+
+/* Test that method called on a mock (of any type) with no matching expectations
+ * will fail test and print all the tried expectations.
+ */
+static void
+run_method_called_but_no_matching_expectation_test(struct kunit *test,
+						   enum mock_type mock_type)
+{
+	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};
+	struct mock_param_matcher *two_matchers[] = {
+		kunit_int_eq(failing_test, 100),
+		kunit_int_eq(failing_test, 100)
+	};
+
+	mock_add_matcher(mock, "add", mock_add, two_matchers,
+			 ARRAY_SIZE(two_matchers));
+
+	mock->type = mock_type;
+
+	mock->do_expect(mock, "add", mock_add, two_param_types, two_params,
+			ARRAY_SIZE(two_params));
+
+	/* Even nice mocks should fail if there's an unmet expectation. */
+	KUNIT_EXPECT_FALSE(test, failing_test->success);
+}
+
+static void mock_test_naggy_no_matching_expectations_fail(struct kunit *test)
+{
+	run_method_called_but_no_matching_expectation_test(test,
+							   MOCK_TYPE_NAGGY);
+}
+
+static void mock_test_strict_no_matching_expectations_fail(struct kunit *test)
+{
+	run_method_called_but_no_matching_expectation_test(test,
+							   MOCK_TYPE_STRICT);
+}
+
+static void mock_test_nice_no_matching_expectations_fail(struct kunit *test)
+{
+	run_method_called_but_no_matching_expectation_test(test,
+							   MOCK_TYPE_NICE);
+}
+
 static void mock_test_mock_validate_expectations(struct kunit *test)
 {
 	struct mock_test_context *ctx = test->priv;
@@ -309,6 +426,54 @@ static void mock_test_mock_validate_expectations(struct kunit *test)
 	KUNIT_EXPECT_FALSE(test, failing_test->success);
 }
 
+static void mock_test_validate_clears_expectations(struct kunit *test)
+{
+	struct mock_test_context *ctx = test->priv;
+	struct kunit *failing_test = ctx->failing_test;
+	struct mock *mock = ctx->mock;
+	struct mock_param_matcher *matchers[] = {
+		kunit_int_eq(failing_test, 5),
+		kunit_int_eq(failing_test, -4)
+	};
+	int param0 = 5, param1 = -4;
+	static const char * const two_param_types[] = {"int", "int"};
+	const void *two_params[] = {&param0, &param1};
+
+	struct mock_expectation *expectation;
+
+	mock->type = MOCK_TYPE_STRICT;
+
+	/* Add an arbitrary matcher for 0 calls */
+	expectation = mock_add_matcher(mock, "add", mock_add,
+				       matchers, ARRAY_SIZE(matchers));
+	expectation->times_called = 0;
+	expectation->min_calls_expected = 0;
+	expectation->max_calls_expected = 0;
+
+	/* Should have 0 calls and should clear the previous expectation */
+	mock_validate_expectations(mock);
+
+	/* Add a new matcher for 1 call */
+	expectation = mock_add_matcher(mock, "add", mock_add,
+				       matchers, ARRAY_SIZE(matchers));
+	expectation->times_called = 0;
+	expectation->min_calls_expected = 1;
+	expectation->max_calls_expected = 1;
+
+	/* Satisfy previous matcher */
+	mock->do_expect(mock, "add", mock_add, two_param_types, two_params,
+			ARRAY_SIZE(two_params));
+
+	/*
+	 * Validate previous satisfy; if we didn't clear the previous
+	 * expectation, it would fail the mock_test.
+	 */
+	mock_validate_expectations(mock);
+
+	/* If all goes well, shouldn't fail the test. */
+	KUNIT_EXPECT_TRUE(test, failing_test->success);
+}
+
 static int mock_test_init(struct kunit *test)
 {
 	struct mock_test_context *ctx;
@@ -346,7 +511,13 @@ 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_strict_no_expectations_will_fail),
 	KUNIT_CASE(mock_test_naggy_no_expectations_no_fail),
+	KUNIT_CASE(mock_test_nice_no_expectations_do_nothing),
+	KUNIT_CASE(mock_test_strict_no_matching_expectations_fail),
+	KUNIT_CASE(mock_test_naggy_no_matching_expectations_fail),
+	KUNIT_CASE(mock_test_nice_no_matching_expectations_fail),
+	KUNIT_CASE(mock_test_validate_clears_expectations),
 	{}
 };
 
diff --git a/lib/kunit/mock.c b/lib/kunit/mock.c
index 12fb88899451..f1fa7a5b9dd4 100644
--- a/lib/kunit/mock.c
+++ b/lib/kunit/mock.c
@@ -85,6 +85,7 @@ void mock_init_ctrl(struct kunit *test, struct mock *mock)
 	mock->test = test;
 	INIT_LIST_HEAD(&mock->methods);
 	mock->do_expect = mock_do_expect;
+	mock->type = DEFAULT_MOCK_TYPE;
 	mock->parent.validate = mock_validate_wrapper;
 	list_add_tail(&mock->parent.node, &test->post_conditions);
 }
@@ -283,7 +284,12 @@ static struct mock_expectation *mock_apply_expectations(
 		mock_add_method_expectation_error(test, stream,
 			"Method was called with no expectations declared: ",
 			mock, method, type_names, params, len);
-		kunit_stream_commit(stream);
+		if (is_strict_mock(mock))
+			fail_and_flush(test, stream);
+		else if (is_naggy_mock(mock))
+			kunit_stream_commit(stream);
+		else
+			kunit_stream_clear(stream);
 		return NULL;
 	}
 
@@ -313,7 +319,7 @@ static struct mock_expectation *mock_apply_expectations(
 		}
 	}
 
-	if (expectations_all_saturated) {
+	if (expectations_all_saturated && !is_nice_mock(mock)) {
 		mock_add_method_expectation_error(test, stream,
 			"Method was called with fully saturated expectations: ",
 			mock, method, type_names, params, len);
-- 
2.28.0.1011.ga647a8990f-goog


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [RFC v2 00/12] kunit: introduce class mocking support.
  2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
                   ` (11 preceding siblings ...)
  2020-10-12 22:20 ` [RFC v2 12/12] kunit: mock: implement nice, strict and naggy mock distinctions Daniel Latypov
@ 2020-11-02 18:00 ` Brendan Higgins
  12 siblings, 0 replies; 14+ messages in thread
From: Brendan Higgins @ 2020-11-02 18:00 UTC (permalink / raw)
  To: Daniel Latypov, Joel Stanley, Daniel Vetter
  Cc: Alan Maguire, David Gow, Kees Cook, KUnit Development,
	Linux Kernel Mailing List, open list:KERNEL SELFTEST FRAMEWORK,
	Luis Chamberlain, Stephen Boyd, Shuah Khan

+Joel Stanley +Daniel Vetter

If I remember correctly, both of you said you were interested in
mocking on KUnit. This RFC only has some of the mocking features that
I mentioned previously, but I would still like to get your thoughts.

On Mon, Oct 12, 2020 at 3:21 PM Daniel Latypov <dlatypov@google.com> wrote:
>
> # Background
> KUnit currently lacks any first-class support for mocking.
> For an overview and discussion on the pros and cons, see
> https://martinfowler.com/articles/mocksArentStubs.html
>
> This patch set introduces the basic machinery needed for mocking:
> setting and validating expectations, setting default actions, etc.
>
> Using that basic infrastructure, we add macros for "class mocking", as
> it's probably the easiest type of mocking to start with.
>
> ## Class mocking
>
> By "class mocking", we're referring mocking out function pointers stored
> in structs like:
>   struct sender {
>         int (*send)(struct sender *sender, int data);
>   };
> or in ops structs
>   struct sender {
>         struct send_ops *ops; // contains `send`
>   };
>
> After the necessary DEFINE_* macros, we can then write code like
>   struct MOCK(sender) mock_sender = CONSTRUCT_MOCK(sender, test);
>
>   /* Fake an error for a specific input. */
>   handle = KUNIT_EXPECT_CALL(send(<omitted>, kunit_int_eq(42)));
>   handle->action = kunit_int_return(test, -EINVAL);
>
>   /* Pass the mocked object to some code under test. */
>   KUNIT_EXPECT_EQ(test, -EINVAL, send_message(...));
>
> I.e. the goal is to make it easier to test
> 1) with less dependencies (we don't need to setup a real `sender`)
> 2) unusual/error conditions more easily.
>
> In the future, we hope to build upon this to support mocking in more
> contexts, e.g. standalone funcs, etc.
>
> # TODOs
>
> ## Naming
> This introduces a number of new macros for dealing with mocks,
> e.g:
>   DEFINE_STRUCT_CLASS_MOCK(METHOD(foo), CLASS(example),
>                            RETURNS(int),
>                            PARAMS(struct example *, int));
>   ...
>   KUNIT_EXPECT_CALL(foo(mock_get_ctrl(mock_example), ...);
> For consistency, we could prefix everything with KUNIT, e.g.
> `KUNIT_DEFINE_STRUCT_CLASS_MOCK` and `kunit_mock_get_ctrl`, but it feels
> like the names might be long enough that they would hinder readability.
>
> ## Usage
> For now the only use of class mocking is in kunit-example-test.c
> As part of changing this from an RFC to a real patch set, we're hoping
> to include at least one example.
>
> Pointers to bits of code where this would be useful that aren't too
> hairy would be appreciated.
> E.g. could easily add a test for tools/perf/ui/progress.h, e.g. that
> ui_progress__init() calls ui_progress_ops.init(), but that likely isn't
> useful to anyone.
>
> ---
> v2:
> * Pass `struct kunit *` to mock init's to allow allocating ops structs.
> * Update kunit-example-test.cc to do so as a more realistic example.
> v1: https://lore.kernel.org/linux-kselftest/20200918183114.2571146-1-dlatypov@google.com/
> ---
>
> Brendan Higgins (9):
>   kunit: test: add kunit_stream a std::stream like logger
>   kunit: test: add concept of post conditions
>   checkpatch: add support for struct MOCK(foo) syntax
>   kunit: mock: add parameter list manipulation macros
>   kunit: mock: add internal mock infrastructure
>   kunit: mock: add basic matchers and actions
>   kunit: mock: add class mocking support
>   kunit: mock: add struct param matcher
>   kunit: mock: implement nice, strict and naggy mock distinctions
>
> Daniel Latypov (2):
>   Revert "kunit: move string-stream.h to lib/kunit"
>   kunit: expose kunit_set_failure() for use by mocking
>
> Marcelo Schmitt (1):
>   kunit: mock: add macro machinery to pick correct format args
>
>  include/kunit/assert.h                 |   3 +-
>  include/kunit/kunit-stream.h           |  94 +++
>  include/kunit/mock.h                   | 902 +++++++++++++++++++++++++
>  include/kunit/params.h                 | 305 +++++++++
>  {lib => include}/kunit/string-stream.h |   2 +
>  include/kunit/test.h                   |   9 +
>  lib/kunit/Makefile                     |   9 +-
>  lib/kunit/assert.c                     |   2 -
>  lib/kunit/common-mocks.c               | 409 +++++++++++
>  lib/kunit/kunit-example-test.c         |  98 +++
>  lib/kunit/kunit-stream.c               | 110 +++
>  lib/kunit/mock-macro-test.c            | 241 +++++++
>  lib/kunit/mock-test.c                  | 531 +++++++++++++++
>  lib/kunit/mock.c                       | 370 ++++++++++
>  lib/kunit/string-stream-test.c         |   3 +-
>  lib/kunit/string-stream.c              |   5 +-
>  lib/kunit/test.c                       |  15 +-
>  scripts/checkpatch.pl                  |   4 +
>  18 files changed, 3099 insertions(+), 13 deletions(-)
>  create mode 100644 include/kunit/kunit-stream.h
>  create mode 100644 include/kunit/mock.h
>  create mode 100644 include/kunit/params.h
>  rename {lib => include}/kunit/string-stream.h (95%)
>  create mode 100644 lib/kunit/common-mocks.c
>  create mode 100644 lib/kunit/kunit-stream.c
>  create mode 100644 lib/kunit/mock-macro-test.c
>  create mode 100644 lib/kunit/mock-test.c
>  create mode 100644 lib/kunit/mock.c
>
>
> base-commit: 10b82d5176488acee2820e5a2cf0f2ec5c3488b6
> --
> 2.28.0.1011.ga647a8990f-goog
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2020-11-02 18:00 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-12 22:20 [RFC v2 00/12] kunit: introduce class mocking support Daniel Latypov
2020-10-12 22:20 ` [RFC v2 01/12] Revert "kunit: move string-stream.h to lib/kunit" Daniel Latypov
2020-10-12 22:20 ` [RFC v2 02/12] kunit: test: add kunit_stream a std::stream like logger Daniel Latypov
2020-10-12 22:20 ` [RFC v2 03/12] kunit: test: add concept of post conditions Daniel Latypov
2020-10-12 22:20 ` [RFC v2 04/12] checkpatch: add support for struct MOCK(foo) syntax Daniel Latypov
2020-10-12 22:20 ` [RFC v2 05/12] kunit: mock: add parameter list manipulation macros Daniel Latypov
2020-10-12 22:20 ` [RFC v2 06/12] kunit: expose kunit_set_failure() for use by mocking Daniel Latypov
2020-10-12 22:20 ` [RFC v2 07/12] kunit: mock: add internal mock infrastructure Daniel Latypov
2020-10-12 22:20 ` [RFC v2 08/12] kunit: mock: add basic matchers and actions Daniel Latypov
2020-10-12 22:20 ` [RFC v2 09/12] kunit: mock: add macro machinery to pick correct format args Daniel Latypov
2020-10-12 22:20 ` [RFC v2 10/12] kunit: mock: add class mocking support Daniel Latypov
2020-10-12 22:20 ` [RFC v2 11/12] kunit: mock: add struct param matcher Daniel Latypov
2020-10-12 22:20 ` [RFC v2 12/12] kunit: mock: implement nice, strict and naggy mock distinctions Daniel Latypov
2020-11-02 18:00 ` [RFC v2 00/12] kunit: introduce class mocking support Brendan Higgins

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).