use rsproperties::{self, PROP_DIRNAME, PROP_VALUE_MAX};
mod common;
use common::init_test;
fn setup_edge_test_env() {
let _ = env_logger::builder().is_test(true).try_init();
init_test();
}
#[test]
fn test_api_constants() {
assert_eq!(
PROP_VALUE_MAX, 92,
"PROP_VALUE_MAX should be 92 bytes as per Android"
);
assert_eq!(
PROP_DIRNAME, "/dev/__properties__",
"PROP_DIRNAME should match Android default"
);
}
#[test]
fn test_empty_property_names() {
setup_edge_test_env();
let result: Result<String, _> = rsproperties::get("");
assert!(result.is_err(), "Getting empty property name should fail");
let default_result = rsproperties::get_or("", "default".to_string());
assert_eq!(
default_result, "default",
"Empty property should return default"
);
let whitespace_names = vec![" ", "\t", "\n", " ", "\t\n "];
for name in whitespace_names {
let result = rsproperties::get_or(name, "default".to_string());
assert_eq!(
result,
"default",
"Whitespace property '{}' should return default",
name.escape_debug()
);
}
}
#[test]
fn test_special_character_property_names() {
setup_edge_test_env();
let special_names = vec![
"prop.with.dots",
"prop_with_underscores",
"prop-with-dashes",
"prop123with456numbers",
"PROP.WITH.UPPERCASE",
"prop.with.MixedCase",
];
for name in special_names {
let result = rsproperties::get_or(name, "not_found".to_string());
assert_eq!(
result, "not_found",
"Special property name '{name}' should return default"
);
}
}
#[test]
fn test_invalid_property_names() {
setup_edge_test_env();
let potentially_invalid_names = vec![
"prop with spaces",
"prop\twith\ttabs",
"prop\nwith\nnewlines",
"prop=with=equals",
"prop:with:colons",
"prop;with;semicolons",
"prop/with/slashes",
"prop\\with\\backslashes",
"prop\"with\"quotes",
"prop'with'singles",
"prop<with>brackets",
"prop{with}braces",
"prop(with)parens",
"prop[with]squares",
"prop|with|pipes",
"prop&with&ersands",
"prop*with*asterisks",
"prop?with?questions",
"prop!with!exclamations",
"prop@with@ats",
"prop#with#hashes",
"prop$with$dollars",
"prop%with%percents",
"prop^with^carets",
"prop+with+pluses",
];
for name in potentially_invalid_names {
let result = rsproperties::get_or(name, "default".to_string());
println!("Property name '{}' -> '{}'", name.escape_debug(), result);
assert_eq!(result, "default");
}
}
#[test]
fn test_long_property_names() {
setup_edge_test_env();
let lengths = vec![100, 500, 1000, 2000];
for length in lengths {
let long_name = "a".repeat(length);
let result = rsproperties::get_or(&long_name, "default".to_string());
assert_eq!(
result, "default",
"Long property name ({length} chars) should return default"
);
println!("Tested property name of length {length}");
}
}
#[test]
fn test_property_value_edge_cases() {
setup_edge_test_env();
let test_values = vec![
("", "empty value"),
(" ", "single space"),
(" ", "multiple spaces"),
("\t", "tab character"),
("\n", "newline character"),
("\r", "carriage return"),
("\r\n", "CRLF"),
("value with spaces", "spaces in value"),
("value\twith\ttabs", "tabs in value"),
("\"quoted value\"", "quoted value"),
("'single quoted'", "single quoted"),
("value=with=equals", "equals in value"),
("value:with:colons", "colons in value"),
("value;with;semicolons", "semicolons in value"),
("value/with/slashes", "slashes in value"),
("value\\with\\backslashes", "backslashes in value"),
("value with unicode: 🚀🌟⭐", "unicode characters"),
("0", "zero"),
("false", "false string"),
("true", "true string"),
("-1", "negative number"),
("3.14159", "decimal number"),
("1e10", "scientific notation"),
];
for (i, (value, description)) in test_values.iter().enumerate() {
let prop_name = format!("edge.value.test.{i}");
match rsproperties::set(&prop_name, value) {
Ok(_) => {
let retrieved: Result<String, _> = rsproperties::get(&prop_name);
assert_eq!(
retrieved.unwrap(),
*value,
"Failed for {}: {}",
description,
value.escape_debug()
);
println!("✓ {}: '{}'", description, value.escape_debug());
}
Err(e) => {
println!(
"✗ Failed to set {}: {} - Error: {}",
description,
value.escape_debug(),
e
);
}
}
}
}
#[test]
#[cfg(feature = "builder")]
fn test_maximum_length_values() {
setup_edge_test_env();
let test_lengths = vec![
PROP_VALUE_MAX - 10,
PROP_VALUE_MAX - 5,
PROP_VALUE_MAX - 1,
PROP_VALUE_MAX,
];
for length in test_lengths {
let prop_name = format!("edge.maxlen.{length}");
let value = "x".repeat(length);
match rsproperties::set(&prop_name, &value) {
Ok(_) => {
let retrieved: Result<String, _> = rsproperties::get(&prop_name);
let retrieved_value = retrieved.unwrap();
assert_eq!(retrieved_value.len(), length);
assert_eq!(retrieved_value, value);
println!("✓ Successfully set/get property with {length} byte value");
}
Err(e) => {
println!("✗ Failed to set property with {length} byte value: {e}");
}
}
}
let oversized_value = "x".repeat(PROP_VALUE_MAX + 10);
let result = rsproperties::set("edge.oversized", &oversized_value);
match result {
Ok(_) => {
let retrieved: Result<String, _> = rsproperties::get("edge.oversized");
let retrieved_value = retrieved.unwrap();
println!(
"Oversized value handling: set {} bytes, retrieved {} bytes",
oversized_value.len(),
retrieved_value.len()
);
assert!(
retrieved_value.len() <= PROP_VALUE_MAX,
"Retrieved value should not exceed PROP_VALUE_MAX"
);
}
Err(e) => {
println!("Oversized value correctly rejected: {e}");
}
}
}
#[test]
fn test_error_handling() {
setup_edge_test_env();
let result: Result<String, _> = rsproperties::get("definitely.does.not.exist.anywhere");
assert!(
result.is_err(),
"Should return error for non-existent property"
);
if let Err(e) = result {
let error_msg = format!("{e}");
assert!(!error_msg.is_empty(), "Error message should not be empty");
println!("Error message for non-existent property: {error_msg}");
}
#[cfg(feature = "builder")]
{
let invalid_cases = vec![("", "some_value", "empty property name")];
for (name, value, description) in invalid_cases {
let result = rsproperties::set(name, value);
println!("Testing {description}: {result:?}");
if let Err(e) = result {
assert!(
!format!("{e}").is_empty(),
"Error message should not be empty for {description}"
);
}
}
}
}
#[test]
#[cfg(feature = "builder")]
fn test_null_bytes_and_special_chars() {
setup_edge_test_env();
let test_cases = vec![
("prop.with.null", "value\0with\0nulls"),
(
"prop.with.high.unicode",
"value with high unicode: \u{1F600}\u{1F601}",
),
(
"prop.with.control.chars",
"value\x01with\x02control\x03chars",
),
("prop.with.del", "value\x7Fwith\x7Fdel"),
("prop.with.escape", "value\x1Bwith\x1Bescape"),
];
for (prop_name, prop_value) in test_cases {
let result = rsproperties::set(prop_name, prop_value);
match result {
Ok(_) => {
let retrieved: Result<String, _> = rsproperties::get(prop_name);
let retrieved_value = retrieved.unwrap();
println!(
"✓ Special chars in '{}': {} bytes -> {} bytes",
prop_name,
prop_value.len(),
retrieved_value.len()
);
}
Err(e) => {
println!("✗ Failed to set '{prop_name}': {e}");
}
}
}
}