feat: Add support for new OS package
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run

- Add a new `sal-os` package containing OS interaction utilities.
- Update workspace members to include the new package.
- Add README and basic usage examples for the new package.
This commit is contained in:
Mahmoud-Emad
2025-06-21 15:45:43 +03:00
parent a35edc2030
commit c4cdb8126c
27 changed files with 1735 additions and 424 deletions

208
os/tests/download_tests.rs Normal file
View File

@@ -0,0 +1,208 @@
use sal_os::{download, DownloadError};
use std::fs;
use tempfile::TempDir;
#[test]
fn test_chmod_exec() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test_script.sh");
// Create a test file
fs::write(&test_file, "#!/bin/bash\necho 'test'").unwrap();
// Make it executable
let result = download::chmod_exec(test_file.to_str().unwrap());
assert!(result.is_ok());
// Check if file is executable (Unix only)
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let metadata = fs::metadata(&test_file).unwrap();
let permissions = metadata.permissions();
assert!(permissions.mode() & 0o111 != 0); // Check if any execute bit is set
}
}
#[test]
fn test_download_error_handling() {
let temp_dir = TempDir::new().unwrap();
// Test with invalid URL
let result = download::download("invalid-url", temp_dir.path().to_str().unwrap(), 0);
assert!(result.is_err());
// Test with non-existent domain
let result = download::download(
"https://nonexistentdomain12345.com/file.txt",
temp_dir.path().to_str().unwrap(),
0,
);
assert!(result.is_err());
}
#[test]
fn test_download_file_error_handling() {
let temp_dir = TempDir::new().unwrap();
let dest_file = temp_dir.path().join("downloaded_file.txt");
// Test with invalid URL
let result = download::download_file("invalid-url", dest_file.to_str().unwrap(), 0);
assert!(result.is_err());
// Test with non-existent domain
let result = download::download_file(
"https://nonexistentdomain12345.com/file.txt",
dest_file.to_str().unwrap(),
0,
);
assert!(result.is_err());
}
#[test]
fn test_download_install_error_handling() {
// Test with invalid URL
let result = download::download_install("invalid-url", 0);
assert!(result.is_err());
// Test with non-existent domain
let result = download::download_install("https://nonexistentdomain12345.com/package.deb", 0);
assert!(result.is_err());
}
#[test]
fn test_download_minimum_size_validation() {
let temp_dir = TempDir::new().unwrap();
// Test with a very high minimum size requirement that won't be met
// This should fail even if the URL exists
let result = download::download(
"https://httpbin.org/bytes/10", // This returns only 10 bytes
temp_dir.path().to_str().unwrap(),
1000, // Require 1000KB minimum
);
// This might succeed or fail depending on network, but we're testing the interface
// The important thing is that it doesn't panic
let _ = result;
}
#[test]
fn test_download_to_nonexistent_directory() {
// Test downloading to a directory that doesn't exist
// The download function should create parent directories
let temp_dir = TempDir::new().unwrap();
let nonexistent_dir = temp_dir.path().join("nonexistent").join("nested");
let _ = download::download(
"https://httpbin.org/status/404", // This will fail, but directory creation should work
nonexistent_dir.to_str().unwrap(),
0,
);
// The directory should be created even if download fails
assert!(nonexistent_dir.exists());
}
#[test]
fn test_chmod_exec_nonexistent_file() {
// Test chmod_exec on a file that doesn't exist
let result = download::chmod_exec("/nonexistent/path/file.sh");
assert!(result.is_err());
}
#[test]
fn test_download_file_path_validation() {
let _ = TempDir::new().unwrap();
// Test with invalid destination path
let result = download::download_file(
"https://httpbin.org/status/404",
"/invalid/path/that/does/not/exist/file.txt",
0,
);
assert!(result.is_err());
}
// Integration test that requires network access
// This test is marked with ignore so it doesn't run by default
#[test]
#[ignore]
fn test_download_real_file() {
let temp_dir = TempDir::new().unwrap();
// Download a small file from httpbin (a testing service)
let result = download::download(
"https://httpbin.org/bytes/100", // Returns 100 random bytes
temp_dir.path().to_str().unwrap(),
0,
);
if result.is_ok() {
// If download succeeded, verify the file exists
let downloaded_path = result.unwrap();
assert!(fs::metadata(&downloaded_path).is_ok());
// Verify file size is approximately correct
let metadata = fs::metadata(&downloaded_path).unwrap();
assert!(metadata.len() >= 90 && metadata.len() <= 110); // Allow some variance
}
// If download failed (network issues), that's okay for this test
}
// Integration test for download_file
#[test]
#[ignore]
fn test_download_file_real() {
let temp_dir = TempDir::new().unwrap();
let dest_file = temp_dir.path().join("test_download.bin");
// Download a small file to specific location
let result = download::download_file(
"https://httpbin.org/bytes/50",
dest_file.to_str().unwrap(),
0,
);
if result.is_ok() {
// Verify the file was created at the specified location
assert!(dest_file.exists());
// Verify file size
let metadata = fs::metadata(&dest_file).unwrap();
assert!(metadata.len() >= 40 && metadata.len() <= 60); // Allow some variance
}
}
#[test]
fn test_download_error_types() {
// DownloadError is already imported at the top
// Test that our error types can be created and displayed
let error = DownloadError::InvalidUrl("test".to_string());
assert!(!error.to_string().is_empty());
let error = DownloadError::DownloadFailed("test".to_string());
assert!(!error.to_string().is_empty());
let error = DownloadError::FileTooSmall(50, 100);
assert!(!error.to_string().is_empty());
}
#[test]
fn test_download_url_parsing() {
let temp_dir = TempDir::new().unwrap();
// Test with URL that has no filename
let result = download::download("https://example.com/", temp_dir.path().to_str().unwrap(), 0);
// Should fail with invalid URL error
assert!(result.is_err());
// Test with URL that has query parameters
let result = download::download(
"https://httpbin.org/get?param=value",
temp_dir.path().to_str().unwrap(),
0,
);
// This might succeed or fail depending on network, but shouldn't panic
let _ = result;
}

212
os/tests/fs_tests.rs Normal file
View File

@@ -0,0 +1,212 @@
use sal_os::fs;
use std::fs as std_fs;
use tempfile::TempDir;
#[test]
fn test_exist() {
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path();
// Test directory exists
assert!(fs::exist(temp_path.to_str().unwrap()));
// Test file doesn't exist
let non_existent = temp_path.join("non_existent.txt");
assert!(!fs::exist(non_existent.to_str().unwrap()));
// Create a file and test it exists
let test_file = temp_path.join("test.txt");
std_fs::write(&test_file, "test content").unwrap();
assert!(fs::exist(test_file.to_str().unwrap()));
}
#[test]
fn test_mkdir() {
let temp_dir = TempDir::new().unwrap();
let new_dir = temp_dir.path().join("new_directory");
// Directory shouldn't exist initially
assert!(!fs::exist(new_dir.to_str().unwrap()));
// Create directory
let result = fs::mkdir(new_dir.to_str().unwrap());
assert!(result.is_ok());
// Directory should now exist
assert!(fs::exist(new_dir.to_str().unwrap()));
// Creating existing directory should not error (defensive)
let result2 = fs::mkdir(new_dir.to_str().unwrap());
assert!(result2.is_ok());
}
#[test]
fn test_file_write_and_read() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test_write.txt");
let content = "Hello, World!";
// Write file
let write_result = fs::file_write(test_file.to_str().unwrap(), content);
assert!(write_result.is_ok());
// File should exist
assert!(fs::exist(test_file.to_str().unwrap()));
// Read file
let read_result = fs::file_read(test_file.to_str().unwrap());
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), content);
}
#[test]
fn test_file_write_append() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test_append.txt");
// Write initial content
let initial_content = "Line 1\n";
let append_content = "Line 2\n";
let write_result = fs::file_write(test_file.to_str().unwrap(), initial_content);
assert!(write_result.is_ok());
// Append content
let append_result = fs::file_write_append(test_file.to_str().unwrap(), append_content);
assert!(append_result.is_ok());
// Read and verify
let read_result = fs::file_read(test_file.to_str().unwrap());
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), format!("{}{}", initial_content, append_content));
}
#[test]
fn test_file_size() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test_size.txt");
let content = "Hello, World!"; // 13 bytes
// Write file
fs::file_write(test_file.to_str().unwrap(), content).unwrap();
// Check size
let size_result = fs::file_size(test_file.to_str().unwrap());
assert!(size_result.is_ok());
assert_eq!(size_result.unwrap(), 13);
}
#[test]
fn test_delete() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test_delete.txt");
// Create file
fs::file_write(test_file.to_str().unwrap(), "test").unwrap();
assert!(fs::exist(test_file.to_str().unwrap()));
// Delete file
let delete_result = fs::delete(test_file.to_str().unwrap());
assert!(delete_result.is_ok());
// File should no longer exist
assert!(!fs::exist(test_file.to_str().unwrap()));
// Deleting non-existent file should not error (defensive)
let delete_result2 = fs::delete(test_file.to_str().unwrap());
assert!(delete_result2.is_ok());
}
#[test]
fn test_copy() {
let temp_dir = TempDir::new().unwrap();
let source_file = temp_dir.path().join("source.txt");
let dest_file = temp_dir.path().join("dest.txt");
let content = "Copy test content";
// Create source file
fs::file_write(source_file.to_str().unwrap(), content).unwrap();
// Copy file
let copy_result = fs::copy(source_file.to_str().unwrap(), dest_file.to_str().unwrap());
assert!(copy_result.is_ok());
// Destination should exist and have same content
assert!(fs::exist(dest_file.to_str().unwrap()));
let dest_content = fs::file_read(dest_file.to_str().unwrap()).unwrap();
assert_eq!(dest_content, content);
}
#[test]
fn test_mv() {
let temp_dir = TempDir::new().unwrap();
let source_file = temp_dir.path().join("source_mv.txt");
let dest_file = temp_dir.path().join("dest_mv.txt");
let content = "Move test content";
// Create source file
fs::file_write(source_file.to_str().unwrap(), content).unwrap();
// Move file
let mv_result = fs::mv(source_file.to_str().unwrap(), dest_file.to_str().unwrap());
assert!(mv_result.is_ok());
// Source should no longer exist, destination should exist
assert!(!fs::exist(source_file.to_str().unwrap()));
assert!(fs::exist(dest_file.to_str().unwrap()));
// Destination should have same content
let dest_content = fs::file_read(dest_file.to_str().unwrap()).unwrap();
assert_eq!(dest_content, content);
}
#[test]
fn test_which() {
// Test with a command that should exist on most systems
let result = fs::which("ls");
assert!(!result.is_empty());
// Test with a command that shouldn't exist
let result = fs::which("nonexistentcommand12345");
assert!(result.is_empty());
}
#[test]
fn test_find_files() {
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path();
// Create test files
fs::file_write(&temp_path.join("test1.txt").to_string_lossy(), "content1").unwrap();
fs::file_write(&temp_path.join("test2.txt").to_string_lossy(), "content2").unwrap();
fs::file_write(&temp_path.join("other.log").to_string_lossy(), "log content").unwrap();
// Find .txt files
let txt_files = fs::find_files(temp_path.to_str().unwrap(), "*.txt");
assert!(txt_files.is_ok());
let files = txt_files.unwrap();
assert_eq!(files.len(), 2);
// Find all files
let all_files = fs::find_files(temp_path.to_str().unwrap(), "*");
assert!(all_files.is_ok());
let files = all_files.unwrap();
assert!(files.len() >= 3); // At least our 3 files
}
#[test]
fn test_find_dirs() {
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path();
// Create test directories
fs::mkdir(&temp_path.join("dir1").to_string_lossy()).unwrap();
fs::mkdir(&temp_path.join("dir2").to_string_lossy()).unwrap();
fs::mkdir(&temp_path.join("subdir").to_string_lossy()).unwrap();
// Find directories
let dirs = fs::find_dirs(temp_path.to_str().unwrap(), "dir*");
assert!(dirs.is_ok());
let found_dirs = dirs.unwrap();
assert!(found_dirs.len() >= 2); // At least dir1 and dir2
}

366
os/tests/package_tests.rs Normal file
View File

@@ -0,0 +1,366 @@
use sal_os::package::{PackHero, Platform};
#[test]
fn test_pack_hero_creation() {
// Test that we can create a PackHero instance
let hero = PackHero::new();
// Test that platform detection works
let platform = hero.platform();
match platform {
Platform::Ubuntu | Platform::MacOS | Platform::Unknown => {
// All valid platforms
}
}
}
#[test]
fn test_platform_detection() {
let hero = PackHero::new();
let platform = hero.platform();
// Platform should be deterministic
let platform2 = hero.platform();
assert_eq!(format!("{:?}", platform), format!("{:?}", platform2));
// Test platform display
match platform {
Platform::Ubuntu => {
assert_eq!(format!("{:?}", platform), "Ubuntu");
}
Platform::MacOS => {
assert_eq!(format!("{:?}", platform), "MacOS");
}
Platform::Unknown => {
assert_eq!(format!("{:?}", platform), "Unknown");
}
}
}
#[test]
fn test_debug_mode() {
let mut hero = PackHero::new();
// Test setting debug mode
hero.set_debug(true);
hero.set_debug(false);
// Debug mode setting should not panic
}
#[test]
fn test_package_operations_error_handling() {
let hero = PackHero::new();
// Test with invalid package name
let result = hero.is_installed("nonexistent-package-12345-xyz");
// This should return a result (either Ok(false) or Err)
// Validate that we get a proper result type
match result {
Ok(is_installed) => {
// Should return false for non-existent package
assert!(
!is_installed,
"Non-existent package should not be reported as installed"
);
}
Err(_) => {
// Error is also acceptable (e.g., no package manager available)
// The important thing is it doesn't panic
}
}
// Test install with invalid package
let result = hero.install("nonexistent-package-12345-xyz");
// This should return an error
assert!(result.is_err());
// Test remove with invalid package
let result = hero.remove("nonexistent-package-12345-xyz");
// This might succeed (if package wasn't installed) or fail
// Validate that we get a proper result type
match result {
Ok(_) => {
// Success is acceptable (package wasn't installed)
}
Err(err) => {
// Error is also acceptable
// Verify error message is meaningful
let error_msg = err.to_string();
assert!(!error_msg.is_empty(), "Error message should not be empty");
}
}
}
#[test]
fn test_package_search_basic() {
let hero = PackHero::new();
// Test search with empty query
let result = hero.search("");
// Should handle empty query gracefully
// Validate that we get a proper result type
match result {
Ok(packages) => {
// Empty search might return all packages or empty list
// Verify the result is a valid vector
assert!(
packages.len() < 50000,
"Empty search returned unreasonably large result"
);
}
Err(err) => {
// Error is acceptable for empty query
let error_msg = err.to_string();
assert!(!error_msg.is_empty(), "Error message should not be empty");
}
}
// Test search with very specific query that likely won't match
let result = hero.search("nonexistent-package-xyz-12345");
if let Ok(packages) = result {
// If search succeeded, it should return a vector
// The vector should be valid (we can get its length)
let _count = packages.len();
// Search results should be reasonable (not absurdly large)
assert!(
packages.len() < 10000,
"Search returned unreasonably large result set"
);
}
// If search failed, that's also acceptable
}
#[test]
fn test_package_list_basic() {
let hero = PackHero::new();
// Test listing installed packages
let result = hero.list_installed();
if let Ok(packages) = result {
// If listing succeeded, it should return a vector
// On most systems, there should be at least some packages installed
println!("Found {} installed packages", packages.len());
}
// If listing failed (e.g., no package manager available), that's acceptable
}
#[test]
fn test_package_update_basic() {
let hero = PackHero::new();
// Test package list update
let result = hero.update();
// This might succeed or fail depending on permissions and network
// Validate that we get a proper result type
match result {
Ok(_) => {
// Success is good - package list was updated
}
Err(err) => {
// Error is acceptable (no permissions, no network, etc.)
let error_msg = err.to_string();
assert!(!error_msg.is_empty(), "Error message should not be empty");
// Common error patterns we expect
let error_lower = error_msg.to_lowercase();
assert!(
error_lower.contains("permission")
|| error_lower.contains("network")
|| error_lower.contains("command")
|| error_lower.contains("not found")
|| error_lower.contains("failed"),
"Error message should indicate a reasonable failure cause: {}",
error_msg
);
}
}
}
#[test]
#[ignore] // Skip by default as this can take a very long time and modify the system
fn test_package_upgrade_basic() {
let hero = PackHero::new();
// Test package upgrade (this is a real system operation)
let result = hero.upgrade();
// Validate that we get a proper result type
match result {
Ok(_) => {
// Success means packages were upgraded
println!("Package upgrade completed successfully");
}
Err(err) => {
// Error is acceptable (no permissions, no packages to upgrade, etc.)
let error_msg = err.to_string();
assert!(!error_msg.is_empty(), "Error message should not be empty");
println!("Package upgrade failed as expected: {}", error_msg);
}
}
}
#[test]
fn test_package_upgrade_interface() {
// Test that the upgrade interface works without actually upgrading
let hero = PackHero::new();
// Verify that PackHero has the upgrade method and it returns the right type
// This tests the interface without performing the actual upgrade
let _upgrade_fn = PackHero::upgrade;
// Test that we can call upgrade (it will likely fail due to permissions/network)
// but we're testing that the interface works correctly
let result = hero.upgrade();
// The result should be a proper Result type
match result {
Ok(_) => {
// Upgrade succeeded (unlikely in test environment)
}
Err(err) => {
// Expected in most test environments
// Verify error is meaningful
let error_msg = err.to_string();
assert!(!error_msg.is_empty(), "Error should have a message");
assert!(error_msg.len() > 5, "Error message should be descriptive");
}
}
}
// Platform-specific tests
#[cfg(target_os = "linux")]
#[test]
fn test_linux_platform_detection() {
let hero = PackHero::new();
let platform = hero.platform();
// On Linux, should detect Ubuntu or Unknown (if not Ubuntu-based)
match platform {
Platform::Ubuntu | Platform::Unknown => {
// Expected on Linux
}
Platform::MacOS => {
panic!("Should not detect macOS on Linux system");
}
}
}
#[cfg(target_os = "macos")]
#[test]
fn test_macos_platform_detection() {
let hero = PackHero::new();
let platform = hero.platform();
// On macOS, should detect MacOS
match platform {
Platform::MacOS => {
// Expected on macOS
}
Platform::Ubuntu | Platform::Unknown => {
panic!("Should detect macOS on macOS system, got {:?}", platform);
}
}
}
// Integration tests that require actual package managers
// These are marked with ignore so they don't run by default
#[test]
#[ignore]
fn test_real_package_check() {
let hero = PackHero::new();
// Test with a package that's commonly installed
#[cfg(target_os = "linux")]
let test_package = "bash";
#[cfg(target_os = "macos")]
let test_package = "bash";
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
let test_package = "unknown";
let result = hero.is_installed(test_package);
if let Ok(is_installed) = result {
println!("Package '{}' is installed: {}", test_package, is_installed);
} else {
println!(
"Failed to check if '{}' is installed: {:?}",
test_package, result
);
}
}
#[test]
#[ignore]
fn test_real_package_search() {
let hero = PackHero::new();
// Search for a common package
let result = hero.search("git");
if let Ok(packages) = result {
println!("Found {} packages matching 'git'", packages.len());
if !packages.is_empty() {
println!(
"First few matches: {:?}",
&packages[..std::cmp::min(5, packages.len())]
);
}
} else {
println!("Package search failed: {:?}", result);
}
}
#[test]
#[ignore]
fn test_real_package_list() {
let hero = PackHero::new();
// List installed packages
let result = hero.list_installed();
if let Ok(packages) = result {
println!("Total installed packages: {}", packages.len());
if !packages.is_empty() {
println!(
"First few packages: {:?}",
&packages[..std::cmp::min(10, packages.len())]
);
}
} else {
println!("Package listing failed: {:?}", result);
}
}
#[test]
fn test_platform_enum_properties() {
// Test that Platform enum can be compared
assert_eq!(Platform::Ubuntu, Platform::Ubuntu);
assert_eq!(Platform::MacOS, Platform::MacOS);
assert_eq!(Platform::Unknown, Platform::Unknown);
assert_ne!(Platform::Ubuntu, Platform::MacOS);
assert_ne!(Platform::Ubuntu, Platform::Unknown);
assert_ne!(Platform::MacOS, Platform::Unknown);
}
#[test]
fn test_pack_hero_multiple_instances() {
// Test that multiple PackHero instances work correctly
let hero1 = PackHero::new();
let hero2 = PackHero::new();
// Both should detect the same platform
assert_eq!(
format!("{:?}", hero1.platform()),
format!("{:?}", hero2.platform())
);
// Both should handle debug mode independently
let mut hero1_mut = hero1;
let mut hero2_mut = hero2;
hero1_mut.set_debug(true);
hero2_mut.set_debug(false);
// No assertions here since debug mode doesn't have observable effects in tests
// But this ensures the API works correctly
}

199
os/tests/platform_tests.rs Normal file
View File

@@ -0,0 +1,199 @@
use sal_os::platform;
#[test]
fn test_platform_detection_consistency() {
// Test that platform detection functions return consistent results
let is_osx = platform::is_osx();
let is_linux = platform::is_linux();
// On any given system, only one of these should be true
// (or both false if running on Windows or other OS)
if is_osx {
assert!(!is_linux, "Cannot be both macOS and Linux");
}
if is_linux {
assert!(!is_osx, "Cannot be both Linux and macOS");
}
}
#[test]
fn test_architecture_detection_consistency() {
// Test that architecture detection functions return consistent results
let is_arm = platform::is_arm();
let is_x86 = platform::is_x86();
// On any given system, only one of these should be true
// (or both false if running on other architectures)
if is_arm {
assert!(!is_x86, "Cannot be both ARM and x86");
}
if is_x86 {
assert!(!is_arm, "Cannot be both x86 and ARM");
}
}
#[test]
fn test_platform_functions_return_bool() {
// Test that all platform detection functions return boolean values
let _: bool = platform::is_osx();
let _: bool = platform::is_linux();
let _: bool = platform::is_arm();
let _: bool = platform::is_x86();
}
#[cfg(target_os = "macos")]
#[test]
fn test_macos_detection() {
// When compiled for macOS, is_osx should return true
assert!(platform::is_osx());
assert!(!platform::is_linux());
}
#[cfg(target_os = "linux")]
#[test]
fn test_linux_detection() {
// When compiled for Linux, is_linux should return true
assert!(platform::is_linux());
assert!(!platform::is_osx());
}
#[cfg(target_arch = "aarch64")]
#[test]
fn test_arm_detection() {
// When compiled for ARM64, is_arm should return true
assert!(platform::is_arm());
assert!(!platform::is_x86());
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_x86_detection() {
// When compiled for x86_64, is_x86 should return true
assert!(platform::is_x86());
assert!(!platform::is_arm());
}
#[test]
fn test_check_linux_x86() {
let result = platform::check_linux_x86();
// The result should depend on the current platform
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
{
assert!(result.is_ok(), "Should succeed on Linux x86_64");
}
#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
{
assert!(result.is_err(), "Should fail on non-Linux x86_64 platforms");
// Check that the error message is meaningful
let error = result.unwrap_err();
let error_string = error.to_string();
assert!(error_string.contains("Linux x86_64"),
"Error message should mention Linux x86_64: {}", error_string);
}
}
#[test]
fn test_check_macos_arm() {
let result = platform::check_macos_arm();
// The result should depend on the current platform
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
{
assert!(result.is_ok(), "Should succeed on macOS ARM");
}
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
{
assert!(result.is_err(), "Should fail on non-macOS ARM platforms");
// Check that the error message is meaningful
let error = result.unwrap_err();
let error_string = error.to_string();
assert!(error_string.contains("macOS ARM"),
"Error message should mention macOS ARM: {}", error_string);
}
}
#[test]
fn test_platform_error_creation() {
use sal_os::platform::PlatformError;
// Test that we can create platform errors
let error = PlatformError::new("Test Error", "This is a test error message");
let error_string = error.to_string();
assert!(error_string.contains("Test Error"));
assert!(error_string.contains("This is a test error message"));
}
#[test]
fn test_platform_error_display() {
use sal_os::platform::PlatformError;
// Test error display formatting
let error = PlatformError::Generic("Category".to_string(), "Message".to_string());
let error_string = format!("{}", error);
assert!(error_string.contains("Category"));
assert!(error_string.contains("Message"));
}
#[test]
fn test_platform_error_debug() {
use sal_os::platform::PlatformError;
// Test error debug formatting
let error = PlatformError::Generic("Category".to_string(), "Message".to_string());
let debug_string = format!("{:?}", error);
assert!(debug_string.contains("Generic"));
assert!(debug_string.contains("Category"));
assert!(debug_string.contains("Message"));
}
#[test]
fn test_platform_functions_are_deterministic() {
// Platform detection should be deterministic - same result every time
let osx1 = platform::is_osx();
let osx2 = platform::is_osx();
assert_eq!(osx1, osx2);
let linux1 = platform::is_linux();
let linux2 = platform::is_linux();
assert_eq!(linux1, linux2);
let arm1 = platform::is_arm();
let arm2 = platform::is_arm();
assert_eq!(arm1, arm2);
let x86_1 = platform::is_x86();
let x86_2 = platform::is_x86();
assert_eq!(x86_1, x86_2);
}
#[test]
fn test_platform_check_functions_consistency() {
// The check functions should be consistent with the individual detection functions
let is_linux_x86 = platform::is_linux() && platform::is_x86();
let check_linux_x86_result = platform::check_linux_x86().is_ok();
assert_eq!(is_linux_x86, check_linux_x86_result);
let is_macos_arm = platform::is_osx() && platform::is_arm();
let check_macos_arm_result = platform::check_macos_arm().is_ok();
assert_eq!(is_macos_arm, check_macos_arm_result);
}
#[test]
fn test_current_platform_info() {
// Print current platform info for debugging (this will show in test output with --nocapture)
println!("Current platform detection:");
println!(" is_osx(): {}", platform::is_osx());
println!(" is_linux(): {}", platform::is_linux());
println!(" is_arm(): {}", platform::is_arm());
println!(" is_x86(): {}", platform::is_x86());
println!(" check_linux_x86(): {:?}", platform::check_linux_x86());
println!(" check_macos_arm(): {:?}", platform::check_macos_arm());
}

View File

@@ -0,0 +1,111 @@
// 01_file_operations.rhai
// Tests for file system operations in the OS module
// Custom assert function
fn assert_true(condition, message) {
if !condition {
print(`ASSERTION FAILED: ${message}`);
throw message;
}
}
// Create a test directory structure
let test_dir = "rhai_test_fs";
let sub_dir = test_dir + "/subdir";
// Test mkdir function
print("Testing mkdir...");
let mkdir_result = mkdir(test_dir);
assert_true(exist(test_dir), "Directory creation failed");
print(`✓ mkdir: ${mkdir_result}`);
// Test nested directory creation
let nested_result = mkdir(sub_dir);
assert_true(exist(sub_dir), "Nested directory creation failed");
print(`✓ mkdir (nested): ${nested_result}`);
// Test file_write function
let test_file = test_dir + "/test.txt";
let file_content = "This is a test file created by Rhai test script.";
let write_result = file_write(test_file, file_content);
assert_true(exist(test_file), "File creation failed");
print(`✓ file_write: ${write_result}`);
// Test file_read function
let read_content = file_read(test_file);
assert_true(read_content == file_content, "File content doesn't match");
print(`✓ file_read: Content matches`);
// Test file_size function
let size = file_size(test_file);
assert_true(size > 0, "File size should be greater than 0");
print(`✓ file_size: ${size} bytes`);
// Test file_write_append function
let append_content = "\nThis is appended content.";
let append_result = file_write_append(test_file, append_content);
let new_content = file_read(test_file);
assert_true(new_content == file_content + append_content, "Appended content doesn't match");
print(`✓ file_write_append: ${append_result}`);
// Test copy function
let copied_file = test_dir + "/copied.txt";
let copy_result = copy(test_file, copied_file);
assert_true(exist(copied_file), "File copy failed");
print(`✓ copy: ${copy_result}`);
// Test mv function
let moved_file = test_dir + "/moved.txt";
let mv_result = mv(copied_file, moved_file);
assert_true(exist(moved_file), "File move failed");
assert_true(!exist(copied_file), "Source file still exists after move");
print(`✓ mv: ${mv_result}`);
// Test find_file function
let found_file = find_file(test_dir, "*.txt");
assert_true(found_file.contains("test.txt") || found_file.contains("moved.txt"), "find_file failed");
print(`✓ find_file: ${found_file}`);
// Test find_files function
let found_files = find_files(test_dir, "*.txt");
assert_true(found_files.len() == 2, "find_files should find 2 files");
print(`✓ find_files: Found ${found_files.len()} files`);
// Test find_dir function
let found_dir = find_dir(test_dir, "sub*");
assert_true(found_dir.contains("subdir"), "find_dir failed");
print(`✓ find_dir: ${found_dir}`);
// Test find_dirs function
let found_dirs = find_dirs(test_dir, "sub*");
assert_true(found_dirs.len() == 1, "find_dirs should find 1 directory");
print(`✓ find_dirs: Found ${found_dirs.len()} directories`);
// Test chdir function
// Save current directory path before changing
let chdir_result = chdir(test_dir);
print(`✓ chdir: ${chdir_result}`);
// Change back to parent directory
chdir("..");
// Test rsync function (if available)
let rsync_dir = test_dir + "/rsync_dest";
mkdir(rsync_dir);
let rsync_result = rsync(test_dir, rsync_dir);
print(`✓ rsync: ${rsync_result}`);
// Test delete function
let delete_file_result = delete(test_file);
assert_true(!exist(test_file), "File deletion failed");
print(`✓ delete (file): ${delete_file_result}`);
// Clean up
delete(moved_file);
delete(sub_dir);
delete(rsync_dir);
delete(test_dir);
assert_true(!exist(test_dir), "Directory deletion failed");
print(`✓ delete (directory): Directory cleaned up`);
print("All file system tests completed successfully!");

View File

@@ -0,0 +1,53 @@
// 02_download_operations.rhai
// Tests for download operations in the OS module
// Custom assert function
fn assert_true(condition, message) {
if !condition {
print(`ASSERTION FAILED: ${message}`);
throw message;
}
}
// Create a test directory
let test_dir = "rhai_test_download";
mkdir(test_dir);
print(`Created test directory: ${test_dir}`);
// Test which function to ensure curl is available
let curl_path = which("curl");
if curl_path == "" {
print("Warning: curl not found, download tests may fail");
} else {
print(`✓ which: curl found at ${curl_path}`);
}
// Test cmd_ensure_exists function
let ensure_result = cmd_ensure_exists("curl");
print(`✓ cmd_ensure_exists: ${ensure_result}`);
// Test download function with a small file
let download_url = "https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT";
let download_dest = test_dir + "/license.txt";
let min_size_kb = 1; // Minimum size in KB
print(`Downloading ${download_url}...`);
let download_result = download_file(download_url, download_dest, min_size_kb);
assert_true(exist(download_dest), "Download failed");
print(`✓ download_file: ${download_result}`);
// Verify the downloaded file
let file_content = file_read(download_dest);
assert_true(file_content.contains("Permission is hereby granted"), "Downloaded file content is incorrect");
print("✓ Downloaded file content verified");
// Test chmod_exec function
let chmod_result = chmod_exec(download_dest);
print(`✓ chmod_exec: ${chmod_result}`);
// Clean up
delete(test_dir);
assert_true(!exist(test_dir), "Directory deletion failed");
print(`✓ Cleanup: Directory ${test_dir} removed`);
print("All download tests completed successfully!");

View File

@@ -0,0 +1,56 @@
// 03_package_operations.rhai
// Tests for package management operations in the OS module
// Custom assert function
fn assert_true(condition, message) {
if !condition {
print(`ASSERTION FAILED: ${message}`);
throw message;
}
}
// Test package_platform function
let platform = package_platform();
print(`Current platform: ${platform}`);
// Test package_set_debug function
let debug_enabled = package_set_debug(true);
assert_true(debug_enabled, "Debug mode should be enabled");
print("✓ package_set_debug: Debug mode enabled");
// Disable debug mode for remaining tests
package_set_debug(false);
// Test package_is_installed function with a package that should exist on most systems
let common_packages = ["bash", "curl", "grep"];
let found_package = false;
for pkg in common_packages {
let is_installed = package_is_installed(pkg);
if is_installed {
print(`✓ package_is_installed: ${pkg} is installed`);
found_package = true;
break;
}
}
if !found_package {
print("Warning: None of the common packages were found installed");
}
// Test package_search function with a common term
// Note: This might be slow and produce a lot of output
print("Testing package_search (this might take a moment)...");
let search_results = package_search("lib");
print(`✓ package_search: Found ${search_results.len()} packages containing 'lib'`);
// Test package_list function
// Note: This might be slow and produce a lot of output
print("Testing package_list (this might take a moment)...");
let installed_packages = package_list();
print(`✓ package_list: Found ${installed_packages.len()} installed packages`);
// Note: We're not testing package_install, package_remove, package_update, or package_upgrade
// as they require root privileges and could modify the system state
print("All package management tests completed successfully!");

View File

@@ -0,0 +1,148 @@
// run_all_tests.rhai
// Runs all OS module tests
print("=== Running OS Module Tests ===");
// Custom assert function
fn assert_true(condition, message) {
if !condition {
print(`ASSERTION FAILED: ${message}`);
throw message;
}
}
// Run each test directly
let passed = 0;
let failed = 0;
// Test 1: File Operations
print("\n--- Running File Operations Tests ---");
try {
// Create a test directory structure
let test_dir = "rhai_test_fs";
let sub_dir = test_dir + "/subdir";
// Test mkdir function
print("Testing mkdir...");
let mkdir_result = mkdir(test_dir);
assert_true(exist(test_dir), "Directory creation failed");
print(`✓ mkdir: ${mkdir_result}`);
// Test nested directory creation
let nested_result = mkdir(sub_dir);
assert_true(exist(sub_dir), "Nested directory creation failed");
print(`✓ mkdir (nested): ${nested_result}`);
// Test file_write function
let test_file = test_dir + "/test.txt";
let file_content = "This is a test file created by Rhai test script.";
let write_result = file_write(test_file, file_content);
assert_true(exist(test_file), "File creation failed");
print(`✓ file_write: ${write_result}`);
// Test file_read function
let read_content = file_read(test_file);
assert_true(read_content == file_content, "File content doesn't match");
print(`✓ file_read: Content matches`);
// Test file_size function
let size = file_size(test_file);
assert_true(size > 0, "File size should be greater than 0");
print(`✓ file_size: ${size} bytes`);
// Clean up
delete(test_file);
delete(sub_dir);
delete(test_dir);
assert_true(!exist(test_dir), "Directory deletion failed");
print(`✓ delete: Directory cleaned up`);
print("--- File Operations Tests completed successfully ---");
passed += 1;
} catch(err) {
print(`!!! Error in File Operations Tests: ${err}`);
failed += 1;
}
// Test 2: Download Operations
print("\n--- Running Download Operations Tests ---");
try {
// Create a test directory
let test_dir = "rhai_test_download";
mkdir(test_dir);
print(`Created test directory: ${test_dir}`);
// Test which function to ensure curl is available
let curl_path = which("curl");
if curl_path == "" {
print("Warning: curl not found, download tests may fail");
} else {
print(`✓ which: curl found at ${curl_path}`);
}
// Test cmd_ensure_exists function
let ensure_result = cmd_ensure_exists("curl");
print(`✓ cmd_ensure_exists: ${ensure_result}`);
// Test download function with a small file
let download_url = "https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT";
let download_dest = test_dir + "/license.txt";
let min_size_kb = 1; // Minimum size in KB
print(`Downloading ${download_url}...`);
let download_result = download_file(download_url, download_dest, min_size_kb);
assert_true(exist(download_dest), "Download failed");
print(`✓ download_file: ${download_result}`);
// Verify the downloaded file
let file_content = file_read(download_dest);
assert_true(file_content.contains("Permission is hereby granted"), "Downloaded file content is incorrect");
print("✓ Downloaded file content verified");
// Clean up
delete(test_dir);
assert_true(!exist(test_dir), "Directory deletion failed");
print(`✓ Cleanup: Directory ${test_dir} removed`);
print("--- Download Operations Tests completed successfully ---");
passed += 1;
} catch(err) {
print(`!!! Error in Download Operations Tests: ${err}`);
failed += 1;
}
// Test 3: Package Operations
print("\n--- Running Package Operations Tests ---");
try {
// Test package_platform function
let platform = package_platform();
print(`Current platform: ${platform}`);
// Test package_set_debug function
let debug_enabled = package_set_debug(true);
assert_true(debug_enabled, "Debug mode should be enabled");
print("✓ package_set_debug: Debug mode enabled");
// Disable debug mode for remaining tests
package_set_debug(false);
print("--- Package Operations Tests completed successfully ---");
passed += 1;
} catch(err) {
print(`!!! Error in Package Operations Tests: ${err}`);
failed += 1;
}
print("\n=== Test Summary ===");
print(`Passed: ${passed}`);
print(`Failed: ${failed}`);
print(`Total: ${passed + failed}`);
if failed == 0 {
print("\n✅ All tests passed!");
} else {
print("\n❌ Some tests failed!");
}
// Return the number of failed tests (0 means success)
failed;

View File

@@ -0,0 +1,364 @@
use rhai::Engine;
use sal_os::rhai::register_os_module;
use tempfile::TempDir;
fn create_test_engine() -> Engine {
let mut engine = Engine::new();
register_os_module(&mut engine).expect("Failed to register OS module");
engine
}
#[test]
fn test_rhai_module_registration() {
// Test that the OS module can be registered without errors
let _engine = create_test_engine();
// If we get here without panicking, the module was registered successfully
// We can't easily test function registration without calling the functions
}
#[test]
fn test_rhai_file_operations() {
let engine = create_test_engine();
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path().to_str().unwrap();
// Test file operations through Rhai
let script = format!(
r#"
let test_dir = "{}/test_rhai";
let test_file = test_dir + "/test.txt";
let content = "Hello from Rhai!";
// Create directory
mkdir(test_dir);
// Check if directory exists
let dir_exists = exist(test_dir);
// Write file
file_write(test_file, content);
// Check if file exists
let file_exists = exist(test_file);
// Read file
let read_content = file_read(test_file);
// Return results
#{{"dir_exists": dir_exists, "file_exists": file_exists, "content_match": read_content == content}}
"#,
temp_path
);
let result: rhai::Map = engine.eval(&script).expect("Script execution failed");
assert_eq!(result["dir_exists"].as_bool().unwrap(), true);
assert_eq!(result["file_exists"].as_bool().unwrap(), true);
assert_eq!(result["content_match"].as_bool().unwrap(), true);
}
#[test]
fn test_rhai_file_size() {
let engine = create_test_engine();
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path().to_str().unwrap();
let script = format!(
r#"
let test_file = "{}/size_test.txt";
let content = "12345"; // 5 bytes
file_write(test_file, content);
let size = file_size(test_file);
size
"#,
temp_path
);
let result: i64 = engine.eval(&script).expect("Script execution failed");
assert_eq!(result, 5);
}
#[test]
fn test_rhai_file_append() {
let engine = create_test_engine();
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path().to_str().unwrap();
let script = format!(
r#"
let test_file = "{}/append_test.txt";
file_write(test_file, "Line 1\n");
file_write_append(test_file, "Line 2\n");
let content = file_read(test_file);
content
"#,
temp_path
);
let result: String = engine.eval(&script).expect("Script execution failed");
assert_eq!(result, "Line 1\nLine 2\n");
}
#[test]
fn test_rhai_copy_and_move() {
let engine = create_test_engine();
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path().to_str().unwrap();
let script = format!(
r#"
let source = "{}/source.txt";
let copy_dest = "{}/copy.txt";
let move_dest = "{}/moved.txt";
let content = "Test content";
// Create source file
file_write(source, content);
// Copy file
copy(source, copy_dest);
// Move the copy
mv(copy_dest, move_dest);
// Check results
let source_exists = exist(source);
let copy_exists = exist(copy_dest);
let move_exists = exist(move_dest);
let move_content = file_read(move_dest);
#{{"source_exists": source_exists, "copy_exists": copy_exists, "move_exists": move_exists, "content_match": move_content == content}}
"#,
temp_path, temp_path, temp_path
);
let result: rhai::Map = engine.eval(&script).expect("Script execution failed");
assert_eq!(result["source_exists"].as_bool().unwrap(), true);
assert_eq!(result["copy_exists"].as_bool().unwrap(), false); // Should be moved
assert_eq!(result["move_exists"].as_bool().unwrap(), true);
assert_eq!(result["content_match"].as_bool().unwrap(), true);
}
#[test]
fn test_rhai_delete() {
let engine = create_test_engine();
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path().to_str().unwrap();
let script = format!(
r#"
let test_file = "{}/delete_test.txt";
// Create file
file_write(test_file, "content");
let exists_before = exist(test_file);
// Delete file
delete(test_file);
let exists_after = exist(test_file);
#{{"before": exists_before, "after": exists_after}}
"#,
temp_path
);
let result: rhai::Map = engine.eval(&script).expect("Script execution failed");
assert_eq!(result["before"].as_bool().unwrap(), true);
assert_eq!(result["after"].as_bool().unwrap(), false);
}
#[test]
fn test_rhai_find_files() {
let engine = create_test_engine();
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path().to_str().unwrap();
let script = format!(
r#"
let test_dir = "{}/find_test";
mkdir(test_dir);
// Create test files
file_write(test_dir + "/file1.txt", "content1");
file_write(test_dir + "/file2.txt", "content2");
file_write(test_dir + "/other.log", "log content");
// Find .txt files
let txt_files = find_files(test_dir, "*.txt");
let all_files = find_files(test_dir, "*");
#{{"txt_count": txt_files.len(), "all_count": all_files.len()}}
"#,
temp_path
);
let result: rhai::Map = engine.eval(&script).expect("Script execution failed");
assert_eq!(result["txt_count"].as_int().unwrap(), 2);
assert!(result["all_count"].as_int().unwrap() >= 3);
}
#[test]
fn test_rhai_which_command() {
let engine = create_test_engine();
let script = r#"
let ls_path = which("ls");
let nonexistent = which("nonexistentcommand12345");
#{"ls_found": ls_path.len() > 0, "nonexistent_found": nonexistent.len() > 0}
"#;
let result: rhai::Map = engine.eval(script).expect("Script execution failed");
assert_eq!(result["ls_found"].as_bool().unwrap(), true);
assert_eq!(result["nonexistent_found"].as_bool().unwrap(), false);
}
#[test]
fn test_rhai_error_handling() {
let engine = create_test_engine();
// Test that errors are properly propagated to Rhai
// Instead of try-catch, just test that the function call fails
let script = r#"file_read("/nonexistent/path/file.txt")"#;
let result = engine.eval::<String>(script);
assert!(
result.is_err(),
"Expected error when reading non-existent file"
);
}
#[test]
fn test_rhai_package_functions() {
let engine = create_test_engine();
// Test that package functions are registered by calling them
let script = r#"
let platform = package_platform();
let debug_result = package_set_debug(true);
#{"platform": platform, "debug": debug_result}
"#;
let result: rhai::Map = engine.eval(script).expect("Script execution failed");
// Platform should be a non-empty string
let platform: String = result["platform"].clone().try_cast().unwrap();
assert!(!platform.is_empty());
// Debug setting should return true
assert_eq!(result["debug"].as_bool().unwrap(), true);
}
#[test]
fn test_rhai_download_functions() {
let engine = create_test_engine();
// Test that download functions are registered by calling them
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path().to_str().unwrap();
let script = format!(
r#"
let test_file = "{}/test_script.sh";
// Create a test script
file_write(test_file, "echo 'test'");
// Make it executable
try {{
let result = chmod_exec(test_file);
result.len() >= 0 // chmod_exec returns a string, so check if it's valid
}} catch {{
false
}}
"#,
temp_path
);
let result: bool = engine.eval(&script).expect("Script execution failed");
assert!(result);
}
#[test]
fn test_rhai_array_returns() {
let engine = create_test_engine();
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path().to_str().unwrap();
let script = format!(
r#"
let test_dir = "{}/array_test";
mkdir(test_dir);
// Create some files
file_write(test_dir + "/file1.txt", "content");
file_write(test_dir + "/file2.txt", "content");
// Test that find_files returns an array
let files = find_files(test_dir, "*.txt");
// Test array operations
let count = files.len();
let first_file = if count > 0 {{ files[0] }} else {{ "" }};
#{{"count": count, "has_files": count > 0, "first_file_exists": first_file.len() > 0}}
"#,
temp_path
);
let result: rhai::Map = engine.eval(&script).expect("Script execution failed");
assert_eq!(result["count"].as_int().unwrap(), 2);
assert_eq!(result["has_files"].as_bool().unwrap(), true);
assert_eq!(result["first_file_exists"].as_bool().unwrap(), true);
}
#[test]
fn test_rhai_platform_functions() {
let engine = create_test_engine();
let script = r#"
let is_osx = platform_is_osx();
let is_linux = platform_is_linux();
let is_arm = platform_is_arm();
let is_x86 = platform_is_x86();
// Test that platform detection is consistent
let platform_consistent = !(is_osx && is_linux);
let arch_consistent = !(is_arm && is_x86);
#{"osx": is_osx, "linux": is_linux, "arm": is_arm, "x86": is_x86, "platform_consistent": platform_consistent, "arch_consistent": arch_consistent}
"#;
let result: rhai::Map = engine.eval(script).expect("Script execution failed");
// Verify platform detection consistency
assert_eq!(result["platform_consistent"].as_bool().unwrap(), true);
assert_eq!(result["arch_consistent"].as_bool().unwrap(), true);
// At least one platform should be detected
let osx = result["osx"].as_bool().unwrap();
let linux = result["linux"].as_bool().unwrap();
// At least one architecture should be detected
let arm = result["arm"].as_bool().unwrap();
let x86 = result["x86"].as_bool().unwrap();
// Print current platform for debugging
println!(
"Platform detection: OSX={}, Linux={}, ARM={}, x86={}",
osx, linux, arm, x86
);
}