diff mbox series

[1/2] kunit: expect failures from dynamic analysis tools

Message ID 20200820220552.3427995-1-urielguajardojr@gmail.com
State New
Headers show
Series [1/2] kunit: expect failures from dynamic analysis tools | expand

Commit Message

Uriel Guajardo Aug. 20, 2020, 10:05 p.m. UTC
Adds support to KUnit for failure expectation for specific tools. Uses
named KUnit resources to keep track of whether or not a failure expectation has
been set by the user.

- Adds a generic KUNIT_EXPECT_TOOL_FAIL macro that can expect failure from any
supported tool that uses the same identifying name

- Adds kunit_fail_from_tool which is used to flag failures for specific
tools.

Requires "kunit: suppport failure from dynamic analysis tools":
https://lore.kernel.org/linux-kselftest/20200813205722.1384108-1-urielguajardojr@gmail.com/

Signed-off-by: Uriel Guajardo <urielguajardo@google.com>
---
 include/kunit/test-bug.h | 53 ++++++++++++++++++++++++++++++++++++++++
 include/kunit/test.h     |  5 ++++
 lib/kunit/test.c         | 46 +++++++++++++++++++++++++++++-----
 3 files changed, 98 insertions(+), 6 deletions(-)
diff mbox series

Patch

diff --git a/include/kunit/test-bug.h b/include/kunit/test-bug.h
index 283c19ec328f..383198f70cb5 100644
--- a/include/kunit/test-bug.h
+++ b/include/kunit/test-bug.h
@@ -9,16 +9,69 @@ 
 #ifndef _KUNIT_TEST_BUG_H
 #define _KUNIT_TEST_BUG_H
 
+/**
+ * struct kunit_expectation - represents expectations in KUnit, specifically
+ * used to keep track of failure expectations from analysis tools
+ */
+struct kunit_expectation {
+	bool expected;
+	bool found;
+};
+
 #if IS_ENABLED(CONFIG_KUNIT)
 
 extern void kunit_fail_current_test(void);
 
+/**
+ * kunit_fail_from_tool() - Fails the currently running KUnit tests under the
+ * given tool name.
+ *
+ * Note: Uses a named KUnit resource to track state. Do not use the name
+ * KUNIT_TOOL_{tool} for KUnit resources outside of here.
+ */
+#define kunit_fail_from_tool(tool)			\
+	if (current->kunit_test)			\
+		kunit_tool_fail(current->kunit_test, #tool,\
+				"KUNIT_TOOL_" #tool)
+
+/**
+ * kunit_tool_expectation() - Returns the kunit_expectation for the given
+ * tool. If it cannot find the expectation, it creates an expectation and
+ * returns it.
+ *
+ * Note: Uses a named KUnit resource to track state. Do not use the name
+ * KUNIT_TOOL_{tool} for KUnit resources outside of here.
+ */
+#define kunit_tool_expectation(test, tool)			\
+	kunit_find_expectation(test, "KUNIT_TOOL_" #tool)
+
+
+/**
+ * KUNIT_EXPECT_TOOL_FAIL() - Fails the currently running KUnit test if the
+ * condition does not cause an error within the given tool.
+ *
+ * Note: 'tool' must be consistent with the name specified in
+ * kunit_fail_from_tool(). If the tool fails KUnit using another name, KUnit
+ * will treat it as a separate tool.
+ */
+#define KUNIT_EXPECT_TOOL_FAIL(test, condition, tool) do {		\
+	struct kunit_expectation *data = kunit_tool_expectation(test, tool);\
+	data->expected = true;						\
+	data->found = false;						\
+	condition;							\
+	KUNIT_EXPECT_EQ(test, data->expected, data->found);		\
+	data->expected = false;						\
+	data->found = false;						\
+} while (0)
+
 #else
 
 static inline void kunit_fail_current_test(void)
 {
 }
 
+#define kunit_fail_from_tool(tool) do { } while (0)
+
 #endif
 
 #endif /* _KUNIT_TEST_BUG_H */
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 81bf43a1abda..3da8e17ee32b 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -511,6 +511,11 @@  static inline int kunit_destroy_named_resource(struct kunit *test,
  */
 void kunit_remove_resource(struct kunit *test, struct kunit_resource *res);
 
+void kunit_tool_fail(struct kunit *test, char *tool_name, char *resource_name);
+
+struct kunit_expectation *kunit_find_expectation(struct kunit *test,
+						 char *resource_name);
+
 /**
  * kunit_kmalloc() - Like kmalloc() except the allocation is *test managed*.
  * @test: The test context object.
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index d8189d827368..458d1ad2daf2 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -22,6 +22,45 @@  void kunit_fail_current_test(void)
 		kunit_set_failure(current->kunit_test);
 }
 
+static void kunit_data_free(struct kunit_resource *res)
+{
+	kfree(res->data);
+}
+
+struct kunit_expectation *kunit_find_expectation(struct kunit *test,
+						 char *resource_name)
+{
+	struct kunit_resource *resource;
+	struct kunit_expectation *expectation;
+
+	struct kunit_resource *existing = kunit_find_named_resource(
+			test, resource_name);
+	if (!existing) {
+		expectation = kzalloc(sizeof(*expectation), GFP_KERNEL);
+		resource = kunit_alloc_and_get_resource(test, NULL,
+					kunit_data_free, GFP_KERNEL,
+					expectation);
+		resource->name = resource_name;
+		kunit_put_resource(resource);
+		return expectation;
+	}
+	kunit_put_resource(existing);
+	return existing->data;
+}
+EXPORT_SYMBOL_GPL(kunit_find_expectation);
+
+void kunit_tool_fail(struct kunit *test, char *tool_name, char *resource_name)
+{
+	struct kunit_expectation *data = kunit_find_expectation(test,
+								resource_name);
+	if (!data->expected) {
+		kunit_warn(test, "Dynamic analysis tool failure from %s",
+			   tool_name);
+		return kunit_fail_current_test();
+	}
+	data->found = true;
+}
+
 static void kunit_print_tap_version(void)
 {
 	static bool kunit_has_printed_tap_version;
@@ -538,11 +577,6 @@  static int kunit_kmalloc_init(struct kunit_resource *res, void *context)
 	return 0;
 }
 
-static void kunit_kmalloc_free(struct kunit_resource *res)
-{
-	kfree(res->data);
-}
-
 void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp)
 {
 	struct kunit_kmalloc_params params = {
@@ -552,7 +586,7 @@  void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp)
 
 	return kunit_alloc_resource(test,
 				    kunit_kmalloc_init,
-				    kunit_kmalloc_free,
+				    kunit_data_free,
 				    gfp,
 				    &params);
 }