feat(userspace): programs in /bin w/o .elf; bin manifests (needs = display) so shell runs snake; Ctrl+C kills foreground via terminal interrupt notification; ctrl keys; sleep
This commit is contained in:
@@ -2,14 +2,21 @@ extern crate std;
|
||||
|
||||
use crate::server::{BACKSPACE, Console, TerminalServer};
|
||||
use server_framework::Server;
|
||||
use std::vec;
|
||||
use std::vec::Vec;
|
||||
use types::bounded::CapSlot;
|
||||
use types::log::{text_of, text_words};
|
||||
use types::terminal::{CTRL_C, KEYS_BIT, TIMER_BIT};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Fake {
|
||||
keys: Vec<u8>,
|
||||
/// Typed while nobody reads a line.
|
||||
typed: Vec<u8>,
|
||||
flushes: usize,
|
||||
timers: usize,
|
||||
focus_taken: usize,
|
||||
interrupted: Vec<CapSlot>,
|
||||
}
|
||||
|
||||
impl Console for Fake {
|
||||
@@ -24,6 +31,15 @@ impl Console for Fake {
|
||||
fn next_key(&mut self) -> u8 {
|
||||
self.keys.remove(0)
|
||||
}
|
||||
fn poll_key(&mut self) -> Option<u8> {
|
||||
(!self.typed.is_empty()).then(|| self.typed.remove(0))
|
||||
}
|
||||
fn take_focus(&mut self) {
|
||||
self.focus_taken += 1;
|
||||
}
|
||||
fn interrupt(&mut self, notification: CapSlot) {
|
||||
self.interrupted.push(notification);
|
||||
}
|
||||
}
|
||||
|
||||
fn term(keys: &[u8]) -> TerminalServer<Fake> {
|
||||
@@ -45,7 +61,7 @@ fn writes_land_on_the_grid_and_flush_once_per_burst() {
|
||||
}
|
||||
assert_eq!(&t.grid().row(0)[..11], b"hello world");
|
||||
assert_eq!((t.console().timers, t.console().flushes), (1, before));
|
||||
t.signal(1);
|
||||
t.signal(TIMER_BIT);
|
||||
assert_eq!(t.console().flushes, before + 1);
|
||||
}
|
||||
|
||||
@@ -68,3 +84,62 @@ fn backspace_on_an_empty_line_does_nothing() {
|
||||
let mut t = term(&[BACKSPACE, b'x', b'\n']);
|
||||
assert_eq!(read(&mut t), b"x\n");
|
||||
}
|
||||
|
||||
fn line_of(t: &mut TerminalServer<Fake>) -> Vec<u8> {
|
||||
let mut line = read(t);
|
||||
while line.last() != Some(&b'\n') {
|
||||
line.extend(read(t));
|
||||
}
|
||||
line
|
||||
}
|
||||
|
||||
fn on_interrupt(t: &mut TerminalServer<Fake>, slot: u8) -> usize {
|
||||
t.handle_call(1, [types::terminal::ON_INTERRUPT, 0, 0, 0], CapSlot::new(slot)).0[0]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ctrl_c_while_a_program_runs_interrupts_it() {
|
||||
let mut t = term(&[]);
|
||||
assert_eq!(on_interrupt(&mut t, 7), 0);
|
||||
t.console_mut_for_test().typed = vec![b'x', CTRL_C];
|
||||
t.signal(KEYS_BIT);
|
||||
assert_eq!(t.console().interrupted, [CapSlot::new(7).unwrap()]);
|
||||
assert_eq!(&t.grid().row(0)[..2], b"^C");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_the_first_interrupt_notification_is_taken() {
|
||||
let mut t = term(&[]);
|
||||
assert_eq!(on_interrupt(&mut t, 7), 0);
|
||||
assert_ne!(on_interrupt(&mut t, 8), 0);
|
||||
t.console_mut_for_test().typed = vec![CTRL_C];
|
||||
t.signal(KEYS_BIT);
|
||||
assert_eq!(t.console().interrupted, [CapSlot::new(7).unwrap()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keys_typed_ahead_start_the_next_line() {
|
||||
let mut t = term(b"s\n");
|
||||
t.console_mut_for_test().typed = b"l".to_vec();
|
||||
t.signal(KEYS_BIT);
|
||||
assert!(t.console().interrupted.is_empty());
|
||||
assert_eq!(line_of(&mut t), b"ls\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ctrl_c_drops_the_line_being_typed() {
|
||||
let mut t = term(&[b'r', b'm', CTRL_C, b'l', b's', b'\n']);
|
||||
assert_eq!(on_interrupt(&mut t, 7), 0);
|
||||
assert_eq!(line_of(&mut t), b"\n");
|
||||
assert!(t.console().interrupted.is_empty(), "nothing runs to interrupt");
|
||||
assert_eq!(line_of(&mut t), b"ls\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redraw_takes_focus_back_and_paints_every_row() {
|
||||
let mut t = term(&[]);
|
||||
t.handle_call(1, text_words(b"hi"), None);
|
||||
t.handle_call(1, [types::terminal::REDRAW, 0, 0, 0], None);
|
||||
assert_eq!(t.console().focus_taken, 1);
|
||||
assert_eq!(&t.grid().row(0)[..2], b"hi");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user