Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ as well to get information about its status.
pty.spawn(cmd, None, None, None).unwrap();

// Read the spawned process standard output (non-blocking).
let output = pty.read(1000, false);
let output = pty.read(false);

// Write to the spawned process standard input.
let to_write = OsString::from("echo \"some str\"\r\n");
Expand Down
12 changes: 6 additions & 6 deletions src/examples/conpty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ fn main() {
println!("{:?}", appname);
match pty.spawn(appname, None, None, None) {
Ok(_) => {
let mut output = pty.read(1000, false);
let mut output = pty.read(false);
match output {
Ok(out) => println!("{}", out.to_str().unwrap()),
Err(err) => panic!("{:?}", err)
}

output = pty.read(1000, false);
output = pty.read(false);
match output {
Ok(out) => println!("{}", out.to_str().unwrap()),
Err(err) => panic!("{:?}", err)
Expand All @@ -35,13 +35,13 @@ fn main() {
Err(err) => panic!("{:?}", err)
}

output = pty.read(1000, false);
output = pty.read(false);
match output {
Ok(out) => println!("{}", out.to_str().unwrap()),
Err(err) => panic!("{:?}", err)
}

output = pty.read(1000, false);
output = pty.read(false);
match output {
Ok(out) => println!("{}", out.to_str().unwrap()),
Err(err) => panic!("{:?}", err)
Expand All @@ -57,13 +57,13 @@ fn main() {
Err(err) => panic!("{:?}", err)
}

output = pty.read(1000, false);
output = pty.read(false);
match output {
Ok(out) => println!("{}", out.to_str().unwrap()),
Err(err) => panic!("{:?}", err)
}

output = pty.read(1000, false);
output = pty.read(false);
match output {
Ok(out) => println!("{}", out.to_str().unwrap()),
Err(err) => panic!("{:?}", err)
Expand Down
12 changes: 6 additions & 6 deletions src/examples/winpty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ fn main() {
println!("{:?}", appname);
match pty.spawn(appname, None, None, None) {
Ok(_) => {
let mut output = pty.read(1000, false);
let mut output = pty.read(false);
match output {
Ok(out) => println!("{:?}", out),
Err(err) => panic!("{:?}", err)
}

output = pty.read(1000, false);
output = pty.read(false);
match output {
Ok(out) => println!("{:?}", out),
Err(err) => panic!("{:?}", err)
Expand All @@ -35,13 +35,13 @@ fn main() {
Err(err) => panic!("{:?}", err)
}

output = pty.read(1000, false);
output = pty.read(false);
match output {
Ok(out) => println!("{:?}", out),
Err(err) => panic!("{:?}", err)
}

output = pty.read(1000, false);
output = pty.read(false);
match output {
Ok(out) => println!("{:?}", out),
Err(err) => panic!("{:?}", err)
Expand All @@ -57,13 +57,13 @@ fn main() {
Err(err) => panic!("{:?}", err)
}

output = pty.read(1000, false);
output = pty.read(false);
match output {
Ok(out) => println!("{:?}", out),
Err(err) => panic!("{:?}", err)
}

output = pty.read(1000, false);
output = pty.read(false);
match output {
Ok(out) => println!("{:?}", out),
Err(err) => panic!("{:?}", err)
Expand Down
152 changes: 152 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,155 @@ extern crate num_traits;
pub mod pty;
// mod pty_spawn;
pub use pty::{PTY, PTYArgs, PTYBackend, MouseMode, AgentConfig};

#[cfg(test)]
mod tests {
use super::*;
use std::time::Instant;
use std::thread::sleep;
use std::time::Duration;
use std::ffi::OsString;

#[test]
fn test_write_performance() {
// Initialize PTY with default arguments
let mut args = PTYArgs::default();
args.cols = 80;
args.rows = 24;
let mut pty = PTY::new_with_backend(&args, PTYBackend::ConPTY).unwrap();

// Spawn cmd.exe
let cmd = OsString::from("c:\\windows\\system32\\cmd.exe");
pty.spawn(cmd, None, None, None).unwrap();

// Wait for process to start
sleep(Duration::from_millis(1000));

// Test data
let test_data = OsString::from("echo test\r\n");
let iterations = 100;
let mut total_time = Duration::from_secs(0);

// Perform write performance test
for i in 0..iterations {
let start = Instant::now();
pty.write(test_data.clone()).unwrap();
let duration = start.elapsed();
total_time += duration;
println!("Write {}: {:?}", i + 1, duration);
}

// Calculate and print statistics
let avg_time = total_time.as_secs_f64() / iterations as f64;
println!("\nWrite Performance Test Results:");
println!("Total time: {:?}", total_time);
println!("Average time per write: {:.2}ms", avg_time * 1000.0);
println!("Total writes: {}", iterations);
}

#[test]
fn test_read_performance() {
// Initialize PTY with default arguments
let mut args = PTYArgs::default();
args.cols = 80;
args.rows = 24;
let mut pty = PTY::new_with_backend(&args, PTYBackend::ConPTY).unwrap();

// Spawn cmd.exe with a command that produces continuous output
let cmd = OsString::from("c:\\windows\\system32\\cmd.exe");
pty.spawn(cmd, Some("/c echo test".into()), None, None).unwrap();

// Wait for process to start
sleep(Duration::from_millis(1000));

// Test parameters
let iterations = 100;
let mut total_time = Duration::from_secs(0);
let mut total_bytes = 0;
let mut successful_reads = 0;

// Perform read performance test
for i in 0..iterations {
let start = Instant::now();
match pty.read(false) {
Ok(data) => {
let duration = start.elapsed();
total_time += duration;
total_bytes += data.len();
successful_reads += 1;
println!("Read {}: {:?}, bytes: {}", i + 1, duration, data.len());
}
Err(e) => {
println!("Read {} failed: {:?}", i + 1, e);
}
}
}

// Calculate and print statistics
let avg_time = if successful_reads > 0 {
total_time.as_secs_f64() / successful_reads as f64
} else {
0.0
};
let avg_bytes = if successful_reads > 0 {
total_bytes as f64 / successful_reads as f64
} else {
0.0
};

println!("\nRead Performance Test Results:");
println!("Total time: {:?}", total_time);
println!("Average time per read: {:.2}ms", avg_time * 1000.0);
println!("Total bytes read: {}", total_bytes);
println!("Average bytes per read: {:.2}", avg_bytes);
println!("Successful reads: {}", successful_reads);
}

#[test]
fn test_nonblocking_read_performance() {
// Initialize PTY with default arguments
let mut args = PTYArgs::default();
args.cols = 80;
args.rows = 24;

let mut pty = PTY::new_with_backend(&args, PTYBackend::ConPTY).unwrap();
pty.spawn(
"cmd.exe".into(),
Some("/c echo test".into()),
None,
None
).unwrap();

// Wait for process to start
std::thread::sleep(std::time::Duration::from_millis(100));

let start = Instant::now();
let mut total_bytes = 0;
let mut read_count = 0;
let mut empty_reads = 0;

// Read 100 times in non-blocking mode
for _ in 0..100 {
match pty.read(false) {
Ok(data) => {
if data.is_empty() {
empty_reads += 1;
} else {
total_bytes += data.len();
read_count += 1;
}
}
Err(_) => break,
}
}

let duration = start.elapsed();
println!("Non-blocking read performance test:");
println!("Total time: {:?}", duration);
println!("Average time per read: {:?}ms", duration.as_secs_f64() * 1000.0 / (read_count + empty_reads) as f64);
println!("Total bytes read: {}", total_bytes);
println!("Successful reads: {}", read_count);
println!("Empty reads: {}", empty_reads);
println!("Average bytes per successful read: {}", if read_count > 0 { total_bytes / read_count } else { 0 });
}
}
37 changes: 24 additions & 13 deletions src/pty.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

//! This module declares the [`PTY`] struct, which enables a Rust
//! program to create a pseudoterminal (PTY) in Windows.
//!
Expand All @@ -15,6 +14,7 @@ mod conpty;
mod base;

use std::ffi::OsString;
use std::default::Default;

// Local imports
use self::winpty::WinPTY;
Expand All @@ -37,6 +37,7 @@ pub enum PTYBackend {
}

/// Data struct that represents the possible arguments used to create a pseudoterminal
#[derive(Clone, Debug)]
pub struct PTYArgs {
// Common arguments
/// Number of character columns to display.
Expand All @@ -53,6 +54,17 @@ pub struct PTYArgs {
pub agent_config: AgentConfig
}

impl Default for PTYArgs {
fn default() -> Self {
Self {
cols: 80,
rows: 24,
mouse_mode: MouseMode::WINPTY_MOUSE_MODE_NONE,
timeout: 10000,
agent_config: AgentConfig::WINPTY_FLAG_COLOR_ESCAPES
}
}
}

/// Pseudoterminal struct that communicates with a spawned process.
///
Expand Down Expand Up @@ -83,7 +95,7 @@ pub struct PTYArgs {
/// pty.spawn(cmd, None, None, None).unwrap();
///
/// // Read the spawned process standard output (non-blocking).
/// let output = pty.read(1000, false);
/// let output = pty.read(false);
///
/// // Write to the spawned process standard input.
/// let to_write = OsString::from("echo \"some str\"\r\n");
Expand Down Expand Up @@ -230,21 +242,20 @@ impl PTY {
self.backend
}

/// Read at most `length` characters from a process standard output.
/// Read all available characters from the standard output of a process.
///
/// # Arguments
/// * `length` - Upper limit on the number of characters to read.
/// * `blocking` - Block the reading thread if no bytes are available.
/// * `blocking` - If true, wait for data to be available. If false, return immediately if no data is available.
///
/// # Notes
/// * If `blocking = false`, then the function will check how much characters are available on
/// the stream and will read the minimum between the input argument and the total number of
/// characters available.
/// # Returns
/// * `Ok(OsString)` - The data read from the process output
/// * `Err(OsString)` - If EOF is reached or an error occurs
///
/// * The bytes returned are represented using a [`OsString`] since Windows operates over
/// `u16` strings.
pub fn read(&self, length: u32, blocking: bool) -> Result<OsString, OsString> {
self.pty.read(length, blocking)
/// # Notes
/// * The actual read operation happens in a background thread
/// * The returned data is represented using a [`OsString`] since Windows operates over `u16` strings
pub fn read(&self, blocking: bool) -> Result<OsString, OsString> {
self.pty.read(blocking)
}

/// Write a (possibly) UTF-16 string into the standard input of a process.
Expand Down
Loading
Loading