From patchwork Wed Nov 28 19:36:18 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703217 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C82A016B1 for ; Wed, 28 Nov 2018 19:37:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BA12B2DC56 for ; Wed, 28 Nov 2018 19:37:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id ADCEC2B991; Wed, 28 Nov 2018 19:37:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BEF1F2C35F for ; Wed, 28 Nov 2018 19:37:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729230AbeK2Gkd (ORCPT ); Thu, 29 Nov 2018 01:40:33 -0500 Received: from mail-qt1-f202.google.com ([209.85.160.202]:42517 "EHLO mail-qt1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728743AbeK2Gkd (ORCPT ); Thu, 29 Nov 2018 01:40:33 -0500 Received: by mail-qt1-f202.google.com with SMTP id n50so25073544qtb.9 for ; Wed, 28 Nov 2018 11:37:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=9/WTV2LtiPKgAVLKUxd/xyVXjHr+yAYWaxXCoSQgwdw=; b=CBghUETxoLN2VHsU7NKROcuFcSSuVV+YHCYeK3mzsQVTNZkkzKyCeUcorOe6DkMoUL zTF24VV0MvJXbGy3zizTVpdFe1tCyjIgk1sF2xPkih3vGffi4LyNxgbqVnROB/cr3SDB ePvb+ezvtllkKUKEJAUHNm2KxZ5+WhsTvhUgIUEzSQ3m5zvMJEZwNuaIhbjAWaZk3szx /KC7ICEA71CPctJOeowZPrWgDMnJ2H+nbsGXN01rKQWuF9o/yTw02Pb7RI618RgNpFSV SbboS40dedNE2VRgkC9lf8qxzYsaczbzNRkNBbE+jrLjsOZqU+UZNpGL0WmI8bo33kRJ B0uQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=9/WTV2LtiPKgAVLKUxd/xyVXjHr+yAYWaxXCoSQgwdw=; b=HwasaAA3coNz0ZAi+Ji/WDNwjL2ws0c06T1q+tOXr/YwGzyME3jtdHriJjJU5w5xHB ZtuDae10zCeMZHYg6W6rKAOymvGigWCjP32YA7+BDvnNHK49wM3cp9oTxYYlm9wPIqFZ LT69Nd4rl1R3ijKQkdKyTKuo+fuvXeO+rSvsua4tWlegJ5WzCRgR/uDzn9y4YNnmC/bb shjjy0KXScigelrCfTrvDvDmI1ITZLA/UUrrSI5wJ0xxEY9v1Dhni+XBK4/S4tYFXY5i g5WdoJ/Emgtv1axWfZmutWGGwDmgVOplPEzsiee853FLa3efszn0DxhX8bvaOriSzp0L O5pw== X-Gm-Message-State: AA+aEWa+uKpOfiQMzIttfzL0HyMnrTbFzuVWg0SsdkDxqdaNta/kDA8g yRkV/ZRqBe7xFBrxwLJWsDaU8GkAL4KHMwXcEEaKfQ== X-Google-Smtp-Source: AFSGD/V71K6ZQXog2F4hTikvKyu/+E/ak95SHNLypykzWzMJ7uagRuIhXDfJruhzkRT+hEubIHzADxxHlhV7URlGJaMfOA== X-Received: by 2002:a37:5246:: with SMTP id g67mr4482868qkb.40.1543433866700; Wed, 28 Nov 2018 11:37:46 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:18 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-2-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 01/19] kunit: test: add KUnit test runner core From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add core facilities for defining unit tests; this provides a common way to define test cases, functions that execute code which is under test and determine whether the code under test behaves as expected; this also provides a way to group together related test cases in test suites (here we call them test_modules). Just define test cases and how to execute them for now; setting expectations on code will be defined later. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 165 ++++++++++++++++++++++++++++++++++++++++++ kunit/Kconfig | 17 +++++ kunit/Makefile | 1 + kunit/test.c | 168 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 351 insertions(+) create mode 100644 include/kunit/test.h create mode 100644 kunit/Kconfig create mode 100644 kunit/Makefile create mode 100644 kunit/test.c diff --git a/include/kunit/test.h b/include/kunit/test.h new file mode 100644 index 0000000000000..ffe66bb355d63 --- /dev/null +++ b/include/kunit/test.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Base unit test (KUnit) API. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _KUNIT_TEST_H +#define _KUNIT_TEST_H + +#include +#include + +struct kunit; + +/** + * struct kunit_case - represents an individual test case. + * @run_case: the function representing the actual test case. + * @name: the name of the test case. + * + * A test case is a function with the signature, ``void (*)(struct kunit *)`` + * that makes expectations (see KUNIT_EXPECT_TRUE()) about code under test. Each + * test case is associated with a &struct kunit_module and will be run after the + * module's init function and followed by the module's exit function. + * + * A test case should be static and should only be created with the KUNIT_CASE() + * macro; additionally, every array of test cases should be terminated with an + * empty test case. + * + * Example: + * + * .. code-block:: c + * + * void add_test_basic(struct kunit *test) + * { + * KUNIT_EXPECT_EQ(test, 1, add(1, 0)); + * KUNIT_EXPECT_EQ(test, 2, add(1, 1)); + * KUNIT_EXPECT_EQ(test, 0, add(-1, 1)); + * KUNIT_EXPECT_EQ(test, INT_MAX, add(0, INT_MAX)); + * KUNIT_EXPECT_EQ(test, -1, add(INT_MAX, INT_MIN)); + * } + * + * static struct kunit_case example_test_cases[] = { + * KUNIT_CASE(add_test_basic), + * {}, + * }; + * + */ +struct kunit_case { + void (*run_case)(struct kunit *test); + const char name[256]; + + /* private: internal use only. */ + bool success; +}; + +/** + * KUNIT_CASE - A helper for creating a &struct kunit_case + * @test_name: a reference to a test case function. + * + * Takes a symbol for a function representing a test case and creates a + * &struct kunit_case object from it. See the documentation for + * &struct kunit_case for an example on how to use it. + */ +#define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name } + +/** + * struct kunit_module - describes a related collection of &struct kunit_case s. + * @name: the name of the test. Purely informational. + * @init: called before every test case. + * @exit: called after every test case. + * @test_cases: a null terminated array of test cases. + * + * A kunit_module is a collection of related &struct kunit_case s, such that + * @init is called before every test case and @exit is called after every test + * case, similar to the notion of a *test fixture* or a *test class* in other + * unit testing frameworks like JUnit or Googletest. + * + * Every &struct kunit_case must be associated with a kunit_module for KUnit to + * run it. + */ +struct kunit_module { + const char name[256]; + int (*init)(struct kunit *test); + void (*exit)(struct kunit *test); + struct kunit_case *test_cases; +}; + +/** + * struct kunit - represents a running instance of a test. + * @priv: for user to store arbitrary data. Commonly used to pass data created + * in the init function (see &struct kunit_module). + * + * Used to store information about the current context under which the test is + * running. Most of this data is private and should only be accessed indirectly + * via public functions; the one exception is @priv which can be used by the + * test writer to store arbitrary data. + */ +struct kunit { + void *priv; + + /* private: internal use only. */ + const char *name; /* Read only after initialization! */ + spinlock_t lock; /* Gaurds all mutable test state. */ + bool success; /* Protected by lock. */ + void (*vprintk)(const struct kunit *test, + const char *level, + struct va_format *vaf); +}; + +int kunit_init_test(struct kunit *test, const char *name); + +int kunit_run_tests(struct kunit_module *module); + +/** + * module_test() - used to register a &struct kunit_module with KUnit. + * @module: a statically allocated &struct kunit_module. + * + * Registers @module with the test framework. See &struct kunit_module for more + * information. + */ +#define module_test(module) \ + static int module_kunit_init##module(void) \ + { \ + return kunit_run_tests(&module); \ + } \ + late_initcall(module_kunit_init##module) + +void __printf(3, 4) kunit_printk(const char *level, + const struct kunit *test, + const char *fmt, ...); + +/** + * kunit_info() - Prints an INFO level message associated with the current test. + * @test: The test context object. + * @fmt: A printk() style format string. + * + * Prints an info level message associated with the test module being run. Takes + * a variable number of format parameters just like printk(). + */ +#define kunit_info(test, fmt, ...) \ + kunit_printk(KERN_INFO, test, fmt, ##__VA_ARGS__) + +/** + * kunit_warn() - Prints a WARN level message associated with the current test. + * @test: The test context object. + * @fmt: A printk() style format string. + * + * See kunit_info(). + */ +#define kunit_warn(test, fmt, ...) \ + kunit_printk(KERN_WARNING, test, fmt, ##__VA_ARGS__) + +/** + * kunit_err() - Prints an ERROR level message associated with the current test. + * @test: The test context object. + * @fmt: A printk() style format string. + * + * See kunit_info(). + */ +#define kunit_err(test, fmt, ...) \ + kunit_printk(KERN_ERR, test, fmt, ##__VA_ARGS__) + +#endif /* _KUNIT_TEST_H */ diff --git a/kunit/Kconfig b/kunit/Kconfig new file mode 100644 index 0000000000000..49b44c4f6630a --- /dev/null +++ b/kunit/Kconfig @@ -0,0 +1,17 @@ +# +# KUnit base configuration +# + +menu "KUnit support" + +config KUNIT + bool "Enable support for unit tests (KUnit)" + depends on UML + help + Enables support for kernel unit tests (KUnit), a lightweight unit + testing and mocking framework for the Linux kernel. These tests are + able to be run locally on a developer's workstation without a VM or + special hardware. For more information, please see + Documentation/kunit/ + +endmenu diff --git a/kunit/Makefile b/kunit/Makefile new file mode 100644 index 0000000000000..5efdc4dea2c08 --- /dev/null +++ b/kunit/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_KUNIT) += test.o diff --git a/kunit/test.c b/kunit/test.c new file mode 100644 index 0000000000000..26d3d6d260e6c --- /dev/null +++ b/kunit/test.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Base unit test (KUnit) API. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include +#include + +static bool kunit_get_success(struct kunit *test) +{ + unsigned long flags; + bool success; + + spin_lock_irqsave(&test->lock, flags); + success = test->success; + spin_unlock_irqrestore(&test->lock, flags); + + return success; +} + +static void kunit_set_success(struct kunit *test, bool success) +{ + unsigned long flags; + + spin_lock_irqsave(&test->lock, flags); + test->success = success; + spin_unlock_irqrestore(&test->lock, flags); +} + +static int kunit_vprintk_emit(const struct kunit *test, + int level, + const char *fmt, + va_list args) +{ + return vprintk_emit(0, level, NULL, 0, fmt, args); +} + +static int kunit_printk_emit(const struct kunit *test, + int level, + const char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = kunit_vprintk_emit(test, level, fmt, args); + va_end(args); + + return ret; +} + +static void kunit_vprintk(const struct kunit *test, + const char *level, + struct va_format *vaf) +{ + kunit_printk_emit(test, + level[1] - '0', + "kunit %s: %pV", test->name, vaf); +} + +int kunit_init_test(struct kunit *test, const char *name) +{ + spin_lock_init(&test->lock); + test->name = name; + test->vprintk = kunit_vprintk; + + return 0; +} + +/* + * Initializes and runs test case. Does not clean up or do post validations. + */ +static void kunit_run_case_internal(struct kunit *test, + struct kunit_module *module, + struct kunit_case *test_case) +{ + int ret; + + if (module->init) { + ret = module->init(test); + if (ret) { + kunit_err(test, "failed to initialize: %d", ret); + kunit_set_success(test, false); + return; + } + } + + test_case->run_case(test); +} + +/* + * Performs post validations and cleanup after a test case was run. + * XXX: Should ONLY BE CALLED AFTER kunit_run_case_internal! + */ +static void kunit_run_case_cleanup(struct kunit *test, + struct kunit_module *module, + struct kunit_case *test_case) +{ + if (module->exit) + module->exit(test); +} + +/* + * Performs all logic to run a test case. + */ +static bool kunit_run_case(struct kunit *test, + struct kunit_module *module, + struct kunit_case *test_case) +{ + kunit_set_success(test, true); + + kunit_run_case_internal(test, module, test_case); + kunit_run_case_cleanup(test, module, test_case); + + return kunit_get_success(test); +} + +int kunit_run_tests(struct kunit_module *module) +{ + bool all_passed = true, success; + struct kunit_case *test_case; + struct kunit test; + int ret; + + ret = kunit_init_test(&test, module->name); + if (ret) + return ret; + + for (test_case = module->test_cases; test_case->run_case; test_case++) { + success = kunit_run_case(&test, module, test_case); + if (!success) + all_passed = false; + + kunit_info(&test, + "%s %s", + test_case->name, + success ? "passed" : "failed"); + } + + if (all_passed) + kunit_info(&test, "all tests passed"); + else + kunit_info(&test, "one or more tests failed"); + + return 0; +} + +void kunit_printk(const char *level, + const struct kunit *test, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + test->vprintk(test, level, &vaf); + + va_end(args); +} From patchwork Wed Nov 28 19:36:19 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703285 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2B2D116B1 for ; Wed, 28 Nov 2018 19:39:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1D10F2D748 for ; Wed, 28 Nov 2018 19:39:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 10B8C2D7B2; Wed, 28 Nov 2018 19:39:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5A8632D748 for ; Wed, 28 Nov 2018 19:39:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729239AbeK2Gkh (ORCPT ); Thu, 29 Nov 2018 01:40:37 -0500 Received: from mail-pl1-f202.google.com ([209.85.214.202]:56432 "EHLO mail-pl1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725994AbeK2Gkd (ORCPT ); Thu, 29 Nov 2018 01:40:33 -0500 Received: by mail-pl1-f202.google.com with SMTP id x7so26282993pll.23 for ; Wed, 28 Nov 2018 11:37:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=vDLmNdG9bXgmUW08xwYOcqoYHxR/pZYPLkV8WNN1gqw=; b=PjdiosbSDH4l8fDAty+OaVfBEGiLzLyqurbuROgpZjcU1iDuHRCjsUZ2R9BcNkzLz7 3Eo+n42zuSsz+kiJArXYity0V+4BTShYkXrJfgVcNDcN2LNmiKwgMkvwf0WOC/Xf/Bez 9pcQKPZ24vxLFC/ebyOkd78cGUxa2YFy+83aWR4eYCmbD/k0X5xvkLqrCXs9nM3NH7du MK8jA0Ly4bo7KnxAJnO76GSbWVO1hXzyBygZqYZmEXCil8bGZ4AEQcfzG8w1U72OXCsh vJcZHB2zHrFZcQlmlNHqSBA46CNX3216bQ7uCJH+rU6nMbCDf31prIcCVf/h0EOqQd2C f2Dw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=vDLmNdG9bXgmUW08xwYOcqoYHxR/pZYPLkV8WNN1gqw=; b=nqb24idTg6GCPw5UfqXPO9x2dFQwz8ZcOlf23ZfcYMH0mqrofqYTimDvO3enGwjGpq 6X2KCEau2mZHvMudlX6jlN8WKKx42Kzci2uXcY5C+4uq1nMbzO/mN0kuSC3oLGTqGI/A mSiBBm3RTb5CkGGCDBJn8i6dfz4xJg9Ddk3a5TbzjsOEzz4YY0rNV8UWz1arF3ekUP41 R+s/luh3WwpxU7VygZcKo4LlQbe96VrcABbo9asvJCF2ruQdYPL/HqhEYcGtmMyuhLr0 flpss5KBFyJuZatYj5D/nT3wWrVECQs60/Wi1Ld6oPINgPFxGQYJr9epUEcNvAl5TKll Z/hA== X-Gm-Message-State: AA+aEWaVbg8uIgjUVS06qKgDn6MvTOQlclkJaIUCQ9FRg8hqbmu2U7C7 j9KzDSm0QukfpxRcyZ0JFodVlqm/4NFDdWkT+oN5yw== X-Google-Smtp-Source: AFSGD/XsDzpc6x23T3Vn7bM0xhEXuAiS2QPqDH047cpZlMjIgpn4AjfIeyAsxo+e+DJWI7Ez53ZeQFkE9E5PkbdDIW2tMA== X-Received: by 2002:a62:640e:: with SMTP id y14-v6mr3072802pfb.45.1543433868758; Wed, 28 Nov 2018 11:37:48 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:19 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-3-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 02/19] kunit: test: add test resource management API From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Create a common API for test managed resources like memory and test objects. A lot of times a test will want to set up infrastructure to be used in test cases; this could be anything from just wanting to allocate some memory to setting up a driver stack; this defines facilities for creating "test resources" which are managed by the test infrastructure and are automatically cleaned up at the conclusion of the test. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 109 +++++++++++++++++++++++++++++++++++++++++++ kunit/test.c | 95 +++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) diff --git a/include/kunit/test.h b/include/kunit/test.h index ffe66bb355d63..583840e24ffda 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -12,6 +12,69 @@ #include #include +struct kunit_resource; + +typedef int (*kunit_resource_init_t)(struct kunit_resource *, void *); +typedef void (*kunit_resource_free_t)(struct kunit_resource *); + +/** + * struct kunit_resource - represents a *test managed resource* + * @allocation: for the user to store arbitrary data. + * @free: a user supplied function to free the resource. Populated by + * kunit_alloc_resource(). + * + * Represents a *test managed resource*, a resource which will automatically be + * cleaned up at the end of a test case. + * + * Example: + * + * .. code-block:: c + * + * struct kunit_kmalloc_params { + * size_t size; + * gfp_t gfp; + * }; + * + * static int kunit_kmalloc_init(struct kunit_resource *res, void *context) + * { + * struct kunit_kmalloc_params *params = context; + * res->allocation = kmalloc(params->size, params->gfp); + * + * if (!res->allocation) + * return -ENOMEM; + * + * return 0; + * } + * + * static void kunit_kmalloc_free(struct kunit_resource *res) + * { + * kfree(res->allocation); + * } + * + * void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp) + * { + * struct kunit_kmalloc_params params; + * struct kunit_resource *res; + * + * params.size = size; + * params.gfp = gfp; + * + * res = kunit_alloc_resource(test, kunit_kmalloc_init, + * kunit_kmalloc_free, ¶ms); + * if (res) + * return res->allocation; + * else + * return NULL; + * } + */ +struct kunit_resource { + void *allocation; + kunit_resource_free_t free; + + /* private: internal use only. */ + struct list_head node; +}; + struct kunit; /** @@ -104,6 +167,7 @@ struct kunit { const char *name; /* Read only after initialization! */ spinlock_t lock; /* Gaurds all mutable test state. */ bool success; /* Protected by lock. */ + struct list_head resources; /* Protected by lock. */ void (*vprintk)(const struct kunit *test, const char *level, struct va_format *vaf); @@ -127,6 +191,51 @@ int kunit_run_tests(struct kunit_module *module); } \ late_initcall(module_kunit_init##module) +/** + * kunit_alloc_resource() - Allocates a *test managed resource*. + * @test: The test context object. + * @init: a user supplied function to initialize the resource. + * @free: a user supplied function to free the resource. + * @context: for the user to pass in arbitrary data. + * + * Allocates a *test managed resource*, a resource which will automatically be + * cleaned up at the end of a test case. See &struct kunit_resource for an + * example. + */ +struct kunit_resource *kunit_alloc_resource(struct kunit *test, + kunit_resource_init_t init, + kunit_resource_free_t free, + void *context); + +void kunit_free_resource(struct kunit *test, struct kunit_resource *res); + +/** + * kunit_kmalloc() - Like kmalloc() except the allocation is *test managed*. + * @test: The test context object. + * @size: The size in bytes of the desired memory. + * @gfp: flags passed to underlying kmalloc(). + * + * Just like `kmalloc(...)`, except the allocation is managed by the test case + * and is automatically cleaned up after the test case concludes. See &struct + * kunit_resource for more information. + */ +void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp); + +/** + * kunit_kzalloc() - Just like kunit_kmalloc(), but zeroes the allocation. + * @test: The test context object. + * @size: The size in bytes of the desired memory. + * @gfp: flags passed to underlying kmalloc(). + * + * See kzalloc() and kunit_kmalloc() for more information. + */ +static inline void *kunit_kzalloc(struct kunit *test, size_t size, gfp_t gfp) +{ + return kunit_kmalloc(test, size, gfp | __GFP_ZERO); +} + +void kunit_cleanup(struct kunit *test); + void __printf(3, 4) kunit_printk(const char *level, const struct kunit *test, const char *fmt, ...); diff --git a/kunit/test.c b/kunit/test.c index 26d3d6d260e6c..fb1a786e4c94f 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -66,6 +66,7 @@ static void kunit_vprintk(const struct kunit *test, int kunit_init_test(struct kunit *test, const char *name) { spin_lock_init(&test->lock); + INIT_LIST_HEAD(&test->resources); test->name = name; test->vprintk = kunit_vprintk; @@ -93,6 +94,11 @@ static void kunit_run_case_internal(struct kunit *test, test_case->run_case(test); } +static void kunit_case_internal_cleanup(struct kunit *test) +{ + kunit_cleanup(test); +} + /* * Performs post validations and cleanup after a test case was run. * XXX: Should ONLY BE CALLED AFTER kunit_run_case_internal! @@ -103,6 +109,8 @@ static void kunit_run_case_cleanup(struct kunit *test, { if (module->exit) module->exit(test); + + kunit_case_internal_cleanup(test); } /* @@ -150,6 +158,93 @@ int kunit_run_tests(struct kunit_module *module) return 0; } +struct kunit_resource *kunit_alloc_resource(struct kunit *test, + kunit_resource_init_t init, + kunit_resource_free_t free, + void *context) +{ + struct kunit_resource *res; + unsigned long flags; + int ret; + + res = kzalloc(sizeof(*res), GFP_KERNEL); + if (!res) + return NULL; + + ret = init(res, context); + if (ret) + return NULL; + + res->free = free; + spin_lock_irqsave(&test->lock, flags); + list_add_tail(&res->node, &test->resources); + spin_unlock_irqrestore(&test->lock, flags); + + return res; +} + +void kunit_free_resource(struct kunit *test, struct kunit_resource *res) +{ + res->free(res); + list_del(&res->node); + kfree(res); +} + +struct kunit_kmalloc_params { + size_t size; + gfp_t gfp; +}; + +static int kunit_kmalloc_init(struct kunit_resource *res, void *context) +{ + struct kunit_kmalloc_params *params = context; + + res->allocation = kmalloc(params->size, params->gfp); + if (!res->allocation) + return -ENOMEM; + + return 0; +} + +static void kunit_kmalloc_free(struct kunit_resource *res) +{ + kfree(res->allocation); +} + +void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp) +{ + struct kunit_kmalloc_params params; + struct kunit_resource *res; + + params.size = size; + params.gfp = gfp; + + res = kunit_alloc_resource(test, + kunit_kmalloc_init, + kunit_kmalloc_free, + ¶ms); + + if (res) + return res->allocation; + else + return NULL; +} + +void kunit_cleanup(struct kunit *test) +{ + struct kunit_resource *resource, *resource_safe; + unsigned long flags; + + spin_lock_irqsave(&test->lock, flags); + list_for_each_entry_safe(resource, + resource_safe, + &test->resources, + node) { + kunit_free_resource(test, resource); + } + spin_unlock_irqrestore(&test->lock, flags); +} + void kunit_printk(const char *level, const struct kunit *test, const char *fmt, ...) From patchwork Wed Nov 28 19:36:20 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703287 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 32F6515A8 for ; Wed, 28 Nov 2018 19:39:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 237432D748 for ; Wed, 28 Nov 2018 19:39:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 095552D76A; Wed, 28 Nov 2018 19:39:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8F1642D762 for ; Wed, 28 Nov 2018 19:39:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728743AbeK2Gkg (ORCPT ); Thu, 29 Nov 2018 01:40:36 -0500 Received: from mail-it1-f201.google.com ([209.85.166.201]:39908 "EHLO mail-it1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729239AbeK2Gkg (ORCPT ); Thu, 29 Nov 2018 01:40:36 -0500 Received: by mail-it1-f201.google.com with SMTP id k133so4321068ite.4 for ; Wed, 28 Nov 2018 11:37:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=Xfxjf+CSXWbmb3vEodnCAywAKpdNOSt3qX2Z15+pCtI=; b=Fv7Sy98Q8RFjZMHxwG7JynC7AcGNeOGek9vQeCpHnDobT29qNccUYR96dj0+VIllkk ZQhjsll+dUALguLu/laCF6LE969tx0gKGzf0JAFf4CI40FwXWZ+HjwBSXCqSfKqwGppQ 6Lj3d5gDH309ilY5/hgd4ovu9kebLsNg+VP/a9Vrt6dwJ9KNR/sK86w/X0hWI1iZS2AV YqSnyKsoKRBpm3zqxVFfxo5fRlpKZsOIiTiMdho1WJdiZ9ZJ5/G7qTzuHeiFRFuh2vuI hYJ5xjiLZ6wW+JzvdMDA28X22ehvu140pEPenelrp0nE0iWgmPdcNAktu/Ze3B2ewb71 oQyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Xfxjf+CSXWbmb3vEodnCAywAKpdNOSt3qX2Z15+pCtI=; b=J6gnldx14nJfco1QYMxpU6O1RJDWRz15gFdFQ9ofi18RnukXCO3aegUc4TbbScFhNO TPIInywS1zmAfDRg9sdTSwsfc+X865S6jVcvKiIdAkQ0DyJZ3FW49LMlTnytnptI3qcf Q5dWiFjmCq7ziv4LyvkbKi4m+k6wofg+jZr5sw8S0o2ri0HkT7d8hVulkondNyp13PmJ Zx3ILi2MJl3uA1IMy6aewTwLYPda6+jwpATmAPvBJEBQql50dGdOtZnPx3mIgLv984yd kcdp6TDeEeVz4/6gmViSJ5BQTGeDuEDwPPuP8ZiyOkOM0mgKUmkkFfoh7ZUOh6JhTqSe +BWQ== X-Gm-Message-State: AA+aEWYCeVGUAWkdkU3cuNPhuUtEj8EM+1OLt1gB2WhJ/rsgFyiSpSIQ 2ViUYfjCYL21gVjID8tbbDWPnlaHTXqNq+sSVSYjwg== X-Google-Smtp-Source: AFSGD/WXZbOijT965825ZIr9pOufHnotkvLuF1R50wIHqrUjKPJrZ6JJcjxQfQjkhabysTq8H+f5AJfYosxEeUhlF5Vbxw== X-Received: by 2002:a24:4ac4:: with SMTP id k187mr3369102itb.8.1543433871194; Wed, 28 Nov 2018 11:37:51 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:20 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-4-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 03/19] kunit: test: add string_stream a std::stream like string builder From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP A number of test features need to do pretty complicated string printing where it may not be possible to rely on a single preallocated string with parameters. So provide a library for constructing the string as you go similar to C++'s std::string. Signed-off-by: Brendan Higgins --- include/kunit/string-stream.h | 44 ++++++++++ kunit/Makefile | 3 +- kunit/string-stream.c | 149 ++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 include/kunit/string-stream.h create mode 100644 kunit/string-stream.c diff --git a/include/kunit/string-stream.h b/include/kunit/string-stream.h new file mode 100644 index 0000000000000..933ed5740cf07 --- /dev/null +++ b/include/kunit/string-stream.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * C++ stream style string builder used in KUnit for building messages. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _KUNIT_STRING_STREAM_H +#define _KUNIT_STRING_STREAM_H + +#include +#include +#include +#include + +struct string_stream_fragment { + struct list_head node; + char *fragment; +}; + +struct string_stream { + size_t length; + struct list_head fragments; + + /* length and fragments are protected by this lock */ + spinlock_t lock; + struct kref refcount; + int (*add)(struct string_stream *this, const char *fmt, ...); + int (*vadd)(struct string_stream *this, const char *fmt, va_list args); + char *(*get_string)(struct string_stream *this); + void (*clear)(struct string_stream *this); + bool (*is_empty)(struct string_stream *this); +}; + +struct string_stream *new_string_stream(void); + +void destroy_string_stream(struct string_stream *stream); + +void string_stream_get(struct string_stream *stream); + +int string_stream_put(struct string_stream *stream); + +#endif /* _KUNIT_STRING_STREAM_H */ diff --git a/kunit/Makefile b/kunit/Makefile index 5efdc4dea2c08..275b565a0e81f 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1 +1,2 @@ -obj-$(CONFIG_KUNIT) += test.o +obj-$(CONFIG_KUNIT) += test.o \ + string-stream.o diff --git a/kunit/string-stream.c b/kunit/string-stream.c new file mode 100644 index 0000000000000..1e7efa630cc35 --- /dev/null +++ b/kunit/string-stream.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * C++ stream style string builder used in KUnit for building messages. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include + +static int string_stream_vadd(struct string_stream *this, + const char *fmt, + va_list args) +{ + struct string_stream_fragment *fragment; + int len; + va_list args_for_counting; + unsigned long flags; + + /* Make a copy because `vsnprintf` could change it */ + va_copy(args_for_counting, args); + + /* Need space for null byte. */ + len = vsnprintf(NULL, 0, fmt, args_for_counting) + 1; + + va_end(args_for_counting); + + fragment = kmalloc(sizeof(*fragment), GFP_KERNEL); + if (!fragment) + return -ENOMEM; + + fragment->fragment = kmalloc(len, GFP_KERNEL); + if (!fragment->fragment) { + kfree(fragment); + return -ENOMEM; + } + + len = vsnprintf(fragment->fragment, len, fmt, args); + spin_lock_irqsave(&this->lock, flags); + this->length += len; + list_add_tail(&fragment->node, &this->fragments); + spin_unlock_irqrestore(&this->lock, flags); + return 0; +} + +static int string_stream_add(struct string_stream *this, const char *fmt, ...) +{ + va_list args; + int result; + + va_start(args, fmt); + result = string_stream_vadd(this, fmt, args); + va_end(args); + return result; +} + +static void string_stream_clear(struct string_stream *this) +{ + struct string_stream_fragment *fragment, *fragment_safe; + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + list_for_each_entry_safe(fragment, + fragment_safe, + &this->fragments, + node) { + list_del(&fragment->node); + kfree(fragment->fragment); + kfree(fragment); + } + this->length = 0; + spin_unlock_irqrestore(&this->lock, flags); +} + +static char *string_stream_get_string(struct string_stream *this) +{ + struct string_stream_fragment *fragment; + size_t buf_len = this->length + 1; /* +1 for null byte. */ + char *buf; + unsigned long flags; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return NULL; + + spin_lock_irqsave(&this->lock, flags); + list_for_each_entry(fragment, &this->fragments, node) + strlcat(buf, fragment->fragment, buf_len); + spin_unlock_irqrestore(&this->lock, flags); + + return buf; +} + +static bool string_stream_is_empty(struct string_stream *this) +{ + bool is_empty; + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + is_empty = list_empty(&this->fragments); + spin_unlock_irqrestore(&this->lock, flags); + + return is_empty; +} + +void destroy_string_stream(struct string_stream *stream) +{ + stream->clear(stream); + kfree(stream); +} + +static void string_stream_destroy(struct kref *kref) +{ + struct string_stream *stream = container_of(kref, + struct string_stream, + refcount); + destroy_string_stream(stream); +} + +struct string_stream *new_string_stream(void) +{ + struct string_stream *stream = kzalloc(sizeof(*stream), GFP_KERNEL); + + if (!stream) + return NULL; + + INIT_LIST_HEAD(&stream->fragments); + spin_lock_init(&stream->lock); + kref_init(&stream->refcount); + stream->add = string_stream_add; + stream->vadd = string_stream_vadd; + stream->get_string = string_stream_get_string; + stream->clear = string_stream_clear; + stream->is_empty = string_stream_is_empty; + return stream; +} + +void string_stream_get(struct string_stream *stream) +{ + kref_get(&stream->refcount); +} + +int string_stream_put(struct string_stream *stream) +{ + return kref_put(&stream->refcount, &string_stream_destroy); +} + From patchwork Wed Nov 28 19:36:21 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703283 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 88FE815A8 for ; Wed, 28 Nov 2018 19:39:36 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 77A602D722 for ; Wed, 28 Nov 2018 19:39:36 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6AD982D762; Wed, 28 Nov 2018 19:39:36 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BF8202D748 for ; Wed, 28 Nov 2018 19:39:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728668AbeK2Gkj (ORCPT ); Thu, 29 Nov 2018 01:40:39 -0500 Received: from mail-ot1-f74.google.com ([209.85.210.74]:53755 "EHLO mail-ot1-f74.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729228AbeK2Gki (ORCPT ); Thu, 29 Nov 2018 01:40:38 -0500 Received: by mail-ot1-f74.google.com with SMTP id o13so6034873otl.20 for ; Wed, 28 Nov 2018 11:37:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=kYyv/RTHzJjGNg9RbFN3NZsq45WeoGrJwFQsspAHU+g=; b=Ob6JecNpave3zhvIzALx2WdTG2Xf/wGlI/aOCikKVn7jzvpI2ltvh23barswRa5c14 3tqzjK0a9Vpy6Zbhov5vaM+si1RPqBkT/9MgrerDKynQXSQzUxuznSJSHNPUNyHZT48H 59hgN9IqUhaCcN9V18TPDmKbCQ5vEQqLm6tNYBGrgtT9a1KCVXvHLEazCzr/yUXaJttV cl7gzvJIN/tCmINTta3CLCiYXRylhaU6LSgdi+Lfvphl7XAdvF6sS19eKGA0Z2iVRRwz VOgxRJFZc/EFRhs7HF2PqZRVu9yzsTNA6j8cbGWZz5o4izuKO5PcRWTp5xKeBioj4jzC KBkQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=kYyv/RTHzJjGNg9RbFN3NZsq45WeoGrJwFQsspAHU+g=; b=iKwJ0zaXCsBYUS4aMoxn/8OzabTunJMLVB5m0TBVpWFmr636nXUgHAlTgdOHQOcjzo 8PZASytNwNzGCOpr8++WH/XSWR74koz/4ADDYMD1dZQ0aC+msJ4vHC4GW0FFFCQ67VOZ OEjTwHaLh6mOgNyaK/MWPkHMEOCm97QXwfWTaXUk6cmLZcLjr7SVHwC5Z4iTtQULxZTG jNdDtPu8vMIeyqmRCtvWqj/lT2766gfSnffWR9/W4qEDHR3GmSDvZc/Md/QW79u3h8af ZMzT9TZNoAkIbCVnyNuKnUKvFvz/jlGY76+svuI+oRPmuBjDTLzleJLmPVJ9VufrtxVa 5scg== X-Gm-Message-State: AA+aEWbKIDFWtud81KAyAfPCbKQBYtg9jyFK4t2p17WXcgwGKPS/pnnw wDl4wp/PDcQNyXNopkF82CThWxPU89UUo6aazOKrKg== X-Google-Smtp-Source: AFSGD/VmjDGXZv/Qf9wHBL015DQW6wWAWy/xIyPqNqjwxASmV1d+01uM7VvOaizuli4IdMjUSTMMhsj0NyLNeOs4RvhTsw== X-Received: by 2002:aca:c255:: with SMTP id s82-v6mr4424965oif.50.1543433873527; Wed, 28 Nov 2018 11:37:53 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:21 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-5-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 04/19] kunit: test: add test_stream a std::stream like logger From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP A lot of the expectation and assertion infrastructure prints out fairly complicated test failure messages, so add a C++ style log library for for logging test results. Signed-off-by: Brendan Higgins --- include/kunit/kunit-stream.h | 50 ++++++++++++ include/kunit/test.h | 2 + kunit/Makefile | 3 +- kunit/kunit-stream.c | 153 +++++++++++++++++++++++++++++++++++ kunit/test.c | 8 ++ 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 include/kunit/kunit-stream.h create mode 100644 kunit/kunit-stream.c diff --git a/include/kunit/kunit-stream.h b/include/kunit/kunit-stream.h new file mode 100644 index 0000000000000..3b3119450be3f --- /dev/null +++ b/include/kunit/kunit-stream.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * C++ stream style string formatter and printer used in KUnit for outputting + * KUnit messages. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _KUNIT_KUNIT_STREAM_H +#define _KUNIT_KUNIT_STREAM_H + +#include +#include + +struct kunit; + +/** + * struct kunit_stream - a std::stream style string builder. + * @set_level: sets the level that this string should be printed at. + * @add: adds the formatted input to the internal buffer. + * @append: adds the contents of other to this. + * @commit: prints out the internal buffer to the user. + * @clear: clears the internal buffer. + * + * A std::stream style string builder. Allows messages to be built up and + * printed all at once. + */ +struct kunit_stream { + void (*set_level)(struct kunit_stream *this, const char *level); + void (*add)(struct kunit_stream *this, const char *fmt, ...); + void (*append)(struct kunit_stream *this, struct kunit_stream *other); + void (*commit)(struct kunit_stream *this); + void (*clear)(struct kunit_stream *this); + /* private: internal use only. */ + struct kunit *test; + spinlock_t lock; /* Guards level. */ + const char *level; + struct string_stream *internal_stream; +}; + +/** + * kunit_new_stream() - constructs a new &struct kunit_stream. + * @test: The test context object. + * + * Constructs a new test managed &struct kunit_stream. + */ +struct kunit_stream *kunit_new_stream(struct kunit *test); + +#endif /* _KUNIT_KUNIT_STREAM_H */ diff --git a/include/kunit/test.h b/include/kunit/test.h index 583840e24ffda..ea424095e4fb4 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -11,6 +11,7 @@ #include #include +#include struct kunit_resource; @@ -171,6 +172,7 @@ struct kunit { void (*vprintk)(const struct kunit *test, const char *level, struct va_format *vaf); + void (*fail)(struct kunit *test, struct kunit_stream *stream); }; int kunit_init_test(struct kunit *test, const char *name); diff --git a/kunit/Makefile b/kunit/Makefile index 275b565a0e81f..6ddc622ee6b1c 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_KUNIT) += test.o \ - string-stream.o + string-stream.o \ + kunit-stream.o diff --git a/kunit/kunit-stream.c b/kunit/kunit-stream.c new file mode 100644 index 0000000000000..70f5182245e0b --- /dev/null +++ b/kunit/kunit-stream.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * C++ stream style string formatter and printer used in KUnit for outputting + * KUnit messages. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include + +static const char *kunit_stream_get_level(struct kunit_stream *this) +{ + unsigned long flags; + const char *level; + + spin_lock_irqsave(&this->lock, flags); + level = this->level; + spin_unlock_irqrestore(&this->lock, flags); + + return level; +} + +static void kunit_stream_set_level(struct kunit_stream *this, const char *level) +{ + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + this->level = level; + spin_unlock_irqrestore(&this->lock, flags); +} + +static void kunit_stream_add(struct kunit_stream *this, const char *fmt, ...) +{ + va_list args; + struct string_stream *stream = this->internal_stream; + + va_start(args, fmt); + if (stream->vadd(stream, fmt, args) < 0) + kunit_err(this->test, "Failed to allocate fragment: %s", fmt); + + va_end(args); +} + +static void kunit_stream_append(struct kunit_stream *this, + struct kunit_stream *other) +{ + struct string_stream *other_stream = other->internal_stream; + const char *other_content; + + other_content = other_stream->get_string(other_stream); + + if (!other_content) { + kunit_err(this->test, + "Failed to get string from second argument for appending."); + return; + } + + this->add(this, other_content); +} + +static void kunit_stream_clear(struct kunit_stream *this) +{ + this->internal_stream->clear(this->internal_stream); +} + +static void kunit_stream_commit(struct kunit_stream *this) +{ + struct string_stream *stream = this->internal_stream; + struct string_stream_fragment *fragment; + const char *level; + char *buf; + + level = kunit_stream_get_level(this); + if (!level) { + kunit_err(this->test, + "Stream was committed without a specified log level."); + level = KERN_ERR; + this->set_level(this, level); + } + + buf = stream->get_string(stream); + if (!buf) { + kunit_err(this->test, + "Could not allocate buffer, dumping stream:"); + list_for_each_entry(fragment, &stream->fragments, node) { + kunit_err(this->test, fragment->fragment); + } + goto cleanup; + } + + kunit_printk(level, this->test, buf); + kfree(buf); + +cleanup: + this->clear(this); +} + +static int kunit_stream_init(struct kunit_resource *res, void *context) +{ + struct kunit *test = context; + struct kunit_stream *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + res->allocation = stream; + stream->test = test; + spin_lock_init(&stream->lock); + stream->internal_stream = new_string_stream(); + + if (!stream->internal_stream) + return -ENOMEM; + + stream->set_level = kunit_stream_set_level; + stream->add = kunit_stream_add; + stream->append = kunit_stream_append; + stream->commit = kunit_stream_commit; + stream->clear = kunit_stream_clear; + + return 0; +} + +static void kunit_stream_free(struct kunit_resource *res) +{ + struct kunit_stream *stream = res->allocation; + + if (!stream->internal_stream->is_empty(stream->internal_stream)) { + kunit_err(stream->test, + "End of test case reached with uncommitted stream entries."); + stream->commit(stream); + } + + destroy_string_stream(stream->internal_stream); + kfree(stream); +} + +struct kunit_stream *kunit_new_stream(struct kunit *test) +{ + struct kunit_resource *res; + + res = kunit_alloc_resource(test, + kunit_stream_init, + kunit_stream_free, + test); + + if (res) + return res->allocation; + else + return NULL; +} diff --git a/kunit/test.c b/kunit/test.c index fb1a786e4c94f..abeb939dc7fa2 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -63,12 +63,20 @@ static void kunit_vprintk(const struct kunit *test, "kunit %s: %pV", test->name, vaf); } +static void kunit_fail(struct kunit *test, struct kunit_stream *stream) +{ + kunit_set_success(test, false); + stream->set_level(stream, KERN_ERR); + stream->commit(stream); +} + int kunit_init_test(struct kunit *test, const char *name) { spin_lock_init(&test->lock); INIT_LIST_HEAD(&test->resources); test->name = name; test->vprintk = kunit_vprintk; + test->fail = kunit_fail; return 0; } From patchwork Wed Nov 28 19:36:22 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703281 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 89A6916B1 for ; Wed, 28 Nov 2018 19:39:29 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7B0AA2D722 for ; Wed, 28 Nov 2018 19:39:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6DFE32D762; Wed, 28 Nov 2018 19:39:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 63B392D722 for ; Wed, 28 Nov 2018 19:39:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729330AbeK2Gkl (ORCPT ); Thu, 29 Nov 2018 01:40:41 -0500 Received: from mail-pg1-f201.google.com ([209.85.215.201]:56446 "EHLO mail-pg1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729293AbeK2Gkk (ORCPT ); Thu, 29 Nov 2018 01:40:40 -0500 Received: by mail-pg1-f201.google.com with SMTP id d3so12692078pgv.23 for ; Wed, 28 Nov 2018 11:37:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=wbYhaIrSssBkmUsCSp4N6B4U31VPkfIfDu5qOK0qxXk=; b=gjKtOTQatUc/vKMc+FuNmH0Ji4Z4TzJI/koox8wdpr0aJz3f+DUQjcok7womlOERYt ZnxleJpaG241zRjqCJOi7bz3DDi8wfa3O7V3HxHsnEzZkkU+Da8dHKURYfN0nZpX79Y2 2vNnbS9W1OO66EsGLg+iAVGrui7QPAoyLQ7Bmip32yltMmIUa1AnZuH3WcxHhWlsXo8j Y8K/8ddAdk74MWAb0Yd0AkYNspsAvzg8kv54k2rMMhyo2NRmTu/CudZCYviDa71niK3+ 63EoIKkK2pJRvS7pxAA/cNPRtZoIJmwCapoMOZbyWrSTHskXK7CQQLruT57ZUxO+4gqF kf4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=wbYhaIrSssBkmUsCSp4N6B4U31VPkfIfDu5qOK0qxXk=; b=E9wO0df3XqaoiYA5xptS7H3CiOYYJC3FZBhGiuQebxKQRd+axmTXs/og6js0ly66Dm dPzz3qk/UwyqZyoGZu8GuCUMBOTcWJc5ffCop6lKz2/Y2CpItOvSiGd7iPqMjDWFdkc7 Y1+rAnev0c49ut7hUTWtN653G6cA733QHX6bUZjnZPAonWNMRlenGwtM6P/GQDz+Lwav A80EhXLPOPhIjFvOkOlFw1tYGwnX7hVdNl61LCGk230LghaqKq7O3xFzBY/cSgk90ts6 f6WfLmX+VXO4WjsPbn6I7Qc4G2MU9svBnsH+pN7VR6Tpk2Sn3Qg0sB92qn7iSU2lvy0j RZYg== X-Gm-Message-State: AA+aEWZj6HUC770CLlq7eu57MHOJKQ1XzlWLfFRPVsBWwQcKUgDEj+BL 0tiHOVoqN9lyfrLXM6XlpErDL7iDkQvcI+pKgSk3MQ== X-Google-Smtp-Source: AFSGD/Vid8yHhh5WOlExXcUQ8FUnJdeHiu6R3Zj5JmsdiSOdcO/sTrQOWJPi+MCD0Hwef88D47UKDqC0Jahg3ZMA6/0pJQ== X-Received: by 2002:a62:1e81:: with SMTP id e123mr3122839pfe.23.1543433875635; Wed, 28 Nov 2018 11:37:55 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:22 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-6-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 05/19] kunit: test: add the concept of expectations From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add support for expectations, which allow properties to be specified and then verified in tests. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 379 +++++++++++++++++++++++++++++++++++++++++++ kunit/test.c | 34 ++++ 2 files changed, 413 insertions(+) diff --git a/include/kunit/test.h b/include/kunit/test.h index ea424095e4fb4..098a9dceef9ea 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -273,4 +273,383 @@ void __printf(3, 4) kunit_printk(const char *level, #define kunit_err(test, fmt, ...) \ kunit_printk(KERN_ERR, test, fmt, ##__VA_ARGS__) +static inline struct kunit_stream *kunit_expect_start(struct kunit *test, + const char *file, + const char *line) +{ + struct kunit_stream *stream = kunit_new_stream(test); + + stream->add(stream, "EXPECTATION FAILED at %s:%s\n\t", file, line); + + return stream; +} + +static inline void kunit_expect_end(struct kunit *test, + bool success, + struct kunit_stream *stream) +{ + if (!success) + test->fail(test, stream); + else + stream->clear(stream); +} + +#define KUNIT_EXPECT_START(test) \ + kunit_expect_start(test, __FILE__, __stringify(__LINE__)) + +#define KUNIT_EXPECT_END(test, success, stream) \ + kunit_expect_end(test, success, stream) + +#define KUNIT_EXPECT_MSG(test, success, message, fmt, ...) do { \ + struct kunit_stream *__stream = KUNIT_EXPECT_START(test); \ + \ + __stream->add(__stream, message); \ + __stream->add(__stream, fmt, ##__VA_ARGS__); \ + KUNIT_EXPECT_END(test, success, __stream); \ +} while (0) + +#define KUNIT_EXPECT(test, success, message) do { \ + struct kunit_stream *__stream = KUNIT_EXPECT_START(test); \ + \ + __stream->add(__stream, message); \ + KUNIT_EXPECT_END(test, success, __stream); \ +} while (0) + +/** + * KUNIT_SUCCEED() - A no-op expectation. Only exists for code clarity. + * @test: The test context object. + * + * The opposite of KUNIT_FAIL(), it is an expectation that cannot fail. In other + * words, it does nothing and only exists for code clarity. See + * KUNIT_EXPECT_TRUE() for more information. + */ +#define KUNIT_SUCCEED(test) do {} while (0) + +/** + * KUNIT_FAIL() - Always causes a test to fail when evaluated. + * @test: The test context object. + * @fmt: an informational message to be printed when the assertion is made. + * @...: string format arguments. + * + * The opposite of KUNIT_SUCCEED(), it is an expectation that always fails. In + * other words, it always results in a failed expectation, and consequently + * always causes the test case to fail when evaluated. See KUNIT_EXPECT_TRUE() + * for more information. + */ +#define KUNIT_FAIL(test, fmt, ...) \ + KUNIT_EXPECT_MSG(test, false, "", fmt, ##__VA_ARGS__) + +/** + * KUNIT_EXPECT_TRUE() - Causes a test failure when the expression is not true. + * @test: The test context object. + * @condition: an arbitrary boolean expression. The test fails when this does + * not evaluate to true. + * + * This and expectations of the form `KUNIT_EXPECT_*` will cause the test case + * to fail when the specified condition is not met; however, it will not prevent + * the test case from continuing to run; this is otherwise known as an + * *expectation failure*. + */ +#define KUNIT_EXPECT_TRUE(test, condition) \ + KUNIT_EXPECT(test, (condition), \ + "Expected " #condition " is true, but is false.") + +#define KUNIT_EXPECT_TRUE_MSG(test, condition, fmt, ...) \ + KUNIT_EXPECT_MSG(test, (condition), \ + "Expected " #condition " is true, but is false.\n",\ + fmt, ##__VA_ARGS__) + +/** + * KUNIT_EXPECT_FALSE() - Makes a test failure when the expression is not false. + * @test: The test context object. + * @condition: an arbitrary boolean expression. The test fails when this does + * not evaluate to false. + * + * Sets an expectation that @condition evaluates to false. See + * KUNIT_EXPECT_TRUE() for more information. + */ +#define KUNIT_EXPECT_FALSE(test, condition) \ + KUNIT_EXPECT(test, !(condition), \ + "Expected " #condition " is false, but is true.") + +#define KUNIT_EXPECT_FALSE_MSG(test, condition, fmt, ...) \ + KUNIT_EXPECT_MSG(test, !(condition), \ + "Expected " #condition " is false, but is true.\n",\ + fmt, ##__VA_ARGS__) + +void kunit_expect_binary_msg(struct kunit *test, + long long left, const char *left_name, + long long right, const char *right_name, + bool compare_result, + const char *compare_name, + const char *file, + const char *line, + const char *fmt, ...); + +static inline void kunit_expect_binary(struct kunit *test, + long long left, const char *left_name, + long long right, const char *right_name, + bool compare_result, + const char *compare_name, + const char *file, + const char *line) +{ + struct kunit_stream *stream = kunit_expect_start(test, file, line); + + stream->add(stream, + "Expected %s %s %s, but\n", + left_name, compare_name, right_name); + stream->add(stream, "\t\t%s == %lld\n", left_name, left); + stream->add(stream, "\t\t%s == %lld", right_name, right); + + kunit_expect_end(test, compare_result, stream); +} + +/* + * A factory macro for defining the expectations for the basic comparisons + * defined for the built in types. + * + * Unfortunately, there is no common type that all types can be promoted to for + * which all the binary operators behave the same way as for the actual types + * (for example, there is no type that long long and unsigned long long can + * both be cast to where the comparison result is preserved for all values). So + * the best we can do is do the comparison in the original types and then coerce + * everything to long long for printing; this way, the comparison behaves + * correctly and the printed out value usually makes sense without + * interpretation, but can always be interpretted to figure out the actual + * value. + */ +#define KUNIT_EXPECT_BINARY(test, left, condition, right) do { \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + kunit_expect_binary(test, \ + (long long) __left, #left, \ + (long long) __right, #right, \ + __left condition __right, #condition, \ + __FILE__, __stringify(__LINE__)); \ +} while (0) + +#define KUNIT_EXPECT_BINARY_MSG(test, left, condition, right, fmt, ...) do { \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + kunit_expect_binary_msg(test, \ + (long long) __left, #left, \ + (long long) __right, #right, \ + __left condition __right, #condition, \ + __FILE__, __stringify(__LINE__), \ + fmt, ##__VA_ARGS__); \ +} while (0) + +/** + * KUNIT_EXPECT_EQ() - Sets an expectation that @left and @right are equal. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an expectation that the values that @left and @right evaluate to are + * equal. This is semantically equivalent to + * KUNIT_EXPECT_TRUE(@test, (@left) == (@right)). See KUNIT_EXPECT_TRUE() for + * more information. + */ +#define KUNIT_EXPECT_EQ(test, left, right) \ + KUNIT_EXPECT_BINARY(test, left, ==, right) + +#define KUNIT_EXPECT_EQ_MSG(test, left, right, fmt, ...) \ + KUNIT_EXPECT_BINARY_MSG(test, \ + left, \ + ==, \ + right, \ + fmt, \ + ##__VA_ARGS__) + +/** + * KUNIT_EXPECT_NE() - An expectation that @left and @right are not equal. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an expectation that the values that @left and @right evaluate to are not + * equal. This is semantically equivalent to + * KUNIT_EXPECT_TRUE(@test, (@left) != (@right)). See KUNIT_EXPECT_TRUE() for + * more information. + */ +#define KUNIT_EXPECT_NE(test, left, right) \ + KUNIT_EXPECT_BINARY(test, left, !=, right) + +#define KUNIT_EXPECT_NE_MSG(test, left, right, fmt, ...) \ + KUNIT_EXPECT_BINARY_MSG(test, \ + left, \ + !=, \ + right, \ + fmt, \ + ##__VA_ARGS__) + +/** + * KUNIT_EXPECT_LT() - An expectation that @left is less than @right. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an expectation that the value that @left evaluates to is less than the + * value that @right evaluates to. This is semantically equivalent to + * KUNIT_EXPECT_TRUE(@test, (@left) < (@right)). See KUNIT_EXPECT_TRUE() for + * more information. + */ +#define KUNIT_EXPECT_LT(test, left, right) \ + KUNIT_EXPECT_BINARY(test, left, <, right) + +#define KUNIT_EXPECT_LT_MSG(test, left, right, fmt, ...) \ + KUNIT_EXPECT_BINARY_MSG(test, \ + left, \ + <, \ + right, \ + fmt, \ + ##__VA_ARGS__) + +/** + * KUNIT_EXPECT_LE() - Expects that @left is less than or equal to @right. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an expectation that the value that @left evaluates to is less than or + * equal to the value that @right evaluates to. Semantically this is equivalent + * to KUNIT_EXPECT_TRUE(@test, (@left) <= (@right)). See KUNIT_EXPECT_TRUE() for + * more information. + */ +#define KUNIT_EXPECT_LE(test, left, right) \ + KUNIT_EXPECT_BINARY(test, left, <=, right) + +#define KUNIT_EXPECT_LE_MSG(test, left, right, fmt, ...) \ + KUNIT_EXPECT_BINARY_MSG(test, \ + left, \ + <=, \ + right, \ + fmt, \ + ##__VA_ARGS__) + +/** + * KUNIT_EXPECT_GT() - An expectation that @left is greater than @right. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an expectation that the value that @left evaluates to is greater than + * the value that @right evaluates to. This is semantically equivalent to + * KUNIT_EXPECT_TRUE(@test, (@left) > (@right)). See KUNIT_EXPECT_TRUE() for + * more information. + */ +#define KUNIT_EXPECT_GT(test, left, right) \ + KUNIT_EXPECT_BINARY(test, left, >, right) + +#define KUNIT_EXPECT_GT_MSG(test, left, right, fmt, ...) \ + KUNIT_EXPECT_BINARY_MSG(test, \ + left, \ + >, \ + right, \ + fmt, \ + ##__VA_ARGS__) + +/** + * KUNIT_EXPECT_GE() - Expects that @left is greater than or equal to @right. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an expectation that the value that @left evaluates to is greater than + * the value that @right evaluates to. This is semantically equivalent to + * KUNIT_EXPECT_TRUE(@test, (@left) >= (@right)). See KUNIT_EXPECT_TRUE() for + * more information. + */ +#define KUNIT_EXPECT_GE(test, left, right) \ + KUNIT_EXPECT_BINARY(test, left, >=, right) + +#define KUNIT_EXPECT_GE_MSG(test, left, right, fmt, ...) \ + KUNIT_EXPECT_BINARY_MSG(test, \ + left, \ + >=, \ + right, \ + fmt, \ + ##__VA_ARGS__) + +/** + * KUNIT_EXPECT_STREQ() - Expects that strings @left and @right are equal. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a null terminated string. + * @right: an arbitrary expression that evaluates to a null terminated string. + * + * Sets an expectation that the values that @left and @right evaluate to are + * equal. This is semantically equivalent to + * KUNIT_EXPECT_TRUE(@test, !strcmp((@left), (@right))). See KUNIT_EXPECT_TRUE() + * for more information. + */ +#define KUNIT_EXPECT_STREQ(test, left, right) do { \ + struct kunit_stream *__stream = KUNIT_EXPECT_START(test); \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + \ + __stream->add(__stream, "Expected " #left " == " #right ", but\n"); \ + __stream->add(__stream, "\t\t%s == %s\n", #left, __left); \ + __stream->add(__stream, "\t\t%s == %s\n", #right, __right); \ + \ + KUNIT_EXPECT_END(test, !strcmp(left, right), __stream); \ +} while (0) + +#define KUNIT_EXPECT_STREQ_MSG(test, left, right, fmt, ...) do { \ + struct kunit_stream *__stream = KUNIT_EXPECT_START(test); \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + \ + __stream->add(__stream, "Expected " #left " == " #right ", but\n"); \ + __stream->add(__stream, "\t\t%s == %s\n", #left, __left); \ + __stream->add(__stream, "\t\t%s == %s\n", #right, __right); \ + __stream->add(__stream, fmt, ##__VA_ARGS__); \ + \ + KUNIT_EXPECT_END(test, !strcmp(left, right), __stream); \ +} while (0) + +/** + * KUNIT_EXPECT_NOT_ERR_OR_NULL() - Expects that @ptr is not null and not err. + * @test: The test context object. + * @ptr: an arbitrary pointer. + * + * Sets an expectation that the value that @ptr evaluates to is not null and not + * an errno stored in a pointer. This is semantically equivalent to + * KUNIT_EXPECT_TRUE(@test, !IS_ERR_OR_NULL(@ptr)). See KUNIT_EXPECT_TRUE() for + * more information. + */ +#define KUNIT_EXPECT_NOT_ERR_OR_NULL(test, ptr) do { \ + struct kunit_stream *__stream = KUNIT_EXPECT_START(test); \ + typeof(ptr) __ptr = (ptr); \ + \ + if (!__ptr) \ + __stream->add(__stream, \ + "Expected " #ptr " is not null, but is."); \ + if (IS_ERR(__ptr)) \ + __stream->add(__stream, \ + "Expected " #ptr " is not error, but is: %ld", \ + PTR_ERR(__ptr)); \ + \ + KUNIT_EXPECT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \ +} while (0) + +#define KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, ptr, fmt, ...) do { \ + struct kunit_stream *__stream = KUNIT_EXPECT_START(test); \ + typeof(ptr) __ptr = (ptr); \ + \ + if (!__ptr) { \ + __stream->add(__stream, \ + "Expected " #ptr " is not null, but is."); \ + __stream->add(__stream, fmt, ##__VA_ARGS__); \ + } \ + if (IS_ERR(__ptr)) { \ + __stream->add(__stream, \ + "Expected " #ptr " is not error, but is: %ld", \ + PTR_ERR(__ptr)); \ + \ + __stream->add(__stream, fmt, ##__VA_ARGS__); \ + } \ + KUNIT_EXPECT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \ +} while (0) + #endif /* _KUNIT_TEST_H */ diff --git a/kunit/test.c b/kunit/test.c index abeb939dc7fa2..0fe6571f23d41 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -269,3 +269,37 @@ void kunit_printk(const char *level, va_end(args); } + +void kunit_expect_binary_msg(struct kunit *test, + long long left, const char *left_name, + long long right, const char *right_name, + bool compare_result, + const char *compare_name, + const char *file, + const char *line, + const char *fmt, ...) +{ + struct kunit_stream *stream = kunit_expect_start(test, file, line); + struct va_format vaf; + va_list args; + + stream->add(stream, + "Expected %s %s %s, but\n", + left_name, compare_name, right_name); + stream->add(stream, "\t\t%s == %lld\n", left_name, left); + stream->add(stream, "\t\t%s == %lld", right_name, right); + + if (fmt) { + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + stream->add(stream, "\n%pV", &vaf); + + va_end(args); + } + + kunit_expect_end(test, compare_result, stream); +} + From patchwork Wed Nov 28 19:36:23 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703279 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 04E7B16B1 for ; Wed, 28 Nov 2018 19:39:28 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E9AFD2D722 for ; Wed, 28 Nov 2018 19:39:27 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DD7CB2D762; Wed, 28 Nov 2018 19:39:27 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 930142D722 for ; Wed, 28 Nov 2018 19:39:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729336AbeK2Gkn (ORCPT ); Thu, 29 Nov 2018 01:40:43 -0500 Received: from mail-pf1-f202.google.com ([209.85.210.202]:46145 "EHLO mail-pf1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729332AbeK2Gkm (ORCPT ); Thu, 29 Nov 2018 01:40:42 -0500 Received: by mail-pf1-f202.google.com with SMTP id h11so9230403pfj.13 for ; Wed, 28 Nov 2018 11:37:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=7o3/F9U9IKjkZ6IM9Sx8c0EMfVbluC0QKxi+aoA3j2Q=; b=cgDgZb3/4GE+EvO6c9q7nE9BKZSxXWI4PGb6FWRR91b4opTteDHUaFx8xi20/y7Mez JpV/5thabDhCBRUHZLG8pUs7FTtMY7V9eWyGJ7TlKv6bSu2qSYQ6ra7+6BdeJTOGyvoX s2bfpnjVtcKcxe12lu/NVowxn4Bu2K1qKFYTgllss2oVmmCQYTwicr29tnrSvAvJVmz7 R5jtAtTMjo8zBdo5vhchlp5rMhkZHvfRa6eZ8sfP8z95YloomWgnnyj2jApAZFQVFRe3 QjI8OiAPTG/swfACWqG2o6vT4kdLvD/tLiqlk8UsEnbfTON19tkBffPA19nL7KKQL9w8 anvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=7o3/F9U9IKjkZ6IM9Sx8c0EMfVbluC0QKxi+aoA3j2Q=; b=TAvttkzAnTm83rBPTllDR49VFYfdvWCxqYUznR6XFOxNs5uH7A+ly/N25PVN/Ykbcw mo+nqla8EgSu+1TsZHF7TCmghO4ifqeDitpgPEYSdXP2/2EmcVjsi5ikxWCEjPWvF0aW g9fSuX8fOqPLmqeiFrVjW52tFERFp0N7DFW5cDl0uB/8oyR9T9+cDYyFt67AU4Xqwled PGMTD9fCHHgvytPhEgcHtqW6AiSkOtrPvxrf1x1ILnd8Zw0yMv6SZVlp0DOiItgKFq1S v9XFgidEMAfV58uLi/RSYHafg9BMVA4avYQb86gCno7rGlzaPLj8LVyDpBJIFZAvtfFl Qmqg== X-Gm-Message-State: AA+aEWaG48xS3WyOylcrAdB3YA1xxAqK12v3uSafA6m7jDOOqzqbnFGN CTPcltsmoADrE79OixpFeFUUfjQ9tfO2TMX9k0ZKBg== X-Google-Smtp-Source: AFSGD/WZCpu2E2XeyMSRagVO1P9amlU5ZIT0i7TxLE9lAIM/bsuosh+GUdbMWKiKnSx20YGh5Rl4K/RvwPKxAslB8knGdQ== X-Received: by 2002:a17:902:1023:: with SMTP id b32mr2501651pla.121.1543433877985; Wed, 28 Nov 2018 11:37:57 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:23 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-7-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 06/19] arch: um: enable running kunit from User Mode Linux From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Make minimum number of changes outside of the KUnit directories for KUnit to build and run using UML. Signed-off-by: Brendan Higgins --- Kconfig | 2 ++ Makefile | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Kconfig b/Kconfig index 48a80beab6853..10428501edb78 100644 --- a/Kconfig +++ b/Kconfig @@ -30,3 +30,5 @@ source "crypto/Kconfig" source "lib/Kconfig" source "lib/Kconfig.debug" + +source "kunit/Kconfig" diff --git a/Makefile b/Makefile index 69fa5c0310d83..197f01cbddf03 100644 --- a/Makefile +++ b/Makefile @@ -966,7 +966,7 @@ endif ifeq ($(KBUILD_EXTMOD),) -core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ +core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ kunit/ vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ From patchwork Wed Nov 28 19:36:24 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703277 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0261D15A8 for ; Wed, 28 Nov 2018 19:39:25 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E77872D722 for ; Wed, 28 Nov 2018 19:39:24 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DBC262D762; Wed, 28 Nov 2018 19:39:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4AC692D722 for ; Wed, 28 Nov 2018 19:39:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729363AbeK2Gkq (ORCPT ); Thu, 29 Nov 2018 01:40:46 -0500 Received: from mail-qt1-f201.google.com ([209.85.160.201]:48138 "EHLO mail-qt1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729196AbeK2Gkq (ORCPT ); Thu, 29 Nov 2018 01:40:46 -0500 Received: by mail-qt1-f201.google.com with SMTP id q3so25113735qtq.15 for ; Wed, 28 Nov 2018 11:38:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=lbEPamgg0YfGPKws1Kfx2vND46wT9mhN58Rq8c+Ms/U=; b=BCf18nLdSIrDiKmi9rclPueM2hWvWF0xEBB67KpbpJHmjxwLHL5Rs0yCNuhHQzjNT9 lnpcxiWrfzUuiee3I09/fHhtr2vo1fKwigqznM/kSUS/Qt9md404n5JjylqEig0+LOfg 05VTccIsSEQ7tk07G5bRLXIXjhQLHV4eFE0oumeab3RE041QXnsZn0isdeBIBUeygaxV 9BSPHV0p8dUFm5aSD4nb7qYI9Ke+uGA1CDPyvDQXd7fCeLxMzajoYbLr6eSKkv1Y7Sk7 HqzVczkGQAbt9MpPAvXtJuGgAL7QGu34BrUg7KIrk3rFRUN8Sk1SFTzZ+Lj98ZyuXwGn D6DA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=lbEPamgg0YfGPKws1Kfx2vND46wT9mhN58Rq8c+Ms/U=; b=FloKxe57tVddQENB7x7thTFyvUUqf63KyjVJmZzGwsWz0w9AXWQ2XYeeZOn/2vKD0A 9g14uuBH20GiylnKqxlTFORBKu7PUlxJSc01El2CsTeW16O8NcaAkC6rC/xBhzEcfrkh 516k5pWOekiSFcw3Ct7+IizKjEDna/1FugW8mNpZ3kjKEJSSNpmOs/91/eB9Iuwoysna QB5a/WK/QlgnO1VA7vlhHPtRDIigMWYNs/NTc0IKjVzs8Zk8KUFGKJY6TQt7KKr6+CX7 ymX3QZL0nPqbkFlymLsTY/Bwnu3Ql509QK7Ua9/6jdN/jrSfVlnpP2D0I6eHL1GG39OK M6Ww== X-Gm-Message-State: AA+aEWYAHZQtx/4sFWwJvsiXXas9dfh0Nsnnh23DA5HmhXXWbFwOISOn nQg0UVopPfElTsslIkH+0v7n2Xgwxrb+24jLjsUeYA== X-Google-Smtp-Source: AFSGD/XEzoQEK96jluYX+xdFPzIoAL8wPQBseWTrLulK6hWWCdG3HJoNzqPaSgXV9i//i3h8CPR3mFgUScS6iod94ckecw== X-Received: by 2002:a37:67d7:: with SMTP id b206mr4522215qkc.17.1543433880832; Wed, 28 Nov 2018 11:38:00 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:24 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-8-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 07/19] kunit: test: add initial tests From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add a test for string stream along with a simpler example. Signed-off-by: Brendan Higgins --- kunit/Kconfig | 12 ++++++ kunit/Makefile | 4 ++ kunit/example-test.c | 88 ++++++++++++++++++++++++++++++++++++++ kunit/string-stream-test.c | 61 ++++++++++++++++++++++++++ 4 files changed, 165 insertions(+) create mode 100644 kunit/example-test.c create mode 100644 kunit/string-stream-test.c diff --git a/kunit/Kconfig b/kunit/Kconfig index 49b44c4f6630a..c3dc7bca83f9d 100644 --- a/kunit/Kconfig +++ b/kunit/Kconfig @@ -14,4 +14,16 @@ config KUNIT special hardware. For more information, please see Documentation/kunit/ +config KUNIT_TEST + bool "KUnit test for KUnit" + depends on KUNIT + help + Enables KUnit test to test KUnit. + +config KUNIT_EXAMPLE_TEST + bool "Example test for KUnit" + depends on KUNIT + help + Enables example KUnit test to demo features of KUnit. + endmenu diff --git a/kunit/Makefile b/kunit/Makefile index 6ddc622ee6b1c..60a9ea6cb4697 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1,3 +1,7 @@ obj-$(CONFIG_KUNIT) += test.o \ string-stream.o \ kunit-stream.o + +obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o + +obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += example-test.o diff --git a/kunit/example-test.c b/kunit/example-test.c new file mode 100644 index 0000000000000..4197cc217d96f --- /dev/null +++ b/kunit/example-test.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Example KUnit test to show how to use KUnit. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include + +/* + * This is the most fundamental element of KUnit, the test case. A test case + * makes a set EXPECTATIONs and ASSERTIONs about the behavior of some code; if + * any expectations or assertions are not met, the test fails; otherwise, the + * test passes. + * + * In KUnit, a test case is just a function with the signature + * `void (*)(struct kunit *)`. `struct kunit` is a context object that stores + * information about the current test. + */ +static void example_simple_test(struct kunit *test) +{ + /* + * This is an EXPECTATION; it is how KUnit tests things. When you want + * to test a piece of code, you set some expectations about what the + * code should do. KUnit then runs the test and verifies that the code's + * behavior matched what was expected. + */ + KUNIT_EXPECT_EQ(test, 1 + 1, 2); +} + +/* + * This is run once before each test case, see the comment on + * example_test_module for more information. + */ +static int example_test_init(struct kunit *test) +{ + kunit_info(test, "initializing"); + + return 0; +} + +/* + * Here we make a list of all the test cases we want to add to the test module + * below. + */ +static struct kunit_case example_test_cases[] = { + /* + * This is a helper to create a test case object from a test case + * function; its exact function is not important to understand how to + * use KUnit, just know that this is how you associate test cases with a + * test module. + */ + KUNIT_CASE(example_simple_test), + {}, +}; + +/* + * This defines a suite or grouping of tests. + * + * Test cases are defined as belonging to the suite by adding them to + * `kunit_cases`. + * + * Often it is desirable to run some function which will set up things which + * will be used by every test; this is accomplished with an `init` function + * which runs before each test case is invoked. Similarly, an `exit` function + * may be specified which runs after every test case and can be used to for + * cleanup. For clarity, running tests in a test module would behave as follows: + * + * module.init(test); + * module.test_case[0](test); + * module.exit(test); + * module.init(test); + * module.test_case[1](test); + * module.exit(test); + * ...; + */ +static struct kunit_module example_test_module = { + .name = "example", + .init = example_test_init, + .test_cases = example_test_cases, +}; + +/* + * This registers the above test module telling KUnit that this is a suite of + * tests that need to be run. + */ +module_test(example_test_module); diff --git a/kunit/string-stream-test.c b/kunit/string-stream-test.c new file mode 100644 index 0000000000000..ec2675593c364 --- /dev/null +++ b/kunit/string-stream-test.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit test for struct string_stream. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include + +static void string_stream_test_get_string(struct kunit *test) +{ + struct string_stream *stream = new_string_stream(); + char *output; + + stream->add(stream, "Foo"); + stream->add(stream, " %s", "bar"); + + output = stream->get_string(stream); + KUNIT_EXPECT_STREQ(test, output, "Foo bar"); + kfree(output); + destroy_string_stream(stream); +} + +static void string_stream_test_add_and_clear(struct kunit *test) +{ + struct string_stream *stream = new_string_stream(); + char *output; + int i; + + for (i = 0; i < 10; i++) + stream->add(stream, "A"); + + output = stream->get_string(stream); + KUNIT_EXPECT_STREQ(test, output, "AAAAAAAAAA"); + KUNIT_EXPECT_EQ(test, stream->length, 10); + KUNIT_EXPECT_FALSE(test, stream->is_empty(stream)); + kfree(output); + + stream->clear(stream); + + output = stream->get_string(stream); + KUNIT_EXPECT_STREQ(test, output, ""); + KUNIT_EXPECT_TRUE(test, stream->is_empty(stream)); + destroy_string_stream(stream); +} + +static struct kunit_case string_stream_test_cases[] = { + KUNIT_CASE(string_stream_test_get_string), + KUNIT_CASE(string_stream_test_add_and_clear), + {} +}; + +static struct kunit_module string_stream_test_module = { + .name = "string-stream-test", + .test_cases = string_stream_test_cases +}; +module_test(string_stream_test_module); + From patchwork Wed Nov 28 19:36:25 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703275 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D098C16B1 for ; Wed, 28 Nov 2018 19:39:22 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C35382D748 for ; Wed, 28 Nov 2018 19:39:22 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B72602D76A; Wed, 28 Nov 2018 19:39:22 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 55B002D748 for ; Wed, 28 Nov 2018 19:39:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729377AbeK2Gks (ORCPT ); Thu, 29 Nov 2018 01:40:48 -0500 Received: from mail-it1-f201.google.com ([209.85.166.201]:55333 "EHLO mail-it1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729357AbeK2Gks (ORCPT ); Thu, 29 Nov 2018 01:40:48 -0500 Received: by mail-it1-f201.google.com with SMTP id j3so4250905itf.5 for ; Wed, 28 Nov 2018 11:38:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=9D74vhDKZ9zweNX8bjF2N0Y9OEVlGStLvbWGF8he3rU=; b=DUCrY0+bV3RwL0ek8kOGRSVO5v/D47qxtVHPVDyn/P0PBKfO7J+dAaLFJrHg6KA1H4 scBxeWPlXg29nrqLVQjBexctKZKf4sJRPuMm2gWIyWNVYbIwQ8repGuBGke3MQgytZ0U 0nWC9xlfOhMq9UV4wdHPOgiwRjjDbXnX672v/DOPwZrEL9SOKJro1mHudBWDcjR9XkAh xupw6E5Ty3A8oacOVm8zPZtMgBf2sTDwvSq0NPCwnsMtj+6r/YHj3x4778++02hbv/HY OApKXZoGIN/lngkLyyBTy92qH6Q1D1NtX26NvOt9gLsQtKYf2qXcWdbomlD1Lp1DPHVR leyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=9D74vhDKZ9zweNX8bjF2N0Y9OEVlGStLvbWGF8he3rU=; b=KfZzskdBaj/nzS1LyVfFcZAtt492Yecu938VcMNUULblooTJMJnxbIXc/fye3kAhMS H0fIgQFMouuCQpbQXaNV78uC+PIr1YO4ryA7N4ehH3uuzvuDF6yOjHl4dmSjj9+z37pA F5ds1fi0fcPp02vxvtU2MU6fxP/k2gTlm0n32wc9bu2li6ksCQbO9qpMFVL6Amu/i2iY B5t0BOf2RRvmBIq9dENl0DRKFAKUCsHGWd69LfJ0TqVXWWhF1gfk5g2mV4cn1gLfaJsL 6Do3rzF9684IpqmXIu4WfsFJKKtXZg0CqJV4ieM2nUEUu5geMbtmXY5rRvU/ZLMeYKgZ mg2w== X-Gm-Message-State: AA+aEWYSMsVmSNoBN71Vdnou9/IBi1/KcOoWBs/J+8Ac7EB0ceopdaM4 VwCoLxWLfENYwi2qt3TG8lTiqtnp1jKMbjwmxPz7rw== X-Google-Smtp-Source: AFSGD/UMhQyCRg6KQGlfagnXK2iwxLqCIGZ8HodTaJ7DpYmtIhCG98itNlSoTEwnNmng08QDMUsUiZnMCHqxdM/0JGaFXw== X-Received: by 2002:a24:74b:: with SMTP id f72-v6mr3353404itf.35.1543433883605; Wed, 28 Nov 2018 11:38:03 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:25 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-9-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 08/19] arch: um: add shim to trap to allow installing a fault catcher for tests From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add context to current thread that allows a test to specify that it wants to skip the normal checks to run an installed fault catcher. Signed-off-by: Brendan Higgins --- arch/um/include/asm/processor-generic.h | 4 +++- arch/um/kernel/trap.c | 15 +++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/arch/um/include/asm/processor-generic.h b/arch/um/include/asm/processor-generic.h index b58b746d3f2ca..d566cd416ff02 100644 --- a/arch/um/include/asm/processor-generic.h +++ b/arch/um/include/asm/processor-generic.h @@ -27,6 +27,7 @@ struct thread_struct { struct task_struct *prev_sched; struct arch_thread arch; jmp_buf switch_buf; + bool is_running_test; struct { int op; union { @@ -51,7 +52,8 @@ struct thread_struct { .fault_addr = NULL, \ .prev_sched = NULL, \ .arch = INIT_ARCH_THREAD, \ - .request = { 0 } \ + .request = { 0 }, \ + .is_running_test = false, \ } static inline void release_thread(struct task_struct *task) diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index cced829460427..bf90e678b3d71 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -201,6 +201,12 @@ void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs); } +static void segv_run_catcher(jmp_buf *catcher, void *fault_addr) +{ + current->thread.fault_addr = fault_addr; + UML_LONGJMP(catcher, 1); +} + /* * We give a *copy* of the faultinfo in the regs to segv. * This must be done, since nesting SEGVs could overwrite @@ -219,7 +225,10 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, if (!is_user && regs) current->thread.segv_regs = container_of(regs, struct pt_regs, regs); - if (!is_user && (address >= start_vm) && (address < end_vm)) { + catcher = current->thread.fault_catcher; + if (catcher && current->thread.is_running_test) + segv_run_catcher(catcher, (void *) address); + else if (!is_user && (address >= start_vm) && (address < end_vm)) { flush_tlb_kernel_vm(); goto out; } @@ -246,12 +255,10 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, address = 0; } - catcher = current->thread.fault_catcher; if (!err) goto out; else if (catcher != NULL) { - current->thread.fault_addr = (void *) address; - UML_LONGJMP(catcher, 1); + segv_run_catcher(catcher, (void *) address); } else if (current->thread.fault_addr != NULL) panic("fault_addr set but no fault catcher"); From patchwork Wed Nov 28 19:36:26 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703233 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5815216B1 for ; Wed, 28 Nov 2018 19:38:11 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 48DA22B214 for ; Wed, 28 Nov 2018 19:38:11 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3B80A2C35F; Wed, 28 Nov 2018 19:38:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BFC162B0FF for ; Wed, 28 Nov 2018 19:38:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729411AbeK2Gkw (ORCPT ); Thu, 29 Nov 2018 01:40:52 -0500 Received: from mail-pl1-f202.google.com ([209.85.214.202]:42047 "EHLO mail-pl1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729391AbeK2Gkv (ORCPT ); Thu, 29 Nov 2018 01:40:51 -0500 Received: by mail-pl1-f202.google.com with SMTP id p3so6881378plk.9 for ; Wed, 28 Nov 2018 11:38:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=fUyubuRsyBNENe/drWcJROmGQyepJJhiD7hQ0KZNNSQ=; b=fAHpzHZrIy++ug74xG4MdRohZvt8JsOK2yzfUTyWB/c/9F2c0prEdYXXJY2P43nRuC VVENxFdx06ADtLGU32gaI9moXNE2OHge1miKPT4HBH1DeQhUwNAY3S8yutD0k40JQyDF LZQSnSFP7VNcnA76mqJggPG+cr+bc8vq1OwQ3OQLGflTkEd9XooGLMXnobaOkzggZr39 bNfziVApjkapxKRccjdkr83ZThXfvsROf2eeLD47CuIQHh43oKiFRh1dLCXaiBXY958t CUnGzMxe0BzAgEeT9J5MaTx0cc0bK+3UjcrDr6DZtaXwOMlRAngseFO1alanki2cTNqr yDrw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=fUyubuRsyBNENe/drWcJROmGQyepJJhiD7hQ0KZNNSQ=; b=DucCumfybuUXw9evkvTzDNgMH/YTkBYV0D4bO0Q8ncyjgXOy463IYBlJ69WXupDseO AdY3D3kC60OY541MoW72z16uOCCWVE8NlJaOkY1TilESq64TkU9q3LtPs8DdbNHaSYQR 6y/mqUExHC8G76nHPWVdtin2slsPE9O726PjuQ9YIExHLS8RN5Dcja89hnJ0NHQyAWOz 1IYipOUFef3Yd+ReuQOHrv/cqXouMRKfLesSx9EyXrhgo4051cnttLoKZ6Uj9L811KDA 2H+eYECx5yTbDNugl+yn2RfU6PoY2sB0KctpTsXnzyVaW74rfVKFhLrfloUHP7VKfi6I e/rg== X-Gm-Message-State: AA+aEWZQSvLb33rtfnc3xG5mH2dWCNEE98VOkFCydJfT89XATsQo4x2m yYQ77+cyecfk7jEcheP5+dGKpx/HDS1ysNHJCXp7hA== X-Google-Smtp-Source: AFSGD/X6POKFKlsoMbWXd59utTgMqb1G5FG8ACX7ykhC6t0/aJDEiHoM8KTbpXuv7Z49NF73azDw/y8P6BbdC4eTYCjF/Q== X-Received: by 2002:a63:4c22:: with SMTP id z34mr3018286pga.111.1543433886133; Wed, 28 Nov 2018 11:38:06 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:26 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-10-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 09/19] kunit: test: add the concept of assertions From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add support for assertions which are like expectations except the test terminates if the assertion is not satisfied. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 388 ++++++++++++++++++++++++++++++++++++- kunit/Makefile | 3 +- kunit/string-stream-test.c | 12 +- kunit/test-test.c | 37 ++++ kunit/test.c | 164 +++++++++++++++- 5 files changed, 586 insertions(+), 18 deletions(-) create mode 100644 kunit/test-test.c diff --git a/include/kunit/test.h b/include/kunit/test.h index 098a9dceef9ea..7be11dba0b14e 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -84,9 +84,10 @@ struct kunit; * @name: the name of the test case. * * A test case is a function with the signature, ``void (*)(struct kunit *)`` - * that makes expectations (see KUNIT_EXPECT_TRUE()) about code under test. Each - * test case is associated with a &struct kunit_module and will be run after the - * module's init function and followed by the module's exit function. + * that makes expectations and assertions (see KUNIT_EXPECT_TRUE() and + * KUNIT_ASSERT_TRUE()) about code under test. Each test case is associated with + * a &struct kunit_module and will be run after the module's init function and + * followed by the module's exit function. * * A test case should be static and should only be created with the KUNIT_CASE() * macro; additionally, every array of test cases should be terminated with an @@ -168,11 +169,14 @@ struct kunit { const char *name; /* Read only after initialization! */ spinlock_t lock; /* Gaurds all mutable test state. */ bool success; /* Protected by lock. */ + bool death_test; /* Protected by lock. */ struct list_head resources; /* Protected by lock. */ + void (*set_death_test)(struct kunit *test, bool death_test); void (*vprintk)(const struct kunit *test, const char *level, struct va_format *vaf); void (*fail)(struct kunit *test, struct kunit_stream *stream); + void (*abort)(struct kunit *test); }; int kunit_init_test(struct kunit *test, const char *name); @@ -652,4 +656,382 @@ static inline void kunit_expect_binary(struct kunit *test, KUNIT_EXPECT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \ } while (0) +static inline struct kunit_stream *kunit_assert_start(struct kunit *test, + const char *file, + const char *line) +{ + struct kunit_stream *stream = kunit_new_stream(test); + + stream->add(stream, "ASSERTION FAILED at %s:%s\n\t", file, line); + + return stream; +} + +static inline void kunit_assert_end(struct kunit *test, + bool success, + struct kunit_stream *stream) +{ + if (!success) { + test->fail(test, stream); + test->abort(test); + } else { + stream->clear(stream); + } +} + +#define KUNIT_ASSERT_START(test) \ + kunit_assert_start(test, __FILE__, __stringify(__LINE__)) + +#define KUNIT_ASSERT_END(test, success, stream) \ + kunit_assert_end(test, success, stream) + +#define KUNIT_ASSERT(test, success, message) do { \ + struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \ + \ + __stream->add(__stream, message); \ + KUNIT_ASSERT_END(test, success, __stream); \ +} while (0) + +#define KUNIT_ASSERT_MSG(test, success, message, fmt, ...) do { \ + struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \ + \ + __stream->add(__stream, message); \ + __stream->add(__stream, fmt, ##__VA_ARGS__); \ + KUNIT_ASSERT_END(test, success, __stream); \ +} while (0) + +#define KUNIT_ASSERT_FAILURE(test, fmt, ...) \ + KUNIT_ASSERT_MSG(test, false, "", fmt, ##__VA_ARGS__) + +/** + * KUNIT_ASSERT_TRUE() - Sets an assertion that @condition is true. + * @test: The test context object. + * @condition: an arbitrary boolean expression. The test fails and aborts when + * this does not evaluate to true. + * + * This and assertions of the form `KUNIT_ASSERT_*` will cause the test case to + * fail *and immediately abort* when the specified condition is not met. Unlike + * an expectation failure, it will prevent the test case from continuing to run; + * this is otherwise known as an *assertion failure*. + */ +#define KUNIT_ASSERT_TRUE(test, condition) \ + KUNIT_ASSERT(test, (condition), \ + "Asserted " #condition " is true, but is false.") + +#define KUNIT_ASSERT_TRUE_MSG(test, condition, fmt, ...) \ + KUNIT_ASSERT_MSG(test, (condition), \ + "Asserted " #condition " is true, but is false.\n",\ + fmt, ##__VA_ARGS__) + +/** + * KUNIT_ASSERT_FALSE() - Sets an assertion that @condition is false. + * @test: The test context object. + * @condition: an arbitrary boolean expression. + * + * Sets an assertion that the value that @condition evaluates to is false. This + * is the same as KUNIT_EXPECT_FALSE(), except it causes an assertion failure + * (see KUNIT_ASSERT_TRUE()) when the assertion is not met. + */ +#define KUNIT_ASSERT_FALSE(test, condition) \ + KUNIT_ASSERT(test, !(condition), \ + "Asserted " #condition " is false, but is true.") + +#define KUNIT_ASSERT_FALSE_MSG(test, condition, fmt, ...) \ + KUNIT_ASSERT_MSG(test, !(condition), \ + "Asserted " #condition " is false, but is true.\n",\ + fmt, ##__VA_ARGS__) + +void kunit_assert_binary_msg(struct kunit *test, + long long left, const char *left_name, + long long right, const char *right_name, + bool compare_result, + const char *compare_name, + const char *file, + const char *line, + const char *fmt, ...); + +static inline void kunit_assert_binary(struct kunit *test, + long long left, const char *left_name, + long long right, const char *right_name, + bool compare_result, + const char *compare_name, + const char *file, + const char *line) +{ + kunit_assert_binary_msg(test, + left, left_name, + right, right_name, + compare_result, + compare_name, + file, + line, + NULL); +} + +/* + * A factory macro for defining the expectations for the basic comparisons + * defined for the built in types. + * + * Unfortunately, there is no common type that all types can be promoted to for + * which all the binary operators behave the same way as for the actual types + * (for example, there is no type that long long and unsigned long long can + * both be cast to where the comparison result is preserved for all values). So + * the best we can do is do the comparison in the original types and then coerce + * everything to long long for printing; this way, the comparison behaves + * correctly and the printed out value usually makes sense without + * interpretation, but can always be interpretted to figure out the actual + * value. + */ +#define KUNIT_ASSERT_BINARY(test, left, condition, right) do { \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + kunit_assert_binary(test, \ + (long long) __left, #left, \ + (long long) __right, #right, \ + __left condition __right, #condition, \ + __FILE__, __stringify(__LINE__)); \ +} while (0) + +#define KUNIT_ASSERT_BINARY_MSG(test, left, condition, right, fmt, ...) do { \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + kunit_assert_binary_msg(test, \ + (long long) __left, #left, \ + (long long) __right, #right, \ + __left condition __right, #condition, \ + __FILE__, __stringify(__LINE__), \ + fmt, ##__VA_ARGS__); \ +} while (0) + +/** + * KUNIT_ASSERT_EQ() - Sets an assertion that @left and @right are equal. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an assertion that the values that @left and @right evaluate to are + * equal. This is the same as KUNIT_EXPECT_EQ(), except it causes an assertion + * failure (see KUNIT_ASSERT_TRUE()) when the assertion is not met. + */ +#define KUNIT_ASSERT_EQ(test, left, right) \ + KUNIT_ASSERT_BINARY(test, left, ==, right) + +#define KUNIT_ASSERT_EQ_MSG(test, left, right, fmt, ...) \ + KUNIT_ASSERT_BINARY_MSG(test, \ + left, \ + ==, \ + right, \ + fmt, \ + ##__VA_ARGS__) + +/** + * KUNIT_ASSERT_NE() - An assertion that @left and @right are not equal. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an assertion that the values that @left and @right evaluate to are not + * equal. This is the same as KUNIT_EXPECT_NE(), except it causes an assertion + * failure (see KUNIT_ASSERT_TRUE()) when the assertion is not met. + */ +#define KUNIT_ASSERT_NE(test, left, right) \ + KUNIT_ASSERT_BINARY(test, left, !=, right) + +#define KUNIT_ASSERT_NE_MSG(test, left, right, fmt, ...) \ + KUNIT_ASSERT_BINARY_MSG(test, \ + left, \ + !=, \ + right, \ + fmt, \ + ##__VA_ARGS__) + +/** + * KUNIT_ASSERT_LT() - An assertion that @left is less than @right. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an assertion that the value that @left evaluates to is less than the + * value that @right evaluates to. This is the same as KUNIT_EXPECT_LT(), except + * it causes an assertion failure (see KUNIT_ASSERT_TRUE()) when the assertion + * is not met. + */ +#define KUNIT_ASSERT_LT(test, left, right) \ + KUNIT_ASSERT_BINARY(test, left, <, right) + +#define KUNIT_ASSERT_LT_MSG(test, left, right, fmt, ...) \ + KUNIT_ASSERT_BINARY_MSG(test, \ + left, \ + <, \ + right, \ + fmt, \ + ##__VA_ARGS__) +/** + * KUNIT_ASSERT_LE() - An assertion that @left is less than or equal to @right. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an assertion that the value that @left evaluates to is less than or + * equal to the value that @right evaluates to. This is the same as + * KUNIT_EXPECT_LE(), except it causes an assertion failure (see + * KUNIT_ASSERT_TRUE()) when the assertion is not met. + */ +#define KUNIT_ASSERT_LE(test, left, right) \ + KUNIT_ASSERT_BINARY(test, left, <=, right) + +#define KUNIT_ASSERT_LE_MSG(test, left, right, fmt, ...) \ + KUNIT_ASSERT_BINARY_MSG(test, \ + left, \ + <=, \ + right, \ + fmt, \ + ##__VA_ARGS__) +/** + * KUNIT_ASSERT_GT() - An assertion that @left is greater than @right. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an assertion that the value that @left evaluates to is greater than the + * value that @right evaluates to. This is the same as KUNIT_EXPECT_GT(), except + * it causes an assertion failure (see KUNIT_ASSERT_TRUE()) when the assertion + * is not met. + */ +#define KUNIT_ASSERT_GT(test, left, right) \ + KUNIT_ASSERT_BINARY(test, left, >, right) + +#define KUNIT_ASSERT_GT_MSG(test, left, right, fmt, ...) \ + KUNIT_ASSERT_BINARY_MSG(test, \ + left, \ + >, \ + right, \ + fmt, \ + ##__VA_ARGS__) + +/** + * KUNIT_ASSERT_GE() - Assertion that @left is greater than or equal to @right. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a primitive C type. + * @right: an arbitrary expression that evaluates to a primitive C type. + * + * Sets an assertion that the value that @left evaluates to is greater than the + * value that @right evaluates to. This is the same as KUNIT_EXPECT_GE(), except + * it causes an assertion failure (see KUNIT_ASSERT_TRUE()) when the assertion + * is not met. + */ +#define KUNIT_ASSERT_GE(test, left, right) \ + KUNIT_ASSERT_BINARY(test, left, >=, right) + +#define KUNIT_ASSERT_GE_MSG(test, left, right, fmt, ...) \ + KUNIT_ASSERT_BINARY_MSG(test, \ + left, \ + >=, \ + right, \ + fmt, \ + ##__VA_ARGS__) + +/** + * KUNIT_ASSERT_STREQ() - An assertion that strings @left and @right are equal. + * @test: The test context object. + * @left: an arbitrary expression that evaluates to a null terminated string. + * @right: an arbitrary expression that evaluates to a null terminated string. + * + * Sets an assertion that the values that @left and @right evaluate to are + * equal. This is the same as KUNIT_EXPECT_STREQ(), except it causes an + * assertion failure (see KUNIT_ASSERT_TRUE()) when the assertion is not met. + */ +#define KUNIT_ASSERT_STREQ(test, left, right) do { \ + struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + \ + __stream->add(__stream, "Asserted " #left " == " #right ", but\n"); \ + __stream->add(__stream, "\t\t%s == %s\n", #left, __left); \ + __stream->add(__stream, "\t\t%s == %s\n", #right, __right); \ + \ + KUNIT_ASSERT_END(test, !strcmp(left, right), __stream); \ +} while (0) + +#define KUNIT_ASSERT_STREQ_MSG(test, left, right, fmt, ...) do { \ + struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + \ + __stream->add(__stream, "Expected " #left " == " #right ", but\n"); \ + __stream->add(__stream, "\t\t%s == %s\n", #left, __left); \ + __stream->add(__stream, "\t\t%s == %s\n", #right, __right); \ + __stream->add(__stream, fmt, ##__VA_ARGS__); \ + \ + KUNIT_ASSERT_END(test, !strcmp(left, right), __stream); \ +} while (0) + +/** + * KUNIT_ASSERT_NOT_ERR_OR_NULL() - Assertion that @ptr is not null and not err. + * @test: The test context object. + * @ptr: an arbitrary pointer. + * + * Sets an assertion that the value that @ptr evaluates to is not null and not + * an errno stored in a pointer. This is the same as + * KUNIT_EXPECT_NOT_ERR_OR_NULL(), except it causes an assertion failure (see + * KUNIT_ASSERT_TRUE()) when the assertion is not met. + */ +#define KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr) do { \ + struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \ + typeof(ptr) __ptr = (ptr); \ + \ + if (!__ptr) \ + __stream->add(__stream, \ + "Asserted " #ptr " is not null, but is."); \ + if (IS_ERR(__ptr)) \ + __stream->add(__stream, \ + "Asserted " #ptr " is not error, but is: %ld", \ + PTR_ERR(__ptr)); \ + \ + KUNIT_ASSERT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \ +} while (0) + +#define KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, ptr, fmt, ...) do { \ + struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \ + typeof(ptr) __ptr = (ptr); \ + \ + if (!__ptr) { \ + __stream->add(__stream, \ + "Asserted " #ptr " is not null, but is."); \ + __stream->add(__stream, fmt, ##__VA_ARGS__); \ + } \ + if (IS_ERR(__ptr)) { \ + __stream->add(__stream, \ + "Asserted " #ptr " is not error, but is: %ld", \ + PTR_ERR(__ptr)); \ + \ + __stream->add(__stream, fmt, ##__VA_ARGS__); \ + } \ + KUNIT_ASSERT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \ +} while (0) + +/** + * KUNIT_ASSERT_SIGSEGV() - An assertion that @expr will cause a segfault. + * @test: The test context object. + * @expr: an arbitrary block of code. + * + * Sets an assertion that @expr, when evaluated, will cause a segfault. + * Currently this assertion is only really useful for testing the KUnit + * framework, as a segmentation fault in normal kernel code is always incorrect. + * However, the plan is to replace this assertion with an arbitrary death + * assertion similar to + * https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#death-tests + * which will probably be massaged to make sense in the context of the kernel + * (maybe assert that a panic occurred, or that BUG() was called). + * + * NOTE: no code after this assertion will ever be executed. + */ +#define KUNIT_ASSERT_SIGSEGV(test, expr) do { \ + test->set_death_test(test, true); \ + expr; \ + test->set_death_test(test, false); \ + KUNIT_ASSERT_FAILURE(test, \ + "Asserted that " #expr " would cause death, but did not.");\ +} while (0) + #endif /* _KUNIT_TEST_H */ diff --git a/kunit/Makefile b/kunit/Makefile index 60a9ea6cb4697..e4c300f67479a 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_KUNIT) += test.o \ string-stream.o \ kunit-stream.o -obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o +obj-$(CONFIG_KUNIT_TEST) += test-test.o \ + string-stream-test.o obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += example-test.o diff --git a/kunit/string-stream-test.c b/kunit/string-stream-test.c index ec2675593c364..c5346a6c932ce 100644 --- a/kunit/string-stream-test.c +++ b/kunit/string-stream-test.c @@ -19,7 +19,7 @@ static void string_stream_test_get_string(struct kunit *test) stream->add(stream, " %s", "bar"); output = stream->get_string(stream); - KUNIT_EXPECT_STREQ(test, output, "Foo bar"); + KUNIT_ASSERT_STREQ(test, output, "Foo bar"); kfree(output); destroy_string_stream(stream); } @@ -34,16 +34,16 @@ static void string_stream_test_add_and_clear(struct kunit *test) stream->add(stream, "A"); output = stream->get_string(stream); - KUNIT_EXPECT_STREQ(test, output, "AAAAAAAAAA"); - KUNIT_EXPECT_EQ(test, stream->length, 10); - KUNIT_EXPECT_FALSE(test, stream->is_empty(stream)); + KUNIT_ASSERT_STREQ(test, output, "AAAAAAAAAA"); + KUNIT_ASSERT_EQ(test, stream->length, 10); + KUNIT_ASSERT_FALSE(test, stream->is_empty(stream)); kfree(output); stream->clear(stream); output = stream->get_string(stream); - KUNIT_EXPECT_STREQ(test, output, ""); - KUNIT_EXPECT_TRUE(test, stream->is_empty(stream)); + KUNIT_ASSERT_STREQ(test, output, ""); + KUNIT_ASSERT_TRUE(test, stream->is_empty(stream)); destroy_string_stream(stream); } diff --git a/kunit/test-test.c b/kunit/test-test.c new file mode 100644 index 0000000000000..88b3bcf9c4e00 --- /dev/null +++ b/kunit/test-test.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit test for core test infrastructure. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ +#include + +static void test_test_catches_segfault(struct kunit *test) +{ + void (*invalid_func)(void) = (void (*)(void)) SIZE_MAX; + + KUNIT_ASSERT_SIGSEGV(test, invalid_func()); +} + +static int test_test_init(struct kunit *test) +{ + return 0; +} + +static void test_test_exit(struct kunit *test) +{ +} + +static struct kunit_case test_test_cases[] = { + KUNIT_CASE(test_test_catches_segfault), + {}, +}; + +static struct kunit_module test_test_module = { + .name = "test-test", + .init = test_test_init, + .exit = test_test_exit, + .test_cases = test_test_cases, +}; +module_test(test_test_module); diff --git a/kunit/test.c b/kunit/test.c index 0fe6571f23d41..db3b0ea0f5888 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -32,6 +32,27 @@ static void kunit_set_success(struct kunit *test, bool success) spin_unlock_irqrestore(&test->lock, flags); } +static bool kunit_get_death_test(struct kunit *test) +{ + unsigned long flags; + bool death_test; + + spin_lock_irqsave(&test->lock, flags); + death_test = test->death_test; + spin_unlock_irqrestore(&test->lock, flags); + + return death_test; +} + +static void kunit_set_death_test(struct kunit *test, bool death_test) +{ + unsigned long flags; + + spin_lock_irqsave(&test->lock, flags); + test->death_test = death_test; + spin_unlock_irqrestore(&test->lock, flags); +} + static int kunit_vprintk_emit(const struct kunit *test, int level, const char *fmt, @@ -70,13 +91,34 @@ static void kunit_fail(struct kunit *test, struct kunit_stream *stream) stream->commit(stream); } +static void __noreturn kunit_abort(struct kunit *test) +{ + kunit_set_death_test(test, true); + if (current->thread.fault_catcher && current->thread.is_running_test) + UML_LONGJMP(current->thread.fault_catcher, 1); + + /* + * Attempted to abort from a not properly initialized test context. + */ + kunit_err(test, + "Attempted to abort from a not properly initialized test context!"); + if (!current->thread.fault_catcher) + kunit_err(test, "No fault_catcher present!"); + if (!current->thread.is_running_test) + kunit_err(test, "is_running_test not set!"); + show_stack(NULL, NULL); + BUG(); +} + int kunit_init_test(struct kunit *test, const char *name) { spin_lock_init(&test->lock); INIT_LIST_HEAD(&test->resources); test->name = name; + test->set_death_test = kunit_set_death_test; test->vprintk = kunit_vprintk; test->fail = kunit_fail; + test->abort = kunit_abort; return 0; } @@ -122,16 +164,89 @@ static void kunit_run_case_cleanup(struct kunit *test, } /* - * Performs all logic to run a test case. + * Handles an unexpected crash in a test case. */ -static bool kunit_run_case(struct kunit *test, - struct kunit_module *module, - struct kunit_case *test_case) +static void kunit_handle_test_crash(struct kunit *test, + struct kunit_module *module, + struct kunit_case *test_case) { - kunit_set_success(test, true); + kunit_err(test, "%s crashed", test_case->name); + /* + * TODO(brendanhiggins@google.com): This prints the stack trace up + * through this frame, not up to the frame that caused the crash. + */ + show_stack(NULL, NULL); - kunit_run_case_internal(test, module, test_case); - kunit_run_case_cleanup(test, module, test_case); + kunit_case_internal_cleanup(test); +} + +/* + * Performs all logic to run a test case. It also catches most errors that + * occurs in a test case and reports them as failures. + * + * XXX: THIS DOES NOT FOLLOW NORMAL CONTROL FLOW. READ CAREFULLY!!! + */ +static bool kunit_run_case_catch_errors(struct kunit *test, + struct kunit_module *module, + struct kunit_case *test_case) +{ + jmp_buf fault_catcher; + int faulted; + + kunit_set_success(test, true); + kunit_set_death_test(test, false); + + /* + * Tell the trap subsystem that we want to catch any segfaults that + * occur. + */ + current->thread.is_running_test = true; + current->thread.fault_catcher = &fault_catcher; + + /* + * ENTER HANDLER: If a failure occurs, we enter here. + */ + faulted = UML_SETJMP(&fault_catcher); + if (faulted == 0) { + /* + * NORMAL CASE: we have not run kunit_run_case_internal yet. + * + * kunit_run_case_internal may encounter a fatal error; if it + * does, we will jump to ENTER_HANDLER above instead of + * continuing normal control flow. + */ + kunit_run_case_internal(test, module, test_case); + /* + * This line may never be reached. + */ + kunit_run_case_cleanup(test, module, test_case); + } else if (kunit_get_death_test(test)) { + /* + * EXPECTED DEATH: kunit_run_case_internal encountered + * anticipated fatal error. Everything should be in a safe + * state. + */ + kunit_run_case_cleanup(test, module, test_case); + } else { + /* + * UNEXPECTED DEATH: kunit_run_case_internal encountered an + * unanticipated fatal error. We have no idea what the state of + * the test case is in. + */ + kunit_handle_test_crash(test, module, test_case); + kunit_set_success(test, false); + } + /* + * EXIT HANDLER: test case has been run and all possible errors have + * been handled. + */ + + /* + * Tell the trap subsystem that we no longer want to catch any + * segfaults. + */ + current->thread.fault_catcher = NULL; + current->thread.is_running_test = false; return kunit_get_success(test); } @@ -148,7 +263,7 @@ int kunit_run_tests(struct kunit_module *module) return ret; for (test_case = module->test_cases; test_case->run_case; test_case++) { - success = kunit_run_case(&test, module, test_case); + success = kunit_run_case_catch_errors(&test, module, test_case); if (!success) all_passed = false; @@ -303,3 +418,36 @@ void kunit_expect_binary_msg(struct kunit *test, kunit_expect_end(test, compare_result, stream); } +void kunit_assert_binary_msg(struct kunit *test, + long long left, const char *left_name, + long long right, const char *right_name, + bool compare_result, + const char *compare_name, + const char *file, + const char *line, + const char *fmt, ...) +{ + struct kunit_stream *stream = kunit_assert_start(test, file, line); + struct va_format vaf; + va_list args; + + stream->add(stream, + "Asserted %s %s %s, but\n", + left_name, compare_name, right_name); + stream->add(stream, "\t\t%s == %lld\n", left_name, left); + stream->add(stream, "\t\t%s == %lld", right_name, right); + + if (fmt) { + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + stream->add(stream, "\n%pV", &vaf); + + va_end(args); + } + + kunit_assert_end(test, compare_result, stream); +} + From patchwork Wed Nov 28 19:36:27 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703237 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E572D18B8 for ; Wed, 28 Nov 2018 19:38:11 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D887C29F77 for ; Wed, 28 Nov 2018 19:38:11 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CCB012B182; Wed, 28 Nov 2018 19:38:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 42CA72B0FF for ; Wed, 28 Nov 2018 19:38:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729391AbeK2Gky (ORCPT ); Thu, 29 Nov 2018 01:40:54 -0500 Received: from mail-pf1-f202.google.com ([209.85.210.202]:40706 "EHLO mail-pf1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727037AbeK2Gkx (ORCPT ); Thu, 29 Nov 2018 01:40:53 -0500 Received: by mail-pf1-f202.google.com with SMTP id p15so12687677pfk.7 for ; Wed, 28 Nov 2018 11:38:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=4IKZcfrlGwiEH28lOjBx5LUer+sMqwxtctpWBn7VdAA=; b=UCoLXEex5P9gF0WFmCQLfGm+EaXZnIgPgEFS3D1b4XYYWAOr+oa/EhGwwpLKUwMoHv tuJbY1Jo3rvL8QmWbs7VoXQHUKjYsLzaBbfgWlUF+9QyDx525KsLaNCtov3bGDxjIhMq aM9iwePUKMTX37lxS70ZAWsaefCmazhkVU1LrnhwSaPzk58SYuodbP5WiEVAf4ddjWGQ vg3Lc4t7W5ZhhY1HXZ3yidiCg+ml4Pij4s8TMqdveqJjh4UqSzIhEcso6TdsYOZbQN5H NW1ao59aOBDsIVqCi+2gphq5l6I2W8ahTtcVviP7cBirF0n/1VG8Pw3Xk2vaa5oYScKA /1Zw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=4IKZcfrlGwiEH28lOjBx5LUer+sMqwxtctpWBn7VdAA=; b=asEmEw77s8iUR89JJf86X+K7Ds5USLvqjCtL2p7ntY9h1lPIC/+/zvvInYdZg558rm 5i+zV1uJqdOsfMtephZf29vf6WWVVapqAkj+wbjp25aXzik2bLt6Zjvbx6+pWmkygUrc HJEr6AMjSQlg8iEANJ5PdJcXZbeoCMxJTiDdtjdqP4JmJGOWgw/wKXLdGi9jXy9QeVBa zoC3i6FwbTtoqeK3SUJg6yCVOaLZphsIaZj1r3B4ieAvXNbTi8BE1GYfbtoQ795EdisT qJeWN5nx49LM9csoJXVHRSs6IVMOcul9cUZ7/R1mww0FFgXl/o+7no83rwZB0g124jYH M/mg== X-Gm-Message-State: AA+aEWZWj3N2dmiramW0HldLYGa4bDcBs3rQQODf3cM0kpTTbcIvdQrY wkPL1XaBu3lfHvsnpSDGo95mhRj+AB2MmvcHa/+snA== X-Google-Smtp-Source: AFSGD/WnggAFh0owRTchvcPojBQP+KX0t2TGac4DD2dNnzBrZvZn2nu84oJBGeXQPeslzFI8Ot+IGoVssdKwirNseddX6g== X-Received: by 2002:aa7:81cc:: with SMTP id c12mr2953373pfn.69.1543433888545; Wed, 28 Nov 2018 11:38:08 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:27 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-11-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 10/19] kunit: test: add test managed resource tests From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins , Avinash Kondareddy Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Tests how tests interact with test managed resources in their lifetime. Signed-off-by: Avinash Kondareddy Signed-off-by: Brendan Higgins --- kunit/test-test.c | 121 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 110 insertions(+), 11 deletions(-) diff --git a/kunit/test-test.c b/kunit/test-test.c index 88b3bcf9c4e00..36fd95c90a26a 100644 --- a/kunit/test-test.c +++ b/kunit/test-test.c @@ -7,31 +7,130 @@ */ #include -static void test_test_catches_segfault(struct kunit *test) +static void kunit_test_catches_segfault(struct kunit *test) { void (*invalid_func)(void) = (void (*)(void)) SIZE_MAX; KUNIT_ASSERT_SIGSEGV(test, invalid_func()); } -static int test_test_init(struct kunit *test) +/* + * Context for testing test managed resources + * is_resource_initialized is used to test arbitrary resources + */ +struct kunit_test_context { + struct kunit test; + bool is_resource_initialized; +}; + +static int fake_resource_init(struct kunit_resource *res, void *context) { + struct kunit_test_context *ctx = context; + + res->allocation = &ctx->is_resource_initialized; + ctx->is_resource_initialized = true; return 0; } -static void test_test_exit(struct kunit *test) +static void fake_resource_free(struct kunit_resource *res) +{ + bool *is_resource_initialized = res->allocation; + + *is_resource_initialized = false; +} + +static void kunit_test_init_resources(struct kunit *test) +{ + struct kunit_test_context *ctx = test->priv; + + kunit_init_test(&ctx->test, "testing_test_init_test"); + + KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources)); +} + +static void kunit_test_alloc_resource(struct kunit *test) +{ + struct kunit_test_context *ctx = test->priv; + struct kunit_resource *res; + kunit_resource_free_t free = fake_resource_free; + + res = kunit_alloc_resource(&ctx->test, + fake_resource_init, + fake_resource_free, + ctx); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, res); + KUNIT_EXPECT_EQ(test, &ctx->is_resource_initialized, res->allocation); + KUNIT_EXPECT_TRUE(test, list_is_last(&res->node, &ctx->test.resources)); + KUNIT_EXPECT_EQ(test, free, res->free); +} + +static void kunit_test_free_resource(struct kunit *test) { + struct kunit_test_context *ctx = test->priv; + struct kunit_resource *res = kunit_alloc_resource(&ctx->test, + fake_resource_init, + fake_resource_free, + ctx); + + kunit_free_resource(&ctx->test, res); + + KUNIT_EXPECT_EQ(test, false, ctx->is_resource_initialized); + KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources)); +} + +static void kunit_test_cleanup_resources(struct kunit *test) +{ + int i; + const int num_res = 5; + struct kunit_test_context *ctx = test->priv; + struct kunit_resource *resources[num_res]; + + for (i = 0; i < num_res; i++) { + resources[i] = kunit_alloc_resource(&ctx->test, + fake_resource_init, + fake_resource_free, + ctx); + } + + kunit_cleanup(&ctx->test); + + KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources)); +} + +static int kunit_test_init(struct kunit *test) +{ + struct kunit_test_context *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + + if (!ctx) + return -ENOMEM; + test->priv = ctx; + + kunit_init_test(&ctx->test, "test_test_context"); + return 0; +} + +static void kunit_test_exit(struct kunit *test) +{ + struct kunit_test_context *ctx = test->priv; + + kunit_cleanup(&ctx->test); + kfree(ctx); } -static struct kunit_case test_test_cases[] = { - KUNIT_CASE(test_test_catches_segfault), +static struct kunit_case kunit_test_cases[] = { + KUNIT_CASE(kunit_test_catches_segfault), + KUNIT_CASE(kunit_test_init_resources), + KUNIT_CASE(kunit_test_alloc_resource), + KUNIT_CASE(kunit_test_free_resource), + KUNIT_CASE(kunit_test_cleanup_resources), {}, }; -static struct kunit_module test_test_module = { - .name = "test-test", - .init = test_test_init, - .exit = test_test_exit, - .test_cases = test_test_cases, +static struct kunit_module kunit_test_module = { + .name = "kunit-test", + .init = kunit_test_init, + .exit = kunit_test_exit, + .test_cases = kunit_test_cases, }; -module_test(test_test_module); +module_test(kunit_test_module); From patchwork Wed Nov 28 19:36:28 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703273 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0612916B1 for ; Wed, 28 Nov 2018 19:39:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EC1C12D722 for ; Wed, 28 Nov 2018 19:39:14 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DFFC52D762; Wed, 28 Nov 2018 19:39:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3BA782D722 for ; Wed, 28 Nov 2018 19:39:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729434AbeK2Gk5 (ORCPT ); Thu, 29 Nov 2018 01:40:57 -0500 Received: from mail-qt1-f201.google.com ([209.85.160.201]:35775 "EHLO mail-qt1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729117AbeK2Gk4 (ORCPT ); Thu, 29 Nov 2018 01:40:56 -0500 Received: by mail-qt1-f201.google.com with SMTP id p24so25042251qtl.2 for ; Wed, 28 Nov 2018 11:38:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=1xbhMNvec4B5xSL1oHl4qV1ywjoofQ6muA5OEf5mErs=; b=r5egsVPRtZM1HiSYPImpPHgaH/CRRTATj35KzJJyDHka55x2wrsE33cx+AjID9zNBs wDl7b+lgf5EXTzSL6dm3PZL1l2IHSLReffNuF9QESIRxzvyJ8YdjxFKR+9FpUx54h6QM p0zzWxOZQ4nGxJqiHkY6IQhw3/9mCvKkz8XPMhFCrQJetPWFAWoa4qL/phXTSdJ93o64 oYLhFcm/fQJNxeK3gh06IQxgJFWl3iyOZubW9VDx3tzfF+RZIac3DLldmaqpaQvgmOPI F44sZgceb68ovu2sBcpMLosRACXuUwStlhbR9a4u05NGltWhikJwTHtMC3pvlZfbvwtJ +4cQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=1xbhMNvec4B5xSL1oHl4qV1ywjoofQ6muA5OEf5mErs=; b=a4pOTS66u8HTOqaNKgVWSDc/mXZHoO8tyznJ81bus6dvPj3SVLXVehCJxOlXTV2Smf HqB/eKJcPMz04hF9mZ5d/fBzGLwqMaRQIPvkblpqUMZdMC6c8Tc8P+83nMAi2/LJ+nEV PmAWf/ckQManTrgag3cDVuLteqxI/3EJniYBd8GQ3P7c5SYeeINFxbA1Q+nYJzqe+glu iSzD+Xsn5Qvg+mWz7KIhqzBZNIs0ms6mChyvA0urphQqaFkzd2OViEC6JJPR8tUAg1vi RAk9uM66eSH29ddHS0xmL2YGo6zuGFZCVJEwbf4awHU+FhhEqDsdSaZcZlu4A+ETUH3W iUlA== X-Gm-Message-State: AA+aEWbXwhyOQIpjh8HDQatLE9S7Ukoq7h9D35bU0qRXwyB/yhbgoMv4 IZcLch2iJ0QDaVFDGIyzkiAmdtmEhcwW52cjCDrEQQ== X-Google-Smtp-Source: AFSGD/Vp+eB7mYlveEADB2/q7q2KIhbKFmASYVVYOI9B5oqatosjGn+i5nND9oFLxE3mJ+hoTvDPmtlDSUMyjBueDGCklw== X-Received: by 2002:a37:67d7:: with SMTP id b206mr4522605qkc.17.1543433891235; Wed, 28 Nov 2018 11:38:11 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:28 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-12-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 11/19] kunit: add Python libraries for handing KUnit config and kernel From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins , Felix Guo Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The ultimate goal is to create minimal isolated test binaries; in the meantime we are using UML to provide the infrastructure to run tests, so define an abstract way to configure and run tests that allow us to change the context in which tests are built without affecting the user. This also makes pretty and dynamic error reporting, and a lot of other nice features easier. kunit_config.py: - parse .config and Kconfig files. kunit_kernel.py: provides helper functions to: - configure the kernel using kunitconfig. - build the kernel with the appropriate configuration. - provide function to invoke the kernel and stream the output back. Signed-off-by: Felix Guo Signed-off-by: Brendan Higgins --- tools/testing/kunit/.gitignore | 3 + tools/testing/kunit/kunit_config.py | 60 +++++++++++++ tools/testing/kunit/kunit_kernel.py | 126 ++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 tools/testing/kunit/.gitignore create mode 100644 tools/testing/kunit/kunit_config.py create mode 100644 tools/testing/kunit/kunit_kernel.py diff --git a/tools/testing/kunit/.gitignore b/tools/testing/kunit/.gitignore new file mode 100644 index 0000000000000..c791ff59a37a9 --- /dev/null +++ b/tools/testing/kunit/.gitignore @@ -0,0 +1,3 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] \ No newline at end of file diff --git a/tools/testing/kunit/kunit_config.py b/tools/testing/kunit/kunit_config.py new file mode 100644 index 0000000000000..183bd5e758762 --- /dev/null +++ b/tools/testing/kunit/kunit_config.py @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0 + +import collections +import re + +CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_\w+ is not set$' +CONFIG_PATTERN = r'^CONFIG_\w+=\S+$' + +KconfigEntryBase = collections.namedtuple('KconfigEntry', ['raw_entry']) + + +class KconfigEntry(KconfigEntryBase): + + def __str__(self) -> str: + return self.raw_entry + + +class KconfigParseError(Exception): + """Error parsing Kconfig defconfig or .config.""" + + +class Kconfig(object): + """Represents defconfig or .config specified using the Kconfig language.""" + + def __init__(self): + self._entries = [] + + def entries(self): + return set(self._entries) + + def add_entry(self, entry: KconfigEntry) -> None: + self._entries.append(entry) + + def is_subset_of(self, other: "Kconfig") -> bool: + return self.entries().issubset(other.entries()) + + def write_to_file(self, path: str) -> None: + with open(path, 'w') as f: + for entry in self.entries(): + f.write(str(entry) + '\n') + + def parse_from_string(self, blob: str) -> None: + """Parses a string containing KconfigEntrys and populates this Kconfig.""" + self._entries = [] + is_not_set_matcher = re.compile(CONFIG_IS_NOT_SET_PATTERN) + config_matcher = re.compile(CONFIG_PATTERN) + for line in blob.split('\n'): + line = line.strip() + if not line: + continue + elif config_matcher.match(line) or is_not_set_matcher.match(line): + self._entries.append(KconfigEntry(line)) + elif line[0] == '#': + continue + else: + raise KconfigParseError('Failed to parse: ' + line) + + def read_from_file(self, path: str) -> None: + with open(path, 'r') as f: + self.parse_from_string(f.read()) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py new file mode 100644 index 0000000000000..bba7ea7ca1869 --- /dev/null +++ b/tools/testing/kunit/kunit_kernel.py @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: GPL-2.0 + +import logging +import subprocess +import os + +import kunit_config + +KCONFIG_PATH = '.config' + +class ConfigError(Exception): + """Represents an error trying to configure the Linux kernel.""" + + +class BuildError(Exception): + """Represents an error trying to build the Linux kernel.""" + + +class LinuxSourceTreeOperations(object): + """An abstraction over command line operations performed on a source tree.""" + + def make_mrproper(self): + try: + subprocess.check_output(['make', 'mrproper']) + except OSError as e: + raise ConfigError('Could not call make command: ' + e) + except subprocess.CalledProcessError as e: + raise ConfigError(e.output) + + def make_olddefconfig(self): + try: + subprocess.check_output(['make', 'ARCH=um', 'olddefconfig']) + except OSError as e: + raise ConfigError('Could not call make command: ' + e) + except subprocess.CalledProcessError as e: + raise ConfigError(e.output) + + def make(self, jobs): + try: + subprocess.check_output([ + 'make', + 'ARCH=um', + '--jobs=' + str(jobs)]) + except OSError as e: + raise BuildError('Could not call execute make: ' + e) + except subprocess.CalledProcessError as e: + raise BuildError(e.output) + + def linux_bin(self, params, timeout): + """Runs the Linux UML binary. Must be named 'linux'.""" + process = subprocess.Popen( + ['./linux'] + params, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + process.wait(timeout=timeout) + return process + + +class LinuxSourceTree(object): + """Represents a Linux kernel source tree with KUnit tests.""" + + def __init__(self): + self._kconfig = kunit_config.Kconfig() + self._kconfig.read_from_file('kunitconfig') + self._ops = LinuxSourceTreeOperations() + + def clean(self): + try: + self._ops.make_mrproper() + except ConfigError as e: + logging.error(e) + return False + return True + + def build_config(self): + self._kconfig.write_to_file(KCONFIG_PATH) + try: + self._ops.make_olddefconfig() + except ConfigError as e: + logging.error(e) + return False + validated_kconfig = kunit_config.Kconfig() + validated_kconfig.read_from_file(KCONFIG_PATH) + if not self._kconfig.is_subset_of(validated_kconfig): + logging.error('Provided Kconfig is not contained in validated .config!') + return False + return True + + def build_reconfig(self): + """Creates a new .config if it is not a subset of the kunitconfig.""" + if os.path.exists(KCONFIG_PATH): + existing_kconfig = kunit_config.Kconfig() + existing_kconfig.read_from_file(KCONFIG_PATH) + if not self._kconfig.is_subset_of(existing_kconfig): + print('Regenerating .config ...') + os.remove(KCONFIG_PATH) + return self.build_config() + else: + return True + else: + print('Generating .config ...') + return self.build_config() + + def build_um_kernel(self, jobs): + try: + self._ops.make_olddefconfig() + self._ops.make(jobs) + except (ConfigError, BuildError) as e: + logging.error(e) + return False + used_kconfig = kunit_config.Kconfig() + used_kconfig.read_from_file(KCONFIG_PATH) + if not self._kconfig.is_subset_of(used_kconfig): + logging.error('Provided Kconfig is not contained in final config!') + return False + return True + + def run_kernel(self, args=[]): + timeout = None + args.extend(['mem=256M']) + process = self._ops.linux_bin(args, timeout) + with open('test.log', 'w') as f: + for line in process.stdout: + f.write(line.rstrip().decode('ascii') + '\n') + yield line.rstrip().decode('ascii') From patchwork Wed Nov 28 19:36:29 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703243 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EC82215A8 for ; Wed, 28 Nov 2018 19:38:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DDB6E29F77 for ; Wed, 28 Nov 2018 19:38:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D0A112B182; Wed, 28 Nov 2018 19:38:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 54C4D29F77 for ; Wed, 28 Nov 2018 19:38:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729451AbeK2Gk7 (ORCPT ); Thu, 29 Nov 2018 01:40:59 -0500 Received: from mail-pg1-f202.google.com ([209.85.215.202]:52198 "EHLO mail-pg1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729433AbeK2Gk6 (ORCPT ); Thu, 29 Nov 2018 01:40:58 -0500 Received: by mail-pg1-f202.google.com with SMTP id t26so12701615pgu.18 for ; Wed, 28 Nov 2018 11:38:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=PSWj5cFU+jrigLyNwOIkjVkSQOjGgnr+grtJvw72p2k=; b=ZVg5n/fJ90O5sJ3IAOCgBQT0Y735goRSISlmv+c+yyF4e39VXUeukE1DtCXZgiFsws 3CywqLzYt/Xhiav0fZQYXuP0MBPa2nj7xGUqjl4yl6PkgRF46WyaNehaXEstMd/0ZPco VTeAOL/V+elEzDVz8z+IOR1L3qbY93bwZ/KLsOrNx4lPYIJw6R+nr+w1Bow/TVkgE3+o BbHyFCw6lvIp1p/XeyiTcwpouOAogKZFDv1/WBmGooPKp1CAggkUUd+xFA//d4OZe+rO kNdACscRvJZ+9+nUv6754NjbTu0ZDERwEC42qfElTYMeQmt85QBlULjbj4UXeWOt1QPD RdxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=PSWj5cFU+jrigLyNwOIkjVkSQOjGgnr+grtJvw72p2k=; b=FFyWoPFCas7xQD5CWDHhbJqRP8+c6AEOIobs2QvUVAjDCopneiBPqUC4AGF/lu+U/q lHmHmyuFoE9MDyhNecaTOXEPkPO6rQbgBIG3skOPC0pARxpXpswo9ol+V1jowijUoPSZ ug4iJhZ/FKk+vl4GQ2cf7OepyLh/BwPtXUyMc9YKRCzEJkjcDhBXduvnWjaZKdyddTtM a4D/U8JcoOslyd7VfgeV6ms6snN5hhdJ4MWGXyCk9Q3v1mIjQI4FAQtvVK6NNdD+Naej P6tILoAHToVwgQvHjyysHmlrn4LgQ3dA59MNGhDyIaOnFwzfEpMRWXVHwLcrTBEyQYBt l0VQ== X-Gm-Message-State: AA+aEWYAogQ0FHn6mTwfHBjRjwttJ91EfCA8XYEGcDjc5ksiKtS0LIDr sR1voafehv0QA9FrYbIAV0wN/QVbjpMTMPnz9Djo4Q== X-Google-Smtp-Source: AFSGD/UNL8VzMXvdy/txBvVB+FEhEs2NNoQ9cq2xnih5rgsgUaCtXAFa837C43GfxD4knn2v0XS0RHUhlr4yWXZK/F1pTA== X-Received: by 2002:a63:b601:: with SMTP id j1mr2949103pgf.58.1543433893299; Wed, 28 Nov 2018 11:38:13 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:29 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-13-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 12/19] kunit: add KUnit wrapper script and simple output parser From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins , Felix Guo Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The KUnit wrapper script interfaces with the two modules (kunit_config.py and kunit_kernel.py) and provides a command line interface for running KUnit tests. This interface allows the caller to specify options like test timeouts. The script handles configuring, building and running the kernel and tests. The output parser (kunit_parser.py) simply strips out all the output from the kernel that is outputted as part of it's initialization sequence. This ensures that only the output from KUnit is displayed on the screen. A full version of the output is written to test.log, or can be seen by passing --raw_output to the wrapper script. Signed-off-by: Felix Guo Signed-off-by: Brendan Higgins --- tools/testing/kunit/kunit.py | 40 +++++++++++++++++++++++++++++ tools/testing/kunit/kunit_kernel.py | 3 +-- tools/testing/kunit/kunit_parser.py | 24 +++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100755 tools/testing/kunit/kunit.py create mode 100644 tools/testing/kunit/kunit_parser.py diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py new file mode 100755 index 0000000000000..1356be404996b --- /dev/null +++ b/tools/testing/kunit/kunit.py @@ -0,0 +1,40 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0 + +# A thin wrapper on top of the KUnit Kernel + +import argparse +import sys +import os + +import kunit_config +import kunit_kernel +import kunit_parser + +parser = argparse.ArgumentParser(description='Runs KUnit tests.') + +parser.add_argument('--raw_output', help='don\'t format output from kernel', + action='store_true') + +parser.add_argument('--timeout', help='maximum number of seconds to allow for ' + 'all tests to run. This does not include time taken to ' + 'build the tests.', type=int, default=300, + metavar='timeout') + +cli_args = parser.parse_args() +linux = kunit_kernel.LinuxSourceTree() + +success = linux.build_reconfig() +if not success: + quit() + +print('Building KUnit Kernel ...') +success = linux.build_um_kernel() +if not success: + quit() + +print('Starting KUnit Kernel ...') +if cli_args.raw_output: + kunit_parser.raw_output(linux.run_kernel(timeout=cli_args.timeout)) +else: + kunit_parser.parse_run_tests(linux.run_kernel(timeout=cli_args.timeout)) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index bba7ea7ca1869..623f25b16f6c8 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -116,8 +116,7 @@ class LinuxSourceTree(object): return False return True - def run_kernel(self, args=[]): - timeout = None + def run_kernel(self, args=[], timeout=None): args.extend(['mem=256M']) process = self._ops.linux_bin(args, timeout) with open('test.log', 'w') as f: diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py new file mode 100644 index 0000000000000..1dff3adb73bd3 --- /dev/null +++ b/tools/testing/kunit/kunit_parser.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0 + +import re + +kunit_start_re = re.compile('console .* enabled') +kunit_end_re = re.compile('List of all partitions:') + +def isolate_kunit_output(kernel_output): + started = False + for line in kernel_output: + if kunit_start_re.match(line): + started = True + elif kunit_end_re.match(line): + break + elif started: + yield line + +def raw_output(kernel_output): + for line in kernel_output: + print(line) + +def parse_run_tests(kernel_output): + for output in isolate_kunit_output(kernel_output): + print(output) From patchwork Wed Nov 28 19:36:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703271 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 89F8715A8 for ; Wed, 28 Nov 2018 19:39:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 79DEB2D722 for ; Wed, 28 Nov 2018 19:39:08 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6AD7C2D762; Wed, 28 Nov 2018 19:39:08 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D72352D722 for ; Wed, 28 Nov 2018 19:39:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729473AbeK2GlB (ORCPT ); Thu, 29 Nov 2018 01:41:01 -0500 Received: from mail-it1-f201.google.com ([209.85.166.201]:52635 "EHLO mail-it1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729470AbeK2GlA (ORCPT ); Thu, 29 Nov 2018 01:41:00 -0500 Received: by mail-it1-f201.google.com with SMTP id o205so4267043itc.2 for ; Wed, 28 Nov 2018 11:38:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=vAK0p7vKTuMPpezuXE/k7QGLFOufe8xVPHMWIxnzcOw=; b=HZFAhEFpsemMuzBPrmsGrjv52lptKO71UvLKo8R2cqvpqs1fsoXvzEW9M82vVbiG4I 8lgm+sQr+xKpgGcI7n57+Iqp4EL/gw1tAT1r5AzBwEMUCY6uDx4HumzI5JusEDB9i0gT 1aXleqwgLn8rIP+5OiJ8PMWwF9NrssiZgN5lGGKq+tgV+qhjf0N0IRW6EXo5QmczSDFv sfTS7DnEtq0yWfymSBjFNaRT2AnoJ0F1kMTq6CJvrMF6HNzvjc4JFjUQ1ZL1V67M+DbU KMzcuowcCU7kdr/QxmqyuYiHmsdeq/7YReCEQcungg8JXo/tErb0d4cugphcf/gfr7a4 3+YA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=vAK0p7vKTuMPpezuXE/k7QGLFOufe8xVPHMWIxnzcOw=; b=gMWftkZLUkbf9jpCVd26Aro7rwLaWxpQpfAvTkm451yaE6jtmLM3duREZDptLcBb23 s3gCE7Jx+As8+PowHpvmFgpY41fBNPwpbz+F17lWaJRn5Mcq9xzR0cLyKc/LJW8/zHOa Z6QAkWk7SFv5kJlDx4BaqaeZUFi0qZdbwtAmsX4yQBYVFc3zrXNOA8lYcFMcQFbw8Tkc z1HPfTO8sW7JBOYQBvTDI991gvN7RjwnxqXLIuEDfUcvh1RIWpJd/VmlzRKHQJJ7rQv7 2trY5995OlPrn9zwgsGmL8RVMuPdb6JqVUGgaSnspVrltmQLALoPFCZL2WqPpXsuzyYF ay/w== X-Gm-Message-State: AA+aEWYn+AgKAQKBxomol5sP4ALKQDicgrD3BpjsfULM3EaV/dhKCWZz NS0p9AFqyRt1gQ5cEZh55WKJptfznD+BJCi7J9DhiA== X-Google-Smtp-Source: AFSGD/Ujo8L+z++cqBiX0TR/cZSdTP71zFmLdi9OlccxU3BUppjqS2CtoJDToemuwTkNGpAvvuqMQL2o2zW3kWe3LvqXRA== X-Received: by 2002:a24:ac6c:: with SMTP id m44-v6mr3565488iti.40.1543433895704; Wed, 28 Nov 2018 11:38:15 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:30 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-14-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 13/19] kunit: improve output from python wrapper From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins , Felix Guo Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP - add colors to displayed output - add timing and summary Signed-off-by: Felix Guo Signed-off-by: Brendan Higgins --- tools/testing/kunit/kunit.py | 27 ++++++++- tools/testing/kunit/kunit_parser.py | 93 ++++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 5 deletions(-) diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py index 1356be404996b..0b8e8c20a746e 100755 --- a/tools/testing/kunit/kunit.py +++ b/tools/testing/kunit/kunit.py @@ -6,6 +6,7 @@ import argparse import sys import os +import time import kunit_config import kunit_kernel @@ -21,20 +22,40 @@ parser.add_argument('--timeout', help='maximum number of seconds to allow for ' 'build the tests.', type=int, default=300, metavar='timeout') +parser.add_argument('--jobs', + help='As in the make command, "Specifies the number of ' + 'jobs (commands) to run simultaneously."', + type=int, default=8, metavar='jobs') + cli_args = parser.parse_args() linux = kunit_kernel.LinuxSourceTree() +config_start = time.time() success = linux.build_reconfig() +config_end = time.time() if not success: quit() -print('Building KUnit Kernel ...') -success = linux.build_um_kernel() +kunit_parser.print_with_timestamp('Building KUnit Kernel ...') + +build_start = time.time() +success = linux.build_um_kernel(jobs=cli_args.jobs) +build_end = time.time() if not success: quit() -print('Starting KUnit Kernel ...') +kunit_parser.print_with_timestamp('Starting KUnit Kernel ...') +test_start = time.time() + if cli_args.raw_output: kunit_parser.raw_output(linux.run_kernel(timeout=cli_args.timeout)) else: kunit_parser.parse_run_tests(linux.run_kernel(timeout=cli_args.timeout)) + +test_end = time.time() + +kunit_parser.print_with_timestamp(( + "Elapsed time: %.3fs total, %.3fs configuring, %.3fs " + + "building, %.3fs running.\n") % (test_end - config_start, + config_end - config_start, build_end - build_start, + test_end - test_start)) diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index 1dff3adb73bd3..d9051e407d5a7 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 import re +from datetime import datetime kunit_start_re = re.compile('console .* enabled') kunit_end_re = re.compile('List of all partitions:') @@ -19,6 +20,94 @@ def raw_output(kernel_output): for line in kernel_output: print(line) +DIVIDER = "=" * 30 + +RESET = '\033[0;0m' + +def red(text): + return '\033[1;31m' + text + RESET + +def yellow(text): + return '\033[1;33m' + text + RESET + +def green(text): + return '\033[1;32m' + text + RESET + +def print_with_timestamp(message): + print('[%s] %s' % (datetime.now().strftime('%H:%M:%S'), message)) + +def print_log(log): + for m in log: + print_with_timestamp(m) + def parse_run_tests(kernel_output): - for output in isolate_kunit_output(kernel_output): - print(output) + test_case_output = re.compile('^kunit .*?: (.*)$') + + test_module_success = re.compile('^kunit .*: all tests passed') + test_module_fail = re.compile('^kunit .*: one or more tests failed') + + test_case_success = re.compile('^kunit (.*): (.*) passed') + test_case_fail = re.compile('^kunit (.*): (.*) failed') + test_case_crash = re.compile('^kunit (.*): (.*) crashed') + + total_tests = set() + failed_tests = set() + crashed_tests = set() + + def get_test_name(match): + return match.group(1) + ":" + match.group(2) + + current_case_log = [] + def end_one_test(match, log): + log.clear() + total_tests.add(get_test_name(match)) + + print_with_timestamp(DIVIDER) + for line in isolate_kunit_output(kernel_output): + # Ignore module output: + if (test_module_success.match(line) or + test_module_fail.match(line)): + print_with_timestamp(DIVIDER) + continue + + match = re.match(test_case_success, line) + if match: + print_with_timestamp(green("[PASSED] ") + + get_test_name(match)) + end_one_test(match, current_case_log) + continue + + match = re.match(test_case_fail, line) + # Crashed tests will report as both failed and crashed. We only + # want to show and count it once. + if match and get_test_name(match) not in crashed_tests: + failed_tests.add(get_test_name(match)) + print_with_timestamp(red("[FAILED] " + + get_test_name(match))) + print_log(map(yellow, current_case_log)) + print_with_timestamp("") + end_one_test(match, current_case_log) + continue + + match = re.match(test_case_crash, line) + if match: + crashed_tests.add(get_test_name(match)) + print_with_timestamp(yellow("[CRASH] " + + get_test_name(match))) + print_log(current_case_log) + print_with_timestamp("") + end_one_test(match, current_case_log) + continue + + # Strip off the `kunit module-name:` prefix + match = re.match(test_case_output, line) + if match: + current_case_log.append(match.group(1)) + else: + current_case_log.append(line) + + fmt = green if (len(failed_tests) + len(crashed_tests) == 0) else red + print_with_timestamp( + fmt("Testing complete. %d tests run. %d failed. %d crashed." % + (len(total_tests), len(failed_tests), len(crashed_tests)))) + From patchwork Wed Nov 28 19:36:31 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703249 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 96AE915A8 for ; Wed, 28 Nov 2018 19:38:22 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 848CC29F77 for ; Wed, 28 Nov 2018 19:38:22 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 788162B214; Wed, 28 Nov 2018 19:38:22 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A46212B0FF for ; Wed, 28 Nov 2018 19:38:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729494AbeK2GlE (ORCPT ); Thu, 29 Nov 2018 01:41:04 -0500 Received: from mail-io1-f74.google.com ([209.85.166.74]:47597 "EHLO mail-io1-f74.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729472AbeK2GlD (ORCPT ); Thu, 29 Nov 2018 01:41:03 -0500 Received: by mail-io1-f74.google.com with SMTP id s25so26404471ioc.14 for ; Wed, 28 Nov 2018 11:38:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=d6uvqmmiqPZQcRsjwqxUb50lbmxr9QOMc7S4OZk1tZs=; b=g5L6XKb2n1pLBlD2qCqIPfv56xWm4u4y4ZStuJiuGj3tQVWRMRW+qP/nQIus+XTVbl w5pLR6jMjQtTpfR+opfQWhObaVWO1CqOeAq1ZGJt26PMSzb6xtYs8AhFxUfKpox1bXq4 uvi+dXel4QYseU+tdHpr9/VVLwKA9BwzODv73YNdtgaiWG+/lgMg1zjizfbDp8KA+Qqv cOKQlBEo/uazNvsSqv83WOvkwiik7lzzO15LCFgPKLlcr2Iq9jHsCUbhkFNovZNoA59l Zx6D1wOtAAE+PP/MeaxZJEGtQU8WiX7ajH6bxA1Sqqkac1M3RGPSTxOrUmZllNB2C3tU CKbw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=d6uvqmmiqPZQcRsjwqxUb50lbmxr9QOMc7S4OZk1tZs=; b=Kof1hmXK8+8tXBNM2bbyHPVI6Ouv482Vtt3wUrpkXIOgVUHguOiTfbkuPWcqGfUom+ sgwiD+CK+k6It9Nnt4NHOHFBEx7X4ra22k86rRzEXyfE2gDd5QIjZicxD0RTOJPyuUqw StnfL7VPy3gy5xrH45uVZ1gNKvcoHUZKqVZs35EWiPkoC32ZnL2QbmAYtKCqiWptg1vC 0nhtLqRkaQ7T93ni70AlYvbbvASXiWuxJCjHaI9rvUjP87DfW0MTJC5H8xB27D6WEc+g aMQ6MnQEP/gMCSwBd3hQpl7yYlIO4FdOmLQo4WwoMgetFV8NAeftun0B5ENj2b8JzI1K HVOw== X-Gm-Message-State: AA+aEWZZYbzqzVeAJxSjHxcpcgH9cIFsk+VkFVrhuMXmC2BGPvCv1uqM cIQkV0AbG5EdCTpxOsMSQ57gWIhXiISQS+DnGgazcA== X-Google-Smtp-Source: AFSGD/U8nIbmFCXwrlRT53SqZChqPAonPAuTb8EOMXvm52RWLK/kOpOgeaRUF7MvLS2H/4Bc7b7XJi4+0a/o1Z7aNuJSmg== X-Received: by 2002:a24:2793:: with SMTP id g141-v6mr3305750ita.39.1543433898077; Wed, 28 Nov 2018 11:38:18 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:31 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-15-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 14/19] Documentation: kunit: add documentation for KUnit From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins , Felix Guo Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add documentation for KUnit, the Linux kernel unit testing framework. - Add intro and usage guide for KUnit - Add API reference Signed-off-by: Felix Guo Signed-off-by: Brendan Higgins --- Documentation/index.rst | 1 + Documentation/kunit/api/index.rst | 16 ++ Documentation/kunit/api/test.rst | 15 + Documentation/kunit/faq.rst | 46 +++ Documentation/kunit/index.rst | 80 ++++++ Documentation/kunit/start.rst | 180 ++++++++++++ Documentation/kunit/usage.rst | 447 ++++++++++++++++++++++++++++++ 7 files changed, 785 insertions(+) create mode 100644 Documentation/kunit/api/index.rst create mode 100644 Documentation/kunit/api/test.rst create mode 100644 Documentation/kunit/faq.rst create mode 100644 Documentation/kunit/index.rst create mode 100644 Documentation/kunit/start.rst create mode 100644 Documentation/kunit/usage.rst diff --git a/Documentation/index.rst b/Documentation/index.rst index 5db7e87c7cb1d..275ef4db79f61 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -68,6 +68,7 @@ merged much easier. kernel-hacking/index trace/index maintainer/index + kunit/index Kernel API documentation ------------------------ diff --git a/Documentation/kunit/api/index.rst b/Documentation/kunit/api/index.rst new file mode 100644 index 0000000000000..c31c530088153 --- /dev/null +++ b/Documentation/kunit/api/index.rst @@ -0,0 +1,16 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============= +API Reference +============= +.. toctree:: + + test + +This section documents the KUnit kernel testing API. It is divided into 3 +sections: + +================================= ============================================== +:doc:`test` documents all of the standard testing API + excluding mocking or mocking related features. +================================= ============================================== diff --git a/Documentation/kunit/api/test.rst b/Documentation/kunit/api/test.rst new file mode 100644 index 0000000000000..7c926014f047c --- /dev/null +++ b/Documentation/kunit/api/test.rst @@ -0,0 +1,15 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======== +Test API +======== + +This file documents all of the standard testing API excluding mocking or mocking +related features. + +.. kernel-doc:: include/kunit/test.h + :internal: + +.. kernel-doc:: include/kunit/kunit-stream.h + :internal: + diff --git a/Documentation/kunit/faq.rst b/Documentation/kunit/faq.rst new file mode 100644 index 0000000000000..cb8e4fb2257a0 --- /dev/null +++ b/Documentation/kunit/faq.rst @@ -0,0 +1,46 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================================= +Frequently Asked Questions +========================================= + +How is this different from Autotest, kselftest, etc? +==================================================== +KUnit is a unit testing framework. Autotest, kselftest (and some others) are +not. + +A `unit test `_ is supposed to +test a single unit of code in isolation, hence the name. A unit test should be +the finest granularity of testing and as such should allow all possible code +paths to be tested in the code under test; this is only possible if the code +under test is very small and does not have any external dependencies outside of +the test's control like hardware. + +There are no testing frameworks currently available for the kernel that do not +require installing the kernel on a test machine or in a VM and all require +tests to be written in userspace and run on the kernel under test; this is true +for Autotest, kselftest, and some others, disqualifying any of them from being +considered unit testing frameworks. + +What is the difference between a unit test and these other kinds of tests? +========================================================================== +Most existing tests for the Linux kernel would be categorized as an integration +test, or an end-to-end test. + +- A unit test is supposed to test a single unit of code in isolation, hence the + name. A unit test should be the finest granularity of testing and as such + should allow all possible code paths to be tested in the code under test; this + is only possible if the code under test is very small and does not have any + external dependencies outside of the test's control like hardware. +- An integration test tests the interaction between a minimal set of components, + usually just two or three. For example, someone might write an integration + test to test the interaction between a driver and a piece of hardware, or to + test the interaction between the userspace libraries the kernel provides and + the kernel itself; however, one of these tests would probably not test the + entire kernel along with hardware interactions and interactions with the + userspace. +- An end-to-end test usually tests the entire system from the perspective of the + code under test. For example, someone might write an end-to-end test for the + kernel by installing a production configuration of the kernel on production + hardware with a production userspace and then trying to exercise some behavior + that depends on interactions between the hardware, the kernel, and userspace. diff --git a/Documentation/kunit/index.rst b/Documentation/kunit/index.rst new file mode 100644 index 0000000000000..c6710211b647f --- /dev/null +++ b/Documentation/kunit/index.rst @@ -0,0 +1,80 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================================= +KUnit - Unit Testing for the Linux Kernel +========================================= + +.. toctree:: + :maxdepth: 2 + + start + usage + api/index + faq + +What is KUnit? +============== + +KUnit is a lightweight unit testing and mocking framework for the Linux kernel. +These tests are able to be run locally on a developer's workstation without a VM +or special hardware. + +KUnit is heavily inspired by JUnit, Python's unittest.mock, and +Googletest/Googlemock for C++. KUnit provides facilities for defining unit test +cases, grouping related test cases into test suites, providing common +infrastructure for running tests, and much more. + +Get started now: :doc:`start` + +Why KUnit? +========== + +A unit test is supposed to test a single unit of code in isolation, hence the +name. A unit test should be the finest granularity of testing and as such should +allow all possible code paths to be tested in the code under test; this is only +possible if the code under test is very small and does not have any external +dependencies outside of the test's control like hardware. + +Outside of KUnit, there are no testing frameworks currently +available for the kernel that do not require installing the kernel on a test +machine or in a VM and all require tests to be written in userspace running on +the kernel; this is true for Autotest, and kselftest, disqualifying +any of them from being considered unit testing frameworks. + +KUnit addresses the problem of being able to run tests without needing a virtual +machine or actual hardware with User Mode Linux. User Mode Linux is a Linux +architecture, like ARM or x86; however, unlike other architectures it compiles +to a standalone program that can be run like any other program directly inside +of a host operating system; to be clear, it does not require any virtualization +support; it is just a regular program. + +KUnit is fast. Excluding build time, from invocation to completion KUnit can run +several dozen tests in only 10 to 20 seconds; this might not sound like a big +deal to some people, but having such fast and easy to run tests fundamentally +changes the way you go about testing and even writing code in the first place. +Linus himself said in his `git talk at Google +`_: + + "... a lot of people seem to think that performance is about doing the + same thing, just doing it faster, and that is not true. That is not what + performance is all about. If you can do something really fast, really + well, people will start using it differently." + +In this context Linus was talking about branching and merging, +but this point also applies to testing. If your tests are slow, unreliable, are +difficult to write, and require a special setup or special hardware to run, +then you wait a lot longer to write tests, and you wait a lot longer to run +tests; this means that tests are likely to break, unlikely to test a lot of +things, and are unlikely to be rerun once they pass. If your tests are really +fast, you run them all the time, every time you make a change, and every time +someone sends you some code. Why trust that someone ran all their tests +correctly on every change when you can just run them yourself in less time than +it takes to read his / her test log? + +How do I use it? +=================== + +* :doc:`start` - for new users of KUnit +* :doc:`usage` - for a more detailed explanation of KUnit features +* :doc:`api/index` - for the list of KUnit APIs used for testing + diff --git a/Documentation/kunit/start.rst b/Documentation/kunit/start.rst new file mode 100644 index 0000000000000..5cdba5091905e --- /dev/null +++ b/Documentation/kunit/start.rst @@ -0,0 +1,180 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=============== +Getting Started +=============== + +Installing dependencies +======================= +KUnit has the same dependencies as the Linux kernel. As long as you can build +the kernel, you can run KUnit. + +KUnit Wrapper +============= +Included with KUnit is a simple Python wrapper that helps format the output to +easily use and read KUnit output. It handles building and running the kernel, as +well as formatting the output. + +The wrapper can be run with: + +.. code-block:: bash + + ./tools/testing/kunit/kunit.py + +Creating a kunitconfig +====================== +The Python script is a thin wrapper around Kbuild as such, it needs to be +configured with a ``kunitconfig`` file. This file essentially contains the +regular Kernel config, with the specific test targets as well. + +.. code-block:: bash + + git clone -b master https://kunit.googlesource.com/kunitconfig $PATH_TO_KUNITCONFIG_REPO + cd $PATH_TO_LINUX_REPO + ln -s $PATH_TO_KUNIT_CONFIG_REPO/kunitconfig kunitconfig + +You may want to add kunitconfig to your local gitignore. + +Verifying KUnit Works +------------------------- + +To make sure that everything is set up correctly, simply invoke the Python +wrapper from your kernel repo: + +.. code-block:: bash + + ./tools/testing/kunit/kunit.py + +.. note:: + You may want to run ``make mrproper`` first. + +If everything worked correctly, you should see the following: + +.. code-block:: bash + + Generating .config ... + Building KUnit Kernel ... + Starting KUnit Kernel ... + +followed by a list of tests that are run. All of them should be passing. + +.. note:: + Because it is building a lot of sources for the first time, the ``Building + kunit kernel`` step may take a while. + +Writing your first test +========================== + +In your kernel repo let's add some code that we can test. Create a file +``drivers/misc/example.h`` with the contents: + +.. code-block:: c + + int misc_example_add(int left, int right); + +create a file ``drivers/misc/example.c``: + +.. code-block:: c + + #include + + #include "example.h" + + int misc_example_add(int left, int right) + { + return left + right; + } + +Now add the following lines to ``drivers/misc/Kconfig``: + +.. code-block:: kconfig + + config MISC_EXAMPLE + bool "My example" + +and the following lines to ``drivers/misc/Makefile``: + +.. code-block:: make + + obj-$(CONFIG_MISC_EXAMPLE) += example.o + +Now we are ready to write the test. The test will be in +``drivers/misc/example-test.c``: + +.. code-block:: c + + #include + #include "example.h" + + /* Define the test cases. */ + + static void misc_example_add_test_basic(struct kunit *test) + { + KUNIT_EXPECT_EQ(test, 1, misc_example_add(1, 0)); + KUNIT_EXPECT_EQ(test, 2, misc_example_add(1, 1)); + KUNIT_EXPECT_EQ(test, 0, misc_example_add(-1, 1)); + KUNIT_EXPECT_EQ(test, INT_MAX, misc_example_add(0, INT_MAX)); + KUNIT_EXPECT_EQ(test, -1, misc_example_add(INT_MAX, INT_MIN)); + } + + static void misc_example_test_failure(struct kunit *test) + { + KUNIT_FAIL(test, "This test never passes."); + } + + static struct kunit_case misc_example_test_cases[] = { + KUNIT_CASE(misc_example_add_test_basic), + KUNIT_CASE(misc_example_test_failure), + {}, + }; + + static struct kunit_module misc_example_test_module = { + .name = "misc-example", + .test_cases = misc_example_test_cases, + }; + module_test(misc_example_test_module); + +Now add the following to ``drivers/misc/Kconfig``: + +.. code-block:: kconfig + + config MISC_EXAMPLE_TEST + bool "Test for my example" + depends on MISC_EXAMPLE && KUNIT + +and the following to ``drivers/misc/Makefile``: + +.. code-block:: make + + obj-$(CONFIG_MISC_EXAMPLE_TEST) += example-test.o + +Now add it to your ``kunitconfig``: + +.. code-block:: none + + CONFIG_MISC_EXAMPLE=y + CONFIG_MISC_EXAMPLE_TEST=y + +Now you can run the test: + +.. code-block:: bash + + ./tools/testing/kunit/kunit.py + +You should see the following failure: + +.. code-block:: none + + ... + [16:08:57] [PASSED] misc-example:misc_example_add_test_basic + [16:08:57] [FAILED] misc-example:misc_example_test_failure + [16:08:57] EXPECTATION FAILED at drivers/misc/example-test.c:17 + [16:08:57] This test never passes. + ... + +Congrats! You just wrote your first KUnit test! + +Next Steps +============= +* Check out the :doc:`usage` page for a more + in-depth explanation of KUnit. diff --git a/Documentation/kunit/usage.rst b/Documentation/kunit/usage.rst new file mode 100644 index 0000000000000..96ef7f9a1add4 --- /dev/null +++ b/Documentation/kunit/usage.rst @@ -0,0 +1,447 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============= +Using KUnit +============= + +The purpose of this document is to describe what KUnit is, how it works, how it +is intended to be used, and all the concepts and terminology that are needed to +understand it. This guide assumes a working knowledge of the Linux kernel and +some basic knowledge of testing. + +For a high level introduction to KUnit, including setting up KUnit for your +project, see :doc:`start`. + +Organization of this document +================================= + +This document is organized into two main sections: Testing and Isolating +Behavior. The first covers what a unit test is and how to use KUnit to write +them. The second covers how to use KUnit to isolate code and make it possible +to unit test code that was otherwise un-unit-testable. + +Testing +========== + +What is KUnit? +------------------ + +"K" is short for "kernel" so "KUnit" is the "(Linux) Kernel Unit Testing +Framework." KUnit is intended first and foremost for writing unit tests; it is +general enough that it can be used to write integration tests; however, this is +a secondary goal. KUnit has no ambition of being the only testing framework for +the kernel; for example, it does not intend to be an end-to-end testing +framework. + +What is Unit Testing? +------------------------- + +A `unit test `_ is a test that +tests code at the smallest possible scope, a *unit* of code. In the C +programming language that's a function. + +Unit tests should be written for all the publicly exposed functions in a +compilation unit; so that is all the functions that are exported in either a +*class* (defined below) or all functions which are **not** static. + +Writing Tests +------------- + +Test Cases +~~~~~~~~~~ + +The fundamental unit in KUnit is the test case. A test case is a function with +the signature ``void (*)(struct kunit *test)``. It calls a function to be tested +and then sets *expectations* for what should happen. For example: + +.. code-block:: c + + void example_test_success(struct kunit *test) + { + } + + void example_test_failure(struct kunit *test) + { + KUNIT_FAIL(test, "This test never passes."); + } + +In the above example ``example_test_success`` always passes because it does +nothing; no expectations are set, so all expectations pass. On the other hand +``example_test_failure`` always fails because it calls ``KUNIT_FAIL``, which is +a special expectation that logs a message and causes the test case to fail. + +Expectations +~~~~~~~~~~~~ +An *expectation* is a way to specify that you expect a piece of code to do +something in a test. An expectation is called like a function. A test is made +by setting expectations about the behavior of a piece of code under test; when +one or more of the expectations fail, the test case fails and information about +the failure is logged. For example: + +.. code-block:: c + + void add_test_basic(struct kunit *test) + { + KUNIT_EXPECT_EQ(test, 1, add(1, 0)); + KUNIT_EXPECT_EQ(test, 2, add(1, 1)); + } + +In the above example ``add_test_basic`` makes a number of assertions about the +behavior of a function called ``add``; the first parameter is always of type +``struct kunit *``, which contains information about the current test context; +the second parameter, in this case, is what the value is expected to be; the +last value is what the value actually is. If ``add`` passes all of these +expectations, the test case, ``add_test_basic`` will pass; if any one of these +expectations fail, the test case will fail. + +It is important to understand that a test case *fails* when any expectation is +violated; however, the test will continue running, potentially trying other +expectations until the test case ends or is otherwise terminated. This is as +opposed to *assertions* which are discussed later. + +To learn about more expectations supported by KUnit, see :doc:`api/test`. + +.. note:: + A single test case should be pretty short, pretty easy to understand, + focused on a single behavior. + +For example, if we wanted to properly test the add function above, we would +create additional tests cases which would each test a different property that an +add function should have like this: + +.. code-block:: c + + void add_test_basic(struct kunit *test) + { + KUNIT_EXPECT_EQ(test, 1, add(1, 0)); + KUNIT_EXPECT_EQ(test, 2, add(1, 1)); + } + + void add_test_negative(struct kunit *test) + { + KUNIT_EXPECT_EQ(test, 0, add(-1, 1)); + } + + void add_test_max(struct kunit *test) + { + KUNIT_EXPECT_EQ(test, INT_MAX, add(0, INT_MAX)); + KUNIT_EXPECT_EQ(test, -1, add(INT_MAX, INT_MIN)); + } + + void add_test_overflow(struct kunit *test) + { + KUNIT_EXPECT_EQ(test, INT_MIN, add(INT_MAX, 1)); + } + +Notice how it is immediately obvious what all the properties that we are testing +for are. + +Assertions +~~~~~~~~~~ + +KUnit also has the concept of an *assertion*. An assertion is just like an +expectation except the assertion immediately terminates the test case if it is +not satisfied. + +For example: + +.. code-block:: c + + static void mock_test_do_expect_default_return(struct kunit *test) + { + struct mock_test_context *ctx = test->priv; + struct mock *mock = ctx->mock; + int param0 = 5, param1 = -5; + const char *two_param_types[] = {"int", "int"}; + const void *two_params[] = {¶m0, ¶m1}; + const void *ret; + + ret = mock->do_expect(mock, + "test_printk", test_printk, + two_param_types, two_params, + ARRAY_SIZE(two_params)); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ret); + KUNIT_EXPECT_EQ(test, -4, *((int *) ret)); + } + +In this example, the method under test should return a pointer to a value, so +if the pointer returned by the method is null or an errno, we don't want to +bother continuing the test since the following expectation could crash the test +case. `ASSERT_NOT_ERR_OR_NULL(...)` allows us to bail out of the test case if +the appropriate conditions have not been satisfied to complete the test. + +Modules / Test Suites +~~~~~~~~~~~~~~~~~~~~~ + +Now obviously one unit test isn't very helpful; the power comes from having +many test cases covering all of your behaviors. Consequently it is common to +have many *similar* tests; in order to reduce duplication in these closely +related tests most unit testing frameworks provide the concept of a *test +suite*, in KUnit we call it a *test module*; all it is is just a collection of +test cases for a unit of code with a set up function that gets invoked before +every test cases and then a tear down function that gets invoked after every +test case completes. + +Example: + +.. code-block:: c + + static struct kunit_case example_test_cases[] = { + KUNIT_CASE(example_test_foo), + KUNIT_CASE(example_test_bar), + KUNIT_CASE(example_test_baz), + {}, + }; + + static struct kunit_module example_test_module[] = { + .name = "example", + .init = example_test_init, + .exit = example_test_exit, + .test_cases = example_test_cases, + }; + module_test(example_test_module); + +In the above example the test suite, ``example_test_module``, would run the test +cases ``example_test_foo``, ``example_test_bar``, and ``example_test_baz``, each +would have ``example_test_init`` called immediately before it and would have +``example_test_exit`` called immediately after it. +``module_test(example_test_module)`` registers the test suite with the KUnit +test framework. + +.. note:: + A test case will only be run if it is associated with a test suite. + +For a more information on these types of things see the :doc:`api/test`. + +Isolating Behavior +================== + +The most important aspect of unit testing that other forms of testing do not +provide is the ability to limit the amount of code under test to a single unit. +In practice, this is only possible by being able to control what code gets run +when the unit under test calls a function and this is usually accomplished +through some sort of indirection where a function is exposed as part of an API +such that the definition of that function can be changed without affecting the +rest of the code base. In the kernel this primarily comes from two constructs, +classes, structs that contain function pointers that are provided by the +implementer, and architecture specific functions which have definitions selected +at compile time. + +Classes +------- + +Classes are not a construct that is built into the C programming language; +however, it is an easily derived concept. Accordingly, pretty much every project +that does not use a standardized object oriented library (like GNOME's GObject) +has their own slightly different way of doing object oriented programming; the +Linux kernel is no exception. + +The central concept in kernel object oriented programming is the class. In the +kernel, a *class* is a struct that contains function pointers. This creates a +contract between *implementers* and *users* since it forces them to use the +same function signature without having to call the function directly. In order +for it to truly be a class, the function pointers must specify that a pointer +to the class, known as a *class handle*, be one of the parameters; this makes +it possible for the member functions (also known as *methods*) to have access +to member variables (more commonly known as *fields*) allowing the same +implementation to have multiple *instances*. + +Typically a class can be *overridden* by *child classes* by embedding the +*parent class* in the child class. Then when a method provided by the child +class is called, the child implementation knows that the pointer passed to it is +of a parent contained within the child; because of this, the child can compute +the pointer to itself because the pointer to the parent is always a fixed offset +from the pointer to the child; this offset is the offset of the parent contained +in the child struct. For example: + +.. code-block:: c + + struct shape { + int (*area)(struct shape *this); + }; + + struct rectangle { + struct shape parent; + int length; + int width; + }; + + int rectangle_area(struct shape *this) + { + struct rectangle *self = container_of(this, struct shape, parent); + + return self->length * self->width; + }; + + void rectangle_new(struct rectangle *self, int length, int width) + { + self->parent.area = rectangle_area; + self->length = length; + self->width = width; + } + +In this example (as in most kernel code) the operation of computing the pointer +to the child from the pointer to the parent is done by ``container_of``. + +Faking Classes +~~~~~~~~~~~~~~ + +In order to unit test a piece of code that calls a method in a class, the +behavior of the method must be controllable, otherwise the test ceases to be a +unit test and becomes an integration test. + +A fake just provides an implementation of a piece of code that is different than +what runs in a production instance, but behaves identically from the standpoint +of the callers; this is usually done to replace a dependency that is hard to +deal with, or is slow. + +A good example for this might be implementing a fake EEPROM that just stores the +"contents" in an internal buffer. For example, let's assume we have a class that +represents an EEPROM: + +.. code-block:: c + + struct eeprom { + ssize_t (*read)(struct eeprom *this, size_t offset, char *buffer, size_t count); + ssize_t (*write)(struct eeprom *this, size_t offset, const char *buffer, size_t count); + }; + +And we want to test some code that buffers writes to the EEPROM: + +.. code-block:: c + + struct eeprom_buffer { + ssize_t (*write)(struct eeprom_buffer *this, const char *buffer, size_t count); + int flush(struct eeprom_buffer *this); + size_t flush_count; /* Flushes when buffer exceeds flush_count. */ + }; + + struct eeprom_buffer *new_eeprom_buffer(struct eeprom *eeprom); + void destroy_eeprom_buffer(struct eeprom *eeprom); + +We can easily test this code by *faking out* the underlying EEPROM: + +.. code-block:: c + + struct fake_eeprom { + struct eeprom parent; + char contents[FAKE_EEPROM_CONTENTS_SIZE]; + }; + + ssize_t fake_eeprom_read(struct eeprom *parent, size_t offset, char *buffer, size_t count) + { + struct fake_eeprom *this = container_of(parent, struct fake_eeprom, parent); + + count = min(count, FAKE_EEPROM_CONTENTS_SIZE - offset); + memcpy(buffer, this->contents + offset, count); + + return count; + } + + ssize_t fake_eeprom_write(struct eeprom *this, size_t offset, const char *buffer, size_t count) + { + struct fake_eeprom *this = container_of(parent, struct fake_eeprom, parent); + + count = min(count, FAKE_EEPROM_CONTENTS_SIZE - offset); + memcpy(this->contents + offset, buffer, count); + + return count; + } + + void fake_eeprom_init(struct fake_eeprom *this) + { + this->parent.read = fake_eeprom_read; + this->parent.write = fake_eeprom_write; + memset(this->contents, 0, FAKE_EEPROM_CONTENTS_SIZE); + } + +We can now use it to test ``struct eeprom_buffer``: + +.. code-block:: c + + struct eeprom_buffer_test { + struct fake_eeprom *fake_eeprom; + struct eeprom_buffer *eeprom_buffer; + }; + + static void eeprom_buffer_test_does_not_write_until_flush(struct kunit *test) + { + struct eeprom_buffer_test *ctx = test->priv; + struct eeprom_buffer *eeprom_buffer = ctx->eeprom_buffer; + struct fake_eeprom *fake_eeprom = ctx->fake_eeprom; + char buffer[] = {0xff}; + + eeprom_buffer->flush_count = SIZE_MAX; + + eeprom_buffer->write(eeprom_buffer, buffer, 1); + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0); + + eeprom_buffer->write(eeprom_buffer, buffer, 1); + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0); + + eeprom_buffer->flush(eeprom_buffer); + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff); + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff); + } + + static void eeprom_buffer_test_flushes_after_flush_count_met(struct kunit *test) + { + struct eeprom_buffer_test *ctx = test->priv; + struct eeprom_buffer *eeprom_buffer = ctx->eeprom_buffer; + struct fake_eeprom *fake_eeprom = ctx->fake_eeprom; + char buffer[] = {0xff}; + + eeprom_buffer->flush_count = 2; + + eeprom_buffer->write(eeprom_buffer, buffer, 1); + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0); + + eeprom_buffer->write(eeprom_buffer, buffer, 1); + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff); + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff); + } + + static void eeprom_buffer_test_flushes_increments_of_flush_count(struct kunit *test) + { + struct eeprom_buffer_test *ctx = test->priv; + struct eeprom_buffer *eeprom_buffer = ctx->eeprom_buffer; + struct fake_eeprom *fake_eeprom = ctx->fake_eeprom; + char buffer[] = {0xff, 0xff}; + + eeprom_buffer->flush_count = 2; + + eeprom_buffer->write(eeprom_buffer, buffer, 1); + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0); + + eeprom_buffer->write(eeprom_buffer, buffer, 2); + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff); + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff); + /* Should have only flushed the first two bytes. */ + KUNIT_EXPECT_EQ(test, fake_eeprom->contents[2], 0); + } + + static int eeprom_buffer_test_init(struct kunit *test) + { + struct eeprom_buffer_test *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + ASSERT_NOT_ERR_OR_NULL(test, ctx); + + ctx->fake_eeprom = kunit_kzalloc(test, sizeof(*ctx->fake_eeprom), GFP_KERNEL); + ASSERT_NOT_ERR_OR_NULL(test, ctx->fake_eeprom); + + ctx->eeprom_buffer = new_eeprom_buffer(&ctx->fake_eeprom->parent); + ASSERT_NOT_ERR_OR_NULL(test, ctx->eeprom_buffer); + + test->priv = ctx; + + return 0; + } + + static void eeprom_buffer_test_exit(struct kunit *test) + { + struct eeprom_buffer_test *ctx = test->priv; + + destroy_eeprom_buffer(ctx->eeprom_buffer); + } + From patchwork Wed Nov 28 19:36:32 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703253 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 31A6C15A8 for ; Wed, 28 Nov 2018 19:38:24 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2463929F77 for ; Wed, 28 Nov 2018 19:38:24 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 184CE2B182; Wed, 28 Nov 2018 19:38:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BA16029F77 for ; Wed, 28 Nov 2018 19:38:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729515AbeK2GlF (ORCPT ); Thu, 29 Nov 2018 01:41:05 -0500 Received: from mail-oi1-f201.google.com ([209.85.167.201]:37135 "EHLO mail-oi1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729433AbeK2GlF (ORCPT ); Thu, 29 Nov 2018 01:41:05 -0500 Received: by mail-oi1-f201.google.com with SMTP id s140so14991878oih.4 for ; Wed, 28 Nov 2018 11:38:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=IPwn7FlEmz0efrEk7wG8vLjYbOsIRixs+AynyGzluZM=; b=SZIMHjultMMoNkAU+NKm0Q/ykcqSV2wcCdCQ2/vMQt+uulpgrJjk8EqZBX6ub16Pkg g19cOLdVJnDX7oj/Z2BNZq5PMX2UISleHlzGJt+wcmKsjSWLb+8TtMCB0niSO4kUPM4u cvMzohh2ZhXecljQz/jKbTnc7fiXoRRy/ogLy9AbfvnNPpHav5rFSdnwusdBA4k5Dv4z Qk1vMW7yHhp36jqMNQRAxA3svSQMrwWAuXMGgL3l/RGRHx4N/xcZmruMTa+/ia4CSdl8 WDofrifRzKNIZVBAeupm4HF9GKnYhNXj9nMDxQkm7w8E1JwqLlU5BjT3yESYO4cnUHcp O/iw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=IPwn7FlEmz0efrEk7wG8vLjYbOsIRixs+AynyGzluZM=; b=pTsJeYM8Dq+g/LMPVhzgUY2WbrPFSpSxpO8RYV2mp32ZKu1YOEocwxiX/ofOZlYSMV DZg1FOgXEYg09JjXOlN0L6WKeL5gduftqX2aiJM9uTzNG7FGEGai3Slmj366ylsAFbUU 0jZazrKDQZMZk3Tp/E9g9wmaYDgJF+VONSfBJ/BssMOzylf6Py+muIe+dgsbipQp7nYe KnRfqUk/5DxcLRyuapMyeg3eO5ul1a522yBjmBjW2hd3jD5Vi+qxpDrkRykVHUovOet5 hBNxMcPSG3/AGrJ6Svlr25qOWhPj4mUEUDMY81eOtydLXEqTaklr+SzVnS+m7QvPQcA0 YfoQ== X-Gm-Message-State: AA+aEWabyKWDM5bBT734qr8SU3ivBmKfFr0S6l1q9mkDq/NyoSMrDiaD U8BKmNzFrvdeh7UpP1njLUdWQjaFsJtGjaRzMa3zDQ== X-Google-Smtp-Source: AFSGD/W8Jg0VQrn7yYtZe3lCCeAX7fGs42GSml3tuX/P4bPrDJzlWEQT/Bl579BoWG5KdP/hqVUw56fSKeMorjuCc3P6KQ== X-Received: by 2002:a05:6830:1554:: with SMTP id l20mr4281073otp.21.1543433900445; Wed, 28 Nov 2018 11:38:20 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:32 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-16-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 15/19] MAINTAINERS: add entry for KUnit the unit testing framework From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add myself as maintainer of KUnit, the Linux kernel's unit testing framework. Signed-off-by: Brendan Higgins --- MAINTAINERS | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index b2f710eee67a7..8c9b56dbc9645 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7988,6 +7988,16 @@ S: Maintained F: tools/testing/selftests/ F: Documentation/dev-tools/kselftest* +KERNEL UNIT TESTING FRAMEWORK (KUnit) +M: Brendan Higgins +L: kunit-dev@googlegroups.com +W: https://google.github.io/kunit-docs/third_party/kernel/docs/ +S: Maintained +F: Documentation/kunit/ +F: include/kunit/ +F: kunit/ +F: tools/testing/kunit/ + KERNEL USERMODE HELPER M: "Luis R. Rodriguez" L: linux-kernel@vger.kernel.org From patchwork Wed Nov 28 19:36:33 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703257 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ED89F15A8 for ; Wed, 28 Nov 2018 19:38:26 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DFE4629F77 for ; Wed, 28 Nov 2018 19:38:26 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D3F3D2B182; Wed, 28 Nov 2018 19:38:26 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8722C29F77 for ; Wed, 28 Nov 2018 19:38:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729514AbeK2GlI (ORCPT ); Thu, 29 Nov 2018 01:41:08 -0500 Received: from mail-qt1-f202.google.com ([209.85.160.202]:52351 "EHLO mail-qt1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729529AbeK2GlI (ORCPT ); Thu, 29 Nov 2018 01:41:08 -0500 Received: by mail-qt1-f202.google.com with SMTP id w15so25045110qtk.19 for ; Wed, 28 Nov 2018 11:38:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=ZXeVKB3z4xYEMo//ei2CNGIhvC0pSeK1m9ZjHQ2diMw=; b=Rb9/h0zVwkkHN++1m58koJGyJMM2Qj3zSthOhQpGBZZ/uJowc3xBQITml1f4/xyZJa B3DhIiwi2xrFoi7Lg1DCPrR7jql0Y5iiGW96UAdIv/l7a72tOaIu/2RUTYsVhdx+iX4R oQTLd2T63dQ5oa12uvSnjG9zKS1z6OdomrilynAIPtjXmcHqnpGxebSh6XpLGu/l3GAs ylLroOjj6q8fZ3fFnQzEjEyoaI8rLhh6rTtvDOqiMTzpL9bPKHMnD6jP9jkDdxo3USg+ v3DuICgLArR4aAaeTCE1kY5NXdiff1auhDnsM8/YzuuEHTJRs7rDohz5swcips2xZ6Ov CFTA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=ZXeVKB3z4xYEMo//ei2CNGIhvC0pSeK1m9ZjHQ2diMw=; b=DEHsQAzeSWvYcGMWCvy9Lrta3QBxMJvJfxqYkHr4ycDVTD2HA2kiP6GelNmpyS/4Ux hYkdkgv2FD8qgGYox/gmgSxnk36EqF6VFK6mtqv6WF7eprgEm21qbQb8c4wTxbERA3Vq ONisd3KOBwbT06Z6R4Ml0U3Vi9KssjEYbJQc3THnRp2qXjep4F2dOAksgcF9SuR5k1Bj YXFeUaB+11b+oD7J/ASL2zoSRZ5VespRqRPdK+Ttt62nVR2+aDAApDw/hDbfwnyjrmBI PDwIDtyjMes3IoVA5Ggfxd0VAASRAroLMBHvjiKs4L4kru4cCrYFQ8oLmECIRyiAVPeQ oQ8w== X-Gm-Message-State: AA+aEWa1NXVLHwB1yzfw0nznEz3nmod+FNxeybiOfG+ryTfzUAJ7zZgE 4e9+gRLMJJUXaBtsd4QmgNjoi17gA2fhU93hczUJjQ== X-Google-Smtp-Source: AFSGD/WNRv4uLe4qzapo7byn71/CSO8pn+dusHGBhoYU/dteolVxh/z9q17isRjU/4o3tgV/+62KxUnN0F0B3WRn+xp75A== X-Received: by 2002:a37:4916:: with SMTP id w22mr4361663qka.6.1543433903147; Wed, 28 Nov 2018 11:38:23 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:33 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-17-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 16/19] arch: um: make UML unflatten device tree when testing From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Make UML unflatten any present device trees when running KUnit tests. Signed-off-by: Brendan Higgins --- arch/um/kernel/um_arch.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index a818ccef30ca2..bd58ae3bf4148 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -347,6 +348,9 @@ void __init setup_arch(char **cmdline_p) read_initrd(); paging_init(); +#if IS_ENABLED(CONFIG_OF_UNITTEST) + unflatten_device_tree(); +#endif strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); *cmdline_p = command_line; setup_hostinfo(host_info, sizeof host_info); From patchwork Wed Nov 28 19:36:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703265 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 806CF16B1 for ; Wed, 28 Nov 2018 19:38:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6A1DC2BC87 for ; Wed, 28 Nov 2018 19:38:37 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5AAB92BDA2; Wed, 28 Nov 2018 19:38:37 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7167F28A8E for ; Wed, 28 Nov 2018 19:38:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729537AbeK2GlP (ORCPT ); Thu, 29 Nov 2018 01:41:15 -0500 Received: from mail-qk1-f201.google.com ([209.85.222.201]:41977 "EHLO mail-qk1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729540AbeK2GlL (ORCPT ); Thu, 29 Nov 2018 01:41:11 -0500 Received: by mail-qk1-f201.google.com with SMTP id n68so27151638qkn.8 for ; Wed, 28 Nov 2018 11:38:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=n3sRFfgHS0+ehQlu28YzkXASfQRHI1FBWnjtKJRIVNc=; b=jv5rPw2vzQyR6i4oGsPi83/nd4YPp620f4LEfflNZa0SDr5mtoT6t2BHkGz9jk2B3v xaGkOpgW++MeyiYvNTsVbXMdMAwmBkFDdzyY8G3yD0HDyoRrqmFsjscL4rd4UPx1D52A 60ltqe2Lzu8B7oG7PhURgdQoc8CJhScO0w/+HV9TKa8aOyODnZgFQ7vw2xxmLVRZs0v0 NWKSPsmmzmdh4hSij8OUPBg0wApkEZaIXu7btDKndWkqmx3WFyezWdOfgdbtxqUIeIlm w9If/qmd+mS1JVYlSRy7Pxmht2/zRVZCtlw6Ay9cGanBNgRg7KhNjqnHI7EiUgUC2+4s lMpQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=n3sRFfgHS0+ehQlu28YzkXASfQRHI1FBWnjtKJRIVNc=; b=g/TgzRo4LlQB8ytFmPki5grDXtckVytyu7ZQTMjlSsHS6w3Pz25CcQQykkjBo/qpxI p1vB5pb2wC7bTv0UQY1CBcwUq4MDm7vplGyEbsxMuErO+ul4NccWTTaq2aJajsvY0fcE vdSlGQxSUpAfKVa79ignoErZGgdsipZEtx/tLKP1/Hhq3xOWcIqZCw2JyOoC5w27dJ8c rPdRir215Of9LIi5PbGdiU4co3Kr7drF0gprodMHjRw4OXB6dTT2QvD8Nn4SC0iufzGa M9l7Q3LN3osEP8RUdY45Lthnb4OyzmLqIwLeSbD/OUiEmZ3gf+cfj8oKAfbe5E3YmvLW eahA== X-Gm-Message-State: AA+aEWYIhBj7YM35y/5+FWWmYcQlrBIQVAO8cDbm9evxHE1xIloUkZ/p 3PP5CR9KlPmdyVuLA1AaCPTFqpvPjEwW3MmPVkuEVw== X-Google-Smtp-Source: AFSGD/UJQnZ/mXZgZxiTRw1yCJ+aQT8SSMjvVEEN1Q+XIJfhm8nBSRiz3g2ZvaX/atiVqnT8/gG5gV0fnJMmGisqQk87WA== X-Received: by 2002:ac8:9d:: with SMTP id c29mr4533913qtg.50.1543433905633; Wed, 28 Nov 2018 11:38:25 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:34 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-18-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 17/19] of: unittest: migrate tests to run on KUnit From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Migrate tests without any cleanup, or modifying test logic in anyway to run under KUnit using the KUnit expectation and assertion API. Signed-off-by: Brendan Higgins --- drivers/of/Kconfig | 1 + drivers/of/unittest.c | 1405 ++++++++++++++++++++++------------------- 2 files changed, 752 insertions(+), 654 deletions(-) diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index ad3fcad4d75b8..f309399deac20 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -15,6 +15,7 @@ if OF config OF_UNITTEST bool "Device Tree runtime unit tests" depends on !SPARC + depends on KUNIT select IRQ_DOMAIN select OF_EARLY_FLATTREE select OF_RESOLVE diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 41b49716ac75f..a5ef44730ffdb 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -26,186 +26,187 @@ #include +#include + #include "of_private.h" -static struct unittest_results { - int passed; - int failed; -} unittest_results; - -#define unittest(result, fmt, ...) ({ \ - bool failed = !(result); \ - if (failed) { \ - unittest_results.failed++; \ - pr_err("FAIL %s():%i " fmt, __func__, __LINE__, ##__VA_ARGS__); \ - } else { \ - unittest_results.passed++; \ - pr_debug("pass %s():%i\n", __func__, __LINE__); \ - } \ - failed; \ -}) - -static void __init of_unittest_find_node_by_name(void) +static void of_unittest_find_node_by_name(struct kunit *test) { struct device_node *np; const char *options, *name; np = of_find_node_by_path("/testcase-data"); name = kasprintf(GFP_KERNEL, "%pOF", np); - unittest(np && !strcmp("/testcase-data", name), - "find /testcase-data failed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "/testcase-data", name, + "find /testcase-data failed\n"); of_node_put(np); kfree(name); /* Test if trailing '/' works */ - np = of_find_node_by_path("/testcase-data/"); - unittest(!np, "trailing '/' on /testcase-data/ should fail\n"); + KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("/testcase-data/"), NULL, + "trailing '/' on /testcase-data/ should fail\n"); np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); name = kasprintf(GFP_KERNEL, "%pOF", np); - unittest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", name), - "find /testcase-data/phandle-tests/consumer-a failed\n"); + KUNIT_EXPECT_STREQ_MSG(test, + "/testcase-data/phandle-tests/consumer-a", name, + "find /testcase-data/phandle-tests/consumer-a failed\n"); of_node_put(np); kfree(name); np = of_find_node_by_path("testcase-alias"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); name = kasprintf(GFP_KERNEL, "%pOF", np); - unittest(np && !strcmp("/testcase-data", name), - "find testcase-alias failed\n"); + KUNIT_EXPECT_STREQ_MSG(test, "/testcase-data", name, + "find testcase-alias failed\n"); of_node_put(np); kfree(name); /* Test if trailing '/' works on aliases */ - np = of_find_node_by_path("testcase-alias/"); - unittest(!np, "trailing '/' on testcase-alias/ should fail\n"); + KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("testcase-alias/"), NULL, + "trailing '/' on testcase-alias/ should fail\n"); np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); name = kasprintf(GFP_KERNEL, "%pOF", np); - unittest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", name), - "find testcase-alias/phandle-tests/consumer-a failed\n"); + KUNIT_EXPECT_STREQ_MSG(test, + "/testcase-data/phandle-tests/consumer-a", name, + "find testcase-alias/phandle-tests/consumer-a failed\n"); of_node_put(np); kfree(name); - np = of_find_node_by_path("/testcase-data/missing-path"); - unittest(!np, "non-existent path returned node %pOF\n", np); + KUNIT_EXPECT_EQ_MSG(test, + of_find_node_by_path("/testcase-data/missing-path"), + NULL, + "non-existent path returned node %pOF\n", np); of_node_put(np); - np = of_find_node_by_path("missing-alias"); - unittest(!np, "non-existent alias returned node %pOF\n", np); + KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("missing-alias"), NULL, + "non-existent alias returned node %pOF\n", np); of_node_put(np); - np = of_find_node_by_path("testcase-alias/missing-path"); - unittest(!np, "non-existent alias with relative path returned node %pOF\n", np); + KUNIT_EXPECT_EQ_MSG(test, + of_find_node_by_path("testcase-alias/missing-path"), + NULL, + "non-existent alias with relative path returned node %pOF\n", + np); of_node_put(np); np = of_find_node_opts_by_path("/testcase-data:testoption", &options); - unittest(np && !strcmp("testoption", options), - "option path test failed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "testoption", options, + "option path test failed\n"); of_node_put(np); np = of_find_node_opts_by_path("/testcase-data:test/option", &options); - unittest(np && !strcmp("test/option", options), - "option path test, subcase #1 failed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "test/option", options, + "option path test, subcase #1 failed\n"); of_node_put(np); np = of_find_node_opts_by_path("/testcase-data/testcase-device1:test/option", &options); - unittest(np && !strcmp("test/option", options), - "option path test, subcase #2 failed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "test/option", options, + "option path test, subcase #2 failed\n"); of_node_put(np); np = of_find_node_opts_by_path("/testcase-data:testoption", NULL); - unittest(np, "NULL option path test failed\n"); + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, np, + "NULL option path test failed\n"); of_node_put(np); np = of_find_node_opts_by_path("testcase-alias:testaliasoption", &options); - unittest(np && !strcmp("testaliasoption", options), - "option alias path test failed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "testaliasoption", options, + "option alias path test failed\n"); of_node_put(np); np = of_find_node_opts_by_path("testcase-alias:test/alias/option", &options); - unittest(np && !strcmp("test/alias/option", options), - "option alias path test, subcase #1 failed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "test/alias/option", options, + "option alias path test, subcase #1 failed\n"); of_node_put(np); np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL); - unittest(np, "NULL option alias path test failed\n"); + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, np, + "NULL option alias path test failed\n"); of_node_put(np); options = "testoption"; np = of_find_node_opts_by_path("testcase-alias", &options); - unittest(np && !options, "option clearing test failed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_EQ_MSG(test, options, NULL, + "option clearing test failed\n"); of_node_put(np); options = "testoption"; np = of_find_node_opts_by_path("/", &options); - unittest(np && !options, "option clearing root node test failed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_EQ_MSG(test, options, NULL, + "option clearing root node test failed\n"); of_node_put(np); } -static void __init of_unittest_dynamic(void) +static void of_unittest_dynamic(struct kunit *test) { struct device_node *np; struct property *prop; np = of_find_node_by_path("/testcase-data"); - if (!np) { - pr_err("missing testcase data\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); /* Array of 4 properties for the purpose of testing */ prop = kcalloc(4, sizeof(*prop), GFP_KERNEL); - if (!prop) { - unittest(0, "kzalloc() failed\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prop); /* Add a new property - should pass*/ prop->name = "new-property"; prop->value = "new-property-data"; prop->length = strlen(prop->value) + 1; - unittest(of_add_property(np, prop) == 0, "Adding a new property failed\n"); + KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop), 0, + "Adding a new property failed\n"); /* Try to add an existing property - should fail */ prop++; prop->name = "new-property"; prop->value = "new-property-data-should-fail"; prop->length = strlen(prop->value) + 1; - unittest(of_add_property(np, prop) != 0, - "Adding an existing property should have failed\n"); + KUNIT_EXPECT_NE_MSG(test, of_add_property(np, prop), 0, + "Adding an existing property should have failed\n"); /* Try to modify an existing property - should pass */ prop->value = "modify-property-data-should-pass"; prop->length = strlen(prop->value) + 1; - unittest(of_update_property(np, prop) == 0, - "Updating an existing property should have passed\n"); + KUNIT_EXPECT_EQ_MSG(test, of_update_property(np, prop), 0, + "Updating an existing property should have passed\n"); /* Try to modify non-existent property - should pass*/ prop++; prop->name = "modify-property"; prop->value = "modify-missing-property-data-should-pass"; prop->length = strlen(prop->value) + 1; - unittest(of_update_property(np, prop) == 0, - "Updating a missing property should have passed\n"); + KUNIT_EXPECT_EQ_MSG(test, of_update_property(np, prop), 0, + "Updating a missing property should have passed\n"); /* Remove property - should pass */ - unittest(of_remove_property(np, prop) == 0, - "Removing a property should have passed\n"); + KUNIT_EXPECT_EQ_MSG(test, of_remove_property(np, prop), 0, + "Removing a property should have passed\n"); /* Adding very large property - should pass */ prop++; prop->name = "large-property-PAGE_SIZEx8"; prop->length = PAGE_SIZE * 8; prop->value = kzalloc(prop->length, GFP_KERNEL); - unittest(prop->value != NULL, "Unable to allocate large buffer\n"); - if (prop->value) - unittest(of_add_property(np, prop) == 0, - "Adding a large property should have passed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prop->value); + KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop), 0, + "Adding a large property should have passed\n"); } -static int __init of_unittest_check_node_linkage(struct device_node *np) +static int of_unittest_check_node_linkage(struct device_node *np) { struct device_node *child; int count = 0, rc; @@ -230,27 +231,29 @@ static int __init of_unittest_check_node_linkage(struct device_node *np) return rc; } -static void __init of_unittest_check_tree_linkage(void) +static void of_unittest_check_tree_linkage(struct kunit *test) { struct device_node *np; int allnode_count = 0, child_count; - if (!of_root) - return; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, of_root); for_each_of_allnodes(np) allnode_count++; child_count = of_unittest_check_node_linkage(of_root); - unittest(child_count > 0, "Device node data structure is corrupted\n"); - unittest(child_count == allnode_count, - "allnodes list size (%i) doesn't match sibling lists size (%i)\n", - allnode_count, child_count); + KUNIT_EXPECT_GT_MSG(test, child_count, 0, + "Device node data structure is corrupted\n"); + KUNIT_EXPECT_EQ_MSG(test, child_count, allnode_count, + "allnodes list size (%i) doesn't match sibling lists size (%i)\n", + allnode_count, child_count); pr_debug("allnodes list size (%i); sibling lists size (%i)\n", allnode_count, child_count); } -static void __init of_unittest_printf_one(struct device_node *np, const char *fmt, - const char *expected) +static void of_unittest_printf_one(struct kunit *test, + struct device_node *np, + const char *fmt, + const char *expected) { unsigned char *buf; int buf_size; @@ -266,9 +269,12 @@ static void __init of_unittest_printf_one(struct device_node *np, const char *fm size = snprintf(buf, buf_size - 2, fmt, np); /* use strcmp() instead of strncmp() here to be absolutely sure strings match */ - unittest((strcmp(buf, expected) == 0) && (buf[size+1] == 0xff), - "sprintf failed; fmt='%s' expected='%s' rslt='%s'\n", - fmt, expected, buf); + KUNIT_EXPECT_STREQ_MSG(test, buf, expected, + "sprintf failed; fmt='%s' expected='%s' rslt='%s'\n", + fmt, expected, buf); + KUNIT_EXPECT_EQ_MSG(test, buf[size+1], 0xff, + "sprintf failed; fmt='%s' expected='%s' rslt='%s'\n", + fmt, expected, buf); /* Make sure length limits work */ size++; @@ -276,40 +282,43 @@ static void __init of_unittest_printf_one(struct device_node *np, const char *fm /* Clear the buffer, and make sure it works correctly still */ memset(buf, 0xff, buf_size); snprintf(buf, size+1, fmt, np); - unittest(strncmp(buf, expected, size) == 0 && (buf[size+1] == 0xff), - "snprintf failed; size=%i fmt='%s' expected='%s' rslt='%s'\n", - size, fmt, expected, buf); + KUNIT_EXPECT_STREQ_MSG(test, buf, expected, + "snprintf failed; size=%i fmt='%s' expected='%s' rslt='%s'\n", + size, fmt, expected, buf); + KUNIT_EXPECT_EQ_MSG(test, buf[size+1], 0xff, + "snprintf failed; size=%i fmt='%s' expected='%s' rslt='%s'\n", + size, fmt, expected, buf); } kfree(buf); } -static void __init of_unittest_printf(void) +static void of_unittest_printf(struct kunit *test) { struct device_node *np; const char *full_name = "/testcase-data/platform-tests/test-device@1/dev@100"; char phandle_str[16] = ""; np = of_find_node_by_path(full_name); - if (!np) { - unittest(np, "testcase data missing\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); num_to_str(phandle_str, sizeof(phandle_str), np->phandle, 0); - of_unittest_printf_one(np, "%pOF", full_name); - of_unittest_printf_one(np, "%pOFf", full_name); - of_unittest_printf_one(np, "%pOFp", phandle_str); - of_unittest_printf_one(np, "%pOFP", "dev@100"); - of_unittest_printf_one(np, "ABC %pOFP ABC", "ABC dev@100 ABC"); - of_unittest_printf_one(np, "%10pOFP", " dev@100"); - of_unittest_printf_one(np, "%-10pOFP", "dev@100 "); - of_unittest_printf_one(of_root, "%pOFP", "/"); - of_unittest_printf_one(np, "%pOFF", "----"); - of_unittest_printf_one(np, "%pOFPF", "dev@100:----"); - of_unittest_printf_one(np, "%pOFPFPc", "dev@100:----:dev@100:test-sub-device"); - of_unittest_printf_one(np, "%pOFc", "test-sub-device"); - of_unittest_printf_one(np, "%pOFC", + of_unittest_printf_one(test, np, "%pOF", full_name); + of_unittest_printf_one(test, np, "%pOFf", full_name); + of_unittest_printf_one(test, np, "%pOFp", phandle_str); + of_unittest_printf_one(test, np, "%pOFP", "dev@100"); + of_unittest_printf_one(test, np, "ABC %pOFP ABC", "ABC dev@100 ABC"); + of_unittest_printf_one(test, np, "%10pOFP", " dev@100"); + of_unittest_printf_one(test, np, "%-10pOFP", "dev@100 "); + of_unittest_printf_one(test, of_root, "%pOFP", "/"); + of_unittest_printf_one(test, np, "%pOFF", "----"); + of_unittest_printf_one(test, np, "%pOFPF", "dev@100:----"); + of_unittest_printf_one(test, + np, + "%pOFPFPc", + "dev@100:----:dev@100:test-sub-device"); + of_unittest_printf_one(test, np, "%pOFc", "test-sub-device"); + of_unittest_printf_one(test, np, "%pOFC", "\"test-sub-device\",\"test-compat2\",\"test-compat3\""); } @@ -319,7 +328,7 @@ struct node_hash { }; static DEFINE_HASHTABLE(phandle_ht, 8); -static void __init of_unittest_check_phandles(void) +static void of_unittest_check_phandles(struct kunit *test) { struct device_node *np; struct node_hash *nh; @@ -331,24 +340,25 @@ static void __init of_unittest_check_phandles(void) continue; hash_for_each_possible(phandle_ht, nh, node, np->phandle) { + KUNIT_EXPECT_NE_MSG(test, nh->np->phandle, np->phandle, + "Duplicate phandle! %i used by %pOF and %pOF\n", + np->phandle, nh->np, np); if (nh->np->phandle == np->phandle) { - pr_info("Duplicate phandle! %i used by %pOF and %pOF\n", - np->phandle, nh->np, np); dup_count++; break; } } nh = kzalloc(sizeof(*nh), GFP_KERNEL); - if (WARN_ON(!nh)) - return; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, nh); nh->np = np; hash_add(phandle_ht, &nh->node, np->phandle); phandle_count++; } - unittest(dup_count == 0, "Found %i duplicates in %i phandles\n", - dup_count, phandle_count); + KUNIT_EXPECT_EQ_MSG(test, dup_count, 0, + "Found %i duplicates in %i phandles\n", + dup_count, phandle_count); /* Clean up */ hash_for_each_safe(phandle_ht, i, tmp, nh, node) { @@ -357,20 +367,22 @@ static void __init of_unittest_check_phandles(void) } } -static void __init of_unittest_parse_phandle_with_args(void) +static void of_unittest_parse_phandle_with_args(struct kunit *test) { struct device_node *np; struct of_phandle_args args; - int i, rc; + int i, rc = 0; np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); - if (!np) { - pr_err("missing testcase data\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - rc = of_count_phandle_with_args(np, "phandle-list", "#phandle-cells"); - unittest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc); + KUNIT_EXPECT_EQ_MSG(test, + of_count_phandle_with_args(np, + "phandle-list", + "#phandle-cells"), + 7, + "of_count_phandle_with_args() returned %i, expected 7\n", + rc); for (i = 0; i < 8; i++) { bool passed = true; @@ -423,81 +435,90 @@ static void __init of_unittest_parse_phandle_with_args(void) passed = false; } - unittest(passed, "index %i - data error on node %pOF rc=%i\n", - i, args.np, rc); + KUNIT_EXPECT_TRUE_MSG(test, passed, + "index %i - data error on node %pOF rc=%i\n", + i, args.np, rc); } /* Check for missing list property */ - rc = of_parse_phandle_with_args(np, "phandle-list-missing", - "#phandle-cells", 0, &args); - unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc); - rc = of_count_phandle_with_args(np, "phandle-list-missing", - "#phandle-cells"); - unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc); + KUNIT_EXPECT_EQ(test, + of_parse_phandle_with_args(np, + "phandle-list-missing", + "#phandle-cells", + 0, &args), + -ENOENT); + KUNIT_EXPECT_EQ(test, + of_count_phandle_with_args(np, + "phandle-list-missing", + "#phandle-cells"), + -ENOENT); /* Check for missing cells property */ - rc = of_parse_phandle_with_args(np, "phandle-list", - "#phandle-cells-missing", 0, &args); - unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); - rc = of_count_phandle_with_args(np, "phandle-list", - "#phandle-cells-missing"); - unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + KUNIT_EXPECT_EQ(test, + of_parse_phandle_with_args(np, + "phandle-list", + "#phandle-cells-missing", + 0, &args), + -EINVAL); + KUNIT_EXPECT_EQ(test, + of_count_phandle_with_args(np, + "phandle-list", + "#phandle-cells-missing"), + -EINVAL); /* Check for bad phandle in list */ - rc = of_parse_phandle_with_args(np, "phandle-list-bad-phandle", - "#phandle-cells", 0, &args); - unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); - rc = of_count_phandle_with_args(np, "phandle-list-bad-phandle", - "#phandle-cells"); - unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + KUNIT_EXPECT_EQ(test, + of_parse_phandle_with_args(np, + "phandle-list-bad-phandle", + "#phandle-cells", + 0, &args), + -EINVAL); + KUNIT_EXPECT_EQ(test, + of_count_phandle_with_args(np, + "phandle-list-bad-phandle", + "#phandle-cells"), + -EINVAL); /* Check for incorrectly formed argument list */ - rc = of_parse_phandle_with_args(np, "phandle-list-bad-args", - "#phandle-cells", 1, &args); - unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); - rc = of_count_phandle_with_args(np, "phandle-list-bad-args", - "#phandle-cells"); - unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + KUNIT_EXPECT_EQ(test, + of_parse_phandle_with_args(np, + "phandle-list-bad-args", + "#phandle-cells", + 1, &args), + -EINVAL); + KUNIT_EXPECT_EQ(test, + of_count_phandle_with_args(np, + "phandle-list-bad-args", + "#phandle-cells"), + -EINVAL); } -static void __init of_unittest_parse_phandle_with_args_map(void) +static void of_unittest_parse_phandle_with_args_map(struct kunit *test) { struct device_node *np, *p0, *p1, *p2, *p3; struct of_phandle_args args; int i, rc; np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-b"); - if (!np) { - pr_err("missing testcase data\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); p0 = of_find_node_by_path("/testcase-data/phandle-tests/provider0"); - if (!p0) { - pr_err("missing testcase data\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p0); p1 = of_find_node_by_path("/testcase-data/phandle-tests/provider1"); - if (!p1) { - pr_err("missing testcase data\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p1); p2 = of_find_node_by_path("/testcase-data/phandle-tests/provider2"); - if (!p2) { - pr_err("missing testcase data\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p2); p3 = of_find_node_by_path("/testcase-data/phandle-tests/provider3"); - if (!p3) { - pr_err("missing testcase data\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p3); - rc = of_count_phandle_with_args(np, "phandle-list", "#phandle-cells"); - unittest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc); + KUNIT_EXPECT_EQ(test, + of_count_phandle_with_args(np, + "phandle-list", + "#phandle-cells"), + 7); for (i = 0; i < 8; i++) { bool passed = true; @@ -554,117 +575,214 @@ static void __init of_unittest_parse_phandle_with_args_map(void) passed = false; } - unittest(passed, "index %i - data error on node %s rc=%i\n", - i, args.np->full_name, rc); + KUNIT_EXPECT_TRUE_MSG(test, passed, + "index %i - data error on node %s rc=%i\n", + i, args.np->full_name, rc); } /* Check for missing list property */ - rc = of_parse_phandle_with_args_map(np, "phandle-list-missing", - "phandle", 0, &args); - unittest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc); + KUNIT_EXPECT_EQ(test, + of_parse_phandle_with_args_map(np, + "phandle-list-missing", + "phandle", + 0, &args), + -ENOENT); /* Check for missing cells,map,mask property */ - rc = of_parse_phandle_with_args_map(np, "phandle-list", - "phandle-missing", 0, &args); - unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + KUNIT_EXPECT_EQ(test, + of_parse_phandle_with_args_map(np, + "phandle-list", + "phandle-missing", + 0, &args), + -EINVAL); /* Check for bad phandle in list */ - rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-phandle", - "phandle", 0, &args); - unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + KUNIT_EXPECT_EQ(test, + of_parse_phandle_with_args_map(np, + "phandle-list-bad-phandle", + "phandle", + 0, &args), + -EINVAL); /* Check for incorrectly formed argument list */ - rc = of_parse_phandle_with_args_map(np, "phandle-list-bad-args", - "phandle", 1, &args); - unittest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); + KUNIT_EXPECT_EQ(test, + of_parse_phandle_with_args_map(np, + "phandle-list-bad-args", + "phandle", + 1, &args), + -EINVAL); } -static void __init of_unittest_property_string(void) +static void of_unittest_property_string(struct kunit *test) { const char *strings[4]; struct device_node *np; int rc; np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); - if (!np) { - pr_err("No testcase data in device tree\n"); - return; - } - - rc = of_property_match_string(np, "phandle-list-names", "first"); - unittest(rc == 0, "first expected:0 got:%i\n", rc); - rc = of_property_match_string(np, "phandle-list-names", "second"); - unittest(rc == 1, "second expected:1 got:%i\n", rc); - rc = of_property_match_string(np, "phandle-list-names", "third"); - unittest(rc == 2, "third expected:2 got:%i\n", rc); - rc = of_property_match_string(np, "phandle-list-names", "fourth"); - unittest(rc == -ENODATA, "unmatched string; rc=%i\n", rc); - rc = of_property_match_string(np, "missing-property", "blah"); - unittest(rc == -EINVAL, "missing property; rc=%i\n", rc); - rc = of_property_match_string(np, "empty-property", "blah"); - unittest(rc == -ENODATA, "empty property; rc=%i\n", rc); - rc = of_property_match_string(np, "unterminated-string", "blah"); - unittest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + + KUNIT_EXPECT_EQ(test, + of_property_match_string(np, + "phandle-list-names", + "first"), + 0); + KUNIT_EXPECT_EQ(test, + of_property_match_string(np, + "phandle-list-names", + "second"), + 1); + KUNIT_EXPECT_EQ(test, + of_property_match_string(np, + "phandle-list-names", + "third"), + 2); + KUNIT_EXPECT_EQ_MSG(test, + of_property_match_string(np, + "phandle-list-names", + "fourth"), + -ENODATA, + "unmatched string"); + KUNIT_EXPECT_EQ_MSG(test, + of_property_match_string(np, + "missing-property", + "blah"), + -EINVAL, + "missing property"); + KUNIT_EXPECT_EQ_MSG(test, + of_property_match_string(np, + "empty-property", + "blah"), + -ENODATA, + "empty property"); + KUNIT_EXPECT_EQ_MSG(test, + of_property_match_string(np, + "unterminated-string", + "blah"), + -EILSEQ, + "unterminated string"); /* of_property_count_strings() tests */ - rc = of_property_count_strings(np, "string-property"); - unittest(rc == 1, "Incorrect string count; rc=%i\n", rc); - rc = of_property_count_strings(np, "phandle-list-names"); - unittest(rc == 3, "Incorrect string count; rc=%i\n", rc); - rc = of_property_count_strings(np, "unterminated-string"); - unittest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc); - rc = of_property_count_strings(np, "unterminated-string-list"); - unittest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc); + KUNIT_EXPECT_EQ(test, + of_property_count_strings(np, "string-property"), + 1); + KUNIT_EXPECT_EQ(test, + of_property_count_strings(np, "phandle-list-names"), + 3); + KUNIT_EXPECT_EQ_MSG(test, + of_property_count_strings(np, + "unterminated-string"), + -EILSEQ, + "unterminated string"); + KUNIT_EXPECT_EQ_MSG(test, + of_property_count_strings( + np, + "unterminated-string-list"), + -EILSEQ, + "unterminated string array"); /* of_property_read_string_index() tests */ rc = of_property_read_string_index(np, "string-property", 0, strings); - unittest(rc == 0 && !strcmp(strings[0], "foobar"), "of_property_read_string_index() failure; rc=%i\n", rc); + KUNIT_ASSERT_EQ(test, rc, 0); + KUNIT_EXPECT_STREQ(test, strings[0], "foobar"); strings[0] = NULL; rc = of_property_read_string_index(np, "string-property", 1, strings); - unittest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc); - rc = of_property_read_string_index(np, "phandle-list-names", 0, strings); - unittest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc); - rc = of_property_read_string_index(np, "phandle-list-names", 1, strings); - unittest(rc == 0 && !strcmp(strings[0], "second"), "of_property_read_string_index() failure; rc=%i\n", rc); - rc = of_property_read_string_index(np, "phandle-list-names", 2, strings); - unittest(rc == 0 && !strcmp(strings[0], "third"), "of_property_read_string_index() failure; rc=%i\n", rc); + KUNIT_EXPECT_EQ(test, rc, -ENODATA); + KUNIT_EXPECT_EQ(test, strings[0], NULL); + rc = of_property_read_string_index(np, + "phandle-list-names", + 0, + strings); + KUNIT_ASSERT_EQ(test, rc, 0); + KUNIT_EXPECT_STREQ(test, strings[0], "first"); + rc = of_property_read_string_index(np, + "phandle-list-names", + 1, + strings); + KUNIT_ASSERT_EQ(test, rc, 0); + KUNIT_EXPECT_STREQ(test, strings[0], "second"); + rc = of_property_read_string_index(np, + "phandle-list-names", + 2, + strings); + KUNIT_ASSERT_EQ(test, rc, 0); + KUNIT_EXPECT_STREQ(test, strings[0], "third"); strings[0] = NULL; - rc = of_property_read_string_index(np, "phandle-list-names", 3, strings); - unittest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc); + rc = of_property_read_string_index(np, + "phandle-list-names", 3, strings); + KUNIT_EXPECT_EQ(test, rc, -ENODATA); + KUNIT_EXPECT_EQ(test, strings[0], NULL); strings[0] = NULL; - rc = of_property_read_string_index(np, "unterminated-string", 0, strings); - unittest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc); - rc = of_property_read_string_index(np, "unterminated-string-list", 0, strings); - unittest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc); + rc = of_property_read_string_index(np, + "unterminated-string", + 0, + strings); + KUNIT_EXPECT_EQ(test, rc, -EILSEQ); + KUNIT_EXPECT_EQ(test, strings[0], NULL); + rc = of_property_read_string_index(np, + "unterminated-string-list", + 0, + strings); + KUNIT_ASSERT_EQ(test, rc, 0); + KUNIT_EXPECT_STREQ(test, strings[0], "first"); strings[0] = NULL; - rc = of_property_read_string_index(np, "unterminated-string-list", 2, strings); /* should fail */ - unittest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc); - strings[1] = NULL; + rc = of_property_read_string_index(np, + "unterminated-string-list", + 2, + strings); /* should fail */ + KUNIT_EXPECT_EQ(test, rc, -EILSEQ); + KUNIT_EXPECT_EQ(test, strings[0], NULL); /* of_property_read_string_array() tests */ - rc = of_property_read_string_array(np, "string-property", strings, 4); - unittest(rc == 1, "Incorrect string count; rc=%i\n", rc); - rc = of_property_read_string_array(np, "phandle-list-names", strings, 4); - unittest(rc == 3, "Incorrect string count; rc=%i\n", rc); - rc = of_property_read_string_array(np, "unterminated-string", strings, 4); - unittest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc); + strings[1] = NULL; + KUNIT_EXPECT_EQ(test, + of_property_read_string_array(np, + "string-property", + strings, 4), + 1); + KUNIT_EXPECT_EQ(test, + of_property_read_string_array(np, + "phandle-list-names", + strings, 4), + 3); + KUNIT_EXPECT_EQ_MSG(test, + of_property_read_string_array(np, + "unterminated-string", + strings, 4), + -EILSEQ, + "unterminated string"); /* -- An incorrectly formed string should cause a failure */ - rc = of_property_read_string_array(np, "unterminated-string-list", strings, 4); - unittest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc); + KUNIT_EXPECT_EQ_MSG(test, + of_property_read_string_array( + np, + "unterminated-string-list", + strings, + 4), + -EILSEQ, + "unterminated string array"); /* -- parsing the correctly formed strings should still work: */ strings[2] = NULL; - rc = of_property_read_string_array(np, "unterminated-string-list", strings, 2); - unittest(rc == 2 && strings[2] == NULL, "of_property_read_string_array() failure; rc=%i\n", rc); + rc = of_property_read_string_array(np, + "unterminated-string-list", + strings, + 2); + KUNIT_EXPECT_EQ(test, rc, 2); + KUNIT_EXPECT_EQ(test, strings[2], NULL); strings[1] = NULL; - rc = of_property_read_string_array(np, "phandle-list-names", strings, 1); - unittest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]); + rc = of_property_read_string_array(np, + "phandle-list-names", + strings, + 1); + KUNIT_ASSERT_EQ(test, rc, 1); + KUNIT_EXPECT_EQ_MSG(test, strings[1], NULL, + "Overwrote end of string array"); } #define propcmp(p1, p2) (((p1)->length == (p2)->length) && \ (p1)->value && (p2)->value && \ !memcmp((p1)->value, (p2)->value, (p1)->length) && \ !strcmp((p1)->name, (p2)->name)) -static void __init of_unittest_property_copy(void) +static void of_unittest_property_copy(struct kunit *test) { #ifdef CONFIG_OF_DYNAMIC struct property p1 = { .name = "p1", .length = 0, .value = "" }; @@ -672,20 +790,24 @@ static void __init of_unittest_property_copy(void) struct property *new; new = __of_prop_dup(&p1, GFP_KERNEL); - unittest(new && propcmp(&p1, new), "empty property didn't copy correctly\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new); + KUNIT_EXPECT_TRUE_MSG(test, propcmp(&p1, new), + "empty property didn't copy correctly"); kfree(new->value); kfree(new->name); kfree(new); new = __of_prop_dup(&p2, GFP_KERNEL); - unittest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new); + KUNIT_EXPECT_TRUE_MSG(test, propcmp(&p2, new), + "non-empty property didn't copy correctly"); kfree(new->value); kfree(new->name); kfree(new); #endif } -static void __init of_unittest_changeset(void) +static void of_unittest_changeset(struct kunit *test) { #ifdef CONFIG_OF_DYNAMIC struct property *ppadd, padd = { .name = "prop-add", .length = 1, .value = "" }; @@ -698,32 +820,32 @@ static void __init of_unittest_changeset(void) struct of_changeset chgset; n1 = __of_node_dup(NULL, "n1"); - unittest(n1, "testcase setup failure\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, n1); n2 = __of_node_dup(NULL, "n2"); - unittest(n2, "testcase setup failure\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, n2); n21 = __of_node_dup(NULL, "n21"); - unittest(n21, "testcase setup failure %p\n", n21); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, n21); nchangeset = of_find_node_by_path("/testcase-data/changeset"); nremove = of_get_child_by_name(nchangeset, "node-remove"); - unittest(nremove, "testcase setup failure\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, nremove); ppadd = __of_prop_dup(&padd, GFP_KERNEL); - unittest(ppadd, "testcase setup failure\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ppadd); ppname_n1 = __of_prop_dup(&pname_n1, GFP_KERNEL); - unittest(ppname_n1, "testcase setup failure\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ppname_n1); ppname_n2 = __of_prop_dup(&pname_n2, GFP_KERNEL); - unittest(ppname_n2, "testcase setup failure\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ppname_n2); ppname_n21 = __of_prop_dup(&pname_n21, GFP_KERNEL); - unittest(ppname_n21, "testcase setup failure\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ppname_n21); ppupdate = __of_prop_dup(&pupdate, GFP_KERNEL); - unittest(ppupdate, "testcase setup failure\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ppupdate); parent = nchangeset; n1->parent = parent; @@ -731,54 +853,82 @@ static void __init of_unittest_changeset(void) n21->parent = n2; ppremove = of_find_property(parent, "prop-remove", NULL); - unittest(ppremove, "failed to find removal prop"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ppremove); of_changeset_init(&chgset); - unittest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n"); - unittest(!of_changeset_add_property(&chgset, n1, ppname_n1), "fail add prop name\n"); - - unittest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n"); - unittest(!of_changeset_add_property(&chgset, n2, ppname_n2), "fail add prop name\n"); - - unittest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n"); - unittest(!of_changeset_add_property(&chgset, n21, ppname_n21), "fail add prop name\n"); - - unittest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n"); - - unittest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop prop-add\n"); - unittest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n"); - unittest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n"); - - unittest(!of_changeset_apply(&chgset), "apply failed\n"); + KUNIT_EXPECT_FALSE_MSG(test, of_changeset_attach_node(&chgset, n1), + "fail attach n1\n"); + KUNIT_EXPECT_FALSE_MSG(test, + of_changeset_add_property(&chgset, + n1, + ppname_n1), + "fail add prop name\n"); + + KUNIT_EXPECT_FALSE_MSG(test, of_changeset_attach_node(&chgset, n2), + "fail attach n2\n"); + KUNIT_EXPECT_FALSE_MSG(test, + of_changeset_add_property(&chgset, + n2, + ppname_n2), + "fail add prop name\n"); + + KUNIT_EXPECT_FALSE_MSG(test, of_changeset_detach_node(&chgset, nremove), + "fail remove node\n"); + KUNIT_EXPECT_FALSE_MSG(test, + of_changeset_add_property(&chgset, + n21, + ppname_n21), + "fail add prop name\n"); + + KUNIT_EXPECT_FALSE_MSG(test, of_changeset_attach_node(&chgset, n21), + "fail attach n21\n"); + + KUNIT_EXPECT_FALSE_MSG(test, + of_changeset_add_property(&chgset, + parent, + ppadd), + "fail add prop prop-add\n"); + KUNIT_EXPECT_FALSE_MSG(test, + of_changeset_update_property(&chgset, + parent, + ppupdate), + "fail update prop\n"); + KUNIT_EXPECT_FALSE_MSG(test, + of_changeset_remove_property(&chgset, + parent, + ppremove), + "fail remove prop\n"); + + KUNIT_EXPECT_FALSE_MSG(test, of_changeset_apply(&chgset), + "apply failed\n"); of_node_put(nchangeset); /* Make sure node names are constructed correctly */ - unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")), - "'%pOF' not added\n", n21); + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG( + test, + np = of_find_node_by_path( + "/testcase-data/changeset/n2/n21"), + "'%pOF' not added\n", n21); of_node_put(np); - unittest(!of_changeset_revert(&chgset), "revert failed\n"); + KUNIT_EXPECT_FALSE(test, of_changeset_revert(&chgset)); of_changeset_destroy(&chgset); #endif } -static void __init of_unittest_parse_interrupts(void) +static void of_unittest_parse_interrupts(struct kunit *test) { struct device_node *np; struct of_phandle_args args; int i, rc; - if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC) - return; + KUNIT_ASSERT_FALSE(test, of_irq_workarounds & OF_IMAP_OLDWORLD_MAC); np = of_find_node_by_path("/testcase-data/interrupts/interrupts0"); - if (!np) { - pr_err("missing testcase data\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); for (i = 0; i < 4; i++) { bool passed = true; @@ -790,16 +940,14 @@ static void __init of_unittest_parse_interrupts(void) passed &= (args.args_count == 1); passed &= (args.args[0] == (i + 1)); - unittest(passed, "index %i - data error on node %pOF rc=%i\n", - i, args.np, rc); + KUNIT_EXPECT_TRUE_MSG(test, passed, + "index %i - data error on node %pOF rc=%i\n", + i, args.np, rc); } of_node_put(np); np = of_find_node_by_path("/testcase-data/interrupts/interrupts1"); - if (!np) { - pr_err("missing testcase data\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); for (i = 0; i < 4; i++) { bool passed = true; @@ -836,26 +984,23 @@ static void __init of_unittest_parse_interrupts(void) default: passed = false; } - unittest(passed, "index %i - data error on node %pOF rc=%i\n", - i, args.np, rc); + KUNIT_EXPECT_TRUE_MSG(test, passed, + "index %i - data error on node %pOF rc=%i\n", + i, args.np, rc); } of_node_put(np); } -static void __init of_unittest_parse_interrupts_extended(void) +static void of_unittest_parse_interrupts_extended(struct kunit *test) { struct device_node *np; struct of_phandle_args args; int i, rc; - if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC) - return; + KUNIT_ASSERT_FALSE(test, of_irq_workarounds & OF_IMAP_OLDWORLD_MAC); np = of_find_node_by_path("/testcase-data/interrupts/interrupts-extended0"); - if (!np) { - pr_err("missing testcase data\n"); - return; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); for (i = 0; i < 7; i++) { bool passed = true; @@ -909,8 +1054,9 @@ static void __init of_unittest_parse_interrupts_extended(void) passed = false; } - unittest(passed, "index %i - data error on node %pOF rc=%i\n", - i, args.np, rc); + KUNIT_EXPECT_TRUE_MSG(test, passed, + "index %i - data error on node %pOF rc=%i\n", + i, args.np, rc); } of_node_put(np); } @@ -950,7 +1096,7 @@ static struct { { .path = "/testcase-data/match-node/name9", .data = "K", }, }; -static void __init of_unittest_match_node(void) +static void of_unittest_match_node(struct kunit *test) { struct device_node *np; const struct of_device_id *match; @@ -958,26 +1104,19 @@ static void __init of_unittest_match_node(void) for (i = 0; i < ARRAY_SIZE(match_node_tests); i++) { np = of_find_node_by_path(match_node_tests[i].path); - if (!np) { - unittest(0, "missing testcase node %s\n", - match_node_tests[i].path); - continue; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); match = of_match_node(match_node_table, np); - if (!match) { - unittest(0, "%s didn't match anything\n", - match_node_tests[i].path); - continue; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, np, + "%s didn't match anything", + match_node_tests[i].path); - if (strcmp(match->data, match_node_tests[i].data) != 0) { - unittest(0, "%s got wrong match. expected %s, got %s\n", - match_node_tests[i].path, match_node_tests[i].data, - (const char *)match->data); - continue; - } - unittest(1, "passed"); + KUNIT_EXPECT_STREQ_MSG(test, + match->data, match_node_tests[i].data, + "%s got wrong match. expected %s, got %s\n", + match_node_tests[i].path, + match_node_tests[i].data, + (const char *)match->data); } } @@ -989,9 +1128,9 @@ static struct resource test_bus_res = { static const struct platform_device_info test_bus_info = { .name = "unittest-bus", }; -static void __init of_unittest_platform_populate(void) +static void of_unittest_platform_populate(struct kunit *test) { - int irq, rc; + int irq; struct device_node *np, *child, *grandchild; struct platform_device *pdev, *test_bus; const struct of_device_id match[] = { @@ -1005,32 +1144,27 @@ static void __init of_unittest_platform_populate(void) /* Test that a missing irq domain returns -EPROBE_DEFER */ np = of_find_node_by_path("/testcase-data/testcase-device1"); pdev = of_find_device_by_node(np); - unittest(pdev, "device 1 creation failed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); if (!(of_irq_workarounds & OF_IMAP_OLDWORLD_MAC)) { irq = platform_get_irq(pdev, 0); - unittest(irq == -EPROBE_DEFER, - "device deferred probe failed - %d\n", irq); + KUNIT_ASSERT_EQ(test, irq, -EPROBE_DEFER); /* Test that a parsing failure does not return -EPROBE_DEFER */ np = of_find_node_by_path("/testcase-data/testcase-device2"); pdev = of_find_device_by_node(np); - unittest(pdev, "device 2 creation failed\n"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); irq = platform_get_irq(pdev, 0); - unittest(irq < 0 && irq != -EPROBE_DEFER, - "device parsing error failed - %d\n", irq); + KUNIT_ASSERT_TRUE_MSG(test, irq < 0 && irq != -EPROBE_DEFER, + "device parsing error failed - %d\n", + irq); } np = of_find_node_by_path("/testcase-data/platform-tests"); - unittest(np, "No testcase data in device tree\n"); - if (!np) - return; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); test_bus = platform_device_register_full(&test_bus_info); - rc = PTR_ERR_OR_ZERO(test_bus); - unittest(!rc, "testbus registration failed; rc=%i\n", rc); - if (rc) - return; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_bus); test_bus->dev.of_node = np; /* @@ -1045,17 +1179,21 @@ static void __init of_unittest_platform_populate(void) of_platform_populate(np, match, NULL, &test_bus->dev); for_each_child_of_node(np, child) { for_each_child_of_node(child, grandchild) - unittest(of_find_device_by_node(grandchild), - "Could not create device for node '%s'\n", - grandchild->name); + KUNIT_EXPECT_TRUE_MSG(test, + of_find_device_by_node( + grandchild), + "Could not create device for node '%s'\n", + grandchild->name); } of_platform_depopulate(&test_bus->dev); for_each_child_of_node(np, child) { for_each_child_of_node(child, grandchild) - unittest(!of_find_device_by_node(grandchild), - "device didn't get destroyed '%s'\n", - grandchild->name); + KUNIT_EXPECT_FALSE_MSG(test, + of_find_device_by_node( + grandchild), + "device didn't get destroyed '%s'\n", + grandchild->name); } platform_device_unregister(test_bus); @@ -1129,7 +1267,7 @@ static int attach_node_and_children(struct device_node *np) * unittest_data_add - Reads, copies data from * linked tree and attaches it to the live tree */ -static int __init unittest_data_add(void) +static int unittest_data_add(void) { void *unittest_data; struct device_node *unittest_data_node, *np; @@ -1200,7 +1338,7 @@ static int __init unittest_data_add(void) } #ifdef CONFIG_OF_OVERLAY -static int __init overlay_data_apply(const char *overlay_name, int *overlay_id); +static int overlay_data_apply(const char *overlay_name, int *overlay_id); static int unittest_probe(struct platform_device *pdev) { @@ -1429,173 +1567,157 @@ static void of_unittest_destroy_tracked_overlays(void) } while (defers > 0); } -static int __init of_unittest_apply_overlay(int overlay_nr, int unittest_nr, - int *overlay_id) +static int of_unittest_apply_overlay(struct kunit *test, + int overlay_nr, + int unittest_nr, + int *overlay_id) { const char *overlay_name; overlay_name = overlay_name_from_nr(overlay_nr); - if (!overlay_data_apply(overlay_name, overlay_id)) { - unittest(0, "could not apply overlay \"%s\"\n", - overlay_name); - return -EFAULT; - } + KUNIT_ASSERT_TRUE_MSG(test, + overlay_data_apply(overlay_name, overlay_id), + "could not apply overlay \"%s\"\n", + overlay_name); of_unittest_track_overlay(*overlay_id); return 0; } /* apply an overlay while checking before and after states */ -static int __init of_unittest_apply_overlay_check(int overlay_nr, +static int of_unittest_apply_overlay_check(struct kunit *test, int overlay_nr, int unittest_nr, int before, int after, enum overlay_type ovtype) { - int ret, ovcs_id; + int ovcs_id; /* unittest device must not be in before state */ - if (of_unittest_device_exists(unittest_nr, ovtype) != before) { - unittest(0, "%s with device @\"%s\" %s\n", - overlay_name_from_nr(overlay_nr), - unittest_path(unittest_nr, ovtype), - !before ? "enabled" : "disabled"); - return -EINVAL; - } + KUNIT_ASSERT_EQ_MSG(test, + of_unittest_device_exists(unittest_nr, ovtype), + before, + "%s with device @\"%s\" %s\n", + overlay_name_from_nr(overlay_nr), + unittest_path(unittest_nr, ovtype), + !before ? "enabled" : "disabled"); ovcs_id = 0; - ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ovcs_id); - if (ret != 0) { - /* of_unittest_apply_overlay already called unittest() */ - return ret; - } + KUNIT_EXPECT_EQ(test, + of_unittest_apply_overlay(test, + overlay_nr, + unittest_nr, + &ovcs_id), + 0); /* unittest device must be to set to after state */ - if (of_unittest_device_exists(unittest_nr, ovtype) != after) { - unittest(0, "%s failed to create @\"%s\" %s\n", - overlay_name_from_nr(overlay_nr), - unittest_path(unittest_nr, ovtype), - !after ? "enabled" : "disabled"); - return -EINVAL; - } + KUNIT_ASSERT_EQ_MSG(test, + of_unittest_device_exists(unittest_nr, ovtype), + after, + "%s failed to create @\"%s\" %s\n", + overlay_name_from_nr(overlay_nr), + unittest_path(unittest_nr, ovtype), + !after ? "enabled" : "disabled"); return 0; } /* apply an overlay and then revert it while checking before, after states */ -static int __init of_unittest_apply_revert_overlay_check(int overlay_nr, +static int of_unittest_apply_revert_overlay_check( + struct kunit *test, int overlay_nr, int unittest_nr, int before, int after, enum overlay_type ovtype) { - int ret, ovcs_id; + int ovcs_id; /* unittest device must be in before state */ - if (of_unittest_device_exists(unittest_nr, ovtype) != before) { - unittest(0, "%s with device @\"%s\" %s\n", - overlay_name_from_nr(overlay_nr), - unittest_path(unittest_nr, ovtype), - !before ? "enabled" : "disabled"); - return -EINVAL; - } + KUNIT_ASSERT_EQ_MSG(test, + of_unittest_device_exists(unittest_nr, ovtype), + before, + "%s with device @\"%s\" %s\n", + overlay_name_from_nr(overlay_nr), + unittest_path(unittest_nr, ovtype), + !before ? "enabled" : "disabled"); /* apply the overlay */ ovcs_id = 0; - ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ovcs_id); - if (ret != 0) { - /* of_unittest_apply_overlay already called unittest() */ - return ret; - } + KUNIT_ASSERT_EQ(test, + of_unittest_apply_overlay(test, + overlay_nr, + unittest_nr, + &ovcs_id), + 0); /* unittest device must be in after state */ - if (of_unittest_device_exists(unittest_nr, ovtype) != after) { - unittest(0, "%s failed to create @\"%s\" %s\n", - overlay_name_from_nr(overlay_nr), - unittest_path(unittest_nr, ovtype), - !after ? "enabled" : "disabled"); - return -EINVAL; - } - - ret = of_overlay_remove(&ovcs_id); - if (ret != 0) { - unittest(0, "%s failed to be destroyed @\"%s\"\n", - overlay_name_from_nr(overlay_nr), - unittest_path(unittest_nr, ovtype)); - return ret; - } + KUNIT_ASSERT_EQ_MSG(test, + of_unittest_device_exists(unittest_nr, ovtype), + after, + "%s failed to create @\"%s\" %s\n", + overlay_name_from_nr(overlay_nr), + unittest_path(unittest_nr, ovtype), + !after ? "enabled" : "disabled"); + + KUNIT_ASSERT_EQ_MSG(test, of_overlay_remove(&ovcs_id), 0, + "%s failed to be destroyed @\"%s\"\n", + overlay_name_from_nr(overlay_nr), + unittest_path(unittest_nr, ovtype)); /* unittest device must be again in before state */ - if (of_unittest_device_exists(unittest_nr, PDEV_OVERLAY) != before) { - unittest(0, "%s with device @\"%s\" %s\n", - overlay_name_from_nr(overlay_nr), - unittest_path(unittest_nr, ovtype), - !before ? "enabled" : "disabled"); - return -EINVAL; - } + KUNIT_ASSERT_EQ_MSG(test, + of_unittest_device_exists(unittest_nr, + PDEV_OVERLAY), + before, + "%s with device @\"%s\" %s\n", + overlay_name_from_nr(overlay_nr), + unittest_path(unittest_nr, ovtype), + !before ? "enabled" : "disabled"); return 0; } /* test activation of device */ -static void __init of_unittest_overlay_0(void) +static void of_unittest_overlay_0(struct kunit *test) { /* device should enable */ - if (of_unittest_apply_overlay_check(0, 0, 0, 1, PDEV_OVERLAY)) - return; - - unittest(1, "overlay test %d passed\n", 0); + of_unittest_apply_overlay_check(test, 0, 0, 0, 1, PDEV_OVERLAY); } /* test deactivation of device */ -static void __init of_unittest_overlay_1(void) +static void of_unittest_overlay_1(struct kunit *test) { /* device should disable */ - if (of_unittest_apply_overlay_check(1, 1, 1, 0, PDEV_OVERLAY)) - return; - - unittest(1, "overlay test %d passed\n", 1); + of_unittest_apply_overlay_check(test, 1, 1, 1, 0, PDEV_OVERLAY); } /* test activation of device */ -static void __init of_unittest_overlay_2(void) +static void of_unittest_overlay_2(struct kunit *test) { /* device should enable */ - if (of_unittest_apply_overlay_check(2, 2, 0, 1, PDEV_OVERLAY)) - return; - - unittest(1, "overlay test %d passed\n", 2); + of_unittest_apply_overlay_check(test, 2, 2, 0, 1, PDEV_OVERLAY); } /* test deactivation of device */ -static void __init of_unittest_overlay_3(void) +static void of_unittest_overlay_3(struct kunit *test) { /* device should disable */ - if (of_unittest_apply_overlay_check(3, 3, 1, 0, PDEV_OVERLAY)) - return; - - unittest(1, "overlay test %d passed\n", 3); + of_unittest_apply_overlay_check(test, 3, 3, 1, 0, PDEV_OVERLAY); } /* test activation of a full device node */ -static void __init of_unittest_overlay_4(void) +static void of_unittest_overlay_4(struct kunit *test) { /* device should disable */ - if (of_unittest_apply_overlay_check(4, 4, 0, 1, PDEV_OVERLAY)) - return; - - unittest(1, "overlay test %d passed\n", 4); + of_unittest_apply_overlay_check(test, 4, 4, 0, 1, PDEV_OVERLAY); } /* test overlay apply/revert sequence */ -static void __init of_unittest_overlay_5(void) +static void of_unittest_overlay_5(struct kunit *test) { /* device should disable */ - if (of_unittest_apply_revert_overlay_check(5, 5, 0, 1, PDEV_OVERLAY)) - return; - - unittest(1, "overlay test %d passed\n", 5); + of_unittest_apply_revert_overlay_check(test, 5, 5, 0, 1, PDEV_OVERLAY); } /* test overlay application in sequence */ -static void __init of_unittest_overlay_6(void) +static void of_unittest_overlay_6(struct kunit *test) { int i, ov_id[2], ovcs_id; int overlay_nr = 6, unittest_nr = 6; @@ -1604,74 +1726,69 @@ static void __init of_unittest_overlay_6(void) /* unittest device must be in before state */ for (i = 0; i < 2; i++) { - if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY) - != before) { - unittest(0, "%s with device @\"%s\" %s\n", - overlay_name_from_nr(overlay_nr + i), - unittest_path(unittest_nr + i, - PDEV_OVERLAY), - !before ? "enabled" : "disabled"); - return; - } + KUNIT_ASSERT_EQ_MSG(test, + of_unittest_device_exists(unittest_nr + i, + PDEV_OVERLAY), + before, + "%s with device @\"%s\" %s\n", + overlay_name_from_nr(overlay_nr + i), + unittest_path(unittest_nr + i, + PDEV_OVERLAY), + !before ? "enabled" : "disabled"); } /* apply the overlays */ for (i = 0; i < 2; i++) { - overlay_name = overlay_name_from_nr(overlay_nr + i); - if (!overlay_data_apply(overlay_name, &ovcs_id)) { - unittest(0, "could not apply overlay \"%s\"\n", - overlay_name); - return; - } + KUNIT_ASSERT_TRUE_MSG(test, + overlay_data_apply(overlay_name, + &ovcs_id), + "could not apply overlay \"%s\"\n", + overlay_name); ov_id[i] = ovcs_id; of_unittest_track_overlay(ov_id[i]); } for (i = 0; i < 2; i++) { /* unittest device must be in after state */ - if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY) - != after) { - unittest(0, "overlay @\"%s\" failed @\"%s\" %s\n", - overlay_name_from_nr(overlay_nr + i), - unittest_path(unittest_nr + i, - PDEV_OVERLAY), - !after ? "enabled" : "disabled"); - return; - } + KUNIT_ASSERT_EQ_MSG(test, + of_unittest_device_exists(unittest_nr + i, + PDEV_OVERLAY), + after, + "overlay @\"%s\" failed @\"%s\" %s\n", + overlay_name_from_nr(overlay_nr + i), + unittest_path(unittest_nr + i, + PDEV_OVERLAY), + !after ? "enabled" : "disabled"); } for (i = 1; i >= 0; i--) { ovcs_id = ov_id[i]; - if (of_overlay_remove(&ovcs_id)) { - unittest(0, "%s failed destroy @\"%s\"\n", - overlay_name_from_nr(overlay_nr + i), - unittest_path(unittest_nr + i, - PDEV_OVERLAY)); - return; - } + KUNIT_ASSERT_FALSE_MSG(test, of_overlay_remove(&ovcs_id), + "%s failed destroy @\"%s\"\n", + overlay_name_from_nr(overlay_nr + i), + unittest_path(unittest_nr + i, + PDEV_OVERLAY)); of_unittest_untrack_overlay(ov_id[i]); } for (i = 0; i < 2; i++) { /* unittest device must be again in before state */ - if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY) - != before) { - unittest(0, "%s with device @\"%s\" %s\n", - overlay_name_from_nr(overlay_nr + i), - unittest_path(unittest_nr + i, - PDEV_OVERLAY), - !before ? "enabled" : "disabled"); - return; - } + KUNIT_ASSERT_EQ_MSG(test, + of_unittest_device_exists(unittest_nr + i, + PDEV_OVERLAY), + before, + "%s with device @\"%s\" %s\n", + overlay_name_from_nr(overlay_nr + i), + unittest_path(unittest_nr + i, + PDEV_OVERLAY), + !before ? "enabled" : "disabled"); } - - unittest(1, "overlay test %d passed\n", 6); } /* test overlay application in sequence */ -static void __init of_unittest_overlay_8(void) +static void of_unittest_overlay_8(struct kunit *test) { int i, ov_id[2], ovcs_id; int overlay_nr = 8, unittest_nr = 8; @@ -1681,76 +1798,73 @@ static void __init of_unittest_overlay_8(void) /* apply the overlays */ for (i = 0; i < 2; i++) { - overlay_name = overlay_name_from_nr(overlay_nr + i); - if (!overlay_data_apply(overlay_name, &ovcs_id)) { - unittest(0, "could not apply overlay \"%s\"\n", - overlay_name); - return; - } + KUNIT_ASSERT_TRUE_MSG(test, + overlay_data_apply(overlay_name, + &ovcs_id), + "could not apply overlay \"%s\"\n", + overlay_name); ov_id[i] = ovcs_id; of_unittest_track_overlay(ov_id[i]); } /* now try to remove first overlay (it should fail) */ ovcs_id = ov_id[0]; - if (!of_overlay_remove(&ovcs_id)) { - unittest(0, "%s was destroyed @\"%s\"\n", - overlay_name_from_nr(overlay_nr + 0), - unittest_path(unittest_nr, - PDEV_OVERLAY)); - return; - } + KUNIT_ASSERT_TRUE_MSG(test, of_overlay_remove(&ovcs_id), + "%s was destroyed @\"%s\"\n", + overlay_name_from_nr(overlay_nr + 0), + unittest_path(unittest_nr, + PDEV_OVERLAY)); /* removing them in order should work */ for (i = 1; i >= 0; i--) { ovcs_id = ov_id[i]; - if (of_overlay_remove(&ovcs_id)) { - unittest(0, "%s not destroyed @\"%s\"\n", - overlay_name_from_nr(overlay_nr + i), - unittest_path(unittest_nr, - PDEV_OVERLAY)); - return; - } + KUNIT_ASSERT_FALSE_MSG(test, of_overlay_remove(&ovcs_id), + "%s not destroyed @\"%s\"\n", + overlay_name_from_nr(overlay_nr + i), + unittest_path(unittest_nr, + PDEV_OVERLAY)); of_unittest_untrack_overlay(ov_id[i]); } - - unittest(1, "overlay test %d passed\n", 8); } /* test insertion of a bus with parent devices */ -static void __init of_unittest_overlay_10(void) +static void of_unittest_overlay_10(struct kunit *test) { - int ret; char *child_path; /* device should disable */ - ret = of_unittest_apply_overlay_check(10, 10, 0, 1, PDEV_OVERLAY); - if (unittest(ret == 0, - "overlay test %d failed; overlay application\n", 10)) - return; + KUNIT_ASSERT_EQ_MSG(test, + of_unittest_apply_overlay_check(test, + 10, + 10, + 0, + 1, + PDEV_OVERLAY), + 0, + "overlay test %d failed; overlay application\n", + 10); child_path = kasprintf(GFP_KERNEL, "%s/test-unittest101", unittest_path(10, PDEV_OVERLAY)); - if (unittest(child_path, "overlay test %d failed; kasprintf\n", 10)) - return; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, child_path); - ret = of_path_device_type_exists(child_path, PDEV_OVERLAY); + KUNIT_EXPECT_TRUE_MSG(test, + of_path_device_type_exists(child_path, + PDEV_OVERLAY), + "overlay test %d failed; no child device\n", 10); kfree(child_path); - - unittest(ret, "overlay test %d failed; no child device\n", 10); } /* test insertion of a bus with parent devices (and revert) */ -static void __init of_unittest_overlay_11(void) +static void of_unittest_overlay_11(struct kunit *test) { - int ret; - /* device should disable */ - ret = of_unittest_apply_revert_overlay_check(11, 11, 0, 1, - PDEV_OVERLAY); - unittest(ret == 0, "overlay test %d failed; overlay apply\n", 11); + KUNIT_EXPECT_FALSE(test, + of_unittest_apply_revert_overlay_check(test, + 11, 11, 0, 1, + PDEV_OVERLAY)); } #if IS_BUILTIN(CONFIG_I2C) && IS_ENABLED(CONFIG_OF_OVERLAY) @@ -1972,25 +2086,23 @@ static struct i2c_driver unittest_i2c_mux_driver = { #endif -static int of_unittest_overlay_i2c_init(void) +static int of_unittest_overlay_i2c_init(struct kunit *test) { - int ret; + KUNIT_ASSERT_EQ_MSG(test, + i2c_add_driver(&unittest_i2c_dev_driver), + 0, + "could not register unittest i2c device driver\n"); - ret = i2c_add_driver(&unittest_i2c_dev_driver); - if (unittest(ret == 0, - "could not register unittest i2c device driver\n")) - return ret; - - ret = platform_driver_register(&unittest_i2c_bus_driver); - if (unittest(ret == 0, - "could not register unittest i2c bus driver\n")) - return ret; + KUNIT_ASSERT_EQ_MSG(test, + platform_driver_register(&unittest_i2c_bus_driver), + 0, + "could not register unittest i2c bus driver\n"); #if IS_BUILTIN(CONFIG_I2C_MUX) - ret = i2c_add_driver(&unittest_i2c_mux_driver); - if (unittest(ret == 0, - "could not register unittest i2c mux driver\n")) - return ret; + KUNIT_ASSERT_EQ_MSG(test, + i2c_add_driver(&unittest_i2c_mux_driver), + 0, + "could not register unittest i2c mux driver\n"); #endif return 0; @@ -2005,101 +2117,89 @@ static void of_unittest_overlay_i2c_cleanup(void) i2c_del_driver(&unittest_i2c_dev_driver); } -static void __init of_unittest_overlay_i2c_12(void) +static void of_unittest_overlay_i2c_12(struct kunit *test) { /* device should enable */ - if (of_unittest_apply_overlay_check(12, 12, 0, 1, I2C_OVERLAY)) - return; - - unittest(1, "overlay test %d passed\n", 12); + of_unittest_apply_overlay_check(test, 12, 12, 0, 1, I2C_OVERLAY); } /* test deactivation of device */ -static void __init of_unittest_overlay_i2c_13(void) +static void of_unittest_overlay_i2c_13(struct kunit *test) { /* device should disable */ - if (of_unittest_apply_overlay_check(13, 13, 1, 0, I2C_OVERLAY)) - return; - - unittest(1, "overlay test %d passed\n", 13); + of_unittest_apply_overlay_check(test, 13, 13, 1, 0, I2C_OVERLAY); } /* just check for i2c mux existence */ -static void of_unittest_overlay_i2c_14(void) +static void of_unittest_overlay_i2c_14(struct kunit *test) { + KUNIT_SUCCEED(test); } -static void __init of_unittest_overlay_i2c_15(void) +static void of_unittest_overlay_i2c_15(struct kunit *test) { /* device should enable */ - if (of_unittest_apply_overlay_check(15, 15, 0, 1, I2C_OVERLAY)) - return; - - unittest(1, "overlay test %d passed\n", 15); + of_unittest_apply_overlay_check(test, 15, 15, 0, 1, I2C_OVERLAY); } #else -static inline void of_unittest_overlay_i2c_14(void) { } -static inline void of_unittest_overlay_i2c_15(void) { } +static inline void of_unittest_overlay_i2c_14(struct kunit *test) { } +static inline void of_unittest_overlay_i2c_15(struct kunit *test) { } #endif -static void __init of_unittest_overlay(void) +static void of_unittest_overlay(struct kunit *test) { struct device_node *bus_np = NULL; - if (platform_driver_register(&unittest_driver)) { - unittest(0, "could not register unittest driver\n"); - goto out; - } + KUNIT_ASSERT_FALSE_MSG(test, + platform_driver_register(&unittest_driver), + "could not register unittest driver\n"); bus_np = of_find_node_by_path(bus_path); - if (bus_np == NULL) { - unittest(0, "could not find bus_path \"%s\"\n", bus_path); - goto out; - } + KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, bus_np, + "could not find bus_path \"%s\"\n", + bus_path); - if (of_platform_default_populate(bus_np, NULL, NULL)) { - unittest(0, "could not populate bus @ \"%s\"\n", bus_path); - goto out; - } + KUNIT_ASSERT_FALSE_MSG(test, + of_platform_default_populate(bus_np, NULL, NULL), + "could not populate bus @ \"%s\"\n", bus_path); - if (!of_unittest_device_exists(100, PDEV_OVERLAY)) { - unittest(0, "could not find unittest0 @ \"%s\"\n", - unittest_path(100, PDEV_OVERLAY)); - goto out; - } + KUNIT_ASSERT_TRUE_MSG(test, + of_unittest_device_exists(100, PDEV_OVERLAY), + "could not find unittest0 @ \"%s\"\n", + unittest_path(100, PDEV_OVERLAY)); - if (of_unittest_device_exists(101, PDEV_OVERLAY)) { - unittest(0, "unittest1 @ \"%s\" should not exist\n", - unittest_path(101, PDEV_OVERLAY)); - goto out; - } - - unittest(1, "basic infrastructure of overlays passed"); + KUNIT_ASSERT_FALSE_MSG(test, + of_unittest_device_exists(101, PDEV_OVERLAY), + "unittest1 @ \"%s\" should not exist\n", + unittest_path(101, PDEV_OVERLAY)); /* tests in sequence */ - of_unittest_overlay_0(); - of_unittest_overlay_1(); - of_unittest_overlay_2(); - of_unittest_overlay_3(); - of_unittest_overlay_4(); - of_unittest_overlay_5(); - of_unittest_overlay_6(); - of_unittest_overlay_8(); - - of_unittest_overlay_10(); - of_unittest_overlay_11(); + of_unittest_overlay_0(test); + of_unittest_overlay_1(test); + of_unittest_overlay_2(test); + of_unittest_overlay_3(test); + of_unittest_overlay_4(test); + of_unittest_overlay_5(test); + of_unittest_overlay_6(test); + of_unittest_overlay_8(test); + + of_unittest_overlay_10(test); + of_unittest_overlay_11(test); #if IS_BUILTIN(CONFIG_I2C) - if (unittest(of_unittest_overlay_i2c_init() == 0, "i2c init failed\n")) + KUNIT_ASSERT_EQ_MSG(test, + of_unittest_overlay_i2c_init(test), + 0, + "i2c init failed\n"); goto out; - of_unittest_overlay_i2c_12(); - of_unittest_overlay_i2c_13(); - of_unittest_overlay_i2c_14(); - of_unittest_overlay_i2c_15(); + of_unittest_overlay_i2c_12(test); + of_unittest_overlay_i2c_13(test); + of_unittest_overlay_i2c_14(test); + of_unittest_overlay_i2c_15(test); of_unittest_overlay_i2c_cleanup(); #endif @@ -2111,7 +2211,7 @@ static void __init of_unittest_overlay(void) } #else -static inline void __init of_unittest_overlay(void) { } +static inline void of_unittest_overlay(struct kunit *test) { } #endif #ifdef CONFIG_OF_OVERLAY @@ -2254,7 +2354,7 @@ void __init unittest_unflatten_overlay_base(void) * * Return 0 on unexpected error. */ -static int __init overlay_data_apply(const char *overlay_name, int *overlay_id) +static int overlay_data_apply(const char *overlay_name, int *overlay_id) { struct overlay_info *info; int found = 0; @@ -2301,19 +2401,17 @@ static int __init overlay_data_apply(const char *overlay_name, int *overlay_id) * The first part of the function is _not_ normal overlay usage; it is * finishing splicing the base overlay device tree into the live tree. */ -static __init void of_unittest_overlay_high_level(void) +static void of_unittest_overlay_high_level(struct kunit *test) { struct device_node *last_sibling; struct device_node *np; struct device_node *of_symbols; - struct device_node *overlay_base_symbols; + struct device_node *overlay_base_symbols = 0; struct device_node **pprev; struct property *prop; - if (!overlay_base_root) { - unittest(0, "overlay_base_root not initialized\n"); - return; - } + KUNIT_ASSERT_TRUE_MSG(test, overlay_base_root, + "overlay_base_root not initialized\n"); /* * Could not fixup phandles in unittest_unflatten_overlay_base() @@ -2358,11 +2456,10 @@ static __init void of_unittest_overlay_high_level(void) } for (np = overlay_base_root->child; np; np = np->sibling) { - if (of_get_child_by_name(of_root, np->name)) { - unittest(0, "illegal node name in overlay_base %s", - np->name); - return; - } + KUNIT_ASSERT_FALSE_MSG(test, + of_get_child_by_name(of_root, np->name), + "illegal node name in overlay_base %s", + np->name); } /* @@ -2395,21 +2492,24 @@ static __init void of_unittest_overlay_high_level(void) new_prop = __of_prop_dup(prop, GFP_KERNEL); if (!new_prop) { - unittest(0, "__of_prop_dup() of '%s' from overlay_base node __symbols__", - prop->name); + KUNIT_FAIL(test, + "__of_prop_dup() of '%s' from overlay_base node __symbols__", + prop->name); goto err_unlock; } if (__of_add_property(of_symbols, new_prop)) { /* "name" auto-generated by unflatten */ if (!strcmp(new_prop->name, "name")) continue; - unittest(0, "duplicate property '%s' in overlay_base node __symbols__", - prop->name); + KUNIT_FAIL(test, + "duplicate property '%s' in overlay_base node __symbols__", + prop->name); goto err_unlock; } if (__of_add_property_sysfs(of_symbols, new_prop)) { - unittest(0, "unable to add property '%s' in overlay_base node __symbols__ to sysfs", - prop->name); + KUNIT_FAIL(test, + "unable to add property '%s' in overlay_base node __symbols__ to sysfs", + prop->name); goto err_unlock; } } @@ -2420,14 +2520,16 @@ static __init void of_unittest_overlay_high_level(void) /* now do the normal overlay usage test */ - unittest(overlay_data_apply("overlay", NULL), - "Adding overlay 'overlay' failed\n"); + KUNIT_EXPECT_TRUE_MSG(test, overlay_data_apply("overlay", NULL), + "Adding overlay 'overlay' failed\n"); - unittest(overlay_data_apply("overlay_bad_phandle", NULL), - "Adding overlay 'overlay_bad_phandle' failed\n"); + KUNIT_EXPECT_TRUE_MSG(test, + overlay_data_apply("overlay_bad_phandle", NULL), + "Adding overlay 'overlay_bad_phandle' failed\n"); - unittest(overlay_data_apply("overlay_bad_symbol", NULL), - "Adding overlay 'overlay_bad_symbol' failed\n"); + KUNIT_EXPECT_TRUE_MSG(test, + overlay_data_apply("overlay_bad_symbol", NULL), + "Adding overlay 'overlay_bad_symbol' failed\n"); return; @@ -2437,54 +2539,49 @@ static __init void of_unittest_overlay_high_level(void) #else -static inline __init void of_unittest_overlay_high_level(void) {} +static inline void of_unittest_overlay_high_level(struct kunit *test) {} #endif -static int __init of_unittest(void) +static int of_test_init(struct kunit *test) { - struct device_node *np; - int res; - /* adding data for unittest */ - res = unittest_data_add(); - if (res) - return res; + KUNIT_ASSERT_EQ(test, 0, unittest_data_add()); + if (!of_aliases) of_aliases = of_find_node_by_path("/aliases"); - np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); - if (!np) { - pr_info("No testcase data in device tree; not running tests\n"); - return 0; - } - of_node_put(np); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, of_find_node_by_path( + "/testcase-data/phandle-tests/consumer-a")); - pr_info("start of unittest - you will see error messages\n"); - of_unittest_check_tree_linkage(); - of_unittest_check_phandles(); - of_unittest_find_node_by_name(); - of_unittest_dynamic(); - of_unittest_parse_phandle_with_args(); - of_unittest_parse_phandle_with_args_map(); - of_unittest_printf(); - of_unittest_property_string(); - of_unittest_property_copy(); - of_unittest_changeset(); - of_unittest_parse_interrupts(); - of_unittest_parse_interrupts_extended(); - of_unittest_match_node(); - of_unittest_platform_populate(); - of_unittest_overlay(); + return 0; +} +static struct kunit_case of_test_cases[] = { + KUNIT_CASE(of_unittest_check_tree_linkage), + KUNIT_CASE(of_unittest_check_phandles), + KUNIT_CASE(of_unittest_find_node_by_name), + KUNIT_CASE(of_unittest_dynamic), + KUNIT_CASE(of_unittest_parse_phandle_with_args), + KUNIT_CASE(of_unittest_parse_phandle_with_args_map), + KUNIT_CASE(of_unittest_printf), + KUNIT_CASE(of_unittest_property_string), + KUNIT_CASE(of_unittest_property_copy), + KUNIT_CASE(of_unittest_changeset), + KUNIT_CASE(of_unittest_parse_interrupts), + KUNIT_CASE(of_unittest_parse_interrupts_extended), + KUNIT_CASE(of_unittest_match_node), + KUNIT_CASE(of_unittest_platform_populate), + KUNIT_CASE(of_unittest_overlay), /* Double check linkage after removing testcase data */ - of_unittest_check_tree_linkage(); - - of_unittest_overlay_high_level(); - - pr_info("end of unittest - %i passed, %i failed\n", - unittest_results.passed, unittest_results.failed); + KUNIT_CASE(of_unittest_check_tree_linkage), + KUNIT_CASE(of_unittest_overlay_high_level), + {}, +}; - return 0; -} -late_initcall(of_unittest); +static struct kunit_module of_test_module = { + .name = "of-test", + .init = of_test_init, + .test_cases = of_test_cases, +}; +module_test(of_test_module); From patchwork Wed Nov 28 19:36:35 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703269 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7DBAD15A8 for ; Wed, 28 Nov 2018 19:38:43 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C9AFA2B2A3 for ; Wed, 28 Nov 2018 19:38:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id BD9512B5A2; Wed, 28 Nov 2018 19:38:42 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5B5252C2D0 for ; Wed, 28 Nov 2018 19:38:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729589AbeK2GlO (ORCPT ); Thu, 29 Nov 2018 01:41:14 -0500 Received: from mail-io1-f73.google.com ([209.85.166.73]:35676 "EHLO mail-io1-f73.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729529AbeK2GlN (ORCPT ); Thu, 29 Nov 2018 01:41:13 -0500 Received: by mail-io1-f73.google.com with SMTP id f24so8850109ioo.2 for ; Wed, 28 Nov 2018 11:38:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=/mzBjTCgNR+ITwmtMF9qcoiYEy9ozlu8eqk6jAOiRYA=; b=HcqYy2fb+ed1TkGEJZ4wFTnIOH9oA57pOC4U69LcUOLCrTcxyutGobWdpVh4e9Fi8q TF3WdPCCcmmsDSX5mavVMQ3w5aBNAT6Sjd/1d3GV8DRirONUxd4DsH7c0z2j8iZ4WYb1 vHe/E8sm/Y4Zw1im/ZIZYJjljsUxWPhae40ue6kclM53uKZLuk6D6Dq5l5n30uxW/PXq KTWkvJHqnCbQugMJo/2OoB+mbOnVXhbQ4oBjwdw2ScoxV6Nzd4zKjyP09y6Cx88/PUqE oprpUE09djLXtNBJMjetg2Q0ym2iFZN0g1+o+upmXXkMst7B1wm/wyEdeq/CEe3R6cBm 3G7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=/mzBjTCgNR+ITwmtMF9qcoiYEy9ozlu8eqk6jAOiRYA=; b=HT+MGbMeWrcQ3GZlg33QNakq61lUDxmUa8OqI5FZeN3OicvcDlM/TFflH/fhRZL/mP p2a4BEMG64X95MfyPcmLzfZY0DLzlhbSg/Iwwyrcf+AIJq3Awm4XnZ9r718AbdxkTcNu qbXHyXlQSQgGDc4ppkGLScRkY+SkdFLP8839dl2lnWGFrCzVcjy57cjso6GQJgZCbZ1C A/2n41rDQC5EP7k9MurSVh+0Hx8sbx6dUpPfB6KmXKWQPAQTGFD9bRXPtOyqJC6gvSUk JgLV0lSYpZcSp9Si5Cm53unMtxddkJnQ5/GKN4IItIENXgJX2Ub1w4eiayPBXt57YMWW 34sQ== X-Gm-Message-State: AA+aEWaMOSb5XcBGG6JCpKgq/9CPi+TsaX4he3lvSpi3fTUEkiL6pZeP FbMY672l5X0pqo2wxIPLAxGSbJuCUtG/fDzySPhJMQ== X-Google-Smtp-Source: AFSGD/X8AwCP9fzFk0xtNNo/pwbkCXqVeuxx9pcTkLMKu5T5TyMXjYm5QyFuo357p300B/56VmkuwVEini49taEgxl4fMA== X-Received: by 2002:a24:2c4d:: with SMTP id i74mr3616474iti.12.1543433908203; Wed, 28 Nov 2018 11:38:28 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:35 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-19-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 18/19] of: unittest: split out a couple of test cases from unittest From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Split out a couple of test cases that these features in base.c from the unittest.c monolith. The intention is that we will eventually split out all test cases and group them together based on what portion of device tree they test. Signed-off-by: Brendan Higgins --- drivers/of/Makefile | 2 +- drivers/of/base-test.c | 214 ++++++++++++++++++++++++++ drivers/of/test-common.c | 149 ++++++++++++++++++ drivers/of/test-common.h | 16 ++ drivers/of/unittest.c | 316 +-------------------------------------- 5 files changed, 381 insertions(+), 316 deletions(-) create mode 100644 drivers/of/base-test.c create mode 100644 drivers/of/test-common.c create mode 100644 drivers/of/test-common.h diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 663a4af0cccd5..4a4bd527d586c 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o obj-$(CONFIG_OF_IRQ) += irq.o obj-$(CONFIG_OF_NET) += of_net.o -obj-$(CONFIG_OF_UNITTEST) += unittest.o +obj-$(CONFIG_OF_UNITTEST) += unittest.o base-test.o test-common.o obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o obj-$(CONFIG_OF_RESOLVE) += resolver.o diff --git a/drivers/of/base-test.c b/drivers/of/base-test.c new file mode 100644 index 0000000000000..5731787a3fca8 --- /dev/null +++ b/drivers/of/base-test.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Unit tests for functions defined in base.c. + */ +#include + +#include + +#include "test-common.h" + +static void of_unittest_find_node_by_name(struct kunit *test) +{ + struct device_node *np; + const char *options, *name; + + np = of_find_node_by_path("/testcase-data"); + name = kasprintf(GFP_KERNEL, "%pOF", np); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "/testcase-data", name, + "find /testcase-data failed\n"); + of_node_put(np); + kfree(name); + + /* Test if trailing '/' works */ + KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("/testcase-data/"), NULL, + "trailing '/' on /testcase-data/ should fail\n"); + + np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + name = kasprintf(GFP_KERNEL, "%pOF", np); + KUNIT_EXPECT_STREQ_MSG(test, + "/testcase-data/phandle-tests/consumer-a", name, + "find /testcase-data/phandle-tests/consumer-a failed\n"); + of_node_put(np); + kfree(name); + + np = of_find_node_by_path("testcase-alias"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + name = kasprintf(GFP_KERNEL, "%pOF", np); + KUNIT_EXPECT_STREQ_MSG(test, "/testcase-data", name, + "find testcase-alias failed\n"); + of_node_put(np); + kfree(name); + + /* Test if trailing '/' works on aliases */ + KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("testcase-alias/"), NULL, + "trailing '/' on testcase-alias/ should fail\n"); + + np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + name = kasprintf(GFP_KERNEL, "%pOF", np); + KUNIT_EXPECT_STREQ_MSG(test, + "/testcase-data/phandle-tests/consumer-a", name, + "find testcase-alias/phandle-tests/consumer-a failed\n"); + of_node_put(np); + kfree(name); + + KUNIT_EXPECT_EQ_MSG(test, + of_find_node_by_path("/testcase-data/missing-path"), + NULL, + "non-existent path returned node %pOF\n", np); + of_node_put(np); + + KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("missing-alias"), NULL, + "non-existent alias returned node %pOF\n", np); + of_node_put(np); + + KUNIT_EXPECT_EQ_MSG(test, + of_find_node_by_path("testcase-alias/missing-path"), + NULL, + "non-existent alias with relative path returned node %pOF\n", + np); + of_node_put(np); + + np = of_find_node_opts_by_path("/testcase-data:testoption", &options); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "testoption", options, + "option path test failed\n"); + of_node_put(np); + + np = of_find_node_opts_by_path("/testcase-data:test/option", &options); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "test/option", options, + "option path test, subcase #1 failed\n"); + of_node_put(np); + + np = of_find_node_opts_by_path( + "/testcase-data/testcase-device1:test/option", + &options); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "test/option", options, + "option path test, subcase #2 failed\n"); + of_node_put(np); + + np = of_find_node_opts_by_path("/testcase-data:testoption", NULL); + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, np, + "NULL option path test failed\n"); + of_node_put(np); + + np = of_find_node_opts_by_path("testcase-alias:testaliasoption", + &options); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "testaliasoption", options, + "option alias path test failed\n"); + of_node_put(np); + + np = of_find_node_opts_by_path("testcase-alias:test/alias/option", + &options); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_STREQ_MSG(test, "test/alias/option", options, + "option alias path test, subcase #1 failed\n"); + of_node_put(np); + + np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL); + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, np, + "NULL option alias path test failed\n"); + of_node_put(np); + + options = "testoption"; + np = of_find_node_opts_by_path("testcase-alias", &options); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_EQ_MSG(test, options, NULL, + "option clearing test failed\n"); + of_node_put(np); + + options = "testoption"; + np = of_find_node_opts_by_path("/", &options); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + KUNIT_EXPECT_EQ_MSG(test, options, NULL, + "option clearing root node test failed\n"); + of_node_put(np); +} + +static void of_unittest_dynamic(struct kunit *test) +{ + struct device_node *np; + struct property *prop; + + np = of_find_node_by_path("/testcase-data"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); + + /* Array of 4 properties for the purpose of testing */ + prop = kcalloc(4, sizeof(*prop), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prop); + + /* Add a new property - should pass*/ + prop->name = "new-property"; + prop->value = "new-property-data"; + prop->length = strlen(prop->value) + 1; + KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop), 0, + "Adding a new property failed\n"); + + /* Try to add an existing property - should fail */ + prop++; + prop->name = "new-property"; + prop->value = "new-property-data-should-fail"; + prop->length = strlen(prop->value) + 1; + KUNIT_EXPECT_NE_MSG(test, of_add_property(np, prop), 0, + "Adding an existing property should have failed\n"); + + /* Try to modify an existing property - should pass */ + prop->value = "modify-property-data-should-pass"; + prop->length = strlen(prop->value) + 1; + KUNIT_EXPECT_EQ_MSG(test, of_update_property(np, prop), 0, + "Updating an existing property should have passed\n"); + + /* Try to modify non-existent property - should pass*/ + prop++; + prop->name = "modify-property"; + prop->value = "modify-missing-property-data-should-pass"; + prop->length = strlen(prop->value) + 1; + KUNIT_EXPECT_EQ_MSG(test, of_update_property(np, prop), 0, + "Updating a missing property should have passed\n"); + + /* Remove property - should pass */ + KUNIT_EXPECT_EQ_MSG(test, of_remove_property(np, prop), 0, + "Removing a property should have passed\n"); + + /* Adding very large property - should pass */ + prop++; + prop->name = "large-property-PAGE_SIZEx8"; + prop->length = PAGE_SIZE * 8; + prop->value = kzalloc(prop->length, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prop->value); + KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop), 0, + "Adding a large property should have passed\n"); +} + +static int of_test_init(struct kunit *test) +{ + /* adding data for unittest */ + KUNIT_ASSERT_EQ(test, 0, unittest_data_add()); + + if (!of_aliases) + of_aliases = of_find_node_by_path("/aliases"); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, of_find_node_by_path( + "/testcase-data/phandle-tests/consumer-a")); + + return 0; +} + +static struct kunit_case of_test_cases[] = { + KUNIT_CASE(of_unittest_find_node_by_name), + KUNIT_CASE(of_unittest_dynamic), + {}, +}; + +static struct kunit_module of_test_module = { + .name = "of-base-test", + .init = of_test_init, + .test_cases = of_test_cases, +}; +module_test(of_test_module); diff --git a/drivers/of/test-common.c b/drivers/of/test-common.c new file mode 100644 index 0000000000000..0b2319fde3b3e --- /dev/null +++ b/drivers/of/test-common.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common code to be used by unit tests. + */ +#include "test-common.h" + +#include +#include + +#include "of_private.h" + +/** + * update_node_properties - adds the properties + * of np into dup node (present in live tree) and + * updates parent of children of np to dup. + * + * @np: node already present in live tree + * @dup: node present in live tree to be updated + */ +static void update_node_properties(struct device_node *np, + struct device_node *dup) +{ + struct property *prop; + struct device_node *child; + + for_each_property_of_node(np, prop) + of_add_property(dup, prop); + + for_each_child_of_node(np, child) + child->parent = dup; +} + +/** + * attach_node_and_children - attaches nodes + * and its children to live tree + * + * @np: Node to attach to live tree + */ +static int attach_node_and_children(struct device_node *np) +{ + struct device_node *next, *dup, *child; + unsigned long flags; + const char *full_name; + + full_name = kasprintf(GFP_KERNEL, "%pOF", np); + dup = of_find_node_by_path(full_name); + kfree(full_name); + if (dup) { + update_node_properties(np, dup); + return 0; + } + + child = np->child; + np->child = NULL; + + mutex_lock(&of_mutex); + raw_spin_lock_irqsave(&devtree_lock, flags); + np->sibling = np->parent->child; + np->parent->child = np; + of_node_clear_flag(np, OF_DETACHED); + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + __of_attach_node_sysfs(np); + mutex_unlock(&of_mutex); + + while (child) { + next = child->sibling; + attach_node_and_children(child); + child = next; + } + + return 0; +} + +/** + * unittest_data_add - Reads, copies data from + * linked tree and attaches it to the live tree + */ +int unittest_data_add(void) +{ + void *unittest_data; + struct device_node *unittest_data_node, *np; + /* + * __dtb_testcases_begin[] and __dtb_testcases_end[] are magically + * created by cmd_dt_S_dtb in scripts/Makefile.lib + */ + extern uint8_t __dtb_testcases_begin[]; + extern uint8_t __dtb_testcases_end[]; + const int size = __dtb_testcases_end - __dtb_testcases_begin; + int rc; + + if (!size) { + pr_warn("%s: No testcase data to attach; not running tests\n", + __func__); + return -ENODATA; + } + + /* creating copy */ + unittest_data = kmemdup(__dtb_testcases_begin, size, GFP_KERNEL); + + if (!unittest_data) { + pr_warn("%s: Failed to allocate memory for unittest_data; " + "not running tests\n", __func__); + return -ENOMEM; + } + of_fdt_unflatten_tree(unittest_data, NULL, &unittest_data_node); + if (!unittest_data_node) { + pr_warn("%s: No tree to attach; not running tests\n", __func__); + return -ENODATA; + } + + /* + * This lock normally encloses of_resolve_phandles() + */ + of_overlay_mutex_lock(); + + rc = of_resolve_phandles(unittest_data_node); + if (rc) { + pr_err("%s: Failed to resolve phandles (rc=%i)\n", + __func__, rc); + of_overlay_mutex_unlock(); + return -EINVAL; + } + + if (!of_root) { + of_root = unittest_data_node; + for_each_of_allnodes(np) + __of_attach_node_sysfs(np); + of_aliases = of_find_node_by_path("/aliases"); + of_chosen = of_find_node_by_path("/chosen"); + of_overlay_mutex_unlock(); + return 0; + } + + /* attach the sub-tree to live tree */ + np = unittest_data_node->child; + while (np) { + struct device_node *next = np->sibling; + + np->parent = of_root; + attach_node_and_children(np); + np = next; + } + + of_overlay_mutex_unlock(); + + return 0; +} + diff --git a/drivers/of/test-common.h b/drivers/of/test-common.h new file mode 100644 index 0000000000000..a35484406bbf1 --- /dev/null +++ b/drivers/of/test-common.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Common code to be used by unit tests. + */ +#ifndef _LINUX_OF_TEST_COMMON_H +#define _LINUX_OF_TEST_COMMON_H + +#include + +/** + * unittest_data_add - Reads, copies data from + * linked tree and attaches it to the live tree + */ +int unittest_data_add(void); + +#endif /* _LINUX_OF_TEST_COMMON_H */ diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index a5ef44730ffdb..b8c220d330f03 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -29,182 +29,7 @@ #include #include "of_private.h" - -static void of_unittest_find_node_by_name(struct kunit *test) -{ - struct device_node *np; - const char *options, *name; - - np = of_find_node_by_path("/testcase-data"); - name = kasprintf(GFP_KERNEL, "%pOF", np); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - KUNIT_EXPECT_STREQ_MSG(test, "/testcase-data", name, - "find /testcase-data failed\n"); - of_node_put(np); - kfree(name); - - /* Test if trailing '/' works */ - KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("/testcase-data/"), NULL, - "trailing '/' on /testcase-data/ should fail\n"); - - np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - name = kasprintf(GFP_KERNEL, "%pOF", np); - KUNIT_EXPECT_STREQ_MSG(test, - "/testcase-data/phandle-tests/consumer-a", name, - "find /testcase-data/phandle-tests/consumer-a failed\n"); - of_node_put(np); - kfree(name); - - np = of_find_node_by_path("testcase-alias"); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - name = kasprintf(GFP_KERNEL, "%pOF", np); - KUNIT_EXPECT_STREQ_MSG(test, "/testcase-data", name, - "find testcase-alias failed\n"); - of_node_put(np); - kfree(name); - - /* Test if trailing '/' works on aliases */ - KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("testcase-alias/"), NULL, - "trailing '/' on testcase-alias/ should fail\n"); - - np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a"); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - name = kasprintf(GFP_KERNEL, "%pOF", np); - KUNIT_EXPECT_STREQ_MSG(test, - "/testcase-data/phandle-tests/consumer-a", name, - "find testcase-alias/phandle-tests/consumer-a failed\n"); - of_node_put(np); - kfree(name); - - KUNIT_EXPECT_EQ_MSG(test, - of_find_node_by_path("/testcase-data/missing-path"), - NULL, - "non-existent path returned node %pOF\n", np); - of_node_put(np); - - KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("missing-alias"), NULL, - "non-existent alias returned node %pOF\n", np); - of_node_put(np); - - KUNIT_EXPECT_EQ_MSG(test, - of_find_node_by_path("testcase-alias/missing-path"), - NULL, - "non-existent alias with relative path returned node %pOF\n", - np); - of_node_put(np); - - np = of_find_node_opts_by_path("/testcase-data:testoption", &options); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - KUNIT_EXPECT_STREQ_MSG(test, "testoption", options, - "option path test failed\n"); - of_node_put(np); - - np = of_find_node_opts_by_path("/testcase-data:test/option", &options); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - KUNIT_EXPECT_STREQ_MSG(test, "test/option", options, - "option path test, subcase #1 failed\n"); - of_node_put(np); - - np = of_find_node_opts_by_path("/testcase-data/testcase-device1:test/option", &options); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - KUNIT_EXPECT_STREQ_MSG(test, "test/option", options, - "option path test, subcase #2 failed\n"); - of_node_put(np); - - np = of_find_node_opts_by_path("/testcase-data:testoption", NULL); - KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, np, - "NULL option path test failed\n"); - of_node_put(np); - - np = of_find_node_opts_by_path("testcase-alias:testaliasoption", - &options); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - KUNIT_EXPECT_STREQ_MSG(test, "testaliasoption", options, - "option alias path test failed\n"); - of_node_put(np); - - np = of_find_node_opts_by_path("testcase-alias:test/alias/option", - &options); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - KUNIT_EXPECT_STREQ_MSG(test, "test/alias/option", options, - "option alias path test, subcase #1 failed\n"); - of_node_put(np); - - np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL); - KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, np, - "NULL option alias path test failed\n"); - of_node_put(np); - - options = "testoption"; - np = of_find_node_opts_by_path("testcase-alias", &options); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - KUNIT_EXPECT_EQ_MSG(test, options, NULL, - "option clearing test failed\n"); - of_node_put(np); - - options = "testoption"; - np = of_find_node_opts_by_path("/", &options); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - KUNIT_EXPECT_EQ_MSG(test, options, NULL, - "option clearing root node test failed\n"); - of_node_put(np); -} - -static void of_unittest_dynamic(struct kunit *test) -{ - struct device_node *np; - struct property *prop; - - np = of_find_node_by_path("/testcase-data"); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); - - /* Array of 4 properties for the purpose of testing */ - prop = kcalloc(4, sizeof(*prop), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prop); - - /* Add a new property - should pass*/ - prop->name = "new-property"; - prop->value = "new-property-data"; - prop->length = strlen(prop->value) + 1; - KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop), 0, - "Adding a new property failed\n"); - - /* Try to add an existing property - should fail */ - prop++; - prop->name = "new-property"; - prop->value = "new-property-data-should-fail"; - prop->length = strlen(prop->value) + 1; - KUNIT_EXPECT_NE_MSG(test, of_add_property(np, prop), 0, - "Adding an existing property should have failed\n"); - - /* Try to modify an existing property - should pass */ - prop->value = "modify-property-data-should-pass"; - prop->length = strlen(prop->value) + 1; - KUNIT_EXPECT_EQ_MSG(test, of_update_property(np, prop), 0, - "Updating an existing property should have passed\n"); - - /* Try to modify non-existent property - should pass*/ - prop++; - prop->name = "modify-property"; - prop->value = "modify-missing-property-data-should-pass"; - prop->length = strlen(prop->value) + 1; - KUNIT_EXPECT_EQ_MSG(test, of_update_property(np, prop), 0, - "Updating a missing property should have passed\n"); - - /* Remove property - should pass */ - KUNIT_EXPECT_EQ_MSG(test, of_remove_property(np, prop), 0, - "Removing a property should have passed\n"); - - /* Adding very large property - should pass */ - prop++; - prop->name = "large-property-PAGE_SIZEx8"; - prop->length = PAGE_SIZE * 8; - prop->value = kzalloc(prop->length, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prop->value); - KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop), 0, - "Adding a large property should have passed\n"); -} +#include "test-common.h" static int of_unittest_check_node_linkage(struct device_node *np) { @@ -1200,143 +1025,6 @@ static void of_unittest_platform_populate(struct kunit *test) of_node_put(np); } -/** - * update_node_properties - adds the properties - * of np into dup node (present in live tree) and - * updates parent of children of np to dup. - * - * @np: node already present in live tree - * @dup: node present in live tree to be updated - */ -static void update_node_properties(struct device_node *np, - struct device_node *dup) -{ - struct property *prop; - struct device_node *child; - - for_each_property_of_node(np, prop) - of_add_property(dup, prop); - - for_each_child_of_node(np, child) - child->parent = dup; -} - -/** - * attach_node_and_children - attaches nodes - * and its children to live tree - * - * @np: Node to attach to live tree - */ -static int attach_node_and_children(struct device_node *np) -{ - struct device_node *next, *dup, *child; - unsigned long flags; - const char *full_name; - - full_name = kasprintf(GFP_KERNEL, "%pOF", np); - dup = of_find_node_by_path(full_name); - kfree(full_name); - if (dup) { - update_node_properties(np, dup); - return 0; - } - - child = np->child; - np->child = NULL; - - mutex_lock(&of_mutex); - raw_spin_lock_irqsave(&devtree_lock, flags); - np->sibling = np->parent->child; - np->parent->child = np; - of_node_clear_flag(np, OF_DETACHED); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - - __of_attach_node_sysfs(np); - mutex_unlock(&of_mutex); - - while (child) { - next = child->sibling; - attach_node_and_children(child); - child = next; - } - - return 0; -} - -/** - * unittest_data_add - Reads, copies data from - * linked tree and attaches it to the live tree - */ -static int unittest_data_add(void) -{ - void *unittest_data; - struct device_node *unittest_data_node, *np; - /* - * __dtb_testcases_begin[] and __dtb_testcases_end[] are magically - * created by cmd_dt_S_dtb in scripts/Makefile.lib - */ - extern uint8_t __dtb_testcases_begin[]; - extern uint8_t __dtb_testcases_end[]; - const int size = __dtb_testcases_end - __dtb_testcases_begin; - int rc; - - if (!size) { - pr_warn("%s: No testcase data to attach; not running tests\n", - __func__); - return -ENODATA; - } - - /* creating copy */ - unittest_data = kmemdup(__dtb_testcases_begin, size, GFP_KERNEL); - - if (!unittest_data) { - pr_warn("%s: Failed to allocate memory for unittest_data; " - "not running tests\n", __func__); - return -ENOMEM; - } - of_fdt_unflatten_tree(unittest_data, NULL, &unittest_data_node); - if (!unittest_data_node) { - pr_warn("%s: No tree to attach; not running tests\n", __func__); - return -ENODATA; - } - - /* - * This lock normally encloses of_resolve_phandles() - */ - of_overlay_mutex_lock(); - - rc = of_resolve_phandles(unittest_data_node); - if (rc) { - pr_err("%s: Failed to resolve phandles (rc=%i)\n", __func__, rc); - of_overlay_mutex_unlock(); - return -EINVAL; - } - - if (!of_root) { - of_root = unittest_data_node; - for_each_of_allnodes(np) - __of_attach_node_sysfs(np); - of_aliases = of_find_node_by_path("/aliases"); - of_chosen = of_find_node_by_path("/chosen"); - of_overlay_mutex_unlock(); - return 0; - } - - /* attach the sub-tree to live tree */ - np = unittest_data_node->child; - while (np) { - struct device_node *next = np->sibling; - - np->parent = of_root; - attach_node_and_children(np); - np = next; - } - - of_overlay_mutex_unlock(); - - return 0; -} - #ifdef CONFIG_OF_OVERLAY static int overlay_data_apply(const char *overlay_name, int *overlay_id); @@ -2560,8 +2248,6 @@ static int of_test_init(struct kunit *test) static struct kunit_case of_test_cases[] = { KUNIT_CASE(of_unittest_check_tree_linkage), KUNIT_CASE(of_unittest_check_phandles), - KUNIT_CASE(of_unittest_find_node_by_name), - KUNIT_CASE(of_unittest_dynamic), KUNIT_CASE(of_unittest_parse_phandle_with_args), KUNIT_CASE(of_unittest_parse_phandle_with_args_map), KUNIT_CASE(of_unittest_printf), From patchwork Wed Nov 28 19:36:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10703267 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5BB4616B1 for ; Wed, 28 Nov 2018 19:38:41 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A6D302C24C for ; Wed, 28 Nov 2018 19:38:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 980E02C2D0; Wed, 28 Nov 2018 19:38:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.5 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, USER_IN_DEF_DKIM_WL autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 912FC2C1F2 for ; Wed, 28 Nov 2018 19:38:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727801AbeK2GlQ (ORCPT ); Thu, 29 Nov 2018 01:41:16 -0500 Received: from mail-pf1-f202.google.com ([209.85.210.202]:50169 "EHLO mail-pf1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729581AbeK2GlP (ORCPT ); Thu, 29 Nov 2018 01:41:15 -0500 Received: by mail-pf1-f202.google.com with SMTP id s14so17620174pfk.16 for ; Wed, 28 Nov 2018 11:38:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=sp1kHit6/X9Lx+5ulpiw22kk2mTJzGayTEtKJslfu0M=; b=cUAKg+1ytLb0pY8GwDhl5PnPY+gnRraLOQn7pPToK80SRIrBbyYPuz4VONEeiPCIaS egCHbfWmzSbjfg+YpAZ6ZAY38gzmlj7XU4u9nIlUiKWmHOTBZkCA75h2GPHjXeQWSBUk S49GT4KEyQ2aQpIQe8TwPnoY1Voa1tlI5oJdUjnnVojjOoy739r2UP88WNR6xGNQNnYk yKMpSkxOVKd9UHOiuVaNRmksDTh4Y27DZiLzwWxFyHt2R7pwVj61ZugbL9f1T+aEoKI1 jtmUsh2jy4SXcRS2IrzSYVFRrh1YmG2K1zsQUog8yDAOXpYkjI8nHwE0Gik9laovSOT0 vhmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=sp1kHit6/X9Lx+5ulpiw22kk2mTJzGayTEtKJslfu0M=; b=nYg/FtxegxRASDOiI4JF0x0eWyogyUraakHon/AQMbJpcTwr4T+B4HhLeD51j8a89Z 1ymw2w3QkISSjdQnsqzx1Gq9GsEtRqI+Hfv0F3zKxHxIDFpJ5ZF7cA5alNOs4MNpwxvh W4YdDsOYOdYA8ZfiBnB7kJ5H9U/JvKxq2mAfVgAh/2Jbz2/xF0Z8o9ihtSIJGK9+XG5a WF3zA3PJJFw/RIDNjNkOkcAu16ZB8QjfYE/KzuX6Hp9WdZyBKgj0YCZLrEPXP4WdtDTi WTFHNhr2gzHsWWEpFTlNzLjiQbZG53bidXgIEdprSd6Ay/ehxeBzlwB9CMcEnBy9hsRR 0m5A== X-Gm-Message-State: AA+aEWYnqZ4mceLPDgQnzX+LUOLffdwDwZ5Gy4X9wHIbcZhvPN/b/1UZ pxIcDjrONg4PsOY0tX1YBdyRaNriVcaok9IPnCTQvA== X-Google-Smtp-Source: AFSGD/WyGpNr9HezowCOD9aoWQFZsmx2Xti5ZlOL81q+feMoxp/0/SBD8aWH84nJe7EVhod1MJODqckNOEfdcJ2r7NXM+A== X-Received: by 2002:a17:902:4483:: with SMTP id l3mr2550600pld.16.1543433910261; Wed, 28 Nov 2018 11:38:30 -0800 (PST) Date: Wed, 28 Nov 2018 11:36:36 -0800 In-Reply-To: <20181128193636.254378-1-brendanhiggins@google.com> Message-Id: <20181128193636.254378-20-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181128193636.254378-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.20.0.rc0.387.gc7a69e6b6c-goog Subject: [RFC v3 19/19] of: unittest: split up some super large test cases From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org Cc: joel@jms.id.au, mpe@ellerman.id.au, joe@perches.com, brakmo@fb.com, rostedt@goodmis.org, Tim.Bird@sony.com, khilman@baylibre.com, julia.lawall@lip6.fr, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org, jdike@addtoit.com, richard@nod.at, linux-um@lists.infradead.org, daniel@ffwll.ch, dri-devel@lists.freedesktop.org, robh@kernel.org, dan.j.williams@intel.com, linux-nvdimm@lists.01.org, kieran.bingham@ideasonboard.com, frowand.list@gmail.com, knut.omang@oracle.com, Brendan Higgins Sender: linux-kselftest-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kselftest@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Split up the super large test cases of_unittest_find_node_by_name and of_unittest_dynamic into properly sized and defined test cases. Signed-off-by: Brendan Higgins --- drivers/of/base-test.c | 315 ++++++++++++++++++++++++++++++++++------- 1 file changed, 260 insertions(+), 55 deletions(-) diff --git a/drivers/of/base-test.c b/drivers/of/base-test.c index 5731787a3fca8..46c3dd9ce6628 100644 --- a/drivers/of/base-test.c +++ b/drivers/of/base-test.c @@ -8,10 +8,10 @@ #include "test-common.h" -static void of_unittest_find_node_by_name(struct kunit *test) +static void of_test_find_node_by_name_basic(struct kunit *test) { struct device_node *np; - const char *options, *name; + const char *name; np = of_find_node_by_path("/testcase-data"); name = kasprintf(GFP_KERNEL, "%pOF", np); @@ -20,11 +20,21 @@ static void of_unittest_find_node_by_name(struct kunit *test) "find /testcase-data failed\n"); of_node_put(np); kfree(name); +} +static void of_test_find_node_by_name_trailing_slash(struct kunit *test) +{ /* Test if trailing '/' works */ KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("/testcase-data/"), NULL, "trailing '/' on /testcase-data/ should fail\n"); +} + +static void of_test_find_node_by_name_multiple_components(struct kunit *test) +{ + struct device_node *np; + const char *name; + np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); name = kasprintf(GFP_KERNEL, "%pOF", np); @@ -33,6 +43,12 @@ static void of_unittest_find_node_by_name(struct kunit *test) "find /testcase-data/phandle-tests/consumer-a failed\n"); of_node_put(np); kfree(name); +} + +static void of_test_find_node_by_name_with_alias(struct kunit *test) +{ + struct device_node *np; + const char *name; np = of_find_node_by_path("testcase-alias"); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); @@ -41,10 +57,23 @@ static void of_unittest_find_node_by_name(struct kunit *test) "find testcase-alias failed\n"); of_node_put(np); kfree(name); +} +static void of_test_find_node_by_name_with_alias_and_slash(struct kunit *test) +{ /* Test if trailing '/' works on aliases */ KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("testcase-alias/"), NULL, - "trailing '/' on testcase-alias/ should fail\n"); + "trailing '/' on testcase-alias/ should fail\n"); +} + +/* + * TODO(brendanhiggins@google.com): This looks like a duplicate of + * of_test_find_node_by_name_multiple_components + */ +static void of_test_find_node_by_name_multiple_components_2(struct kunit *test) +{ + struct device_node *np; + const char *name; np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a"); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); @@ -54,29 +83,60 @@ static void of_unittest_find_node_by_name(struct kunit *test) "find testcase-alias/phandle-tests/consumer-a failed\n"); of_node_put(np); kfree(name); +} + +static void of_test_find_node_by_name_missing_path(struct kunit *test) +{ + struct device_node *np; KUNIT_EXPECT_EQ_MSG(test, - of_find_node_by_path("/testcase-data/missing-path"), + np = of_find_node_by_path( + "/testcase-data/missing-path"), NULL, - "non-existent path returned node %pOF\n", np); + "non-existent path returned node %pOF\n", np); of_node_put(np); +} - KUNIT_EXPECT_EQ_MSG(test, of_find_node_by_path("missing-alias"), NULL, - "non-existent alias returned node %pOF\n", np); +static void of_test_find_node_by_name_missing_alias(struct kunit *test) +{ + struct device_node *np; + + KUNIT_EXPECT_EQ_MSG(test, + np = of_find_node_by_path("missing-alias"), NULL, + "non-existent alias returned node %pOF\n", np); of_node_put(np); +} + +static void of_test_find_node_by_name_missing_alias_with_relative_path( + struct kunit *test) +{ + struct device_node *np; KUNIT_EXPECT_EQ_MSG(test, - of_find_node_by_path("testcase-alias/missing-path"), + np = of_find_node_by_path( + "testcase-alias/missing-path"), NULL, - "non-existent alias with relative path returned node %pOF\n", - np); + "non-existent alias with relative path returned node %pOF\n", + np); of_node_put(np); +} + +static void of_test_find_node_by_name_with_option(struct kunit *test) +{ + struct device_node *np; + const char *options; np = of_find_node_opts_by_path("/testcase-data:testoption", &options); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); KUNIT_EXPECT_STREQ_MSG(test, "testoption", options, "option path test failed\n"); of_node_put(np); +} + +static void of_test_find_node_by_name_with_option_and_slash(struct kunit *test) +{ + struct device_node *np; + const char *options; np = of_find_node_opts_by_path("/testcase-data:test/option", &options); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); @@ -91,11 +151,22 @@ static void of_unittest_find_node_by_name(struct kunit *test) KUNIT_EXPECT_STREQ_MSG(test, "test/option", options, "option path test, subcase #2 failed\n"); of_node_put(np); +} + +static void of_test_find_node_by_name_with_null_option(struct kunit *test) +{ + struct device_node *np; np = of_find_node_opts_by_path("/testcase-data:testoption", NULL); KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, np, "NULL option path test failed\n"); of_node_put(np); +} + +static void of_test_find_node_by_name_with_option_alias(struct kunit *test) +{ + struct device_node *np; + const char *options; np = of_find_node_opts_by_path("testcase-alias:testaliasoption", &options); @@ -103,6 +174,13 @@ static void of_unittest_find_node_by_name(struct kunit *test) KUNIT_EXPECT_STREQ_MSG(test, "testaliasoption", options, "option alias path test failed\n"); of_node_put(np); +} + +static void of_test_find_node_by_name_with_option_alias_and_slash( + struct kunit *test) +{ + struct device_node *np; + const char *options; np = of_find_node_opts_by_path("testcase-alias:test/alias/option", &options); @@ -110,11 +188,22 @@ static void of_unittest_find_node_by_name(struct kunit *test) KUNIT_EXPECT_STREQ_MSG(test, "test/alias/option", options, "option alias path test, subcase #1 failed\n"); of_node_put(np); +} + +static void of_test_find_node_by_name_with_null_option_alias(struct kunit *test) +{ + struct device_node *np; np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL); - KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG(test, np, - "NULL option alias path test failed\n"); + KUNIT_EXPECT_NOT_ERR_OR_NULL_MSG( + test, np, "NULL option alias path test failed\n"); of_node_put(np); +} + +static void of_test_find_node_by_name_option_clearing(struct kunit *test) +{ + struct device_node *np; + const char *options; options = "testoption"; np = of_find_node_opts_by_path("testcase-alias", &options); @@ -122,6 +211,12 @@ static void of_unittest_find_node_by_name(struct kunit *test) KUNIT_EXPECT_EQ_MSG(test, options, NULL, "option clearing test failed\n"); of_node_put(np); +} + +static void of_test_find_node_by_name_option_clearing_root(struct kunit *test) +{ + struct device_node *np; + const char *options; options = "testoption"; np = of_find_node_opts_by_path("/", &options); @@ -131,64 +226,147 @@ static void of_unittest_find_node_by_name(struct kunit *test) of_node_put(np); } -static void of_unittest_dynamic(struct kunit *test) +static int of_test_find_node_by_name_init(struct kunit *test) { + /* adding data for unittest */ + KUNIT_ASSERT_EQ(test, 0, unittest_data_add()); + + if (!of_aliases) + of_aliases = of_find_node_by_path("/aliases"); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, of_find_node_by_path( + "/testcase-data/phandle-tests/consumer-a")); + + return 0; +} + +static struct kunit_case of_test_find_node_by_name_cases[] = { + KUNIT_CASE(of_test_find_node_by_name_basic), + KUNIT_CASE(of_test_find_node_by_name_trailing_slash), + KUNIT_CASE(of_test_find_node_by_name_multiple_components), + KUNIT_CASE(of_test_find_node_by_name_with_alias), + KUNIT_CASE(of_test_find_node_by_name_with_alias_and_slash), + KUNIT_CASE(of_test_find_node_by_name_multiple_components_2), + KUNIT_CASE(of_test_find_node_by_name_missing_path), + KUNIT_CASE(of_test_find_node_by_name_missing_alias), + KUNIT_CASE(of_test_find_node_by_name_missing_alias_with_relative_path), + KUNIT_CASE(of_test_find_node_by_name_with_option), + KUNIT_CASE(of_test_find_node_by_name_with_option_and_slash), + KUNIT_CASE(of_test_find_node_by_name_with_null_option), + KUNIT_CASE(of_test_find_node_by_name_with_option_alias), + KUNIT_CASE(of_test_find_node_by_name_with_option_alias_and_slash), + KUNIT_CASE(of_test_find_node_by_name_with_null_option_alias), + KUNIT_CASE(of_test_find_node_by_name_option_clearing), + KUNIT_CASE(of_test_find_node_by_name_option_clearing_root), + {}, +}; + +static struct kunit_module of_test_find_node_by_name_module = { + .name = "of-test-find-node-by-name", + .init = of_test_find_node_by_name_init, + .test_cases = of_test_find_node_by_name_cases, +}; +module_test(of_test_find_node_by_name_module); + +struct of_test_dynamic_context { struct device_node *np; - struct property *prop; + struct property *prop0; + struct property *prop1; +}; - np = of_find_node_by_path("/testcase-data"); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, np); +static void of_test_dynamic_basic(struct kunit *test) +{ + struct of_test_dynamic_context *ctx = test->priv; + struct device_node *np = ctx->np; + struct property *prop0 = ctx->prop0; - /* Array of 4 properties for the purpose of testing */ - prop = kcalloc(4, sizeof(*prop), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prop); + /* Add a new property - should pass*/ + prop0->name = "new-property"; + prop0->value = "new-property-data"; + prop0->length = strlen(prop0->value) + 1; + KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop0), 0, + "Adding a new property failed\n"); + + /* Test that we can remove a property */ + KUNIT_EXPECT_EQ(test, of_remove_property(np, prop0), 0); +} + +static void of_test_dynamic_add_existing_property(struct kunit *test) +{ + struct of_test_dynamic_context *ctx = test->priv; + struct device_node *np = ctx->np; + struct property *prop0 = ctx->prop0, *prop1 = ctx->prop1; /* Add a new property - should pass*/ - prop->name = "new-property"; - prop->value = "new-property-data"; - prop->length = strlen(prop->value) + 1; - KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop), 0, + prop0->name = "new-property"; + prop0->value = "new-property-data"; + prop0->length = strlen(prop0->value) + 1; + KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop0), 0, "Adding a new property failed\n"); /* Try to add an existing property - should fail */ - prop++; - prop->name = "new-property"; - prop->value = "new-property-data-should-fail"; - prop->length = strlen(prop->value) + 1; - KUNIT_EXPECT_NE_MSG(test, of_add_property(np, prop), 0, + prop1->name = "new-property"; + prop1->value = "new-property-data-should-fail"; + prop1->length = strlen(prop1->value) + 1; + KUNIT_EXPECT_NE_MSG(test, of_add_property(np, prop1), 0, "Adding an existing property should have failed\n"); +} + +static void of_test_dynamic_modify_existing_property(struct kunit *test) +{ + struct of_test_dynamic_context *ctx = test->priv; + struct device_node *np = ctx->np; + struct property *prop0 = ctx->prop0, *prop1 = ctx->prop1; + + /* Add a new property - should pass*/ + prop0->name = "new-property"; + prop0->value = "new-property-data"; + prop0->length = strlen(prop0->value) + 1; + KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop0), 0, + "Adding a new property failed\n"); /* Try to modify an existing property - should pass */ - prop->value = "modify-property-data-should-pass"; - prop->length = strlen(prop->value) + 1; - KUNIT_EXPECT_EQ_MSG(test, of_update_property(np, prop), 0, + prop1->name = "new-property"; + prop1->value = "modify-property-data-should-pass"; + prop1->length = strlen(prop1->value) + 1; + KUNIT_EXPECT_EQ_MSG(test, of_update_property(np, prop1), 0, "Updating an existing property should have passed\n"); +} + +static void of_test_dynamic_modify_non_existent_property(struct kunit *test) +{ + struct of_test_dynamic_context *ctx = test->priv; + struct device_node *np = ctx->np; + struct property *prop0 = ctx->prop0; /* Try to modify non-existent property - should pass*/ - prop++; - prop->name = "modify-property"; - prop->value = "modify-missing-property-data-should-pass"; - prop->length = strlen(prop->value) + 1; - KUNIT_EXPECT_EQ_MSG(test, of_update_property(np, prop), 0, + prop0->name = "modify-property"; + prop0->value = "modify-missing-property-data-should-pass"; + prop0->length = strlen(prop0->value) + 1; + KUNIT_EXPECT_EQ_MSG(test, of_update_property(np, prop0), 0, "Updating a missing property should have passed\n"); +} - /* Remove property - should pass */ - KUNIT_EXPECT_EQ_MSG(test, of_remove_property(np, prop), 0, - "Removing a property should have passed\n"); +static void of_test_dynamic_large_property(struct kunit *test) +{ + struct of_test_dynamic_context *ctx = test->priv; + struct device_node *np = ctx->np; + struct property *prop0 = ctx->prop0; /* Adding very large property - should pass */ - prop++; - prop->name = "large-property-PAGE_SIZEx8"; - prop->length = PAGE_SIZE * 8; - prop->value = kzalloc(prop->length, GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prop->value); - KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop), 0, + prop0->name = "large-property-PAGE_SIZEx8"; + prop0->length = PAGE_SIZE * 8; + prop0->value = kunit_kzalloc(test, prop0->length, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prop0->value); + + KUNIT_EXPECT_EQ_MSG(test, of_add_property(np, prop0), 0, "Adding a large property should have passed\n"); } -static int of_test_init(struct kunit *test) +static int of_test_dynamic_init(struct kunit *test) { - /* adding data for unittest */ + struct of_test_dynamic_context *ctx; + KUNIT_ASSERT_EQ(test, 0, unittest_data_add()); if (!of_aliases) @@ -197,18 +375,45 @@ static int of_test_init(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, of_find_node_by_path( "/testcase-data/phandle-tests/consumer-a")); + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + test->priv = ctx; + + ctx->np = of_find_node_by_path("/testcase-data"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->np); + + ctx->prop0 = kunit_kzalloc(test, sizeof(*ctx->prop0), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->prop0); + + ctx->prop1 = kunit_kzalloc(test, sizeof(*ctx->prop1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->prop1); + return 0; } -static struct kunit_case of_test_cases[] = { - KUNIT_CASE(of_unittest_find_node_by_name), - KUNIT_CASE(of_unittest_dynamic), +static void of_test_dynamic_exit(struct kunit *test) +{ + struct of_test_dynamic_context *ctx = test->priv; + struct device_node *np = ctx->np; + + of_remove_property(np, ctx->prop0); + of_remove_property(np, ctx->prop1); + of_node_put(np); +} + +static struct kunit_case of_test_dynamic_cases[] = { + KUNIT_CASE(of_test_dynamic_basic), + KUNIT_CASE(of_test_dynamic_add_existing_property), + KUNIT_CASE(of_test_dynamic_modify_existing_property), + KUNIT_CASE(of_test_dynamic_modify_non_existent_property), + KUNIT_CASE(of_test_dynamic_large_property), {}, }; -static struct kunit_module of_test_module = { - .name = "of-base-test", - .init = of_test_init, - .test_cases = of_test_cases, +static struct kunit_module of_test_dynamic_module = { + .name = "of-dynamic-test", + .init = of_test_dynamic_init, + .exit = of_test_dynamic_exit, + .test_cases = of_test_dynamic_cases, }; -module_test(of_test_module); +module_test(of_test_dynamic_module);