auv-cli 0.0.22

AUV core command-line frontend
use std::sync::{Arc, Mutex};

use auv_api_proto::auv::api::driver::v1 as proto;
use auv_api_proto::auv::api::driver::v1::recent_frames_service_server::RecentFramesService as _;
use tonic::Request;

use super::*;

#[derive(Clone)]
struct FakeFactory {
  opened: Arc<Mutex<Vec<CaptureTarget>>>,
  size: (u32, u32),
}

impl FrameSourceFactory for FakeFactory {
  fn open(&self, target: CaptureTarget) -> Result<Box<dyn FrameSource>, Status> {
    self.opened.lock().expect("opened mutex").push(target);
    Ok(Box::new(FakeSource { size: self.size }))
  }
}

struct FakeSource {
  size: (u32, u32),
}

impl FrameSource for FakeSource {
  fn capture(&mut self) -> Result<auv_driver::Capture, String> {
    Ok(capture(self.size, 1))
  }
}

fn capture(size: (u32, u32), value: u8) -> auv_driver::Capture {
  auv_driver::Capture {
    origin: None,
    image: image::RgbaImage::from_pixel(size.0, size.1, image::Rgba([value, 2, 3, 255])),
    bounds: auv_driver::Rect::new(10.0, 20.0, f64::from(size.0), f64::from(size.1)),
    scale_factor: 1.0,
    backend: "fake".to_string(),
    fallback_reason: None,
  }
}

#[test]
fn history_retains_only_the_newest_native_rgba_frames() {
  let mut history = History::new(2);
  history.push(capture((3, 2), 1));
  history.push(capture((3, 2), 2));
  history.push(capture((3, 2), 3));

  let response = history.recent(1).expect("read recent frames");
  assert_eq!(response.frames.iter().map(|frame| frame.sequence).collect::<Vec<_>>(), [2, 3]);
  assert_eq!(response.dropped_frames, 1);
  let image = response.frames[1].capture.as_ref().and_then(|capture| capture.image.as_ref()).expect("RGBA frame");
  assert_eq!((image.width, image.height, image.data.len()), (3, 2, 24));
}

#[tokio::test]
async fn service_applies_caller_size_to_a_generic_region_target() {
  let opened = Arc::new(Mutex::new(Vec::new()));
  let service = Service::with_factory(Arc::new(FakeFactory {
    opened: Arc::clone(&opened),
    size: (4, 3),
  }));
  let response = service
    .open_frame_buffer(Request::new(proto::OpenFrameBufferRequest {
      target: Some(proto::CaptureTarget {
        target: Some(proto::capture_target::Target::Region(proto::RegionCaptureTarget {
          region: Some(proto::ScreenRect {
            x: 1.0,
            y: 2.0,
            width: 4.0,
            height: 3.0,
          }),
          selector: None,
        })),
      }),
      target_fps: 30,
      frame_capacity: 2,
      output_size: Some(auv_api_proto::auv::api::image::v1::PixelSize {
        width: 2,
        height: 1,
      }),
    }))
    .await
    .expect("open frame buffer")
    .into_inner();
  let reference = response.frame_buffer.expect("frame buffer reference");
  let batch = service
    .get_recent_frames(Request::new(proto::GetRecentFramesRequest {
      frame_buffer: Some(reference.clone()),
      after_sequence: 0,
    }))
    .await
    .expect("get frame")
    .into_inner();

  let image = batch.frames[0].capture.as_ref().and_then(|capture| capture.image.as_ref()).expect("RGBA frame");
  assert_eq!((image.width, image.height, image.data.len()), (2, 1, 8));
  assert_eq!(
    opened.lock().expect("opened targets").as_slice(),
    &[CaptureTarget::Region {
      region: auv_driver::Rect::new(1.0, 2.0, 4.0, 3.0),
      display: None,
    }]
  );

  service
    .close_frame_buffer(Request::new(proto::CloseFrameBufferRequest {
      frame_buffer: Some(reference),
    }))
    .await
    .expect("close frame buffer");
}