mod common;
use common::init_test;
#[test]
fn test_library_constants() {
assert_eq!(
rsproperties::PROP_VALUE_MAX,
92,
"PROP_VALUE_MAX should be 92 bytes"
);
assert_eq!(
rsproperties::PROP_DIRNAME,
"/dev/__properties__",
"PROP_DIRNAME should match Android default"
);
println!("✓ Library constants are correct");
println!(" PROP_VALUE_MAX = {}", rsproperties::PROP_VALUE_MAX);
println!(" PROP_DIRNAME = {}", rsproperties::PROP_DIRNAME);
}
#[test]
fn init_test_and_dirname() {
init_test();
let dirname = rsproperties::properties_dir();
assert!(
!dirname.to_string_lossy().is_empty(),
"dirname should not be empty after init"
);
println!("✓ init() and dirname() work correctly");
println!(" Property directory: {dirname:?}");
}
#[test]
fn test_get_with_default_functionality() {
init_test();
let result = rsproperties::get_or(
"test.nonexistent.property.12345",
"default_value".to_string(),
);
assert_eq!(
result, "default_value",
"Should return default for non-existent property"
);
let result = rsproperties::get_or("", "empty_default".to_string());
assert_eq!(
result, "empty_default",
"Should return default for empty property name"
);
let long_name = "a".repeat(500);
let result = rsproperties::get_or(&long_name, "long_default".to_string());
assert_eq!(
result, "long_default",
"Should return default for very long property name"
);
println!("✓ get_or() works correctly for non-existent properties");
}
#[test]
fn test_get_functionality() {
init_test();
let result: Result<String, _> = rsproperties::get("test.nonexistent.property.67890");
assert!(
result.is_err(),
"Should return error for non-existent property"
);
println!("✓ get() returns error for non-existent properties");
}
#[cfg(feature = "builder")]
mod write_tests {
use super::*;
#[test]
fn test_set_functionality() {
init_test();
let result = rsproperties::set("test.property.name", "test_value");
match result {
Ok(_) => {
println!("✓ set() function succeeded");
let value: Result<String, _> = rsproperties::get("test.property.name");
assert_eq!(
value.unwrap(),
"test_value",
"Read value should match written value"
);
println!("✓ Property read/write cycle successful");
}
Err(e) => {
println!("⚠ set() function failed: {e}");
}
}
}
#[test]
fn test_set_property_edge_cases() {
init_test();
let result = rsproperties::set("test.empty.value", "");
match result {
Ok(_) => println!("✓ Setting empty value succeeded"),
Err(e) => println!("⚠ Setting empty value failed: {e}"),
}
let long_value = "x".repeat(rsproperties::PROP_VALUE_MAX + 10);
let result = rsproperties::set("test.long.value", &long_value);
match result {
Ok(_) => println!("✓ Setting long value succeeded"),
Err(e) => println!("⚠ Setting long value failed (expected): {e}"),
}
let result = rsproperties::set("test.special.chars!@#", "special_value");
match result {
Ok(_) => println!("✓ Setting property with special chars succeeded"),
Err(e) => println!("⚠ Setting property with special chars failed: {e}"),
}
}
}
#[test]
fn test_property_name_validation() {
init_test();
let edge_cases = vec![
("", "Empty name"),
(".", "Single dot"),
("..", "Double dots"),
("test..double.dots", "Double dots in middle"),
("test.property.with.many.dots", "Many dots"),
("CAPS_PROPERTY", "Capital letters"),
("property123", "Numbers"),
("property_with_underscores", "Underscores"),
(
"very.long.property.name.with.many.segments.to.test.limits",
"Long segmented name",
),
];
for (name, description) in edge_cases {
let result = rsproperties::get_or(name, "default".to_string());
assert_eq!(result, "default", "Should handle edge case: {description}");
}
println!("✓ Property name edge cases handled correctly");
}
#[test]
fn test_thread_safety() {
init_test();
use std::sync::Arc;
use std::sync::Barrier;
use std::thread;
let num_threads = 4;
let iterations = 100;
let barrier = Arc::new(Barrier::new(num_threads));
let handles: Vec<_> = (0..num_threads)
.map(|thread_id| {
let barrier = Arc::clone(&barrier);
thread::spawn(move || {
barrier.wait();
for i in 0..iterations {
let prop_name = format!("thread.{thread_id}.property.{i}");
let _result = rsproperties::get_or(&prop_name, "default".to_string());
let _result: Result<String, _> = rsproperties::get(&prop_name);
}
})
})
.collect();
for handle in handles {
handle.join().expect("Thread should complete successfully");
}
println!("✓ Thread safety test completed successfully");
println!(
" {} threads × {} iterations = {} total operations",
num_threads,
iterations,
num_threads * iterations
);
}
#[test]
fn test_api_completeness() {
init_test();
let _: String = rsproperties::get_or("test", "default".to_string());
let _ = rsproperties::get::<String>("test");
#[cfg(feature = "builder")]
{
let _: Result<(), _> = rsproperties::set("test", "value");
}
let _: std::path::PathBuf = rsproperties::properties_dir().to_path_buf();
let _: usize = rsproperties::PROP_VALUE_MAX;
let _: &str = rsproperties::PROP_DIRNAME;
println!("✓ All expected API functions are accessible");
#[cfg(feature = "builder")]
println!(" ✓ Builder feature enabled - write functions available");
#[cfg(not(feature = "builder"))]
println!(" ⚠ Builder feature disabled - write functions not available");
}
#[test]
fn test_error_handling_robustness() {
init_test();
let very_long_name = "x".repeat(10000);
let result = rsproperties::get_or(&very_long_name, "default".to_string());
assert_eq!(result, "default", "Should handle very long property names");
let null_name = "test\0property";
let result = rsproperties::get_or(null_name, "default".to_string());
assert_eq!(
result, "default",
"Should handle property names with null bytes"
);
let unicode_name = "test.🚀.property.世界";
let result = rsproperties::get_or(unicode_name, "default".to_string());
assert_eq!(result, "default", "Should handle Unicode property names");
println!("✓ Error handling robustness tests passed");
}
#[test]
fn test_performance_characteristics() {
init_test();
use std::time::Instant;
let start = Instant::now();
let iterations = 1000;
for i in 0..iterations {
let prop_name = format!("perf.test.property.{i}");
let _result = rsproperties::get_or(&prop_name, "default".to_string());
}
let duration = start.elapsed();
let avg_time_ns = duration.as_nanos() / iterations as u128;
println!("✓ Performance test completed");
println!(" {iterations} iterations in {duration:?}");
println!(" Average time per operation: {avg_time_ns} ns");
assert!(
avg_time_ns < 200_000,
"Operations should be reasonably fast"
);
}
#[test]
fn test_real_android_properties() {
init_test();
let common_properties = vec![
"ro.build.version.sdk",
"ro.build.version.release",
"ro.product.model",
"ro.product.manufacturer",
"ro.hardware",
"ro.board.platform",
"sys.boot_completed",
"init.svc.zygote",
];
let mut found_count = 0;
for prop in common_properties {
match rsproperties::get::<String>(prop) {
Ok(value) => {
found_count += 1;
println!(" {prop} = {value}");
}
Err(_) => {
let default_val = rsproperties::get_or(prop, "not_found".to_string());
assert_eq!(
default_val, "not_found",
"get_or should work even if get fails"
);
}
}
}
println!("✓ Real Android property tests completed");
println!(" Found {found_count} real properties");
if found_count == 0 {
println!(" ⚠ No real Android properties found (running on non-Android system)");
}
}