#![allow(clippy::unwrap_used, clippy::expect_used)]
use jsonld::{
JsonLdProcessor,
NoLoader,
RemoteContextReference,
RemoteDocument,
syntax::{Parse, context::Context},
};
use std::thread;
const TOKIO_WORKER_STACK: usize = 2 * 1024 * 1024;
const NEARLY_EXHAUSTED_STACK: usize = 256 * 1024;
const SERDE_JSON_RECURSION_LIMIT: usize = 128;
fn nested_document(depth: usize) -> String {
let mut node = r#"{"p": "leaf"}"#.to_owned();
for _ in 0..depth {
node = format!(r#"{{"p": [{node}]}}"#);
}
format!(r#"{{"@context": {{"p": "http://example.com/nesting#p"}}, "@id": "http://example.com/node", "p": [{node}]}}"#)
}
#[derive(Debug, PartialEq, Eq)]
enum Outcome {
Completed,
Failed,
}
fn run_on_stack(input: String, stack: usize, algorithm: fn(&RemoteDocument) -> Outcome) -> Outcome {
thread::Builder::new()
.stack_size(stack)
.spawn(move || {
let (json, _) = jsonld::syntax::Value::parse_str(&input).unwrap();
algorithm(&RemoteDocument::new(None, None, json))
})
.unwrap()
.join()
.unwrap()
}
fn expand(document: &RemoteDocument) -> Outcome {
let runtime = tokio::runtime::Builder::new_current_thread().build().unwrap();
match runtime.block_on(document.expand(&NoLoader)) {
Ok(_) => Outcome::Completed,
Err(_) => Outcome::Failed,
}
}
fn compact(document: &RemoteDocument) -> Outcome {
let runtime = tokio::runtime::Builder::new_current_thread().build().unwrap();
let context = RemoteContextReference::Loaded(RemoteDocument::new(None, None, Context::default()));
match runtime.block_on(document.compact(context, &NoLoader)) {
Ok(_) => Outcome::Completed,
Err(_) => Outcome::Failed,
}
}
#[test]
fn a_document_nested_to_the_serde_json_limit_expands_on_a_tokio_worker_stack() {
let outcome = run_on_stack(nested_document(SERDE_JSON_RECURSION_LIMIT), TOKIO_WORKER_STACK, expand);
assert_eq!(outcome, Outcome::Completed);
}
#[test]
fn a_document_nested_to_the_serde_json_limit_expands_on_a_nearly_exhausted_stack() {
let outcome = run_on_stack(nested_document(SERDE_JSON_RECURSION_LIMIT), NEARLY_EXHAUSTED_STACK, expand);
assert_eq!(outcome, Outcome::Completed);
}
#[test]
fn a_document_nested_to_the_serde_json_limit_compacts_on_a_tokio_worker_stack() {
let outcome = run_on_stack(nested_document(SERDE_JSON_RECURSION_LIMIT), TOKIO_WORKER_STACK, compact);
assert_eq!(outcome, Outcome::Completed);
}
#[test]
fn a_document_nested_to_the_serde_json_limit_compacts_on_a_nearly_exhausted_stack() {
let outcome = run_on_stack(nested_document(SERDE_JSON_RECURSION_LIMIT), NEARLY_EXHAUSTED_STACK, compact);
assert_eq!(outcome, Outcome::Completed);
}
#[test]
fn a_shallow_document_expands_and_compacts_on_a_nearly_exhausted_stack() {
assert_eq!(run_on_stack(nested_document(4), NEARLY_EXHAUSTED_STACK, expand), Outcome::Completed);
assert_eq!(run_on_stack(nested_document(4), NEARLY_EXHAUSTED_STACK, compact), Outcome::Completed);
}