reform test_layer2_API with googletest framwork and revise mkdir & mknod return type

This commit is contained in:
Victor 2023-11-22 21:43:38 -08:00
parent 6f5bb3bdcc
commit 60678711d2
4 changed files with 341 additions and 19 deletions

View File

@ -18,8 +18,8 @@ class FilesOperation {
void unlink_inode(u_int64_t inode_number); void unlink_inode(u_int64_t inode_number);
u_int64_t disk_namei(const char* path); u_int64_t disk_namei(const char* path);
u_int64_t namei(const char* path); u_int64_t namei(const char* path);
u_int64_t fischl_mkdir(const char*, mode_t); int fischl_mkdir(const char*, mode_t);
u_int64_t fischl_mknod(const char*, mode_t); int fischl_mknod(const char*, mode_t);
//int fischl_readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags); //int fischl_readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags);
int fischl_unlink (const char *); int fischl_unlink (const char *);
//int fischl_open (const char *, struct fuse_file_info *); //int fischl_open (const char *, struct fuse_file_info *);

View File

@ -111,7 +111,7 @@ void FilesOperation::create_dot_dotdot(INode* inode, u_int64_t parent_inode_numb
void FilesOperation::initialize_rootinode() { void FilesOperation::initialize_rootinode() {
// this method must be called explicitly right after initializion // this method must be called explicitly right after initializion
u_int64_t root_inode_number = inop.inode_allocate(disk); u_int64_t root_inode_number = inop.inode_allocate(disk);
printf("Info: root inode number: %llu\n", root_inode_number); // printf("Info: root inode number: %llu\n", root_inode_number);
INode *root_inode = new_inode(root_inode_number, S_IFDIR); INode *root_inode = new_inode(root_inode_number, S_IFDIR);
create_dot_dotdot(root_inode, root_inode_number); create_dot_dotdot(root_inode, root_inode_number);
root_node = fischl_init_entry(root_inode_number, "/", root_inode); root_node = fischl_init_entry(root_inode_number, "/", root_inode);
@ -142,7 +142,7 @@ INode* FilesOperation::create_new_inode(u_int64_t parent_inode_number, const cha
INode inode; INode inode;
inode.inode_construct(parent_inode_number, disk); inode.inode_construct(parent_inode_number, disk);
if ((inode.permissions & S_IFMT) != S_IFDIR) { if ((inode.permissions & S_IFMT) != S_IFDIR) {
printf("Parent Inode is not a directory\n"); fprintf(stderr,"[%s ,%d] please create under directory\n",__func__,__LINE__);
return NULL; return NULL;
} }
@ -154,7 +154,11 @@ INode* FilesOperation::create_new_inode(u_int64_t parent_inode_number, const cha
for(int i=0;i<=IO_BLOCK_SIZE-264;i+=264){ for(int i=0;i<=IO_BLOCK_SIZE-264;i+=264){
ent.deserialize(r_buffer+i); ent.deserialize(r_buffer+i);
if (strcmp(ent.file_name, name)==0) { if (strcmp(ent.file_name, name)==0) {
printf("Already exists file or directory with name %s, cannot not create\n", name); if((mode & S_IFMT) == S_IFDIR){
fprintf(stderr,"[%s ,%d] %s/ already exists\n",__func__,__LINE__, name);
}else{
fprintf(stderr,"[%s ,%d] %s already exists\n",__func__,__LINE__, name);
}
return NULL; return NULL;
} }
} }
@ -255,7 +259,7 @@ u_int64_t FilesOperation::namei(const char* path) {
else return -1; else return -1;
} }
u_int64_t FilesOperation::fischl_mkdir(const char* path, mode_t mode) { int FilesOperation::fischl_mkdir(const char* path, mode_t mode) {
//check path //check path
char *pathdup = strdup(path); char *pathdup = strdup(path);
char *lastSlash = strrchr(pathdup, '/'); char *lastSlash = strrchr(pathdup, '/');
@ -265,44 +269,41 @@ u_int64_t FilesOperation::fischl_mkdir(const char* path, mode_t mode) {
FileNode *parent_filenode = strlen(ParentPath)? fischl_find_entry(root_node, ParentPath): root_node->self_info; FileNode *parent_filenode = strlen(ParentPath)? fischl_find_entry(root_node, ParentPath): root_node->self_info;
if (parent_filenode == NULL) { if (parent_filenode == NULL) {
printf("parent %s not found by fischl_find_entry\n", ParentPath); fprintf(stderr,"[%s ,%d] ParentPath:{%s} not found\n",__func__,__LINE__, ParentPath);
delete pathdup; delete pathdup;
return -1; return -ENOENT;//parentpath directory does not exist
} }
u_int64_t parent_inode_number = parent_filenode->inode_number; u_int64_t parent_inode_number = parent_filenode->inode_number;
//make new inode //make new inode
INode* ret = create_new_inode(parent_inode_number, newDirname, mode|S_IFDIR);//specify S_IFDIR as directory INode* ret = create_new_inode(parent_inode_number, newDirname, mode|S_IFDIR);//specify S_IFDIR as directory
if (ret == NULL) return -1; if (ret == NULL) return -1;//ENOSPC but create_new_inode handle ENAMETOOLONG EEXIST
fischl_add_entry(parent_filenode->subdirectory, ret->block_number, newDirname, ret); fischl_add_entry(parent_filenode->subdirectory, ret->block_number, newDirname, ret);
delete pathdup; delete pathdup;
return ret->block_number; return 0;//SUCCESS
//after new_inode(mkfile), go to fischl_add_entry record
} }
u_int64_t FilesOperation::fischl_mknod(const char* path, mode_t mode) { int FilesOperation::fischl_mknod(const char* path, mode_t mode) {
//check path //check path
char *pathdup = strdup(path); char *pathdup = strdup(path);
char *lastSlash = strrchr(pathdup, '/'); char *lastSlash = strrchr(pathdup, '/');
*lastSlash = '\0'; // Split the string into parent path and new directory name; <parent path>\0<direcotry name> *lastSlash = '\0'; // Split the string into parent path and new directory name; <parent path>\0<direcotry name>
char *newFilename = lastSlash+1; //\0<direcotry name>, get from <direcotry name> char *newFilename = lastSlash+1; //\0<direcotry name>, get from <direcotry name>
char *ParentPath = pathdup;//pathdup are separated by pathdup, so it take <parent path> only char *ParentPath = pathdup;//pathdup are separated by pathdup, so it take <parent path> only
printf("mknod ParentPath:%s, strlen=%d\n", ParentPath, strlen(ParentPath)); // fprintf(stderr,"[%s ,%d] ParentPath:%s, strlen=%d\n",__func__,__LINE__, ParentPath, strlen(ParentPath));
FileNode *parent_filenode = strlen(ParentPath)? fischl_find_entry(root_node, ParentPath): root_node->self_info; FileNode *parent_filenode = strlen(ParentPath)? fischl_find_entry(root_node, ParentPath): root_node->self_info;
if (parent_filenode == NULL) { if (parent_filenode == NULL) {
printf("parent %s not found by fischl_find_entry\n", ParentPath); fprintf(stderr,"[%s ,%d] ParentPath:{%s} not found\n",__func__,__LINE__, ParentPath);
delete pathdup; delete pathdup;
return -1; return -1;
} }
u_int64_t parent_inode_number = parent_filenode->inode_number; u_int64_t parent_inode_number = parent_filenode->inode_number;
//make new inode //make new inode
INode* ret = create_new_inode(parent_inode_number, newFilename, mode); INode* ret = create_new_inode(parent_inode_number, newFilename, mode);
if (ret == NULL) return -1; if (ret == NULL) return -1;//ENOSPC but create_new_inode handle ENAMETOOLONG EEXIST
//make new node //make new node
fischl_add_entry(parent_filenode->subdirectory, ret->block_number, newFilename, ret); fischl_add_entry(parent_filenode->subdirectory, ret->block_number, newFilename, ret);
delete pathdup; delete pathdup;
return ret->block_number; return 0;//SUCESS
} }
void FilesOperation::unlink_inode(u_int64_t inode_number) { void FilesOperation::unlink_inode(u_int64_t inode_number) {

View File

@ -16,7 +16,7 @@ add_executable(${TARGET_LAYER1_API}
add_executable(${TARGET_LAYER2_API} add_executable(${TARGET_LAYER2_API}
../lib/direntry.cpp ../lib/direntry.cpp
../lib/files.cpp ../lib/files.cpp
layer2_API.cpp layer2_API_dir.cpp
) )
add_executable(${TARGET_DIR_API} add_executable(${TARGET_DIR_API}
../lib/direntry.cpp ../lib/direntry.cpp
@ -26,6 +26,7 @@ add_executable(${TARGET_DIR_API}
# Link Google Test to your test executables # Link Google Test to your test executables
target_link_libraries(${TARGET_LAYER0} gtest gtest_main) target_link_libraries(${TARGET_LAYER0} gtest gtest_main)
target_link_libraries(${TARGET_LAYER1_API} gtest gtest_main) target_link_libraries(${TARGET_LAYER1_API} gtest gtest_main)
target_link_libraries(${TARGET_LAYER2_API} gtest gtest_main)
target_link_libraries(${TARGET_DIR_API} gtest gtest_main) target_link_libraries(${TARGET_DIR_API} gtest gtest_main)
# add test to activate ctest -VV # add test to activate ctest -VV

320
test/layer2_API_dir.cpp Normal file
View File

@ -0,0 +1,320 @@
#include <stdio.h>
#include <string>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <gtest/gtest.h>
#include <iostream>
#include "files.h"
typedef struct file_test{
const char* name;
file_test* next;//use linked-list to know the file at the same level directory
}file_test;
typedef struct dir_test{
const char* name;
file_test* inFile;
dir_test* subdir;
dir_test* next;//use linked-list to know the other dir at the same parent dir.
}dir_test;
void setupTestDirectory(dir_test** root);
void freeDirHierarchy(dir_test* dir);
void traverseDirHierarchy(const dir_test* dir, int depth);
//global can be taken
const char* d;
TreeNode *root;
std::string target_filepath;
dir_test* mock_root = nullptr;
int total_dir_num = 0;
int total_file_num = 0;
int total_free_dir = 0;
int total_free_file = 0;
TEST(FileOperationTest, MkdirnodTest) {
RawDisk *H = new RawDisk(d);
FilesOperation fsop(*H);
fsop.initialize_rootinode();
mode_t mode;//set mode
mode = S_IRWXU | S_IRWXG | S_IRWXO;//future should test permission
//S_IRWXU(S_IRUSR | S_IWUSR | S_IXUSR) (owner), S_IRWXG(S_IRGRP | S_IWGRP | S_IXGRP) (group), S_IRWXO(S_IROTH | S_IWOTH | S_IXOTH)
EXPECT_EQ(fsop.fischl_mknod("/test", mode), 0); // mode here is not used yet
EXPECT_EQ(fsop.fischl_mkdir("/foo", mode), 0);
EXPECT_EQ(fsop.fischl_mkdir("/foo/bar", mode),0);
EXPECT_EQ(fsop.fischl_mknod("/foo/bar/baz", mode), 0);
// the following three testcases will fail
EXPECT_TRUE(fsop.fischl_mkdir("foo/bar", mode) < 0);
EXPECT_TRUE(fsop.fischl_mkdir("/doesnt_exist/bar", mode) < 0);
EXPECT_TRUE(fsop.fischl_mkdir("/test/bar", mode) < 0);
EXPECT_TRUE(fsop.fischl_mkdir("/test", mode) < 0);
EXPECT_TRUE(fsop.fischl_mkdir("/foo/bar", mode) < 0);
EXPECT_TRUE(fsop.fischl_mkdir("/foo/bar/..", mode) < 0);
}
// TEST(FileOperationTest, WriteTest) {
// // write to files (TODO: fischl_write)
// // read and write to indirect datablocks are not supported yet
// printf("=== Part 2: write to files ===\n");
// char buffer[IO_BLOCK_SIZE] = {0};
// INode inode;
// inode.inode_construct(file1, *H);
// buffer[0] = '1';
// fsop.write_datablock(inode, 0, buffer);
// inode.inode_save(*H);
// inode.inode_construct(file4, *H);
// buffer[0] = '4';
// fsop.write_datablock(inode, 3, buffer);
// buffer[0] = '5';
// fsop.write_datablock(inode, 101, buffer);
// inode.inode_save(*H);
// // TODO: guard against overwriting directory datablocks
// }
// TEST(FileOperationTest, RamTest) {
// // retrieve inode-number by path
// u_int64_t file_test = fsop.namei("/test");
// printf("inode number for \"/test\" is %llu\n", file_test);
// assert(file_test == file1);
// u_int64_t file_baz = fsop.namei("/foo/bar/baz");
// printf("inode number for \"/foo/bar/baz\" is %llu\n", file_baz);
// assert(file_baz == file4);
// u_int64_t file_foo = fsop.namei("/foo/bar/..");
// printf("inode number for \"/foo/bar/..\" is %llu\n", file_foo);
// assert(file_foo == file2);
// u_int64_t file_bar = fsop.namei("/foo/bar/.");
// printf("inode number for \"/foo/bar/.\" is %llu\n", file_bar);
// assert(file_bar == file3);
// }
// TEST(FileOperationTest, DiskTest) {
// // retrieve inode-number by path
// u_int64_t file_test = fsop.namei("/test");
// printf("inode number for \"/test\" is %llu\n", file_test);
// assert(file_test == file1);
// u_int64_t file_baz = fsop.namei("/foo/bar/baz");
// printf("inode number for \"/foo/bar/baz\" is %llu\n", file_baz);
// assert(file_baz == file4);
// u_int64_t file_foo = fsop.namei("/foo/bar/..");
// printf("inode number for \"/foo/bar/..\" is %llu\n", file_foo);
// assert(file_foo == file2);
// u_int64_t file_bar = fsop.namei("/foo/bar/.");
// printf("inode number for \"/foo/bar/.\" is %llu\n", file_bar);
// assert(file_bar == file3);
// }
// TEST(FileOperationTest, ReadTest) {
// // read files (TODO: fischl_read)
// char read_buffer[IO_BLOCK_SIZE] = {0};
// INode inode_read;
// inode_read.inode_construct(file_test, *H);
// fsop.read_datablock(inode_read, 0, read_buffer);
// assert(read_buffer[0] == '1');
// inode_read.inode_construct(file_baz, *H);
// fsop.read_datablock(inode_read, 3, read_buffer);
// assert(read_buffer[0] == '4');
// fsop.read_datablock(inode_read, 101, read_buffer);
// assert(read_buffer[0] == '5');
// }
// TEST(FileOperationTest, PressureTest) {
// printf("=== Part 5: pressure test create files ===\n");
// u_int64_t file_pressure = fsop.fischl_mkdir("/pressure", 0);
// u_int64_t inode_numbers[700];
// std::string prefix = "/pressure/No_";
// for(int i=0;i<700;i++){
// inode_numbers[i] = fsop.fischl_mkdir((prefix+std::to_string(i)).c_str(), 0);
// }
// for(int i=0;i<700;i++){
// u_int64_t inode_number = fsop.namei((prefix+std::to_string(i)).c_str());
// assert(inode_number == inode_numbers[i]);
// }
// }
// TEST(FileOperationTest, UnlinkTest) {
// printf("=== Part 6: unlink test ===\n");
// fsop.printDirectory(file_pressure);
// for(int i=0;i<700;i+=2){
// assert(!fsop.fischl_unlink((prefix+std::to_string(i)).c_str()));
// }
// for(int i=0;i<4;i+=2){
// assert(fsop.namei((prefix+std::to_string(i)).c_str())==(u_int64_t)(-1));
// }
// for(int i=1;i<700;i+=2){
// u_int64_t inode_number = fsop.namei((prefix+std::to_string(i)).c_str());
// assert(inode_number == inode_numbers[i]);
// }
// fsop.printDirectory(file_pressure);
// std::string newprefix = "/pressure/New";
// for(int i=0;i<700;i+=2){
// inode_numbers[i] = fsop.fischl_mkdir((newprefix+std::to_string(i)).c_str(), 0);
// }
// for(int i=0;i<700;i+=2){
// u_int64_t inode_number = fsop.namei((newprefix+std::to_string(i)).c_str());
// assert(inode_number == inode_numbers[i]);
// }
// fsop.printDirectory(file_pressure);
// // long filename test
// std::string longfilename = std::string(255,'A');
// u_int64_t filelong = fsop.fischl_mknod((std::string("/")+longfilename).c_str(),0);
// printf("/AAA...AAA is inode %llu, it is a file\n", filelong);
// }
int main(int argc, char **argv) {
srand(time(NULL)); // Seed the random number generator
d = (argc < 2) ? "/dev/vdc" : argv[1];
setupTestDirectory(&mock_root);
::testing::InitGoogleTest(&argc, argv);
int result = RUN_ALL_TESTS();
total_dir_num = 0;
total_file_num = 0;
traverseDirHierarchy(mock_root, 0);//mock_root
printf("Traverse Total: Dir %d, File %d\n",total_dir_num, total_file_num);
// Cleanup
freeDirHierarchy(mock_root);//mock_root
printf("Free Total: Dir %d, File %d\n",total_free_dir, total_free_file);
freeTree(root);
return result;
}
const char* generateRandomName(size_t length) {
const std::string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
std::string randomString;
for (size_t i = 0; i < length; ++i) {
randomString += chars[rand() % chars.size()];
}
// Allocate memory and copy the string
char* name = new char[randomString.length() + 1];
strcpy(name, randomString.c_str());
return name;
}
// Recursive function to create directory hierarchy
dir_test* createDirHierarchy(int level, int maxLevel) {
if (level > maxLevel) {
return nullptr;
}
dir_test* head = nullptr;
dir_test* current = nullptr;
for (int i = 0; i < 3; ++i) {
dir_test* newDir = new dir_test;
newDir->name = generateRandomName(6); // Generate a random name for the directory
newDir->inFile = nullptr; // Initialize file list to nullptr
newDir->subdir = createDirHierarchy(level + 1, maxLevel); // Recursively create subdirectories
newDir->next = nullptr;
// Create file list for this directory
file_test* fileHead = nullptr;
file_test* fileCurrent = nullptr;
for (int j = 0; j < 3; ++j) {
file_test* newFile = new file_test;
newFile->name = generateRandomName(6); // Generate a random name for the file
newFile->next = nullptr;
if (!fileHead) {
fileHead = newFile;
} else {
fileCurrent->next = newFile;
}
fileCurrent = newFile;
}
newDir->inFile = fileHead;
// Add the new directory to the list
if (!head) {
head = newDir;
} else {
current->next = newDir;
}
current = newDir;
}
return head;
}
// Setup function for the test directory
void setupTestDirectory(dir_test** root) {
// Allocate memory for root
*root = new dir_test;
(*root)->name = strdup("/"); // use / as begin
(*root)->inFile = nullptr; // Initialize file list to nullptr
(*root)->subdir = createDirHierarchy(0, 1);
(*root)->next = nullptr;
file_test* fileHead = nullptr;
file_test* fileCurrent = nullptr;
for (int j = 0; j < 3; ++j) {
file_test* newFile = new file_test;
newFile->name = generateRandomName(6); // Generate a random name for the file
newFile->next = nullptr;
if (!fileHead) {
fileHead = newFile;
} else {
fileCurrent->next = newFile;
}
fileCurrent = newFile;
}
(*root)->inFile = fileHead;
}
// Function to free a list of files
void freeFileList(file_test* fileList) {
while (fileList != nullptr) {
file_test* temp = fileList;
fileList = fileList->next;
total_free_file++;//for debug
delete[] temp->name; // Free the name string
delete temp; // Free the current file
}
}
// Recursive function to free the directory hierarchy
void freeDirHierarchy(dir_test* dir) {
while (dir != nullptr) {
dir_test* temp = dir;
dir = dir->next;
total_free_dir++;//for debug
freeFileList(temp->inFile); // Free the list of files in the directory
freeDirHierarchy(temp->subdir); // Recursively free subdirectories
delete[] temp->name; // Free the name string
delete temp; // Free the current directory
}
}
// Function to print the list of files in a directory
void printFileList(const file_test* fileList) {
const file_test* currentFile = fileList;
while (currentFile != nullptr) {
// std::cout << " File: " << currentFile->name << std::endl;
total_file_num++;
currentFile = currentFile->next;
}
}
void traverseDirHierarchy(const dir_test* dir, int depth = 0) {
while (dir != nullptr) {
// std::cout << "Depth " << depth << ", Directory: " << dir->name << std::endl;
total_dir_num++;//for debug
// Print files in this directory
printFileList(dir->inFile);
// Recursively traverse subdirectories
traverseDirHierarchy(dir->subdir, depth + 1);
// Go to the next directory at the same level
dir = dir->next;
}
}