use super::*;
use crate::transform::xpath_analyze::{XPathAnalysis, analyze_xpath};
use crate::xpath::parser::parse_xpath;
use std::collections::HashMap;
fn get_streamable_xpath(xpath_str: &str) -> StreamableXPath {
let expr = parse_xpath(xpath_str).unwrap();
match analyze_xpath(&expr) {
XPathAnalysis::Streamable(s) => s,
XPathAnalysis::NotStreamable(r) => panic!("Expected streamable xpath: {:?}", r),
}
}
#[test]
fn test_simple_transform() {
let input = r#"<root><item id="1">A</item><item id="2">B</item></root>"#;
let xpath = get_streamable_xpath("//item[@id='2']");
let mut output = Vec::new();
let count = process_streaming(
input,
&xpath,
&HashMap::new(),
|node| {
node.set_attribute("modified", "true");
},
&mut output,
)
.unwrap();
let result = String::from_utf8(output).unwrap();
assert_eq!(count, 1);
assert!(result.contains(r#"item id="1""#));
assert!(result.contains(r#"modified="true""#));
}
#[test]
fn test_no_match() {
let input = r#"<root><item id="1">A</item></root>"#;
let xpath = get_streamable_xpath("//item[@id='999']");
let mut output = Vec::new();
let count = process_streaming(input, &xpath, &HashMap::new(), |_node| {}, &mut output).unwrap();
let result = String::from_utf8(output).unwrap();
assert_eq!(count, 0);
assert_eq!(result, input);
}
#[test]
fn test_multi_transform_non_overlapping() {
let input = r#"<root><item id="1">A</item><other id="2">B</other></root>"#;
let xpath_item = get_streamable_xpath("//item");
let xpath_other = get_streamable_xpath("//other");
let mut handler1 = |node: &mut EditableNode| {
node.set_attribute("type", "item");
};
let mut handler2 = |node: &mut EditableNode| {
node.set_attribute("type", "other");
};
let mut handlers: Vec<MultiTransformHandler<'_>> =
vec![(&xpath_item, &mut handler1), (&xpath_other, &mut handler2)];
let mut output = Vec::new();
let count =
process_streaming_multi(input, &mut handlers, &HashMap::new(), &mut output).unwrap();
let result = String::from_utf8(output).unwrap();
assert_eq!(count, 2);
assert!(result.contains(r#"type="item""#));
assert!(result.contains(r#"type="other""#));
}
#[test]
fn test_multi_transform_interleaved() {
let input = r#"<root><item>1</item><other>2</other><item>3</item></root>"#;
let xpath_item = get_streamable_xpath("//item");
let xpath_other = get_streamable_xpath("//other");
let mut handler1 = |node: &mut EditableNode| {
node.set_attribute("type", "item");
};
let mut handler2 = |node: &mut EditableNode| {
node.set_attribute("type", "other");
};
let mut handlers: Vec<MultiTransformHandler<'_>> =
vec![(&xpath_item, &mut handler1), (&xpath_other, &mut handler2)];
let mut output = Vec::new();
let count =
process_streaming_multi(input, &mut handlers, &HashMap::new(), &mut output).unwrap();
let result = String::from_utf8(output).unwrap();
assert_eq!(count, 3);
let item1_pos = result.find("<item type=\"item\">1</item>").unwrap();
let other_pos = result.find("<other type=\"other\">2</other>").unwrap();
let item2_pos = result.rfind("<item type=\"item\">3</item>").unwrap();
assert!(item1_pos < other_pos);
assert!(other_pos < item2_pos);
}
#[test]
fn test_multi_transform_zero_copy() {
let input = r#"<?xml version="1.0"?>
<root>
<!-- comment -->
<unchanged>keep me</unchanged>
<item>transform</item>
<also-unchanged attr="value">keep this too</also-unchanged>
</root>"#;
let xpath = get_streamable_xpath("//item");
let mut handler = |node: &mut EditableNode| {
node.set_attribute("modified", "true");
};
let mut handlers: Vec<MultiTransformHandler<'_>> = vec![(&xpath, &mut handler)];
let mut output = Vec::new();
let count =
process_streaming_multi(input, &mut handlers, &HashMap::new(), &mut output).unwrap();
let result = String::from_utf8(output).unwrap();
assert_eq!(count, 1);
assert!(result.starts_with(r#"<?xml version="1.0"?>"#));
assert!(result.contains("<unchanged>keep me</unchanged>"));
assert!(result.contains(r#"<also-unchanged attr="value">keep this too</also-unchanged>"#));
assert!(result.contains("<!-- comment -->"));
assert!(result.contains(r#"<item modified="true">transform</item>"#));
}
#[test]
fn test_multi_transform_empty_elements() {
let input = r#"<root><item/><other/><item/></root>"#;
let xpath_item = get_streamable_xpath("//item");
let xpath_other = get_streamable_xpath("//other");
let mut handler1 = |node: &mut EditableNode| {
node.set_attribute("type", "item");
};
let mut handler2 = |node: &mut EditableNode| {
node.set_attribute("type", "other");
};
let mut handlers: Vec<MultiTransformHandler<'_>> =
vec![(&xpath_item, &mut handler1), (&xpath_other, &mut handler2)];
let mut output = Vec::new();
let count =
process_streaming_multi(input, &mut handlers, &HashMap::new(), &mut output).unwrap();
let result = String::from_utf8(output).unwrap();
assert_eq!(count, 3);
assert_eq!(result.matches(r#"type="item""#).count(), 2);
assert_eq!(result.matches(r#"type="other""#).count(), 1);
}
#[test]
fn test_multi_transform_with_context() {
use crate::transform::context::TransformContext;
let input = r#"<root><items><item>A</item><item>B</item></items></root>"#;
let xpath = get_streamable_xpath("//item");
let mut handler = |node: &mut EditableNode, ctx: &TransformContext| {
node.set_attribute("pos", &ctx.position().to_string());
node.set_attribute("depth", &ctx.depth().to_string());
};
let mut handlers: Vec<MultiTransformHandlerWithContext<'_>> = vec![(&xpath, &mut handler)];
let mut output = Vec::new();
let count =
process_streaming_multi_with_context(input, &mut handlers, &HashMap::new(), &mut output)
.unwrap();
let result = String::from_utf8(output).unwrap();
assert_eq!(count, 2);
assert!(result.contains(r#"pos="1""#));
assert!(result.contains(r#"pos="2""#));
assert!(result.contains(r#"depth="3""#)); }
#[test]
fn test_multi_transform_first_match_wins() {
let input = r#"<root><items><item>A</item></items></root>"#;
let xpath_items = get_streamable_xpath("//items");
let xpath_item = get_streamable_xpath("//item");
let mut items_matched = false;
let mut item_matched = false;
let mut handler1 = |_node: &mut EditableNode| {
items_matched = true;
};
let mut handler2 = |_node: &mut EditableNode| {
item_matched = true;
};
let mut handlers: Vec<MultiTransformHandler<'_>> =
vec![(&xpath_items, &mut handler1), (&xpath_item, &mut handler2)];
let mut output = Vec::new();
let count =
process_streaming_multi(input, &mut handlers, &HashMap::new(), &mut output).unwrap();
assert_eq!(count, 1);
assert!(items_matched);
assert!(!item_matched); }
#[test]
fn test_multi_transform_remove_elements() {
let input = r#"<root><keep>A</keep><remove>B</remove><keep>C</keep></root>"#;
let xpath_remove = get_streamable_xpath("//remove");
let mut handler = |node: &mut EditableNode| {
node.remove();
};
let mut handlers: Vec<MultiTransformHandler<'_>> = vec![(&xpath_remove, &mut handler)];
let mut output = Vec::new();
let count =
process_streaming_multi(input, &mut handlers, &HashMap::new(), &mut output).unwrap();
let result = String::from_utf8(output).unwrap();
assert_eq!(count, 1);
assert!(!result.contains("<remove>"));
assert!(result.contains("<keep>A</keep>"));
assert!(result.contains("<keep>C</keep>"));
}