mocking.rst: add discussion on how to get state in funcs for mocks/fakes
This goes over using named resources and container_of(), as these are
probably the two best ways we can demonstrate for stashing away some
state.
Signed-off-by: Daniel Latypov <dlatypov@google.com>
Change-Id: I7804b75dd986a4df0cde4d8fea6b4b276029a039
diff --git a/mocking.rst b/mocking.rst
index b84bbe1..2791e87 100644
--- a/mocking.rst
+++ b/mocking.rst
@@ -241,3 +241,181 @@
TODO(dlatypov@google.com): include discussion on global functions/general statefulness.
TODO(dlatypov@google.com): include section on worked example use cases.
+
+Storing and accessing state for fakes/mocks
+===========================================
+
+One of the challenges of implementing both mocks and fakes is how to track
+state. We can't pass in additional parameters since that'll change the function
+signature, so we need some way of stashing state somewhere.
+
+Below, we have two examples of how you can do so fairly cleanly.
+
+Using named resources
+---------------------
+
+We can use ``current->kunit_test`` with ``kunit_add_named_resource`` to store
+and retrieve test-specific data, e.g.
+
+.. code-block:: c
+
+ /* in some shared file, mock_write.h/c */
+
+ /* Store some data per-test and have a kunit_resource handle for it. */
+ struct mock_write_data {
+ int times_called;
+ bool should_return_error;
+ };
+ static struct kunit_resource mock_write_data_resource;
+
+ int mock_write_init(struct kunit *test, struct mock_write_data *data) {
+ data->times_called = 0;
+ data->should_return_error = false;
+
+ return kunit_add_named_resource(test, NULL, NULL, &mock_write_data_resource,
+ "mock_write_data", data);
+ };
+
+ int mock_write(const char *data)
+ {
+ struct kunit_resource *resource;
+ struct mock_write_data *mock;
+
+ if (!current->kunit_test)
+ return -1;
+
+ resource = kunit_find_named_resource(current->kunit_test, "mock_write_data");
+ if (!resource) {
+ KUNIT_FAIL(current->kunit_test, "mock_write called before mock_write_init()!");
+ return -1;
+ }
+
+ mock = resource->data;
+ mock->times_called++;
+ return mock->should_return_error ? -1 : 0;
+ }
+
+
+ /* Then in the test file, can use the mock like so */
+
+ static void example_write_test(struct kunit *test)
+ {
+ struct mock_write_data mock;
+ mock_write_init(test, &mock);
+
+ mock.should_return_error = true;
+ KUNIT_EXPECT_LT(test, mock_write("hi"), 0);
+
+ mock.should_return_error = false;
+ KUNIT_EXPECT_EQ(test, mock_write("hi"), 0);
+
+ KUNIT_EXPECT_EQ(test, mock.times_called, 2);
+ }
+
+Storing state without KUnit
+---------------------------
+
+The approach above is tied to KUnit, but it's obviously possible to come up
+with ways to do it without that dependency.
+
+For example, if you're targeting an ops struct, we can employ some
+``container_of()`` shenanigans.
+
+To make the example a bit simpler, let's assume our ops struct passes a pointer
+to itself for each operation.
+
+.. code-block:: c
+
+ struct writer {
+ int (*write)(struct writer *writer, const char *data);
+ };
+
+ /* in mock_writer.h/c */
+ struct mock_writer {
+ struct writer ops;
+ int times_called;
+ bool should_return_error;
+ };
+
+ static int mock_write(struct writer *writer, const char *data)
+ {
+ struct mock_writer *mock = container_of(writer, struct mock_writer, ops);
+
+ mock->times_called++;
+ return mock->should_return_error ? -1 : 0;
+ }
+
+ void init_mock_writer(struct mock_writer *mock) {
+ mock->ops.write = mock_write;
+ mock->times_called = 0;
+ mock->should_return_error = false;
+ }
+
+
+ /* Then in the test file */
+
+ static void example_simple_test(struct kunit *test)
+ {
+ struct mock_writer mock;
+ struct writer *writer = &mock.ops;
+
+ init_mock_writer(&mock);
+
+ mock.should_return_error = true;
+ KUNIT_EXPECT_LT(test, writer->write(writer, "hi"), 0);
+
+ mock.should_return_error = false;
+ KUNIT_EXPECT_EQ(test, writer->write(writer, "hi"), 0);
+
+ KUNIT_EXPECT_EQ(test, mock.times_called, 2);
+ }
+
+
+If this seems unrealistic, that's because it is, but it's not too far from the
+truth. E.g. ``struct inode`` has a ``struct inode_operations *i_ops`` member
+and each operation takes a ``struct inode*`` as an argument (or a ``struct
+dentry`` which we can easily convert over via ``d_inode()``).
+
+So in that more realistic example, we'd have:
+
+.. code-block:: c
+
+ struct mock_inode {
+ struct inode real;
+
+ /* mock/fake state stuff */
+ int readlink_err;
+ int get_acl_err;
+ };
+
+ static struct posix_acl *mock_get_acl(struct inode *inode, int type)
+ {
+ struct mock_inode *mock = container_of(inode, struct mock_inode, real);
+
+ if (mock->get_acl_err)
+ return ERR_PTR(get_acl_err);
+
+ return posix_acl_alloc(3, GFP_KERNEL);
+ }
+
+ static int mock_readlink(struct dentry *dentry, char __user * buffer, int buflen)
+ {
+ /* get mock_inode indrectly */
+ struct inode *inode = d_inode(dentry);
+ struct mock_inode *mock = container_of(inode, struct mock_inode, real);
+
+ return mock->readlink_err;
+ }
+
+ struct inode_operations mock_inode_operations = {
+ .get_acl = mock_get_acl,
+ .readlink = mock_readlink,
+ /* ... */
+ };
+
+ void mock_inode_init(struct mock_inode *mock)
+ {
+ /* ... */
+
+ mock->real.i_ops = &mock_inode_operations;
+ }