Refactor crate into multiple subcrates
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
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 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-codegen = {{ path = \"..\" }}\nroto-runtime = {{ path = \"../../runtime\" }}\n\n[workspace]\n",
|
||||
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 mut all_code = String::new();
|
||||
for (_, content) in generated_files {
|
||||
all_code.push_str(&content);
|
||||
all_code.push_str("\n");
|
||||
}
|
||||
let final_code = all_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!"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
use roto_codegen::generator::generate_rust_code;
|
||||
use roto_codegen::google::protobuf::descriptor::FileDescriptorSet;
|
||||
use std::fs;
|
||||
|
||||
#[test]
|
||||
fn test_nested_proto_generation_contains_modules() {
|
||||
let request_path = "data/request.bin";
|
||||
if !std::path::Path::new(request_path).exists() {
|
||||
panic!("data/request.bin not found. This test requires the sample request binary.");
|
||||
}
|
||||
|
||||
let data = fs::read(request_path).expect("Failed to read request.bin");
|
||||
|
||||
// The existing test logic to build a FileDescriptorSet from CodeGeneratorRequest
|
||||
// We can simplify this by just wrapping the data if it's already a FileDescriptorSet,
|
||||
// but request.bin is usually a CodeGeneratorRequest.
|
||||
|
||||
// Let's use the same logic as build_generated_code.rs to get a FileDescriptorSet
|
||||
let request =
|
||||
roto_codegen::google::protobuf::compiler::plugin::CodeGeneratorRequest::new(&data)
|
||||
.expect("Failed to parse CodeGeneratorRequest");
|
||||
|
||||
let mut set_buf = Vec::new();
|
||||
for file_res in request.proto_file() {
|
||||
let (file_data, _) = file_res.expect("Failed to iterate proto_file");
|
||||
set_buf.push(10);
|
||||
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]);
|
||||
set_buf.extend_from_slice(file_data);
|
||||
}
|
||||
let set = FileDescriptorSet::new(&set_buf).expect("Failed to create FileDescriptorSet");
|
||||
|
||||
let generated_files = generate_rust_code(&set, None, false);
|
||||
|
||||
let all_code: String = generated_files
|
||||
.into_iter()
|
||||
.map(|(_, content)| content)
|
||||
.collect();
|
||||
println!("Generated Code:\n{}", all_code);
|
||||
|
||||
// We want to see if any message has a nested module.
|
||||
// Since we don't know exactly what's in request.bin, we'll look for ANY 'pub mod' inside the generated code
|
||||
// that isn't at the top level (though the generator puts them inside the message definition).
|
||||
assert!(
|
||||
all_code.contains("pub mod "),
|
||||
"Generated code should contain at least one nested module for nested types"
|
||||
);
|
||||
assert!(
|
||||
all_code.contains("pub struct "),
|
||||
"Generated code should contain structs"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
use roto_codegen::generator::generate_rust_code;
|
||||
use roto_codegen::google::protobuf::compiler::plugin::CodeGeneratorRequest;
|
||||
use roto_codegen::google::protobuf::descriptor::FileDescriptorSet;
|
||||
use std::fs;
|
||||
|
||||
fn load_generated_code() -> String {
|
||||
let data = fs::read("data/request.bin").expect("Failed to read data/request.bin");
|
||||
let request = CodeGeneratorRequest::new(&data).expect("Failed to parse CodeGeneratorRequest");
|
||||
|
||||
let mut set_buf = Vec::new();
|
||||
for file_res in request.proto_file() {
|
||||
let (file_data, _) = file_res.expect("Failed to iterate proto_file");
|
||||
set_buf.push(10u8);
|
||||
let len = file_data.len() as u64;
|
||||
let mut len_buf = [0u8; 10];
|
||||
let len_size = roto_runtime::write_varint(len, &mut len_buf).unwrap();
|
||||
set_buf.extend_from_slice(&len_buf[..len_size]);
|
||||
set_buf.extend_from_slice(file_data);
|
||||
}
|
||||
let set = FileDescriptorSet::new(&set_buf).expect("Failed to create FileDescriptorSet");
|
||||
|
||||
generate_rust_code(&set, None, false)
|
||||
.into_iter()
|
||||
.map(|(_, content)| content)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_structs_have_written_flags() {
|
||||
let code = load_generated_code();
|
||||
assert!(
|
||||
code.contains("_written: bool"),
|
||||
"Builder structs should contain `_written: bool` fields for each proto field"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_constructor_initialises_written_flags_to_false() {
|
||||
let code = load_generated_code();
|
||||
assert!(
|
||||
code.contains("_written: false"),
|
||||
"Builder constructors should initialise every `_written` flag to false"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_setters_mark_field_as_written() {
|
||||
let code = load_generated_code();
|
||||
assert!(
|
||||
code.contains("_written = true"),
|
||||
"Each builder setter should set its `_written` flag to true"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_has_with_method() {
|
||||
let code = load_generated_code();
|
||||
assert!(
|
||||
code.contains("pub fn with("),
|
||||
"Each builder impl should expose a `with` method"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_structs_have_raw_fields_method() {
|
||||
let code = load_generated_code();
|
||||
assert!(
|
||||
code.contains("pub fn raw_fields("),
|
||||
"Each message struct impl should expose a `raw_fields` method"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_method_uses_write_raw() {
|
||||
let code = load_generated_code();
|
||||
assert!(
|
||||
code.contains("write_raw(raw_bytes)"),
|
||||
"The `with` method should call `write_raw` to copy field bytes"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user