use anyhow::Result;
use iri_string::types::{IriAbsoluteString, IriString};
use xee_xpath::{
context::{self},
Queries, Query,
};
use xee_xpath_load::{convert_string, ContextLoadable};
use crate::{
catalog::{Catalog, LoadContext},
language::XPathLanguage,
runcontext::RunContext,
testset::TestSet,
};
use super::{
assert::TestCaseResult,
core::{Runnable, TestCase},
outcome::TestOutcome,
};
#[derive(Debug)]
pub(crate) struct XPathTestCase {
pub(crate) test_case: TestCase<XPathLanguage>,
pub(crate) test: String,
}
impl XPathTestCase {
fn namespaces<'a>(
&'a self,
catalog: &'a Catalog<XPathLanguage>,
test_set: &'a TestSet<XPathLanguage>,
) -> anyhow::Result<Vec<(&'a str, &'a str)>> {
let environments = self
.test_case
.environments(catalog, test_set)
.collect::<Result<Vec<_>, crate::error::Error>>()?;
let namespaces = environments
.iter()
.flat_map(|environment| environment.namespace_pairs())
.collect();
Ok(namespaces)
}
}
impl Runnable<XPathLanguage> for XPathTestCase {
fn test_case(&self) -> &TestCase<XPathLanguage> {
&self.test_case
}
fn run(
&self,
run_context: &mut RunContext,
catalog: &Catalog<XPathLanguage>,
test_set: &TestSet<XPathLanguage>,
) -> TestOutcome {
let mut static_context_builder = context::StaticContextBuilder::default();
let static_base_uri = self.test_case.static_base_uri(catalog, test_set);
let static_base_uri = match static_base_uri {
Ok(static_base_uri) => static_base_uri,
Err(error) => return TestOutcome::EnvironmentError(error.to_string()),
};
let static_base_uri = if let Some(static_base_uri) = static_base_uri {
if static_base_uri != "#UNDEFINED" {
let iri: IriAbsoluteString = static_base_uri.try_into().unwrap();
Some(iri)
} else {
None
}
} else {
Some(test_set.file_uri())
};
static_context_builder.static_base_uri(static_base_uri.clone());
let variables =
self.test_case
.variables(run_context, catalog, test_set, static_base_uri.as_deref());
let variables = match variables {
Ok(variables) => variables,
Err(error) => return TestOutcome::EnvironmentError(error.to_string()),
};
let variable_names: Vec<_> = variables.iter().map(|(name, _)| name.clone()).collect();
static_context_builder.variable_names(variable_names);
let namespaces = self.namespaces(catalog, test_set);
let namespaces = match namespaces {
Ok(namespaces) => namespaces,
Err(error) => return TestOutcome::EnvironmentError(error.to_string()),
};
static_context_builder.namespaces(namespaces);
let static_context = static_context_builder.build();
let queries = Queries::default();
let query = queries.sequence_with_context(&self.test, static_context);
let query = match query {
Ok(query) => query,
Err(error) => {
return match &self.test_case.result {
TestCaseResult::AssertError(assert_error) => {
assert_error.assert_error(&error.error)
}
TestCaseResult::AnyOf(any_of) => any_of.assert_error(&error.error),
_ => TestOutcome::CompilationError(error.error),
}
}
};
let r =
self.test_case
.load_sources(run_context, catalog, test_set, static_base_uri.as_deref());
match r {
Ok(_) => (),
Err(error) => return TestOutcome::EnvironmentError(error.to_string()),
}
let context_item =
self.test_case
.context_item(run_context, catalog, test_set, static_base_uri.as_deref());
let context_item = match context_item {
Ok(context_item) => context_item,
Err(error) => return TestOutcome::EnvironmentError(error.to_string()),
};
let mut builder = query.dynamic_context_builder(run_context.documents);
if let Some(context_item) = context_item {
builder.context_item(context_item);
}
builder.variables(variables.clone());
let r = self.test_case.load_collections(
run_context,
catalog,
test_set,
static_base_uri.as_deref(),
);
let collections = match r {
Ok(collections) => collections,
Err(error) => return TestOutcome::EnvironmentError(error.to_string()),
};
for (uri, collection) in collections {
if uri.is_empty() {
builder.default_collection(collection);
continue;
} else {
let uri: IriString = uri.try_into().unwrap();
builder.collection(&uri, collection);
}
}
let context = builder.build();
let result = query.execute_with_context(run_context.documents, &context);
self.test_case.result.assert_result(
&context,
run_context.documents,
&result.map_err(|error| error.error),
)
}
fn load(queries: &Queries, context: &LoadContext) -> Result<impl Query<Self>> {
XPathTestCase::load_with_context(queries, context)
}
}
impl ContextLoadable<LoadContext> for XPathTestCase {
fn static_context_builder(context: &LoadContext) -> context::StaticContextBuilder {
let mut builder = context::StaticContextBuilder::default();
builder.default_element_namespace(context.catalog_ns);
builder
}
fn load_with_context(queries: &Queries, context: &LoadContext) -> Result<impl Query<Self>> {
let test_query = queries.one("test/string()", convert_string)?;
let test_case_query = TestCase::load_with_context(queries, context)?;
let test_case_query = test_case_query.map(move |test_case, documents, context| {
Ok(XPathTestCase {
test_case,
test: test_query.execute_with_context(documents, context)?,
})
});
Ok(test_case_query)
}
}