mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-18 22:33:56 -07:00
203 lines
5.5 KiB
C++
203 lines
5.5 KiB
C++
/* Copyright (C) 2025 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/code_annotation.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_path.h"
|
|
#include "lib/file/vfs/vfs_tree.h"
|
|
#include "lib/os_path.h"
|
|
#include "lib/status.h"
|
|
#include "lib/types.h"
|
|
|
|
#include <cstddef>
|
|
|
|
class MockLoader : public IFileLoader
|
|
{
|
|
public:
|
|
MockLoader(size_t precedence) :
|
|
m_Precedence(precedence)
|
|
{
|
|
}
|
|
|
|
size_t Precedence() const override { return m_Precedence; }
|
|
wchar_t LocationCode() const override { return L'\0'; }
|
|
const OsPath& Path() const override { return m_Path; }
|
|
Status Load(const OsPath& /*name*/, const std::shared_ptr<u8>& /*buf*/, size_t /*size*/) const override
|
|
{
|
|
return INFO::OK;
|
|
}
|
|
|
|
private:
|
|
size_t m_Precedence;
|
|
OsPath m_Path;
|
|
};
|
|
|
|
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();
|
|
}
|
|
};
|