From 59eb644925fb1b77e3c6d54d7a9578a2d17a8c32 Mon Sep 17 00:00:00 2001 From: Ling Wang Date: Sun, 22 Sep 2024 23:29:19 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A6=84=20refactor:=20Merge=20InnerTty=20i?= =?UTF-8?q?nto=20WrapperTty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 +++-- TODO.md | 8 +++ src/cli/asciicast.rs | 141 +++++++++++++++++++++++++++++-------------- src/cli/deansi.rs | 9 ++- src/cli/mod.rs | 3 +- src/cli/recorder.rs | 10 ++- src/cli/tee.rs | 11 +++- src/cli/tty.rs | 4 -- src/exec/cli_api.rs | 4 +- src/exec/cli_exec.rs | 11 +--- src/util/util.rs | 8 --- 11 files changed, 146 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 8052368..f4b2a73 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # autotester -部分文档见 [doc](./doc) +Python 部分文档见 [doc](./doc) +Rust 部分代码包含 rustdoc ## 文件夹释义 @@ -10,8 +11,13 @@ - tests:应用测试及示例 - apps:Python 实际测试脚本 -## fin +## 目前完成状况 -Python: -- Shell、Serial、SSH -- os-autoinst cli api 部分 +Rust CLI: 基本稳定,已完成的 API 不会有大的改动,可能会有新增中间处理层 +Rust CLI Exec:稳定,不太可能有更多变动(其它兼容/功能请在 FFI/外部实现) + +Rust GUI:不稳定,API 不太可能有太大变动不过 +Rust GUI Exec:不稳定,API 变动可能(根据新功能实验更改) + +Rust Python Api:我保证它能用…… **计划改动,我尽量做到不破坏现有 API** +UI:~~一团空气~~ diff --git a/TODO.md b/TODO.md index 5fa3eb1..919c203 100644 --- a/TODO.md +++ b/TODO.md @@ -12,6 +12,9 @@ - [ ] **可维护性:想办法把那个 PyTty 缩小掉……** +- [ ] CLI + - [ ] 获取内层某个层级的 ref/mut 然后更改?不知道会不会破坏借用检查器不过…会的话可能得从 Box 换成 Rc 了 + - [ ] 更多的连接方式 - [x] 更完善的 SSH - [ ] 通过 tunnel 连接 @@ -31,8 +34,13 @@ - [?] 从 dyn Tty 中区分出这个巨型 wrapper,并分开实现(可以在每次开头前都试一试?) - [ ] - [x] 执行器 + - [ ] Python API 多个层级之间不太能互通…(inner_ref 和 inner_mut 不能直接用)需要想个办法处理下 - [ ] 与下一步测试软件的进一步集成 - [ ] GUI 部分框架 + - [ ] UI 部分 + - [ ] UI 设计 + - 目前考虑多窗口,然后在上面放上一个编辑器和执行器什么的…这样可以实时编辑、打 needle 啥的? + - 然后由于 Wrapper 的层级越来越高…要不要写个啥展示下当前层级的样子呢(思考) - [ ] 实际应用 diff --git a/src/cli/asciicast.rs b/src/cli/asciicast.rs index a2f9aed..f823e56 100644 --- a/src/cli/asciicast.rs +++ b/src/cli/asciicast.rs @@ -1,5 +1,5 @@ //! Asciicast recorder. -//! +//! //! The Asciicast recorder is a recorder that records the terminal output //! in the asciicast v2 format. @@ -7,6 +7,7 @@ use std::{ any::Any, collections::HashMap, error::Error, + mem::replace, sync::{Arc, Mutex}, thread::{sleep, spawn, JoinHandle}, time::{Duration, SystemTime}, @@ -15,11 +16,7 @@ use std::{ use asciicast::{Entry, EventType, Header}; use serde_json::to_string; -use crate::{ - consts::DURATION, - info, - util::anybase::AnyBase, -}; +use crate::{consts::DURATION, info, util::anybase::AnyBase}; use super::{ recorder::Recorder, @@ -27,7 +24,8 @@ use super::{ }; pub struct Asciicast { - inner: Arc>>, + inner: Arc>, + inner_took: Arc>, head: Header, data: Arc>>, logged: Arc>>, @@ -38,10 +36,11 @@ pub struct Asciicast { impl Asciicast { pub fn build(inner: DynTty) -> Asciicast { - let inner = Arc::new(Mutex::new(Some(inner))); + let inner = Arc::new(Mutex::new(inner)); let mut res = Asciicast { inner: inner.clone(), + inner_took: Arc::new(Mutex::new(false)), head: Header { version: 2, width: 80, @@ -64,38 +63,47 @@ impl Asciicast { }; let inner = inner.clone(); + let inner_took = res.inner_took.clone(); let data = res.data.clone(); let logged = res.logged.clone(); let begin = res.begin.clone(); let begin_time = res.begin_time.clone(); let process = move || loop { sleep(Duration::from_millis(DURATION)); - let mut inner = inner.lock().unwrap(); - if inner.is_none() { - return; - } - let inner = inner.as_mut().unwrap(); - let new_data = inner.read(); - if new_data.is_err() { - return; + { + let inner_took = inner_took.lock().unwrap(); + if *inner_took { + return; + } } - let new_data = new_data.unwrap(); - - let begin = begin.lock().unwrap(); - if *begin && !new_data.is_empty() { - let time = begin_time.lock().unwrap().elapsed().unwrap(); - let timestamp = time.as_millis(); - let timestamp = timestamp as f64 / 1000.0; - let mut logged = logged.lock().unwrap(); - logged.push(Entry { - time: timestamp, - event_type: EventType::Output, - event_data: String::from_utf8(new_data.clone()).unwrap_or_default(), - }); + let new_data = { + let mut inner = inner.lock().unwrap(); + let new_data = inner.read(); + if new_data.is_err() { + return; + } + new_data.unwrap() + }; + + { + let begin = begin.lock().unwrap(); + if *begin && !new_data.is_empty() { + let time = begin_time.lock().unwrap().elapsed().unwrap(); + let timestamp = time.as_millis(); + let timestamp = timestamp as f64 / 1000.0; + let mut logged = logged.lock().unwrap(); + logged.push(Entry { + time: timestamp, + event_type: EventType::Output, + event_data: String::from_utf8(new_data.clone()).unwrap_or_default(), + }); + } } - let mut data = data.lock().unwrap(); - data.extend(new_data); + { + let mut data = data.lock().unwrap(); + data.extend(new_data); + } }; let thread = spawn(process); @@ -149,21 +157,60 @@ impl Tty for Asciicast { Ok(res) } fn write(&mut self, data: &[u8]) -> Result<(), Box> { - let inner = self.inner.clone(); - let mut inner = inner.lock().unwrap(); - if inner.is_none() { - return Err(Box::::from("You've already exited.")); + { + let inner_took = self.inner_took.lock().unwrap(); + if *inner_took { + return Err(Box::::from("You've already exited.")); + } + } + { + let inner = self.inner.clone(); + let mut inner = inner.lock().unwrap(); + inner.write(data) } - let inner = inner.as_mut().unwrap(); - inner.write(data) + } +} + +struct DummyTty {} +impl AnyBase for DummyTty { + fn as_any(&self) -> &dyn Any { + self + } + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + fn into_any(self: Box) -> Box { + self + } +} +impl Tty for DummyTty { + fn read(&mut self) -> Result, Box> { + Ok(Vec::new()) + } + fn read_line(&mut self) -> Result, Box> { + Ok(Vec::new()) + } + fn write(&mut self, _data: &[u8]) -> Result<(), Box> { + Ok(()) } } impl WrapperTty for Asciicast { - fn exit(self) -> DynTty { - let inner = self.inner.clone(); - let mut inner = inner.lock().unwrap(); - inner.take().unwrap() + fn exit(mut self) -> DynTty { + { + let mut inner_took = self.inner_took.lock().unwrap(); + *inner_took = true; + } + let dummy = DummyTty {}; + self.swap(Box::new(dummy)).unwrap() + } + + fn inner_mut(&mut self) -> &mut DynTty { + panic!("Asciicast recorder does not support inner_mut method... I have no idea how to implement it."); + } + + fn inner_ref(&self) -> &DynTty { + panic!("Asciicast recorder does not support inner_mut method... I have no idea how to implement it."); } } @@ -262,13 +309,15 @@ impl Recorder for Asciicast { fn swap(&mut self, target: DynTty) -> Result> { sleep(Duration::from_micros(DURATION)); + { + let inner_took = self.inner_took.lock().unwrap(); + if *inner_took { + return Err(Box::::from("You've already exited.")); + } + } let inner = self.inner.clone(); let mut inner = inner.lock().unwrap(); - if inner.is_none() { - return Err(Box::::from("You've already exited.")); - } - let res = inner.take().unwrap(); - *inner = Some(target); + let res = replace(&mut *inner, target); Ok(res) } } diff --git a/src/cli/deansi.rs b/src/cli/deansi.rs index 0130ab4..7cd357b 100644 --- a/src/cli/deansi.rs +++ b/src/cli/deansi.rs @@ -61,8 +61,15 @@ impl Tty for DeANSI { } impl WrapperTty for DeANSI { - /// Exit the Tty and return the inner Tty fn exit(self) -> DynTty { self.inner } + + fn inner_ref(&self) -> &DynTty { + &self.inner + } + + fn inner_mut(&mut self) -> &mut DynTty { + &mut self.inner + } } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 583116e..d27b127 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -15,8 +15,7 @@ //! functionality, or do some pre-processing before passing the data to //! the inner [`Tty`]. //! -//! Sometimes, we may need to modify the config **inside** a [`WrapperTty`]. -//! That's where the [`InnerTty`] trait comes in. It allows us to access the +//! Sometimes, we may need to modify the config **inside** a [`WrapperTty`]. It allows us to access the //! inner [`Tty`] of a [`WrapperTty`] (by ref or mut, ofcourse). //! //! Then, based on this concept, we can build a lot of useful tools, such as diff --git a/src/cli/recorder.rs b/src/cli/recorder.rs index 84a23a4..24e51e9 100644 --- a/src/cli/recorder.rs +++ b/src/cli/recorder.rs @@ -1,6 +1,6 @@ use std::{any::Any, error::Error, mem::replace}; -use crate::{info, cli::tty::Tty, util::anybase::AnyBase}; +use crate::{cli::tty::Tty, info, util::anybase::AnyBase}; use super::tty::{DynTty, WrapperTty}; @@ -75,6 +75,14 @@ impl WrapperTty for SimpleRecorder { fn exit(self) -> DynTty { self.inner } + + fn inner_ref(&self) -> &DynTty { + &self.inner + } + + fn inner_mut(&mut self) -> &mut DynTty { + &mut self.inner + } } impl Recorder for SimpleRecorder diff --git a/src/cli/tee.rs b/src/cli/tee.rs index f41fa19..eb77ecc 100644 --- a/src/cli/tee.rs +++ b/src/cli/tee.rs @@ -74,8 +74,15 @@ impl Tty for Tee { } impl WrapperTty for Tee { - fn exit(mut self) -> DynTty { - self.file.flush().unwrap(); + fn exit(self) -> DynTty { self.inner } + + fn inner_ref(&self) -> &DynTty { + &self.inner + } + + fn inner_mut(&mut self) -> &mut DynTty { + &mut self.inner + } } diff --git a/src/cli/tty.rs b/src/cli/tty.rs index fafe864..350a4dd 100644 --- a/src/cli/tty.rs +++ b/src/cli/tty.rs @@ -28,10 +28,6 @@ pub trait WrapperTty: Tty { /// Exit the Tty and return the inner Tty fn exit(self) -> DynTty; -} - -/// A trait for accessing the inner `Tty` of a `WrapperTty` -pub trait InnerTty: WrapperTty { /// Get a reference to the inner Tty fn inner_ref(&self) -> &DynTty; diff --git a/src/exec/cli_api.rs b/src/exec/cli_api.rs index c4c7bf6..587648b 100644 --- a/src/exec/cli_api.rs +++ b/src/exec/cli_api.rs @@ -1,8 +1,8 @@ use std::error::Error; -use crate::cli::tty::InnerTty; +use crate::cli::tty::WrapperTty; -pub trait CliTestApi: InnerTty { +pub trait CliTestApi: WrapperTty { /// /// /// You may found this func includes assert_script_run and script_output diff --git a/src/exec/cli_exec.rs b/src/exec/cli_exec.rs index 064eb54..963a7ff 100644 --- a/src/exec/cli_exec.rs +++ b/src/exec/cli_exec.rs @@ -6,10 +6,7 @@ use std::{ }; use crate::{ - cli::tty::{DynTty, InnerTty, Tty, WrapperTty}, - consts::DURATION, - err, info, - util::{anybase::AnyBase, util::rand_string}, + cli::tty::{DynTty, Tty, WrapperTty}, consts::DURATION, err, info, util::{anybase::AnyBase, util::rand_string} }; use super::cli_api::{CliTestApi, SudoCliTestApi}; @@ -63,12 +60,11 @@ impl WrapperTty for CliTester { fn exit(self) -> DynTty { self.inner } -} -impl InnerTty for CliTester { fn inner_ref(&self) -> &DynTty { &self.inner } + fn inner_mut(&mut self) -> &mut DynTty { &mut self.inner } @@ -189,12 +185,11 @@ impl WrapperTty for SudoCliTester { fn exit(self) -> DynTty { self.inner.exit() } -} -impl InnerTty for SudoCliTester { fn inner_ref(&self) -> &DynTty { self.inner.inner_ref() } + fn inner_mut(&mut self) -> &mut DynTty { self.inner.inner_mut() } diff --git a/src/util/util.rs b/src/util/util.rs index b9f1e9c..ffa9366 100644 --- a/src/util/util.rs +++ b/src/util/util.rs @@ -5,14 +5,6 @@ use std::{ use rand::{distributions::Alphanumeric, thread_rng, Rng}; - -#[macro_export] -macro_rules! todo { - () => { - Err("TODO".into()) - }; -} - #[macro_export] macro_rules! unfinished { () => {