use std::{
collections::VecDeque,
sync::{Arc, Mutex},
};
use nu_protocol::ShellError;
use crate::{plugin::PluginIdentity, protocol::PluginInput, PluginOutput};
use super::{EngineInterfaceManager, PluginInterfaceManager, PluginRead, PluginWrite};
#[derive(Debug, Clone)]
pub(crate) struct TestCase<I, O> {
r#in: Arc<Mutex<TestData<I>>>,
out: Arc<Mutex<TestData<O>>>,
}
#[derive(Debug)]
pub(crate) struct TestData<T> {
data: VecDeque<T>,
error: Option<ShellError>,
flushed: bool,
}
impl<T> Default for TestData<T> {
fn default() -> Self {
TestData {
data: VecDeque::new(),
error: None,
flushed: false,
}
}
}
impl<I, O> PluginRead<I> for TestCase<I, O> {
fn read(&mut self) -> Result<Option<I>, ShellError> {
let mut lock = self.r#in.lock().unwrap();
if let Some(err) = lock.error.take() {
Err(err)
} else {
Ok(lock.data.pop_front())
}
}
}
impl<I, O> PluginWrite<O> for TestCase<I, O>
where
I: Send + Clone,
O: Send + Clone,
{
fn write(&self, data: &O) -> Result<(), ShellError> {
let mut lock = self.out.lock().unwrap();
lock.flushed = false;
if let Some(err) = lock.error.take() {
Err(err)
} else {
lock.data.push_back(data.clone());
Ok(())
}
}
fn flush(&self) -> Result<(), ShellError> {
let mut lock = self.out.lock().unwrap();
lock.flushed = true;
Ok(())
}
}
#[allow(dead_code)]
impl<I, O> TestCase<I, O> {
pub(crate) fn new() -> TestCase<I, O> {
TestCase {
r#in: Default::default(),
out: Default::default(),
}
}
pub(crate) fn clear(&self) {
self.r#in.lock().unwrap().data.truncate(0);
}
pub(crate) fn add(&self, input: impl Into<I>) {
self.r#in.lock().unwrap().data.push_back(input.into());
}
pub(crate) fn extend(&self, inputs: impl IntoIterator<Item = I>) {
self.r#in.lock().unwrap().data.extend(inputs);
}
pub(crate) fn set_read_error(&self, err: ShellError) {
self.r#in.lock().unwrap().error = Some(err);
}
pub(crate) fn set_write_error(&self, err: ShellError) {
self.out.lock().unwrap().error = Some(err);
}
pub(crate) fn next_written(&self) -> Option<O> {
self.out.lock().unwrap().data.pop_front()
}
pub(crate) fn written(&self) -> impl Iterator<Item = O> + '_ {
std::iter::from_fn(|| self.next_written())
}
pub(crate) fn was_flushed(&self) -> bool {
self.out.lock().unwrap().flushed
}
pub(crate) fn has_unconsumed_read(&self) -> bool {
!self.r#in.lock().unwrap().data.is_empty()
}
pub(crate) fn has_unconsumed_write(&self) -> bool {
!self.out.lock().unwrap().data.is_empty()
}
}
impl TestCase<PluginOutput, PluginInput> {
pub(crate) fn plugin(&self, name: &str) -> PluginInterfaceManager {
PluginInterfaceManager::new(PluginIdentity::new_fake(name), self.clone())
}
}
impl TestCase<PluginInput, PluginOutput> {
pub(crate) fn engine(&self) -> EngineInterfaceManager {
EngineInterfaceManager::new(self.clone())
}
}