substrait_explain/
fixtures.rs

1//! Test fixtures for working with Substrait plans and substrait_explain
2
3use crate::extensions::simple::ExtensionKind;
4use crate::extensions::{ExtensionRegistry, SimpleExtensions};
5use crate::format;
6use crate::parser::{MessageParseError, Parser, ScopedParse};
7use crate::textify::foundation::{ErrorAccumulator, ErrorList};
8use crate::textify::plan::PlanWriter;
9use crate::textify::{ErrorQueue, OutputOptions, Scope, ScopedContext, Textify};
10
11pub struct TestContext {
12    pub options: OutputOptions,
13    pub extensions: SimpleExtensions,
14    pub extension_registry: ExtensionRegistry,
15}
16
17impl Default for TestContext {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl TestContext {
24    pub fn new() -> Self {
25        Self {
26            options: OutputOptions::default(),
27            extensions: SimpleExtensions::new(),
28            extension_registry: ExtensionRegistry::new(),
29        }
30    }
31
32    pub fn with_options(mut self, options: OutputOptions) -> Self {
33        self.options = options;
34        self
35    }
36
37    pub fn with_urn(mut self, anchor: u32, urn: &str) -> Self {
38        self.extensions
39            .add_extension_urn(urn.to_string(), anchor)
40            .unwrap();
41        self
42    }
43
44    pub fn with_function(mut self, urn: u32, anchor: u32, name: impl Into<String>) -> Self {
45        assert!(self.extensions.find_urn(urn).is_ok());
46        self.extensions
47            .add_extension(ExtensionKind::Function, urn, anchor, name.into())
48            .unwrap();
49        self
50    }
51
52    pub fn with_type(mut self, urn: u32, anchor: u32, name: impl Into<String>) -> Self {
53        assert!(self.extensions.find_urn(urn).is_ok());
54        self.extensions
55            .add_extension(ExtensionKind::Type, urn, anchor, name.into())
56            .unwrap();
57        self
58    }
59
60    pub fn with_type_variation(mut self, urn: u32, anchor: u32, name: impl Into<String>) -> Self {
61        assert!(self.extensions.find_urn(urn).is_ok());
62        self.extensions
63            .add_extension(ExtensionKind::TypeVariation, urn, anchor, name.into())
64            .unwrap();
65        self
66    }
67
68    pub fn scope<'e, E: ErrorAccumulator>(&'e self, errors: &'e E) -> impl Scope + 'e {
69        ScopedContext::new(
70            &self.options,
71            errors,
72            &self.extensions,
73            &self.extension_registry,
74        )
75    }
76
77    pub fn textify<T: Textify>(&self, t: &T) -> (String, ErrorList) {
78        let errors = ErrorQueue::default();
79        let mut output = String::new();
80
81        let scope = self.scope(&errors);
82        t.textify(&scope, &mut output).unwrap();
83
84        let evec = errors.into_iter().collect();
85        (output, ErrorList(evec))
86    }
87
88    pub fn textify_no_errors<T: Textify>(&self, t: &T) -> String {
89        let (s, errs) = self.textify(t);
90        assert!(errs.is_empty(), "{} Errors: {}", errs.0.len(), errs.0[0]);
91        s
92    }
93
94    pub fn parse<T: ScopedParse>(&self, input: &str) -> Result<T, MessageParseError> {
95        T::parse(&self.extensions, input)
96    }
97}
98
99/// Roundtrip a plan and verify that the output is the same as the input, after
100/// being parsed to a Substrait plan and then back to text.
101pub fn roundtrip_plan(input: &str) {
102    // Parse the plan using the simplified interface
103    let plan = Parser::parse(input).unwrap_or_else(|e| {
104        println!("Error parsing plan:\n{e}");
105        panic!("{e}");
106    });
107
108    // Format the plan back to text using the simplified interface
109    let (actual, errors) = format(&plan);
110
111    // Check for formatting errors
112    if !errors.is_empty() {
113        println!("Formatting errors:");
114        for error in errors {
115            println!("  {error}");
116        }
117        panic!("Formatting errors occurred");
118    }
119
120    // Compare the output with the input, printing the difference.
121    assert_eq!(
122        actual.trim(),
123        input.trim(),
124        "Expected:\n---\n{}\n---\nActual:\n---\n{}\n---",
125        input.trim(),
126        actual.trim()
127    );
128}
129
130/// Roundtrip a plan and verify that the output is the same as the input, after
131/// being parsed to a Substrait plan and then back to text.
132pub fn roundtrip_plan_with_verbose(input: &str, verbose_input: &str) {
133    let default_registry = ExtensionRegistry::new();
134
135    // Parse the simple plan
136    let simple_plan = Parser::parse(input).unwrap_or_else(|e| {
137        println!("Error parsing simple plan:\n{e}");
138        panic!("{e}");
139    });
140
141    // Parse the verbose plan
142    let verbose_plan = Parser::parse(verbose_input).unwrap_or_else(|e| {
143        println!("Error parsing verbose plan:\n{e}");
144        panic!("{e}");
145    });
146
147    // Test verbose output from both plans
148    let verbose_options = OutputOptions::verbose();
149    let (simple_verbose_writer, simple_verbose_errors) =
150        PlanWriter::<ErrorQueue>::new(&verbose_options, &simple_plan, &default_registry);
151    let simple_verbose_actual = format!("{simple_verbose_writer}");
152    simple_verbose_errors
153        .errs()
154        .expect("Errors during simple -> verbose conversion");
155
156    let (verbose_verbose_writer, verbose_verbose_errors) =
157        PlanWriter::<ErrorQueue>::new(&verbose_options, &verbose_plan, &default_registry);
158    let verbose_verbose_actual = format!("{verbose_verbose_writer}");
159    verbose_verbose_errors
160        .errs()
161        .expect("Errors during verbose -> verbose conversion");
162
163    assert_eq!(
164        simple_verbose_actual.trim(),
165        verbose_verbose_actual.trim(),
166        "Expected verbose outputs to match:\n---\n{}\n---\n---\n{}\n---",
167        simple_verbose_actual.trim(),
168        verbose_verbose_actual.trim()
169    );
170
171    // Test simple output from both plans
172    let simple_options = OutputOptions::default();
173    let (simple_simple_writer, simple_simple_errors) =
174        PlanWriter::<ErrorQueue>::new(&simple_options, &simple_plan, &default_registry);
175    let simple_simple_actual = format!("{simple_simple_writer}");
176    simple_simple_errors
177        .errs()
178        .expect("Errors during simple -> simple conversion");
179
180    let (verbose_simple_writer, verbose_simple_errors) =
181        PlanWriter::<ErrorQueue>::new(&simple_options, &verbose_plan, &default_registry);
182    let verbose_simple_actual = format!("{verbose_simple_writer}");
183    verbose_simple_errors
184        .errs()
185        .expect("Errors during verbose -> simple conversion");
186
187    assert_eq!(
188        simple_simple_actual.trim(),
189        verbose_simple_actual.trim(),
190        "Expected simple outputs to match:\n---\n{}\n---\n---\n{}\n---",
191        simple_simple_actual.trim(),
192        verbose_simple_actual.trim()
193    );
194}