From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.5 required=3.0 tests=DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH, MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B73E8C3A5A2 for ; Fri, 20 Sep 2019 23:50:42 +0000 (UTC) Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 83D0D2173E for ; Fri, 20 Sep 2019 23:50:42 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=google.com header.i=@google.com header.b="EIigQoGz" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 83D0D2173E Authentication-Results: mail.kernel.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-nvdimm-bounces@lists.01.org Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 4A627202ECFCF; Fri, 20 Sep 2019 16:49:37 -0700 (PDT) Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::84a; helo=mail-qt1-x84a.google.com; envelope-from=3o16fxq4kdgghxktjgtnommotymuumrk.iusrotad-tbjossroyzy.gh.uxm@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 083B1202ECFBC for ; Fri, 20 Sep 2019 16:49:35 -0700 (PDT) Received: by mail-qt1-x84a.google.com with SMTP id r15so10029453qtn.12 for ; Fri, 20 Sep 2019 16:50:41 -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=jTA+bk9ZymR7w9FA5GVlXgp3DFkWG3VqdQfYnO3OtT4=; b=EIigQoGzuto4GqRTiOhHSB7j2E2nPoH/i6A/aheNezYCgb2C5Mfrql1LZVCsDwvDp5 p9nk+S25MBCa19ftyNBkaRIZLgAp6ph7b7s/a/m3rKJrSMo4M0OVFGhw+/69iuxC+pA/ VQuBuMb54V2g72mvh+otmrIOWsvJXytOHfFcE38PknaARu4ZmYSypmHekzbxyNYKeDWy RZav/Bet81bu50nxa8ZelErYEADvTDz1wBTT2DmOP64xNiO5MIy9UCNDSnL/TGDF5a/5 UMyUYwWs3xevql/yhfOYUlDhIx3IfySIxD3Piy/L4WgSzyrJJQYbSnSfHRIDli/s7dod yILw== 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=jTA+bk9ZymR7w9FA5GVlXgp3DFkWG3VqdQfYnO3OtT4=; b=I/VOjHF5bY4tGyprqRDMgUz3kgH7w3Se6Vz0hW0VX3MAHV+oPYjGTgmSgjnJQGmfGy FKGBYh13ANACR6oFbZBnVTKqQpurhhalLmKArT8MMiPPimILR7B+kQTP/C1alG/9qhox B9Hk2tfzESkvCdGBIDFSBHOdtRPNUbHEM9bMhC4G0RH95HBmK+7sDIyebd49GR2MJVFz n/+5xsvJmXMS1mH+SioyeLlg77RaUSnQMjoO0eupfvThJIEZtkY3wAkhDvbeA5d3wQg0 rSGGT+PY/xljm4hcC545nXGyXKvs8QaTX9SZgiD516v6f8x+0is02bCSqqJu/ZH5tWi8 qosQ== X-Gm-Message-State: APjAAAURweY4qQa2TCK9tdYh8O6IQeHDhZJQW+D8fa5NFjGce6wUQLqD lU4WMswULEnc1GiD9m1bY9hfUD6X0HquZjcmSd7dcw== X-Google-Smtp-Source: APXvYqyc0qO3eA1hHtP2jT6KGMfXP8qBY3vL1yKY80fWZTfKLiEgZCZn92wrccWqj2PU8t6Gc40A6XwqKQJWx57x7Qhw5w== X-Received: by 2002:ac8:41c1:: with SMTP id o1mr5992524qtm.341.1569021603503; Fri, 20 Sep 2019 16:20:03 -0700 (PDT) Date: Fri, 20 Sep 2019 16:19:13 -0700 In-Reply-To: <20190920231923.141900-1-brendanhiggins@google.com> Message-Id: <20190920231923.141900-10-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190920231923.141900-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.23.0.351.gc4317032e6-goog Subject: [PATCH v16 09/19] kunit: test: add support for test abort From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, jpoimboe@redhat.com, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, peterz@infradead.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org, tytso@mit.edu, yamada.masahiro@socionext.com 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, torvalds@linux-foundation.org, rdunlap@infradead.org, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" 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 Reviewed-by: Greg Kroah-Hartman Reviewed-by: Logan Gunthorpe Reviewed-by: Stephen Boyd --- include/kunit/test.h | 2 + include/kunit/try-catch.h | 75 +++++++++++++++++++++ lib/kunit/Makefile | 3 +- lib/kunit/test.c | 137 +++++++++++++++++++++++++++++++++----- lib/kunit/try-catch.c | 118 ++++++++++++++++++++++++++++++++ 5 files changed, 319 insertions(+), 16 deletions(-) create mode 100644 include/kunit/try-catch.h create mode 100644 lib/kunit/try-catch.c diff --git a/include/kunit/test.h b/include/kunit/test.h index 30a62de16bc9..3d554d7c1c79 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -10,6 +10,7 @@ #define _KUNIT_TEST_H #include +#include #include #include #include @@ -172,6 +173,7 @@ struct kunit { /* private: internal use only. */ const char *name; /* Read only after initialization! */ + struct kunit_try_catch try_catch; /* * success starts as true, and may only be set to false during a * test case; thus, it is safe to update this across multiple diff --git a/include/kunit/try-catch.h b/include/kunit/try-catch.h new file mode 100644 index 000000000000..404f336cbdc8 --- /dev/null +++ b/include/kunit/try-catch.h @@ -0,0 +1,75 @@ +/* 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 completion; +struct kunit; + +/** + * struct kunit_try_catch - provides a generic way to run code which might fail. + * @test: The test case that is currently being executed. + * @try_completion: Completion that the control thread waits on while test runs. + * @try_result: Contains any errno obtained while running test case. + * @try: The function, the test case, to attempt to run. + * @catch: The function called if @try bails out. + * @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 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. */ + struct kunit *test; + struct completion *try_completion; + int try_result; + kunit_try_catch_func_t try; + kunit_try_catch_func_t catch; + void *context; +}; + +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); + +void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context); + +void __noreturn kunit_try_catch_throw(struct kunit_try_catch *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/lib/kunit/Makefile b/lib/kunit/Makefile index 4e46450bcb3a..c9176c9c578c 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_KUNIT) += test.o \ string-stream.o \ - assert.o + assert.o \ + try-catch.o obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 3cbceb34b3b3..b2ca9b94c353 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -7,7 +7,9 @@ */ #include +#include #include +#include static void kunit_set_failure(struct kunit *test) { @@ -162,6 +164,19 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert) WARN_ON(string_stream_destroy(stream)); } +static void __noreturn kunit_abort(struct kunit *test) +{ + kunit_try_catch_throw(&test->try_catch); /* Does not return. */ + + /* + * 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"); +} + void kunit_do_assertion(struct kunit *test, struct kunit_assert *assert, bool pass, @@ -180,6 +195,9 @@ void kunit_do_assertion(struct kunit *test, kunit_fail(test, assert); va_end(args); + + if (assert->type == KUNIT_ASSERTION) + kunit_abort(test); } void kunit_init_test(struct kunit *test, const char *name) @@ -191,33 +209,122 @@ void kunit_init_test(struct kunit *test, const char *name) } /* - * Performs all logic to run a test case. + * Initializes and runs test case. Does not clean up or do post validations. */ -static void kunit_run_case(struct kunit_suite *suite, - struct kunit_case *test_case) +static void kunit_run_case_internal(struct kunit *test, + struct kunit_suite *suite, + struct kunit_case *test_case) { - struct kunit test; - - kunit_init_test(&test, test_case->name); - if (suite->init) { int ret; - ret = suite->init(&test); + ret = suite->init(test); if (ret) { - kunit_err(&test, "failed to initialize: %d\n", ret); - kunit_set_failure(&test); - test_case->success = test.success; + kunit_err(test, "failed to initialize: %d\n", ret); + kunit_set_failure(test); return; } } - test_case->run_case(&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! + */ +static void kunit_run_case_cleanup(struct kunit *test, + struct kunit_suite *suite) +{ if (suite->exit) - suite->exit(&test); + suite->exit(test); + + kunit_case_internal_cleanup(test); +} + +struct kunit_try_catch_context { + struct kunit *test; + struct kunit_suite *suite; + 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_suite *suite = ctx->suite; + 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, suite, test_case); + /* This line may never be reached. */ + kunit_run_case_cleanup(test, suite); +} + +static void kunit_catch_run_case(void *data) +{ + struct kunit_try_catch_context *ctx = data; + struct kunit *test = ctx->test; + struct kunit_suite *suite = ctx->suite; + int try_exit_code = kunit_try_catch_get_result(&test->try_catch); + + if (try_exit_code) { + kunit_set_failure(test); + /* + * 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; + } + + /* + * Test case was run, but aborted. It is the test case's business as to + * whether it failed or not, we just need to clean up. + */ + kunit_run_case_cleanup(test, suite); +} + +/* + * Performs all logic to run a test case. It also catches most errors that + * occur in a test case and reports them as failures. + */ +static void kunit_run_case_catch_errors(struct kunit_suite *suite, + struct kunit_case *test_case) +{ + struct kunit_try_catch_context context; + struct kunit_try_catch *try_catch; + struct kunit test; + + kunit_init_test(&test, test_case->name); + try_catch = &test.try_catch; - kunit_cleanup(&test); + kunit_try_catch_init(try_catch, + &test, + kunit_try_run_case, + kunit_catch_run_case); + context.test = &test; + context.suite = suite; + context.test_case = test_case; + kunit_try_catch_run(try_catch, &context); test_case->success = test.success; } @@ -230,7 +337,7 @@ int kunit_run_tests(struct kunit_suite *suite) kunit_print_subtest_start(suite); for (test_case = suite->test_cases; test_case->run_case; test_case++) { - kunit_run_case(suite, test_case); + kunit_run_case_catch_errors(suite, test_case); kunit_print_test_case_ok_not_ok(test_case, test_case_count++); } diff --git a/lib/kunit/try-catch.c b/lib/kunit/try-catch.c new file mode 100644 index 000000000000..55686839eb61 --- /dev/null +++ b/lib/kunit/try-catch.c @@ -0,0 +1,118 @@ +// 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 +#include +#include + +void __noreturn kunit_try_catch_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 unsigned long kunit_test_timeout(void) +{ + unsigned long timeout_msecs; + + /* + * 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 + */ + if (sysctl_hung_task_timeout_secs) { + /* + * If sysctl_hung_task is active, just set the timeout to some + * value less than that. + * + * In regards to the above TODO, if we decide on variable + * timeouts, this logic will likely need to change. + */ + timeout_msecs = (sysctl_hung_task_timeout_secs - 1) * + MSEC_PER_SEC; + } else { + timeout_msecs = 300 * MSEC_PER_SEC; /* 5 min */ + } + + return timeout_msecs; +} + +void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context) +{ + DECLARE_COMPLETION_ONSTACK(try_completion); + struct kunit *test = try_catch->test; + struct task_struct *task_struct; + int exit_code, time_remaining; + + try_catch->context = context; + 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; + } + + time_remaining = wait_for_completion_timeout(&try_completion, + kunit_test_timeout()); + if (time_remaining == 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_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; + try_catch->try = try; + try_catch->catch = catch; +} -- 2.23.0.351.gc4317032e6-goog _______________________________________________ Linux-nvdimm mailing list Linux-nvdimm@lists.01.org https://lists.01.org/mailman/listinfo/linux-nvdimm From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-17.4 required=3.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH, MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0A1F2C49ED7 for ; Fri, 20 Sep 2019 23:21:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B64C32173E for ; Fri, 20 Sep 2019 23:21:15 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="EIigQoGz" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2437240AbfITXVO (ORCPT ); Fri, 20 Sep 2019 19:21:14 -0400 Received: from mail-qt1-f201.google.com ([209.85.160.201]:51098 "EHLO mail-qt1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2437009AbfITXUG (ORCPT ); Fri, 20 Sep 2019 19:20:06 -0400 Received: by mail-qt1-f201.google.com with SMTP id x26so9947669qtr.17 for ; Fri, 20 Sep 2019 16:20:04 -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=jTA+bk9ZymR7w9FA5GVlXgp3DFkWG3VqdQfYnO3OtT4=; b=EIigQoGzuto4GqRTiOhHSB7j2E2nPoH/i6A/aheNezYCgb2C5Mfrql1LZVCsDwvDp5 p9nk+S25MBCa19ftyNBkaRIZLgAp6ph7b7s/a/m3rKJrSMo4M0OVFGhw+/69iuxC+pA/ VQuBuMb54V2g72mvh+otmrIOWsvJXytOHfFcE38PknaARu4ZmYSypmHekzbxyNYKeDWy RZav/Bet81bu50nxa8ZelErYEADvTDz1wBTT2DmOP64xNiO5MIy9UCNDSnL/TGDF5a/5 UMyUYwWs3xevql/yhfOYUlDhIx3IfySIxD3Piy/L4WgSzyrJJQYbSnSfHRIDli/s7dod yILw== 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=jTA+bk9ZymR7w9FA5GVlXgp3DFkWG3VqdQfYnO3OtT4=; b=j2nwrtp3Bo5lme8AaiGjA0yr401Urtd8FkHSnMHDKZUFrmnjygeit5hvhmiG9L2R/Y 4R2H35WCYcKGynwtgKilO/om9OIkQQ9kdCM3Li/it9H2KsDGKIPcvjVwjhoeGirVMTx9 U0n87Fu3+uBmiMiinRJG24qQxdKAiHkiWObJ5zEPAEdkTClbOsqva2UYsLOl/uU7y+N8 h6BpLX6a8v7MmWKflmdfbROxnxQr/rXr2IhpMmk5D2IldwLNWMOdBrUdnB4pl8F4VcRh a2gEpM4ecjw7DdEPjkx8OZJt4mnYvnyNOdrmLQFAgEUMDF/Qch56ScL16vE+ZcG4m+c2 cB+A== X-Gm-Message-State: APjAAAVvgbSaUuEpi0Gityk6xxMNcf6FivDQew+VNlM+S5d/Y4ipriyt 7Sc7zF8tGiWpQE3T8mCoP9n0VlIvCc2ypsHQ6F3NHw== X-Google-Smtp-Source: APXvYqyc0qO3eA1hHtP2jT6KGMfXP8qBY3vL1yKY80fWZTfKLiEgZCZn92wrccWqj2PU8t6Gc40A6XwqKQJWx57x7Qhw5w== X-Received: by 2002:ac8:41c1:: with SMTP id o1mr5992524qtm.341.1569021603503; Fri, 20 Sep 2019 16:20:03 -0700 (PDT) Date: Fri, 20 Sep 2019 16:19:13 -0700 In-Reply-To: <20190920231923.141900-1-brendanhiggins@google.com> Message-Id: <20190920231923.141900-10-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190920231923.141900-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.23.0.351.gc4317032e6-goog Subject: [PATCH v16 09/19] kunit: test: add support for test abort From: Brendan Higgins To: frowand.list@gmail.com, gregkh@linuxfoundation.org, jpoimboe@redhat.com, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, peterz@infradead.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org, tytso@mit.edu, yamada.masahiro@socionext.com Cc: devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, kunit-dev@googlegroups.com, linux-doc@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-nvdimm@lists.01.org, linux-um@lists.infradead.org, Alexander.Levin@microsoft.com, Tim.Bird@sony.com, amir73il@gmail.com, dan.carpenter@oracle.com, daniel@ffwll.ch, jdike@addtoit.com, joel@jms.id.au, julia.lawall@lip6.fr, khilman@baylibre.com, knut.omang@oracle.com, logang@deltatee.com, mpe@ellerman.id.au, pmladek@suse.com, rdunlap@infradead.org, richard@nod.at, rientjes@google.com, rostedt@goodmis.org, wfg@linux.intel.com, torvalds@linux-foundation.org, Brendan Higgins Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 Reviewed-by: Greg Kroah-Hartman Reviewed-by: Logan Gunthorpe Reviewed-by: Stephen Boyd --- include/kunit/test.h | 2 + include/kunit/try-catch.h | 75 +++++++++++++++++++++ lib/kunit/Makefile | 3 +- lib/kunit/test.c | 137 +++++++++++++++++++++++++++++++++----- lib/kunit/try-catch.c | 118 ++++++++++++++++++++++++++++++++ 5 files changed, 319 insertions(+), 16 deletions(-) create mode 100644 include/kunit/try-catch.h create mode 100644 lib/kunit/try-catch.c diff --git a/include/kunit/test.h b/include/kunit/test.h index 30a62de16bc9..3d554d7c1c79 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -10,6 +10,7 @@ #define _KUNIT_TEST_H #include +#include #include #include #include @@ -172,6 +173,7 @@ struct kunit { /* private: internal use only. */ const char *name; /* Read only after initialization! */ + struct kunit_try_catch try_catch; /* * success starts as true, and may only be set to false during a * test case; thus, it is safe to update this across multiple diff --git a/include/kunit/try-catch.h b/include/kunit/try-catch.h new file mode 100644 index 000000000000..404f336cbdc8 --- /dev/null +++ b/include/kunit/try-catch.h @@ -0,0 +1,75 @@ +/* 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 completion; +struct kunit; + +/** + * struct kunit_try_catch - provides a generic way to run code which might fail. + * @test: The test case that is currently being executed. + * @try_completion: Completion that the control thread waits on while test runs. + * @try_result: Contains any errno obtained while running test case. + * @try: The function, the test case, to attempt to run. + * @catch: The function called if @try bails out. + * @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 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. */ + struct kunit *test; + struct completion *try_completion; + int try_result; + kunit_try_catch_func_t try; + kunit_try_catch_func_t catch; + void *context; +}; + +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); + +void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context); + +void __noreturn kunit_try_catch_throw(struct kunit_try_catch *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/lib/kunit/Makefile b/lib/kunit/Makefile index 4e46450bcb3a..c9176c9c578c 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_KUNIT) += test.o \ string-stream.o \ - assert.o + assert.o \ + try-catch.o obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 3cbceb34b3b3..b2ca9b94c353 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -7,7 +7,9 @@ */ #include +#include #include +#include static void kunit_set_failure(struct kunit *test) { @@ -162,6 +164,19 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert) WARN_ON(string_stream_destroy(stream)); } +static void __noreturn kunit_abort(struct kunit *test) +{ + kunit_try_catch_throw(&test->try_catch); /* Does not return. */ + + /* + * 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"); +} + void kunit_do_assertion(struct kunit *test, struct kunit_assert *assert, bool pass, @@ -180,6 +195,9 @@ void kunit_do_assertion(struct kunit *test, kunit_fail(test, assert); va_end(args); + + if (assert->type == KUNIT_ASSERTION) + kunit_abort(test); } void kunit_init_test(struct kunit *test, const char *name) @@ -191,33 +209,122 @@ void kunit_init_test(struct kunit *test, const char *name) } /* - * Performs all logic to run a test case. + * Initializes and runs test case. Does not clean up or do post validations. */ -static void kunit_run_case(struct kunit_suite *suite, - struct kunit_case *test_case) +static void kunit_run_case_internal(struct kunit *test, + struct kunit_suite *suite, + struct kunit_case *test_case) { - struct kunit test; - - kunit_init_test(&test, test_case->name); - if (suite->init) { int ret; - ret = suite->init(&test); + ret = suite->init(test); if (ret) { - kunit_err(&test, "failed to initialize: %d\n", ret); - kunit_set_failure(&test); - test_case->success = test.success; + kunit_err(test, "failed to initialize: %d\n", ret); + kunit_set_failure(test); return; } } - test_case->run_case(&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! + */ +static void kunit_run_case_cleanup(struct kunit *test, + struct kunit_suite *suite) +{ if (suite->exit) - suite->exit(&test); + suite->exit(test); + + kunit_case_internal_cleanup(test); +} + +struct kunit_try_catch_context { + struct kunit *test; + struct kunit_suite *suite; + 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_suite *suite = ctx->suite; + 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, suite, test_case); + /* This line may never be reached. */ + kunit_run_case_cleanup(test, suite); +} + +static void kunit_catch_run_case(void *data) +{ + struct kunit_try_catch_context *ctx = data; + struct kunit *test = ctx->test; + struct kunit_suite *suite = ctx->suite; + int try_exit_code = kunit_try_catch_get_result(&test->try_catch); + + if (try_exit_code) { + kunit_set_failure(test); + /* + * 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; + } + + /* + * Test case was run, but aborted. It is the test case's business as to + * whether it failed or not, we just need to clean up. + */ + kunit_run_case_cleanup(test, suite); +} + +/* + * Performs all logic to run a test case. It also catches most errors that + * occur in a test case and reports them as failures. + */ +static void kunit_run_case_catch_errors(struct kunit_suite *suite, + struct kunit_case *test_case) +{ + struct kunit_try_catch_context context; + struct kunit_try_catch *try_catch; + struct kunit test; + + kunit_init_test(&test, test_case->name); + try_catch = &test.try_catch; - kunit_cleanup(&test); + kunit_try_catch_init(try_catch, + &test, + kunit_try_run_case, + kunit_catch_run_case); + context.test = &test; + context.suite = suite; + context.test_case = test_case; + kunit_try_catch_run(try_catch, &context); test_case->success = test.success; } @@ -230,7 +337,7 @@ int kunit_run_tests(struct kunit_suite *suite) kunit_print_subtest_start(suite); for (test_case = suite->test_cases; test_case->run_case; test_case++) { - kunit_run_case(suite, test_case); + kunit_run_case_catch_errors(suite, test_case); kunit_print_test_case_ok_not_ok(test_case, test_case_count++); } diff --git a/lib/kunit/try-catch.c b/lib/kunit/try-catch.c new file mode 100644 index 000000000000..55686839eb61 --- /dev/null +++ b/lib/kunit/try-catch.c @@ -0,0 +1,118 @@ +// 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 +#include +#include + +void __noreturn kunit_try_catch_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 unsigned long kunit_test_timeout(void) +{ + unsigned long timeout_msecs; + + /* + * 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 + */ + if (sysctl_hung_task_timeout_secs) { + /* + * If sysctl_hung_task is active, just set the timeout to some + * value less than that. + * + * In regards to the above TODO, if we decide on variable + * timeouts, this logic will likely need to change. + */ + timeout_msecs = (sysctl_hung_task_timeout_secs - 1) * + MSEC_PER_SEC; + } else { + timeout_msecs = 300 * MSEC_PER_SEC; /* 5 min */ + } + + return timeout_msecs; +} + +void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context) +{ + DECLARE_COMPLETION_ONSTACK(try_completion); + struct kunit *test = try_catch->test; + struct task_struct *task_struct; + int exit_code, time_remaining; + + try_catch->context = context; + 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; + } + + time_remaining = wait_for_completion_timeout(&try_completion, + kunit_test_timeout()); + if (time_remaining == 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_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; + try_catch->try = try; + try_catch->catch = catch; +} -- 2.23.0.351.gc4317032e6-goog From mboxrd@z Thu Jan 1 00:00:00 1970 From: Brendan Higgins Subject: [PATCH v16 09/19] kunit: test: add support for test abort Date: Fri, 20 Sep 2019 16:19:13 -0700 Message-ID: <20190920231923.141900-10-brendanhiggins@google.com> References: <20190920231923.141900-1-brendanhiggins@google.com> Mime-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Return-path: In-Reply-To: <20190920231923.141900-1-brendanhiggins@google.com> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" To: frowand.list@gmail.com, gregkh@linuxfoundation.org, jpoimboe@redhat.com, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, peterz@infradead.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org, tytso@mit.edu, yamada.masahiro@socionext.com 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, torvalds@linux-foundation.org, rdunlap@infradead.org, linux-kernel@vger.kernel.org, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org, logang@deltatee.com List-Id: devicetree@vger.kernel.org QWRkIHN1cHBvcnQgZm9yIGFib3J0aW5nL2JhaWxpbmcgb3V0IG9mIHRlc3QgY2FzZXMsIHdoaWNo IGlzIG5lZWRlZCBmb3IKaW1wbGVtZW50aW5nIGFzc2VydGlvbnMuCgpBbiBhc3NlcnRpb24gaXMg bGlrZSBhbiBleHBlY3RhdGlvbiwgYnV0IGJhaWxzIG91dCBvZiB0aGUgdGVzdCBjYXNlCmVhcmx5 IGlmIHRoZSBhc3NlcnRpb24gaXMgbm90IG1ldC4gVGhlIGlkZWEgd2l0aCBhc3NlcnRpb25zIGlz IHRoYXQgeW91CnVzZSB0aGVtIHRvIHN0YXRlIGFsbCB0aGUgcHJlY29uZGl0aW9ucyBmb3IgeW91 ciB0ZXN0LiBMb2dpY2FsbHkKc3BlYWtpbmcsIHRoZXNlIGFyZSB0aGUgcHJlbWlzZXMgb2YgdGhl IHRlc3QgY2FzZSwgc28gaWYgYSBwcmVtaXNlIGlzbid0CnRydWUsIHRoZXJlIGlzIG5vIHBvaW50 IGluIGNvbnRpbnVpbmcgdGhlIHRlc3QgY2FzZSBiZWNhdXNlIHRoZXJlIGFyZSBubwpjb25jbHVz aW9ucyB0aGF0IGNhbiBiZSBkcmF3biB3aXRob3V0IHRoZSBwcmVtaXNlcy4gV2hlcmVhcywgdGhl CmV4cGVjdGF0aW9uIGlzIHRoZSB0aGluZyB5b3UgYXJlIHRyeWluZyB0byBwcm92ZS4KClNpZ25l ZC1vZmYtYnk6IEJyZW5kYW4gSGlnZ2lucyA8YnJlbmRhbmhpZ2dpbnNAZ29vZ2xlLmNvbT4KUmV2 aWV3ZWQtYnk6IEdyZWcgS3JvYWgtSGFydG1hbiA8Z3JlZ2toQGxpbnV4Zm91bmRhdGlvbi5vcmc+ ClJldmlld2VkLWJ5OiBMb2dhbiBHdW50aG9ycGUgPGxvZ2FuZ0BkZWx0YXRlZS5jb20+ClJldmll d2VkLWJ5OiBTdGVwaGVuIEJveWQgPHNib3lkQGtlcm5lbC5vcmc+Ci0tLQogaW5jbHVkZS9rdW5p dC90ZXN0LmggICAgICB8ICAgMiArCiBpbmNsdWRlL2t1bml0L3RyeS1jYXRjaC5oIHwgIDc1ICsr KysrKysrKysrKysrKysrKysrKwogbGliL2t1bml0L01ha2VmaWxlICAgICAgICB8ICAgMyArLQog bGliL2t1bml0L3Rlc3QuYyAgICAgICAgICB8IDEzNyArKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKystLS0tLQogbGliL2t1bml0L3RyeS1jYXRjaC5jICAgICB8IDExOCArKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKwogNSBmaWxlcyBjaGFuZ2VkLCAzMTkgaW5zZXJ0aW9ucygr KSwgMTYgZGVsZXRpb25zKC0pCiBjcmVhdGUgbW9kZSAxMDA2NDQgaW5jbHVkZS9rdW5pdC90cnkt Y2F0Y2guaAogY3JlYXRlIG1vZGUgMTAwNjQ0IGxpYi9rdW5pdC90cnktY2F0Y2guYwoKZGlmZiAt LWdpdCBhL2luY2x1ZGUva3VuaXQvdGVzdC5oIGIvaW5jbHVkZS9rdW5pdC90ZXN0LmgKaW5kZXgg MzBhNjJkZTE2YmM5Li4zZDU1NGQ3YzFjNzkgMTAwNjQ0Ci0tLSBhL2luY2x1ZGUva3VuaXQvdGVz dC5oCisrKyBiL2luY2x1ZGUva3VuaXQvdGVzdC5oCkBAIC0xMCw2ICsxMCw3IEBACiAjZGVmaW5l IF9LVU5JVF9URVNUX0gKIAogI2luY2x1ZGUgPGt1bml0L2Fzc2VydC5oPgorI2luY2x1ZGUgPGt1 bml0L3RyeS1jYXRjaC5oPgogI2luY2x1ZGUgPGxpbnV4L2tlcm5lbC5oPgogI2luY2x1ZGUgPGxp bnV4L3NsYWIuaD4KICNpbmNsdWRlIDxsaW51eC90eXBlcy5oPgpAQCAtMTcyLDYgKzE3Myw3IEBA IHN0cnVjdCBrdW5pdCB7CiAKIAkvKiBwcml2YXRlOiBpbnRlcm5hbCB1c2Ugb25seS4gKi8KIAlj b25zdCBjaGFyICpuYW1lOyAvKiBSZWFkIG9ubHkgYWZ0ZXIgaW5pdGlhbGl6YXRpb24hICovCisJ c3RydWN0IGt1bml0X3RyeV9jYXRjaCB0cnlfY2F0Y2g7CiAJLyoKIAkgKiBzdWNjZXNzIHN0YXJ0 cyBhcyB0cnVlLCBhbmQgbWF5IG9ubHkgYmUgc2V0IHRvIGZhbHNlIGR1cmluZyBhCiAJICogdGVz dCBjYXNlOyB0aHVzLCBpdCBpcyBzYWZlIHRvIHVwZGF0ZSB0aGlzIGFjcm9zcyBtdWx0aXBsZQpk aWZmIC0tZ2l0IGEvaW5jbHVkZS9rdW5pdC90cnktY2F0Y2guaCBiL2luY2x1ZGUva3VuaXQvdHJ5 LWNhdGNoLmgKbmV3IGZpbGUgbW9kZSAxMDA2NDQKaW5kZXggMDAwMDAwMDAwMDAwLi40MDRmMzM2 Y2JkYzgKLS0tIC9kZXYvbnVsbAorKysgYi9pbmNsdWRlL2t1bml0L3RyeS1jYXRjaC5oCkBAIC0w LDAgKzEsNzUgQEAKKy8qIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBHUEwtMi4wICovCisvKgor ICogQW4gQVBJIHRvIGFsbG93IGEgZnVuY3Rpb24sIHRoYXQgbWF5IGZhaWwsIHRvIGJlIGV4ZWN1 dGVkLCBhbmQgcmVjb3ZlciBpbiBhCisgKiBjb250cm9sbGVkIG1hbm5lci4KKyAqCisgKiBDb3B5 cmlnaHQgKEMpIDIwMTksIEdvb2dsZSBMTEMuCisgKiBBdXRob3I6IEJyZW5kYW4gSGlnZ2lucyA8 YnJlbmRhbmhpZ2dpbnNAZ29vZ2xlLmNvbT4KKyAqLworCisjaWZuZGVmIF9LVU5JVF9UUllfQ0FU Q0hfSAorI2RlZmluZSBfS1VOSVRfVFJZX0NBVENIX0gKKworI2luY2x1ZGUgPGxpbnV4L3R5cGVz Lmg+CisKK3R5cGVkZWYgdm9pZCAoKmt1bml0X3RyeV9jYXRjaF9mdW5jX3QpKHZvaWQgKik7CisK K3N0cnVjdCBjb21wbGV0aW9uOworc3RydWN0IGt1bml0OworCisvKioKKyAqIHN0cnVjdCBrdW5p dF90cnlfY2F0Y2ggLSBwcm92aWRlcyBhIGdlbmVyaWMgd2F5IHRvIHJ1biBjb2RlIHdoaWNoIG1p Z2h0IGZhaWwuCisgKiBAdGVzdDogVGhlIHRlc3QgY2FzZSB0aGF0IGlzIGN1cnJlbnRseSBiZWlu ZyBleGVjdXRlZC4KKyAqIEB0cnlfY29tcGxldGlvbjogQ29tcGxldGlvbiB0aGF0IHRoZSBjb250 cm9sIHRocmVhZCB3YWl0cyBvbiB3aGlsZSB0ZXN0IHJ1bnMuCisgKiBAdHJ5X3Jlc3VsdDogQ29u dGFpbnMgYW55IGVycm5vIG9idGFpbmVkIHdoaWxlIHJ1bm5pbmcgdGVzdCBjYXNlLgorICogQHRy eTogVGhlIGZ1bmN0aW9uLCB0aGUgdGVzdCBjYXNlLCB0byBhdHRlbXB0IHRvIHJ1bi4KKyAqIEBj YXRjaDogVGhlIGZ1bmN0aW9uIGNhbGxlZCBpZiBAdHJ5IGJhaWxzIG91dC4KKyAqIEBjb250ZXh0 OiB1c2VkIHRvIHBhc3MgdXNlciBkYXRhIHRvIHRoZSB0cnkgYW5kIGNhdGNoIGZ1bmN0aW9ucy4K KyAqCisgKiBrdW5pdF90cnlfY2F0Y2ggcHJvdmlkZXMgYSBnZW5lcmljLCBhcmNoaXRlY3R1cmUg aW5kZXBlbmRlbnQgd2F5IHRvIGV4ZWN1dGUKKyAqIGFuIGFyYml0cmFyeSBmdW5jdGlvbiBvZiB0 eXBlIGt1bml0X3RyeV9jYXRjaF9mdW5jX3Qgd2hpY2ggbWF5IGJhaWwgb3V0IGJ5CisgKiBjYWxs aW5nIGt1bml0X3RyeV9jYXRjaF90aHJvdygpLiBJZiBrdW5pdF90cnlfY2F0Y2hfdGhyb3coKSBp cyBjYWxsZWQsIEB0cnkKKyAqIGlzIHN0b3BwZWQgYXQgdGhlIHNpdGUgb2YgaW52b2NhdGlvbiBh bmQgQGNhdGNoIGlzIGNhbGxlZC4KKyAqCisgKiBzdHJ1Y3Qga3VuaXRfdHJ5X2NhdGNoIHByb3Zp ZGVzIGEgZ2VuZXJpYyBpbnRlcmZhY2UgZm9yIHRoZSBmdW5jdGlvbmFsaXR5CisgKiBuZWVkZWQg dG8gaW1wbGVtZW50IGt1bml0LT5hYm9ydCgpIHdoaWNoIGluIHR1cm4gaXMgbmVlZGVkIGZvciBp bXBsZW1lbnRpbmcKKyAqIGFzc2VydGlvbnMuIEFzc2VydGlvbnMgYWxsb3cgc3RhdGluZyBhIHBy ZWNvbmRpdGlvbiBmb3IgYSB0ZXN0IHNpbXBsaWZ5aW5nCisgKiBob3cgdGVzdCBjYXNlcyBhcmUg d3JpdHRlbiBhbmQgcHJlc2VudGVkLgorICoKKyAqIEFzc2VydGlvbnMgYXJlIGxpa2UgZXhwZWN0 YXRpb25zLCBleGNlcHQgdGhleSBhYm9ydCAoY2FsbAorICoga3VuaXRfdHJ5X2NhdGNoX3Rocm93 KCkpIHdoZW4gdGhlIHNwZWNpZmllZCBjb25kaXRpb24gaXMgbm90IG1ldC4gVGhpcyBpcworICog dXNlZnVsIHdoZW4geW91IGxvb2sgYXQgYSB0ZXN0IGNhc2UgYXMgYSBsb2dpY2FsIHN0YXRlbWVu dCBhYm91dCBzb21lIHBpZWNlCisgKiBvZiBjb2RlLCB3aGVyZSBhc3NlcnRpb25zIGFyZSB0aGUg cHJlbWlzZXMgZm9yIHRoZSB0ZXN0IGNhc2UsIGFuZCB0aGUKKyAqIGNvbmNsdXNpb24gaXMgYSBz ZXQgb2YgcHJlZGljYXRlcywgcmF0aGVyIGV4cGVjdGF0aW9ucywgdGhhdCBtdXN0IGFsbCBiZQor ICogdHJ1ZS4gSWYgeW91ciBwcmVtaXNlcyBhcmUgdmlvbGF0ZWQsIGl0IGRvZXMgbm90IG1ha2Vz IHNlbnNlIHRvIGNvbnRpbnVlLgorICovCitzdHJ1Y3Qga3VuaXRfdHJ5X2NhdGNoIHsKKwkvKiBw cml2YXRlOiBpbnRlcm5hbCB1c2Ugb25seS4gKi8KKwlzdHJ1Y3Qga3VuaXQgKnRlc3Q7CisJc3Ry dWN0IGNvbXBsZXRpb24gKnRyeV9jb21wbGV0aW9uOworCWludCB0cnlfcmVzdWx0OworCWt1bml0 X3RyeV9jYXRjaF9mdW5jX3QgdHJ5OworCWt1bml0X3RyeV9jYXRjaF9mdW5jX3QgY2F0Y2g7CisJ dm9pZCAqY29udGV4dDsKK307CisKK3ZvaWQga3VuaXRfdHJ5X2NhdGNoX2luaXQoc3RydWN0IGt1 bml0X3RyeV9jYXRjaCAqdHJ5X2NhdGNoLAorCQkJICBzdHJ1Y3Qga3VuaXQgKnRlc3QsCisJCQkg IGt1bml0X3RyeV9jYXRjaF9mdW5jX3QgdHJ5LAorCQkJICBrdW5pdF90cnlfY2F0Y2hfZnVuY190 IGNhdGNoKTsKKwordm9pZCBrdW5pdF90cnlfY2F0Y2hfcnVuKHN0cnVjdCBrdW5pdF90cnlfY2F0 Y2ggKnRyeV9jYXRjaCwgdm9pZCAqY29udGV4dCk7CisKK3ZvaWQgX19ub3JldHVybiBrdW5pdF90 cnlfY2F0Y2hfdGhyb3coc3RydWN0IGt1bml0X3RyeV9jYXRjaCAqdHJ5X2NhdGNoKTsKKworc3Rh dGljIGlubGluZSBpbnQga3VuaXRfdHJ5X2NhdGNoX2dldF9yZXN1bHQoc3RydWN0IGt1bml0X3Ry eV9jYXRjaCAqdHJ5X2NhdGNoKQoreworCXJldHVybiB0cnlfY2F0Y2gtPnRyeV9yZXN1bHQ7Cit9 CisKKy8qCisgKiBFeHBvc2VkIGZvciB0ZXN0aW5nIG9ubHkuCisgKi8KK3ZvaWQga3VuaXRfZ2Vu ZXJpY190cnlfY2F0Y2hfaW5pdChzdHJ1Y3Qga3VuaXRfdHJ5X2NhdGNoICp0cnlfY2F0Y2gpOwor CisjZW5kaWYgLyogX0tVTklUX1RSWV9DQVRDSF9IICovCmRpZmYgLS1naXQgYS9saWIva3VuaXQv TWFrZWZpbGUgYi9saWIva3VuaXQvTWFrZWZpbGUKaW5kZXggNGU0NjQ1MGJjYjNhLi5jOTE3NmM5 YzU3OGMgMTAwNjQ0Ci0tLSBhL2xpYi9rdW5pdC9NYWtlZmlsZQorKysgYi9saWIva3VuaXQvTWFr ZWZpbGUKQEAgLTEsNiArMSw3IEBACiBvYmotJChDT05GSUdfS1VOSVQpICs9CQkJdGVzdC5vIFwK IAkJCQkJc3RyaW5nLXN0cmVhbS5vIFwKLQkJCQkJYXNzZXJ0Lm8KKwkJCQkJYXNzZXJ0Lm8gXAor CQkJCQl0cnktY2F0Y2gubwogCiBvYmotJChDT05GSUdfS1VOSVRfVEVTVCkgKz0JCXN0cmluZy1z dHJlYW0tdGVzdC5vCiAKZGlmZiAtLWdpdCBhL2xpYi9rdW5pdC90ZXN0LmMgYi9saWIva3VuaXQv dGVzdC5jCmluZGV4IDNjYmNlYjM0YjNiMy4uYjJjYTliOTRjMzUzIDEwMDY0NAotLS0gYS9saWIv a3VuaXQvdGVzdC5jCisrKyBiL2xpYi9rdW5pdC90ZXN0LmMKQEAgLTcsNyArNyw5IEBACiAgKi8K IAogI2luY2x1ZGUgPGt1bml0L3Rlc3QuaD4KKyNpbmNsdWRlIDxrdW5pdC90cnktY2F0Y2guaD4K ICNpbmNsdWRlIDxsaW51eC9rZXJuZWwuaD4KKyNpbmNsdWRlIDxsaW51eC9zY2hlZC9kZWJ1Zy5o PgogCiBzdGF0aWMgdm9pZCBrdW5pdF9zZXRfZmFpbHVyZShzdHJ1Y3Qga3VuaXQgKnRlc3QpCiB7 CkBAIC0xNjIsNiArMTY0LDE5IEBAIHN0YXRpYyB2b2lkIGt1bml0X2ZhaWwoc3RydWN0IGt1bml0 ICp0ZXN0LCBzdHJ1Y3Qga3VuaXRfYXNzZXJ0ICphc3NlcnQpCiAJV0FSTl9PTihzdHJpbmdfc3Ry ZWFtX2Rlc3Ryb3koc3RyZWFtKSk7CiB9CiAKK3N0YXRpYyB2b2lkIF9fbm9yZXR1cm4ga3VuaXRf YWJvcnQoc3RydWN0IGt1bml0ICp0ZXN0KQoreworCWt1bml0X3RyeV9jYXRjaF90aHJvdygmdGVz dC0+dHJ5X2NhdGNoKTsgLyogRG9lcyBub3QgcmV0dXJuLiAqLworCisJLyoKKwkgKiBUaHJvdyBj b3VsZCBub3QgYWJvcnQgZnJvbSB0ZXN0LgorCSAqCisJICogWFhYOiB3ZSBzaG91bGQgbmV2ZXIg cmVhY2ggdGhpcyBsaW5lISBBcyBrdW5pdF90cnlfY2F0Y2hfdGhyb3cgaXMKKwkgKiBtYXJrZWQg X19ub3JldHVybi4KKwkgKi8KKwlXQVJOX09OQ0UodHJ1ZSwgIlRocm93IGNvdWxkIG5vdCBhYm9y dCBmcm9tIHRlc3QhXG4iKTsKK30KKwogdm9pZCBrdW5pdF9kb19hc3NlcnRpb24oc3RydWN0IGt1 bml0ICp0ZXN0LAogCQkJc3RydWN0IGt1bml0X2Fzc2VydCAqYXNzZXJ0LAogCQkJYm9vbCBwYXNz LApAQCAtMTgwLDYgKzE5NSw5IEBAIHZvaWQga3VuaXRfZG9fYXNzZXJ0aW9uKHN0cnVjdCBrdW5p dCAqdGVzdCwKIAlrdW5pdF9mYWlsKHRlc3QsIGFzc2VydCk7CiAKIAl2YV9lbmQoYXJncyk7CisK KwlpZiAoYXNzZXJ0LT50eXBlID09IEtVTklUX0FTU0VSVElPTikKKwkJa3VuaXRfYWJvcnQodGVz dCk7CiB9CiAKIHZvaWQga3VuaXRfaW5pdF90ZXN0KHN0cnVjdCBrdW5pdCAqdGVzdCwgY29uc3Qg Y2hhciAqbmFtZSkKQEAgLTE5MSwzMyArMjA5LDEyMiBAQCB2b2lkIGt1bml0X2luaXRfdGVzdChz dHJ1Y3Qga3VuaXQgKnRlc3QsIGNvbnN0IGNoYXIgKm5hbWUpCiB9CiAKIC8qCi0gKiBQZXJmb3Jt cyBhbGwgbG9naWMgdG8gcnVuIGEgdGVzdCBjYXNlLgorICogSW5pdGlhbGl6ZXMgYW5kIHJ1bnMg dGVzdCBjYXNlLiBEb2VzIG5vdCBjbGVhbiB1cCBvciBkbyBwb3N0IHZhbGlkYXRpb25zLgogICov Ci1zdGF0aWMgdm9pZCBrdW5pdF9ydW5fY2FzZShzdHJ1Y3Qga3VuaXRfc3VpdGUgKnN1aXRlLAot CQkJICAgc3RydWN0IGt1bml0X2Nhc2UgKnRlc3RfY2FzZSkKK3N0YXRpYyB2b2lkIGt1bml0X3J1 bl9jYXNlX2ludGVybmFsKHN0cnVjdCBrdW5pdCAqdGVzdCwKKwkJCQkgICAgc3RydWN0IGt1bml0 X3N1aXRlICpzdWl0ZSwKKwkJCQkgICAgc3RydWN0IGt1bml0X2Nhc2UgKnRlc3RfY2FzZSkKIHsK LQlzdHJ1Y3Qga3VuaXQgdGVzdDsKLQotCWt1bml0X2luaXRfdGVzdCgmdGVzdCwgdGVzdF9jYXNl LT5uYW1lKTsKLQogCWlmIChzdWl0ZS0+aW5pdCkgewogCQlpbnQgcmV0OwogCi0JCXJldCA9IHN1 aXRlLT5pbml0KCZ0ZXN0KTsKKwkJcmV0ID0gc3VpdGUtPmluaXQodGVzdCk7CiAJCWlmIChyZXQp IHsKLQkJCWt1bml0X2VycigmdGVzdCwgImZhaWxlZCB0byBpbml0aWFsaXplOiAlZFxuIiwgcmV0 KTsKLQkJCWt1bml0X3NldF9mYWlsdXJlKCZ0ZXN0KTsKLQkJCXRlc3RfY2FzZS0+c3VjY2VzcyA9 IHRlc3Quc3VjY2VzczsKKwkJCWt1bml0X2Vycih0ZXN0LCAiZmFpbGVkIHRvIGluaXRpYWxpemU6 ICVkXG4iLCByZXQpOworCQkJa3VuaXRfc2V0X2ZhaWx1cmUodGVzdCk7CiAJCQlyZXR1cm47CiAJ CX0KIAl9CiAKLQl0ZXN0X2Nhc2UtPnJ1bl9jYXNlKCZ0ZXN0KTsKKwl0ZXN0X2Nhc2UtPnJ1bl9j YXNlKHRlc3QpOworfQogCitzdGF0aWMgdm9pZCBrdW5pdF9jYXNlX2ludGVybmFsX2NsZWFudXAo c3RydWN0IGt1bml0ICp0ZXN0KQoreworCWt1bml0X2NsZWFudXAodGVzdCk7Cit9CisKKy8qCisg KiBQZXJmb3JtcyBwb3N0IHZhbGlkYXRpb25zIGFuZCBjbGVhbnVwIGFmdGVyIGEgdGVzdCBjYXNl IHdhcyBydW4uCisgKiBYWFg6IFNob3VsZCBPTkxZIEJFIENBTExFRCBBRlRFUiBrdW5pdF9ydW5f Y2FzZV9pbnRlcm5hbCEKKyAqLworc3RhdGljIHZvaWQga3VuaXRfcnVuX2Nhc2VfY2xlYW51cChz dHJ1Y3Qga3VuaXQgKnRlc3QsCisJCQkJICAgc3RydWN0IGt1bml0X3N1aXRlICpzdWl0ZSkKK3sK IAlpZiAoc3VpdGUtPmV4aXQpCi0JCXN1aXRlLT5leGl0KCZ0ZXN0KTsKKwkJc3VpdGUtPmV4aXQo dGVzdCk7CisKKwlrdW5pdF9jYXNlX2ludGVybmFsX2NsZWFudXAodGVzdCk7Cit9CisKK3N0cnVj dCBrdW5pdF90cnlfY2F0Y2hfY29udGV4dCB7CisJc3RydWN0IGt1bml0ICp0ZXN0OworCXN0cnVj dCBrdW5pdF9zdWl0ZSAqc3VpdGU7CisJc3RydWN0IGt1bml0X2Nhc2UgKnRlc3RfY2FzZTsKK307 CisKK3N0YXRpYyB2b2lkIGt1bml0X3RyeV9ydW5fY2FzZSh2b2lkICpkYXRhKQoreworCXN0cnVj dCBrdW5pdF90cnlfY2F0Y2hfY29udGV4dCAqY3R4ID0gZGF0YTsKKwlzdHJ1Y3Qga3VuaXQgKnRl c3QgPSBjdHgtPnRlc3Q7CisJc3RydWN0IGt1bml0X3N1aXRlICpzdWl0ZSA9IGN0eC0+c3VpdGU7 CisJc3RydWN0IGt1bml0X2Nhc2UgKnRlc3RfY2FzZSA9IGN0eC0+dGVzdF9jYXNlOworCisJLyoK KwkgKiBrdW5pdF9ydW5fY2FzZV9pbnRlcm5hbCBtYXkgZW5jb3VudGVyIGEgZmF0YWwgZXJyb3I7 IGlmIGl0IGRvZXMsCisJICogYWJvcnQgd2lsbCBiZSBjYWxsZWQsIHRoaXMgdGhyZWFkIHdpbGwg ZXhpdCwgYW5kIGZpbmFsbHkgdGhlIHBhcmVudAorCSAqIHRocmVhZCB3aWxsIHJlc3VtZSBjb250 cm9sIGFuZCBoYW5kbGUgYW55IG5lY2Vzc2FyeSBjbGVhbiB1cC4KKwkgKi8KKwlrdW5pdF9ydW5f Y2FzZV9pbnRlcm5hbCh0ZXN0LCBzdWl0ZSwgdGVzdF9jYXNlKTsKKwkvKiBUaGlzIGxpbmUgbWF5 IG5ldmVyIGJlIHJlYWNoZWQuICovCisJa3VuaXRfcnVuX2Nhc2VfY2xlYW51cCh0ZXN0LCBzdWl0 ZSk7Cit9CisKK3N0YXRpYyB2b2lkIGt1bml0X2NhdGNoX3J1bl9jYXNlKHZvaWQgKmRhdGEpCit7 CisJc3RydWN0IGt1bml0X3RyeV9jYXRjaF9jb250ZXh0ICpjdHggPSBkYXRhOworCXN0cnVjdCBr dW5pdCAqdGVzdCA9IGN0eC0+dGVzdDsKKwlzdHJ1Y3Qga3VuaXRfc3VpdGUgKnN1aXRlID0gY3R4 LT5zdWl0ZTsKKwlpbnQgdHJ5X2V4aXRfY29kZSA9IGt1bml0X3RyeV9jYXRjaF9nZXRfcmVzdWx0 KCZ0ZXN0LT50cnlfY2F0Y2gpOworCisJaWYgKHRyeV9leGl0X2NvZGUpIHsKKwkJa3VuaXRfc2V0 X2ZhaWx1cmUodGVzdCk7CisJCS8qCisJCSAqIFRlc3QgY2FzZSBjb3VsZCBub3QgZmluaXNoLCB3 ZSBoYXZlIG5vIGlkZWEgd2hhdCBzdGF0ZSBpdCBpcworCQkgKiBpbiwgc28gZG9uJ3QgZG8gY2xl YW4gdXAuCisJCSAqLworCQlpZiAodHJ5X2V4aXRfY29kZSA9PSAtRVRJTUVET1VUKSB7CisJCQlr dW5pdF9lcnIodGVzdCwgInRlc3QgY2FzZSB0aW1lZCBvdXRcbiIpOworCQkvKgorCQkgKiBVbmtu b3duIGludGVybmFsIGVycm9yIG9jY3VycmVkIHByZXZlbnRpbmcgdGVzdCBjYXNlIGZyb20KKwkJ ICogcnVubmluZywgc28gdGhlcmUgaXMgbm90aGluZyB0byBjbGVhbiB1cC4KKwkJICovCisJCX0g ZWxzZSB7CisJCQlrdW5pdF9lcnIodGVzdCwgImludGVybmFsIGVycm9yIG9jY3VycmVkIHByZXZl bnRpbmcgdGVzdCBjYXNlIGZyb20gcnVubmluZzogJWRcbiIsCisJCQkJICB0cnlfZXhpdF9jb2Rl KTsKKwkJfQorCQlyZXR1cm47CisJfQorCisJLyoKKwkgKiBUZXN0IGNhc2Ugd2FzIHJ1biwgYnV0 IGFib3J0ZWQuIEl0IGlzIHRoZSB0ZXN0IGNhc2UncyBidXNpbmVzcyBhcyB0bworCSAqIHdoZXRo ZXIgaXQgZmFpbGVkIG9yIG5vdCwgd2UganVzdCBuZWVkIHRvIGNsZWFuIHVwLgorCSAqLworCWt1 bml0X3J1bl9jYXNlX2NsZWFudXAodGVzdCwgc3VpdGUpOworfQorCisvKgorICogUGVyZm9ybXMg YWxsIGxvZ2ljIHRvIHJ1biBhIHRlc3QgY2FzZS4gSXQgYWxzbyBjYXRjaGVzIG1vc3QgZXJyb3Jz IHRoYXQKKyAqIG9jY3VyIGluIGEgdGVzdCBjYXNlIGFuZCByZXBvcnRzIHRoZW0gYXMgZmFpbHVy ZXMuCisgKi8KK3N0YXRpYyB2b2lkIGt1bml0X3J1bl9jYXNlX2NhdGNoX2Vycm9ycyhzdHJ1Y3Qg a3VuaXRfc3VpdGUgKnN1aXRlLAorCQkJCQlzdHJ1Y3Qga3VuaXRfY2FzZSAqdGVzdF9jYXNlKQor eworCXN0cnVjdCBrdW5pdF90cnlfY2F0Y2hfY29udGV4dCBjb250ZXh0OworCXN0cnVjdCBrdW5p dF90cnlfY2F0Y2ggKnRyeV9jYXRjaDsKKwlzdHJ1Y3Qga3VuaXQgdGVzdDsKKworCWt1bml0X2lu aXRfdGVzdCgmdGVzdCwgdGVzdF9jYXNlLT5uYW1lKTsKKwl0cnlfY2F0Y2ggPSAmdGVzdC50cnlf Y2F0Y2g7CiAKLQlrdW5pdF9jbGVhbnVwKCZ0ZXN0KTsKKwlrdW5pdF90cnlfY2F0Y2hfaW5pdCh0 cnlfY2F0Y2gsCisJCQkgICAgICZ0ZXN0LAorCQkJICAgICBrdW5pdF90cnlfcnVuX2Nhc2UsCisJ CQkgICAgIGt1bml0X2NhdGNoX3J1bl9jYXNlKTsKKwljb250ZXh0LnRlc3QgPSAmdGVzdDsKKwlj b250ZXh0LnN1aXRlID0gc3VpdGU7CisJY29udGV4dC50ZXN0X2Nhc2UgPSB0ZXN0X2Nhc2U7CisJ a3VuaXRfdHJ5X2NhdGNoX3J1bih0cnlfY2F0Y2gsICZjb250ZXh0KTsKIAogCXRlc3RfY2FzZS0+ c3VjY2VzcyA9IHRlc3Quc3VjY2VzczsKIH0KQEAgLTIzMCw3ICszMzcsNyBAQCBpbnQga3VuaXRf cnVuX3Rlc3RzKHN0cnVjdCBrdW5pdF9zdWl0ZSAqc3VpdGUpCiAJa3VuaXRfcHJpbnRfc3VidGVz dF9zdGFydChzdWl0ZSk7CiAKIAlmb3IgKHRlc3RfY2FzZSA9IHN1aXRlLT50ZXN0X2Nhc2VzOyB0 ZXN0X2Nhc2UtPnJ1bl9jYXNlOyB0ZXN0X2Nhc2UrKykgewotCQlrdW5pdF9ydW5fY2FzZShzdWl0 ZSwgdGVzdF9jYXNlKTsKKwkJa3VuaXRfcnVuX2Nhc2VfY2F0Y2hfZXJyb3JzKHN1aXRlLCB0ZXN0 X2Nhc2UpOwogCQlrdW5pdF9wcmludF90ZXN0X2Nhc2Vfb2tfbm90X29rKHRlc3RfY2FzZSwgdGVz dF9jYXNlX2NvdW50KyspOwogCX0KIApkaWZmIC0tZ2l0IGEvbGliL2t1bml0L3RyeS1jYXRjaC5j IGIvbGliL2t1bml0L3RyeS1jYXRjaC5jCm5ldyBmaWxlIG1vZGUgMTAwNjQ0CmluZGV4IDAwMDAw MDAwMDAwMC4uNTU2ODY4MzllYjYxCi0tLSAvZGV2L251bGwKKysrIGIvbGliL2t1bml0L3RyeS1j YXRjaC5jCkBAIC0wLDAgKzEsMTE4IEBACisvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogR1BM LTIuMAorLyoKKyAqIEFuIEFQSSB0byBhbGxvdyBhIGZ1bmN0aW9uLCB0aGF0IG1heSBmYWlsLCB0 byBiZSBleGVjdXRlZCwgYW5kIHJlY292ZXIgaW4gYQorICogY29udHJvbGxlZCBtYW5uZXIuCisg KgorICogQ29weXJpZ2h0IChDKSAyMDE5LCBHb29nbGUgTExDLgorICogQXV0aG9yOiBCcmVuZGFu IEhpZ2dpbnMgPGJyZW5kYW5oaWdnaW5zQGdvb2dsZS5jb20+CisgKi8KKworI2luY2x1ZGUgPGt1 bml0L3Rlc3QuaD4KKyNpbmNsdWRlIDxrdW5pdC90cnktY2F0Y2guaD4KKyNpbmNsdWRlIDxsaW51 eC9jb21wbGV0aW9uLmg+CisjaW5jbHVkZSA8bGludXgva2VybmVsLmg+CisjaW5jbHVkZSA8bGlu dXgva3RocmVhZC5oPgorI2luY2x1ZGUgPGxpbnV4L3NjaGVkL3N5c2N0bC5oPgorCit2b2lkIF9f bm9yZXR1cm4ga3VuaXRfdHJ5X2NhdGNoX3Rocm93KHN0cnVjdCBrdW5pdF90cnlfY2F0Y2ggKnRy eV9jYXRjaCkKK3sKKwl0cnlfY2F0Y2gtPnRyeV9yZXN1bHQgPSAtRUZBVUxUOworCWNvbXBsZXRl X2FuZF9leGl0KHRyeV9jYXRjaC0+dHJ5X2NvbXBsZXRpb24sIC1FRkFVTFQpOworfQorCitzdGF0 aWMgaW50IGt1bml0X2dlbmVyaWNfcnVuX3RocmVhZGZuX2FkYXB0ZXIodm9pZCAqZGF0YSkKK3sK KwlzdHJ1Y3Qga3VuaXRfdHJ5X2NhdGNoICp0cnlfY2F0Y2ggPSBkYXRhOworCisJdHJ5X2NhdGNo LT50cnkodHJ5X2NhdGNoLT5jb250ZXh0KTsKKworCWNvbXBsZXRlX2FuZF9leGl0KHRyeV9jYXRj aC0+dHJ5X2NvbXBsZXRpb24sIDApOworfQorCitzdGF0aWMgdW5zaWduZWQgbG9uZyBrdW5pdF90 ZXN0X3RpbWVvdXQodm9pZCkKK3sKKwl1bnNpZ25lZCBsb25nIHRpbWVvdXRfbXNlY3M7CisKKwkv KgorCSAqIFRPRE8oYnJlbmRhbmhpZ2dpbnNAZ29vZ2xlLmNvbSk6IFdlIHNob3VsZCBwcm9iYWJs eSBoYXZlIHNvbWUgdHlwZSBvZgorCSAqIHZhcmlhYmxlIHRpbWVvdXQgaGVyZS4gVGhlIG9ubHkg cXVlc3Rpb24gaXMgd2hhdCB0aGF0IHRpbWVvdXQgdmFsdWUKKwkgKiBzaG91bGQgYmUuCisJICoK KwkgKiBUaGUgaW50ZW50aW9uIGhhcyBhbHdheXMgYmVlbiwgYXQgc29tZSBwb2ludCwgdG8gYmUg YWJsZSB0byBsYWJlbAorCSAqIHRlc3RzIHdpdGggc29tZSB0eXBlIG9mIHNpemUgYnVja2V0ICh1 bml0L3NtYWxsLCBpbnRlZ3JhdGlvbi9tZWRpdW0sCisJICogbGFyZ2Uvc3lzdGVtL2VuZC10by1l bmQsIGV0YyksIHdoZXJlIGVhY2ggc2l6ZSBidWNrZXQgd291bGQgZ2V0IGEKKwkgKiBkZWZhdWx0 IHRpbWVvdXQgdmFsdWUga2luZCBvZiBsaWtlIHdoYXQgQmF6ZWwgZG9lczoKKwkgKiBodHRwczov L2RvY3MuYmF6ZWwuYnVpbGQvdmVyc2lvbnMvbWFzdGVyL2JlL2NvbW1vbi1kZWZpbml0aW9ucy5o dG1sI3Rlc3Quc2l6ZQorCSAqIFRoZXJlIGlzIHN0aWxsIHNvbWUgZGViYXRlIHRvIGJlIGhhZCBv biBleGFjdGx5IGhvdyB3ZSBkbyB0aGlzLiAoRm9yCisJICogb25lLCB3ZSBwcm9iYWJseSB3YW50 IHRvIGhhdmUgc29tZSBzb3J0IG9mIHRlc3QgcnVubmVyIGxldmVsCisJICogdGltZW91dC4pCisJ ICoKKwkgKiBGb3IgbW9yZSBiYWNrZ3JvdW5kIG9uIHRoaXMgdG9waWMsIHNlZToKKwkgKiBodHRw czovL21pa2UtYmxhbmQuY29tLzIwMTEvMTEvMDEvc21hbGwtbWVkaXVtLWxhcmdlLmh0bWwKKwkg Ki8KKwlpZiAoc3lzY3RsX2h1bmdfdGFza190aW1lb3V0X3NlY3MpIHsKKwkJLyoKKwkJICogSWYg c3lzY3RsX2h1bmdfdGFzayBpcyBhY3RpdmUsIGp1c3Qgc2V0IHRoZSB0aW1lb3V0IHRvIHNvbWUK KwkJICogdmFsdWUgbGVzcyB0aGFuIHRoYXQuCisJCSAqCisJCSAqIEluIHJlZ2FyZHMgdG8gdGhl IGFib3ZlIFRPRE8sIGlmIHdlIGRlY2lkZSBvbiB2YXJpYWJsZQorCQkgKiB0aW1lb3V0cywgdGhp cyBsb2dpYyB3aWxsIGxpa2VseSBuZWVkIHRvIGNoYW5nZS4KKwkJICovCisJCXRpbWVvdXRfbXNl Y3MgPSAoc3lzY3RsX2h1bmdfdGFza190aW1lb3V0X3NlY3MgLSAxKSAqCisJCQkJTVNFQ19QRVJf U0VDOworCX0gZWxzZSB7CisJCXRpbWVvdXRfbXNlY3MgPSAzMDAgKiBNU0VDX1BFUl9TRUM7IC8q IDUgbWluICovCisJfQorCisJcmV0dXJuIHRpbWVvdXRfbXNlY3M7Cit9CisKK3ZvaWQga3VuaXRf dHJ5X2NhdGNoX3J1bihzdHJ1Y3Qga3VuaXRfdHJ5X2NhdGNoICp0cnlfY2F0Y2gsIHZvaWQgKmNv bnRleHQpCit7CisJREVDTEFSRV9DT01QTEVUSU9OX09OU1RBQ0sodHJ5X2NvbXBsZXRpb24pOwor CXN0cnVjdCBrdW5pdCAqdGVzdCA9IHRyeV9jYXRjaC0+dGVzdDsKKwlzdHJ1Y3QgdGFza19zdHJ1 Y3QgKnRhc2tfc3RydWN0OworCWludCBleGl0X2NvZGUsIHRpbWVfcmVtYWluaW5nOworCisJdHJ5 X2NhdGNoLT5jb250ZXh0ID0gY29udGV4dDsKKwl0cnlfY2F0Y2gtPnRyeV9jb21wbGV0aW9uID0g JnRyeV9jb21wbGV0aW9uOworCXRyeV9jYXRjaC0+dHJ5X3Jlc3VsdCA9IDA7CisJdGFza19zdHJ1 Y3QgPSBrdGhyZWFkX3J1bihrdW5pdF9nZW5lcmljX3J1bl90aHJlYWRmbl9hZGFwdGVyLAorCQkJ CSAgdHJ5X2NhdGNoLAorCQkJCSAgImt1bml0X3RyeV9jYXRjaF90aHJlYWQiKTsKKwlpZiAoSVNf RVJSKHRhc2tfc3RydWN0KSkgeworCQl0cnlfY2F0Y2gtPmNhdGNoKHRyeV9jYXRjaC0+Y29udGV4 dCk7CisJCXJldHVybjsKKwl9CisKKwl0aW1lX3JlbWFpbmluZyA9IHdhaXRfZm9yX2NvbXBsZXRp b25fdGltZW91dCgmdHJ5X2NvbXBsZXRpb24sCisJCQkJCQkgICAgIGt1bml0X3Rlc3RfdGltZW91 dCgpKTsKKwlpZiAodGltZV9yZW1haW5pbmcgPT0gMCkgeworCQlrdW5pdF9lcnIodGVzdCwgInRy eSB0aW1lZCBvdXRcbiIpOworCQl0cnlfY2F0Y2gtPnRyeV9yZXN1bHQgPSAtRVRJTUVET1VUOwor CX0KKworCWV4aXRfY29kZSA9IHRyeV9jYXRjaC0+dHJ5X3Jlc3VsdDsKKworCWlmICghZXhpdF9j b2RlKQorCQlyZXR1cm47CisKKwlpZiAoZXhpdF9jb2RlID09IC1FRkFVTFQpCisJCXRyeV9jYXRj aC0+dHJ5X3Jlc3VsdCA9IDA7CisJZWxzZSBpZiAoZXhpdF9jb2RlID09IC1FSU5UUikKKwkJa3Vu aXRfZXJyKHRlc3QsICJ3YWtlX3VwX3Byb2Nlc3MoKSB3YXMgbmV2ZXIgY2FsbGVkXG4iKTsKKwll bHNlIGlmIChleGl0X2NvZGUpCisJCWt1bml0X2Vycih0ZXN0LCAiVW5rbm93biBlcnJvcjogJWRc biIsIGV4aXRfY29kZSk7CisKKwl0cnlfY2F0Y2gtPmNhdGNoKHRyeV9jYXRjaC0+Y29udGV4dCk7 Cit9CisKK3ZvaWQga3VuaXRfdHJ5X2NhdGNoX2luaXQoc3RydWN0IGt1bml0X3RyeV9jYXRjaCAq dHJ5X2NhdGNoLAorCQkJICBzdHJ1Y3Qga3VuaXQgKnRlc3QsCisJCQkgIGt1bml0X3RyeV9jYXRj aF9mdW5jX3QgdHJ5LAorCQkJICBrdW5pdF90cnlfY2F0Y2hfZnVuY190IGNhdGNoKQoreworCXRy eV9jYXRjaC0+dGVzdCA9IHRlc3Q7CisJdHJ5X2NhdGNoLT50cnkgPSB0cnk7CisJdHJ5X2NhdGNo LT5jYXRjaCA9IGNhdGNoOworfQotLSAKMi4yMy4wLjM1MS5nYzQzMTcwMzJlNi1nb29nCgpfX19f X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXwpkcmktZGV2ZWwgbWFp bGluZyBsaXN0CmRyaS1kZXZlbEBsaXN0cy5mcmVlZGVza3RvcC5vcmcKaHR0cHM6Ly9saXN0cy5m cmVlZGVza3RvcC5vcmcvbWFpbG1hbi9saXN0aW5mby9kcmktZGV2ZWw= From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-qk1-x749.google.com ([2607:f8b0:4864:20::749]) by bombadil.infradead.org with esmtps (Exim 4.92.2 #3 (Red Hat Linux)) id 1iBSC6-0003oN-1M for linux-um@lists.infradead.org; Fri, 20 Sep 2019 23:20:08 +0000 Received: by mail-qk1-x749.google.com with SMTP id s3so10041552qkd.6 for ; Fri, 20 Sep 2019 16:20:04 -0700 (PDT) Date: Fri, 20 Sep 2019 16:19:13 -0700 In-Reply-To: <20190920231923.141900-1-brendanhiggins@google.com> Message-Id: <20190920231923.141900-10-brendanhiggins@google.com> Mime-Version: 1.0 References: <20190920231923.141900-1-brendanhiggins@google.com> Subject: [PATCH v16 09/19] kunit: test: add support for test abort From: Brendan Higgins List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-um" Errors-To: linux-um-bounces+geert=linux-m68k.org@lists.infradead.org To: frowand.list@gmail.com, gregkh@linuxfoundation.org, jpoimboe@redhat.com, keescook@google.com, kieran.bingham@ideasonboard.com, mcgrof@kernel.org, peterz@infradead.org, robh@kernel.org, sboyd@kernel.org, shuah@kernel.org, tytso@mit.edu, yamada.masahiro@socionext.com 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, torvalds@linux-foundation.org, rdunlap@infradead.org, linux-kernel@vger.kernel.org, daniel@ffwll.ch, mpe@ellerman.id.au, linux-fsdevel@vger.kernel.org, logang@deltatee.com 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 Reviewed-by: Greg Kroah-Hartman Reviewed-by: Logan Gunthorpe Reviewed-by: Stephen Boyd --- include/kunit/test.h | 2 + include/kunit/try-catch.h | 75 +++++++++++++++++++++ lib/kunit/Makefile | 3 +- lib/kunit/test.c | 137 +++++++++++++++++++++++++++++++++----- lib/kunit/try-catch.c | 118 ++++++++++++++++++++++++++++++++ 5 files changed, 319 insertions(+), 16 deletions(-) create mode 100644 include/kunit/try-catch.h create mode 100644 lib/kunit/try-catch.c diff --git a/include/kunit/test.h b/include/kunit/test.h index 30a62de16bc9..3d554d7c1c79 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -10,6 +10,7 @@ #define _KUNIT_TEST_H #include +#include #include #include #include @@ -172,6 +173,7 @@ struct kunit { /* private: internal use only. */ const char *name; /* Read only after initialization! */ + struct kunit_try_catch try_catch; /* * success starts as true, and may only be set to false during a * test case; thus, it is safe to update this across multiple diff --git a/include/kunit/try-catch.h b/include/kunit/try-catch.h new file mode 100644 index 000000000000..404f336cbdc8 --- /dev/null +++ b/include/kunit/try-catch.h @@ -0,0 +1,75 @@ +/* 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 completion; +struct kunit; + +/** + * struct kunit_try_catch - provides a generic way to run code which might fail. + * @test: The test case that is currently being executed. + * @try_completion: Completion that the control thread waits on while test runs. + * @try_result: Contains any errno obtained while running test case. + * @try: The function, the test case, to attempt to run. + * @catch: The function called if @try bails out. + * @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 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. */ + struct kunit *test; + struct completion *try_completion; + int try_result; + kunit_try_catch_func_t try; + kunit_try_catch_func_t catch; + void *context; +}; + +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); + +void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context); + +void __noreturn kunit_try_catch_throw(struct kunit_try_catch *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/lib/kunit/Makefile b/lib/kunit/Makefile index 4e46450bcb3a..c9176c9c578c 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_KUNIT) += test.o \ string-stream.o \ - assert.o + assert.o \ + try-catch.o obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 3cbceb34b3b3..b2ca9b94c353 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -7,7 +7,9 @@ */ #include +#include #include +#include static void kunit_set_failure(struct kunit *test) { @@ -162,6 +164,19 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert) WARN_ON(string_stream_destroy(stream)); } +static void __noreturn kunit_abort(struct kunit *test) +{ + kunit_try_catch_throw(&test->try_catch); /* Does not return. */ + + /* + * 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"); +} + void kunit_do_assertion(struct kunit *test, struct kunit_assert *assert, bool pass, @@ -180,6 +195,9 @@ void kunit_do_assertion(struct kunit *test, kunit_fail(test, assert); va_end(args); + + if (assert->type == KUNIT_ASSERTION) + kunit_abort(test); } void kunit_init_test(struct kunit *test, const char *name) @@ -191,33 +209,122 @@ void kunit_init_test(struct kunit *test, const char *name) } /* - * Performs all logic to run a test case. + * Initializes and runs test case. Does not clean up or do post validations. */ -static void kunit_run_case(struct kunit_suite *suite, - struct kunit_case *test_case) +static void kunit_run_case_internal(struct kunit *test, + struct kunit_suite *suite, + struct kunit_case *test_case) { - struct kunit test; - - kunit_init_test(&test, test_case->name); - if (suite->init) { int ret; - ret = suite->init(&test); + ret = suite->init(test); if (ret) { - kunit_err(&test, "failed to initialize: %d\n", ret); - kunit_set_failure(&test); - test_case->success = test.success; + kunit_err(test, "failed to initialize: %d\n", ret); + kunit_set_failure(test); return; } } - test_case->run_case(&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! + */ +static void kunit_run_case_cleanup(struct kunit *test, + struct kunit_suite *suite) +{ if (suite->exit) - suite->exit(&test); + suite->exit(test); + + kunit_case_internal_cleanup(test); +} + +struct kunit_try_catch_context { + struct kunit *test; + struct kunit_suite *suite; + 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_suite *suite = ctx->suite; + 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, suite, test_case); + /* This line may never be reached. */ + kunit_run_case_cleanup(test, suite); +} + +static void kunit_catch_run_case(void *data) +{ + struct kunit_try_catch_context *ctx = data; + struct kunit *test = ctx->test; + struct kunit_suite *suite = ctx->suite; + int try_exit_code = kunit_try_catch_get_result(&test->try_catch); + + if (try_exit_code) { + kunit_set_failure(test); + /* + * 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; + } + + /* + * Test case was run, but aborted. It is the test case's business as to + * whether it failed or not, we just need to clean up. + */ + kunit_run_case_cleanup(test, suite); +} + +/* + * Performs all logic to run a test case. It also catches most errors that + * occur in a test case and reports them as failures. + */ +static void kunit_run_case_catch_errors(struct kunit_suite *suite, + struct kunit_case *test_case) +{ + struct kunit_try_catch_context context; + struct kunit_try_catch *try_catch; + struct kunit test; + + kunit_init_test(&test, test_case->name); + try_catch = &test.try_catch; - kunit_cleanup(&test); + kunit_try_catch_init(try_catch, + &test, + kunit_try_run_case, + kunit_catch_run_case); + context.test = &test; + context.suite = suite; + context.test_case = test_case; + kunit_try_catch_run(try_catch, &context); test_case->success = test.success; } @@ -230,7 +337,7 @@ int kunit_run_tests(struct kunit_suite *suite) kunit_print_subtest_start(suite); for (test_case = suite->test_cases; test_case->run_case; test_case++) { - kunit_run_case(suite, test_case); + kunit_run_case_catch_errors(suite, test_case); kunit_print_test_case_ok_not_ok(test_case, test_case_count++); } diff --git a/lib/kunit/try-catch.c b/lib/kunit/try-catch.c new file mode 100644 index 000000000000..55686839eb61 --- /dev/null +++ b/lib/kunit/try-catch.c @@ -0,0 +1,118 @@ +// 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 +#include +#include + +void __noreturn kunit_try_catch_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 unsigned long kunit_test_timeout(void) +{ + unsigned long timeout_msecs; + + /* + * 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 + */ + if (sysctl_hung_task_timeout_secs) { + /* + * If sysctl_hung_task is active, just set the timeout to some + * value less than that. + * + * In regards to the above TODO, if we decide on variable + * timeouts, this logic will likely need to change. + */ + timeout_msecs = (sysctl_hung_task_timeout_secs - 1) * + MSEC_PER_SEC; + } else { + timeout_msecs = 300 * MSEC_PER_SEC; /* 5 min */ + } + + return timeout_msecs; +} + +void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context) +{ + DECLARE_COMPLETION_ONSTACK(try_completion); + struct kunit *test = try_catch->test; + struct task_struct *task_struct; + int exit_code, time_remaining; + + try_catch->context = context; + 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; + } + + time_remaining = wait_for_completion_timeout(&try_completion, + kunit_test_timeout()); + if (time_remaining == 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_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; + try_catch->try = try; + try_catch->catch = catch; +} -- 2.23.0.351.gc4317032e6-goog _______________________________________________ linux-um mailing list linux-um@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-um