use roto_codegen::google::protobuf::compiler::plugin::CodeGeneratorRequest; use roto_codegen::google::protobuf::descriptor::FileDescriptorSet; use std::fs; use std::process::Command; #[test] fn test_generated_code_builds() { // 1. Generate Rust code from data/request.bin let request_path = "data/request.bin"; let data = fs::read(request_path).expect("Failed to read request.bin"); let request = CodeGeneratorRequest::new(&data).expect("Failed to parse CodeGeneratorRequest"); // Mimic the logic from protoc-gen-roto to build a FileDescriptorSet let mut set_buf = Vec::new(); for file_res in request.proto_file() { let (file_data, _) = file_res.expect("Failed to iterate proto_file"); // Tag 1, Length-delimited: (1 << 3) | 2 = 10 set_buf.push(10); // Write length as varint let len = file_data.len() as u64; let mut len_buf = [0u8; 10]; let len_size = roto_runtime::write_varint(len, &mut len_buf).expect("Failed to write varint length"); set_buf.extend_from_slice(&len_buf[..len_size]); // Write data set_buf.extend_from_slice(file_data); } let set = FileDescriptorSet::new(&set_buf).expect("Failed to create FileDescriptorSet"); let generated_files = roto_codegen::generator::generate_rust_code(&set, None, false); assert!( !generated_files.is_empty(), "Generated code should not be empty" ); // 2. Setup a temporary Cargo project to verify the code builds let codegen_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let project_root = codegen_root.parent().expect("Failed to get project root"); let temp_project_dir = std::path::PathBuf::from("/tmp/roto_test_gen_project"); // Clean up previous runs if temp_project_dir.exists() { fs::remove_dir_all(&temp_project_dir).expect("Failed to clean up temp project directory"); } // Create new library project let status = Command::new("cargo") .args(["new", "--lib", temp_project_dir.to_str().expect("Invalid path")]) .status() .expect("Failed to run cargo new"); assert!(status.success(), "cargo new failed"); // 3. Configure the project to depend on the current roto crate let cargo_toml_path = temp_project_dir.join("Cargo.toml"); let cargo_toml_content = fs::read_to_string(&cargo_toml_path).expect("Failed to read Cargo.toml"); let updated_cargo_toml = format!( "{}\n\nroto-codegen = {{ path = \"{}\" }}\nroto-runtime = {{ path = \"{}\" }}\nroto-tonic = {{ path = \"{}\" }}\nbytes = \"1.7\"\ntonic = \"0.12\"\ntokio-stream = \"0.1\"\ntower = \"0.4\"\nfutures-util = \"0.3\"\nhttp-body-util = \"0.1\"\nhttp-body = \"1.0\"\n\n[workspace]\n", cargo_toml_content, codegen_root.to_string_lossy(), project_root.join("runtime").to_string_lossy(), project_root.join("roto-tonic").to_string_lossy() ); fs::write(cargo_toml_path, updated_cargo_toml).expect("Failed to write Cargo.toml"); // 4. Write the generated code to src/lib.rs // The generated code uses `use crate::{...}`, but it's now in a separate crate. // Replace `crate` with `roto_tonic` to reference the types in the dependency. let mut all_code = String::new(); for (_, content) in generated_files { let replaced = content.replace("use crate::{BufferPool, StatusBody};", "use roto_tonic::{BufferPool, StatusBody};"); all_code.push_str(&replaced); all_code.push_str("\n"); } let lib_path = temp_project_dir.join("src/lib.rs"); fs::write(lib_path, all_code).expect("Failed to write generated code to src/lib.rs"); // 5. Attempt to build the project let build_output = Command::new("cargo") .args(["build"]) .current_dir(&temp_project_dir) .output() .expect("Failed to run cargo build"); if !build_output.status.success() { eprintln!("Cargo build failed output:\n{}", String::from_utf8_lossy(&build_output.stderr)); } assert!( build_output.status.success(), "The generated Rust code failed to build in a standalone project!" ); }