From patchwork Tue Oct 16 23:50:50 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985037 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="VOq6hj3R"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="ahWh4KkX"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXGD4NQHz9sCT for ; Wed, 17 Oct 2018 10:54:12 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=maOB9M0QHmWZ8gn/iB5ZDl7kqy9oUuTTBU1Q7wAYKcg=; b=VOq6hj3RytHl4q PJftGxc3qmdSln93/1SNhCixdiytvHz1HhqAHIHt5YyEoi8b3gPZ8HT3/3VaA5m7qDon0NUdKP18X kb0Z0qkRw0KbnRBGzVPTkX8vyaFOxTOKe3/PVXHa9FejqCytoafsFvHAO4mkAbgG4UAHWanAHXkVf fBw3xdbZyPDHEe+3ofXCMH7aXyRa3GCbg69VcwV3wyOMXKK2W6TbH4lqD6/ZvNSGyuh4ZKHZTEnKd e3HcZ0TRNOBX80gIJRXLZTf7XhLGIyh4lIkfs5yj9Qn2aVEdsD56dibiRV3GQ0BeyWU1ZwnKFd/eh KnxX+kATshwgOi1Xk1YA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZA4-000412-EL; Tue, 16 Oct 2018 23:54:04 +0000 Received: from mail-ua1-x949.google.com ([2607:f8b0:4864:20::949]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZA0-0003yG-OA for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:02 +0000 Received: by mail-ua1-x949.google.com with SMTP id c9so2178410uad.12 for ; Tue, 16 Oct 2018 16:53:49 -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=dJsydBHJBHH2EKQc0vg9/d0tw0KtYquVOLVEuVx4f9A=; b=ahWh4KkXQtuYcu7CO92XtCDzry1BTlRTtfQGrykZ/KsFGJmgJP3gWTjYVgnB29L8UT zH2nf/kBBSG8sHFtkQ0p0w9BAv0hrT22lG8JE+8GmvdZpyht/muIo6uPGBcn65PgzXjL kCxfT1XMDyBk40JbCU398KycI5ege89JsGYgonlK6Se6Ft+sAWAfr5os86cHPj0eaOZ0 ArIw8I9+9FHExxXdTRgQ1quIFT8XYxcAIvegKY/SSYBI05kJroegTnvc9bMsj/oticYS tZjaj1PpuSmIK8ZR6um2MRsMO6HeeUCAI+WIwHT2+yfjYELcLW3FGRTB8qKweqIx2f11 gCSA== 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=dJsydBHJBHH2EKQc0vg9/d0tw0KtYquVOLVEuVx4f9A=; b=g2eoR0oHjTFFA4k2Zva+opXjDXP4EDY8U3SWct3SJ/NvKiCoiAtJB66n43IJvb1AGT my10IGnDL7Fe/Y1D5AYhEDUiK/SzFhWC1D84CawQqS7nex07ugLNJDRkzT2va05wuCVW vLlrTPTS5SiSS2wvZ89/MtTByG8guQoS7Pbd/0Y6uQiVPJCIdidxmDHEK8TciiJ7cYNR /vwZUZJg1q9H1Qsi0EMr51Bz0GdxKb9eGRGPQpcr6cBPlLqeXEckr4zZWAmXsTgVc5++ 5VWLoH+tT86ooK4+4WDKq2s8xbIZtGewcmHn+qMB3F4f7vcCsTbn4V/C7H0Pvhd3+Etd eCrw== X-Gm-Message-State: ABuFfoiQXZ7ehzp6Rgt3nMfT7y0K9fBw2/1OtI5IyeGLlWakN24QxtBb CvHYpx+kBemmWGxgmVUScL7klqp+Peo6D7EKvjFHkA== X-Google-Smtp-Source: ACcGV626iFXh+sYRDn2tqPaZXBMHp7jmeuW/cFe896r88kpzZ7TnegCDwJzWKXnQAckRCYWcOiA4sPPYTsgsJZ0VbWeWzA== X-Received: by 2002:a1f:3250:: with SMTP id y77mr10272981vky.4.1539734028287; Tue, 16 Oct 2018 16:53:48 -0700 (PDT) Date: Tue, 16 Oct 2018 16:50:50 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-2-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 01/31] kunit: test: added string_stream a std::stream like string builder From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165400_811688_F6EBE37C X-CRM114-Status: GOOD ( 13.47 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:949 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org 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. This provides a library for constructing the string as you go similar to C++'s std::string. Signed-off-by: Brendan Higgins --- include/kunit/string-stream.h | 44 ++++++++++ kunit/Kconfig | 16 ++++ kunit/Makefile | 1 + kunit/string-stream.c | 149 ++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 include/kunit/string-stream.h create mode 100644 kunit/Kconfig create mode 100644 kunit/Makefile create mode 100644 kunit/string-stream.c diff --git a/include/kunit/string-stream.h b/include/kunit/string-stream.h new file mode 100644 index 0000000000000..933ed5740cf07 --- /dev/null +++ b/include/kunit/string-stream.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * C++ stream style string builder used in KUnit for building messages. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _KUNIT_STRING_STREAM_H +#define _KUNIT_STRING_STREAM_H + +#include +#include +#include +#include + +struct string_stream_fragment { + struct list_head node; + char *fragment; +}; + +struct string_stream { + size_t length; + struct list_head fragments; + + /* length and fragments are protected by this lock */ + spinlock_t lock; + struct kref refcount; + int (*add)(struct string_stream *this, const char *fmt, ...); + int (*vadd)(struct string_stream *this, const char *fmt, va_list args); + char *(*get_string)(struct string_stream *this); + void (*clear)(struct string_stream *this); + bool (*is_empty)(struct string_stream *this); +}; + +struct string_stream *new_string_stream(void); + +void destroy_string_stream(struct string_stream *stream); + +void string_stream_get(struct string_stream *stream); + +int string_stream_put(struct string_stream *stream); + +#endif /* _KUNIT_STRING_STREAM_H */ diff --git a/kunit/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..de16cce9d6a27 --- /dev/null +++ b/kunit/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_KUNIT) += string-stream.o diff --git a/kunit/string-stream.c b/kunit/string-stream.c new file mode 100644 index 0000000000000..1e7efa630cc35 --- /dev/null +++ b/kunit/string-stream.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * C++ stream style string builder used in KUnit for building messages. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include + +static int string_stream_vadd(struct string_stream *this, + const char *fmt, + va_list args) +{ + struct string_stream_fragment *fragment; + int len; + va_list args_for_counting; + unsigned long flags; + + /* Make a copy because `vsnprintf` could change it */ + va_copy(args_for_counting, args); + + /* Need space for null byte. */ + len = vsnprintf(NULL, 0, fmt, args_for_counting) + 1; + + va_end(args_for_counting); + + fragment = kmalloc(sizeof(*fragment), GFP_KERNEL); + if (!fragment) + return -ENOMEM; + + fragment->fragment = kmalloc(len, GFP_KERNEL); + if (!fragment->fragment) { + kfree(fragment); + return -ENOMEM; + } + + len = vsnprintf(fragment->fragment, len, fmt, args); + spin_lock_irqsave(&this->lock, flags); + this->length += len; + list_add_tail(&fragment->node, &this->fragments); + spin_unlock_irqrestore(&this->lock, flags); + return 0; +} + +static int string_stream_add(struct string_stream *this, const char *fmt, ...) +{ + va_list args; + int result; + + va_start(args, fmt); + result = string_stream_vadd(this, fmt, args); + va_end(args); + return result; +} + +static void string_stream_clear(struct string_stream *this) +{ + struct string_stream_fragment *fragment, *fragment_safe; + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + list_for_each_entry_safe(fragment, + fragment_safe, + &this->fragments, + node) { + list_del(&fragment->node); + kfree(fragment->fragment); + kfree(fragment); + } + this->length = 0; + spin_unlock_irqrestore(&this->lock, flags); +} + +static char *string_stream_get_string(struct string_stream *this) +{ + struct string_stream_fragment *fragment; + size_t buf_len = this->length + 1; /* +1 for null byte. */ + char *buf; + unsigned long flags; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return NULL; + + spin_lock_irqsave(&this->lock, flags); + list_for_each_entry(fragment, &this->fragments, node) + strlcat(buf, fragment->fragment, buf_len); + spin_unlock_irqrestore(&this->lock, flags); + + return buf; +} + +static bool string_stream_is_empty(struct string_stream *this) +{ + bool is_empty; + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + is_empty = list_empty(&this->fragments); + spin_unlock_irqrestore(&this->lock, flags); + + return is_empty; +} + +void destroy_string_stream(struct string_stream *stream) +{ + stream->clear(stream); + kfree(stream); +} + +static void string_stream_destroy(struct kref *kref) +{ + struct string_stream *stream = container_of(kref, + struct string_stream, + refcount); + destroy_string_stream(stream); +} + +struct string_stream *new_string_stream(void) +{ + struct string_stream *stream = kzalloc(sizeof(*stream), GFP_KERNEL); + + if (!stream) + return NULL; + + INIT_LIST_HEAD(&stream->fragments); + spin_lock_init(&stream->lock); + kref_init(&stream->refcount); + stream->add = string_stream_add; + stream->vadd = string_stream_vadd; + stream->get_string = string_stream_get_string; + stream->clear = string_stream_clear; + stream->is_empty = string_stream_is_empty; + return stream; +} + +void string_stream_get(struct string_stream *stream) +{ + kref_get(&stream->refcount); +} + +int string_stream_put(struct string_stream *stream) +{ + return kref_put(&stream->refcount, &string_stream_destroy); +} + From patchwork Tue Oct 16 23:50:51 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985038 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="TxHExoAN"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="Og+J2o2y"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXGG4gKPz9sCf for ; Wed, 17 Oct 2018 10:54:14 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=YfswmxZRZPhvgYve4KoRMsPlwtflsFg7iU9uZ8ItafU=; b=TxHExoAN7oX1Zj 6ngDxYNmrjb8zsGii1HL8R/34hJ6VvC0zIGEooZB946enN39DQDJ6qtmlo+CNL40C9M9LTw6oclgU F+ZnmHddsZlYeYYJBF+Kw7I0fSd4+z3BOS9ozGcp0POhe+TdcYkcksPMjxCClCQNQbV0eLNDa3CCn fSitTIByFQuWUoDvMijVlgIhLMkEcdximP8aKpLgOd3eGHoSB3FAwC+tD70kFbnbZAWpPykO/Npog 5ECtcWs2mepU1MEhAF+uD1qHXmYJ540ooDJKH4fKDx6vYTTFjKcW3OaUE0gyz+xonrgzMU/NcnuE4 g6F3oN/O4Kvrbk3lDQ+w==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZA6-00043S-SY; Tue, 16 Oct 2018 23:54:06 +0000 Received: from mail-it1-x14a.google.com ([2607:f8b0:4864:20::14a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZA1-0003yQ-Up for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:04 +0000 Received: by mail-it1-x14a.google.com with SMTP id f79-v6so231297ita.3 for ; Tue, 16 Oct 2018 16:53: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=g8+DXEehmEsKPJ5EdMFvj4dPmmBKovok5Rh31GZ5sNo=; b=Og+J2o2y/qIJzKDTrhQzAbp6JfF26UQSl33cgzhn0CeXHTzSX64huCQNNYnFXuFHr9 2iA+EmBI8JAeyPR6su6LG7qP5Quokm+Na5QjsX7gBpB0+YR+011cpKarp4jQfYXl+fKq qHdy2YqgAX7tKDKFk2n2xx9H1Vl/e5vKTlAkeYLak9qtTejIk2zZS11ZmscASPDqegXf TY//M+N5LBIgTxbzuK2LYHKvfhbmy9p4QheVg0q5g/sq/ZJp7DrnpPv0H0D+QeQ4iULI YfLBC4kLT1jthZeMilJL4PWBYHRSmMEzOJ/9vho/NKHen5xLKzfFCmXY80k0oijMfaxN EQwQ== 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=g8+DXEehmEsKPJ5EdMFvj4dPmmBKovok5Rh31GZ5sNo=; b=Avl33kEm0jTcLZ+ZMBqSeZv0qcm9Wt+LQf7vRi7m+xym0+Eox+IlPSzPNlGSu3aZeB qUlLBrEkn6LbSkQ3wqEh4/KXSeBMxdV+JM+AdNYnYNoNbsxwflpAanIQx+wr9vJN09w1 2hFwDsh2OmNT5D6yWeWAkscK/YdmVBRXw9drSo3IIJz1Ux7PsuKYUM0BV4tV2a6+FGcd cjPS8IRQFbtU7am48ZgheOALgmdwqtZUVsQQM/Bk24ALg6d4N3mKBOy4rs7R9JBqBKsT kO0XzHOp0Fwn1KyB31ll8mZQ5N8UDCMUpxihO+Enk7Fe+OW9srb2VASdOjR65gdeGHtV 6znA== X-Gm-Message-State: AGRZ1gJWnWhYmTgVlWXHBxtl7Hphn5FgFJiaMJId4KQ1rbe8iQdeH2ri CAWXjOt+27VBcAYP6FwJceymlShgfMk78hhAQC0osQ== X-Google-Smtp-Source: ACcGV62YFgztZjXre4oDVW7eHJ6X2JeDtpCzrkRI/kSKmsM/prFgxXrNKgeYROcVEqyYKwOGkaTe1ObvRb/7C3tFPSAvPw== X-Received: by 2002:a24:6b97:: with SMTP id v145-v6mr203332itc.40.1539734030616; Tue, 16 Oct 2018 16:53:50 -0700 (PDT) Date: Tue, 16 Oct 2018 16:50:51 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-3-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 02/31] kunit: test: adds KUnit test runner core From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165401_998758_72D3250D X-CRM114-Status: GOOD ( 19.74 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:14a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Adds the KUnit core, which allows test cases to be defined and associated with common initialization and cleanup logic. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 165 ++++++++++++++++++++++++++++++++++++++++++ kunit/Makefile | 2 +- kunit/test.c | 168 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 include/kunit/test.h create mode 100644 kunit/test.c diff --git a/include/kunit/test.h b/include/kunit/test.h new file mode 100644 index 0000000000000..e0b14b227ac44 --- /dev/null +++ b/include/kunit/test.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Base unit test (KUnit) API. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _KUNIT_TEST_H +#define _KUNIT_TEST_H + +#include +#include + +struct test; + +/** + * struct test_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 test *)`` + * that makes expectations (see TEST_EXPECT_TRUE()) about code under test. Each + * test case is associated with a &struct test_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 TEST_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 test *test) + * { + * TEST_EXPECT_EQ(test, 1, add(1, 0)); + * TEST_EXPECT_EQ(test, 2, add(1, 1)); + * TEST_EXPECT_EQ(test, 0, add(-1, 1)); + * TEST_EXPECT_EQ(test, INT_MAX, add(0, INT_MAX)); + * TEST_EXPECT_EQ(test, -1, add(INT_MAX, INT_MIN)); + * } + * + * static struct test_case example_test_cases[] = { + * TEST_CASE(add_test_basic), + * {}, + * }; + * + */ +struct test_case { + void (*run_case)(struct test *test); + const char name[256]; + + /* private: internal use only. */ + bool success; +}; + +/** + * TEST_CASE - A helper for creating a &struct test_case + * @test_name: a reference to a test case function. + * + * Takes a symbol for a function representing a test case and creates a &struct + * test_case object from it. See the documentation for &struct test_case for an + * example on how to use it. + */ +#define TEST_CASE(test_name) { .run_case = test_name, .name = #test_name } + +/** + * struct test_module - describes a related collection of &struct test_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 test_module is a collection of related &struct test_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 test_case must be associated with a test_module for KUnit to + * run it. + */ +struct test_module { + const char name[256]; + int (*init)(struct test *test); + void (*exit)(struct test *test); + struct test_case *test_cases; +}; + +/** + * struct test - 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 test_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 test { + 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 test *test, + const char *level, + struct va_format *vaf); +}; + +int test_init_test(struct test *test, const char *name); + +int test_run_tests(struct test_module *module); + +/** + * module_test() - used to register a &struct test_module with KUnit. + * @module: a statically allocated &struct test_module. + * + * Registers @module with the test framework. See &struct test_module for more + * information. + */ +#define module_test(module) \ + static int module_test_init##module(void) \ + { \ + return test_run_tests(&module); \ + } \ + late_initcall(module_test_init##module) + +void __printf(3, 4) test_printk(const char *level, + const struct test *test, + const char *fmt, ...); + +/** + * test_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 test_info(test, fmt, ...) \ + test_printk(KERN_INFO, test, fmt, ##__VA_ARGS__) + +/** + * test_warn() - Prints a WARN level message associated with the current test. + * @test: The test context object. + * @fmt: A printk() style format string. + * + * See test_info(). + */ +#define test_warn(test, fmt, ...) \ + test_printk(KERN_WARNING, test, fmt, ##__VA_ARGS__) + +/** + * test_err() - Prints an ERROR level message associated with the current test. + * @test: The test context object. + * @fmt: A printk() style format string. + * + * See test_info(). + */ +#define test_err(test, fmt, ...) \ + test_printk(KERN_ERR, test, fmt, ##__VA_ARGS__) + +#endif /* _KUNIT_TEST_H */ diff --git a/kunit/Makefile b/kunit/Makefile index de16cce9d6a27..7fc613a9b383b 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1 +1 @@ -obj-$(CONFIG_KUNIT) += string-stream.o +obj-$(CONFIG_KUNIT) += test.o string-stream.o diff --git a/kunit/test.c b/kunit/test.c new file mode 100644 index 0000000000000..4732e5f0d7575 --- /dev/null +++ b/kunit/test.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Base unit test (KUnit) API. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include +#include + +static bool test_get_success(struct test *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 test_set_success(struct test *test, bool success) +{ + unsigned long flags; + + spin_lock_irqsave(&test->lock, flags); + test->success = success; + spin_unlock_irqrestore(&test->lock, flags); +} + +static int test_vprintk_emit(const struct test *test, + int level, + const char *fmt, + va_list args) +{ + return vprintk_emit(0, level, NULL, 0, fmt, args); +} + +static int test_printk_emit(const struct test *test, + int level, + const char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = test_vprintk_emit(test, level, fmt, args); + va_end(args); + + return ret; +} + +static void test_vprintk(const struct test *test, + const char *level, + struct va_format *vaf) +{ + test_printk_emit(test, + level[1] - '0', + "kunit %s: %pV", test->name, vaf); +} + +int test_init_test(struct test *test, const char *name) +{ + spin_lock_init(&test->lock); + test->name = name; + test->vprintk = test_vprintk; + + return 0; +} + +/* + * Initializes and runs test case. Does not clean up or do post validations. + */ +static void test_run_case_internal(struct test *test, + struct test_module *module, + struct test_case *test_case) +{ + int ret; + + if (module->init) { + ret = module->init(test); + if (ret) { + test_err(test, "failed to initialize: %d", ret); + test_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 test_run_case_internal! + */ +static void test_run_case_cleanup(struct test *test, + struct test_module *module, + struct test_case *test_case) +{ + if (module->exit) + module->exit(test); +} + +/* + * Performs all logic to run a test case. + */ +static bool test_run_case(struct test *test, + struct test_module *module, + struct test_case *test_case) +{ + test_set_success(test, true); + + test_run_case_internal(test, module, test_case); + test_run_case_cleanup(test, module, test_case); + + return test_get_success(test); +} + +int test_run_tests(struct test_module *module) +{ + bool all_passed = true, success; + struct test_case *test_case; + struct test test; + int ret; + + ret = test_init_test(&test, module->name); + if (ret) + return ret; + + for (test_case = module->test_cases; test_case->run_case; test_case++) { + success = test_run_case(&test, module, test_case); + if (!success) + all_passed = false; + + test_info(&test, + "%s %s", + test_case->name, + success ? "passed" : "failed"); + } + + if (all_passed) + test_info(&test, "all tests passed"); + else + test_info(&test, "one or more tests failed"); + + return 0; +} + +void test_printk(const char *level, + const struct test *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 Tue Oct 16 23:50:52 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985039 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="OA/4As+3"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="miUaO1AJ"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXGN3LLRz9sCf for ; Wed, 17 Oct 2018 10:54:20 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=ly0FpUQlagLzXvh2qfMhJ/Ps4vZKztyy2QltC3rzofs=; b=OA/4As+3S8/nNC g9U2QvqTPx3aA7Bez2iR/HixFlZ5oBw6DZ3L5I/jj2oDFdWNDfzpicPiCt61Hv4E4+UtXVkS0x6Ja ASEQ0mavu8sGEnUeHkfeU90DxdCCINPwdX6TtfT0m2SIgGx7LhSulvK/S+vSlehjTK/Q9ns/kQUW5 PIG+qFAe5skPkn3enimjvBX7z+Jua4CvTK2BqbJ2K9tTE+I6IYI1S8KBljxTJIQDnmaknbQde3Ad2 HPDpMf+7r92BXYHbWXzV65sgaMCq07Ff5DQL1KssIBoLR1LC9fupTdF5KqOw5DbB2EgOPopnBbIkt 6DPDKmcBYXIrJBlyhG8Q==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAC-00049O-6m; Tue, 16 Oct 2018 23:54:12 +0000 Received: from mail-ot1-x34a.google.com ([2607:f8b0:4864:20::34a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZA5-0003ye-EO for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:09 +0000 Received: by mail-ot1-x34a.google.com with SMTP id c46so18317906otd.0 for ; Tue, 16 Oct 2018 16:53:54 -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=EYulDhUL2KJVxxT6QuwulYTstXbq7G4YKkwaMmylt1I=; b=miUaO1AJLXHjsNVBpjHC9wyz/g+j/lcPAowKHe7DbDrdWInlhVembL7hgcfTsA3LPM IfsjBgjXdJuSxXIgUh2Qe0J3y8k5CLel8kfxRRZTLNqiogneQuujdSXQ/kXG1P0GpXpY NhIPynQfbzvbzZwVfJvHQJRoTCdIGAImo0aUYPQrZ0QDePW+lIqtcBqbZ/VSY5MsYcrr Og3he5RBlbSVenrSm3kI1nCfqCocmHcptt2Gqr9+ERrIDtRQnfH4QiTybeISLfkGxZ1W ClMDVFcT+VGMqBXSNaBkOWAOm4dM5rz/i1WsHg3RcVqRQBH8vYdQeaHo2dOnHkuRcg0e ljzw== 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=EYulDhUL2KJVxxT6QuwulYTstXbq7G4YKkwaMmylt1I=; b=gid7C9kkmBDRYLi106cO0LRst33zlWwKOoXo2aVg2uq4IB9Q2EnafRJZDRKqC+RUiI Uuy/9InZXvPDEA7fH38z004VdmqJkOGtRXsEvjQPvwqb4rDPVMAGO0X3CTNXhq+Msl0g Diu+wfANDJNRfr6sRWro9DBBGzXtudvLOLlLQ3/3EkbwVZR9bCCM+GVJzGrn02FJ461r nywFf/3gDhTyjvgNI/BpOdIj4YikMgJGPwvrfNhuRI4Zdrt6EdIU18a0CaRHRDtXyDDs Y/r6crcNj9m/05yD4b7Rk++0AwHdEchYEhxNykZKN283jCU6korcPH8dgcpnGTtJWRRR bbFw== X-Gm-Message-State: ABuFfoglhhx7EwwahQaI7Ts0q3ErEiOFTeQ7ijnvzizyVabkoGbftxlI pJPlAflooqWDtRS/y4ovRm/0fVaGNC44L1tF3D0KxA== X-Google-Smtp-Source: ACcGV62cYwMOTA2v1YY9R8wzoV39vOQvSZOW4hobtRYUMuHbOvcBHZfxiG+3UH0nQZ2u1v2ftoc/qEqZuufghCk5we7cKA== X-Received: by 2002:aca:6a88:: with SMTP id f130-v6mr20441244oic.0.1539734033400; Tue, 16 Oct 2018 16:53:53 -0700 (PDT) Date: Tue, 16 Oct 2018 16:50:52 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-4-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 03/31] kunit: test: added test resource management API From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165405_515354_AAE94E9D X-CRM114-Status: GOOD ( 13.27 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org This creates a common API for test managed resources like memory and test objects. 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 e0b14b227ac44..1c116a20063da 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -12,6 +12,69 @@ #include #include +struct test_resource; + +typedef int (*test_resource_init_t)(struct test_resource *, void *); +typedef void (*test_resource_free_t)(struct test_resource *); + +/** + * struct test_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 + * test_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 test_kmalloc_params { + * size_t size; + * gfp_t gfp; + * }; + * + * static int test_kmalloc_init(struct test_resource *res, void *context) + * { + * struct test_kmalloc_params *params = context; + * res->allocation = kmalloc(params->size, params->gfp); + * + * if (!res->allocation) + * return -ENOMEM; + * + * return 0; + * } + * + * static void test_kmalloc_free(struct test_resource *res) + * { + * kfree(res->allocation); + * } + * + * void *test_kmalloc(struct test *test, size_t size, gfp_t gfp) + * { + * struct test_kmalloc_params params; + * struct test_resource *res; + * + * params.size = size; + * params.gfp = gfp; + * + * res = test_alloc_resource(test, test_kmalloc_init, + * test_kmalloc_free, ¶ms); + * if (res) + * return res->allocation; + * else + * return NULL; + * } + */ +struct test_resource { + void *allocation; + test_resource_free_t free; + + /* private: internal use only. */ + struct list_head node; +}; + struct test; /** @@ -104,6 +167,7 @@ struct test { 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 test *test, const char *level, struct va_format *vaf); @@ -127,6 +191,51 @@ int test_run_tests(struct test_module *module); } \ late_initcall(module_test_init##module) +/** + * test_alloc_resource() - Allocates a *test managed resource*. + * @test: The test context object. + * @init: a user supplied function to initialize the resource. + * @free: a user supplied function to free the resource. + * @context: for the user to pass in arbitrary data. + * + * Allocates a *test managed resource*, a resource which will automatically be + * cleaned up at the end of a test case. See &struct test_resource for an + * example. + */ +struct test_resource *test_alloc_resource(struct test *test, + test_resource_init_t init, + test_resource_free_t free, + void *context); + +void test_free_resource(struct test *test, struct test_resource *res); + +/** + * test_kmalloc() - Just 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 + * test_resource for more information. + */ +void *test_kmalloc(struct test *test, size_t size, gfp_t gfp); + +/** + * test_kzalloc() - Just like test_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 test_kmalloc() for more information. + */ +static inline void *test_kzalloc(struct test *test, size_t size, gfp_t gfp) +{ + return test_kmalloc(test, size, gfp | __GFP_ZERO); +} + +void test_cleanup(struct test *test); + void __printf(3, 4) test_printk(const char *level, const struct test *test, const char *fmt, ...); diff --git a/kunit/test.c b/kunit/test.c index 4732e5f0d7575..fd0a51245215e 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -66,6 +66,7 @@ static void test_vprintk(const struct test *test, int test_init_test(struct test *test, const char *name) { spin_lock_init(&test->lock); + INIT_LIST_HEAD(&test->resources); test->name = name; test->vprintk = test_vprintk; @@ -93,6 +94,11 @@ static void test_run_case_internal(struct test *test, test_case->run_case(test); } +static void test_case_internal_cleanup(struct test *test) +{ + test_cleanup(test); +} + /* * Performs post validations and cleanup after a test case was run. * XXX: Should ONLY BE CALLED AFTER test_run_case_internal! @@ -103,6 +109,8 @@ static void test_run_case_cleanup(struct test *test, { if (module->exit) module->exit(test); + + test_case_internal_cleanup(test); } /* @@ -150,6 +158,93 @@ int test_run_tests(struct test_module *module) return 0; } +struct test_resource *test_alloc_resource(struct test *test, + test_resource_init_t init, + test_resource_free_t free, + void *context) +{ + struct test_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 test_free_resource(struct test *test, struct test_resource *res) +{ + res->free(res); + list_del(&res->node); + kfree(res); +} + +struct test_kmalloc_params { + size_t size; + gfp_t gfp; +}; + +static int test_kmalloc_init(struct test_resource *res, void *context) +{ + struct test_kmalloc_params *params = context; + + res->allocation = kmalloc(params->size, params->gfp); + if (!res->allocation) + return -ENOMEM; + + return 0; +} + +static void test_kmalloc_free(struct test_resource *res) +{ + kfree(res->allocation); +} + +void *test_kmalloc(struct test *test, size_t size, gfp_t gfp) +{ + struct test_kmalloc_params params; + struct test_resource *res; + + params.size = size; + params.gfp = gfp; + + res = test_alloc_resource(test, + test_kmalloc_init, + test_kmalloc_free, + ¶ms); + + if (res) + return res->allocation; + else + return NULL; +} + +void test_cleanup(struct test *test) +{ + struct test_resource *resource, *resource_safe; + unsigned long flags; + + spin_lock_irqsave(&test->lock, flags); + list_for_each_entry_safe(resource, + resource_safe, + &test->resources, + node) { + test_free_resource(test, resource); + } + spin_unlock_irqrestore(&test->lock, flags); +} + void test_printk(const char *level, const struct test *test, const char *fmt, ...) From patchwork Tue Oct 16 23:50:53 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985040 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="juj1+8IB"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="DLTnLmeH"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXGP60DWz9sCf for ; Wed, 17 Oct 2018 10:54:21 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=5+2T9aSKKI1+OEhxQrG7iL7QWbvaPlS+0uSTE1XFPY0=; b=juj1+8IBzwUqHL 9URbh2aQV9kLlwJZSWzdqWVL+aX0sLJkug9i+CEKWNcIV/4vS91a243jeCBUnc3YSxkgzqG74h22O qX35ghzOBUGEj4V61H0aM4OwVUOQppo1y+zt12FCyK9MNOhTIQwHTsG5hs4r0o5TKc7Sv+BMO6H2+ yt4WQ5m6bh8+boULt2cajtvFxO8RIXQJKW5llYUwXw+XK+G3BiPOfxYvMdvSGu3nKxHTVbl/QejT0 80EkGxg4r0e5XASgMjOnAtrQUMioMTPB+dvwzWi6P686oeBtTPZy61XizpGUGC4AdS4mvdQoMgWmN 9Yc+u7NF+szfi6m0lVaA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAC-00049f-R4; Tue, 16 Oct 2018 23:54:12 +0000 Received: from mail-yb1-xb4a.google.com ([2607:f8b0:4864:20::b4a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZA7-0003zB-TA for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:10 +0000 Received: by mail-yb1-xb4a.google.com with SMTP id g194-v6so13892275ybf.5 for ; Tue, 16 Oct 2018 16:53:56 -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=IGBYeiYpCY/8G3zSHyqqYAJpX70mNKHubWHsRN/Tq5c=; b=DLTnLmeHnpLH0JF8SkW3nbKgh9Sg210AkhRc6IKlOEVJN8mXJGvOaLWJH0d3DAsji7 2V7f2ftM+attD9ElyBfEAzK1i4bKElAiX+QKHrDth9jQFvLOcfE4ldgxnZu66RPnrJox AePVjP6+otP/P00+F9edI3Lskh9pNaGHrsFEqdCK3wARUMA9s8cN5ECRwhSMAMf7K4YK wnDdO3wXaYJs+J4YkG5KmKXWvQIVSCRIIBGekSGF72nDsVlhOx0HHtpqzukzI5He0gq/ oHTKJwx+CkqKcZINhugkcnjf3byhh+F+ZH9I7AIomu4YvNnbvWd3t9xY+k/zhiGA2uBd ehXQ== 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=IGBYeiYpCY/8G3zSHyqqYAJpX70mNKHubWHsRN/Tq5c=; b=T+r0valRcR577sQ6h8RnGYMBisUt5jv7Fstxxqrh5RKf5QHDmPXKE9wMs/q4I8q0ds FwurqHL4qCQcKuz6H4YnSiqVDa9Jv1ihmhMtKJRVlc94/pjVFd8xDZCj7tzn6pv3PchV ITC5kf6bu/wmpIiub/3wa5mCE2ZYSh0Ofys5IIu450tW/I638uzG/AwP6tailQsDcjj7 sFKnEqPi0JOrL5ZHctaJC24SkhVQLHMdh066AbSwCEkNLlrPVBzAeESBPLwDwDgHV1TS kmmKW8XdLLf1aQIlwD9AtOTBLvfQXSGNkCV8dgB8qP3/9yAJ4hTx0qdfAFCqYKITM0pj 12dQ== X-Gm-Message-State: ABuFfogsKNaU/shhui0uTg8ihJqV8dEEbs8FdIrx9eITbZSLTWmCrNvt 6Uk8JNBJZ9UChHiTyY55rq1ORT0wGMQVeyAJ04iWuQ== X-Google-Smtp-Source: ACcGV63J5rTOco7nLPJ1eo5y8XVI9nCTpZyMdVRbiYVu8yPY/WuefiE+LM1q6V/7vTNgZwEypiWyAszH5LhrvyRi9cp8jg== X-Received: by 2002:a25:a091:: with SMTP id y17-v6mr14812433ybh.53.1539734035869; Tue, 16 Oct 2018 16:53:55 -0700 (PDT) Date: Tue, 16 Oct 2018 16:50:53 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-5-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 04/31] kunit: test: added test_stream a std::stream like logger From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165408_004354_836770DB X-CRM114-Status: GOOD ( 15.29 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:b4a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Adds a C++ style log library for for logging test results. Signed-off-by: Brendan Higgins --- include/kunit/test-stream.h | 49 ++++++++++++ include/kunit/test.h | 2 + kunit/Makefile | 2 +- kunit/test-stream.c | 153 ++++++++++++++++++++++++++++++++++++ kunit/test.c | 8 ++ 5 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 include/kunit/test-stream.h create mode 100644 kunit/test-stream.c diff --git a/include/kunit/test-stream.h b/include/kunit/test-stream.h new file mode 100644 index 0000000000000..c5dfd95ef21bb --- /dev/null +++ b/include/kunit/test-stream.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * C++ stream style string formatter and printer used in KUnit for outputting + * KUnit messages. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _KUNIT_TEST_STREAM_H +#define _KUNIT_TEST_STREAM_H + +#include +#include + +struct test; + +/** + * struct test_stream - a std::stream style string builder. + * @set_level: sets the level that this string should be printed at. + * @add: adds the formatted input to the internal buffer. + * @commit: prints out the internal buffer to the user. + * @clear: clears the internal buffer. + * + * A std::stream style string builder. Allows messages to be built up and + * printed all at once. + */ +struct test_stream { + void (*set_level)(struct test_stream *this, const char *level); + void (*add)(struct test_stream *this, const char *fmt, ...); + void (*append)(struct test_stream *this, struct test_stream *other); + void (*commit)(struct test_stream *this); + void (*clear)(struct test_stream *this); + /* private: internal use only. */ + struct test *test; + spinlock_t lock; /* Guards level. */ + const char *level; + struct string_stream *internal_stream; +}; + +/** + * test_new_stream() - constructs a new &struct test_stream. + * @test: The test context object. + * + * Constructs a new test managed &struct test_stream. + */ +struct test_stream *test_new_stream(struct test *test); + +#endif /* _KUNIT_TEST_STREAM_H */ diff --git a/include/kunit/test.h b/include/kunit/test.h index 1c116a20063da..68320fa2452de 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -11,6 +11,7 @@ #include #include +#include struct test_resource; @@ -171,6 +172,7 @@ struct test { void (*vprintk)(const struct test *test, const char *level, struct va_format *vaf); + void (*fail)(struct test *test, struct test_stream *stream); }; int test_init_test(struct test *test, const char *name); diff --git a/kunit/Makefile b/kunit/Makefile index 7fc613a9b383b..5b4562ea7f322 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1 +1 @@ -obj-$(CONFIG_KUNIT) += test.o string-stream.o +obj-$(CONFIG_KUNIT) += test.o string-stream.o test-stream.o diff --git a/kunit/test-stream.c b/kunit/test-stream.c new file mode 100644 index 0000000000000..392966864a708 --- /dev/null +++ b/kunit/test-stream.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * C++ stream style string formatter and printer used in KUnit for outputting + * KUnit messages. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include + +static const char *test_stream_get_level(struct test_stream *this) +{ + unsigned long flags; + const char *level; + + spin_lock_irqsave(&this->lock, flags); + level = this->level; + spin_unlock_irqrestore(&this->lock, flags); + + return level; +} + +static void test_stream_set_level(struct test_stream *this, const char *level) +{ + unsigned long flags; + + spin_lock_irqsave(&this->lock, flags); + this->level = level; + spin_unlock_irqrestore(&this->lock, flags); +} + +static void test_stream_add(struct test_stream *this, const char *fmt, ...) +{ + va_list args; + struct string_stream *stream = this->internal_stream; + + va_start(args, fmt); + if (stream->vadd(stream, fmt, args) < 0) + test_err(this->test, "Failed to allocate fragment: %s", fmt); + + va_end(args); +} + +static void test_stream_append(struct test_stream *this, + struct test_stream *other) +{ + struct string_stream *other_stream = other->internal_stream; + const char *other_content; + + other_content = other_stream->get_string(other_stream); + + if (!other_content) { + test_err(this->test, + "Failed to get string from second argument for appending."); + return; + } + + this->add(this, other_content); +} + +static void test_stream_clear(struct test_stream *this) +{ + this->internal_stream->clear(this->internal_stream); +} + +static void test_stream_commit(struct test_stream *this) +{ + struct string_stream *stream = this->internal_stream; + struct string_stream_fragment *fragment; + const char *level; + char *buf; + + level = test_stream_get_level(this); + if (!level) { + test_err(this->test, + "Stream was committed without a specified log level."); + level = KERN_ERR; + this->set_level(this, level); + } + + buf = stream->get_string(stream); + if (!buf) { + test_err(this->test, + "Could not allocate buffer, dumping stream:"); + list_for_each_entry(fragment, &stream->fragments, node) { + test_err(this->test, fragment->fragment); + } + goto cleanup; + } + + test_printk(level, this->test, buf); + kfree(buf); + +cleanup: + this->clear(this); +} + +static int test_stream_init(struct test_resource *res, void *context) +{ + struct test *test = context; + struct test_stream *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + res->allocation = stream; + stream->test = test; + spin_lock_init(&stream->lock); + stream->internal_stream = new_string_stream(); + + if (!stream->internal_stream) + return -ENOMEM; + + stream->set_level = test_stream_set_level; + stream->add = test_stream_add; + stream->append = test_stream_append; + stream->commit = test_stream_commit; + stream->clear = test_stream_clear; + + return 0; +} + +static void test_stream_free(struct test_resource *res) +{ + struct test_stream *stream = res->allocation; + + if (!stream->internal_stream->is_empty(stream->internal_stream)) { + test_err(stream->test, + "End of test case reached with uncommitted stream entries."); + stream->commit(stream); + } + + destroy_string_stream(stream->internal_stream); + kfree(stream); +} + +struct test_stream *test_new_stream(struct test *test) +{ + struct test_resource *res; + + res = test_alloc_resource(test, + test_stream_init, + test_stream_free, + test); + + if (res) + return res->allocation; + else + return NULL; +} diff --git a/kunit/test.c b/kunit/test.c index fd0a51245215e..f798183533c8d 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -63,12 +63,20 @@ static void test_vprintk(const struct test *test, "kunit %s: %pV", test->name, vaf); } +static void test_fail(struct test *test, struct test_stream *stream) +{ + test_set_success(test, false); + stream->set_level(stream, KERN_ERR); + stream->commit(stream); +} + int test_init_test(struct test *test, const char *name) { spin_lock_init(&test->lock); INIT_LIST_HEAD(&test->resources); test->name = name; test->vprintk = test_vprintk; + test->fail = test_fail; return 0; } From patchwork Tue Oct 16 23:50:54 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985041 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="VL+SIogf"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="S6vu7l5f"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXGV4yd2z9sBN for ; Wed, 17 Oct 2018 10:54:26 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=5g9ND2w/B7IXF3ecm84+EXz8dwt5ZIsdOxXa+ckhhWg=; b=VL+SIogfjMjHNJ Yab+t0LQQkvjijl+VtkBkpFnUf9faoGqLS4lRwkMNiAUoUnrjtmw7wiXvcJ4VZ2TXGu8k2gOjQ+eJ 80QFp3u23xqtDP5H/gTdFaw6pryaFmohfOywnTljxr9Xo4GPHbHv3cNJONlgrq6Ovmudi1Yx+vUSb mnBATdMRewFjP/aU4P850344ksg5gVkEh7w5P926au/oQqW4AVm/9vXI/obZUpW419asH2S29cqog DDmQyPGHGenqRqEMTnRW/pejFV1XwC6MoArbw1L1/bB1nHK42fshiYxF6o4uAfHOQOCWhly7jUUdq KJ9xq4vI3jTfmcISAZLQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAH-0004E8-Gt; Tue, 16 Oct 2018 23:54:17 +0000 Received: from mail-qt1-x849.google.com ([2607:f8b0:4864:20::849]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAA-0003zf-Fr for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:13 +0000 Received: by mail-qt1-x849.google.com with SMTP id i64-v6so26466104qtb.21 for ; Tue, 16 Oct 2018 16:53:59 -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=SMfpYWXs7qn4Sw4Z7AL4Z3LRAaaGLbh/rhFfhnaO+uA=; b=S6vu7l5fvXpOUULIQIlpB+Fvika8OOQAf1IeSogBtJfdbdpFRHbnt55m/v+T0uCRws STUGVGDtc+JKs/ekscRGuMJGNkDmrQnEmkyhkzm3iyw2TScSRELLPkDRwPMa3vT2y/G3 PwDlRccwpaKzbr5PJEMdQKWiLv1o9131r3IA6Bjfg/eANqoni3EYDI8NK0zrbNwpczuH kzl7Xh4nf9qmssCRLgk0PsIYr8gUcsCIZvdCfk3Vhzxrah7K1O1wEmRv/ZEHzClygNby Lzr8b4ZYQFJuSwkVEZyYQzlcDl6oYbname/2/QRw+VPMCIC5Xdxt5LxSTv5/rqMtiHnB UzMQ== 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=SMfpYWXs7qn4Sw4Z7AL4Z3LRAaaGLbh/rhFfhnaO+uA=; b=QGQDZ8dZ7Fx/0ZxOWWr2WV8MlDk2oAn/dI8Fjx+ztfW2Knu+hShsbawIUGulHLc14K rm+gCWlo06vkdSG86iCyLzqnMwnffx5PYTnbPpZh2pTUvgETTngjFDytjFUIgsWMDAjG Y6E+CST1+ByIQ9/B6ykQIAq/ZfvL4+vgl3tyM3eCa0a2xOxRBjnTXLFS5HEXzhMzzpX8 dnZyOCCVTl5gpDAQXPPwExtjlRAC1Tn1FfMCwX4o74U7ScdkiF58AVyEM25w/y/77A1c aweciyKiooczS/z7cfT6a/5N5EnWQZSrQjqH7P8lPRPtnu+tJ4q7m9rpf+EXOtscPjPw FDRw== X-Gm-Message-State: ABuFfoifWg0DY8rXSa/0p15r9FpC+Lf5s49ytoYHKBTdHnoIrOnMvOHF GIuFOYt2qT6ZiWtUeAI3cXravpylhFEvXuh/a0U80w== X-Google-Smtp-Source: ACcGV63vK8S1qFNhUgm7on4fM0NsUIifBJ57Xr+AXYTE21YTajkAaryoKydALzboq67bT/3rmKwXYuYp21Jp4nUfZoVuNQ== X-Received: by 2002:a37:3cc2:: with SMTP id j185-v6mr316589qka.48.1539734038831; Tue, 16 Oct 2018 16:53:58 -0700 (PDT) Date: Tue, 16 Oct 2018 16:50:54 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-6-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 05/31] kunit: test: added the concept of expectations From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165410_580392_AEE57C05 X-CRM114-Status: GOOD ( 15.44 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:849 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Added support for expectations, which allow properties to be specified and then verified in tests. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 259 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) diff --git a/include/kunit/test.h b/include/kunit/test.h index 68320fa2452de..d652825d7296f 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -273,4 +273,263 @@ void __printf(3, 4) test_printk(const char *level, #define test_err(test, fmt, ...) \ test_printk(KERN_ERR, test, fmt, ##__VA_ARGS__) +static inline struct test_stream *test_expect_start(struct test *test, + const char *file, + const char *line) +{ + struct test_stream *stream = test_new_stream(test); + + stream->add(stream, "EXPECTATION FAILED at %s:%s\n\t", file, line); + + return stream; +} + +static inline void test_expect_end(struct test *test, + bool success, + struct test_stream *stream) +{ + if (!success) + test->fail(test, stream); + else + stream->clear(stream); +} + +#define TEST_EXPECT_START(test) \ + test_expect_start(test, __FILE__, __stringify(__LINE__)) + +#define TEST_EXPECT_END(test, success, stream) \ + test_expect_end(test, success, stream) + +#define TEST_EXPECT(test, success, message) do { \ + struct test_stream *__stream = TEST_EXPECT_START(test); \ + \ + __stream->add(__stream, message); \ + TEST_EXPECT_END(test, success, __stream); \ +} while (0) + +/** + * TEST_SUCCEED() - A no-op expectation. Only exists for code clarity. + * @test: The test context object. + * + * The opposite of TEST_FAIL(), it is an expectation that cannot fail. In other + * words, it does nothing and only exists for code clarity. See + * TEST_EXPECT_TRUE() for more information. + */ +#define TEST_SUCCEED(test) do {} while (0) + +/** + * TEST_FAIL() - Always causes a test to fail when evaluated. + * @test: The test context object. + * @message: an informational message to be printed when the assertion is made. + * + * The opposite of TEST_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 TEST_EXPECT_TRUE() + * for more information. + */ +#define TEST_FAIL(test, message) TEST_EXPECT(test, false, message) + +/** + * TEST_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 `TEST_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 TEST_EXPECT_TRUE(test, condition) \ + TEST_EXPECT(test, (condition), \ + "Expected " #condition " is true, but is false.") + +/** + * TEST_EXPECT_FALSE() - Causes 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 + * TEST_EXPECT_TRUE() for more information. + */ +#define TEST_EXPECT_FALSE(test, condition) \ + TEST_EXPECT(test, !(condition), \ + "Expected " #condition " is false, but is true.") + +static inline void test_expect_binary(struct test *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 test_stream *stream = test_expect_start(test, file, line); + + stream->add(stream, + "Expected %s %s %s, but\n", + left_name, compare_name, right_name); + stream->add(stream, "\t\t%s == %lld\n", left_name, left); + stream->add(stream, "\t\t%s == %lld", right_name, right); + + test_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 TEST_EXPECT_BINARY(test, left, condition, right) do { \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + test_expect_binary(test, \ + (long long) __left, #left, \ + (long long) __right, #right, \ + __left condition __right, #condition, \ + __FILE__, __stringify(__LINE__)); \ +} while (0) + +/** + * TEST_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 + * TEST_EXPECT_TRUE(@test, (@left) == (@right)). See TEST_EXPECT_TRUE() for more + * information. + */ +#define TEST_EXPECT_EQ(test, left, right) \ + TEST_EXPECT_BINARY(test, left, ==, right) + +/** + * TEST_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 + * TEST_EXPECT_TRUE(@test, (@left) != (@right)). See TEST_EXPECT_TRUE() for more + * information. + */ +#define TEST_EXPECT_NE(test, left, right) \ + TEST_EXPECT_BINARY(test, left, !=, right) + +/** + * TEST_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 + * TEST_EXPECT_TRUE(@test, (@left) < (@right)). See TEST_EXPECT_TRUE() for more + * information. + */ +#define TEST_EXPECT_LT(test, left, right) \ + TEST_EXPECT_BINARY(test, left, <, right) + +/** + * TEST_EXPECT_LE() - An expectation 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 TEST_EXPECT_TRUE(@test, (@left) <= (@right)). See TEST_EXPECT_TRUE() for + * more information. + */ +#define TEST_EXPECT_LE(test, left, right) \ + TEST_EXPECT_BINARY(test, left, <=, right) + +/** + * TEST_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 + * TEST_EXPECT_TRUE(@test, (@left) > (@right)). See TEST_EXPECT_TRUE() for more + * information. + */ +#define TEST_EXPECT_GT(test, left, right) \ + TEST_EXPECT_BINARY(test, left, >, right) + +/** + * TEST_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 + * TEST_EXPECT_TRUE(@test, (@left) >= (@right)). See TEST_EXPECT_TRUE() for more + * information. + */ +#define TEST_EXPECT_GE(test, left, right) \ + TEST_EXPECT_BINARY(test, left, >=, right) + +/** + * TEST_EXPECT_STREQ() - An expectation 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 + * TEST_EXPECT_TRUE(@test, !strcmp((@left), (@right))). See TEST_EXPECT_TRUE() + * for more information. + */ +#define TEST_EXPECT_STREQ(test, left, right) do { \ + struct test_stream *__stream = TEST_EXPECT_START(test); \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + \ + __stream->add(__stream, "Expected " #left " == " #right ", but\n"); \ + __stream->add(__stream, "\t\t%s == %s\n", #left, __left); \ + __stream->add(__stream, "\t\t%s == %s\n", #right, __right); \ + \ + TEST_EXPECT_END(test, !strcmp(left, right), __stream); \ +} while (0) + +/** + * TEST_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 + * TEST_EXPECT_TRUE(@test, !IS_ERR_OR_NULL(@ptr)). See TEST_EXPECT_TRUE() for + * more information. + */ +#define TEST_EXPECT_NOT_ERR_OR_NULL(test, ptr) do { \ + struct test_stream *__stream = TEST_EXPECT_START(test); \ + typeof(ptr) __ptr = (ptr); \ + \ + if (!__ptr) \ + __stream->add(__stream, \ + "Expected " #ptr " is not null, but is."); \ + if (IS_ERR(__ptr)) \ + __stream->add(__stream, \ + "Expected " #ptr " is not error, but is: %ld", \ + PTR_ERR(__ptr)); \ + \ + TEST_EXPECT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \ +} while (0) + #endif /* _KUNIT_TEST_H */ From patchwork Tue Oct 16 23:50:55 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985042 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="PsP5LsS2"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="X6G8Q/xz"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXGX05PQz9sj9 for ; Wed, 17 Oct 2018 10:54:28 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=EsgKfF8xZgXmCYlj50WWHKz0Cxziw+SdFgt1xrtSa1w=; b=PsP5LsS2Zmd+TO K+lq85umXzp5t88Et+NWNCTmyCYwfgIgTvUVhFdeXp4KPSX8sxChl17+TTU1eZGk3TksZMAUFU+6B 7znciUNOINCbvZ/liGfPLBawz8aJfgSQVZV/Ncuk3jxzI95XVeK+cIGqECreBNbyu+IMCKsncbZAo gUg2JEud0Y3PnLkZKEme/NRozIleRhohCG/SYZPfxud9PJueGswR6aZJXN27xE0Rfp+pyEizGRmBl DPadVuWuqaQ0UpCeAu8ixx2uBrBrJqk/iO9eHsy0xS6saOPd30eJOCZLd/waRu6N/1vo4x6yGkdtS j2Vzk16UFCDtxvq6eXnw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAK-0004GB-0x; Tue, 16 Oct 2018 23:54:20 +0000 Received: from mail-pf1-x449.google.com ([2607:f8b0:4864:20::449]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAD-00040I-D7 for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:16 +0000 Received: by mail-pf1-x449.google.com with SMTP id n81-v6so25321511pfi.20 for ; Tue, 16 Oct 2018 16:54: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=z58nC2iAZa+aWGH30Fl4KryPGgLRMoTZOR6aRJpK+Rc=; b=X6G8Q/xz0LHNn4askpya4Qyjb5yi0UFIqxSD98dPg2XnCRxBCWVSS+vhfvpHxdgZ61 c8MOcFdxRdS7RvaoTKOpSWmv/d588hkCfMLticT622iptne2B4SinvQthUHuCSQIzpmi 5NRik3PeyN00lSHTmNDR8St6U112z+m9a6hYY7kin6bs8iBVJl7HO0o8Q/41uh51hyul U++OufyoHMd+uGcdmV29bmdotEcHuGPQyeR/9f43r460hzOU6CWx1kPp1gjqMGzIEwtV XySW/0X7EHZPSJQAX7MNzAqiyHRSZ1eGqanOxIYRR2Ho/7Yb/W03dhboQAwNc7myUD46 zH3g== 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=z58nC2iAZa+aWGH30Fl4KryPGgLRMoTZOR6aRJpK+Rc=; b=uNTGHUTx1IER9l3BBF4+igrUzhimxZCcb+zcs5wqzr4faLZmJwxTD5Qnd3w0v/w9PJ lC4oQq5KWMZ2eT6ZituFebPY1AYv0t4sOfTfuZt+bo9GvI7WqQ2i6AKAY95QVYct1zkx 21YM9sqZaqMgONFbfNG366NWGtgCCQrovBWlX/ljyJinc+z2GnrZEqDuZSxbgorXmhzA uRdcjrWQTypYkqN58+xymsZJHP0No/cSmgsn9TffPLl3kfLGyu1wU3E7ooXA+OvnrACx pXn+FWkjz7bvYkG+uRo1RAJVsal84MjOTbyQ4aY2JWP1PCzy7VmNgSLykFK55VCZUhLx +Bpw== X-Gm-Message-State: ABuFfohoPM0Uw8j2J13kfuNRZXeRitdHRh9kv9wsLKJ80swz7L8pwg5a pNnMJ9lw6crWi4PfZXJ6HxpHtqiGzryB4Nbri8v5eg== X-Google-Smtp-Source: ACcGV63Eo+I6sCgcX/mpPbvZqH7+J7ycAASublcwpAXVYGmTf5rbFj1Qj6EfGfLoj+l0BjWoS2iTim6llDlsLHdgvElOGQ== X-Received: by 2002:a62:908e:: with SMTP id q14-v6mr12401580pfk.62.1539734041297; Tue, 16 Oct 2018 16:54:01 -0700 (PDT) Date: Tue, 16 Oct 2018 16:50:55 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-7-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 06/31] arch: um: enabled running kunit from User Mode Linux From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165413_514934_EF195FCD X-CRM114-Status: UNSURE ( 7.11 ) X-CRM114-Notice: Please train this message. X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Makes minimum number of changes outside of the KUnit directories for KUnit to build and run using UML. Signed-off-by: Brendan Higgins --- Makefile | 2 +- arch/um/Kconfig.rest | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 863f58503beed..c8e659468ed49 100644 --- a/Makefile +++ b/Makefile @@ -944,7 +944,7 @@ endif ifeq ($(KBUILD_EXTMOD),) -core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ +core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ kunit/ vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ diff --git a/arch/um/Kconfig.rest b/arch/um/Kconfig.rest index 08327b9c0cbea..484c4cfcad1ef 100644 --- a/arch/um/Kconfig.rest +++ b/arch/um/Kconfig.rest @@ -1,4 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 + +source "kunit/Kconfig" + source "init/Kconfig" source "kernel/Kconfig.freezer" From patchwork Tue Oct 16 23:50:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985043 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="JaCeJtCE"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="Wt2CLAmZ"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXGd0YFZz9sDb for ; Wed, 17 Oct 2018 10:54:33 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=3EXPVU0Gtavs0qb1+Iu+0GRtA/UAJUv5Rz1TyJ1YcGs=; b=JaCeJtCEqgHYND K2GzfM4xdJLiMgo45NFYO8Me/W+50/aquJOxYeOlhVCnhmwVtLuoytnyzeS/pruWv2zPqXz3ia2Ky 75SOfE/qNtaxj679KqQ/O0NwqpW7D82eVx09gHlmaLHqdcIQjwzIIEQF8wlzKRuDOFLKMwB7KcLBS 5RnjFNhYVTEfM2tN4Phwd7FUA0reX5nJqaGQZsWv8RZVqngcQQCs6VRAvvmKFrU+Jnj0wdwPFNDu5 XIYz9qJxHBC8aROj6Pf7D9ghB2gASm8UYdmWfe9wTXTKGuhtiHh7gLmzvgdlI0SvhQXITFOnLZzSO Hr6Esi4TfaerIwS2Jhlg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAO-0004JL-Ho; Tue, 16 Oct 2018 23:54:24 +0000 Received: from mail-it1-x149.google.com ([2607:f8b0:4864:20::149]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAF-000410-TU for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:19 +0000 Received: by mail-it1-x149.google.com with SMTP id p125-v6so269637itg.1 for ; Tue, 16 Oct 2018 16:54: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=uw9R47stJ0hTnKGMpz+45emoI+mx7jNptXF9kuz1348=; b=Wt2CLAmZ01nzug3igoC7QMWBTWiNCk2KyQeIforWF4nlybC7ZEhAAyYRVdH1zxAtzd pkOhW6g8H71Qvjbo+4UDKVWHR25ZLcCGnFZJlvKN2nXEE1zPb7NLvTY1IDQuohmg2Fxq wM3p2EuCN/DKI+SvMDHbH5K8RZtXeJWOQ9/MkvXW3qxPyjQGRjIpq91SNprEskd7YUnG MazK/PM7sdXZ1Y1eGs6Cf8sh7PODTkKGVurljr+88cCTY2JnkTNpnmCKZzSiRghYa4gw 5xBGJDMA7i2TQpHLnBIHS5pZz4COlFB+wDLnDM3KxhKGj89TVgGvSYScAe2JrawpCkhB hmEg== 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=uw9R47stJ0hTnKGMpz+45emoI+mx7jNptXF9kuz1348=; b=qcoHMKJbNJBG112Rr00cL0cObZw38wVy282IJ0hdA9dgNfAXyPshHIWI4VhgcABxMN uNRijGIe0qyacGsluK1zdqxQZQ9tXmBLW5++c4194PXnqq1If/OoIT69kJBWJUDyFYM6 Gw/EQXFsnTWBXxETIXz3MKo1LSxUq3ykDs2U0+OdMzdHcQoykHV2v75VUAlgozebojwj hYYtkwon04uSAsxazr2VoJPKPFZB0tET+jJIThl+YOzhFloqiKJXMJKDrbjIOd5bwIoi S+6wbPDL2BnFSYyRQVjBCftuZWsyiPrfJClQFOH2qtWg/E7vvUD+t4RRP7cSs1SfEUQw aNtQ== X-Gm-Message-State: ABuFfoiVDJbQ7n3U153sjxxqRoQeanl9iAEc7EjHTSvoa+41gHDV9YmZ eXRw7KQWXxVxM6UCmDuQhsfeVUqbYuZFy7E3Whfl2w== X-Google-Smtp-Source: ACcGV61KdtPmTfcO7rEaKongwQUAt/iV2T/cpr8Eoi6ca+lQX2dvI0CMHPDnpwwbfq9++5sRsoyW2MyoUagwnWdjIHuwYA== X-Received: by 2002:a24:8082:: with SMTP id g124-v6mr223410itd.12.1539734043663; Tue, 16 Oct 2018 16:54:03 -0700 (PDT) Date: Tue, 16 Oct 2018 16:50:56 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-8-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 07/31] kunit: test: added initial tests From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165415_978769_55E57976 X-CRM114-Status: GOOD ( 17.87 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:149 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Added a test for string stream along with a more simple example. Signed-off-by: Brendan Higgins --- kunit/Kconfig | 12 ++++++ kunit/Makefile | 2 + kunit/example-test.c | 88 ++++++++++++++++++++++++++++++++++++++ kunit/string-stream-test.c | 61 ++++++++++++++++++++++++++ 4 files changed, 163 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 5b4562ea7f322..319eb9dc8be0e 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1 +1,3 @@ obj-$(CONFIG_KUNIT) += test.o string-stream.o test-stream.o +obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o +obj-$(CONFIG_EXAMPLE_TEST) += example-test.o diff --git a/kunit/example-test.c b/kunit/example-test.c new file mode 100644 index 0000000000000..e9bd2b41c5fd2 --- /dev/null +++ b/kunit/example-test.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Example KUnit test to show how to use KUnit. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include + +/* + * This is the most fundamental element of KUnit, the test case. A test case + * makes a set EXPECTATIONs and ASSERTIONs about the behavior of some code; if + * any expectations or assertions are not met, the test fails; otherwise, the + * test passes. + * + * In KUnit, a test case is just a function with the signature + * `void (*)(struct test *)`. `struct test` is a context object that stores + * information about the current test. + */ +static void example_simple_test(struct test *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. + */ + TEST_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 test *test) +{ + test_info(test, "initializing"); + + return 0; +} + +/* + * Here we make a list of all the test cases we want to add to the test module + * below. + */ +static struct test_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. + */ + TEST_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 + * `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 test_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..07c626cbfffbf --- /dev/null +++ b/kunit/string-stream-test.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit test for struct string_stream. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include + +static void string_stream_test_get_string(struct test *test) +{ + struct string_stream *stream = new_string_stream(); + char *output; + + stream->add(stream, "Foo"); + stream->add(stream, " %s", "bar"); + + output = stream->get_string(stream); + TEST_EXPECT_STREQ(test, output, "Foo bar"); + kfree(output); + destroy_string_stream(stream); +} + +static void string_stream_test_add_and_clear(struct test *test) +{ + struct string_stream *stream = new_string_stream(); + char *output; + int i; + + for (i = 0; i < 10; i++) + stream->add(stream, "A"); + + output = stream->get_string(stream); + TEST_EXPECT_STREQ(test, output, "AAAAAAAAAA"); + TEST_EXPECT_EQ(test, stream->length, 10); + TEST_EXPECT_FALSE(test, stream->is_empty(stream)); + kfree(output); + + stream->clear(stream); + + output = stream->get_string(stream); + TEST_EXPECT_STREQ(test, output, ""); + TEST_EXPECT_TRUE(test, stream->is_empty(stream)); + destroy_string_stream(stream); +} + +static struct test_case string_stream_test_cases[] = { + TEST_CASE(string_stream_test_get_string), + TEST_CASE(string_stream_test_add_and_clear), + {} +}; + +static struct test_module string_stream_test_module = { + .name = "string-stream-test", + .test_cases = string_stream_test_cases +}; +module_test(string_stream_test_module); + From patchwork Tue Oct 16 23:50:57 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985044 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="NRumdtRe"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="YPx2z2OG"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXGf7166z9sj9 for ; Wed, 17 Oct 2018 10:54:34 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=xMIhSoF5pWqDMComGI8UIbmbAl72N1l2zTWAzIxO23Q=; b=NRumdtReLdFgGh PMx+mTBZZPPTCGUy7PxS/QVH92TMGiJ2JJToWd/pzZS3hwZTqrirgOe/Oqq5hB28h5OdyJrqnjoiF tH7DDW0/inpZo2E5X5XX6lfPHTfA6IuEhDqz8VqjJ8/mWiHJ5IjLB5h0H7UdWX5OOX6vR87Lx8umP 0FN7DIID9pTBTTveX5smR6E4MGmb9b5V5hcxajq97v/QeOUWNpBnAqLldz22n13X8OL92mPKbmsWE wfZCTG47btmf8S9WnexEWSvHVS6/C0VwLOCd9qYTvS1IbCotzXVPe6O/kFIMP0lamq1wIWogppKzW xl7CMvJE+b6kIOgwyjFw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAP-0004Jr-2p; Tue, 16 Oct 2018 23:54:25 +0000 Received: from mail-ua1-x94a.google.com ([2607:f8b0:4864:20::94a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAI-00043J-Gj for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:20 +0000 Received: by mail-ua1-x94a.google.com with SMTP id o25so2253369ual.13 for ; Tue, 16 Oct 2018 16:54:07 -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=QsZ3was/QkGUo6BYsP86tczumclzu/oVJ8cXDvZ38AM=; b=YPx2z2OGVftv8Dgf9hsP/BheUOYZFFBIJGxjmrXiQJYj4a1tn1kaNN09vpi1NKcpm2 JAZ8twZJiDMzSLhE+Mh397wmrl9kofyv/dOYsA4HBUqYXjYVAc0ZT6pON/EHIJaYUT2B 9jwfmA2gJxOXt1amkDD6rO+kaCLxft4cJg2eyLovzY/K0a7FvFC2VmYJ/IRq8ioWyYER G+9z/EDxYQMHTe5+Ld4lCNch12O10HzHmHkaRi5j/bLIhl/lKlSYzqW2f10LyWJ4n3XD Uvj/JkYva3y5wiRudK4WLtPONTRM51xi4AMDENu5RyPMsKIxm3H8Dn2ACpQ9JTB3V+wW iB7A== 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=QsZ3was/QkGUo6BYsP86tczumclzu/oVJ8cXDvZ38AM=; b=VDNqzDX/beV6ztFJPF02sptb9tFCycVRgv3UTVEQ6F4fzcfLY/pHf+fyS+OmlfDFX3 4Hdj8eQ8SyhBn428hcDzzVOMH/VPX9g+tJyPuzXDHpCq2swjlEkFKtB+JfA0xWmkHwcY lVQhb97gYK+GxcFBENQmmj/zZHmeG4q4iX8wLC35znqpjXbPV7hRjatfiAUdrIZBnjdb O7XelFLCJ+rto9HYQLaoBlPybudkVu4zdwKg37qBRrrhe8hWdeEDBqnlqOgoSapgCPDX IAUPpHn7c+pzEJhjRoWQEeZJoBnCh1AfMmmc8fHAltAXjERJhb4fPC7uvGRu/6iXl7gn s/uA== X-Gm-Message-State: ABuFfogUU5oCO88oho/vmOMtUdCS5vqc6y6C5jesZ7P3CNzfFHWXoLL/ 7/c3bqewxrNeAyXUHpHq6P+HtF6LqFb7KdP4WNPk2g== X-Google-Smtp-Source: ACcGV60TLAfJsKdTy2qQsKGEQgz5iDLsnZOMXyseSry9uD/hbZHFRPqzws3bKPdJyVvsNAbfBdbXDALvGPxOk4AAqTFd0g== X-Received: by 2002:a67:4551:: with SMTP id s78mr23044696vsa.12.1539734046197; Tue, 16 Oct 2018 16:54:06 -0700 (PDT) Date: Tue, 16 Oct 2018 16:50:57 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-9-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 08/31] arch: um: added shim to trap to allow installing a fault catcher for tests From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165418_601861_82300645 X-CRM114-Status: UNSURE ( 9.79 ) X-CRM114-Notice: Please train this message. X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:94a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Added context to current thread that allows a test to specify that it wants to skip the normal checks to run an installed fault catcher. Signed-off-by: Brendan Higgins --- arch/um/include/asm/processor-generic.h | 4 +++- arch/um/kernel/trap.c | 15 +++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/arch/um/include/asm/processor-generic.h b/arch/um/include/asm/processor-generic.h index b58b746d3f2ca..d566cd416ff02 100644 --- a/arch/um/include/asm/processor-generic.h +++ b/arch/um/include/asm/processor-generic.h @@ -27,6 +27,7 @@ struct thread_struct { struct task_struct *prev_sched; struct arch_thread arch; jmp_buf switch_buf; + bool is_running_test; struct { int op; union { @@ -51,7 +52,8 @@ struct thread_struct { .fault_addr = NULL, \ .prev_sched = NULL, \ .arch = INIT_ARCH_THREAD, \ - .request = { 0 } \ + .request = { 0 }, \ + .is_running_test = false, \ } static inline void release_thread(struct task_struct *task) diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index ec9a42c14c565..9b97712daf14f 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -201,6 +201,12 @@ void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) segv(*fi, UPT_IP(regs), UPT_IS_USER(regs), regs); } +static void segv_run_catcher(jmp_buf *catcher, void *fault_addr) +{ + current->thread.fault_addr = fault_addr; + UML_LONGJMP(catcher, 1); +} + /* * We give a *copy* of the faultinfo in the regs to segv. * This must be done, since nesting SEGVs could overwrite @@ -219,7 +225,10 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, if (!is_user && regs) current->thread.segv_regs = container_of(regs, struct pt_regs, regs); - if (!is_user && (address >= start_vm) && (address < end_vm)) { + catcher = current->thread.fault_catcher; + if (catcher && current->thread.is_running_test) + segv_run_catcher(catcher, (void *) address); + else if (!is_user && (address >= start_vm) && (address < end_vm)) { flush_tlb_kernel_vm(); goto out; } @@ -246,12 +255,10 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, address = 0; } - catcher = current->thread.fault_catcher; if (!err) goto out; else if (catcher != NULL) { - current->thread.fault_addr = (void *) address; - UML_LONGJMP(catcher, 1); + segv_run_catcher(catcher, (void *) address); } else if (current->thread.fault_addr != NULL) panic("fault_addr set but no fault catcher"); From patchwork Tue Oct 16 23:50:58 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985048 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="W5Yh1kxy"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="qHllMCBV"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHD1r4hzB4MZ for ; Wed, 17 Oct 2018 10:55:04 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=VhGF3EUa8u2MutchjJ+2u+2QGpG0jmaH2SbR2cw7t5s=; b=W5Yh1kxyurtXl3 5V//spD1957l8Ww39p4P8co6sG1Ni2+RqUw0FeeTYtem+rpfLa2Rt0r4Npz2LvSCYq/7++gRkT/bZ fR6JpNd7CVqNnfmWjj48Azg/0Ul5RRT7sxgTjldbkgZo0YVa4eKAgoNv0S7CC0XPZ8oKiBb9wpKmC ACbh2aMg23LeE44/l9c1EBJAtXX3LWSwGPdLW5sU1k8Bvn44wK79JkgqA4iNic9SrsbVndcfK21op bqcBJm82y5/RuY1mmVYytmTWPA29D4yLaukb6b6mKu+WrTJ7q5byrSUny9AmSfUUuoHvKUrlJuROX dbEUVtmjRKO5a57ZXAhA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAq-0004aq-Uf; Tue, 16 Oct 2018 23:54:53 +0000 Received: from mail-io1-xd4a.google.com ([2607:f8b0:4864:20::d4a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAK-00045F-K2 for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:45 +0000 Received: by mail-io1-xd4a.google.com with SMTP id z20-v6so23257998ioh.2 for ; Tue, 16 Oct 2018 16:54:09 -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=hoWMrx1LnWdrk+qi4HZ2nqAB1LeI+S+nnMfo9UFX7P8=; b=qHllMCBVapR6QbawDIW4ljX3xy+P1rMX/HZWlunfI40SxdQWfxfp4QaSvpY8WlQEUn CBYUciJIxL802WdWoxlldqzUBVO85Ktes3pqHSZ0u0qyVgNC5hAOM6c+JTFiyeWCj/Nv nG0F+jLEQ9eJyUFPUFSY0EIwN0QMZEvIhZdpzBRPa2zUYNoKkZtYi/OLF4sK6G5q2UNn JxW58L/tdtY98D0JuIZ2B2fhWEr3pk2nbMobP4TfWFVM7zViqagsajVJ0fyzyOJvxe3F wxAR/ZPPmUPRdkM7eVzGLAkaJjZj4yTFd6qNYTHg+fEC6DGITlNZ3gnsWGeXE2n3+spD YzAQ== 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=hoWMrx1LnWdrk+qi4HZ2nqAB1LeI+S+nnMfo9UFX7P8=; b=JenKXU7kO5oFZrT6C06VznH132EyyswSNLnoZgRv7aCxNTtqsM+E/tgTr3ZJq37XIT YQw2eonEIMt2Z9m3OkAsLxoT838xwKv2ENGfbPgPnlwG4y0d2O23/vB3aawpdiKkcpOW 2uyQClcZihI4ksB/2fyO5NR+rAYNtHE5sCPplLfqfY0QRVl7bf49h34uuq+Tvf03CIBu RVwHlfPLm+vqxqNeA7NQ5hd14VcEuTms0Ag8Sqc6sphCH4A1oJmYzwQBsiALa+bYkOmv JLYCLwqAU/YIxgFjsMQ4ZNKs5mHdV0br8uOYginiptCFuelwwilcGy73xTXua9ozhthH mR6Q== X-Gm-Message-State: ABuFfohAKHBs6R5SsrTKs+jOu2QQApJvBgYxvSF02hSvwoHIPLR7ObFQ CFKlLWXS+5CjkQDkvWPvcQendqko3Dur624Jln6puA== X-Google-Smtp-Source: ACcGV60mfm454NLOeoai/yUu8A9xdqtZ8mTX/de3qVkEXsOYWxKUPokzB8OpFdjQ7ZaC9ZdbLa/DhSGzbJ6jkUtio9l32A== X-Received: by 2002:a05:660c:743:: with SMTP id a3mr191332itl.36.1539734048506; Tue, 16 Oct 2018 16:54:08 -0700 (PDT) Date: Tue, 16 Oct 2018 16:50:58 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-10-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 09/31] kunit: test: added the concept of assertions From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165420_703863_FAA4DB7D X-CRM114-Status: GOOD ( 19.57 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:d4a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Added support for assertions which are like expectations except the test terminates if the assertion is not satisfied. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 272 ++++++++++++++++++++++++++++++++++++- kunit/Makefile | 2 +- kunit/string-stream-test.c | 12 +- kunit/test-test.c | 37 +++++ kunit/test.c | 131 ++++++++++++++++-- 5 files changed, 436 insertions(+), 18 deletions(-) create mode 100644 kunit/test-test.c diff --git a/include/kunit/test.h b/include/kunit/test.h index d652825d7296f..49a9d6e43992c 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -84,9 +84,10 @@ struct test; * @name: the name of the test case. * * A test case is a function with the signature, ``void (*)(struct test *)`` - * that makes expectations (see TEST_EXPECT_TRUE()) about code under test. Each - * test case is associated with a &struct test_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 TEST_EXPECT_TRUE() and + * TEST_ASSERT_TRUE()) about code under test. Each test case is associated with + * a &struct test_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 TEST_CASE() * macro; additionally, every array of test cases should be terminated with an @@ -168,11 +169,14 @@ struct test { const char *name; /* Read only after initialization! */ spinlock_t lock; /* Gaurds all mutable test state. */ bool success; /* Protected by lock. */ + bool death_test; /* Protected by lock. */ struct list_head resources; /* Protected by lock. */ + void (*set_death_test)(struct test *test, bool death_test); void (*vprintk)(const struct test *test, const char *level, struct va_format *vaf); void (*fail)(struct test *test, struct test_stream *stream); + void (*abort)(struct test *test); }; int test_init_test(struct test *test, const char *name); @@ -532,4 +536,266 @@ static inline void test_expect_binary(struct test *test, TEST_EXPECT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \ } while (0) +static inline struct test_stream *test_assert_start(struct test *test, + const char *file, + const char *line) +{ + struct test_stream *stream = test_new_stream(test); + + stream->add(stream, "ASSERTION FAILED at %s:%s\n\t", file, line); + + return stream; +} + +static inline void test_assert_end(struct test *test, + bool success, + struct test_stream *stream) +{ + if (!success) { + test->fail(test, stream); + test->abort(test); + } else { + stream->clear(stream); + } +} + +#define TEST_ASSERT_START(test) \ + test_assert_start(test, __FILE__, __stringify(__LINE__)) + +#define TEST_ASSERT_END(test, success, stream) \ + test_assert_end(test, success, stream) + +#define TEST_ASSERT(test, success, message) do { \ + struct test_stream *__stream = TEST_ASSERT_START(test); \ + \ + __stream->add(__stream, message); \ + TEST_ASSERT_END(test, success, __stream); \ +} while (0) + +#define TEST_ASSERT_FAILURE(test, message) TEST_ASSERT(test, false, message) + +/** + * TEST_ASSERT_TRUE() - Causes an assertion failure when expression is not 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 `TEST_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 TEST_ASSERT_TRUE(test, condition) \ + TEST_ASSERT(test, (condition), \ + "Asserted " #condition " is true, but is false.") + +/** + * TEST_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 TEST_EXPECT_FALSE(), except it causes an assertion failure + * (see TEST_ASSERT_TRUE()) when the assertion is not met. + */ +#define TEST_ASSERT_FALSE(test, condition) \ + TEST_ASSERT(test, !(condition), \ + "Asserted " #condition " is false, but is true.") + +static inline void test_assert_binary(struct test *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 test_stream *stream = test_assert_start(test, file, line); + + stream->add(stream, + "Asserted %s %s %s, but\n", + left_name, compare_name, right_name); + stream->add(stream, "\t\t%s == %lld\n", left_name, left); + stream->add(stream, "\t\t%s == %lld", right_name, right); + + test_assert_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 TEST_ASSERT_BINARY(test, left, condition, right) do { \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + test_assert_binary(test, \ + (long long) __left, #left, \ + (long long) __right, #right, \ + __left condition __right, #condition, \ + __FILE__, __stringify(__LINE__)); \ +} while (0) + +/** + * TEST_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 TEST_EXPECT_EQ(), except it causes an assertion + * failure (see TEST_ASSERT_TRUE()) when the assertion is not met. + */ +#define TEST_ASSERT_EQ(test, left, right) \ + TEST_ASSERT_BINARY(test, left, ==, right) + +/** + * TEST_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 TEST_EXPECT_NE(), except it causes an assertion + * failure (see TEST_ASSERT_TRUE()) when the assertion is not met. + */ +#define TEST_ASSERT_NE(test, left, right) \ + TEST_ASSERT_BINARY(test, left, !=, right) + +/** + * TEST_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 TEST_EXPECT_LT(), except + * it causes an assertion failure (see TEST_ASSERT_TRUE()) when the assertion is + * not met. + */ +#define TEST_ASSERT_LT(test, left, right) \ + TEST_ASSERT_BINARY(test, left, <, right) + +/** + * TEST_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 + * TEST_EXPECT_LE(), except it causes an assertion failure (see + * TEST_ASSERT_TRUE()) when the assertion is not met. + */ +#define TEST_ASSERT_LE(test, left, right) \ + TEST_ASSERT_BINARY(test, left, <=, right) + +/** + * TEST_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 TEST_EXPECT_GT(), except + * it causes an assertion failure (see TEST_ASSERT_TRUE()) when the assertion is + * not met. + */ +#define TEST_ASSERT_GT(test, left, right) \ + TEST_ASSERT_BINARY(test, left, >, right) + +/** + * TEST_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 TEST_EXPECT_GE(), except + * it causes an assertion failure (see TEST_ASSERT_TRUE()) when the assertion is + * not met. + */ +#define TEST_ASSERT_GE(test, left, right) \ + TEST_ASSERT_BINARY(test, left, >=, right) + +/** + * TEST_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 TEST_EXPECT_STREQ(), except it causes an + * assertion failure (see TEST_ASSERT_TRUE()) when the assertion is not met. + */ +#define TEST_ASSERT_STREQ(test, left, right) do { \ + struct test_stream *__stream = TEST_ASSERT_START(test); \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + \ + __stream->add(__stream, "Asserted " #left " == " #right ", but\n"); \ + __stream->add(__stream, "\t\t%s == %s\n", #left, __left); \ + __stream->add(__stream, "\t\t%s == %s\n", #right, __right); \ + \ + TEST_ASSERT_END(test, !strcmp(left, right), __stream); \ +} while (0) + +/** + * TEST_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 + * TEST_EXPECT_NOT_ERR_OR_NULL(), except it causes an assertion failure (see + * TEST_ASSERT_TRUE()) when the assertion is not met. + */ +#define TEST_ASSERT_NOT_ERR_OR_NULL(test, ptr) do { \ + struct test_stream *__stream = TEST_ASSERT_START(test); \ + typeof(ptr) __ptr = (ptr); \ + \ + if (!__ptr) \ + __stream->add(__stream, \ + "Asserted " #ptr " is not null, but is."); \ + if (IS_ERR(__ptr)) \ + __stream->add(__stream, \ + "Asserted " #ptr " is not error, but is: %ld", \ + PTR_ERR(__ptr)); \ + \ + TEST_ASSERT_END(test, !IS_ERR_OR_NULL(__ptr), __stream); \ +} while (0) + +/** + * TEST_ASSERT_SIGSEGV() - An assertion that @expr will cause a segfault. + * @test: The test context object. + * @expr: an arbitrary block of code. + * + * Sets an assertion that @expr, when evaluated, will cause a segfault. + * Currently this assertion is only really useful for testing the KUnit + * framework, as a segmentation fault in normal kernel code is always incorrect. + * However, the plan is to replace this assertion with an arbitrary death + * assertion similar to + * https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#death-tests + * which will probably be massaged to make sense in the context of the kernel + * (maybe assert that a panic occurred, or that BUG() was called). + * + * NOTE: no code after this assertion will ever be executed. + */ +#define TEST_ASSERT_SIGSEGV(test, expr) do { \ + test->set_death_test(test, true); \ + expr; \ + test->set_death_test(test, false); \ + TEST_ASSERT_FAILURE(test, \ + "Asserted that " #expr " would cause death, but did not.");\ +} while (0) + #endif /* _KUNIT_TEST_H */ diff --git a/kunit/Makefile b/kunit/Makefile index 319eb9dc8be0e..2f1c069e165cb 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_KUNIT) += test.o string-stream.o test-stream.o -obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o +obj-$(CONFIG_KUNIT_TEST) += test-test.o string-stream-test.o obj-$(CONFIG_EXAMPLE_TEST) += example-test.o diff --git a/kunit/string-stream-test.c b/kunit/string-stream-test.c index 07c626cbfffbf..5947fada67d96 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 test *test) stream->add(stream, " %s", "bar"); output = stream->get_string(stream); - TEST_EXPECT_STREQ(test, output, "Foo bar"); + TEST_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 test *test) stream->add(stream, "A"); output = stream->get_string(stream); - TEST_EXPECT_STREQ(test, output, "AAAAAAAAAA"); - TEST_EXPECT_EQ(test, stream->length, 10); - TEST_EXPECT_FALSE(test, stream->is_empty(stream)); + TEST_ASSERT_STREQ(test, output, "AAAAAAAAAA"); + TEST_ASSERT_EQ(test, stream->length, 10); + TEST_ASSERT_FALSE(test, stream->is_empty(stream)); kfree(output); stream->clear(stream); output = stream->get_string(stream); - TEST_EXPECT_STREQ(test, output, ""); - TEST_EXPECT_TRUE(test, stream->is_empty(stream)); + TEST_ASSERT_STREQ(test, output, ""); + TEST_ASSERT_TRUE(test, stream->is_empty(stream)); destroy_string_stream(stream); } diff --git a/kunit/test-test.c b/kunit/test-test.c new file mode 100644 index 0000000000000..fd4b90208f0c3 --- /dev/null +++ b/kunit/test-test.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit test for core test infrastructure. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ +#include + +static void test_test_catches_segfault(struct test *test) +{ + void (*invalid_func)(void) = (void (*)(void)) SIZE_MAX; + + TEST_ASSERT_SIGSEGV(test, invalid_func()); +} + +static int test_test_init(struct test *test) +{ + return 0; +} + +static void test_test_exit(struct test *test) +{ +} + +static struct test_case test_test_cases[] = { + TEST_CASE(test_test_catches_segfault), + {}, +}; + +static struct test_module test_test_module = { + .name = "test-test", + .init = test_test_init, + .exit = test_test_exit, + .test_cases = test_test_cases, +}; +module_test(test_test_module); diff --git a/kunit/test.c b/kunit/test.c index f798183533c8d..f89cfaaf5eb79 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -32,6 +32,27 @@ static void test_set_success(struct test *test, bool success) spin_unlock_irqrestore(&test->lock, flags); } +static bool test_get_death_test(struct test *test) +{ + unsigned long flags; + bool death_test; + + spin_lock_irqsave(&test->lock, flags); + death_test = test->death_test; + spin_unlock_irqrestore(&test->lock, flags); + + return death_test; +} + +static void test_set_death_test(struct test *test, bool death_test) +{ + unsigned long flags; + + spin_lock_irqsave(&test->lock, flags); + test->death_test = death_test; + spin_unlock_irqrestore(&test->lock, flags); +} + static int test_vprintk_emit(const struct test *test, int level, const char *fmt, @@ -70,13 +91,34 @@ static void test_fail(struct test *test, struct test_stream *stream) stream->commit(stream); } +static void __noreturn test_abort(struct test *test) +{ + test_set_death_test(test, true); + if (current->thread.fault_catcher && current->thread.is_running_test) + UML_LONGJMP(current->thread.fault_catcher, 1); + + /* + * Attempted to abort from a not properly initialized test context. + */ + test_err(test, + "Attempted to abort from a not properly initialized test context!"); + if (!current->thread.fault_catcher) + test_err(test, "No fault_catcher present!"); + if (!current->thread.is_running_test) + test_err(test, "is_running_test not set!"); + show_stack(NULL, NULL); + BUG(); +} + int test_init_test(struct test *test, const char *name) { spin_lock_init(&test->lock); INIT_LIST_HEAD(&test->resources); test->name = name; + test->set_death_test = test_set_death_test; test->vprintk = test_vprintk; test->fail = test_fail; + test->abort = test_abort; return 0; } @@ -122,16 +164,89 @@ static void test_run_case_cleanup(struct test *test, } /* - * Performs all logic to run a test case. + * Handles an unexpected crash in a test case. */ -static bool test_run_case(struct test *test, - struct test_module *module, - struct test_case *test_case) +static void test_handle_test_crash(struct test *test, + struct test_module *module, + struct test_case *test_case) { - test_set_success(test, true); + test_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); + + test_case_internal_cleanup(test); +} - test_run_case_internal(test, module, test_case); - test_run_case_cleanup(test, module, test_case); +/* + * Performs all logic to run a test case. It also catches most errors that + * occurs in a test case and reports them as failures. + * + * XXX: THIS DOES NOT FOLLOW NORMAL CONTROL FLOW. READ CAREFULLY!!! + */ +static bool test_run_case_catch_errors(struct test *test, + struct test_module *module, + struct test_case *test_case) +{ + jmp_buf fault_catcher; + int faulted; + + test_set_success(test, true); + test_set_death_test(test, false); + + /* + * Tell the trap subsystem that we want to catch any segfaults that + * occur. + */ + current->thread.is_running_test = true; + current->thread.fault_catcher = &fault_catcher; + + /* + * ENTER HANDLER: If a failure occurs, we enter here. + */ + faulted = UML_SETJMP(&fault_catcher); + if (faulted == 0) { + /* + * NORMAL CASE: we have not run test_run_case_internal yet. + * + * test_run_case_internal may encounter a fatal error; if it + * does, we will jump to ENTER_HANDLER above instead of + * continuing normal control flow. + */ + test_run_case_internal(test, module, test_case); + /* + * This line may never be reached. + */ + test_run_case_cleanup(test, module, test_case); + } else if (test_get_death_test(test)) { + /* + * EXPECTED DEATH: test_run_case_internal encountered + * anticipated fatal error. Everything should be in a safe + * state. + */ + test_run_case_cleanup(test, module, test_case); + } else { + /* + * UNEXPECTED DEATH: test_run_case_internal encountered an + * unanticipated fatal error. We have no idea what the state of + * the test case is in. + */ + test_handle_test_crash(test, module, test_case); + test_set_success(test, false); + } + /* + * EXIT HANDLER: test case has been run and all possible errors have + * been handled. + */ + + /* + * Tell the trap subsystem that we no longer want to catch any + * segfaults. + */ + current->thread.fault_catcher = NULL; + current->thread.is_running_test = false; return test_get_success(test); } @@ -148,7 +263,7 @@ int test_run_tests(struct test_module *module) return ret; for (test_case = module->test_cases; test_case->run_case; test_case++) { - success = test_run_case(&test, module, test_case); + success = test_run_case_catch_errors(&test, module, test_case); if (!success) all_passed = false; From patchwork Tue Oct 16 23:50:59 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985045 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="ryX64oEX"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="Yx0djIaN"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXGy0MmMz9sDn for ; Wed, 17 Oct 2018 10:54:50 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=818AkaDtEd1+KD37prOREExmwODFPIz3TXTJkSnkeJo=; b=ryX64oEXVFu6+B 2wWJYt339BY4Gbj+gxyCUx/L11xoIIqAlpwT8WtgSEa5+ixQVS6refMcaDqfkq6d/FVN+PaIxHR0K BwdB97jD4vEd7GrAqAJXSBDM/84FhculSi9CPGv/bc8vWXXVdTUbEAtWtuvZwT0LkzlRhPRAh5MPZ sCLaTGeN9pozxJd9e2wNnHdswMi+oMYxM37GrEylMGwnvmXrJIZvU4sp4PbIjQO7/HZZxUA8J3v9t Md66nrotepk1cdlWjSH0xPPKuRUqd+nA5zt5uyDcDE9tZCkJGU0lAPNQeXkMlKnlaaLx5KesXieOe 0EHIzCEy8ttJmk4nlS8w==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAa-0004QN-MR; Tue, 16 Oct 2018 23:54:36 +0000 Received: from mail-qt1-x849.google.com ([2607:f8b0:4864:20::849]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAM-000487-Ll for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:26 +0000 Received: by mail-qt1-x849.google.com with SMTP id f20-v6so26497660qta.16 for ; Tue, 16 Oct 2018 16:54: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=d9RWd340hsvL8f+86Hq8ZYiuwlmFywi6LURyY+fbx2M=; b=Yx0djIaN7qGsKbKVWcYTEqjXtuCt5mJzBG+llgMyRaDxHH9Nk2+9u04slBVXvfc3wS WWJHLZ4Cy0qql1s9KGSUtvIF+PX0aE7H32+1XHTQwloMetji6MM1GrYxeoWsHDNEyHme 26sRpezs791PHz4VTxdpWVRBV4zsZkpnI+T4tVqgNpfWT3JVKRx9VCbofy90SGD86rNZ CVw0KoKkl9sSfkCpgdn4xhVAM4qWEBIHZA6zZhx9yOE/i8xtzOj5GcVO+gq7diSY3OG3 3aJXOMLC0d6eKbLtdgSbwRXBJ4WRK0n7XsIsJ8o1UnMToMe3XRTQjSJFXuGQOvz9UjCu IUHg== 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=d9RWd340hsvL8f+86Hq8ZYiuwlmFywi6LURyY+fbx2M=; b=qGSWddmSXsdu5/kNTcBOGKo/f30ON74f1w78FyFAYVyWp0xQ/bg60DYbCVrfBBRAH/ OCcYjvgTWNkuIZg+8RtCajlscajJ9k0SjChQ8wrp4qWbTch1TGgMozhGMqZsGoYNAw50 GHxMSGE4xvvTWHzfJTNS/CLMDX1FZrfFlaT4xaidggPNMn0jPHeQ6fJipslYAUdnGVni yihPJfBnoO7QJDHjVZySk+xhW/z2JuVmvWtCTFno6K22wMXPn+5Kbp4t499aeL7x0owD mrfEUTSC4HNrBBk+z4ox1v5zy70AENztL2u6+SRuKAE9w43hUDsMpI7Kz0f/49A+eSjM B6tw== X-Gm-Message-State: ABuFfogek+XKtK2LvjrtCw+S5UZjYFD6g4zww+juc3VHg7Ou5bOcX8uk bqrzwZWimf7W53SJUnlx+x7GxxdkUAeiusTsEXd1ew== X-Google-Smtp-Source: ACcGV60dfjopFdXI57rHANuj2fTuNIbfr0UGtI22ZYmJqsrCuYMIb093FrDiq5/oUqMydOfiaVQ+Y58DAxIqLJV1QYBy9g== X-Received: by 2002:ac8:8d4:: with SMTP id y20-v6mr20143675qth.9.1539734050869; Tue, 16 Oct 2018 16:54:10 -0700 (PDT) Date: Tue, 16 Oct 2018 16:50:59 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-11-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 10/31] kunit: test: added concept of initcalls From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165422_755206_9258C2A3 X-CRM114-Status: GOOD ( 10.02 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:849 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Added a way to add plugins that require a test module to be loaded during initialization. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 19 +++++++++++++++++++ kunit/test.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/include/kunit/test.h b/include/kunit/test.h index 49a9d6e43992c..58dbe2aee423f 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -152,6 +152,12 @@ struct test_module { struct test_case *test_cases; }; +struct test_initcall { + struct list_head node; + int (*init)(struct test_initcall *this, struct test *test); + void (*exit)(struct test_initcall *this); +}; + /** * struct test - represents a running instance of a test. * @priv: for user to store arbitrary data. Commonly used to pass data created @@ -183,6 +189,19 @@ int test_init_test(struct test *test, const char *name); int test_run_tests(struct test_module *module); +void test_install_initcall(struct test_initcall *initcall); + +#define test_pure_initcall(fn) postcore_initcall(fn) + +#define test_register_initcall(initcall) \ + static int register_test_initcall_##initcall(void) \ + { \ + test_install_initcall(&initcall); \ + \ + return 0; \ + } \ + test_pure_initcall(register_test_initcall_##initcall) + /** * module_test() - used to register a &struct test_module with KUnit. * @module: a statically allocated &struct test_module. diff --git a/kunit/test.c b/kunit/test.c index f89cfaaf5eb79..9737465fb0568 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -53,6 +53,19 @@ static void test_set_death_test(struct test *test, bool death_test) spin_unlock_irqrestore(&test->lock, flags); } +struct test_global_context { + struct list_head initcalls; +}; + +static struct test_global_context test_global_context = { + .initcalls = LIST_HEAD_INIT(test_global_context.initcalls), +}; + +void test_install_initcall(struct test_initcall *initcall) +{ + list_add_tail(&initcall->node, &test_global_context.initcalls); +} + static int test_vprintk_emit(const struct test *test, int level, const char *fmt, @@ -130,8 +143,18 @@ static void test_run_case_internal(struct test *test, struct test_module *module, struct test_case *test_case) { + struct test_initcall *initcall; int ret; + list_for_each_entry(initcall, &test_global_context.initcalls, node) { + ret = initcall->init(initcall, test); + if (ret) { + test_err(test, "failed to initialize: %d", ret); + test->success = false; + return; + } + } + if (module->init) { ret = module->init(test); if (ret) { @@ -146,6 +169,12 @@ static void test_run_case_internal(struct test *test, static void test_case_internal_cleanup(struct test *test) { + struct test_initcall *initcall; + + list_for_each_entry(initcall, &test_global_context.initcalls, node) { + initcall->exit(initcall); + } + test_cleanup(test); } From patchwork Tue Oct 16 23:51:00 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985046 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="WHK8L/K2"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="ubrWuUTt"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXH23YJ2zB2xk for ; Wed, 17 Oct 2018 10:54:54 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=w7ZF3y7Zf7B4OW/TZlKDXVYFtcK51kt+EAa1uRR5J9c=; b=WHK8L/K2ddpvx/ AhqVws5PKrsZ1eylJsRY4B0VHlrswikyqeJ0cnhibgFN740VVwgnHXflzlcEljryV46HyVBDtodaz FmHzZ7g0WJ0GbXFOcZFzcxRlpadqteyMqbu/RS29zjxL6U3AvUJnYTw71QbEiOiSWSBPm8dxYu0qj nTzi6jbCUIpldYVvSIJYKcdnCuIeLfxSTEw99ziH/dlI/a+z3/7hgJtsF/38HeSWJov0mSfuEo5o/ AP68ocJZtXxpM8LYoLamZSILBWqALaoEQLlTh6mi9qAOUa6ovAnk0lZabsiLjjdJGkluQ3+cPRSkY 3455XCJS6DGdaE9lg1tg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAh-0004Tp-8L; Tue, 16 Oct 2018 23:54:43 +0000 Received: from mail-qk1-x749.google.com ([2607:f8b0:4864:20::749]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAP-0004AR-Rb for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:30 +0000 Received: by mail-qk1-x749.google.com with SMTP id r77-v6so25979704qke.3 for ; Tue, 16 Oct 2018 16:54:14 -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=qCMB3fmL8RW2FIsxtQnM0B4a1KuW981wvpeU2rLEEcQ=; b=ubrWuUTt+zLu0Ttoi9FT1FxFTtzIg0tY/UoZu25xt9iDVBIuAa9bDH/YNOArK3efUq Ul+yBUWzSlLCAT1S2u8XiehSp1PdWTJJgxl/W2+BSa6BBURSNMpxJJ3+jcj73YySSq1I 5+C3SrLZ8lWF+pfw0Dqkgw+aD8nn9wubggiCTnkiQhcavMEAkkq6mNZ6hs4m1WH8cIts x/tyRHU1drdYAW5nWdlBPtOTVvJfZ2Cy1A4Kd9pccZKJol8DTRo6BQx6W6jpWK0IQ27R 7bZVu4RyDxk4k/UdGU2gLJW7gB7IbvC/bkRNJ8BSBe1YIgxt56QkuKZOhR9K7kCzfrFL eWfg== 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=qCMB3fmL8RW2FIsxtQnM0B4a1KuW981wvpeU2rLEEcQ=; b=sEtg+dDWBXowYRByqHzCausCFe59vTqktY9g4LpntreMy8Vmho0DIqhNGOCqlO2YvU 98+Z/rKsjZ/a5mBNXZiPfLxaXmCxc9oddXPVbJbK2xKbuQbUCAgosy/US5GJrk6ZeMXp kDZnKweVAhOy+TUMCq4Z8kNOZbktp+cPUFhhA8jyBC27L/WBS9hCAsdy3Z28Tjwp0iCI r6czdRj9Jl3p/icazISPtr0K1GbUk9jUYJpUflEuu0SK46WAu9YHXzEBcc49amWM55L7 8crfQqynJFBJbOLLIHRrfMALNT1uQNopgWvqjaAuhOxTZxuuBY96eJOCfrkWYGjljGDN XB7Q== X-Gm-Message-State: ABuFfojl0aTz9g2M7K98tt9ZNTyli8ikFYH5TLVTSrR9FBnqci3QgItl xHj8OBZ7Z195j+1O1UIWxY/O+cBlsMmsnQZ6jH6svg== X-Google-Smtp-Source: ACcGV638XMOy/Ffu7bY3flfOsNB7BxsnbCcBa1BVNU0gynFQlCvza9x/sIIIJIGOqlAphgLA6gtXViWigbl/A6krJCynaA== X-Received: by 2002:a0c:8a17:: with SMTP id 23mr19940884qvt.49.1539734053222; Tue, 16 Oct 2018 16:54:13 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:00 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-12-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 11/31] kunit: test: added concept of post conditions From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165425_933956_C2CAF17C X-CRM114-Status: UNSURE ( 9.49 ) X-CRM114-Notice: Please train this message. X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:749 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Adds a way to specify that certain conditions must be met at the end of a test case. Signed-off-by: Brendan Higgins --- include/kunit/test.h | 6 ++++++ kunit/test.c | 13 ++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/kunit/test.h b/include/kunit/test.h index 58dbe2aee423f..be2b11117d1de 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -158,6 +158,11 @@ struct test_initcall { void (*exit)(struct test_initcall *this); }; +struct test_post_condition { + struct list_head node; + void (*validate)(struct test_post_condition *condition); +}; + /** * struct test - represents a running instance of a test. * @priv: for user to store arbitrary data. Commonly used to pass data created @@ -177,6 +182,7 @@ struct test { bool success; /* Protected by lock. */ bool death_test; /* Protected by lock. */ struct list_head resources; /* Protected by lock. */ + struct list_head post_conditions; void (*set_death_test)(struct test *test, bool death_test); void (*vprintk)(const struct test *test, const char *level, diff --git a/kunit/test.c b/kunit/test.c index 9737465fb0568..6ea60059b4918 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -125,8 +125,9 @@ static void __noreturn test_abort(struct test *test) int test_init_test(struct test *test, const char *name) { - spin_lock_init(&test->lock); INIT_LIST_HEAD(&test->resources); + INIT_LIST_HEAD(&test->post_conditions); + spin_lock_init(&test->lock); test->name = name; test->set_death_test = test_set_death_test; test->vprintk = test_vprintk; @@ -186,6 +187,16 @@ static void test_run_case_cleanup(struct test *test, struct test_module *module, struct test_case *test_case) { + struct test_post_condition *condition, *condition_safe; + + list_for_each_entry_safe(condition, + condition_safe, + &test->post_conditions, + node) { + condition->validate(condition); + list_del(&condition->node); + } + if (module->exit) module->exit(test); From patchwork Tue Oct 16 23:51:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985047 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="rl0PTKoR"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="iP/y8MyF"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXH25nH8z9sCf for ; Wed, 17 Oct 2018 10:54:54 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=6nUHlqZyy0KbxOsJMuOoxBEVBvxRYbecB3GM7TdUS8g=; b=rl0PTKoRPLRRGE N2knvkcZI3sH/bBNBL5iPAu2ZIedX8PXo4a2mW/t3YM4eObBTbfY/bhHXj/F0ipNi/gMFAOC8GxVL W2uPfjHuvIZgciKcxGoZ/29xyz3/+wfOcOEYI59u9VYqUNOMPTG/L+GW2xDMrKvWG85MgDObSLN7S ubQjypFv2QPQtcggHF6+gNChJcHhT1m4pmxcN41Bj7ypA5OjkRER5jxHgYDa4c4BArWPZHgT5i9nU 2jY1uq0oxhHOX9/uk83KhBg1AP1OuN7RRvizYeILsKFfGqnIC4AnLe+TvcUccLsvcVHJV5Zq3FRWm F9QyK1jqJdJ+6ysZUL2w==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAj-0004W3-II; Tue, 16 Oct 2018 23:54:45 +0000 Received: from mail-it1-x14a.google.com ([2607:f8b0:4864:20::14a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAR-0004Cq-5P for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:31 +0000 Received: by mail-it1-x14a.google.com with SMTP id h20-v6so243866ita.0 for ; Tue, 16 Oct 2018 16:54:16 -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=aOdwwUuIBSYrb87g8bgBF5GxaXD8rfXvHJcd9fKQrvE=; b=iP/y8MyFsGmeSdqTp8obc/qxmgmmwf9LAObDYZtUylHVznP5XcuFzkpY8gv4WaAD0e L+btQwyLCtTxGnIse6dnkKY/36GGaBW9paJBnJ8t56dRi0Lza3SCKJ0ggJByY4ncyPJn bYFHjWGLxBoCZRo5cMXb+2ZiYOeivU7vz9Pzjh+I6QvydFhrsnz+FLlbSbSA20eL6ACz ROH0FnhfrLrPthtqwyxiU9xPDTs5vueFT2vNO6Wqqa/QVfkyI9LYXMrefxr0Xj+g/qkF vIqdH11a+uPoNd3j3DCR5mIPtRBQiSuHdaHYL8naDULXhd1L9Ljrd1ZDV/NstzLC/IXn UdGw== 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=aOdwwUuIBSYrb87g8bgBF5GxaXD8rfXvHJcd9fKQrvE=; b=YYLTvtvYs11CdFhaXhjGNSomAi7tRM1c9PqFveiM431p51vmXMJF6ftlgf6bqyXMAk paPKpeG/w9kfETN5zw6RiVYKdknPOeOtS0xvPKAZxA8VvIzzLen4IRi1A/oYkepQnrc6 RRYOwazVYmUenUnfcNVcRrpI6le/Yx0qXRyskJIusZIkWrFtV7duwfFZmUI4lHFACPs5 qnqtatZvhy71AMu79IvoWNsTDNOtBNrD55+BUzcyQY5J4CFY3PHVQSWwT4EVwhGuo1LV yfbd1zQVE9q7XXyOFX+WEqxVOFBC6LTIs46tNRqlgLK6mczv9ydddujGf6Y9J6XBTc19 hq9w== X-Gm-Message-State: ABuFfoiAsYwi/yX9iZvYpCXczBGL5HCUjbvuRZuO4jAbW3rzFO3x2Olm cZxOxuCM7MruYUXAIP0q+aPwf+bR7Xa3sY0X8XWRZQ== X-Google-Smtp-Source: ACcGV6200mvffJhOSAeesbf9j6TKPFpbmQQCP6h6Z/FAjTh6tgj7iDw357BsNkJb8O++l+p6080vLDr9Q7wrZbFtKCQf3Q== X-Received: by 2002:a24:7f05:: with SMTP id r5-v6mr17838180itc.2.1539734056061; Tue, 16 Oct 2018 16:54:16 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:01 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-13-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 12/31] checkpatch: added support for struct MOCK(foo) syntax From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165427_233040_B5EB3717 X-CRM114-Status: UNSURE ( 6.98 ) X-CRM114-Notice: Please train this message. X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:14a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org This adds struct MOCK(foo) as a NonptrType so that it is recognized correctly in declarations. Signed-off-by: Brendan Higgins --- scripts/checkpatch.pl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 447857ffaf6be..9806f190796de 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -745,6 +745,10 @@ sub build_types { (?: (?:typeof|__typeof__)\s*\([^\)]*\)| (?:$typeTypedefs\b)| + # Matching a \b breaks struct MOCK(foo) syntax, + # so we need to have it not lumped in with the + # types in @typeList. + (?:struct\s+MOCK\($Ident\))| (?:${all}\b) ) (?:\s+$Modifier|\s+const)* From patchwork Tue Oct 16 23:51:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985050 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="TRBTHznp"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="HUzm2Rzp"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHM3NpGzB4Md for ; Wed, 17 Oct 2018 10:55:11 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=+mi/3Wq5Wz6KlYZ8UfYvabLMqmFrPdkychu/FptlWZM=; b=TRBTHznp2BH8Ch wGsLZ5mTjYQUW95n2FiNifBI2jKYpESKVwARE0p1AheAKQ3f1PIlq8o6RKS5mnMt5T4Gh1jDz1a6M uosjktSZDdwcn9ifDmVURwMRPAuydDoGROA1O4p30BRj8mmZpF2P85aWKLnUUFFUQ+kgfmh7h4aqX X91M27l6a2NVlwoqlhnSYJGH2fNRkwgzT3anvRXqUpKxjvCJJScnVAemg3FoxU0OvW/cjaslrHIVA ub/B65zy1Gr5B7ZgnkQAYNBqcpwBSZTmR986QX04dT6ADd3Ad2T26ZN59sZJG2CLP3vn7KvkAL216 cXyvXxvd2cc+wFsKMpVA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAx-0004dX-Q4; Tue, 16 Oct 2018 23:54:59 +0000 Received: from mail-pg1-x549.google.com ([2607:f8b0:4864:20::549]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAV-0004F9-BW for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:54 +0000 Received: by mail-pg1-x549.google.com with SMTP id m4-v6so18624147pgv.15 for ; Tue, 16 Oct 2018 16:54:19 -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=Ji1t0J8eGmzbl7MSzcr9XutVhXSRB1rerFRgUj/ivO8=; b=HUzm2RzpAf7KqyFVBvVTe4BrOZbQ0+Amgf4b325MRKc69JUHL0InN6yTxbKrDPXae9 GrJ49aePoJpXuAWZzrm0bMlPZ3nu7a30ZIIEFQfLF9tnJ8BiPMoAWKkTszzXfvnWw6yZ Jnx10o+coULr4DUsEYbRN9xuBYwcNB05bM+IQBGYIJcClrTrkfTXzibvxalBmDNokaSk OZ5S+j3QprSngUeIyKL8RhESjw2zJ0MNkrcPJtSkWUTtQLCCfjWvJrA830tS5nMelJOW 1+aWEPAjABYCIrbz+J1Wv+n52G3IU44cWeXtmAbpxjCcJwHJswuvYsHM04CB8SN0iGcD D9bQ== 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=Ji1t0J8eGmzbl7MSzcr9XutVhXSRB1rerFRgUj/ivO8=; b=Ug3STOw938Z7xyjrlD+5+w+IvwTtUQ48ZT3hEbjgJ0EB/iMFdbT5xLAOp90QOzKUju tjtzf2FJYvbcJJpQckzvqGWyNZuTP/dTGuFjoLUF3qQpnuOfRFOV+AfOi0wXVvelteuT 0CyFBrBaN5LKc7SYQ17s+LAkJhlaqzUwL6Iq6Yb67EXy6adISE5xRxKocObTwv9XJ9Pt 6DUWKu1YMzTGrO5DC3jcdFhuwJME1aIf+g7EzMdHIylxDA8TqOg2jPyL0HNHGynKfgzI 0I3xi+A+ekAFllKv441ctqkDKEirM9pedAlEUZMmNPFLXGczzmjuGCDKLWkeMiKa2ao5 gHxw== X-Gm-Message-State: ABuFfojjsN49/+vtu+PdL8YdXS6GUv9vNbEUPDEqemsNHsbKc72usRg8 8DLlFIXPtxYi1ujHcLjh268oXmxofsae5pikTIVJ+A== X-Google-Smtp-Source: ACcGV61O5ZEPCWXnN8I0E+VNPClekZmLZJGbUygjOMJ+Av+8wDP8THabU2aj1USBxJfOKwZfSYWL2aUGXLcnSyvUpkoVzA== X-Received: by 2002:a65:60cc:: with SMTP id r12-v6mr11893684pgv.78.1539734058465; Tue, 16 Oct 2018 16:54:18 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:02 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-14-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 13/31] kunit: mock: added parameter list minipulation macros From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165431_449901_22C42C53 X-CRM114-Status: GOOD ( 15.79 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:549 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Adds macros for parsing and manipulating parameter lists needed for generating mocks. Signed-off-by: Brendan Higgins --- include/kunit/params.h | 305 ++++++++++++++++++++++++++++++++++++++++ kunit/Makefile | 2 +- kunit/mock-macro-test.c | 149 ++++++++++++++++++++ 3 files changed, 455 insertions(+), 1 deletion(-) create mode 100644 include/kunit/params.h create mode 100644 kunit/mock-macro-test.c diff --git a/include/kunit/params.h b/include/kunit/params.h new file mode 100644 index 0000000000000..f9692d3cd703f --- /dev/null +++ b/include/kunit/params.h @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Macros for parsing and manipulating parameter lists needed for generating + * mocks. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _KUNIT_PARAMS_H +#define _KUNIT_PARAMS_H + +#define NUM_VA_ARGS_IMPL(__dummy, \ + __1, \ + __2, \ + __3, \ + __4, \ + __5, \ + __6, \ + __7, \ + __8, \ + __9, \ + __10, \ + __11, \ + __12, \ + __13, \ + __14, \ + __15, \ + __16, \ + __nargs, args...) __nargs + +#define NUM_VA_ARGS(args...) NUM_VA_ARGS_IMPL(__dummy, ##args, \ + 16, \ + 15, \ + 14, \ + 13, \ + 12, \ + 11, \ + 10, \ + 9, \ + 8, \ + 7, \ + 6, \ + 5, \ + 4, \ + 3, \ + 2, \ + 1, \ + 0) + +#define CONCAT_INTERNAL(left, right) left##right +#define CONCAT(left, right) CONCAT_INTERNAL(left, right) + +#define EMPTY() + +/* + * Takes the name of a function style macro such as FOO() and prevents it from + * being evaluated on the current pass. + * + * This is useful when you need to write a "recursive" macro since a macro name + * becomes painted after it is pasted. If a different macro is pasted, this + * different macro won't be pasted; if we then defer the evaluation of the this + * "indirection macro", we can prevent the original definition from getting + * painted. + * + * Example: + * #define EXAMPLE EXPAND(FOO()) // FOO() is evaluated on 1st pass. + * #define EXAMPLE EXPAND(DEFER(FOO)()) // FOO() is evaluated on the second + * // pass. + */ +#define DEFER(macro_id) macro_id EMPTY() + +/* + * Takes the name of a function style macro such as FOO() and prevents it from + * being evaluated on the current or following pass. + * + * This is useful when you need to DEFER inside an operation which causes an + * extra pass, like IF. + * + * Example: + * #define EXAMPLE EXPAND(FOO()) // FOO() is evaluated on 1st pass. + * #define EXAMPLE EXPAND(DEFER(FOO)()) // FOO() is evaluated on the second + * // pass. + * #define EXAMPLE EXPAND(OBSTRUCT(FOO)()) // FOO() is evaluated on the third + * // pass. + */ +#define OBSTRUCT(macro_id) macro_id DEFER(EMPTY)() + +#define EXPAND_1(args...) args +#define EXPAND_2(args...) EXPAND_1(EXPAND_1(args)) +#define EXPAND_4(args...) EXPAND_2(EXPAND_2(args)) +#define EXPAND_8(args...) EXPAND_4(EXPAND_4(args)) +#define EXPAND_16(args...) EXPAND_8(EXPAND_8(args)) + +/* + * Causes multiple evaluation passes of a macro. + * + * CPP is implemented as a push down automaton. It consumes a stream of tokens + * and as it comes across macros, it either evaluates them and pastes the + * result, or if the macro is a function macro, it pushes the macro to a stack, + * it evaluates the input to the function macro, pops the state from the stack + * and continues. + * + * This macro serves the function of making the cursor return to the beginging + * of a macro that requires mulitple passes to evaluate. It is most useful when + * used with DEFER(...) and OBSTRUCT(...). + */ +#define EXPAND(args...) EXPAND_16(args) + +#define INC(id) INC_##id +#define INC_0 1 +#define INC_1 2 +#define INC_2 3 +#define INC_3 4 +#define INC_4 5 +#define INC_5 6 +#define INC_6 7 +#define INC_7 8 +#define INC_8 9 +#define INC_9 10 +#define INC_10 11 +#define INC_11 12 +#define INC_12 13 +#define INC_13 14 +#define INC_14 15 +#define INC_15 16 +#define INC_16 17 + +#define DEC(id) DEC_##id +#define DEC_1 0 +#define DEC_2 1 +#define DEC_3 2 +#define DEC_4 3 +#define DEC_5 4 +#define DEC_6 5 +#define DEC_7 6 +#define DEC_8 7 +#define DEC_9 8 +#define DEC_10 9 +#define DEC_11 10 +#define DEC_12 11 +#define DEC_13 12 +#define DEC_14 13 +#define DEC_15 14 +#define DEC_16 15 + +#define DROP_FIRST_ARG_INTERNAL(dropped, x, args...) x +#define DROP_FIRST_ARG(args...) DROP_FIRST_ARG_INTERNAL(args) + +#define EQUAL(left, right) EQUAL_##left##_##right +#define EQUAL_0_0 dropped, 1 +#define EQUAL_1_1 dropped, 1 +#define EQUAL_2_2 dropped, 1 +#define EQUAL_3_3 dropped, 1 +#define EQUAL_4_4 dropped, 1 +#define EQUAL_5_5 dropped, 1 +#define EQUAL_6_6 dropped, 1 +#define EQUAL_7_7 dropped, 1 +#define EQUAL_8_8 dropped, 1 +#define EQUAL_9_9 dropped, 1 +#define EQUAL_10_10 dropped, 1 +#define EQUAL_11_11 dropped, 1 +#define EQUAL_12_12 dropped, 1 +#define EQUAL_13_13 dropped, 1 +#define EQUAL_14_14 dropped, 1 +#define EQUAL_15_15 dropped, 1 +#define EQUAL_16_16 dropped, 1 + +#define IS_EQUAL(left, right) DROP_FIRST_ARG(EQUAL(left, right), 0) + +#define NOT_INTERNAL(condition) NOT_##condition +#define NOT(condition) NOT_INTERNAL(condition) +#define NOT_0 1 +#define NOT_1 0 + +#define IS_NOT_EQUAL(left, right) NOT(IS_EQUAL(left, right)) + +#define EMPTY_IMPL(tokens) CONCAT(EMPTY_, tokens) +#define IS_EMPTY(tokens) + +#define OR_INTERNAL(left, right) OR_##left##_##right +#define OR(left, right) OR_INTERNAL(left, right) +#define OR_0_0 0 +#define OR_0_1 1 +#define OR_1_0 1 +#define OR_1_1 1 + +#define IF(condition) CONCAT(IF_, condition) +#define IF_0(body) +#define IF_1(body) body + +#define COMMA() , + +#define APPLY_TOKENS_INTERNAL(tokens, yield_token, seen_token) \ + IF(yield_token)(IF(seen_token)(COMMA()) tokens) +#define APPLY_TOKENS(tokens, yield_token, seen_token) \ + APPLY_TOKENS_INTERNAL(tokens, yield_token, seen_token) + +/* + * Provides the indirection to keep the PARAM_LIST_RECURSE_INTERNAL from getting + * pasted, only useful if used with DEFER(...) or OBSTRUCT(...). + */ +#define PARAM_LIST_RECURSE_INDIRECT() PARAM_LIST_RECURSE_INTERNAL + +/* + * Given a starting index, a number of args, a MACRO to apply, and a list of + * types (with at least one element) this will call MACRO with the first type in + * the list and index; it will then call itself again on all remaining types, if + * any, while incrementing index, and decrementing nargs. + * + * Assumes nargs is the number of types in the list. + */ +#define PARAM_LIST_RECURSE_INTERNAL(index, \ + nargs, \ + MACRO, \ + FILTER, \ + context, \ + seen_token, \ + type, \ + args...) \ + APPLY_TOKENS(MACRO(context, type, index), \ + FILTER(context, type, index), \ + seen_token) \ + IF(IS_NOT_EQUAL(nargs, 1)) \ + (OBSTRUCT(PARAM_LIST_RECURSE_INDIRECT)() \ + (INC(index), DEC(nargs), \ + MACRO, FILTER, context, \ + OR(seen_token, FILTER(context, type, index)), \ + args)) + +#define PARAM_LIST_RECURSE(index, nargs, MACRO, FILTER, context, args...) \ + IF(IS_NOT_EQUAL(nargs, 0)) \ + (OBSTRUCT(PARAM_LIST_RECURSE_INTERNAL)(index, \ + nargs, \ + MACRO, \ + FILTER, \ + context, \ + 0, \ + args)) + +#define FILTER_NONE(context, type, index) 1 + +#define FILTER_INDEX_INTERNAL(index_to_drop, type, index) \ + IS_NOT_EQUAL(index, index_to_drop) +#define FILTER_INDEX(index_to_drop, type, index) \ + FILTER_INDEX_INTERNAL(index_to_drop, type, index) + +/* + * Applies a MACRO which takes a type and the index of the type and outputs a + * sequence of tokens to a list of types. + */ +#define FOR_EACH_PARAM(MACRO, FILTER, context, args...) \ + EXPAND(PARAM_LIST_RECURSE(0,\ + NUM_VA_ARGS(args),\ + MACRO,\ + FILTER,\ + context,\ + args)) + +#define PRODUCE_TYPE_AND_ARG(context, type, index) type arg##index +#define PARAM_LIST_FROM_TYPES(args...) \ + FOR_EACH_PARAM(PRODUCE_TYPE_AND_ARG, \ + FILTER_NONE, \ + not_used, \ + args) + +#define PRODUCE_TYPE_NAME(context, type, index) #type +#define TYPE_NAMES_FROM_TYPES(handle_index, args...) \ + FOR_EACH_PARAM(PRODUCE_TYPE_NAME, \ + FILTER_INDEX, \ + handle_index, \ + args) + +#define PRODUCE_PTR_TO_ARG(context, type, index) &arg##index +#define PTR_TO_ARG_FROM_TYPES(handle_index, args...) \ + FOR_EACH_PARAM(PRODUCE_PTR_TO_ARG, \ + FILTER_INDEX, \ + handle_index, \ + args) + +#define PRODUCE_MATCHER_AND_ARG(ctrl_index, type, index) \ + IF(IS_EQUAL(index, ctrl_index))(struct mock *arg##ctrl_index) \ + IF(IS_NOT_EQUAL(index, ctrl_index))( \ + struct mock_param_matcher *arg##index) +#define MATCHER_PARAM_LIST_FROM_TYPES(ctrl_index, args...) \ + FOR_EACH_PARAM(PRODUCE_MATCHER_AND_ARG, \ + FILTER_NONE, \ + ctrl_index, \ + args) + +#define PRODUCE_ARG(context, type, index) arg##index +#define ARG_NAMES_FROM_TYPES(ctrl_index, args...) \ + FOR_EACH_PARAM(PRODUCE_ARG, \ + FILTER_INDEX, \ + ctrl_index, \ + args) + +#define PRODUCE_ARRAY_ACCESSOR(context, type, index) *((type *) params[index]) +#define ARRAY_ACCESSORS_FROM_TYPES(args...) \ + FOR_EACH_PARAM(PRODUCE_ARRAY_ACCESSOR, \ + FILTER_NONE, \ + not_used, \ + args) + +#endif /* _KUNIT_PARAMS_H */ diff --git a/kunit/Makefile b/kunit/Makefile index 2f1c069e165cb..f72a02cb9f23d 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_KUNIT) += test.o string-stream.o test-stream.o -obj-$(CONFIG_KUNIT_TEST) += test-test.o string-stream-test.o +obj-$(CONFIG_KUNIT_TEST) += test-test.o mock-macro-test.o string-stream-test.o obj-$(CONFIG_EXAMPLE_TEST) += example-test.o diff --git a/kunit/mock-macro-test.c b/kunit/mock-macro-test.c new file mode 100644 index 0000000000000..c30b859ff2b14 --- /dev/null +++ b/kunit/mock-macro-test.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit test for parameter list parsing macros. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include + +#define TO_STR_INTERNAL(...) #__VA_ARGS__ +#define TO_STR(...) TO_STR_INTERNAL(__VA_ARGS__) + +static void mock_macro_is_equal(struct test *test) +{ + TEST_EXPECT_STREQ(test, "dropped, 1", TO_STR(EQUAL(1, 1))); + TEST_EXPECT_EQ(test, 1, DROP_FIRST_ARG(dropped, 1, 0)); + TEST_EXPECT_EQ(test, 0, DROP_FIRST_ARG(1, 0)); + TEST_EXPECT_EQ(test, 0, DROP_FIRST_ARG(EQUAL(1, 0), 0)); + TEST_EXPECT_EQ(test, 1, IS_EQUAL(1, 1)); + TEST_EXPECT_EQ(test, 0, IS_EQUAL(1, 0)); +} + +static void mock_macro_if(struct test *test) +{ + TEST_EXPECT_STREQ(test, "body", ""IF(1)("body")); + TEST_EXPECT_STREQ(test, "", ""IF(0)("body")); + TEST_EXPECT_STREQ(test, "body", ""IF(IS_EQUAL(1, 1))("body")); + TEST_EXPECT_STREQ(test, "", ""IF(IS_EQUAL(0, 1))("body")); +} + +static void mock_macro_apply_tokens(struct test *test) +{ + TEST_EXPECT_STREQ(test, "type", TO_STR(APPLY_TOKENS(type, 1, 0))); + TEST_EXPECT_STREQ(test, ", type", TO_STR(APPLY_TOKENS(type, 1, 1))); + TEST_EXPECT_STREQ(test, "", TO_STR(APPLY_TOKENS(type, 0, 1))); +} + +#define IDENTITY(context, type, index) type + +static void mock_macro_param_list_recurse(struct test *test) +{ + TEST_EXPECT_STREQ(test, "", TO_STR(PARAM_LIST_RECURSE(0, + 0, + IDENTITY, + FILTER_NONE, + not_used))); + TEST_EXPECT_STREQ(test, + "type", + TO_STR(EXPAND(PARAM_LIST_RECURSE(0, + 1, + IDENTITY, + FILTER_NONE, + not_used, + type)))); + TEST_EXPECT_STREQ(test, + "type0 , type1 , type2 , type3 , type4 , type5 , " + "type6 , type7 , type8 , type9 , type10 , type11 , " + "type12 , type13 , type14 , type15", + TO_STR(EXPAND(PARAM_LIST_RECURSE(0, 16, + IDENTITY, + FILTER_NONE, + not_used, + type0, type1, type2, + type3, type4, type5, + type6, type7, type8, + type9, type10, + type11, type12, + type13, type14, + type15)))); +} + +static void mock_macro_for_each_param(struct test *test) +{ + TEST_EXPECT_STREQ(test, + "type0 , type1", + TO_STR(FOR_EACH_PARAM(IDENTITY, + FILTER_NONE, + not_used, + type0, + type1))); + TEST_EXPECT_STREQ(test, + "type1", + TO_STR(FOR_EACH_PARAM(IDENTITY, + FILTER_INDEX, + 0, + type0, + type1))); +} + +static void mock_macro_param_list_from_types_basic(struct test *test) +{ + TEST_EXPECT_STREQ(test, "", TO_STR(PARAM_LIST_FROM_TYPES())); + TEST_EXPECT_STREQ(test, "int arg0", TO_STR(PARAM_LIST_FROM_TYPES(int))); + TEST_EXPECT_STREQ(test, "struct test_struct * arg0 , int arg1", + TO_STR(PARAM_LIST_FROM_TYPES(struct test_struct *, + int))); + TEST_EXPECT_STREQ(test, + "type0 arg0 , type1 arg1 , type2 arg2 , type3 arg3 , " + "type4 arg4 , type5 arg5 , type6 arg6 , type7 arg7 , " + "type8 arg8 , type9 arg9 , type10 arg10 , " + "type11 arg11 , type12 arg12 , type13 arg13 , " + "type14 arg14 , type15 arg15", + TO_STR(PARAM_LIST_FROM_TYPES(type0, type1, type2, + type3, type4, type5, + type6, type7, type8, + type9, type10, type11, + type12, type13, type14, + type15))); +} + +static void mock_macro_arg_names_from_types(struct test *test) +{ + TEST_EXPECT_STREQ(test, "", TO_STR(ARG_NAMES_FROM_TYPES(0))); + TEST_EXPECT_STREQ(test, "", TO_STR(ARG_NAMES_FROM_TYPES(0, int))); + TEST_EXPECT_STREQ(test, + "arg1", + TO_STR(ARG_NAMES_FROM_TYPES(0, + struct test_struct *, + int))); + TEST_EXPECT_STREQ(test, + "arg0 , arg1 , arg3 , arg4 , arg5 , arg6 , arg7 , " + "arg8 , arg9 , arg10 , arg11 , arg12 , arg13 , " + "arg14 , arg15", + TO_STR(ARG_NAMES_FROM_TYPES(2, type0, type1, type2, + type3, type4, type5, + type6, type7, type8, + type9, type10, type11, + type12, type13, type14, + type15))); +} + +static struct test_case mock_macro_test_cases[] = { + TEST_CASE(mock_macro_is_equal), + TEST_CASE(mock_macro_if), + TEST_CASE(mock_macro_apply_tokens), + TEST_CASE(mock_macro_param_list_recurse), + TEST_CASE(mock_macro_for_each_param), + TEST_CASE(mock_macro_param_list_from_types_basic), + TEST_CASE(mock_macro_arg_names_from_types), + {}, +}; + +static struct test_module mock_macro_test_module = { + .name = "mock-macro-test", + .test_cases = mock_macro_test_cases, +}; +module_test(mock_macro_test_module); From patchwork Tue Oct 16 23:51:03 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985049 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="It+dzrMD"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="B9kdliPt"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHG2YwTz9s9J for ; Wed, 17 Oct 2018 10:55:06 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=DyJpoqaGGhG6gtEERazUvh/ZQezF+jhNH4k8R9a40/E=; b=It+dzrMDiWGDve K8cp4kccyQkGlCHV3kN9R9gUkcN92z0JN889drS8VcD8VOf+v3fez0RqobLVGybEjoSRpLdPz5VAy 0fmy02FCY31N+VaqdVAGUYOLqAv5e5CSqnLnFOAa0KYNb5egPJT7Mmns02+PbusL1oTIhp8TA2/vh aM5jIbtSQdWLb7RHiIRXKuxBx93b83YwmyJe32qtkfzlPJzAxqNaSOp9GGl4ytOOBZ1w0OH/0BEbr dYGJHLECqLI/F7p2w/ZNmJYAvniM52kZoTN7/RGdVf4KHLw7sc+BIxNLKatpLGoBqEbLl14SrP23Y RTLgMEg9Qm4zCzrZgPfA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAu-0004cd-W6; Tue, 16 Oct 2018 23:54:57 +0000 Received: from mail-it1-x14a.google.com ([2607:f8b0:4864:20::14a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAR-0004HJ-Lr for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:51 +0000 Received: by mail-it1-x14a.google.com with SMTP id h62-v6so258375itb.4 for ; Tue, 16 Oct 2018 16:54:21 -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=2nQLxz37HjUkuxUdygmRZnfP5MQCOlygPN6h14eHwdw=; b=B9kdliPtLkssRcDNBO20RpbwwI987uXusttn+Q36tcmnUTC7qDFs3Yo/15XuSDIcUh JD2McpsO2rXUn+6He3Subar9wT3HYpzhEC+bCE5ot9wvISC5vxgBy6X9sIEF7Mbz1kCZ qq6dB+bLtKFiQuVU6IMAvLYq808mPvEm/+x82/m7nT3GUJeuiCKUk+vEYyi3hEFGwqPS sZr84Hwtb3hVFyER/iiWkkmDYq1QUrhKE73wvtePsg1OAp1VjHpk//vPUJ+7l7X8x48Z Pu8QykfuIHC/zNb9GTm5VZowq1JL2mMTMv5WjPJAcQJGUF13s0cTxVMK5IVLXgeXiGJ2 Jo+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=2nQLxz37HjUkuxUdygmRZnfP5MQCOlygPN6h14eHwdw=; b=NI58k4hMisJ5QU4tAoFLkhCxloqIsEBk/5vrZVNqmJgYpETHb69dZwW4mR7tTsKygO ZADmaqb0Z6ifpxVM2mL+8XYPiInXPM6l8cxPUWV9wCE0qkAGeebxiWV861gkyeryNGfw 9I62AAEhKJjAMv0EjbYMpqHWo566Fg8W5NKNnflFrtjzBHiNGuGThGQjBjQ2XReVLl8g aLoSgQ0zx4ehmA55a9+y5tnaMwv7vv6NWf4hvGpIPp+rBIEhekGaNvvP4b57iiwzbvvX Unnx2IK72YetGIY68Nwia+yM3hqFGshX400AU77NkFs1DZOmblrz18eLAUYPerfQJl3S ho8g== X-Gm-Message-State: ABuFfogyw3abYQICwmwXDXKTpMVEYxhnvc3QnUD6GW67K8kJUKE1vCNn 5JBfUqICObo6SCaEZ1GDPlE/It78BvSjHYLZm4yQZQ== X-Google-Smtp-Source: ACcGV62LSCvUVvKuQcKAzN7Zy7BphoHPZ6h3wkJ7rRKFgGJjkjCaPIHSivbsxMPzvUQJ2xCUfWyjfRwMQGlX3sis4gFhgA== X-Received: by 2002:a24:3949:: with SMTP id l70-v6mr221173ita.6.1539734061067; Tue, 16 Oct 2018 16:54:21 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:03 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-15-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 14/31] kunit: mock: added internal mock infrastructure From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165427_739931_76EC5A89 X-CRM114-Status: GOOD ( 19.02 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:14a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Adds the core internal mechanisms that mocks are implemented with; in particular, this adds the mechanisms by which expectation on mocks are validated and by which actions may be supplied and then executed when mocks are called. Signed-off-by: Brendan Higgins --- include/kunit/mock.h | 125 +++++++++++++++ kunit/Makefile | 5 +- kunit/mock.c | 359 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 487 insertions(+), 2 deletions(-) create mode 100644 include/kunit/mock.h create mode 100644 kunit/mock.c diff --git a/include/kunit/mock.h b/include/kunit/mock.h new file mode 100644 index 0000000000000..1a35c5702cb15 --- /dev/null +++ b/include/kunit/mock.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Mocking API for KUnit. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _KUNIT_MOCK_H +#define _KUNIT_MOCK_H + +#include +#include /* For PARAMS(...) */ +#include +#include +#include + +/** + * struct mock_param_matcher - represents a matcher used in a *call expectation* + * @match: the function that performs the matching + * + * The matching function takes a couple of parameters: + * + * - ``this``: refers to the parent struct + * - ``stream``: a &test_stream to which a detailed message should be added as + * to why the parameter matches or not + * - ``param``: a pointer to the parameter to check for a match + * + * The matching function should return whether or not the passed parameter + * matches. + */ +struct mock_param_matcher { + bool (*match)(struct mock_param_matcher *this, + struct test_stream *stream, + const void *param); +}; + +#define MOCK_MAX_PARAMS 255 + +struct mock_matcher { + struct mock_param_matcher *matchers[MOCK_MAX_PARAMS]; + int num; +}; + +/** + * struct mock_action - Represents an action that a mock performs when + * expectation is matched + * @do_action: the action to perform + * + * The action function is given some parameters: + * + * - ``this``: refers to the parent struct + * - ``params``: an array of pointers to the params passed into the mocked + * method or function. **The class argument is excluded for a mocked class + * method.** + * - ``len``: size of ``params`` + * + * The action function returns a pointer to the value that the mocked method + * or function should be returning. + */ +struct mock_action { + void *(*do_action)(struct mock_action *this, + const void **params, + int len); +}; + +/** + * struct mock_expectation - represents a *call expectation* on a function. + * @action: A &struct mock_action to perform when the function is called. + * @max_calls_expected: maximum number of times an expectation may be called. + * @min_calls_expected: minimum number of times an expectation may be called. + * @retire_on_saturation: no longer match once ``max_calls_expected`` is + * reached. + * + * Represents a *call expectation* on a function created with EXPECT_CALL(). + */ +struct mock_expectation { + struct mock_action *action; + int max_calls_expected; + int min_calls_expected; + bool retire_on_saturation; + /* private: internal use only. */ + const char *expectation_name; + struct list_head node; + struct mock_matcher *matcher; + int times_called; +}; + +struct mock_method { + struct list_head node; + const char *method_name; + const void *method_ptr; + struct mock_action *default_action; + struct list_head expectations; +}; + +struct mock { + struct test_post_condition parent; + struct test *test; + struct list_head methods; + /* TODO(brendanhiggins@google.com): add locking to do_expect. */ + const void *(*do_expect)(struct mock *mock, + const char *method_name, + const void *method_ptr, + const char * const *param_types, + const void **params, + int len); +}; + +void mock_init_ctrl(struct test *test, struct mock *mock); + +void mock_validate_expectations(struct mock *mock); + +int mock_set_default_action(struct mock *mock, + const char *method_name, + const void *method_ptr, + struct mock_action *action); + +struct mock_expectation *mock_add_matcher(struct mock *mock, + const char *method_name, + const void *method_ptr, + struct mock_param_matcher *matchers[], + int len); + +#endif /* _KUNIT_MOCK_H */ diff --git a/kunit/Makefile b/kunit/Makefile index f72a02cb9f23d..ad58110de695c 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1,3 +1,4 @@ -obj-$(CONFIG_KUNIT) += test.o string-stream.o test-stream.o -obj-$(CONFIG_KUNIT_TEST) += test-test.o mock-macro-test.o string-stream-test.o +obj-$(CONFIG_KUNIT) += test.o mock.o string-stream.o test-stream.o +obj-$(CONFIG_KUNIT_TEST) += \ + test-test.o mock-macro-test.o string-stream-test.o obj-$(CONFIG_EXAMPLE_TEST) += example-test.o diff --git a/kunit/mock.c b/kunit/mock.c new file mode 100644 index 0000000000000..424c612de553b --- /dev/null +++ b/kunit/mock.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mocking API for KUnit. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include + +static bool mock_match_params(struct mock_matcher *matcher, + struct test_stream *stream, + const void **params, + int len) +{ + struct mock_param_matcher *param_matcher; + bool ret = true, tmp; + int i; + + BUG_ON(matcher->num != len); + + for (i = 0; i < matcher->num; i++) { + param_matcher = matcher->matchers[i]; + stream->add(stream, "\t"); + tmp = param_matcher->match(param_matcher, stream, params[i]); + ret = ret && tmp; + stream->add(stream, "\n"); + } + + return ret; +} + +static const void *mock_do_expect(struct mock *mock, + const char *method_name, + const void *method_ptr, + const char * const *type_names, + const void **params, + int len); + +void mock_validate_expectations(struct mock *mock) +{ + struct mock_expectation *expectation, *expectation_safe; + struct mock_method *method; + struct test_stream *stream; + int times_called; + + stream = test_new_stream(mock->test); + list_for_each_entry(method, &mock->methods, node) { + list_for_each_entry_safe(expectation, expectation_safe, + &method->expectations, node) { + times_called = expectation->times_called; + if (!(expectation->min_calls_expected <= times_called && + times_called <= expectation->max_calls_expected) + ) { + stream->add(stream, + "Expectation was not called the specified number of times:\n\t"); + stream->add(stream, + "Function: %s, min calls: %d, max calls: %d, actual calls: %d", + method->method_name, + expectation->min_calls_expected, + expectation->max_calls_expected, + times_called); + mock->test->fail(mock->test, stream); + } + list_del(&expectation->node); + } + } +} + +static void mock_validate_wrapper(struct test_post_condition *condition) +{ + struct mock *mock = container_of(condition, struct mock, parent); + + mock_validate_expectations(mock); +} + +void mock_init_ctrl(struct test *test, struct mock *mock) +{ + mock->test = test; + INIT_LIST_HEAD(&mock->methods); + mock->do_expect = mock_do_expect; + mock->parent.validate = mock_validate_wrapper; + list_add_tail(&mock->parent.node, &test->post_conditions); +} + +static struct mock_method *mock_lookup_method(struct mock *mock, + const void *method_ptr) +{ + struct mock_method *ret; + + list_for_each_entry(ret, &mock->methods, node) { + if (ret->method_ptr == method_ptr) + return ret; + } + + return NULL; +} + +static struct mock_method *mock_add_method(struct mock *mock, + const char *method_name, + const void *method_ptr) +{ + struct mock_method *method; + + method = test_kzalloc(mock->test, sizeof(*method), GFP_KERNEL); + if (!method) + return NULL; + + INIT_LIST_HEAD(&method->expectations); + method->method_name = method_name; + method->method_ptr = method_ptr; + list_add_tail(&method->node, &mock->methods); + + return method; +} + +static int mock_add_expectation(struct mock *mock, + const char *method_name, + const void *method_ptr, + struct mock_expectation *expectation) +{ + struct mock_method *method; + + method = mock_lookup_method(mock, method_ptr); + if (!method) { + method = mock_add_method(mock, method_name, method_ptr); + if (!method) + return -ENOMEM; + } + + list_add_tail(&expectation->node, &method->expectations); + + return 0; +} + +struct mock_expectation *mock_add_matcher(struct mock *mock, + const char *method_name, + const void *method_ptr, + struct mock_param_matcher *matchers[], + int len) +{ + struct mock_expectation *expectation; + struct mock_matcher *matcher; + int ret; + + expectation = test_kzalloc(mock->test, + sizeof(*expectation), + GFP_KERNEL); + if (!expectation) + return NULL; + + matcher = test_kmalloc(mock->test, sizeof(*matcher), GFP_KERNEL); + if (!matcher) + return NULL; + + memcpy(&matcher->matchers, matchers, sizeof(*matchers) * len); + matcher->num = len; + + expectation->matcher = matcher; + expectation->max_calls_expected = 1; + expectation->min_calls_expected = 1; + + ret = mock_add_expectation(mock, method_name, method_ptr, expectation); + if (ret < 0) + return NULL; + + return expectation; +} + +int mock_set_default_action(struct mock *mock, + const char *method_name, + const void *method_ptr, + struct mock_action *action) +{ + struct mock_method *method; + + method = mock_lookup_method(mock, method_ptr); + if (!method) { + method = mock_add_method(mock, method_name, method_ptr); + if (!method) + return -ENOMEM; + } + + method->default_action = action; + + return 0; +} + +static void mock_format_param(struct test_stream *stream, + const char *type_name, + const void *param) +{ + /* + * Cannot find formatter, so just print the pointer of the + * symbol. + */ + stream->add(stream, "<%pS>", param); +} + +static void mock_add_method_declaration_to_stream( + struct test_stream *stream, + const char *function_name, + const char * const *type_names, + const void **params, + int len) +{ + int i; + + stream->add(stream, "%s(", function_name); + for (i = 0; i < len; i++) { + mock_format_param(stream, type_names[i], params[i]); + if (i < len - 1) + stream->add(stream, ", "); + } + stream->add(stream, ")\n"); +} + +static struct test_stream *mock_initialize_failure_message( + struct test *test, + const char *function_name, + const char * const *type_names, + const void **params, + int len) +{ + struct test_stream *stream; + + stream = test_new_stream(test); + if (!stream) + return NULL; + + stream->add(stream, "EXPECTATION FAILED: no expectation for call: "); + mock_add_method_declaration_to_stream(stream, + function_name, + type_names, + params, + len); + return stream; +} + +static bool mock_is_expectation_retired(struct mock_expectation *expectation) +{ + return expectation->retire_on_saturation && + expectation->times_called == + expectation->max_calls_expected; +} + +static void mock_add_method_expectation_error(struct test *test, + struct test_stream *stream, + char *message, + struct mock *mock, + struct mock_method *method, + const char * const *type_names, + const void **params, + int len) +{ + stream->clear(stream); + stream->set_level(stream, KERN_WARNING); + stream->add(stream, message); + mock_add_method_declaration_to_stream(stream, + method->method_name, type_names, params, len); +} + +static struct mock_expectation *mock_apply_expectations( + struct mock *mock, + struct mock_method *method, + const char * const *type_names, + const void **params, + int len) +{ + struct test *test = mock->test; + struct mock_expectation *ret; + struct test_stream *attempted_matching_stream; + bool expectations_all_saturated = true; + + struct test_stream *stream = test_new_stream(test); + + if (list_empty(&method->expectations)) { + mock_add_method_expectation_error(test, stream, + "Method was called with no expectations declared: ", + mock, method, type_names, params, len); + stream->commit(stream); + return NULL; + } + + attempted_matching_stream = mock_initialize_failure_message( + test, + method->method_name, + type_names, + params, + len); + + list_for_each_entry(ret, &method->expectations, node) { + if (mock_is_expectation_retired(ret)) + continue; + expectations_all_saturated = false; + + attempted_matching_stream->add(attempted_matching_stream, + "Tried expectation: %s, but\n", ret->expectation_name); + if (mock_match_params(ret->matcher, + attempted_matching_stream, params, len)) { + /* + * Matcher was found; we won't print, so clean up the + * log. + */ + attempted_matching_stream->clear( + attempted_matching_stream); + return ret; + } + } + + if (expectations_all_saturated) { + mock_add_method_expectation_error(test, stream, + "Method was called with fully saturated expectations: ", + mock, method, type_names, params, len); + } else { + mock_add_method_expectation_error(test, stream, + "Method called that did not match any expectations: ", + mock, method, type_names, params, len); + stream->append(stream, attempted_matching_stream); + } + test->fail(test, stream); + attempted_matching_stream->clear(attempted_matching_stream); + return NULL; +} + +static const void *mock_do_expect(struct mock *mock, + const char *method_name, + const void *method_ptr, + const char * const *param_types, + const void **params, + int len) +{ + struct mock_expectation *expectation; + struct mock_method *method; + struct mock_action *action; + + method = mock_lookup_method(mock, method_ptr); + if (!method) + return NULL; + + expectation = mock_apply_expectations(mock, + method, + param_types, + params, + len); + if (!expectation) { + action = method->default_action; + } else { + expectation->times_called++; + if (expectation->action) + action = expectation->action; + else + action = method->default_action; + } + if (!action) + return NULL; + + return action->do_action(action, params, len); +} From patchwork Tue Oct 16 23:51:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985052 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="b40EjPB9"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="cD87gkTc"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHT42Fyz9s9J for ; Wed, 17 Oct 2018 10:55:17 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=i/g6FxkmGf30chFOPYxfwAKOvgRpG9IGqJJusajFJxs=; b=b40EjPB9bKlvGs Bu8pW13rEvfYbLfgma9LZ7k9B/QaiJXFy5LGTSy+HjXK6zMKMN+WRijxXJHv7jitiVRdsTfGwfGfh /5HjY7I+ai7fqjwoI2tffGlZqNB0HNC0S1jXpzrko3iUUazEcuq1DB8s5DmqVZrjPSeGcIV+/sonk j0wQ6iHc2mxaId9i/WU3KCrfFs7YhEJ3UUbz+83NrIUX0k9EEG1zq0TmSrQKflU0d+DirIzcsO1KB PgYz3E0QqS0kDGZk+seQ5SfIDGfrlpINYovEG+rV3T3ucl3T1hWyYWJzfTXYuoQHYxax0JIk81nvW e5cEzpco/jF6Fk6cwuxg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZB3-0004iP-3c; Tue, 16 Oct 2018 23:55:05 +0000 Received: from mail-qt1-x84a.google.com ([2607:f8b0:4864:20::84a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAY-0004Il-SN for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:58 +0000 Received: by mail-qt1-x84a.google.com with SMTP id j63-v6so16143258qte.13 for ; Tue, 16 Oct 2018 16:54:24 -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=G8mUMu2GbUg2JX8LWDmnjinAwy7AJF1XWi8Z4EzyP2w=; b=cD87gkTcOY15FVf3OnY9ju2AYChLswyGiPO4qaT8gdvcGVkGIvKvt7wtEHYP80HnD1 QRpEJLRWwfE0WMkD1M1v7egeqhHB1KM1hjHU0LAZynpldBJyOqtN1FQelV07daXW/iuB Yx0kYYsbE5KqlEMvVPWS0iVoCsyt5ypyRX9I81qXLSGh4shAbpO0J0mVaAxfm7AxXkOZ hDoAaEEmWwfU2Ow4n3z99cD+pqTBArGldXauqEFv+4/0TzC5iG/O/pUkWxoY9XUYZZ1R PdknRKzx2mMaPprbC6+0mlMsnjxSjjyY2e9AnkpN5tIdZ5gCh4PyO7fwmvQJ3gnxuSkb sHtg== 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=G8mUMu2GbUg2JX8LWDmnjinAwy7AJF1XWi8Z4EzyP2w=; b=e+t5mKQ7s2RL5JSEuPxOA0684FbUAEFD0RsRgp/BHW/Qszk1uMSYzsqTDi3UPJBfeB nyH/YEcJW0adsRZj5Nz9mUn4tqwReAGBcWOvQc3bNQCzkX+tPLY/hRoLDbmjLT762cjp TXIXsSRUxlzSsSJvFuLbHJIe1vAxuLEBABtyRXW1AIcD6Gi4aQQF8+T0kuXh043d+49M XzvgOr+dN2o7HufhcgSn44hLJur/PWOk3CxMkdSt9uIeq267QQHANr5OqlJAGfTWW/pG onkk9xoL/MAw4PwWioEqsNmPaO1+w/NexbvNtOj9e9LAAqWMVzn0m/HWoOq9s3LaFU5c J2GQ== X-Gm-Message-State: ABuFfohLZrHZe45ftq+ozPySNHLS2SGeVQ3DPIwFluTGhGg9ez6suHDV Y1TVnF30hEyLEM3nnnEt+R40ElFosvsOp/QWS4Qc0g== X-Google-Smtp-Source: ACcGV63goyHFI0Ba5xYbIs9fHChd50SB7jnKKtV9799SecgHVnuGXxqOIClC29aphcr7I+bghKungYtXv51a2aZu1iC9HA== X-Received: by 2002:a0c:d0f2:: with SMTP id b47-v6mr19387561qvh.20.1539734063602; Tue, 16 Oct 2018 16:54:23 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:04 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-16-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 15/31] kunit: mock: added basic matchers and actions From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165434_963811_03F83B33 X-CRM114-Status: UNSURE ( 9.83 ) X-CRM114-Notice: Please train this message. X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:84a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Added basic matchers and actions needed for any kind of mocking to be useful; these matchers and actions are how expectations for mocks are described: what calls the mocks are expected to receive, and what the mock should do under those circumstances. Signed-off-by: Brendan Higgins --- include/kunit/mock.h | 222 +++++++++++++++++++++++++++++++++++ kunit/Makefile | 3 +- kunit/common-mocks.c | 272 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 496 insertions(+), 1 deletion(-) create mode 100644 kunit/common-mocks.c diff --git a/include/kunit/mock.h b/include/kunit/mock.h index 1a35c5702cb15..62e8afcaeab55 100644 --- a/include/kunit/mock.h +++ b/include/kunit/mock.h @@ -122,4 +122,226 @@ struct mock_expectation *mock_add_matcher(struct mock *mock, struct mock_param_matcher *matchers[], int len); +#define CONVERT_TO_ACTUAL_TYPE(type, ptr) (*((type *) ptr)) + +/** + * DOC: Built In Matchers + * + * These are the matchers that can be used when matching arguments in + * :c:func:`EXPECT_CALL` (more can be defined manually). + * + * For example, there's a matcher that matches any arguments: + * + * .. code-block:: c + * + * struct mock_param_matcher *any(struct test *test); + * + * There are matchers for integers based on the binary condition: + * + * * eq: equals to + * * ne: not equal to + * * lt: less than + * * le: less than or equal to + * * gt: greater than + * * ge: greater than or equal to + * + * .. code-block:: c + * + * struct mock_param_matcher *test_int_eq(struct test *test, int expected); + * struct mock_param_matcher *test_int_ne(struct test *test, int expected); + * struct mock_param_matcher *test_int_lt(struct test *test, int expected); + * struct mock_param_matcher *test_int_le(struct test *test, int expected); + * struct mock_param_matcher *test_int_gt(struct test *test, int expected); + * struct mock_param_matcher *test_int_ge(struct test *test, int expected); + * + * For a detailed list, please see + * ``include/linux/mock.h``. + */ + +/* Matches any argument */ +struct mock_param_matcher *test_any(struct test *test); + +/* + * Matches different types of integers, the argument is compared to the + * `expected` field, based on the comparison defined. + */ +struct mock_param_matcher *test_u8_eq(struct test *test, u8 expected); +struct mock_param_matcher *test_u8_ne(struct test *test, u8 expected); +struct mock_param_matcher *test_u8_le(struct test *test, u8 expected); +struct mock_param_matcher *test_u8_lt(struct test *test, u8 expected); +struct mock_param_matcher *test_u8_ge(struct test *test, u8 expected); +struct mock_param_matcher *test_u8_gt(struct test *test, u8 expected); + +struct mock_param_matcher *test_u16_eq(struct test *test, u16 expected); +struct mock_param_matcher *test_u16_ne(struct test *test, u16 expected); +struct mock_param_matcher *test_u16_le(struct test *test, u16 expected); +struct mock_param_matcher *test_u16_lt(struct test *test, u16 expected); +struct mock_param_matcher *test_u16_ge(struct test *test, u16 expected); +struct mock_param_matcher *test_u16_gt(struct test *test, u16 expected); + +struct mock_param_matcher *test_u32_eq(struct test *test, u32 expected); +struct mock_param_matcher *test_u32_ne(struct test *test, u32 expected); +struct mock_param_matcher *test_u32_le(struct test *test, u32 expected); +struct mock_param_matcher *test_u32_lt(struct test *test, u32 expected); +struct mock_param_matcher *test_u32_ge(struct test *test, u32 expected); +struct mock_param_matcher *test_u32_gt(struct test *test, u32 expected); + +struct mock_param_matcher *test_u64_eq(struct test *test, u64 expected); +struct mock_param_matcher *test_u64_ne(struct test *test, u64 expected); +struct mock_param_matcher *test_u64_le(struct test *test, u64 expected); +struct mock_param_matcher *test_u64_lt(struct test *test, u64 expected); +struct mock_param_matcher *test_u64_ge(struct test *test, u64 expected); +struct mock_param_matcher *test_u64_gt(struct test *test, u64 expected); + +struct mock_param_matcher *test_char_eq(struct test *test, char expected); +struct mock_param_matcher *test_char_ne(struct test *test, char expected); +struct mock_param_matcher *test_char_le(struct test *test, char expected); +struct mock_param_matcher *test_char_lt(struct test *test, char expected); +struct mock_param_matcher *test_char_ge(struct test *test, char expected); +struct mock_param_matcher *test_char_gt(struct test *test, char expected); + +struct mock_param_matcher *test_uchar_eq(struct test *test, + unsigned char expected); +struct mock_param_matcher *test_uchar_ne(struct test *test, + unsigned char expected); +struct mock_param_matcher *test_uchar_le(struct test *test, + unsigned char expected); +struct mock_param_matcher *test_uchar_lt(struct test *test, + unsigned char expected); +struct mock_param_matcher *test_uchar_ge(struct test *test, + unsigned char expected); +struct mock_param_matcher *test_uchar_gt(struct test *test, + unsigned char expected); + +struct mock_param_matcher *test_schar_eq(struct test *test, + signed char expected); +struct mock_param_matcher *test_schar_ne(struct test *test, + signed char expected); +struct mock_param_matcher *test_schar_le(struct test *test, + signed char expected); +struct mock_param_matcher *test_schar_lt(struct test *test, + signed char expected); +struct mock_param_matcher *test_schar_ge(struct test *test, + signed char expected); +struct mock_param_matcher *test_schar_gt(struct test *test, + signed char expected); + +struct mock_param_matcher *test_short_eq(struct test *test, short expected); +struct mock_param_matcher *test_short_ne(struct test *test, short expected); +struct mock_param_matcher *test_short_le(struct test *test, short expected); +struct mock_param_matcher *test_short_lt(struct test *test, short expected); +struct mock_param_matcher *test_short_ge(struct test *test, short expected); +struct mock_param_matcher *test_short_gt(struct test *test, short expected); + +struct mock_param_matcher *test_ushort_eq(struct test *test, + unsigned short expected); +struct mock_param_matcher *test_ushort_ne(struct test *test, + unsigned short expected); +struct mock_param_matcher *test_ushort_le(struct test *test, + unsigned short expected); +struct mock_param_matcher *test_ushort_lt(struct test *test, + unsigned short expected); +struct mock_param_matcher *test_ushort_ge(struct test *test, + unsigned short expected); +struct mock_param_matcher *test_ushort_gt(struct test *test, + unsigned short expected); + +struct mock_param_matcher *test_int_eq(struct test *test, int expected); +struct mock_param_matcher *test_int_ne(struct test *test, int expected); +struct mock_param_matcher *test_int_lt(struct test *test, int expected); +struct mock_param_matcher *test_int_le(struct test *test, int expected); +struct mock_param_matcher *test_int_gt(struct test *test, int expected); +struct mock_param_matcher *test_int_ge(struct test *test, int expected); + +struct mock_param_matcher *test_uint_eq(struct test *test, + unsigned int expected); +struct mock_param_matcher *test_uint_ne(struct test *test, + unsigned int expected); +struct mock_param_matcher *test_uint_lt(struct test *test, + unsigned int expected); +struct mock_param_matcher *test_uint_le(struct test *test, + unsigned int expected); +struct mock_param_matcher *test_uint_gt(struct test *test, + unsigned int expected); +struct mock_param_matcher *test_uint_ge(struct test *test, + unsigned int expected); + +struct mock_param_matcher *test_long_eq(struct test *test, long expected); +struct mock_param_matcher *test_long_ne(struct test *test, long expected); +struct mock_param_matcher *test_long_le(struct test *test, long expected); +struct mock_param_matcher *test_long_lt(struct test *test, long expected); +struct mock_param_matcher *test_long_ge(struct test *test, long expected); +struct mock_param_matcher *test_long_gt(struct test *test, long expected); + +struct mock_param_matcher *test_ulong_eq(struct test *test, + unsigned long expected); +struct mock_param_matcher *test_ulong_ne(struct test *test, + unsigned long expected); +struct mock_param_matcher *test_ulong_le(struct test *test, + unsigned long expected); +struct mock_param_matcher *test_ulong_lt(struct test *test, + unsigned long expected); +struct mock_param_matcher *test_ulong_ge(struct test *test, + unsigned long expected); +struct mock_param_matcher *test_ulong_gt(struct test *test, + unsigned long expected); + +struct mock_param_matcher *test_longlong_eq(struct test *test, + long long expected); +struct mock_param_matcher *test_longlong_ne(struct test *test, + long long expected); +struct mock_param_matcher *test_longlong_le(struct test *test, + long long expected); +struct mock_param_matcher *test_longlong_lt(struct test *test, + long long expected); +struct mock_param_matcher *test_longlong_ge(struct test *test, + long long expected); +struct mock_param_matcher *test_longlong_gt(struct test *test, + long long expected); + +struct mock_param_matcher *test_ulonglong_eq(struct test *test, + unsigned long long expected); +struct mock_param_matcher *test_ulonglong_ne(struct test *test, + unsigned long long expected); +struct mock_param_matcher *test_ulonglong_le(struct test *test, + unsigned long long expected); +struct mock_param_matcher *test_ulonglong_lt(struct test *test, + unsigned long long expected); +struct mock_param_matcher *test_ulonglong_ge(struct test *test, + unsigned long long expected); +struct mock_param_matcher *test_ulonglong_gt(struct test *test, + unsigned long long expected); + +/* Matches pointers. */ +struct mock_param_matcher *test_ptr_eq(struct test *test, void *expected); +struct mock_param_matcher *test_ptr_ne(struct test *test, void *expected); +struct mock_param_matcher *test_ptr_lt(struct test *test, void *expected); +struct mock_param_matcher *test_ptr_le(struct test *test, void *expected); +struct mock_param_matcher *test_ptr_gt(struct test *test, void *expected); +struct mock_param_matcher *test_ptr_ge(struct test *test, void *expected); + +/* Matches memory sections and strings. */ +struct mock_param_matcher *test_memeq(struct test *test, + const void *buf, + size_t size); +struct mock_param_matcher *test_streq(struct test *test, const char *str); + +struct mock_action *test_u8_return(struct test *test, u8 ret); +struct mock_action *test_u16_return(struct test *test, u16 ret); +struct mock_action *test_u32_return(struct test *test, u32 ret); +struct mock_action *test_u64_return(struct test *test, u64 ret); +struct mock_action *test_char_return(struct test *test, char ret); +struct mock_action *test_uchar_return(struct test *test, unsigned char ret); +struct mock_action *test_schar_return(struct test *test, signed char ret); +struct mock_action *test_short_return(struct test *test, short ret); +struct mock_action *test_ushort_return(struct test *test, unsigned short ret); +struct mock_action *test_int_return(struct test *test, int ret); +struct mock_action *test_uint_return(struct test *test, unsigned int ret); +struct mock_action *test_long_return(struct test *test, long ret); +struct mock_action *test_ulong_return(struct test *test, unsigned long ret); +struct mock_action *test_longlong_return(struct test *test, long long ret); +struct mock_action *test_ulonglong_return(struct test *test, + unsigned long long ret); +struct mock_action *test_ptr_return(struct test *test, void *ret); + #endif /* _KUNIT_MOCK_H */ diff --git a/kunit/Makefile b/kunit/Makefile index ad58110de695c..52a1da46cbd21 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1,4 +1,5 @@ -obj-$(CONFIG_KUNIT) += test.o mock.o string-stream.o test-stream.o +obj-$(CONFIG_KUNIT) += test.o mock.o common-mocks.o string-stream.o \ + test-stream.o obj-$(CONFIG_KUNIT_TEST) += \ test-test.o mock-macro-test.o string-stream-test.o obj-$(CONFIG_EXAMPLE_TEST) += example-test.o diff --git a/kunit/common-mocks.c b/kunit/common-mocks.c new file mode 100644 index 0000000000000..ecac9c1c29c0e --- /dev/null +++ b/kunit/common-mocks.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common KUnit mock call arg matchers and formatters. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include + +static bool match_any(struct mock_param_matcher *pmatcher, + struct test_stream *stream, + const void *actual) +{ + stream->add(stream, "don't care"); + return true; +} + +static struct mock_param_matcher any_matcher = { + .match = match_any, +}; + +struct mock_param_matcher *test_any(struct test *test) +{ + return &any_matcher; +} + +#define DEFINE_MATCHER_STRUCT(type_name, type) \ + struct mock_##type_name##_matcher { \ + struct mock_param_matcher matcher; \ + type expected; \ + }; + +#define DEFINE_TO_MATCHER_STRUCT(type_name) \ + struct mock_##type_name##_matcher * \ + to_mock_##type_name##_matcher( \ + struct mock_param_matcher *matcher) \ + { \ + return container_of(matcher, \ + struct mock_##type_name##_matcher, \ + matcher); \ + } + +#define DEFINE_MATCH_FUNC(type_name, type, op_name, op) \ + bool match_##type_name##_##op_name( \ + struct mock_param_matcher *pmatcher, \ + struct test_stream *stream, \ + const void *pactual) \ + { \ + struct mock_##type_name##_matcher *matcher = \ + to_mock_##type_name##_matcher(pmatcher); \ + type actual = *((type *) pactual); \ + bool matches = actual op matcher->expected; \ + \ + if (matches) \ + stream->add(stream, \ + "%d "#op" %d", \ + actual, \ + matcher->expected); \ + else \ + stream->add(stream, \ + "%d not "#op" %d", \ + actual, \ + matcher->expected); \ + \ + return matches; \ + } + +#define DEFINE_MATCH_FACTORY(type_name, type, op_name) \ + struct mock_param_matcher *test_##type_name##_##op_name( \ + struct test *test, type expected) \ + { \ + struct mock_##type_name##_matcher *matcher; \ + \ + matcher = test_kmalloc(test, \ + sizeof(*matcher), \ + GFP_KERNEL); \ + if (!matcher) \ + return NULL; \ + \ + matcher->matcher.match = match_##type_name##_##op_name;\ + matcher->expected = expected; \ + return &matcher->matcher; \ + } + +#define DEFINE_MATCHER_WITH_TYPENAME(type_name, type) \ + DEFINE_MATCHER_STRUCT(type_name, type) \ + DEFINE_TO_MATCHER_STRUCT(type_name) \ + DEFINE_MATCH_FUNC(type_name, type, eq, ==) \ + DEFINE_MATCH_FACTORY(type_name, type, eq) \ + DEFINE_MATCH_FUNC(type_name, type, ne, !=) \ + DEFINE_MATCH_FACTORY(type_name, type, ne) \ + DEFINE_MATCH_FUNC(type_name, type, le, <=) \ + DEFINE_MATCH_FACTORY(type_name, type, le) \ + DEFINE_MATCH_FUNC(type_name, type, lt, <) \ + DEFINE_MATCH_FACTORY(type_name, type, lt) \ + DEFINE_MATCH_FUNC(type_name, type, ge, >=) \ + DEFINE_MATCH_FACTORY(type_name, type, ge) \ + DEFINE_MATCH_FUNC(type_name, type, gt, >) \ + DEFINE_MATCH_FACTORY(type_name, type, gt) + +#define DEFINE_MATCHER(type) DEFINE_MATCHER_WITH_TYPENAME(type, type) + +DEFINE_MATCHER(u8); +DEFINE_MATCHER(u16); +DEFINE_MATCHER(u32); +DEFINE_MATCHER(u64); +DEFINE_MATCHER(char); +DEFINE_MATCHER_WITH_TYPENAME(uchar, unsigned char); +DEFINE_MATCHER_WITH_TYPENAME(schar, signed char); +DEFINE_MATCHER(short); +DEFINE_MATCHER_WITH_TYPENAME(ushort, unsigned short); +DEFINE_MATCHER(int); +DEFINE_MATCHER_WITH_TYPENAME(uint, unsigned int); +DEFINE_MATCHER(long); +DEFINE_MATCHER_WITH_TYPENAME(ulong, unsigned long); +DEFINE_MATCHER_WITH_TYPENAME(longlong, long long); +DEFINE_MATCHER_WITH_TYPENAME(ulonglong, unsigned long long); + +DEFINE_MATCHER_WITH_TYPENAME(ptr, void *); + +struct mock_memeq_matcher { + struct mock_param_matcher matcher; + const void *expected; + size_t size; +}; + +static bool match_memeq(struct mock_param_matcher *pmatcher, + struct test_stream *stream, + const void *pactual) +{ + struct mock_memeq_matcher *matcher = + container_of(pmatcher, + struct mock_memeq_matcher, + matcher); + const void *actual = CONVERT_TO_ACTUAL_TYPE(const void *, pactual); + bool matches = !memcmp(actual, matcher->expected, matcher->size); + int i; + + for (i = 0; i < matcher->size; i++) + stream->add(stream, "%02x, ", ((const char *) actual)[i]); + if (matches) + stream->add(stream, "== "); + else + stream->add(stream, "!= "); + for (i = 0; i < matcher->size; i++) + stream->add(stream, + "%02x, ", + ((const char *) matcher->expected)[i]); + + return matches; +} + +struct mock_param_matcher *test_memeq(struct test *test, + const void *buf, + size_t size) +{ + struct mock_memeq_matcher *matcher; + + matcher = test_kzalloc(test, sizeof(*matcher), GFP_KERNEL); + if (!matcher) + return NULL; + + matcher->matcher.match = match_memeq; + matcher->expected = buf; + matcher->size = size; + + return &matcher->matcher; +} + +struct mock_streq_matcher { + struct mock_param_matcher matcher; + const char *expected; +}; + +static bool match_streq(struct mock_param_matcher *pmatcher, + struct test_stream *stream, + const void *pactual) +{ + struct mock_streq_matcher *matcher = + container_of(pmatcher, + struct mock_streq_matcher, + matcher); + const char *actual = CONVERT_TO_ACTUAL_TYPE(const char *, pactual); + bool matches = !strcmp(actual, matcher->expected); + + if (matches) + stream->add(stream, "%s == %s", actual, matcher->expected); + else + stream->add(stream, "%s != %s", actual, matcher->expected); + + return matches; +} + +struct mock_param_matcher *test_streq(struct test *test, const char *str) +{ + struct mock_streq_matcher *matcher; + + matcher = test_kzalloc(test, sizeof(*matcher), GFP_KERNEL); + if (!matcher) + return NULL; + + matcher->matcher.match = match_streq; + matcher->expected = str; + + return &matcher->matcher; +} + +#define DEFINE_RETURN_ACTION_STRUCT(type_name, type) \ + struct mock_##type_name##_action { \ + struct mock_action action; \ + type ret; \ + }; + +#define DEFINE_RETURN_ACTION_FUNC(type_name, type) \ + void *do_##type_name##_return(struct mock_action *paction, \ + const void **params, \ + int len) \ + { \ + struct mock_##type_name##_action *action = \ + container_of(paction, \ + struct mock_##type_name##_action,\ + action); \ + \ + return (void *) &action->ret; \ + } + +#define DEFINE_RETURN_ACTION_FACTORY(type_name, type) \ + struct mock_action *test_##type_name##_return( \ + struct test *test, \ + type ret) \ + { \ + struct mock_##type_name##_action *action; \ + \ + action = test_kmalloc(test, \ + sizeof(*action), \ + GFP_KERNEL); \ + if (!action) \ + return NULL; \ + \ + action->action.do_action = do_##type_name##_return; \ + action->ret = ret; \ + \ + return &action->action; \ + } + +#define DEFINE_RETURN_ACTION_WITH_TYPENAME(type_name, type) \ + DEFINE_RETURN_ACTION_STRUCT(type_name, type); \ + DEFINE_RETURN_ACTION_FUNC(type_name, type); \ + DEFINE_RETURN_ACTION_FACTORY(type_name, type); + +#define DEFINE_RETURN_ACTION(type) \ + DEFINE_RETURN_ACTION_WITH_TYPENAME(type, type) + +DEFINE_RETURN_ACTION(u8); +DEFINE_RETURN_ACTION(u16); +DEFINE_RETURN_ACTION(u32); +DEFINE_RETURN_ACTION(u64); +DEFINE_RETURN_ACTION(char); +DEFINE_RETURN_ACTION_WITH_TYPENAME(uchar, unsigned char); +DEFINE_RETURN_ACTION_WITH_TYPENAME(schar, signed char); +DEFINE_RETURN_ACTION(short); +DEFINE_RETURN_ACTION_WITH_TYPENAME(ushort, unsigned short); +DEFINE_RETURN_ACTION(int); +DEFINE_RETURN_ACTION_WITH_TYPENAME(uint, unsigned int); +DEFINE_RETURN_ACTION(long); +DEFINE_RETURN_ACTION_WITH_TYPENAME(ulong, unsigned long); +DEFINE_RETURN_ACTION_WITH_TYPENAME(longlong, long long); +DEFINE_RETURN_ACTION_WITH_TYPENAME(ulonglong, unsigned long long); +DEFINE_RETURN_ACTION_WITH_TYPENAME(ptr, void *); + From patchwork Tue Oct 16 23:51:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985057 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="OJUtyV4c"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="t25OKSQ9"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHr1XSNz9sCT for ; Wed, 17 Oct 2018 10:55:36 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=wgUOvleSjEtwzf2UAu9jrS+FMdjB7AmxFNsBVGjVZ1Y=; b=OJUtyV4c/3gmrC UsCeEKKuLjqWoXfjuMyL3t1syZshGsmYEcuWZge6NtyDl2lztZeWMgY8ztWLz+5U3Sx6YUjG1aLAs MY5aBYildxqL+l+mPrJbqtRr1tFnmzINkV3K6UD9WJCZKvyVA5nzBml0Yi/ncnLHiN8CykBdGPbeU J1wAHigeaK2aIkLJ9qTlacCD+tQtbNbcPuA+fjvQEmuLIra4ppLfRXrQO87XItx2/0YOUEcBRENAS x39MUJZmxWaK4L/rgPq/BWMAXRyXhzpCN8bG6WX0DuZginRlzBU9oKKhGem8v+sRC3QDApXiMytIB Tl/YPmwIyT+Im8cIihLw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBK-0005bF-Lh; Tue, 16 Oct 2018 23:55:23 +0000 Received: from mail-io1-xd4a.google.com ([2607:f8b0:4864:20::d4a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAc-0004Km-4d for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:12 +0000 Received: by mail-io1-xd4a.google.com with SMTP id q126-v6so11343062iod.19 for ; Tue, 16 Oct 2018 16:54: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=eYgRPNileDwgvqqyHv6bH3k+biiDMeehKa3nWE2sDeg=; b=t25OKSQ9zcvMWXvA/j/yO893F3RUC3gcgUH+TXYxjVYtjtHFseMJ1CVGF1VWpviqRv qno8hUtS214ythFe7WcAPrfua4RYeFFDg0TEz2gz3/iJkWJiTNNJsTJh/K0y7oX7Uq59 D+PCMMOU5Sf3kTtZgcXRmDTcTwJVhcLbYP0DIiAsFlvdn3mHPu9Ww7p2BG8EWeeoGJMh +Gkrh1wod+Jq8hih6xsBW0jiZPy0YLUO7aLyiG96/YP846CDo6+OSlz/OVCqoFTvwje0 c5tpk4dR6j6vaQkD7AQpuS2xllFYPBCWHJxavSQn4D3GaDZfpsuPZPUJcjvNE/DIVaqi oSIw== 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=eYgRPNileDwgvqqyHv6bH3k+biiDMeehKa3nWE2sDeg=; b=GpL9jI3MOK9EcAIsYPd7+qa8Iz1KHnnbnmTpwtHLXfcbTkmK4QM1TotQKFyrsjQY7B zS25ZeKMxeXdtzh8oVcELRhNsYlieOHciw1O2Mvp88KT+DvHYbK02zpZ0EHi4lC+1zWN mz9LELEfkLAf/YGB7JxbTrNqKYtK7DfGCBdOQR1WXXeQ/yKNHC26byAhTGuIfq5nGwhR FS1wXd1c7V4Q3mmj7A7YCYf/oGO8fKub8MPnOwGwzUllkDCzj7VR7by4buQeo91uEYnN p/RjHNfhMyEhQL3oiEM1QeX0XEf+PvPDhwEPsZrmsYI0c5Iu+cNZNKohXbk8IQadeFkz +pSQ== X-Gm-Message-State: ABuFfoiaHgKHcx8KnJIYSzff4S9GjhzXKfQn5L0SW0TgLCodI/twcOTm Dj20l0WsnxFoO2QhpZoxF377LMI5ZaqjqFifc/evPA== X-Google-Smtp-Source: ACcGV603sB8VhNBjAmSgSitkpW+o+yP69w/yJ4Vhra8w7uyYdqVP6BkVPXavyl4xUanhAR3xjB2ufoSwlE7WnJLPTEJ3Aw== X-Received: by 2002:a24:17c5:: with SMTP id 188-v6mr203693ith.19.1539734066132; Tue, 16 Oct 2018 16:54:26 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:05 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-17-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 16/31] kunit: mock: added class mocking support From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165438_290548_991F8B6F X-CRM114-Status: GOOD ( 13.98 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:d4a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Introduces basic class mocking, the ability to automatically generate a Linux C-style class implementation whose behavior is controlled by test cases, which can also set expectations on when and how mocks are called. Signed-off-by: Brendan Higgins --- include/kunit/mock.h | 438 +++++++++++++++++++++++++++++++++++++++- kunit/Makefile | 2 +- kunit/example-test.c | 90 +++++++++ kunit/mock-macro-test.c | 82 ++++++++ kunit/mock-test.c | 276 +++++++++++++++++++++++++ kunit/test-mock.c | 39 ++++ kunit/test-mock.h | 23 +++ 7 files changed, 947 insertions(+), 3 deletions(-) create mode 100644 kunit/mock-test.c create mode 100644 kunit/test-mock.c create mode 100644 kunit/test-mock.h diff --git a/include/kunit/mock.h b/include/kunit/mock.h index 62e8afcaeab55..1b7485e2cedb8 100644 --- a/include/kunit/mock.h +++ b/include/kunit/mock.h @@ -72,7 +72,8 @@ struct mock_action { * @retire_on_saturation: no longer match once ``max_calls_expected`` is * reached. * - * Represents a *call expectation* on a function created with EXPECT_CALL(). + * Represents a *call expectation* on a function created with + * TEST_EXPECT_CALL(). */ struct mock_expectation { struct mock_action *action; @@ -122,13 +123,446 @@ struct mock_expectation *mock_add_matcher(struct mock *mock, struct mock_param_matcher *matchers[], int len); +#define MOCK(name) name##_mock + +/** + * TEST_EXPECT_CALL() - Declares a *call expectation* on a mock function. + * @expectation_call: a mocked method or function with parameters replaced with + * matchers. + * + * Example: + * + * .. code-block:: c + * + * // Class to mock. + * struct example { + * int (*foo)(struct example *, int); + * }; + * + * // Define the mock. + * DECLARE_STRUCT_CLASS_MOCK_PREREQS(example); + * + * DEFINE_STRUCT_CLASS_MOCK(METHOD(foo), CLASS(example), + * RETURNS(int), + * PARAMS(struct example *, int)); + * + * static int example_init(struct MOCK(example) *mock_example) + * { + * struct example *example = mock_get_trgt(mock_example); + * + * example->foo = foo; + * return 0; + * } + * + * DEFINE_STRUCT_CLASS_MOCK_INIT(example, example_init); + * + * static void foo_example_test_success(struct test *test) + * { + * struct MOCK(example) *mock_example; + * struct example *example = mock_get_trgt(mock_example); + * struct mock_expectation *handle; + * + * mock_example = CONSTRUCT_MOCK(example, test); + * + * handle = TEST_EXPECT_CALL(foo(mock_get_ctrl(mock_example), + * test_int_eq(test, 5))); + * handle->action = int_return(test, 2); + * + * EXPECT_EQ(test, 2, example_bar(example, 5)); + * } + * + * Return: + * A &struct mock_expectation representing the call expectation. + * allowing additional conditions and actions to be specified. + */ +#define TEST_EXPECT_CALL(expectation_call) mock_master_##expectation_call + +#define mock_get_ctrl_internal(mock_object) (&(mock_object)->ctrl) +#define mock_get_ctrl(mock_object) mock_get_ctrl_internal(mock_object) + +#define mock_get_trgt_internal(mock_object) (&(mock_object)->trgt) +#define mock_get_trgt(mock_object) mock_get_trgt_internal(mock_object) + +#define mock_get_test(mock_object) (mock_get_ctrl(mock_object)->test) + +#define CLASS(struct_name) struct_name +#define HANDLE_INDEX(index) index +#define METHOD(method_name) method_name +#define RETURNS(return_type) return_type +/* #define PARAMS(...) __VA_ARGS__ included by linux/tracepoint.h */ + +#define MOCK_INIT_ID(struct_name) struct_name##mock_init +#define REAL_ID(func_name) __real__##func_name +#define INVOKE_ID(func_name) __invoke__##func_name + +#define DECLARE_MOCK_CLIENT(name, return_type, param_types...) \ + return_type name(PARAM_LIST_FROM_TYPES(param_types)) + +#define DECLARE_MOCK_MASTER(name, ctrl_index, param_types...) \ + struct mock_expectation *mock_master_##name( \ + MATCHER_PARAM_LIST_FROM_TYPES(ctrl_index, \ + param_types)); + +#define DECLARE_MOCK_COMMON(name, handle_index, return_type, param_types...) \ + DECLARE_MOCK_CLIENT(name, return_type, param_types); \ + DECLARE_MOCK_MASTER(name, handle_index, param_types) + +#define DECLARE_STRUCT_CLASS_MOCK_STRUCT(struct_name) \ + struct MOCK(struct_name) { \ + struct mock ctrl; \ + struct struct_name trgt; \ + } + +#define DECLARE_STRUCT_CLASS_MOCK_CONVERTER(struct_name) \ + static inline struct mock *from_##struct_name##_to_mock( \ + const struct struct_name *trgt) \ + { \ + return mock_get_ctrl( \ + container_of(trgt, \ + struct MOCK(struct_name), \ + trgt)); \ + } + +/** + * DECLARE_STRUCT_CLASS_MOCK_PREREQS() - Create a mock child class + * @struct_name: name of the class/struct to be mocked + * + * Creates a mock child class of ``struct_name`` named + * ``struct MOCK(struct_name)`` along with supporting internally used methods. + * + * See TEST_EXPECT_CALL() for example usages. + */ +#define DECLARE_STRUCT_CLASS_MOCK_PREREQS(struct_name) \ + DECLARE_STRUCT_CLASS_MOCK_STRUCT(struct_name); \ + DECLARE_STRUCT_CLASS_MOCK_CONVERTER(struct_name) + +#define DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name, \ + struct_name, \ + handle_index, \ + return_type, \ + param_types...) \ + DECLARE_MOCK_COMMON(name, \ + handle_index, \ + return_type, \ + param_types) + +#define DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX(name, \ + struct_name, \ + handle_index, \ + return_type, \ + param_types...) \ + DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name, \ + struct_name, \ + handle_index, \ + return_type, \ + param_types) + +/** + * DECLARE_STRUCT_CLASS_MOCK() + * @name: method name + * @struct_name: name of the class/struct + * @return_type: return type of the method + * @param_types: parameters of the method + * + * Same as DEFINE_STRUCT_CLASS_MOCK(), but only makes header compatible + * declarations. + */ +#define DECLARE_STRUCT_CLASS_MOCK(name, \ + struct_name, \ + return_type, \ + param_types...) \ + DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX(name, \ + struct_name, \ + 0, \ + return_type, \ + param_types) + +/** + * DECLARE_STRUCT_CLASS_MOCK_VOID_RETURN() + * @name: method name + * @struct_name: name of the class/struct + * @param_types: parameters of the method + * + * Same as DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN(), but only makes header + * compatible declarations. + */ +#define DECLARE_STRUCT_CLASS_MOCK_VOID_RETURN(name, \ + struct_name, \ + param_types...) \ + DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX(name, \ + struct_name, \ + 0, \ + void, \ + param_types) + +/** + * DECLARE_STRUCT_CLASS_MOCK_INIT() + * @struct_name: name of the class/struct + * + * Same as DEFINE_STRUCT_CLASS_MOCK_INIT(), but only makes header compatible + * declarations. + */ +#define DECLARE_STRUCT_CLASS_MOCK_INIT(struct_name) \ + struct MOCK(struct_name) *MOCK_INIT_ID(struct_name)( \ + struct test *test) + +/** + * CONSTRUCT_MOCK() + * @struct_name: name of the class + * @test: associated test + * + * Constructs and allocates a test managed ``struct MOCK(struct_name)`` given + * the name of the class for which the mock is defined and a test object. + * + * See TEST_EXPECT_CALL() for example usage. + */ +#define CONSTRUCT_MOCK(struct_name, test) MOCK_INIT_ID(struct_name)(test) + +#define DEFINE_MOCK_CLIENT_COMMON(name, \ + handle_index, \ + MOCK_SOURCE, \ + mock_source_ctx, \ + return_type, \ + RETURN, \ + param_types...) \ + return_type name(PARAM_LIST_FROM_TYPES(param_types)) \ + { \ + struct mock *mock = MOCK_SOURCE(mock_source_ctx, \ + handle_index); \ + static const char * const param_type_names[] = { \ + TYPE_NAMES_FROM_TYPES(handle_index, \ + param_types) \ + }; \ + const void *params[] = { \ + PTR_TO_ARG_FROM_TYPES(handle_index, \ + param_types) \ + }; \ + const void *retval; \ + \ + retval = mock->do_expect(mock, \ + #name, \ + name, \ + param_type_names, \ + params, \ + ARRAY_SIZE(params)); \ + TEST_ASSERT_NOT_ERR_OR_NULL(mock->test, retval); \ + if (!retval) { \ + test_info(mock->test, \ + "no action installed for "#name); \ + BUG(); \ + } \ + RETURN(return_type, retval); \ + } + +#define CLASS_MOCK_CLIENT_SOURCE(ctx, handle_index) ctx(arg##handle_index) +#define DEFINE_MOCK_METHOD_CLIENT_COMMON(name, \ + handle_index, \ + mock_converter, \ + return_type, \ + RETURN, \ + param_types...) \ + DEFINE_MOCK_CLIENT_COMMON(name, \ + handle_index, \ + CLASS_MOCK_CLIENT_SOURCE, \ + mock_converter, \ + return_type, \ + RETURN, \ + param_types) + +#define CAST_AND_RETURN(return_type, retval) return *((return_type *) retval) +#define NO_RETURN(return_type, retval) + +#define DEFINE_MOCK_METHOD_CLIENT(name, \ + handle_index, \ + mock_converter, \ + return_type, \ + param_types...) \ + DEFINE_MOCK_METHOD_CLIENT_COMMON(name, \ + handle_index, \ + mock_converter, \ + return_type, \ + CAST_AND_RETURN, \ + param_types) + +#define DEFINE_MOCK_METHOD_CLIENT_VOID_RETURN(name, \ + handle_index, \ + mock_converter, \ + param_types...) \ + DEFINE_MOCK_METHOD_CLIENT_COMMON(name, \ + handle_index, \ + mock_converter, \ + void, \ + NO_RETURN, \ + param_types) + +#define DEFINE_MOCK_MASTER_COMMON_INTERNAL(name, \ + ctrl_index, \ + MOCK_SOURCE, \ + param_types...) \ + struct mock_expectation *mock_master_##name( \ + MATCHER_PARAM_LIST_FROM_TYPES(ctrl_index, \ + param_types)) \ + { \ + struct mock_param_matcher *matchers[] = { \ + ARG_NAMES_FROM_TYPES(ctrl_index, param_types) \ + }; \ + \ + return mock_add_matcher(MOCK_SOURCE(ctrl_index), \ + #name, \ + (const void *) name, \ + matchers, \ + ARRAY_SIZE(matchers)); \ + } +#define DEFINE_MOCK_MASTER_COMMON(name, \ + ctrl_index, \ + MOCK_SOURCE, \ + param_types...) \ + DEFINE_MOCK_MASTER_COMMON_INTERNAL(name, \ + ctrl_index, \ + MOCK_SOURCE, \ + param_types) + +#define CLASS_MOCK_MASTER_SOURCE(ctrl_index) arg##ctrl_index +#define DEFINE_MOCK_METHOD_MASTER(name, ctrl_index, param_types...) \ + DEFINE_MOCK_MASTER_COMMON(name, \ + ctrl_index, \ + CLASS_MOCK_MASTER_SOURCE, \ + param_types) + +#define DEFINE_MOCK_COMMON(name, \ + handle_index, \ + mock_converter, \ + return_type, \ + param_types...) \ + DEFINE_MOCK_METHOD_CLIENT(name, \ + handle_index, \ + mock_converter, \ + return_type, \ + param_types); \ + DEFINE_MOCK_METHOD_MASTER(name, handle_index, param_types) + +#define DEFINE_MOCK_COMMON_VOID_RETURN(name, \ + handle_index, \ + mock_converter, \ + param_types...) \ + DEFINE_MOCK_METHOD_CLIENT_VOID_RETURN(name, \ + handle_index, \ + mock_converter, \ + param_types); \ + DEFINE_MOCK_METHOD_MASTER(name, handle_index, param_types) + +#define DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name, \ + struct_name, \ + handle_index, \ + return_type, \ + param_types...) \ + DEFINE_MOCK_COMMON(name, \ + handle_index, \ + from_##struct_name##_to_mock, \ + return_type, param_types) +#define DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX(name, \ + struct_name, \ + handle_index, \ + return_type, \ + param_types...) \ + DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name, \ + struct_name, \ + handle_index, \ + return_type, \ + param_types) + +#define DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_VOID_RETURN_INTERNAL( \ + name, \ + struct_name, \ + handle_index, \ + param_types...) \ + DEFINE_MOCK_COMMON_VOID_RETURN(name, \ + handle_index, \ + from_##struct_name##_to_mock, \ + param_types) +#define DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_VOID_RETURN(name, \ + struct_name, \ + handle_index, \ + param_types...) \ + DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_VOID_RETURN_INTERNAL( \ + name, \ + struct_name, \ + handle_index, \ + param_types) + +/** + * DEFINE_STRUCT_CLASS_MOCK() + * @name: name of the method + * @struct_name: name of the class of which the method belongs + * @return_type: return type of the method to be created. **Must not be void.** + * @param_types: parameters to method to be created. + * + * See TEST_EXPECT_CALL() for example usage. + */ +#define DEFINE_STRUCT_CLASS_MOCK(name, \ + struct_name, \ + return_type, \ + param_types...) \ + DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX(name, \ + struct_name, \ + 0, \ + return_type, \ + param_types) + +/** + * DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN() + * @name: name of the method + * @struct_name: name of the class of which the method belongs + * @param_types: parameters to method to be created. + * + * Same as DEFINE_STRUCT_CLASS_MOCK() except the method has a ``void`` return + * type. + */ +#define DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN(name, struct_name, param_types...)\ + DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX_VOID_RETURN(name, \ + struct_name, \ + 0, \ + param_types) + +/** + * DEFINE_STRUCT_CLASS_MOCK_INIT() + * @struct_name: name of the class + * @init_func: a function of type ``int (*)(struct MOCK(struct_name) *)``. This + * function is passed a pointer to an allocated, *but not + * initialized*, ``struct MOCK(struct_name)``. The job of this user + * provided function is to perform remaining initialization. Usually + * this entails assigning mock methods to the function pointers in + * the parent struct. + * + * See TEST_EXPECT_CALL() for example usage. + */ +#define DEFINE_STRUCT_CLASS_MOCK_INIT(struct_name, init_func) \ + struct MOCK(struct_name) *MOCK_INIT_ID(struct_name)( \ + struct test *test) \ + { \ + struct MOCK(struct_name) *mock_obj; \ + \ + mock_obj = test_kzalloc(test, \ + sizeof(*mock_obj), \ + GFP_KERNEL); \ + if (!mock_obj) \ + return NULL; \ + \ + mock_init_ctrl(test, mock_get_ctrl(mock_obj)); \ + \ + if (init_func(mock_obj)) \ + return NULL; \ + \ + return mock_obj; \ + } + #define CONVERT_TO_ACTUAL_TYPE(type, ptr) (*((type *) ptr)) /** * DOC: Built In Matchers * * These are the matchers that can be used when matching arguments in - * :c:func:`EXPECT_CALL` (more can be defined manually). + * :c:func:`TEST_EXPECT_CALL` (more can be defined manually). * * For example, there's a matcher that matches any arguments: * diff --git a/kunit/Makefile b/kunit/Makefile index 52a1da46cbd21..6fccfcdbc6f84 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_KUNIT) += test.o mock.o common-mocks.o string-stream.o \ test-stream.o obj-$(CONFIG_KUNIT_TEST) += \ - test-test.o mock-macro-test.o string-stream-test.o + test-test.o test-mock.o mock-macro-test.o mock-test.o string-stream-test.o obj-$(CONFIG_EXAMPLE_TEST) += example-test.o diff --git a/kunit/example-test.c b/kunit/example-test.c index e9bd2b41c5fd2..dd3b75a61d5b4 100644 --- a/kunit/example-test.c +++ b/kunit/example-test.c @@ -7,6 +7,7 @@ */ #include +#include /* * This is the most fundamental element of KUnit, the test case. A test case @@ -29,6 +30,84 @@ static void example_simple_test(struct test *test) TEST_EXPECT_EQ(test, 1 + 1, 2); } +/* + * A lot of times, you have a C-style class like this, which acts an abstraction + * over hardware, a file system implementation, or some other subsystem that you + * want to reason about in a generic way. + */ +struct example { + int (*foo)(struct example *example, int num); +}; + +static int example_bar(struct example *example, int num) +{ + return example->foo(example, num); +} + +/* + * KUnit allows such a class to be "mocked out" with the following: + */ + +/* + * This macro creates a mock subclass of the specified class. + */ +DECLARE_STRUCT_CLASS_MOCK_PREREQS(example); + +/* + * This macro creates a mock implementation of the specified method of the + * specified class. + */ +DEFINE_STRUCT_CLASS_MOCK(METHOD(foo), CLASS(example), + RETURNS(int), + PARAMS(struct example *, int)); + +/* + * This tells KUnit how to initialize the parts of the mock that come from the + * parent. In this example, all we have to do is populate the member functions + * of the parent class with the mock versions we defined. + */ +static int example_init(struct MOCK(example) *mock_example) +{ + /* This is how you get a pointer to the parent class of a mock. */ + struct example *example = mock_get_trgt(mock_example); + + /* + * Here we populate the member function (method) with our mock method. + */ + example->foo = foo; + return 0; +} + +/* + * This registers our parent init function above, allowing KUnit to create a + * constructor for the mock. + */ +DEFINE_STRUCT_CLASS_MOCK_INIT(example, example_init); + +/* + * This is a test case where we use our mock. + */ +static void example_mock_test(struct test *test) +{ + struct MOCK(example) *mock_example = test->priv; + struct example *example = mock_get_trgt(mock_example); + struct mock_expectation *handle; + + /* + * Here we make an expectation that our mock method will be called with + * a parameter equal to 5 passed in. + */ + handle = TEST_EXPECT_CALL(foo(mock_get_ctrl(mock_example), + test_int_eq(test, 5))); + /* + * We specify that when our mock is called in this way, we want it to + * return 2. + */ + handle->action = test_int_return(test, 2); + + TEST_EXPECT_EQ(test, 2, example_bar(example, 5)); +} + /* * This is run once before each test case, see the comment on * example_test_module for more information. @@ -37,6 +116,16 @@ static int example_test_init(struct test *test) { test_info(test, "initializing"); + /* + * Here we construct the mock and store it in test's `priv` field; this + * field is for KUnit users. You can put whatever you want here, but + * most often it is a place that the init function can put stuff to be + * used by test cases. + */ + test->priv = CONSTRUCT_MOCK(example, test); + if (!test->priv) + return -EINVAL; + return 0; } @@ -52,6 +141,7 @@ static struct test_case example_test_cases[] = { * test module. */ TEST_CASE(example_simple_test), + TEST_CASE(example_mock_test), {}, }; diff --git a/kunit/mock-macro-test.c b/kunit/mock-macro-test.c index c30b859ff2b14..84d9d3f484366 100644 --- a/kunit/mock-macro-test.c +++ b/kunit/mock-macro-test.c @@ -8,6 +8,49 @@ #include #include +#include + +struct test_struct { + int (*one_param)(struct test_struct *test_struct); + int (*two_param)(struct test_struct *test_struct, int num); + int (*non_first_slot_param)(int num, struct test_struct *test_struct); + void *(*void_ptr_return)(struct test_struct *test_struct); +}; + +DECLARE_STRUCT_CLASS_MOCK_PREREQS(test_struct); + +DEFINE_STRUCT_CLASS_MOCK(METHOD(one_param), CLASS(test_struct), + RETURNS(int), + PARAMS(struct test_struct *)); + +DEFINE_STRUCT_CLASS_MOCK(METHOD(two_param), CLASS(test_struct), + RETURNS(int), + PARAMS(struct test_struct *, int)); + +DEFINE_STRUCT_CLASS_MOCK_HANDLE_INDEX(METHOD(non_first_slot_param), + CLASS(test_struct), HANDLE_INDEX(1), + RETURNS(int), + PARAMS(int, struct test_struct *)); + +DEFINE_STRUCT_CLASS_MOCK(METHOD(void_ptr_return), CLASS(test_struct), + RETURNS(void *), + PARAMS(struct test_struct *)); + +static int test_struct_init(struct MOCK(test_struct) *mock_test_struct) +{ + struct test_struct *test_struct = mock_get_trgt(mock_test_struct); + + test_struct->one_param = one_param; + test_struct->two_param = two_param; + test_struct->non_first_slot_param = non_first_slot_param; + return 0; +} + +DEFINE_STRUCT_CLASS_MOCK_INIT(test_struct, test_struct_init); + +struct mock_macro_context { + struct MOCK(test_struct) *mock_test_struct; +}; #define TO_STR_INTERNAL(...) #__VA_ARGS__ #define TO_STR(...) TO_STR_INTERNAL(__VA_ARGS__) @@ -131,6 +174,43 @@ static void mock_macro_arg_names_from_types(struct test *test) type15))); } +static void mock_macro_test_generated_method_code_works(struct test *test) +{ + struct mock_macro_context *ctx = test->priv; + struct MOCK(test_struct) *mock_test_struct = ctx->mock_test_struct; + struct test_struct *test_struct = mock_get_trgt(mock_test_struct); + struct mock_expectation *handle; + + handle = TEST_EXPECT_CALL(one_param(mock_get_ctrl(mock_test_struct))); + handle->action = test_int_return(test, 0); + handle = TEST_EXPECT_CALL(two_param(mock_get_ctrl(mock_test_struct), + test_int_eq(test, 5))); + handle->action = test_int_return(test, 1); + handle = TEST_EXPECT_CALL(non_first_slot_param( + test_int_eq(test, 5), mock_get_ctrl(mock_test_struct))); + handle->action = test_int_return(test, 1); + + test_struct->one_param(test_struct); + test_struct->two_param(test_struct, 5); + test_struct->non_first_slot_param(5, test_struct); +} + +static int mock_macro_test_init(struct test *test) +{ + struct mock_macro_context *ctx; + + ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + test->priv = ctx; + + ctx->mock_test_struct = CONSTRUCT_MOCK(test_struct, test); + if (!ctx->mock_test_struct) + return -EINVAL; + + return 0; +} + static struct test_case mock_macro_test_cases[] = { TEST_CASE(mock_macro_is_equal), TEST_CASE(mock_macro_if), @@ -139,11 +219,13 @@ static struct test_case mock_macro_test_cases[] = { TEST_CASE(mock_macro_for_each_param), TEST_CASE(mock_macro_param_list_from_types_basic), TEST_CASE(mock_macro_arg_names_from_types), + TEST_CASE(mock_macro_test_generated_method_code_works), {}, }; static struct test_module mock_macro_test_module = { .name = "mock-macro-test", + .init = mock_macro_test_init, .test_cases = mock_macro_test_cases, }; module_test(mock_macro_test_module); diff --git a/kunit/mock-test.c b/kunit/mock-test.c new file mode 100644 index 0000000000000..523ddee8f24e2 --- /dev/null +++ b/kunit/mock-test.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit test for mock.h. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include + +#include "test-mock.h" + +struct mock_test_context { + struct MOCK(test) *mock_test; + struct mock *mock; +}; + +static void mock_test_do_expect_basic(struct test *test) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test *trgt = mock_get_trgt(mock_test); + struct mock *mock = ctx->mock; + int param0 = 5, param1 = -4; + static const char * const two_param_types[] = {"int", "int"}; + const void *two_params[] = {¶m0, ¶m1}; + struct mock_param_matcher *matchers_any_two[] = { + test_any(trgt), test_any(trgt) + }; + struct mock_expectation *expectation; + const void *ret; + + expectation = mock_add_matcher(mock, + "", + NULL, + matchers_any_two, + ARRAY_SIZE(matchers_any_two)); + expectation->action = test_int_return(trgt, 5); + TEST_EXPECT_EQ(test, 0, expectation->times_called); + + ret = mock->do_expect(mock, + "", + NULL, + two_param_types, + two_params, + ARRAY_SIZE(two_params)); + TEST_ASSERT_NOT_ERR_OR_NULL(test, ret); + TEST_EXPECT_EQ(test, 5, *((int *) ret)); + TEST_EXPECT_EQ(test, 1, expectation->times_called); +} + +static void mock_test_ptr_eq(struct test *test) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test *trgt = mock_get_trgt(mock_test); + struct mock *mock = ctx->mock; + void *param0 = ctx, *param1 = trgt; + static const char * const two_param_types[] = {"void *", "void *"}; + const void *two_params[] = {¶m0, ¶m1}; + struct mock_param_matcher *matchers_two_ptrs[] = { + test_ptr_eq(trgt, param0), test_ptr_eq(trgt, param1) + }; + struct mock_expectation *expectation; + const void *ret; + + expectation = mock_add_matcher(mock, + "", + NULL, + matchers_two_ptrs, + ARRAY_SIZE(matchers_two_ptrs)); + expectation->action = test_int_return(trgt, 0); + TEST_EXPECT_EQ(test, 0, expectation->times_called); + + ret = mock->do_expect(mock, + "", + NULL, + two_param_types, + two_params, + ARRAY_SIZE(two_params)); + TEST_ASSERT_NOT_ERR_OR_NULL(test, ret); + TEST_EXPECT_EQ(test, 1, expectation->times_called); +} + +static void mock_test_ptr_eq_not_equal(struct test *test) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test *trgt = mock_get_trgt(mock_test); + struct mock *mock = ctx->mock; + void *param0 = ctx, *param1 = trgt; + static const char * const two_param_types[] = {"void *", "void *"}; + const void *two_params[] = {¶m0, ¶m1}; + struct mock_param_matcher *matchers_two_ptrs[] = { + test_ptr_eq(trgt, param0), test_ptr_eq(trgt, param1 - 1) + }; + struct mock_expectation *expectation; + const void *ret; + + expectation = mock_add_matcher(mock, + "", + NULL, + matchers_two_ptrs, + ARRAY_SIZE(matchers_two_ptrs)); + expectation->action = test_int_return(trgt, 0); + TEST_EXPECT_EQ(test, 0, expectation->times_called); + + ret = mock->do_expect(mock, + "", + NULL, + two_param_types, + two_params, + ARRAY_SIZE(two_params)); + TEST_EXPECT_FALSE(test, ret); + TEST_EXPECT_EQ(test, 0, expectation->times_called); +} + +/* + * In order for us to be able to rely on TEST_EXPECT_CALL to validate other + * behavior, we need to test that unsatisfied TEST_EXPECT_CALL causes a test + * failure. + */ +static void mock_test_failed_expect_call_fails_test(struct test *test) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct mock *mock = ctx->mock; + + /* mock is a pretend mock belonging to our mocked_test */ + + /* Put an expectation on mocked mock */ + TEST_EXPECT_CALL(fail(mock, test_any(mock_get_trgt(mock_test)))); + + /* + * Expect that mock_test will fail because the above won't be satisfied + */ + TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test), test_any(test))); + + /* + * Validate expectations of mocked mock, which should fail mocked test + */ + mock_validate_expectations(mock); + + /* Validate mock_test's expectations, that is, it should have failed */ + mock_validate_expectations(mock_get_ctrl(mock_test)); + TEST_EXPECT_FALSE(test, mock_get_trgt(mock_test)->success); +} + +static void mock_test_do_expect_default_return(struct test *test) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test *trgt = mock_get_trgt(mock_test); + struct mock *mock = ctx->mock; + int param0 = 5, param1 = -5; + static const char * const two_param_types[] = {"int", "int"}; + const void *two_params[] = {¶m0, ¶m1}; + struct mock_param_matcher *matchers[] = { + test_int_eq(trgt, 5), + test_int_eq(trgt, -4) + }; + struct mock_expectation *expectation; + const void *ret; + + expectation = mock_add_matcher(mock, + "test_printk", + test_printk, + matchers, + ARRAY_SIZE(matchers)); + expectation->action = test_int_return(trgt, 5); + TEST_EXPECT_EQ(test, 0, expectation->times_called); + + TEST_EXPECT_FALSE(test, mock_set_default_action(mock, + "test_printk", + test_printk, + test_int_return(trgt, -4))); + + ret = mock->do_expect(mock, + "test_printk", + test_printk, + two_param_types, + two_params, + ARRAY_SIZE(two_params)); + TEST_ASSERT_NOT_ERR_OR_NULL(test, ret); + TEST_EXPECT_EQ(test, -4, *((int *) ret)); + TEST_EXPECT_EQ(test, 0, expectation->times_called); +} + +static void mock_test_mock_validate_expectations(struct test *test) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test *trgt = mock_get_trgt(mock_test); + struct mock *mock = ctx->mock; + struct mock_param_matcher *matchers[] = { + test_int_eq(trgt, 5), + test_int_eq(trgt, -4) + }; + struct mock_expectation *expectation; + + TEST_EXPECT_EQ(test, mock_get_trgt(mock_test), mock->test); + + expectation = mock_add_matcher(mock, + "test_printk", + test_printk, + matchers, + ARRAY_SIZE(matchers)); + expectation->times_called = 0; + expectation->min_calls_expected = 1; + expectation->max_calls_expected = 1; + + TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test), test_any(test))); + + mock_validate_expectations(mock); +} + +void *do_mocked_fail(struct mock_action *this, const void **params, int len) +{ + static const int ret; + struct test_stream * const *stream_ptr = params[0]; + struct test_stream *stream = *stream_ptr; + + stream->set_level(stream, KERN_ERR); + stream->commit(stream); + return (void *) &ret; +} + +static struct mock_action mocked_fail = { + .do_action = do_mocked_fail +}; + +static int mock_test_init(struct test *test) +{ + struct mock_test_context *ctx; + + ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + test->priv = ctx; + + ctx->mock_test = CONSTRUCT_MOCK(test, test); + if (!ctx->mock_test) + return -EINVAL; + + ctx->mock = test_kzalloc(test, sizeof(*ctx->mock), GFP_KERNEL); + if (!ctx->mock) + return -ENOMEM; + mock_init_ctrl(mock_get_trgt(ctx->mock_test), ctx->mock); + + /* This test suite tests the behaviour of the error messages printed + * when mocks fail, which requires the mocked fail to commit the + * stream. + */ + mock_set_default_action(mock_get_ctrl(ctx->mock_test), + "fail", fail, &mocked_fail); + return 0; +} + +static struct test_case mock_test_cases[] = { + TEST_CASE(mock_test_do_expect_basic), + TEST_CASE(mock_test_ptr_eq), + TEST_CASE(mock_test_ptr_eq_not_equal), + TEST_CASE(mock_test_failed_expect_call_fails_test), + TEST_CASE(mock_test_do_expect_default_return), + TEST_CASE(mock_test_mock_validate_expectations), + {}, +}; + +static struct test_module mock_test_module = { + .name = "mock-test", + .init = mock_test_init, + .test_cases = mock_test_cases, +}; + +module_test(mock_test_module); diff --git a/kunit/test-mock.c b/kunit/test-mock.c new file mode 100644 index 0000000000000..a0858d6b3f9c1 --- /dev/null +++ b/kunit/test-mock.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit mock for struct test. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include "test-mock.h" + +DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN(METHOD(fail), CLASS(test), + PARAMS(struct test *, + struct test_stream *)); + +DEFINE_STRUCT_CLASS_MOCK_VOID_RETURN(METHOD(mock_vprintk), CLASS(test), + PARAMS(const struct test *, + const char *, + struct va_format *)); + +static int test_init(struct MOCK(test) *mock_test) +{ + struct test *trgt = mock_get_trgt(mock_test); + int ret; + + ret = test_init_test(trgt, "MOCK(test)"); + trgt->fail = fail; + mock_set_default_action(mock_get_ctrl(mock_test), + "fail", + fail, + test_int_return(mock_get_test(mock_test), 0)); + trgt->vprintk = mock_vprintk; + mock_set_default_action(mock_get_ctrl(mock_test), + "mock_vprintk", + mock_vprintk, + test_int_return(mock_get_test(mock_test), 0)); + return ret; +} + +DEFINE_STRUCT_CLASS_MOCK_INIT(test, test_init); diff --git a/kunit/test-mock.h b/kunit/test-mock.h new file mode 100644 index 0000000000000..c57e9384b1d8a --- /dev/null +++ b/kunit/test-mock.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * KUnit mock for struct test. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include + +DECLARE_STRUCT_CLASS_MOCK_PREREQS(test); + +DECLARE_STRUCT_CLASS_MOCK_VOID_RETURN(METHOD(fail), CLASS(test), + PARAMS(struct test *, + struct test_stream *)); + +DECLARE_STRUCT_CLASS_MOCK_VOID_RETURN(METHOD(mock_vprintk), CLASS(test), + PARAMS(const struct test *, + const char *, + struct va_format *)); + +DECLARE_STRUCT_CLASS_MOCK_INIT(test); From patchwork Tue Oct 16 23:51:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985051 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="DoFglWfs"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="dG5MD/pz"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHT4nCWz9sBN for ; Wed, 17 Oct 2018 10:55:17 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=x7F+HebwkfUR4D7ewJwlNCeUfZTw0hjZ6cEp4wLCwmA=; b=DoFglWfsnPfcyd jyyuzVb+h1p0B0DUV23vN4qrGHY8HI3u+s8431uox5hPmOWcC5waruGKNz4db54IIu96Jo8smkW21 PKne5wrPvXE5ccEUavGLlGiVr9IicEwwB9BHpl9PT4QVLQ8LIA++L/F3RKAIOnyyLFFh60XrpqdTD NF/+B+XXDYb2V+7rLPS7n7H5FIMAHGvtGUqKFPKMiyKbgvBOZrhBiG4qJKR4jSm+rIYP2KOTpz9so 332ISNSOThLczl/CtgIVKeaUVy7OewYZhn8XX8Hzm3b67hpcto6O2P+/VSb0kXWALeRlouqf/ZV31 3dHHsEhZxpbt0tjmzA+g==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZB4-0004lN-Sx; Tue, 16 Oct 2018 23:55:07 +0000 Received: from mail-it1-x14a.google.com ([2607:f8b0:4864:20::14a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAe-0004MP-Bj for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:54:59 +0000 Received: by mail-it1-x14a.google.com with SMTP id y73-v6so268190ita.2 for ; Tue, 16 Oct 2018 16:54:29 -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=lfNINMQ+IyHrLDQeQUVkm//8VUVasJhq9PVEdLv21nY=; b=dG5MD/pzsjGCpoKMy2PSS8155+4z5wONx8PWgP2KNQi/BJByt+XuSXqFPg+mcRzE6b 2CT+PFk3nWpaD1vwaiY2PXJUxXQBceFqeQRXyxpxwt5W6+wlMd/Xbe+j2AkrZZ77Tcff TvZinXEV0XDXr6UBeNxISWHKwLgzdYFGARspt2ceEYIL9+/uk/3S3Hzg5nF7PJDcWyja etjMxVUQmvOYm72FNnUP+mzKerbFhR/7V4LJgg4UOkAmxCc/gWNVdOOnpl7RY+05b+M5 uzmeKVv/SCJLfM+aIVTKhT1bNemNL7u/idoUkAHsHkgcIR7afoqkf+ItDVHXFVPwN34U Opgg== 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=lfNINMQ+IyHrLDQeQUVkm//8VUVasJhq9PVEdLv21nY=; b=eDQsClq8fLjnPpCgjpE1ZYOHXAtai26GWb71vdEmWHZ9KJu87Wwf65Sn9bW2gr9qlj rS7WIbScILMFwmLjVns87XTozevxX3QTL1d5x3g+T/MuSOasyt6vTCgyO4UwO62xM6dO UecUVhPsXZ+++r22+AR/y+ZgQI0SznWa2LbVCpqUOZjRXnOK4Iaofp9lHoU6F0Jw+tGJ gCM9E6JAOc8y5ktr8IFVrmi9tSpOPZT8kXjsS6pjn1n9zCkGcrpnpx/R/ivGH76utHTW c3YWrXrv1c/ybCZidPSuCa0y9Cmtu4lQXOfS3wHhRcEKiJBUQ6JwfoGCl7fnoQygfJMk wGSw== X-Gm-Message-State: ABuFfoi/dMCyjL1dmmEKEP14H0Z97G+QvOprfMvKfkNO8xxJFJA9r1O8 soVIXyLxOrBNf1SfibJScMTN6xYW544rjXlCpc223g== X-Google-Smtp-Source: ACcGV60Xwiqanmyz+prnq+7iNzan30s043A+XTR5tldda/2sWKDM+suB9CFthV9Ju05AdksQBUTyhLbrl+yS2cEuWD5woQ== X-Received: by 2002:a24:2ecd:: with SMTP id i196-v6mr202768ita.7.1539734068734; Tue, 16 Oct 2018 16:54:28 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:06 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-18-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 17/31] kunit: mock: added struct param matcher From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165440_526595_1E37263E X-CRM114-Status: GOOD ( 16.13 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:14a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Added parameter matcher builder for matching struct values. Signed-off-by: Brendan Higgins --- include/kunit/mock.h | 56 +++++++++++++++ kunit/Makefile | 5 +- kunit/common-mocks.c | 116 ++++++++++++++++++++++++++++++ kunit/mock-test.c | 43 +++++++++++ kunit/test-stream-test.c | 150 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 368 insertions(+), 2 deletions(-) create mode 100644 kunit/test-stream-test.c diff --git a/include/kunit/mock.h b/include/kunit/mock.h index 1b7485e2cedb8..daf965cf954e6 100644 --- a/include/kunit/mock.h +++ b/include/kunit/mock.h @@ -760,6 +760,14 @@ struct mock_param_matcher *test_memeq(struct test *test, size_t size); struct mock_param_matcher *test_streq(struct test *test, const char *str); +struct mock_param_matcher *test_str_contains(struct test *test, + const char *needle); + +/* Matches var-arg arguments. */ +struct mock_param_matcher *test_va_format_cmp(struct test *test, + struct mock_param_matcher *fmt_matcher, + struct mock_param_matcher *va_matcher); + struct mock_action *test_u8_return(struct test *test, u8 ret); struct mock_action *test_u16_return(struct test *test, u16 ret); struct mock_action *test_u32_return(struct test *test, u32 ret); @@ -778,4 +786,52 @@ struct mock_action *test_ulonglong_return(struct test *test, unsigned long long ret); struct mock_action *test_ptr_return(struct test *test, void *ret); +/** + * struct mock_struct_matcher_entry - composed with other &struct + * mock_struct_matcher_entry to make a + * &struct struct_matcher + * @member_offset: offset of this member + * @matcher: matcher for this particular member + * + * This is used for struct_cmp() matchers. + */ +struct mock_struct_matcher_entry { + size_t member_offset; + struct mock_param_matcher *matcher; +}; + +static inline void init_mock_struct_matcher_entry_internal( + struct mock_struct_matcher_entry *entry, + size_t offset, + struct mock_param_matcher *matcher) +{ + entry->member_offset = offset; + entry->matcher = matcher; +} + +/** + * INIT_MOCK_STRUCT_MATCHER_ENTRY() + * @entry: the &struct mock_struct_matcher_entry to initialize + * @type: the struct being matched + * @member: the member of the struct being matched, used to calculate the offset + * @matcher: matcher to match that member + * + * Initializes ``entry`` to match ``type->member`` with ``matcher``. + */ +#define INIT_MOCK_STRUCT_MATCHER_ENTRY(entry, type, member, matcher) \ + init_mock_struct_matcher_entry_internal(entry, \ + offsetof(type, member),\ + matcher) + +static inline void INIT_MOCK_STRUCT_MATCHER_ENTRY_LAST( + struct mock_struct_matcher_entry *entry) +{ + entry->matcher = NULL; +} + +struct mock_param_matcher *test_struct_cmp( + struct test *test, + const char *struct_name, + struct mock_struct_matcher_entry *entries); + #endif /* _KUNIT_MOCK_H */ diff --git a/kunit/Makefile b/kunit/Makefile index 6fccfcdbc6f84..e05fbcae8bfb0 100644 --- a/kunit/Makefile +++ b/kunit/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_KUNIT) += test.o mock.o common-mocks.o string-stream.o \ test-stream.o -obj-$(CONFIG_KUNIT_TEST) += \ - test-test.o test-mock.o mock-macro-test.o mock-test.o string-stream-test.o +obj-$(CONFIG_KUNIT_TEST) += \ + test-test.o test-mock.o mock-macro-test.o mock-test.o string-stream-test.o \ + test-stream-test.o obj-$(CONFIG_EXAMPLE_TEST) += example-test.o diff --git a/kunit/common-mocks.c b/kunit/common-mocks.c index ecac9c1c29c0e..ef88f8b8acda3 100644 --- a/kunit/common-mocks.c +++ b/kunit/common-mocks.c @@ -207,6 +207,122 @@ struct mock_param_matcher *test_streq(struct test *test, const char *str) return &matcher->matcher; } +struct mock_str_contains_matcher { + struct mock_param_matcher matcher; + const char *needle; +}; + +static bool match_str_contains(struct mock_param_matcher *pmatcher, + struct test_stream *stream, + const void *phaystack) +{ + struct mock_str_contains_matcher *matcher = + container_of(pmatcher, + struct mock_str_contains_matcher, + matcher); + const char *haystack = CONVERT_TO_ACTUAL_TYPE(const char *, phaystack); + bool matches = strstr(haystack, matcher->needle); + + if (matches) + stream->add(stream, + "'%s' found in '%s'", + matcher->needle, + haystack); + else + stream->add(stream, + "'%s' not found in '%s'", + matcher->needle, + haystack); + return matches; +} + +struct mock_param_matcher *test_str_contains(struct test *test, const char *str) +{ + struct mock_str_contains_matcher *matcher; + + matcher = test_kzalloc(test, sizeof(*matcher), GFP_KERNEL); + if (!matcher) + return NULL; + + matcher->matcher.match = match_str_contains; + matcher->needle = str; + + return &matcher->matcher; +} + +struct mock_param_matcher *test_va_format_cmp( + struct test *test, + struct mock_param_matcher *fmt_matcher, + struct mock_param_matcher *va_matcher) +{ + struct mock_struct_matcher_entry *entries; + + entries = test_kzalloc(test, sizeof(*entries) * 3, GFP_KERNEL); + if (!entries) + return NULL; + + INIT_MOCK_STRUCT_MATCHER_ENTRY(&entries[0], + struct va_format, + fmt, + fmt_matcher); + INIT_MOCK_STRUCT_MATCHER_ENTRY(&entries[1], + struct va_format, + va, + va_matcher); + INIT_MOCK_STRUCT_MATCHER_ENTRY_LAST(&entries[2]); + + return test_struct_cmp(test, "va_format", entries); +} + +struct mock_struct_matcher { + struct mock_param_matcher matcher; + const char *struct_name; + struct mock_struct_matcher_entry *entries; +}; + +static bool match_struct(struct mock_param_matcher *pmatcher, + struct test_stream *stream, + const void *pactual) +{ + struct mock_struct_matcher *matcher = + container_of(pmatcher, + struct mock_struct_matcher, + matcher); + struct mock_struct_matcher_entry *entry; + const char *actual = CONVERT_TO_ACTUAL_TYPE(const char *, pactual); + const char *member_ptr; + bool matches = true, tmp; + + stream->add(stream, "struct %s {", matcher->struct_name); + for (entry = matcher->entries; entry->matcher; entry++) { + member_ptr = actual + entry->member_offset; + tmp = entry->matcher->match(entry->matcher, stream, member_ptr); + matches = matches && tmp; + stream->add(stream, ", "); + } + stream->add(stream, "}"); + + return matches; +} + +struct mock_param_matcher *test_struct_cmp( + struct test *test, + const char *struct_name, + struct mock_struct_matcher_entry *entries) +{ + struct mock_struct_matcher *matcher; + + matcher = test_kzalloc(test, sizeof(*matcher), GFP_KERNEL); + if (!matcher) + return NULL; + + matcher->matcher.match = match_struct; + matcher->struct_name = struct_name; + matcher->entries = entries; + + return &matcher->matcher; +} + #define DEFINE_RETURN_ACTION_STRUCT(type_name, type) \ struct mock_##type_name##_action { \ struct mock_action action; \ diff --git a/kunit/mock-test.c b/kunit/mock-test.c index 523ddee8f24e2..77b16ad754424 100644 --- a/kunit/mock-test.c +++ b/kunit/mock-test.c @@ -187,6 +187,48 @@ static void mock_test_do_expect_default_return(struct test *test) TEST_EXPECT_EQ(test, 0, expectation->times_called); } +/* + * Method called on naggy mock with no expectations will not fail, but will show + * a warning message + */ +static void mock_test_naggy_no_expectations_no_fail(struct test *test) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test *trgt = mock_get_trgt(mock_test); + struct mock *mock = ctx->mock; + int param0 = 5, param1 = -5; + static const char * const two_param_types[] = {"int", "int"}; + const void *two_params[] = {¶m0, ¶m1}; + struct mock_expectation *expectation; + + mock_set_default_action(mock, + "test_printk", + test_printk, + test_int_return(trgt, -4)); + + expectation = TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test), + test_any(test))); + expectation->min_calls_expected = 0; + expectation->max_calls_expected = 0; + + TEST_EXPECT_CALL(mock_vprintk( + mock_get_ctrl(mock_test), + test_any(test), + test_va_format_cmp(test, + test_str_contains(test, + "Method was called with no expectations declared"), + test_any(test)))); + + mock->do_expect(mock, + "test_printk", + test_printk, + two_param_types, + two_params, + ARRAY_SIZE(two_params)); + mock_validate_expectations(mock); +} + static void mock_test_mock_validate_expectations(struct test *test) { struct mock_test_context *ctx = test->priv; @@ -264,6 +306,7 @@ static struct test_case mock_test_cases[] = { TEST_CASE(mock_test_failed_expect_call_fails_test), TEST_CASE(mock_test_do_expect_default_return), TEST_CASE(mock_test_mock_validate_expectations), + TEST_CASE(mock_test_naggy_no_expectations_no_fail), {}, }; diff --git a/kunit/test-stream-test.c b/kunit/test-stream-test.c new file mode 100644 index 0000000000000..875b0db15878d --- /dev/null +++ b/kunit/test-stream-test.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit test for struct test_stream. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include + +#include "test-mock.h" + +struct test_stream_test_context { + struct MOCK(test) *mock_test; + struct test_stream *stream; +}; + +static void test_stream_test_add(struct test *test) +{ + struct test_stream_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test_stream *stream = ctx->stream; + + stream->add(stream, "Foo"); + stream->add(stream, " %s", "bar"); + stream->set_level(stream, KERN_INFO); + + TEST_EXPECT_CALL(mock_vprintk( + mock_get_ctrl(mock_test), + test_any(test), + test_va_format_cmp(test, + test_streq(test, "Foo bar"), + test_any(test)))); + + stream->commit(stream); +} + +static void test_stream_test_append(struct test *test) +{ + struct test_stream_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test_stream *stream = ctx->stream; + struct test_stream *other_stream; + + stream->add(stream, "Foo"); + stream->set_level(stream, KERN_INFO); + other_stream = test_new_stream(mock_get_trgt(mock_test)); + other_stream->add(other_stream, " %s", "bar"); + + stream->append(stream, other_stream); + TEST_EXPECT_CALL(mock_vprintk( + mock_get_ctrl(mock_test), + test_any(test), + test_va_format_cmp(test, + test_streq(test, "Foo bar"), + test_any(test)))); + + stream->commit(stream); +} + +static void test_stream_error_message_when_no_level_set(struct test *test) +{ + struct test_stream_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test_stream *stream = ctx->stream; + struct test_stream *other_stream; + + stream->add(stream, "Foo bar"); + other_stream = test_new_stream(mock_get_trgt(mock_test)); + + stream->append(stream, other_stream); + TEST_EXPECT_CALL(mock_vprintk( + mock_get_ctrl(mock_test), + test_any(test), + test_va_format_cmp(test, + test_streq(test, + "Stream was committed without a specified log level."), + test_any(test)))); + TEST_EXPECT_CALL(mock_vprintk( + mock_get_ctrl(mock_test), + test_any(test), + test_va_format_cmp(test, + test_streq(test, "Foo bar"), + test_any(test)))); + stream->commit(stream); +} + +static void test_stream_test_commits_any_uncommitted_when_cleanup( + struct test *test) +{ + struct test_stream_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test_stream *stream = ctx->stream; + + stream->add(stream, "Hello World"); + stream->set_level(stream, KERN_WARNING); + + TEST_EXPECT_CALL(mock_vprintk( + mock_get_ctrl(mock_test), + test_any(test), + test_va_format_cmp(test, + test_streq(test, + "End of test case reached with uncommitted stream entries."), + test_any(test)))); + TEST_EXPECT_CALL(mock_vprintk( + mock_get_ctrl(mock_test), + test_any(test), + test_va_format_cmp(test, + test_streq(test, + "Hello World"), + test_any(test)))); + test_cleanup(mock_get_trgt(mock_test)); +} + +static int test_stream_test_init(struct test *test) +{ + struct test_stream_test_context *ctx; + + ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + test->priv = ctx; + + ctx->mock_test = CONSTRUCT_MOCK(test, test); + if (!ctx->mock_test) + return -EINVAL; + + ctx->stream = test_new_stream(mock_get_trgt(ctx->mock_test)); + if (!ctx->stream) + return -ENOMEM; + + return 0; +} + +static struct test_case test_stream_test_cases[] = { + TEST_CASE(test_stream_test_add), + TEST_CASE(test_stream_test_append), + TEST_CASE(test_stream_test_commits_any_uncommitted_when_cleanup), + TEST_CASE(test_stream_error_message_when_no_level_set), + {}, +}; + +static struct test_module test_stream_test_module = { + .name = "test-stream-test", + .init = test_stream_test_init, + .test_cases = test_stream_test_cases, +}; +module_test(test_stream_test_module); From patchwork Tue Oct 16 23:51:07 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985053 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="CqTo8rgZ"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="Q/tv9vgZ"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHb0j4Kz9s9J for ; Wed, 17 Oct 2018 10:55:22 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=P+rMrKHBijYyQ6P6iUSP3fIqKn43sqC5rm89lFJ4iZo=; b=CqTo8rgZyVRoM2 aYlIqbf/VEyBmX+54rVxfhTvGi3iCR9dDAJ7IvxANmeqNXebD9HVJIcOo5Rp9RB9vvyfN1n13Hdcq 9W1YjZArmu0qg3LmfBVlVOG/x73e4zeHOavANeLW66LEvP/5JKdHLQCDlTAU8+tm5wyJkmSGAQ0eW I8IolqUiFDr5+IBCtAVzw7gofOWkFW44C/a8/8igMLuiWrFI/8sV+JNjq4BeH9ujCimt/row2cgIe S6iVsaaj/Gyl7+ThVYHVB1sxYsmCzIVIDYasfeBvKJc0d0ewOpVaF2c38rakAC8LwpHNBJGJTyVDQ mFv9uebG7hlZJmiEuoWQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZB9-0004yd-Cx; Tue, 16 Oct 2018 23:55:11 +0000 Received: from mail-yb1-xb49.google.com ([2607:f8b0:4864:20::b49]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAh-0004NY-BM for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:01 +0000 Received: by mail-yb1-xb49.google.com with SMTP id g194-v6so13894003ybf.5 for ; Tue, 16 Oct 2018 16:54: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=NPgAtxmzKWmzJQDMuIIOgBsv8K6wiZu8ozAY1cnx0F8=; b=Q/tv9vgZsEphVdS8HaQDVBjgIEXGCpYldgs1kF++knTxbQoZofgIh2eqo0t1KqAWnO 8X8Ki8IfmT4QpY4TzVkXbIABTK87mF9uGnn2i4+/yQsqCVYm6i5NFVJqkDeZ22T3khrp BiqpvgivIDIGmLPn2vl1nZLnLn7AIZYptlM6x1C5JHoEDcgA0Mf1K9vs/T+ObfZ2MY3M 2whtguxy29sW2/mO4wDsRo06rwG4Ui3rj+QO0IGku1c403mq/DJ91s9crxCuTtILqAGc cRNTWVh3kpdc+oHAVuG3gGCV+akOA7I9NmEOQBrZ6TVlU4/ZPVAPwS9q+unFEN8FWfGj VQkA== 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=NPgAtxmzKWmzJQDMuIIOgBsv8K6wiZu8ozAY1cnx0F8=; b=er1M17MuvLDqfoCo1IBbpuHvf4A3S+SmFmD1OQOe2RiG1ry1c50aXMaRI1h17fikWd lwP0Mr0350EUoT7q2nUpkBx0+0azDAos5iySa9rRGBsGOZ9Y1r3WMcXCdhCaAV0uPnDb Eb2Dt6Q55bUG62eGtnfI7q6IMro27nxF0o5AReYrwJFk7ItVD++bCowiU56Rv32z30Eg K8J6pm/UV8swjmzYo+zekc7ymS84vCDTRoGXs7DigM0xZjlMu36StYnWO5CoTNC/I3ka vsqtIU9HTzt+sELdMc6yCSj8zTh+WJg7NQiDlhNVGD7JLDTgz/g/ScjOZeWZBxnuR4jz UhQg== X-Gm-Message-State: ABuFfohEsHxkLjr32XqsFEktQ7FXSOy9Y/CcH1EDcOkL1sg46OU25blo rxWq/5XL66ZVx2sERfiRK3I+OvcD1l42bvu6ukYZ1g== X-Google-Smtp-Source: ACcGV61nBpihFd18QPAhR/+fL6wQRLHt3ulP5Yqt//HsZMAp8D37O46JimAE0LsjbahZLMwMg5n+RVsupdDO/PfLOIeY7A== X-Received: by 2002:a25:a122:: with SMTP id z31-v6mr13459409ybh.85.1539734071113; Tue, 16 Oct 2018 16:54:31 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:07 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-19-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 18/31] kunit: mock: added parameter formatters From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165443_419461_F82474DE X-CRM114-Status: GOOD ( 12.31 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:b49 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Added parameter formatters which provide string formatting for parameters that have no matchers to be matched against. Signed-off-by: Brendan Higgins --- include/kunit/mock.h | 49 +++++++++++++++ kunit/common-mocks.c | 132 +++++++++++++++++++++++++++++++++++++++ kunit/mock.c | 48 ++++++++++++-- kunit/test-stream-test.c | 28 +++++++++ 4 files changed, 252 insertions(+), 5 deletions(-) diff --git a/include/kunit/mock.h b/include/kunit/mock.h index daf965cf954e6..4f85b39d628d0 100644 --- a/include/kunit/mock.h +++ b/include/kunit/mock.h @@ -123,6 +123,18 @@ struct mock_expectation *mock_add_matcher(struct mock *mock, struct mock_param_matcher *matchers[], int len); +struct mock_param_formatter { + struct list_head node; + const char *type_name; + void (*format)(struct mock_param_formatter *formatter, + struct test_stream *stream, + const void *param); +}; + +void mock_register_formatter(struct mock_param_formatter *formatter); + +void mock_unregister_formatter(struct mock_param_formatter *formatter); + #define MOCK(name) name##_mock /** @@ -834,4 +846,41 @@ struct mock_param_matcher *test_struct_cmp( const char *struct_name, struct mock_struct_matcher_entry *entries); +struct mock_struct_formatter_entry { + size_t member_offset; + struct mock_param_formatter *formatter; +}; + +static inline void init_mock_struct_formatter_entry_internal( + struct mock_struct_formatter_entry *entry, + size_t offset, + struct mock_param_formatter *formatter) +{ + entry->member_offset = offset; + entry->formatter = formatter; +} + +#define INIT_MOCK_STRUCT_FORMATTER_ENTRY(entry, type, member, formatter) \ + init_mock_struct_formatter_entry_internal(entry, \ + offsetof(type, \ + member), \ + formatter) + +static inline void INIT_MOCK_STRUCT_FORMATTER_ENTRY_LAST( + struct mock_struct_formatter_entry *entry) +{ + entry->formatter = NULL; +} + +struct mock_param_formatter *mock_struct_formatter( + struct test *test, + const char *struct_name, + struct mock_struct_formatter_entry *entries); + +struct mock_param_formatter *mock_find_formatter(const char *type_name); + +#define FORMATTER_FROM_TYPE(type) mock_find_formatter(#type) + +extern struct mock_param_formatter unknown_formatter[]; + #endif /* _KUNIT_MOCK_H */ diff --git a/kunit/common-mocks.c b/kunit/common-mocks.c index ef88f8b8acda3..1c52522808cab 100644 --- a/kunit/common-mocks.c +++ b/kunit/common-mocks.c @@ -386,3 +386,135 @@ DEFINE_RETURN_ACTION_WITH_TYPENAME(longlong, long long); DEFINE_RETURN_ACTION_WITH_TYPENAME(ulonglong, unsigned long long); DEFINE_RETURN_ACTION_WITH_TYPENAME(ptr, void *); +struct mock_param_integer_formatter { + struct mock_param_formatter formatter; + const char *fmt_str; +}; + +static void mock_format_integer(struct mock_param_formatter *pformatter, + struct test_stream *stream, + const void *pparam) +{ + struct mock_param_integer_formatter *formatter = + container_of(pformatter, + struct mock_param_integer_formatter, + formatter); + long long param = CONVERT_TO_ACTUAL_TYPE(long long, pparam); + + stream->add(stream, formatter->fmt_str, param); +} + +#define INTEGER_FORMATTER_INIT(type, fmt) { \ + .formatter = { \ + .type_name = #type, \ + .format = mock_format_integer, \ + }, \ + .fmt_str = fmt, \ +} + +static struct mock_param_integer_formatter integer_formatters[] = { + INTEGER_FORMATTER_INIT(u8, "%PRIu8"), + INTEGER_FORMATTER_INIT(u16, "%PRIu16"), + INTEGER_FORMATTER_INIT(u32, "%PRIu32"), + INTEGER_FORMATTER_INIT(u64, "%PRIu64"), + INTEGER_FORMATTER_INIT(char, "%c"), + INTEGER_FORMATTER_INIT(unsigned char, "%hhu"), + INTEGER_FORMATTER_INIT(signed char, "%hhd"), + INTEGER_FORMATTER_INIT(short, "%hd"), + INTEGER_FORMATTER_INIT(unsigned short, "%hu"), + INTEGER_FORMATTER_INIT(int, "%d"), + INTEGER_FORMATTER_INIT(unsigned int, "%u"), + INTEGER_FORMATTER_INIT(long, "%ld"), + INTEGER_FORMATTER_INIT(unsigned long, "%lu"), + INTEGER_FORMATTER_INIT(long long, "%lld"), + INTEGER_FORMATTER_INIT(unsigned long long, "%llu"), + INTEGER_FORMATTER_INIT(void *, "%px"), +}; + +static void mock_format_string(struct mock_param_formatter *formatter, + struct test_stream *stream, + const void *pparam) +{ + const char *param = CONVERT_TO_ACTUAL_TYPE(const char *, pparam); + + stream->add(stream, "%s", param); +} + +static struct mock_param_formatter string_formatter = { + .type_name = "const char *", + .format = mock_format_string, +}; + +static void mock_format_unknown(struct mock_param_formatter *formatter, + struct test_stream *stream, + const void *param) +{ + stream->add(stream, "%pS", param); +} + +struct mock_param_formatter unknown_formatter[] = { + { + .type_name = "", + .format = mock_format_unknown, + }, + {}, +}; + +static int mock_register_all_formatters(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(integer_formatters); i++) + mock_register_formatter(&integer_formatters[i].formatter); + + mock_register_formatter(&string_formatter); + + return 0; +} +test_pure_initcall(mock_register_all_formatters); + +struct mock_struct_formatter { + struct mock_param_formatter formatter; + const char *type_name; + struct mock_struct_formatter_entry *entries; +}; + +static void mock_format_struct(struct mock_param_formatter *pformatter, + struct test_stream *stream, + const void *pparam) +{ + struct mock_struct_formatter *formatter = + container_of(pformatter, + struct mock_struct_formatter, + formatter); + struct mock_struct_formatter_entry *entry; + const char *param = CONVERT_TO_ACTUAL_TYPE(const char *, pparam); + const char *member_ptr; + + stream->add(stream, "%s {", formatter->type_name); + for (entry = formatter->entries; entry->formatter; entry++) { + member_ptr = param + entry->member_offset; + entry->formatter->format(entry->formatter, stream, member_ptr); + stream->add(stream, ", "); + } + stream->add(stream, "}"); +} + +struct mock_param_formatter *mock_struct_formatter( + struct test *test, + const char *type_name, + struct mock_struct_formatter_entry *entries) +{ + struct mock_struct_formatter *formatter; + + formatter = test_kzalloc(test, sizeof(*formatter), GFP_KERNEL); + if (!formatter) + return NULL; + + formatter->formatter.type_name = type_name; + formatter->formatter.format = mock_format_struct; + formatter->type_name = type_name; + formatter->entries = entries; + + return &formatter->formatter; +} diff --git a/kunit/mock.c b/kunit/mock.c index 424c612de553b..9be6b2d3621c4 100644 --- a/kunit/mock.c +++ b/kunit/mock.c @@ -186,15 +186,53 @@ int mock_set_default_action(struct mock *mock, return 0; } +struct mock_param_formatter_repo { + struct list_head formatters; +}; + +static struct mock_param_formatter_repo mock_param_formatter_repo = { + .formatters = LIST_HEAD_INIT(mock_param_formatter_repo.formatters), +}; + +void mock_register_formatter(struct mock_param_formatter *formatter) +{ + list_add_tail(&formatter->node, &mock_param_formatter_repo.formatters); +} + +void mock_unregister_formatter(struct mock_param_formatter *formatter) +{ + list_del(&formatter->node); +} + +struct mock_param_formatter *mock_find_formatter(const char *type_name) +{ + struct mock_param_formatter *formatter; + + list_for_each_entry(formatter, + &mock_param_formatter_repo.formatters, + node) { + if (!strcmp(type_name, formatter->type_name)) + return formatter; + } + + return NULL; +} + static void mock_format_param(struct test_stream *stream, const char *type_name, const void *param) { - /* - * Cannot find formatter, so just print the pointer of the - * symbol. - */ - stream->add(stream, "<%pS>", param); + struct mock_param_formatter *formatter; + + formatter = mock_find_formatter(type_name); + if (formatter) + formatter->format(formatter, stream, param); + else + /* + * Cannot find formatter, so just print the pointer of the + * symbol. + */ + stream->add(stream, "<%pS>", param); } static void mock_add_method_declaration_to_stream( diff --git a/kunit/test-stream-test.c b/kunit/test-stream-test.c index 875b0db15878d..b335e09805a0f 100644 --- a/kunit/test-stream-test.c +++ b/kunit/test-stream-test.c @@ -116,6 +116,7 @@ static void test_stream_test_commits_any_uncommitted_when_cleanup( static int test_stream_test_init(struct test *test) { + struct mock_struct_formatter_entry *entries; struct test_stream_test_context *ctx; ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL); @@ -131,9 +132,35 @@ static int test_stream_test_init(struct test *test) if (!ctx->stream) return -ENOMEM; + entries = test_kzalloc(test, sizeof(*entries) * 3, GFP_KERNEL); + if (!entries) { + test_warn(test, + "Could not allocate arg formatter for struct va_format"); + return 0; + } + + INIT_MOCK_STRUCT_FORMATTER_ENTRY(&entries[0], + struct va_format, + fmt, + FORMATTER_FROM_TYPE(const char *)); + INIT_MOCK_STRUCT_FORMATTER_ENTRY(&entries[1], + struct va_format, + va, + unknown_formatter); + INIT_MOCK_STRUCT_FORMATTER_ENTRY_LAST(&entries[2]); + + mock_register_formatter(mock_struct_formatter(test, + "struct va_format *", + entries)); + return 0; } +static void test_stream_test_exit(struct test *test) +{ + mock_unregister_formatter(mock_find_formatter("struct va_format *")); +} + static struct test_case test_stream_test_cases[] = { TEST_CASE(test_stream_test_add), TEST_CASE(test_stream_test_append), @@ -145,6 +172,7 @@ static struct test_case test_stream_test_cases[] = { static struct test_module test_stream_test_module = { .name = "test-stream-test", .init = test_stream_test_init, + .exit = test_stream_test_exit, .test_cases = test_stream_test_cases, }; module_test(test_stream_test_module); From patchwork Tue Oct 16 23:51:08 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985054 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="QEWnXroQ"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="C+OPkpbp"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHh4zP9z9s9J for ; Wed, 17 Oct 2018 10:55:28 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=NLX/Ig0oZ6WPvIv+w9GHwI9dsIQNYTA0iWelnKOKHkE=; b=QEWnXroQR/Izbz 4Yx4O5sr2QW94SA7JcshzmBtUQJE9C4lDfFB3yA9d7rJ8JHdYxT4Sad+AqwSUQfsSr7/6Z3+1134n +bl9tLLKjZtfgF4IejfV2BVbjYqCvKfqXlWkUBSV27KDYQ8y5rI6H1xeCSfzsqKi8YMnpyxXTXpiq hDxQDMcFHcTGAToKSbes1LzSoFBrt/wcDucQiSD+3zQiBouvl5tUH2NEp9LFfr3Hq8zBxvxJV+xg4 8MMXBF/NWGfytyBHMIiayZtHitPu7vdA3qCMTkgcv73UzInzm6sXm+kWqPB1SHkzxOD9Cf8SrUkkw /iGEea8Q/kXASctHWsaQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBG-0005HA-RI; Tue, 16 Oct 2018 23:55:18 +0000 Received: from mail-pg1-x54a.google.com ([2607:f8b0:4864:20::54a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAi-0004Ov-Vt for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:10 +0000 Received: by mail-pg1-x54a.google.com with SMTP id o18-v6so18627816pgv.14 for ; Tue, 16 Oct 2018 16:54:34 -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=ZdhUWDmbd6jQ8OjAdvOY6t1C+GbX1k8RU5FTIqQdOg8=; b=C+OPkpbpTZbplYVmCFp2b9hJn6do+39K9nclx7JsOuXlb38ha65VqVZGHn/RuUJLAK laNKNNpg/dY5pFjEt00k9XH2lTbIhyQzivthYVohqzBJ4pduCG8SpHujHdsPyUqT23HJ TniphiJLHF3qSBiGtDooujiA36wMhRyqKDMHr6z2qq49h+vNAhW1UH/MFoezO0/JlnlT dd6yuMiVZ6Gk16KpmuJT49SZAUUtOu6NHWSSBFw+Zc0x2MAfZf0zjmdZ7tTeWtH3NYaz ZspiCaYHtB9klrCx5vU56JRVzFG5oS/uiqPDSOYVIAhEltoF356A3JyBSEP7UH9jvNZF GLFg== 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=ZdhUWDmbd6jQ8OjAdvOY6t1C+GbX1k8RU5FTIqQdOg8=; b=N1DEdm1vj5nmOdKMeT5u9CKwQaUDC1Ur6zt1Xwxbdbs7T2HFbhomApw4zqugO/9FX9 MxxxYefKEYME2zvpCd8/To4LyFqyYbtm4457BDaLqMifq048Qjndc4EekBtnIHhpip71 gLFuIIz5A0Y6lZwV772cWiUoSSu2dt/O/A0x5+0aWGyCO4T4QLZKpViDbbCDF60UNn2o ZdBAXxKsCoO3irZFY7Yng8dRzNZER8TmquzzGTQh9aoFcaFmnpcoIoTa/RskPxO4jlfe +hV9UX8lpBUtrbwgL5YKr0ak4bECjSHxdad6i6o9fzoYSvlCV8fatiSHbgcGy+3c70fM 2ugA== X-Gm-Message-State: ABuFfogvTJxO8xpGeZDXcqUbaegotGjjnbUF54aUCehyM3fupTKmNIJN oqAcXFp9X7KxleqrDCEtl8AxwoHpUsUlsgf7o9Ergw== X-Google-Smtp-Source: ACcGV617DwSk1hxsN27UphaMlR2h9I9aAKE+by99l1mdjsaqbOEZAeMJmNyvq7HhsEsxPE8jeirXuFyxr8F6Zoywmjcm0g== X-Received: by 2002:a63:5920:: with SMTP id n32-v6mr11619406pgb.47.1539734073215; Tue, 16 Oct 2018 16:54:33 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:08 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-20-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 19/31] kunit: mock: implemented nice, strict and naggy mock distinctions From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165445_245068_C315C8DB X-CRM114-Status: GOOD ( 19.08 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:54a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, Felix Guo , joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Nice mocks only fail when there is an expectation on a method, but none match a given call. Strict mocks only pass when there is a matching expectation for every call. Naggy mocks have the same pass/fail behavior as nice, but report a warning in any case a strict mock would fail. Signed-off-by: Felix Guo Signed-off-by: Brendan Higgins --- include/kunit/mock.h | 63 +++++++++++++ kunit/mock-test.c | 192 ++++++++++++++++++++++++++++++++++++++- kunit/mock.c | 10 +- kunit/test-stream-test.c | 6 +- 4 files changed, 265 insertions(+), 6 deletions(-) diff --git a/include/kunit/mock.h b/include/kunit/mock.h index 4f85b39d628d0..8d155b27a257a 100644 --- a/include/kunit/mock.h +++ b/include/kunit/mock.h @@ -95,10 +95,17 @@ struct mock_method { struct list_head expectations; }; +enum mock_type { + MOCK_TYPE_NICE, + MOCK_TYPE_NAGGY, + MOCK_TYPE_STRICT +}; + struct mock { struct test_post_condition parent; struct test *test; struct list_head methods; + enum mock_type type; /* TODO(brendanhiggins@google.com): add locking to do_expect. */ const void *(*do_expect)(struct mock *mock, const char *method_name, @@ -108,6 +115,8 @@ struct mock { int len); }; +#define DEFAULT_MOCK_TYPE MOCK_TYPE_NAGGY + void mock_init_ctrl(struct test *test, struct mock *mock); void mock_validate_expectations(struct mock *mock); @@ -137,6 +146,60 @@ void mock_unregister_formatter(struct mock_param_formatter *formatter); #define MOCK(name) name##_mock +/** + * STRICT_MOCK() - sets the mock to be strict and returns the mock + * @mock: the mock + * + * For an example, see ``The Nice, the Strict, and the Naggy`` under + * ``Using KUnit``. + */ +#define STRICT_MOCK(mock) \ +({ \ + mock_get_ctrl(mock)->type = MOCK_TYPE_STRICT; \ + mock; \ +}) + +static inline bool is_strict_mock(struct mock *mock) +{ + return mock->type == MOCK_TYPE_STRICT; +} + +/** + * NICE_MOCK() - sets the mock to be nice and returns the mock + * @mock: the mock + * + * For an example, see ``The Nice, the Strict, and the Naggy`` under + * ``Using KUnit``. + */ +#define NICE_MOCK(mock) \ +({ \ + mock_get_ctrl(mock)->type = MOCK_TYPE_NICE; \ + mock; \ +}) + +static inline bool is_nice_mock(struct mock *mock) +{ + return mock->type == MOCK_TYPE_NICE; +} + +/** + * NAGGY_MOCK() - sets the mock to be naggy and returns the mock + * @mock: the mock + * + * For an example, see ``The Nice, the Strict, and the Naggy`` under + * ``Using KUnit``. + */ +#define NAGGY_MOCK(mock) \ +({ \ + mock_get_ctrl(mock)->type = MOCK_TYPE_NAGGY; \ + mock; \ +}) + +static inline bool is_naggy_mock(struct mock *mock) +{ + return mock->type == MOCK_TYPE_NAGGY; +} + /** * TEST_EXPECT_CALL() - Declares a *call expectation* on a mock function. * @expectation_call: a mocked method or function with parameters replaced with diff --git a/kunit/mock-test.c b/kunit/mock-test.c index 77b16ad754424..675387743ada4 100644 --- a/kunit/mock-test.c +++ b/kunit/mock-test.c @@ -150,7 +150,7 @@ static void mock_test_failed_expect_call_fails_test(struct test *test) static void mock_test_do_expect_default_return(struct test *test) { struct mock_test_context *ctx = test->priv; - struct MOCK(test) *mock_test = ctx->mock_test; + struct MOCK(test) *mock_test = NICE_MOCK(ctx->mock_test); struct test *trgt = mock_get_trgt(mock_test); struct mock *mock = ctx->mock; int param0 = 5, param1 = -5; @@ -187,6 +187,49 @@ static void mock_test_do_expect_default_return(struct test *test) TEST_EXPECT_EQ(test, 0, expectation->times_called); } +/** + * DOC: Testing the failure condition of different mock types. + * + * The following tests will test the behaviour of expectations under different + * conditions. For example, what happens when an expectation: + * - is not satisfied at the end of the test + * - is fulfilled but the expected function is called again + * - a function is called without expectations set on it + * + * For each of these conditions, there may be variations between the different + * types of mocks: nice mocks, naggy mocks (the default) and strict mocks. + * + * More information about these mocks can be found in the kernel documentation + * under Documentation/test/api/class-and-function-mocking + */ + +/* Method called on strict mock with no expectations will fail */ +static void mock_test_strict_no_expectations_will_fail(struct test *test) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test *trgt = mock_get_trgt(mock_test); + struct mock *mock = ctx->mock; + int param0 = 5, param1 = -5; + static const char * const two_param_types[] = {"int", "int"}; + const void *two_params[] = {¶m0, ¶m1}; + struct mock_expectation *expectation; + + mock->type = MOCK_TYPE_STRICT; + + mock_set_default_action(mock, + "test_printk", + test_printk, + test_int_return(trgt, -4)); + + expectation = TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test), + test_any(test))); + + mock->do_expect(mock, "test_printk", test_printk, two_param_types, + two_params, ARRAY_SIZE(two_params)); + mock_validate_expectations(mock); +} + /* * Method called on naggy mock with no expectations will not fail, but will show * a warning message @@ -202,6 +245,8 @@ static void mock_test_naggy_no_expectations_no_fail(struct test *test) const void *two_params[] = {¶m0, ¶m1}; struct mock_expectation *expectation; + mock->type = MOCK_TYPE_NAGGY; + mock_set_default_action(mock, "test_printk", test_printk, @@ -229,6 +274,93 @@ static void mock_test_naggy_no_expectations_no_fail(struct test *test) mock_validate_expectations(mock); } +/* Method called on nice mock with no expectations will do nothing. */ +static void mock_test_nice_no_expectations_do_nothing(struct test *test) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test *trgt = mock_get_trgt(mock_test); + struct mock *mock = ctx->mock; + int param0 = 5, param1 = -5; + static const char * const two_param_types[] = {"int", "int"}; + const void *two_params[] = {¶m0, ¶m1}; + struct mock_expectation *expectation; + + mock->type = MOCK_TYPE_NICE; + + mock_set_default_action(mock, + "test_printk", + test_printk, + test_int_return(trgt, -4)); + + expectation = TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test), + test_any(test))); + expectation->min_calls_expected = 0; + expectation->max_calls_expected = 0; + + expectation = TEST_EXPECT_CALL(mock_vprintk(mock_get_ctrl(mock_test), + test_any(test), + test_any(test))); + expectation->min_calls_expected = 0; + expectation->max_calls_expected = 0; + + mock->do_expect(mock, + "test_printk", + test_printk, + two_param_types, + two_params, + ARRAY_SIZE(two_params)); + mock_validate_expectations(mock); +} + +/* Test that method called on a mock (of any type) with no matching expectations + * will fail test and print all the tried expectations. + */ +static void +run_method_called_but_no_matching_expectation_test(struct test *test, + enum mock_type mock_type) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test *trgt = mock_get_trgt(mock_test); + struct mock *mock = ctx->mock; + int param0 = 5, param1 = -5; + static const char * const two_param_types[] = {"int", "int"}; + const void *two_params[] = {¶m0, ¶m1}; + struct mock_expectation *handle; + struct mock_param_matcher *two_matchers[] = { + test_int_eq(trgt, 100), + test_int_eq(trgt, 100) + }; + mock_add_matcher(mock, "test_printk", test_printk, two_matchers, + ARRAY_SIZE(two_matchers)); + handle = TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test), + test_any(test))); + + mock->type = mock_type; + + mock->do_expect(mock, "test_printk", test_printk, two_param_types, + two_params, ARRAY_SIZE(two_params)); +} + +static void mock_test_naggy_no_matching_expectations_fail(struct test *test) +{ + run_method_called_but_no_matching_expectation_test(test, + MOCK_TYPE_NAGGY); +} + +static void mock_test_strict_no_matching_expectations_fail(struct test *test) +{ + run_method_called_but_no_matching_expectation_test(test, + MOCK_TYPE_STRICT); +} + +static void mock_test_nice_no_matching_expectations_fail(struct test *test) +{ + run_method_called_but_no_matching_expectation_test(test, + MOCK_TYPE_NICE); +} + static void mock_test_mock_validate_expectations(struct test *test) { struct mock_test_context *ctx = test->priv; @@ -257,6 +389,58 @@ static void mock_test_mock_validate_expectations(struct test *test) mock_validate_expectations(mock); } +static void mock_test_validate_clears_expectations(struct test *test) +{ + struct mock_test_context *ctx = test->priv; + struct MOCK(test) *mock_test = ctx->mock_test; + struct test *trgt = mock_get_trgt(mock_test); + struct mock *mock = ctx->mock; + struct mock_param_matcher *matchers[] = { + test_int_eq(trgt, 5), + test_int_eq(trgt, -4) + }; + int param0 = 5, param1 = -4; + static const char * const two_param_types[] = {"int", "int"}; + const void *two_params[] = {¶m0, ¶m1}; + + struct mock_expectation *expectation; + + mock->type = MOCK_TYPE_STRICT; + + /* If all goes well, the mock_test should not fail. */ + expectation = TEST_EXPECT_CALL(fail(mock_get_ctrl(mock_test), + test_any(test))); + expectation->min_calls_expected = 0; + expectation->max_calls_expected = 0; + + /* Add an arbitrary matcher for 0 calls */ + expectation = mock_add_matcher(mock, "test_printk", test_printk, + matchers, ARRAY_SIZE(matchers)); + expectation->times_called = 0; + expectation->min_calls_expected = 0; + expectation->max_calls_expected = 0; + + /* Should have 0 calls and should clear the previous expectation */ + mock_validate_expectations(mock); + + /* Add a new matcher for 1 call */ + expectation = mock_add_matcher(mock, "test_printk", test_printk, + matchers, ARRAY_SIZE(matchers)); + expectation->times_called = 0; + expectation->min_calls_expected = 1; + expectation->max_calls_expected = 1; + + /* Satisfy previous matcher */ + mock->do_expect(mock, "test_printk", test_printk, two_param_types, + two_params, ARRAY_SIZE(two_params)); + + /* + * Validate previous satisfy; if we didn't clear the previous + * expectation, it would fail the mock_test. + */ + mock_validate_expectations(mock); +} + void *do_mocked_fail(struct mock_action *this, const void **params, int len) { static const int ret; @@ -306,7 +490,13 @@ static struct test_case mock_test_cases[] = { TEST_CASE(mock_test_failed_expect_call_fails_test), TEST_CASE(mock_test_do_expect_default_return), TEST_CASE(mock_test_mock_validate_expectations), + TEST_CASE(mock_test_strict_no_expectations_will_fail), TEST_CASE(mock_test_naggy_no_expectations_no_fail), + TEST_CASE(mock_test_nice_no_expectations_do_nothing), + TEST_CASE(mock_test_strict_no_matching_expectations_fail), + TEST_CASE(mock_test_naggy_no_matching_expectations_fail), + TEST_CASE(mock_test_nice_no_matching_expectations_fail), + TEST_CASE(mock_test_validate_clears_expectations), {}, }; diff --git a/kunit/mock.c b/kunit/mock.c index 9be6b2d3621c4..314cebb54e236 100644 --- a/kunit/mock.c +++ b/kunit/mock.c @@ -79,6 +79,7 @@ void mock_init_ctrl(struct test *test, struct mock *mock) mock->test = test; INIT_LIST_HEAD(&mock->methods); mock->do_expect = mock_do_expect; + mock->type = DEFAULT_MOCK_TYPE; mock->parent.validate = mock_validate_wrapper; list_add_tail(&mock->parent.node, &test->post_conditions); } @@ -316,7 +317,12 @@ static struct mock_expectation *mock_apply_expectations( mock_add_method_expectation_error(test, stream, "Method was called with no expectations declared: ", mock, method, type_names, params, len); - stream->commit(stream); + if (is_strict_mock(mock)) + test->fail(test, stream); + else if (is_naggy_mock(mock)) + stream->commit(stream); + else + stream->clear(stream); return NULL; } @@ -346,7 +352,7 @@ static struct mock_expectation *mock_apply_expectations( } } - if (expectations_all_saturated) { + if (expectations_all_saturated && !is_nice_mock(mock)) { mock_add_method_expectation_error(test, stream, "Method was called with fully saturated expectations: ", mock, method, type_names, params, len); diff --git a/kunit/test-stream-test.c b/kunit/test-stream-test.c index b335e09805a0f..738a2692f7ba4 100644 --- a/kunit/test-stream-test.c +++ b/kunit/test-stream-test.c @@ -20,7 +20,7 @@ struct test_stream_test_context { static void test_stream_test_add(struct test *test) { struct test_stream_test_context *ctx = test->priv; - struct MOCK(test) *mock_test = ctx->mock_test; + struct MOCK(test) *mock_test = NICE_MOCK(ctx->mock_test); struct test_stream *stream = ctx->stream; stream->add(stream, "Foo"); @@ -40,7 +40,7 @@ static void test_stream_test_add(struct test *test) static void test_stream_test_append(struct test *test) { struct test_stream_test_context *ctx = test->priv; - struct MOCK(test) *mock_test = ctx->mock_test; + struct MOCK(test) *mock_test = NICE_MOCK(ctx->mock_test); struct test_stream *stream = ctx->stream; struct test_stream *other_stream; @@ -63,7 +63,7 @@ static void test_stream_test_append(struct test *test) static void test_stream_error_message_when_no_level_set(struct test *test) { struct test_stream_test_context *ctx = test->priv; - struct MOCK(test) *mock_test = ctx->mock_test; + struct MOCK(test) *mock_test = NICE_MOCK(ctx->mock_test); struct test_stream *stream = ctx->stream; struct test_stream *other_stream; From patchwork Tue Oct 16 23:51:09 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985055 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="KHU2zHn9"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="S8sUC5H0"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHm26jKz9sBN for ; Wed, 17 Oct 2018 10:55:31 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=l3+RnGvcDb64ISf2ucZYRB/BOI2nkk4y1bPCbDBRomc=; b=KHU2zHn9IEJXB1 DFTuBrYsDYZV6ehz2TLqso4Rzpn9o/nETz13DeOStJ5s4/VvA1Z0/mAP2Q0wEDfMhkbhfv7iO83XM BqkyiPo+POydCOZigcY6GgoSuKI6592w5OgWdAg8ZXIxSwq5NjV4R4wotaOqijbpjPrdfaQ/pylbc nF94/YBXpaPtQN4VQNuqlnKjDojo4NoXXF1ghBRD74yiDQKRKByQt8Xh27BzREWIM/rLFtUe+m3Tj aUpPDEbku3Tgy9FmJ/0m1jvkwj7gpc/Xo3bYq7l5IzomH694SAI1D1jIcw1ZIWcrbQXo0cepgb+Ii MDztNtXSgN4XUc723f2Q==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBI-0005TG-N4; Tue, 16 Oct 2018 23:55:20 +0000 Received: from mail-io1-xd49.google.com ([2607:f8b0:4864:20::d49]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAl-0004QH-Vq for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:09 +0000 Received: by mail-io1-xd49.google.com with SMTP id f9-v6so22692314iok.23 for ; Tue, 16 Oct 2018 16:54:36 -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=JQQypJJAnP+qflEUqHeoEbsw+NOpA25x72IRPpWFfJA=; b=S8sUC5H0lz/9D9UvlvG5pDq8orOH5Gw7jK5Rn1lGsaYiVb8o+UYMu0DYMc40ewW3OG AfFWdGKpnlA53/s7mR+yqgKCmp4SA9qjvuQfZjRGqqw7T4qkGmC5VP+ViGez9hSx/4xu XtdqiAerQCxKR/Npt6f3+wTvILEVLuF2Uqpdz00GxYige47tiD9WIDSe/HJPU54FPqYT d5fQ0IAUGLMAqp6cGP1j+4+UemlZPAruB4NcPwC11cH/DrPipLKusIK2I7M+2Y9M30CS b9IjhWWSfhuG/o3uVuYgkefYl25nnW6nytosnUypbP7LsCWaJFOT4TC2OykloUkQd6mq j9hw== 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=JQQypJJAnP+qflEUqHeoEbsw+NOpA25x72IRPpWFfJA=; b=V1ow7Qb5rxg92bYPr0MmGBcvvVqL3jV9MRBLFQi93RQVFocMDTrLpNTYvwrXgHsID+ pfInKOqOcgy2e/dpLCkct3bzp1RUejRiWRbflwB6RP22GtCjJcbySV0p12vU9KaQagju qIn5FWgNJA+EbU1OkMpquAfo3bC/wzY9Cciuzzt1BOiyxPXiZkm+jk/bINxRMkLf30W+ EIge1DcbcpUZB0FW7deTvHq8B7M1e0yIpKq4vRKnwtV6dxBsZ/ETVOCkLPWqhEqY7q8T 7EW2sMdLt75qgPbwQqOSrtThgQOLLTjxfONou/5l6EKr7iKe9UFy1CrunVFoqc6h3/78 fk6w== X-Gm-Message-State: ABuFfohFFdnQG9IinC9GJ9myZNjT+00HDdfKvsXq6J0dLPWnp9yBDd91 Om6Q3FvSgmFbdOnQotYeWb5SBAAxuZ9U3nkDOnr7Ug== X-Google-Smtp-Source: ACcGV62N4dkBvqEoT4+RYlijGYh5xVfbd4JDN4WJS0sm/H+cbP/iw13mEIJbg0zbO8CYJ23NyxKgn74atOFhtcnRDz826g== X-Received: by 2002:a24:7f05:: with SMTP id r5-v6mr17839290itc.2.1539734075686; Tue, 16 Oct 2018 16:54:35 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:09 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-21-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 20/31] kunit: mock: add ability to mock functions with void context From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165448_133772_F15434CA X-CRM114-Status: UNSURE ( 9.46 ) X-CRM114-Notice: Please train this message. X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:d49 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Adds ability to mock functions with a void* context object. Signed-off-by: Brendan Higgins --- include/kunit/mock.h | 53 +++++++++++++++++++++++++++++++++++++++++ kunit/mock-macro-test.c | 30 +++++++++++++++++++++++ kunit/mock.c | 9 +++++++ 3 files changed, 92 insertions(+) diff --git a/include/kunit/mock.h b/include/kunit/mock.h index 8d155b27a257a..89e95b3fcf09e 100644 --- a/include/kunit/mock.h +++ b/include/kunit/mock.h @@ -381,6 +381,24 @@ static inline bool is_naggy_mock(struct mock *mock) struct MOCK(struct_name) *MOCK_INIT_ID(struct_name)( \ struct test *test) +#define DECLARE_VOID_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name, \ + handle_index, \ + return_type, \ + param_types...) \ + DECLARE_MOCK_COMMON(name, \ + handle_index, \ + return_type, \ + param_types) + +#define DECLARE_VOID_CLASS_MOCK_HANDLE_INDEX(name, \ + handle_index, \ + return_type, \ + param_types...) \ + DECLARE_VOID_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name, \ + handle_index, \ + return_type, \ + param_types) + /** * CONSTRUCT_MOCK() * @struct_name: name of the class @@ -631,6 +649,41 @@ static inline bool is_naggy_mock(struct mock *mock) return mock_obj; \ } +struct MOCK(void) { + struct mock ctrl; + void *trgt; +}; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" +static inline struct mock *from_void_ptr_to_mock(const void *ptr) +{ + struct MOCK(void) *mock_void_ptr = ptr; + + return mock_get_ctrl(mock_void_ptr); +} +#pragma GCC diagnostic pop + +#define DEFINE_VOID_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name, \ + handle_index, \ + return_type, \ + param_types...) \ + DEFINE_MOCK_COMMON(name, \ + handle_index, \ + from_void_ptr_to_mock, \ + return_type, \ + param_types) +#define DEFINE_VOID_CLASS_MOCK_HANDLE_INDEX(name, \ + handle_index, \ + return_type, \ + param_types...) \ + DEFINE_VOID_CLASS_MOCK_HANDLE_INDEX_INTERNAL(name, \ + handle_index, \ + return_type, \ + param_types) + +DECLARE_STRUCT_CLASS_MOCK_INIT(void); + #define CONVERT_TO_ACTUAL_TYPE(type, ptr) (*((type *) ptr)) /** diff --git a/kunit/mock-macro-test.c b/kunit/mock-macro-test.c index 84d9d3f484366..0f95105ec032a 100644 --- a/kunit/mock-macro-test.c +++ b/kunit/mock-macro-test.c @@ -48,8 +48,19 @@ static int test_struct_init(struct MOCK(test_struct) *mock_test_struct) DEFINE_STRUCT_CLASS_MOCK_INIT(test_struct, test_struct_init); +DECLARE_VOID_CLASS_MOCK_HANDLE_INDEX(METHOD(test_void_ptr_func), + HANDLE_INDEX(0), + RETURNS(int), + PARAMS(void*, int)); + +DEFINE_VOID_CLASS_MOCK_HANDLE_INDEX(METHOD(test_void_ptr_func), + HANDLE_INDEX(0), + RETURNS(int), + PARAMS(void*, int)); + struct mock_macro_context { struct MOCK(test_struct) *mock_test_struct; + struct MOCK(void) *mock_void_ptr; }; #define TO_STR_INTERNAL(...) #__VA_ARGS__ @@ -195,6 +206,20 @@ static void mock_macro_test_generated_method_code_works(struct test *test) test_struct->non_first_slot_param(5, test_struct); } +static void mock_macro_test_generated_method_void_code_works(struct test *test) +{ + struct mock_macro_context *ctx = test->priv; + struct MOCK(void) *mock_void_ptr = ctx->mock_void_ptr; + struct mock_expectation *handle; + + handle = TEST_EXPECT_CALL(test_void_ptr_func( + mock_get_ctrl(mock_void_ptr), + test_int_eq(test, 3))); + handle->action = test_int_return(test, 0); + + test_void_ptr_func(mock_void_ptr, 3); +} + static int mock_macro_test_init(struct test *test) { struct mock_macro_context *ctx; @@ -208,6 +233,10 @@ static int mock_macro_test_init(struct test *test) if (!ctx->mock_test_struct) return -EINVAL; + ctx->mock_void_ptr = CONSTRUCT_MOCK(void, test); + if (!ctx->mock_void_ptr) + return -EINVAL; + return 0; } @@ -220,6 +249,7 @@ static struct test_case mock_macro_test_cases[] = { TEST_CASE(mock_macro_param_list_from_types_basic), TEST_CASE(mock_macro_arg_names_from_types), TEST_CASE(mock_macro_test_generated_method_code_works), + TEST_CASE(mock_macro_test_generated_method_void_code_works), {}, }; diff --git a/kunit/mock.c b/kunit/mock.c index 314cebb54e236..7a9fcf6ae4a55 100644 --- a/kunit/mock.c +++ b/kunit/mock.c @@ -8,6 +8,15 @@ #include +static int mock_void_ptr_init(struct MOCK(void) *mock_void_ptr) +{ + mock_void_ptr->trgt = mock_void_ptr; + + return 0; +} + +DEFINE_STRUCT_CLASS_MOCK_INIT(void, mock_void_ptr_init); + static bool mock_match_params(struct mock_matcher *matcher, struct test_stream *stream, const void **params, From patchwork Tue Oct 16 23:51:10 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985056 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="VsrcxDfq"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="VfVwa0vk"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHq59Knz9s9J for ; Wed, 17 Oct 2018 10:55:35 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=vpPtv3qSnmKYwdOfIFIbICTGzLL96kSU6vZwBPn6XIc=; b=VsrcxDfqTd8jXd c/imnV3K1FhY4RFOUkxfGLg2g5Q9sqlZ/rUtdnf3+lP/x43FXvSJ/jq2grPVyLNMlooF3+32InOHI hBrySaQPUBOiVFL9cQIRbbN38AT/CkzoiGWpp/OZlqtq3zZEqPKLjyR3c3gOcp3FW4V3ENoKg6wDm U1oes0q9HV86m2nJwdpGvPHzpkUzKQdwW3MxHq+C12tTCW+8F/NArm6p/B7/JteTfCDIsyVKcdJjt hnr+DpsFe40Dgq7LZe3fr2z9GMmAQwpQjtxOlTIOxp2iDCzIurW2u6dofR1VHEndcUuaq3FGukpZB VjRBuuKFirm45WNt1mBQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBM-0005kv-H8; Tue, 16 Oct 2018 23:55:24 +0000 Received: from mail-io1-xd49.google.com ([2607:f8b0:4864:20::d49]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAl-0004Ro-Uc for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:14 +0000 Received: by mail-io1-xd49.google.com with SMTP id k9-v6so23056498iob.16 for ; Tue, 16 Oct 2018 16:54:39 -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=XJBXERTeyDT3By2lgjIVo05iHCIAcMCMn8bvbRDnspA=; b=VfVwa0vkb/93+cpra+9YBhynQ9UjnMBJBc4wZLyeXFXfDJVsxx8FJD3Of6Vi94x+I6 +APX3u8Y5Pg2WkRb3jVFy7tyQ67v2v8upHWAs3ffGMNBP8HYtw4TTjNgkqDuz6JXB+a9 fqPbvE8BvS4IJ1knkXH51IDP0FfuXxjJBev9N5JOWAjiZpz6nE/2qqX3ifb6LHNGENZC EgoaSGWww0OhfXC7YkY8r3JL6HJVCOv3RHi6Aa+2Wb+bDPdbt0gCfJbR4e6z6KT+EJes 34uXKYGk91BdtheWBcbQ6UMvaNgC5bEWhMu2elwnekPr5BzZMaR0WhHcubhvaRFpKjPp saiQ== 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=XJBXERTeyDT3By2lgjIVo05iHCIAcMCMn8bvbRDnspA=; b=t7bzueH0NBFHgNR4jIcBNll43H1WELM3SKDZ58r4DO4LmoJbR86vNLQcVOuH2/LIB+ x/M2yA8LCAmyyNpXun4kiRKguNh4UtO3hQiJUpqq30R8jfGUSw3zZ9bWOpwOeje4EgzG 8+MrfKNGW8plmfday/YO1a59r6P10txYmRlDE8ETHUBF5Nn4mXI3FgK9/Xorsls7v3Hq TRDClS4sWQ8WrjM/qZARCCgBMvhXRQ4+kaTrlHceWXAq95Sg4Lwg2hAFaUDXKIz/Hxj1 0FFUJWt5DALzCp6ie6tRTUAmKsysZNg5UcD2b+1UZdOQY/qH2ZSpzP+t5n5WZGnJPGU9 fEdg== X-Gm-Message-State: ABuFfogjfV1WHsHXzzZhlNeocU7eoTxipAa51GMf7qnTcDjldLWrLicE 8d78AzP7rAFxbXDEjp5k46Mk6za9c2m5sA2JOaVV/g== X-Google-Smtp-Source: AJdET5dp8faAFMDxfYk9L34Xr3adUFy2YxvwdvtIB6sC9LYpnMpz2hoPhMOM1bPQ4WpR2722W5lwY6KQ1bqYuKGK7hlPaw== X-Received: by 2002:a05:660c:b0c:: with SMTP id f12mr201759itk.22.1539734077965; Tue, 16 Oct 2018 16:54:37 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:10 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-22-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 21/31] kunit: mock: added support for arbitrary function mocking From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165448_142035_27FB7732 X-CRM114-Status: GOOD ( 10.55 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:d49 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Up to this point KUnit only supported method style function mocking where there was some type of class or context object and the function was only accessed via a pointer. This adds support for mocking any function via the __mockable attribute. Signed-off-by: Brendan Higgins --- include/kunit/mock.h | 107 ++++++++++++++++++++++++++++++++++++++++ kunit/mock-macro-test.c | 14 ++++++ kunit/mock.c | 41 +++++++++++++++ 3 files changed, 162 insertions(+) diff --git a/include/kunit/mock.h b/include/kunit/mock.h index 89e95b3fcf09e..b58e30ba02ce2 100644 --- a/include/kunit/mock.h +++ b/include/kunit/mock.h @@ -144,6 +144,8 @@ void mock_register_formatter(struct mock_param_formatter *formatter); void mock_unregister_formatter(struct mock_param_formatter *formatter); +struct mock *mock_get_global_mock(void); + #define MOCK(name) name##_mock /** @@ -282,6 +284,12 @@ static inline bool is_naggy_mock(struct mock *mock) DECLARE_MOCK_CLIENT(name, return_type, param_types); \ DECLARE_MOCK_MASTER(name, handle_index, param_types) +#define DECLARE_MOCK_FUNC_CLIENT(name, return_type, param_types...) \ + DECLARE_MOCK_CLIENT(name, return_type, param_types) + +#define DECLARE_MOCK_FUNC_MASTER(name, param_types...) \ + DECLARE_MOCK_MASTER(name, MOCK_MAX_PARAMS, param_types) + #define DECLARE_STRUCT_CLASS_MOCK_STRUCT(struct_name) \ struct MOCK(struct_name) { \ struct mock ctrl; \ @@ -411,6 +419,16 @@ static inline bool is_naggy_mock(struct mock *mock) */ #define CONSTRUCT_MOCK(struct_name, test) MOCK_INIT_ID(struct_name)(test) +#define DECLARE_FUNCTION_MOCK_INTERNAL(name, return_type, param_types...) \ + DECLARE_MOCK_FUNC_CLIENT(name, return_type, param_types); \ + DECLARE_MOCK_FUNC_MASTER(name, param_types); + +#define DECLARE_FUNCTION_MOCK(name, return_type, param_types...) \ + DECLARE_FUNCTION_MOCK_INTERNAL(name, return_type, param_types) + +#define DECLARE_FUNCTION_MOCK_VOID_RETURN(name, param_types...) \ + DECLARE_FUNCTION_MOCK(name, void, param_types) + #define DEFINE_MOCK_CLIENT_COMMON(name, \ handle_index, \ MOCK_SOURCE, \ @@ -488,6 +506,31 @@ static inline bool is_naggy_mock(struct mock *mock) NO_RETURN, \ param_types) +#define FUNC_MOCK_SOURCE(ctx, handle_index) mock_get_global_mock() +#define DEFINE_MOCK_FUNC_CLIENT_COMMON(name, \ + return_type, \ + RETURN, \ + param_types...) \ + DEFINE_MOCK_CLIENT_COMMON(name, \ + MOCK_MAX_PARAMS, \ + FUNC_MOCK_SOURCE, \ + name, \ + return_type, \ + RETURN, \ + param_types) + +#define DEFINE_MOCK_FUNC_CLIENT(name, return_type, param_types...) \ + DEFINE_MOCK_FUNC_CLIENT_COMMON(name, \ + return_type, \ + CAST_AND_RETURN, \ + param_types) + +#define DEFINE_MOCK_FUNC_CLIENT_VOID_RETURN(name, param_types...) \ + DEFINE_MOCK_FUNC_CLIENT_COMMON(name, \ + void, \ + NO_RETURN, \ + param_types) + #define DEFINE_MOCK_MASTER_COMMON_INTERNAL(name, \ ctrl_index, \ MOCK_SOURCE, \ @@ -522,6 +565,13 @@ static inline bool is_naggy_mock(struct mock *mock) CLASS_MOCK_MASTER_SOURCE, \ param_types) +#define FUNC_MOCK_CLIENT_SOURCE(ctrl_index) mock_get_global_mock() +#define DEFINE_MOCK_FUNC_MASTER(name, param_types...) \ + DEFINE_MOCK_MASTER_COMMON(name, \ + MOCK_MAX_PARAMS, \ + FUNC_MOCK_CLIENT_SOURCE, \ + param_types) + #define DEFINE_MOCK_COMMON(name, \ handle_index, \ mock_converter, \ @@ -684,6 +734,63 @@ static inline struct mock *from_void_ptr_to_mock(const void *ptr) DECLARE_STRUCT_CLASS_MOCK_INIT(void); +#define DEFINE_FUNCTION_MOCK_INTERNAL(name, return_type, param_types...) \ + DEFINE_MOCK_FUNC_CLIENT(name, return_type, param_types); \ + DEFINE_MOCK_FUNC_MASTER(name, param_types) + +/** + * DEFINE_FUNCTION_MOCK() + * @name: name of the function + * @return_type: return type of the function + * @...: parameter types of the function + * + * Same as DEFINE_STRUCT_CLASS_MOCK() except can be used to mock any function + * declared %__mockable or DEFINE_REDIRECT_MOCKABLE() + */ +#define DEFINE_FUNCTION_MOCK(name, return_type, param_types...) \ + DEFINE_FUNCTION_MOCK_INTERNAL(name, return_type, param_types) + +#define DEFINE_FUNCTION_MOCK_VOID_RETURN_INTERNAL(name, param_types...) \ + DEFINE_MOCK_FUNC_CLIENT_VOID_RETURN(name, param_types); \ + DEFINE_MOCK_FUNC_MASTER(name, param_types) + +/** + * DEFINE_FUNCTION_MOCK_VOID_RETURN() + * @name: name of the function + * @...: parameter types of the function + * + * Same as DEFINE_FUNCTION_MOCK() except the method has a ``void`` return + * type. + */ +#define DEFINE_FUNCTION_MOCK_VOID_RETURN(name, param_types...) \ + DEFINE_FUNCTION_MOCK_VOID_RETURN_INTERNAL(name, param_types) + +#if IS_ENABLED(CONFIG_KUNIT) + +/** + * __mockable - A function decorator that allows the function to be mocked. + * + * Example: + * + * .. code-block:: c + * + * int __mockable example(int arg) { ... } + */ +#define __mockable __weak + +/** + * __visible_for_testing - Makes a static function visible when testing. + * + * A macro that replaces the `static` specifier on functions and global + * variables that is static when compiled normally and visible when compiled for + * tests. + */ +#define __visible_for_testing +#else +#define __mockable +#define __visible_for_testing static +#endif + #define CONVERT_TO_ACTUAL_TYPE(type, ptr) (*((type *) ptr)) /** diff --git a/kunit/mock-macro-test.c b/kunit/mock-macro-test.c index 0f95105ec032a..a2628a70bc4e4 100644 --- a/kunit/mock-macro-test.c +++ b/kunit/mock-macro-test.c @@ -58,6 +58,8 @@ DEFINE_VOID_CLASS_MOCK_HANDLE_INDEX(METHOD(test_void_ptr_func), RETURNS(int), PARAMS(void*, int)); +DEFINE_FUNCTION_MOCK(add, RETURNS(int), PARAMS(int, int)); + struct mock_macro_context { struct MOCK(test_struct) *mock_test_struct; struct MOCK(void) *mock_void_ptr; @@ -220,6 +222,17 @@ static void mock_macro_test_generated_method_void_code_works(struct test *test) test_void_ptr_func(mock_void_ptr, 3); } +static void mock_macro_test_generated_function_code_works(struct test *test) +{ + struct mock_expectation *handle; + + handle = TEST_EXPECT_CALL(add(test_int_eq(test, 4), + test_int_eq(test, 3))); + handle->action = test_int_return(test, 7); + + TEST_EXPECT_EQ(test, 7, add(4, 3)); +} + static int mock_macro_test_init(struct test *test) { struct mock_macro_context *ctx; @@ -250,6 +263,7 @@ static struct test_case mock_macro_test_cases[] = { TEST_CASE(mock_macro_arg_names_from_types), TEST_CASE(mock_macro_test_generated_method_code_works), TEST_CASE(mock_macro_test_generated_method_void_code_works), + TEST_CASE(mock_macro_test_generated_function_code_works), {}, }; diff --git a/kunit/mock.c b/kunit/mock.c index 7a9fcf6ae4a55..2b91ea08b6064 100644 --- a/kunit/mock.c +++ b/kunit/mock.c @@ -93,6 +93,47 @@ void mock_init_ctrl(struct test *test, struct mock *mock) list_add_tail(&mock->parent.node, &test->post_conditions); } +struct global_mock { + struct mock ctrl; + bool is_initialized; +}; + +static struct global_mock global_mock = { + .is_initialized = false, +}; + +static int mock_init_global_mock(struct test_initcall *initcall, + struct test *test) +{ + BUG_ON(global_mock.is_initialized); + + mock_init_ctrl(test, &global_mock.ctrl); + global_mock.is_initialized = true; + + return 0; +} + +static void mock_exit_global_mock(struct test_initcall *initcall) +{ + BUG_ON(!global_mock.is_initialized); + + global_mock.ctrl.test = NULL; + global_mock.is_initialized = false; +} + +static struct test_initcall global_mock_initcall = { + .init = mock_init_global_mock, + .exit = mock_exit_global_mock, +}; +test_register_initcall(global_mock_initcall); + +struct mock *mock_get_global_mock(void) +{ + BUG_ON(!global_mock.is_initialized); + + return &global_mock.ctrl; +} + static struct mock_method *mock_lookup_method(struct mock *mock, const void *method_ptr) { From patchwork Tue Oct 16 23:51:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985058 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="kfvFmPa9"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="cdL/WTHG"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHt6JTYz9sB5 for ; Wed, 17 Oct 2018 10:55:38 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=0k7U7yrWrqVQstvq57Mx4kRR6IWp5hFJsLi/RoSsnEA=; b=kfvFmPa9Y6b9wL TxG7TIOz9Qv+7Och55CginllNd020QJiKS8moxPT1xrqsbXlI+KMR02KdlPAYAZxSWjZKHmfImiCj ha6QqYv9bdh7Z2WHQxv0C7Y1iwERB1MNAfDvDhEPDqj2xKYWn7BTEfUnDSc1+ZY/RnKH0Q3VHnxtF lhZXW2pZmLTh6rpicZPy2+l+yD2OFY3TlwZMY5crmK+NRIml1uW5UodKsMUjpxq3k21cnBrVZpXlc XJJQZEWMgBsfFdD/ugJ1IMqtTPrp/6Fd8AVOiwXV89ACU72EEPdJ4sSN86W0kbLjBuKm0614mxCkn zjPVUNbV7GFbs1L1Fzdg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBO-0005vZ-UJ; Tue, 16 Oct 2018 23:55:27 +0000 Received: from mail-it1-x14a.google.com ([2607:f8b0:4864:20::14a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAq-0004T5-4L for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:17 +0000 Received: by mail-it1-x14a.google.com with SMTP id v13-v6so230178itc.4 for ; Tue, 16 Oct 2018 16:54:40 -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=wDfUjpTFuScKe5JOSK0jo+HGTTRhetz+kQRVWENyJS0=; b=cdL/WTHGLGFeVRZQPi0GA7VS9QWPA39qDWYUDKxfj0K8ZnvYhzXxc0dlKh9zoNK+/8 5NFq0ybqzXiW0oX3Fxu5zsNkAMag+bGBLsqFd9DPf6Ek3o3qnboiaUx8FjDoVKr/BTPD cZvXDqLB1jf9twv3snl37NsoHfY6BW/TWQKNrQ8BSycAHI+3piQFPzSSLw+XfX2++QDB Lv99kVOuYqcHnwPtn2fJ+8KtvIHPFpCqOZ2x7bj5PRM1ax1/wVH4ZvchOWxBWxAIFnol HidUOJebWRxgtkIziwgrCLseGFjSw/xLl4pGOpfVkOfMcFX6zoGWTDd9dzRp2sYe4PwB xKqg== 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=wDfUjpTFuScKe5JOSK0jo+HGTTRhetz+kQRVWENyJS0=; b=s5MJRWWlY1KzZ8SHojvB1qTt/NZU9xOK37qtZAEW3QVJXawTpmiMJCxvyNn8A5jR53 cvENwhCyLoN3+QQiv5iMnpIG656Xl5zXOG1zSTrLhSqVh0T8e5QyQaS0TZBtaSPme/SP OMa1S/si3qRlVOTqxTilKFLfd88dsmiW/m94vFmFT5wacEJLy35EVmv4L89XDfpQLCbG ZARbekYf7ogc5yW26zlUgey/h2eN4p2uOA7ZmL/QgFSCjTfDUur8+zOnXogFb5+5se2k iu0NQVTMOojt64SgJ00Xw14dUA0gPbcICu0xMGeufa4uFzyyPiU/wu9MJ68HOBjSw/aL UK4g== X-Gm-Message-State: ABuFfogyGHhwn0GoUlBQiiTUepLD0j1zsdGexeSrd6AHwtjHYMmVO/X5 6ZDRpZkVlT/pxwfk4Uevq0p1ZBXI6uZLxVNGQGXVMQ== X-Google-Smtp-Source: ACcGV60fnn6vyQbvIqsJoUCyM0HIqt275NDvsjCWheXNCV4cPttX975jId2HpGF/Krthsvf7AvK26S6Z857gbtlbIDPNog== X-Received: by 2002:a24:fe01:: with SMTP id w1-v6mr224481ith.0.1539734080433; Tue, 16 Oct 2018 16:54:40 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:11 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-23-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 22/31] kunit: mock: add the concept of spyable functions From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165452_226693_DBCB99F7 X-CRM114-Status: GOOD ( 13.33 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:14a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Adds the concept of spying like in Mockito (http://static.javadoc.io/org.mockito/mockito-core/2.20.0/org/mockito/Mockito.html#spy-T-). This allows a function declaration to be labled as spyable which allows the function to be mocked *and* to allow the mock to invoke the original function definition. Signed-off-by: Brendan Higgins --- include/kunit/mock.h | 123 ++++++++++++++++++++++++++++++++++++++++++- kunit/common-mocks.c | 36 +++++++++++++ 2 files changed, 158 insertions(+), 1 deletion(-) diff --git a/include/kunit/mock.h b/include/kunit/mock.h index b58e30ba02ce2..c3615e80d96ee 100644 --- a/include/kunit/mock.h +++ b/include/kunit/mock.h @@ -284,6 +284,13 @@ static inline bool is_naggy_mock(struct mock *mock) DECLARE_MOCK_CLIENT(name, return_type, param_types); \ DECLARE_MOCK_MASTER(name, handle_index, param_types) +#define DECLARE_SPYABLE(name, return_type, param_types...) \ + return_type REAL_ID(name)(param_types); \ + return_type name(param_types); \ + void *INVOKE_ID(name)(struct test *test, \ + const void *params[], \ + int len) + #define DECLARE_MOCK_FUNC_CLIENT(name, return_type, param_types...) \ DECLARE_MOCK_CLIENT(name, return_type, param_types) @@ -465,6 +472,100 @@ static inline bool is_naggy_mock(struct mock *mock) RETURN(return_type, retval); \ } +#if IS_ENABLED(CONFIG_KUNIT) +#define DEFINE_INVOKABLE(name, return_type, RETURN_ASSIGN, param_types...) \ + void *INVOKE_ID(name)(struct test *test, \ + const void *params[], \ + int len) { \ + return_type *retval; \ + \ + TEST_ASSERT_EQ(test, NUM_VA_ARGS(param_types), len); \ + retval = test_kzalloc(test, \ + sizeof(*retval), \ + GFP_KERNEL); \ + TEST_ASSERT_NOT_ERR_OR_NULL(test, retval); \ + RETURN_ASSIGN() REAL_ID(name)( \ + ARRAY_ACCESSORS_FROM_TYPES( \ + param_types)); \ + return retval; \ + } +#else +#define DEFINE_INVOKABLE(name, return_type, RETURN_ASSIGN, param_types...) +#endif + +#define DEFINE_SPYABLE_COMMON(name, \ + return_type, \ + RETURN_ASSIGN, \ + param_types...) \ + return_type REAL_ID(name)(param_types); \ + return_type name(param_types) __mockable_alias(REAL_ID(name)); \ + DEFINE_INVOKABLE(name, return_type, RETURN_ASSIGN, param_types); + +#define ASSIGN() *retval = + +/** + * DEFINE_SPYABLE() + * @name: name of the function + * @return_type: return type of the function + * @param_types: parameter types of the function + * + * Used to define a function which is *redirect-mockable*, which allows the + * function to be mocked and refer to the original definition via + * INVOKE_REAL(). + * + * Example: + * + * .. code-block:: c + * + * DEFINE_SPYABLE(i2c_add_adapter, + * RETURNS(int), PARAMS(struct i2c_adapter *)); + * int REAL_ID(i2c_add_adapter)(struct i2c_adapter *adapter) + * { + * ... + * } + * + * static int aspeed_i2c_test_init(struct test *test) + * { + * struct mock_param_capturer *adap_capturer; + * struct mock_expectation *handle; + * struct aspeed_i2c_test *ctx; + * int ret; + * + * ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + * if (!ctx) + * return -ENOMEM; + * test->priv = ctx; + * + * handle = TEST_EXPECT_CALL( + * i2c_add_adapter(capturer_to_matcher( + * adap_capturer))); + * handle->action = INVOKE_REAL(test, i2c_add_adapter); + * ret = of_fake_probe_platform_by_name(test, + * "aspeed-i2c-bus", + * "test-i2c-bus"); + * if (ret < 0) + * return ret; + * + * TEST_ASSERT_PARAM_CAPTURED(test, adap_capturer); + * ctx->adap = mock_capturer_get(adap_capturer, + * struct i2c_adapter *); + * + * return 0; + * } + */ +#define DEFINE_SPYABLE(name, return_type, param_types...) \ + DEFINE_SPYABLE_COMMON(name, \ + return_type, \ + ASSIGN, \ + param_types) + +#define NO_ASSIGN() +#define DEFINE_SPYABLE_VOID_RETURN(name, param_types) \ + DEFINE_SPYABLE_COMMON(name, \ + void, \ + NO_ASSIGN, \ + param_types) + #define CLASS_MOCK_CLIENT_SOURCE(ctx, handle_index) ctx(arg##handle_index) #define DEFINE_MOCK_METHOD_CLIENT_COMMON(name, \ handle_index, \ @@ -745,7 +846,7 @@ DECLARE_STRUCT_CLASS_MOCK_INIT(void); * @...: parameter types of the function * * Same as DEFINE_STRUCT_CLASS_MOCK() except can be used to mock any function - * declared %__mockable or DEFINE_REDIRECT_MOCKABLE() + * declared %__mockable or DEFINE_SPYABLE() */ #define DEFINE_FUNCTION_MOCK(name, return_type, param_types...) \ DEFINE_FUNCTION_MOCK_INTERNAL(name, return_type, param_types) @@ -777,6 +878,7 @@ DECLARE_STRUCT_CLASS_MOCK_INIT(void); * int __mockable example(int arg) { ... } */ #define __mockable __weak +#define __mockable_alias(id) __weak __alias(id) /** * __visible_for_testing - Makes a static function visible when testing. @@ -788,6 +890,7 @@ DECLARE_STRUCT_CLASS_MOCK_INIT(void); #define __visible_for_testing #else #define __mockable +#define __mockable_alias(id) __alias(id) #define __visible_for_testing static #endif @@ -1069,6 +1172,24 @@ struct mock_param_matcher *test_struct_cmp( const char *struct_name, struct mock_struct_matcher_entry *entries); +struct mock_action *invoke(struct test *test, + void *(*invokable)(struct test *, + const void *params[], + int len)); + +/** + * INVOKE_REAL() + * @test: associated test + * @func_name: name of the function + * + * See DEFINE_SPYABLE() for an example. + * + * Return: &struct mock_action that makes the associated mock method or function + * call the original function definition of a redirect-mockable + * function. + */ +#define INVOKE_REAL(test, func_name) invoke(test, INVOKE_ID(func_name)) + struct mock_struct_formatter_entry { size_t member_offset; struct mock_param_formatter *formatter; diff --git a/kunit/common-mocks.c b/kunit/common-mocks.c index 1c52522808cab..ce0159923814d 100644 --- a/kunit/common-mocks.c +++ b/kunit/common-mocks.c @@ -386,6 +386,42 @@ DEFINE_RETURN_ACTION_WITH_TYPENAME(longlong, long long); DEFINE_RETURN_ACTION_WITH_TYPENAME(ulonglong, unsigned long long); DEFINE_RETURN_ACTION_WITH_TYPENAME(ptr, void *); +struct mock_invoke_action { + struct mock_action action; + struct test *test; + void *(*invokable)(struct test *test, const void *params[], int len); +}; + +static void *do_invoke(struct mock_action *paction, + const void *params[], + int len) +{ + struct mock_invoke_action *action = + container_of(paction, + struct mock_invoke_action, + action); + + return action->invokable(action->test, params, len); +} + +struct mock_action *invoke(struct test *test, + void *(*invokable)(struct test *, + const void *params[], + int len)) +{ + struct mock_invoke_action *action; + + action = test_kmalloc(test, sizeof(*action), GFP_KERNEL); + if (!action) + return NULL; + + action->action.do_action = do_invoke; + action->test = test; + action->invokable = invokable; + + return &action->action; +} + struct mock_param_integer_formatter { struct mock_param_formatter formatter; const char *fmt_str; From patchwork Tue Oct 16 23:51:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985060 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="pcK04B+2"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="jObYhDzd"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHx0CqKz9sBN for ; Wed, 17 Oct 2018 10:55:40 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=jpXk08dl+M/+rvXRqTz/9U1LNgCeveCL4NaUEiKMIyc=; b=pcK04B+2Zml46/ P115UR0GdQQldoQ8JoJ3So8WS6UBLTHD8Cp8qdBik0lAKeAZ1mnCN1Kd1lQIzo226Keu0ktKaFcUD UoVcCz9CLuQWaKcxUd240SvdoDmib5gy0rRkCsuvsylm3yfCHHdaOT1tCxzoFpNU/lKzJIlYITeSd vXN+gwiKIAhbCtMEQtcYw9a+tpe3+Uxa1mWUo4KPr7zDfMxzyZGkLOtUQXqjpam0RVSravYb3JZTH dZnpw5HWXJW+nKEKTX6FwJzl2mkCQWhrgDHKI+05sxoYZBBkdMXCTLoKGTrpbAoLtxqtlcsWaMOjh /DlhNxXbhvgBfmpnronw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBT-00065z-Ji; Tue, 16 Oct 2018 23:55:31 +0000 Received: from mail-it1-x149.google.com ([2607:f8b0:4864:20::149]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAs-0004UP-4H for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:21 +0000 Received: by mail-it1-x149.google.com with SMTP id z136-v6so255383itc.5 for ; Tue, 16 Oct 2018 16:54:43 -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=FPIYCn6EOx1XsTWZ4p6VpTMVblaLhIfS9g7PdcTELME=; b=jObYhDzdAodeqbMaupPyJOZrepJm1v4ub0X324PwVojcSD6LzyRBYEJlGkf5WJ/4iO xFCcn4TF+vvga75ygcFE5OqkwSSNioPrAG5NtFTrZpiOcyiNYFFwjREjywoR6FdAnR6o mvym6+WDHPTe2YlVpA4vkRw1McojScwQZB/0y11Ox51mDiFrxxPrMNXDI6/7O2i0MJbs NyzyQUUgpacKymiZ+cJRCrU4qhwH+yr4D9/IhS5eaPOcgCAp7ts/9oaCvgk4NTW/Cqn9 QXAEmzv9NWz/ypXUomOiaLdlMowNBH7nzRHi+j5kKS4EimsM22xRppfl76ZVeacwtf5P i3xg== 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=FPIYCn6EOx1XsTWZ4p6VpTMVblaLhIfS9g7PdcTELME=; b=neq8PdkSfSt8aCqFPuZ6Af318KCMfe9OJDMSTcI0dSfAeh3dZugqJVFhjPfwSxeF6t 6nM5372f7niv/Om0oM24bzt5jM85tiSqoD+a8eHzhohCyZhqtMMmwktFo+D+MLODG0Ur GFSIL2T6roDlp1t4PkFjFgooBIq/YJTu1WQ9JZpU7v5kmWxbKY2IcIRNsu8+kmbn3uUm D6oOsmA5ANdLJLQqs6jhaKswjDLViFzW2jE+sMgiCxkbnM+y/nuXqmsH/aAumWQYIloJ piu0b4OHx+KVCyeA8X+YUhOnXrhfI3Ahcf/TFEcEtlTpyfAI3Bmo+m7C2zvl1PO5zt3P juZg== X-Gm-Message-State: ABuFfogYEIthsu/LFx6AlAVyomp9LOoLN3w9aO7Prz4LCCi2z4LxN2QS aS3lOy1124Ejr9cj+ZR08Qwh6W+rcZ4saNA2VKCadg== X-Google-Smtp-Source: ACcGV62ehVJ8AHeD9ZnEiqipPbKj1hD4xI+nJJKTsv8DRZBI3hKxGL6otUBF/c4WZRoECtmpAv/O5TQ2IAmPVzpPgWWMTg== X-Received: by 2002:a24:8903:: with SMTP id s3-v6mr211702itd.26.1539734082598; Tue, 16 Oct 2018 16:54:42 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:12 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-24-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 23/31] kunit: mock: add parameter capturers From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165454_210566_55736B59 X-CRM114-Status: GOOD ( 11.27 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:149 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Adds the concept of an argument capturer which, when used with a matcher in an EXPECT_CALL(...), will capture the value of the matching argument. Signed-off-by: Brendan Higgins --- include/kunit/mock.h | 83 ++++++++++++++++++++++++++++++++++++++++++++ kunit/common-mocks.c | 78 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/include/kunit/mock.h b/include/kunit/mock.h index c3615e80d96ee..0e1aa568709a1 100644 --- a/include/kunit/mock.h +++ b/include/kunit/mock.h @@ -1172,6 +1172,89 @@ struct mock_param_matcher *test_struct_cmp( const char *struct_name, struct mock_struct_matcher_entry *entries); +/** + * struct mock_param_capturer - used to capture parameter when matching + * + * Use the associated helper macros to access relevant fields. + * Example: + * + * .. code-block::c + * + * static int some_test(struct test *test) + * { + * // imagine a mocked function: int add(int a, int b) + * struct mock_param_capturer *capturer = + * mock_int_capturer_create(test, any(test)); + * TEST_EXPECT_CALL(add(any(test), capturer_to_matcher(capturer))); + * TEST_ASSERT_PARAM_CAPTURED(test, capturer); + * + * int captured_value = mock_capturer_get(capturer, int); + * } + */ +struct mock_param_capturer { + /* private: internal use only. */ + struct mock_param_matcher matcher; + struct mock_param_matcher *child_matcher; + void *(*capture_param)(struct test *test, const void *param); + void *captured_param; +}; + +struct mock_param_capturer *mock_param_capturer_create( + struct test *test, + struct mock_param_matcher *child_matcher, + void *(*capture_param)(struct test *, const void *)); + +/** + * mock_int_capturer_create() - creates a int parameter capturer + * @test: associated test + * @child_matcher: matcher used to match the integer + * + * The capturer will capture the value if the matcher is satisfied. + */ +struct mock_param_capturer *mock_int_capturer_create( + struct test *test, struct mock_param_matcher *child_matcher); + +/** + * mock_int_capturer_create() - creates a generic pointer parameter capturer + * @test: associated test + * @child_matcher: matcher used to match the pointer + * + * The capturer will capture the value if the matcher is satisfied + */ +struct mock_param_capturer *mock_ptr_capturer_create( + struct test *test, struct mock_param_matcher *child_matcher); + +/** + * capturer_to_matcher() + * @capturer: the param capturer + * + * Use this function when passing a capturer into an EXPECT_CALL() where a + * matcher would be expected. See the example for &struct mock_param_capturer. + */ +#define capturer_to_matcher(capturer) (&(capturer)->matcher) + +/** + * TEST_ASSERT_PARAM_CAPTURED(): Asserts that a parameter has been captured. + * @test: the associated test + * @capturer: the param capturer + * + * See &struct mock_param_capturer for an example. + */ +#define TEST_ASSERT_PARAM_CAPTURED(test, capturer) \ + TEST_ASSERT(test, \ + !IS_ERR_OR_NULL((capturer)->captured_param), \ + "Asserted " #capturer " captured param, but did not.") + +/** + * mock_capturer_get(): Returns the value captured by ``capturer`` + * @capturer: the param capturer + * @type: the type of the value + * + * See &struct mock_param_capturer for an example. + */ +#define mock_capturer_get(capturer, type) \ + CONVERT_TO_ACTUAL_TYPE(type, (capturer)->captured_param) + struct mock_action *invoke(struct test *test, void *(*invokable)(struct test *, const void *params[], diff --git a/kunit/common-mocks.c b/kunit/common-mocks.c index ce0159923814d..62528b7df83c6 100644 --- a/kunit/common-mocks.c +++ b/kunit/common-mocks.c @@ -323,6 +323,84 @@ struct mock_param_matcher *test_struct_cmp( return &matcher->matcher; } +static bool match_and_capture_param(struct mock_param_matcher *pmatcher, + struct test_stream *stream, + const void *param) +{ + struct mock_param_capturer *capturer = + container_of(pmatcher, + struct mock_param_capturer, + matcher); + struct mock_param_matcher *child_matcher = capturer->child_matcher; + bool matches; + + matches = child_matcher->match(child_matcher, stream, param); + if (matches) + capturer->captured_param = capturer->capture_param(stream->test, + param); + + return matches; +} + +struct mock_param_capturer *mock_param_capturer_create( + struct test *test, + struct mock_param_matcher *child_matcher, + void *(*capture_param)(struct test *, const void *)) +{ + struct mock_param_capturer *capturer; + + capturer = test_kzalloc(test, sizeof(*capturer), GFP_KERNEL); + if (!capturer) + return NULL; + + capturer->matcher.match = match_and_capture_param; + capturer->child_matcher = child_matcher; + capturer->capture_param = capture_param; + capturer->captured_param = NULL; + + return capturer; +} + +static void *mock_capture_int(struct test *test, const void *param) +{ + int value = CONVERT_TO_ACTUAL_TYPE(int, param); + int *pvalue; + + pvalue = test_kzalloc(test, sizeof(*pvalue), GFP_KERNEL); + if (!pvalue) + return NULL; + *pvalue = value; + + return pvalue; +} + +struct mock_param_capturer *mock_int_capturer_create( + struct test *test, struct mock_param_matcher *child_matcher) +{ + return mock_param_capturer_create(test, + child_matcher, + mock_capture_int); +} + +static void *mock_capture_ptr(struct test *test, const void *param) +{ + void *ptr = CONVERT_TO_ACTUAL_TYPE(void *, param); + void **pptr; + + pptr = test_kzalloc(test, sizeof(*pptr), GFP_KERNEL); + *pptr = ptr; + + return pptr; +} + +struct mock_param_capturer *mock_ptr_capturer_create( + struct test *test, struct mock_param_matcher *child_matcher) +{ + return mock_param_capturer_create(test, + child_matcher, + mock_capture_ptr); +} + #define DEFINE_RETURN_ACTION_STRUCT(type_name, type) \ struct mock_##type_name##_action { \ struct mock_action action; \ From patchwork Tue Oct 16 23:51:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985059 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="MWRKcAiZ"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="OBvgp0mJ"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHv20Lzz9s9J for ; Wed, 17 Oct 2018 10:55:39 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=4MnPLzTrN/+eClbGGjTAK4sEkL98pljKeuZKtsIKX6U=; b=MWRKcAiZffbYu6 6+1LMhnRQL4QXo5MF1fKCSeMtEUC2qalkNI+f+FTk3bcecXjNFsUxKkDu7KMbexOJScBabEMMsJVq wIopOQoix6yBBheWijhSmGiDcRX9F7PqshxIobasG5MpKMVFQf10IOYPflsFwvxMcfaVUMfuEt81M IcLGn+V5kutpx3dZmwL47VI/GORlELljr/7pEDZ4nCKMzcE2PnWs+U1FSTy53MJrM/TWRy0ZKQNkM P09pOCZPoW267te6QyMo3m3E6hNQU7rBKRiulSSdTbMYwH8O+x2w1uVm2DowfzbdU5GTfE1W61PkF FYuY+50R/M70w7eqP99A==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBS-00065W-7k; Tue, 16 Oct 2018 23:55:30 +0000 Received: from mail-io1-xd4a.google.com ([2607:f8b0:4864:20::d4a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAu-0004WM-FB for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:20 +0000 Received: by mail-io1-xd4a.google.com with SMTP id z20-v6so23259503ioh.2 for ; Tue, 16 Oct 2018 16:54:45 -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=/cIwxaWjuX5zXpExKbickYNnSKyceHv4B+fk4+zCXKw=; b=OBvgp0mJoUKBhNDc6ja0qaG2wAkB/90CV8v/Kg64dnb5uhOHYgNt0vzASZj3BC/gFI w5LcxMMLMCl5Pwqlt2IMQ1RMPA36CUqknXp1erMIeAw1MbwYapX+oAVc5l4lND9WHFo9 b/ix6YlvXa1qVgmRZnDewfyU7zeaO8zdj00glgldVCmtP8JZ89aX+Sfgq9tPXoY/Y1Xy Cq9liBf5Fw4Huhl4Rr/5/zZ/6lIIejypwAIRx87X/mHqG7psjzMbKN6ED08PYfCArJwt 4TjkiQL5HNW274WbipUOqzDrjbYgMiR/DZfHkvBJaTbNtYquCqmH9u27BlLfYYT4rbFV Ahug== 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=/cIwxaWjuX5zXpExKbickYNnSKyceHv4B+fk4+zCXKw=; b=X7FSwXBxvH8Y3gmkAfxSel9EdV2x+KJ6r+UCPHYOg8Zu+rgLqbT1Qh1EOErtWzSjCx aPfb+l51Xu1a4E176E0c6VdNtiGMWr+1woUrDtWod1DqLOmL9sgCZL8JVJcfzxfSyRWG 8Rz+cMgaTyAltWu95RPoPThm4e0+Np5c7Kyol0wkKw7fB2UqiWWfP+Kc275f4c2eFf2t Sqo1EsLDGw+092mDxdAotUzJmu4R7SA2tl1OjqQ9BitIGGjsHx7ECIR7aNVIZWVxgnEA Lh1axpxW9NT+xTtOLC10lyIQgX+1G2lL2VXzZXoqOydR5faNUJYL4fK2pmUDH6IHZBix Tivg== X-Gm-Message-State: ABuFfogR1dqYvXgxKvBXOQe9CVIw+Pe63PQzLZVloLgq0day3ML5TOVF 51ht91Ohm4RkjAg3frIfReBNEyqdqc1cFMWzdg/9wg== X-Google-Smtp-Source: ACcGV62l5qWluLUdWqzgSRINvFIqAn0zVr+2iNoYAcJiT8V6kuklivgL4UhosQ3bkbFHrilEw3E9ehC0f6RUy2ud4LHt7A== X-Received: by 2002:a24:fe01:: with SMTP id w1-v6mr224693ith.0.1539734085013; Tue, 16 Oct 2018 16:54:45 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:13 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-25-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 24/31] kunit: improved sigsegv stack trace printing From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165456_513251_654AEEA1 X-CRM114-Status: GOOD ( 12.35 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:d4a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Sacrificed the control of printing stack trace within the crash handler in the test runner for getting a better stack trace; this is still not ideal, but much better than before. Signed-off-by: Brendan Higgins --- arch/um/kernel/trap.c | 11 ++++++++++- kunit/test.c | 13 +++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index 9b97712daf14f..c3ff8346800c4 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -226,8 +226,17 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, current->thread.segv_regs = container_of(regs, struct pt_regs, regs); catcher = current->thread.fault_catcher; - if (catcher && current->thread.is_running_test) + if (catcher && current->thread.is_running_test) { + /* + * TODO(b/77223210): Right now we don't have a way to store a + * copy of the stack, or a copy of information from the stack, + * so we need to print it now; otherwise, the stack will be + * destroyed by segv_run_catcher which works by popping off + * stack frames. + */ + show_stack(NULL, NULL); segv_run_catcher(catcher, (void *) address); + } else if (!is_user && (address >= start_vm) && (address < end_vm)) { flush_tlb_kernel_vm(); goto out; diff --git a/kunit/test.c b/kunit/test.c index 6ea60059b4918..5d78f76b421af 100644 --- a/kunit/test.c +++ b/kunit/test.c @@ -210,12 +210,17 @@ static void test_handle_test_crash(struct test *test, struct test_module *module, struct test_case *test_case) { - test_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. + * TODO(brendanhiggins@google.com): Right now we don't have a way to + * store a copy of the stack, or a copy of information from the stack, + * so we need to print it in the "trap" handler; otherwise, the stack + * will be destroyed when it returns to us by popping off the + * appropriate stack frames (see longjmp). + * + * Ideally we would print the stack trace here, but we do not have the + * ability to do so with meaningful information at this time. */ - show_stack(NULL, NULL); + test_err(test, "%s crashed", test_case->name); test_case_internal_cleanup(test); } From patchwork Tue Oct 16 23:51:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985063 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="KrTFzp6M"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="SkamGQ8F"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXJ23lQqz9sB5 for ; Wed, 17 Oct 2018 10:55:46 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=txCuQnKaMkmHJJgBshWdvvqU9NFqBapsUrQofV3pJcI=; b=KrTFzp6Md76zxE uhnRVvfUvIkiUAtLhErG4N7fTd2pJwqkItC4QwWPmY5PHmh9jAa5KgEROoEYz/27/3P7bhpdeX+rB yD/1c/WDApmOJ0hNo6Pv4KG2ygk+Us6bRc1XSFeI1Pk33AwTiVptxEnZ6jU8GgdJLCOq5xm8tNFj2 FsZdK1uk8V2D8Sm8W2lJ9J8Ic3VDcNnOd2BOYoIgON+wy8/RqgyjVZ0YGSHj2xnRnxgp04/bqXAH/ QOd9MM5uDVxGaVQGulmVO0ZBLYTMlSjybKtOp8KwW+aiMUWNMWwRBKocKZpFajNXeFcTUbsm1LQRw xrFwpN64EJSrXRyBZAKA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBY-00067W-8P; Tue, 16 Oct 2018 23:55:36 +0000 Received: from mail-vk1-xa4a.google.com ([2607:f8b0:4864:20::a4a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAx-0004Xj-R5 for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:26 +0000 Received: by mail-vk1-xa4a.google.com with SMTP id y199-v6so2823231vkd.20 for ; Tue, 16 Oct 2018 16:54:48 -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=v7Vdu6qIuJDj39fxhnwZmKq9fD4gFEoXGX/BaHbf6WY=; b=SkamGQ8FhTSHorjRljiJ6OFjD1m25IP36W7aMKtTvCYBTsCiHMQ+g6OFZ5CrW0Upqi 7mPFJKchLsgfdjOW+QOJbhaJfZoUjNBWn9pxuTZBLRFB/G3iYschPGYmqGTRLXnWXe56 E+Ku6Hy4CgqEyeNB+DFizdSn/TCDkQ73T+lJA0qmOY+SQIGxS8zbc7Yu8SDw01YaZYbQ 3c7PRNsuK6UycYb0zxh8yXy4612Ntg8n3d0GWgXfEUqF0cnDN9FCfxwow5Au5BXO1w+l 5vR1oqiR6EH4rgQ6FYCUHMk9hG+r+Vwcq9dCw+Vig44hEnQNg09IjV8bDhM4LIW0YOUH z6+A== 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=v7Vdu6qIuJDj39fxhnwZmKq9fD4gFEoXGX/BaHbf6WY=; b=VfOMwDlMgbrl/UnPZ3wNvxZmG0vsMdaPOmqteXpsQOczntrNowd3Iaoo1NC7jTqSac yXPLKRDfePHJij9N6wICZbRhPMQHgLzowU0cJQ3H4iYZqVXDnSfGFe/Xy9v3nmfqHF7M L5CyJDsHwLuiZaPbMfzV/AUuLI8c45IAQwfPrDd6858TSveqv9qbt+6LxgJUK6CftABn TeRucXcDqv8OiRkYTX5Sh+ZEd0Dq/0tjzswWHxc3CN8WKCO1zm5YU3vB6WR78efurLPp tFq0zy8uhyrS7q7TC5u2UZqtwm5NtGdp+rUcZP/R5Y2XYAgeDIUXhiGSDwLz4GD7ovkI WO1Q== X-Gm-Message-State: ABuFfogwmrHU8aH58pi6U0i3yTBucwf7Rmebi9rZscsDCAmpKH5Gol/G vw6nsbtvze61Ryh+4CcnA0UZx5ULQ3qwtarQ8voPEw== X-Google-Smtp-Source: ACcGV624pVPucriQYprUzO/GVEaPpNRkvZ/3eMhUsnZvHlDsB/kz5iwwgeHbsp39vDToO03LzP5qRgwLVX6LGUat1s5cMA== X-Received: by 2002:a67:7ec5:: with SMTP id z188mr18417863vsc.30.1539734087634; Tue, 16 Oct 2018 16:54:47 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:14 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-26-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 25/31] kunit: added concept of platform mocking From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165459_906365_C98EE9C2 X-CRM114-Status: GOOD ( 11.92 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:a4a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Platform mocking is the mocking of all platform specific functions that interact directly with hardware. In effect, this provides the ability to mock any hardware behavior. Signed-off-by: Brendan Higgins --- drivers/base/Makefile | 1 + drivers/base/platform-mock.c | 65 ++++++++++++++++++++++++++++ include/linux/platform_device_mock.h | 64 +++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 drivers/base/platform-mock.c create mode 100644 include/linux/platform_device_mock.h diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 704f442958103..77cc599daa020 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_PINCTRL) += pinctrl.o obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o obj-$(CONFIG_GENERIC_MSI_IRQ_DOMAIN) += platform-msi.o obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += arch_topology.o +obj-$(CONFIG_PLATFORM_MOCK) += platform-mock.o obj-y += test/ diff --git a/drivers/base/platform-mock.c b/drivers/base/platform-mock.c new file mode 100644 index 0000000000000..3df9f1b0bb50f --- /dev/null +++ b/drivers/base/platform-mock.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Fake platform device API for unit testing platform drivers. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include + +struct device_node *of_fake_node(struct test *test, const char *name) +{ + struct device_node *node; + + node = test_kzalloc(test, sizeof(*node), GFP_KERNEL); + if (!node) + return NULL; + + of_node_init(node); + + return node; +} + +struct platform_device * +of_fake_probe_platform(struct test *test, + const struct platform_driver *driver, + const char *node_name) +{ + struct platform_device *pdev; + struct device_node *of_node; + int ret; + + of_node = of_fake_node(test, node_name); + if (!of_node) + return ERR_PTR(-ENOMEM); + + test_info(test, "Creating device"); + pdev = of_platform_device_create(of_node, node_name, NULL); + if (!pdev) + return ERR_PTR(-ENODEV); + + test_info(test, "Probing"); + ret = driver->probe(pdev); + if (ret) + return ERR_PTR(ret); + + return pdev; +} + +struct platform_device *of_fake_probe_platform_by_name(struct test *test, + const char *driver_name, + const char *node_name) +{ + const struct device_driver *driver; + + test_info(test, "Locating driver by name"); + driver = driver_find(driver_name, &platform_bus_type); + if (!driver) + return ERR_PTR(-ENODEV); + + return of_fake_probe_platform(test, + to_platform_driver(driver), + node_name); +} diff --git a/include/linux/platform_device_mock.h b/include/linux/platform_device_mock.h new file mode 100644 index 0000000000000..898539d166f66 --- /dev/null +++ b/include/linux/platform_device_mock.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Fake platform device API for unit testing platform drivers. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include + +static inline struct platform_driver *platform_driver_find(const char *name) +{ + struct device_driver *driver; + + driver = driver_find(name, &platform_bus_type); + if (!driver) + return NULL; + + return to_platform_driver(driver); +} + +/** + * of_fake_node() + * @test: the test to associate node with + * @name: name of the node + * + * The &struct device_node returned is allocated as a root node with the given + * name and otherwise behaves as a real &struct device_node. + * + * Returns: the faked &struct device_node + */ +struct device_node *of_fake_node(struct test *test, const char *name); + +/** + * of_fake_probe_platform() + * @test: the test to associate the fake platform device with + * @driver: driver to probe + * @node_name: name of the device node created + * + * Creates a &struct platform_device and an associated &struct device_node, + * probes the provided &struct platform_driver with the &struct platform_device. + * + * Returns: the &struct platform_device that was created + */ +struct platform_device * +of_fake_probe_platform(struct test *test, + const struct platform_driver *driver, + const char *node_name); + +/** + * of_fake_probe_platform_by_name() + * @test: the test to associate the fake platform device with + * @driver_name: name of the driver to probe + * @node_name: name of the device node created + * + * Same as of_fake_probe_platform() but looks up the &struct platform_driver by + * the provided name. + * + * Returns: the &struct platform_device that was created + */ +struct platform_device *of_fake_probe_platform_by_name(struct test *test, + const char *driver_name, + const char *node_name); From patchwork Tue Oct 16 23:51:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985062 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="jUvbXmIb"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="Ij1H6ZBo"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXJ15GSwz9s9J for ; Wed, 17 Oct 2018 10:55:45 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=6meoGXwQmcZz2BUNZ2cXjUG103oOPQ8EJgwaqRhQwEQ=; b=jUvbXmIb1Pu/8F G21HanK5kl9STFtRBu5m1MYEKXWyseS8SSfJMuQRWCrPqFNPMrTfS6+THAzc+37GWFDJfjjVkbEme urTw1OYSqJT2sKiUmvsvmhB5mBzudczKTszfD2ZSKYOj3AwmCiK9pZBciftcu6CogqogD20OG8PQf FNo1aPXmv+kFqVkBMdtIlUOx6LjenTvDkjM03VdvlkJI6Bc3htb4RNkxWu83tEiwKMzbhuQSq1B0u +JdOOTpYZTBPPM2ba938rzOKRI67bBXfy5E5BYtGNd2Jm79bAxMlZcPVrwiJenC1C5b1c1RSoXOGZ 61eQFac1QWUODVR91RDA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBW-00066z-KO; Tue, 16 Oct 2018 23:55:34 +0000 Received: from mail-it1-x149.google.com ([2607:f8b0:4864:20::149]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAs-0004ZQ-O8 for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:24 +0000 Received: by mail-it1-x149.google.com with SMTP id p125-v6so271843itg.1 for ; Tue, 16 Oct 2018 16:54:50 -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=rTMtFvbEL5wKlvpvyNmblvvMe/SeevVrMylypi+1pmA=; b=Ij1H6ZBoqLFXY4LSZ+F8jqafESI09IwnXUv/SayN/rV7iRL83lzp1R2mBkHzggwzs9 MXXwlgtYixKHjLyfIl2WO/XgBTnPG+XHVwvWnQjXhJcaW279Lo/J8U5XesswVnNhCQbe uUJpe8pwYL0m8ca8bNnoUM3khas3fIhyY2cX8jCDIXnGSJse35/pjJo6loDFqzb2CAT3 u35UN8SnnwuREmP8xbDVvX306UmsGOuteOTWn72WeNtfA+I/30JWNkxXsw7eell8JQij HCONAWQhFvaJYlOIKdiYhMve/YRRsvfwyZxAz6NnL5kDW3zNU/YQK7J07/3Kj3wnDybU STxw== 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=rTMtFvbEL5wKlvpvyNmblvvMe/SeevVrMylypi+1pmA=; b=TOBZVNeXQnAQvqJ3cFUSI/6k1m02ksh6C3KWkYcJAIEmSwKVJugtKLH8op9DJvUzGv qfRxn1bFFdk15ICqLObbdtGomitzE4HPsBfMEgdh5u+mZHIUZad10GQICFSOTiZwrQqx 3gH3HF91B6eKrVNPchHhO+80EI/BNtrBZHIpSigXFm7ZgbxdsKA2olYn3VSpjzXipcQ5 H6Dfwa1AFbsgGT6/hmqOuO9AG8NivkEp9clubGuHuxOtAFw0SLNBRa/J9M/NEOqt2ChO 95kUcge+XsRim/fp8mru/cqClCQu15897mVQF+6+WJ8Fff9YFamIxJcB+z1qD4MhxXrZ JqIw== X-Gm-Message-State: ABuFfogq0eKJFaHQm/ox7SRDXutNsRpOYy+wv7qMXvbh9lSm9G7P2Wza 63wm8EzGUz34CzPr2/tGA4J2F+wmFxWaplVY4cNbSQ== X-Google-Smtp-Source: ACcGV60qsgyfUWmkMgB33t8fclVO9HCQg4LIMj60RsbXwcv+KsjUPuYEU7pePKSmHdn+QyrEW5B4LfgVo/7XN134Sm2RWw== X-Received: by 2002:a05:660c:310:: with SMTP id u16mr218066itj.1.1539734089937; Tue, 16 Oct 2018 16:54:49 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:15 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-27-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 26/31] arch: um: added stubs for mock iomem for KUnit From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165454_889269_CC87AB01 X-CRM114-Status: GOOD ( 13.66 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:149 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org This mocks out some iomem functions (functions like readl and writel), for mocking hardware interfaces. Signed-off-by: Brendan Higgins --- arch/um/Kconfig.common | 8 +++++- arch/um/Kconfig.um | 5 ++++ arch/um/include/asm/Kbuild | 1 - arch/um/include/asm/io-mock-shared.h | 33 +++++++++++++++++++++ arch/um/include/asm/io-mock.h | 43 ++++++++++++++++++++++++++++ arch/um/include/asm/io.h | 8 ++++++ arch/um/kernel/Makefile | 1 + arch/um/kernel/io-mock.c | 40 ++++++++++++++++++++++++++ kunit/Kconfig | 1 + 9 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 arch/um/include/asm/io-mock-shared.h create mode 100644 arch/um/include/asm/io-mock.h create mode 100644 arch/um/kernel/io-mock.c diff --git a/arch/um/Kconfig.common b/arch/um/Kconfig.common index 07f84c842cc31..72e7efb74f7fd 100644 --- a/arch/um/Kconfig.common +++ b/arch/um/Kconfig.common @@ -19,7 +19,13 @@ config MMU default y config NO_IOMEM - def_bool y + bool + default y if !KUNIT + +config HAS_IOMEM + bool "Turns on fake IOMEM support for KUnit" + depends on KUNIT + select MOCK_IOMEM config ISA bool diff --git a/arch/um/Kconfig.um b/arch/um/Kconfig.um index 20da5a8ca9490..8d35e0e2c23d1 100644 --- a/arch/um/Kconfig.um +++ b/arch/um/Kconfig.um @@ -122,3 +122,8 @@ config SECCOMP defined by each seccomp mode. If unsure, say Y. + +config PLATFORM_MOCK + bool "Enable a mock architecture used for unit testing." + depends on KUNIT && OF + default n diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild index b10dde6cb793b..9fd2827ab76d1 100644 --- a/arch/um/include/asm/Kbuild +++ b/arch/um/include/asm/Kbuild @@ -12,7 +12,6 @@ generic-y += ftrace.h generic-y += futex.h generic-y += hardirq.h generic-y += hw_irq.h -generic-y += io.h generic-y += irq_regs.h generic-y += irq_work.h generic-y += kdebug.h diff --git a/arch/um/include/asm/io-mock-shared.h b/arch/um/include/asm/io-mock-shared.h new file mode 100644 index 0000000000000..6baf59cb17a58 --- /dev/null +++ b/arch/um/include/asm/io-mock-shared.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_UM_IO_MOCK_SHARED_H +#define _ASM_UM_IO_MOCK_SHARED_H + +#define readb readb +u8 readb(const volatile void __iomem *); + +#define readw readw +u16 readw(const volatile void __iomem *); + +#define readl readl +u32 readl(const volatile void __iomem *); + +#ifdef CONFIG_64BIT +#define readq readq +u64 readq(const volatile void __iomem *); +#endif /* CONFIG_64BIT */ + +#define writeb writeb +void writeb(u8, const volatile void __iomem *); + +#define writew writew +void writew(u16, const volatile void __iomem *); + +#define writel writel +void writel(u32, const volatile void __iomem *); + +#ifdef CONFIG_64BIT +#define writeq writeq +void writeq(u64, const volatile void __iomem *); +#endif /* CONFIG_64BIT */ + +#endif /* _ASM_UM_IO_MOCK_SHARED_H */ diff --git a/arch/um/include/asm/io-mock.h b/arch/um/include/asm/io-mock.h new file mode 100644 index 0000000000000..bdc5cd1d4e33c --- /dev/null +++ b/arch/um/include/asm/io-mock.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Mock IO functions. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#ifndef _ASM_UM_IO_MOCK_H +#define _ASM_UM_IO_MOCK_H + +#include +#include + +DECLARE_FUNCTION_MOCK(readb, + RETURNS(u8), PARAMS(const volatile void __iomem *)); + +DECLARE_FUNCTION_MOCK(readw, + RETURNS(u16), PARAMS(const volatile void __iomem *)); + +DECLARE_FUNCTION_MOCK(readl, + RETURNS(u32), PARAMS(const volatile void __iomem *)); + +#ifdef CONFIG_64BIT +DECLARE_FUNCTION_MOCK(readq, + RETURNS(u64), PARAMS(const volatile void __iomem *)); +#endif /* CONFIG_64BIT */ + +DECLARE_FUNCTION_MOCK_VOID_RETURN(writeb, + PARAMS(u8, const volatile void __iomem *)); + +DECLARE_FUNCTION_MOCK_VOID_RETURN(writew, + PARAMS(u16, const volatile void __iomem *)); + +DECLARE_FUNCTION_MOCK_VOID_RETURN(writel, + PARAMS(u32, const volatile void __iomem *)); + +#ifdef CONFIG_64BIT +DECLARE_FUNCTION_MOCK_VOID_RETURN(writeq, + PARAMS(u64, const volatile void __iomem *)); +#endif /* CONFIG_64BIT */ + +#endif /* _ASM_UM_IO_MOCK_H */ diff --git a/arch/um/include/asm/io.h b/arch/um/include/asm/io.h index 96f77b5232aaf..a7f61cf963756 100644 --- a/arch/um/include/asm/io.h +++ b/arch/um/include/asm/io.h @@ -2,11 +2,19 @@ #ifndef _ASM_UM_IO_H #define _ASM_UM_IO_H +#include +#include + +#if IS_ENABLED(CONFIG_PLATFORM_MOCK) +#include +#endif + #define ioremap ioremap static inline void __iomem *ioremap(phys_addr_t offset, size_t size) { return (void __iomem *)(unsigned long)offset; } +#define ioremap_nocache ioremap #define iounmap iounmap static inline void iounmap(void __iomem *addr) diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile index 2f36d515762ec..770c480d5a101 100644 --- a/arch/um/kernel/Makefile +++ b/arch/um/kernel/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_GPROF) += gprof_syms.o obj-$(CONFIG_GCOV) += gmon_syms.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_STACKTRACE) += stacktrace.o +obj-$(CONFIG_PLATFORM_MOCK) += io-mock.o USER_OBJS := config.o diff --git a/arch/um/kernel/io-mock.c b/arch/um/kernel/io-mock.c new file mode 100644 index 0000000000000..e0d4648e97a6c --- /dev/null +++ b/arch/um/kernel/io-mock.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mock IO functions. + * + * Copyright (C) 2018, Google LLC. + * Author: Brendan Higgins + */ + +#include +#include +#include +#include + +DEFINE_FUNCTION_MOCK(readb, + RETURNS(u8), PARAMS(const volatile void __iomem *)); + +DEFINE_FUNCTION_MOCK(readw, + RETURNS(u16), PARAMS(const volatile void __iomem *)); + +DEFINE_FUNCTION_MOCK(readl, + RETURNS(u32), PARAMS(const volatile void __iomem *)); + +#ifdef CONFIG_64BIT +DEFINE_FUNCTION_MOCK(readq, + RETURNS(u64), PARAMS(const volatile void __iomem *)); +#endif /* CONFIG_64BIT */ + +DEFINE_FUNCTION_MOCK_VOID_RETURN(writeb, + PARAMS(u8, const volatile void __iomem *)); + +DEFINE_FUNCTION_MOCK_VOID_RETURN(writew, + PARAMS(u16, const volatile void __iomem *)); + +DEFINE_FUNCTION_MOCK_VOID_RETURN(writel, + PARAMS(u32, const volatile void __iomem *)); + +#ifdef CONFIG_64BIT +DEFINE_FUNCTION_MOCK_VOID_RETURN(writeq, + PARAMS(u64, const volatile void __iomem *)); +#endif /* CONFIG_64BIT */ diff --git a/kunit/Kconfig b/kunit/Kconfig index 5cb500355c873..9d4b7cfff9d92 100644 --- a/kunit/Kconfig +++ b/kunit/Kconfig @@ -6,6 +6,7 @@ menu "KUnit support" config KUNIT bool "Enable support for unit tests (KUnit)" + select HAS_IOMEM help Enables support for kernel unit tests (KUnit), a lightweight unit testing and mocking framework for the Linux kernel. These tests are From patchwork Tue Oct 16 23:51:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985067 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="dLa9rtFR"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="JxG0cA2G"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXJJ4rqkz9sB5 for ; Wed, 17 Oct 2018 10:56:00 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=3KS72spZjWsZsDiyRGvLApahaEqm2qFiLzURa6upq+M=; b=dLa9rtFR+JK1yA 90pJHOZWuLkvvIw6TFXeqsXl0iUWrQC5n+pTOlBk5nR4zbj6nuLSior5sy1FaRQ+pQ82TE99CBQAA 7FFUgMKUecfj8gaQrH51DKFOZU8M0kzVho8I+m6a5LFkIoYe/NLQHJJbhP25jlVtnIN3SzRzbiFiF MBZEY7rFetLrtfId82KsifgYB3nV1i8nM649bUDZqRNyJ/g86Krh85npwY1RRoMpvJPszLkh680DW UA9V4Du/DrpaUwAnNL23qGgT3hpwxj6G99XLPZzHR+vOI1CxqI4W2TuDItFHYEXsAY1eti4jz32Od wlcpZtqRhN/Kqy09b2Pw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBn-0006CT-Qa; Tue, 16 Oct 2018 23:55:51 +0000 Received: from mail-ot1-x349.google.com ([2607:f8b0:4864:20::349]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZB2-0004am-CZ for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:49 +0000 Received: by mail-ot1-x349.google.com with SMTP id s68so17972114ota.11 for ; Tue, 16 Oct 2018 16:54:53 -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=8JQjq3jjxfWsRI34be5tLEmUCC8ErrwqnGVaEIBnglA=; b=JxG0cA2GwjOvv3iDVcP7ijufVw+YGRKlIvHvVhvryhtz78KrzjBQTLlTy4B2+td7HU bw9+S5O+sV4q03T74EQLRYZPBtqy6hBHKuScJHEvIg1gAxiPTNERo4jfr0BMavdNc6HW 8/daEv9k1GfFI2YZSZBhDTNrCLWg2vWYKRZbrRT1oOoCqSgScPKnuY1W/uPBX48549jk P+OxStD42LP/ZB/OMP8YK0S2B2lyvo//+NHmusmP9GIPWvp+ZZlnpaN+tHBHbDT3OZQC lqWfBS2SKb12+70/q7x3kBGK3yuvqB/03PdGbXHdKY6bBMSVIAoV1FYi+vLyFXmOg5VC F8bQ== 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=8JQjq3jjxfWsRI34be5tLEmUCC8ErrwqnGVaEIBnglA=; b=f51u+3kl7v+o/PqbXkopdUlBQnFyI955LGMoV1VAXRqolWHsAKTtqUy07JTPBz9WgU 1JDGfsLUwAsVFAQcu6kOvQM+Rzans+U/36EVUwoiJccig3OfQTujBBcn5+cd7kg3PYjA RfQurPx/1DvEcZ6mjgA4mAFv/983gsQNvq2FovvJD5a27/+kYVlDDIIH15wPaPNyRAYI 8+XNuhegr9esV/OB/VLX0js22p3V7ZgYsMQkqDSYmGOsioc7as/5ddUNBPQ4rPDL0p/P 9ymjn3EcUoimCzpU7ISmcY9y/xZxViSWcvMnhX1k/V9aHo8NfpZOLv9jFDFkbsHGEzer wlAA== X-Gm-Message-State: ABuFfohgc7C79fJXNPfq/8QDI4Gy3PjJao9tKpAWlVLWe+WOXo57rwbg R1Wtui7wkDLgF7nM/NWlHTE17nHtovPD2yWZ05gxxg== X-Google-Smtp-Source: ACcGV62ZGWlrAKherce8lXLVzlDaHnPMCD/L2BzmINAK+Ck2DKM0kXSRftQSX1xTvkyKOT86jQFdDzHHS8HvgkXhi0JMqw== X-Received: by 2002:aca:3110:: with SMTP id x16-v6mr20157312oix.8.1539734092276; Tue, 16 Oct 2018 16:54:52 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:16 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-28-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 27/31] Documentation: kunit: adds complete documentation for KUnit From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165504_593666_5E5F3511 X-CRM114-Status: GOOD ( 20.70 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, Felix Guo , joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org - Added intro and usage guide for KUnit - Added API reference Signed-off-by: Felix Guo Signed-off-by: Brendan Higgins --- Documentation/index.rst | 1 + .../kunit/api/class-and-function-mocking.rst | 68 ++ Documentation/kunit/api/index.rst | 21 + Documentation/kunit/api/platform-mocking.rst | 36 + Documentation/kunit/api/test.rst | 15 + Documentation/kunit/faq.rst | 46 + Documentation/kunit/index.rst | 84 ++ Documentation/kunit/start.rst | 185 ++++ Documentation/kunit/usage.rst | 876 ++++++++++++++++++ 9 files changed, 1332 insertions(+) create mode 100644 Documentation/kunit/api/class-and-function-mocking.rst create mode 100644 Documentation/kunit/api/index.rst create mode 100644 Documentation/kunit/api/platform-mocking.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 fdc585703498e..9415b6536d04b 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -66,6 +66,7 @@ merged much easier. kernel-hacking/index trace/index maintainer/index + kunit/index Kernel API documentation ------------------------ diff --git a/Documentation/kunit/api/class-and-function-mocking.rst b/Documentation/kunit/api/class-and-function-mocking.rst new file mode 100644 index 0000000000000..3e53291145f1f --- /dev/null +++ b/Documentation/kunit/api/class-and-function-mocking.rst @@ -0,0 +1,68 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================== +Class and Function Mocking +========================== + +This file documents class and function mocking features. + +.. note:: + If possible, prefer class mocking over arbitrary function mocking. Class + mocking has a much more limited scope and provides more control. + This file documents class mocking and most mocking features that do not + depend on function or platform mocking. + +Readability Macros +------------------ +When defining and declaring mock stubs, use these readability macros. + +.. code-block:: c + + #define CLASS(struct_name) struct_name + #define HANDLE_INDEX(index) index + #define METHOD(method_name) method_name + #define RETURNS(return_type) return_type + #define PARAMS(...) __VA_ARGS__ + +Consider a ``struct Foo`` with a member function +``int add(struct Foo*, int a, int b);`` + +When generating a mock stub with :c:func:`DEFINE_STRUCT_CLASS_MOCK`, which +takes a method name, struct name, return type, and method parameters, the +arguments should be passed in with the readability macros. + +.. code-block:: c + + DEFINE_STRUCT_CLASS_MOCK( + METHOD(add), + CLASS(Foo), + RETURNS(int), + PARAMS(struct Foo *, int, int) + ); + +For a more detailed example of this, take a look at the example in +:doc:`../start` + +These macros should only be used in the context of the mock stub generators. + + +Built in Matchers +----------------- + +.. kernel-doc:: include/kunit/mock.h + :doc: Built In Matchers + +Mock Returns +------------ +These functions can be used to specify a value to be returned (``ret``) when a +mocked function is intercepted via :c:func:`EXPECT_CALL`. + +.. code-block:: c + + struct mock_action *int_return(struct test *test, int ret); + struct mock_action *u32_return(struct test *test, u32 ret); + +API +--- +.. kernel-doc:: include/kunit/mock.h + :internal: diff --git a/Documentation/kunit/api/index.rst b/Documentation/kunit/api/index.rst new file mode 100644 index 0000000000000..a4fdc35b32c5c --- /dev/null +++ b/Documentation/kunit/api/index.rst @@ -0,0 +1,21 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============= +API Reference +============= +.. toctree:: + + test + class-and-function-mocking + platform-mocking + +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. +:doc:`class-and-function-mocking` documents class and function mocking features. +:doc:`platform-mocking` documents mocking libraries that mock out + platform specific features. +================================= ============================================== diff --git a/Documentation/kunit/api/platform-mocking.rst b/Documentation/kunit/api/platform-mocking.rst new file mode 100644 index 0000000000000..72555eb5b1de1 --- /dev/null +++ b/Documentation/kunit/api/platform-mocking.rst @@ -0,0 +1,36 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================ +Platform Mocking +================ + +This file documents *platform mocking*, mocking libraries that mock out platform +specific features and aid in writing mocks for platform drivers and other low +level kernel code. + +Enable Platform Mocking +----------------------- +``CONFIG_PLATFORM_MOCK`` needs to be added to the .config (or kunitconfig) to +enable platform mocking. + +Mocked IO Functions +------------------- +The following functions have been mocked for convenience. + +.. code-block:: c + + u8 readb(const volatile void __iomem *); + u16 readw(const volatile void __iomem *); + u32 readl(const volatile void __iomem *); + u64 readq(const volatile void __iomem *); + void writeb(u8, const volatile void __iomem *); + void writew(u16, const volatile void __iomem *); + void writel(u32, const volatile void __iomem *); + void writeq(u64, const volatile void __iomem *); + +.. note:: These functions do not have any non-mocked behaviour in UML. + +API +--- +.. kernel-doc:: include/linux/platform_device_mock.h + :internal: diff --git a/Documentation/kunit/api/test.rst b/Documentation/kunit/api/test.rst new file mode 100644 index 0000000000000..7f22db32536eb --- /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/test-stream.h + :internal: + diff --git a/Documentation/kunit/faq.rst b/Documentation/kunit/faq.rst new file mode 100644 index 0000000000000..fce128d804b49 --- /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..fc2716f155d74 --- /dev/null +++ b/Documentation/kunit/index.rst @@ -0,0 +1,84 @@ +.. 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++. They have the same structure for defining test +suites and test cases. KUnit defines a way to mock out C style classes and +functions and create expectations on methods called within the code under test. + +Get started now: :doc:`start` + +Why KUnit? +========== + +Aside from KUnit there is no true unit testing framework for the Linux kernel. +Autotest and kselftest are sometimes cited as unit testing frameworks; however, +they are not by most reasonable definitions of unit tests. + +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..14d1e4bd02f58 --- /dev/null +++ b/Documentation/kunit/start.rst @@ -0,0 +1,185 @@ +.. 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 + #include "example.h" + + /* Define the test cases. */ + + static void misc_example_add_test_basic(struct test *test) + { + TEST_EXPECT_EQ(test, 1, misc_example_add(1, 0)); + TEST_EXPECT_EQ(test, 2, misc_example_add(1, 1)); + TEST_EXPECT_EQ(test, 0, misc_example_add(-1, 1)); + TEST_EXPECT_EQ(test, INT_MAX, misc_example_add(0, INT_MAX)); + TEST_EXPECT_EQ(test, -1, misc_example_add(INT_MAX, INT_MIN)); + } + + static void misc_example_test_failure(struct test *test) + { + TEST_FAIL(test, "This test never passes."); + } + + static struct test_case misc_example_test_cases[] = { + TEST_CASE(misc_example_add_test_basic), + TEST_CASE(misc_example_test_failure), + {}, + }; + + static struct test_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 && TEST + +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: + +.. TODO(brendanhiggins@google.com): update me!!! + +.. code-block:: none + + ... + kunit misc-example: misc_example_bar_test_success passed + kunit misc-example: EXPECTATION FAILED at drivers/misc/example-test.c:48 + Expected -22 == misc_example_bar(example), but + -22 == -22 + misc_example_bar(example) == -5 + kunit misc-example: misc_example_bar_test_failure failed + kunit misc-example: one or more tests failed + +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..605bc4d1165a7 --- /dev/null +++ b/Documentation/kunit/usage.rst @@ -0,0 +1,876 @@ +.. 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 test *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 test *test) + { + } + + void example_test_failure(struct test *test) + { + TEST_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 ``TEST_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 test *test) + { + TEST_EXPECT_EQ(test, 1, add(1, 0)); + TEST_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 test *``, 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 test *test) + { + TEST_EXPECT_EQ(test, 1, add(1, 0)); + TEST_EXPECT_EQ(test, 2, add(1, 1)); + } + + void add_test_negative(struct test *test) + { + TEST_EXPECT_EQ(test, 0, add(-1, 1)); + } + + void add_test_max(struct test *test) + { + TEST_EXPECT_EQ(test, INT_MAX, add(0, INT_MAX)); + TEST_EXPECT_EQ(test, -1, add(INT_MAX, INT_MIN)); + } + + void add_test_overflow(struct test *test) + { + TEST_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 test *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)); + TEST_ASSERT_NOT_ERR_OR_NULL(test, ret); + TEST_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 test_case example_test_cases[] = { + TEST_CASE(example_test_foo), + TEST_CASE(example_test_bar), + TEST_CASE(example_test_baz), + {}, + }; + + static struct test_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 test *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); + TEST_EXPECT_EQ(test, fake_eeprom->contents[0], 0); + + eeprom_buffer->write(eeprom_buffer, buffer, 1); + TEST_EXPECT_EQ(test, fake_eeprom->contents[1], 0); + + eeprom_buffer->flush(eeprom_buffer); + TEST_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff); + TEST_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff); + } + + static void eeprom_buffer_test_flushes_after_flush_count_met(struct test *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); + TEST_EXPECT_EQ(test, fake_eeprom->contents[0], 0); + + eeprom_buffer->write(eeprom_buffer, buffer, 1); + TEST_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff); + TEST_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff); + } + + static void eeprom_buffer_test_flushes_increments_of_flush_count(struct test *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); + TEST_EXPECT_EQ(test, fake_eeprom->contents[0], 0); + + eeprom_buffer->write(eeprom_buffer, buffer, 2); + TEST_EXPECT_EQ(test, fake_eeprom->contents[0], 0xff); + TEST_EXPECT_EQ(test, fake_eeprom->contents[1], 0xff); + /* Should have only flushed the first two bytes. */ + TEST_EXPECT_EQ(test, fake_eeprom->contents[2], 0); + } + + static int eeprom_buffer_test_init(struct test *test) + { + struct eeprom_buffer_test *ctx; + + ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + ASSERT_NOT_ERR_OR_NULL(test, ctx); + + ctx->fake_eeprom = test_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 test *test) + { + struct eeprom_buffer_test *ctx = test->priv; + + destroy_eeprom_buffer(ctx->eeprom_buffer); + } + +Mocking Classes +~~~~~~~~~~~~~~~ + +Sometimes the easiest way to make assertions about behavior is to verify +certain methods or functions were called with appropriate arguments. KUnit +allows classes to be *mocked* which means that it generates subclasses whose +behavior can be specified in a test case. KUnit accomplishes this with two sets +of macros: the mock generation macros and the ``TEST_EXPECT_CALL`` macro. + +For example, let's go back to the EEPROM example; instead of faking the EEPROM, +we could have *mocked it out* with the following code: + +.. code-block:: c + + DECLARE_STRUCT_CLASS_MOCK_PREREQS(eeprom); + + DEFINE_STRUCT_CLASS_MOCK(METHOD(read), CLASS(eeprom), + RETURNS(ssize_t), + PARAMS(struct eeprom *, size_t, char *, size_t)); + + DEFINE_STRUCT_CLASS_MOCK(METHOD(write), CLASS(eeprom), + RETURNS(ssize_t), + PARAMS(struct eeprom *, size_t, const char *, size_t)); + + static int eeprom_init(struct MOCK(eeprom) *mock_eeprom) + { + struct eeprom *eeprom = mock_get_trgt(mock_eeprom); + + eeprom->read = read; + eeprom->write = write; + + return 0; + } + + DEFINE_STRUCT_CLASS_MOCK_INIT(eeprom, eeprom); + +We could use the mock in a test as follows: + +.. code-block:: c + + struct eeprom_buffer_test { + struct MOCK(eeprom) *mock_eeprom; + struct eeprom_buffer *eeprom_buffer; + }; + + static void eeprom_buffer_test_does_not_write_until_flush(struct test *test) + { + struct eeprom_buffer_test *ctx = test->priv; + struct eeprom_buffer *eeprom_buffer = ctx->eeprom_buffer; + struct MOCK(eeprom) *mock_eeprom = ctx->mock_eeprom; + struct mock_expectation *expectation; + char buffer[] = {0xff, 0xff}; + + eeprom_buffer->flush_count = SIZE_MAX; + + expectation = TEST_EXPECT_CALL(write(mock_get_ctrl(mock_eeprom), + test_any(test), + test_any(test), + test_any(test))); + expectation->max_calls_expected = 0; + expectation->min_calls_expected = 0; + + eeprom_buffer->write(eeprom_buffer, buffer, 1); + eeprom_buffer->write(eeprom_buffer, buffer, 1); + + mock_validate_expectations(mock_get_ctrl(mock_eeprom)); + + expectation = TEST_EXPECT_CALL(write(mock_get_ctrl(mock_eeprom), + test_any(test), + test_memeq(test, + buffer, + ARRAY_SIZE(buffer)), + test_ulong_eq(test, 2))); + expectation->max_calls_expected = 1; + expectation->min_calls_expected = 1; + expectation->action = test_long_return(test, 2); + + eeprom_buffer->flush(eeprom_buffer); + } + + static int eeprom_buffer_test_init(struct test *test) + { + struct eeprom_buffer_test *ctx; + + ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + ASSERT_NOT_ERR_OR_NULL(test, ctx); + + ctx->mock_eeprom = CONSTRUCT_MOCK(eeprom, test); + ASSERT_NOT_ERR_OR_NULL(test, ctx->fake_eeprom); + + ctx->eeprom_buffer = new_eeprom_buffer(mock_get_trgt(ctx->mock_eeprom)); + ASSERT_NOT_ERR_OR_NULL(test, ctx->eeprom_buffer); + + test->priv = ctx; + + return 0; + } + + static void eeprom_buffer_test_exit(struct test *test) + { + struct eeprom_buffer_test *ctx = test->priv; + + destroy_eeprom_buffer(ctx->eeprom_buffer); + } + +This test case tests the same thing as the +``eeprom_buffer_test_does_not_write_until_flush`` test case from the example in +the faking section. Observe that in this test case you specify how you expect +the mock to be called (technically this is both stubbing and mocking `which are +different things +`_, +but KUnit combines them as many other xUnit testing libraries do) and also how +the mock should behave when those expectations are met (see +``test_long_return``). + +Mocks are extremely powerful as they allow you the finest possible granularity +for verifying how units interact, and allows the injection of arbitrary +behavior. But as Uncle Ben said, "Great power comes with great responsibility." +Mocks are not to be used lightly; they make it possible to test things which are +otherwise difficult or impossible to test, but when used improperly they have a +much higher maintenance burden than using the real thing or even a high quality +fake. + +Compare the ``eeprom_buffer_test_does_not_write_until_flush`` in the faking +example to the above version that uses mocking. It is pretty clear that the +version that uses faking is easier to read. It is also pretty clear that common +behavior between test cases would have to be duplicated with the mocking +version; the fake has the advantage of implementing desired behavior in a single +place. Finally, it is pretty clear that the fake would be much easier to +maintain. Of course what's even easier than having to maintain a fake is not +not having to maintain anything at all. Thus, + +.. important:: + Always prefer high quality fakes over mocks, and always prefer "real" code to + fakes. + +Fakes should generally be used when there is an external dependency that there +is no way around; in the kernel that usually means hardware. If you write a fake +you have to make sure it can be maintained; consequently, it is just as +important as real code and it should get its own tests to verify it works as +expected. Yes, we are telling you to write tests for your fakes. + +Of course sometimes faking something out is infeasible, or there is some code +that is just otherwise impossible to reach; generally this means that your code +should be refactored, but not always. Either way, well tested code in need of +refactoring is better than code that needs refactoring but has no tests. This +leads to the single most important testing principle that overrides all others: + +.. important:: + **Always prefer tests over no tests, no matter what!** + +For more information on class mocking see :doc:`api/class-and-function-mocking`. + +Mocking Arbitrary Functions +--------------------------- + +.. important:: + Always prefer class mocking over arbitrary function mocking where possible. + Class mocking has a much more limited scope and provides more control. + +Sometimes it is necessary to mock a function that does not use any class style +indirection. First and foremost, if you encounter this in your own code, please +rewrite it so that uses class style indirection discussed above, but if this is +in some code that is outside of your control you may use KUnit's function +mocking features. + +KUnit provides macros to allow arbitrary functions to be overridden so that the +original definition is replaced with a mock stub. For most functions, all you +have to do is label the function ``__mockable``: + +.. code-block:: c + + int __mockable example(int arg) {...} + +If a function is ``__mockable`` and a mock is defined: + +.. code-block:: c + + DEFINE_FUNCTION_MOCK(example, RETURNS(int), PARAMS(int)); + +When the function is called, the mock stub will actually be called. + +.. note:: + There is no performance penalty or potential side effects from doing this. + When not compiling for testing, ``__mockable`` compiles away. + +.. note:: + ``__mockable`` does not work on inlined functions. + +Spying +~~~~~~ + +Sometimes it is desirable to have a mock function that delegates to the original +definition in some or all circumstances. This is called *spying*: + +.. code-block:: c + + DEFINE_SPYABLE(i2c_add_adapter, RETURNS(int), PARAMS(struct i2c_adapter *)); + int REAL_ID(i2c_add_adapter)(struct i2c_adapter *adapter) + { + ... + } + +This allows the function to be overridden by a mock as with ``__mockable``; +however, it associates the original definition of the function with an alternate +symbol that KUnit can still reference. This makes it possible to mock the +function and then have the mock delegate to the original function definition +with the ``INVOKE_REAL(...)`` action: + +.. code-block:: c + + static int aspeed_i2c_test_init(struct test *test) + { + struct mock_param_capturer *adap_capturer; + struct mock_expectation *handle; + struct aspeed_i2c_test *ctx; + int ret; + + ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + test->priv = ctx; + + handle = TEST_EXPECT_CALL( + i2c_add_adapter(capturer_to_matcher(adap_capturer))); + handle->action = INVOKE_REAL(test, i2c_add_adapter); + ret = of_fake_probe_platform_by_name(test, + "aspeed-i2c-bus", + "test-i2c-bus"); + if (ret < 0) + return ret; + + ASSERT_PARAM_CAPTURED(test, adap_capturer); + ctx->adap = mock_capturer_get(adap_capturer, struct i2c_adapter *); + + return 0; + } + +For more information on function mocking see +:doc:`api/class-and-function-mocking`. + +Platform Mocking +---------------- +The Linux kernel generally forbids normal code from accessing architecture +specific features. Instead, low level hardware features are usually abstracted +so that architecture specific code can live in the ``arch/`` directory and all +other code relies on APIs exposed by it. + +KUnit provides a mock architecture that currently allows mocking basic IO memory +accessors and in the future will provide even more. A major use case for +platform mocking is unit testing platform drivers, so KUnit also provides +helpers for this as well. + +In order to use platform mocking, ``CONFIG_PLATFORM_MOCK`` must be enabled in +your ``kunitconfig``. + +For more information on platform mocking see :doc:`api/platform-mocking`. + +Method Call Expectations +======================== +Once we have classes and methods mocked, we can place more advanced +expectations. Previously, we could only place expectations on simple return +values. With the :c:func:`TEST_EXPECT_CALL` macro, which allows you to make +assertions that a certain mocked function is called with specific arguments +given some code to be run. + +Basic Usage +----------- +Imagine we had some kind of dependency like this: + +.. code-block:: c + + struct Printer { + void (*print)(int arg); + }; + + // Printer's print + void printer_print(int arg) + { + do_something_to_print_to_screen(arg); + } + + struct Foo { + struct Printer *internal_printer; + void (*print_add_two)(struct Foo*, int); + }; + + // Foo's print_add_two: + void foo_print_add_two(struct Foo *this, int arg) + { + internal_printer->print(arg + 2); + } + +and we wanted to test ``struct Foo``'s behaviour, that ``foo->print_add_two`` +actually adds 2 to the argument passed. To properly unit test this, we create +mocks for all of ``struct Foo``'s dependencies, like ``struct Printer``. +We first setup stubs for ``MOCK(Printer)`` and its ``print`` function. + +In the real code, we'd assign a real ``struct Printer`` to the +``internal_printer`` variable in our ``struct Foo`` object, but in the +test, we'd construct a ``struct Foo`` with our ``MOCK(Printer)``. + +Finally, we can place expectations on the ``MOCK(Printer)``. + +For example: + +.. code-block:: c + + static int test_foo_add_two(struct test *test) + { + struct MOCK(Printer) *mock_printer = get_mocked_printer(); + struct Foo *foo = initialize_foo(mock_printer); + + // print() is a mocked method stub + TEST_EXPECT_CALL(print(test_any(test), test_int_eq(test, 12))); + + foo->print_add_two(foo, 10); + } + +Here, we expect that the printer's print function will be called (by default, +once), and that it will be called with the argument ``12``. Once we've placed +expectations, we can call the function we want to test to see that it behaves +as we expected. + +Matchers +-------- +Above, we see ``test_any`` and ``test_int_eq``, which are matchers. A matcher +simply asserts that the argument passed to that function call fulfills some +condition. In this case, ``test_any()`` matches any argument, and +``test_int_eq(12)`` asserts that the argument passed to that function must +equal 12. If we had called: ``foo->print_add_two(foo, 9)`` instead, the +expectation would not have been fulfilled. There are a variety of built-in +matchers: :doc:`api/class-and-function-mocking` has a section about these +matchers. + +.. note:: + :c:func:`TEST_EXPECT_CALL` only works with mocked functions and methods. + Matchers may only be used within the function inside the + :c:func:`TEST_EXPECT_CALL`. + +Additional :c:func:`EXPECT_CALL` Properties +------------------------------------------- + +The return value of :c:func:`TEST_EXPECT_CALL` is a ``struct +mock_expectation``. We can capture the value and add extra properties to it as +defined by the ``struct mock_expectation`` interface. + +Times Called +~~~~~~~~~~~~ +In the previous example, if we wanted assert that the method is never called, +we could write: + +.. code-block:: c + + ... + struct mock_expectation* handle = TEST_EXPECT_CALL(...); + handle->min_calls_expected = 0; + handle->max_calls_expected = 0; + ... + +Both those fields are set to 1 by default and can be changed to assert a range +of times that the method or function is called. + +Mocked Actions +~~~~~~~~~~~~~~ +Because ``mock_printer`` is a mock, it doesn't actually perform any task. If +the function had some side effect that ``struct Foo`` requires to have been +done, such as modifying some state, we could mock that as well. + +Each expectation has an associated ``struct mock_action`` which can be set with +``handle->action``. By default, there are two actions that mock return values. +Those can also be found in :doc:`api/class-and-function-mocking`. + +Custom actions can be defined by simply creating a ``struct mock_action`` and +assigning the appropriate function to ``do_action``. Mocked actions have access +to the parameters passed to the mocked function, as well as have the ability to +change / set the return value. + + +The Nice, the Strict, and the Naggy +=================================== +KUnit has three different mock types that can be set on a mocked class: nice +mocks, strict mocks, and naggy mocks. These are set via the corresponding macros +:c:func:`NICE_MOCK`, :c:func:`STRICT_MOCK`, and :c:func:`NAGGY_MOCK`, with naggy +mocks being the default. + +The type of mock simply dictates the behaviour the mock exhibits when +expectations are placed on it. + ++-----------------------+------------+--------------------+--------------------+ +| | **Nice** | **Naggy (default)**| **Strict** | ++-----------------------+------------+--------------------+--------------------+ +| Method called with no | Do nothing | Prints warning for | Fails test, prints | +| expectations on it | | uninteresting call | warning | +| | | | uninteresting call | ++-----------------------+------------+--------------------+--------------------+ +| Method called with no | Fails test, prints warnings, prints tried | +| matching expectations | expectations | +| on it | | ++-----------------------+------------------------------------------------------+ +| Test ends with an | Fail test, print warning | +| unfulfilled | | +| expectation | | ++-----------------------+------------------------------------------------------+ + +These macros take a ``MOCK(struct_name)`` and so should be used when retrieving +the mocked object. Following the example in :doc:`start`, there was this test +case: + +.. code-block:: c + + static void misc_example_bar_test_success(struct test *test) + { + struct MOCK(misc_example) *mock_example = test->priv; + struct misc_example *example = mock_get_trgt(mock_example); + struct mock_expectation *handle; + + handle = TEST_EXPECT_CALL(misc_example_foo(mock_get_ctrl(mock_example), + test_int_eq(test, 5))); + handle->action = int_return(test, 0); + + TEST_EXPECT_EQ(test, 0, misc_example_bar(example)); + } + +If we wanted ``mock_example`` to be a nice mock instead, we would simply write: + +.. code-block:: c + + struct MOCK(misc_example) *mock_example = NICE_MOCK(test->priv); From patchwork Tue Oct 16 23:51:17 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985064 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="BCSbRRjV"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="BBrFvPu6"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXJ66NPzz9s9J for ; Wed, 17 Oct 2018 10:55:50 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=pafe3KCwddzxlY5GqbWo4eYkOcA1NPNuKbXhP5JgCVA=; b=BCSbRRjVuLFFtj mYva7fpSf/8i4acO6TWScxWw2+6fA5eq37B2LIZKoXKrkh3q2uxt4/L2vwINb8iqIqnkU5TVmwNc5 zf2LuFJE1lMc2qiiu+oblwfHuGLgjvC96RcMM35m1TT3xF5k/86m+OT7OzRlfAk9UwgJsUYk/sUdQ WFKutVK5KHmsiNG7XAbozR/DSQp3EzJ+z/GvE1oR02Lt89ZG56fKUpM6V+sgO4ul8Q32szb/4vrxo ZOkzgHz2zK+asuvOpmaBOpUjn+mUbzx2z+xgtiDPIjKGnL5mUtMyoVfftZgPCKTaomVmgcusN9HTY e4qW4hhyMpxIcW4QP4FQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBf-00069m-GS; Tue, 16 Oct 2018 23:55:43 +0000 Received: from mail-qk1-x74a.google.com ([2607:f8b0:4864:20::74a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZB4-0004bo-H7 for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:38 +0000 Received: by mail-qk1-x74a.google.com with SMTP id w6-v6so22529519qka.15 for ; Tue, 16 Oct 2018 16:54:55 -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=4xqp6vFemfXW8THZIer615fzQIJFwGYlGopIVPlWEZU=; b=BBrFvPu6rMmaVi30GrjwrXOOL8LtR+BGznO8B6Lu7/D0K7Rxmkg7bSDYdO0W37ggd6 KUNSubXNekqIeyP7oqpcPSpGI0mcv6Vtw2I5Z3936kap9Ya6W1ZImCRBoHm8wueyJwFs n3DXvsdpVwzqIG6E5uoEIgwtyv44VHSz4lAto6aCW+jm0k6tzCeeR55BZmH2pd3RGt4g JcdwDvt19oZbFzthf6WjznwYRdU7Wo4RSSdCEfrnbjAXW9y9Kzjl6Y+tfZ7jDte0Y1Zr 7s2y2yF6SPkihHyj/U3+03bMRJrvXI/JB2rc/uNM0cpzs9K2a3FRYrGnf1rSvlzbT2SQ qbkg== 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=4xqp6vFemfXW8THZIer615fzQIJFwGYlGopIVPlWEZU=; b=Ybd2FBVOC/EiWutKa+yXkLpyAakBLDmtBb5wEhMSbFm2WPxyQ4gBqFLmqK1xyM4gJB gHe6gDJCdtWL8p236cnEVuwAhttSJykpcTClwhCXEaT+WR8JA4tvm6mYBh/kEn03pQ7T InhdSLgGnenWzyx1cTrV1bwnc1jzUlbb2DKXKVPBxnNo7pVBXtLMvIUWxEa8D1XhoiJr aqoNCXffG6MjdSRbPgL3z8JTcKXZchFEPa9jiQGq1M0zkJC2VZvTnROmieR0k5PhKT37 pPosc5PthTJCr6vYY4pzneiBaM3G/9Zzq8CCs1XKW4Y+rEbota9NDjVVn+z/bYON52Lh WngA== X-Gm-Message-State: ABuFfoipr/XVJRdblcKoo3uU06RXllWHxjMjqBoNJ0moJ7i2+gfLYZBh eM7XfkkirdJtwnWQDuu8pWMcRbdYs3OTG0HE2Vbyzg== X-Google-Smtp-Source: ACcGV61ZHBmusrNthEsvEz3NbEV4FbUz7HloQ+nhCM0P9R1x78s+c2LlynbGJcX007Ruj/zTWK2lQyTYOoSWK9tR8t8v0Q== X-Received: by 2002:a0c:c13b:: with SMTP id f56mr20378309qvh.11.1539734094801; Tue, 16 Oct 2018 16:54:54 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:17 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-29-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 28/31] kunit: added Python libraries for handing KUnit config and kernel From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165506_668977_988FA90F X-CRM114-Status: GOOD ( 13.28 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:74a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, Felix Guo , joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org kunit_config.py: - parses .config and Kconfig files kunit_kernel.py: provides helper functions to: - configure the kernel using kunitconfig - builds the kernel with the correct architecture - provides function to invoke the kernel and stream the output back The kernel invocation is wrapped in a subprocess call within the module because regular invocation of the kernel (./linux) may modify TTY settings. Signed-off-by: Felix Guo Signed-off-by: Brendan Higgins --- tools/testing/kunit/.gitignore | 3 + tools/testing/kunit/kunit_config.py | 60 ++++++++++++++ tools/testing/kunit/kunit_kernel.py | 123 ++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 tools/testing/kunit/.gitignore create mode 100644 tools/testing/kunit/kunit_config.py create mode 100644 tools/testing/kunit/kunit_kernel.py diff --git a/tools/testing/kunit/.gitignore b/tools/testing/kunit/.gitignore new file mode 100644 index 0000000000000..c791ff59a37a9 --- /dev/null +++ b/tools/testing/kunit/.gitignore @@ -0,0 +1,3 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] \ No newline at end of file diff --git a/tools/testing/kunit/kunit_config.py b/tools/testing/kunit/kunit_config.py new file mode 100644 index 0000000000000..183bd5e758762 --- /dev/null +++ b/tools/testing/kunit/kunit_config.py @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0 + +import collections +import re + +CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_\w+ is not set$' +CONFIG_PATTERN = r'^CONFIG_\w+=\S+$' + +KconfigEntryBase = collections.namedtuple('KconfigEntry', ['raw_entry']) + + +class KconfigEntry(KconfigEntryBase): + + def __str__(self) -> str: + return self.raw_entry + + +class KconfigParseError(Exception): + """Error parsing Kconfig defconfig or .config.""" + + +class Kconfig(object): + """Represents defconfig or .config specified using the Kconfig language.""" + + def __init__(self): + self._entries = [] + + def entries(self): + return set(self._entries) + + def add_entry(self, entry: KconfigEntry) -> None: + self._entries.append(entry) + + def is_subset_of(self, other: "Kconfig") -> bool: + return self.entries().issubset(other.entries()) + + def write_to_file(self, path: str) -> None: + with open(path, 'w') as f: + for entry in self.entries(): + f.write(str(entry) + '\n') + + def parse_from_string(self, blob: str) -> None: + """Parses a string containing KconfigEntrys and populates this Kconfig.""" + self._entries = [] + is_not_set_matcher = re.compile(CONFIG_IS_NOT_SET_PATTERN) + config_matcher = re.compile(CONFIG_PATTERN) + for line in blob.split('\n'): + line = line.strip() + if not line: + continue + elif config_matcher.match(line) or is_not_set_matcher.match(line): + self._entries.append(KconfigEntry(line)) + elif line[0] == '#': + continue + else: + raise KconfigParseError('Failed to parse: ' + line) + + def read_from_file(self, path: str) -> None: + with open(path, 'r') as f: + self.parse_from_string(f.read()) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py new file mode 100644 index 0000000000000..87abaede50513 --- /dev/null +++ b/tools/testing/kunit/kunit_kernel.py @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: GPL-2.0 + +import logging +import subprocess +import os + +import kunit_config + +KCONFIG_PATH = '.config' + +class ConfigError(Exception): + """Represents an error trying to configure the Linux kernel.""" + + +class BuildError(Exception): + """Represents an error trying to build the Linux kernel.""" + + +class LinuxSourceTreeOperations(object): + """An abstraction over command line operations performed on a source tree.""" + + def make_mrproper(self): + try: + subprocess.check_output(['make', 'mrproper']) + except OSError as e: + raise ConfigError('Could not call make command: ' + e) + except subprocess.CalledProcessError as e: + raise ConfigError(e.output) + + def make_olddefconfig(self): + try: + subprocess.check_output(['make', 'ARCH=um', 'olddefconfig']) + except OSError as e: + raise ConfigError('Could not call make command: ' + e) + except subprocess.CalledProcessError as e: + raise ConfigError(e.output) + + def make(self): + try: + subprocess.check_output(['make', 'ARCH=um']) + except OSError as e: + raise BuildError('Could not call execute make: ' + e) + except subprocess.CalledProcessError as e: + raise BuildError(e.output) + + def linux_bin(self, params, timeout): + """Runs the Linux UML binary. Must be named 'linux'.""" + process = subprocess.Popen( + ['./linux'] + params, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + process.wait(timeout=timeout) + return process + + +class LinuxSourceTree(object): + """Represents a Linux kernel source tree with KUnit tests.""" + + def __init__(self): + self._kconfig = kunit_config.Kconfig() + self._kconfig.read_from_file('kunitconfig') + self._ops = LinuxSourceTreeOperations() + + def clean(self): + try: + self._ops.make_mrproper() + except ConfigError as e: + logging.error(e) + return False + return True + + def build_config(self): + self._kconfig.write_to_file(KCONFIG_PATH) + try: + self._ops.make_olddefconfig() + except ConfigError as e: + logging.error(e) + return False + validated_kconfig = kunit_config.Kconfig() + validated_kconfig.read_from_file(KCONFIG_PATH) + if not self._kconfig.is_subset_of(validated_kconfig): + logging.error('Provided Kconfig is not contained in validated .config!') + return False + return True + + def build_reconfig(self): + """Creates a new .config if it is not a subset of the kunitconfig.""" + if os.path.exists(KCONFIG_PATH): + existing_kconfig = kunit_config.Kconfig() + existing_kconfig.read_from_file(KCONFIG_PATH) + if not self._kconfig.is_subset_of(existing_kconfig): + print('Regenerating .config ...') + os.remove(KCONFIG_PATH) + return self.build_config() + else: + return True + else: + print('Generating .config ...') + return self.build_config() + + def build_um_kernel(self): + try: + self._ops.make_olddefconfig() + self._ops.make() + except (ConfigError, BuildError) as e: + logging.error(e) + return False + used_kconfig = kunit_config.Kconfig() + used_kconfig.read_from_file(KCONFIG_PATH) + if not self._kconfig.is_subset_of(used_kconfig): + logging.error('Provided Kconfig is not contained in final config!') + return False + return True + + def run_kernel(self, args=[]): + timeout = None + args.extend(['mem=256M']) + process = self._ops.linux_bin(args, timeout) + with open('test.log', 'w') as f: + for line in process.stdout: + f.write(line.rstrip().decode('ascii') + '\n') + yield line.rstrip().decode('ascii') From patchwork Tue Oct 16 23:51:18 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985061 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="Wrw9BXvq"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="MFgOnFqN"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXHy2rZyz9sCT for ; Wed, 17 Oct 2018 10:55:42 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=4Dd4QOSJTQ6cw5DwGVNfVvkyl/Zk6QjeOvq+1f6I4WM=; b=Wrw9BXvq6Poi5R Fo9YgQge1MBel5cdIwKfX3N5aYk3VwX/aGCkcjWdCdtrYCZhCO1qUx0q6KAAiRkLy5u8lLLa8sJ6N T7H1yqKYugaRrXzTr17a+UIuP7gLRhJesnAiYxOE+8GBdrMmpuvsOI6TFwozOLvVdczyNELyKIN3c 2oOQRtb1h2k9EIiNWiETqYH6p1Umi+k7MVCBK0EQb4qz4agIBSJCZfKEBdR5kGBVN9eGMk5VwVBda r4dhTpVgVLsNYcI2Z+XylJSsSAz2briE8k8YL/oNbSLb8b1EEed/AM8E/iaTuFcNaRCV63IURichp q/Q1BTUjpqWLnXtJHaPA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBV-00066Y-7k; Tue, 16 Oct 2018 23:55:33 +0000 Received: from mail-vk1-xa4a.google.com ([2607:f8b0:4864:20::a4a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZAx-0004cn-R2 for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:23 +0000 Received: by mail-vk1-xa4a.google.com with SMTP id 62-v6so6867946vkl.10 for ; Tue, 16 Oct 2018 16:54:57 -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=b78I6LKXjAodHUg1SMFyP1EtiuoqQQpIaEv86gV4Xbc=; b=MFgOnFqNwee3qTqac0+1kGkl0rS+vYGvFIBLBufRfS3v1MXYhn22rsjgmcf11NzfKb 6vIzxA5cHEUZtogrsugVhk0X+fCnrwD0sn0GQ/e/u1WuLEn2VaybNHXot4Q4T67CWL8P /W6ZtQ/keAdQ2Sltw52cXo/2I+yjSyuTdfg2jw0mi5EkpY5gKg+WanunU6o1qrHeCdgv avoH4eG6Ah64+dwj2C98wAan5tXz1n470+Dy5k6jrxzvQ58MbzfZ1OjTQGoOGD+TPufz zd9h5aiF0nwj2vFWNUXCQiBBCarUOVN7UYjKu/VuIJngDm52b8v5CFbEgg1oYoXtkfj0 Ugrw== 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=b78I6LKXjAodHUg1SMFyP1EtiuoqQQpIaEv86gV4Xbc=; b=a1HLeJPaviQT/gQ471LA46ypFZvEXKZYj1V5bXcyyY04345aL8j4Q0STbRGaeN4I0M eCrHFPIXymGYCK3OHfBYLaHPtSAEa/LSYYBzpY8oBCTqrdF0tTdFO71YtqzWiSfhM5J0 Okc3VaRaARzHHmVVKzJQtJCGaew/ShKjB3UflibOz21/p6Lks00bUQQK/R92BdU9LJNV uN1tV7pE7r+7cyv8c4t+EL/uMpJuXHNq7vWO/FPIp5wYnZCmL6ZZXfbKXrJtnFE/PdT0 by6pCNAlW3u8ZH7bKWjEIvGhjDD8P3aHUZSxpIfwsMrUiEeE4Vav9zrWPoAGDIfrqULG nxiA== X-Gm-Message-State: ABuFfoj5EZAsWtQRCGT6aJG23FEs/meZKLAKZVqg5MeKI/uV9y03q70z SedCv8CtXUjESqkWBQA2qXTyk9UQUmMX8auGRAXmDA== X-Google-Smtp-Source: ACcGV625MofNGMvfOzo8UkZmTs25veQzMn5HpPhVQdFoT4Fqjoc0uo2vdAsUhJJMhyisfsXRetfoFMHpRW53BERU3en0xA== X-Received: by 2002:a1f:720f:: with SMTP id n15mr4022284vkc.5.1539734097147; Tue, 16 Oct 2018 16:54:57 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:18 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-30-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 29/31] kunit: added KUnit wrapper script and simple output parser From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165459_910953_AC8B5A5F X-CRM114-Status: GOOD ( 13.33 ) X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:a4a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, Felix Guo , joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org The KUnit wrapper script interfaces with the two modules (kunit_config.py and kunit_kernel.py) and provides a command line interface for running KUnit tests. This interface allows the caller to specify options like test timeouts. The script handles configuring, building and running the kernel and tests. The output parser (kunit_parser.py) simply strips out all the output from the kernel that is outputted as part of it's initialization sequence. This ensures that only the output from KUnit is displayed on the screen. A full version of the output is written to test.log, or can be seen by passing --raw_output to the wrapper script. Signed-off-by: Felix Guo Signed-off-by: Brendan Higgins --- tools/testing/kunit/kunit.py | 40 +++++++++++++++++++++++++++++ tools/testing/kunit/kunit_kernel.py | 3 +-- tools/testing/kunit/kunit_parser.py | 24 +++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100755 tools/testing/kunit/kunit.py create mode 100644 tools/testing/kunit/kunit_parser.py diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py new file mode 100755 index 0000000000000..1356be404996b --- /dev/null +++ b/tools/testing/kunit/kunit.py @@ -0,0 +1,40 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0 + +# A thin wrapper on top of the KUnit Kernel + +import argparse +import sys +import os + +import kunit_config +import kunit_kernel +import kunit_parser + +parser = argparse.ArgumentParser(description='Runs KUnit tests.') + +parser.add_argument('--raw_output', help='don\'t format output from kernel', + action='store_true') + +parser.add_argument('--timeout', help='maximum number of seconds to allow for ' + 'all tests to run. This does not include time taken to ' + 'build the tests.', type=int, default=300, + metavar='timeout') + +cli_args = parser.parse_args() +linux = kunit_kernel.LinuxSourceTree() + +success = linux.build_reconfig() +if not success: + quit() + +print('Building KUnit Kernel ...') +success = linux.build_um_kernel() +if not success: + quit() + +print('Starting KUnit Kernel ...') +if cli_args.raw_output: + kunit_parser.raw_output(linux.run_kernel(timeout=cli_args.timeout)) +else: + kunit_parser.parse_run_tests(linux.run_kernel(timeout=cli_args.timeout)) diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index 87abaede50513..c1259b174f7d4 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -113,8 +113,7 @@ class LinuxSourceTree(object): return False return True - def run_kernel(self, args=[]): - timeout = None + def run_kernel(self, args=[], timeout=None): args.extend(['mem=256M']) process = self._ops.linux_bin(args, timeout) with open('test.log', 'w') as f: diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py new file mode 100644 index 0000000000000..1dff3adb73bd3 --- /dev/null +++ b/tools/testing/kunit/kunit_parser.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0 + +import re + +kunit_start_re = re.compile('console .* enabled') +kunit_end_re = re.compile('List of all partitions:') + +def isolate_kunit_output(kernel_output): + started = False + for line in kernel_output: + if kunit_start_re.match(line): + started = True + elif kunit_end_re.match(line): + break + elif started: + yield line + +def raw_output(kernel_output): + for line in kernel_output: + print(line) + +def parse_run_tests(kernel_output): + for output in isolate_kunit_output(kernel_output): + print(output) From patchwork Tue Oct 16 23:51:19 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985066 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="Ajgofgn5"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="dSXKMpCb"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXJ911sgz9s9J for ; Wed, 17 Oct 2018 10:55:53 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=WSYccqCNtGrm+JoFU5VWGyamMDzlW/c8CKZDk46FSRg=; b=Ajgofgn5LuosVR cX5AwjPjWCr6AIxCIGt3CceNH88Op+8Ag+p2aCYQiMDgcw07If9AFF8hPu1MvydAWH7kZOJna9nto Zq1jpwGWtdRIpIx9S443+aznk/f4G5sH6fmPEyYz/pG7tZX49ou8JjLBkgNCDRECdtu1j7Hiw6QKD 1pzzTeAb9HA6rWZVtSIbQkB/sd/XreLN5XoPMhtDsGRdm38WJ6nhGJwehRGLpR8D2cFbb5MHqBdvY bi9VKsCloIBr7YzzOZEf8Q8XNeJ43WZzI38eUryZQT+okEo0PlAXt2atJIlV4oo06KLACc3nf621S QIigDKuwuMtOTETxtcPQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBh-0006AE-83; Tue, 16 Oct 2018 23:55:45 +0000 Received: from mail-qt1-x849.google.com ([2607:f8b0:4864:20::849]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZB9-0004dt-VK for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:40 +0000 Received: by mail-qt1-x849.google.com with SMTP id s56-v6so26849759qtk.2 for ; Tue, 16 Oct 2018 16:55: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=5dprwvK9IJgFRBjX/ISiarJvYuvbs0jFW7OlgQMJm8I=; b=dSXKMpCbuIlYnpI1hlRtmq6MA/oRv9qpsgQHnNlkOBfTHrSV/UczexmeG77U8nXJK/ +q3WDvcIk3oCW6Ets+isSXbSovrwsvr4DQziaIICQXL2UfbtctXjXuAEXeKaemTH4dlb BcwfvfOWGSiU6H1o4hII6x06Z3/I9HU283Nb2/3+hjKZL9zFNduWl8quYe31uWhV5nNc /+9Ek+niYhgUBfQnkPVfs0Tvd5tMfdjUBtWR1kbwo3t1yGLbhc8voco0Y5GRW5gYHEIT p7zMxAemxClKx8g09oBkMUObWQ5ROgmfwDJ6lZM6PaLysHdgtGATwG7nxSN81KwZqC1t 86zg== 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=5dprwvK9IJgFRBjX/ISiarJvYuvbs0jFW7OlgQMJm8I=; b=UNUWxvIwhrIX4W4wFhWU3HuJnkeNxqMbHar2MeaB9P0P0Y/6ZIJNk6ICWXrz5pQk5A r+jQ413u36cMva7UlWcldnYOwcsHT7GzW3Ctgm1iGHZxv5MzSwk4h437gS9cUE3ua3RU ozBCVWvNbX0rdj9mT8rB+xL6v4KWJ2qIxJMPrDi/XXBnl8qBHr4LLW2tn2KCFW/4Ztcu QBwJKfR2zx++DDK4UeySEkOnR7+7MuL2oyOU3Z1LaRpu3TW5zvYfzbBY6oyxwdt+XJyw RAc3SG+5aM4I89HICyFYZhKGjkHOsYSn3vQJY5T8Bx699xTcSSGbGup2A9iD4x1Tz2ln GMRQ== X-Gm-Message-State: ABuFfogxo3IQZ9geWIB+VeTOtxIlAQVQJW/Z6zgLx1UM8WWvvz0SwIS4 IplZJUyGUz1aB2r4WoL3vbLiA6R0Ufp0wcvnK86dSA== X-Google-Smtp-Source: ACcGV63ROah9d53juzCYX23nCcGofEXHDuv8K02/zEQJYw0jFEhdV289crgD1tPhONes154Y95YbNVfoGwQxteOf3JruBA== X-Received: by 2002:ac8:24d5:: with SMTP id t21-v6mr19984069qtt.14.1539734099648; Tue, 16 Oct 2018 16:54:59 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:19 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-31-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 30/31] kunit.py: improved output from python wrapper From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165512_078056_EEAFCDF8 X-CRM114-Status: UNSURE ( 8.82 ) X-CRM114-Notice: Please train this message. X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:849 listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, Felix Guo , joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org - added colors to displayed output - added timing and summary Signed-off-by: Felix Guo Signed-off-by: Brendan Higgins --- tools/testing/kunit/kunit.py | 20 ++++++- tools/testing/kunit/kunit_parser.py | 93 ++++++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 4 deletions(-) diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py index 1356be404996b..b36c7b7924567 100755 --- a/tools/testing/kunit/kunit.py +++ b/tools/testing/kunit/kunit.py @@ -6,6 +6,7 @@ import argparse import sys import os +import time import kunit_config import kunit_kernel @@ -24,17 +25,32 @@ parser.add_argument('--timeout', help='maximum number of seconds to allow for ' cli_args = parser.parse_args() linux = kunit_kernel.LinuxSourceTree() +config_start = time.time() success = linux.build_reconfig() +config_end = time.time() if not success: quit() -print('Building KUnit Kernel ...') +kunit_parser.print_with_timestamp('Building KUnit Kernel ...') + +build_start = time.time() success = linux.build_um_kernel() +build_end = time.time() if not success: quit() -print('Starting KUnit Kernel ...') +kunit_parser.print_with_timestamp('Starting KUnit Kernel ...') +test_start = time.time() + if cli_args.raw_output: kunit_parser.raw_output(linux.run_kernel(timeout=cli_args.timeout)) else: kunit_parser.parse_run_tests(linux.run_kernel(timeout=cli_args.timeout)) + +test_end = time.time() + +kunit_parser.print_with_timestamp(( + "Elapsed time: %.3fs total, %.3fs configuring, %.3fs " + + "building, %.3fs running.\n") % (test_end - config_start, + config_end - config_start, build_end - build_start, + test_end - test_start)) diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index 1dff3adb73bd3..d9051e407d5a7 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 import re +from datetime import datetime kunit_start_re = re.compile('console .* enabled') kunit_end_re = re.compile('List of all partitions:') @@ -19,6 +20,94 @@ def raw_output(kernel_output): for line in kernel_output: print(line) +DIVIDER = "=" * 30 + +RESET = '\033[0;0m' + +def red(text): + return '\033[1;31m' + text + RESET + +def yellow(text): + return '\033[1;33m' + text + RESET + +def green(text): + return '\033[1;32m' + text + RESET + +def print_with_timestamp(message): + print('[%s] %s' % (datetime.now().strftime('%H:%M:%S'), message)) + +def print_log(log): + for m in log: + print_with_timestamp(m) + def parse_run_tests(kernel_output): - for output in isolate_kunit_output(kernel_output): - print(output) + test_case_output = re.compile('^kunit .*?: (.*)$') + + test_module_success = re.compile('^kunit .*: all tests passed') + test_module_fail = re.compile('^kunit .*: one or more tests failed') + + test_case_success = re.compile('^kunit (.*): (.*) passed') + test_case_fail = re.compile('^kunit (.*): (.*) failed') + test_case_crash = re.compile('^kunit (.*): (.*) crashed') + + total_tests = set() + failed_tests = set() + crashed_tests = set() + + def get_test_name(match): + return match.group(1) + ":" + match.group(2) + + current_case_log = [] + def end_one_test(match, log): + log.clear() + total_tests.add(get_test_name(match)) + + print_with_timestamp(DIVIDER) + for line in isolate_kunit_output(kernel_output): + # Ignore module output: + if (test_module_success.match(line) or + test_module_fail.match(line)): + print_with_timestamp(DIVIDER) + continue + + match = re.match(test_case_success, line) + if match: + print_with_timestamp(green("[PASSED] ") + + get_test_name(match)) + end_one_test(match, current_case_log) + continue + + match = re.match(test_case_fail, line) + # Crashed tests will report as both failed and crashed. We only + # want to show and count it once. + if match and get_test_name(match) not in crashed_tests: + failed_tests.add(get_test_name(match)) + print_with_timestamp(red("[FAILED] " + + get_test_name(match))) + print_log(map(yellow, current_case_log)) + print_with_timestamp("") + end_one_test(match, current_case_log) + continue + + match = re.match(test_case_crash, line) + if match: + crashed_tests.add(get_test_name(match)) + print_with_timestamp(yellow("[CRASH] " + + get_test_name(match))) + print_log(current_case_log) + print_with_timestamp("") + end_one_test(match, current_case_log) + continue + + # Strip off the `kunit module-name:` prefix + match = re.match(test_case_output, line) + if match: + current_case_log.append(match.group(1)) + else: + current_case_log.append(line) + + fmt = green if (len(failed_tests) + len(crashed_tests) == 0) else red + print_with_timestamp( + fmt("Testing complete. %d tests run. %d failed. %d crashed." % + (len(total_tests), len(failed_tests), len(crashed_tests)))) + From patchwork Tue Oct 16 23:51:20 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brendan Higgins X-Patchwork-Id: 985065 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="sJ8B67iJ"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="IXegup74"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42ZXJ70FdWz9sB5 for ; Wed, 17 Oct 2018 10:55:51 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:From:Subject:References:Mime-Version :Message-Id:In-Reply-To:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=AN4tgaIEP+FVDqKIqLyKnX9zUn0YEz5x3kwX/0wMYNU=; b=sJ8B67iJHyQBnq lmh3c2A+imcGne7Z2QZDY+h7bx+fFYcvmyXVdD8jmXVQAfk+JebEEcHO2IgnExvvT5g466Nz2i//e hBmh8+O2+MW9UvG7gT5yeqq/lI6x83QuHlEbZOXvUhwKOVppPZOYYOTQD4tPFXkSVBNhl/XDH9non IRLkjgnOQOY54Hvw8TEzU0SUXeOWD6+K1co7kp5tmgj8DTbV2b3vNhaQYeINwXj2VsB7729CJEnae YsVjAb79Wv2aManvuzWK1yCDH+y71wLdXlEOZJxKWduN6FssiUVYSjAH2YHPi1DUsupSji+HMDD+7 YHQqvvq4yBE00wGY52Rw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZBf-00069X-05; Tue, 16 Oct 2018 23:55:43 +0000 Received: from mail-qk1-x74a.google.com ([2607:f8b0:4864:20::74a]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gCZB5-0004f5-4O for linux-um@lists.infradead.org; Tue, 16 Oct 2018 23:55:35 +0000 Received: by mail-qk1-x74a.google.com with SMTP id a102-v6so25939075qka.0 for ; Tue, 16 Oct 2018 16:55:02 -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=/YSc+DXfgHSn0UTc27tWo8tZWoS60tFcG57Z6QARVOU=; b=IXegup746F+BLsmZfImNKweB07my/EWdOCH/1Wjfs3qOq/zcf8e1r9QnXsIpsb9Wv6 zHFtH2/RxzSaI7uFdgeoA263N337cYscszf038ZukK/6q76pQn8pkPuT7inrucWhqLb4 uPwDKuA43p7EgX1TrtHesRa9n1OS6z7HEbkSItpEEkkDemYV92jxEnCXRhj+ISiytXcg LM8oKFXN4P5KeElTHfuzQm8yAvTr5CXuClJSrCz941ymoxiMSM++ZTo82fxmXJTmESMO P7mSWjMhBuJ4L0svYNLnxlinWC/BbFPM+ycZxUoGE6bLpAcmafMKAHJa+kPwhsgYz6F2 Tpvw== 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=/YSc+DXfgHSn0UTc27tWo8tZWoS60tFcG57Z6QARVOU=; b=rTaSlCgBU7uI3WcBFumtwgoTUgjrcKpMDW+3uYT79n+RwpTktp88XmWzRzFwQ2y8+e uLSKv9aqPJYGrlcYNYemyyN6lrvsMkYM0NZzsNRZhQf1pCLff3WH4oNR2sjcBid78qO2 cbLrMlJ7F51KzzDoohgK9FWpPvWMYPBTELcD0Q68j0ZDQR7MpFgu5taWmaDW2SjLmxiR QdkIJl/uUmY9l/5n+3YK+gvWDjRZbqA5RJlE3B621zOb5d+pXmIyGYSjKyXT1ntCA7FU OXaWKvkWQSPg/X5uq1pqG6u29MtBTTyB3TXT5je7qWWnFvXNSjFjr0wgVDDrizPA6Nm1 W4nw== X-Gm-Message-State: ABuFfoj5RqttWwixw3pwGNB7Y6JdcPTvUdQKq5LZjodIaZ+wIloxPp2X 7FTo1h03CYKuv3AzUbvT+C/tNsmNzf49KfsqOYkSeQ== X-Google-Smtp-Source: ACcGV61fMJ2XqYGxE2Gqd2wOEa7O3zdmwDvctW8XJpGQG7JMwP95EidIc+b0X7pqhvUYBVjt4z4h9wDHwKfgxXMrHSwQ6Q== X-Received: by 2002:ac8:362c:: with SMTP id m41-v6mr15972638qtb.21.1539734102061; Tue, 16 Oct 2018 16:55:02 -0700 (PDT) Date: Tue, 16 Oct 2018 16:51:20 -0700 In-Reply-To: <20181016235120.138227-1-brendanhiggins@google.com> Message-Id: <20181016235120.138227-32-brendanhiggins@google.com> Mime-Version: 1.0 References: <20181016235120.138227-1-brendanhiggins@google.com> X-Mailer: git-send-email 2.19.1.331.ge82ca0e54c-goog Subject: [RFC v1 31/31] MAINTAINERS: add entry for KUnit the unit testing framework From: Brendan Higgins To: gregkh@linuxfoundation.org, keescook@google.com, mcgrof@kernel.org, shuah@kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181016_165507_206198_1E0A56ED X-CRM114-Status: UNSURE ( 5.90 ) X-CRM114-Notice: Please train this message. X-Spam-Score: -7.6 (-------) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-7.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:74a listed in] [list.dnswl.org] -7.5 USER_IN_DEF_DKIM_WL From: address is in the default DKIM white-list -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.0 DKIMWL_WL_MED DKIMwl.org - Whitelisted Medium sender X-BeenThere: linux-um@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: brakmo@fb.com, richard@nod.at, mpe@ellerman.id.au, Tim.Bird@sony.com, linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, julia.lawall@lip6.fr, joel@jms.id.au, linux-kselftest@vger.kernel.org, khilman@baylibre.com, joe@perches.com, jdike@addtoit.com, Brendan Higgins , kunit-dev@googlegroups.com Sender: "linux-um" Errors-To: linux-um-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Added myself as maintainer of KUnit, the Linux kernel's unit testing framework. Signed-off-by: Brendan Higgins --- MAINTAINERS | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 544cac829cf44..9c3d34f0062ad 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7801,6 +7801,21 @@ 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: arch/um/include/asm/io-mock-shared.h +F: arch/um/include/asm/io-mock.h +F: arch/um/kernel/io-mock.c +F: drivers/base/platform-mock.c +F: include/linux/platform_device_mock.h +F: include/kunit/ +F: kunit/ +F: tools/testing/kunit/ + KERNEL USERMODE HELPER M: "Luis R. Rodriguez" L: linux-kernel@vger.kernel.org