-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds incompleteness detection tests #819
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -534,7 +534,10 @@ impl<'top, D: Decoder> ExpandedStructIterator<'top, D> { | |
evaluator.push(expansion); | ||
let expanded_value = match evaluator.next()? { | ||
Some(item) => item, | ||
None => return IonResult::decoding_error(format!("macros in field name position must produce a single struct; '{:?}' produced nothing", invocation)), | ||
None => { | ||
// The macro produced an empty stream; return to reading from input. | ||
return Ok(()); | ||
} | ||
Comment on lines
-537
to
+540
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗺️ The |
||
}; | ||
let struct_ = match expanded_value.read()? { | ||
ExpandedValueRef::Struct(s) => s, | ||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -141,13 +141,23 @@ impl<'data> From<InvalidInputError<'data>> for IonError { | |
let (buffer_head, buffer_tail) = match input.as_text() { | ||
// The buffer contains UTF-8 bytes, so we'll display it as text | ||
Ok(text) => { | ||
let head = text.chars().take(NUM_CHARS_TO_SHOW).collect::<String>(); | ||
let tail_backwards = text | ||
.chars() | ||
.rev() | ||
let mut head_chars = text.chars(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗺️ This part of the PR is making sure that the error's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's the little things. |
||
let mut head = (&mut head_chars) | ||
.take(NUM_CHARS_TO_SHOW) | ||
.collect::<String>(); | ||
if head_chars.next().is_some() { | ||
head.push_str("..."); | ||
} | ||
let mut tail_chars = text.chars().rev(); | ||
let tail_backwards = (&mut tail_chars) | ||
.take(NUM_CHARS_TO_SHOW) | ||
.collect::<Vec<char>>(); | ||
let tail = tail_backwards.iter().rev().collect::<String>(); | ||
let mut tail = String::new(); | ||
if tail_chars.next().is_some() { | ||
tail.push_str("..."); | ||
} | ||
tail.push_str(tail_backwards.iter().rev().collect::<String>().as_str()); | ||
|
||
(head, tail) | ||
} | ||
// The buffer contains non-text bytes, so we'll show its contents as formatted hex | ||
|
@@ -170,8 +180,8 @@ impl<'data> From<InvalidInputError<'data>> for IonError { | |
message, | ||
r#" | ||
offset={} | ||
buffer head=<{}...> | ||
buffer tail=<...{}> | ||
buffer head=<{}> | ||
buffer tail=<{}> | ||
buffer len={} | ||
"#, | ||
invalid_input_error.input.offset(), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#![cfg(feature = "experimental-reader-writer")] | ||
|
||
use crate::ion_tests::{DataStraw, ELEMENT_GLOBAL_SKIP_LIST}; | ||
use ion_rs::{AnyEncoding, ElementReader, IonError, IonStream, Reader}; | ||
use std::fs; | ||
use std::io::BufReader; | ||
use test_generator::test_resources; | ||
|
||
#[test_resources("ion-tests/iontestdata_1_1/good/**/*.ion")] | ||
fn detect_incomplete_input(file_name: &str) { | ||
let skip_list_1_1: Vec<String> = ELEMENT_GLOBAL_SKIP_LIST | ||
.iter() | ||
.map(|file_1_0| file_1_0.replace("_1_0", "_1_1")) | ||
.collect(); | ||
if skip_list_1_1.contains(&file_name.to_owned()) { | ||
return; | ||
} | ||
println!("testing {file_name}"); | ||
let file = fs::File::open(file_name).unwrap(); | ||
let buf_reader = BufReader::new(file); | ||
let input = DataStraw::new(buf_reader); | ||
let ion_stream = IonStream::new(input); | ||
let mut reader = Reader::new(AnyEncoding, ion_stream).unwrap(); | ||
// Manually unwrap to allow for pretty-printing of errors | ||
match reader.read_all_elements() { | ||
Ok(_) => {} | ||
Err(IonError::Decoding(e)) => { | ||
panic!("{:?}: {}", e.position(), e); | ||
} | ||
Err(other) => { | ||
panic!("{other:#?}"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,8 @@ | |
#![allow(dead_code)] | ||
|
||
use std::fs::read; | ||
use std::io; | ||
use std::io::Read; | ||
use std::path::MAIN_SEPARATOR_STR as PATH_SEPARATOR; | ||
|
||
use ion_rs::v1_0; | ||
|
@@ -12,6 +14,7 @@ use ion_rs::{ | |
Symbol, Value, | ||
}; | ||
|
||
mod detect_incomplete_text; | ||
pub mod lazy_element_ion_tests; | ||
|
||
/// Concatenates two slices of string slices together. | ||
|
@@ -435,3 +438,29 @@ pub const ELEMENT_EQUIVS_SKIP_LIST: SkipList = &[ | |
"ion-tests/iontestdata_1_0/good/equivs/localSymbolTableNullSlots.ion", | ||
"ion-tests/iontestdata_1_0/good/equivs/nonIVMNoOps.ion", | ||
]; | ||
|
||
/// An implementation of `io::Read` that only yields a single byte on each | ||
/// call to `read()`. This is used in tests to confirm that the reader's | ||
/// data-incompleteness and retry logic will properly handle all corner | ||
/// cases. | ||
pub(crate) struct DataStraw<R> { | ||
input: R, | ||
} | ||
|
||
impl<R> DataStraw<R> { | ||
pub fn new(input: R) -> Self { | ||
Self { input } | ||
} | ||
} | ||
|
||
impl<R: Read> Read for DataStraw<R> { | ||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||
let single_byte_buffer = &mut [0u8; 1]; | ||
let bytes_read = self.input.read(single_byte_buffer)?; | ||
if bytes_read == 0 { | ||
return Ok(0); | ||
} | ||
buf[0] = single_byte_buffer[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is byte-by-byte enough for full coverage, or do you also need to test what happens when the stream returns 0 bytes at any point (i.e. having the DataStraw alternate between returning one byte and zero bytes)? Or is the reader not continuable, but just needs to be able to handle arbitrary-sized chunks being provided from the input? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading one byte at a time should be enough for full coverage. The reader is always operating on a fixed input buffer slice that holds (at least) the current top level value. If the reader hit an |
||
Ok(1) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ I've disabled this check until we address #812.