better tests, and new writing routines
This commit is contained in:
parent
102bed0d1c
commit
7adbc23d64
@ -46,7 +46,7 @@ pub struct Query {
|
||||
|
||||
impl Query {
|
||||
pub fn parse(data: &mut Vec<u8>) -> Self {
|
||||
let name = Name::parse(data).unwrap();
|
||||
let name = Name::parse(data);
|
||||
let query_type = RecordType::parse(data);
|
||||
let query_class = DNSClass::parse(data);
|
||||
|
||||
|
@ -3,6 +3,7 @@ use std::ops::Index;
|
||||
|
||||
use super::util;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Name {
|
||||
labels: Vec<String>
|
||||
}
|
||||
@ -12,7 +13,7 @@ impl Name {
|
||||
/// this has a max of 255 octets, with each label being less than 63.
|
||||
/// all names will be stored lowercase internally.
|
||||
/// This will consume the portions of the Vec which it is reading...
|
||||
pub fn parse(slice: &mut Vec<u8>) -> Result<Name, FromUtf8Error> {
|
||||
pub fn parse(slice: &mut Vec<u8>) -> Name {
|
||||
let mut state: LabelParseState = LabelParseState::LabelLengthOrPointer;
|
||||
let mut labels: Vec<String> = Vec::with_capacity(3); // most labels will be around three, e.g. www.example.com
|
||||
|
||||
@ -25,35 +26,47 @@ impl Name {
|
||||
state = match state {
|
||||
LabelParseState::LabelLengthOrPointer => {
|
||||
// determine what the next label is
|
||||
match slice.pop() {
|
||||
Some(0) | None => LabelParseState::Root,
|
||||
Some(byte) if byte & 0xC0 == 0xC0 => LabelParseState::Pointer(byte & 0x3F),
|
||||
Some(byte) if byte <= 0x3F => LabelParseState::Label(byte),
|
||||
match slice.last() {
|
||||
Some(&0) | None => LabelParseState::Root,
|
||||
Some(&byte) if byte & 0xC0 == 0xC0 => LabelParseState::Pointer,
|
||||
Some(&byte) if byte <= 0x3F => LabelParseState::Label,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
},
|
||||
LabelParseState::Label(count) => {
|
||||
labels.push(try!(util::parse_label(slice, count)));
|
||||
LabelParseState::Label => {
|
||||
labels.push(util::parse_character_data(slice));
|
||||
|
||||
// reset to collect more data
|
||||
LabelParseState::LabelLengthOrPointer
|
||||
},
|
||||
LabelParseState::Pointer(offset) => {
|
||||
LabelParseState::Pointer => {
|
||||
// lookup in the hashmap the label to use
|
||||
unimplemented!()
|
||||
},
|
||||
LabelParseState::Root => {
|
||||
// technically could return here...
|
||||
// need to pop() the 0 off the stack...
|
||||
slice.pop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Name { labels: labels })
|
||||
Name { labels: labels }
|
||||
}
|
||||
|
||||
pub fn write_to(&self, buf: &mut Vec<u8>) {
|
||||
let buf_len = buf.len(); // lazily assert the size is less than 255...
|
||||
for label in &self.labels {
|
||||
util::write_character_data_to(buf, label);
|
||||
assert!((buf.len() - buf_len) <= 63); // individual labels must be shorter than 63.
|
||||
}
|
||||
|
||||
// the end of the list of names
|
||||
buf.push(0);
|
||||
assert!((buf.len() - buf_len) <= 255); // the entire name needs to be less than 256.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Index<usize> for Name {
|
||||
type Output = String;
|
||||
|
||||
@ -65,35 +78,38 @@ impl Index<usize> for Name {
|
||||
/// This is the list of states for the label parsing state machine
|
||||
enum LabelParseState {
|
||||
LabelLengthOrPointer, // basically the start of the FSM
|
||||
Label(u8), // storing length of the label, must be < 63
|
||||
Pointer(u8), // location of pointer in slice,
|
||||
Root, // root is the end of the labels list, aka null
|
||||
Label, // storing length of the label, must be < 63
|
||||
Pointer, // location of pointer in slice,
|
||||
Root, // root is the end of the labels list, aka null
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::util::tests::{test_parse_data_set, test_write_data_set_to};
|
||||
|
||||
#[test]
|
||||
fn parse() {
|
||||
let data: Vec<(Vec<u8>, Vec<String>)> = vec![
|
||||
(vec![0], vec![]), // base case, only the root
|
||||
(vec![1,b'a',0], vec!["a".to_string()]), // a single 'a' label
|
||||
(vec![1,b'a',2,b'b',b'c',0], vec!["a".to_string(), "bc".to_string()]), // two labels, 'a.bc'
|
||||
(vec![1,b'a',3,0xE2,0x99,0xA5,0], vec!["a".to_string(), "♥".to_string()]), // two labels utf8, 'a.♥'
|
||||
(vec![1,b'A',0], vec!["a".to_string()]), // a single 'a' label, lowercased
|
||||
let data: Vec<(Vec<u8>, Name)> = vec![
|
||||
(vec![0], Name{ labels: vec![] }), // base case, only the root
|
||||
(vec![1,b'a',0], Name{ labels: vec!["a".to_string()] }), // a single 'a' label
|
||||
(vec![1,b'a',2,b'b',b'c',0], Name{ labels: vec!["a".to_string(), "bc".to_string()] }), // two labels, 'a.bc'
|
||||
(vec![1,b'a',3,0xE2,0x99,0xA5,0], Name{ labels: vec!["a".to_string(), "♥".to_string()] }), // two labels utf8, 'a.♥'
|
||||
(vec![1,b'A',0], Name { labels: vec!["a".to_string()] }), // a single 'a' label, lowercased
|
||||
];
|
||||
|
||||
let mut test_num = 0;
|
||||
for (mut binary, expect) in data {
|
||||
test_num += 1;
|
||||
println!("test: {}", test_num);
|
||||
binary.reverse();
|
||||
let name = Name::parse(&mut binary).ok().unwrap();
|
||||
assert_eq!(name.labels, expect);
|
||||
if expect.len() > 0 {
|
||||
assert_eq!(name[0], expect[0]);
|
||||
}
|
||||
}
|
||||
test_parse_data_set(data, |b| Name::parse(b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_to() {
|
||||
let data: Vec<(Name, Vec<u8>)> = vec![
|
||||
(Name { labels: vec![] }, vec![0]), // base case, only the root
|
||||
(Name { labels: vec!["a".to_string()] }, vec![1,b'a',0]), // a single 'a' label
|
||||
(Name { labels: vec!["a".to_string(), "bc".to_string()] }, vec![1,b'a',2,b'b',b'c',0]), // two labels, 'a.bc'
|
||||
(Name { labels: vec!["a".to_string(), "♥".to_string()] }, vec![1,b'a',3,0xE2,0x99,0xA5,0]), // two labels utf8, 'a.♥'
|
||||
];
|
||||
|
||||
test_write_data_set_to(data, |b, n| n.write_to(b));
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use super::super::domain::Name;
|
||||
//
|
||||
// CNAME { cname: Name },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
RData::CNAME{ cname: Name::parse(data).unwrap() }
|
||||
RData::CNAME{ cname: Name::parse(data) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -24,7 +24,7 @@ use super::super::util;
|
||||
//
|
||||
// HINFO { cpu: String, os: String},
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
RData::HINFO { cpu: util::parse_character_data(data).unwrap(), os: util::parse_character_data(data).unwrap() }
|
||||
RData::HINFO { cpu: util::parse_character_data(data), os: util::parse_character_data(data)}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -26,7 +26,7 @@ use super::super::domain::Name;
|
||||
//
|
||||
// MX { preference: u16, exchange: Name },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
RData::MX { preference: util::parse_u16(data), exchange: Name::parse(data).unwrap() }
|
||||
RData::MX { preference: util::parse_u16(data), exchange: Name::parse(data) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -26,7 +26,7 @@ use super::super::domain::Name;
|
||||
//
|
||||
// NS { nsdname: Name },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
RData::NS{ nsdname: Name::parse(data).unwrap() }
|
||||
RData::NS{ nsdname: Name::parse(data) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -20,7 +20,7 @@ use super::super::domain::Name;
|
||||
//
|
||||
// PTR { ptrdname: Name },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
RData::PTR{ ptrdname: Name::parse(data).unwrap() }
|
||||
RData::PTR{ ptrdname: Name::parse(data) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -70,8 +70,8 @@ use super::super::domain::Name;
|
||||
// SOA { mname: Name, rname: Name, serial: u32, refresh: i32, retry: i32, expire: i32, minimum: u32, },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
RData::SOA{
|
||||
mname: Name::parse(data).unwrap(),
|
||||
rname: Name::parse(data).unwrap(),
|
||||
mname: Name::parse(data),
|
||||
rname: Name::parse(data),
|
||||
serial: util::parse_u32(data),
|
||||
refresh: util::parse_i32(data),
|
||||
retry: util::parse_i32(data),
|
||||
|
@ -22,7 +22,7 @@ pub fn parse(data: &mut Vec<u8>, count: u16) -> RData {
|
||||
let mut strings = Vec::with_capacity(1);
|
||||
|
||||
while data_len - data.len() < count as usize {
|
||||
util::parse_character_data(data).and_then(|s: String| -> Result<(), FromUtf8Error> { strings.push(s); Ok(()) });
|
||||
strings.push(util::parse_character_data(data));
|
||||
}
|
||||
RData::TXT{ txt_data: strings }
|
||||
}
|
||||
|
@ -51,6 +51,10 @@ impl RecordType {
|
||||
pub fn parse(data: &mut Vec<u8>) -> Self {
|
||||
util::parse_u16(data).into()
|
||||
}
|
||||
|
||||
pub fn write_to(buf: &mut Vec<u8>) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,7 +30,7 @@ impl Record {
|
||||
|
||||
// NAME an owner name, i.e., the name of the node to which this
|
||||
// resource record pertains.
|
||||
let name_labels: domain::Name = try!(domain::Name::parse(&mut data));
|
||||
let name_labels: domain::Name = domain::Name::parse(&mut data);
|
||||
|
||||
// TYPE two octets containing one of the RR TYPE codes.
|
||||
let record_type: RecordType = RecordType::parse(&mut data);
|
||||
|
195
src/rr/util.rs
195
src/rr/util.rs
@ -1,22 +1,17 @@
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
// TODO: !!! Need to convert to an internally stored [] and not destroy on read,
|
||||
// so that "pointer" types can be handled
|
||||
|
||||
///<character-string> is a single
|
||||
/// length octet followed by that number of characters. <character-string>
|
||||
/// is treated as binary information, and can be up to 256 characters in
|
||||
/// length (including the length octet).
|
||||
///
|
||||
/// the vector should be reversed before calling.
|
||||
pub fn parse_character_data(data: &mut Vec<u8>) -> Result<String, FromUtf8Error> {
|
||||
let length: u8 = data.pop().unwrap_or(0);
|
||||
parse_label(data, length)
|
||||
}
|
||||
|
||||
/// parse a label of a particular length (it's a portion of the vector)
|
||||
/// the vector should be reversed before calling.
|
||||
///```
|
||||
/// assert_eq(parse_lable(b"bbbaaa", 6).ok().unwrap(), "aaabbb".to_string());
|
||||
///```
|
||||
pub fn parse_label(data: &mut Vec<u8>, length: u8) -> Result<String, FromUtf8Error> {
|
||||
pub fn parse_character_data(data: &mut Vec<u8>) -> String {
|
||||
let length: u8 = data.pop().unwrap();
|
||||
assert!(length <= 255u8);
|
||||
// TODO once Drain stabalizes on Vec, this should be replaced...
|
||||
let mut label_vec: Vec<u8> = Vec::with_capacity(length as usize);
|
||||
for _ in 0..length as usize {
|
||||
@ -27,16 +22,38 @@ pub fn parse_label(data: &mut Vec<u8>, length: u8) -> Result<String, FromUtf8Err
|
||||
}
|
||||
|
||||
// translate bytes to string, then lowercase...
|
||||
Ok(try!(String::from_utf8(label_vec)).to_lowercase())
|
||||
String::from_utf8(label_vec).unwrap().to_lowercase()
|
||||
}
|
||||
|
||||
/// matches description from above.
|
||||
///
|
||||
/// ```
|
||||
/// use trust_dns::rr::util;
|
||||
///
|
||||
/// let mut buf: Vec<u8> = Vec::new();
|
||||
/// util::write_character_data_to(&mut buf, "abc");
|
||||
/// assert_eq!(buf, vec![3,b'a',b'b',b'c']);
|
||||
/// ```
|
||||
pub fn write_character_data_to(buf: &mut Vec<u8>, char_data: &str) {
|
||||
let char_bytes = char_data.as_bytes();
|
||||
assert!(char_bytes.len() < 256);
|
||||
|
||||
buf.reserve(char_bytes.len()+1); // reserve the full space for the string
|
||||
buf.push(char_bytes.len() as u8);
|
||||
|
||||
// a separate writer isn't necessary for label since it's the same first byte that's being written
|
||||
|
||||
// TODO use append() once it stabalizes
|
||||
for b in char_bytes {
|
||||
buf.push(*b);
|
||||
}
|
||||
}
|
||||
|
||||
/// parses the next 2 bytes into u16. This performs a byte-by-byte manipulation, there
|
||||
/// which means endianness is implicitly handled (i.e. no network to little endian (intel), issues)
|
||||
///
|
||||
/// the vector should be reversed before calling.
|
||||
pub fn parse_u16(data: &mut Vec<u8>) -> u16 {
|
||||
// TODO should this use a default rather than the panic! that will happen in the None case?
|
||||
let b1: u8 = data.pop().unwrap();
|
||||
let b2: u8 = data.pop().unwrap();
|
||||
|
||||
@ -44,6 +61,16 @@ pub fn parse_u16(data: &mut Vec<u8>) -> u16 {
|
||||
((b1 as u16) << 8) + (b2 as u16)
|
||||
}
|
||||
|
||||
pub fn write_u16_to(buf: &mut Vec<u8>, data: u16) {
|
||||
buf.reserve(2); // two bytes coming
|
||||
|
||||
let b1: u8 = (data >> 8 & 0xFF) as u8;
|
||||
let b2: u8 = (data & 0xFF) as u8;
|
||||
|
||||
buf.push(b1);
|
||||
buf.push(b2);
|
||||
}
|
||||
|
||||
/// parses the next four bytes into i32. This performs a byte-by-byte manipulation, there
|
||||
/// which means endianness is implicitly handled (i.e. no network to little endian (intel), issues)
|
||||
///
|
||||
@ -59,6 +86,20 @@ pub fn parse_i32(data: &mut Vec<u8>) -> i32 {
|
||||
((b1 as i32) << 24) + ((b2 as i32) << 16) + ((b3 as i32) << 8) + (b4 as i32)
|
||||
}
|
||||
|
||||
pub fn write_i32_to(buf: &mut Vec<u8>, data: i32) {
|
||||
buf.reserve(4); // four bytes coming...
|
||||
|
||||
let b1: u8 = (data >> 24 & 0xFF) as u8;
|
||||
let b2: u8 = (data >> 16 & 0xFF) as u8;
|
||||
let b3: u8 = (data >> 8 & 0xFF) as u8;
|
||||
let b4: u8 = (data & 0xFF) as u8;
|
||||
|
||||
buf.push(b1);
|
||||
buf.push(b2);
|
||||
buf.push(b3);
|
||||
buf.push(b4);
|
||||
}
|
||||
|
||||
/// parses the next four bytes into u32. This performs a byte-by-byte manipulation, there
|
||||
/// which means endianness is implicitly handled (i.e. no network to little endian (intel), issues)
|
||||
///
|
||||
@ -74,8 +115,24 @@ pub fn parse_u32(data: &mut Vec<u8>) -> u32 {
|
||||
((b1 as u32) << 24) + ((b2 as u32) << 16) + ((b3 as u32) << 8) + (b4 as u32)
|
||||
}
|
||||
|
||||
pub fn write_u32_to(buf: &mut Vec<u8>, data: u32) {
|
||||
buf.reserve(4); // four bytes coming...
|
||||
|
||||
let b1: u8 = (data >> 24 & 0xFF) as u8;
|
||||
let b2: u8 = (data >> 16 & 0xFF) as u8;
|
||||
let b3: u8 = (data >> 8 & 0xFF) as u8;
|
||||
let b4: u8 = (data & 0xFF) as u8;
|
||||
|
||||
buf.push(b1);
|
||||
buf.push(b2);
|
||||
buf.push(b3);
|
||||
buf.push(b4);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub mod tests {
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[test]
|
||||
fn parse_character_data() {
|
||||
let data: Vec<(Vec<u8>, String)> = vec![
|
||||
@ -86,12 +143,25 @@ mod tests {
|
||||
(vec![1,b'A'], "a".to_string()), // a single 'a' label, lowercased
|
||||
];
|
||||
|
||||
test_parse_data_set(data, |b| super::parse_character_data(b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_character_data() {
|
||||
let data: Vec<(&'static str, Vec<u8>)> = vec![
|
||||
("", vec![0]), // base case, only the root
|
||||
("a", vec![1,b'a']), // a single 'a' label
|
||||
("bc", vec![2,b'b',b'c']), // two labels, 'a.bc'
|
||||
("♥", vec![3,0xE2,0x99,0xA5]), // two labels utf8, 'a.♥'
|
||||
];
|
||||
|
||||
let mut test_num = 0;
|
||||
for (mut binary, expect) in data {
|
||||
for (data, expect) in data {
|
||||
test_num += 1;
|
||||
println!("test: {}", test_num);
|
||||
binary.reverse();
|
||||
assert_eq!(super::parse_character_data(&mut binary).ok().unwrap(), expect);
|
||||
println!("test {}: {:?}", test_num, data);
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(expect.len());
|
||||
super::write_character_data_to(&mut buf, data);
|
||||
assert_eq!(buf, expect);
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,13 +174,19 @@ mod tests {
|
||||
(vec![0xFF,0xFF], u16::max_value()),
|
||||
];
|
||||
|
||||
let mut test_num = 0;
|
||||
for (mut binary, expect) in data {
|
||||
test_num += 1;
|
||||
println!("test: {}", test_num);
|
||||
binary.reverse();
|
||||
assert_eq!(super::parse_u16(&mut binary), expect);
|
||||
}
|
||||
test_parse_data_set(data, |b| super::parse_u16(b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_u16() {
|
||||
let data: Vec<(u16, Vec<u8>)> = vec![
|
||||
(0, vec![0x00,0x00]),
|
||||
(1, vec![0x00,0x01]),
|
||||
(256, vec![0x01,0x00]),
|
||||
(u16::max_value(), vec![0xFF,0xFF]),
|
||||
];
|
||||
|
||||
test_write_data_set_to(data, |b, d| super::write_u16_to(b,d));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -126,13 +202,23 @@ mod tests {
|
||||
(vec![0x7F,0xFF,0xFF,0xFF], i32::max_value()),
|
||||
];
|
||||
|
||||
let mut test_num = 0;
|
||||
for (mut binary, expect) in data {
|
||||
test_num += 1;
|
||||
println!("test: {}", test_num);
|
||||
binary.reverse();
|
||||
assert_eq!(super::parse_i32(&mut binary), expect);
|
||||
}
|
||||
test_parse_data_set(data, |b| super::parse_i32(b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_i32() {
|
||||
let data: Vec<(i32, Vec<u8>)> = vec![
|
||||
(0, vec![0x00,0x00,0x00,0x00]),
|
||||
(1, vec![0x00,0x00,0x00,0x01]),
|
||||
(256, vec![0x00,0x00,0x01,0x00]),
|
||||
(256*256, vec![0x00,0x01,0x00,0x00]),
|
||||
(256*256*256, vec![0x01,0x00,0x00,0x00]),
|
||||
(-1, vec![0xFF,0xFF,0xFF,0xFF]),
|
||||
(i32::min_value(), vec![0x80,0x00,0x00,0x00]),
|
||||
(i32::max_value(), vec![0x7F,0xFF,0xFF,0xFF]),
|
||||
];
|
||||
|
||||
test_write_data_set_to(data, |b, d| super::write_i32_to(b,d));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -148,12 +234,47 @@ mod tests {
|
||||
(vec![0x7F,0xFF,0xFF,0xFF], i32::max_value() as u32),
|
||||
];
|
||||
|
||||
let mut test_num = 0;
|
||||
for (mut binary, expect) in data {
|
||||
test_num += 1;
|
||||
println!("test: {} binary: {:?} expect: {:?}", test_num, binary, expect);
|
||||
test_parse_data_set(data, |b| super::parse_u32(b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_u32() {
|
||||
let data: Vec<(u32, Vec<u8>)> = vec![
|
||||
(0, vec![0x00,0x00,0x00,0x00]),
|
||||
(1, vec![0x00,0x00,0x00,0x01]),
|
||||
(256, vec![0x00,0x00,0x01,0x00]),
|
||||
(256*256, vec![0x00,0x01,0x00,0x00]),
|
||||
(256*256*256, vec![0x01,0x00,0x00,0x00]),
|
||||
(u32::max_value(), vec![0xFF,0xFF,0xFF,0xFF]),
|
||||
(2147483648, vec![0x80,0x00,0x00,0x00]),
|
||||
(i32::max_value() as u32, vec![0x7F,0xFF,0xFF,0xFF]),
|
||||
];
|
||||
|
||||
test_write_data_set_to(data, |b, d| super::write_u32_to(b,d));
|
||||
}
|
||||
|
||||
|
||||
pub fn test_parse_data_set<E, F>(data_set: Vec<(Vec<u8>, E)>, parse_func: F)
|
||||
where E: PartialEq<E> + Debug, F: Fn(&mut Vec<u8>) -> E {
|
||||
let mut test_pass = 0;
|
||||
for (mut binary, expect) in data_set {
|
||||
test_pass += 1;
|
||||
println!("test {}: {:?}", test_pass, binary);
|
||||
binary.reverse();
|
||||
assert_eq!(super::parse_u32(&mut binary), expect);
|
||||
assert_eq!(parse_func(&mut binary), expect);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_write_data_set_to<S, F>(data_set: Vec<(S, Vec<u8>)>, write_func: F)
|
||||
where F: Fn(&mut Vec<u8>, S), S: Debug {
|
||||
let mut test_pass = 0;
|
||||
|
||||
for (data, expect) in data_set {
|
||||
test_pass += 1;
|
||||
println!("test {}: {:?}", test_pass, data);
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(expect.len());
|
||||
write_func(&mut buf, data);
|
||||
assert_eq!(buf, expect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user