use carla::{
client::{ActorBase, Client, Vehicle},
geom::{Location, Rotation, Transform, Vector3D},
};
type TestResult = Result<(), Box<dyn std::error::Error>>;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Utility Functions Tests ===\n");
let client = Client::connect("127.0.0.1", 2000, None)?;
let mut world = client.world()?;
println!("Connected to CARLA server\n");
let vehicle = setup_scenario(&mut world);
let mut passed = 0;
let mut failed = 0;
println!("--- Transform Operations ---");
run_test(
"test_transform_multiplication",
test_transform_multiplication,
&mut passed,
&mut failed,
);
run_test(
"test_transform_inverse",
test_transform_inverse,
&mut passed,
&mut failed,
);
run_test(
"test_transform_point",
test_transform_point,
&mut passed,
&mut failed,
);
run_test(
"test_transform_vector",
test_transform_vector,
&mut passed,
&mut failed,
);
run_test(
"test_transform_identity",
test_transform_identity,
&mut passed,
&mut failed,
);
run_test(
"test_transform_chain",
test_transform_chain,
&mut passed,
&mut failed,
);
run_test(
"test_transform_parent_child",
|| test_transform_parent_child(&vehicle),
&mut passed,
&mut failed,
);
run_test(
"test_transform_world_to_local",
test_transform_world_to_local,
&mut passed,
&mut failed,
);
run_test(
"test_transform_rotation_only",
test_transform_rotation_only,
&mut passed,
&mut failed,
);
run_test(
"test_transform_translation_only",
test_transform_translation_only,
&mut passed,
&mut failed,
);
println!("\n--- String and Utility Operations ---");
run_test(
"test_actor_id_from_string",
|| test_actor_id_from_string(&vehicle),
&mut passed,
&mut failed,
);
run_test(
"test_string_to_actor_id_invalid",
test_string_to_actor_id_invalid,
&mut passed,
&mut failed,
);
run_test(
"test_episode_id_equality",
|| test_episode_id_equality(&world),
&mut passed,
&mut failed,
);
run_test(
"test_episode_id_string_conversion",
|| test_episode_id_string_conversion(&world),
&mut passed,
&mut failed,
);
run_test(
"test_get_client_version",
test_get_client_version,
&mut passed,
&mut failed,
);
run_test(
"test_get_server_version",
|| test_get_server_version(&world),
&mut passed,
&mut failed,
);
run_test(
"test_version_comparison",
test_version_comparison,
&mut passed,
&mut failed,
);
run_test(
"test_environment_variable_access",
test_environment_variable_access,
&mut passed,
&mut failed,
);
run_test(
"test_debug_flag_checking",
test_debug_flag_checking,
&mut passed,
&mut failed,
);
run_test(
"test_timeout_config",
|| test_timeout_config(&client),
&mut passed,
&mut failed,
);
println!("\n=== Results ===");
println!("Passed: {}", passed);
println!("Failed: {}", failed);
std::process::exit(if failed > 0 { 1 } else { 0 });
}
fn setup_scenario(world: &mut carla::client::World) -> Vehicle {
println!("Setting up test scenario...");
let blueprint_library = world
.blueprint_library()
.expect("Failed to get blueprint library");
let vehicle_bp = blueprint_library
.find("vehicle.tesla.model3")
.expect("Failed to query blueprint")
.expect("Vehicle blueprint not found");
let spawn_points = world
.map()
.expect("Failed to get map")
.recommended_spawn_points()
.expect("Failed to get spawn points");
for (i, spawn_point) in spawn_points.iter().take(10).enumerate() {
if let Ok(vehicle_actor) = world.spawn_actor(&vehicle_bp, spawn_point)
&& let Ok(vehicle) = Vehicle::try_from(vehicle_actor)
{
println!("Vehicle spawned at spawn point {}", i);
println!("Scenario setup complete\n");
return vehicle;
}
}
panic!(
"Failed to spawn vehicle at any of the first 10 spawn points. Try running scripts/clean-carla-world.py"
);
}
fn run_test<F>(name: &str, test_fn: F, passed: &mut i32, failed: &mut i32)
where
F: FnOnce() -> TestResult,
{
print!("Testing {}... ", name);
match test_fn() {
Ok(_) => {
println!("✓ PASS");
*passed += 1;
}
Err(e) => {
println!("✗ FAIL: {}", e);
*failed += 1;
}
}
}
fn test_transform_multiplication() -> TestResult {
let t1 = Transform {
location: Location::new(1.0, 2.0, 3.0),
rotation: Rotation::new(0.0, 0.0, 0.0),
};
let t2 = Transform {
location: Location::new(4.0, 5.0, 6.0),
rotation: Rotation::new(0.0, 0.0, 0.0),
};
let combined = Transform {
location: t1.location + t2.location,
rotation: t1.rotation,
};
assert!((combined.location.x - 5.0).abs() < 0.001);
assert!((combined.location.y - 7.0).abs() < 0.001);
assert!((combined.location.z - 9.0).abs() < 0.001);
Ok(())
}
fn test_transform_inverse() -> TestResult {
let transform = Transform {
location: Location::new(10.0, 20.0, 30.0),
rotation: Rotation::new(0.0, 0.0, 0.0),
};
let inverse = Transform {
location: Location::new(
-transform.location.x,
-transform.location.y,
-transform.location.z,
),
rotation: Rotation::new(
-transform.rotation.pitch,
-transform.rotation.yaw,
-transform.rotation.roll,
),
};
assert!((inverse.location.x + 10.0).abs() < 0.001);
Ok(())
}
fn test_transform_point() -> TestResult {
let transform = Transform {
location: Location::new(5.0, 10.0, 15.0),
rotation: Rotation::new(0.0, 0.0, 0.0),
};
let point = Location::new(1.0, 2.0, 3.0);
let transformed = point + transform.location;
assert!((transformed.x - 6.0).abs() < 0.001);
assert!((transformed.y - 12.0).abs() < 0.001);
assert!((transformed.z - 18.0).abs() < 0.001);
Ok(())
}
fn test_transform_vector() -> TestResult {
let vector = Vector3D::new(1.0, 0.0, 0.0);
assert!((vector.length() - 1.0).abs() < 0.001);
Ok(())
}
fn test_transform_identity() -> TestResult {
let identity = Transform {
location: Location::new(0.0, 0.0, 0.0),
rotation: Rotation::new(0.0, 0.0, 0.0),
};
let point = Location::new(5.0, 10.0, 15.0);
let transformed = point + identity.location;
assert_eq!(transformed.x, point.x);
assert_eq!(transformed.y, point.y);
assert_eq!(transformed.z, point.z);
Ok(())
}
fn test_transform_chain() -> TestResult {
let t1 = Transform {
location: Location::new(1.0, 0.0, 0.0),
rotation: Rotation::new(0.0, 0.0, 0.0),
};
let t2 = Transform {
location: Location::new(0.0, 1.0, 0.0),
rotation: Rotation::new(0.0, 0.0, 0.0),
};
let t3 = Transform {
location: Location::new(0.0, 0.0, 1.0),
rotation: Rotation::new(0.0, 0.0, 0.0),
};
let mut result = Location::new(0.0, 0.0, 0.0);
result += t1.location;
result += t2.location;
result += t3.location;
assert!((result.x - 1.0).abs() < 0.001);
assert!((result.y - 1.0).abs() < 0.001);
assert!((result.z - 1.0).abs() < 0.001);
Ok(())
}
fn test_transform_parent_child(vehicle: &Vehicle) -> TestResult {
let transform = vehicle.transform()?;
assert!(transform.location.x.is_finite());
assert!(transform.location.y.is_finite());
assert!(transform.location.z.is_finite());
Ok(())
}
fn test_transform_world_to_local() -> TestResult {
let world_point = Location::new(100.0, 200.0, 50.0);
let origin = Location::new(10.0, 20.0, 5.0);
let local = world_point - origin;
assert!((local.x - 90.0).abs() < 0.001);
assert!((local.y - 180.0).abs() < 0.001);
assert!((local.z - 45.0).abs() < 0.001);
Ok(())
}
fn test_transform_rotation_only() -> TestResult {
let rotation = Rotation::new(10.0, 20.0, 30.0);
let transform = Transform {
location: Location::new(0.0, 0.0, 0.0),
rotation,
};
assert_eq!(transform.rotation.pitch, 10.0);
assert_eq!(transform.rotation.yaw, 20.0);
assert_eq!(transform.rotation.roll, 30.0);
Ok(())
}
fn test_transform_translation_only() -> TestResult {
let location = Location::new(5.0, 10.0, 15.0);
let transform = Transform {
location,
rotation: Rotation::new(0.0, 0.0, 0.0),
};
assert_eq!(transform.location.x, 5.0);
assert_eq!(transform.location.y, 10.0);
assert_eq!(transform.location.z, 15.0);
Ok(())
}
fn test_actor_id_from_string(vehicle: &Vehicle) -> TestResult {
let id = vehicle.id();
assert!(id > 0);
Ok(())
}
fn test_string_to_actor_id_invalid() -> TestResult {
let invalid_id = 0u32;
assert_eq!(invalid_id, 0);
Ok(())
}
fn test_episode_id_equality(world: &carla::client::World) -> TestResult {
let episode = world.id()?;
let episode2 = world.id()?;
assert_eq!(episode, episode2);
Ok(())
}
fn test_episode_id_string_conversion(world: &carla::client::World) -> TestResult {
let _episode = world.id()?;
Ok(())
}
fn test_get_client_version() -> TestResult {
let client = Client::connect("127.0.0.1", 2000, None)?;
let version = client.client_version()?;
println!(" Client version: {}", version);
assert!(!version.is_empty(), "Client version should not be empty");
Ok(())
}
fn test_get_server_version(_world: &carla::client::World) -> TestResult {
let client = Client::connect("127.0.0.1", 2000, None)?;
let version = client.server_version()?;
println!(" Server version: {}", version);
assert!(!version.is_empty(), "Server version should not be empty");
Ok(())
}
fn test_version_comparison() -> TestResult {
let client = Client::connect("127.0.0.1", 2000, None)?;
let client_ver = client.client_version()?;
let server_ver = client.server_version()?;
println!(
" Comparing: client='{}' vs server='{}'",
client_ver, server_ver
);
assert!(!client_ver.is_empty());
assert!(!server_ver.is_empty());
assert!(
client_ver.contains('.') || client_ver.len() >= 8,
"Client version should be semantic (with '.') or git hash (8+ chars)"
);
assert!(
server_ver.contains('.'),
"Server version should contain '.'"
);
Ok(())
}
fn test_environment_variable_access() -> TestResult {
match std::env::var("CARLA_VERSION") {
Ok(ver) => {
println!(" CARLA_VERSION={}", ver);
assert!(!ver.is_empty());
}
Err(_) => {
let path = std::env::var("PATH").expect("PATH should exist");
assert!(!path.is_empty());
println!(" Environment access working (verified PATH)");
}
}
Ok(())
}
fn test_debug_flag_checking() -> TestResult {
match std::env::var("RUST_LOG") {
Ok(level) => {
println!(" RUST_LOG={}", level);
}
Err(_) => {
println!(" RUST_LOG not set (debug flags can be checked via std::env)");
}
}
#[cfg(debug_assertions)]
println!(" Running in debug mode");
#[cfg(not(debug_assertions))]
println!(" Running in release mode");
Ok(())
}
fn test_timeout_config(_client: &Client) -> TestResult {
use std::time::Duration;
let mut client = Client::connect("127.0.0.1", 2000, None)?;
let original_timeout = client.timeout()?;
println!(" Original timeout: {:?}", original_timeout);
assert!(
original_timeout.as_millis() > 0,
"Timeout should be positive"
);
let new_timeout = Duration::from_secs(10);
client.set_timeout(new_timeout)?;
let current_timeout = client.timeout()?;
println!(" New timeout: {:?}", current_timeout);
assert_eq!(current_timeout, new_timeout, "Timeout should be updated");
client.set_timeout(original_timeout)?;
let restored = client.timeout()?;
assert_eq!(restored, original_timeout, "Timeout should be restored");
Ok(())
}