Received: by 2002:a05:6a10:22f:0:0:0:0 with SMTP id 15csp3382146pxk; Mon, 28 Sep 2020 16:29:22 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwp0T2fv5RFWOQLTceFZzOXN1eksLQSCB5nj3KkWqBlYsUlKIcDxCGdrjMEugjVJkKcOxkL X-Received: by 2002:a17:906:2b48:: with SMTP id b8mr1142074ejg.125.1601335762284; Mon, 28 Sep 2020 16:29:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1601335762; cv=none; d=google.com; s=arc-20160816; b=rZ0D1j5cweX+riqR0bMhv6GkBRCtehlhbI6XHaoj0fcmBomhMNcTHvEbvbR2PqkXBx +WpbnagPG6TdOpjDp8uqX0BppkdFcLJ9tozqELV/Q9k4WTpIeuJZazthsmca0kMxUVQ7 7jAlr33UqXXlBPfR0k4JE1wmrW/hq4KEF0ICdqLXuYzrUOSxKJc5/LZUQziCUBFjwytw rUjyuUjZjcSPCj8IEkknoWJBVim9VL/qOg+scCZhxP4Ym9NY7V96hk6+EaGVsauYgJzQ MuUYwFXgJbw/P0Hkq8Pu1thuefhLCtOUQTRywnvfwUbwFcsUrPGikP1Km1CoQnlTBFHF PZGw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:dkim-signature; bh=c8c07NCI0HNyfXA8pNgzDuXTfAuvdY/rU2PMr/Kt1gE=; b=IJ4uDa7FqzvEG2LPs9Pla/tqR30IFh7LFGu5gph7quxmTQfIHz1j3sHZPoa3lFx17g 9TkzpjUAz8Tq99SXaLAwQC4M/NONsLeOntUBpToq51w8kWJmPstdplaGELjWAmBCbejM X3bgUf6Z5LxpbqI/QzlxnvogBWmdSAB7XPk+BDwXIjXCLxk6SSy/EC8MoIrmDT68dWtx oSqw9ywHkq5NpKdioIHkxt2LrTyGJDl0miG7gZCP6hY2yNXmG9xMeWMXecuFnk4TQBM8 XEgvF192uSBkZN3M76P8ZCWFEgw2SvDKrXk6i567jQJQQsvbqUJySFMAWJgCb69CYgmg DTPA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=Kd6WT9db; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id n15si1592957eje.58.2020.09.28.16.28.51; Mon, 28 Sep 2020 16:29:22 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=Kd6WT9db; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726992AbgI1XYh (ORCPT + 99 others); Mon, 28 Sep 2020 19:24:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38236 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726932AbgI1XYh (ORCPT ); Mon, 28 Sep 2020 19:24:37 -0400 Received: from mail-pf1-x443.google.com (mail-pf1-x443.google.com [IPv6:2607:f8b0:4864:20::443]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 397F3C0613D4 for ; Mon, 28 Sep 2020 16:24:37 -0700 (PDT) Received: by mail-pf1-x443.google.com with SMTP id k13so2692084pfg.1 for ; Mon, 28 Sep 2020 16:24:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=c8c07NCI0HNyfXA8pNgzDuXTfAuvdY/rU2PMr/Kt1gE=; b=Kd6WT9dbwHkkmu8kpx/n9Lm5oozOFJtfnMPVThHwHXXdkXXa+d6q82yImX08oVRfb0 h4Ihz7RvaRAuevB7IgvvAJubZaJ7Sfnj6jleqaFR2+VKhr9OOdOPNcBb6tllCi57ounz gu9ECoqp0Dgxz52ntjUnsoQwI6lK2cvmsowWnfTI1ZcqLnmrRh/bgxLV1XJWvaN24jFD EqQlD5o3cdL3m8hfyo8pLU1BtZyDhdZwHq1NOc9V4hh2MRt7zQHf++k9tgB4fEKhOApI Z6/m1EKa3uizyyVFkT4ibgf45o5d/aHx5svhDl3f/p9xDJNypXftBhltZ/ywIcGvIzid 8ZEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=c8c07NCI0HNyfXA8pNgzDuXTfAuvdY/rU2PMr/Kt1gE=; b=pWZvdQQMelDtkfA2U8uTXBX4UWsRGfphdDWIO0njvjIaVZgMoUh8+5M+1oTGwnyJNV r5Kwp+5COemxKCZDCjn+xNz1A51YY0aGPOmejvObMU5QPDBftQWtrgm8HXTYCwlZpJSc DXBn67zgDWqKIMsnRbdn625T8DjJlMTHrAylxQ5kZSKeCXZK7j0F/1VJAGxodDR55uCP I4wSrH74QRZNR8UInYnrXXV3E76hr6ZtQNcbFtpIkn/addP9PvRPaZVdZw/x1YXumJfO kG4hQeS+FNtutRgCg+VCNmsmcVyPZo4W8LWLiUBR4RB0pg96YWYfT67qfhkON2B4zIBV +xdg== X-Gm-Message-State: AOAM532gQl45vXYk3bmZwgBs9p6uHKRs+YcTATLdHP2Qoh9ZZGDIecMB wtJk7LD9L7M5UaqvJjaT8wDEfsuMQAcvT8c4ISy+KA== X-Received: by 2002:a63:fd03:: with SMTP id d3mr999729pgh.201.1601335476300; Mon, 28 Sep 2020 16:24:36 -0700 (PDT) MIME-Version: 1.0 References: <20200918183114.2571146-1-dlatypov@google.com> In-Reply-To: From: Brendan Higgins Date: Mon, 28 Sep 2020 16:24:25 -0700 Message-ID: Subject: Re: [RFC v1 00/12] kunit: introduce class mocking support. To: Daniel Latypov Cc: David Gow , Shuah Khan , KUnit Development , "open list:KERNEL SELFTEST FRAMEWORK" , Linux Kernel Mailing List , Kees Cook , Luis Chamberlain , Alan Maguire , Stephen Boyd Content-Type: text/plain; charset="UTF-8" Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Tue, Sep 22, 2020 at 5:24 PM Daniel Latypov wrote: > > On Fri, Sep 18, 2020 at 11:31 AM Daniel Latypov wrote: > > > > # Background > > KUnit currently lacks any first-class support for mocking. > > For an overview and discussion on the pros and cons, see > > https://martinfowler.com/articles/mocksArentStubs.html > > > > This patch set introduces the basic machinery needed for mocking: > > setting and validating expectations, setting default actions, etc. > > > > Using that basic infrastructure, we add macros for "class mocking", as > > it's probably the easiest type of mocking to start with. > > > > ## Class mocking > > > > By "class mocking", we're referring mocking out function pointers stored > > in structs like: > > struct sender { > > int (*send)(struct sender *sender, int data); > > }; > > Discussed this offline a bit with Brendan and David. > The requirement that the first argument is a `sender *` means this > doesn't work for a few common cases. > > E.g. ops structs don't usually have funcs that take `XXX_ops *` > `pci_ops` all take a `struct pci_bus *`, which at least contains a > `struct pci_ops*`. > > Why does this matter? > We need to stash a `struct mock` somewhere to store expectations. > For this version of class mocking (in patch 10), we've assumed we could have > > struct MOCK(sender) { > struct mock ctrl; > struct sender trgt; //&trgt assumed to be the first param > } > > Then we can always use `container_of(sender)` to get the MOCK(sender) > which holds `ctrl`. > But if the first parameter isn't a `struct sender*`, this trick > obviously doesn't work. > > So to support something like pci_ops, we'd need another approach to > stash `ctrl`. > > After thinking a bit more, we could perhaps decouple the first param > from the mocked struct. > Using pci_ops as the example: > > struct MOCK(pci_ops) { > struct mock ctrl; > struct pci_bus *self; // this is the first param. Using > python terminology here. > struct pci_ops trgt; // continue to store this, as this holds > the function pointers > } > > // Name of this func is currently based on the class we're mocking > static inline struct mock *from_pci_ops_to_mock( > const struct pci_bus *self) > { > return mock_get_ctrl(container_of(self, struct MOCK(pci_ops), self)); > } > > // then in tests, we'd need something like > struct pci_bus bus; > bus.ops = mock_get_trgt(mock_ops); > mock_ops.self = &bus; > > Do others have thoughts/opinions? In the above example, wouldn't we actually want to mock struct pci_bus, not struct pci_ops? I think pci_bus is what actually gets implemented. Consider the Rockchip PCIe host controller: Rockchip provides it's own pci_ops struct: https://elixir.bootlin.com/linux/latest/source/drivers/pci/controller/pcie-rockchip-host.c#L256 If you look at one of the implemented methods, say rockchip_pcie_rd_conf(), you will see: ... struct rockchip_pcie *rockchip = bus->sysdata; ... (This function signature is: int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val); ). Thus, conceptually struct pci_bus is what is being manipulated; it best fits the candidate for the *self pointer. In fact - if I am not mistaken - I think if you were to mock pci_bus and just pretend that the methods were on pci_bus, not pci_ops, I think it might just work. The bigger problem is that it seems pci_bus does not want the user to allocate it: in the case of rockchip_pcie, it is allocated by devm_pci_alloc_host_bridge(). Thus, embedding pci_bus inside of a MOCK(pci_bus) would probably not work, so you would need to have different tooling around that and would then need to provide a custom definition of from_pci_bus_to_mock() (generated by DECLARE_STRUCT_CLASS_MOCK_CONVERTER()). > After grepping around for examples, I'm struck by how the number of > outliers there are. > E.g. most take a `struct thing *` as the first param, but cfspi_ifc in > caif_spi.h opts to take that as the last parameter. That's not a problem, just use the DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX() variant to create your mock (as opposed to DECLARE_STRUCT_CLASS_MOCK()). For example let's say you have the following struct that you want to mock: struct example { ... int (*foo)(int bar, char baz, struct example *self); }; You could mock the function with: DECLARE_STRUCT_CLASS_MOCK_HANDLE_INDEX( METHOD(foo), CLASS(example), 2, /* The "handle" is in position 2. */ RETURNS(int), PARAMS(int, char, struct example *)); > And others take a different mix of structs for each function. > > But it feels like a decoupled self / struct-holding-func-pointers > approach should be generic enough, as far as I can tell. > The more exotic types will probably have to remain unsupported. I think the code is pretty much here to handle any situation in which one of the parameters input to the function can be used to look up a mock object; we just need to expose the from__to_mock() function to the user to override. The problem I see with what we have now is the assumption that the user gets to create the object that they want to mock. Your example has illustrated that that is not a safe assumption. But yes, sufficiently exoctic cases will have to be unsupported. BTW, sorry for not sharing the above in our offline discussion; since we were talking without concrete examples in front of us, the problem sounded worse than it looks here. [...]