/* Copyright (C) 2021 Wildfire Games. * * 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. */ #include "lib/self_test.h" #include "lib/file/common/file_loader.h" #include "lib/file/vfs/vfs.h" #include "lib/file/vfs/vfs_lookup.h" #include "lib/file/vfs/vfs_tree.h" class MockLoader : public IFileLoader { private: size_t m_Precedence; public: MockLoader(size_t precedence) : m_Precedence(precedence) { } size_t Precedence() const { return m_Precedence; } wchar_t LocationCode() const { return L'\0'; } OsPath Path() const { return L"";} Status Load(const OsPath& UNUSED(name), const std::shared_ptr& UNUSED(buf), size_t UNUSED(size)) const {return INFO::OK; } }; class TestVfsTree : public CxxTest::TestSuite { /** * We create a few different "mods" here to test proper .DELETED * behavior. * * To check which file is used we use the priority. * * 1 * +--a * +--b/ * | +--a * | \--b/ * | \--a * \--c/ * +--a * \--b * * 2 * +--a.DELETED * +--b/ * | +--a * | \--b.DELETED * +--c.DELETED * \--c/ * +--a * \--b * * 3 * +--a * \--b/ * \--b/ * \--a */ void mount_mod(size_t mod, VfsDirectory& dir) { size_t priority = mod; PIFileLoader loader(new MockLoader(1)); switch(mod) { case 1: { dir.AddFile(VfsFile("a", 0, 0, priority, loader)); VfsDirectory* b = dir.AddSubdirectory("b"); b->AddFile(VfsFile("a", 0, 0, priority, loader)); VfsDirectory* b_b = b->AddSubdirectory("b"); b_b->AddFile(VfsFile("a", 0, 0, priority, loader)); VfsDirectory* c = dir.AddSubdirectory("c"); c->AddFile(VfsFile("a", 0, 0, priority, loader)); c->AddFile(VfsFile("b", 0, 0, priority, loader)); break; } case 2: { dir.DeleteSubtree(VfsFile("a.DELETED", 0, 0, priority, loader)); VfsDirectory* b = dir.AddSubdirectory("b"); b->AddFile(VfsFile("a", 0, 0, priority, loader)); b->DeleteSubtree(VfsFile("b.DELETED", 0, 0, priority, loader)); dir.DeleteSubtree(VfsFile("c.DELETED", 0, 0, priority, loader)); VfsDirectory* c = dir.AddSubdirectory("c"); c->AddFile(VfsFile("a", 0, 0, priority, loader)); c->AddFile(VfsFile("b", 0, 0, priority, loader)); break; } case 3: { dir.AddFile(VfsFile("a", 0, 0, priority, loader)); VfsDirectory* b = dir.AddSubdirectory("b"); VfsDirectory* b_b = b->AddSubdirectory("b"); b_b->AddFile(VfsFile("a", 0, 0, priority, loader)); break; } NODEFAULT; } } void check_priority(VfsDirectory& root, const VfsPath& path, size_t priority) { VfsDirectory* dir; VfsFile* file; TS_ASSERT_OK(vfs_Lookup(path, &root, dir, &file, VFS_LOOKUP_SKIP_POPULATE)); TS_ASSERT_EQUALS(file->Priority(), priority); } void file_does_not_exists(VfsDirectory& root, const VfsPath& path) { VfsDirectory* dir; VfsFile* file; TS_ASSERT_EQUALS(vfs_Lookup(path, &root, dir, &file, VFS_LOOKUP_SKIP_POPULATE), ERR::VFS_FILE_NOT_FOUND); } void directory_exists(VfsDirectory& root, const VfsPath& path, Status error = INFO::OK) { VfsDirectory* dir; TS_ASSERT_EQUALS(vfs_Lookup(path, &root, dir, nullptr, VFS_LOOKUP_SKIP_POPULATE), error); } public: void test_replacement() { VfsDirectory dir; PIFileLoader loader(new MockLoader(1)); VfsFile file0("a", 0, 0, 0, loader); VfsFile file1("a", 0, 20, 0, loader); VfsFile file2("a", 0, 10, 1, loader); // Modification time TS_ASSERT_EQUALS(dir.AddFile(file0)->MTime(), file0.MTime()); TS_ASSERT_EQUALS(dir.AddFile(file1)->MTime(), file1.MTime()); TS_ASSERT_EQUALS(dir.AddFile(file0)->MTime(), file1.MTime()); // Priority TS_ASSERT_EQUALS(dir.AddFile(file2)->MTime(), file2.MTime()); TS_ASSERT_EQUALS(dir.AddFile(file1)->MTime(), file2.MTime()); } void test_deleted() { VfsDirectory dir; mount_mod(1, dir); mount_mod(2, dir); file_does_not_exists(dir, "a"); check_priority(dir, "b/a", 2); directory_exists(dir, "b/b/", ERR::VFS_DIR_NOT_FOUND); directory_exists(dir, "c/"); check_priority(dir, "c/a", 2); check_priority(dir, "c/b", 2); dir.Clear(); mount_mod(1, dir); mount_mod(2, dir); mount_mod(3, dir); check_priority(dir, "a", 3); check_priority(dir, "b/b/a", 3); dir.Clear(); mount_mod(1, dir); mount_mod(3, dir); mount_mod(2, dir); check_priority(dir, "a", 3); check_priority(dir, "b/b/a", 3); dir.Clear(); } };