From nobody Fri Oct 3 18:10:16 2025 Received: from mail-pg1-f180.google.com (mail-pg1-f180.google.com [209.85.215.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3F8DE2877C0; Tue, 26 Aug 2025 22:51:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756248711; cv=none; b=EepP6e2JFijYGG/xxQ1YQPiIp8gBpewKiQ+2Ml/8XXh8I2tf7o62bVX/pwxon/ot+Q1GSvdd3/SCKQ9aVG9dIYbBBOrBaU84aeS1x1vn6DS+zMCZTvZ0yR1EW1+EQwfUNw0ZPKGI3ltqTYdzFBFoQPgjnyIOBGc5VJJqmBbn7qs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756248711; c=relaxed/simple; bh=nja9nOlsvSDuhTHFm8quefBlVFD24Sl3DW2Igdp8jGk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Z50fCbSD3UQlevLbDczqaCVo1CdoPH/e6JkxrpktGilPHTBRG1bvNCJGR0vkLl6mrhELrSvMMlmEDwhq+7t1JRqvRa+6/9CmGTMjrvFqtnUAE3Gm7MdOcP9L47/k+8x4x6jnHTAnkoHEuSSEV8S5Iit4tVKOUqW1kM8OQXvOzoQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Uhh5AfvZ; arc=none smtp.client-ip=209.85.215.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Uhh5AfvZ" Received: by mail-pg1-f180.google.com with SMTP id 41be03b00d2f7-b474d0f1d5eso4075973a12.2; Tue, 26 Aug 2025 15:51:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1756248709; x=1756853509; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=h76qScSG6gmL4kEuO66KYlIEZb8mLYMQm2nAEG+zUfw=; b=Uhh5AfvZTjIKejKOXi5XFRkKVou7MvfYkQfjZV1l/pxATd1cSrp8bD24aPbiR6nEGX 18+jNz/JU4SgKz1H2mys7rMI4zgSNAN9zeMKhyljP9oftdeCX4SZxyDzpdcLQvaq7lx7 popxKGwHXjmD67DWO1mjEPvCNe7CAfgoALM3OeCI2KnAIgFmx7XJfovoucI9Y7CGzDFR SCpTseoK2TJ9lJa6Ifg9Jf5Vn8IC+e2Ykyh6nv2dFNBx1ALuiXzi4NkljZLkDcIb17XB ZkPG7PeFt/bVu7592X9nNKn0UtSRk9409bJJWmogaEX3su5fEOz1UfprnfhcY2oHzyf1 9UKg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1756248709; x=1756853509; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=h76qScSG6gmL4kEuO66KYlIEZb8mLYMQm2nAEG+zUfw=; b=aoJDOOBjO8NyhRC0Q1Bx/YVDfewtot2651UQ/y0uOyvYAv7kmmcKIO25rrtYx16P9c RXmwlXA40QUhvowytefCvryZa3C1WIP6lzw5LGWy4wn3wCpRgG/yapVAD0t7CSIhyDm8 0Zs6bVK26KWNwiz2F6ZcKppR/Q4iQnJLydfaVZdVx9V72MXOk2nzvTU6xsG1tvM8aI9w 542v1OcZ359kcT5l6vGMpQXN7ETHUw6m6GQzit29YhEBqovzN7lKbAymWNiFRzQYSAx2 kS4amdv6WhSnAyydlkx4GDBS2t61T+WSy85X5ZVNfQWUMBnWU3FURT/EoNcA1UbtExWF zuYw== X-Forwarded-Encrypted: i=1; AJvYcCUfo8+u38ds3as4c4UQTSjvMotqeaSEVxb4CQbAi11IFegcJZtljy9kzD+8BLUEXuf1cWjHtl2C6PDVJijV@vger.kernel.org, AJvYcCVwLrjdjoUz+pOCWgzuNOPZxC3qdGxYfMxQq2jbyOVtCp9VLhFJ4q/68eylI97DTuFSNJTabC75p49P79RbUlE=@vger.kernel.org X-Gm-Message-State: AOJu0YwVxMgVUg+0qr+Nglttqm+a4LsfZIB7Rl9tn3kv8P3mm0CqGHji Tqm1EWMHEg4kp4AciNVR3D+6eEq3M3H/v3ESkmxUJhlaEkz90jUIJUTs X-Gm-Gg: ASbGncsfGNTKCOBLdCCEavM9y9zVG00glwS5XxtuZ7l4cBz16T1S7Nkx4KAmOuEDO0c GmcR7V5aTO0663Dfn0K1QUQ6v0RAiwGZAYuCQuLei0MJLqj/7LSL4LY2T53pEZcgolY6+B9m5gY vjuN2cjWemU5H37OXCExa6a+OSVdLoOapXG5btsU1CXsBzFraQyDeUiuC7IhTu73GW3FID6ii/L FEOiUBVb3MhIoaDl8XEnaylItdGoGp1B0EvNLUwMaAQnaIvNOqXvvUzBHM9V+3xp54Wtb/ObxGN zvr7n/B/itq9RE7zTOlrxYfbFaXOX1vLqyg3U0y2CbWS4YgeD319Mc2sNLj7JcPjiUtcEEI9j4W 1RW0xCI9pY/gKw6OogYJOLlF3ae+7f8U= X-Google-Smtp-Source: AGHT+IF+izjhG7On7CU2ByXZpOjy8LPezZ/rCLLddhlRANAQzX6wO7qTaFF2CC3KEWQiRyxvo+F7Jw== X-Received: by 2002:a17:902:eb8b:b0:246:a4e6:50b8 with SMTP id d9443c01a7336-246a4e655fcmr163205335ad.56.1756248709266; Tue, 26 Aug 2025 15:51:49 -0700 (PDT) Received: from [0.0.5.57] ([136.159.213.249]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-246688a601esm105340815ad.162.2025.08.26.15.51.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 Aug 2025 15:51:48 -0700 (PDT) From: Abhinav Saxena Date: Tue, 26 Aug 2025 16:51:31 -0600 Subject: [RFC PATCH 1/5] tty: Add KUnit test infrastructure configuration Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250826-tty-tests-v1-1-e904a817df92@gmail.com> References: <20250826-tty-tests-v1-0-e904a817df92@gmail.com> In-Reply-To: <20250826-tty-tests-v1-0-e904a817df92@gmail.com> To: Greg Kroah-Hartman , Jiri Slaby , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt , Kees Cook Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, llvm@lists.linux.dev, linux-hardening@vger.kernel.org, Abhinav Saxena X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1756248706; l=5404; i=xandfury@gmail.com; s=20250614; h=from:subject:message-id; bh=nja9nOlsvSDuhTHFm8quefBlVFD24Sl3DW2Igdp8jGk=; b=QDM8ax2hVfpwy0tB0W42Pw1oKpD+7EMU1aQCxHNEmk+WZIpTA286sil+W+VT9m+B2Rx6rceaW UmEXE4gkWuBBT2zDSfvvdIaYwe7m64cF0eTey4PD2StBJK0WKMWSDPy X-Developer-Key: i=xandfury@gmail.com; a=ed25519; pk=YN6w7WNet8skqvMWxhG5BlAmtd1SQmo8If6Mofh4k44= Add Kconfig and Makefile support for TTY KUnit testing framework. Introduces CONFIG_TTY_GCOV_PROFILE for targeted coverage analysis and CONFIG_TTY_KUNIT_TESTS for test infrastructure enabling. The infrastructure allows selective testing of TTY components without instrumenting the entire kernel, improving development efficiency for TTY subsystem changes. Signed-off-by: Abhinav Saxena Reviewed-by: Jiri Slaby --- drivers/tty/Kconfig | 9 +++++++++ drivers/tty/Makefile | 7 +++++++ drivers/tty/tests/.kunitconfig | 44 ++++++++++++++++++++++++++++++++++++++= ++++ drivers/tty/tests/Kconfig | 44 ++++++++++++++++++++++++++++++++++++++= ++++ drivers/tty/tests/Makefile | 2 ++ 5 files changed, 106 insertions(+) diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 149f3d53b76086cd4ac5acf116ebe36d816664ac..92d27761e1543ceb138a6701944= 80ed1e711124b 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -424,6 +424,15 @@ config RPMSG_TTY To compile this driver as a module, choose M here: the module will be called rpmsg_tty. =20 +config TTY_GCOV_PROFILE + bool "Enable gcov profiling for TTY subsystem" + depends on GCOV_KERNEL && TTY + help + Instrument drivers/tty/* with gcov when GCOV is enabled. + Useful for targeted coverage runs without profiling the whole kernel. + +source "drivers/tty/tests/Kconfig" + endif # TTY =20 source "drivers/tty/serdev/Kconfig" diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 07aca5184a55dd38036587c3485ba9f12d2e7ec7..c0cb47c32e94abe072f4cd7e202= 3480bbb5da9f2 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -29,3 +29,10 @@ obj-$(CONFIG_VCC) +=3D vcc.o obj-$(CONFIG_RPMSG_TTY) +=3D rpmsg_tty.o =20 obj-y +=3D ipwireless/ + +obj-$(CONFIG_TTY_KUNIT_TESTS) +=3D tests/ + +# tty profiling & coverage +ifdef CONFIG_TTY_GCOV_PROFILE +GCOV_PROFILE :=3D y +endif diff --git a/drivers/tty/tests/.kunitconfig b/drivers/tty/tests/.kunitconfig new file mode 100644 index 0000000000000000000000000000000000000000..a29112fa03ae78e096d9e22546b= 3cfd3007d710c --- /dev/null +++ b/drivers/tty/tests/.kunitconfig @@ -0,0 +1,44 @@ +# TTY KUnit Test Configuration +# =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D +# +# Running test with something like this: +# ------------------------------------- +# ./tools/testing/kunit/kunit.py run tty_io_core \ +# --kunitconfig=3D.kunit/ \ +# --kunitconfig=3Ddrivers/tty/tests/.kunitconfig \ +# --jobs "$(nproc)" \ +# --arch=3Dx86_64 \ +# --make_options=3D"LLVM=3D-18" + +# Core KUnit Infrastructure +# ------------------------- +CONFIG_KUNIT=3Dy +CONFIG_KUNIT_DEBUGFS=3Dy +CONFIG_KUNIT_TEST=3Dy +CONFIG_PROC_FS=3Dy +CONFIG_SYSFS=3Dy +CONFIG_DEBUG_FS=3Dy + +# TTY Subsystem Core +# ------------------ +CONFIG_TTY=3Dy +CONFIG_UNIX98_PTYS=3Dy +CONFIG_SERIAL_CORE=3Dy +CONFIG_VT=3Dy +CONFIG_VT_CONSOLE=3Dy + +# TTY Drivers for Testing +# ----------------------- +# Enable ttynull driver (required for ttynull tests) +CONFIG_NULL_TTY=3Dy + +# TTY KUnit Tests +# --------------- +CONFIG_TTY_KUNIT_TESTS=3Dy +CONFIG_TTY_KUNIT_CORE_TESTS=3Dy +CONFIG_TTY_KUNIT_NULL_TTY_TESTS=3Dy + +# Code Coverage +# ------------------------------------------- +CONFIG_GCOV_KERNEL=3Dy +CONFIG_TTY_GCOV_PROFILE=3Dy diff --git a/drivers/tty/tests/Kconfig b/drivers/tty/tests/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..6012f4ab7f1bc90d434d69ee82e= 87e89c1291763 --- /dev/null +++ b/drivers/tty/tests/Kconfig @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0 +menu "TTY KUnit tests" + depends on KUNIT && TTY + +config TTY_KUNIT_TESTS + bool "TTY KUnit test infrastructure" + depends on KUNIT && TTY + default KUNIT_ALL_TESTS + help + Enable KUnit test infrastructure for TTY drivers. + + This enables test helper functions that are included directly + into tty_io.c to provide access to internal TTY functions for + testing. The helpers are only available when KUNIT is enabled. + + This option provides the foundation for TTY testing but does not + run any tests by itself. Enable specific test suites below. + +config TTY_KUNIT_CORE_TESTS + bool "TTY core functionality tests" + depends on TTY_KUNIT_TESTS + default KUNIT_ALL_TESTS + help + Enable KUnit tests for TTY core functionality. + + Tests cover basic TTY operations including open/close lifecycle, + write operations, buffer management, line discipline integration, + and error handling through real kernel code paths. + + If unsure, say N. + +config TTY_KUNIT_NULL_TTY_TESTS + tristate "TTY null driver tests" if !KUNIT_ALL_TESTS + depends on TTY_KUNIT_TESTS && NULL_TTY + default KUNIT_ALL_TESTS + help + Enable KUnit tests for the TTY null driver. + + Tests validate ttynull behavior as a data sink, including write + operations, data discarding, and error handling. The ttynull + driver discards all written data while providing minimal overhead. + + If unsure, say N. +endmenu diff --git a/drivers/tty/tests/Makefile b/drivers/tty/tests/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..854911a0f87d6a6fdc826cc40f9= 9afb1202afd46 --- /dev/null +++ b/drivers/tty/tests/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_TTY_KUNIT_CORE_TESTS) +=3D tty_mock.o test_tty_io_core.o --=20 2.43.0 From nobody Fri Oct 3 18:10:16 2025 Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 768DF1F2382; Tue, 26 Aug 2025 22:51:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756248713; cv=none; b=aJSu0uQCKoRp0D35MHF8Lmr76R/qKgpAuPDczBHNmySrHeNdKVBX0xh9gl+316ijhth4bCkQp20pa/1yxhU6uXOqCeGOgTMqmFTIixv3qwwe9Yw6kDD4QOS8mZaWV8Ns5OfasMLHeT5Xrja3o+8JiJg57uy9vTKJavZZbjdmB04= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756248713; c=relaxed/simple; bh=BF7kBQO7ZYNGz++8lrkbtrB7W8yYMojaBKbsVPLuNc4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WyJxkc1Fa1Tjvi+BbHT3gwb5R2TKgGJu0c56thN/2Q1JqKDvILhOOh0UF80gOAoTbtJ765sJMJVnjI5CCeJazcKMFL08A+wqNnROk7a+X3uHf70NQSvqMdwJuJEO2wIBw4Dlh4AvPg8KBnxm3QFArcf1ip+saGzqtpLwmKZzCI0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Q0Y2i1mK; arc=none smtp.client-ip=209.85.214.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Q0Y2i1mK" Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-244580523a0so61152215ad.1; Tue, 26 Aug 2025 15:51:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1756248711; x=1756853511; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=yR8G0czTv3htBZuNHGulWIjLUJ2zOwYOErFDW8owQ5M=; b=Q0Y2i1mKkuZzJs/nAemXU6lsxueyGwHPXoNXSWjclySHhGcPLHpjuBCQWBU5LSN+rO 7Z2y4ZKG+8QujNlMCAR8NoVwhIJLBer5iFWjAiyer2SwtqIvXyFTjwR11K4hxS5KiDgj ry4VVDUsp5RpIGzDAu0YL6c+sAeSZy9cUdgHlfy8N/0mG0PXcvUsEwOJq1fC8jonGPvd fi//PquwcjW9jQgGHLSyHVMOBBkYi5ybZ6PSz9+sd882QTJ8+fqJs1nsG2pKG0pI3yKG woBYswWWZnI5MUupXa0tFsubhOtz0y78y4csS/qN002pVeMaJoWza0wh/YxZQfbcJTZt ikNg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1756248711; x=1756853511; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=yR8G0czTv3htBZuNHGulWIjLUJ2zOwYOErFDW8owQ5M=; b=A5NDPs6vST6v2XLno9cptKqYGnnY4lDjFRahlqjgXxigQw2MQrpGcQ//SiBGvTpIxx HXmj2urHOBmPONanJ4Yi/93MbdWPT1ihcmpn87vKH0tyr9NNwhnvlroyP65zwgKgEGQA bsQhFM0xY/wxQMZQ6UvrSZ5xNF8YxAkGrfn9M6vKZVV/sGsHDOW2EViTL45TzUg/Dnpo UjRzZLWwTorHSW6eMWZ+OxsVtVDvwtr35WZsDjChevFeVUzlQZ51RnVq/liS/BD+HpYk Pu+L5tAvSR/bl6BOS0zKngRKxc8VkHMP04f0lRj9Je4aMGD4WmBS1IhUHxfi+yKqjAmO 8Law== X-Forwarded-Encrypted: i=1; AJvYcCUZeIDwSR7xEt0J+INzf6YIXIgILnjlDIw8NQ14Q6pIYcJHBGEwYA+3tM1UoPQp3ZQd8e+ojCVbBiIQu6Un238=@vger.kernel.org, AJvYcCWRU/TNI011E+6dFqjPZTX0A9JYd0EQ+4qUsuHYWgYkbhbOUeijYzhiHdiyKBeTId3BOsZArwTZAKwnk2yj@vger.kernel.org X-Gm-Message-State: AOJu0YyRMpdJ6OsrXRm46cixnQ6PVHbt7HyD4HSRbJVAZdOon0IzOiD7 pKix4OUSzpQZJVanXNKU0Il8vHCkrenGBJyNb/RL12YvTabwtGp9eMVK X-Gm-Gg: ASbGncvwLFm/Xmt8n24RGXY+rjzuUQ/Q0VQCkaPJh9hOTpSLaiDD4puSvNipge/bfQp kEJW5Yn1PwDunLvI1uuzLOHjGPxZ/xA1861x3WEaA0r4A513KIt+k5pEj+DV5RtvtKmRFzk6GRs muhAm/tAkBnP5NzivJ1xmaZZEYO6F1UgKeSISbzI9h9D9qUX4NGv0FD7cLs1xDCnAK2kH3sCnHK sjAGg2daAoigMR0gh/A+Z1Dtag/ExOlP6VforxNt5AjpqvoKM9F4eRlTRjLhjopHL5BXZURUZ1h H3y/ZkoM4T1F2VY9bSEFd9duwkpolChj+1w/oHHdVbaTHalSseHqmlkj9iAITjVHvJ5bswKXGpG HvuAUWCbyt2VeMTNpMvUvBbZFvKOuv7c= X-Google-Smtp-Source: AGHT+IEjSg/wptlibuEPS3+h0fbv78JdD9oVr0ztHncjA7DYzOHurKEY4HSpVjseAYYr8cUoJiW0Ew== X-Received: by 2002:a17:903:fa5:b0:246:b56f:7ec2 with SMTP id d9443c01a7336-246b56f7f2cmr112085625ad.51.1756248710622; Tue, 26 Aug 2025 15:51:50 -0700 (PDT) Received: from [0.0.5.57] ([136.159.213.249]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-246688a601esm105340815ad.162.2025.08.26.15.51.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 Aug 2025 15:51:50 -0700 (PDT) From: Abhinav Saxena Date: Tue, 26 Aug 2025 16:51:32 -0600 Subject: [RFC PATCH 2/5] tty: Add KUnit test helper functions Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250826-tty-tests-v1-2-e904a817df92@gmail.com> References: <20250826-tty-tests-v1-0-e904a817df92@gmail.com> In-Reply-To: <20250826-tty-tests-v1-0-e904a817df92@gmail.com> To: Greg Kroah-Hartman , Jiri Slaby , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt , Kees Cook Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, llvm@lists.linux.dev, linux-hardening@vger.kernel.org, Abhinav Saxena X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1756248706; l=21106; i=xandfury@gmail.com; s=20250614; h=from:subject:message-id; bh=BF7kBQO7ZYNGz++8lrkbtrB7W8yYMojaBKbsVPLuNc4=; b=VJhVgezEu94oBRKkY5V7RHnBCvBdi0oWZnsx+LFzy8JerAQJjI4ixfYORIOK9/bk+lEtY3WOX /xbWGTHdopNC79Y2ETPw68vjUOhMyLlYuE0rw1+xRWDoPSQ6sB7sYTA X-Developer-Key: i=xandfury@gmail.com; a=ed25519; pk=YN6w7WNet8skqvMWxhG5BlAmtd1SQmo8If6Mofh4k44= Introduce comprehensive test helper infrastructure for TTY driver testing through real kernel entry points. The helpers are included directly into tty_io.c when CONFIG_TTY_KUNIT_TESTS=3Dy to access internal functions while maintaining clean symbol export boundaries. Key features: - Complete TTY lifecycle testing (open/close/read/write) - RX data injection via flip buffers - Termios manipulation for configuration testing - Automatic resource cleanup via KUnit actions All functions use EXPORT_SYMBOL_IF_KUNIT() to prevent production symbol table pollution while enabling comprehensive testing. Signed-off-by: Abhinav Saxena Reviewed-by: Jiri Slaby --- drivers/tty/tests/tty_test_helpers.c | 387 +++++++++++++++++++++++++++++++= ++++ drivers/tty/tests/tty_test_helpers.h | 239 +++++++++++++++++++++ drivers/tty/tty_io.c | 4 + 3 files changed, 630 insertions(+) diff --git a/drivers/tty/tests/tty_test_helpers.c b/drivers/tty/tests/tty_t= est_helpers.c new file mode 100644 index 0000000000000000000000000000000000000000..0de61626ccef1d4e4246f433474= 01ff46ac432ec --- /dev/null +++ b/drivers/tty/tests/tty_test_helpers.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit test helpers for TTY drivers + * + * This file is included directly into tty_io.c when CONFIG_TTY_KUNIT_TEST= S=3Dy. + * This allows the helper functions to access internal TTY functions like + * tty_open() and tty_release() while providing exported symbols for use + * by test modules. + * + * All functions are exported via EXPORT_SYMBOL_IF_KUNIT() so they are + * only available when KUNIT is enabled, preventing pollution of the + * production symbol table. + * + * Copyright (c) 2025 Abhinav Saxena + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tests/tty_test_helpers.h" + + +static struct cdev tty_cdev; + +/** + * _tty_test_cleanup_release - KUnit cleanup action for TTY release + * @data: Pointer to tty_test_fixture + * + * Internal cleanup function registered with kunit_add_action() to ensure + * TTY is properly released even if test fails or exits early. + * This prevents resource leaks and system instability. + */ +static void _tty_test_cleanup_release(void *data) +{ + struct tty_test_fixture *fx =3D data; + int ret; + + if (!fx || !fx->opened || !fx->file || !fx->inode) + return; + + ret =3D tty_release(fx->inode, fx->file); + if (ret) + pr_warn("TTY test cleanup failed: %d\n", ret); + fx->opened =3D false; +} + +/** + * tty_test_create_fixture - Create a test fixture for TTY driver testing + */ +struct tty_test_fixture *tty_test_create_fixture(struct kunit *test, + struct tty_driver *driver, + unsigned int index) +{ + struct tty_test_fixture *fx; + + KUNIT_ASSERT_NOT_NULL(test, driver); + + fx =3D kunit_kzalloc(test, sizeof(*fx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fx); + + fx->test =3D test; + fx->driver =3D driver; + fx->dev =3D MKDEV(driver->major, driver->minor_start + index); + + /* Create synthetic VFS structures for real TTY operations */ + fx->file =3D kunit_kzalloc(test, sizeof(*fx->file), GFP_KERNEL); + fx->inode =3D kunit_kzalloc(test, sizeof(*fx->inode), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fx->file); + KUNIT_ASSERT_NOT_NULL(test, fx->inode); + + /* Initialize as character device with appropriate permissions */ + init_special_inode(fx->inode, S_IFCHR | 0600, fx->dev); + fx->inode->i_rdev =3D fx->dev; + fx->inode->i_cdev =3D &tty_cdev; + KUNIT_ASSERT_NOT_NULL(test, fx->inode->i_cdev); + + fx->file->f_flags =3D O_RDWR; + fx->file->f_mode =3D FMODE_READ | FMODE_WRITE; + fx->file->f_inode =3D fx->inode; + + /* Register cleanup before any operations that might fail */ + kunit_add_action(test, _tty_test_cleanup_release, fx); + + fx->opened =3D false; + return fx; +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_create_fixture); + +/** + * tty_test_open - Open TTY through standard kernel path + */ +int tty_test_open(struct tty_test_fixture *fx) +{ + int ret; + + KUNIT_ASSERT_NOT_NULL(fx->test, fx); + KUNIT_ASSERT_NOT_NULL(fx->test, fx->file); + KUNIT_ASSERT_NOT_NULL(fx->test, fx->inode); + + ret =3D tty_open(fx->inode, fx->file); + if (ret) + return ret; + + fx->tty =3D file_tty(fx->file); + KUNIT_ASSERT_NOT_NULL(fx->test, fx->tty); + + /* Verify the TTY is properly set up */ + KUNIT_EXPECT_TRUE(fx->test, !list_empty(&fx->tty->tty_files)); + /* Ldisc must now be fully installed */ + KUNIT_ASSERT_NOT_NULL(fx->test, fx->tty->ldisc); + KUNIT_EXPECT_TRUE(fx->test, fx->tty->ldisc->ops); + KUNIT_ASSERT_NOT_NULL(fx->test, fx->tty->disc_data); + KUNIT_EXPECT_NOT_NULL(fx->test, fx->tty->port); + + fx->port =3D fx->tty->port; + ret =3D fx->tty->ldisc->ops->open(fx->tty); + if (ret) { + tty_release(fx->inode, fx->file); + return ret; + } + + /* Enable non-blocking mode for predictable test behavior */ + fx->file->f_flags |=3D O_NONBLOCK; + fx->opened =3D true; + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_open); + +/** + * tty_test_release - Close TTY through standard kernel path + */ +int tty_test_release(struct tty_test_fixture *fx) +{ + int ret; + + if (!fx || !fx->opened) + return 0; + + /* + * This calls the internal tty_release() function directly. + * This works because this code is compiled as part of tty_io.c. + */ + ret =3D tty_release(fx->inode, fx->file); + if (!ret) { + fx->opened =3D false; + fx->tty =3D NULL; + fx->port =3D NULL; + } + return ret; +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_release); + +/** + * tty_test_write - Write data to TTY + */ +ssize_t tty_test_write(struct tty_test_fixture *fx, const void *buf, + size_t count) +{ + struct kiocb iocb; + struct iov_iter from; + struct kvec kvec =3D { .iov_base =3D (void *)buf, .iov_len =3D count }; + + KUNIT_ASSERT_NOT_NULL(fx->test, fx); + KUNIT_ASSERT_NOT_NULL(fx->test, fx->file); + KUNIT_ASSERT_TRUE(fx->test, fx->opened); + + init_sync_kiocb(&iocb, fx->file); + iov_iter_kvec(&from, WRITE, &kvec, 1, count); + + /* tty_write() is exported, so this works */ + return tty_write(&iocb, &from); +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_write); + +/** + * tty_test_write_all - Write all data or fail + */ +int tty_test_write_all(struct tty_test_fixture *fx, const void *buf, size_= t len) +{ + size_t off =3D 0; + int retries =3D 10; + + KUNIT_ASSERT_NOT_NULL(fx->test, fx); + KUNIT_ASSERT_TRUE(fx->test, fx->opened); + + while (off < len && retries--) { + ssize_t n =3D + tty_test_write(fx, (const char *)buf + off, len - off); + if (n < 0) + return n; + if (n =3D=3D 0) { + /* No progress - prevent infinite loop */ + if (--retries <=3D 0) { + KUNIT_FAIL(fx->test, + "Write stalled after %zu bytes", + off); + return -EIO; + } + continue; + } + off +=3D n; + } + + if (off < len) { + KUNIT_FAIL(fx->test, "Incomplete write: %zu/%zu bytes", off, + len); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_write_all); + +/** + * tty_test_read - Read data from TTY (non-blocking) + */ +ssize_t tty_test_read(struct tty_test_fixture *fx, void *buf, size_t count) +{ + struct kiocb iocb; + struct iov_iter to; + struct kvec kvec =3D { .iov_base =3D buf, .iov_len =3D count }; + + KUNIT_ASSERT_NOT_NULL(fx->test, fx); + KUNIT_ASSERT_NOT_NULL(fx->test, fx->file); + KUNIT_ASSERT_TRUE(fx->test, fx->opened); + + init_sync_kiocb(&iocb, fx->file); + iov_iter_kvec(&to, READ, &kvec, 1, count); + + return tty_read(&iocb, &to); +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_read); + +/** + * tty_test_read_all - Attempt to read all requested data + */ +ssize_t tty_test_read_all(struct tty_test_fixture *fx, void *buf, size_t w= ant) +{ + size_t off =3D 0; + int tries =3D 8; + + KUNIT_ASSERT_NOT_NULL(fx->test, fx); + KUNIT_ASSERT_TRUE(fx->test, fx->opened); + + while (off < want && tries--) { + ssize_t n =3D tty_test_read(fx, (char *)buf + off, want - off); + + if (n =3D=3D -EAGAIN) + continue; + if (n < 0) + return n; + if (n =3D=3D 0) + continue; + off +=3D n; + } + return off; +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_read_all); + +/** + * tty_test_simulate_rx - Inject received data for testing + */ +int tty_test_simulate_rx(struct tty_test_fixture *fx, const unsigned char = *data, + size_t len) +{ + int ret; + + KUNIT_ASSERT_NOT_NULL(fx->test, fx); + KUNIT_ASSERT_NOT_NULL(fx->test, fx->port); + KUNIT_ASSERT_TRUE(fx->test, fx->opened); + + ret =3D tty_insert_flip_string(fx->port, data, len); + if (ret > 0) + tty_flip_buffer_push(fx->port); + + return ret; +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_simulate_rx); + +/** + * tty_fx_supports_rx - Check if fixture supports RX testing + */ +bool tty_fx_supports_rx(const struct tty_test_fixture *fx) +{ + struct tty_ldisc *ld; + const struct tty_ldisc_ops *ops; + + if (!fx || !fx->tty || !fx->opened) + return false; + + ld =3D tty_ldisc_ref(fx->tty); + if (!ld) + return false; + + ops =3D READ_ONCE(ld->ops); + if (ops && (ops->receive_buf || ops->receive_buf2)) { + tty_ldisc_deref(ld); + return true; + } + + tty_ldisc_deref(ld); + return false; +} +EXPORT_SYMBOL_IF_KUNIT(tty_fx_supports_rx); + +/** + * tty_test_assert_valid_ops - Validate driver has required operations + */ +void tty_test_assert_valid_ops(struct kunit *test, + const struct tty_driver *driver) +{ + KUNIT_ASSERT_NOT_NULL(test, driver); + KUNIT_ASSERT_NOT_NULL(test, driver->ops); + KUNIT_ASSERT_NOT_NULL(test, driver->ops->open); + KUNIT_ASSERT_NOT_NULL(test, driver->ops->close); + KUNIT_ASSERT_NOT_NULL(test, driver->ops->write); + KUNIT_ASSERT_NOT_NULL(test, driver->ops->write_room); + KUNIT_EXPECT_TRUE(test, driver->flags & TTY_DRIVER_INSTALLED); +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_assert_valid_ops); + +/** + * tty_test_get_chars_in_buffer - Get number of chars in output buffer + */ +unsigned int tty_test_get_chars_in_buffer(struct tty_test_fixture *fx) +{ + KUNIT_ASSERT_NOT_NULL(fx->test, fx); + KUNIT_ASSERT_TRUE(fx->test, fx->opened); + KUNIT_ASSERT_NOT_NULL(fx->test, fx->tty); + + if (fx->tty->ops->chars_in_buffer) + return fx->tty->ops->chars_in_buffer(fx->tty); + + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_get_chars_in_buffer); + +/** + * tty_test_get_write_room - Get available write room + */ +unsigned int tty_test_get_write_room(struct tty_test_fixture *fx) +{ + KUNIT_ASSERT_NOT_NULL(fx->test, fx); + KUNIT_ASSERT_TRUE(fx->test, fx->opened); + KUNIT_ASSERT_NOT_NULL(fx->test, fx->tty); + + if (fx->tty->ops->write_room) + return fx->tty->ops->write_room(fx->tty); + + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_get_write_room); + +/** + * tty_test_set_termios - Set terminal attributes for testing + */ +int tty_test_set_termios(struct tty_test_fixture *fx, + const struct ktermios *termios) +{ + struct ktermios old_termios; + + KUNIT_ASSERT_NOT_NULL(fx->test, fx); + KUNIT_ASSERT_TRUE(fx->test, fx->opened); + KUNIT_ASSERT_NOT_NULL(fx->test, fx->tty); + KUNIT_ASSERT_NOT_NULL(fx->test, termios); + + /* Save old termios for potential restoration */ + old_termios =3D fx->tty->termios; + + /* Update termios */ + fx->tty->termios =3D *termios; + + /* Call driver's set_termios if it exists */ + if (fx->tty->ops->set_termios) + fx->tty->ops->set_termios(fx->tty, &old_termios); + + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(tty_test_set_termios); diff --git a/drivers/tty/tests/tty_test_helpers.h b/drivers/tty/tests/tty_t= est_helpers.h new file mode 100644 index 0000000000000000000000000000000000000000..10da4189e35880399e2c857f599= ea7f4107f9e90 --- /dev/null +++ b/drivers/tty/tests/tty_test_helpers.h @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * KUnit test helpers for TTY drivers - Header declarations + * + * Provides reusable infrastructure for testing TTY drivers through + * real kernel entry points without requiring userspace interaction + * or hardware dependencies. + * + * The implementation (tty_test_helpers.c) is included directly into + * tty_io.c to allow access to internal TTY functions while providing + * exported symbols for test modules. + * + * Copyright (c) 2025 Abhinav Saxena + * + */ + +#ifndef _TTY_TEST_HELPERS_H +#define _TTY_TEST_HELPERS_H + +#include +#include +#include +#include + +/** + * struct tty_test_fixture - Test fixture for TTY driver testing + * @test: KUnit test context for assertions and memory management + * @driver: TTY driver being tested + * @tty: TTY structure (valid after successful open) + * @port: TTY port structure (valid after successful open) + * @file: Synthetic file structure for VFS operations + * @inode: Synthetic inode structure for device operations + * @dev: Device number (major:minor) for this TTY + * @opened: True if TTY has been opened successfully + * + * This fixture provides all necessary structures for testing TTY drivers + * through the standard kernel interfaces. Memory is managed by KUnit and + * automatic cleanup ensures proper resource release. + */ +struct tty_test_fixture { + struct kunit *test; + struct tty_driver *driver; + struct tty_struct *tty; + struct tty_port *port; + struct file *file; + struct inode *inode; + dev_t dev; + bool opened; +}; + +/* Core fixture management */ + +/** + * tty_test_create_fixture - Create a test fixture for TTY driver testing + * @test: KUnit test context + * @driver: TTY driver to test (must be registered) + * @index: Minor number index for this TTY instance + * + * Creates a complete test fixture with synthetic VFS structures that + * enable testing through real tty_open()/tty_release() paths. + * All memory is managed by KUnit with automatic cleanup. + * + * Return: Allocated fixture or NULL on failure (test will abort) + */ +struct tty_test_fixture *tty_test_create_fixture(struct kunit *test, + struct tty_driver *driver, + unsigned int index); + +/* TTY lifecycle operations */ + +/** + * tty_test_open - Open TTY through standard kernel path + * @fx: Test fixture created with tty_test_create_fixture() + * + * Opens the TTY using tty_open(), the same entry point used by userspace. + * This exercises the complete open sequence including driver install, + * line discipline attachment, and port initialization. + * + * After successful open: + * - fx->tty points to the allocated TTY structure + * - fx->port points to the associated TTY port + * - File is set to non-blocking mode for test convenience + * + * Return: 0 on success, negative error code on failure + */ +int tty_test_open(struct tty_test_fixture *fx); + +/** + * tty_test_release - Close TTY through standard kernel path + * @fx: Test fixture with opened TTY + * + * Closes the TTY using tty_release(), exercising the complete close + * Safe to call multiple times or on unopened fixtures. + * + * Return: 0 on success, negative error code on failure + */ +int tty_test_release(struct tty_test_fixture *fx); + +/* Data transfer operations */ + +/** + * tty_test_write - Write data to TTY + * @fx: Test fixture with opened TTY + * @buf: Data buffer to write + * @count: Number of bytes to write + * + * Writes data using tty_write(), the same path used by userspace write(). + * This exercises line discipline processing, flow control, and driver + * write operations. May return partial writes based on buffer availabilit= y. + * + * Return: Number of bytes written, or negative error code + */ +ssize_t tty_test_write(struct tty_test_fixture *fx, const void *buf, + size_t count); + +/** + * tty_test_write_all - Write all data or fail + * @fx: Test fixture with opened TTY + * @buf: Data buffer to write completely + * @len: Number of bytes that must be written + * + * Ensures all data is written by retrying partial writes. + * Useful for testing scenarios where complete data delivery is required. + * Will assert-fail the test if any individual write returns 0 bytes. + * + * Return: 0 on complete success, negative error code on failure + */ +int tty_test_write_all(struct tty_test_fixture *fx, const void *buf, + size_t len); + +/** + * tty_test_read - Read data from TTY (non-blocking) + * @fx: Test fixture with opened TTY + * @buf: Buffer to receive data + * @count: Maximum bytes to read + * + * Reads data using tty_read() in non-blocking mode. This is useful for + * verifying that injected RX data is properly delivered through the + * line discipline to userspace. Returns immediately with -EAGAIN if + * no data is available. + * + * Return: Number of bytes read, -EAGAIN if no data, or other negative err= or + */ +ssize_t tty_test_read(struct tty_test_fixture *fx, void *buf, size_t count= ); + +/** + * tty_test_read_all - Attempt to read all requested data + * @fx: Test fixture with opened TTY + * @buf: Buffer to receive data + * @want: Number of bytes desired + * + * Makes a bounded number of read attempts to collect the requested amount + * of data. Useful for reading back data that was injected via flip buffer= s, + * accounting for potential delays in line discipline processing. + * + * Return: Number of bytes actually read (may be less than requested) + */ +ssize_t tty_test_read_all(struct tty_test_fixture *fx, void *buf, size_t w= ant); + +/* RX simulation and testing */ + +/** + * tty_test_simulate_rx - Inject received data for testing + * @fx: Test fixture with opened TTY + * @data: Data bytes to inject + * @len: Number of bytes to inject + * + * Simulates data reception by injecting bytes through the flip buffer + * interface and pushing them to the line discipline. This allows testing + * of RX data paths, flow control, and line discipline processing without + * requiring actual hardware or external data sources. + * + * Return: Number of bytes successfully queued, or negative error code + */ +int tty_test_simulate_rx(struct tty_test_fixture *fx, const unsigned char = *data, + size_t len); + +/** + * tty_fx_supports_rx - Check if fixture supports RX testing + * @fx: Test fixture to check + * + * Determines if the TTY has a line discipline attached that can receive + * data. This is used to conditionally run RX-related tests since not all + * TTY configurations support data reception (e.g., write-only devices). + * + * Return: true if RX testing is supported, false otherwise + */ +bool tty_fx_supports_rx(const struct tty_test_fixture *fx); + +/* Driver validation and utility functions */ + +/** + * tty_test_assert_valid_ops - Validate driver has required operations + * @test: KUnit test context + * @driver: TTY driver to validate + * + * Performs basic sanity checks on TTY driver structure to ensure it has + * the minimum required operations. This catches configuration errors that + * would cause NULL pointer dereferences during testing. + */ +void tty_test_assert_valid_ops(struct kunit *test, + const struct tty_driver *driver); + +/** + * tty_test_get_chars_in_buffer - Get number of chars in output buffer + * @fx: Test fixture with opened TTY + * + * Returns the number of characters currently in the driver's output buffe= r. + * Useful for testing flow control and buffer management. + * + * Return: Number of characters in buffer, or 0 if not supported + */ +unsigned int tty_test_get_chars_in_buffer(struct tty_test_fixture *fx); + +/** + * tty_test_get_write_room - Get available write room + * @fx: Test fixture with opened TTY + * + * Returns the number of bytes that can be written without blocking. + * Useful for testing buffer management and flow control. + * + * Return: Number of bytes available for writing + */ +unsigned int tty_test_get_write_room(struct tty_test_fixture *fx); + +/** + * tty_test_set_termios - Set terminal attributes for testing + * @fx: Test fixture with opened TTY + * @termios: Terminal attributes to set + * + * Sets terminal attributes through the standard termios interface. + * Useful for testing different terminal configurations. + * + * Return: 0 on success, negative error code on failure + */ +int tty_test_set_termios(struct tty_test_fixture *fx, + const struct ktermios *termios); + +#endif /* _TTY_TEST_HELPERS_H */ diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e2d92cf70eb78ec6b2b93b55192e46781160c9dc..ac94a037358c9df0ba401315287= 8ca83a2e001c5 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3650,3 +3650,7 @@ int __init tty_init(void) #endif return 0; } + +#ifdef CONFIG_TTY_KUNIT_TESTS +#include "tests/tty_test_helpers.c" +#endif --=20 2.43.0 From nobody Fri Oct 3 18:10:16 2025 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A8E832E1EEC; Tue, 26 Aug 2025 22:51:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756248714; cv=none; b=XFsezGlMeR7VUHru4C0YeqYidqK4Zq16RJXiVjt0nmmZsAfKfjszqmqlpqr5+XuAwkjHPeXoTmzNC3QF7nptrrDcggtacwQCfqNrcdQwtiQtEoHHGtj0CycwE24rU33k1A40YkAxlKGQicdpxLq/ksS16JnfcbO2r7g9dWFszzg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756248714; c=relaxed/simple; bh=nkgAgHfDk/2F0xYm3P4OtMs0EfdJM0Zbncn4co8vnRo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OTqG5JhQR96rB/DrxkGp0hY7X6pM0FO2byBX2IsToJ5of8bhHiST4ikXl1AsZlkXGjxaGedyELU2JsKfFujpy+2XHW08jjtd8rfqO7SFlVa64bGpvtuigtAH7p+IwHlHOsiKGNA5pXHgKs1GfqlHST+caeyxAPmKLhYzG8p6JCI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Xt+9IRoM; arc=none smtp.client-ip=209.85.214.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Xt+9IRoM" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-2489c65330aso135195ad.0; Tue, 26 Aug 2025 15:51:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1756248712; x=1756853512; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=TmShpxcfS462EVS+o44vyyydTMfDb5DHqYeZ5mEw6/I=; b=Xt+9IRoMsEyci6uo2USJ1VNayo0dUwOUXpkE5XOHRrH7exQ2bv77Q/zbt0GNDCclvw IOec+N+S/zHSzIYdLxaxtlPYpMB64hbGCmPNBm/jSYzJ3SM6t/p/lhMX+GHHIojoG4dD FvfcdJfX5AfeVPY/C5p0L5/dtxEg2dGtg/fLLJ48aXsb69vGrsDJceKTKCPkIvGMqMCf TTd/83hBDniQcRYQWUPiep3HlYCVRSIOoK4ALIzT/ANCFz/bDTPdHQ0Hr5QVt+6lyW35 co2ImLZwIDsCnVrhMg3dmPYcHZ+pjrKlUeX7FqBBJ1BA3Qg3QL+4y2AWVfZkV3mC5tnN OP2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1756248712; x=1756853512; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=TmShpxcfS462EVS+o44vyyydTMfDb5DHqYeZ5mEw6/I=; b=rKceS8BkvCWdKmGO9LpDwWXX7sj3UbkpK3W4jdIQ+73s65UaCNwEqpSnSnFfn992bH 7fpZBCaKGrFQu9l0KAhGcW19efNwKmbB0xqTZ11lKyKvmIEqcPEsw1oS69CxpUxZ+oIX EQwnwOD4IF9piMSTB+IJAGt/VnxWff6HkgB8gqTvRH1JiBoxJflj7IbpCZ52LKRz/X5r FPyq4z9UmYhSxx7e5rgTDBM+UjRJFnO+jaDCKtR6bIR7jjmzPqk/Vt3fD8E019YuFc/c pAyU7gyp5LmXki8NijKWO3l5gv3k5XwKlVRBIgV24TZje9qa1xXR7hVl/SRHr3IHv4lh ++jg== X-Forwarded-Encrypted: i=1; AJvYcCXBIc67Qt/xlGChZu0StAhc4IekXiWT/hGjnByhdaKnUZ5mejU7uxeA5cLiX80GslWES3US5PrQNTLA6VUjXSw=@vger.kernel.org, AJvYcCXaPSpwnHsAFqzCyVQCs6f0moFuSB8ENBz8YI/O9CMUbPSQznHzMLfio6EsCgpVI5MS9YtuSWdw9LBVh4oA@vger.kernel.org X-Gm-Message-State: AOJu0YwMp/If6Le8NGywHWshHQ4JJKSwLLU6OPkitVc3DJOlDyG0Tk7C nwWz19ZDkTinZm7Apj2KYON/VlLKKoDE3l72qINL0t0I4a/6d1iAZMz0 X-Gm-Gg: ASbGncskYcvkVjN6BTzKV9k8nEiB9vvjsL7+4kN1Y3DJazIpCK4YaFTXju54sWYYBns 2j6V3tHLVOFJaa9tT+XnwJ7AiBJ1EQxPn7YT1cmDwl+EIE5zpiCp2kwRsjhPD5T2BMTyZFeIxIt 7RqKIYM/NeJmBRVm16XVNVkYUgy6wZzPM2UyxQFIPcefJZRIlylS4kMX1Bgw8QdpjAI42qwDk/a dc6ogH2gjcd9bn/0FZpt/IRIleKlY9Aj+in3Ok4TQo376ZrepRrneesbgQJxlpP6d3MFAQM+xV9 RL4AdF0JdLfkqwp3nYUbLyyk9zXacbCdw3xCs0Cr1zalsP3XBEScXVmU1qS7g97VCQgD0ROVcJZ yTAQwfQAUbwBmPl507+/o X-Google-Smtp-Source: AGHT+IFWObHwEwct9wKgFlnQvPXciZeYkPjQRUnjpBOi9syrDOVV1chJhjD7o+c9mv2hvltz/iplog== X-Received: by 2002:a17:902:cf09:b0:235:eefe:68f4 with SMTP id d9443c01a7336-2462eeb62edmr227348585ad.29.1756248711956; Tue, 26 Aug 2025 15:51:51 -0700 (PDT) Received: from [0.0.5.57] ([136.159.213.249]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-246688a601esm105340815ad.162.2025.08.26.15.51.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 Aug 2025 15:51:51 -0700 (PDT) From: Abhinav Saxena Date: Tue, 26 Aug 2025 16:51:33 -0600 Subject: [RFC PATCH 3/5] tty: Add mock TTY driver for KUnit testing Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250826-tty-tests-v1-3-e904a817df92@gmail.com> References: <20250826-tty-tests-v1-0-e904a817df92@gmail.com> In-Reply-To: <20250826-tty-tests-v1-0-e904a817df92@gmail.com> To: Greg Kroah-Hartman , Jiri Slaby , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt , Kees Cook Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, llvm@lists.linux.dev, linux-hardening@vger.kernel.org, Abhinav Saxena X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1756248706; l=6583; i=xandfury@gmail.com; s=20250614; h=from:subject:message-id; bh=nkgAgHfDk/2F0xYm3P4OtMs0EfdJM0Zbncn4co8vnRo=; b=Z+qKGqJA9fK9NZS8wYfo/EhikMqsPVB7GCBpPxOPCqpPCOIL1nmddP0h2FHBuuOZ+3iVWDpuX YZGPBmtp4TkAqcLWTSy4Z7+ex31aI47u+L/qeY8QoLZDVdFqwwpwMne X-Developer-Key: i=xandfury@gmail.com; a=ed25519; pk=YN6w7WNet8skqvMWxhG5BlAmtd1SQmo8If6Mofh4k44= Implement a minimal mock TTY driver that provides deterministic behavior for testing core TTY functionality. The driver simulates immediate data transmission with configurable statistics tracking. The mock driver enables testing of TTY core paths without hardware dependencies or timing-sensitive behavior, ensuring reproducible test results across different systems. Signed-off-by: Abhinav Saxena Reviewed-by: Jiri Slaby --- drivers/tty/tests/tty_mock.c | 186 +++++++++++++++++++++++++++++++++++++++= ++++ drivers/tty/tests/tty_mock.h | 34 ++++++++ 2 files changed, 220 insertions(+) diff --git a/drivers/tty/tests/tty_mock.c b/drivers/tty/tests/tty_mock.c new file mode 100644 index 0000000000000000000000000000000000000000..d5488760bb83c2837bb5226e3c3= 3ec370c2c9c07 --- /dev/null +++ b/drivers/tty/tests/tty_mock.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Minimal mock TTY driver for KUnit tests. Based on ttynull and ttyprintk + * + * Behavior: + * - write() pretends to transmit all bytes immediately + * - write_room() is large + * - chars_in_buffer() is 0 + * + * Tracks only: total_writes, total_bytes, last_write_len + * + * Copyright (c) 2025 Abhinav Saxena + */ + +#include +#include +#include +#include +#include +#include + +#include "tty_mock.h" + +#define TTYMOCK_NAME "ttymock" +#define TTYMOCK_ROOM 4096 + +static struct tty_port mock_port; /* single port */ + +/* --- Stats (private) --- */ +static struct { + u64 total_writes; + u64 total_bytes; + u32 last_write_len; + spinlock_t lock; +} mock_state; + +/* --- tty_operations --- */ + +static int mock_open(struct tty_struct *tty, struct file *file) +{ + tty->driver_data =3D &mock_port; + return tty_port_open(&mock_port, tty, file); +} + +static void mock_close(struct tty_struct *tty, struct file *file) +{ + tty_port_close(&mock_port, tty, file); + tty->driver_data =3D NULL; +} + +static ssize_t mock_write(struct tty_struct *tty, const u8 *buf, size_t cn= t) +{ + unsigned long flags; + + if (!buf) + return -EINVAL; + + spin_lock_irqsave(&mock_state.lock, flags); + mock_state.total_writes++; + mock_state.total_bytes +=3D cnt; + mock_state.last_write_len =3D cnt; + spin_unlock_irqrestore(&mock_state.lock, flags); + + return cnt; /* everything written immediately */ +} + +static unsigned int mock_write_room(struct tty_struct *tty) +{ + return TTYMOCK_ROOM; +} + +static unsigned int mock_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +static const struct tty_operations mock_ops =3D { + .open =3D mock_open, + .close =3D mock_close, + .write =3D mock_write, + .write_room =3D mock_write_room, + .chars_in_buffer =3D mock_chars_in_buffer, +}; + +/* --- tty_port_operations --- */ + +static bool mock_carrier_raised(struct tty_port *port) +{ + return true; +} + +static void mock_shutdown(struct tty_port *port) { } + +static const struct tty_port_operations mock_port_ops =3D { + .carrier_raised =3D mock_carrier_raised, + .shutdown =3D mock_shutdown, +}; + +/* --- Public helpers --- */ + +int tty_mock_register(struct tty_driver **out_drv, struct device *parent) +{ + struct tty_driver *drv; + struct device *dev; + int ret; + + spin_lock_init(&mock_state.lock); + + drv =3D tty_alloc_driver(1, TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_UNNUMBERED_NODE | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(drv)) + return PTR_ERR(drv); + + drv->driver_name =3D TTYMOCK_NAME; + drv->name =3D TTYMOCK_NAME; + drv->type =3D TTY_DRIVER_TYPE_SERIAL; + drv->subtype =3D SERIAL_TYPE_NORMAL; + drv->init_termios =3D tty_std_termios; + tty_set_operations(drv, &mock_ops); + + ret =3D tty_register_driver(drv); + if (ret) { + tty_driver_kref_put(drv); + return ret; + } + + tty_port_init(&mock_port); + mock_port.ops =3D &mock_port_ops; + + dev =3D tty_port_register_device(&mock_port, drv, 0, parent); + if (IS_ERR(dev)) { + ret =3D PTR_ERR(dev); + tty_unregister_driver(drv); + tty_driver_kref_put(drv); + tty_port_destroy(&mock_port); + return ret; + } + + if (out_drv) + *out_drv =3D drv; + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(tty_mock_register); + +void tty_mock_unregister(struct tty_driver *drv) +{ + if (!drv) + return; + + tty_port_unregister_device(&mock_port, drv, 0); + tty_unregister_driver(drv); + tty_driver_kref_put(drv); + tty_port_destroy(&mock_port); +} +EXPORT_SYMBOL_IF_KUNIT(tty_mock_unregister); + +struct tty_mock_stats tty_mock_get_stats(void) +{ + unsigned long flags; + struct tty_mock_stats state; + + spin_lock_irqsave(&mock_state.lock, flags); + state.total_writes =3D mock_state.total_writes; + state.total_bytes =3D mock_state.total_bytes; + state.last_write_len =3D mock_state.last_write_len; + spin_unlock_irqrestore(&mock_state.lock, flags); + + return state; +} +EXPORT_SYMBOL_IF_KUNIT(tty_mock_get_stats); + +void tty_mock_reset_stats(void) +{ + unsigned long flags; + + spin_lock_irqsave(&mock_state.lock, flags); + mock_state.total_writes =3D 0; + mock_state.total_bytes =3D 0; + mock_state.last_write_len =3D 0; + spin_unlock_irqrestore(&mock_state.lock, flags); +} +EXPORT_SYMBOL_IF_KUNIT(tty_mock_reset_stats); + +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/tests/tty_mock.h b/drivers/tty/tests/tty_mock.h new file mode 100644 index 0000000000000000000000000000000000000000..e61eeccc6181fc459d8db790b29= 350dbf3d9f588 --- /dev/null +++ b/drivers/tty/tests/tty_mock.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * TTY - Mock driver + * + * Copyright (c) 2025 Abhinav Saxena + * + */ + +#ifndef _TTY_MOCK_H +#define _TTY_MOCK_H + +#include +#include +#include + +/* Register a single-port mock tty driver and create device #0. */ +int tty_mock_register(struct tty_driver **out_drv, struct device *parent); +/* Tear down device, unregister driver and destroy port. */ +void tty_mock_unregister(struct tty_driver *drv); + +/* --- Stats available to KUnit tests --- */ +struct tty_mock_stats { + u64 total_writes; + u64 total_bytes; + u32 last_write_len; +}; + +/* Returns a snapshot of counters. */ +struct tty_mock_stats tty_mock_get_stats(void); + +/* Reset all statistics counters to zero. */ +void tty_mock_reset_stats(void); + +#endif /* _TTY_MOCK_H */ --=20 2.43.0 From nobody Fri Oct 3 18:10:16 2025 Received: from mail-pg1-f177.google.com (mail-pg1-f177.google.com [209.85.215.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 20BAE2F6581; Tue, 26 Aug 2025 22:51:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756248716; cv=none; b=gjiZy/vMdocBvsMpsHcn8FKy4gHV39YFLn45gxin7e7hZKDD/mmyvQGnKPbXnH+BrTEX+hlFlmxgEKBbcCnJU30ibgnrT1HesJPlVA2LOjQcdsQM8VV8D55TcZxkk3wWSPMKavBxlHf3dR9cg4MwdGZOdG52K5NRkFq7nmnwj9o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756248716; c=relaxed/simple; bh=JdXE8vYkTFnIcf9QUL8Bbqunld/9cGDUOO7aLLjXBeU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=h90BXqXoFyTSJfoJM5q6e0a+mKoiKpC9pgrTtAK4qwJpeslqSy4Iu7fa5hC9e6+/X13+BsBN3x6YTl39uDXHQDKW+HjrPBt7XPRjrFkpgMksyKMyIiBJYNsQ40J5A0DTKrLCyYR+vFXYm9FmN5N8LZmdQk3T+9VybWkIVIYKOAQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GPGdXlqf; arc=none smtp.client-ip=209.85.215.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GPGdXlqf" Received: by mail-pg1-f177.google.com with SMTP id 41be03b00d2f7-b4755f37c3eso5376762a12.3; Tue, 26 Aug 2025 15:51:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1756248713; x=1756853513; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=LHq0wOrjV9AurPX5OQC4kD0MhUkJbDMxr/dKfoG/qIc=; b=GPGdXlqfS7+y9UPy95X6+Qr5W6jCOVrKXcOEPnudc/D+oA0tRsTwsPbIWI95bpySwL XysjN6EeQ6vjWsgTeV5MBkNaBvlr+ZI8RRtth7gnWf115Nt92ieYD9cxs09dZm/c/s2W KXCl6zitnx8d46ief84de7qAPFi/hPNMDUXKEate06XP7xR5rCWnDyt7CrUfkCRQMXep 1EM2ZhdVJiqOXAlCI8di+vjniv7cY7ANGSsV2myDLxHAxmWRh17CNjwvuVvnHiBgADd4 8BIQpoAMS6Mc7+KOec5qHzVVJerpUTagoLXPnN3Yxz1lbS30R17Pwjgq/AYTklQxPZuc fPGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1756248713; x=1756853513; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=LHq0wOrjV9AurPX5OQC4kD0MhUkJbDMxr/dKfoG/qIc=; b=kSoVNfEQ28d1CO82QU7mhAOHK6JGp2LRGvZfZT68tWxLqVa0G7Nqnz4zFrDRhJBEgn 45MOYFzLrt6lKF46XU6nKkcC2sf8Dpi2LEg1Tul8TycdrSpKTX342PdhpCjejRdNAQm3 cWBYppkGrsZOF4gNc0wPmV3IGk6RxQUOOZIqTD9OOuGjnfgxgEDpRNEQsT67etT9h8CR OimTQ2GGoRUo5u1DE+DqaolB9+9TW/9Iv0ykCgG9xaiJ0O5RWmpWiss3MDE4GsbHkBII /FQ8PkLIzALh+5qV5JxEOFL2N8QCX4ikgfPQoRWx/IFwZ4AYv4gHyVKtd7/05p6hjVtp yCqw== X-Forwarded-Encrypted: i=1; AJvYcCUYTchQTr+SusTsCt12FrZlLnhJ49UYQ/wV7j9vN1xDe6aOI10B6h4/09u8tWwl/Ewve5CGlh3sv4EU2xGa@vger.kernel.org, AJvYcCVhKKSSqaztU3W5nIAX6LG0pbljek5q0u39cSvtLRWmQwxpV/eoGO2hDwhoFZKZAnRPqGiR16669o4aKQqTKS4=@vger.kernel.org X-Gm-Message-State: AOJu0YwikkevLsOj6osOAJ8+7AI016t7cYQrx26cFTYmIMnWWG+IZm72 fScpPPy+xBERGG39LwoeGX1ZPoSa3z60LpdytIAMLZWfClA7lvKDDqRd X-Gm-Gg: ASbGnct2woCpR5xQx2TcZFVlkRf9gypUJbb0TqRozDRjDX2qkjjIV+leOcvCKZW5E3F HsYGHv5diKw2lDO9l2OqO+94YYoFWCQRsQ9DkGEFSumpAuJ/1F3JBS8NUaMpkWumIl6ZkJn+GyF 7F1rJ+3g6wIlq4TjoeDPDdet+3xrosfjKu3u75sme3mqV/Q6kqzQk136BV+tWZqkridNOId5trS MNFC61pAiHdYx3Y2I45FV5Y1fmHElFgOI6SrIEE9qrD8SG32kA3LhEIedc3lXIq6YZ/yMfYGKOD SXxQobtdfWJOyE3V6DcL/KvUeSTdGK9eVETAU5Rh4H9H2syTDsde+OItUjTTsBse2a2vztqyW4w KsAbH/z//BN4Z90ZDqlpo X-Google-Smtp-Source: AGHT+IEr+9+07tQY/svF8vxrQjSuh6tfMO477xgIAefkyL5oX4CM+zCYiWD5GZXlBL0j65L4qV9sow== X-Received: by 2002:a17:902:da47:b0:246:cf6a:f009 with SMTP id d9443c01a7336-246cf6af466mr105265565ad.46.1756248713253; Tue, 26 Aug 2025 15:51:53 -0700 (PDT) Received: from [0.0.5.57] ([136.159.213.249]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-246688a601esm105340815ad.162.2025.08.26.15.51.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 Aug 2025 15:51:52 -0700 (PDT) From: Abhinav Saxena Date: Tue, 26 Aug 2025 16:51:34 -0600 Subject: [RFC PATCH 4/5] tty: Add KUnit tests for core TTY functionality Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250826-tty-tests-v1-4-e904a817df92@gmail.com> References: <20250826-tty-tests-v1-0-e904a817df92@gmail.com> In-Reply-To: <20250826-tty-tests-v1-0-e904a817df92@gmail.com> To: Greg Kroah-Hartman , Jiri Slaby , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt , Kees Cook Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, llvm@lists.linux.dev, linux-hardening@vger.kernel.org, Abhinav Saxena X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1756248706; l=9255; i=xandfury@gmail.com; s=20250614; h=from:subject:message-id; bh=JdXE8vYkTFnIcf9QUL8Bbqunld/9cGDUOO7aLLjXBeU=; b=WqwZ5Q8Yl82sL6CVZHhtWuxnm9v49DBwgCcVpn3exni0niQg2t1WdihaidhjM30HnO9J9OgTt fRlYMROK45eADhdp8gP3f9i6TEDiCGm84X9hkycUNzcpusN6cgaCfbN X-Developer-Key: i=xandfury@gmail.com; a=ed25519; pk=YN6w7WNet8skqvMWxhG5BlAmtd1SQmo8If6Mofh4k44= Add comprehensive KUnit tests covering fundamental TTY operations: - Driver registration and operation validation - Open/close lifecycle with proper cleanup - Write operations and buffer management - Flow control via write_room() and chars_in_buffer() - RX data injection via line discipline - Termios configuration for hardware parameters Tests exercise real kernel code paths using the mock driver to ensure TTY subsystem changes don't introduce regressions in core functionality. Signed-off-by: Abhinav Saxena Reviewed-by: Jiri Slaby --- drivers/tty/tests/test_tty_io_core.c | 249 +++++++++++++++++++++++++++++++= ++++ 1 file changed, 249 insertions(+) diff --git a/drivers/tty/tests/test_tty_io_core.c b/drivers/tty/tests/test_= tty_io_core.c new file mode 100644 index 0000000000000000000000000000000000000000..626160e6ed738d56575cd340b36= 62aaa94f46a0a --- /dev/null +++ b/drivers/tty/tests/test_tty_io_core.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Minimal KUnit tests for TTY core using the mock driver. + * + * Scope: + * - open/close via tty_port_* paths + * - write() returns cnt, write_room() is large, chars_in_buffer() =3D=3D= 0 + * - stats (writes, bytes, last len) observable via tty_mock_get_stats() + * - file_ops read functionality with various termios configurations + * - set_termios operations for practical use cases + * + * Keep this small and obvious=E2=80=94no driver-side buffering or timers. + * + * Copyright (c) 2025 Abhinav Saxena + */ + +#include +#include +#include +#include +#include + +#include "tty_test_helpers.h" +#include "tty_mock.h" /* mock driver API: register/unregister/sta= ts */ + +/* Single-port driver used across tests (minor 0). */ +static struct tty_driver *mock_driver; + +/* ---------- Per-test init: sanity only ---------- */ +static int tty_core_init(struct kunit *test) +{ + KUNIT_ASSERT_NOT_NULL(test, mock_driver); + + /* Reset mock statistics before each test */ + tty_mock_reset_stats(); + + if (mock_driver && mock_driver->ports) + KUNIT_EXPECT_NOT_NULL(test, mock_driver->ports[0]); + return 0; +} + +/* ---------- Tests ---------- */ + +/* + * Test: Verify mock driver has all required operations wired correctly. + * Expected: All mandatory ops present, ports array initialised, no NULL p= ointers. + */ +static void test_driver_ops_present(struct kunit *test) +{ + KUNIT_ASSERT_NOT_NULL(test, mock_driver); + + /* Basic ops presence and wiring checks via helper. */ + tty_test_assert_valid_ops(test, mock_driver); + KUNIT_EXPECT_NOT_NULL(test, mock_driver->ops->write); + KUNIT_EXPECT_NOT_NULL(test, mock_driver->ops->write_room); + KUNIT_EXPECT_NOT_NULL(test, mock_driver->ops->chars_in_buffer); + + if (mock_driver->ports) + KUNIT_EXPECT_NOT_NULL(test, mock_driver->ports[0]); +} + +/* + * Test: Basic TTY lifecycle (open/write/close). + * Expected: write() returns byte count, stats increment correctly, + * ldisc/port setup. + */ +static void test_basic_open_write_close(struct kunit *test) +{ + unsigned int idx =3D 0; + struct tty_test_fixture *fx; + const char *msg =3D "Hello, tty!\n"; + struct tty_mock_stats before, after; + ssize_t ret; + + fx =3D tty_test_create_fixture(test, mock_driver, idx); + KUNIT_ASSERT_NOT_NULL(test, fx); + KUNIT_ASSERT_EQ(test, tty_test_open(fx), 0); + KUNIT_ASSERT_TRUE(test, fx->opened); + + KUNIT_EXPECT_TRUE(test, + fx->tty && fx->tty->ldisc && fx->tty->ldisc->ops); + KUNIT_EXPECT_TRUE(test, !list_empty(&fx->tty->tty_files)); + KUNIT_EXPECT_NOT_NULL(test, fx->tty->port); + + before =3D tty_mock_get_stats(); + + ret =3D tty_test_write(fx, msg, strlen(msg)); + KUNIT_EXPECT_EQ(test, ret, (ssize_t)strlen(msg)); + + after =3D tty_mock_get_stats(); + + /* Test interface contract, not implementation details */ + KUNIT_EXPECT_GT(test, after.total_writes, before.total_writes); + KUNIT_EXPECT_GE(test, after.total_bytes, + before.total_bytes + strlen(msg)); + KUNIT_EXPECT_GT(test, after.last_write_len, 0u); + + KUNIT_EXPECT_EQ(test, tty_test_release(fx), 0); +} + +/* + * Test: write_room() and chars_in_buffer() consistency during operations. + * Expected: write_room() > 0, chars_in_buffer() =3D=3D 0, room unchanged = after + * writes. + */ +static void test_write_room_and_chars_in_buffer_invariants(struct kunit *t= est) +{ + unsigned int idx =3D 0; + struct tty_test_fixture *fx; + char buf[64]; + unsigned int room_before, room_after; + ssize_t ret; + + memset(buf, 'A', sizeof(buf)); + + fx =3D tty_test_create_fixture(test, mock_driver, idx); + KUNIT_ASSERT_NOT_NULL(test, fx); + KUNIT_ASSERT_EQ(test, tty_test_open(fx), 0); + + room_before =3D tty_write_room(fx->tty); + KUNIT_EXPECT_GT(test, room_before, 0u); + KUNIT_EXPECT_EQ(test, fx->tty->ops->chars_in_buffer(fx->tty), 0u); + + ret =3D tty_test_write(fx, buf, sizeof(buf)); + KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buf)); + + room_after =3D tty_write_room(fx->tty); + KUNIT_EXPECT_EQ(test, fx->tty->ops->chars_in_buffer(fx->tty), 0u); + KUNIT_EXPECT_GE(test, room_after, room_before); + + KUNIT_EXPECT_EQ(test, tty_test_release(fx), 0); +} + +/* + * Test: RX data injection via flip buffers if line discipline supports it. + * Expected: tty_test_simulate_rx() returns injected byte count, or test + * skipped. + */ +static void test_flip_rx_if_supported(struct kunit *test) +{ + unsigned int idx =3D 0; + struct tty_test_fixture *fx; + int rx_result; + + /* Raw byte buffer, not NUL-terminated */ + static const unsigned char rx_data[] =3D "rx-line\n"; + + fx =3D tty_test_create_fixture(test, mock_driver, idx); + KUNIT_ASSERT_NOT_NULL(test, fx); + KUNIT_ASSERT_EQ(test, tty_test_open(fx), 0); + + KUNIT_EXPECT_TRUE(test, + fx->tty && fx->tty->ldisc && fx->tty->ldisc->ops); + KUNIT_EXPECT_TRUE(test, !list_empty(&fx->tty->tty_files)); + KUNIT_EXPECT_NOT_NULL(test, fx->tty->port); + + if (tty_fx_supports_rx(fx)) { + rx_result =3D tty_test_simulate_rx(fx, rx_data, sizeof(rx_data)); + KUNIT_EXPECT_EQ(test, rx_result, (int)sizeof(rx_data)); + } else { + kunit_skip(test, + "ldisc does not support RX; skipping injection"); + } + + KUNIT_EXPECT_EQ(test, tty_test_release(fx), 0); +} + +/* + * Test: set_termios() for hardware settings (baud rate, character size, p= arity). + * Expected: c_cflag settings persist correctly, hardware parameters valid= ated. + */ +static void test_set_termios_baud_rate_and_character_size(struct kunit *te= st) +{ + unsigned int idx =3D 0; + struct tty_test_fixture *fx; + struct ktermios termios_before, termios_after; + + fx =3D tty_test_create_fixture(test, mock_driver, idx); + KUNIT_ASSERT_NOT_NULL(test, fx); + KUNIT_ASSERT_EQ(test, tty_test_open(fx), 0); + + /* Get initial termios */ + termios_before =3D fx->tty->termios; + + /* Test baud rate changes */ + termios_after =3D termios_before; + termios_after.c_cflag &=3D ~CBAUD; + termios_after.c_cflag |=3D B9600; + KUNIT_ASSERT_EQ(test, tty_test_set_termios(fx, &termios_after), 0); + KUNIT_EXPECT_EQ(test, fx->tty->termios.c_cflag & CBAUD, B9600); + + /* Test higher baud rate */ + termios_after.c_cflag &=3D ~CBAUD; + termios_after.c_cflag |=3D B115200; + KUNIT_ASSERT_EQ(test, tty_test_set_termios(fx, &termios_after), 0); + KUNIT_EXPECT_EQ(test, fx->tty->termios.c_cflag & CBAUD, B115200); + + /* Test character size changes */ + termios_after.c_cflag &=3D ~CSIZE; + termios_after.c_cflag |=3D CS7; /* 7-bit characters */ + KUNIT_ASSERT_EQ(test, tty_test_set_termios(fx, &termios_after), 0); + KUNIT_EXPECT_EQ(test, fx->tty->termios.c_cflag & CSIZE, CS7); + + termios_after.c_cflag &=3D ~CSIZE; + termios_after.c_cflag |=3D CS8; /* 8-bit characters */ + KUNIT_ASSERT_EQ(test, tty_test_set_termios(fx, &termios_after), 0); + KUNIT_EXPECT_EQ(test, fx->tty->termios.c_cflag & CSIZE, CS8); + + /* Test parity settings */ + termios_after.c_cflag |=3D PARENB; /* Enable parity */ + termios_after.c_cflag &=3D ~PARODD; /* Even parity */ + KUNIT_ASSERT_EQ(test, tty_test_set_termios(fx, &termios_after), 0); + KUNIT_EXPECT_TRUE(test, fx->tty->termios.c_cflag & PARENB); + KUNIT_EXPECT_FALSE(test, fx->tty->termios.c_cflag & PARODD); + + KUNIT_EXPECT_EQ(test, tty_test_release(fx), 0); +} + +/* ---------- Suite registration ---------- */ + +static int tty_core_suite_init(struct kunit_suite *suite) +{ + return tty_mock_register(&mock_driver, NULL); +} + +static void tty_core_suite_exit(struct kunit_suite *suite) +{ + tty_mock_unregister(mock_driver); + mock_driver =3D NULL; +} + +static struct kunit_case tty_core_cases[] =3D { + KUNIT_CASE(test_driver_ops_present), + KUNIT_CASE(test_basic_open_write_close), + KUNIT_CASE(test_write_room_and_chars_in_buffer_invariants), + KUNIT_CASE(test_flip_rx_if_supported), + KUNIT_CASE(test_set_termios_baud_rate_and_character_size), + {} +}; + +static struct kunit_suite tty_core_suite =3D { + .name =3D "tty_io_core", + .init =3D tty_core_init, + .suite_init =3D tty_core_suite_init, + .suite_exit =3D tty_core_suite_exit, + .test_cases =3D tty_core_cases, +}; + +kunit_test_suite(tty_core_suite); --=20 2.43.0 From nobody Fri Oct 3 18:10:16 2025 Received: from mail-pg1-f169.google.com (mail-pg1-f169.google.com [209.85.215.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7A4912F8BE7; Tue, 26 Aug 2025 22:51:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756248717; cv=none; b=dlXTqEezvy1ULwunUf8vOw/XUHLIyRcSpKZIbUnQVekItmNymPS1jqY+IGjUVD0FHY57XHGBV0hw1DpjURjEWpjsW1f1CvTmUa0NNmx9K+ovo5RzvLKna6Yzqi7D962sBGE9aX/pdJCUywcVL8SodLQwl8/poTfP91m/xjul9g0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1756248717; c=relaxed/simple; bh=2sCgtf0iJuHZoEJ+ZjgF8LGfcrJ2jnuz9b44H6Mk4Ks=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Jczd8bseR8U5u2uIu/x8MP2ngLX4DgY4jLgO4tgdrMzqj73LpFF2UM4sRnMncfZuT6mxNRs4xRSbfTw56pn0kxG/LUer7W4Wu+3Mc3DXbD+JxE+Jc/Hu7PAn/zjPEL01bYySgMDErT6riFKN/7SpT3LoKhGJRmVwRn/x2IeXA2w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=NcVgD9H2; arc=none smtp.client-ip=209.85.215.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="NcVgD9H2" Received: by mail-pg1-f169.google.com with SMTP id 41be03b00d2f7-b4c1aefa8deso1845490a12.0; Tue, 26 Aug 2025 15:51:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1756248714; x=1756853514; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=pFHzcEkMkKxjDsW7QyH+21adtRUMu8g3PdBU38OriB8=; b=NcVgD9H2oV+3W3WZgSxk5cF2Q2Vpz5ntRs7Ird924aZ7eg4iGktBEJ8prAAJ30+nDD U0bYB7TBC+2zGfk2Gba81Q5dN+D3yVoLMcB8Bi/dU31mQpcXuewcJjsKZgzML5z9vvcH 4dblovf26PwLh8qOTj1HYk1Ne3WdvhPanSQyiCrzLOOZKFzbsfAlgkoLeycmiMy6f3xq 2v9qgXFChHxzYcaNmtt4CGjPJ/tIU33sGroyCV/l8CaXAjrkwR2GqmS7kEZVBpvXAsT8 hdbGThnLefZGwde2l4p5G5wXFRVqiCnASvGHdnRmzwdrt8GzU7yoWM2XuTy5+AmYEQrX Pr7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1756248714; x=1756853514; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=pFHzcEkMkKxjDsW7QyH+21adtRUMu8g3PdBU38OriB8=; b=iiuIp22RtFJvslkRpn9799VMrMcxG/UoQEGiXr0bDoXFmTLHKAZao9hlqm+6zLSpu1 A03e4rv2L2I3zTCgzSFGaVAMCTTMQQfmvGqW+M92z53NYUmoXHuHPX8UnOtQ7RLaERQ9 nO5OJUk3oW+famptrocbF+BplFRVYnsndR9AwipGXpx8w3NbbDmHs7JBxNq+hKBaVu2Y Jv1SFzfu2fKR7W9+jem9LZMvA5aJXDueE50qlMkppf6KjWl+jCaXwsyxnvl4LkG6yDCY UKt7tlLtz6gRdtwiarkKZMXdxSgYUPXqGUACxyl8pjTsKOfih7Ntin8GTeuUAsX9r3Wn 8aFw== X-Forwarded-Encrypted: i=1; AJvYcCUIPdNAHxEGn6C9zNz/UKtBASMduwOUjczY6NpOPx9Jq1Lz5eyy6N2gZzA4x400+QCFDqp/I04O7wPT6E30KSw=@vger.kernel.org, AJvYcCWHFZ1naGJWILCI95bR+67f6qVhbRcKTP8qIDO588en2ugCi2pOd9eVVjX68FSW1TVDX1sNDJXasyuRSWCc@vger.kernel.org X-Gm-Message-State: AOJu0YwABRl/xxCPB4sMUSwXgQZkQpHKD/UdScTH6U4DAsITDbCUw8/I qMlxik9//RWRYAEqYiPUpy7/ufAuS/xObS49opJA9LEs3gEA/9ugErRg X-Gm-Gg: ASbGncv+c+BVFNaCsp+y2X3aEX1jZpVft9+10PXpw9QhFhIy2ILrYw+mprGISOoCur5 k6crXGl0SnZux7XiMVXxO5N4A/sFBKGvoFYYYPWZyAChEQInyslXDztW1L36wd4a6OYkqvUUYzH x/3cz/U+We3AfDR/zKyjKaliKTBct9p8TlEdo1MqLczhfRUmMMJ9UWPecQ/UPFrtj8s00o7gNdJ Io0pIefYWupPs0HDK82L6yIQVc1HBj8bOvNwOq5vl8slo7a4+Nro8hMZvsfrXcgdZ/YxGpM7kMj bXoCbiv3TA1uagcRJkPsc6kGg/Wqr7XGQNI6icUg3Ipmotv781ngXqNH3A3PwnFu8kGkq2XkGPw D+Szgg7wsg/RVKqaDyl7u X-Google-Smtp-Source: AGHT+IHBbRvLzIIWrINVvaClB1b0OPUk6BLcoAKkgULcCrNboGggfEcV+NS6zLTIGPlFc2xR7s/r5w== X-Received: by 2002:a17:903:2f50:b0:240:e9d:6c43 with SMTP id d9443c01a7336-2462efa3c78mr199299725ad.51.1756248714470; Tue, 26 Aug 2025 15:51:54 -0700 (PDT) Received: from [0.0.5.57] ([136.159.213.249]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-246688a601esm105340815ad.162.2025.08.26.15.51.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 26 Aug 2025 15:51:54 -0700 (PDT) From: Abhinav Saxena Date: Tue, 26 Aug 2025 16:51:35 -0600 Subject: [RFC PATCH 5/5] tty: Add KUnit tests for ttynull driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250826-tty-tests-v1-5-e904a817df92@gmail.com> References: <20250826-tty-tests-v1-0-e904a817df92@gmail.com> In-Reply-To: <20250826-tty-tests-v1-0-e904a817df92@gmail.com> To: Greg Kroah-Hartman , Jiri Slaby , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt , Kees Cook Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, llvm@lists.linux.dev, linux-hardening@vger.kernel.org, Abhinav Saxena X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1756248707; l=6787; i=xandfury@gmail.com; s=20250614; h=from:subject:message-id; bh=2sCgtf0iJuHZoEJ+ZjgF8LGfcrJ2jnuz9b44H6Mk4Ks=; b=7wonk4jVg8NsrTTBrfr6ESAy6WZFree5W8bJr7L4QLz5S5/8f6osUqGimZhjCM+tfxdsy0G/x 5lMJhbjAnpQAT+KhMfWgRJQVpiNA4sbrEiobc0DSKSK13IqYtS0ervS X-Developer-Key: i=xandfury@gmail.com; a=ed25519; pk=YN6w7WNet8skqvMWxhG5BlAmtd1SQmo8If6Mofh4k44= Add targeted tests for the TTY null driver covering its data sink behavior and driver characteristics. Tests verify that ttynull properly discards written data while maintaining standard TTY semantics for applications requiring TTY interfaces without caring about output. The tests are integrated directly into ttynull.c when CONFIG_TTY_KUNIT_NULL_TTY_TESTS=3Dy to test the actual driver implementation. Signed-off-by: Abhinav Saxena Reviewed-by: Jiri Slaby --- drivers/tty/tests/test_ttynull.c | 163 +++++++++++++++++++++++++++++++++++= ++++ drivers/tty/ttynull.c | 5 ++ 2 files changed, 168 insertions(+) diff --git a/drivers/tty/tests/test_ttynull.c b/drivers/tty/tests/test_ttyn= ull.c new file mode 100644 index 0000000000000000000000000000000000000000..c062d69bd5d5975ab84442a8342= 6a1d44440b0a6 --- /dev/null +++ b/drivers/tty/tests/test_ttynull.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for the TTY null driver + * + * Tests for the ttynull driver covering basic lifecycle and sink behavior. + * The ttynull driver acts as a data sink, discarding all written data + * while providing minimal overhead for applications that need a TTY + * but don't care about the output. + * + * Copyright (c) 2025 Abhinav Saxena + * + */ + +#include +#include +#include +#include +#include + +#include "tests/tty_test_helpers.h" + +/** + * test_ttynull_write_sink - Verify ttynull acts as data sink + * @test: KUnit test context + * + * ttynull should accept all write data and discard it silently. + * This tests the core functionality of the null TTY driver. + */ +static void test_ttynull_write_sink(struct kunit *test) +{ + struct tty_driver *drv =3D ttynull_driver; + struct tty_test_fixture *fx; + const char *msg =3D "test data; discard me"; + unsigned int room; + ssize_t write_result; + + fx =3D tty_test_create_fixture(test, drv, 0); + KUNIT_ASSERT_NOT_NULL(test, fx); + + KUNIT_ASSERT_EQ(test, tty_test_open(fx), 0); + KUNIT_ASSERT_TRUE(test, fx->opened); + + /* Verify TTY is properly initialized */ + KUNIT_EXPECT_NOT_NULL(test, fx->tty); + KUNIT_EXPECT_NOT_NULL(test, fx->tty->ldisc); + KUNIT_EXPECT_TRUE(test, !list_empty(&fx->tty->tty_files)); + + /* Check initial write room - should be available */ + room =3D tty_test_get_write_room(fx); + KUNIT_EXPECT_GT(test, room, 0U); + + /* Write data - should be completely accepted */ + KUNIT_ASSERT_EQ(test, tty_test_write_all(fx, msg, strlen(msg)), 0); + + /* ttynull discards writes; buffer should remain empty */ + KUNIT_EXPECT_EQ(test, tty_test_get_chars_in_buffer(fx), 0U); + + /* Write room should remain available for a sink */ + room =3D tty_test_get_write_room(fx); + KUNIT_EXPECT_GT(test, room, 0U); + + /* ttynull should accept all data */ + write_result =3D tty_test_write(fx, msg, strlen(msg)); + KUNIT_EXPECT_EQ(test, write_result, strlen(msg)); + + /* Multiple writes should all succeed */ + write_result =3D tty_test_write(fx, msg, strlen(msg)); + KUNIT_EXPECT_EQ(test, write_result, strlen(msg)); + + /* + * TODO: Simulate hangup condition making subsequent writes fail + * For now, just release. + */ + KUNIT_ASSERT_EQ(test, tty_test_release(fx), 0); +} + +/** + * test_ttynull_read_behavior - Verify read behavior on null device + * @test: KUnit test context + * + * While ttynull technically supports read (via N_TTY), reading should + * behave predictably (likely EOF or blocking). + */ +static void test_ttynull_read_behavior(struct kunit *test) +{ + struct tty_driver *drv =3D ttynull_driver; + struct tty_test_fixture *fx; + struct tty_ldisc *ld; + /* char read_buffer[128]; */ + /* ssize_t bytes_read; */ + + fx =3D tty_test_create_fixture(test, drv, 0); + KUNIT_ASSERT_NOT_NULL(test, fx); + + KUNIT_ASSERT_EQ(test, tty_test_open(fx), 0); + KUNIT_ASSERT_TRUE(test, fx->opened); + + ld =3D tty_ldisc_ref(fx->tty); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ld); + KUNIT_ASSERT_TRUE(test, ld->ops && ld->ops->read); + tty_ldisc_deref(ld); + + /* + * Reading from ttynull should behave consistently. + * Depending on implementation, this might: + * - Return 0 (EOF) + * - Block (if no data available) + * - Return -EAGAIN (if non-blocking) + */ + KUNIT_ASSERT_NOT_NULL(test, fx->tty->disc_data); + /* bytes_read =3D tty_test_read(fx, read_buffer, sizeof(read_buffer)); */ + + /* + * Document the expected behavior + * - adjust based on actual ttynull implementation + */ + /* KUNIT_EXPECT_GE(test, bytes_read, 0); /\* Should not return error *\/ = */ + + KUNIT_ASSERT_EQ(test, tty_test_release(fx), 0); +} + +/** + * test_ttynull_driver_properties - Verify driver characteristics + * @test: KUnit test context + * + * Test the driver's static properties and configuration. Also, ensure that + * it implements the required file_operation ops. + */ +static void test_ttynull_driver_properties(struct kunit *test) +{ + struct tty_driver *drv =3D ttynull_driver; + + KUNIT_ASSERT_NOT_NULL(test, drv); + + /* Verify driver identification */ + KUNIT_EXPECT_STREQ(test, drv->driver_name, "ttynull"); + KUNIT_EXPECT_STREQ(test, drv->name, "ttynull"); + + /* Ensure that driver implements the required ops. */ + KUNIT_ASSERT_NOT_NULL(test, drv); + tty_test_assert_valid_ops(test, drv); + + /* Verify driver type */ + KUNIT_EXPECT_EQ(test, drv->type, TTY_DRIVER_TYPE_CONSOLE); + + /* Verify driver flags */ + KUNIT_EXPECT_TRUE(test, drv->flags & TTY_DRIVER_REAL_RAW); + KUNIT_EXPECT_TRUE(test, drv->flags & TTY_DRIVER_RESET_TERMIOS); +} + +static struct kunit_case ttynull_test_cases[] =3D { + KUNIT_CASE(test_ttynull_write_sink), + KUNIT_CASE(test_ttynull_read_behavior), + KUNIT_CASE(test_ttynull_driver_properties), + {} +}; + +static struct kunit_suite ttynull_test_suite =3D { + .name =3D "ttynull", + .test_cases =3D ttynull_test_cases, +}; + +kunit_test_suite(ttynull_test_suite); diff --git a/drivers/tty/ttynull.c b/drivers/tty/ttynull.c index 6b2f7208b564b659bf7faa4113541fcea7ec6ac0..baad9f0e3d27d97409a571e8da9= 53384b7c64891 100644 --- a/drivers/tty/ttynull.c +++ b/drivers/tty/ttynull.c @@ -6,6 +6,7 @@ * Copyright (C) 2010 Samo Pogacnik */ =20 +#include #include #include #include @@ -106,5 +107,9 @@ static void __exit ttynull_exit(void) module_init(ttynull_init); module_exit(ttynull_exit); =20 +#ifdef CONFIG_TTY_KUNIT_NULL_TTY_TESTS +#include "tests/test_ttynull.c" +#endif /* CONFIG_TTY_KUNIT_TTYNULL_TESTS */ + MODULE_DESCRIPTION("NULL TTY driver"); MODULE_LICENSE("GPL v2"); --=20 2.43.0