kunit: added InSequence implementation

- added prerequisite checking system
- added macro for defining sequences
- added tests for InSequence behaviour

Change-Id: Iada5aa9ebac8403f0a948f4335e4b0962cc468f9
Signed-off-by: Felix Guo <felixguo@google.com>
diff --git a/include/test/mock.h b/include/test/mock.h
index fc209f9..b104fd6 100644
--- a/include/test/mock.h
+++ b/include/test/mock.h
@@ -77,6 +77,13 @@
 	struct mock_matcher *matcher;
 	struct mock_method *method;
 	int times_called;
+	/* internal list of prerequisites */
+	struct list_head prerequisites;
+};
+
+struct mock_expectation_prereq_entry {
+	struct mock_expectation *expectation;
+	struct list_head node;
 };
 
 struct mock_method {
@@ -333,6 +340,30 @@
 	return ActionOnMatch(expectation, return_action);
 }
 
+/**
+ * InSequence() - defines an order for expectations to be matched
+ * @test: the test, used for internal resource allocations
+ * @first: the first &struct mock_expectation in the sequence
+ * @...: the rest of the expectations in order following
+ *
+ * Example:
+ *
+ * .. code-block:: c
+ *
+ *	struct mock_expectation *a = EXPECT_CALL(...);
+ *	struct mock_expectation *b = EXPECT_CALL(...);
+ *	struct mock_expectation *c = EXPECT_CALL(...);
+ *
+ *	InSequence(test, a, b, c);
+ *
+ * Return:
+ * 0 if everything was successful, otherwise a memory allocation error
+ */
+#define InSequence(test, first, ...) \
+	mock_in_sequence(test, first, __VA_ARGS__, 0)
+
+int mock_in_sequence(struct test *test, struct mock_expectation *first, ...);
+
 #define mock_get_ctrl_internal(mock_object) (&(mock_object)->ctrl)
 #define mock_get_ctrl(mock_object) mock_get_ctrl_internal(mock_object)
 
diff --git a/test/mock-test.c b/test/mock-test.c
index 1281473..83e43ce 100644
--- a/test/mock-test.c
+++ b/test/mock-test.c
@@ -415,6 +415,161 @@
 	mock_validate_expectations(mock);
 }
 
+
+static void mock_stub(int a) { }
+
+/* Common references for InSequence tests */
+static int param_len = 1;
+static const char * const param_type[] = {"int"};
+
+static const void *a_params[] = { &(int){1} };
+static const void *b_params[] = { &(int){2} };
+static const void *c_params[] = { &(int){3} };
+
+/* Simple test of InSequence, a -> b -> c */
+static void mock_test_in_sequence_simple_pass(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 *a_matchers[] = { int_eq(trgt, 1) };
+	struct mock_param_matcher *b_matchers[] = { int_eq(trgt, 2) };
+	struct mock_param_matcher *c_matchers[] = { int_eq(trgt, 3) };
+
+	struct mock_expectation *c = mock_add_matcher(mock, "c", mock_stub,
+		c_matchers, param_len);
+	struct mock_expectation *b = mock_add_matcher(mock, "b", mock_stub,
+		b_matchers, param_len);
+	struct mock_expectation *a = mock_add_matcher(mock, "a", mock_stub,
+		a_matchers, param_len);
+
+	InSequence(test, a, b, c);
+
+	Never(EXPECT_CALL(fail(mock_get_ctrl(mock_test), any(test))));
+
+	mock->do_expect(mock, "a", mock_stub, param_type, a_params, param_len);
+	mock->do_expect(mock, "b", mock_stub, param_type, b_params, param_len);
+	mock->do_expect(mock, "c", mock_stub, param_type, c_params, param_len);
+
+	mock_validate_expectations(mock);
+}
+
+static void mock_test_in_sequence_simple_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;
+
+	struct mock_param_matcher *a_matchers[] = { int_eq(trgt, 1) };
+	struct mock_param_matcher *b_matchers[] = { int_eq(trgt, 2) };
+	struct mock_param_matcher *c_matchers[] = { int_eq(trgt, 3) };
+
+	struct mock_expectation *c = mock_add_matcher(mock, "c", mock_stub,
+		c_matchers, param_len);
+	struct mock_expectation *b = mock_add_matcher(mock, "b", mock_stub,
+		b_matchers, param_len);
+	struct mock_expectation *a = mock_add_matcher(mock, "a", mock_stub,
+		a_matchers, param_len);
+
+	InSequence(test, a, b, c);
+
+	EXPECT_CALL(fail(mock_get_ctrl(mock_test), any(test)));
+
+	mock->do_expect(mock, "a", mock_stub, param_type, a_params, param_len);
+	mock->do_expect(mock, "c", mock_stub, param_type, c_params, param_len);
+	mock->do_expect(mock, "b", mock_stub, param_type, b_params, param_len);
+}
+
+/* More complex test of InSequence on two chains in v formation:
+ *   a -> c
+ *   b -> c
+ */
+static void mock_test_in_sequence_abc_success(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 *a_matchers[] = { int_eq(trgt, 1) };
+	struct mock_param_matcher *b_matchers[] = { int_eq(trgt, 2) };
+	struct mock_param_matcher *c_matchers[] = { int_eq(trgt, 3) };
+
+	struct mock_expectation *c = mock_add_matcher(mock, "c", mock_stub,
+		c_matchers, param_len);
+	struct mock_expectation *b = mock_add_matcher(mock, "b", mock_stub,
+		b_matchers, param_len);
+	struct mock_expectation *a = mock_add_matcher(mock, "a", mock_stub,
+		a_matchers, param_len);
+
+	InSequence(test, a, c);
+	InSequence(test, b, c);
+
+	Never(EXPECT_CALL(fail(mock_get_ctrl(mock_test), any(test))));
+
+	mock->do_expect(mock, "a", mock_stub, param_type, a_params, param_len);
+	mock->do_expect(mock, "b", mock_stub, param_type, b_params, param_len);
+	mock->do_expect(mock, "c", mock_stub, param_type, c_params, param_len);
+}
+
+static void mock_test_in_sequence_bac_success(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 *a_matchers[] = { int_eq(trgt, 1) };
+	struct mock_param_matcher *b_matchers[] = { int_eq(trgt, 2) };
+	struct mock_param_matcher *c_matchers[] = { int_eq(trgt, 3) };
+
+	struct mock_expectation *c = mock_add_matcher(mock, "c", mock_stub,
+		c_matchers, param_len);
+	struct mock_expectation *b = mock_add_matcher(mock, "b", mock_stub,
+		b_matchers, param_len);
+	struct mock_expectation *a = mock_add_matcher(mock, "a", mock_stub,
+		a_matchers, param_len);
+
+	InSequence(test, a, c);
+	InSequence(test, b, c);
+
+	Never(EXPECT_CALL(fail(mock_get_ctrl(mock_test), any(test))));
+
+	mock->do_expect(mock, "b", mock_stub, param_type, b_params, param_len);
+	mock->do_expect(mock, "a", mock_stub, param_type, a_params, param_len);
+	mock->do_expect(mock, "c", mock_stub, param_type, c_params, param_len);
+}
+
+static void mock_test_in_sequence_no_a_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;
+
+	struct mock_param_matcher *a_matchers[] = { int_eq(trgt, 1) };
+	struct mock_param_matcher *b_matchers[] = { int_eq(trgt, 2) };
+	struct mock_param_matcher *c_matchers[] = { int_eq(trgt, 3) };
+
+	struct mock_expectation *c = mock_add_matcher(mock, "c", mock_stub,
+		c_matchers, param_len);
+	struct mock_expectation *b = mock_add_matcher(mock, "b", mock_stub,
+		b_matchers, param_len);
+	struct mock_expectation *a = mock_add_matcher(mock, "a", mock_stub,
+		a_matchers, param_len);
+
+	InSequence(test, a, c);
+	InSequence(test, b, c);
+
+	EXPECT_CALL(fail(mock_get_ctrl(mock_test), any(test)));
+
+	mock->do_expect(mock, "b", mock_stub, param_type, b_params, param_len);
+	mock->do_expect(mock, "c", mock_stub, param_type, c_params, param_len);
+}
+
 void *do_mocked_fail(struct mock_action *this, const void **params, int len)
 {
 	static const int ret;
@@ -471,6 +626,11 @@
 	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),
+	TEST_CASE(mock_test_in_sequence_simple_pass),
+	TEST_CASE(mock_test_in_sequence_simple_fail),
+	TEST_CASE(mock_test_in_sequence_abc_success),
+	TEST_CASE(mock_test_in_sequence_bac_success),
+	TEST_CASE(mock_test_in_sequence_no_a_fail),
 	{},
 };
 
diff --git a/test/mock.c b/test/mock.c
index 8928de8..5d44f71 100644
--- a/test/mock.c
+++ b/test/mock.c
@@ -38,29 +38,39 @@
 				  const void **params,
 				  int len);
 
+static bool mock_is_expectation_satisfied(struct mock_expectation *expectation)
+{
+	return (expectation->min_calls_expected <= expectation->times_called &&
+		expectation->times_called <= expectation->max_calls_expected);
+}
+
+static void mock_write_expectation_unsatisfied_message(
+	struct mock_expectation *expectation,
+	struct test_stream *stream)
+{
+	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",
+		    expectation->method->method_name,
+		    expectation->min_calls_expected,
+		    expectation->max_calls_expected,
+		    expectation->times_called);
+}
+
 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",
-					    expectation->method->method_name,
-					    expectation->min_calls_expected,
-					    expectation->max_calls_expected,
-					    times_called);
+			if (!mock_is_expectation_satisfied(expectation)) {
+				mock_write_expectation_unsatisfied_message(
+					expectation, stream);
 				mock->test->fail(mock->test, stream);
 			}
 			list_del(&expectation->node);
@@ -203,6 +213,7 @@
 	expectation->max_calls_expected = 1;
 	expectation->min_calls_expected = 1;
 
+	INIT_LIST_HEAD(&expectation->prerequisites);
 	ret = mock_add_expectation(mock, method_name, method_ptr, expectation);
 	if (ret < 0)
 		return NULL;
@@ -341,6 +352,61 @@
 		method->method_name, type_names, params, len);
 }
 
+static bool mock_are_prereqs_satisfied(struct mock_expectation *expectation,
+				       struct test_stream *stream)
+{
+	struct mock_expectation_prereq_entry *entry, *entry_safe;
+
+	list_for_each_entry_safe(entry, entry_safe,
+				 &expectation->prerequisites, node) {
+		if (!mock_is_expectation_satisfied(entry->expectation)) {
+			stream->add(stream,
+				    "Expectation %s matched but prerequisite expectation was not satisfied:\n",
+				    expectation->expectation_name);
+			mock_write_expectation_unsatisfied_message(
+				entry->expectation, stream);
+			return false;
+		}
+		/* Don't need to check satisfied prereq again. */
+		list_del(&entry->node);
+	}
+	return true;
+}
+
+/* Assumes that the var args are null terminated. */
+int mock_in_sequence(struct test *test, struct mock_expectation *first, ...)
+{
+	struct mock_expectation *prereq = first;
+	struct mock_expectation *curr = NULL;
+	struct mock_expectation_prereq_entry *entry;
+	va_list args;
+
+	va_start(args, first);
+
+	while ((curr = va_arg(args, struct mock_expectation*))) {
+		entry = test_kzalloc(test, sizeof(*entry), GFP_KERNEL);
+		if (!entry) {
+			va_end(args);
+			return -ENOMEM;
+		}
+		entry->expectation = prereq;
+		list_add_tail(&entry->node, &curr->prerequisites);
+		prereq = curr;
+	}
+	va_end(args);
+	return 0;
+}
+
+static inline bool does_mock_expectation_match_call(
+	struct mock_expectation *expectation,
+	struct test_stream *stream,
+	const void **params,
+	int len)
+{
+	return mock_match_params(expectation->matcher, stream, params, len) &&
+	       mock_are_prereqs_satisfied(expectation, stream);
+}
+
 static struct mock_expectation *mock_apply_expectations(
 		struct mock *mock,
 		struct mock_method *method,
@@ -382,7 +448,7 @@
 
 		attempted_matching_stream->add(attempted_matching_stream,
 			"Tried expectation: %s, but\n", ret->expectation_name);
-		if (mock_match_params(ret->matcher,
+		if (does_mock_expectation_match_call(ret,
 			attempted_matching_stream, params, len)) {
 			/*
 			 * Matcher was found; we won't print, so clean up the