aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorGurjant Kalsi <me@gurjantkalsi.com>2015-11-10 21:05:07 -0800
committerGurjant Kalsi <me@gurjantkalsi.com>2015-11-10 21:05:07 -0800
commit202ebfcee7352e05fef055cc6a71087ed3f6057b (patch)
tree3c451e7c6a8b7e14e1b51a2edcee3fb24e0fdfbe /lib
parent08ad97fe151fa7c6670e056cbe731119b1983e43 (diff)
downloadcommon-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.mk8
-rw-r--r--lib/fs/spifs/test/spifstest.c546
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