From patchwork Wed May 1 23:01:10 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925723 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 D7643912 for ; Wed, 1 May 2019 23:02:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C586928F63 for ; Wed, 1 May 2019 23:02:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B89C328F65; Wed, 1 May 2019 23:02:54 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id E202128F6B for ; Wed, 1 May 2019 23:02:53 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id C2E5621A070B8; Wed, 1 May 2019 16:02:53 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::749; helo=mail-qk1-x749.google.com; envelope-from=3mixkxa4kdpkcsfoeboijhhjothpphmf.dpnmjovy-owejnnmjtut.12.psh@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-qk1-x749.google.com (mail-qk1-x749.google.com [IPv6:2607:f8b0:4864:20::749]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 00B342120ADE0 for ; Wed, 1 May 2019 16:02:51 -0700 (PDT) Received: by mail-qk1-x749.google.com with SMTP id t5so632642qkt.23 for ; Wed, 01 May 2019 16:02:51 -0700 (PDT) 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=kFWtAgBPGj3VtorE+bfOf8XMNY/MRN76S4Rd4LwqO+o=; b=kv72hmQ+vEjebFzuxGVQmgBW+v+GmP+oBFZM9w7rI+SbI4txdaYC7jQeYk4sjCeHht 0z1+guCNxNq3sI01l0jNqt+q5NPd50ZvX8Ajqx4ygNgzRCWMjTnaHov8YemBdfusXoO4 z3S5Wx4xCUK2kSfKWJgGqNB//bo9Dt15/BkgoIrAWF3L7cJKh93poIUnvZE7e6R8JWhc N3gPAKM4xDg5lws8Ytx/fjfMwkfqAcrLYNpBRIWIAZrsikHM+lB+SkQIfI3u0DXNR/Nr VpUAPtUTrf97DpczXKcSji005O1WNv+GmMbeMRKVuFC3YaUQr2+c7qoGgw9zysm26Blw 5c+g== 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=kFWtAgBPGj3VtorE+bfOf8XMNY/MRN76S4Rd4LwqO+o=; b=sIvV7ZzixNiZZXccS5mSRBMea5cJuKzXhXLRUFdx1AzGTZVFZa2w6zMKfIwyXmJDYN 7JTwFYSer32RtJ2tIBSXdL7iT/yBhWyIF06Pny7W+D8RKp/5jpr8exZWmmyA2YTH7mW0 qULOFDHDpS7nk5gc+tApRA67czw1LaxlOUufu4gAi63yNDD0t7owzyCUJzcl1UDhlPqA t/aVnDExsEnkSj6kXZztkBcaHpGDBAoa5NM5H/2fp7BP16SC6whyr1SNl0Mj4DI+6xBK SVRa7FAH5kaDHAKLq1fLpZpVOuV49vRcs+JwKD5+PE0ANcYSXXGThO3um5vVFBagPDoC cNWQ== X-Gm-Message-State: APjAAAWb1Afxw9M1YvzgKF/LYuZ8COhU5HWToomPr19+so9fjnvFLIhg 9DAb5Q4H3q4t5qQVDUwIa+V9DHNjMDPuoopriMeRVQ== X-Google-Smtp-Source: APXvYqwZ7F52In6i6HNcgyh0/dAU4PsSOsGxH5cHQ2Bx9fHgV5ifum4WrM4I+awJiteEsIjhPbCah4cevlUJnEfMHGw8yg== X-Received: by 2002:a0c:d6ce:: with SMTP id l14mr673149qvi.2.1556751770279; Wed, 01 May 2019 16:02:50 -0700 (PDT) Date: Wed, 1 May 2019 16:01:10 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-2-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 01/17] kunit: test: add KUnit test runner core From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" 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 | 16 +++++ kunit/Makefile | 1 + kunit/test.c | 168 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 350 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..23c2ebedd6dd9 --- /dev/null +++ b/include/kunit/test.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Base unit test (KUnit) API. + * + * Copyright (C) 2019, 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..64480092b2c24 --- /dev/null +++ b/kunit/Kconfig @@ -0,0 +1,16 @@ +# +# KUnit base configuration +# + +menu "KUnit support" + +config KUNIT + bool "Enable support for unit tests (KUnit)" + 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..5bf97e2935579 --- /dev/null +++ b/kunit/test.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Base unit test (KUnit) API. + * + * Copyright (C) 2019, 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\n", 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\n", + test_case->name, + success ? "passed" : "failed"); + } + + if (all_passed) + kunit_info(&test, "all tests passed\n"); + else + kunit_info(&test, "one or more tests failed\n"); + + 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 May 1 23:01:11 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925727 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 11BA3912 for ; Wed, 1 May 2019 23:03:04 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 01E2428F63 for ; Wed, 1 May 2019 23:03:04 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E9C0A28F7A; Wed, 1 May 2019 23:03:03 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 3064828F63 for ; Wed, 1 May 2019 23:03:03 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 23943212377E4; Wed, 1 May 2019 16:03:03 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::74a; helo=mail-qk1-x74a.google.com; envelope-from=3pcxkxa4kdaugwjsifsmnllnsxlttlqj.htrqnsz2-s0inrrqnxyx.56.twl@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-qk1-x74a.google.com (mail-qk1-x74a.google.com [IPv6:2607:f8b0:4864:20::74a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id CFB182120ADF4 for ; Wed, 1 May 2019 16:03:01 -0700 (PDT) Received: by mail-qk1-x74a.google.com with SMTP id u65so655911qkd.17 for ; Wed, 01 May 2019 16:03:01 -0700 (PDT) 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=WBzJ19nC8IuqkBcKxxV42TfRISABudjBoJLmYR5hSKo=; b=ilaIbocGv7xrPb0aAy+7Bg6GwWLHnZUp6L6/cNcZkqEinCfdGZ0etp24jSVtgAG2Sb 6rIAt3IUgY//P5EioZajVMSt7mN3cYTiLSEuUVozB+uL0TBjCOi6Mt5F43FCZmvvPE7O h5K8qrSuPXRVkv8Jm4XJMjOmzMEx27gHboiyI44vk7ObllgE331cHf/Ru0GbHK4lXRwq 7f3z9b0NyF3ZOgdTXzhgeaFJ6SVHyYLpP44s8SF0fx2GUqkHKCOmgB+xlIE80la5yxvr AG9U4wBIQ1P5AyHP6iBYGk7vcBp3mYFRV2q7rG0g9LGwQ0My+FCju1odtsjf0t/oq1iM 7D0Q== 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=WBzJ19nC8IuqkBcKxxV42TfRISABudjBoJLmYR5hSKo=; b=KArfpKi3n42P08dFS+AiHC1GAx4p1/An6Tcqwj5fbiZJYhwZWliJTY6PlaCq/LkCQU KbWmeibDfKXCi67T5pA8kMp3gqdHFAJAy45G+S2H1lVq14yn95YdOzZM9Ny+RA96suTm EboOoU0iHhcIgeM9bO0kBqaj8wWp4H1ugA4lFy1Elt49GqsYTOyQzyIwyQ7NydjY5lj9 v7sOgFSwxAfjpIVOdSlhPKOtZg56f6u43WVTld4VIkWx/PtBzoeJo596rryI2/JJEBoh wdFNxCnbAFX9BUShjyji9t/TXU3OPdeoUYlsLB/vOqt9wB/cYHowU310ztBIaPJLLYqR 6Nug== X-Gm-Message-State: APjAAAVHbnNWWGAOOeG6vZhU41ZjY7SxLE8+GcnaTnK54ACBiwOC9qMM FYIWXhye76zrFbMpnvBp1+y/Dysb4gF8WWx3s97Rlg== X-Google-Smtp-Source: APXvYqwYxsnAxJym2b9e5x/7Qaxf348dkQ4h5lFxzEuYKroNsf/GILXwwBIlSIdJCXhACkbYLcwEi9jYbbIFVj8HtFw+Vw== X-Received: by 2002:ac8:26e1:: with SMTP id 30mr559535qtp.305.1556751780638; Wed, 01 May 2019 16:03:00 -0700 (PDT) Date: Wed, 1 May 2019 16:01:11 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-3-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 02/17] kunit: test: add test resource management API From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" 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 23c2ebedd6dd9..819edd8db4e81 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 to the init function. + * + * 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 5bf97e2935579..541f9adb1608c 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 May 1 23:01:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925741 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 2F322933 for ; Wed, 1 May 2019 23:03:13 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1FB2328F60 for ; Wed, 1 May 2019 23:03:13 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1401E28F6B; Wed, 1 May 2019 23:03:13 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 90BD328F60 for ; Wed, 1 May 2019 23:03:12 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 5A52D212377E5; Wed, 1 May 2019 16:03:12 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::849; helo=mail-qt1-x849.google.com; envelope-from=3rsxkxa4kda4p5s1ro1vwuuw16u22uzs.q20zw18b-19rw00zw676.ef.25u@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-qt1-x849.google.com (mail-qt1-x849.google.com [IPv6:2607:f8b0:4864:20::849]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id A99B6212377E2 for ; Wed, 1 May 2019 16:03:10 -0700 (PDT) Received: by mail-qt1-x849.google.com with SMTP id f48so325249qtk.17 for ; Wed, 01 May 2019 16:03:10 -0700 (PDT) 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=IzqxQT7jsHC3pdKh/vHgviJNyBbjnFac75RkNa3+hdY=; b=sGJCDhDHzL4+6Xf33NRGdp9J2J2NKagWonrzxFNvxH4esajJdCH1lvWkC+VnYAmoSF F4/6LUSKaWS2597garPIIpQcaFBdzCYRb2fubnUHTmTL1TvdqZMU+AIVRmHyNWsCcgTn zffbgvX2sF+RH6Ci73pr9KOouIRw/b4cQAngonJdX/gI0FDqvbsR9ltQtQxdqCzCqv3a 6OcK1IhALgX7ycvXl93cjEgyGu2i7cMu/nN1dE9w8Ji78YjA0zdrOKtLIxRWf1tGjjnm kkqmaTuZzpQeatG6lNwMGgt01UlLDADWIJay+toOzCYKwPgJu7kYIONbV19QDSHOXTxu 0P2Q== 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=IzqxQT7jsHC3pdKh/vHgviJNyBbjnFac75RkNa3+hdY=; b=q9w4npfLji/J7Ukvdyrm12np16W0BXRIqMaI1w6pRi1QmKDR25jpBJW24PzdzzmDE6 K2nMckwtXA3NcJlJ6ENJihf0uWqNrph5zmELIgamHiTgQyvZp6j/Prx1GXs2qii9Qv9E V8hmAvMb8i7Ytg4e74TrkN7s/3JxA4FJyQE/kJFJkLy2VfCre3Ym+Nj/xBmmhbJY3S5G bI109qX5nKLNYumoLRSrC8EAEK18Cupqf2ekRpCQ/xuKeRBCboVRJ+F9UpsH6trcaMYw +o2nxjW8BcXjBaZbvBIAXdEVdMzYuw8lxbARdxX/8ltF2kdoKI3bj7gapTCdbe6DuIrq WF/Q== X-Gm-Message-State: APjAAAWWfRGBnJjLbK5K3cXjwcgozkDvrhq9JeCPUkK+9xKsHF2xZfmB GQOBazxsXoPnh2I0PEyYblxgcEmd4D/31bMhKQbw2Q== X-Google-Smtp-Source: APXvYqwT8Fz2u7wM1L2XKjcUUCuxqZ8Ij4j/4bmAnposiCgOw8xOk308pVfIwSr+9K6imggTT/x6mxXNffcqOQRc8sb+lA== X-Received: by 2002:a0c:96eb:: with SMTP id b40mr633284qvd.131.1556751789360; Wed, 01 May 2019 16:03:09 -0700 (PDT) Date: Wed, 1 May 2019 16:01:12 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-4-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 03/17] kunit: test: add string_stream a std::stream like string builder From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" 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 | 51 ++++++++++++ kunit/Makefile | 3 +- kunit/string-stream.c | 144 ++++++++++++++++++++++++++++++++++ 3 files changed, 197 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..567a4629406da --- /dev/null +++ b/include/kunit/string-stream.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * C++ stream style string builder used in KUnit for building messages. + * + * Copyright (C) 2019, 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; +}; + +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); + +int string_stream_add(struct string_stream *this, const char *fmt, ...); + +int string_stream_vadd(struct string_stream *this, + const char *fmt, + va_list args); + +char *string_stream_get_string(struct string_stream *this); + +void string_stream_clear(struct string_stream *this); + +bool string_stream_is_empty(struct string_stream *this); + +#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..7018194ecf2fa --- /dev/null +++ b/kunit/string-stream.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * C++ stream style string builder used in KUnit for building messages. + * + * Copyright (C) 2019, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include + +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; +} + +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; +} + +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); +} + +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; +} + +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) +{ + string_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); + 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 May 1 23:01:13 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925749 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 828F6933 for ; Wed, 1 May 2019 23:03:29 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 703A828F63 for ; Wed, 1 May 2019 23:03:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6429C28F6B; Wed, 1 May 2019 23:03: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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id B879F28F65 for ; Wed, 1 May 2019 23:03:27 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 9A18F212377EB; Wed, 1 May 2019 16:03:27 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::84a; helo=mail-qt1-x84a.google.com; envelope-from=3vcxkxa4kdb04k7g63gab99bgl9hh9e7.5hfebgnq-go6bffeblml.tu.hk9@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-qt1-x84a.google.com (mail-qt1-x84a.google.com [IPv6:2607:f8b0:4864:20::84a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 8DFFB2120ADE0 for ; Wed, 1 May 2019 16:03:25 -0700 (PDT) Received: by mail-qt1-x84a.google.com with SMTP id f53so316203qte.21 for ; Wed, 01 May 2019 16:03:25 -0700 (PDT) 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=urCLrOiw73Vv5m0tMcCZvTwJh/aJ5kKPWwQUr8lpbDs=; b=v3lZRqq8dgL9+PqnTUTbY+8dJh4vp53nIOdXSIzGhHpcQ0w6vRo1cvTduoA4Nj1eHO Vj1iscYKcGHhJT1LDHo8rdN4jjC9NUVjk4dIx4jQtvhcXEgglk4duUVMreW99xnbfCvc bSH283YpBrzoc5hyBIq5VaCNQgTA5Go0LwNo98GpxaGYnHQOqpwwWwPEOsJ6eFfp2kzQ W9BbQpk5TAL2/OVhivKy9yEhBjRCwIKJf6tje/aOObay9RAn9ha33Xzn0VmEpWnRGIld RrBcIhMCibkbFSaJA/uzVkDRd1srcEaUzbR2flslENnIUlVc8jnqbwDWrLThgi8BlCDk kYlw== 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=urCLrOiw73Vv5m0tMcCZvTwJh/aJ5kKPWwQUr8lpbDs=; b=uV89kacLZ6/u/taQ4uhWu3xHofjacESl36tbRjKaGy14vzkLCVVWFkDsqqwxXXd32L 2a2FwwvhSrzpDgN5eLCd+2pCHXNazghSz76rlTT1gFOxhtlAz25ZojSNVGlctVZPpXkp xV2Si+gaFq4q4KUowWEuo1ozi8wcVjmDCC+p9ghb1oRi8oKPNRp1mDNr24uZgc24VmDv 1fhRH9zSYtt1rQMEAK8zm3gPJqbJQqYXD3s8lGv+HChxS6QR5LSG9RkXUulbPhQLHt+Z 71ME8R49fcZ4Qxsr92ZF9+/KtYbSiS/LJQJYk9YTvw117e7GigGAI964BACurhqih3c7 O7VQ== X-Gm-Message-State: APjAAAU9+7piFUdLVVA+c1rOtDS5pEWVMrifxZPiLkSptKZq3KF5ozPL 7xWXwgUD8hryOV3SxGIJ+GvQ2f7lwdh7x/zNKBtoKQ== X-Google-Smtp-Source: APXvYqzuaah6eN+BnmU2QIpoTy8skknwndesM81SnMIUGPEVKZJRALywtm4GzLckHSPDXmcsRuwc+JtPytB6XBF7YEiU2A== X-Received: by 2002:a37:b584:: with SMTP id e126mr518765qkf.199.1556751804530; Wed, 01 May 2019 16:03:24 -0700 (PDT) Date: Wed, 1 May 2019 16:01:13 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-5-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 04/17] kunit: test: add kunit_stream a std::stream like logger From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" 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 | 85 ++++++++++++++++++++ include/kunit/test.h | 2 + kunit/Makefile | 3 +- kunit/kunit-stream.c | 149 +++++++++++++++++++++++++++++++++++ kunit/test.c | 8 ++ 5 files changed, 246 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..d457a54fe0100 --- /dev/null +++ b/include/kunit/kunit-stream.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * C++ stream style string formatter and printer used in KUnit for outputting + * KUnit messages. + * + * Copyright (C) 2019, 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. + * + * A std::stream style string builder. Allows messages to be built up and + * printed all at once. + */ +struct kunit_stream { + /* 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); + +/** + * kunit_stream_set_level(): sets the level that string should be printed at. + * @this: the stream being operated on. + * @level: the print level the stream is set to output to. + * + * Sets the print level at which the stream outputs. + */ +void kunit_stream_set_level(struct kunit_stream *this, const char *level); + +/** + * kunit_stream_add(): adds the formatted input to the internal buffer. + * @this: the stream being operated on. + * @fmt: printf style format string to append to stream. + * + * Appends the formatted string, @fmt, to the internal buffer. + */ +void __printf(2, 3) kunit_stream_add(struct kunit_stream *this, + const char *fmt, ...); + +/** + * kunit_stream_append(): appends the contents of @other to @this. + * @this: the stream to which @other is appended. + * @other: the stream whose contents are appended to @this. + * + * Appends the contents of @other to @this. + */ +void kunit_stream_append(struct kunit_stream *this, struct kunit_stream *other); + +/** + * kunit_stream_commit(): prints out the internal buffer to the user. + * @this: the stream being operated on. + * + * Outputs the contents of the internal buffer as a kunit_printk formatted + * output. + */ +void kunit_stream_commit(struct kunit_stream *this); + +/** + * kunit_stream_clear(): clears the internal buffer. + * @this: the stream being operated on. + * + * Clears the contents of the internal buffer. + */ +void kunit_stream_clear(struct kunit_stream *this); + +#endif /* _KUNIT_KUNIT_STREAM_H */ diff --git a/include/kunit/test.h b/include/kunit/test.h index 819edd8db4e81..4668e8a635954 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..93c14eec03844 --- /dev/null +++ b/kunit/kunit-stream.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * C++ stream style string formatter and printer used in KUnit for outputting + * KUnit messages. + * + * Copyright (C) 2019, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include + +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; +} + +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); +} + +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 (string_stream_vadd(stream, fmt, args) < 0) + kunit_err(this->test, "Failed to allocate fragment: %s\n", fmt); + + va_end(args); +} + +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 = string_stream_get_string(other_stream); + + if (!other_content) { + kunit_err(this->test, + "Failed to get string from second argument for appending.\n"); + return; + } + + kunit_stream_add(this, other_content); +} + +void kunit_stream_clear(struct kunit_stream *this) +{ + string_stream_clear(this->internal_stream); +} + +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.\n"); + level = KERN_ERR; + kunit_stream_set_level(this, level); + } + + buf = string_stream_get_string(stream); + if (!buf) { + kunit_err(this->test, + "Could not allocate buffer, dumping stream:\n"); + list_for_each_entry(fragment, &stream->fragments, node) { + kunit_err(this->test, fragment->fragment); + } + kunit_err(this->test, "\n"); + goto cleanup; + } + + kunit_printk(level, this->test, buf); + kfree(buf); + +cleanup: + kunit_stream_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; + + return 0; +} + +static void kunit_stream_free(struct kunit_resource *res) +{ + struct kunit_stream *stream = res->allocation; + + if (!string_stream_is_empty(stream->internal_stream)) { + kunit_err(stream->test, + "End of test case reached with uncommitted stream entries.\n"); + kunit_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 541f9adb1608c..f7575b127e2df 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); + kunit_stream_set_level(stream, KERN_ERR); + kunit_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 May 1 23:01:14 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925759 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 2D094912 for ; Wed, 1 May 2019 23:03:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1AA0F28F63 for ; Wed, 1 May 2019 23:03:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0D66E28F7A; Wed, 1 May 2019 23:03:40 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 0444A28F65 for ; Wed, 1 May 2019 23:03:39 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id EC7BB212377E2; Wed, 1 May 2019 16:03:38 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::c4a; helo=mail-yw1-xc4a.google.com; envelope-from=3ycxkxa4kdckgwjsifsmnllnsxlttlqj.htrqnszc-sainrrqnxyx.fg.twl@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-yw1-xc4a.google.com (mail-yw1-xc4a.google.com [IPv6:2607:f8b0:4864:20::c4a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 5422C2120ADE0 for ; Wed, 1 May 2019 16:03:37 -0700 (PDT) Received: by mail-yw1-xc4a.google.com with SMTP id 11so1118526ywt.12 for ; Wed, 01 May 2019 16:03:37 -0700 (PDT) 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=ZGugbd6ry3Wlf19Kn+GtsjZdXaG5gr13Xptu5ffd5W8=; b=Blt9AVsNLDcY4dzWPpbbUqOR70Z2yqiMu11f00krd3MJTxoBrHCbZjPNMOfmqUzUlm JoQ2gF3twFejCu7TtwthLPhkQmvI+pmE8VakV2hrGG+DX+VgXkE4LGd4TJZlvNeBsZ8B GMxgX6Umco53wrVpRiic4BgicoDXY0d3Fhu4T2YzAv7oure8ZU1hPeaq4XIBQw7DROUO olkHb0KzDgdTN2n5ayE9AwiKUiGoU7pREdZe80Rc3xCTp3+wMiQjQ0nMpA5mqHfrpEiD YDcZh1Anpr8hUvFaGu1+eabGEvECX8y0t7TQJTMyyJqzlSefENyhxfKn//0WzqSrfw7s eHCA== 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=ZGugbd6ry3Wlf19Kn+GtsjZdXaG5gr13Xptu5ffd5W8=; b=YEFQlXXQXKTu9V+9FFWS8kfdA30P6a1+grkoo+l8EL4m53GDdk7o2juoUP1lNE/O4w bZCptecRdNU3mD9PYVviIsyIvO5ggBdlKfNsrLJI18JTpdICAP0MMi1I8qogfXg4dxIZ 4Y0s6rzxFsc/UOGWlnFTmxf6CGNLc6SjZBF36ZFINFhtR062zRY6zqUM2vwh7OQ/J9NQ Sij4WowadAl8DlXjER1S1FAgrnfRTsgPWXsfl80oJh0cbLB8r3CMh96366no/opxBvJl b6IFvbe9+7yzvZIdieihcWAfpm6wr8PDG9M2rJ1iDuek8lNX1HbAyJSD7oxTB15QrGMJ ZzLw== X-Gm-Message-State: APjAAAXyV6dRqnHcpR1Pyd0pixMNnZSn12Sr26K80hjZkJWd5iLFdTTB Ov94QdPoYOunSo3qXE4R6SXCCzdest2fS+/8kztI0g== X-Google-Smtp-Source: APXvYqzD/aP88Rwq3+lV5P2sQiGmwwwxO51iSW+eD4GJ96LdaOvHlPNRalIRxFHPHpVX9suc+9bmSwJN9Hgpb+Y/smWI9w== X-Received: by 2002:a25:c5c8:: with SMTP id v191mr422844ybe.52.1556751816142; Wed, 01 May 2019 16:03:36 -0700 (PDT) Date: Wed, 1 May 2019 16:01:14 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-6-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 05/17] kunit: test: add the concept of expectations From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" 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 | 419 +++++++++++++++++++++++++++++++++++++++++++ kunit/test.c | 34 ++++ 2 files changed, 453 insertions(+) diff --git a/include/kunit/test.h b/include/kunit/test.h index 4668e8a635954..e441270561ece 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -273,4 +273,423 @@ 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); + + kunit_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 + kunit_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); \ + \ + kunit_stream_add(__stream, message); \ + kunit_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); \ + \ + kunit_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, ...) do { \ + struct kunit_stream *__stream = KUNIT_EXPECT_START(test); \ + \ + kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \ + KUNIT_EXPECT_END(test, false, __stream); \ +} while (0) + +/** + * 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.\n") + +#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); + + kunit_stream_add(stream, + "Expected %s %s %s, but\n", + left_name, compare_name, right_name); + kunit_stream_add(stream, "\t\t%s == %lld\n", left_name, left); + kunit_stream_add(stream, "\t\t%s == %lld\n", 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); \ + \ + kunit_stream_add(__stream, "Expected " #left " == " #right ", but\n"); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \ + kunit_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); \ + \ + kunit_stream_add(__stream, "Expected " #left " == " #right ", but\n"); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #right, __right); \ + kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \ + \ + KUNIT_EXPECT_END(test, !strcmp(left, right), __stream); \ +} while (0) + +/** + * KUNIT_EXPECT_STRNEQ() - Expects that strings @left and @right are not 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 + * not equal. This is semantically equivalent to + * KUNIT_EXPECT_TRUE(@test, strcmp((@left), (@right))). See KUNIT_EXPECT_TRUE() + * for more information. + */ +#define KUNIT_EXPECT_STRNEQ(test, left, right) do { \ + struct kunit_stream *__stream = KUNIT_EXPECT_START(test); \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + \ + kunit_stream_add(__stream, "Expected " #left " != " #right ", but\n"); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #right, __right); \ + \ + KUNIT_EXPECT_END(test, strcmp(left, right), __stream); \ +} while (0) + +#define KUNIT_EXPECT_STRNEQ_MSG(test, left, right, fmt, ...) do { \ + struct kunit_stream *__stream = KUNIT_EXPECT_START(test); \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + \ + kunit_stream_add(__stream, "Expected " #left " != " #right ", but\n"); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #right, __right); \ + kunit_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) \ + kunit_stream_add(__stream, \ + "Expected " #ptr " is not null, but is."); \ + if (IS_ERR(__ptr)) \ + kunit_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) { \ + kunit_stream_add(__stream, \ + "Expected " #ptr " is not null, but is."); \ + kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \ + } \ + if (IS_ERR(__ptr)) { \ + kunit_stream_add(__stream, \ + "Expected " #ptr " is not error, but is: %ld", \ + PTR_ERR(__ptr)); \ + \ + kunit_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 f7575b127e2df..01e82c18b2fa6 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; + + kunit_stream_add(stream, + "Expected %s %s %s, but\n", + left_name, compare_name, right_name); + kunit_stream_add(stream, "\t\t%s == %lld\n", left_name, left); + kunit_stream_add(stream, "\t\t%s == %lld", right_name, right); + + if (fmt) { + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + kunit_stream_add(stream, "\n%pV", &vaf); + + va_end(args); + } + + kunit_expect_end(test, compare_result, stream); +} + From patchwork Wed May 1 23:01:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925767 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 AD2E8912 for ; Wed, 1 May 2019 23:03:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9C44B28F60 for ; Wed, 1 May 2019 23:03:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8F9E328F65; Wed, 1 May 2019 23:03:53 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 4ABF428F60 for ; Wed, 1 May 2019 23:03:53 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 3D288212377EE; Wed, 1 May 2019 16:03:53 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::c49; helo=mail-yw1-xc49.google.com; envelope-from=31ixkxa4kddcukxgwtgabzzbglzhhzex.vhfebgnq-gowbffeblml.tu.hkz@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-yw1-xc49.google.com (mail-yw1-xc49.google.com [IPv6:2607:f8b0:4864:20::c49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id E16162120ADE0 for ; Wed, 1 May 2019 16:03:51 -0700 (PDT) Received: by mail-yw1-xc49.google.com with SMTP id v83so1164965ywa.1 for ; Wed, 01 May 2019 16:03:51 -0700 (PDT) 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=D2ed1P31yZM+QgJhlTzUd+8uEk27FFwdOzf2d998Bjo=; b=m4vpSF6hFQAvyyKEfmi7ffOWm5Cf8YCWIkxIT3+iDVpIWfsS1ULEGwFu6Ost8hat7u rnhVAV5Vv9xJT6lOo63I/DHxpQiJpkpLAZN5u2XWO0V9OZ52Ydehqa5w7vEuBON6+gXZ 3STK4kvqQOndMzEtqqLGS2SsTTQyPWssIWHMj8ElJqdMrQ0MviI+sSHecTz/rzS/tJ10 CF6G0RJyeUDcEjoGw8kICUMbi8yNT0pIJerdnIVD9Od+WW+ixwtgrJBY3UFTTqTO2Fw3 Zh8c7NNZ6oqBIahVmM/STUzcD/MOIBXoqKt5JLGq4l8hekE/mIoIfmXWjCZu/3iEiU9+ WUKg== 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=D2ed1P31yZM+QgJhlTzUd+8uEk27FFwdOzf2d998Bjo=; b=ZEszHLxea4Orzu3J48vO4v0EM7lTwMvjP1eRcw8ENgeCfGkmeorRM6ediBBQapY/XZ HxFD9iAecyh3F/+w0mVA4Ei8Yv+BWlKDXeDSGA0bIBlcLDw2Wvgpk8wSxA+wNiraJvwd gz3q8klfz4ImLUG/9sv7L9Vio3IGZHpEzjz4bc+1LS62kW3VLUS9k3fp3112qEblTV7G 15/1N/ZuPOs2M//Soe+kbdb5JFdl+/xpRV9W2T7BQajJxmQ9tykjULoxNrxTkxhXTcBU cqBhITm9EL98+E5Eq8F2K0/4lU69V7g1rTmTD2G9PUs6wLf7ivfK3vt4oEv7AgHwfZW6 BnFg== X-Gm-Message-State: APjAAAXtephke2bY40Np8Yb/bnndMMm/KQXX2iOJlyTsB+CmnL4zbnGn 3XXxsgG6e9DFn3Qt2p600FXlrZ+FJg/LgDyGt2ReaA== X-Google-Smtp-Source: APXvYqyFMKmsofIlGtyIYxY2auw/Yjye1Kqb5EY69r+kXArsnv/jL7sTkqGRu0OAeuaZKfIN87CHtxO88oGy68C0yADwLw== X-Received: by 2002:a25:6649:: with SMTP id z9mr362486ybm.25.1556751830941; Wed, 01 May 2019 16:03:50 -0700 (PDT) Date: Wed, 1 May 2019 16:01:15 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-7-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 06/17] kbuild: enable building KUnit From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org, Masahiro Yamada X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Add KUnit to root Kconfig and Makefile allowing it to actually be built. 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 2b99679148dc7..77368f498d84c 100644 --- a/Makefile +++ b/Makefile @@ -969,7 +969,7 @@ endif PHONY += prepare0 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 May 1 23:01:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925777 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 32393912 for ; Wed, 1 May 2019 23:04:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1FB4D28F63 for ; Wed, 1 May 2019 23:04:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 12E3F28F65; Wed, 1 May 2019 23:04:05 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 8A33F28F60 for ; Wed, 1 May 2019 23:04:04 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 7D33B212377F1; Wed, 1 May 2019 16:04:04 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::64a; helo=mail-pl1-x64a.google.com; envelope-from=34ixkxa4kdemgwjsifsmnllnsxlttlqj.htrqnsz2-s0inrrqnxyx.56.twl@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-pl1-x64a.google.com (mail-pl1-x64a.google.com [IPv6:2607:f8b0:4864:20::64a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 3B83B212377E6 for ; Wed, 1 May 2019 16:04:03 -0700 (PDT) Received: by mail-pl1-x64a.google.com with SMTP id cp15so272813plb.10 for ; Wed, 01 May 2019 16:04:03 -0700 (PDT) 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=YVYGoozNW1oK9Pxwi3Zx9NUDVKcfq/grXAlQifS5URw=; b=Y76ssic07itjfGQGNmhyQDrNY8QfCH3OQJnoZ4EHlZJNTyDHQngPbslrv77bRVVLFP sxhk+RkJD/vUt5kRfSM6dnO2k2nS0Kod7Zj0hFLTHMTpYsYYQQA/tkKhHsLWZi7UGz/p tYo2XDEIMrLR/VCrz9CHcrpgw/r869mlrnUe26NvGWeRyFB8Enq803huKjFYif7mt55S E8dBCmIcUh+tP1SY1Wpa9WauGKmW5iCRlqTcCmSsjg7fbFS7InS2z/bzhImoGZG2syiY Ns6xxC6GWORAcnzIajB2tHbXjXcQkVrmMyQCTN6fpi+FkyTeINPuo3yoBwuXBwPAHtp9 MzDw== 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=YVYGoozNW1oK9Pxwi3Zx9NUDVKcfq/grXAlQifS5URw=; b=BksGkG/wG1ze4wYHhGYe4Gh/ime1vaWg4kxEhHpnlfxIf83rpEBqbMLehfJNUHIERy RhUmzD2u1HH2yne/kBnnSkZaBoYHhWZUNwXJ8VAJCOosbvk81IQf0YoQ0u/sX77RD11H qiYdZ46KxF5c+u8XDkVFCo8qYfsAhUXhQsp7A50rIO9KNk9LSym2ZJoFw1b5jpuWvfEq Bw6QOuifuIgFyCDPJEufnXWHIkBUbAe5aBstp8pNAjsnFm4cK4cHcz/2yxDxN8OfF6p8 3gMrF+kf/JjJO4weP0PCz8ipxMV8/b/blsuy1P5tz+ca5Inq5hElnX49S5ASby7rU83L T+CA== X-Gm-Message-State: APjAAAWxbmwsj7zQCHPo13RfyltnP3bigEqq1jzKy55z5sfYT8VieToc IMOGozNVtV4vzy90p0mulRjp0Egz24K51YXqgF2Jow== X-Google-Smtp-Source: APXvYqz0pkYBiPKRV7XwFRATidRGmRCE3DcxKYOa52o7H0pWOF808PMJU5QfSstPko0tY1TZPeStQAi1xmx7bMzlEyJ9DQ== X-Received: by 2002:a65:5286:: with SMTP id y6mr557437pgp.79.1556751842420; Wed, 01 May 2019 16:04:02 -0700 (PDT) Date: Wed, 1 May 2019 16:01:16 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-8-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 07/17] kunit: test: add initial tests From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" 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 64480092b2c24..5cb500355c873 100644 --- a/kunit/Kconfig +++ b/kunit/Kconfig @@ -13,4 +13,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..3947dd7c8f922 --- /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) 2019, 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\n"); + + 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..b2a98576797c9 --- /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) 2019, 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; + + string_stream_add(stream, "Foo"); + string_stream_add(stream, " %s", "bar"); + + output = string_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++) + string_stream_add(stream, "A"); + + output = string_stream_get_string(stream); + KUNIT_EXPECT_STREQ(test, output, "AAAAAAAAAA"); + KUNIT_EXPECT_EQ(test, stream->length, 10); + KUNIT_EXPECT_FALSE(test, string_stream_is_empty(stream)); + kfree(output); + + string_stream_clear(stream); + + output = string_stream_get_string(stream); + KUNIT_EXPECT_STREQ(test, output, ""); + KUNIT_EXPECT_TRUE(test, string_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 May 1 23:01:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925787 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 071AC912 for ; Wed, 1 May 2019 23:04:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EA85728F60 for ; Wed, 1 May 2019 23:04:14 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DE5A228F6B; Wed, 1 May 2019 23:04: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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id E736228F60 for ; Wed, 1 May 2019 23:04:13 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id D96BF212377EC; Wed, 1 May 2019 16:04:13 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::849; helo=mail-qt1-x849.google.com; envelope-from=36ixkxa4kdeso4r0qn0uvttv05t11tyr.p1zyv07a-08qvzzyv565.de.14t@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-qt1-x849.google.com (mail-qt1-x849.google.com [IPv6:2607:f8b0:4864:20::849]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id CD92D212377E6 for ; Wed, 1 May 2019 16:04:11 -0700 (PDT) Received: by mail-qt1-x849.google.com with SMTP id b21so323762qtk.18 for ; Wed, 01 May 2019 16:04:11 -0700 (PDT) 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=VK9HINDKe1ly4ZzA9popLYLUhn/WyGwpic6aL5MWTO0=; b=jDzODj1qOqjPXNLY9SVJEw5Z0OL/0dGHwL3ylc6JfNaDy0fvD1/ek5QldXoMtT+Wii gZN8qdXAofPpnVzgPYSBP2tpuJl/33rX41ety9coai/kBRfaomAudXxcuqd/B+pMTQM4 nuvD74OodxNqb+puJnuKFxtGoGtQ8Jb8YOcneWgLvqMNcLWOR/JRY9EEXH29vEXPAY9F WM7FBucz36tyOAodIwtDlAy6eteYxtsnUPf67zap2Ens8+2BUGDRijlfBit6ckvICDQF 7jFcaxQCG0Y1WF3Jug5mZ7Hq2Arz5NtYi1IUx8xpdBh+nqkeSPd22OXr8VTCQofgU42T NUXQ== 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=VK9HINDKe1ly4ZzA9popLYLUhn/WyGwpic6aL5MWTO0=; b=rvrmJO6OZpp8ZNiQbQWYbBexrNB70W8mM3rrS/yxPS1ciLqShD20AsMcRdTguDD/YO xMQsjKzm8puXMB8UnDyAFet2moo9LVmokdofm0S7DlnBcc/mDjlBHmvsbSWT6sRYWWv3 e1FGobQdvvqKYtQ3J0VYVVr89jUrKSzd8mXKVL5cB+pyNd09jJpJCiQVLjuOJx3qhoTx o2+dNHfVAsbaefEHnmz1aT6n1/yV1OElqVkUE6hdX+Tvdzkmmcq6X8XR1gafydWasu6l ZrBvhh5MJ0XHl6l3PIO6dpmzIvz/NDldGvI+GQEU6/XPTDrgZ2eJVIp+QmTyifvi2DBI /UyA== X-Gm-Message-State: APjAAAXtd7OAmIvMrf1T3qKxr/iLSL5bKBW8JwIyD39g37GQugIB7uyF jwK5ZK7m5c4EsesC1PKMQ6TyMH1lfARaM8yNYum3Aw== X-Google-Smtp-Source: APXvYqyncJUl34uJdfoIKmQZCNZHzvln1ZZjvyIJpHC8YcbxXcKTFTZ+XKDcvReciymhjzaPtz7JOzt9WvEWM/epLxeiRw== X-Received: by 2002:ac8:1082:: with SMTP id a2mr561124qtj.231.1556751850877; Wed, 01 May 2019 16:04:10 -0700 (PDT) Date: Wed, 1 May 2019 16:01:17 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-9-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 08/17] kunit: test: add support for test abort From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Add support for aborting/bailing out of test cases, which is needed for implementing assertions. An assertion is like an expectation, but bails out of the test case early if the assertion is not met. The idea with assertions is that you use them to state all the preconditions for your test. Logically speaking, these are the premises of the test case, so if a premise isn't true, there is no point in continuing the test case because there are no conclusions that can be drawn without the premises. Whereas, the expectation is the thing you are trying to prove. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 13 ++++ include/kunit/try-catch.h | 91 +++++++++++++++++++++++++ kunit/Makefile | 3 +- kunit/test.c | 138 +++++++++++++++++++++++++++++++++++--- kunit/try-catch.c | 96 ++++++++++++++++++++++++++ 5 files changed, 332 insertions(+), 9 deletions(-) create mode 100644 include/kunit/try-catch.h create mode 100644 kunit/try-catch.c diff --git a/include/kunit/test.h b/include/kunit/test.h index e441270561ece..1b77caeb5d51f 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -12,6 +12,7 @@ #include #include #include +#include struct kunit_resource; @@ -166,15 +167,27 @@ struct kunit { /* private: internal use only. */ const char *name; /* Read only after initialization! */ + struct kunit_try_catch try_catch; 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 (*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); }; +static inline 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); +} + int kunit_init_test(struct kunit *test, const char *name); int kunit_run_tests(struct kunit_module *module); diff --git a/include/kunit/try-catch.h b/include/kunit/try-catch.h new file mode 100644 index 0000000000000..e85abe044b6b5 --- /dev/null +++ b/include/kunit/try-catch.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * An API to allow a function, that may fail, to be executed, and recover in a + * controlled manner. + * + * Copyright (C) 2019, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _KUNIT_TRY_CATCH_H +#define _KUNIT_TRY_CATCH_H + +#include + +typedef void (*kunit_try_catch_func_t)(void *); + +struct kunit; + +/* + * struct kunit_try_catch - provides a generic way to run code which might fail. + * @context: used to pass user data to the try and catch functions. + * + * kunit_try_catch provides a generic, architecture independent way to execute + * an arbitrary function of type kunit_try_catch_func_t which may bail out by + * calling kunit_try_catch_throw(). If kunit_try_catch_throw() is called, @try + * is stopped at the site of invocation and @catch is catch is called. + * + * struct kunit_try_catch provides a generic interface for the functionality + * needed to implement kunit->abort() which in turn is needed for implementing + * assertions. Assertions allow stating a precondition for a test simplifying + * how test cases are written and presented. + * + * Assertions are like expectations, except they abort (call + * kunit_try_catch_throw()) when the specified condition is not met. This is + * useful when you look at a test case as a logical statement about some piece + * of code, where assertions are the premises for the test case, and the + * conclusion is a set of predicates, rather expectations, that must all be + * true. If your premises are violated, it does not makes sense to continue. + */ +struct kunit_try_catch { + /* private: internal use only. */ + void (*run)(struct kunit_try_catch *try_catch); + void __noreturn (*throw)(struct kunit_try_catch *try_catch); + struct kunit *test; + struct completion *try_completion; + int try_result; + kunit_try_catch_func_t try; + kunit_try_catch_func_t catch; + void *context; +}; + +/* + * Exposed to be overridden for other architectures. + */ +void kunit_try_catch_init_internal(struct kunit_try_catch *try_catch); + +static inline void kunit_try_catch_init(struct kunit_try_catch *try_catch, + struct kunit *test, + kunit_try_catch_func_t try, + kunit_try_catch_func_t catch) +{ + try_catch->test = test; + kunit_try_catch_init_internal(try_catch); + try_catch->try = try; + try_catch->catch = catch; +} + +static inline void kunit_try_catch_run(struct kunit_try_catch *try_catch, + void *context) +{ + try_catch->context = context; + try_catch->run(try_catch); +} + +static inline void __noreturn kunit_try_catch_throw( + struct kunit_try_catch *try_catch) +{ + try_catch->throw(try_catch); +} + +static inline int kunit_try_catch_get_result(struct kunit_try_catch *try_catch) +{ + return try_catch->try_result; +} + +/* + * Exposed for testing only. + */ +void kunit_generic_try_catch_init(struct kunit_try_catch *try_catch); + +#endif /* _KUNIT_TRY_CATCH_H */ diff --git a/kunit/Makefile b/kunit/Makefile index 60a9ea6cb4697..1f7680cfa11ad 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_KUNIT) += test.o \ string-stream.o \ - kunit-stream.o + kunit-stream.o \ + try-catch.o obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o diff --git a/kunit/test.c b/kunit/test.c index 01e82c18b2fa6..5eace2a527a4e 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -6,10 +6,9 @@ * Author: Brendan Higgins */ -#include #include -#include #include +#include static bool kunit_get_success(struct kunit *test) { @@ -32,6 +31,18 @@ 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 int kunit_vprintk_emit(const struct kunit *test, int level, const char *fmt, @@ -70,6 +81,21 @@ static void kunit_fail(struct kunit *test, struct kunit_stream *stream) kunit_stream_commit(stream); } +static void __noreturn kunit_abort(struct kunit *test) +{ + kunit_set_death_test(test, true); + + kunit_try_catch_throw(&test->try_catch); + + /* + * Throw could not abort from test. + * + * XXX: we should never reach this line! As kunit_try_catch_throw is + * marked __noreturn. + */ + WARN_ONCE(true, "Throw could not abort from test!\n"); +} + int kunit_init_test(struct kunit *test, const char *name) { spin_lock_init(&test->lock); @@ -77,6 +103,7 @@ int kunit_init_test(struct kunit *test, const char *name) test->name = name; test->vprintk = kunit_vprintk; test->fail = kunit_fail; + test->abort = kunit_abort; return 0; } @@ -122,16 +149,111 @@ 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_case_internal_cleanup(test); +} +struct kunit_try_catch_context { + struct kunit *test; + struct kunit_module *module; + struct kunit_case *test_case; +}; + +static void kunit_try_run_case(void *data) +{ + struct kunit_try_catch_context *ctx = data; + struct kunit *test = ctx->test; + struct kunit_module *module = ctx->module; + struct kunit_case *test_case = ctx->test_case; + + /* + * kunit_run_case_internal may encounter a fatal error; if it does, + * abort will be called, this thread will exit, and finally the parent + * thread will resume control and handle any necessary clean up. + */ kunit_run_case_internal(test, module, test_case); + /* This line may never be reached. */ kunit_run_case_cleanup(test, module, test_case); +} + +static void kunit_catch_run_case(void *data) +{ + struct kunit_try_catch_context *ctx = data; + struct kunit *test = ctx->test; + struct kunit_module *module = ctx->module; + struct kunit_case *test_case = ctx->test_case; + int try_exit_code = kunit_try_catch_get_result(&test->try_catch); + + if (try_exit_code) { + kunit_set_success(test, false); + /* + * Test case could not finish, we have no idea what state it is + * in, so don't do clean up. + */ + if (try_exit_code == -ETIMEDOUT) + kunit_err(test, "test case timed out\n"); + /* + * Unknown internal error occurred preventing test case from + * running, so there is nothing to clean up. + */ + else + kunit_err(test, "internal error occurred preventing test case from running: %d\n", + try_exit_code); + return; + } + + 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); + } +} + +/* + * Performs all logic to run a test case. It also catches most errors that + * occurs in a test case and reports them as failures. + */ +static bool kunit_run_case_catch_errors(struct kunit *test, + struct kunit_module *module, + struct kunit_case *test_case) +{ + struct kunit_try_catch *try_catch = &test->try_catch; + struct kunit_try_catch_context context; + + kunit_set_success(test, true); + kunit_set_death_test(test, false); + + kunit_try_catch_init(try_catch, + test, + kunit_try_run_case, + kunit_catch_run_case); + context.test = test; + context.module = module; + context.test_case = test_case; + kunit_try_catch_run(try_catch, &context); return kunit_get_success(test); } @@ -148,7 +270,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; diff --git a/kunit/try-catch.c b/kunit/try-catch.c new file mode 100644 index 0000000000000..c4cdb30880714 --- /dev/null +++ b/kunit/try-catch.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An API to allow a function, that may fail, to be executed, and recover in a + * controlled manner. + * + * Copyright (C) 2019, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include +#include + +static void __noreturn kunit_generic_throw(struct kunit_try_catch *try_catch) +{ + try_catch->try_result = -EFAULT; + complete_and_exit(try_catch->try_completion, -EFAULT); +} + +static int kunit_generic_run_threadfn_adapter(void *data) +{ + struct kunit_try_catch *try_catch = data; + + try_catch->try(try_catch->context); + + complete_and_exit(try_catch->try_completion, 0); +} + +static void kunit_generic_run_try_catch(struct kunit_try_catch *try_catch) +{ + DECLARE_COMPLETION_ONSTACK(try_completion); + struct kunit *test = try_catch->test; + struct task_struct *task_struct; + int exit_code, status; + + try_catch->try_completion = &try_completion; + try_catch->try_result = 0; + task_struct = kthread_run(kunit_generic_run_threadfn_adapter, + try_catch, + "kunit_try_catch_thread"); + if (IS_ERR(task_struct)) { + try_catch->catch(try_catch->context); + return; + } + + /* + * TODO(brendanhiggins@google.com): We should probably have some type of + * variable timeout here. The only question is what that timeout value + * should be. + * + * The intention has always been, at some point, to be able to label + * tests with some type of size bucket (unit/small, integration/medium, + * large/system/end-to-end, etc), where each size bucket would get a + * default timeout value kind of like what Bazel does: + * https://docs.bazel.build/versions/master/be/common-definitions.html#test.size + * There is still some debate to be had on exactly how we do this. (For + * one, we probably want to have some sort of test runner level + * timeout.) + * + * For more background on this topic, see: + * https://mike-bland.com/2011/11/01/small-medium-large.html + */ + status = wait_for_completion_timeout(&try_completion, + 300 * MSEC_PER_SEC); /* 5 min */ + if (status < 0) { + kunit_err(test, "try timed out\n"); + try_catch->try_result = -ETIMEDOUT; + } + + exit_code = try_catch->try_result; + + if (!exit_code) + return; + + if (exit_code == -EFAULT) + try_catch->try_result = 0; + else if (exit_code == -EINTR) + kunit_err(test, "wake_up_process() was never called\n"); + else if (exit_code) + kunit_err(test, "Unknown error: %d\n", exit_code); + + try_catch->catch(try_catch->context); +} + +void kunit_generic_try_catch_init(struct kunit_try_catch *try_catch) +{ + try_catch->run = kunit_generic_run_try_catch; + try_catch->throw = kunit_generic_throw; +} + +void __weak kunit_try_catch_init_internal(struct kunit_try_catch *try_catch) +{ + kunit_generic_try_catch_init(try_catch); +} + From patchwork Wed May 1 23:01:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925799 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 DD287912 for ; Wed, 1 May 2019 23:04:22 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CC5C828ED5 for ; Wed, 1 May 2019 23:04:22 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C052D28F65; Wed, 1 May 2019 23:04: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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 2FE7D28F60 for ; Wed, 1 May 2019 23:04:22 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 22BBB212377F2; Wed, 1 May 2019 16:04:22 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::24a; helo=mail-oi1-x24a.google.com; envelope-from=38yxkxa4kdfqxd09zw9342249e2aa270.ya8749gj-9hz48874efe.mn.ad2@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-oi1-x24a.google.com (mail-oi1-x24a.google.com [IPv6:2607:f8b0:4864:20::24a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 8628F212377E6 for ; Wed, 1 May 2019 16:04:20 -0700 (PDT) Received: by mail-oi1-x24a.google.com with SMTP id o132so341123oib.5 for ; Wed, 01 May 2019 16:04:20 -0700 (PDT) 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=OoB1Q1B1OKTnVM2sKsSTjgsrzyoIYKMiukBZ0PgDZ9o=; b=awoM/T0AoqG0wYtJorCPvm2AZEqZUzUs5qq1R8YR0UNkjXyRX24zF53GZ1UD6KiV+8 WoKSipt9phdWDdeo/zDMvjArUywT8O+1qVccX5uPZnOzbzBfB+8h4jIyjzJGGBdg4n7N imrAzBCt4voZvz3O+nOIMytkIln4VOZ1LvzQprZkOv9VhoaC6AXS8ZkpWrt4ZerM7ljw vSmK9aGxj4/37leOcxAYhpXvhHLCXqqaZL5x06G9q7/18UfD7rqvqJjZVKB+iD2XROqO KzY2suaK26r7WlIWBRrvFuLTnnO37Kovql58dhGKMkYPGEiPzC9D5ieJu5MBGxBF7KnL 22pw== 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=OoB1Q1B1OKTnVM2sKsSTjgsrzyoIYKMiukBZ0PgDZ9o=; b=tgyzg1kOEN4WRF9E9LyYDv9TwH1DTc3dnb/e4wp8CwrEstL9c2s5pAY2+9Whv9oJxN jyZd6aWEg8lUgdOGf8CUG8VQzIEHVytoql/H8PkQlUwt4sftJEYanq6Ob0ZrpSDDK1+t 4ndd0CdjOAG3y1k5Fu16v1xwFZZ17fqzt5X5MPMZzQWQgjSqY0xrIaaxBbYYxhyDmLS9 foFKqFZW2FQKIzqVZXVKsC1FWVaCYEhbSWn6IRt3BueYJhIP7asXaRghV/sTpBh7MSlI cRqecHwB+FOvEGRIpgiwSESEVmPtRNgA9smyqZwu+BnOz5T8Y+3I01ZuvKRZUp5JND+J shKQ== X-Gm-Message-State: APjAAAVXBDh83eQ20wB8INlFsXtSaGqUAPak/xHCWmoE8jHzaUr6Bsj6 WRUGSKarl8d5Vgkt8chlTe+bGQSJDMn162dbTuhHcA== X-Google-Smtp-Source: APXvYqxKMbUJy/xMLdXAeTw/fGE6G1FNY/xDT4Lln76o9R+/ouozcRUb5NrhTFvEA3khcNSkJVq0eQ+XTBqWt1COBuiXjg== X-Received: by 2002:a9d:6b93:: with SMTP id b19mr372226otq.313.1556751859218; Wed, 01 May 2019 16:04:19 -0700 (PDT) Date: Wed, 1 May 2019 16:01:18 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-10-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 09/17] kunit: test: add tests for kunit test abort From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Add KUnit tests for the KUnit test abort mechanism (see preceding commit). Add tests both for general try catch mechanism as well as non-architecture specific mechanism. Signed-off-by: Brendan Higgins --- kunit/Makefile | 3 +- kunit/test-test.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 kunit/test-test.c diff --git a/kunit/Makefile b/kunit/Makefile index 1f7680cfa11ad..533355867abd2 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_KUNIT) += test.o \ kunit-stream.o \ try-catch.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/test-test.c b/kunit/test-test.c new file mode 100644 index 0000000000000..c81ae6efb959f --- /dev/null +++ b/kunit/test-test.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit test for core test infrastructure. + * + * Copyright (C) 2019, Google LLC. + * Author: Brendan Higgins + */ +#include + +struct kunit_try_catch_test_context { + struct kunit_try_catch *try_catch; + bool function_called; +}; + +void kunit_test_successful_try(void *data) +{ + struct kunit *test = data; + struct kunit_try_catch_test_context *ctx = test->priv; + + ctx->function_called = true; +} + +void kunit_test_no_catch(void *data) +{ + struct kunit *test = data; + + KUNIT_FAIL(test, "Catch should not be called.\n"); +} + +static void kunit_test_try_catch_successful_try_no_catch(struct kunit *test) +{ + struct kunit_try_catch_test_context *ctx = test->priv; + struct kunit_try_catch *try_catch = ctx->try_catch; + + kunit_try_catch_init(try_catch, + test, + kunit_test_successful_try, + kunit_test_no_catch); + kunit_try_catch_run(try_catch, test); + + KUNIT_EXPECT_TRUE(test, ctx->function_called); +} + +void kunit_test_unsuccessful_try(void *data) +{ + struct kunit *test = data; + struct kunit_try_catch_test_context *ctx = test->priv; + struct kunit_try_catch *try_catch = ctx->try_catch; + + kunit_try_catch_throw(try_catch); + KUNIT_FAIL(test, "This line should never be reached.\n"); +} + +void kunit_test_catch(void *data) +{ + struct kunit *test = data; + struct kunit_try_catch_test_context *ctx = test->priv; + + ctx->function_called = true; +} + +static void kunit_test_try_catch_unsuccessful_try_does_catch(struct kunit *test) +{ + struct kunit_try_catch_test_context *ctx = test->priv; + struct kunit_try_catch *try_catch = ctx->try_catch; + + kunit_try_catch_init(try_catch, + test, + kunit_test_unsuccessful_try, + kunit_test_catch); + kunit_try_catch_run(try_catch, test); + + KUNIT_EXPECT_TRUE(test, ctx->function_called); +} + +static void kunit_test_generic_try_catch_successful_try_no_catch( + struct kunit *test) +{ + struct kunit_try_catch_test_context *ctx = test->priv; + struct kunit_try_catch *try_catch = ctx->try_catch; + + try_catch->test = test; + kunit_generic_try_catch_init(try_catch); + try_catch->try = kunit_test_successful_try; + try_catch->catch = kunit_test_no_catch; + + kunit_try_catch_run(try_catch, test); + + KUNIT_EXPECT_TRUE(test, ctx->function_called); +} + +static void kunit_test_generic_try_catch_unsuccessful_try_does_catch( + struct kunit *test) +{ + struct kunit_try_catch_test_context *ctx = test->priv; + struct kunit_try_catch *try_catch = ctx->try_catch; + + try_catch->test = test; + kunit_generic_try_catch_init(try_catch); + try_catch->try = kunit_test_unsuccessful_try; + try_catch->catch = kunit_test_catch; + + kunit_try_catch_run(try_catch, test); + + KUNIT_EXPECT_TRUE(test, ctx->function_called); +} + +static int kunit_try_catch_test_init(struct kunit *test) +{ + struct kunit_try_catch_test_context *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + test->priv = ctx; + + ctx->try_catch = kunit_kmalloc(test, + sizeof(*ctx->try_catch), + GFP_KERNEL); + + return 0; +} + +static struct kunit_case kunit_try_catch_test_cases[] = { + KUNIT_CASE(kunit_test_try_catch_successful_try_no_catch), + KUNIT_CASE(kunit_test_try_catch_unsuccessful_try_does_catch), + KUNIT_CASE(kunit_test_generic_try_catch_successful_try_no_catch), + KUNIT_CASE(kunit_test_generic_try_catch_unsuccessful_try_does_catch), + {}, +}; + +static struct kunit_module kunit_try_catch_test_module = { + .name = "kunit-try-catch-test", + .init = kunit_try_catch_test_init, + .test_cases = kunit_try_catch_test_cases, +}; +module_test(kunit_try_catch_test_module); From patchwork Wed May 1 23:01:19 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925811 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 464AC912 for ; Wed, 1 May 2019 23:04:30 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3389228F60 for ; Wed, 1 May 2019 23:04:30 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2644D28F65; Wed, 1 May 2019 23:04:30 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 6F54D28ED5 for ; Wed, 1 May 2019 23:04:28 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 63288212377F6; Wed, 1 May 2019 16:04:28 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::449; helo=mail-pf1-x449.google.com; envelope-from=3-sxkxa4kdfo3j6f52f9a88afk8gg8d6.4gedafmp-fn5aeedaklk.st.gj8@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-pf1-x449.google.com (mail-pf1-x449.google.com [IPv6:2607:f8b0:4864:20::449]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 30559212377E6 for ; Wed, 1 May 2019 16:04:26 -0700 (PDT) Received: by mail-pf1-x449.google.com with SMTP id s26so196118pfm.18 for ; Wed, 01 May 2019 16:04:26 -0700 (PDT) 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=+G7LPM+i6NmkVoLbCU7sAmRt474gzlRI9LU6GUnDedQ=; b=uzPCjNqZ93NeuPZv1xpyKr2I02dQc9jXSsfzeRE9d/VVIp/KCqGRtPOuQRczZOqBBy KiP0Jy16kw2cRjZ6q7g+FeMc92Wd2TQJatyA3GeaZ43pwFhVKKyPeEfwzNjUVFB1OwTT agFWxKDeW5uLx19DJdgcnZ4Y7WL7qraikxIcYSHrBt48BZQb9XBvWHjAkQ+HZ3MhaW26 TMbwN4V6UA52307leg95t5nPs2bB2AiVWTvE7NcOI+LNeFGFRd2a02pQt53fgw7rDKMX /ZNZzs/kjy3pQoqX3aX1uFENj83rL1Cnr94qmhaM2bj/vEkOD4mivJEc+5p3TCaKvqIV uzfg== 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=+G7LPM+i6NmkVoLbCU7sAmRt474gzlRI9LU6GUnDedQ=; b=OSbQmg/Q90NUeAp8wuOqNGO2T+Y9hm4WoWrxPmSHDZ1RyC7A5VQtHhkPnET7HLARjW hu7W8v3FttMiqCODIkTCxbeS7eVKW0bBC+bf/HExK4CSSfjwsNPkNyrL4lq5fEviHKe8 3Mv2eCJ9LOfg3/OXrEMP7yOrvOwbVOaHCoKk2LaGl69uC3cDOj5dLW0R5QL4jTMryThe yGaQe2uAE9RM4Kum1LsEZ5+5L9BFa7SZYAq7VfYBfMwhJV+jKP02gfeZ5P1cFXQHR1LZ 6I/Aw9iBTN5yodMI6aVjYCHW+OrDVP1enOm5e5xYFS/rFVAA3967nlgH3UyqWcmvpEZr Q9TQ== X-Gm-Message-State: APjAAAV83/oJT4XqccLz3vpA1Nx7Y/uR57lzbzWlQ1Ir7zzWZflr8OYz miC+wCm1Ivto86gjZ04KI4B8oG6gcO9IFowYCoaNjg== X-Google-Smtp-Source: APXvYqxsjU1jFWzO3KypwMN2P1uX9t76vdHAoPQyTHx1Ml5WxKjLGQPFdzuPJIDtR+IZMzabIuwjhDfLR7kIiw0TlZZr+w== X-Received: by 2002:a63:30c5:: with SMTP id w188mr617139pgw.76.1556751865309; Wed, 01 May 2019 16:04:25 -0700 (PDT) Date: Wed, 1 May 2019 16:01:19 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-11-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 10/17] kunit: test: add the concept of assertions From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Add support for assertions which are like expectations except the test terminates if the assertion is not satisfied. The idea with assertions is that you use them to state all the preconditions for your test. Logically speaking, these are the premises of the test case, so if a premise isn't true, there is no point in continuing the test case because there are no conclusions that can be drawn without the premises. Whereas, the expectation is the thing you are trying to prove. It is not used universally in x-unit style test frameworks, but I really like it as a convention. You could still express the idea of a premise using the above idiom, but I think KUNIT_ASSERT_* states the intended idea perfectly. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 401 ++++++++++++++++++++++++++++++++++++- kunit/string-stream-test.c | 12 +- kunit/test-test.c | 2 + kunit/test.c | 33 +++ 4 files changed, 439 insertions(+), 9 deletions(-) diff --git a/include/kunit/test.h b/include/kunit/test.h index 1b77caeb5d51f..bb2f3e63a3522 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -85,9 +85,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 @@ -705,4 +706,398 @@ 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); + + kunit_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 { + kunit_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); \ + \ + kunit_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); \ + \ + kunit_stream_add(__stream, message); \ + kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \ + KUNIT_ASSERT_END(test, success, __stream); \ +} while (0) + +#define KUNIT_ASSERT_FAILURE(test, fmt, ...) do { \ + struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \ + \ + kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \ + KUNIT_ASSERT_END(test, false, __stream); \ +} while (0) + +/** + * 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.\n") + +#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.\n") + +#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); \ + \ + kunit_stream_add(__stream, "Asserted " #left " == " #right ", but\n"); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \ + kunit_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); \ + \ + kunit_stream_add(__stream, "Asserted " #left " == " #right ", but\n"); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #right, __right); \ + kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \ + \ + KUNIT_ASSERT_END(test, !strcmp(left, right), __stream); \ +} while (0) + +/** + * KUNIT_ASSERT_STRNEQ() - Expects that strings @left and @right are not 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 + * not equal. This is semantically equivalent to + * KUNIT_ASSERT_TRUE(@test, strcmp((@left), (@right))). See KUNIT_ASSERT_TRUE() + * for more information. + */ +#define KUNIT_ASSERT_STRNEQ(test, left, right) do { \ + struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + \ + kunit_stream_add(__stream, "Asserted " #left " == " #right ", but\n"); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #right, __right); \ + \ + KUNIT_ASSERT_END(test, strcmp(left, right), __stream); \ +} while (0) + +#define KUNIT_ASSERT_STRNEQ_MSG(test, left, right, fmt, ...) do { \ + struct kunit_stream *__stream = KUNIT_ASSERT_START(test); \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + \ + kunit_stream_add(__stream, "Asserted " #left " == " #right ", but\n"); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #left, __left); \ + kunit_stream_add(__stream, "\t\t%s == %s\n", #right, __right); \ + kunit_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) \ + kunit_stream_add(__stream, \ + "Asserted " #ptr " is not null, but is.\n"); \ + if (IS_ERR(__ptr)) \ + kunit_stream_add(__stream, \ + "Asserted " #ptr " is not error, but is: %ld\n",\ + 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) { \ + kunit_stream_add(__stream, \ + "Asserted " #ptr " is not null, but is.\n"); \ + kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \ + } \ + if (IS_ERR(__ptr)) { \ + kunit_stream_add(__stream, \ + "Asserted " #ptr " is not error, but is: %ld\n",\ + PTR_ERR(__ptr)); \ + \ + kunit_stream_add(__stream, fmt, ##__VA_ARGS__); \ + } \ + KUNIT_ASSERT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \ +} while (0) + #endif /* _KUNIT_TEST_H */ diff --git a/kunit/string-stream-test.c b/kunit/string-stream-test.c index b2a98576797c9..96819b99b5713 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) string_stream_add(stream, " %s", "bar"); output = string_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) string_stream_add(stream, "A"); output = string_stream_get_string(stream); - KUNIT_EXPECT_STREQ(test, output, "AAAAAAAAAA"); - KUNIT_EXPECT_EQ(test, stream->length, 10); - KUNIT_EXPECT_FALSE(test, string_stream_is_empty(stream)); + KUNIT_ASSERT_STREQ(test, output, "AAAAAAAAAA"); + KUNIT_ASSERT_EQ(test, stream->length, 10); + KUNIT_ASSERT_FALSE(test, string_stream_is_empty(stream)); kfree(output); string_stream_clear(stream); output = string_stream_get_string(stream); - KUNIT_EXPECT_STREQ(test, output, ""); - KUNIT_EXPECT_TRUE(test, string_stream_is_empty(stream)); + KUNIT_ASSERT_STREQ(test, output, ""); + KUNIT_ASSERT_TRUE(test, string_stream_is_empty(stream)); destroy_string_stream(stream); } diff --git a/kunit/test-test.c b/kunit/test-test.c index c81ae6efb959f..4bd7a34d0a6cb 100644 --- a/kunit/test-test.c +++ b/kunit/test-test.c @@ -110,11 +110,13 @@ static int kunit_try_catch_test_init(struct kunit *test) struct kunit_try_catch_test_context *ctx; ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); test->priv = ctx; ctx->try_catch = kunit_kmalloc(test, sizeof(*ctx->try_catch), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->try_catch); return 0; } diff --git a/kunit/test.c b/kunit/test.c index 5eace2a527a4e..55561eadc459d 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -425,3 +425,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; + + kunit_stream_add(stream, + "Asserted %s %s %s, but\n", + left_name, compare_name, right_name); + kunit_stream_add(stream, "\t\t%s == %lld\n", left_name, left); + kunit_stream_add(stream, "\t\t%s == %lld\n", right_name, right); + + if (fmt) { + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + kunit_stream_add(stream, "\n%pV", &vaf); + + va_end(args); + } + + kunit_assert_end(test, compare_result, stream); +} + From patchwork Wed May 1 23:01:20 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925815 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 6ACE6933 for ; Wed, 1 May 2019 23:04:33 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 55B7F28F65 for ; Wed, 1 May 2019 23:04:33 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4982B28F7A; Wed, 1 May 2019 23:04:33 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id D5D2D28F6B for ; Wed, 1 May 2019 23:04:32 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id B98CE212377F8; Wed, 1 May 2019 16:04:32 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::349; helo=mail-ot1-x349.google.com; envelope-from=3_ixkxa4kdf88obka7kefddfkpdlldib.9ljifkru-ksafjjifpqp.xy.lod@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-ot1-x349.google.com (mail-ot1-x349.google.com [IPv6:2607:f8b0:4864:20::349]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id B37A0212377E6 for ; Wed, 1 May 2019 16:04:31 -0700 (PDT) Received: by mail-ot1-x349.google.com with SMTP id 7so316684otj.1 for ; Wed, 01 May 2019 16:04:31 -0700 (PDT) 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=OvFsjJX2Uyffa7cEX+3tzIASfZRboLmkTB5evz5SdF8=; b=qPlP4wtsZ4AQcatp9BF+OrCpLj05h8DEI/0tQy+x1kv62wInQV0ExwEGLaSfmw4wfx vw6ztQSToC85b77HPNuG6YPhcRe0VFLQbcP0C/R1n6r629R/7oepiRk83g6gb7uqUAJU x14XDp7hhyem57L5ZmWaQpCTC4n6fOPvcu7bARwPtL5iDm7KlAjijzVRvklVx5yPNctL XBOagQedBEK5DjJEdzWuRUIWOF/83Nj815rfBHZy2kbBvKLDAanbKfijUzWAe9klznKB RZeGENHbw5NMnYDuLHKUvQCi3lF7/v6gdZ3ySbcQdAiu336KQzA3HXAE7rqRLgY7Ls3v J8lw== 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=OvFsjJX2Uyffa7cEX+3tzIASfZRboLmkTB5evz5SdF8=; b=AwlDYkYk7F0Sj/32s8eUNMG+0qDHupK+wt9UqWXN7oUGfoDkvgs+SEyY3MFsB3SvM5 6Q/dpefxqoaV064STvM+TfIRHEYdgQFFb5EAJgszrZjW6rdrLd/9+rAqH3J/NkBoj6FL VNM72KYhiI2DNhi+qfPbnpimZPPn7CfrQfcw0stFKPxFHSd+ZyrvWStIw/ctzC+2r0p2 8ROJEFtXUcWi+w5c/xAzOrcsLebR/Y/+TCJor3Tre86ojj3TKx3YYbJ2WbckHa0ony9d jcxnfKzHf3eEI/wTiQTgeNYs4BVTICJniAuNo9Y8SA9zl/ooIeArl8+KwancVuqu5teO 1ejQ== X-Gm-Message-State: APjAAAVF7sEVo2w/zUfQMUV+4L38ANu7/pEFshTYaYNC4iWGJfY2iO2p hx4KQZxSD2m3YLXUPrLgFuyET5BPQhfX9yb8xzHNNg== X-Google-Smtp-Source: APXvYqwURwsTc0EBmXs/5Frvf/VNd5IIE0WpLM5wqVrJUnNg2ceQ9OXpweFVzu+hmZ8XaVWtbRFH9X4o3/ZG9dzoOx/vwg== X-Received: by 2002:aca:d557:: with SMTP id m84mr506901oig.50.1556751870612; Wed, 01 May 2019 16:04:30 -0700 (PDT) Date: Wed, 1 May 2019 16:01:20 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-12-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 11/17] kunit: test: add test managed resource tests From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, Avinash Kondareddy , linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP From: Avinash Kondareddy 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 | 122 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/kunit/test-test.c b/kunit/test-test.c index 4bd7a34d0a6cb..54add8ca418a0 100644 --- a/kunit/test-test.c +++ b/kunit/test-test.c @@ -135,3 +135,125 @@ static struct kunit_module kunit_try_catch_test_module = { .test_cases = kunit_try_catch_test_cases, }; module_test(kunit_try_catch_test_module); + +/* + * Context for testing test managed resources + * is_resource_initialized is used to test arbitrary resources + */ +struct kunit_test_resource_context { + struct kunit test; + bool is_resource_initialized; +}; + +static int fake_resource_init(struct kunit_resource *res, void *context) +{ + struct kunit_test_resource_context *ctx = context; + + res->allocation = &ctx->is_resource_initialized; + ctx->is_resource_initialized = true; + return 0; +} + +static void fake_resource_free(struct kunit_resource *res) +{ + bool *is_resource_initialized = res->allocation; + + *is_resource_initialized = false; +} + +static void kunit_resource_test_init_resources(struct kunit *test) +{ + struct kunit_test_resource_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_resource_test_alloc_resource(struct kunit *test) +{ + struct kunit_test_resource_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_resource_test_free_resource(struct kunit *test) +{ + struct kunit_test_resource_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_resource_test_cleanup_resources(struct kunit *test) +{ + int i; + struct kunit_test_resource_context *ctx = test->priv; + struct kunit_resource *resources[5]; + + for (i = 0; i < ARRAY_SIZE(resources); 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_resource_test_init(struct kunit *test) +{ + struct kunit_test_resource_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_resource_test_exit(struct kunit *test) +{ + struct kunit_test_resource_context *ctx = test->priv; + + kunit_cleanup(&ctx->test); + kfree(ctx); +} + +static struct kunit_case kunit_resource_test_cases[] = { + KUNIT_CASE(kunit_resource_test_init_resources), + KUNIT_CASE(kunit_resource_test_alloc_resource), + KUNIT_CASE(kunit_resource_test_free_resource), + KUNIT_CASE(kunit_resource_test_cleanup_resources), + {}, +}; + +static struct kunit_module kunit_resource_test_module = { + .name = "kunit-resource-test", + .init = kunit_resource_test_init, + .exit = kunit_resource_test_exit, + .test_cases = kunit_resource_test_cases, +}; +module_test(kunit_resource_test_module); From patchwork Wed May 1 23:01:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925817 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 302D1933 for ; Wed, 1 May 2019 23:04:38 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1A9F928F60 for ; Wed, 1 May 2019 23:04:38 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0B4EC28ED5; Wed, 1 May 2019 23:04:38 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 1C37728ED5 for ; Wed, 1 May 2019 23:04:37 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 0E29D212377FA; Wed, 1 May 2019 16:04:37 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::349; helo=mail-ot1-x349.google.com; envelope-from=3aibkxa4kdgmcsfoeboijhhjothpphmf.dpnmjovy-owejnnmjtut.bc.psh@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-ot1-x349.google.com (mail-ot1-x349.google.com [IPv6:2607:f8b0:4864:20::349]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id D7830212377F4 for ; Wed, 1 May 2019 16:04:35 -0700 (PDT) Received: by mail-ot1-x349.google.com with SMTP id d11so274588otp.22 for ; Wed, 01 May 2019 16:04:35 -0700 (PDT) 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=2w5xfu9GjviUjmXUiGL3y5UBmpKTNTw//vQlQgl2ho0=; b=aPhp4K7Ppw2uScWyrHYIcTzDgEoUszftPdVVA9a2w+upJnoB681LBPoHg2s0RrLbB0 E+zIbzzmAk8ElUsZTQZ0UuySBbdQ0cWlk43efUiq01/SV+9jXrLapAocH9jrN3kqC/2k jhupUv/3SweTHXV7iGSHIU4gHqXerCIyln6KuCbCRv1WxerOUxnmi8MaOA2E/SU+MpEn PvVPGrkKk8eI3pLOxoW9dQhsVRjSSiKDdxKVu6pHBn0xoo6//BO+GJ1HySq1Xfy22P+4 8xiSgxU1fqNIXnjiOOAHkdZgusoTSYtpfDrR1JvqjD5uuPqEajAJG+D5PUavkjRdjLEL MLuA== 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=2w5xfu9GjviUjmXUiGL3y5UBmpKTNTw//vQlQgl2ho0=; b=B/iGVgS8FLcrUdJgPnVCPdM5XdceSJV9m1HhX+k0tsMm5aRxe1nQud8Gj8QP0hOTN9 Xy0Q1ZZKdxJxjQkJfbViNNk27PApwBp+G7KRQzZ8vzZuCUIiStdc/OnTs5EN1aYIN7m+ MTpnjDWD6y5P7QOAOWUxxzZoGypzI1fQ+zvrjJfOYiWzsfwM1GFvFNYNGWuDcrTdIs+g Y0OZAV1YTQxf4+OaoiriyvThOXp36Ywrc2qUfdEN8G0kJbvcnKC+uc6cgMsZrm1QNTEG OWt0Z909CIdPPrxLkRKIt0cuOSD7/PUIuDODRVfD69yfPwBLsYwvbLDO0heiChFwTph8 l5Ag== X-Gm-Message-State: APjAAAVazabRRvBiJ5FzMsGDMfCTSC7cVUDYW0z8mx9LA3lErFaYYf6U kVj6ggbloN8JX59zoMBPhtnTsaO3oSByJhsnyBNqCw== X-Google-Smtp-Source: APXvYqxJxPO6GlZz4/bAGqvwWNxlwLQ0kC05vzwzyt72esVI2M3tr11TsCRGVwL3AImQYpA1YthLZLN/JqdKDdr96Su2yw== X-Received: by 2002:a05:6830:1449:: with SMTP id w9mr382814otp.276.1556751874971; Wed, 01 May 2019 16:04:34 -0700 (PDT) Date: Wed, 1 May 2019 16:01:21 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-13-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 12/17] kunit: tool: add Python wrappers for running KUnit tests From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, Felix Guo , wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP From: Felix Guo 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.py | 78 +++++++++++++++ tools/testing/kunit/kunit_config.py | 66 +++++++++++++ tools/testing/kunit/kunit_kernel.py | 148 ++++++++++++++++++++++++++++ tools/testing/kunit/kunit_parser.py | 119 ++++++++++++++++++++++ 5 files changed, 414 insertions(+) create mode 100644 tools/testing/kunit/.gitignore create mode 100755 tools/testing/kunit/kunit.py create mode 100644 tools/testing/kunit/kunit_config.py create mode 100644 tools/testing/kunit/kunit_kernel.py create mode 100644 tools/testing/kunit/kunit_parser.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.py b/tools/testing/kunit/kunit.py new file mode 100755 index 0000000000000..7413ec7351a20 --- /dev/null +++ b/tools/testing/kunit/kunit.py @@ -0,0 +1,78 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0 +# +# A thin wrapper on top of the KUnit Kernel +# +# Copyright (C) 2019, Google LLC. +# Author: Felix Guo +# Author: Brendan Higgins + +import argparse +import sys +import os +import time + +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') + +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') + +parser.add_argument('--build_dir', + help='As in the make command, it specifies the build ' + 'directory.', + type=str, default=None, metavar='build_dir') + +cli_args = parser.parse_args() + +linux = kunit_kernel.LinuxSourceTree() + +build_dir = None +if cli_args.build_dir: + build_dir = cli_args.build_dir + +config_start = time.time() +success = linux.build_reconfig(build_dir) +config_end = time.time() +if not success: + quit() + +kunit_parser.print_with_timestamp('Building KUnit Kernel ...') + +build_start = time.time() + +success = linux.build_um_kernel(jobs=cli_args.jobs, build_dir=build_dir) +build_end = time.time() +if not success: + quit() + +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, + build_dir=build_dir)) +else: + kunit_parser.parse_run_tests(linux.run_kernel(timeout=cli_args.timeout, + build_dir=build_dir)) + +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_config.py b/tools/testing/kunit/kunit_config.py new file mode 100644 index 0000000000000..167f47d9ab8e4 --- /dev/null +++ b/tools/testing/kunit/kunit_config.py @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Builds a .config from a kunitconfig. +# +# Copyright (C) 2019, Google LLC. +# Author: Felix Guo +# Author: Brendan Higgins + +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..07c0abf2f47df --- /dev/null +++ b/tools/testing/kunit/kunit_kernel.py @@ -0,0 +1,148 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Runs UML kernel, collects output, and handles errors. +# +# Copyright (C) 2019, Google LLC. +# Author: Felix Guo +# Author: Brendan Higgins + + +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, build_dir): + command = ['make', 'ARCH=um', 'olddefconfig'] + if build_dir: + command += ['O=' + build_dir] + try: + subprocess.check_output(command) + 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, build_dir): + command = ['make', 'ARCH=um', '--jobs=' + str(jobs)] + if build_dir: + command += ['O=' + build_dir] + try: + subprocess.check_output(command) + 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, build_dir): + """Runs the Linux UML binary. Must be named 'linux'.""" + linux_bin = './linux' + if build_dir: + linux_bin = os.path.join(build_dir, 'linux') + process = subprocess.Popen( + [linux_bin] + params, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + process.wait(timeout=timeout) + return process + + +def get_kconfig_path(build_dir): + kconfig_path = KCONFIG_PATH + if build_dir: + kconfig_path = os.path.join(build_dir, KCONFIG_PATH) + return kconfig_path + +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, build_dir): + kconfig_path = get_kconfig_path(build_dir) + if build_dir and not os.path.exists(build_dir): + os.mkdir(build_dir) + self._kconfig.write_to_file(kconfig_path) + try: + self._ops.make_olddefconfig(build_dir) + 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, build_dir): + """Creates a new .config if it is not a subset of the kunitconfig.""" + kconfig_path = get_kconfig_path(build_dir) + 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(build_dir) + else: + return True + else: + print('Generating .config ...') + return self.build_config(build_dir) + + def build_um_kernel(self, jobs, build_dir): + try: + self._ops.make_olddefconfig(build_dir) + self._ops.make(jobs, build_dir) + except (ConfigError, BuildError) as e: + logging.error(e) + return False + used_kconfig = kunit_config.Kconfig() + used_kconfig.read_from_file(get_kconfig_path(build_dir)) + 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, build_dir=None): + args.extend(['mem=256M']) + process = self._ops.linux_bin(args, timeout, build_dir) + with open('test.log', 'w') as f: + for line in process.stdout: + f.write(line.rstrip().decode('ascii') + '\n') + yield line.rstrip().decode('ascii') diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py new file mode 100644 index 0000000000000..6c81d4dcfabb5 --- /dev/null +++ b/tools/testing/kunit/kunit_parser.py @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Parses test results from a kernel dmesg log. +# +# Copyright (C) 2019, Google LLC. +# Author: Felix Guo +# Author: Brendan Higgins + +import re +from datetime import datetime + +kunit_start_re = re.compile('printk: 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) + +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): + 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 May 1 23:01:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925827 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 03D68912 for ; Wed, 1 May 2019 23:04:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E5B7328F7C for ; Wed, 1 May 2019 23:04:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D4A8428F7A; Wed, 1 May 2019 23:04:46 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 7943428F6A for ; Wed, 1 May 2019 23:04:46 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 57BCC212377FB; Wed, 1 May 2019 16:04:46 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::249; helo=mail-oi1-x249.google.com; envelope-from=3cybkxa4kdgwlboxnkxrsqqsxcqyyqvo.mywvsxeh-xfnswwvscdc.kl.ybq@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-oi1-x249.google.com (mail-oi1-x249.google.com [IPv6:2607:f8b0:4864:20::249]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id C85E621962301 for ; Wed, 1 May 2019 16:04:44 -0700 (PDT) Received: by mail-oi1-x249.google.com with SMTP id y197so323649oia.22 for ; Wed, 01 May 2019 16:04:44 -0700 (PDT) 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=chqbiB4K0p/zEKsVbXe4iW2Xpj6yAVIrWQhjpUaEvyA=; b=GE0P464mBtC8wrhfBO7g793E4I4dqW2RIP7+tHwU/PC57hjNo6fmzw5HElvCwVJ32i ZkuQrQPVcHSfFt6v723klhDSf/BCGNNvLIImZbUWeECzTjsM5G6js6Y4BB5vjlq16LAN nySbgJK0p0eYO+3PMfew/5txRXxjgd1tovWq5YKZjGYzlDxEQ+Q6GSZnOMuy+DFhqKMT uJ3lLNt16MIjzkoA11kKC2qDuhYsfvUVFDvaTEF8ASb3zPAzcT2kDwqalnmACvYsWp+Z 7T2yCIRZwzp4TkmtK1c2L5CoSAeNHI4xKfYHm9/xZ7l5IMHM0zff93mqcrZ2D4EpNlPo NA2Q== 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=chqbiB4K0p/zEKsVbXe4iW2Xpj6yAVIrWQhjpUaEvyA=; b=I+ms70Gec9yk1E8hJu/buWkWAAF8AZo8AJNin2l6XVx23f7CKA7slzVzYJu5OJXxBD +4K6ozph817l03063944CNyTRiwS201KxKwHmsJ51QP0TzklUC0rm7C5AmC9qPcJ6P+9 odietrlBt8m/RDZxbLzHlSSm7mS7vZ00mibhSF0yZnk7z9ZwDPjj2dsHNo3pK3/PVe1s j3E/3j7sLRmgIyOjawSUUZO458mr6THJL+dRf+pc8Ea3mVQjpgpbbqw4wJ5w+rk6eSuO jw7sNW/81nTUlnrwdXb5GeFe/QvdlyHat6nU4SZHyFGNQF1e4QGw/RmDAJhw0HIPh5j7 2WWQ== X-Gm-Message-State: APjAAAXObDC52m3g94WeUW9wMK4OJ8QOZonEPMumx/fikqHe0tihg6gE 9owzcFp7WDLTy/jNlDbDafNT0h70DwfrXROKZ0xcpw== X-Google-Smtp-Source: APXvYqziahGWgdBZbQfWG2ft5ebLp+ozkgppLiv8kSSis3P9fMvGcrL9eofrdvhLG15CPkadoMaZYjcvL9G8Pk6lBON+yg== X-Received: by 2002:aca:cd88:: with SMTP id d130mr481038oig.63.1556751883876; Wed, 01 May 2019 16:04:43 -0700 (PDT) Date: Wed, 1 May 2019 16:01:22 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-14-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 13/17] kunit: defconfig: add defconfigs for building KUnit tests From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Add defconfig for UML and a fragment that can be used to configure other architectures for building KUnit tests. Add option to kunit_tool to use a defconfig to create the kunitconfig. Signed-off-by: Brendan Higgins --- arch/um/configs/kunit_defconfig | 8 ++++++++ tools/testing/kunit/configs/all_tests.config | 8 ++++++++ tools/testing/kunit/kunit.py | 17 +++++++++++++++-- tools/testing/kunit/kunit_kernel.py | 3 ++- 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 arch/um/configs/kunit_defconfig create mode 100644 tools/testing/kunit/configs/all_tests.config diff --git a/arch/um/configs/kunit_defconfig b/arch/um/configs/kunit_defconfig new file mode 100644 index 0000000000000..bfe49689038f1 --- /dev/null +++ b/arch/um/configs/kunit_defconfig @@ -0,0 +1,8 @@ +CONFIG_OF=y +CONFIG_OF_UNITTEST=y +CONFIG_OF_OVERLAY=y +CONFIG_I2C=y +CONFIG_I2C_MUX=y +CONFIG_KUNIT=y +CONFIG_KUNIT_TEST=y +CONFIG_KUNIT_EXAMPLE_TEST=y diff --git a/tools/testing/kunit/configs/all_tests.config b/tools/testing/kunit/configs/all_tests.config new file mode 100644 index 0000000000000..bfe49689038f1 --- /dev/null +++ b/tools/testing/kunit/configs/all_tests.config @@ -0,0 +1,8 @@ +CONFIG_OF=y +CONFIG_OF_UNITTEST=y +CONFIG_OF_OVERLAY=y +CONFIG_I2C=y +CONFIG_I2C_MUX=y +CONFIG_KUNIT=y +CONFIG_KUNIT_TEST=y +CONFIG_KUNIT_EXAMPLE_TEST=y diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py index 7413ec7351a20..63e9fb3b60200 100755 --- a/tools/testing/kunit/kunit.py +++ b/tools/testing/kunit/kunit.py @@ -11,6 +11,7 @@ import argparse import sys import os import time +import shutil import kunit_config import kunit_kernel @@ -36,14 +37,26 @@ parser.add_argument('--build_dir', 'directory.', type=str, default=None, metavar='build_dir') -cli_args = parser.parse_args() +parser.add_argument('--defconfig', + help='Uses a default kunitconfig.', + action='store_true') -linux = kunit_kernel.LinuxSourceTree() +def create_default_kunitconfig(): + if not os.path.exists(kunit_kernel.KUNITCONFIG_PATH): + shutil.copyfile('arch/um/configs/kunit_defconfig', + kunit_kernel.KUNITCONFIG_PATH) + +cli_args = parser.parse_args() build_dir = None if cli_args.build_dir: build_dir = cli_args.build_dir +if cli_args.defconfig: + create_default_kunitconfig() + +linux = kunit_kernel.LinuxSourceTree() + config_start = time.time() success = linux.build_reconfig(build_dir) config_end = time.time() diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index 07c0abf2f47df..bf38768353313 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -14,6 +14,7 @@ import os import kunit_config KCONFIG_PATH = '.config' +KUNITCONFIG_PATH = 'kunitconfig' class ConfigError(Exception): """Represents an error trying to configure the Linux kernel.""" @@ -81,7 +82,7 @@ class LinuxSourceTree(object): def __init__(self): self._kconfig = kunit_config.Kconfig() - self._kconfig.read_from_file('kunitconfig') + self._kconfig.read_from_file(KUNITCONFIG_PATH) self._ops = LinuxSourceTreeOperations() def clean(self): From patchwork Wed May 1 23:01:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925847 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 A1C28912 for ; Wed, 1 May 2019 23:05:03 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8E79828BEE for ; Wed, 1 May 2019 23:05:03 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7F7BA28C0D; Wed, 1 May 2019 23:05:03 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id B87ED28BF9 for ; Wed, 1 May 2019 23:05:01 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id ABA8E212377F0; Wed, 1 May 2019 16:05:01 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::84a; helo=mail-qt1-x84a.google.com; envelope-from=3gybkxa4kdhwgwjsifsmnllnsxlttlqj.htrqnsz2-s0inrrqnxyx.56.twl@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-qt1-x84a.google.com (mail-qt1-x84a.google.com [IPv6:2607:f8b0:4864:20::84a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id C3AB1212377E6 for ; Wed, 1 May 2019 16:05:00 -0700 (PDT) Received: by mail-qt1-x84a.google.com with SMTP id f53so320296qte.21 for ; Wed, 01 May 2019 16:05:00 -0700 (PDT) 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=++IqKeBLqckP3pzOmVar4rTFxdL/ygBrsNe3aTLWHFw=; b=Mh4yNfHbU/r/hynANIpH/uBHmMlHKZNXH2yEHtO6ZZC18QRTV6NjLTD0oQA4RLSt+E 9zxm4myKMxtoyylfxzctOfq6/wmUE47cqwwTwujoD7BntKXRRLrkdNjFNR03r7Xl6SLi ef0Oj4UO/rD2VyyJe2njl4Pav0uHqoc3LDxaSLZ9QA8MNsk8FBTic2QtzrfT+1femF9f kMNbw8OuB0ybOhfF0/CUaJ/pALKgjk8agsnIe4NGtcuzMP48GTU0XTTDwBIvryD27EP5 8L3KWlvgTkHt477RyvSuLBaqZZdeIt6CfPZs1MXdW56e7bdqfQ6Ghre7zWFu0Tqe0etO 1/xw== 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=++IqKeBLqckP3pzOmVar4rTFxdL/ygBrsNe3aTLWHFw=; b=X1fznYW25fklzGWaGVA8xtuz6La9v/ExC+dMpiE2fJHX+/soUN0tkH5MYsXWXIRC3G M+E0aIUe/RH8gPdy2W6xveM3SzWTv1cGUr1k2oD7nslswSf2nZy2n06zwWpBCzXeSCVX 6uJoxHAQjG5AqY4wjaU8s065xkNDxfjngN8rW32cJba8rfLhoXn/kJ75C82JnUlvNkg7 HwKf9hPD7YulH+aD0K4th8vKlf3UxGBsuKrabxraFBal5Kpe9whI1HYIRs24ZHxtwwcT gFyYKxqaXoavFV8BXWz0FwWMcGlUZPvyrmJxDx7XnaNdSqv63ocdUzCJjP26k+ZNcUI6 8HPg== X-Gm-Message-State: APjAAAUzMhf2IljAq+6Vma/GNMohL+ga3Bk0xFp7XtMBb55YJfsrxSuG 1M0xuYWE+pF9TfLNv0e974ZkfFtYyqr/7LiXVHLi0g== X-Google-Smtp-Source: APXvYqxYOhEIiQvkotM+u1/2nPU9T6qCWImyl7hRlg584MNpx99ut/j31a6fba5mvJccBbafeTt79jt+YeSAwDMMlnVMqg== X-Received: by 2002:ae9:ee15:: with SMTP id i21mr523362qkg.107.1556751899843; Wed, 01 May 2019 16:04:59 -0700 (PDT) Date: Wed, 1 May 2019 16:01:23 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-15-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 14/17] Documentation: kunit: add documentation for KUnit From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org, Jonathan Corbet X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, Felix Guo , wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" 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 80a421cb935e7..264cfd613a774 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -65,6 +65,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..5c83ea9e21bc5 --- /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 May 1 23:01:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925853 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 6C823912 for ; Wed, 1 May 2019 23:05:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5DE7728BF0 for ; Wed, 1 May 2019 23:05:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 519B728C20; Wed, 1 May 2019 23:05:05 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 113CF28BF0 for ; Wed, 1 May 2019 23:05:05 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 07B5F212377FE; Wed, 1 May 2019 16:05:05 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::749; helo=mail-qk1-x749.google.com; envelope-from=3hibkxa4kdh8euhqgdqkljjlqvjrrjoh.frpolqx0-qyglppolvwv.34.ruj@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-qk1-x749.google.com (mail-qk1-x749.google.com [IPv6:2607:f8b0:4864:20::749]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 6726E212377E6 for ; Wed, 1 May 2019 16:05:03 -0700 (PDT) Received: by mail-qk1-x749.google.com with SMTP id t67so669002qkd.15 for ; Wed, 01 May 2019 16:05:03 -0700 (PDT) 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=afBg8MXFYLPd4ElKfFZ/trSYMLXMP5LFZoVsy/mQzmY=; b=PS4XqgmjxFmmLNG4a7+Yce39oWOBnGeJYietXRtGGHG0s3tkKrTLVv7t3JuVJrcTc1 KutFoAYJImIE74KhH7bcdjYmLxEqwrGbPqelhF2X00dTLnac17nc5IPrwiXTTicbz2f9 yHnW72a+93CtmfTKR5QnGRSzt8et/Ly4ZcAVMfSHn+SCxSXzw1zkw1nB3vFldf3GmDRW V4jWuf1z1oJKJ/mTHNywIeCyEt6ToYpE7TZ6xlVusdVzZZyPSGv5HNpNNowdGSJw2egz dfdafxGTQxLWVkGcavQ+2Y+ar4k87+QhgoKGq5GA6P9r8ZHVHqdJk/unxr+39pqN5T9x Di2w== 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=afBg8MXFYLPd4ElKfFZ/trSYMLXMP5LFZoVsy/mQzmY=; b=PLicmJDKvA1lSNKe+B1LlphMBrX6hGNznDmeL39rcDwrS5TjA/whqfdmUyS5neN1MY 3awh8lQVmpsZgfWQhAs6BeVaazkrBQ+Q0JxZ/Rme1czLned1+hyAgth+75IaiH3nVikc 9DwLAQ92u0jW11gsjhJeg+zH932yVJNN98nMvBM2kxeX8eohDZVRCrnB4ydkZNuZSozJ N7zDNm/f06aXddrX0rKcH10Vnu9Ahxw6pj2EM8Ny5lmD3Rj2HFYp0JuT46rSsxBQ7bvG eeav/rt8JiB7JdeNsNwLB6G1DZhy6Npfe17RuNK8HIwYNgdomd557CT4qMBfa6W+v414 x/0g== X-Gm-Message-State: APjAAAViKGw/iTPe3v25wyZJ41c5CzcArBc5Hte0piDK208DKjkdpAYW RvhmtLGCqt29QP50nM1oaj0uNIHURfjvxZrnsJi/LQ== X-Google-Smtp-Source: APXvYqyrbTtQWB061i/IuTmGR1fZJktprQX+nOIOp8d1N9azYCgnL4QkQ8IwHRr2AUs5hUUYn4R+op23q7+ENLYHRpSNog== X-Received: by 2002:ad4:5365:: with SMTP id e5mr604591qvv.197.1556751902442; Wed, 01 May 2019 16:05:02 -0700 (PDT) Date: Wed, 1 May 2019 16:01:24 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-16-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 15/17] MAINTAINERS: add entry for KUnit the unit testing framework From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" 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 5c38f21aee787..c78ae95c56b80 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8448,6 +8448,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 Chamberlain L: linux-kernel@vger.kernel.org From patchwork Wed May 1 23:01:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925861 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 15722933 for ; Wed, 1 May 2019 23:05:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 02AEB28BEB for ; Wed, 1 May 2019 23:05:08 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EAB6B28BF0; Wed, 1 May 2019 23:05:07 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 4856928BEB for ; Wed, 1 May 2019 23:05:07 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 3C6C5212377F4; Wed, 1 May 2019 16:05:07 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::e49; helo=mail-vs1-xe49.google.com; envelope-from=3isbkxa4kdiihxktjgtnommotymuumrk.iusrot03-t1jossroyzy.67.uxm@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-vs1-xe49.google.com (mail-vs1-xe49.google.com [IPv6:2607:f8b0:4864:20::e49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 31A85212377E6 for ; Wed, 1 May 2019 16:05:06 -0700 (PDT) Received: by mail-vs1-xe49.google.com with SMTP id p134so40499vsc.10 for ; Wed, 01 May 2019 16:05:06 -0700 (PDT) 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=PFBS31SOQXYxw4yS+C4amFknlw8hawiRGGMxTbQ36y4=; b=CT9czlwnMnLnA2cs0qndAQOiX2aPx2OSYAqbzTjXJr/BBhWM9cK1PNnFVhmDqrO9Nj NkQml1h79HIx2biFYzVc5UeKJHOL44Q1INMyy/lKH50HhKXx0N2SESZFVEWWH6yuHvkr 2d6C1BrjDq9fm2aFqJ+RhhcZDwa6yMEiVjAniuAxk7neMUkPfEJfF16rOpLWJxCNajJr sTVm9wiXxWgnLk64sKD4lOyDUkMUaKjOKM/EZahUgPoF4dqE866ndUC4DZs0iOz20pxU e+hRKa5endOO4JBLJiHhqK2KxRUvVFsiFIdK7rmY4OLCE+PpIn9AVR8dqbRl4NI3BEJG KcAg== 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=PFBS31SOQXYxw4yS+C4amFknlw8hawiRGGMxTbQ36y4=; b=jQgbCUhChrlpzfUnWUUErHAV3GMIj+k+BDlnb89BDfoUpjNr1dUolhl6y0XdvEn/1M YcmvgUfyt0HcP++VPBH1KCNjecHyEHlN7DDz1EQc8clN3XB5jYizXqIYMINsyWo/1BNX Gb2bOK89kcNM4Zt8xKY9lP3Cn0vMT9Vjfev4pX2XHszpe7+rt1kCKwzMkW22Jv3qQvxf QMIUrnKbyNWZ6qHeDt89iC1tcPjDZ1uWVMw028/mHweQjEXxgLjISzaxhv7YnEfUe11j pG1xMIJVINExGS/653L1xdBDi+YAKXFpORN4Pvahr9SqR3HDpUVgRsYP2ZnNqlqO3NM1 3qbg== X-Gm-Message-State: APjAAAV9tDHoWakYeok/y1XdGjoUxc+N4uF03w0yPOzdO8QCnWxEOj+z 7rXXqxMENs2ehiL0Ve+RQjm6NkmYygB8aIbpmYxnqA== X-Google-Smtp-Source: APXvYqyW0QaIJ9/fn349r10l0ts9l27TIANLui0Et8DiwcFOvDM/az2AcijbBC7llTdGDodV01VSlxHM/JSGOugaJ7WgKw== X-Received: by 2002:a67:ed44:: with SMTP id m4mr319097vsp.112.1556751905107; Wed, 01 May 2019 16:05:05 -0700 (PDT) Date: Wed, 1 May 2019 16:01:25 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-17-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 16/17] kernel/sysctl-test: Add null pointer test for sysctl.c:proc_dointvec() From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, Iurii Zaikin , jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP From: Iurii Zaikin KUnit tests for initialized data behavior of proc_dointvec that is explicitly checked in the code. Includes basic parsing tests including int min/max overflow. Signed-off-by: Iurii Zaikin Signed-off-by: Brendan Higgins --- kernel/Makefile | 2 + kernel/sysctl-test.c | 292 +++++++++++++++++++++++++++++++++++++++++++ lib/Kconfig.debug | 6 + 3 files changed, 300 insertions(+) create mode 100644 kernel/sysctl-test.c diff --git a/kernel/Makefile b/kernel/Makefile index 6c57e78817dad..c81a8976b6a4b 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -112,6 +112,8 @@ obj-$(CONFIG_HAS_IOMEM) += iomem.o obj-$(CONFIG_ZONE_DEVICE) += memremap.o obj-$(CONFIG_RSEQ) += rseq.o +obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o + obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o KASAN_SANITIZE_stackleak.o := n KCOV_INSTRUMENT_stackleak.o := n diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c new file mode 100644 index 0000000000000..a0fba6b6fc2dc --- /dev/null +++ b/kernel/sysctl-test.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit test of proc sysctl. + */ + +#include +#include +#include +#include + +static int i_zero; +static int i_one_hundred = 100; + +struct test_sysctl_data { + int int_0001; + int int_0002; + int int_0003[4]; + + unsigned int uint_0001; + + char string_0001[65]; +}; + +static struct test_sysctl_data test_data = { + .int_0001 = 60, + .int_0002 = 1, + + .int_0003[0] = 0, + .int_0003[1] = 1, + .int_0003[2] = 2, + .int_0003[3] = 3, + + .uint_0001 = 314, + + .string_0001 = "(none)", +}; + +static void sysctl_test_dointvec_null_tbl_data(struct kunit *test) +{ + struct ctl_table table = { + .procname = "foo", + .data = NULL, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + .extra1 = &i_zero, + .extra2 = &i_one_hundred, + }; + void *buffer = kunit_kzalloc(test, sizeof(int), GFP_USER); + size_t len; + loff_t pos; + + len = 1234; + KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 0, buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, len); + len = 1234; + KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 1, buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, len); +} + +static void sysctl_test_dointvec_table_maxlen_unset(struct kunit *test) +{ + struct ctl_table table = { + .procname = "foo", + .data = &test_data.int_0001, + .maxlen = 0, + .mode = 0644, + .proc_handler = proc_dointvec, + .extra1 = &i_zero, + .extra2 = &i_one_hundred, + }; + void *buffer = kunit_kzalloc(test, sizeof(int), GFP_USER); + size_t len; + loff_t pos; + + len = 1234; + KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 0, buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, len); + len = 1234; + KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 1, buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, len); +} + +static void sysctl_test_dointvec_table_len_is_zero(struct kunit *test) +{ + struct ctl_table table = { + .procname = "foo", + .data = &test_data.int_0001, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + .extra1 = &i_zero, + .extra2 = &i_one_hundred, + }; + void *buffer = kunit_kzalloc(test, sizeof(int), GFP_USER); + size_t len; + loff_t pos; + + len = 0; + KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 0, buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, len); + KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 1, buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, len); +} + +static void sysctl_test_dointvec_table_read_but_position_set(struct kunit *test) +{ + struct ctl_table table = { + .procname = "foo", + .data = &test_data.int_0001, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + .extra1 = &i_zero, + .extra2 = &i_one_hundred, + }; + void *buffer = kunit_kzalloc(test, sizeof(int), GFP_USER); + size_t len; + loff_t pos; + + len = 1234; + pos = 1; + KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 0, buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, len); +} + +static void sysctl_test_dointvec_happy_single_positive(struct kunit *test) +{ + struct ctl_table table = { + .procname = "foo", + .data = &test_data.int_0001, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + .extra1 = &i_zero, + .extra2 = &i_one_hundred, + }; + char input[] = "9"; + size_t len = sizeof(input) - 1; + loff_t pos = 0; + + table.data = kunit_kzalloc(test, sizeof(int), GFP_USER); + KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 1, input, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len); + KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos); + KUNIT_EXPECT_EQ(test, 9, *(int *)table.data); +} + +static void sysctl_test_dointvec_happy_single_negative(struct kunit *test) +{ + struct ctl_table table = { + .procname = "foo", + .data = &test_data.int_0001, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + .extra1 = &i_zero, + .extra2 = &i_one_hundred, + }; + char input[] = "-9"; + size_t len = sizeof(input) - 1; + loff_t pos = 0; + + table.data = kunit_kzalloc(test, sizeof(int), GFP_USER); + KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, 1, input, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len); + KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos); + KUNIT_EXPECT_EQ(test, -9, *(int *)table.data); +} + +static void sysctl_test_dointvec_single_less_int_min(struct kunit *test) +{ + struct ctl_table table = { + .procname = "foo", + .data = &test_data.int_0001, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + .extra1 = &i_zero, + .extra2 = &i_one_hundred, + }; + char input[32]; + size_t len = sizeof(input) - 1; + loff_t pos = 0; + long less_than_min = (long)INT_MIN - 1; + + KUNIT_EXPECT_LT(test, less_than_min, INT_MIN); + KUNIT_EXPECT_LT(test, + snprintf(input, sizeof(input), "%ld", less_than_min), + sizeof(input)); + + table.data = kunit_kzalloc(test, sizeof(int), GFP_USER); + KUNIT_EXPECT_EQ(test, -EINVAL, + proc_dointvec(&table, 1, input, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len); + KUNIT_EXPECT_EQ(test, 0, *(int *)table.data); +} + +static void sysctl_test_dointvec_single_greater_int_max(struct kunit *test) +{ + struct ctl_table table = { + .procname = "foo", + .data = &test_data.int_0001, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + .extra1 = &i_zero, + .extra2 = &i_one_hundred, + }; + char input[32]; + size_t len = sizeof(input) - 1; + loff_t pos = 0; + long greater_than_max = (long)INT_MAX + 1; + + KUNIT_EXPECT_GT(test, greater_than_max, INT_MAX); + KUNIT_EXPECT_LT(test, snprintf(input, sizeof(input), "%ld", + greater_than_max), + sizeof(input)); + table.data = kunit_kzalloc(test, sizeof(int), GFP_USER); + KUNIT_EXPECT_EQ(test, -EINVAL, + proc_dointvec(&table, 1, input, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len); + KUNIT_EXPECT_EQ(test, 0, *(int *)table.data); +} + +static int sysctl_test_init(struct kunit *test) +{ + return 0; +} + +/* + * This is run once after each test case, see the comment on example_test_module + * for more information. + */ +static void sysctl_test_exit(struct kunit *test) +{ +} + +/* + * Here we make a list of all the test cases we want to add to the test module + * below. + */ +static struct kunit_case sysctl_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(sysctl_test_dointvec_null_tbl_data), + KUNIT_CASE(sysctl_test_dointvec_table_maxlen_unset), + KUNIT_CASE(sysctl_test_dointvec_table_len_is_zero), + KUNIT_CASE(sysctl_test_dointvec_table_read_but_position_set), + KUNIT_CASE(sysctl_test_dointvec_happy_single_positive), + KUNIT_CASE(sysctl_test_dointvec_happy_single_negative), + KUNIT_CASE(sysctl_test_dointvec_single_less_int_min), + KUNIT_CASE(sysctl_test_dointvec_single_greater_int_max), + {}, +}; + +/* + * This defines a suite or grouping of tests. + * + * Test cases are defined as belonging to the suite by adding them to + * `test_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 sysctl_test_module = { + .name = "sysctl_test", + .init = sysctl_test_init, + .exit = sysctl_test_exit, + .test_cases = sysctl_test_cases, +}; + +/* + * This registers the above test module telling KUnit that this is a suite of + * tests that need to be run. + */ +module_test(sysctl_test_module); diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index d5a4a4036d2f8..772af4ec70111 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1908,6 +1908,12 @@ config TEST_SYSCTL If unsure, say N. +config SYSCTL_KUNIT_TEST + bool "KUnit test for sysctl" + depends on KUNIT + help + Enables KUnit sysctl test. + config TEST_UDELAY tristate "udelay test driver" help From patchwork Wed May 1 23:01:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 10925865 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 D637E933 for ; Wed, 1 May 2019 23:05:10 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C4D1428BEB for ; Wed, 1 May 2019 23:05:10 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B86E628BF0; Wed, 1 May 2019 23:05:10 +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=-2.7 required=2.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 7E4BE28BEB for ; Wed, 1 May 2019 23:05:10 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 72B8E21237801; Wed, 1 May 2019 16:05:10 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::c4a; helo=mail-yw1-xc4a.google.com; envelope-from=3iybkxa4kdiqjzmvlivpqooqv0owwotm.kwutqv25-v3lquutq010.89.wzo@flex--brendanhiggins.bounces.google.com; receiver=linux-nvdimm@lists.01.org Received: from mail-yw1-xc4a.google.com (mail-yw1-xc4a.google.com [IPv6:2607:f8b0:4864:20::c4a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 76878212377F9 for ; Wed, 1 May 2019 16:05:08 -0700 (PDT) Received: by mail-yw1-xc4a.google.com with SMTP id v127so1087067ywb.20 for ; Wed, 01 May 2019 16:05:08 -0700 (PDT) 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=PRLXnvg3FnToIBJTwXVR7d2TK/MHZSW5pjHfdNwtr7Q=; b=K5TwnumODggM3WBroA2xjKir+Hf59I1szowVRTt0lzCy/25DKO4Auoy9KuasdBqFUu UWvBOKOTmtxVhAOl4er1T2140+nu1Bz+99TiGL9+yTK//z+jqboMkU5aijHYP0BbYsOh P/McaehM5toEAU772+pJGpQWNEZx5xhfrNPAJoKaW0IpWIVJaq0KjD8R8F3QXpB30i5m XrT04TmEJJAw/8yrNXA4XJeGyU/uVcdsIpKRYnl9kRb2X6reSvrGJiWqLpi2xAFDwIgz M+mClrFoRM5PxaczMRkZTVN0VXOBXS2l+ZY6dXc7xjKICM5LbtnpXVDT7unPYWdXqa7Y wpCA== 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=PRLXnvg3FnToIBJTwXVR7d2TK/MHZSW5pjHfdNwtr7Q=; b=DqIHdnMSfNZVaZyP9wptLw0vPH25WHzsxQ/o49ZQSPrW0WBB3zOqN4c11hv4z86DVe C6GbPWt8jP9593Q6E+qKspWm1vHpPHMx9xcr7qGV5W6CIJMqYHECg2XaiytuSjMLq/X2 lOK40eyOlQQdea/AUOT5gFMtXOjk36NOIsPuDau4LjWFlzk76ej8h3jqZKxbxK7L1DNW loWvOxdjrrbhJGheRkFLD8oOa60IPFNrXyjJT1rKse1qF2KCQ7zsrNkrl1lu4e9xjdBc GWjr23qpEtlHQmKR+ZGEj12cbx6Uk4PU2kL1RSltqu8xX8NrJsagl0ipgp1zrbm+Lhgl 0rbQ== X-Gm-Message-State: APjAAAXILLKjkDSxbVSVZdoxtehMl8aHtPm758FhtkeRdOIT4+AWbK8V 3O/jWpRvRomYZsX9C45eEoO+Ka4Ybyae2hFgdi1FZQ== X-Google-Smtp-Source: APXvYqxSccXA/VUA/6JwG1W+T505ogTb6cCEY1XA3skvxQHV+dYiYJA75B70f0UwwhMZGG1vCcs3UYjo4MLfLh9fEqXVcw== X-Received: by 2002:a25:aa87:: with SMTP id t7mr327533ybi.419.1556751907604; Wed, 01 May 2019 16:05:07 -0700 (PDT) Date: Wed, 1 May 2019 16:01:26 -0700 In-Reply-To: <20190501230126.229218-1-brendanhiggins@google.com> Message-Id: <20190501230126.229218-18-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190501230126.229218-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.21.0.593.g511ec345e18-goog Subject: [PATCH v2 17/17] MAINTAINERS: add proc sysctl KUnit test to PROC SYSCTL section From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pmladek@suse.com, linux-doc@vger.kernel.org, amir73il@gmail.com, Brendan Higgins , dri-devel@lists.freedesktop.org, Alexander.Levin@microsoft.com, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, khilman@baylibre.com, knut.omang@oracle.com, wfg@linux.intel.com, joel@jms.id.au, rientjes@google.com, jdike@addtoit.com, dan.carpenter@oracle.com, devicetree@vger.kernel.org, linux-kbuild@vger.kernel.org, Tim.Bird@sony.com, linux-um@lists.infradead.org, rostedt@goodmis.org, julia.lawall@lip6.fr, kunit-dev@googlegroups.com, richard@nod.at, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Add entry for the new proc sysctl KUnit test to the PROC SYSCTL section. Signed-off-by: Brendan Higgins --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index c78ae95c56b80..23cc97332c3d7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12524,6 +12524,7 @@ S: Maintained F: fs/proc/proc_sysctl.c F: include/linux/sysctl.h F: kernel/sysctl.c +F: kernel/sysctl-test.c F: tools/testing/selftests/sysctl/ PS3 NETWORK SUPPORT