diff --git a/.gitignore b/.gitignore index ea8c4bf..b154832 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +test_gen_project diff --git a/request.bin b/data/request.bin similarity index 100% rename from request.bin rename to data/request.bin diff --git a/src/lib.rs b/src/lib.rs index 4d2d139..00db91e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ pub mod proto_gen; pub mod generator; // Uncomment this to check if the code compiles -#[path = "../proto/google/protobuf/descriptor.rs"] -pub mod descriptor; +// #[path = "../proto/google/protobuf/descriptor.rs"] +// pub mod descriptor; use std::fmt; diff --git a/tests/build_generated_code.rs b/tests/build_generated_code.rs new file mode 100644 index 0000000..fb25bdb --- /dev/null +++ b/tests/build_generated_code.rs @@ -0,0 +1,77 @@ +use std::fs; +use std::process::Command; +use std::path::PathBuf; + +#[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 = roto::proto_gen::google::protobuf::descriptor::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::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 = roto::proto_gen::google::protobuf::descriptor::FileDescriptorSet::new(&set_buf) + .expect("Failed to create FileDescriptorSet"); + + let generated_code = roto::generator::generate_rust_code(&set); + assert!(!generated_code.is_empty(), "Generated code should not be empty"); + + // 2. Setup a temporary Cargo project to verify the code builds + let root = std::env::current_dir().expect("Failed to get current directory"); + let temp_project_dir = root.join("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", "test_gen_project"]) + .current_dir(&root) + .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 = {{ path = \"..\" }}", + cargo_toml_content + ); + 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` to reference the types in the dependency. + let final_code = generated_code.replace("use crate::", "use roto::"); + let lib_path = temp_project_dir.join("src/lib.rs"); + fs::write(lib_path, final_code).expect("Failed to write generated code to src/lib.rs"); + + // 5. Attempt to build the project + let build_status = Command::new("cargo") + .args(["build"]) + .current_dir(&temp_project_dir) + .status() + .expect("Failed to run cargo build"); + + assert!(build_status.success(), "The generated Rust code failed to build in a standalone project!"); +}