diff --git a/tests/unittests/tests-vfs/Makefile b/tests/unittests/tests-vfs/Makefile new file mode 100644 index 0000000000000..48422e909a47d --- /dev/null +++ b/tests/unittests/tests-vfs/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-vfs/Makefile.include b/tests/unittests/tests-vfs/Makefile.include new file mode 100644 index 0000000000000..f35c90d855fba --- /dev/null +++ b/tests/unittests/tests-vfs/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE += vfs +USEMODULE += constfs diff --git a/tests/unittests/tests-vfs/tests-vfs-bind.c b/tests/unittests/tests-vfs/tests-vfs-bind.c new file mode 100644 index 0000000000000..1647d1a3cbf03 --- /dev/null +++ b/tests/unittests/tests-vfs/tests-vfs-bind.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2016 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + */ +#include +#include +#include +#include +#include + +#include "embUnit/embUnit.h" + +#include "vfs.h" + +#include "tests-vfs.h" + +static const uint8_t str_data[] = "This is a test file"; + /* 01234567890123456789 */ + /* 0 1 */ + +#define _VFS_TEST_BIND_BUFSIZE 8 + +static ssize_t _mock_write(vfs_file_t *filp, const void *src, size_t nbytes); +static ssize_t _mock_read(vfs_file_t *filp, void *dest, size_t nbytes); + +static volatile int _mock_write_calls = 0; +static volatile int _mock_read_calls = 0; + +static vfs_file_ops_t _test_bind_ops = { + .read = _mock_read, + .write = _mock_write, +}; + +static ssize_t _mock_write(vfs_file_t *filp, const void *src, size_t nbytes) +{ + void *dest = filp->private_data; + ++_mock_write_calls; + if (nbytes > _VFS_TEST_BIND_BUFSIZE) { + nbytes = _VFS_TEST_BIND_BUFSIZE; + } + memcpy(dest, src, nbytes); + return nbytes; +} + +static ssize_t _mock_read(vfs_file_t *filp, void *dest, size_t nbytes) +{ + void *src = filp->private_data; + ++_mock_read_calls; + if (nbytes > _VFS_TEST_BIND_BUFSIZE) { + nbytes = _VFS_TEST_BIND_BUFSIZE; + } + memcpy(dest, src, nbytes); + return nbytes; +} + +static void test_vfs_bind(void) +{ + int fd; + uint8_t buf[_VFS_TEST_BIND_BUFSIZE]; + fd = vfs_bind(VFS_ANY_FD, O_RDWR, &_test_bind_ops, &buf[0]); + TEST_ASSERT(fd >= 0); + if (fd < 0) { + return; + } + + ssize_t nbytes; + int ncalls = _mock_write_calls; + nbytes = vfs_write(fd, &str_data[0], sizeof(str_data)); + TEST_ASSERT_EQUAL_INT(_mock_write_calls, ncalls + 1); + TEST_ASSERT_EQUAL_INT(_VFS_TEST_BIND_BUFSIZE, nbytes); + TEST_ASSERT_EQUAL_INT(0, memcmp(&str_data[0], &buf[0], nbytes)); + + char strbuf[64]; + memset(strbuf, '\0', sizeof(strbuf)); + ncalls = _mock_read_calls; + nbytes = vfs_read(fd, strbuf, sizeof(strbuf)); + TEST_ASSERT_EQUAL_INT(_mock_read_calls, ncalls + 1); + TEST_ASSERT_EQUAL_INT(_VFS_TEST_BIND_BUFSIZE, nbytes); + TEST_ASSERT_EQUAL_INT(0, memcmp(&buf[0], &strbuf[0], nbytes)); + + int res = vfs_close(fd); + TEST_ASSERT_EQUAL_INT(0, res); +} + +static void test_vfs_bind__leak_fds(void) +{ + /* This test was added after a bug was discovered in the _allocate_fd code to + * make sure that fds are not leaked when doing multiple bind/close calls */ + /* Try >VFS_MAX_OPEN_FILES times to open and close fds to see that they are + * not leaked */ + for (unsigned int i = 0; i < VFS_MAX_OPEN_FILES; ++i) { + test_vfs_bind(); + } + /* The following will fail if the fds above are not properly freed */ + test_vfs_bind(); +} + +Test *tests_vfs_bind_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_vfs_bind), + new_TestFixture(test_vfs_bind__leak_fds), + }; + + EMB_UNIT_TESTCALLER(vfs_bind_tests, NULL, NULL, fixtures); + + return (Test *)&vfs_bind_tests; +} + +/** @} */ diff --git a/tests/unittests/tests-vfs/tests-vfs-mount-constfs.c b/tests/unittests/tests-vfs/tests-vfs-mount-constfs.c new file mode 100644 index 0000000000000..4f93b7d9a0367 --- /dev/null +++ b/tests/unittests/tests-vfs/tests-vfs-mount-constfs.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2016 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief vfs_mount related unit tests + */ +#include +#include +#include +#include +#include +#include + +#include "embUnit/embUnit.h" + +#include "vfs.h" +#include "fs/constfs.h" + +#include "tests-vfs.h" + +static const uint8_t bin_data[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, +}; +static const uint8_t str_data[] = "This is a test file"; + /* 01234567890123456789 */ + /* 0 1 */ +static const constfs_file_t _files[] = { + { + .path = "/test.txt", + .data = str_data, + .size = 20, + }, + { + .path = "/data.bin", + .data = bin_data, + .size = 32, + }, +}; + +static const constfs_t fs = { + .files = _files, + .nfiles = sizeof(_files) / sizeof(_files[0]), +}; + +static void test_vfs_mount_umount(void) +{ + int md = vfs_mount(&constfs_file_system, "/test", (void *)&fs); + TEST_ASSERT(md >= 0); + int res = vfs_umount(md); + TEST_ASSERT(res >= 0); +} + +static void test_vfs_umount__invalid_md(void) +{ + int res; + res = vfs_umount(-1); + TEST_ASSERT(res < 0); + res = vfs_umount(VFS_MAX_MOUNTS); + TEST_ASSERT(res < 0); +} + +static void test_vfs_constfs_open(void) +{ + int res; + int md = vfs_mount(&constfs_file_system, "/test", (void *)&fs); + TEST_ASSERT(md >= 0); + + int fd; + fd = vfs_open("/test/notfound", O_RDONLY, 0); + TEST_ASSERT(fd == -ENOENT); + if (fd >= 0) { + vfs_close(fd); + } + fd = vfs_open("/test/test.txt", O_WRONLY, 0); + TEST_ASSERT(fd == -EROFS); + if (fd >= 0) { + vfs_close(fd); + } + fd = vfs_open("/test/test.txt", O_RDWR, 0); + TEST_ASSERT(fd == -EROFS); + if (fd >= 0) { + vfs_close(fd); + } + fd = vfs_open("/test/test.txt", O_RDONLY, 0); + TEST_ASSERT(fd >= 0); + if (fd >= 0) { + res = vfs_close(fd); + TEST_ASSERT_EQUAL_INT(0, res); + } + + res = vfs_umount(md); + TEST_ASSERT_EQUAL_INT(0, res); +} + +static void test_vfs_constfs_read(void) +{ + int res; + int md = vfs_mount(&constfs_file_system, "/test", (void *)&fs); + TEST_ASSERT(md >= 0); + + int fd = vfs_open("/test/test.txt", O_RDONLY, 0); + TEST_ASSERT(fd >= 0); + + char strbuf[64]; + memset(strbuf, '\0', sizeof(strbuf)); + ssize_t nbytes; + nbytes = vfs_read(fd, strbuf, sizeof(strbuf)); + TEST_ASSERT_EQUAL_INT(sizeof(str_data), nbytes); + TEST_ASSERT_EQUAL_STRING((const char *)&str_data[0], (const char *)&strbuf[0]); + + res = vfs_close(fd); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_umount(md); + TEST_ASSERT_EQUAL_INT(0, res); +} + +#if MODULE_NEWLIB +static void test_vfs_constfs__posix(void) +{ + int res; + int md = vfs_mount(&constfs_file_system, "/test", (void *)&fs); + TEST_ASSERT(md >= 0); + + int fd = open("/test/test.txt", O_RDONLY, 0); + TEST_ASSERT(fd >= 0); + + char strbuf[64]; + memset(strbuf, '\0', sizeof(strbuf)); + ssize_t nbytes; + nbytes = read(fd, strbuf, sizeof(strbuf)); + TEST_ASSERT_EQUAL_INT(sizeof(str_data), nbytes); + TEST_ASSERT_EQUAL_STRING((const char *)&str_data[0], (const char *)&strbuf[0]); + + res = close(fd); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_umount(md); + TEST_ASSERT_EQUAL_INT(0, res); +} +#endif + +Test *tests_vfs_mount_constfs_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_vfs_mount_umount), + new_TestFixture(test_vfs_umount__invalid_md), + new_TestFixture(test_vfs_constfs_open), + new_TestFixture(test_vfs_constfs_read), +#if MODULE_NEWLIB + new_TestFixture(test_vfs_constfs__posix), +#endif + }; + + EMB_UNIT_TESTCALLER(vfs_mount_tests, NULL, NULL, fixtures); + + return (Test *)&vfs_mount_tests; +} + +/** @} */ diff --git a/tests/unittests/tests-vfs/tests-vfs-normalize_path.c b/tests/unittests/tests-vfs/tests-vfs-normalize_path.c new file mode 100644 index 0000000000000..6ea54af48a005 --- /dev/null +++ b/tests/unittests/tests-vfs/tests-vfs-normalize_path.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2016 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + */ +#include +#include +#include +#include +#include + +#include "embUnit/embUnit.h" + +#include "vfs.h" + +#include "tests-vfs.h" + +static void test_vfs_normalize_path__noop(void) +{ + static const char path[] = "/this/is/a/test"; + char buf[16]; + int res = vfs_normalize_path(buf, path, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(4, res); + TEST_ASSERT_EQUAL_STRING((const char *)&path[0], (const char *)&buf[0]); +} + +static void test_vfs_normalize_path__slashes(void) +{ + static const char path[] = "///////////////////////////////"; + static const char expected[] = "/"; + char buf[4]; + int res = vfs_normalize_path(buf, path, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(1, res); + TEST_ASSERT_EQUAL_STRING((const char *)&expected[0], (const char *)&buf[0]); +} + +static void test_vfs_normalize_path__dot(void) +{ + static const char path[] = "/abc/./def/././zxcv././."; + static const char expected[] = "/abc/def/zxcv."; + char buf[16]; + int res = vfs_normalize_path(buf, path, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(3, res); + TEST_ASSERT_EQUAL_STRING((const char *)&expected[0], (const char *)&buf[0]); +} + +static void test_vfs_normalize_path__reduce(void) +{ + static const char path[] = "/abc/../def"; + static const char expected[] = "/def"; + char buf[16]; + int res = vfs_normalize_path(buf, path, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(1, res); + TEST_ASSERT_EQUAL_STRING((const char *)&expected[0], (const char *)&buf[0]); +} + +static void test_vfs_normalize_path__trailing(void) +{ + static const char path[] = "/mydir/"; + static const char expected[] = "/mydir/"; + char buf[16]; + int res = vfs_normalize_path(buf, path, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(1, res); + TEST_ASSERT_EQUAL_STRING((const char *)&expected[0], (const char *)&buf[0]); +} + +static void test_vfs_normalize_path__outside(void) +{ + static const char path[] = "/somewhere/../.."; + static const char path2[] = "/../abdgh"; + char buf[16]; + int res = vfs_normalize_path(buf, path, sizeof(buf)); + TEST_ASSERT(res < 0); + res = vfs_normalize_path(buf, path2, sizeof(buf)); + TEST_ASSERT(res < 0); +} + +static void test_vfs_normalize_path__toolong(void) +{ + static const char path[] = "/abc"; + char buf[4]; + int res = vfs_normalize_path(buf, path, sizeof(buf)); + TEST_ASSERT(res < 0); +} + +static void test_vfs_normalize_path__shorten(void) +{ +#if 0 + /* Not supported by the current implementation */ + /* The current implementation needs enough buffer space to store the longest + * prefix path before each ../ reduction */ + static const char path[] = "/qwerty/asdfghjkl/.."; + static const char expected[] = "/qwerty"; + char buf[8]; +#endif + static const char path[] = "/12345/6789/.."; + static const char expected[] = "/12345"; + char buf[12]; + int res = vfs_normalize_path(buf, path, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(1, res); + TEST_ASSERT_EQUAL_STRING((const char *)&expected[0], (const char *)&buf[0]); +} + +static void test_vfs_normalize_path__shorten_inplace(void) +{ + char path[] = "/qwerty/asdfghjkl/.."; + static const char expected[] = "/qwerty"; + int res = vfs_normalize_path(path, path, sizeof(path)); + TEST_ASSERT_EQUAL_INT(1, res); + TEST_ASSERT_EQUAL_STRING((const char *)&expected[0], (const char *)&path[0]); +} + +static void test_vfs_normalize_path__empty(void) +{ + char path[] = ""; + static const char expected[] = ""; + char buf[4]; + int res = vfs_normalize_path(buf, path, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_STRING((const char *)&expected[0], (const char *)&path[0]); +} + +Test *tests_vfs_normalize_path_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_vfs_normalize_path__noop), + new_TestFixture(test_vfs_normalize_path__slashes), + new_TestFixture(test_vfs_normalize_path__dot), + new_TestFixture(test_vfs_normalize_path__reduce), + new_TestFixture(test_vfs_normalize_path__trailing), + new_TestFixture(test_vfs_normalize_path__outside), + new_TestFixture(test_vfs_normalize_path__toolong), + new_TestFixture(test_vfs_normalize_path__shorten), + new_TestFixture(test_vfs_normalize_path__shorten_inplace), + new_TestFixture(test_vfs_normalize_path__empty), + }; + + EMB_UNIT_TESTCALLER(vfs_normalize_path_tests, NULL, NULL, fixtures); + + return (Test *)&vfs_normalize_path_tests; +} +/** @} */ diff --git a/tests/unittests/tests-vfs/tests-vfs-open-close.c b/tests/unittests/tests-vfs/tests-vfs-open-close.c new file mode 100644 index 0000000000000..8864cba984497 --- /dev/null +++ b/tests/unittests/tests-vfs/tests-vfs-open-close.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + */ +#include +#include +#include +#include +#include + +#include "embUnit/embUnit.h" + +#include "vfs.h" + +#include "tests-vfs.h" + +static void test_vfs_close__invalid_fd(void) +{ + int res = vfs_close(-1); + TEST_ASSERT(res < 0); +} + +static void test_vfs_open__notfound(void) +{ + int fd = vfs_open("/notfound/path", O_RDONLY, 0); + TEST_ASSERT(fd < 0); +} + +Test *tests_vfs_open_close_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_vfs_open__notfound), + new_TestFixture(test_vfs_close__invalid_fd), + }; + + EMB_UNIT_TESTCALLER(vfs_open_close_tests, NULL, NULL, fixtures); + + return (Test *)&vfs_open_close_tests; +} +/** @} */ diff --git a/tests/unittests/tests-vfs/tests-vfs.c b/tests/unittests/tests-vfs/tests-vfs.c new file mode 100644 index 0000000000000..2f9da4cc7eb01 --- /dev/null +++ b/tests/unittests/tests-vfs/tests-vfs.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + */ + +#include "embUnit/embUnit.h" + +#include "tests-vfs.h" + +Test *tests_vfs_bind_tests(void); +Test *tests_vfs_mount_constfs_tests(void); +Test *tests_vfs_open_close_tests(void); +Test *tests_vfs_normalize_path_tests(void); + +void tests_vfs(void) +{ + TESTS_RUN(tests_vfs_open_close_tests()); + TESTS_RUN(tests_vfs_bind_tests()); + TESTS_RUN(tests_vfs_mount_constfs_tests()); + TESTS_RUN(tests_vfs_normalize_path_tests()); +} +/** @} */ diff --git a/tests/unittests/tests-vfs/tests-vfs.h b/tests/unittests/tests-vfs/tests-vfs.h new file mode 100644 index 0000000000000..edd6457537e9a --- /dev/null +++ b/tests/unittests/tests-vfs/tests-vfs.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 Eistec AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Unittests for the ``vfs`` module + * + * @author Joakim NohlgÄrd + */ +#ifndef TESTS_VFS_H +#define TESTS_VFS_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_vfs(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_VFS_H */ +/** @} */