use rsproperties::{PROP_DIRNAME, PROP_VALUE_MAX};
#[cfg(not(target_os = "android"))]
use std::sync::Once;
#[cfg(not(target_os = "android"))]
static INIT_ONCE: Once = Once::new();
fn ensure_init() {
#[cfg(not(target_os = "android"))]
INIT_ONCE.call_once(|| {
let props_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("__properties__");
let config = rsproperties::PropertyConfig::with_properties_dir(props_dir);
rsproperties::init(config);
});
}
#[test]
fn test_constants_are_correct() {
assert_eq!(
PROP_VALUE_MAX, 92,
"PROP_VALUE_MAX should match Android standard"
);
assert_eq!(
PROP_DIRNAME, "/dev/__properties__",
"PROP_DIRNAME should match Android default"
);
println!("✓ API constants are correct");
}
#[test]
fn test_get_with_default_functionality() {
ensure_init();
let result = rsproperties::get_or(
"test.nonexistent.property.12345",
"default_value".to_string(),
);
assert_eq!(
result, "default_value",
"Should return default value for non-existent properties"
);
let result = rsproperties::get_or("test.another.nonexistent", "".to_string());
assert_eq!(result, "", "Should return empty default value");
let test_cases = [
("test.case1", "simple"),
("test.case2", "with spaces"),
("test.case3", "with.dots.and.numbers.123"),
("test.case4", "special!@#$%chars"),
];
for (prop, default) in &test_cases {
let result = rsproperties::get_or(prop, default.to_string());
assert_eq!(result, *default, "Should return default for {prop}");
}
println!("✓ get_or functionality works correctly");
}
#[test]
fn test_get_nonexistent_properties() {
ensure_init();
let nonexistent_props = [
"test.definitely.not.there",
"fake.property.name",
"test.12345.67890",
"nonexistent.prop.with.long.name.that.should.not.exist",
];
for prop in &nonexistent_props {
let result: Result<String, _> = rsproperties::get(prop);
assert!(
result.is_err(),
"Getting non-existent property '{prop}' should return error"
);
}
println!("✓ get returns errors for non-existent properties");
}
#[test]
fn test_dirname_function() {
ensure_init();
let dirname = rsproperties::properties_dir();
let dirname_str = dirname.to_string_lossy();
assert!(!dirname_str.is_empty(), "dirname should not be empty");
assert!(
dirname_str.contains("properties") || dirname_str.starts_with("/"),
"dirname should be a valid path, got: {dirname_str}"
);
println!("✓ dirname function returns: {dirname_str}");
}
#[test]
fn test_property_name_validation() {
ensure_init();
let valid_names = [
"ro.build.version.sdk",
"sys.boot_completed",
"persist.sys.timezone",
"test.property",
"a.b.c.d.e.f.g",
"prop123",
];
for name in &valid_names {
let _result: Result<String, _> = rsproperties::get(name);
println!("Tested property name: {name}");
}
let invalid_names = [
"", ".", "..", "name.", ".name", ];
for name in &invalid_names {
let result: Result<String, _> = rsproperties::get(name);
println!("Tested invalid name '{}': {:?}", name, result.is_err());
}
println!("✓ Property name validation tests completed");
}
#[test]
fn test_thread_safety() {
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;
ensure_init();
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = vec![];
for i in 0..10 {
let counter_clone = Arc::clone(&counter);
let handle = thread::spawn(move || {
for j in 0..5 {
let prop_name = format!("test.thread.{i}.{j}");
let _result: String = rsproperties::get_or(&prop_name, "default".to_string());
counter_clone.fetch_add(1, Ordering::SeqCst);
let _result: Result<String, _> = rsproperties::get(&prop_name);
counter_clone.fetch_add(1, Ordering::SeqCst);
let _dirname = rsproperties::properties_dir();
counter_clone.fetch_add(1, Ordering::SeqCst);
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("Thread should complete successfully");
}
let final_count = counter.load(Ordering::SeqCst);
assert_eq!(
final_count,
10 * 5 * 3,
"All thread operations should complete"
);
println!("✓ Thread safety test completed with {final_count} operations");
}
#[test]
fn test_property_value_length_constraints() {
let max_length_value = "x".repeat(PROP_VALUE_MAX);
assert_eq!(max_length_value.len(), PROP_VALUE_MAX);
let too_long_value = "x".repeat(PROP_VALUE_MAX + 1);
assert_eq!(too_long_value.len(), PROP_VALUE_MAX + 1);
ensure_init();
let result1 = rsproperties::get_or("test.max.length", max_length_value.clone());
assert_eq!(result1, max_length_value);
let result2 = rsproperties::get_or("test.too.long", too_long_value.clone());
assert_eq!(result2, too_long_value);
println!("✓ Property value length constraint tests passed");
}
#[cfg(feature = "builder")]
mod builder_tests {
use super::*;
#[test]
fn test_set_property_basic() {
ensure_init();
let result = rsproperties::set("test.basic.property", "test_value");
match result {
Ok(_) => {
println!("✓ Property set successfully");
let value: Result<String, _> = rsproperties::get("test.basic.property");
assert_eq!(value.unwrap(), "test_value");
println!("✓ Property read back successfully");
}
Err(e) => {
println!("⚠ Property set failed (expected without property service): {e}");
}
}
}
#[test]
fn test_set_property_various_values() {
ensure_init();
let test_cases = [
("test.empty", ""),
("test.simple", "value"),
("test.numbers", "12345"),
("test.special", "value with spaces!@#"),
("test.unicode", "üñíçødé"),
];
for (prop, value) in &test_cases {
match rsproperties::set(prop, value) {
Ok(_) => println!("✓ Set property {prop} = {value}"),
Err(e) => println!("⚠ Failed to set property {prop}: {e}"),
}
}
}
#[test]
fn test_set_property_max_length() {
ensure_init();
let max_value = "x".repeat(PROP_VALUE_MAX);
let result = rsproperties::set("test.max.length", &max_value);
match result {
Ok(_) => println!("✓ Successfully set property with max length"),
Err(e) => println!("⚠ Failed to set max length property: {e}"),
}
}
#[test]
fn test_set_property_too_long() {
ensure_init();
let too_long_value = "x".repeat(PROP_VALUE_MAX + 1);
let result = rsproperties::set("test.too.long", &too_long_value);
assert!(
result.is_err(),
"Setting property with value too long should fail"
);
println!("✓ Correctly rejected property value that is too long");
}
#[test]
fn test_property_update() {
ensure_init();
let prop_name = "test.update.property";
match rsproperties::set(prop_name, "initial") {
Ok(_) => {
println!("✓ Set initial property value");
match rsproperties::set(prop_name, "updated") {
Ok(_) => {
println!("✓ Updated property value");
let value: Result<String, _> = rsproperties::get(prop_name);
assert_eq!(value.unwrap(), "updated");
println!("✓ Property update verified");
}
Err(e) => println!("⚠ Property update failed: {e}"),
}
}
Err(e) => println!("⚠ Initial property set failed: {e}"),
}
}
#[test]
fn test_concurrent_property_operations() {
use std::thread;
ensure_init();
let mut handles = vec![];
for i in 0..5 {
let handle = thread::spawn(move || {
let prop_name = format!("test.concurrent.{i}");
let prop_value = format!("value_{i}");
match rsproperties::set(&prop_name, &prop_value) {
Ok(_) => {
println!("Thread {i}: Set property {prop_name} = {prop_value}");
let value: Result<String, _> = rsproperties::get(&prop_name);
println!("Thread {i}: Read back: {value:?}");
}
Err(e) => println!("Thread {i}: Set failed: {e}"),
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("Thread should complete");
}
println!("✓ Concurrent property operations completed");
}
}
#[test]
fn test_error_handling() {
ensure_init();
let long_name = "test.".repeat(100) + "property";
let result: Result<String, _> = rsproperties::get(&long_name);
println!("Long property name test: {:?}", result.is_err());
let result: Result<String, _> = rsproperties::get("");
println!("Empty property name test: {:?}", result.is_err());
println!("✓ Error handling tests completed");
}
#[test]
fn test_real_android_properties() {
ensure_init();
let common_props = [
"ro.build.version.sdk",
"ro.build.version.release",
"ro.product.model",
"ro.product.manufacturer",
"sys.boot_completed",
"persist.sys.timezone",
];
for prop in &common_props {
match rsproperties::get::<String>(prop) {
Ok(value) => println!("Found property {prop} = {value}"),
Err(_) => {
let default_value = rsproperties::get_or(prop, "not_found".to_string());
println!("Property {prop} not found, default: {default_value}");
}
}
}
println!("✓ Real Android properties test completed");
}