kunit: mock: added basic mock infrastructure

Introduces 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.

Change-Id: Id9d535be1da95dd42844c43f6dfcca3a7441bd81
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
diff --git a/include/test/mock.h b/include/test/mock.h
new file mode 100644
index 0000000..e0090142
--- /dev/null
+++ b/include/test/mock.h
@@ -0,0 +1,740 @@
+#ifndef _TEST_MOCK_H
+#define _TEST_MOCK_H
+
+#include <linux/types.h>
+#include <linux/tracepoint.h> /* For PARAMS(...) */
+#include <test/test.h>
+#include <test/test-stream.h>
+#include <test/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 &test_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 test_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 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 test_post_condition parent;
+	struct test *test;
+	struct list_head methods;
+	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 test *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);
+
+#define MOCK(name) name##_mock
+
+/**
+ * EXPECT_CALL() - Declares a *call expectation* on a mock method or 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 test *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 = EXPECT_CALL(foo(mock_get_ctrl(mock_example),
+ *				     int_eq(test, 5)));
+ *		handle->action = int_return(test, 2);
+ *
+ *		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 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 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 test *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 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));	       \
+			ASSERT_NOT_ERR_OR_NULL(mock->test, retval);	       \
+			if (!retval) {					       \
+				test_info(mock->test,			       \
+					  "no action installed for "#name);    \
+				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 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 MOCK(struct_name) *)``. This
+ *             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 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 test *test)			       \
+		{							       \
+			struct MOCK(struct_name) *mock_obj;		       \
+									       \
+			mock_obj = test_kzalloc(test,			       \
+						sizeof(*mock_obj),	       \
+						GFP_KERNEL);		       \
+			if (!mock_obj)					       \
+				return NULL;				       \
+									       \
+			mock_init_ctrl(test, mock_get_ctrl(mock_obj));	       \
+									       \
+			if (init_func(mock_obj))			       \
+				return NULL;				       \
+									       \
+			return mock_obj;				       \
+		}
+
+#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:`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 test *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 *int_eq(struct test *test, int expected);
+ *    struct mock_param_matcher *int_ne(struct test *test, int expected);
+ *    struct mock_param_matcher *int_lt(struct test *test, int expected);
+ *    struct mock_param_matcher *int_le(struct test *test, int expected);
+ *    struct mock_param_matcher *int_gt(struct test *test, int expected);
+ *    struct mock_param_matcher *int_ge(struct test *test, int expected);
+ *
+ * For a detailed list, please see
+ * ``include/linux/mock.h``.
+ */
+
+/* Matches any argument */
+struct mock_param_matcher *any(struct test *test);
+
+/*
+ * Matches different types of integers, the argument is compared to the
+ * `expected` field, based on the comparison defined.
+ */
+struct mock_param_matcher *u8_eq(struct test *test, u8 expected);
+struct mock_param_matcher *u8_ne(struct test *test, u8 expected);
+struct mock_param_matcher *u8_le(struct test *test, u8 expected);
+struct mock_param_matcher *u8_lt(struct test *test, u8 expected);
+struct mock_param_matcher *u8_ge(struct test *test, u8 expected);
+struct mock_param_matcher *u8_gt(struct test *test, u8 expected);
+
+struct mock_param_matcher *u16_eq(struct test *test, u16 expected);
+struct mock_param_matcher *u16_ne(struct test *test, u16 expected);
+struct mock_param_matcher *u16_le(struct test *test, u16 expected);
+struct mock_param_matcher *u16_lt(struct test *test, u16 expected);
+struct mock_param_matcher *u16_ge(struct test *test, u16 expected);
+struct mock_param_matcher *u16_gt(struct test *test, u16 expected);
+
+struct mock_param_matcher *u32_eq(struct test *test, u32 expected);
+struct mock_param_matcher *u32_ne(struct test *test, u32 expected);
+struct mock_param_matcher *u32_le(struct test *test, u32 expected);
+struct mock_param_matcher *u32_lt(struct test *test, u32 expected);
+struct mock_param_matcher *u32_ge(struct test *test, u32 expected);
+struct mock_param_matcher *u32_gt(struct test *test, u32 expected);
+
+struct mock_param_matcher *u64_eq(struct test *test, u64 expected);
+struct mock_param_matcher *u64_ne(struct test *test, u64 expected);
+struct mock_param_matcher *u64_le(struct test *test, u64 expected);
+struct mock_param_matcher *u64_lt(struct test *test, u64 expected);
+struct mock_param_matcher *u64_ge(struct test *test, u64 expected);
+struct mock_param_matcher *u64_gt(struct test *test, u64 expected);
+
+struct mock_param_matcher *char_eq(struct test *test, char expected);
+struct mock_param_matcher *char_ne(struct test *test, char expected);
+struct mock_param_matcher *char_le(struct test *test, char expected);
+struct mock_param_matcher *char_lt(struct test *test, char expected);
+struct mock_param_matcher *char_ge(struct test *test, char expected);
+struct mock_param_matcher *char_gt(struct test *test, char expected);
+
+struct mock_param_matcher *uchar_eq(struct test *test, unsigned char expected);
+struct mock_param_matcher *uchar_ne(struct test *test, unsigned char expected);
+struct mock_param_matcher *uchar_le(struct test *test, unsigned char expected);
+struct mock_param_matcher *uchar_lt(struct test *test, unsigned char expected);
+struct mock_param_matcher *uchar_ge(struct test *test, unsigned char expected);
+struct mock_param_matcher *uchar_gt(struct test *test, unsigned char expected);
+
+struct mock_param_matcher *schar_eq(struct test *test, signed char expected);
+struct mock_param_matcher *schar_ne(struct test *test, signed char expected);
+struct mock_param_matcher *schar_le(struct test *test, signed char expected);
+struct mock_param_matcher *schar_lt(struct test *test, signed char expected);
+struct mock_param_matcher *schar_ge(struct test *test, signed char expected);
+struct mock_param_matcher *schar_gt(struct test *test, signed char expected);
+
+struct mock_param_matcher *short_eq(struct test *test, short expected);
+struct mock_param_matcher *short_ne(struct test *test, short expected);
+struct mock_param_matcher *short_le(struct test *test, short expected);
+struct mock_param_matcher *short_lt(struct test *test, short expected);
+struct mock_param_matcher *short_ge(struct test *test, short expected);
+struct mock_param_matcher *short_gt(struct test *test, short expected);
+
+struct mock_param_matcher *ushort_eq(struct test *test,
+				     unsigned short expected);
+struct mock_param_matcher *ushort_ne(struct test *test,
+				     unsigned short expected);
+struct mock_param_matcher *ushort_le(struct test *test,
+				     unsigned short expected);
+struct mock_param_matcher *ushort_lt(struct test *test,
+				     unsigned short expected);
+struct mock_param_matcher *ushort_ge(struct test *test,
+				     unsigned short expected);
+struct mock_param_matcher *ushort_gt(struct test *test,
+				     unsigned short expected);
+
+struct mock_param_matcher *int_eq(struct test *test, int expected);
+struct mock_param_matcher *int_ne(struct test *test, int expected);
+struct mock_param_matcher *int_lt(struct test *test, int expected);
+struct mock_param_matcher *int_le(struct test *test, int expected);
+struct mock_param_matcher *int_gt(struct test *test, int expected);
+struct mock_param_matcher *int_ge(struct test *test, int expected);
+
+struct mock_param_matcher *uint_eq(struct test *test, unsigned int expected);
+struct mock_param_matcher *uint_ne(struct test *test, unsigned int expected);
+struct mock_param_matcher *uint_lt(struct test *test, unsigned int expected);
+struct mock_param_matcher *uint_le(struct test *test, unsigned int expected);
+struct mock_param_matcher *uint_gt(struct test *test, unsigned int expected);
+struct mock_param_matcher *uint_ge(struct test *test, unsigned int expected);
+
+struct mock_param_matcher *long_eq(struct test *test, long expected);
+struct mock_param_matcher *long_ne(struct test *test, long expected);
+struct mock_param_matcher *long_le(struct test *test, long expected);
+struct mock_param_matcher *long_lt(struct test *test, long expected);
+struct mock_param_matcher *long_ge(struct test *test, long expected);
+struct mock_param_matcher *long_gt(struct test *test, long expected);
+
+struct mock_param_matcher *ulong_eq(struct test *test, unsigned long expected);
+struct mock_param_matcher *ulong_ne(struct test *test, unsigned long expected);
+struct mock_param_matcher *ulong_le(struct test *test, unsigned long expected);
+struct mock_param_matcher *ulong_lt(struct test *test, unsigned long expected);
+struct mock_param_matcher *ulong_ge(struct test *test, unsigned long expected);
+struct mock_param_matcher *ulong_gt(struct test *test, unsigned long expected);
+
+struct mock_param_matcher *longlong_eq(struct test *test, long long expected);
+struct mock_param_matcher *longlong_ne(struct test *test, long long expected);
+struct mock_param_matcher *longlong_le(struct test *test, long long expected);
+struct mock_param_matcher *longlong_lt(struct test *test, long long expected);
+struct mock_param_matcher *longlong_ge(struct test *test, long long expected);
+struct mock_param_matcher *longlong_gt(struct test *test, long long expected);
+
+struct mock_param_matcher *ulonglong_eq(struct test *test,
+					unsigned long long expected);
+struct mock_param_matcher *ulonglong_ne(struct test *test,
+					unsigned long long expected);
+struct mock_param_matcher *ulonglong_le(struct test *test,
+					unsigned long long expected);
+struct mock_param_matcher *ulonglong_lt(struct test *test,
+					unsigned long long expected);
+struct mock_param_matcher *ulonglong_ge(struct test *test,
+					unsigned long long expected);
+struct mock_param_matcher *ulonglong_gt(struct test *test,
+					unsigned long long expected);
+
+/* Matches pointers. */
+struct mock_param_matcher *ptr_eq(struct test *test, void *expected);
+struct mock_param_matcher *ptr_ne(struct test *test, void *expected);
+struct mock_param_matcher *ptr_lt(struct test *test, void *expected);
+struct mock_param_matcher *ptr_le(struct test *test, void *expected);
+struct mock_param_matcher *ptr_gt(struct test *test, void *expected);
+struct mock_param_matcher *ptr_ge(struct test *test, void *expected);
+
+/* Matches memory sections and strings. */
+struct mock_param_matcher *memeq(struct test *test,
+				 const void *buf,
+				 size_t size);
+struct mock_param_matcher *streq(struct test *test, const char *str);
+
+struct mock_action *u8_return(struct test *test, u8 ret);
+struct mock_action *u16_return(struct test *test, u16 ret);
+struct mock_action *u32_return(struct test *test, u32 ret);
+struct mock_action *u64_return(struct test *test, u64 ret);
+struct mock_action *char_return(struct test *test, char ret);
+struct mock_action *uchar_return(struct test *test, unsigned char ret);
+struct mock_action *schar_return(struct test *test, signed char ret);
+struct mock_action *short_return(struct test *test, short ret);
+struct mock_action *ushort_return(struct test *test, unsigned short ret);
+struct mock_action *int_return(struct test *test, int ret);
+struct mock_action *uint_return(struct test *test, unsigned int ret);
+struct mock_action *long_return(struct test *test, long ret);
+struct mock_action *ulong_return(struct test *test, unsigned long ret);
+struct mock_action *longlong_return(struct test *test, long long ret);
+struct mock_action *ulonglong_return(struct test *test, unsigned long long ret);
+struct mock_action *ptr_return(struct test *test, void *ret);
+
+#endif /* _TEST_MOCK_H */
diff --git a/test/Makefile b/test/Makefile
index ddff1ba..6fe9ab4 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,3 +1,5 @@
-obj-$(CONFIG_TEST)		+= test.o string-stream.o test-stream.o
-obj-$(CONFIG_TEST_TEST)		+= test-test.o mock-macro-test.o string-stream-test.o
+obj-$(CONFIG_TEST)		+= test.o mock.o common-mocks.o string-stream.o \
+  test-stream.o
+obj-$(CONFIG_TEST_TEST)		+= \
+  test-test.o test-mock.o mock-macro-test.o mock-test.o string-stream-test.o
 obj-$(CONFIG_EXAMPLE_TEST)	+= example-test.o
diff --git a/test/common-mocks.c b/test/common-mocks.c
new file mode 100644
index 0000000..9206115
--- /dev/null
+++ b/test/common-mocks.c
@@ -0,0 +1,263 @@
+#include <linux/kernel.h>
+#include <test/mock.h>
+
+static bool match_any(struct mock_param_matcher *pmatcher,
+		      struct test_stream *stream,
+		      const void *actual)
+{
+	stream->add(stream, "don't care");
+	return true;
+}
+
+static struct mock_param_matcher any_matcher = {
+	.match = match_any,
+};
+
+struct mock_param_matcher *any(struct test *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)			       \
+		bool match_##type_name##_##op_name(			       \
+				struct mock_param_matcher *pmatcher,	       \
+				struct test_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)					       \
+				stream->add(stream,			       \
+					    "%d "#op" %d",		       \
+					    actual,			       \
+					    matcher->expected);		       \
+			else						       \
+				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 *type_name##_##op_name(	       \
+				struct test *test, type expected)	       \
+		{							       \
+			struct mock_##type_name##_matcher *matcher;	       \
+									       \
+			matcher = test_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 test_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++)
+		stream->add(stream, "%02x, ", ((const char *) actual)[i]);
+	if (matches)
+		stream->add(stream, "== ");
+	else
+		stream->add(stream, "!= ");
+	for (i = 0; i < matcher->size; i++)
+		stream->add(stream,
+			    "%02x, ",
+			    ((const char *) matcher->expected)[i]);
+
+	return matches;
+}
+
+struct mock_param_matcher *memeq(struct test *test,
+				 const void *buf,
+				 size_t size)
+{
+	struct mock_memeq_matcher *matcher;
+
+	matcher = test_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 test_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)
+		stream->add(stream, "%s == %s", actual, matcher->expected);
+	else
+		stream->add(stream, "%s != %s", actual, matcher->expected);
+
+	return matches;
+}
+
+struct mock_param_matcher *streq(struct test *test, const char *str)
+{
+	struct mock_streq_matcher *matcher;
+
+	matcher = test_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 *type_name##_return(struct test *test,      \
+						       type ret)	       \
+		{							       \
+			struct mock_##type_name##_action *action;	       \
+									       \
+			action = test_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 *);
+
diff --git a/test/example-test.c b/test/example-test.c
index 53ec488..2f472c5 100644
--- a/test/example-test.c
+++ b/test/example-test.c
@@ -1,19 +1,62 @@
 #include <test/test.h>
+#include <test/mock.h>
+
+struct example {
+	int (*foo)(struct example *example, int num);
+};
+
+static int example_bar(struct example *example, int num)
+{
+	return example->foo(example, num);
+}
+
+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 example_simple_test(struct test *test)
 {
 	EXPECT_EQ(test, 1, 1);
 }
 
+static void example_mock_test(struct test *test)
+{
+	struct MOCK(example) *mock_example = test->priv;
+	struct example *example = mock_get_trgt(mock_example);
+	struct mock_expectation *handle;
+
+	handle = EXPECT_CALL(foo(mock_get_ctrl(mock_example), int_eq(test, 5)));
+	handle->action = int_return(test, 2);
+
+	EXPECT_EQ(test, 2, example_bar(example, 5));
+}
+
 static int example_test_init(struct test *test)
 {
 	test_info(test, "initializing");
 
+	test->priv = CONSTRUCT_MOCK(example, test);
+	if (!test->priv)
+		return -EINVAL;
+
 	return 0;
 }
 
 static struct test_case example_test_cases[] = {
 	TEST_CASE(example_simple_test),
+	TEST_CASE(example_mock_test),
 	{},
 };
 
diff --git a/test/mock-macro-test.c b/test/mock-macro-test.c
index f70393b..48d9478 100644
--- a/test/mock-macro-test.c
+++ b/test/mock-macro-test.c
@@ -1,5 +1,47 @@
 #include <test/test.h>
-#include <test/params.h>
+#include <test/mock.h>
+
+struct test_struct {
+	int (*one_param)(struct test_struct *test_struct);
+	int (*two_param)(struct test_struct *test_struct, int num);
+	int (*non_first_slot_param)(int num, struct test_struct *test_struct);
+	void *(*void_ptr_return)(struct test_struct *test_struct);
+};
+
+DECLARE_STRUCT_CLASS_MOCK_PREREQS(test_struct);
+
+DEFINE_STRUCT_CLASS_MOCK(METHOD(one_param), CLASS(test_struct),
+			 RETURNS(int),
+			 PARAMS(struct test_struct *));
+
+DEFINE_STRUCT_CLASS_MOCK(METHOD(two_param), CLASS(test_struct),
+			 RETURNS(int),
+			 PARAMS(struct test_struct *, int));
+
+DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX(METHOD(non_first_slot_param),
+			 CLASS(test_struct), HANDLE_INDEX(1),
+			 RETURNS(int),
+			 PARAMS(int, struct test_struct *));
+
+DEFINE_STRUCT_CLASS_MOCK(METHOD(void_ptr_return), CLASS(test_struct),
+			 RETURNS(void *),
+			 PARAMS(struct test_struct *));
+
+static int test_struct_init(struct MOCK(test_struct) *mock_test_struct)
+{
+	struct test_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(test_struct, test_struct_init);
+
+struct mock_macro_context {
+	struct MOCK(test_struct) *mock_test_struct;
+};
 
 #define TO_STR_INTERNAL(...) #__VA_ARGS__
 #define TO_STR(...) TO_STR_INTERNAL(__VA_ARGS__)
@@ -113,6 +155,43 @@
 						  type15)));
 }
 
+static void mock_macro_test_generated_method_code_works(struct test *test)
+{
+	struct mock_macro_context *ctx = test->priv;
+	struct MOCK(test_struct) *mock_test_struct = ctx->mock_test_struct;
+	struct test_struct *test_struct = mock_get_trgt(mock_test_struct);
+	struct mock_expectation *handle;
+
+	handle = EXPECT_CALL(one_param(mock_get_ctrl(mock_test_struct)));
+	handle->action = int_return(test, 0);
+	handle = EXPECT_CALL(two_param(mock_get_ctrl(mock_test_struct),
+				       int_eq(test, 5)));
+	handle->action = int_return(test, 1);
+	handle = EXPECT_CALL(non_first_slot_param(
+			int_eq(test, 5), mock_get_ctrl(mock_test_struct)));
+	handle->action = 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 test *test)
+{
+	struct mock_macro_context *ctx;
+
+	ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+	test->priv = ctx;
+
+	ctx->mock_test_struct = CONSTRUCT_MOCK(test_struct, test);
+	if (!ctx->mock_test_struct)
+		return -EINVAL;
+
+	return 0;
+}
+
 static struct test_case mock_macro_test_cases[] = {
 	TEST_CASE(mock_macro_is_equal),
 	TEST_CASE(mock_macro_if),
@@ -121,11 +200,13 @@
 	TEST_CASE(mock_macro_for_each_param),
 	TEST_CASE(mock_macro_param_list_from_types_basic),
 	TEST_CASE(mock_macro_arg_names_from_types),
+	TEST_CASE(mock_macro_test_generated_method_code_works),
 	{},
 };
 
 static struct test_module mock_macro_test_module = {
 	.name = "mock-macro-test",
+	.init = mock_macro_test_init,
 	.test_cases = mock_macro_test_cases,
 };
 module_test(mock_macro_test_module);
diff --git a/test/mock-test.c b/test/mock-test.c
new file mode 100644
index 0000000..bae6bbf
--- /dev/null
+++ b/test/mock-test.c
@@ -0,0 +1,265 @@
+#include <test/test.h>
+#include <test/mock.h>
+
+#include "test-mock.h"
+
+struct mock_test_context {
+	struct MOCK(test)	*mock_test;
+	struct mock		*mock;
+};
+
+static void mock_test_do_expect_basic(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 = -4;
+	static const char * const two_param_types[] = {"int", "int"};
+	const void *two_params[] = {&param0, &param1};
+	struct mock_param_matcher *matchers_any_two[] = {any(trgt), any(trgt)};
+	struct mock_expectation *expectation;
+	const void *ret;
+
+	expectation = mock_add_matcher(mock,
+				       "",
+				       NULL,
+				       matchers_any_two,
+				       ARRAY_SIZE(matchers_any_two));
+	expectation->action = int_return(trgt, 5);
+	EXPECT_EQ(test, 0, expectation->times_called);
+
+	ret = mock->do_expect(mock,
+			      "",
+			      NULL,
+			      two_param_types,
+			      two_params,
+			      ARRAY_SIZE(two_params));
+	ASSERT_NOT_ERR_OR_NULL(test, ret);
+	EXPECT_EQ(test, 5, *((int *) ret));
+	EXPECT_EQ(test, 1, expectation->times_called);
+}
+
+static void mock_test_ptr_eq(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;
+	void *param0 = ctx, *param1 = trgt;
+	static const char * const two_param_types[] = {"void *", "void *"};
+	const void *two_params[] = {&param0, &param1};
+	struct mock_param_matcher *matchers_two_ptrs[] = {
+		ptr_eq(trgt, param0), ptr_eq(trgt, param1)
+	};
+	struct mock_expectation *expectation;
+	const void *ret;
+
+	expectation = mock_add_matcher(mock,
+				       "",
+				       NULL,
+				       matchers_two_ptrs,
+				       ARRAY_SIZE(matchers_two_ptrs));
+	expectation->action = int_return(trgt, 0);
+	EXPECT_EQ(test, 0, expectation->times_called);
+
+	ret = mock->do_expect(mock,
+			      "",
+			      NULL,
+			      two_param_types,
+			      two_params,
+			      ARRAY_SIZE(two_params));
+	ASSERT_NOT_ERR_OR_NULL(test, ret);
+	EXPECT_EQ(test, 1, expectation->times_called);
+}
+
+static void mock_test_ptr_eq_not_equal(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;
+	void *param0 = ctx, *param1 = trgt;
+	static const char * const two_param_types[] = {"void *", "void *"};
+	const void *two_params[] = {&param0, &param1};
+	struct mock_param_matcher *matchers_two_ptrs[] = {
+		ptr_eq(trgt, param0), ptr_eq(trgt, 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 = int_return(trgt, 0);
+	EXPECT_EQ(test, 0, expectation->times_called);
+
+	ret = mock->do_expect(mock,
+			      "",
+			      NULL,
+			      two_param_types,
+			      two_params,
+			      ARRAY_SIZE(two_params));
+	EXPECT_FALSE(test, ret);
+	EXPECT_EQ(test, 0, expectation->times_called);
+}
+
+/*
+ * In order for us to be able to rely on EXPECT_CALL to validate other behavior,
+ * we need to test that unsatisfied EXPECT_CALL causes a test failure.
+ */
+static void mock_test_failed_expect_call_fails_test(struct test *test)
+{
+	struct mock_test_context *ctx = test->priv;
+	struct MOCK(test) *mock_test = ctx->mock_test;
+	struct mock *mock = ctx->mock;
+
+	/* mock is a pretend mock belonging to our mocked_test */
+
+	/* Put an expectation on mocked mock */
+	EXPECT_CALL(fail(mock, any(mock_get_trgt(mock_test))));
+
+	/*
+	 * Expect that mock_test will fail because the above won't be satisfied
+	 */
+	EXPECT_CALL(fail(mock_get_ctrl(mock_test), any(test)));
+
+	/*
+	 * Validate expectations of mocked mock, which should fail mocked test
+	 */
+	mock_validate_expectations(mock);
+
+	/* Validate mock_test's expectations, that is, it should have failed */
+	mock_validate_expectations(mock_get_ctrl(mock_test));
+	EXPECT_FALSE(test, mock_get_trgt(mock_test)->success);
+}
+
+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 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[] = {&param0, &param1};
+	struct mock_param_matcher *matchers[] = {
+		int_eq(trgt, 5),
+		int_eq(trgt, -4)
+	};
+	struct mock_expectation *expectation;
+	const void *ret;
+
+	expectation = mock_add_matcher(mock,
+				       "test_printk",
+				       test_printk,
+				       matchers,
+				       ARRAY_SIZE(matchers));
+	expectation->action = int_return(trgt, 5);
+	EXPECT_EQ(test, 0, expectation->times_called);
+
+	EXPECT_FALSE(test, mock_set_default_action(mock,
+						   "test_printk",
+						   test_printk,
+						   int_return(trgt, -4)));
+
+	ret = mock->do_expect(mock,
+			      "test_printk",
+			      test_printk,
+			      two_param_types,
+			      two_params,
+			      ARRAY_SIZE(two_params));
+	ASSERT_NOT_ERR_OR_NULL(test, ret);
+	EXPECT_EQ(test, -4, *((int *) ret));
+	EXPECT_EQ(test, 0, expectation->times_called);
+}
+
+static void mock_test_mock_validate_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[] = {
+		int_eq(trgt, 5),
+		int_eq(trgt, -4)
+	};
+	struct mock_expectation *expectation;
+
+	EXPECT_EQ(test, mock_get_trgt(mock_test), mock->test);
+
+	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;
+
+	EXPECT_CALL(fail(mock_get_ctrl(mock_test), any(test)));
+
+	mock_validate_expectations(mock);
+}
+
+void *do_mocked_fail(struct mock_action *this, const void **params, int len)
+{
+	static const int ret;
+	struct test_stream * const *stream_ptr = params[0];
+	struct test_stream *stream = *stream_ptr;
+
+	stream->set_level(stream, KERN_ERR);
+	stream->commit(stream);
+	return (void *) &ret;
+}
+
+static struct mock_action mocked_fail = {
+	.do_action = do_mocked_fail
+};
+
+static int mock_test_init(struct test *test)
+{
+	struct mock_test_context *ctx;
+
+	ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+	test->priv = ctx;
+
+	ctx->mock_test = CONSTRUCT_MOCK(test, test);
+	if (!ctx->mock_test)
+		return -EINVAL;
+
+	ctx->mock = test_kzalloc(test, sizeof(*ctx->mock), GFP_KERNEL);
+	if (!ctx->mock)
+		return -ENOMEM;
+	mock_init_ctrl(mock_get_trgt(ctx->mock_test), ctx->mock);
+
+	/* This test suite tests the behaviour of the error messages printed
+	 * when mocks fail, which requires the mocked fail to commit the
+	 * stream.
+	 */
+	mock_set_default_action(mock_get_ctrl(ctx->mock_test),
+		"fail", fail, &mocked_fail);
+	return 0;
+}
+
+static struct test_case mock_test_cases[] = {
+	TEST_CASE(mock_test_do_expect_basic),
+	TEST_CASE(mock_test_ptr_eq),
+	TEST_CASE(mock_test_ptr_eq_not_equal),
+	TEST_CASE(mock_test_failed_expect_call_fails_test),
+	TEST_CASE(mock_test_do_expect_default_return),
+	TEST_CASE(mock_test_mock_validate_expectations),
+	{},
+};
+
+static struct test_module mock_test_module = {
+	.name = "mock-test",
+	.init = mock_test_init,
+	.test_cases = mock_test_cases,
+};
+
+module_test(mock_test_module);
diff --git a/test/mock.c b/test/mock.c
new file mode 100644
index 0000000..12afb6c
--- /dev/null
+++ b/test/mock.c
@@ -0,0 +1,351 @@
+#include <test/mock.h>
+
+static bool mock_match_params(struct mock_matcher *matcher,
+		       struct test_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];
+		stream->add(stream, "\t");
+		tmp = param_matcher->match(param_matcher, stream, params[i]);
+		ret = ret && tmp;
+		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);
+
+void mock_validate_expectations(struct mock *mock)
+{
+	struct mock_expectation *expectation, *expectation_safe;
+	struct mock_method *method;
+	struct test_stream *stream;
+	int times_called;
+
+	stream = test_new_stream(mock->test);
+	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)
+			    ) {
+				stream->add(stream,
+					    "Expectation was not called the specified number of times:\n\t");
+				stream->add(stream,
+					    "Function: %s, min calls: %d, max calls: %d, actual calls: %d",
+					    method->method_name,
+					    expectation->min_calls_expected,
+					    expectation->max_calls_expected,
+					    times_called);
+				mock->test->fail(mock->test, stream);
+			}
+			list_del(&expectation->node);
+		}
+	}
+}
+
+static void mock_validate_wrapper(struct test_post_condition *condition)
+{
+	struct mock *mock = container_of(condition, struct mock, parent);
+
+	mock_validate_expectations(mock);
+}
+
+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->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 = test_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 = test_kzalloc(mock->test,
+				   sizeof(*expectation),
+				   GFP_KERNEL);
+	if (!expectation)
+		return NULL;
+
+	matcher = test_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 test_stream *stream,
+			      const char *type_name,
+			      const void *param)
+{
+	/*
+	 * Cannot find formatter, so just print the pointer of the
+	 * symbol.
+	 */
+	stream->add(stream, "<%pS>", param);
+}
+
+static void mock_add_method_declaration_to_stream(
+		struct test_stream *stream,
+		const char *function_name,
+		const char * const *type_names,
+		const void **params,
+		int len)
+{
+	int i;
+
+	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)
+			stream->add(stream, ", ");
+	}
+	stream->add(stream, ")\n");
+}
+
+static struct test_stream *mock_initialize_failure_message(
+		struct test *test,
+		const char *function_name,
+		const char * const *type_names,
+		const void **params,
+		int len)
+{
+	struct test_stream *stream;
+
+	stream = test_new_stream(test);
+	if (!stream)
+		return NULL;
+
+	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 test *test,
+					      struct test_stream *stream,
+					      char *message,
+					      struct mock *mock,
+					      struct mock_method *method,
+					      const char * const *type_names,
+					      const void **params,
+					      int len)
+{
+	stream->clear(stream);
+	stream->set_level(stream, KERN_WARNING);
+	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 test *test = mock->test;
+	struct mock_expectation *ret;
+	struct test_stream *attempted_matching_stream;
+	bool expectations_all_saturated = true;
+
+	struct test_stream *stream = test_new_stream(test);
+
+	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);
+		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;
+
+		attempted_matching_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.
+			 */
+			attempted_matching_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);
+		stream->append(stream, attempted_matching_stream);
+	}
+	test->fail(test, stream);
+	attempted_matching_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);
+}
diff --git a/test/test-mock.c b/test/test-mock.c
new file mode 100644
index 0000000..0ae4a7c
--- /dev/null
+++ b/test/test-mock.c
@@ -0,0 +1,31 @@
+#include "test-mock.h"
+
+DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN(METHOD(fail), CLASS(test),
+				     PARAMS(struct test *,
+					    struct test_stream *));
+
+DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN(METHOD(mock_vprintk), CLASS(test),
+				     PARAMS(const struct test *,
+					    const char *,
+					    struct va_format *));
+
+static int test_init(struct MOCK(test) *mock_test)
+{
+	struct test *trgt = mock_get_trgt(mock_test);
+	int ret;
+
+	ret = test_init_test(trgt, "MOCK(test)");
+	trgt->fail = fail;
+	mock_set_default_action(mock_get_ctrl(mock_test),
+				"fail",
+				fail,
+				int_return(mock_get_test(mock_test), 0));
+	trgt->vprintk = mock_vprintk;
+	mock_set_default_action(mock_get_ctrl(mock_test),
+				"mock_vprintk",
+				mock_vprintk,
+				int_return(mock_get_test(mock_test), 0));
+	return ret;
+}
+
+DEFINE_STRUCT_CLASS_MOCK_INIT(test, test_init);
diff --git a/test/test-mock.h b/test/test-mock.h
new file mode 100644
index 0000000..ed4c9df
--- /dev/null
+++ b/test/test-mock.h
@@ -0,0 +1,15 @@
+#include <test/test.h>
+#include <test/mock.h>
+
+DECLARE_STRUCT_CLASS_MOCK_PREREQS(test);
+
+DECLARE_STRUCT_CLASS_MOCK_VOID_RETURN(METHOD(fail), CLASS(test),
+				      PARAMS(struct test *,
+					     struct test_stream *));
+
+DECLARE_STRUCT_CLASS_MOCK_VOID_RETURN(METHOD(mock_vprintk), CLASS(test),
+				      PARAMS(const struct test *,
+					     const char *,
+					     struct va_format *));
+
+DECLARE_STRUCT_CLASS_MOCK_INIT(test);