Skip to content
Merged
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
32 changes: 32 additions & 0 deletions src/bin/sqlite-rs/readline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,19 @@ mod tests {
assert_eq!(editor.as_str(), "zab");
}

#[test]
fn dispatch_ctrl_e_moves_to_end() {
let mut editor = LineEditor::new();
editor.set("abc");
editor.move_home();
let mut src = byte_source(&[]);
assert!(matches!(
dispatch_byte(0x05, &mut editor, &mut src), // Ctrl-E
Ok(Dispatch::Continue)
));
assert_eq!(editor.cursor(), 3);
}

#[test]
fn dispatch_ctrl_k_and_ctrl_u_clear_to_end_and_home() {
let mut editor = LineEditor::new();
Expand Down Expand Up @@ -664,10 +677,29 @@ mod tests {
apply_escape_action(&mut editor, EscapeAction::Home, &mut history);
assert_eq!(editor.cursor(), 0);

apply_escape_action(&mut editor, EscapeAction::Right, &mut history);
assert_eq!(editor.cursor(), 1);

apply_escape_action(&mut editor, EscapeAction::End, &mut history);
assert_eq!(editor.cursor(), 2);
}

#[test]
fn readline_error_display_matches_each_variant() {
assert_eq!(ReadlineError::Eof.to_string(), "EOF");
assert_eq!(ReadlineError::Interrupted.to_string(), "interrupted");
let io_err = ReadlineError::Io(io::Error::other("boom"));
assert_eq!(io_err.to_string(), "boom");
}

#[test]
fn redraw_writes_highlighted_and_plain_prompt_lines() {
let mut editor = LineEditor::new();
editor.set("select 1");
redraw("> ", &editor, true);
redraw("> ", &editor, false);
}

#[test]
fn apply_escape_action_up_and_down_navigate_history() {
let mut history = History::new();
Expand Down
36 changes: 36 additions & 0 deletions src/bin/sqlite-rs/readline/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,39 @@ pub fn cursor_to_col(col: usize) -> String {

/// Clears from the cursor to the end of the line.
pub const CLEAR_TO_EOL: &str = "\x1b[K";

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;

#[test]
fn cursor_to_col_formats_escape_sequence() {
assert_eq!(cursor_to_col(0), "\r\x1b[0C");
assert_eq!(cursor_to_col(42), "\r\x1b[42C");
}

#[test]
fn color_constants_are_expected_escape_codes() {
assert_eq!(RESET, "\x1b[0m");
assert_eq!(BOLD_BLUE, "\x1b[1;34m");
assert_eq!(GREEN, "\x1b[32m");
assert_eq!(CYAN, "\x1b[36m");
assert_eq!(GRAY, "\x1b[90m");
assert_eq!(YELLOW, "\x1b[33m");
assert_eq!(CLEAR_TO_EOL, "\x1b[K");
}

#[test]
fn write_flush_writes_to_stdout() {
write_flush("").unwrap();
}

// `enable()` falls back to `None` when stdin isn't a tty (piped input),
// which is exactly how `cargo test` runs — no controlling tty attached.
#[test]
fn raw_mode_enable_returns_none_without_tty() {
let result = RawMode::enable().unwrap();
assert!(result.is_none());
}
}
154 changes: 154 additions & 0 deletions src/btree/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,157 @@ impl From<PagerError> for BtreeError {
BtreeError::Pager(source)
}
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::indexing_slicing)]
mod tests {
use super::*;

#[test]
fn display_all_variants() {
assert!(BtreeError::InvalidKeyRecord(RecordError::InvalidUtf8)
.to_string()
.contains("decoding a key record"));
assert!(BtreeError::PageSource {
page_num: 1,
source: PageError::InvalidPageNumber,
}
.to_string()
.contains("reading page 1"));
assert_eq!(
BtreeError::PageTooShort {
page_num: 1,
len: 3
}
.to_string(),
"page 1 is too short (3 bytes) to contain a b-tree page header"
);
assert_eq!(
BtreeError::CursorNotPositioned {
operation: "next",
required: "seek",
}
.to_string(),
"next called on a cursor that was never positioned by seek"
);
assert_eq!(
BtreeError::UnexpectedPageType {
page_num: 2,
page_type: 0xff
}
.to_string(),
"page 2 has unexpected b-tree page type 0xff"
);
assert_eq!(
BtreeError::InvalidCellPointer {
page_num: 2,
index: 9
}
.to_string(),
"page 2 cell pointer at index 9 is out of bounds"
);
assert!(BtreeError::InvalidCellVarint {
page_num: 2,
source: RecordError::InvalidUtf8,
}
.to_string()
.contains("cell varint decode failed"));
assert_eq!(
BtreeError::PayloadTooShort { page_num: 2 }.to_string(),
"page 2 cell payload is shorter than its declared local size"
);
assert_eq!(
BtreeError::PayloadTooLarge {
page_num: 2,
payload_len: 999
}
.to_string(),
"page 2 declares an implausible payload length 999"
);
assert_eq!(
BtreeError::OverflowChainTooLong {
page_num: 2,
max: 10
}
.to_string(),
"overflow chain from page 2 exceeded 10 pages (possible cycle)"
);
assert_eq!(
BtreeError::OverflowChainCycle {
page_num: 2,
revisited_page: 3
}
.to_string(),
"overflow chain from page 2 revisited page 3 (cycle)"
);
assert_eq!(
BtreeError::OverflowChainTruncated { page_num: 2 }.to_string(),
"overflow chain from page 2 ended before all payload bytes were read"
);
assert_eq!(
BtreeError::TraversalTooLong { max: 100 }.to_string(),
"b-tree traversal visited more than 100 pages (possible cycle)"
);
assert!(BtreeError::Pager(PagerError::PendingTransaction)
.to_string()
.contains("pager error"));
assert_eq!(
BtreeError::DuplicateRowid { rowid: 5 }.to_string(),
"cannot insert duplicate rowid 5"
);
assert_eq!(
BtreeError::MissingChildRoute {
page_num: 2,
child: 4
}
.to_string(),
"interior page 2 has no routing entry for child page 4"
);
assert_eq!(
BtreeError::RowidNotFound { rowid: 5 }.to_string(),
"cannot delete rowid 5: no such row"
);
assert_eq!(
BtreeError::DuplicateKey.to_string(),
"cannot insert duplicate index key"
);
assert_eq!(
BtreeError::KeyNotFound.to_string(),
"cannot delete index key: no such entry"
);
assert_eq!(
BtreeError::InvalidRootPage {
name: "t".to_string(),
rootpage: -1
}
.to_string(),
"sqlite_master entry \"t\" has out-of-range rootpage -1"
);
assert_eq!(
BtreeError::MasterEntryNotFound {
name: "t".to_string()
}
.to_string(),
"cannot delete sqlite_master entry \"t\": no such entry"
);
assert_eq!(
BtreeError::Internal("bad state").to_string(),
"internal invariant violated: bad state"
);
}

#[test]
fn from_conversions() {
let e: BtreeError = RecordError::InvalidUtf8.into();
assert!(matches!(e, BtreeError::InvalidKeyRecord(_)));

let e: BtreeError = PagerError::PendingTransaction.into();
assert!(matches!(e, BtreeError::Pager(_)));
}

#[test]
fn implements_std_error() {
let err = BtreeError::DuplicateKey;
assert!(std::error::Error::source(&err).is_none());
}
}
95 changes: 95 additions & 0 deletions src/codegen/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,98 @@ pub(crate) use entry::{
};
pub(crate) use joins::compile_select_joined_scan;
pub(crate) use limit_scan::{is_rowid_reference, top_level_equality_operands};

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::indexing_slicing)]
mod tests {
use super::*;

#[test]
fn codegen_error_display_variants() {
assert_eq!(
CodegenError::NoFromClause.to_string(),
"SELECT has no FROM clause — not supported by this V2-scope compiler"
);
assert_eq!(
CodegenError::UnknownColumn {
name: "x".to_string()
}
.to_string(),
"unknown column \"x\""
);
assert_eq!(
CodegenError::AmbiguousColumn {
name: "x".to_string()
}
.to_string(),
"ambiguous column name: \"x\""
);
assert_eq!(
CodegenError::Unsupported {
reason: "foo".to_string()
}
.to_string(),
"unsupported: foo"
);
assert_eq!(
CodegenError::RowShapeMismatch {
table: "t".to_string(),
expected: 2,
found: 3,
}
.to_string(),
"t has 2 columns but 3 values were supplied"
);
assert_eq!(
CodegenError::CompoundColumnMismatch {
expected: 2,
found: 3,
}
.to_string(),
"SELECTs to the left and right of UNION ALL do not have the same number of result \
columns: expected 2, found 3"
);
assert_eq!(
CodegenError::CircularView {
name: "v".to_string()
}
.to_string(),
"view v is circularly defined"
);
}

#[test]
fn codegen_error_is_std_error() {
let err = CodegenError::NoFromClause;
assert!(std::error::Error::source(&err).is_none());
}

#[test]
fn scan_cursors_for_standalone_select() {
let cursors = ScanCursors::for_standalone_select();
assert_eq!(cursors.table, TABLE_CURSOR);
assert_eq!(cursors.sort, SORT_CURSOR);
assert_eq!(cursors.pseudo, PSEUDO_CURSOR);
assert_eq!(cursors.distinct, DISTINCT_CURSOR);
}

#[test]
fn scan_cursors_for_arm_offsets_by_four() {
let arm0 = ScanCursors::for_arm(0);
assert_eq!(
(arm0.table, arm0.sort, arm0.pseudo, arm0.distinct),
(0, 1, 2, 3)
);
let arm1 = ScanCursors::for_arm(1);
assert_eq!(
(arm1.table, arm1.sort, arm1.pseudo, arm1.distinct),
(4, 5, 6, 7)
);
}

#[test]
fn scan_cursors_after_arms() {
assert_eq!(ScanCursors::after_arms(0), 0);
assert_eq!(ScanCursors::after_arms(3), 12);
}
}
Loading
Loading