320 lines
12 KiB
Rust
320 lines
12 KiB
Rust
//! System call tests
|
|
//! Tests for syscall dispatch and error handling
|
|
|
|
#![allow(dead_code)]
|
|
|
|
use kernel::syscalls::common::{SyscallError, syscall_error_to_errno};
|
|
|
|
#[cfg(test)]
|
|
mod syscall_error_handling_tests {
|
|
use super::*;
|
|
|
|
/// Test that SyscallError maps to correct POSIX errno values
|
|
#[test]
|
|
fn test_error_to_errno_mapping() {
|
|
// Test basic error mappings
|
|
assert_eq!(syscall_error_to_errno(SyscallError::InvalidSyscall), 38); // ENOSYS
|
|
assert_eq!(syscall_error_to_errno(SyscallError::PermissionDenied), 1); // EPERM
|
|
assert_eq!(syscall_error_to_errno(SyscallError::InvalidArgument), 22); // EINVAL
|
|
assert_eq!(syscall_error_to_errno(SyscallError::NotFound), 2); // ENOENT
|
|
assert_eq!(syscall_error_to_errno(SyscallError::OutOfMemory), 12); // ENOMEM
|
|
assert_eq!(syscall_error_to_errno(SyscallError::Interrupted), 4); // EINTR
|
|
assert_eq!(syscall_error_to_errno(SyscallError::IoError), 5); // EIO
|
|
assert_eq!(syscall_error_to_errno(SyscallError::WouldBlock), 11); // EAGAIN
|
|
assert_eq!(syscall_error_to_errno(SyscallError::NotSupported), 95); // EOPNOTSUPP
|
|
}
|
|
|
|
/// Test extended error mappings
|
|
#[test]
|
|
fn test_extended_error_mappings() {
|
|
assert_eq!(syscall_error_to_errno(SyscallError::BadFileDescriptor), 9); // EBADF
|
|
assert_eq!(syscall_error_to_errno(SyscallError::TooManyOpenFiles), 24); // EMFILE
|
|
assert_eq!(syscall_error_to_errno(SyscallError::NoBufferSpace), 105); // ENOBUFS
|
|
assert_eq!(syscall_error_to_errno(SyscallError::NotADirectory), 20); // ENOTDIR
|
|
assert_eq!(syscall_error_to_errno(SyscallError::IsADirectory), 21); // EISDIR
|
|
assert_eq!(syscall_error_to_errno(SyscallError::DirectoryNotEmpty), 39); // ENOTEMPTY
|
|
assert_eq!(syscall_error_to_errno(SyscallError::FileExists), 17); // EEXIST
|
|
assert_eq!(syscall_error_to_errno(SyscallError::CrossDeviceLink), 18); // EXDEV
|
|
assert_eq!(syscall_error_to_errno(SyscallError::FileTooBig), 27); // EFBIG
|
|
assert_eq!(syscall_error_to_errno(SyscallError::NoSpaceLeft), 28); // ENOSPC
|
|
assert_eq!(syscall_error_to_errno(SyscallError::BadAddress), 14); // EFAULT
|
|
assert_eq!(syscall_error_to_errno(SyscallError::DeadlockWouldOccur), 35);// EDEADLK
|
|
assert_eq!(syscall_error_to_errno(SyscallError::NameTooLong), 36); // ENAMETOOLONG
|
|
assert_eq!(syscall_error_to_errno(SyscallError::TooManySymlinks), 40); // ELOOP
|
|
assert_eq!(syscall_error_to_errno(SyscallError::ConnectionRefused), 111);// ECONNREFUSED
|
|
assert_eq!(syscall_error_to_errno(SyscallError::ConnectionReset), 104); // ECONNRESET
|
|
assert_eq!(syscall_error_to_errno(SyscallError::BrokenPipe), 32); // EPIPE
|
|
assert_eq!(syscall_error_to_errno(SyscallError::TimedOut), 110); // ETIMEDOUT
|
|
}
|
|
|
|
/// Test that negative errno is returned for errors
|
|
#[test]
|
|
fn test_error_negation() {
|
|
let errno = syscall_error_to_errno(SyscallError::InvalidArgument);
|
|
let neg_errno = -(errno as isize);
|
|
assert!(neg_errno < 0);
|
|
assert_eq!(neg_errno, -22);
|
|
}
|
|
|
|
/// Test that errors are distinct
|
|
#[test]
|
|
fn test_error_distinctiveness() {
|
|
let errors = vec![
|
|
SyscallError::InvalidSyscall,
|
|
SyscallError::PermissionDenied,
|
|
SyscallError::InvalidArgument,
|
|
SyscallError::NotFound,
|
|
SyscallError::OutOfMemory,
|
|
SyscallError::BadFileDescriptor,
|
|
SyscallError::TooManyOpenFiles,
|
|
SyscallError::NotADirectory,
|
|
SyscallError::IsADirectory,
|
|
];
|
|
|
|
let mut errno_values = Vec::new();
|
|
for error in errors {
|
|
let errno = syscall_error_to_errno(error);
|
|
// Ensure no duplicates (except where explicitly aliased like EAGAIN/EWOULDBLOCK)
|
|
assert!(!errno_values.contains(&errno) || errno == 11,
|
|
"Duplicate errno {} for error {:?}", errno, error);
|
|
errno_values.push(errno);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod syscall_dispatch_tests {
|
|
use super::*;
|
|
|
|
/// Test invalid syscall number
|
|
#[test]
|
|
fn test_invalid_syscall() {
|
|
// Syscall number out of range should return ENOSYS
|
|
let result = kernel::syscalls::dispatch(0xFFFFFFFF, &[]);
|
|
assert_eq!(result, -38); // -ENOSYS
|
|
}
|
|
|
|
/// Test basic syscall structure
|
|
#[test]
|
|
fn test_syscall_ranges() {
|
|
// Verify that different syscall ranges are properly routed
|
|
// This is a basic sanity check
|
|
|
|
// Process management syscalls should return an error (no process to manage in test)
|
|
let result = kernel::syscalls::dispatch(0x1000, &[]);
|
|
// Result could be -ENOSYS or -EINVAL depending on implementation
|
|
assert!(result < 0);
|
|
|
|
// Invalid range should return -ENOSYS
|
|
let result = kernel::syscalls::dispatch(0x0FFF, &[]);
|
|
assert_eq!(result, -38); // -ENOSYS
|
|
}
|
|
|
|
/// Test argument conversion
|
|
#[test]
|
|
fn test_argument_conversion() {
|
|
// Test that usize arguments are properly converted
|
|
let args = vec![1, 2, 3, 4, 5, 6];
|
|
let _result = kernel::syscalls::dispatch(0x1000, &args);
|
|
// Should not panic during conversion
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod error_handling_consistency_tests {
|
|
use super::*;
|
|
|
|
/// Test that all syscall modules return consistent error types
|
|
#[test]
|
|
fn test_error_consistency() {
|
|
// Verify all errors map to valid POSIX errno values
|
|
let errors = vec![
|
|
SyscallError::InvalidSyscall,
|
|
SyscallError::PermissionDenied,
|
|
SyscallError::InvalidArgument,
|
|
SyscallError::NotFound,
|
|
SyscallError::OutOfMemory,
|
|
SyscallError::Interrupted,
|
|
SyscallError::IoError,
|
|
SyscallError::WouldBlock,
|
|
SyscallError::NotSupported,
|
|
SyscallError::BadFileDescriptor,
|
|
SyscallError::TooManyOpenFiles,
|
|
SyscallError::NoBufferSpace,
|
|
SyscallError::NotADirectory,
|
|
SyscallError::IsADirectory,
|
|
SyscallError::DirectoryNotEmpty,
|
|
SyscallError::FileExists,
|
|
SyscallError::CrossDeviceLink,
|
|
SyscallError::FileTooBig,
|
|
SyscallError::NoSpaceLeft,
|
|
SyscallError::BadAddress,
|
|
SyscallError::DeadlockWouldOccur,
|
|
SyscallError::NameTooLong,
|
|
SyscallError::TooManySymlinks,
|
|
SyscallError::ConnectionRefused,
|
|
SyscallError::ConnectionReset,
|
|
SyscallError::BrokenPipe,
|
|
SyscallError::TimedOut,
|
|
];
|
|
|
|
for error in errors {
|
|
let errno = syscall_error_to_errno(error);
|
|
// Check that errno is positive and within valid range
|
|
assert!(errno > 0 && errno <= 133,
|
|
"Invalid errno {} for error {:?}", errno, error);
|
|
// Check that negation produces expected negative value
|
|
let neg_errno = -(errno as isize);
|
|
assert!(neg_errno < 0);
|
|
}
|
|
}
|
|
|
|
/// Test Result type consistency
|
|
#[test]
|
|
fn test_result_type_usage() {
|
|
// Test that SyscallResult can be constructed and converted
|
|
let ok_result: Result<u64, SyscallError> = Ok(42);
|
|
assert!(ok_result.is_ok());
|
|
assert_eq!(ok_result.unwrap(), 42);
|
|
|
|
let err_result: Result<u64, SyscallError> = Err(SyscallError::InvalidArgument);
|
|
assert!(err_result.is_err());
|
|
let errno = syscall_error_to_errno(err_result.unwrap_err());
|
|
assert_eq!(errno, 22); // EINVAL
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod syscall_argument_tests {
|
|
use super::*;
|
|
|
|
/// Test system call argument conversion optimization
|
|
#[test]
|
|
fn test_syscall_args_no_heap_allocation() {
|
|
// Test that system call arguments are converted without heap allocation
|
|
// Using fixed-size array instead of Vec
|
|
let args = [1usize, 2, 3, 4, 5, 6];
|
|
|
|
// This should not allocate on the heap
|
|
let result = kernel::syscalls::dispatch(0x1000, &args);
|
|
// Should not panic and should return an error (no process context)
|
|
assert!(result <= 0);
|
|
}
|
|
|
|
/// Test argument extraction
|
|
#[test]
|
|
fn test_argument_extraction() {
|
|
use kernel::syscalls::common::extract_args;
|
|
|
|
let args = [1u64, 2, 3, 4];
|
|
|
|
// Extract 2 arguments
|
|
let result = extract_args(&args, 2);
|
|
assert!(result.is_ok());
|
|
let extracted = result.unwrap();
|
|
assert_eq!(extracted.len(), 2);
|
|
assert_eq!(extracted[0], 1);
|
|
assert_eq!(extracted[1], 2);
|
|
|
|
// Extract more than available should fail
|
|
let result = extract_args(&args, 5);
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
/// Test argument bounds checking
|
|
#[test]
|
|
fn test_argument_bounds() {
|
|
let args = [1usize, 2, 3];
|
|
|
|
// Test with valid arguments
|
|
let result = kernel::syscalls::dispatch(0x1000, &args);
|
|
assert!(result <= 0); // Should return error (no process context)
|
|
|
|
// Test with empty arguments
|
|
let result = kernel::syscalls::dispatch(0x1000, &[]);
|
|
assert!(result <= 0);
|
|
|
|
// Test with maximum arguments
|
|
let args = [1usize, 2, 3, 4, 5, 6, 7, 8];
|
|
let result = kernel::syscalls::dispatch(0x1000, &args);
|
|
assert!(result <= 0);
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod epoll_tests {
|
|
use super::*;
|
|
|
|
/// Test epoll_create syscall
|
|
#[test]
|
|
fn test_epoll_create() {
|
|
// Test epoll_create with size parameter
|
|
let args = [128u64]; // size parameter
|
|
let result = kernel::syscalls::dispatch(0xA000, &args);
|
|
// Should return a file descriptor (positive) or error
|
|
// In test environment, might return error if not initialized
|
|
assert!(result != 0 || result < 0);
|
|
}
|
|
|
|
/// Test epoll_create1 syscall
|
|
#[test]
|
|
fn test_epoll_create1() {
|
|
// Test epoll_create1 with flags
|
|
let args = [0u64]; // flags = 0
|
|
let result = kernel::syscalls::dispatch(0xA001, &args);
|
|
assert!(result != 0 || result < 0);
|
|
}
|
|
|
|
/// Test epoll_ctl syscall
|
|
#[test]
|
|
fn test_epoll_ctl() {
|
|
// Test epoll_ctl with invalid epfd (should fail)
|
|
let args = [999u64, 1u64, 1u64, 0u64]; // epfd, op, fd, event_ptr
|
|
let result = kernel::syscalls::dispatch(0xA002, &args);
|
|
// Should return error for invalid epfd
|
|
assert!(result < 0);
|
|
}
|
|
|
|
/// Test epoll_wait syscall
|
|
#[test]
|
|
fn test_epoll_wait() {
|
|
// Test epoll_wait with invalid epfd
|
|
let args = [999u64, 0u64, 10u64, 0u64]; // epfd, events, maxevents, timeout
|
|
let result = kernel::syscalls::dispatch(0xA003, &args);
|
|
// Should return error for invalid epfd
|
|
assert!(result < 0);
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod filesystem_tests {
|
|
use super::*;
|
|
|
|
/// Test symlink syscall error handling
|
|
#[test]
|
|
fn test_symlink_error_handling() {
|
|
// Test with null pointers (should return EFAULT)
|
|
let args = [0u64, 0u64]; // null pointers
|
|
let result = kernel::syscalls::dispatch(0x7008, &args);
|
|
assert!(result < 0); // Should return error
|
|
}
|
|
|
|
/// Test readlink syscall error handling
|
|
#[test]
|
|
fn test_readlink_error_handling() {
|
|
// Test with null pointers (should return EFAULT)
|
|
let args = [0u64, 0u64, 0u64]; // null pointers, zero size
|
|
let result = kernel::syscalls::dispatch(0x7009, &args);
|
|
assert!(result < 0); // Should return error
|
|
}
|
|
|
|
/// Test fcntl syscall error handling
|
|
#[test]
|
|
fn test_fcntl_error_handling() {
|
|
// Test with invalid file descriptor
|
|
let args = [999u64, 3u64, 0u64]; // invalid fd, F_GETFL, arg
|
|
let result = kernel::syscalls::dispatch(0x200A, &args);
|
|
assert!(result < 0); // Should return EBADF
|
|
}
|
|
}
|