From: Brendan Higgins <brendanhiggins@google.com>
To: gregkh@linuxfoundation.org, keescook@google.com,
mcgrof@kernel.org, shuah@kernel.org
Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au,
Tim.Bird@sony.com, linux-um@lists.infradead.org,
linux-kernel@vger.kernel.org, rostedt@goodmis.org,
julia.lawall@lip6.fr, Felix Guo <felixguoxiuping@gmail.com>,
joel@jms.id.au, linux-kselftest@vger.kernel.org,
khilman@baylibre.com, joe@perches.com, jdike@addtoit.com,
Brendan Higgins <brendanhiggins@google.com>,
kunit-dev@googlegroups.com
Subject: [RFC v1 19/31] kunit: mock: implemented nice, strict and naggy mock distinctions
Date: Tue, 16 Oct 2018 16:51:08 -0700 [thread overview]
Message-ID: <20181016235120.138227-20-brendanhiggins@google.com> (raw)
In-Reply-To: <20181016235120.138227-1-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>
---
include/kunit/mock.h | 63 +++++++++++++
kunit/mock-test.c | 192 ++++++++++++++++++++++++++++++++++++++-
kunit/mock.c | 10 +-
kunit/test-stream-test.c | 6 +-
4 files changed, 265 insertions(+), 6 deletions(-)
diff --git a/include/kunit/mock.h b/include/kunit/mock.h
index 4f85b39d628d0..8d155b27a257a 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 test_post_condition parent;
struct test *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 test *test, struct mock *mock);
void mock_validate_expectations(struct mock *mock);
@@ -137,6 +146,60 @@ void mock_unregister_formatter(struct mock_param_formatter *formatter);
#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;
+}
+
/**
* TEST_EXPECT_CALL() - Declares a *call expectation* on a mock function.
* @expectation_call: a mocked method or function with parameters replaced with
diff --git a/kunit/mock-test.c b/kunit/mock-test.c
index 77b16ad754424..675387743ada4 100644
--- a/kunit/mock-test.c
+++ b/kunit/mock-test.c
@@ -150,7 +150,7 @@ static void mock_test_failed_expect_call_fails_test(struct test *test)
static void mock_test_do_expect_default_return(struct test *test)
{
struct mock_test_context *ctx = test->priv;
- struct MOCK(test) *mock_test = ctx->mock_test;
+ struct MOCK(test) *mock_test = NICE_MOCK(ctx->mock_test);
struct test *trgt = mock_get_trgt(mock_test);
struct mock *mock = ctx->mock;
int param0 = 5, param1 = -5;
@@ -187,6 +187,49 @@ static void mock_test_do_expect_default_return(struct test *test)
TEST_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 test *test)
+{
+ struct mock_test_context *ctx = test->priv;
+ struct MOCK(test) *mock_test = ctx->mock_test;
+ struct test *trgt = mock_get_trgt(mock_test);
+ struct mock *mock = ctx->mock;
+ int param0 = 5, param1 = -5;
+ static const char * const two_param_types[] = {"int", "int"};
+ const void *two_params[] = {¶m0, ¶m1};
+ struct mock_expectation *expectation;
+
+ mock->type = MOCK_TYPE_STRICT;
+
+ mock_set_default_action(mock,
+ "test_printk",
+ test_printk,
+ test_int_return(trgt, -4));
+
+ expectation = TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test),
+ test_any(test)));
+
+ mock->do_expect(mock, "test_printk", test_printk, two_param_types,
+ two_params, ARRAY_SIZE(two_params));
+ mock_validate_expectations(mock);
+}
+
/*
* Method called on naggy mock with no expectations will not fail, but will show
* a warning message
@@ -202,6 +245,8 @@ static void mock_test_naggy_no_expectations_no_fail(struct test *test)
const void *two_params[] = {¶m0, ¶m1};
struct mock_expectation *expectation;
+ mock->type = MOCK_TYPE_NAGGY;
+
mock_set_default_action(mock,
"test_printk",
test_printk,
@@ -229,6 +274,93 @@ static void mock_test_naggy_no_expectations_no_fail(struct test *test)
mock_validate_expectations(mock);
}
+/* Method called on nice mock with no expectations will do nothing. */
+static void mock_test_nice_no_expectations_do_nothing(struct test *test)
+{
+ struct mock_test_context *ctx = test->priv;
+ struct MOCK(test) *mock_test = ctx->mock_test;
+ struct test *trgt = mock_get_trgt(mock_test);
+ struct mock *mock = ctx->mock;
+ int param0 = 5, param1 = -5;
+ static const char * const two_param_types[] = {"int", "int"};
+ const void *two_params[] = {¶m0, ¶m1};
+ struct mock_expectation *expectation;
+
+ mock->type = MOCK_TYPE_NICE;
+
+ mock_set_default_action(mock,
+ "test_printk",
+ test_printk,
+ test_int_return(trgt, -4));
+
+ expectation = TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test),
+ test_any(test)));
+ expectation->min_calls_expected = 0;
+ expectation->max_calls_expected = 0;
+
+ expectation = TEST_EXPECT_CALL(mock_vprintk(mock_get_ctrl(mock_test),
+ test_any(test),
+ test_any(test)));
+ expectation->min_calls_expected = 0;
+ expectation->max_calls_expected = 0;
+
+ mock->do_expect(mock,
+ "test_printk",
+ test_printk,
+ two_param_types,
+ two_params,
+ ARRAY_SIZE(two_params));
+ mock_validate_expectations(mock);
+}
+
+/* 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 test *test,
+ enum mock_type mock_type)
+{
+ struct mock_test_context *ctx = test->priv;
+ struct MOCK(test) *mock_test = ctx->mock_test;
+ struct test *trgt = mock_get_trgt(mock_test);
+ struct mock *mock = ctx->mock;
+ int param0 = 5, param1 = -5;
+ static const char * const two_param_types[] = {"int", "int"};
+ const void *two_params[] = {¶m0, ¶m1};
+ struct mock_expectation *handle;
+ struct mock_param_matcher *two_matchers[] = {
+ test_int_eq(trgt, 100),
+ test_int_eq(trgt, 100)
+ };
+ mock_add_matcher(mock, "test_printk", test_printk, two_matchers,
+ ARRAY_SIZE(two_matchers));
+ handle = TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test),
+ test_any(test)));
+
+ mock->type = mock_type;
+
+ mock->do_expect(mock, "test_printk", test_printk, two_param_types,
+ two_params, ARRAY_SIZE(two_params));
+}
+
+static void mock_test_naggy_no_matching_expectations_fail(struct test *test)
+{
+ run_method_called_but_no_matching_expectation_test(test,
+ MOCK_TYPE_NAGGY);
+}
+
+static void mock_test_strict_no_matching_expectations_fail(struct test *test)
+{
+ run_method_called_but_no_matching_expectation_test(test,
+ MOCK_TYPE_STRICT);
+}
+
+static void mock_test_nice_no_matching_expectations_fail(struct test *test)
+{
+ run_method_called_but_no_matching_expectation_test(test,
+ MOCK_TYPE_NICE);
+}
+
static void mock_test_mock_validate_expectations(struct test *test)
{
struct mock_test_context *ctx = test->priv;
@@ -257,6 +389,58 @@ static void mock_test_mock_validate_expectations(struct test *test)
mock_validate_expectations(mock);
}
+static void mock_test_validate_clears_expectations(struct test *test)
+{
+ struct mock_test_context *ctx = test->priv;
+ struct MOCK(test) *mock_test = ctx->mock_test;
+ struct test *trgt = mock_get_trgt(mock_test);
+ struct mock *mock = ctx->mock;
+ struct mock_param_matcher *matchers[] = {
+ test_int_eq(trgt, 5),
+ test_int_eq(trgt, -4)
+ };
+ int param0 = 5, param1 = -4;
+ static const char * const two_param_types[] = {"int", "int"};
+ const void *two_params[] = {¶m0, ¶m1};
+
+ struct mock_expectation *expectation;
+
+ mock->type = MOCK_TYPE_STRICT;
+
+ /* If all goes well, the mock_test should not fail. */
+ expectation = TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test),
+ test_any(test)));
+ expectation->min_calls_expected = 0;
+ expectation->max_calls_expected = 0;
+
+ /* Add an arbitrary matcher for 0 calls */
+ expectation = mock_add_matcher(mock, "test_printk", test_printk,
+ 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, "test_printk", test_printk,
+ 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, "test_printk", test_printk, 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);
+}
+
void *do_mocked_fail(struct mock_action *this, const void **params, int len)
{
static const int ret;
@@ -306,7 +490,13 @@ static struct test_case mock_test_cases[] = {
TEST_CASE(mock_test_failed_expect_call_fails_test),
TEST_CASE(mock_test_do_expect_default_return),
TEST_CASE(mock_test_mock_validate_expectations),
+ TEST_CASE(mock_test_strict_no_expectations_will_fail),
TEST_CASE(mock_test_naggy_no_expectations_no_fail),
+ TEST_CASE(mock_test_nice_no_expectations_do_nothing),
+ TEST_CASE(mock_test_strict_no_matching_expectations_fail),
+ TEST_CASE(mock_test_naggy_no_matching_expectations_fail),
+ TEST_CASE(mock_test_nice_no_matching_expectations_fail),
+ TEST_CASE(mock_test_validate_clears_expectations),
{},
};
diff --git a/kunit/mock.c b/kunit/mock.c
index 9be6b2d3621c4..314cebb54e236 100644
--- a/kunit/mock.c
+++ b/kunit/mock.c
@@ -79,6 +79,7 @@ void mock_init_ctrl(struct test *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);
}
@@ -316,7 +317,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);
- stream->commit(stream);
+ if (is_strict_mock(mock))
+ test->fail(test, stream);
+ else if (is_naggy_mock(mock))
+ stream->commit(stream);
+ else
+ stream->clear(stream);
return NULL;
}
@@ -346,7 +352,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);
diff --git a/kunit/test-stream-test.c b/kunit/test-stream-test.c
index b335e09805a0f..738a2692f7ba4 100644
--- a/kunit/test-stream-test.c
+++ b/kunit/test-stream-test.c
@@ -20,7 +20,7 @@ struct test_stream_test_context {
static void test_stream_test_add(struct test *test)
{
struct test_stream_test_context *ctx = test->priv;
- struct MOCK(test) *mock_test = ctx->mock_test;
+ struct MOCK(test) *mock_test = NICE_MOCK(ctx->mock_test);
struct test_stream *stream = ctx->stream;
stream->add(stream, "Foo");
@@ -40,7 +40,7 @@ static void test_stream_test_add(struct test *test)
static void test_stream_test_append(struct test *test)
{
struct test_stream_test_context *ctx = test->priv;
- struct MOCK(test) *mock_test = ctx->mock_test;
+ struct MOCK(test) *mock_test = NICE_MOCK(ctx->mock_test);
struct test_stream *stream = ctx->stream;
struct test_stream *other_stream;
@@ -63,7 +63,7 @@ static void test_stream_test_append(struct test *test)
static void test_stream_error_message_when_no_level_set(struct test *test)
{
struct test_stream_test_context *ctx = test->priv;
- struct MOCK(test) *mock_test = ctx->mock_test;
+ struct MOCK(test) *mock_test = NICE_MOCK(ctx->mock_test);
struct test_stream *stream = ctx->stream;
struct test_stream *other_stream;
--
2.19.1.331.ge82ca0e54c-goog
_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um
next prev parent reply other threads:[~2018-10-16 23:55 UTC|newest]
Thread overview: 52+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-10-16 23:50 [RFC v1 00/31] kunit: Introducing KUnit, the Linux kernel unit testing framework Brendan Higgins
2018-10-16 23:50 ` [RFC v1 01/31] kunit: test: added string_stream a std::stream like string builder Brendan Higgins
2018-10-16 23:50 ` [RFC v1 02/31] kunit: test: adds KUnit test runner core Brendan Higgins
2018-10-16 23:50 ` [RFC v1 03/31] kunit: test: added test resource management API Brendan Higgins
2018-10-16 23:50 ` [RFC v1 04/31] kunit: test: added test_stream a std::stream like logger Brendan Higgins
2018-10-16 23:50 ` [RFC v1 05/31] kunit: test: added the concept of expectations Brendan Higgins
2018-10-16 23:50 ` [RFC v1 06/31] arch: um: enabled running kunit from User Mode Linux Brendan Higgins
2018-10-17 15:29 ` Kieran Bingham
2018-10-17 17:43 ` Brendan Higgins
2018-10-17 17:52 ` Tim.Bird
2018-10-17 21:09 ` Brendan Higgins
2018-10-17 21:18 ` Tim.Bird
2018-10-17 22:45 ` Brendan Higgins
2018-10-16 23:50 ` [RFC v1 07/31] kunit: test: added initial tests Brendan Higgins
2018-10-16 23:50 ` [RFC v1 08/31] arch: um: added shim to trap to allow installing a fault catcher for tests Brendan Higgins
2018-10-16 23:50 ` [RFC v1 09/31] kunit: test: added the concept of assertions Brendan Higgins
2018-10-16 23:50 ` [RFC v1 10/31] kunit: test: added concept of initcalls Brendan Higgins
2018-10-16 23:51 ` [RFC v1 11/31] kunit: test: added concept of post conditions Brendan Higgins
2018-10-16 23:51 ` [RFC v1 12/31] checkpatch: added support for struct MOCK(foo) syntax Brendan Higgins
2018-10-16 23:59 ` Joe Perches
2018-10-17 0:03 ` Brendan Higgins
2018-10-16 23:51 ` [RFC v1 13/31] kunit: mock: added parameter list minipulation macros Brendan Higgins
2018-10-16 23:51 ` [RFC v1 14/31] kunit: mock: added internal mock infrastructure Brendan Higgins
2018-10-16 23:51 ` [RFC v1 15/31] kunit: mock: added basic matchers and actions Brendan Higgins
2018-10-16 23:51 ` [RFC v1 16/31] kunit: mock: added class mocking support Brendan Higgins
2018-10-16 23:51 ` [RFC v1 17/31] kunit: mock: added struct param matcher Brendan Higgins
2018-10-16 23:51 ` [RFC v1 18/31] kunit: mock: added parameter formatters Brendan Higgins
2018-10-16 23:51 ` Brendan Higgins [this message]
2018-10-16 23:51 ` [RFC v1 20/31] kunit: mock: add ability to mock functions with void context Brendan Higgins
2018-10-16 23:51 ` [RFC v1 21/31] kunit: mock: added support for arbitrary function mocking Brendan Higgins
2018-10-16 23:51 ` [RFC v1 22/31] kunit: mock: add the concept of spyable functions Brendan Higgins
2018-10-17 22:46 ` Rob Herring
2018-10-18 1:32 ` Brendan Higgins
2018-10-16 23:51 ` [RFC v1 23/31] kunit: mock: add parameter capturers Brendan Higgins
2018-10-16 23:51 ` [RFC v1 24/31] kunit: improved sigsegv stack trace printing Brendan Higgins
2018-10-16 23:51 ` [RFC v1 25/31] kunit: added concept of platform mocking Brendan Higgins
2018-10-16 23:51 ` [RFC v1 26/31] arch: um: added stubs for mock iomem for KUnit Brendan Higgins
2018-10-17 22:28 ` Rob Herring
2018-10-18 1:14 ` Brendan Higgins
2018-10-16 23:51 ` [RFC v1 27/31] Documentation: kunit: adds complete documentation " Brendan Higgins
2018-10-16 23:51 ` [RFC v1 28/31] kunit: added Python libraries for handing KUnit config and kernel Brendan Higgins
2018-10-16 23:51 ` [RFC v1 29/31] kunit: added KUnit wrapper script and simple output parser Brendan Higgins
2018-10-16 23:51 ` [RFC v1 30/31] kunit.py: improved output from python wrapper Brendan Higgins
2018-10-16 23:51 ` [RFC v1 31/31] MAINTAINERS: add entry for KUnit the unit testing framework Brendan Higgins
2018-10-17 9:08 ` [RFC v1 00/31] kunit: Introducing KUnit, the Linux kernel " Daniel Vetter
2018-10-17 17:49 ` Tim.Bird
2018-10-17 22:22 ` Brendan Higgins
2018-10-17 20:43 ` Rob Herring
2018-10-17 23:12 ` Randy Dunlap
2018-10-18 2:05 ` Brendan Higgins
2018-10-18 3:55 ` Dan Williams
2018-10-19 6:27 ` Brendan Higgins
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20181016235120.138227-20-brendanhiggins@google.com \
--to=brendanhiggins@google.com \
--cc=Tim.Bird@sony.com \
--cc=brakmo@fb.com \
--cc=felixguoxiuping@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=jdike@addtoit.com \
--cc=joe@perches.com \
--cc=joel@jms.id.au \
--cc=julia.lawall@lip6.fr \
--cc=keescook@google.com \
--cc=khilman@baylibre.com \
--cc=kunit-dev@googlegroups.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-um@lists.infradead.org \
--cc=mcgrof@kernel.org \
--cc=mpe@ellerman.id.au \
--cc=richard@nod.at \
--cc=rostedt@goodmis.org \
--cc=shuah@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is 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).