Refactor tests
diff --git a/Cargo.toml b/Cargo.toml
index 643422d..7ef2aa4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,3 +28,4 @@
[dev-dependencies]
tempdir = "0.3"
+assert_matches = "1.2"
diff --git a/src/edit.rs b/src/edit.rs
index 045ed3a..0cce2b6 100644
--- a/src/edit.rs
+++ b/src/edit.rs
@@ -493,10 +493,11 @@
mod test {
use super::init_state;
use history::History;
+ use tty::Sink;
#[test]
fn edit_history_next() {
- let mut out = ::std::io::sink();
+ let mut out = Sink::new();
let line = "current edited line";
let mut s = init_state(&mut out, line, 6);
let mut history = History::new();
diff --git a/src/keymap.rs b/src/keymap.rs
index 9656e3a..e08838f 100644
--- a/src/keymap.rs
+++ b/src/keymap.rs
@@ -813,9 +813,9 @@
KeyPress::Ctrl('T') => Cmd::TransposeChars,
KeyPress::Ctrl('U') => {
if positive {
- Cmd::Kill(Movement::BeginningOfLine)
+ Cmd::Kill(Movement::BeginningOfLine)
} else {
- Cmd::Kill(Movement::EndOfLine)
+ Cmd::Kill(Movement::EndOfLine)
}
},
KeyPress::Ctrl('Q') | // most terminals override Ctrl+Q to resume execution
diff --git a/src/lib.rs b/src/lib.rs
index e898b34..903fbaa 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -802,122 +802,7 @@
}
#[cfg(test)]
-mod test {
- use std::cell::RefCell;
- use std::collections::HashMap;
- use std::rc::Rc;
-
- use super::{Editor, Result};
- use completion::Completer;
- use config::Config;
- use consts::KeyPress;
- use edit::init_state;
- use keymap::{Cmd, InputState};
-
- fn init_editor(keys: &[KeyPress]) -> Editor<()> {
- let mut editor = Editor::<()>::new();
- editor.term.keys.extend(keys.iter().cloned());
- editor
- }
-
- struct SimpleCompleter;
- impl Completer for SimpleCompleter {
- fn complete(&self, line: &str, _pos: usize) -> Result<(usize, Vec<String>)> {
- Ok((0, vec![line.to_owned() + "t"]))
- }
- }
-
- #[test]
- fn complete_line() {
- let mut out = ::std::io::sink();
- let mut s = init_state(&mut out, "rus", 3);
- let config = Config::default();
- let mut input_state = InputState::new(&config, Rc::new(RefCell::new(HashMap::new())));
- let keys = &[KeyPress::Enter];
- let mut rdr = keys.iter();
- let completer = SimpleCompleter;
- let cmd = super::complete_line(
- &mut rdr,
- &mut s,
- &mut input_state,
- &completer,
- &Config::default(),
- ).unwrap();
- assert_eq!(Some(Cmd::AcceptLine), cmd);
- assert_eq!("rust", s.line.as_str());
- assert_eq!(4, s.line.pos());
- }
-
- fn assert_line(keys: &[KeyPress], expected_line: &str) {
- let mut editor = init_editor(keys);
- let actual_line = editor.readline(&">>").unwrap();
- assert_eq!(expected_line, actual_line);
- }
-
- #[test]
- fn delete_key() {
- assert_line(
- &[KeyPress::Char('a'), KeyPress::Delete, KeyPress::Enter],
- "a",
- );
- assert_line(
- &[
- KeyPress::Char('a'),
- KeyPress::Left,
- KeyPress::Delete,
- KeyPress::Enter,
- ],
- "",
- );
- }
-
- #[test]
- fn down_key() {
- assert_line(&[KeyPress::Down, KeyPress::Enter], "");
- }
-
- #[test]
- fn end_key() {
- assert_line(&[KeyPress::End, KeyPress::Enter], "");
- }
-
- #[test]
- fn home_key() {
- assert_line(&[KeyPress::Home, KeyPress::Enter], "");
- }
-
- #[test]
- fn left_key() {
- assert_line(&[KeyPress::Left, KeyPress::Enter], "");
- }
-
- #[test]
- fn meta_backspace_key() {
- assert_line(&[KeyPress::Meta('\x08'), KeyPress::Enter], "");
- }
-
- #[test]
- fn page_down_key() {
- assert_line(&[KeyPress::PageDown, KeyPress::Enter], "");
- }
-
- #[test]
- fn page_up_key() {
- assert_line(&[KeyPress::PageUp, KeyPress::Enter], "");
- }
-
- #[test]
- fn right_key() {
- assert_line(&[KeyPress::Right, KeyPress::Enter], "");
- }
-
- #[test]
- fn up_key() {
- assert_line(&[KeyPress::Up, KeyPress::Enter], "");
- }
-
- #[test]
- fn unknown_esc_key() {
- assert_line(&[KeyPress::UnknownEscSeq, KeyPress::Enter], "");
- }
-}
+#[macro_use]
+extern crate assert_matches;
+#[cfg(test)]
+mod test;
diff --git a/src/test/common.rs b/src/test/common.rs
new file mode 100644
index 0000000..8205bc2
--- /dev/null
+++ b/src/test/common.rs
@@ -0,0 +1,112 @@
+use super::{assert_cursor, assert_line, assert_line_with_initial, init_editor};
+use consts::KeyPress;
+use error::ReadlineError;
+
+#[test]
+fn home_key() {
+ assert_cursor(("", ""), &[KeyPress::Home, KeyPress::Enter], 0);
+ assert_cursor(("Hi", ""), &[KeyPress::Home, KeyPress::Enter], 0);
+}
+
+#[test]
+fn end_key() {
+ assert_cursor(("", ""), &[KeyPress::End, KeyPress::Enter], 0);
+ //assert_cursor(("H", "i"), &[KeyPress::End, KeyPress::Enter], 2); FIXME
+}
+
+#[test]
+fn left_key() {
+ assert_cursor(("Hi", ""), &[KeyPress::Left, KeyPress::Enter], 1);
+ assert_cursor(("H", "i"), &[KeyPress::Left, KeyPress::Enter], 0);
+ assert_cursor(("", "Hi"), &[KeyPress::Left, KeyPress::Enter], 0);
+}
+
+#[test]
+fn right_key() {
+ assert_cursor(("", ""), &[KeyPress::Right, KeyPress::Enter], 0);
+ assert_cursor(("", "Hi"), &[KeyPress::Right, KeyPress::Enter], 1);
+ assert_cursor(("B", "ye"), &[KeyPress::Right, KeyPress::Enter], 2);
+}
+
+#[test]
+fn enter_key() {
+ assert_line(&[KeyPress::Enter], "");
+ assert_line(&[KeyPress::Char('a'), KeyPress::Enter], "a");
+ assert_line_with_initial(("Hi", ""), &[KeyPress::Enter], "Hi");
+ assert_line_with_initial(("", "Hi"), &[KeyPress::Enter], "Hi");
+ assert_line_with_initial(("H", "i"), &[KeyPress::Enter], "Hi");
+}
+
+#[test]
+fn newline_key() {
+ assert_line(&[KeyPress::Ctrl('J')], "");
+ assert_line(&[KeyPress::Char('a'), KeyPress::Ctrl('J')], "a");
+}
+
+#[test]
+fn eof_key() {
+ let mut editor = init_editor(&[KeyPress::Ctrl('D')]);
+ let err = editor.readline(">>");
+ assert_matches!(err, Err(ReadlineError::Eof));
+
+ assert_line(
+ &[KeyPress::Char('a'), KeyPress::Ctrl('D'), KeyPress::Enter],
+ "a",
+ );
+ assert_line_with_initial(("", "Hi"), &[KeyPress::Ctrl('D'), KeyPress::Enter], "i");
+}
+
+#[test]
+fn interrupt_key() {
+ let mut editor = init_editor(&[KeyPress::Ctrl('C')]);
+ let err = editor.readline(">>");
+ assert_matches!(err, Err(ReadlineError::Interrupted));
+
+ let mut editor = init_editor(&[KeyPress::Ctrl('C')]);
+ let err = editor.readline_with_initial(">>", ("Hi", ""));
+ assert_matches!(err, Err(ReadlineError::Interrupted));
+}
+
+#[test]
+fn delete_key() {
+ assert_line_with_initial(("a", ""), &[KeyPress::Delete, KeyPress::Enter], "a");
+ assert_line_with_initial(("", "a"), &[KeyPress::Delete, KeyPress::Enter], "");
+}
+
+#[test]
+fn ctrl_t() {
+ assert_line_with_initial(("a", "b"), &[KeyPress::Ctrl('T'), KeyPress::Enter], "ba");
+ assert_line_with_initial(
+ ("ab", "cd"),
+ &[KeyPress::Ctrl('T'), KeyPress::Enter],
+ "acbd",
+ );
+}
+
+#[test]
+fn ctrl_u() {
+ assert_line_with_initial(("a", "b"), &[KeyPress::Ctrl('U'), KeyPress::Enter], "b");
+ assert_line_with_initial(("", "a"), &[KeyPress::Ctrl('U'), KeyPress::Enter], "a");
+}
+
+#[test]
+fn ctrl_v() {
+ assert_line(
+ &[KeyPress::Ctrl('V'), KeyPress::Char('\t'), KeyPress::Enter],
+ "\t",
+ );
+}
+
+#[test]
+fn ctrl_w() {
+ assert_line_with_initial(
+ ("Hello, ", "world"),
+ &[KeyPress::Ctrl('W'), KeyPress::Enter],
+ "world",
+ );
+ assert_line_with_initial(
+ ("Hello, world.", ""),
+ &[KeyPress::Ctrl('W'), KeyPress::Enter],
+ "Hello, ",
+ );
+}
diff --git a/src/test/mod.rs b/src/test/mod.rs
new file mode 100644
index 0000000..b741868
--- /dev/null
+++ b/src/test/mod.rs
@@ -0,0 +1,93 @@
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::rc::Rc;
+
+use super::{Editor, Result};
+use completion::Completer;
+use config::Config;
+use consts::KeyPress;
+use edit::init_state;
+use keymap::{Cmd, InputState};
+use tty::Sink;
+
+mod common;
+
+fn init_editor(keys: &[KeyPress]) -> Editor<()> {
+ let mut editor = Editor::<()>::new();
+ editor.term.keys.extend(keys.iter().cloned());
+ editor
+}
+
+struct SimpleCompleter;
+impl Completer for SimpleCompleter {
+ fn complete(&self, line: &str, _pos: usize) -> Result<(usize, Vec<String>)> {
+ Ok((0, vec![line.to_owned() + "t"]))
+ }
+}
+
+#[test]
+fn complete_line() {
+ let mut out = Sink::new();
+ let mut s = init_state(&mut out, "rus", 3);
+ let config = Config::default();
+ let mut input_state = InputState::new(&config, Rc::new(RefCell::new(HashMap::new())));
+ let keys = &[KeyPress::Enter];
+ let mut rdr = keys.iter();
+ let completer = SimpleCompleter;
+ let cmd = super::complete_line(
+ &mut rdr,
+ &mut s,
+ &mut input_state,
+ &completer,
+ &Config::default(),
+ ).unwrap();
+ assert_eq!(Some(Cmd::AcceptLine), cmd);
+ assert_eq!("rust", s.line.as_str());
+ assert_eq!(4, s.line.pos());
+}
+
+fn assert_line(keys: &[KeyPress], expected_line: &str) {
+ let mut editor = init_editor(keys);
+ let actual_line = editor.readline(">>").unwrap();
+ assert_eq!(expected_line, actual_line);
+}
+fn assert_line_with_initial(initial: (&str, &str), keys: &[KeyPress], expected_line: &str) {
+ let mut editor = init_editor(keys);
+ let actual_line = editor.readline_with_initial(">>", initial).unwrap();
+ assert_eq!(expected_line, actual_line);
+}
+fn assert_cursor(initial: (&str, &str), keys: &[KeyPress], expected_cursor: usize) {
+ let mut editor = init_editor(keys);
+ editor.readline_with_initial("", initial).unwrap();
+ assert_eq!(expected_cursor, editor.term.cursor.get());
+}
+
+#[test]
+fn down_key() {
+ assert_line(&[KeyPress::Down, KeyPress::Enter], "");
+}
+
+#[test]
+fn meta_backspace_key() {
+ assert_line(&[KeyPress::Meta('\x08'), KeyPress::Enter], "");
+}
+
+#[test]
+fn page_down_key() {
+ assert_line(&[KeyPress::PageDown, KeyPress::Enter], "");
+}
+
+#[test]
+fn page_up_key() {
+ assert_line(&[KeyPress::PageUp, KeyPress::Enter], "");
+}
+
+#[test]
+fn up_key() {
+ assert_line(&[KeyPress::Up, KeyPress::Enter], "");
+}
+
+#[test]
+fn unknown_esc_key() {
+ assert_line(&[KeyPress::UnknownEscSeq, KeyPress::Enter], "");
+}
diff --git a/src/tty/test.rs b/src/tty/test.rs
index 7a19c9a..3d7933f 100644
--- a/src/tty/test.rs
+++ b/src/tty/test.rs
@@ -1,6 +1,7 @@
//! Tests specific definitions
-use std::io::{self, Sink, Write};
+use std::cell::Cell;
use std::iter::IntoIterator;
+use std::rc::Rc;
use std::slice::Iter;
use std::vec::IntoIter;
@@ -41,40 +42,60 @@
}
#[cfg(unix)]
fn next_char(&mut self) -> Result<char> {
- unimplemented!();
+ match self.next() {
+ Some(KeyPress::Char(c)) => Ok(c),
+ None => Err(ReadlineError::Eof),
+ _ => unimplemented!(),
+ }
+ }
+}
+
+pub struct Sink {
+ cursor: Rc<Cell<usize>>, // cursor position before last command
+ last: usize,
+}
+
+impl Sink {
+ pub fn new() -> Sink {
+ Sink {
+ cursor: Rc::new(Cell::new(0)),
+ last: 0,
+ }
}
}
impl Renderer for Sink {
- fn move_cursor(&mut self, _: Position, _: Position) -> Result<()> {
+ fn move_cursor(&mut self, _: Position, new: Position) -> Result<()> {
+ self.cursor.replace(self.last);
+ self.last = new.col;
Ok(())
}
fn refresh_line(
&mut self,
- prompt: &str,
+ _: &str,
prompt_size: Position,
line: &LineBuffer,
hint: Option<String>,
_: usize,
_: usize,
) -> Result<(Position, Position)> {
- try!(self.write_all(prompt.as_bytes()));
- try!(self.write_all(line.as_bytes()));
+ let cursor = self.calculate_position(&line[..line.pos()], prompt_size);
+ self.last = cursor.col;
if let Some(hint) = hint {
- try!(self.write_all(truncate(&hint, 0, 80).as_bytes()));
+ truncate(&hint, 0, 80);
}
- Ok((prompt_size, prompt_size))
+ let end = self.calculate_position(&line, prompt_size);
+ Ok((cursor, end))
}
- /// Characters with 2 column width are correctly handled (not splitted).
- fn calculate_position(&self, _: &str, orig: Position) -> Position {
- orig
+ fn calculate_position(&self, s: &str, orig: Position) -> Position {
+ let mut pos = orig;
+ pos.col += s.len();
+ pos
}
- fn write_and_flush(&mut self, buf: &[u8]) -> Result<()> {
- try!(self.write_all(buf));
- try!(self.flush());
+ fn write_and_flush(&mut self, _: &[u8]) -> Result<()> {
Ok(())
}
@@ -82,12 +103,10 @@
Ok(())
}
- /// Clear the screen. Used to handle ctrl+l
fn clear_screen(&mut self) -> Result<()> {
Ok(())
}
- /// Check if a SIGWINCH signal has been received
fn sigwinch(&self) -> bool {
false
}
@@ -105,6 +124,7 @@
#[derive(Clone, Debug)]
pub struct DummyTerminal {
pub keys: Vec<KeyPress>,
+ pub cursor: Rc<Cell<usize>>, // cursor position before last command
}
impl Term for DummyTerminal {
@@ -113,18 +133,18 @@
type Mode = Mode;
fn new() -> DummyTerminal {
- DummyTerminal { keys: Vec::new() }
+ DummyTerminal {
+ keys: Vec::new(),
+ cursor: Rc::new(Cell::new(0)),
+ }
}
// Init checks:
- /// Check if current terminal can provide a rich line-editing user
- /// interface.
fn is_unsupported(&self) -> bool {
false
}
- /// check if stdin is connected to a terminal.
fn is_stdin_tty(&self) -> bool {
true
}
@@ -135,13 +155,15 @@
Ok(())
}
- /// Create a RAW reader
fn create_reader(&self, _: &Config) -> Result<IntoIter<KeyPress>> {
Ok(self.keys.clone().into_iter())
}
fn create_writer(&self) -> Sink {
- io::sink()
+ Sink {
+ cursor: self.cursor.clone(),
+ last: 0,
+ }
}
}