From e6a8245e4235d61ed3050b27361adee20a5fb769 Mon Sep 17 00:00:00 2001 From: Ling Wang Date: Fri, 30 Aug 2024 01:16:09 +0800 Subject: [PATCH] Impl python Api --- .env/lib64 | 1 + .env/pyvenv.cfg | 5 ++ .github/workflows/CI.yml | 169 ++++++++++++++++++++++++++++++++++++ .gitignore | 74 +++++++++++++++- Cargo.lock | 45 ++++++---- Cargo.toml | 9 +- TODO.md | 18 ---- pyproject.toml | 15 ++++ src/consts.rs | 2 +- src/lib.rs | 23 +++-- src/main.rs | 1 - src/pythonapi/shell_like.rs | 23 +++-- tests/gen_record.rs | 82 ----------------- tests/two_shell.rs | 71 --------------- 14 files changed, 326 insertions(+), 212 deletions(-) create mode 120000 .env/lib64 create mode 100644 .env/pyvenv.cfg create mode 100644 .github/workflows/CI.yml delete mode 100644 TODO.md create mode 100644 pyproject.toml delete mode 100644 src/main.rs delete mode 100644 tests/gen_record.rs delete mode 100644 tests/two_shell.rs diff --git a/.env/lib64 b/.env/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/.env/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/.env/pyvenv.cfg b/.env/pyvenv.cfg new file mode 100644 index 0000000..c85dcda --- /dev/null +++ b/.env/pyvenv.cfg @@ -0,0 +1,5 @@ +home = /usr/bin +include-system-site-packages = false +version = 3.12.5 +executable = /usr/bin/python3.12 +command = /usr/bin/python -m venv /home/lw/Work/plct/tester/.env diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..a4c4c30 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,169 @@ +# This file is autogenerated by maturin v1.7.1 +# To update, run +# +# maturin generate-ci github +# +name: CI + +on: + push: + branches: + - main + - master + tags: + - '*' + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + linux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-latest + target: x86_64 + - runner: ubuntu-latest + target: x86 + - runner: ubuntu-latest + target: aarch64 + - runner: ubuntu-latest + target: armv7 + - runner: ubuntu-latest + target: s390x + - runner: ubuntu-latest + target: ppc64le + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + manylinux: auto + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-linux-${{ matrix.platform.target }} + path: dist + + musllinux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-latest + target: x86_64 + - runner: ubuntu-latest + target: x86 + - runner: ubuntu-latest + target: aarch64 + - runner: ubuntu-latest + target: armv7 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + manylinux: musllinux_1_2 + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-musllinux-${{ matrix.platform.target }} + path: dist + + windows: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: windows-latest + target: x64 + - runner: windows-latest + target: x86 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + architecture: ${{ matrix.platform.target }} + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-windows-${{ matrix.platform.target }} + path: dist + + macos: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: macos-12 + target: x86_64 + - runner: macos-14 + target: aarch64 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-macos-${{ matrix.platform.target }} + path: dist + + sdist: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build sdist + uses: PyO3/maturin-action@v1 + with: + command: sdist + args: --out dist + - name: Upload sdist + uses: actions/upload-artifact@v4 + with: + name: wheels-sdist + path: dist + + release: + name: Release + runs-on: ubuntu-latest + if: "startsWith(github.ref, 'refs/tags/')" + needs: [linux, musllinux, windows, macos, sdist] + steps: + - uses: actions/download-artifact@v4 + - name: Publish to PyPI + uses: PyO3/maturin-action@v1 + env: + MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }} + with: + command: upload + args: --non-interactive --skip-existing wheels-*/* diff --git a/.gitignore b/.gitignore index dfd0a5a..c8f0442 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,72 @@ /target -*.bak -*.cast -*.log \ No newline at end of file + +# Byte-compiled / optimized / DLL files +__pycache__/ +.pytest_cache/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +.venv/ +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +include/ +man/ +venv/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt +pip-selfcheck.json + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +.DS_Store + +# Sphinx documentation +docs/_build/ + +# PyCharm +.idea/ + +# VSCode +.vscode/ + +# Pyenv +.python-version diff --git a/Cargo.lock b/Cargo.lock index 2be88d1..8a37607 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,9 +97,12 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.1.10" +version = "1.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -124,9 +127,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "equivalent" @@ -244,9 +247,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libssh2-sys" @@ -284,9 +287,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.19" +version = "1.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc53a7799a7496ebc9fd29f31f7df80e83c9bda5299768af5f9e59eeea74647" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" dependencies = [ "cc", "libc", @@ -345,9 +348,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", @@ -551,9 +554,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -646,9 +649,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "itoa", "memchr", @@ -683,6 +686,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -732,9 +741,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" dependencies = [ "proc-macro2", "quote", @@ -785,9 +794,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.39.2" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 056263f..2271bc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,14 @@ name = "tester" version = "0.1.0" edition = "2021" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "tester" +crate-type = ["cdylib"] + [dependencies] +pyo3 = "0.22.0" asciicast = "0.2.2" -pyo3 = "0.22.2" rand = "0.8.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.125" @@ -16,4 +21,4 @@ tokio = { version = "1", features = ["full"] } toml = "0.8.19" [toolchain] -channel = "nightly" +channel = "nightly" \ No newline at end of file diff --git a/TODO.md b/TODO.md deleted file mode 100644 index ec48c34..0000000 --- a/TODO.md +++ /dev/null @@ -1,18 +0,0 @@ -- [ ] 更多的连接方式 - - [x] 更完善的 SSH - - [ ] 通过 tunnel 连接 - -- [ ] 外设支持 - - [ ] 外设抽象 : mod devhost - - [ ] 外设编写 - - [x] SdWireC - -- [ ] 设备抽象 : mod device - -- [x] 更加的多态支持 : where T: Tty -> Box - -- [ ] 导出的 API - - [ ] 实现 cli-like 面向外界的哪一个巨型 wrapper - - [ ] 从 dyn Tty 中区分出这个巨型 wrapper,并分开实现(可以在每次开头前都试一试?) - -- [ ] 与下一步测试软件的进一步集成 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..daaae52 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[build-system] +requires = ["maturin>=1.7,<2.0"] +build-backend = "maturin" + +[project] +name = "tester" +requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Rust", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] +dynamic = ["version"] +[tool.maturin] +features = ["pyo3/extension-module"] diff --git a/src/consts.rs b/src/consts.rs index c72c635..9ec39b6 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,4 +1,4 @@ pub const DURATION: u64 = 25; pub const SHELL_DURATION: u64 = 1; -pub const SHELL_PROMPT: &str = "$shell> "; // I don't know why it doesn't echo back the prompt... Add this as a workaround \ No newline at end of file +pub const SHELL_PROMPT: &str = ""; // I don't know why it doesn't echo back the prompt... Add this as a workaround \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0520235..8ad9deb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,16 @@ #![feature(box_into_inner)] -pub mod logger; pub mod consts; +pub mod logger; pub mod term { pub mod tty; - pub mod ssh; pub mod serial; pub mod shell; + pub mod ssh; - pub mod recorder; pub mod asciicast; + pub mod recorder; } pub mod exec { pub mod cli_api; @@ -26,12 +26,21 @@ pub mod devhost { pub mod device { pub mod device; } - pub mod util { - pub mod util; pub mod anybase; + pub mod util; } - pub mod pythonapi { pub mod shell_like; -} \ No newline at end of file + + // pub mod testapi; +} + +use pyo3::prelude::*; +use pythonapi::shell_like::PyTty; + +#[pymodule] +fn tester(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.add_class::()?; + Ok(()) +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 95106f5..0000000 --- a/src/main.rs +++ /dev/null @@ -1 +0,0 @@ -fn main(){} \ No newline at end of file diff --git a/src/pythonapi/shell_like.rs b/src/pythonapi/shell_like.rs index 23b35e7..d417739 100644 --- a/src/pythonapi/shell_like.rs +++ b/src/pythonapi/shell_like.rs @@ -3,13 +3,16 @@ use std::ptr::null_mut; use pyo3::{exceptions::PyTypeError, prelude::*}; use serde::Deserialize; -use crate::term::{ - asciicast::Asciicast, - recorder::{Recorder, SimpleRecorder}, - serial::Serial, - shell::Shell, - ssh::Ssh, - tty::{DynTty, WrapperTty}, +use crate::{ + logger::log, + term::{ + asciicast::Asciicast, + recorder::{Recorder, SimpleRecorder}, + serial::Serial, + shell::Shell, + ssh::Ssh, + tty::{DynTty, WrapperTty}, + }, }; type TtyType = DynTty; @@ -54,7 +57,7 @@ impl PyTtyWrapper { unsafe impl Send for PyTtyWrapper {} #[pyclass] -struct PyTty { +pub struct PyTty { inner: PyTtyWrapper, } @@ -168,7 +171,9 @@ impl PyTty { #[new] #[pyo3(signature = (conf, be_wrapped=None))] fn py_new(conf: &str, be_wrapped: Option<&mut PyTty>) -> PyResult { - let conf: PyTtyConf = serde_json::from_str(conf).unwrap(); + log(format!("Got conf: {}", conf)); + + let conf: PyTtyConf = toml::from_str(conf).unwrap(); let mut inner = None; diff --git a/tests/gen_record.rs b/tests/gen_record.rs deleted file mode 100644 index 2951402..0000000 --- a/tests/gen_record.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::{fs, thread::sleep, time::Duration}; - -use tester::{ - devhost::sdwirec::{SdwirecChooser, SdwirecProd}, - dyn_cast_mut, dyn_into, - exec::{ - cli_api::{CliTestApi, ExecBase, SudoCliTestApi}, - cli_exec::CliTester, - cli_exec_sudo::SudoCliTester, - }, - term::{ - asciicast::Asciicast, - recorder::{Recorder, SimpleRecorder}, - serial::Serial, - shell::Shell, - tty::WrapperTty, - }, -}; - -#[test] -fn gen_record() { - let sd = SdwirecProd::new(SdwirecChooser::Id(0)); - let ts = Shell::build(Some("/bin/bash")).unwrap(); - let rec = Asciicast::build(Box::new(ts)); - - // 获取镜像 ... - let mut exec = SudoCliTester::build(Box::new(rec)); - exec.script_run("cd ~/Work/plct/boards/d1/debian", 2) - .unwrap(); - exec.assert_script_run("unzip RVBoards_D1_Debian_lxde_img_linux_v0.4.1.img.zip", 60) - .unwrap(); - - // 录屏从此处开始 - let rec = dyn_cast_mut!(exec.inner_mut(), Asciicast).unwrap(); - rec.begin().unwrap(); - sd.to_ts().unwrap(); - // 刷写镜像 ... - exec.assert_script_sudo( - "dd if=RVBoards_D1_Debian_lxde_img_linux_v0.4.1.img of=/dev/sda bs=4M status=progress", - 600, - ) - .unwrap(); - - sd.to_dut().unwrap(); - // 刷写完成 ... 让我们把镜头切到 DUT 上 - let dut = Serial::build("/dev/ttyUSB0", 115200).unwrap(); - let rec = dyn_cast_mut!(exec.inner_mut(), Asciicast).unwrap(); - let ts = rec.swap(Box::new(dut)).unwrap(); // 录屏切换到 DUT - - // 等它到登录后,我们需要文字 log 了 - exec.wait_serial("login:", 60).unwrap(); - - let rec = dyn_into!(exec.exit(), Asciicast).unwrap(); - let mut logger = SimpleRecorder::build(rec); - logger.begin().unwrap(); - let mut exec = CliTester::build(Box::new(logger)); - - // 记录需要的信息,完毕 - exec.writeln("root").unwrap(); - exec.wait_serial("password", 5).unwrap(); - exec.writeln("rvboards").unwrap(); - sleep(Duration::from_secs(3)); - exec.writeln("cat /etc/os-release").unwrap(); - sleep(Duration::from_secs(5)); - exec.writeln("uname -a").unwrap(); - sleep(Duration::from_secs(5)); - exec.script_run("whoami", 2).unwrap(); - - let mut logger = dyn_into!(exec.exit(), SimpleRecorder).unwrap(); - let dut_log = logger.end().unwrap(); - let mut rec = dyn_into!(logger.exit(), Asciicast).unwrap(); - // 结束了,保存录屏 - let rec_cast = rec.end().unwrap(); - // 关闭 - let dut = dyn_into!(rec.exit(), Serial).unwrap(); - let ts = dyn_into!(ts, Shell).unwrap(); - // dut.stop(); - ts.stop(); - // 保存文件 - fs::write("dut.log", dut_log).unwrap(); - fs::write("rec.cast", rec_cast).unwrap(); -} diff --git a/tests/two_shell.rs b/tests/two_shell.rs deleted file mode 100644 index 620570a..0000000 --- a/tests/two_shell.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::fs; - -use tester::{ - dyn_cast_mut, dyn_into, - exec::{ - cli_api::{CliTestApi, ExecBase, SudoCliTestApi}, - cli_exec_sudo::SudoCliTester, - }, - term::{ - asciicast::Asciicast, - recorder::{Recorder, SimpleRecorder}, - shell::Shell, - tty::WrapperTty, - }, -}; - -#[test] -fn two_shell() { - let ts = Shell::build(Some("/bin/sh")).unwrap(); - // let dut = Serial::build("/dev/ttyUSB0", 115200).unwrap(); - let dut = Shell::build(Some("/bin/sh")).unwrap(); - let mut ts = SimpleRecorder::build(Box::new(ts)); - ts.begin().unwrap(); - let mut dut = SimpleRecorder::build(Box::new(dut)); - dut.begin().unwrap(); - let mut rec = Asciicast::build(Box::new(ts)); - rec.begin().unwrap(); - - let mut exec = SudoCliTester::build(Box::new(rec)); - - // exec.script_run("tty").unwrap(); - exec.script_run("ls", 2).unwrap(); - - exec.assert_script_run("mkdir /tmp/test1", 5).unwrap(); - exec.assert_script_run("echo \"Test Test\" > /tmp/test1/test.txt", 5) - .unwrap(); - - let rec = dyn_cast_mut!(exec.inner_mut(), Asciicast).unwrap(); - let ts = rec.swap(Box::new(dut)).unwrap(); - - exec.assert_script_run("sleep 1", 5).unwrap(); - // exec.script_run("tty").unwrap(); - exec.assert_script_sudo("cat /tmp/test1/test.txt", 5) - .unwrap(); - exec.writeln("date").unwrap(); - exec.script_run("sleep 1", 2).unwrap(); - - println!("Done"); - - let rec = exec.exit(); - let mut rec = dyn_into!(rec, Asciicast).unwrap(); - - let rec_log = rec.end().unwrap(); - let dut = rec.exit(); - let mut dut = dyn_into!(dut, SimpleRecorder).unwrap(); - let mut ts = dyn_into!(ts, SimpleRecorder).unwrap(); - let ts_log = ts.end().unwrap(); - let dut_log = dut.end().unwrap(); - - let ts = ts.exit(); - let ts = dyn_into!(ts, Shell).unwrap(); - let dut = dut.exit(); - let dut = dyn_into!(dut, Shell).unwrap(); - - fs::write("ts.log", ts_log).unwrap(); - fs::write("dut.log", dut_log).unwrap(); - fs::write("rec.cast", rec_log).unwrap(); - - ts.stop(); - dut.stop(); -}