Skip to content

Commit 5ac7973

Browse files
committed
platform: Add test managed platform_device/driver APIs
Introduce KUnit resource wrappers around platform_driver_register(), platform_device_alloc(), and platform_device_add() so that test authors can register platform drivers/devices from their tests and have the drivers/devices automatically be unregistered when the test is done. This makes test setup code simpler when a platform driver or platform device is needed. Add a few test cases at the same time to make sure the APIs work as intended. Cc: Brendan Higgins <[email protected]> Reviewed-by: David Gow <[email protected]> Cc: Rae Moar <[email protected]> Reviewed-by: Greg Kroah-Hartman <[email protected]> Cc: "Rafael J. Wysocki" <[email protected]> Signed-off-by: Stephen Boyd <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 5c9dd72 commit 5ac7973

File tree

7 files changed

+565
-1
lines changed

7 files changed

+565
-1
lines changed

Documentation/dev-tools/kunit/api/index.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ API Reference
1010
resource
1111
functionredirection
1212
of
13+
platformdevice
1314

1415

1516
This page documents the KUnit kernel testing API. It is divided into the
@@ -36,3 +37,7 @@ Driver KUnit API
3637
Documentation/dev-tools/kunit/api/of.rst
3738

3839
- Documents the KUnit device tree (OF) API
40+
41+
Documentation/dev-tools/kunit/api/platformdevice.rst
42+
43+
- Documents the KUnit platform device API
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
===================
4+
Platform Device API
5+
===================
6+
7+
The KUnit platform device API is used to test platform devices.
8+
9+
.. kernel-doc:: lib/kunit/platform.c
10+
:export:

drivers/base/dd.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ bool device_is_bound(struct device *dev)
393393
{
394394
return dev->p && klist_node_attached(&dev->p->knode_driver);
395395
}
396+
EXPORT_SYMBOL_GPL(device_is_bound);
396397

397398
static void driver_bound(struct device *dev)
398399
{

include/kunit/platform_device.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _KUNIT_PLATFORM_DRIVER_H
3+
#define _KUNIT_PLATFORM_DRIVER_H
4+
5+
struct kunit;
6+
struct platform_device;
7+
struct platform_driver;
8+
9+
struct platform_device *
10+
kunit_platform_device_alloc(struct kunit *test, const char *name, int id);
11+
int kunit_platform_device_add(struct kunit *test, struct platform_device *pdev);
12+
13+
int kunit_platform_device_prepare_wait_for_probe(struct kunit *test,
14+
struct platform_device *pdev,
15+
struct completion *x);
16+
17+
int kunit_platform_driver_register(struct kunit *test,
18+
struct platform_driver *drv);
19+
20+
#endif

lib/kunit/Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ kunit-objs += test.o \
99
try-catch.o \
1010
executor.o \
1111
attributes.o \
12-
device.o
12+
device.o \
13+
platform.o
1314

1415
ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
1516
kunit-objs += debugfs.o
@@ -19,6 +20,7 @@ endif
1920
obj-y += hooks.o
2021

2122
obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
23+
obj-$(CONFIG_KUNIT_TEST) += platform-test.o
2224

2325
# string-stream-test compiles built-in only.
2426
ifeq ($(CONFIG_KUNIT_TEST),y)

lib/kunit/platform-test.c

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* KUnit test for KUnit platform driver infrastructure.
4+
*/
5+
6+
#include <linux/platform_device.h>
7+
8+
#include <kunit/platform_device.h>
9+
#include <kunit/test.h>
10+
11+
/*
12+
* Test that kunit_platform_device_alloc() creates a platform device.
13+
*/
14+
static void kunit_platform_device_alloc_test(struct kunit *test)
15+
{
16+
KUNIT_EXPECT_NOT_ERR_OR_NULL(test,
17+
kunit_platform_device_alloc(test, "kunit-platform", 1));
18+
}
19+
20+
/*
21+
* Test that kunit_platform_device_add() registers a platform device on the
22+
* platform bus with the proper name and id.
23+
*/
24+
static void kunit_platform_device_add_test(struct kunit *test)
25+
{
26+
struct platform_device *pdev;
27+
const char *name = "kunit-platform-add";
28+
const int id = -1;
29+
30+
pdev = kunit_platform_device_alloc(test, name, id);
31+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
32+
33+
KUNIT_EXPECT_EQ(test, 0, kunit_platform_device_add(test, pdev));
34+
KUNIT_EXPECT_TRUE(test, dev_is_platform(&pdev->dev));
35+
KUNIT_EXPECT_STREQ(test, pdev->name, name);
36+
KUNIT_EXPECT_EQ(test, pdev->id, id);
37+
}
38+
39+
/*
40+
* Test that kunit_platform_device_add() called twice with the same device name
41+
* and id fails the second time and properly cleans up.
42+
*/
43+
static void kunit_platform_device_add_twice_fails_test(struct kunit *test)
44+
{
45+
struct platform_device *pdev;
46+
const char *name = "kunit-platform-add-2";
47+
const int id = -1;
48+
49+
pdev = kunit_platform_device_alloc(test, name, id);
50+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
51+
KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, pdev));
52+
53+
pdev = kunit_platform_device_alloc(test, name, id);
54+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
55+
56+
KUNIT_EXPECT_NE(test, 0, kunit_platform_device_add(test, pdev));
57+
}
58+
59+
static int kunit_platform_device_find_by_name(struct device *dev, const void *data)
60+
{
61+
return strcmp(dev_name(dev), data) == 0;
62+
}
63+
64+
/*
65+
* Test that kunit_platform_device_add() cleans up by removing the platform
66+
* device when the test finishes. */
67+
static void kunit_platform_device_add_cleans_up(struct kunit *test)
68+
{
69+
struct platform_device *pdev;
70+
const char *name = "kunit-platform-clean";
71+
const int id = -1;
72+
struct kunit fake;
73+
struct device *dev;
74+
75+
kunit_init_test(&fake, "kunit_platform_device_add_fake_test", NULL);
76+
KUNIT_ASSERT_EQ(test, fake.status, KUNIT_SUCCESS);
77+
78+
pdev = kunit_platform_device_alloc(&fake, name, id);
79+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
80+
KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(&fake, pdev));
81+
dev = bus_find_device(&platform_bus_type, NULL, name,
82+
kunit_platform_device_find_by_name);
83+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
84+
put_device(dev);
85+
86+
/* Remove pdev */
87+
kunit_cleanup(&fake);
88+
89+
/*
90+
* Failing to migrate the kunit_resource would lead to an extra
91+
* put_device() call on the platform device. The best we can do here is
92+
* make sure the device no longer exists on the bus, but if something
93+
* is wrong we'll see a refcount underflow here. We can't test for a
94+
* refcount underflow because the kref matches the lifetime of the
95+
* device which should already be freed and could be used by something
96+
* else.
97+
*/
98+
dev = bus_find_device(&platform_bus_type, NULL, name,
99+
kunit_platform_device_find_by_name);
100+
KUNIT_EXPECT_PTR_EQ(test, NULL, dev);
101+
put_device(dev);
102+
}
103+
104+
/*
105+
* Test suite for struct platform_device kunit APIs
106+
*/
107+
static struct kunit_case kunit_platform_device_test_cases[] = {
108+
KUNIT_CASE(kunit_platform_device_alloc_test),
109+
KUNIT_CASE(kunit_platform_device_add_test),
110+
KUNIT_CASE(kunit_platform_device_add_twice_fails_test),
111+
KUNIT_CASE(kunit_platform_device_add_cleans_up),
112+
{}
113+
};
114+
115+
static struct kunit_suite kunit_platform_device_suite = {
116+
.name = "kunit_platform_device",
117+
.test_cases = kunit_platform_device_test_cases,
118+
};
119+
120+
struct kunit_platform_driver_test_context {
121+
struct platform_driver pdrv;
122+
const char *data;
123+
};
124+
125+
static const char * const test_data = "test data";
126+
127+
static inline struct kunit_platform_driver_test_context *
128+
to_test_context(struct platform_device *pdev)
129+
{
130+
return container_of(to_platform_driver(pdev->dev.driver),
131+
struct kunit_platform_driver_test_context,
132+
pdrv);
133+
}
134+
135+
static int kunit_platform_driver_probe(struct platform_device *pdev)
136+
{
137+
struct kunit_platform_driver_test_context *ctx;
138+
139+
ctx = to_test_context(pdev);
140+
ctx->data = test_data;
141+
142+
return 0;
143+
}
144+
145+
/* Test that kunit_platform_driver_register() registers a driver that probes. */
146+
static void kunit_platform_driver_register_test(struct kunit *test)
147+
{
148+
struct platform_device *pdev;
149+
struct kunit_platform_driver_test_context *ctx;
150+
DECLARE_COMPLETION_ONSTACK(comp);
151+
const char *name = "kunit-platform-register";
152+
153+
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
154+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
155+
156+
pdev = kunit_platform_device_alloc(test, name, -1);
157+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
158+
KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, pdev));
159+
160+
ctx->pdrv.probe = kunit_platform_driver_probe;
161+
ctx->pdrv.driver.name = name;
162+
ctx->pdrv.driver.owner = THIS_MODULE;
163+
164+
KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_prepare_wait_for_probe(test, pdev, &comp));
165+
166+
KUNIT_EXPECT_EQ(test, 0, kunit_platform_driver_register(test, &ctx->pdrv));
167+
KUNIT_EXPECT_NE(test, 0, wait_for_completion_timeout(&comp, 3 * HZ));
168+
KUNIT_EXPECT_STREQ(test, ctx->data, test_data);
169+
}
170+
171+
/*
172+
* Test that kunit_platform_device_prepare_wait_for_probe() completes the completion
173+
* when the device is already probed.
174+
*/
175+
static void kunit_platform_device_prepare_wait_for_probe_completes_when_already_probed(struct kunit *test)
176+
{
177+
struct platform_device *pdev;
178+
struct kunit_platform_driver_test_context *ctx;
179+
DECLARE_COMPLETION_ONSTACK(comp);
180+
const char *name = "kunit-platform-wait";
181+
182+
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
183+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
184+
185+
pdev = kunit_platform_device_alloc(test, name, -1);
186+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
187+
KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, pdev));
188+
189+
ctx->pdrv.probe = kunit_platform_driver_probe;
190+
ctx->pdrv.driver.name = name;
191+
ctx->pdrv.driver.owner = THIS_MODULE;
192+
193+
/* Make sure driver has actually probed */
194+
KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_prepare_wait_for_probe(test, pdev, &comp));
195+
KUNIT_ASSERT_EQ(test, 0, kunit_platform_driver_register(test, &ctx->pdrv));
196+
KUNIT_ASSERT_NE(test, 0, wait_for_completion_timeout(&comp, 3 * HZ));
197+
198+
reinit_completion(&comp);
199+
KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_prepare_wait_for_probe(test, pdev, &comp));
200+
201+
KUNIT_EXPECT_NE(test, 0, wait_for_completion_timeout(&comp, HZ));
202+
}
203+
204+
static struct kunit_case kunit_platform_driver_test_cases[] = {
205+
KUNIT_CASE(kunit_platform_driver_register_test),
206+
KUNIT_CASE(kunit_platform_device_prepare_wait_for_probe_completes_when_already_probed),
207+
{}
208+
};
209+
210+
/*
211+
* Test suite for struct platform_driver kunit APIs
212+
*/
213+
static struct kunit_suite kunit_platform_driver_suite = {
214+
.name = "kunit_platform_driver",
215+
.test_cases = kunit_platform_driver_test_cases,
216+
};
217+
218+
kunit_test_suites(
219+
&kunit_platform_device_suite,
220+
&kunit_platform_driver_suite,
221+
);
222+
223+
MODULE_LICENSE("GPL");
224+
MODULE_DESCRIPTION("KUnit test for KUnit platform driver infrastructure");

0 commit comments

Comments
 (0)