mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-01 10:42:11 +00:00
linux_kselftest-kunit-6.8-rc1
This KUnit update for Linux 6.8-rc1 consists of: - a new feature that adds APIs for managing devices introducing a set of helper functions which allow devices (internally a struct kunit_device) to be created and managed by KUnit. These devices will be automatically unregistered on test exit. These helpers can either use a user-provided struct device_driver, or have one automatically created and managed by KUnit. In both cases, the device lives on a new kunit_bus. - changes to switch drm/tests to use kunit devices - several fixes and enhancements to attribute feature - changes to reorganize deferred action function introducing KUNIT_DEFINE_ACTION_WRAPPER - new feature adds ability to run tests after boot using debugfs - fixes and enhancements to string-stream-test: - parse ERR_PTR in string_stream_destroy() - unchecked dereference in bug fix in debugfs_print_results() - handling errors from alloc_string_stream() - NULL-dereference bug fix in kunit_init_suite() -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEPZKym/RZuOCGeA/kCwJExA0NQxwFAmWdiHIACgkQCwJExA0N QxxzCxAAmhn+rkKV4DfuGXxUAJbO109H7LSP1Y7FKMYCVp83msWKASziujb2IQR9 87jnmgeJMbmQaPcc9m//NHuFhZmJwQZwAdGZryoDiz7XK+1MwLxYeUj92HI7FPaD o5Jz6tlqFdehx5jCOymgwbvhI5kJMkQCTTtnEaiHCByfaA02UqmTtt3bXK5OeJkZ UG0HqdvI/6Xo01i+BnerRBZFcQV49GMhl4acw1k+dJnPLkqusL6txftRBoKtxuVd mXQHKS1SmNgiNA+nqs4d/8qERoMJWuwj6wV4pldVBXhgZwOHXbBxBf67i7hTakE/ TkEURCkOb5X0QrT6akJj6phJ2xqXsF7xwzBJh9G4jF2Pdwwo8GGuAXW+ol0TRrm8 ZEQ4eMBGIK07Lb9FeBMLO2bZ0Ox+oiN+YNGY/bs1d6Ibf4PnBUfy7IlmMjKL9h/V A/EpYdaq5q72IZZQ2pu1rYkBRPbnP7vHmjLXVYIq7Pq8bLA9/ycKO/0jnGHdo1oz rBK/6t7yB+ATi4KeKQpjpmUTX/vdEenUQI/QDn9ngXIEwYQfNrEUZitEvBXR1Kw+ T8iKDIPFkvb/yEZgjWgNpxETooDx3yBkeeC29gKMj4QoN38wEdfy0Xltp8eqq9cS 6lijRoipUypHRAuXeSJMW2dflLnFIt4mtC25hBNF+DmyNVT+IF4= =79+u -----END PGP SIGNATURE----- Merge tag 'linux_kselftest-kunit-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest Pull KUnit updates from Shuah Khan: - a new feature that adds APIs for managing devices introducing a set of helper functions which allow devices (internally a struct kunit_device) to be created and managed by KUnit. These devices will be automatically unregistered on test exit. These helpers can either use a user-provided struct device_driver, or have one automatically created and managed by KUnit. In both cases, the device lives on a new kunit_bus. - changes to switch drm/tests to use kunit devices - several fixes and enhancements to attribute feature - changes to reorganize deferred action function introducing KUNIT_DEFINE_ACTION_WRAPPER - new feature adds ability to run tests after boot using debugfs - fixes and enhancements to string-stream-test: - parse ERR_PTR in string_stream_destroy() - unchecked dereference in bug fix in debugfs_print_results() - handling errors from alloc_string_stream() - NULL-dereference bug fix in kunit_init_suite() * tag 'linux_kselftest-kunit-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: (27 commits) kunit: Fix some comments which were mistakenly kerneldoc kunit: Protect string comparisons against NULL kunit: Add example of kunit_activate_static_stub() with pointer-to-function kunit: Allow passing function pointer to kunit_activate_static_stub() kunit: Fix NULL-dereference in kunit_init_suite() if suite->log is NULL kunit: Reset test->priv after each param iteration kunit: Add example for using test->priv drm/tests: Switch to kunit devices ASoC: topology: Replace fake root_device with kunit_device in tests overflow: Replace fake root_device with kunit_device fortify: test: Use kunit_device kunit: Add APIs for managing devices Documentation: Add debugfs docs with run after boot kunit: add ability to run tests after boot using debugfs kunit: add is_init test attribute kunit: add example suite to test init suites kunit: add KUNIT_INIT_TABLE to init linker section kunit: move KUNIT_TABLE out of INIT_DATA kunit: tool: add test for parsing attributes kunit: tool: fix parsing of test attributes ...
This commit is contained in:
commit
41daf06ea1
@ -11,3 +11,12 @@ state on a per-test basis, register custom cleanup actions, and more.
|
||||
|
||||
.. kernel-doc:: include/kunit/resource.h
|
||||
:internal:
|
||||
|
||||
Managed Devices
|
||||
---------------
|
||||
|
||||
Functions for using KUnit-managed struct device and struct device_driver.
|
||||
Include ``kunit/device.h`` to use these.
|
||||
|
||||
.. kernel-doc:: include/kunit/device.h
|
||||
:internal:
|
||||
|
@ -49,9 +49,52 @@ loaded.
|
||||
|
||||
The results will appear in TAP format in ``dmesg``.
|
||||
|
||||
debugfs
|
||||
=======
|
||||
|
||||
KUnit can be accessed from userspace via the debugfs filesystem (See more
|
||||
information about debugfs at Documentation/filesystems/debugfs.rst).
|
||||
|
||||
If ``CONFIG_KUNIT_DEBUGFS`` is enabled, the KUnit debugfs filesystem is
|
||||
mounted at /sys/kernel/debug/kunit. You can use this filesystem to perform
|
||||
the following actions.
|
||||
|
||||
Retrieve Test Results
|
||||
=====================
|
||||
|
||||
You can use debugfs to retrieve KUnit test results. The test results are
|
||||
accessible from the debugfs filesystem in the following read-only file:
|
||||
|
||||
.. code-block :: bash
|
||||
|
||||
/sys/kernel/debug/kunit/<test_suite>/results
|
||||
|
||||
The test results are printed in a KTAP document. Note this document is separate
|
||||
to the kernel log and thus, may have different test suite numbering.
|
||||
|
||||
Run Tests After Kernel Has Booted
|
||||
=================================
|
||||
|
||||
You can use the debugfs filesystem to trigger built-in tests to run after
|
||||
boot. To run the test suite, you can use the following command to write to
|
||||
the ``/sys/kernel/debug/kunit/<test_suite>/run`` file:
|
||||
|
||||
.. code-block :: bash
|
||||
|
||||
echo "any string" > /sys/kernel/debugfs/kunit/<test_suite>/run
|
||||
|
||||
As a result, the test suite runs and the results are printed to the kernel
|
||||
log.
|
||||
|
||||
However, this feature is not available with KUnit suites that use init data,
|
||||
because init data may have been discarded after the kernel boots. KUnit
|
||||
suites that use init data should be defined using the
|
||||
kunit_test_init_section_suites() macro.
|
||||
|
||||
Also, you cannot use this feature to run tests concurrently. Instead a test
|
||||
will wait to run until other tests have completed or failed.
|
||||
|
||||
.. note ::
|
||||
|
||||
If ``CONFIG_KUNIT_DEBUGFS`` is enabled, KUnit test results will
|
||||
be accessible from the ``debugfs`` filesystem (if mounted).
|
||||
They will be in ``/sys/kernel/debug/kunit/<test_suite>/results``, in
|
||||
TAP format.
|
||||
For test authors, to use this feature, tests will need to correctly initialise
|
||||
and/or clean up any data, so the test runs correctly a second time.
|
||||
|
@ -428,3 +428,10 @@ This attribute indicates the name of the module associated with the test.
|
||||
|
||||
This attribute is automatically saved as a string and is printed for each suite.
|
||||
Tests can also be filtered using this attribute.
|
||||
|
||||
``is_init``
|
||||
|
||||
This attribute indicates whether the test uses init data or functions.
|
||||
|
||||
This attribute is automatically saved as a boolean and tests can also be
|
||||
filtered using this attribute.
|
||||
|
@ -651,12 +651,16 @@ For example:
|
||||
}
|
||||
|
||||
Note that, for functions like device_unregister which only accept a single
|
||||
pointer-sized argument, it's possible to directly cast that function to
|
||||
a ``kunit_action_t`` rather than writing a wrapper function, for example:
|
||||
pointer-sized argument, it's possible to automatically generate a wrapper
|
||||
with the ``KUNIT_DEFINE_ACTION_WRAPPER()`` macro, for example:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
kunit_add_action(test, (kunit_action_t *)&device_unregister, &dev);
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(device_unregister, device_unregister_wrapper, struct device *);
|
||||
kunit_add_action(test, &device_unregister_wrapper, &dev);
|
||||
|
||||
You should do this in preference to manually casting to the ``kunit_action_t`` type,
|
||||
as casting function pointers will break Control Flow Integrity (CFI).
|
||||
|
||||
``kunit_add_action`` can fail if, for example, the system is out of memory.
|
||||
You can use ``kunit_add_action_or_reset`` instead which runs the action
|
||||
@ -793,3 +797,53 @@ structures as shown below:
|
||||
KUnit is not enabled, or if no test is running in the current task, it will do
|
||||
nothing. This compiles down to either a no-op or a static key check, so will
|
||||
have a negligible performance impact when no test is running.
|
||||
|
||||
Managing Fake Devices and Drivers
|
||||
---------------------------------
|
||||
|
||||
When testing drivers or code which interacts with drivers, many functions will
|
||||
require a ``struct device`` or ``struct device_driver``. In many cases, setting
|
||||
up a real device is not required to test any given function, so a fake device
|
||||
can be used instead.
|
||||
|
||||
KUnit provides helper functions to create and manage these fake devices, which
|
||||
are internally of type ``struct kunit_device``, and are attached to a special
|
||||
``kunit_bus``. These devices support managed device resources (devres), as
|
||||
described in Documentation/driver-api/driver-model/devres.rst
|
||||
|
||||
To create a KUnit-managed ``struct device_driver``, use ``kunit_driver_create()``,
|
||||
which will create a driver with the given name, on the ``kunit_bus``. This driver
|
||||
will automatically be destroyed when the corresponding test finishes, but can also
|
||||
be manually destroyed with ``driver_unregister()``.
|
||||
|
||||
To create a fake device, use the ``kunit_device_register()``, which will create
|
||||
and register a device, using a new KUnit-managed driver created with ``kunit_driver_create()``.
|
||||
To provide a specific, non-KUnit-managed driver, use ``kunit_device_register_with_driver()``
|
||||
instead. Like with managed drivers, KUnit-managed fake devices are automatically
|
||||
cleaned up when the test finishes, but can be manually cleaned up early with
|
||||
``kunit_device_unregister()``.
|
||||
|
||||
The KUnit devices should be used in preference to ``root_device_register()``, and
|
||||
instead of ``platform_device_register()`` in cases where the device is not otherwise
|
||||
a platform device.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include <kunit/device.h>
|
||||
|
||||
static void test_my_device(struct kunit *test)
|
||||
{
|
||||
struct device *fake_device;
|
||||
const char *dev_managed_string;
|
||||
|
||||
// Create a fake device.
|
||||
fake_device = kunit_device_register(test, "my_device");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_device)
|
||||
|
||||
// Pass it to functions which need a device.
|
||||
dev_managed_string = devm_kstrdup(fake_device, "Hello, World!");
|
||||
|
||||
// Everything is cleaned up automatically when the test ends.
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
#include <drm/drm_kunit_helpers.h>
|
||||
#include <drm/drm_managed.h>
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/resource.h>
|
||||
|
||||
#include <linux/device.h>
|
||||
@ -15,40 +16,6 @@
|
||||
static const struct drm_mode_config_funcs drm_mode_config_funcs = {
|
||||
};
|
||||
|
||||
static int fake_probe(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver fake_platform_driver = {
|
||||
.probe = fake_probe,
|
||||
.driver = {
|
||||
.name = KUNIT_DEVICE_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static void kunit_action_platform_driver_unregister(void *ptr)
|
||||
{
|
||||
struct platform_driver *drv = ptr;
|
||||
|
||||
platform_driver_unregister(drv);
|
||||
|
||||
}
|
||||
|
||||
static void kunit_action_platform_device_put(void *ptr)
|
||||
{
|
||||
struct platform_device *pdev = ptr;
|
||||
|
||||
platform_device_put(pdev);
|
||||
}
|
||||
|
||||
static void kunit_action_platform_device_del(void *ptr)
|
||||
{
|
||||
struct platform_device *pdev = ptr;
|
||||
|
||||
platform_device_del(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test
|
||||
* @test: The test context object
|
||||
@ -66,34 +33,7 @@ static void kunit_action_platform_device_del(void *ptr)
|
||||
*/
|
||||
struct device *drm_kunit_helper_alloc_device(struct kunit *test)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&fake_platform_driver);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ret = kunit_add_action_or_reset(test,
|
||||
kunit_action_platform_driver_unregister,
|
||||
&fake_platform_driver);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
|
||||
|
||||
ret = kunit_add_action_or_reset(test,
|
||||
kunit_action_platform_device_put,
|
||||
pdev);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ret = platform_device_add(pdev);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ret = kunit_add_action_or_reset(test,
|
||||
kunit_action_platform_device_del,
|
||||
pdev);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
return &pdev->dev;
|
||||
return kunit_device_register(test, KUNIT_DEVICE_NAME);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device);
|
||||
|
||||
@ -106,19 +46,7 @@ EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device);
|
||||
*/
|
||||
void drm_kunit_helper_free_device(struct kunit *test, struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
kunit_release_action(test,
|
||||
kunit_action_platform_device_del,
|
||||
pdev);
|
||||
|
||||
kunit_release_action(test,
|
||||
kunit_action_platform_device_put,
|
||||
pdev);
|
||||
|
||||
kunit_release_action(test,
|
||||
kunit_action_platform_driver_unregister,
|
||||
&fake_platform_driver);
|
||||
kunit_device_unregister(test, dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);
|
||||
|
||||
|
@ -153,12 +153,9 @@ static int __build_mock(struct kunit *test, struct drm_device *drm,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kunit_action_drm_dev_unregister(void *ptr)
|
||||
{
|
||||
struct drm_device *drm = ptr;
|
||||
|
||||
drm_dev_unregister(drm);
|
||||
}
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(kunit_action_drm_dev_unregister,
|
||||
drm_dev_unregister,
|
||||
struct drm_device *);
|
||||
|
||||
static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5)
|
||||
{
|
||||
|
@ -370,7 +370,8 @@
|
||||
BRANCH_PROFILE() \
|
||||
TRACE_PRINTKS() \
|
||||
BPF_RAW_TP() \
|
||||
TRACEPOINT_STR()
|
||||
TRACEPOINT_STR() \
|
||||
KUNIT_TABLE()
|
||||
|
||||
/*
|
||||
* Data section helpers
|
||||
@ -700,7 +701,7 @@
|
||||
EARLYCON_TABLE() \
|
||||
LSM_TABLE() \
|
||||
EARLY_LSM_TABLE() \
|
||||
KUNIT_TABLE()
|
||||
KUNIT_INIT_TABLE()
|
||||
|
||||
#define INIT_TEXT \
|
||||
*(.init.text .init.text.*) \
|
||||
@ -926,6 +927,12 @@
|
||||
. = ALIGN(8); \
|
||||
BOUNDED_SECTION_POST_LABEL(.kunit_test_suites, __kunit_suites, _start, _end)
|
||||
|
||||
/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
|
||||
#define KUNIT_INIT_TABLE() \
|
||||
. = ALIGN(8); \
|
||||
BOUNDED_SECTION_POST_LABEL(.kunit_init_test_suites, \
|
||||
__kunit_init_suites, _start, _end)
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
#define INIT_RAM_FS \
|
||||
. = ALIGN(4); \
|
||||
|
80
include/kunit/device.h
Normal file
80
include/kunit/device.h
Normal file
@ -0,0 +1,80 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* KUnit basic device implementation
|
||||
*
|
||||
* Helpers for creating and managing fake devices for KUnit tests.
|
||||
*
|
||||
* Copyright (C) 2023, Google LLC.
|
||||
* Author: David Gow <davidgow@google.com>
|
||||
*/
|
||||
|
||||
#ifndef _KUNIT_DEVICE_H
|
||||
#define _KUNIT_DEVICE_H
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
|
||||
#include <kunit/test.h>
|
||||
|
||||
struct device;
|
||||
struct device_driver;
|
||||
|
||||
/**
|
||||
* kunit_driver_create() - Create a struct device_driver attached to the kunit_bus
|
||||
* @test: The test context object.
|
||||
* @name: The name to give the created driver.
|
||||
*
|
||||
* Creates a struct device_driver attached to the kunit_bus, with the name @name.
|
||||
* This driver will automatically be cleaned up on test exit.
|
||||
*
|
||||
* Return: a stub struct device_driver, managed by KUnit, with the name @name.
|
||||
*/
|
||||
struct device_driver *kunit_driver_create(struct kunit *test, const char *name);
|
||||
|
||||
/**
|
||||
* kunit_device_register() - Create a struct device for use in KUnit tests
|
||||
* @test: The test context object.
|
||||
* @name: The name to give the created device.
|
||||
*
|
||||
* Creates a struct kunit_device (which is a struct device) with the given name,
|
||||
* and a corresponding driver. The device and driver will be cleaned up on test
|
||||
* exit, or when kunit_device_unregister is called. See also
|
||||
* kunit_device_register_with_driver, if you wish to provide your own
|
||||
* struct device_driver.
|
||||
*
|
||||
* Return: a pointer to a struct device which will be cleaned up when the test
|
||||
* exits, or an error pointer if the device could not be allocated or registered.
|
||||
*/
|
||||
struct device *kunit_device_register(struct kunit *test, const char *name);
|
||||
|
||||
/**
|
||||
* kunit_device_register_with_driver() - Create a struct device for use in KUnit tests
|
||||
* @test: The test context object.
|
||||
* @name: The name to give the created device.
|
||||
* @drv: The struct device_driver to associate with the device.
|
||||
*
|
||||
* Creates a struct kunit_device (which is a struct device) with the given
|
||||
* name, and driver. The device will be cleaned up on test exit, or when
|
||||
* kunit_device_unregister is called. See also kunit_device_register, if you
|
||||
* wish KUnit to create and manage a driver for you.
|
||||
*
|
||||
* Return: a pointer to a struct device which will be cleaned up when the test
|
||||
* exits, or an error pointer if the device could not be allocated or registered.
|
||||
*/
|
||||
struct device *kunit_device_register_with_driver(struct kunit *test,
|
||||
const char *name,
|
||||
const struct device_driver *drv);
|
||||
|
||||
/**
|
||||
* kunit_device_unregister() - Unregister a KUnit-managed device
|
||||
* @test: The test context object which created the device
|
||||
* @dev: The device.
|
||||
*
|
||||
* Unregisters and destroys a struct device which was created with
|
||||
* kunit_device_register or kunit_device_register_with_driver. If KUnit created
|
||||
* a driver, cleans it up as well.
|
||||
*/
|
||||
void kunit_device_unregister(struct kunit *test, struct device *dev);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -390,6 +390,27 @@ void kunit_remove_resource(struct kunit *test, struct kunit_resource *res);
|
||||
/* A 'deferred action' function to be used with kunit_add_action. */
|
||||
typedef void (kunit_action_t)(void *);
|
||||
|
||||
/**
|
||||
* KUNIT_DEFINE_ACTION_WRAPPER() - Wrap a function for use as a deferred action.
|
||||
*
|
||||
* @wrapper: The name of the new wrapper function define.
|
||||
* @orig: The original function to wrap.
|
||||
* @arg_type: The type of the argument accepted by @orig.
|
||||
*
|
||||
* Defines a wrapper for a function which accepts a single, pointer-sized
|
||||
* argument. This wrapper can then be passed to kunit_add_action() and
|
||||
* similar. This should be used in preference to casting a function
|
||||
* directly to kunit_action_t, as casting function pointers will break
|
||||
* control flow integrity (CFI), leading to crashes.
|
||||
*/
|
||||
#define KUNIT_DEFINE_ACTION_WRAPPER(wrapper, orig, arg_type) \
|
||||
static void wrapper(void *in) \
|
||||
{ \
|
||||
arg_type arg = (arg_type)in; \
|
||||
orig(arg); \
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* kunit_add_action() - Call a function when the test ends.
|
||||
* @test: Test case to associate the action with.
|
||||
|
@ -93,7 +93,7 @@ void __kunit_activate_static_stub(struct kunit *test,
|
||||
* The redirection can be disabled again with kunit_deactivate_static_stub().
|
||||
*/
|
||||
#define kunit_activate_static_stub(test, real_fn_addr, replacement_addr) do { \
|
||||
typecheck_fn(typeof(&real_fn_addr), replacement_addr); \
|
||||
typecheck_fn(typeof(&replacement_addr), real_fn_addr); \
|
||||
__kunit_activate_static_stub(test, real_fn_addr, replacement_addr); \
|
||||
} while (0)
|
||||
|
||||
|
@ -253,6 +253,7 @@ struct kunit_suite {
|
||||
struct dentry *debugfs;
|
||||
struct string_stream *log;
|
||||
int suite_init_err;
|
||||
bool is_init;
|
||||
};
|
||||
|
||||
/* Stores an array of suites, end points one past the end */
|
||||
@ -337,6 +338,9 @@ void __kunit_test_suites_exit(struct kunit_suite **suites, int num_suites);
|
||||
void kunit_exec_run_tests(struct kunit_suite_set *suite_set, bool builtin);
|
||||
void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr);
|
||||
|
||||
struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
|
||||
struct kunit_suite_set suite_set);
|
||||
|
||||
#if IS_BUILTIN(CONFIG_KUNIT)
|
||||
int kunit_run_all_tests(void);
|
||||
#else
|
||||
@ -371,6 +375,11 @@ static inline int kunit_run_all_tests(void)
|
||||
|
||||
#define kunit_test_suite(suite) kunit_test_suites(&suite)
|
||||
|
||||
#define __kunit_init_test_suites(unique_array, ...) \
|
||||
static struct kunit_suite *unique_array[] \
|
||||
__aligned(sizeof(struct kunit_suite *)) \
|
||||
__used __section(".kunit_init_test_suites") = { __VA_ARGS__ }
|
||||
|
||||
/**
|
||||
* kunit_test_init_section_suites() - used to register one or more &struct
|
||||
* kunit_suite containing init functions or
|
||||
@ -378,21 +387,21 @@ static inline int kunit_run_all_tests(void)
|
||||
*
|
||||
* @__suites: a statically allocated list of &struct kunit_suite.
|
||||
*
|
||||
* This functions identically as kunit_test_suites() except that it suppresses
|
||||
* modpost warnings for referencing functions marked __init or data marked
|
||||
* __initdata; this is OK because currently KUnit only runs tests upon boot
|
||||
* during the init phase or upon loading a module during the init phase.
|
||||
* This functions similar to kunit_test_suites() except that it compiles the
|
||||
* list of suites during init phase.
|
||||
*
|
||||
* NOTE TO KUNIT DEVS: If we ever allow KUnit tests to be run after boot, these
|
||||
* tests must be excluded.
|
||||
* This macro also suffixes the array and suite declarations it makes with
|
||||
* _probe; so that modpost suppresses warnings about referencing init data
|
||||
* for symbols named in this manner.
|
||||
*
|
||||
* The only thing this macro does that's different from kunit_test_suites is
|
||||
* that it suffixes the array and suite declarations it makes with _probe;
|
||||
* modpost suppresses warnings about referencing init data for symbols named in
|
||||
* this manner.
|
||||
* Note: these init tests are not able to be run after boot so there is no
|
||||
* "run" debugfs file generated for these tests.
|
||||
*
|
||||
* Also, do not mark the suite or test case structs with __initdata because
|
||||
* they will be used after the init phase with debugfs.
|
||||
*/
|
||||
#define kunit_test_init_section_suites(__suites...) \
|
||||
__kunit_test_suites(CONCATENATE(__UNIQUE_ID(array), _probe), \
|
||||
__kunit_init_test_suites(CONCATENATE(__UNIQUE_ID(array), _probe), \
|
||||
##__suites)
|
||||
|
||||
#define kunit_test_init_section_suite(suite) \
|
||||
@ -749,7 +758,7 @@ do { \
|
||||
.right_text = #right, \
|
||||
}; \
|
||||
\
|
||||
if (likely(strcmp(__left, __right) op 0)) \
|
||||
if (likely((__left) && (__right) && (strcmp(__left, __right) op 0))) \
|
||||
break; \
|
||||
\
|
||||
\
|
||||
|
@ -540,6 +540,8 @@ struct module {
|
||||
struct static_call_site *static_call_sites;
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
int num_kunit_init_suites;
|
||||
struct kunit_suite **kunit_init_suites;
|
||||
int num_kunit_suites;
|
||||
struct kunit_suite **kunit_suites;
|
||||
#endif
|
||||
|
@ -2199,6 +2199,9 @@ static int find_module_sections(struct module *mod, struct load_info *info)
|
||||
mod->kunit_suites = section_objs(info, ".kunit_test_suites",
|
||||
sizeof(*mod->kunit_suites),
|
||||
&mod->num_kunit_suites);
|
||||
mod->kunit_init_suites = section_objs(info, ".kunit_init_test_suites",
|
||||
sizeof(*mod->kunit_init_suites),
|
||||
&mod->num_kunit_init_suites);
|
||||
#endif
|
||||
|
||||
mod->extable = section_objs(info, "__ex_table",
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
@ -269,7 +270,7 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc)
|
||||
size_t len; \
|
||||
\
|
||||
/* Create dummy device for devm_kmalloc()-family tests. */ \
|
||||
dev = root_device_register(dev_name); \
|
||||
dev = kunit_device_register(test, dev_name); \
|
||||
KUNIT_ASSERT_FALSE_MSG(test, IS_ERR(dev), \
|
||||
"Cannot register test device\n"); \
|
||||
\
|
||||
@ -303,7 +304,7 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc)
|
||||
checker(len, devm_kmemdup(dev, "Ohai", len, gfp), \
|
||||
devm_kfree(dev, p)); \
|
||||
\
|
||||
device_unregister(dev); \
|
||||
kunit_device_unregister(test, dev); \
|
||||
} while (0)
|
||||
DEFINE_ALLOC_SIZE_TEST_PAIR(devm_kmalloc)
|
||||
|
||||
|
@ -7,7 +7,8 @@ kunit-objs += test.o \
|
||||
assert.o \
|
||||
try-catch.o \
|
||||
executor.o \
|
||||
attributes.o
|
||||
attributes.o \
|
||||
device.o
|
||||
|
||||
ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
|
||||
kunit-objs += debugfs.o
|
||||
|
@ -58,6 +58,16 @@ static const char *attr_enum_to_string(void *attr, const char * const str_list[]
|
||||
return str_list[val];
|
||||
}
|
||||
|
||||
static const char *attr_bool_to_string(void *attr, bool *to_free)
|
||||
{
|
||||
bool val = (bool)attr;
|
||||
|
||||
*to_free = false;
|
||||
if (val)
|
||||
return "true";
|
||||
return "false";
|
||||
}
|
||||
|
||||
static const char *attr_speed_to_string(void *attr, bool *to_free)
|
||||
{
|
||||
return attr_enum_to_string(attr, speed_str_list, to_free);
|
||||
@ -166,6 +176,37 @@ static int attr_string_filter(void *attr, const char *input, int *err)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int attr_bool_filter(void *attr, const char *input, int *err)
|
||||
{
|
||||
int i, input_int = -1;
|
||||
long val = (long)attr;
|
||||
const char *input_str = NULL;
|
||||
|
||||
for (i = 0; input[i]; i++) {
|
||||
if (!strchr(op_list, input[i])) {
|
||||
input_str = input + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!input_str) {
|
||||
*err = -EINVAL;
|
||||
pr_err("kunit executor: filter value not found: %s\n", input);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strcmp(input_str, "true"))
|
||||
input_int = (int)true;
|
||||
else if (!strcmp(input_str, "false"))
|
||||
input_int = (int)false;
|
||||
else {
|
||||
*err = -EINVAL;
|
||||
pr_err("kunit executor: invalid filter input: %s\n", input);
|
||||
return false;
|
||||
}
|
||||
|
||||
return int_filter(val, input, input_int, err);
|
||||
}
|
||||
|
||||
/* Get Attribute Methods */
|
||||
|
||||
@ -194,6 +235,17 @@ static void *attr_module_get(void *test_or_suite, bool is_test)
|
||||
return (void *) "";
|
||||
}
|
||||
|
||||
static void *attr_is_init_get(void *test_or_suite, bool is_test)
|
||||
{
|
||||
struct kunit_suite *suite = is_test ? NULL : test_or_suite;
|
||||
struct kunit_case *test = is_test ? test_or_suite : NULL;
|
||||
|
||||
if (test)
|
||||
return ((void *) NULL);
|
||||
else
|
||||
return ((void *) suite->is_init);
|
||||
}
|
||||
|
||||
/* List of all Test Attributes */
|
||||
|
||||
static struct kunit_attr kunit_attr_list[] = {
|
||||
@ -212,6 +264,14 @@ static struct kunit_attr kunit_attr_list[] = {
|
||||
.filter = attr_string_filter,
|
||||
.attr_default = (void *)"",
|
||||
.print = PRINT_SUITE,
|
||||
},
|
||||
{
|
||||
.name = "is_init",
|
||||
.get_attr = attr_is_init_get,
|
||||
.to_string = attr_bool_to_string,
|
||||
.filter = attr_bool_filter,
|
||||
.attr_default = (void *)false,
|
||||
.print = PRINT_SUITE,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,12 +8,14 @@
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/test-bug.h>
|
||||
|
||||
#include "string-stream.h"
|
||||
#include "debugfs.h"
|
||||
|
||||
#define KUNIT_DEBUGFS_ROOT "kunit"
|
||||
#define KUNIT_DEBUGFS_RESULTS "results"
|
||||
#define KUNIT_DEBUGFS_RUN "run"
|
||||
|
||||
/*
|
||||
* Create a debugfs representation of test suites:
|
||||
@ -21,6 +23,8 @@
|
||||
* Path Semantics
|
||||
* /sys/kernel/debug/kunit/<testsuite>/results Show results of last run for
|
||||
* testsuite
|
||||
* /sys/kernel/debug/kunit/<testsuite>/run Write to this file to trigger
|
||||
* testsuite to run
|
||||
*
|
||||
*/
|
||||
|
||||
@ -60,12 +64,14 @@ static void debugfs_print_result(struct seq_file *seq, struct string_stream *log
|
||||
static int debugfs_print_results(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct kunit_suite *suite = (struct kunit_suite *)seq->private;
|
||||
enum kunit_status success = kunit_suite_has_succeeded(suite);
|
||||
enum kunit_status success;
|
||||
struct kunit_case *test_case;
|
||||
|
||||
if (!suite)
|
||||
return 0;
|
||||
|
||||
success = kunit_suite_has_succeeded(suite);
|
||||
|
||||
/* Print KTAP header so the debugfs log can be parsed as valid KTAP. */
|
||||
seq_puts(seq, "KTAP version 1\n");
|
||||
seq_puts(seq, "1..1\n");
|
||||
@ -99,6 +105,51 @@ static int debugfs_results_open(struct inode *inode, struct file *file)
|
||||
return single_open(file, debugfs_print_results, suite);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a usage message to the debugfs "run" file
|
||||
* (/sys/kernel/debug/kunit/<testsuite>/run) if opened.
|
||||
*/
|
||||
static int debugfs_print_run(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct kunit_suite *suite = (struct kunit_suite *)seq->private;
|
||||
|
||||
seq_puts(seq, "Write to this file to trigger the test suite to run.\n");
|
||||
seq_printf(seq, "usage: echo \"any string\" > /sys/kernel/debugfs/kunit/%s/run\n",
|
||||
suite->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The debugfs "run" file (/sys/kernel/debug/kunit/<testsuite>/run)
|
||||
* contains no information. Write to the file to trigger the test suite
|
||||
* to run.
|
||||
*/
|
||||
static int debugfs_run_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct kunit_suite *suite;
|
||||
|
||||
suite = (struct kunit_suite *)inode->i_private;
|
||||
|
||||
return single_open(file, debugfs_print_run, suite);
|
||||
}
|
||||
|
||||
/*
|
||||
* Trigger a test suite to run by writing to the suite's "run" debugfs
|
||||
* file found at: /sys/kernel/debug/kunit/<testsuite>/run
|
||||
*
|
||||
* Note: what is written to this file will not be saved.
|
||||
*/
|
||||
static ssize_t debugfs_run(struct file *file,
|
||||
const char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct inode *f_inode = file->f_inode;
|
||||
struct kunit_suite *suite = (struct kunit_suite *) f_inode->i_private;
|
||||
|
||||
__kunit_test_suites_init(&suite, 1);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations debugfs_results_fops = {
|
||||
.open = debugfs_results_open,
|
||||
.read = seq_read,
|
||||
@ -106,17 +157,43 @@ static const struct file_operations debugfs_results_fops = {
|
||||
.release = debugfs_release,
|
||||
};
|
||||
|
||||
static const struct file_operations debugfs_run_fops = {
|
||||
.open = debugfs_run_open,
|
||||
.read = seq_read,
|
||||
.write = debugfs_run,
|
||||
.llseek = seq_lseek,
|
||||
.release = debugfs_release,
|
||||
};
|
||||
|
||||
void kunit_debugfs_create_suite(struct kunit_suite *suite)
|
||||
{
|
||||
struct kunit_case *test_case;
|
||||
struct string_stream *stream;
|
||||
|
||||
/* Allocate logs before creating debugfs representation. */
|
||||
suite->log = alloc_string_stream(GFP_KERNEL);
|
||||
string_stream_set_append_newlines(suite->log, true);
|
||||
/* If suite log already allocated, do not create new debugfs files. */
|
||||
if (suite->log)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Allocate logs before creating debugfs representation.
|
||||
* The suite->log and test_case->log pointer are expected to be NULL
|
||||
* if there isn't a log, so only set it if the log stream was created
|
||||
* successfully.
|
||||
*/
|
||||
stream = alloc_string_stream(GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(stream))
|
||||
return;
|
||||
|
||||
string_stream_set_append_newlines(stream, true);
|
||||
suite->log = stream;
|
||||
|
||||
kunit_suite_for_each_test_case(suite, test_case) {
|
||||
test_case->log = alloc_string_stream(GFP_KERNEL);
|
||||
string_stream_set_append_newlines(test_case->log, true);
|
||||
stream = alloc_string_stream(GFP_KERNEL);
|
||||
if (IS_ERR_OR_NULL(stream))
|
||||
goto err;
|
||||
|
||||
string_stream_set_append_newlines(stream, true);
|
||||
test_case->log = stream;
|
||||
}
|
||||
|
||||
suite->debugfs = debugfs_create_dir(suite->name, debugfs_rootdir);
|
||||
@ -124,6 +201,19 @@ void kunit_debugfs_create_suite(struct kunit_suite *suite)
|
||||
debugfs_create_file(KUNIT_DEBUGFS_RESULTS, S_IFREG | 0444,
|
||||
suite->debugfs,
|
||||
suite, &debugfs_results_fops);
|
||||
|
||||
/* Do not create file to re-run test if test runs on init */
|
||||
if (!suite->is_init) {
|
||||
debugfs_create_file(KUNIT_DEBUGFS_RUN, S_IFREG | 0644,
|
||||
suite->debugfs,
|
||||
suite, &debugfs_run_fops);
|
||||
}
|
||||
return;
|
||||
|
||||
err:
|
||||
string_stream_destroy(suite->log);
|
||||
kunit_suite_for_each_test_case(suite, test_case)
|
||||
string_stream_destroy(test_case->log);
|
||||
}
|
||||
|
||||
void kunit_debugfs_destroy_suite(struct kunit_suite *suite)
|
||||
|
17
lib/kunit/device-impl.h
Normal file
17
lib/kunit/device-impl.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* KUnit internal header for device helpers
|
||||
*
|
||||
* Header for KUnit-internal driver / bus management.
|
||||
*
|
||||
* Copyright (C) 2023, Google LLC.
|
||||
* Author: David Gow <davidgow@google.com>
|
||||
*/
|
||||
|
||||
#ifndef _KUNIT_DEVICE_IMPL_H
|
||||
#define _KUNIT_DEVICE_IMPL_H
|
||||
|
||||
// For internal use only -- registers the kunit_bus.
|
||||
int kunit_bus_init(void);
|
||||
|
||||
#endif //_KUNIT_DEVICE_IMPL_H
|
181
lib/kunit/device.c
Normal file
181
lib/kunit/device.c
Normal file
@ -0,0 +1,181 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* KUnit-managed device implementation
|
||||
*
|
||||
* Implementation of struct kunit_device helpers for fake devices whose
|
||||
* lifecycle is managed by KUnit.
|
||||
*
|
||||
* Copyright (C) 2023, Google LLC.
|
||||
* Author: David Gow <davidgow@google.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/resource.h>
|
||||
|
||||
#include "device-impl.h"
|
||||
|
||||
/* Wrappers for use with kunit_add_action() */
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(device_unregister_wrapper, device_unregister, struct device *);
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(driver_unregister_wrapper, driver_unregister, struct device_driver *);
|
||||
|
||||
/* The root device for the KUnit bus, parent of all kunit_devices. */
|
||||
static struct device *kunit_bus_device;
|
||||
|
||||
/* A device owned by a KUnit test. */
|
||||
struct kunit_device {
|
||||
struct device dev;
|
||||
/* The KUnit test which owns this device. */
|
||||
struct kunit *owner;
|
||||
/* If the driver is managed by KUnit and unique to this device. */
|
||||
const struct device_driver *driver;
|
||||
};
|
||||
|
||||
#define to_kunit_device(d) container_of_const(d, struct kunit_device, dev)
|
||||
|
||||
static struct bus_type kunit_bus_type = {
|
||||
.name = "kunit",
|
||||
};
|
||||
|
||||
/* Register the 'kunit_bus' used for fake devices. */
|
||||
int kunit_bus_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
kunit_bus_device = root_device_register("kunit");
|
||||
if (!kunit_bus_device)
|
||||
return -ENOMEM;
|
||||
|
||||
error = bus_register(&kunit_bus_type);
|
||||
if (error)
|
||||
bus_unregister(&kunit_bus_type);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Release a 'fake' KUnit device. */
|
||||
static void kunit_device_release(struct device *d)
|
||||
{
|
||||
kfree(to_kunit_device(d));
|
||||
}
|
||||
|
||||
/*
|
||||
* Create and register a KUnit-managed struct device_driver on the kunit_bus.
|
||||
* Returns an error pointer on failure.
|
||||
*/
|
||||
struct device_driver *kunit_driver_create(struct kunit *test, const char *name)
|
||||
{
|
||||
struct device_driver *driver;
|
||||
int err = -ENOMEM;
|
||||
|
||||
driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL);
|
||||
|
||||
if (!driver)
|
||||
return ERR_PTR(err);
|
||||
|
||||
driver->name = name;
|
||||
driver->bus = &kunit_bus_type;
|
||||
driver->owner = THIS_MODULE;
|
||||
|
||||
err = driver_register(driver);
|
||||
if (err) {
|
||||
kunit_kfree(test, driver);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
kunit_add_action(test, driver_unregister_wrapper, driver);
|
||||
return driver;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_driver_create);
|
||||
|
||||
/* Helper which creates a kunit_device, attaches it to the kunit_bus*/
|
||||
static struct kunit_device *kunit_device_register_internal(struct kunit *test,
|
||||
const char *name,
|
||||
const struct device_driver *drv)
|
||||
{
|
||||
struct kunit_device *kunit_dev;
|
||||
int err = -ENOMEM;
|
||||
|
||||
kunit_dev = kzalloc(sizeof(*kunit_dev), GFP_KERNEL);
|
||||
if (!kunit_dev)
|
||||
return ERR_PTR(err);
|
||||
|
||||
kunit_dev->owner = test;
|
||||
|
||||
err = dev_set_name(&kunit_dev->dev, "%s.%s", test->name, name);
|
||||
if (err) {
|
||||
kfree(kunit_dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
kunit_dev->dev.release = kunit_device_release;
|
||||
kunit_dev->dev.bus = &kunit_bus_type;
|
||||
kunit_dev->dev.parent = kunit_bus_device;
|
||||
|
||||
err = device_register(&kunit_dev->dev);
|
||||
if (err) {
|
||||
put_device(&kunit_dev->dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
kunit_add_action(test, device_unregister_wrapper, &kunit_dev->dev);
|
||||
|
||||
return kunit_dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create and register a new KUnit-managed device, using the user-supplied device_driver.
|
||||
* On failure, returns an error pointer.
|
||||
*/
|
||||
struct device *kunit_device_register_with_driver(struct kunit *test,
|
||||
const char *name,
|
||||
const struct device_driver *drv)
|
||||
{
|
||||
struct kunit_device *kunit_dev = kunit_device_register_internal(test, name, drv);
|
||||
|
||||
if (IS_ERR_OR_NULL(kunit_dev))
|
||||
return ERR_CAST(kunit_dev);
|
||||
|
||||
return &kunit_dev->dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_device_register_with_driver);
|
||||
|
||||
/*
|
||||
* Create and register a new KUnit-managed device, including a matching device_driver.
|
||||
* On failure, returns an error pointer.
|
||||
*/
|
||||
struct device *kunit_device_register(struct kunit *test, const char *name)
|
||||
{
|
||||
struct device_driver *drv;
|
||||
struct kunit_device *dev;
|
||||
|
||||
drv = kunit_driver_create(test, name);
|
||||
if (IS_ERR(drv))
|
||||
return ERR_CAST(drv);
|
||||
|
||||
dev = kunit_device_register_internal(test, name, drv);
|
||||
if (IS_ERR(dev)) {
|
||||
kunit_release_action(test, driver_unregister_wrapper, (void *)drv);
|
||||
return ERR_CAST(dev);
|
||||
}
|
||||
|
||||
/* Request the driver be freed. */
|
||||
dev->driver = drv;
|
||||
|
||||
|
||||
return &dev->dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_device_register);
|
||||
|
||||
/* Unregisters a KUnit-managed device early (including the driver, if automatically created). */
|
||||
void kunit_device_unregister(struct kunit *test, struct device *dev)
|
||||
{
|
||||
const struct device_driver *driver = to_kunit_device(dev)->driver;
|
||||
|
||||
kunit_release_action(test, device_unregister_wrapper, dev);
|
||||
if (driver)
|
||||
kunit_release_action(test, driver_unregister_wrapper, (void *)driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_device_unregister);
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
extern struct kunit_suite * const __kunit_suites_start[];
|
||||
extern struct kunit_suite * const __kunit_suites_end[];
|
||||
extern struct kunit_suite * const __kunit_init_suites_start[];
|
||||
extern struct kunit_suite * const __kunit_init_suites_end[];
|
||||
|
||||
static char *action_param;
|
||||
|
||||
@ -292,6 +294,37 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
|
||||
}
|
||||
}
|
||||
|
||||
struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
|
||||
struct kunit_suite_set suite_set)
|
||||
{
|
||||
struct kunit_suite_set total_suite_set = {NULL, NULL};
|
||||
struct kunit_suite **total_suite_start = NULL;
|
||||
size_t init_num_suites, num_suites, suite_size;
|
||||
int i = 0;
|
||||
|
||||
init_num_suites = init_suite_set.end - init_suite_set.start;
|
||||
num_suites = suite_set.end - suite_set.start;
|
||||
suite_size = sizeof(suite_set.start);
|
||||
|
||||
/* Allocate memory for array of all kunit suites */
|
||||
total_suite_start = kmalloc_array(init_num_suites + num_suites, suite_size, GFP_KERNEL);
|
||||
if (!total_suite_start)
|
||||
return total_suite_set;
|
||||
|
||||
/* Append and mark init suites and then append all other kunit suites */
|
||||
memcpy(total_suite_start, init_suite_set.start, init_num_suites * suite_size);
|
||||
for (i = 0; i < init_num_suites; i++)
|
||||
total_suite_start[i]->is_init = true;
|
||||
|
||||
memcpy(total_suite_start + init_num_suites, suite_set.start, num_suites * suite_size);
|
||||
|
||||
/* Set kunit suite set start and end */
|
||||
total_suite_set.start = total_suite_start;
|
||||
total_suite_set.end = total_suite_start + (init_num_suites + num_suites);
|
||||
|
||||
return total_suite_set;
|
||||
}
|
||||
|
||||
#if IS_BUILTIN(CONFIG_KUNIT)
|
||||
|
||||
static char *kunit_shutdown;
|
||||
@ -313,21 +346,41 @@ static void kunit_handle_shutdown(void)
|
||||
|
||||
int kunit_run_all_tests(void)
|
||||
{
|
||||
struct kunit_suite_set suite_set = {
|
||||
struct kunit_suite_set suite_set = {NULL, NULL};
|
||||
struct kunit_suite_set filtered_suite_set = {NULL, NULL};
|
||||
struct kunit_suite_set init_suite_set = {
|
||||
__kunit_init_suites_start, __kunit_init_suites_end,
|
||||
};
|
||||
struct kunit_suite_set normal_suite_set = {
|
||||
__kunit_suites_start, __kunit_suites_end,
|
||||
};
|
||||
size_t init_num_suites = init_suite_set.end - init_suite_set.start;
|
||||
int err = 0;
|
||||
|
||||
if (init_num_suites > 0) {
|
||||
suite_set = kunit_merge_suite_sets(init_suite_set, normal_suite_set);
|
||||
if (!suite_set.start)
|
||||
goto out;
|
||||
} else
|
||||
suite_set = normal_suite_set;
|
||||
|
||||
if (!kunit_enabled()) {
|
||||
pr_info("kunit: disabled\n");
|
||||
goto out;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
if (filter_glob_param || filter_param) {
|
||||
suite_set = kunit_filter_suites(&suite_set, filter_glob_param,
|
||||
filtered_suite_set = kunit_filter_suites(&suite_set, filter_glob_param,
|
||||
filter_param, filter_action_param, &err);
|
||||
|
||||
/* Free original suite set before using filtered suite set */
|
||||
if (init_num_suites > 0)
|
||||
kfree(suite_set.start);
|
||||
suite_set = filtered_suite_set;
|
||||
|
||||
if (err) {
|
||||
pr_err("kunit executor: error filtering suites: %d\n", err);
|
||||
goto out;
|
||||
goto free_out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,9 +393,12 @@ int kunit_run_all_tests(void)
|
||||
else
|
||||
pr_err("kunit executor: unknown action '%s'\n", action_param);
|
||||
|
||||
if (filter_glob_param || filter_param) { /* a copy was made of each suite */
|
||||
free_out:
|
||||
if (filter_glob_param || filter_param)
|
||||
kunit_free_suite_set(suite_set);
|
||||
}
|
||||
else if (init_num_suites > 0)
|
||||
/* Don't use kunit_free_suite_set because suites aren't individually allocated */
|
||||
kfree(suite_set.start);
|
||||
|
||||
out:
|
||||
kunit_handle_shutdown();
|
||||
|
@ -168,6 +168,16 @@ static int subtract_one(int i)
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the function to be replaced is static within a module it is
|
||||
* useful to export a pointer to that function instead of having
|
||||
* to change the static function to a non-static exported function.
|
||||
*
|
||||
* This pointer simulates a module exporting a pointer to a static
|
||||
* function.
|
||||
*/
|
||||
static int (* const add_one_fn_ptr)(int i) = add_one;
|
||||
|
||||
/*
|
||||
* This test shows the use of static stubs.
|
||||
*/
|
||||
@ -187,6 +197,30 @@ static void example_static_stub_test(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, add_one(1), 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* This test shows the use of static stubs when the function being
|
||||
* replaced is provided as a pointer-to-function instead of the
|
||||
* actual function. This is useful for providing access to static
|
||||
* functions in a module by exporting a pointer to that function
|
||||
* instead of having to change the static function to a non-static
|
||||
* exported function.
|
||||
*/
|
||||
static void example_static_stub_using_fn_ptr_test(struct kunit *test)
|
||||
{
|
||||
/* By default, function is not stubbed. */
|
||||
KUNIT_EXPECT_EQ(test, add_one(1), 2);
|
||||
|
||||
/* Replace add_one() with subtract_one(). */
|
||||
kunit_activate_static_stub(test, add_one_fn_ptr, subtract_one);
|
||||
|
||||
/* add_one() is now replaced. */
|
||||
KUNIT_EXPECT_EQ(test, add_one(1), 0);
|
||||
|
||||
/* Return add_one() to normal. */
|
||||
kunit_deactivate_static_stub(test, add_one_fn_ptr);
|
||||
KUNIT_EXPECT_EQ(test, add_one(1), 2);
|
||||
}
|
||||
|
||||
static const struct example_param {
|
||||
int value;
|
||||
} example_params_array[] = {
|
||||
@ -221,6 +255,20 @@ static void example_params_test(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, param->value % param->value, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This test shows the use of test->priv.
|
||||
*/
|
||||
static void example_priv_test(struct kunit *test)
|
||||
{
|
||||
/* unless setup in suite->init(), test->priv is NULL */
|
||||
KUNIT_ASSERT_NULL(test, test->priv);
|
||||
|
||||
/* but can be used to pass arbitrary data to other functions */
|
||||
test->priv = kunit_kzalloc(test, 1, GFP_KERNEL);
|
||||
KUNIT_EXPECT_NOT_NULL(test, test->priv);
|
||||
KUNIT_ASSERT_PTR_EQ(test, test->priv, kunit_get_current_test()->priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* This test should always pass. Can be used to practice filtering attributes.
|
||||
*/
|
||||
@ -245,6 +293,8 @@ static struct kunit_case example_test_cases[] = {
|
||||
KUNIT_CASE(example_mark_skipped_test),
|
||||
KUNIT_CASE(example_all_expect_macros_test),
|
||||
KUNIT_CASE(example_static_stub_test),
|
||||
KUNIT_CASE(example_static_stub_using_fn_ptr_test),
|
||||
KUNIT_CASE(example_priv_test),
|
||||
KUNIT_CASE_PARAM(example_params_test, example_gen_params),
|
||||
KUNIT_CASE_SLOW(example_slow_test),
|
||||
{}
|
||||
@ -287,4 +337,41 @@ static struct kunit_suite example_test_suite = {
|
||||
*/
|
||||
kunit_test_suites(&example_test_suite);
|
||||
|
||||
static int __init init_add(int x, int y)
|
||||
{
|
||||
return (x + y);
|
||||
}
|
||||
|
||||
/*
|
||||
* This test should always pass. Can be used to test init suites.
|
||||
*/
|
||||
static void __init example_init_test(struct kunit *test)
|
||||
{
|
||||
KUNIT_EXPECT_EQ(test, init_add(1, 1), 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* The kunit_case struct cannot be marked as __initdata as this will be
|
||||
* used in debugfs to retrieve results after test has run
|
||||
*/
|
||||
static struct kunit_case __refdata example_init_test_cases[] = {
|
||||
KUNIT_CASE(example_init_test),
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* The kunit_suite struct cannot be marked as __initdata as this will be
|
||||
* used in debugfs to retrieve results after test has run
|
||||
*/
|
||||
static struct kunit_suite example_init_test_suite = {
|
||||
.name = "example_init",
|
||||
.test_cases = example_init_test_cases,
|
||||
};
|
||||
|
||||
/*
|
||||
* This registers the test suite and marks the suite as using init data
|
||||
* and/or functions.
|
||||
*/
|
||||
kunit_test_init_section_suites(&example_init_test_suite);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -5,9 +5,13 @@
|
||||
* Copyright (C) 2019, Google LLC.
|
||||
* Author: Brendan Higgins <brendanhiggins@google.com>
|
||||
*/
|
||||
#include "linux/gfp_types.h"
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/test-bug.h>
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <kunit/device.h>
|
||||
|
||||
#include "string-stream.h"
|
||||
#include "try-catch-impl.h"
|
||||
|
||||
@ -538,10 +542,7 @@ static struct kunit_suite kunit_resource_test_suite = {
|
||||
#if IS_BUILTIN(CONFIG_KUNIT_TEST)
|
||||
|
||||
/* This avoids a cast warning if kfree() is passed direct to kunit_add_action(). */
|
||||
static void kfree_wrapper(void *p)
|
||||
{
|
||||
kfree(p);
|
||||
}
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(kfree_wrapper, kfree, const void *);
|
||||
|
||||
static void kunit_log_test(struct kunit *test)
|
||||
{
|
||||
@ -690,6 +691,134 @@ static struct kunit_case kunit_current_test_cases[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static void test_dev_action(void *priv)
|
||||
{
|
||||
*(void **)priv = (void *)1;
|
||||
}
|
||||
|
||||
static void kunit_device_test(struct kunit *test)
|
||||
{
|
||||
struct device *test_device;
|
||||
long action_was_run = 0;
|
||||
|
||||
test_device = kunit_device_register(test, "my_device");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_device);
|
||||
|
||||
// Add an action to verify cleanup.
|
||||
devm_add_action(test_device, test_dev_action, &action_was_run);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, action_was_run, 0);
|
||||
|
||||
kunit_device_unregister(test, test_device);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, action_was_run, 1);
|
||||
}
|
||||
|
||||
static void kunit_device_cleanup_test(struct kunit *test)
|
||||
{
|
||||
struct device *test_device;
|
||||
long action_was_run = 0;
|
||||
|
||||
test_device = kunit_device_register(test, "my_device");
|
||||
KUNIT_ASSERT_NOT_NULL(test, test_device);
|
||||
|
||||
/* Add an action to verify cleanup. */
|
||||
devm_add_action(test_device, test_dev_action, &action_was_run);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, action_was_run, 0);
|
||||
|
||||
/* Force KUnit to run cleanup early. */
|
||||
kunit_cleanup(test);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, action_was_run, 1);
|
||||
}
|
||||
|
||||
struct driver_test_state {
|
||||
bool driver_device_probed;
|
||||
bool driver_device_removed;
|
||||
long action_was_run;
|
||||
};
|
||||
|
||||
static int driver_probe_hook(struct device *dev)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct driver_test_state *state = (struct driver_test_state *)test->priv;
|
||||
|
||||
state->driver_device_probed = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int driver_remove_hook(struct device *dev)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct driver_test_state *state = (struct driver_test_state *)test->priv;
|
||||
|
||||
state->driver_device_removed = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kunit_device_driver_test(struct kunit *test)
|
||||
{
|
||||
struct device_driver *test_driver;
|
||||
struct device *test_device;
|
||||
struct driver_test_state *test_state = kunit_kzalloc(test, sizeof(*test_state), GFP_KERNEL);
|
||||
|
||||
test->priv = test_state;
|
||||
test_driver = kunit_driver_create(test, "my_driver");
|
||||
|
||||
// This can fail with an error pointer.
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_driver);
|
||||
|
||||
test_driver->probe = driver_probe_hook;
|
||||
test_driver->remove = driver_remove_hook;
|
||||
|
||||
test_device = kunit_device_register_with_driver(test, "my_device", test_driver);
|
||||
|
||||
// This can fail with an error pointer.
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_device);
|
||||
|
||||
// Make sure the probe function was called.
|
||||
KUNIT_ASSERT_TRUE(test, test_state->driver_device_probed);
|
||||
|
||||
// Add an action to verify cleanup.
|
||||
devm_add_action(test_device, test_dev_action, &test_state->action_was_run);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, test_state->action_was_run, 0);
|
||||
|
||||
kunit_device_unregister(test, test_device);
|
||||
test_device = NULL;
|
||||
|
||||
// Make sure the remove hook was called.
|
||||
KUNIT_ASSERT_TRUE(test, test_state->driver_device_removed);
|
||||
|
||||
// We're going to test this again.
|
||||
test_state->driver_device_probed = false;
|
||||
|
||||
// The driver should not automatically be destroyed by
|
||||
// kunit_device_unregister, so we can re-use it.
|
||||
test_device = kunit_device_register_with_driver(test, "my_device", test_driver);
|
||||
|
||||
// This can fail with an error pointer.
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_device);
|
||||
|
||||
// Probe was called again.
|
||||
KUNIT_ASSERT_TRUE(test, test_state->driver_device_probed);
|
||||
|
||||
// Everything is automatically freed here.
|
||||
}
|
||||
|
||||
static struct kunit_case kunit_device_test_cases[] = {
|
||||
KUNIT_CASE(kunit_device_test),
|
||||
KUNIT_CASE(kunit_device_cleanup_test),
|
||||
KUNIT_CASE(kunit_device_driver_test),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite kunit_device_test_suite = {
|
||||
.name = "kunit_device",
|
||||
.test_cases = kunit_device_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite kunit_current_test_suite = {
|
||||
.name = "kunit_current",
|
||||
.test_cases = kunit_current_test_cases,
|
||||
@ -697,6 +826,6 @@ static struct kunit_suite kunit_current_test_suite = {
|
||||
|
||||
kunit_test_suites(&kunit_try_catch_test_suite, &kunit_resource_test_suite,
|
||||
&kunit_log_test_suite, &kunit_status_test_suite,
|
||||
&kunit_current_test_suite);
|
||||
&kunit_current_test_suite, &kunit_device_test_suite);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -72,7 +72,7 @@ static void string_stream_unmanaged_init_test(struct kunit *test)
|
||||
|
||||
KUNIT_EXPECT_EQ(test, stream->length, 0);
|
||||
KUNIT_EXPECT_TRUE(test, list_empty(&stream->fragments));
|
||||
KUNIT_EXPECT_EQ(test, stream->gfp, GFP_KERNEL);
|
||||
KUNIT_EXPECT_TRUE(test, (stream->gfp == GFP_KERNEL));
|
||||
KUNIT_EXPECT_FALSE(test, stream->append_newlines);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, string_stream_is_empty(stream));
|
||||
|
@ -173,7 +173,7 @@ void string_stream_destroy(struct string_stream *stream)
|
||||
{
|
||||
KUNIT_STATIC_STUB_REDIRECT(string_stream_destroy, stream);
|
||||
|
||||
if (!stream)
|
||||
if (IS_ERR_OR_NULL(stream))
|
||||
return;
|
||||
|
||||
string_stream_clear(stream);
|
||||
|
@ -13,15 +13,19 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/panic.h>
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "debugfs.h"
|
||||
#include "device-impl.h"
|
||||
#include "hooks-impl.h"
|
||||
#include "string-stream.h"
|
||||
#include "try-catch-impl.h"
|
||||
|
||||
static DEFINE_MUTEX(kunit_run_lock);
|
||||
|
||||
/*
|
||||
* Hook to fail the current test and print an error message to the log.
|
||||
*/
|
||||
@ -660,6 +664,7 @@ int kunit_run_tests(struct kunit_suite *suite)
|
||||
test.param_index++;
|
||||
test.status = KUNIT_SUCCESS;
|
||||
test.status_comment[0] = '\0';
|
||||
test.priv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -692,6 +697,9 @@ static void kunit_init_suite(struct kunit_suite *suite)
|
||||
kunit_debugfs_create_suite(suite);
|
||||
suite->status_comment[0] = '\0';
|
||||
suite->suite_init_err = 0;
|
||||
|
||||
if (suite->log)
|
||||
string_stream_clear(suite->log);
|
||||
}
|
||||
|
||||
bool kunit_enabled(void)
|
||||
@ -710,6 +718,11 @@ int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_
|
||||
|
||||
kunit_suite_counter = 1;
|
||||
|
||||
/* Use mutex lock to guard against running tests concurrently. */
|
||||
if (mutex_lock_interruptible(&kunit_run_lock)) {
|
||||
pr_err("kunit: test interrupted\n");
|
||||
return -EINTR;
|
||||
}
|
||||
static_branch_inc(&kunit_running);
|
||||
|
||||
for (i = 0; i < num_suites; i++) {
|
||||
@ -718,6 +731,7 @@ int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_
|
||||
}
|
||||
|
||||
static_branch_dec(&kunit_running);
|
||||
mutex_unlock(&kunit_run_lock);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__kunit_test_suites_init);
|
||||
@ -742,28 +756,40 @@ EXPORT_SYMBOL_GPL(__kunit_test_suites_exit);
|
||||
#ifdef CONFIG_MODULES
|
||||
static void kunit_module_init(struct module *mod)
|
||||
{
|
||||
struct kunit_suite_set suite_set = {
|
||||
struct kunit_suite_set suite_set, filtered_set;
|
||||
struct kunit_suite_set normal_suite_set = {
|
||||
mod->kunit_suites, mod->kunit_suites + mod->num_kunit_suites,
|
||||
};
|
||||
struct kunit_suite_set init_suite_set = {
|
||||
mod->kunit_init_suites, mod->kunit_init_suites + mod->num_kunit_init_suites,
|
||||
};
|
||||
const char *action = kunit_action();
|
||||
int err = 0;
|
||||
|
||||
suite_set = kunit_filter_suites(&suite_set,
|
||||
if (mod->num_kunit_init_suites > 0)
|
||||
suite_set = kunit_merge_suite_sets(init_suite_set, normal_suite_set);
|
||||
else
|
||||
suite_set = normal_suite_set;
|
||||
|
||||
filtered_set = kunit_filter_suites(&suite_set,
|
||||
kunit_filter_glob() ?: "*.*",
|
||||
kunit_filter(), kunit_filter_action(),
|
||||
&err);
|
||||
if (err)
|
||||
pr_err("kunit module: error filtering suites: %d\n", err);
|
||||
|
||||
mod->kunit_suites = (struct kunit_suite **)suite_set.start;
|
||||
mod->num_kunit_suites = suite_set.end - suite_set.start;
|
||||
mod->kunit_suites = (struct kunit_suite **)filtered_set.start;
|
||||
mod->num_kunit_suites = filtered_set.end - filtered_set.start;
|
||||
|
||||
if (mod->num_kunit_init_suites > 0)
|
||||
kfree(suite_set.start);
|
||||
|
||||
if (!action)
|
||||
kunit_exec_run_tests(&suite_set, false);
|
||||
kunit_exec_run_tests(&filtered_set, false);
|
||||
else if (!strcmp(action, "list"))
|
||||
kunit_exec_list_tests(&suite_set, false);
|
||||
kunit_exec_list_tests(&filtered_set, false);
|
||||
else if (!strcmp(action, "list_attr"))
|
||||
kunit_exec_list_tests(&suite_set, true);
|
||||
kunit_exec_list_tests(&filtered_set, true);
|
||||
else
|
||||
pr_err("kunit: unknown action '%s'\n", action);
|
||||
}
|
||||
@ -810,6 +836,8 @@ static struct notifier_block kunit_mod_nb = {
|
||||
};
|
||||
#endif
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(kfree_action_wrapper, kfree, const void *)
|
||||
|
||||
void *kunit_kmalloc_array(struct kunit *test, size_t n, size_t size, gfp_t gfp)
|
||||
{
|
||||
void *data;
|
||||
@ -819,7 +847,7 @@ void *kunit_kmalloc_array(struct kunit *test, size_t n, size_t size, gfp_t gfp)
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
if (kunit_add_action_or_reset(test, (kunit_action_t *)kfree, data) != 0)
|
||||
if (kunit_add_action_or_reset(test, kfree_action_wrapper, data) != 0)
|
||||
return NULL;
|
||||
|
||||
return data;
|
||||
@ -831,7 +859,7 @@ void kunit_kfree(struct kunit *test, const void *ptr)
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
kunit_release_action(test, (kunit_action_t *)kfree, (void *)ptr);
|
||||
kunit_release_action(test, kfree_action_wrapper, (void *)ptr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_kfree);
|
||||
|
||||
@ -876,6 +904,8 @@ static int __init kunit_init(void)
|
||||
kunit_install_hooks();
|
||||
|
||||
kunit_debugfs_init();
|
||||
|
||||
kunit_bus_init();
|
||||
#ifdef CONFIG_MODULES
|
||||
return register_module_notifier(&kunit_mod_nb);
|
||||
#else
|
||||
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -618,7 +619,7 @@ static void overflow_allocation_test(struct kunit *test)
|
||||
} while (0)
|
||||
|
||||
/* Create dummy device for devm_kmalloc()-family tests. */
|
||||
dev = root_device_register(device_name);
|
||||
dev = kunit_device_register(test, device_name);
|
||||
KUNIT_ASSERT_FALSE_MSG(test, IS_ERR(dev),
|
||||
"Cannot register test device\n");
|
||||
|
||||
@ -634,8 +635,6 @@ static void overflow_allocation_test(struct kunit *test)
|
||||
check_allocation_overflow(devm_kmalloc);
|
||||
check_allocation_overflow(devm_kzalloc);
|
||||
|
||||
device_unregister(dev);
|
||||
|
||||
kunit_info(test, "%d allocation overflow tests finished\n", count);
|
||||
#undef check_allocation_overflow
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <sound/core.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-topology.h>
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/test.h>
|
||||
|
||||
/* ===== HELPER FUNCTIONS =================================================== */
|
||||
@ -21,26 +22,19 @@
|
||||
*/
|
||||
static struct device *test_dev;
|
||||
|
||||
static struct device_driver test_drv = {
|
||||
.name = "sound-soc-topology-test-driver",
|
||||
};
|
||||
|
||||
static int snd_soc_tplg_test_init(struct kunit *test)
|
||||
{
|
||||
test_dev = root_device_register("sound-soc-topology-test");
|
||||
test_dev = kunit_device_register(test, "sound-soc-topology-test");
|
||||
test_dev = get_device(test_dev);
|
||||
if (!test_dev)
|
||||
return -ENODEV;
|
||||
|
||||
test_dev->driver = &test_drv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_soc_tplg_test_exit(struct kunit *test)
|
||||
{
|
||||
put_device(test_dev);
|
||||
root_device_unregister(test_dev);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -450,7 +450,7 @@ def parse_diagnostic(lines: LineStream) -> List[str]:
|
||||
Log of diagnostic lines
|
||||
"""
|
||||
log = [] # type: List[str]
|
||||
non_diagnostic_lines = [TEST_RESULT, TEST_HEADER, KTAP_START, TAP_START]
|
||||
non_diagnostic_lines = [TEST_RESULT, TEST_HEADER, KTAP_START, TAP_START, TEST_PLAN]
|
||||
while lines and not any(re.match(lines.peek())
|
||||
for re in non_diagnostic_lines):
|
||||
log.append(lines.pop())
|
||||
@ -726,6 +726,7 @@ def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest:
|
||||
# test plan
|
||||
test.name = "main"
|
||||
ktap_line = parse_ktap_header(lines, test)
|
||||
test.log.extend(parse_diagnostic(lines))
|
||||
parse_test_plan(lines, test)
|
||||
parent_test = True
|
||||
else:
|
||||
@ -737,6 +738,7 @@ def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest:
|
||||
if parent_test:
|
||||
# If KTAP version line and/or subtest header is found, attempt
|
||||
# to parse test plan and print test header
|
||||
test.log.extend(parse_diagnostic(lines))
|
||||
parse_test_plan(lines, test)
|
||||
print_test_header(test)
|
||||
expected_count = test.expected_count
|
||||
|
@ -331,6 +331,22 @@ class KUnitParserTest(unittest.TestCase):
|
||||
kunit_parser.parse_run_tests(file.readlines())
|
||||
self.print_mock.assert_any_call(StrContains('suite (1 subtest)'))
|
||||
|
||||
def test_parse_attributes(self):
|
||||
ktap_log = test_data_path('test_parse_attributes.log')
|
||||
with open(ktap_log) as file:
|
||||
result = kunit_parser.parse_run_tests(file.readlines())
|
||||
|
||||
# Test should pass with no errors
|
||||
self.assertEqual(result.counts, kunit_parser.TestCounts(passed=1, errors=0))
|
||||
self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.status)
|
||||
|
||||
# Ensure suite header is parsed correctly
|
||||
self.print_mock.assert_any_call(StrContains('suite (1 subtest)'))
|
||||
|
||||
# Ensure attributes in correct test log
|
||||
self.assertContains('# module: example', result.subtests[0].log)
|
||||
self.assertContains('# test.speed: slow', result.subtests[0].subtests[0].log)
|
||||
|
||||
def test_show_test_output_on_failure(self):
|
||||
output = """
|
||||
KTAP version 1
|
||||
|
9
tools/testing/kunit/test_data/test_parse_attributes.log
Normal file
9
tools/testing/kunit/test_data/test_parse_attributes.log
Normal file
@ -0,0 +1,9 @@
|
||||
KTAP version 1
|
||||
1..1
|
||||
KTAP version 1
|
||||
# Subtest: suite
|
||||
# module: example
|
||||
1..1
|
||||
# test.speed: slow
|
||||
ok 1 test
|
||||
ok 1 suite
|
Loading…
Reference in New Issue
Block a user