Add some basic sanity-check tests for the fat_checksum() function and the fat_time_unix2fat() and fat_time_fat2unix() functions. These unit tests verify these functions return correct output for a number of test inputs.
These tests were inspored by -- and serve a similar purpose to -- the timestamp parsing KUnit tests in ext4[1].
Note that, unlike fat_time_unix2fat, fat_time_fat2unix wasn't previously exported, so this patch exports it as well. This is required for the case where we're building the fat and fat_test as modules.
[1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/e...
Signed-off-by: David Gow davidgow@google.com Acked-by: OGAWA Hirofumi hirofumi@mail.parknet.co.jp ---
Changes since v1: https://lore.kernel.org/linux-kselftest/20201017064107.375174-1-davidgow@goo... - Now export fat_time_fat2unix() so that the test can access it when built as a module.
fs/fat/Kconfig | 13 +++ fs/fat/Makefile | 2 + fs/fat/fat_test.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fat/misc.c | 1 + 4 files changed, 213 insertions(+) create mode 100644 fs/fat/fat_test.c
diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig index 66532a71e8fd..fdef03b79c69 100644 --- a/fs/fat/Kconfig +++ b/fs/fat/Kconfig @@ -115,3 +115,16 @@ config FAT_DEFAULT_UTF8 Say Y if you use UTF-8 encoding for file names, N otherwise.
See file:Documentation/filesystems/vfat.rst for more information. + +config FAT_KUNIT_TEST + tristate "Unit Tests for FAT filesystems" if !KUNIT_ALL_TESTS + select FAT_FS + depends on KUNIT + default KUNIT_ALL_TESTS + help + This builds the FAT KUnit tests + + For more information on KUnit and unit tests in general, please refer + to the KUnit documentation in Documentation/dev-tools/kunit + + If unsure, say N diff --git a/fs/fat/Makefile b/fs/fat/Makefile index 70645ce2f7fc..2b034112690d 100644 --- a/fs/fat/Makefile +++ b/fs/fat/Makefile @@ -10,3 +10,5 @@ obj-$(CONFIG_MSDOS_FS) += msdos.o fat-y := cache.o dir.o fatent.o file.o inode.o misc.o nfs.o vfat-y := namei_vfat.o msdos-y := namei_msdos.o + +obj-$(CONFIG_FAT_KUNIT_TEST) += fat_test.o diff --git a/fs/fat/fat_test.c b/fs/fat/fat_test.c new file mode 100644 index 000000000000..c1b4348b9b3b --- /dev/null +++ b/fs/fat/fat_test.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for FAT filesystems. + * + * Copyright (C) 2020 Google LLC. + * Author: David Gow davidgow@google.com + */ + +#include <kunit/test.h> + +#include "fat.h" + +static void fat_checksum_test(struct kunit *test) +{ + /* With no extension. */ + KUNIT_EXPECT_EQ(test, fat_checksum("VMLINUX "), 44); + /* With 3-letter extension. */ + KUNIT_EXPECT_EQ(test, fat_checksum("README TXT"), 115); + /* With short (1-letter) extension. */ + KUNIT_EXPECT_EQ(test, fat_checksum("ABCDEFGHA "), 98); +} + + +struct fat_timestamp_testcase { + const char *name; + struct timespec64 ts; + __le16 time; + __le16 date; + u8 cs; + int time_offset; +}; + +const static struct fat_timestamp_testcase time_test_cases[] = { + { + .name = "Earliest possible UTC (1980-01-01 00:00:00)", + .ts = {.tv_sec = 315532800LL, .tv_nsec = 0L}, + .time = 0, + .date = 33, + .cs = 0, + .time_offset = 0, + }, + { + .name = "Latest possible UTC (2107-12-31 23:59:58)", + .ts = {.tv_sec = 4354819198LL, .tv_nsec = 0L}, + .time = 49021, + .date = 65439, + .cs = 0, + .time_offset = 0, + }, + { + .name = "Earliest possible (UTC-11) (== 1979-12-31 13:00:00 UTC)", + .ts = {.tv_sec = 315493200LL, .tv_nsec = 0L}, + .time = 0, + .date = 33, + .cs = 0, + .time_offset = 11 * 60, + }, + { + .name = "Latest possible (UTC+11) (== 2108-01-01 10:59:58 UTC)", + .ts = {.tv_sec = 4354858798LL, .tv_nsec = 0L}, + .time = 49021, + .date = 65439, + .cs = 0, + .time_offset = -11 * 60, + }, + { + .name = "Leap Day / Year (1996-02-29 00:00:00)", + .ts = {.tv_sec = 825552000LL, .tv_nsec = 0L}, + .time = 0, + .date = 8285, + .cs = 0, + .time_offset = 0, + }, + { + .name = "Year 2000 is leap year (2000-02-29 00:00:00)", + .ts = {.tv_sec = 951782400LL, .tv_nsec = 0L}, + .time = 0, + .date = 10333, + .cs = 0, + .time_offset = 0, + }, + { + .name = "Year 2100 not leap year (2100-03-01 00:00:00)", + .ts = {.tv_sec = 4107542400LL, .tv_nsec = 0L}, + .time = 0, + .date = 61537, + .cs = 0, + .time_offset = 0, + }, + { + .name = "Leap year + timezone UTC+1 (== 2004-02-29 00:30:00 UTC)", + .ts = {.tv_sec = 1078014600LL, .tv_nsec = 0L}, + .time = 48064, + .date = 12380, + .cs = 0, + .time_offset = -60, + }, + { + .name = "Leap year + timezone UTC-1 (== 2004-02-29 23:30:00 UTC)", + .ts = {.tv_sec = 1078097400LL, .tv_nsec = 0L}, + .time = 960, + .date = 12385, + .cs = 0, + .time_offset = 60, + }, + { + .name = "VFAT odd-second resolution (1999-12-31 23:59:59)", + .ts = {.tv_sec = 946684799LL, .tv_nsec = 0L}, + .time = 49021, + .date = 10143, + .cs = 100, + .time_offset = 0, + }, + { + .name = "VFAT 10ms resolution (1980-01-01 00:00:00:0010)", + .ts = {.tv_sec = 315532800LL, .tv_nsec = 10000000L}, + .time = 0, + .date = 33, + .cs = 1, + .time_offset = 0, + }, +}; + +static void fat_time_fat2unix_test(struct kunit *test) +{ + struct msdos_sb_info fake_sb; + int i; + struct timespec64 ts; + + for (i = 0; i < ARRAY_SIZE(time_test_cases); ++i) { + fake_sb.options.tz_set = 1; + fake_sb.options.time_offset = time_test_cases[i].time_offset; + + fat_time_fat2unix(&fake_sb, &ts, + time_test_cases[i].time, + time_test_cases[i].date, + time_test_cases[i].cs); + KUNIT_EXPECT_EQ_MSG(test, + time_test_cases[i].ts.tv_sec, + ts.tv_sec, + "Timestamp mismatch (seconds) in case "%s"\n", + time_test_cases[i].name); + KUNIT_EXPECT_EQ_MSG(test, + time_test_cases[i].ts.tv_nsec, + ts.tv_nsec, + "Timestamp mismatch (nanoseconds) in case "%s"\n", + time_test_cases[i].name); + } +} + +static void fat_time_unix2fat_test(struct kunit *test) +{ + struct msdos_sb_info fake_sb; + int i; + __le16 date, time; + u8 cs; + + for (i = 0; i < ARRAY_SIZE(time_test_cases); ++i) { + fake_sb.options.tz_set = 1; + fake_sb.options.time_offset = time_test_cases[i].time_offset; + + fat_time_unix2fat(&fake_sb, &time_test_cases[i].ts, + &time, &date, &cs); + KUNIT_EXPECT_EQ_MSG(test, + time_test_cases[i].time, + time, + "Time mismatch in case "%s"\n", + time_test_cases[i].name); + KUNIT_EXPECT_EQ_MSG(test, + time_test_cases[i].date, + date, + "Date mismatch in case "%s"\n", + time_test_cases[i].name); + KUNIT_EXPECT_EQ_MSG(test, + time_test_cases[i].cs, + cs, + "Centisecond mismatch in case "%s"\n", + time_test_cases[i].name); + } +} + +static struct kunit_case fat_test_cases[] = { + KUNIT_CASE(fat_checksum_test), + KUNIT_CASE(fat_time_fat2unix_test), + KUNIT_CASE(fat_time_unix2fat_test), + {}, +}; + +static struct kunit_suite fat_test_suite = { + .name = "fat_test", + .test_cases = fat_test_cases, +}; + +kunit_test_suites(&fat_test_suite); + +MODULE_LICENSE("GPL v2"); + diff --git a/fs/fat/misc.c b/fs/fat/misc.c index f1b2a1fc2a6a..445ad3542e74 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -229,6 +229,7 @@ void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts, ts->tv_nsec = 0; } } +EXPORT_SYMBOL_GPL(fat_time_fat2unix);
/* Convert linear UNIX date to a FAT time/date pair. */ void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts,
David Gow davidgow@google.com writes:
diff --git a/fs/fat/misc.c b/fs/fat/misc.c index f1b2a1fc2a6a..445ad3542e74 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -229,6 +229,7 @@ void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts, ts->tv_nsec = 0; } } +EXPORT_SYMBOL_GPL(fat_time_fat2unix);
Hm, can this export only if FAT_KUNIT_TEST is builtin or module (maybe #if IS_ENABLED(...))? And #if will also be worked as the comment too.
/* Convert linear UNIX date to a FAT time/date pair. */ void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts,
On Tue, Oct 20, 2020 at 2:51 PM OGAWA Hirofumi hirofumi@mail.parknet.co.jp wrote:
David Gow davidgow@google.com writes:
diff --git a/fs/fat/misc.c b/fs/fat/misc.c index f1b2a1fc2a6a..445ad3542e74 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -229,6 +229,7 @@ void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts, ts->tv_nsec = 0; } } +EXPORT_SYMBOL_GPL(fat_time_fat2unix);
Hm, can this export only if FAT_KUNIT_TEST is builtin or module (maybe #if IS_ENABLED(...))? And #if will also be worked as the comment too.
That's possible, but I'd prefer to export it unconditionally for two reasons: 1. It'd make it possible to build the fat_test module without having to rebuild the whole kernel/fat. 2. It'd be consistent with fat_time_unix2fat(), which is exported for use in vfat/msdos anyway.
Neither of those are dealbreakers, though, so if you'd still prefer this to be behind an ifdef, I'll change it.
-- David
David Gow davidgow@google.com writes:
Hm, can this export only if FAT_KUNIT_TEST is builtin or module (maybe #if IS_ENABLED(...))? And #if will also be worked as the comment too.
That's possible, but I'd prefer to export it unconditionally for two reasons:
- It'd make it possible to build the fat_test module without having
to rebuild the whole kernel/fat. 2. It'd be consistent with fat_time_unix2fat(), which is exported for use in vfat/msdos anyway.
Neither of those are dealbreakers, though, so if you'd still prefer this to be behind an ifdef, I'll change it.
OK. If nobody complain, let's export. However, then, can you add the comment instead of ifdef to mark for kunit?
Thanks.
linux-kselftest-mirror@lists.linaro.org