aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-11-13 16:36:01 -0500
committerJesse Luehrs <doy@tozt.net>2019-11-13 16:36:01 -0500
commit8a7b71bc65ea2699bfea17e842888c1febd3c4bc (patch)
tree693395c8243802c78a89026a7c194cb989556b05
parentc6ac53dd7c29094e10dc6bc45d2e60cd13ff758a (diff)
downloadteleterm-8a7b71bc65ea2699bfea17e842888c1febd3c4bc.tar.gz
teleterm-8a7b71bc65ea2699bfea17e842888c1febd3c4bc.zip
replace use of term::Buffer in server with vt100
-rw-r--r--src/cmd/server.rs6
-rw-r--r--src/config.rs24
-rw-r--r--src/server.rs44
-rw-r--r--src/server/tls.rs2
-rw-r--r--src/term.rs417
5 files changed, 23 insertions, 470 deletions
diff --git a/src/cmd/server.rs b/src/cmd/server.rs
index 979bbd4..c4d2075 100644
--- a/src/cmd/server.rs
+++ b/src/cmd/server.rs
@@ -32,7 +32,6 @@ impl crate::config::Config for Config {
if let Some(tls_identity_file) = &self.server.tls_identity_file {
create_server_tls(
self.server.listen_address,
- self.server.buffer_size,
self.server.read_timeout,
tls_identity_file,
self.server.allowed_login_methods.clone(),
@@ -43,7 +42,6 @@ impl crate::config::Config for Config {
} else {
create_server(
self.server.listen_address,
- self.server.buffer_size,
self.server.read_timeout,
self.server.allowed_login_methods.clone(),
self.oauth_configs.clone(),
@@ -73,7 +71,6 @@ pub fn config(
fn create_server(
address: std::net::SocketAddr,
- buffer_size: usize,
read_timeout: std::time::Duration,
allowed_login_methods: std::collections::HashSet<
crate::protocol::AuthType,
@@ -93,7 +90,6 @@ fn create_server(
let acceptor = listener.incoming().context(crate::error::Acceptor);
let server = crate::server::Server::new(
Box::new(acceptor),
- buffer_size,
read_timeout,
allowed_login_methods,
oauth_configs,
@@ -104,7 +100,6 @@ fn create_server(
fn create_server_tls(
address: std::net::SocketAddr,
- buffer_size: usize,
read_timeout: std::time::Duration,
tls_identity_file: &str,
allowed_login_methods: std::collections::HashSet<
@@ -133,7 +128,6 @@ fn create_server_tls(
.map(move |sock| tls_acceptor.accept(sock));
let server = crate::server::tls::Server::new(
Box::new(acceptor),
- buffer_size,
read_timeout,
allowed_login_methods,
oauth_configs,
diff --git a/src/config.rs b/src/config.rs
index 5a2b67b..ceee9fc 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -9,7 +9,6 @@ const CONFIG_FILENAME: &str = "config.toml";
const ALLOWED_LOGIN_METHODS_OPTION: &str = "allowed-login-methods";
const ARGS_OPTION: &str = "args";
-const BUFFER_SIZE_OPTION: &str = "buffer-size";
const COMMAND_OPTION: &str = "command";
const CONNECT_ADDRESS_OPTION: &str = "connect-address";
const FILENAME_OPTION: &str = "filename";
@@ -25,7 +24,6 @@ const TLS_OPTION: &str = "tls";
const DEFAULT_LISTEN_ADDRESS: &str = "127.0.0.1:4144";
const DEFAULT_CONNECT_ADDRESS: &str = "127.0.0.1:4144";
-const DEFAULT_BUFFER_SIZE: usize = 4 * 1024 * 1024;
const DEFAULT_READ_TIMEOUT: std::time::Duration =
std::time::Duration::from_secs(120);
const DEFAULT_AUTH_TYPE: crate::protocol::AuthType =
@@ -234,9 +232,6 @@ pub struct Server {
)]
pub listen_address: std::net::SocketAddr,
- #[serde(default = "default_buffer_size")]
- pub buffer_size: usize,
-
#[serde(
rename = "read_timeout_secs",
deserialize_with = "read_timeout",
@@ -264,7 +259,6 @@ impl Server {
pub fn cmd<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
let listen_address_help =
"Host and port to listen on (defaults to localhost:4144)";
- let buffer_size_help = "Number of bytes to store for each connection in order to send them to newly connected watchers (defaults to 4194304)";
let read_timeout_help = "Number of idle seconds to wait before disconnecting a client (defaults to 30)";
let tls_identity_file_help = "File containing the TLS certificate and private key to use for accepting TLS connections. Must be in pfx format. The server will only allow connections over TLS if this option is set.";
let allowed_login_methods_help = "Comma separated list containing the auth methods this server should allow. Allows everything by default, valid values are plain, recurse_center";
@@ -276,13 +270,6 @@ impl Server {
.help(listen_address_help),
)
.arg(
- clap::Arg::with_name(BUFFER_SIZE_OPTION)
- .long(BUFFER_SIZE_OPTION)
- .takes_value(true)
- .value_name("BYTES")
- .help(buffer_size_help),
- )
- .arg(
clap::Arg::with_name(READ_TIMEOUT_OPTION)
.long(READ_TIMEOUT_OPTION)
.takes_value(true)
@@ -317,12 +304,6 @@ impl Server {
.parse()
.context(crate::error::ParseAddr)?;
}
- if matches.is_present(BUFFER_SIZE_OPTION) {
- let s = matches.value_of(BUFFER_SIZE_OPTION).unwrap();
- self.buffer_size = s
- .parse()
- .context(crate::error::ParseBufferSize { input: s })?;
- }
if matches.is_present(READ_TIMEOUT_OPTION) {
let s = matches.value_of(READ_TIMEOUT_OPTION).unwrap();
self.read_timeout = s
@@ -355,7 +336,6 @@ impl Default for Server {
fn default() -> Self {
Self {
listen_address: default_listen_address(),
- buffer_size: default_buffer_size(),
read_timeout: default_read_timeout(),
tls_identity_file: None,
allowed_login_methods: default_allowed_login_methods(),
@@ -383,10 +363,6 @@ fn to_listen_address(address: &str) -> Result<std::net::SocketAddr> {
address.parse().context(crate::error::ParseAddr)
}
-fn default_buffer_size() -> usize {
- DEFAULT_BUFFER_SIZE
-}
-
fn read_timeout<'a, D>(
deserializer: D,
) -> std::result::Result<std::time::Duration, D::Error>
diff --git a/src/server.rs b/src/server.rs
index 5f0da66..d18fa95 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -49,7 +49,7 @@ struct TerminalInfo {
size: crate::term::Size,
}
-#[derive(Debug)]
+#[allow(clippy::large_enum_variant)]
// XXX https://github.com/rust-lang/rust/issues/64362
#[allow(dead_code)]
enum ConnectionState {
@@ -64,7 +64,7 @@ enum ConnectionState {
Streaming {
username: String,
term_info: TerminalInfo,
- saved_data: crate::term::Buffer,
+ term: vt100::Parser,
},
Watching {
username: String,
@@ -108,22 +108,22 @@ impl ConnectionState {
}
}
- fn saved_data(&self) -> Option<&crate::term::Buffer> {
+ fn term(&self) -> Option<&vt100::Parser> {
match self {
Self::Accepted => None,
Self::LoggingIn { .. } => None,
Self::LoggedIn { .. } => None,
- Self::Streaming { saved_data, .. } => Some(saved_data),
+ Self::Streaming { term, .. } => Some(term),
Self::Watching { .. } => None,
}
}
- fn saved_data_mut(&mut self) -> Option<&mut crate::term::Buffer> {
+ fn term_mut(&mut self) -> Option<&mut vt100::Parser> {
match self {
Self::Accepted => None,
Self::LoggingIn { .. } => None,
Self::LoggedIn { .. } => None,
- Self::Streaming { saved_data, .. } => Some(saved_data),
+ Self::Streaming { term, .. } => Some(term),
Self::Watching { .. } => None,
}
}
@@ -174,7 +174,7 @@ impl ConnectionState {
}
}
- fn stream(&mut self, buffer_size: usize) {
+ fn stream(&mut self) {
if let Self::LoggedIn {
username,
term_info,
@@ -183,7 +183,7 @@ impl ConnectionState {
*self = Self::Streaming {
username,
term_info,
- saved_data: crate::term::Buffer::new(buffer_size),
+ term: vt100::Parser::default(),
};
} else {
unreachable!()
@@ -265,8 +265,8 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
};
let title = self
.state
- .saved_data()
- .map_or("", crate::term::Buffer::title);
+ .term()
+ .map_or("", |parser| parser.screen().title());
// i don't really care if things break for a connection that has been
// idle for 136 years
@@ -301,7 +301,6 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
pub struct Server<
S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static,
> {
- buffer_size: usize,
read_timeout: std::time::Duration,
acceptor:
Box<dyn futures::stream::Stream<Item = S, Error = Error> + Send>,
@@ -321,7 +320,6 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
acceptor: Box<
dyn futures::stream::Stream<Item = S, Error = Error> + Send,
>,
- buffer_size: usize,
read_timeout: std::time::Duration,
allowed_auth_types: std::collections::HashSet<
crate::protocol::AuthType,
@@ -332,7 +330,6 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
>,
) -> Self {
Self {
- buffer_size,
read_timeout,
acceptor,
connections: std::collections::HashMap::new(),
@@ -481,7 +478,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
let username = conn.state.username().unwrap();
log::info!("{}: stream({})", conn.id, username);
- conn.state.stream(self.buffer_size);
+ conn.state.stream();
Ok(())
}
@@ -496,8 +493,8 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
if let Some(stream_conn) = self.connections.get(&id) {
let data = stream_conn
.state
- .saved_data()
- .map(crate::term::Buffer::contents)
+ .term()
+ .map(|parser| parser.screen().contents_formatted())
.ok_or_else(|| Error::InvalidWatchId {
id: id.to_string(),
})?;
@@ -505,7 +502,7 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
log::info!("{}: watch({}, {})", conn.id, username, id);
conn.state.watch(&id);
conn.send_message(crate::protocol::Message::terminal_output(
- data,
+ &data,
));
Ok(())
@@ -528,14 +525,16 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
conn: &mut Connection<S>,
data: &[u8],
) -> Result<()> {
- let saved_data = conn.state.saved_data_mut().unwrap();
+ let parser = conn.state.term_mut().unwrap();
- saved_data.append_server(data);
+ let screen = parser.screen().clone();
+ parser.process(data);
+ let diff = parser.screen().contents_diff(&screen);
for watch_conn in self.watchers_mut() {
let watch_id = watch_conn.state.watch_id().unwrap();
if conn.id == watch_id {
watch_conn.send_message(
- crate::protocol::Message::terminal_output(data),
+ crate::protocol::Message::terminal_output(&diff),
);
}
}
@@ -582,9 +581,12 @@ impl<S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static>
size: crate::term::Size,
) -> Result<()> {
let term_info = conn.state.term_info_mut().unwrap();
-
term_info.size = size;
+ if let Some(parser) = conn.state.term_mut() {
+ parser.set_size(size.rows, size.cols);
+ }
+
Ok(())
}
diff --git a/src/server/tls.rs b/src/server/tls.rs
index 9db4603..39e63ba 100644
--- a/src/server/tls.rs
+++ b/src/server/tls.rs
@@ -22,7 +22,6 @@ impl Server {
Error = Error,
> + Send,
>,
- buffer_size: usize,
read_timeout: std::time::Duration,
allowed_login_methods: std::collections::HashSet<
crate::protocol::AuthType,
@@ -38,7 +37,6 @@ impl Server {
Box::new(
tls_sock_r.context(crate::error::SocketChannelReceive),
),
- buffer_size,
read_timeout,
allowed_login_methods,
oauth_configs,
diff --git a/src/term.rs b/src/term.rs
index 3561521..02c21bb 100644
--- a/src/term.rs
+++ b/src/term.rs
@@ -1,11 +1,5 @@
use crate::prelude::*;
-const RESET: &[&[u8]] = &[b"\x1bc"];
-const CLEAR: &[&[u8]] =
- &[b"\x1b[3J\x1b[H\x1b[2J", b"\x1b[H\x1b[J", b"\x1b[H\x1b[2J"];
-const WINDOW_TITLE: &[(&[u8], &[u8])] =
- &[(b"\x1b]0;", b"\x07"), (b"\x1b]2;", b"\x07")];
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Size {
pub rows: u16,
@@ -29,414 +23,3 @@ impl std::fmt::Display for Size {
std::fmt::Display::fmt(&format!("{}x{}", self.cols, self.rows), f)
}
}
-
-#[derive(Debug, Clone, Default)]
-pub struct Buffer {
- max_size: usize,
- contents: Vec<u8>,
- title: String,
-}
-
-impl Buffer {
- pub fn new(max_size: usize) -> Self {
- let mut self_ = Self::default();
- self_.max_size = max_size;
- self_
- }
-
- // doesn't parse out window titles, since we don't care about them. also
- // tries to spread out truncations a bit to keep each individual run of
- // this function reasonably fast (trying to avoid most runs being fast but
- // occasionally one being extra slow when it hits a limit). this makes it
- // a bit less accurate (clears don't actually reset the entire terminal
- // state, so dropping everything before them when we see one isn't quite
- // correct), but the client side buffer is only used in the case where
- // someone reconnects after disconnecting from the server, which should be
- // uncommon enough that it shouldn't matter all that much.
- pub fn append_client(&mut self, buf: &[u8], written: usize) -> usize {
- self.contents.extend_from_slice(buf);
-
- if written == 0 {
- return written;
- }
-
- if self.find_reset(buf).is_some() {
- self.truncate_at(written);
- return written;
- }
-
- if self.find_clear(buf).is_some() {
- self.truncate_at(written);
- return written;
- }
-
- if self.contents.len() > self.max_size {
- let to_drop = self.contents.len() - self.max_size / 2;
- let to_drop = to_drop.min(written);
- if to_drop > 0 {
- self.truncate_at(to_drop);
- return to_drop;
- }
- }
-
- 0
- }
-
- // aim for accuracy over speed - on the client side, we're guaranteed to
- // see every byte because the bytes are coming from the client, but our
- // truncation behavior isn't particularly guaranteed to give correct
- // results in all cases, so try to avoid doing it too much if we can help
- // it. this makes this function slower (and spikier) than append_client,
- // but that's pretty okay because we don't care as much about latency
- // here.
- pub fn append_server(&mut self, buf: &[u8]) {
- self.contents.extend_from_slice(buf);
-
- if let Some(title) = self.find_window_title(buf) {
- self.title = title;
- }
-
- // resets are actually safe to truncate at, because they reset ~all
- // terminal state
- if let Some(i) = self.find_reset(buf) {
- self.truncate_at(self.contents.len() - buf.len() + i);
- }
-
- while self.contents.len() > self.max_size {
- let hard_truncate = self.contents.len() - self.max_size / 2;
- // off by one so that we skip over a clear escape sequence at the
- // start of the buffer
- if let Some(i) = self.find_clear(&self.contents[1..]) {
- self.truncate_at((i + 1).min(hard_truncate));
- } else {
- self.truncate_at(hard_truncate);
- }
- }
- }
-
- pub fn contents(&self) -> &[u8] {
- &self.contents
- }
-
- pub fn len(&self) -> usize {
- self.contents.len()
- }
-
- pub fn title(&self) -> &str {
- &self.title
- }
-
- fn find_window_title(&self, buf: &[u8]) -> Option<String> {
- let mut found = None;
- for window_title in WINDOW_TITLE {
- if let Some(i) = twoway::rfind_bytes(buf, window_title.0) {
- if let Some(j) = twoway::find_bytes(&buf[i..], window_title.1)
- {
- let start = i + window_title.0.len();
- let end = j + i;
- if let Ok(title) = std::str::from_utf8(&buf[start..end]) {
- if let Some((i2, _)) = found {
- if i > i2 {
- found = Some((i, title));
- }
- } else {
- found = Some((i, title));
- }
- }
- }
- }
- }
- found.map(|(_, title)| title.to_string())
- }
-
- fn find_clear(&self, buf: &[u8]) -> Option<usize> {
- for clear in CLEAR {
- if let Some(i) = twoway::find_bytes(buf, clear) {
- return Some(i);
- }
- }
- None
- }
-
- fn find_reset(&self, buf: &[u8]) -> Option<usize> {
- for reset in RESET {
- // rfind because we only ever care about the most recent reset,
- // unlike clears where we might want to be more conservative
- if let Some(i) = twoway::rfind_bytes(buf, reset) {
- return Some(i);
- }
- }
- None
- }
-
- fn truncate_at(&mut self, i: usize) {
- let new_contents = self.contents.split_off(i);
- self.contents = new_contents;
- }
-}
-
-#[cfg(test)]
-#[allow(clippy::cognitive_complexity)]
-#[allow(clippy::redundant_clone)]
-#[allow(clippy::shadow_unrelated)]
-mod test {
- use super::*;
-
- #[test]
- fn test_basic() {
- let mut buffer = Buffer::new(100);
- assert_eq!(buffer.contents(), b"");
- assert_eq!(buffer.len(), 0);
- assert_eq!(buffer.title(), "");
-
- let n = buffer.append_client(b"foo", 0);
- assert_eq!(buffer.contents(), b"foo");
- assert_eq!(buffer.len(), 3);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let n = buffer.append_client(b"bar", 3);
- assert_eq!(buffer.contents(), b"foobar");
- assert_eq!(buffer.len(), 6);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
- }
-
- #[test]
- fn test_clear() {
- let mut buffer = Buffer::new(100);
-
- let n = buffer.append_client(b"foo", 0);
- assert_eq!(buffer.contents(), b"foo");
- assert_eq!(buffer.len(), 3);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let n = buffer.append_client(b"\x1b[3J\x1b[H\x1b[2J", 3);
- assert_eq!(buffer.contents(), b"\x1b[3J\x1b[H\x1b[2J");
- assert_eq!(buffer.len(), 11);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 3);
-
- let n = buffer.append_client(b"bar", 11);
- assert_eq!(buffer.contents(), b"\x1b[3J\x1b[H\x1b[2Jbar");
- assert_eq!(buffer.len(), 14);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let n = buffer.append_client(b"baz\x1bcquux", 14);
- assert_eq!(buffer.contents(), b"baz\x1bcquux");
- assert_eq!(buffer.len(), 9);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 14);
-
- let n = buffer.append_client(b"blorg\x1b[H\x1b[J", 9);
- assert_eq!(buffer.contents(), b"blorg\x1b[H\x1b[J");
- assert_eq!(buffer.len(), 11);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 9);
-
- let n = buffer.append_client(b"\x1b[H\x1b[2Jabc", 11);
- assert_eq!(buffer.contents(), b"\x1b[H\x1b[2Jabc");
- assert_eq!(buffer.len(), 10);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 11);
-
- let n =
- buffer.append_client(b"first\x1bcsecond\x1b[H\x1b[2Jthird", 10);
- assert_eq!(buffer.contents(), b"first\x1bcsecond\x1b[H\x1b[2Jthird");
- assert_eq!(buffer.len(), 25);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 10);
-
- let n =
- buffer.append_client(b"first\x1b[H\x1b[2Jsecond\x1bcthird", 25);
- assert_eq!(buffer.contents(), b"first\x1b[H\x1b[2Jsecond\x1bcthird");
- assert_eq!(buffer.len(), 25);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 25);
- }
-
- #[test]
- fn test_title() {
- let mut buffer = Buffer::new(50);
-
- buffer.append_server(b"\x1b]0;this is a title\x07");
- assert_eq!(buffer.contents(), b"\x1b]0;this is a title\x07");
- assert_eq!(buffer.len(), 20);
- assert_eq!(buffer.title(), "this is a title");
-
- buffer.append_server(b"\x1b]2;this is another title\x07");
- assert_eq!(
- buffer.contents(),
- &b"\x1b]0;this is a title\x07\x1b]2;this is another title\x07"[..]
- );
- assert_eq!(buffer.len(), 46);
- assert_eq!(buffer.title(), "this is another title");
-
- buffer.append_server(b"\x1bcfoo");
- assert_eq!(buffer.contents(), &b"\x1bcfoo"[..]);
- assert_eq!(buffer.len(), 5);
- assert_eq!(buffer.title(), "this is another title");
-
- buffer.append_server(
- b"\x1bcabc\x1b]0;title1\x07def\x1b]2;title2\x07ghi",
- );
- assert_eq!(
- buffer.contents(),
- &b"\x1bcabc\x1b]0;title1\x07def\x1b]2;title2\x07ghi"[..]
- );
- assert_eq!(buffer.len(), 33);
- assert_eq!(buffer.title(), "title2");
-
- buffer.append_server(
- b"\x1bcabc\x1b]2;title3\x07def\x1b]0;title4\x07ghi",
- );
- assert_eq!(
- buffer.contents(),
- &b"\x1bcabc\x1b]2;title3\x07def\x1b]0;title4\x07ghi"[..]
- );
- assert_eq!(buffer.len(), 33);
- assert_eq!(buffer.title(), "title4");
- }
-
- #[test]
- fn test_size_limit_client() {
- let mut buffer = Buffer::new(100);
-
- let n = buffer.append_client(b"foobarbazq", 0);
- assert_eq!(buffer.contents(), b"foobarbazq");
- assert_eq!(buffer.len(), 10);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let n = buffer.append_client("0123456789".repeat(9).as_ref(), 10);
- assert_eq!(
- buffer.contents(),
- &b"foobarbazq012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"[..]
- );
- assert_eq!(buffer.len(), 100);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let n = buffer.append_client(b"z", 100);
- assert_eq!(
- buffer.contents(),
- &b"1234567890123456789012345678901234567890123456789z"[..]
- );
- assert_eq!(buffer.len(), 50);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 51);
-
- let n = buffer.append_client("abcdefghij".repeat(15).as_ref(), 50);
- assert_eq!(
- buffer.contents(),
- &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..]
- );
- assert_eq!(buffer.len(), 150);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 50);
- }
-
- #[test]
- fn test_written() {
- let mut buffer = Buffer::new(100);
-
- let n = buffer.append_client("abcdefghij".repeat(15).as_ref(), 0);
- assert_eq!(
- buffer.contents(),
- &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..]
- );
- assert_eq!(buffer.len(), 150);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let mut buffer2 = buffer.clone();
- let n = buffer2.append_client(b"z", 0);
- assert_eq!(
- buffer2.contents(),
- &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..]
- );
- assert_eq!(buffer2.len(), 151);
- assert_eq!(buffer2.title(), "");
- assert_eq!(n, 0);
-
- let mut buffer2 = buffer.clone();
- let n = buffer2.append_client(b"z", 20);
- assert_eq!(
- buffer2.contents(),
- &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..]
- );
- assert_eq!(buffer2.len(), 131);
- assert_eq!(buffer2.title(), "");
- assert_eq!(n, 20);
-
- let mut buffer2 = buffer.clone();
- let n = buffer2.append_client(b"z", 130);
- assert_eq!(
- buffer2.contents(),
- &b"bcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..]
- );
- assert_eq!(buffer2.len(), 50);
- assert_eq!(buffer2.title(), "");
- assert_eq!(n, 101);
- }
-
- #[test]
- fn test_server() {
- let mut buffer = Buffer::new(100);
-
- buffer.append_server(b"foobar");
- assert_eq!(buffer.contents(), b"foobar");
- assert_eq!(buffer.len(), 6);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server(b"\x1b[3J\x1b[H\x1b[2J");
- assert_eq!(buffer.contents(), b"foobar\x1b[3J\x1b[H\x1b[2J");
- assert_eq!(buffer.len(), 17);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server("abcdefghij".repeat(8).as_ref());
- assert_eq!(buffer.contents(), &b"foobar\x1b[3J\x1b[H\x1b[2Jabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..]);
- assert_eq!(buffer.len(), 97);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server(b"abcd");
- assert_eq!(buffer.contents(), &b"\x1b[3J\x1b[H\x1b[2Jabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcd"[..]);
- assert_eq!(buffer.len(), 95);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server("\x1b[H\x1b[Jfooo".repeat(8).as_ref());
- assert_eq!(buffer.contents(), &b"\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo"[..]);
- assert_eq!(buffer.len(), 80);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server("abcdefghij".repeat(5).as_ref());
- assert_eq!(buffer.contents(), &b"\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfoooabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..]);
- assert_eq!(buffer.len(), 100);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server(b"z");
- assert_eq!(buffer.contents(), &b"\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfoooabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..]);
- assert_eq!(buffer.len(), 91);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server(b"bcdefghijabcdefghij\x1b[H\x1b[Jfooo");
- assert_eq!(buffer.contents(), &b"\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfoooabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijzbcdefghijabcdefghij\x1b[H\x1b[Jfooo"[..]);
- assert_eq!(buffer.len(), 100);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server(b"abcdefghijz");
- assert_eq!(
- buffer.contents(),
- &b"bcdefghijzbcdefghijabcdefghij\x1b[H\x1b[Jfoooabcdefghijz"[..]
- );
- assert_eq!(buffer.len(), 50);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server("\x1bcfooobaar".repeat(8).as_ref());
- assert_eq!(buffer.contents(), b"\x1bcfooobaar");
- assert_eq!(buffer.len(), 10);
- assert_eq!(buffer.title(), "");
- }
-}