Add test case for FLOCK
This commit is contained in:
		
							parent
							
								
									f52bf0b514
								
							
						
					
					
						commit
						d0f6c9b6b6
					
				| @ -22,7 +22,7 @@ TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe | |||||||
| 	truncate readdir mkdir open stat link symlink chmod chown tls pthread system_info rlimit \
 | 	truncate readdir mkdir open stat link symlink chmod chown tls pthread system_info rlimit \
 | ||||||
| 	server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group posix_flock \
 | 	server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group posix_flock \
 | ||||||
| 	ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait \
 | 	ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait \
 | ||||||
| 	spawn_attribute exec statfs random umask pgrp vfork mount | 	spawn_attribute exec statfs random umask pgrp vfork mount flock | ||||||
| # Benchmarks: need to be compiled and run by bench-% target
 | # Benchmarks: need to be compiled and run by bench-% target
 | ||||||
| BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput | BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								test/flock/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										5
									
								
								test/flock/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | include ../test_common.mk | ||||||
|  | 
 | ||||||
|  | EXTRA_C_FLAGS := | ||||||
|  | EXTRA_LINK_FLAGS := | ||||||
|  | BIN_ARGS := | ||||||
							
								
								
									
										199
									
								
								test/flock/main.c
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										199
									
								
								test/flock/main.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,199 @@ | |||||||
|  | #include <string.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <spawn.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <sys/wait.h> | ||||||
|  | #include <sys/file.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include "test.h" | ||||||
|  | 
 | ||||||
|  | // ============================================================================
 | ||||||
|  | // Helper structs & variables & functions
 | ||||||
|  | // ============================================================================
 | ||||||
|  | 
 | ||||||
|  | const char *g_file_path = "/root/test_flock_file.txt"; | ||||||
|  | int g_fd; | ||||||
|  | 
 | ||||||
|  | static int open_or_create_file() { | ||||||
|  |     int flags = O_RDWR | O_CREAT; | ||||||
|  |     int mode = 00666; | ||||||
|  | 
 | ||||||
|  |     int fd = open(g_file_path, flags, mode); | ||||||
|  |     if (fd < 0) { | ||||||
|  |         THROW_ERROR("failed to open or create file"); | ||||||
|  |     } | ||||||
|  |     return fd; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int remove_file() { | ||||||
|  |     if (unlink(g_file_path) < 0) { | ||||||
|  |         THROW_ERROR("failed to unlink the created file"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ============================================================================
 | ||||||
|  | // Test cases for FLOCK
 | ||||||
|  | // ============================================================================
 | ||||||
|  | 
 | ||||||
|  | static int test_invalid_operation() { | ||||||
|  |     // Check the operation with expected errno
 | ||||||
|  |     int ops_with_expected_errno[5][2] = { | ||||||
|  |         {LOCK_SH | LOCK_EX, EINVAL}, | ||||||
|  |         {LOCK_SH | LOCK_UN, EINVAL}, | ||||||
|  |         {LOCK_EX | LOCK_UN, EINVAL}, | ||||||
|  |         {LOCK_SH | 0x1000, EINVAL}, | ||||||
|  |         {LOCK_NB, EINVAL}, | ||||||
|  |     }; | ||||||
|  |     int row_cnt = (sizeof(ops_with_expected_errno) / sizeof(int)) / | ||||||
|  |                   (sizeof(ops_with_expected_errno[0]) / sizeof(int)); | ||||||
|  |     for (int i = 0; i < row_cnt; i++) { | ||||||
|  |         int ops = ops_with_expected_errno[i][0]; | ||||||
|  |         int expected_errno = ops_with_expected_errno[i][1]; | ||||||
|  |         errno = 0; | ||||||
|  | 
 | ||||||
|  |         int ret = flock(g_fd, ops); | ||||||
|  |         if (!(ret < 0 && errno == expected_errno)) { | ||||||
|  |             THROW_ERROR("failed to check flock with invalid operation"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int test_lock() { | ||||||
|  |     int operation = LOCK_EX | LOCK_NB; | ||||||
|  |     if (flock(g_fd, operation) < 0) { | ||||||
|  |         THROW_ERROR("failed to lock file"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     operation = LOCK_SH | LOCK_NB; | ||||||
|  |     if (flock(g_fd, operation) < 0) { | ||||||
|  |         THROW_ERROR("failed to lock file"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int test_spawn_child_and_unlock() { | ||||||
|  |     int status, child_pid; | ||||||
|  | 
 | ||||||
|  |     char g_fd_buf[16]; | ||||||
|  |     sprintf(g_fd_buf, "%d", g_fd); | ||||||
|  |     const char *child_argv[3] = { | ||||||
|  |         "flock", | ||||||
|  |         g_fd_buf, | ||||||
|  |         NULL | ||||||
|  |     }; | ||||||
|  |     int ret = posix_spawn(&child_pid, | ||||||
|  |                           "/bin/flock", NULL, NULL, | ||||||
|  |                           (char *const *)child_argv, | ||||||
|  |                           NULL); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         THROW_ERROR("spawn process error"); | ||||||
|  |     } | ||||||
|  |     printf("Spawn a child process with pid=%d\n", child_pid); | ||||||
|  | 
 | ||||||
|  |     // Sleep 3s for the child to run flock test and wait, is 3s enough?
 | ||||||
|  |     sleep(3); | ||||||
|  | 
 | ||||||
|  |     // Unlock the flock will cause child process to finish running
 | ||||||
|  |     int operation = LOCK_UN; | ||||||
|  |     if (flock(g_fd, operation) < 0) { | ||||||
|  |         THROW_ERROR("failed to unlock the lock"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Wait for child exit
 | ||||||
|  |     ret = wait4(child_pid, &status, 0, NULL); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         THROW_ERROR("failed to wait4 the child process"); | ||||||
|  |     } | ||||||
|  |     if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) { | ||||||
|  |         THROW_ERROR("test cases in child faild"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // The lock will be unlocked on child exit, so we can lock again
 | ||||||
|  |     operation = LOCK_EX; | ||||||
|  |     ret = flock(g_fd, operation); | ||||||
|  |     if (ret < 0 && errno != EINTR) { | ||||||
|  |         THROW_ERROR("failed to check the result of flock"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ============================================================================
 | ||||||
|  | // Child Test cases
 | ||||||
|  | // ============================================================================
 | ||||||
|  | 
 | ||||||
|  | static int test_child_lock_wait() { | ||||||
|  |     // Child open the file with new fd
 | ||||||
|  |     int new_fd = open_or_create_file(); | ||||||
|  | 
 | ||||||
|  |     int operation = LOCK_SH | LOCK_NB; | ||||||
|  |     if (flock(new_fd, operation) < 0) { | ||||||
|  |         THROW_ERROR("failed set shared flock"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     operation = LOCK_UN; | ||||||
|  |     if (flock(new_fd, operation) < 0) { | ||||||
|  |         THROW_ERROR("failed to unlock the new lock"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Child inherits file table, so it can change the old lock to exclusive
 | ||||||
|  |     operation = LOCK_EX | LOCK_NB; | ||||||
|  |     if (flock(g_fd, operation) < 0) { | ||||||
|  |         THROW_ERROR("failed change the lock type to exclusive lock"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Try to set new lock
 | ||||||
|  |     operation = LOCK_SH | LOCK_NB; | ||||||
|  |     int res = flock(new_fd, operation); | ||||||
|  |     if (!(res < 0 && errno == EAGAIN)) { | ||||||
|  |         THROW_ERROR("failed to check the file lock state"); | ||||||
|  |     } | ||||||
|  |     // Child will wait here
 | ||||||
|  |     operation = LOCK_SH; | ||||||
|  |     res = flock(new_fd, operation); | ||||||
|  |     if (res < 0 && errno != EINTR) { | ||||||
|  |         THROW_ERROR("failed to check the result of flock with conflict lock"); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ============================================================================
 | ||||||
|  | // Test suite main
 | ||||||
|  | // ============================================================================
 | ||||||
|  | 
 | ||||||
|  | static test_case_t test_cases[] = { | ||||||
|  |     TEST_CASE(test_invalid_operation), | ||||||
|  |     TEST_CASE(test_lock), | ||||||
|  |     TEST_CASE(test_spawn_child_and_unlock), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static test_case_t child_test_cases[] = { | ||||||
|  |     TEST_CASE(test_child_lock_wait), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int main(int argc, const char *argv[]) { | ||||||
|  |     // Test argc
 | ||||||
|  |     if (argc == 2) { | ||||||
|  |         g_fd = atoi(argv[1]); | ||||||
|  |         if (test_suite_run(child_test_cases, ARRAY_SIZE(child_test_cases)) < 0) { | ||||||
|  |             THROW_ERROR("failed run child test"); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         g_fd = open_or_create_file(); | ||||||
|  |         if (g_fd < 0) { | ||||||
|  |             THROW_ERROR("failed to open/create file"); | ||||||
|  |         } | ||||||
|  |         if (test_suite_run(test_cases, ARRAY_SIZE(test_cases)) < 0) { | ||||||
|  |             THROW_ERROR("failed run test"); | ||||||
|  |         } | ||||||
|  |         close(g_fd); | ||||||
|  |         if (remove_file() < 0) { | ||||||
|  |             THROW_ERROR("failed to remove file after test"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user