diff options
author | Gurjant Kalsi <me@gurjantkalsi.com> | 2015-11-10 21:05:07 -0800 |
---|---|---|
committer | Gurjant Kalsi <me@gurjantkalsi.com> | 2015-11-10 21:05:07 -0800 |
commit | 202ebfcee7352e05fef055cc6a71087ed3f6057b (patch) | |
tree | 3c451e7c6a8b7e14e1b51a2edcee3fb24e0fdfbe /lib | |
parent | 08ad97fe151fa7c6670e056cbe731119b1983e43 (diff) | |
download | common-202ebfcee7352e05fef055cc6a71087ed3f6057b.tar.gz |
[fs][spifs][test] Add unit tests for the spifs implementation.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/fs/spifs/test/rules.mk | 8 | ||||
-rw-r--r-- | lib/fs/spifs/test/spifstest.c | 546 |
2 files changed, 554 insertions, 0 deletions
diff --git a/lib/fs/spifs/test/rules.mk b/lib/fs/spifs/test/rules.mk new file mode 100644 index 00000000..344c7b5f --- /dev/null +++ b/lib/fs/spifs/test/rules.mk @@ -0,0 +1,8 @@ +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS += \ + $(LOCAL_DIR)/spifstest.c + +include make/module.mk diff --git a/lib/fs/spifs/test/spifstest.c b/lib/fs/spifs/test/spifstest.c new file mode 100644 index 00000000..7d0d46ac --- /dev/null +++ b/lib/fs/spifs/test/spifstest.c @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2015 Gurjant Kalsi <me@gurjantkalsi.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if LK_DEBUGLEVEL > 1 + +#include <string.h> +#include <err.h> +#include <stdlib.h> + +#include <lib/bio.h> +#include <lib/console.h> +#include <lib/fs/spifs.h> + +#define FS_NAME "spifs" +#define MNT_PATH "/s" +#define TEST_FILE_PATH "/s/test" +#define TEST_PATH_MAX_SIZE 16 + +typedef bool(*test_func)(const char *); + +typedef struct { + test_func func; + const char* name; + uint32_t toc_pages; +} test; + +bool test_empty_after_format(const char *); +bool test_double_create_file(const char *); +bool test_write_read_normal(const char *); +bool test_write_past_eof(const char *); +bool test_full_toc(const char *); +bool test_full_fs(const char *); +bool test_write_past_end_of_capacity(const char *); +bool test_rm_reclaim(const char *); +bool test_corrupt_toc(const char *); + +static test tests[] = { + {&test_empty_after_format, "Test no files in ToC after format.", 1}, + {&test_write_read_normal, "Test the normal read/write file paths.", 1}, + {&test_double_create_file, "Test file cannot be created if it already exists.", 1}, + {&test_write_past_eof, "Test that file can grow up to capacity.", 1}, + {&test_full_toc, "Test that files cannot be created once the ToC is full.", 2}, + {&test_full_fs, "Test that files cannot be created once the device is full.", 1}, + {&test_rm_reclaim, "Test that files can be deleted and that used space is reclaimed.", 1}, + {&test_write_past_end_of_capacity, "Test that we cannot write past the capacity of a file.", 1}, + {&test_corrupt_toc, "Test that FS can be mounted with one corrupt ToC.", 1}, +}; + +bool test_setup(const char *dev_name, uint32_t toc_pages) +{ + spifs_format_args_t args = { + .toc_pages = toc_pages, + }; + + status_t res = fs_format_device(FS_NAME, dev_name, (void*)&args); + if (res != NO_ERROR) { + printf("spifs_format failed dev = %s, toc_pages = %u, retcode = %d\n", + dev_name, toc_pages, res); + return false; + } + + res = fs_mount(MNT_PATH, FS_NAME, dev_name); + if (res != NO_ERROR) { + printf("fs_mount failed path = %s, fs name = %s, dev name = %s," + " retcode = %d\n", MNT_PATH, FS_NAME, dev_name, res); + return false; + } + + return true; +} + +bool test_teardown(void) +{ + if (fs_unmount(MNT_PATH) != NO_ERROR) { + printf("Unmount failed\n"); + return false; + } + + return true;; +} + +bool test_empty_after_format(const char *dev_name) +{ + dirhandle *dhandle; + status_t err = fs_open_dir(MNT_PATH, &dhandle); + if (err != NO_ERROR) { + return false; + } + + struct dirent ent; + if (fs_read_dir(dhandle, &ent) >= 0) { + fs_close_dir(dhandle); + return false; + } + + fs_close_dir(dhandle); + return true; +} + +bool test_double_create_file(const char *dev_name) +{ + status_t status; + + struct dirent *ent = malloc(sizeof(*ent)); + size_t num_files = 0; + + filehandle *handle; + status = fs_create_file(TEST_FILE_PATH, &handle, 10); + if (status != NO_ERROR) { + goto err; + } + fs_close_file(handle); + + filehandle *duphandle; + status = fs_create_file(TEST_FILE_PATH, &duphandle, 20); + if (status != ERR_ALREADY_EXISTS) { + goto err; + } + + dirhandle *dhandle; + status = fs_open_dir(MNT_PATH, &dhandle); + if (status != NO_ERROR) { + goto err; + } + + while ((status = fs_read_dir(dhandle, ent)) >= 0) { + num_files++; + } + + status = NO_ERROR; + + fs_close_dir(dhandle); + + +err: + free(ent); + + return status == NO_ERROR ? num_files == 1 : false; +} + +bool test_write_read_normal(const char *dev_name) +{ + char test_message[] = "spifs test"; + char test_buf[sizeof(test_message)]; + + bdev_t *dev = bio_open(dev_name); + if (!dev) { + return false; + } + uint8_t erase_byte = dev->erase_byte; + bio_close(dev); + + filehandle *handle; + status_t status = + fs_create_file(TEST_FILE_PATH, &handle, sizeof(test_message)); + if (status != NO_ERROR) { + return false; + } + + ssize_t bytes; + + // New files should be initialized to 'erase_byte' + bytes = fs_read_file(handle, test_buf, 0, sizeof(test_buf)); + if (bytes != sizeof(test_buf)) { + return false; + } + + for (size_t i = 0; i < sizeof(test_buf); i++) { + if (test_buf[i] != erase_byte) { + return false; + } + } + + bytes = fs_write_file(handle, test_message, 0, sizeof(test_message)); + if (bytes != sizeof(test_message)) { + return false; + } + + bytes = fs_read_file(handle, test_buf, 0, sizeof(test_buf)); + if (bytes != sizeof(test_buf)) { + return false; + } + + status = fs_close_file(handle); + if (status != NO_ERROR) { + return false; + } + + return strncmp(test_message, test_buf, sizeof(test_message)) == 0; +} + +bool test_write_past_eof(const char *dev_name) +{ + char test_message[] = "spifs test"; + + // Create a 0 length file. + filehandle *handle; + status_t status = + fs_create_file(TEST_FILE_PATH, &handle, 0); + if (status != NO_ERROR) { + return false; + } + + ssize_t bytes = fs_write_file(handle, test_message, 0, sizeof(test_message)); + if (bytes != sizeof(test_message)) { + return false; + } + + // Make sure the file grows. + struct file_stat stat; + fs_stat_file(handle, &stat); + if (stat.is_dir != false && stat.size != sizeof(test_message)) { + return false; + } + + fs_close_file(handle); + + return true; +} + +bool test_full_toc(const char *dev_name) +{ + struct fs_stat stat; + + fs_stat_fs(MNT_PATH, &stat); + + char test_file_name[TEST_PATH_MAX_SIZE]; + + filehandle *handle; + for (size_t i = 0; i < stat.free_inodes; i++) { + memset(test_file_name, 0, TEST_PATH_MAX_SIZE); + + char filenum[] = "000"; + filenum[0] += i / 100; + filenum[1] += (i / 10) % 10; + filenum[2] += i % 10; + + strncat(test_file_name, MNT_PATH, strlen(MNT_PATH)); + strncat(test_file_name, "/", 1); + strncat(test_file_name, filenum, strlen(filenum)); + + status_t status = + fs_create_file(test_file_name, &handle, 1); + + if (status != NO_ERROR) { + return false; + } + + fs_close_file(handle); + } + + // There shouldn't be enough space for this file since we've exhausted all + // the inodes. + + status_t status = fs_create_file(TEST_FILE_PATH, &handle, 1); + if (status != ERR_TOO_BIG) { + return false; + } + + return true; +} + +bool test_rm_reclaim(const char *dev_name) +{ + // Create some number of files that's a power of 2; + size_t n_files = 4; + + struct fs_stat stat; + + fs_stat_fs(MNT_PATH, &stat); + + size_t file_size = stat.free_space / (n_files + 1); + + char test_file_name[TEST_PATH_MAX_SIZE]; + + filehandle *handle; + for (size_t i = 0; i < n_files; i++) { + memset(test_file_name, 0, TEST_PATH_MAX_SIZE); + + char filenum[] = "000"; + filenum[0] += i / 100; + filenum[1] += (i / 10) % 10; + filenum[2] += i % 10; + + strcat(test_file_name, MNT_PATH); + strcat(test_file_name, filenum); + + status_t status = + fs_create_file(test_file_name, &handle, file_size); + + if (status != NO_ERROR) { + return false; + } + + fs_close_file(handle); + } + + // Try to create a new Big file. + char filename[] = "BIGFILE"; + memset(test_file_name, 0, TEST_PATH_MAX_SIZE); + strcat(test_file_name, MNT_PATH); + strcat(test_file_name, filename); + + status_t status; + + // This should fail because there's no more space. + fs_stat_fs(MNT_PATH, &stat); + status = fs_create_file(test_file_name, &handle, stat.free_space + 1); + if (status != ERR_TOO_BIG) { + return false; + } + + // Delete an existing file to make space for the new file. + char existing_filename[] = "001"; + memset(test_file_name, 0, TEST_PATH_MAX_SIZE); + strcat(test_file_name, MNT_PATH); + strcat(test_file_name, existing_filename); + + status = fs_remove_file(test_file_name); + if (status != NO_ERROR) { + return false; + } + + + // Now this should go through because we've reclaimed the space. + status = fs_create_file(test_file_name, &handle, stat.free_space + 1); + if (status != NO_ERROR) { + return false; + } + + fs_close_file(handle); + return true; +} + +bool test_full_fs(const char *dev_name) +{ + struct fs_stat stat; + + fs_stat_fs(MNT_PATH, &stat); + + char second_file_path[TEST_PATH_MAX_SIZE]; + memset(second_file_path, 0, TEST_PATH_MAX_SIZE); + strcpy(second_file_path, MNT_PATH); + strcat(second_file_path, "/fail"); + + filehandle *handle; + status_t status = fs_create_file(TEST_FILE_PATH, &handle, stat.free_space); + if (status != NO_ERROR) { + return false; + } + + fs_close_file(handle); + + // There shouldn't be enough space for this file since we've used all the + // space. + status = fs_create_file(second_file_path, &handle, 1); + if (status != ERR_TOO_BIG) { + return false; + } + + return true; +} + +bool test_write_past_end_of_capacity(const char *dev_name) +{ + filehandle *handle; + status_t status = fs_create_file(TEST_FILE_PATH, &handle, 0); + if (status != NO_ERROR) { + return false; + } + + struct file_stat stat; + status = fs_stat_file(handle, &stat); + if (status != NO_ERROR) { + goto finish; + } + + // We shouldn't be able to write past the capacity of a file. + char buf[1]; + status = fs_write_file(handle, buf, stat.capacity, 1); + if (status == ERR_OUT_OF_RANGE) { + status = NO_ERROR; + } else { + status = ERR_IO; + } + +finish: + fs_close_file(handle); + return status == NO_ERROR; +} + +bool test_corrupt_toc(const char *dev_name) +{ + // Create a zero byte file. + filehandle *handle; + status_t status = fs_create_file(TEST_FILE_PATH, &handle, 0); + if (status != NO_ERROR) { + return false; + } + + // Grow the file to one byte. This should trigger a ToC flush. Now the + // ToC record for this file should exist in both ToCs. Therefore corrupting + // either of the ToCs will still yield this file readable. + char buf[1] = { 'a' }; + status = fs_write_file(handle, buf, 0, 1); + if (status != 1) { + return false; + } + + status = fs_close_file(handle); + if (status != NO_ERROR) { + return false; + } + + status = fs_unmount(MNT_PATH); + if (status != NO_ERROR) { + return false; + } + + // Now we're going to manually corrupt one of the ToCs + bdev_t *dev = bio_open(dev_name); + if (!dev) { + return false; + } + + // Directly write 0s to the block that contains the front-ToC. + size_t block_size = dev->block_size; + uint8_t *block_buf = memalign(CACHE_LINE, block_size); + if (!block_buf) { + return false; + } + memset(block_buf, 0, block_size); + + ssize_t bytes = bio_write_block(dev, block_buf, 0, 1); + + free(block_buf); + + bio_close(dev); + + if (bytes != (ssize_t)block_size) { + return false; + } + + // Mount the FS again and make sure that the file we created is still there. + status = fs_mount(MNT_PATH, FS_NAME, dev_name); + if (status != NO_ERROR) { + return false; + } + + status = fs_open_file(TEST_FILE_PATH, &handle); + if (status != NO_ERROR) { + return false; + } + + struct file_stat stat; + status = fs_stat_file(handle, &stat); + if (status != NO_ERROR) { + return false; + } + + status = fs_close_file(handle); + if (status != NO_ERROR) { + return false; + } + + return true; +} + + +static int cmd_spifs(int argc, const cmd_args *argv) +{ + if (argc < 3) { +notenoughargs: + printf("not enough arguments:\n"); +usage: + printf("%s test <device>\n", argv[0].str); + return -1; + } + + if (strcmp(argv[1].str, "test")) { + goto usage; + } + + // Make sure this block device is legit. + bdev_t *dev = bio_open(argv[2].str); + if (!dev) { + printf("error: could not open block device %s\n", argv[2].str); + return -1; + } + bio_close(dev); + + size_t passed = 0; + size_t attempted = 0; + for (size_t i = 0; i < countof(tests); i++) { + ++attempted; + if (!test_setup(argv[2].str, tests[i].toc_pages)) { + printf("Test Setup failed before %s. Exiting.\n", tests[i].name); + break; + } + + if (tests[i].func(argv[2].str)) { + printf(" [Passed] %s\n", tests[i].name); + ++passed; + } else { + printf(" [Failed] %s\n", tests[i].name); + } + + if (!test_teardown()) { + printf("Test teardown failed after %s. Exiting.\n", tests[i].name); + break; + } + } + printf("\nPassed %u of %u tests.\n", passed, attempted); + + if (attempted != countof(tests)) { + printf("(Skipped %u)\n", countof(tests) - attempted); + } + + return countof(tests) - passed; +} + +STATIC_COMMAND_START + STATIC_COMMAND("spifs", "commands related to the spifs implementation.", &cmd_spifs) +STATIC_COMMAND_END(spifs); + +#endif // LK_DEBUGLEVEL > 1
\ No newline at end of file |