use std::fs;
use std::process::Command;
use tempfile::tempdir;
use polyxml_cli::config::WorkspaceManifest;
#[test]
fn test_cli_help() {
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.arg("--help")
.output()
.expect("Failed to execute binary");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("Modern XSD-to-code generator"));
assert!(stdout.contains("generate"));
assert!(stdout.contains("build"));
assert!(stdout.contains("validate"));
}
#[test]
fn test_cli_generate_help() {
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args(["generate", "--help"])
.output()
.expect("Failed to execute binary");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("--lang"));
assert!(stdout.contains("--out"));
assert!(stdout.contains("--strict-facets"));
assert!(stdout.contains("--dry-run"));
assert!(stdout.contains("--format"));
}
#[test]
fn test_cli_validate_valid_and_invalid() {
let dir = tempdir().unwrap();
let valid_xsd = dir.path().join("test.xsd");
fs::write(
&valid_xsd,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:test">
<xs:element name="Hello" type="xs:string"/>
</xs:schema>"#,
)
.unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args(["validate", valid_xsd.to_str().unwrap()])
.output()
.expect("Failed to execute validate");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("Valid schema"));
assert!(stdout.contains("urn:test"));
let missing_xsd = dir.path().join("missing.xsd");
let bad_output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args(["validate", missing_xsd.to_str().unwrap()])
.output()
.expect("Failed to execute validate");
assert!(!bad_output.status.success());
}
#[test]
fn test_cli_generate_dry_run() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("order.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:orders">
<xs:complexType name="Order">
<xs:sequence>
<xs:element name="Id" type="xs:string"/>
<xs:element name="Amount" type="xs:decimal"/>
</xs:sequence>
</xs:complexType>
<xs:element name="PurchaseOrder" type="Order"/>
</xs:schema>"#,
)
.unwrap();
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args(["generate", "--dry-run", schema_file.to_str().unwrap()])
.output()
.expect("Failed to execute generate --dry-run");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("Dry run completed successfully. No files written."));
assert!(stdout.contains("Namespace: urn:orders"));
assert!(stdout.contains("Root elements: 1"));
}
#[test]
fn test_cli_generate_multi_lang() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("sample.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:sample">
<xs:complexType name="SampleItem">
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
)
.unwrap();
let out_dir = dir.path().join("out");
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"python",
"--lang",
"rust",
"--out",
out_dir.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute generate");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("Code generation complete."));
assert!(out_dir.join("python/sample.py").exists());
assert!(out_dir.join("rust/sample.rs").exists());
}
#[test]
fn test_polyxml_toml_manifest_build() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("contract.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:corp">
<xs:element name="Agreement" type="xs:string"/>
</xs:schema>"#,
)
.unwrap();
let manifest_file = dir.path().join("polyxml.toml");
let manifest_content = r#"
[workspace]
name = "contracts"
schemas = ["*.xsd"]
output_base_dir = "./dist"
[[generate]]
target = "python"
output = "py_models"
backend = "pydantic-v2"
[codegen.rust]
enabled = true
output = "rs_models"
zero_copy = true
"#;
fs::write(&manifest_file, manifest_content).unwrap();
let parsed = WorkspaceManifest::from_file(&manifest_file).unwrap();
assert_eq!(
parsed.workspace.as_ref().unwrap().name.as_deref(),
Some("contracts")
);
let targets = parsed.resolved_targets();
assert_eq!(targets.len(), 2);
let schemas = parsed.expand_schemas(dir.path()).unwrap();
assert_eq!(schemas.len(), 1);
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args(["build", "--config", manifest_file.to_str().unwrap()])
.output()
.expect("Failed to execute build");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("Build finished successfully."));
assert!(dir.path().join("dist/py_models/contract.py").exists());
assert!(dir.path().join("dist/rs_models/contract.rs").exists());
}
#[test]
fn test_cli_generate_python_backends() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("invoice.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="https://example.com/invoice">
<xs:simpleType name="InvoiceCode">
<xs:restriction base="xs:string">
<xs:minLength value="5"/>
<xs:maxLength value="10"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Invoice">
<xs:sequence>
<xs:element name="Code" type="InvoiceCode"/>
<xs:element name="Total" type="xs:decimal"/>
<xs:element name="Note" type="xs:string" minOccurs="0" nillable="true"/>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required"/>
</xs:complexType>
<xs:element name="InvoiceDoc" type="Invoice"/>
</xs:schema>"#,
)
.unwrap();
let dc_out = dir.path().join("out_dataclass");
let output_dc = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"python",
"--backend",
"dataclass",
"--out",
dc_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute dataclass generate");
assert!(output_dc.status.success());
let dc_py = fs::read_to_string(dc_out.join("invoice.py")).unwrap();
assert!(dc_py.contains("@dataclass(slots=True, kw_only=True)"));
assert!(dc_py.contains("class Invoice:"));
assert!(dc_py.contains("code: InvoiceCode = field("));
assert!(dc_py.contains("total: Decimal = field("));
assert!(dc_py.contains("note: str | None = field(default=None"));
assert!(dc_py.contains("id: int = field("));
assert!(dc_py.contains("type InvoiceDoc = Invoice"));
let pydantic_out = dir.path().join("out_pydantic");
let output_pydantic = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"python",
"--backend",
"pydantic",
"--out",
pydantic_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute pydantic generate");
assert!(output_pydantic.status.success());
let pyd_py = fs::read_to_string(pydantic_out.join("invoice.py")).unwrap();
assert!(pyd_py.contains("class Invoice(BaseModel):"));
assert!(pyd_py.contains("model_config = ConfigDict(defer_build=True, populate_by_name=True)"));
assert!(
pyd_py.contains("type InvoiceCode = Annotated[str, Field(min_length=5, max_length=10)]")
);
assert!(pyd_py.contains("code: InvoiceCode = Field(..., alias=\"Code\", serialization_alias=\"Code\", json_schema_extra={\"type\": \"Element\", \"name\": \"Code\", \"json_name\": \"Code\", \"namespace\": \"https://example.com/invoice\"})"));
assert!(pyd_py.contains("total: Decimal = Field(..., alias=\"Total\", serialization_alias=\"Total\", json_schema_extra={\"type\": \"Element\", \"name\": \"Total\", \"json_name\": \"Total\", \"namespace\": \"https://example.com/invoice\"})"));
assert!(pyd_py.contains("note: str | None = Field(default=None, alias=\"Note\", serialization_alias=\"Note\", json_schema_extra={\"type\": \"Element\", \"name\": \"Note\", \"json_name\": \"Note\", \"namespace\": \"https://example.com/invoice\", \"nillable\": True})"));
}
#[test]
fn test_cli_generate_rust_zero_copy_and_owned() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("customer.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:customers">
<xs:simpleType name="Status">
<xs:restriction base="xs:string">
<xs:enumeration value="Active"/>
<xs:enumeration value="Suspended"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Customer">
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Status" type="Status"/>
<xs:element name="Balance" type="xs:decimal"/>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required"/>
</xs:complexType>
<xs:element name="CustomerRecord" type="Customer"/>
</xs:schema>"#,
)
.unwrap();
let zc_out = dir.path().join("out_rust_zc");
let output_zc = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"rust",
"--zero-copy",
"--out",
zc_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute rust zero-copy generate");
assert!(output_zc.status.success());
assert!(zc_out.join("mod.rs").exists());
let zc_rs = fs::read_to_string(zc_out.join("customer.rs")).unwrap();
assert!(zc_rs.contains("use std::borrow::Cow;"));
assert!(zc_rs.contains("pub struct Customer<'a>"));
assert!(zc_rs.contains("pub name: Cow<'a, str>"));
assert!(zc_rs.contains("pub enum Status"));
assert!(zc_rs.contains("impl Status"));
assert!(zc_rs.contains("pub type CustomerRecord<'a> = Customer<'a>;"));
let owned_out = dir.path().join("out_rust_owned");
let output_owned = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"rust",
"--zero-copy",
"false",
"--out",
owned_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute rust owned generate");
assert!(output_owned.status.success());
let owned_rs = fs::read_to_string(owned_out.join("customer.rs")).unwrap();
assert!(owned_rs.contains("pub struct Customer {"));
assert!(owned_rs.contains("pub name: String"));
assert!(!owned_rs.contains("Cow<'a"));
assert!(owned_rs.contains("pub type CustomerRecord = Customer;"));
}
#[test]
fn test_cli_typescript_generation() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("customer.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:crm">
<xs:simpleType name="Status">
<xs:restriction base="xs:string">
<xs:enumeration value="active"/>
<xs:enumeration value="suspended"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Customer">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="status" type="Status"/>
<xs:element name="tag" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required"/>
</xs:complexType>
<xs:element name="CustomerRecord" type="Customer"/>
</xs:schema>"#,
)
.unwrap();
let ts_out = dir.path().join("out_ts");
let output_ts = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"ts",
"--out",
ts_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute typescript generate");
assert!(output_ts.status.success());
assert!(ts_out.join("index.ts").exists());
let ts_code = fs::read_to_string(ts_out.join("customer.ts")).unwrap();
assert!(ts_code.contains("export interface Customer {"));
assert!(ts_code.contains("id: number;"));
assert!(ts_code.contains("name: string;"));
assert!(ts_code.contains("status: Status;"));
assert!(ts_code.contains("tag: string[];"));
assert!(ts_code.contains("export const Status = {"));
assert!(ts_code.contains("export type Status = (typeof Status)[keyof typeof Status];"));
assert!(ts_code.contains("export type CustomerRecord = Customer;"));
let tsc_check = Command::new("tsc")
.args([
"--noEmit",
"--strict",
"--target",
"es2022",
ts_out.join("customer.ts").to_str().unwrap(),
])
.output();
if let Ok(tsc_out) = tsc_check {
assert!(
tsc_out.status.success(),
"tsc failed on customer.ts: {}\nstdout: {}",
String::from_utf8_lossy(&tsc_out.stderr),
String::from_utf8_lossy(&tsc_out.stdout)
);
}
let zod_out = dir.path().join("out_ts_zod");
let output_zod = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"ts",
"--zod",
"--out",
zod_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute typescript zod generate");
assert!(output_zod.status.success());
let zod_code = fs::read_to_string(zod_out.join("customer.ts")).unwrap();
assert!(zod_code.contains("import { z } from \"zod\";"));
assert!(zod_code.contains("export const StatusSchema = z.enum([\"active\", \"suspended\"]);"));
assert!(zod_code.contains("export const CustomerSchema = z.object({"));
assert!(zod_code.contains("id: z.number().int(),"));
assert!(zod_code.contains("name: z.string(),"));
assert!(zod_code.contains("status: StatusSchema,"));
assert!(zod_code.contains("tag: z.array(z.string()),"));
let stub_file = zod_out.join("zod_stub.d.ts");
fs::write(
&stub_file,
"declare module \"zod\" { export const z: any; export namespace z { export type ZodType<T = any> = any; } }\n",
)
.unwrap();
let tsc_zod_check = Command::new("tsc")
.args([
"--noEmit",
"--strict",
"--target",
"es2022",
stub_file.to_str().unwrap(),
zod_out.join("customer.ts").to_str().unwrap(),
])
.output();
if let Ok(tsc_out) = tsc_zod_check {
assert!(
tsc_out.status.success(),
"tsc failed on customer.ts with zod: {}\nstdout: {}",
String::from_utf8_lossy(&tsc_out.stderr),
String::from_utf8_lossy(&tsc_out.stdout)
);
}
}
#[test]
fn test_cli_java_generation() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("customer.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:crm">
<xs:simpleType name="Status">
<xs:restriction base="xs:string">
<xs:enumeration value="active"/>
<xs:enumeration value="suspended"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Customer">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="status" type="Status"/>
<xs:element name="tag" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required"/>
</xs:complexType>
<xs:element name="CustomerRecord" type="Customer"/>
</xs:schema>"#,
)
.unwrap();
let java_out = dir.path().join("out_java");
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"java",
"--package",
"com.enterprise.crm",
"--out",
java_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute java generate");
assert!(output.status.success());
assert!(java_out.join("Customer.java").exists());
assert!(java_out.join("Status.java").exists());
let customer_code = fs::read_to_string(java_out.join("Customer.java")).unwrap();
assert!(customer_code.contains("package com.enterprise.crm;"));
assert!(customer_code.contains("public record Customer("));
assert!(customer_code.contains("int id"));
assert!(customer_code.contains("String name,"));
assert!(customer_code.contains("Status status,"));
assert!(customer_code.contains("java.util.List<String> tag"));
let status_code = fs::read_to_string(java_out.join("Status.java")).unwrap();
assert!(status_code.contains("package com.enterprise.crm;"));
assert!(status_code.contains("public enum Status {"));
assert!(status_code.contains("ACTIVE(\"active\"),"));
assert!(status_code.contains("SUSPENDED(\"suspended\");"));
let javac_check = Command::new("javac")
.args([
"-Werror",
java_out.join("Status.java").to_str().unwrap(),
java_out.join("Customer.java").to_str().unwrap(),
])
.output();
if let Ok(javac_out) = javac_check {
assert!(
javac_out.status.success(),
"javac failed on generated Java 21 files: {}\nstdout: {}",
String::from_utf8_lossy(&javac_out.stderr),
String::from_utf8_lossy(&javac_out.stdout)
);
}
}
#[test]
fn test_cli_cpp_generation() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("crm.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:enterprise:crm">
<xs:simpleType name="Status">
<xs:restriction base="xs:string">
<xs:enumeration value="active"/>
<xs:enumeration value="suspended"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Customer">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="email" type="xs:string" minOccurs="0"/>
<xs:element name="status" type="Status"/>
<xs:element name="tag" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required"/>
</xs:complexType>
<xs:element name="CustomerRecord" type="Customer"/>
</xs:schema>"#,
)
.unwrap();
let cpp_out = dir.path().join("out_cpp");
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"cpp",
"--namespace",
"enterprise::crm",
"--out",
cpp_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
"--format",
])
.output()
.expect("Failed to execute cpp generate");
assert!(
output.status.success(),
"polyxml generate failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let header_file = cpp_out.join("crm.hpp");
assert!(header_file.exists(), "crm.hpp was not generated");
let cpp_code = fs::read_to_string(&header_file).unwrap();
assert!(cpp_code.contains("namespace enterprise::crm {"));
assert!(cpp_code.contains("struct Customer {"));
assert!(cpp_code.contains("std::string name"));
assert!(cpp_code.contains("std::optional<std::string> email"));
assert!(cpp_code.contains("Status status"));
assert!(cpp_code.contains("std::vector<std::string> tag"));
assert!(cpp_code.contains("std::int32_t id"));
assert!(cpp_code.contains("enum class Status {"));
assert!(cpp_code.contains("Active,"));
assert!(cpp_code.contains("Suspended,"));
assert!(cpp_code.contains("using CustomerRecord = Customer;"));
assert!(cpp_code.contains("operator==") && cpp_code.contains("default"));
let driver_cpp = dir.path().join("driver.cpp");
fs::write(
&driver_cpp,
r#"
#include "crm.hpp"
#include <cassert>
#include <iostream>
int main() {
using namespace enterprise::crm;
Customer c1{
.name = "Acme Corp",
.email = "info@acme.com",
.status = Status::Active,
.tag = {"enterprise", "partner"},
.id = 100
};
Customer c2{
.name = "Acme Corp",
.email = "info@acme.com",
.status = Status::Active,
.tag = {"enterprise", "partner"},
.id = 100
};
Customer c3{
.name = "Beta LLC",
.email = std::nullopt,
.status = Status::Suspended,
.tag = {},
.id = 101
};
assert(c1 == c2);
assert(!(c1 == c3));
assert(to_string(Status::Active) == "active");
assert(to_string(Status::Suspended) == "suspended");
std::cout << "E2E C++20 driver passed!" << std::endl;
return 0;
}
"#,
)
.unwrap();
let out_bin = dir.path().join("driver_bin");
let compile_status = Command::new("g++")
.args([
"-std=c++20",
"-Wall",
"-Wextra",
"-Wpedantic",
"-Werror",
"-I",
cpp_out.to_str().unwrap(),
driver_cpp.to_str().unwrap(),
"-o",
out_bin.to_str().unwrap(),
])
.status()
.expect("Failed to execute g++");
assert!(
compile_status.success(),
"g++ compilation of generated crm.hpp failed"
);
let run_status = Command::new(&out_bin)
.status()
.expect("Failed to run compiled C++ binary");
assert!(run_status.success(), "C++ test driver failed execution");
}
#[test]
fn test_cli_cpp_modules_and_glaze() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("crm.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:enterprise:crm">
<xs:simpleType name="Status">
<xs:restriction base="xs:string">
<xs:enumeration value="active"/>
<xs:enumeration value="suspended"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Customer">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="status" type="Status"/>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required"/>
</xs:complexType>
</xs:schema>"#,
)
.unwrap();
let modules_out = dir.path().join("cpp_modules");
let bin = env!("CARGO_BIN_EXE_polyxml");
let output = Command::new(bin)
.args([
"generate",
"--lang",
"cpp",
"--mode",
"modules",
"--package",
"enterprise::crm",
"--out",
modules_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute cpp modules generate");
assert!(
output.status.success(),
"polyxml generate --mode modules failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let cppm_file = modules_out.join("crm.cppm");
assert!(cppm_file.exists(), "crm.cppm was not generated");
let cppm_code = fs::read_to_string(&cppm_file).unwrap();
assert!(cppm_code.contains("export module crm;"));
assert!(cppm_code.contains("export namespace enterprise::crm {"));
assert!(cppm_code.contains("struct Customer {"));
let glaze_out = dir.path().join("cpp_glaze");
let output2 = Command::new(bin)
.args([
"generate",
"--lang",
"cpp",
"--backend",
"glaze",
"--package",
"enterprise::crm",
"--out",
glaze_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute cpp glaze generate");
assert!(
output2.status.success(),
"polyxml generate --backend glaze failed: {}",
String::from_utf8_lossy(&output2.stderr)
);
let hpp_file = glaze_out.join("crm.hpp");
assert!(hpp_file.exists(), "crm.hpp was not generated");
let hpp_code = fs::read_to_string(&hpp_file).unwrap();
assert!(hpp_code.contains("#include <glaze/glaze.hpp>"));
assert!(hpp_code.contains("struct glz::meta<enterprise::crm::Customer> {"));
assert!(hpp_code.contains("struct glz::meta<enterprise::crm::Status> {"));
assert!(hpp_code.contains("\"active\", T::Active"));
assert!(hpp_code.contains("\"name\", &T::name"));
}
#[test]
fn test_cli_go_generation() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("crm.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="https://example.com/crm">
<xs:simpleType name="AccountTier">
<xs:restriction base="xs:string">
<xs:enumeration value="standard"/>
<xs:enumeration value="premium"/>
<xs:enumeration value="enterprise"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Account">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="tier" type="AccountTier"/>
<xs:element name="balance" type="xs:decimal"/>
<xs:element name="alias" type="xs:string" minOccurs="0"/>
<xs:element name="tag" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required"/>
</xs:complexType>
<xs:element name="AccountRecord" type="Account"/>
</xs:schema>"#,
)
.unwrap();
let go_out = dir.path().join("out_go");
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"go",
"--package",
"crm",
"--out",
go_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
"--format",
])
.output()
.expect("Failed to execute Go generate");
assert!(
output.status.success(),
"CLI generate --lang go failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let generated_file = go_out.join("crm.go");
assert!(generated_file.exists(), "crm.go was not created");
let go_code = fs::read_to_string(&generated_file).unwrap();
println!("GO_CODE:\n{}", go_code);
assert!(go_code.contains("package crm"));
assert!(go_code.contains("type AccountTier string"));
assert!(go_code.contains("AccountTierStandard"));
assert!(go_code.contains("\"standard\""));
assert!(go_code.contains("func (e AccountTier) IsValid() bool"));
assert!(go_code.contains("type Account struct {"));
assert!(go_code.contains("XMLName xml.Name") && go_code.contains("`json:\"-\"`"));
assert!(
go_code.contains("Name")
&& go_code.contains("xml:\"name\"")
&& go_code.contains("json:\"name\"")
);
assert!(
go_code.contains("Tier")
&& go_code.contains("AccountTier")
&& go_code.contains("xml:\"tier\"")
&& go_code.contains("json:\"tier\"")
);
assert!(
go_code.contains("Balance")
&& go_code.contains("float64")
&& go_code.contains("xml:\"balance\"")
&& go_code.contains("json:\"balance\"")
);
assert!(
go_code.contains("Alias")
&& go_code.contains("*string")
&& go_code.contains("xml:\"alias,omitempty\"")
&& go_code.contains("json:\"alias,omitempty\"")
);
assert!(
go_code.contains("Tag")
&& go_code.contains("[]string")
&& go_code.contains("xml:\"tag\"")
&& go_code.contains("json:\"tag\"")
);
assert!(
go_code.contains("ID")
&& go_code.contains("int32")
&& go_code.contains("xml:\"id,attr\"")
&& go_code.contains("json:\"id\"")
);
assert!(go_code.contains("func (s Account) Validate() error"));
let driver_go = go_out.join("crm_test.go");
fs::write(
&driver_go,
r#"package crm
import (
"encoding/xml"
"testing"
)
func TestAccountE2E(t *testing.T) {
alias := "AcmeMain"
acc := Account{
Name: "Acme Corp",
Tier: AccountTierPremium,
Balance: 1250.50,
Alias: &alias,
Tag: []string{"b2b", "strategic"},
ID: 1001,
}
if !acc.Tier.IsValid() {
t.Fatalf("expected tier to be valid")
}
data, err := xml.MarshalIndent(acc, "", " ")
if err != nil {
t.Fatalf("xml.Marshal failed: %v", err)
}
var decoded Account
if err := xml.Unmarshal(data, &decoded); err != nil {
t.Fatalf("xml.Unmarshal failed: %v", err)
}
if decoded.Name != "Acme Corp" || decoded.Tier != AccountTierPremium || decoded.ID != 1001 {
t.Fatalf("mismatched decoded values: %+v", decoded)
}
if decoded.Alias == nil || *decoded.Alias != "AcmeMain" {
t.Fatalf("mismatched alias: %+v", decoded.Alias)
}
if len(decoded.Tag) != 2 || decoded.Tag[0] != "b2b" {
t.Fatalf("mismatched tags: %+v", decoded.Tag)
}
if err := decoded.Validate(); err != nil {
t.Fatalf("validation failed: %v", err)
}
}
"#,
)
.unwrap();
let init_status = Command::new("go")
.args(["mod", "init", "crm"])
.current_dir(&go_out)
.status()
.expect("Failed to run go mod init");
assert!(init_status.success(), "go mod init failed");
let vet_status = Command::new("go")
.args(["vet", "."])
.current_dir(&go_out)
.status()
.expect("Failed to run go vet");
assert!(vet_status.success(), "go vet failed on generated Go models");
let test_status = Command::new("go")
.args(["test", "-v", "."])
.current_dir(&go_out)
.status()
.expect("Failed to run go test");
assert!(
test_status.success(),
"go test failed on generated Go models"
);
}
#[test]
fn test_cli_csharp_generation() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("customer.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="https://example.com/crm">
<xs:simpleType name="AccountTier">
<xs:restriction base="xs:string">
<xs:enumeration value="standard"/>
<xs:enumeration value="premium"/>
<xs:enumeration value="enterprise"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="Account">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="tier" type="AccountTier"/>
<xs:element name="balance" type="xs:decimal"/>
<xs:element name="alias" type="xs:string" minOccurs="0"/>
<xs:element name="tag" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required"/>
</xs:complexType>
<xs:element name="AccountRecord" type="Account"/>
</xs:schema>"#,
)
.unwrap();
let cs_out = dir.path().join("out_cs");
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"csharp",
"--package",
"Enterprise.Crm",
"--out",
cs_out.to_str().unwrap(),
schema_file.to_str().unwrap(),
"--format",
])
.output()
.expect("Failed to execute C# generate");
assert!(
output.status.success(),
"CLI generate --lang csharp failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let generated_file = cs_out.join("Customer.cs");
assert!(generated_file.exists(), "Customer.cs was not created");
let cs_code = fs::read_to_string(&generated_file).unwrap();
assert!(cs_code.contains("namespace Enterprise.Crm;"));
assert!(cs_code.contains("public enum AccountTier"));
assert!(cs_code.contains("[XmlEnum(\"standard\")]"));
assert!(cs_code.contains("public static bool IsValid(this AccountTier value)"));
assert!(cs_code.contains("public record Account("));
assert!(
cs_code.contains("XmlAttribute(\"id\")")
&& cs_code.contains("JsonPropertyName(\"id\")")
&& cs_code.contains("int Id")
);
assert!(
cs_code.contains("XmlElement(\"name\")")
&& cs_code.contains("JsonPropertyName(\"name\")")
&& cs_code.contains("string Name")
);
assert!(
cs_code.contains("XmlElement(\"tier\")")
&& cs_code.contains("JsonPropertyName(\"tier\")")
&& cs_code.contains("AccountTier Tier")
);
assert!(
cs_code.contains("XmlElement(\"balance\")")
&& cs_code.contains("JsonPropertyName(\"balance\")")
&& cs_code.contains("decimal Balance")
);
assert!(
cs_code.contains("XmlElement(\"alias\")")
&& cs_code.contains("JsonPropertyName(\"alias\")")
&& cs_code.contains("string? Alias")
);
assert!(
cs_code.contains("XmlElement(\"tag\")")
&& cs_code.contains("JsonPropertyName(\"tag\")")
&& cs_code.contains("List<string>? Tag")
);
assert!(cs_code.contains("public Account() : this("));
let app_dir = dir.path().join("cli_csharp_app");
fs::create_dir_all(&app_dir).unwrap();
let csproj = r#"<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>"#;
fs::write(app_dir.join("CrmCliApp.csproj"), csproj).unwrap();
fs::copy(&generated_file, app_dir.join("Customer.cs")).unwrap();
fs::write(
app_dir.join("Program.cs"),
r#"using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
using Enterprise.Crm;
public class Program
{
public static int Main()
{
var acc = new Account(
Id: 999,
Name: "Enterprise LLC",
Tier: AccountTier.Enterprise,
Balance: 5000.75m,
Alias: "EntCorp",
Tag: new List<string> { "b2b", "tier1" }
);
if (!acc.Tier.IsValid() || acc.Tier.ToXmlValue() != "enterprise")
{
Console.WriteLine("Tier enum methods failed");
return 1;
}
var serializer = new XmlSerializer(typeof(Account));
using var sw = new StringWriter();
serializer.Serialize(sw, acc);
var xml = sw.ToString();
using var sr = new StringReader(xml);
var decoded = (Account?)serializer.Deserialize(sr);
if (decoded == null)
{
Console.WriteLine("Deserialization failed");
return 1;
}
if (decoded.Id != 999 || decoded.Name != "Enterprise LLC" || decoded.Tier != AccountTier.Enterprise || decoded.Balance != 5000.75m)
{
Console.WriteLine("Account fields mismatch");
return 1;
}
if (decoded.Alias != "EntCorp" || decoded.Tag == null || decoded.Tag.Count != 2)
{
Console.WriteLine("Alias or Tag mismatch");
return 1;
}
Console.WriteLine("E2E C# CLI test passed successfully!");
return 0;
}
}
"#,
)
.unwrap();
let build_status = Command::new("dotnet")
.env("DOTNET_NOLOGO", "1")
.env("DOTNET_CLI_TELEMETRY_OPTOUT", "1")
.env("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1")
.args(["build", "--warnaserror"])
.current_dir(&app_dir)
.status()
.expect("Failed to run dotnet build");
assert!(
build_status.success(),
"dotnet build failed on CLI generated C# files"
);
let run_status = Command::new("dotnet")
.env("DOTNET_NOLOGO", "1")
.env("DOTNET_CLI_TELEMETRY_OPTOUT", "1")
.env("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1")
.args(["run"])
.current_dir(&app_dir)
.status()
.expect("Failed to run dotnet run");
assert!(
run_status.success(),
"dotnet run failed on CLI generated C# files"
);
}
#[test]
fn test_cli_transcode_bidirectional() {
let dir = tempdir().unwrap();
let xml_file = dir.path().join("input.xml");
let json_file = dir.path().join("output.json");
let roundtrip_xml_file = dir.path().join("roundtrip.xml");
fs::write(
&xml_file,
r#"<service id="99" enabled="true"><name>API Gateway</name><port>8080</port><port>8443</port></service>"#,
)
.unwrap();
let exe = env!("CARGO_BIN_EXE_polyxml");
let status = Command::new(exe)
.args([
"transcode",
xml_file.to_str().unwrap(),
"-o",
json_file.to_str().unwrap(),
"--pretty",
])
.status()
.expect("failed to execute polyxml transcode");
assert!(status.success());
let json_str = fs::read_to_string(&json_file).unwrap();
assert!(json_str.contains("\"@id\": 99"));
assert!(json_str.contains("\"@enabled\": true"));
assert!(json_str.contains("\"name\": \"API Gateway\""));
assert!(json_str.contains("\"port\": [\n 8080,\n 8443\n ]"));
let status2 = Command::new(exe)
.args([
"transcode",
json_file.to_str().unwrap(),
"-o",
roundtrip_xml_file.to_str().unwrap(),
"--pretty",
])
.status()
.expect("failed to execute polyxml transcode");
assert!(status2.success());
let roundtrip_str = fs::read_to_string(&roundtrip_xml_file).unwrap();
assert!(
roundtrip_str.contains("<service")
&& roundtrip_str.contains("id=\"99\"")
&& roundtrip_str.contains("enabled=\"true\"")
);
assert!(roundtrip_str.contains("<name>API Gateway</name>"));
assert!(roundtrip_str.contains("<port>8080</port>"));
assert!(roundtrip_str.contains("<port>8443</port>"));
}
#[test]
fn test_cli_csharp_source_gen_and_record_struct() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("customer.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:crm">
<xs:complexType name="Customer">
<xs:sequence>
<xs:element name="Id" type="xs:int"/>
<xs:element name="Name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
)
.unwrap();
let out_dir = dir.path().join("csharp_out");
let status = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
schema_file.to_str().unwrap(),
"--lang",
"csharp",
"--source-gen",
"--record-kind",
"struct",
"-o",
out_dir.to_str().unwrap(),
])
.status()
.expect("Failed to execute generate");
assert!(status.success());
let cs_file = out_dir.join("Customer.cs");
let content = fs::read_to_string(&cs_file).unwrap();
assert!(content.contains("public readonly record struct Customer("));
assert!(content.contains("[JsonSourceGenerationOptions(WriteIndented = true)]"));
assert!(content.contains("[JsonSerializable(typeof(Customer))]"));
assert!(content.contains("public partial class CustomerJsonContext : JsonSerializerContext"));
}
#[test]
fn test_cli_typescript_valibot_and_typebox_backend() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("item.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:store">
<xs:complexType name="Item">
<xs:sequence>
<xs:element name="Sku" type="xs:string"/>
<xs:element name="Price" type="xs:decimal"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
)
.unwrap();
let vali_dir = dir.path().join("ts_vali");
let status_vali = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
schema_file.to_str().unwrap(),
"--lang",
"ts",
"--backend",
"valibot",
"-o",
vali_dir.to_str().unwrap(),
])
.status()
.expect("Failed to execute generate with valibot");
assert!(status_vali.success());
let vali_content = fs::read_to_string(vali_dir.join("item.ts")).unwrap();
assert!(vali_content.contains("import * as v from \"valibot\";"));
assert!(vali_content.contains("export const ItemSchema = v.object({"));
let tb_dir = dir.path().join("ts_tb");
let status_tb = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
schema_file.to_str().unwrap(),
"--lang",
"ts",
"--backend",
"typebox",
"-o",
tb_dir.to_str().unwrap(),
])
.status()
.expect("Failed to execute generate with typebox");
assert!(status_tb.success());
let tb_content = fs::read_to_string(tb_dir.join("item.ts")).unwrap();
assert!(tb_content.contains("import { Type, Static } from \"@sinclair/typebox\";"));
assert!(tb_content.contains("export const ItemSchema = Type.Object({"));
}
#[test]
fn test_cli_go_easyjson_and_sonic_backend() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("service.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:service">
<xs:complexType name="Config">
<xs:sequence>
<xs:element name="Host" type="xs:string"/>
<xs:element name="Port" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
)
.unwrap();
let easy_dir = dir.path().join("go_easy");
let status_easy = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
schema_file.to_str().unwrap(),
"--lang",
"go",
"--backend",
"easyjson",
"-o",
easy_dir.to_str().unwrap(),
])
.status()
.expect("Failed to execute generate with easyjson");
assert!(status_easy.success());
let easy_content = fs::read_to_string(easy_dir.join("service.go")).unwrap();
assert!(easy_content.contains("//easyjson:json"));
assert!(easy_content.contains("type Config struct {"));
let sonic_dir = dir.path().join("go_sonic");
let status_sonic = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
schema_file.to_str().unwrap(),
"--lang",
"go",
"--backend",
"sonic",
"-o",
sonic_dir.to_str().unwrap(),
])
.status()
.expect("Failed to execute generate with sonic");
assert!(status_sonic.success());
let sonic_content = fs::read_to_string(sonic_dir.join("service.go")).unwrap();
assert!(sonic_content.contains("sonic:\"Host\""));
assert!(sonic_content.contains("sonic:\"Port\""));
}
#[test]
fn test_cli_rust_rkyv_flag() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("event.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:events">
<xs:complexType name="Event">
<xs:sequence>
<xs:element name="Id" type="xs:long"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
)
.unwrap();
let out_dir = dir.path().join("rust_out");
let status = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
schema_file.to_str().unwrap(),
"--lang",
"rust",
"--rkyv",
"-o",
out_dir.to_str().unwrap(),
])
.status()
.expect("Failed to execute generate with rkyv");
assert!(status.success());
let rs_content = fs::read_to_string(out_dir.join("event.rs")).unwrap();
assert!(rs_content.contains("#[cfg_attr(feature = \"rkyv\", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]"));
assert!(rs_content.contains("#[cfg_attr(feature = \"rkyv\", rkyv(check_bytes))]"));
}
#[test]
fn test_cli_build_polyxml_manifest_with_new_features() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("model.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:app">
<xs:complexType name="AppConfig">
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
)
.unwrap();
let manifest_file = dir.path().join("polyxml.toml");
fs::write(
&manifest_file,
r#"
[workspace]
name = "feature-test"
schemas = ["model.xsd"]
[[generate]]
target = "csharp"
output = "cs"
source_gen = true
record_kind = "struct"
[[generate]]
target = "ts"
output = "ts"
backend = "valibot"
[[generate]]
target = "go"
output = "go"
backend = "sonic"
[[generate]]
target = "rust"
output = "rs"
rkyv = true
"#,
)
.unwrap();
let status = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args(["build", "-c", manifest_file.to_str().unwrap()])
.current_dir(dir.path())
.status()
.expect("Failed to execute polyxml build");
assert!(status.success());
let cs = fs::read_to_string(dir.path().join("cs").join("Model.cs")).unwrap();
assert!(cs.contains("record struct AppConfig"));
assert!(cs.contains("[JsonSourceGenerationOptions"));
let ts = fs::read_to_string(dir.path().join("ts").join("model.ts")).unwrap();
assert!(ts.contains("import * as v from \"valibot\";"));
let go = fs::read_to_string(dir.path().join("go").join("model.go")).unwrap();
assert!(go.contains("sonic:\"Name\""));
let rs = fs::read_to_string(dir.path().join("rs").join("model.rs")).unwrap();
assert!(rs.contains("derive(rkyv::Archive"));
}
#[test]
fn test_cli_generate_custom_header_and_generated_tags() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("service.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="Config">
<xs:sequence>
<xs:element name="host" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
)
.unwrap();
let out_dir = dir.path().join("out_py");
let header_text = "# Copyright (c) 2026 Acme Corp\n# ruff: noqa";
let output = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
"--lang",
"python",
"--custom-header",
header_text,
"--out",
out_dir.to_str().unwrap(),
schema_file.to_str().unwrap(),
])
.output()
.expect("Failed to execute polyxml generate");
assert!(
output.status.success(),
"Stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let py_file = out_dir.join("service.py");
assert!(py_file.exists());
let py_code = fs::read_to_string(&py_file).unwrap();
assert!(py_code.starts_with(
"# Copyright (c) 2026 Acme Corp\n# ruff: noqa\n\n# @generated by PolyXML Compiler"
));
assert!(py_code.contains("class Config:"));
}
#[test]
fn test_cli_manifest_custom_header_workspace_and_target_override() {
let dir = tempdir().unwrap();
let schema_file = dir.path().join("schema.xsd");
fs::write(
&schema_file,
r#"<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="Server">
<xs:sequence>
<xs:element name="port" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>"#,
)
.unwrap();
let manifest_file = dir.path().join("polyxml.toml");
fs::write(
&manifest_file,
r#"
[workspace]
schemas = ["schema.xsd"]
custom_header = "// Shared Workspace License Header"
[[generate]]
target = "rust"
output = "rs"
[[generate]]
target = "go"
output = "go"
custom_header = "// Target Specific Go Header"
[[generate]]
target = "csharp"
output = "cs"
"#,
)
.unwrap();
let status = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args(["build", "-c", manifest_file.to_str().unwrap()])
.current_dir(dir.path())
.status()
.expect("Failed to execute polyxml build");
assert!(status.success());
let rs = fs::read_to_string(dir.path().join("rs").join("schema.rs")).unwrap();
assert!(
rs.starts_with("// Shared Workspace License Header\n\n// @generated by PolyXML Compiler")
);
let go = fs::read_to_string(dir.path().join("go").join("schema.go")).unwrap();
assert!(go.starts_with("// Target Specific Go Header\n\n// Code generated by PolyXML Compiler"));
assert!(go.contains("// @generated by PolyXML Compiler"));
let cs = fs::read_to_string(dir.path().join("cs").join("Schema.cs")).unwrap();
assert!(cs.starts_with("// Shared Workspace License Header\n\n// <auto-generated/>\n// @generated by PolyXML Compiler"));
}
#[test]
fn test_java_csharp_model_style_options_and_manifest() {
let dir = tempdir().unwrap();
let schema = dir.path().join("model.xsd");
fs::write(&schema,r#"<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:complexType name="Model"><xs:sequence><xs:element name="id" type="xs:string"/></xs:sequence></xs:complexType></xs:schema>"#).unwrap();
let java = dir.path().join("java");
let result = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
schema.to_str().unwrap(),
"--lang",
"java",
"--style",
"pojo",
"--builder",
"--codec",
"direct",
"--out",
java.to_str().unwrap(),
])
.output()
.unwrap();
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
let model = fs::read_to_string(java.join("Model.java")).unwrap();
assert!(model.contains("public class Model"));
assert!(model.contains("ModelBuilder builder()"));
assert!(java.join("ModelCodec.java").exists());
for args in [
vec!["--lang", "go", "--style", "pojo"],
vec!["--lang", "csharp", "--builder"],
vec![
"--lang",
"csharp",
"--style",
"class",
"--record-kind",
"struct",
],
vec!["--lang", "java", "--codec", "unknown"],
] {
let result = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args([
"generate",
schema.to_str().unwrap(),
"--out",
dir.path().join("bad").to_str().unwrap(),
])
.args(args)
.output()
.unwrap();
assert!(!result.status.success());
}
let manifest = dir.path().join("polyxml.toml");
fs::write(
&manifest,
r#"[workspace]
schemas = ["model.xsd"]
[[generate]]
target = "java"
output = "generated-java"
style = "class"
builder = true
codec = "direct"
[codegen.csharp]
output = "generated-csharp"
style = "pojo"
"#,
)
.unwrap();
let result = Command::new(env!("CARGO_BIN_EXE_polyxml"))
.args(["build", "--config", manifest.to_str().unwrap()])
.output()
.unwrap();
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
assert!(dir.path().join("generated-java/ModelCodec.java").exists());
let cs = fs::read_to_string(dir.path().join("generated-csharp/Model.cs")).unwrap();
assert!(cs.contains("public class Model"));
assert!(cs.contains("get; set;"));
let parsed: WorkspaceManifest = r#"[codegen.java]
style="pojo"
builder=true
codec="direct"
"#
.parse()
.unwrap();
let target = &parsed.resolved_targets()[0];
assert_eq!(target.style.as_deref(), Some("pojo"));
assert_eq!(target.builder, Some(true));
assert_eq!(target.codec.as_deref(), Some("direct"));
}