Add packet tests
This commit is contained in:
parent
68d05e3a11
commit
c345a5b531
2 changed files with 236 additions and 7 deletions
|
|
@ -9,7 +9,7 @@ pub struct Message;
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
pub fn send_data(socket: &UdpSocket, data: &[u8]) -> Result<(), Box<dyn Error>> {
|
pub fn send_data(socket: &UdpSocket, data: &[u8]) -> Result<(), Box<dyn Error>> {
|
||||||
let buf = [&[0x00, Opcode::Data as u8], data].concat();
|
let buf = [&Opcode::Data.as_bytes()[..], data].concat();
|
||||||
|
|
||||||
socket.send(&buf)?;
|
socket.send(&buf)?;
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ impl Message {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_ack(socket: &UdpSocket, block: u16) -> Result<(), Box<dyn Error>> {
|
pub fn send_ack(socket: &UdpSocket, block: u16) -> Result<(), Box<dyn Error>> {
|
||||||
let buf = [&[0x00, Opcode::Ack as u8], &block.to_be_bytes()[..]].concat();
|
let buf = [Opcode::Ack.as_bytes(), block.to_be_bytes()].concat();
|
||||||
|
|
||||||
socket.send(&buf)?;
|
socket.send(&buf)?;
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ impl Message {
|
||||||
socket: &UdpSocket,
|
socket: &UdpSocket,
|
||||||
options: &Vec<TransferOption>,
|
options: &Vec<TransferOption>,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
let mut buf = vec![0x00, Opcode::Oack as u8];
|
let mut buf = Opcode::Oack.as_bytes().to_vec();
|
||||||
|
|
||||||
for option in options {
|
for option in options {
|
||||||
buf = [buf, option.as_bytes()].concat();
|
buf = [buf, option.as_bytes()].concat();
|
||||||
|
|
@ -70,8 +70,9 @@ impl Message {
|
||||||
|
|
||||||
fn get_error_buf(code: ErrorCode, msg: &str) -> Vec<u8> {
|
fn get_error_buf(code: ErrorCode, msg: &str) -> Vec<u8> {
|
||||||
[
|
[
|
||||||
&[0x00, Opcode::Error as u8, 0x00, code as u8],
|
&Opcode::Error.as_bytes()[..],
|
||||||
msg.as_bytes(),
|
&code.as_bytes()[..],
|
||||||
|
&msg.as_bytes()[..],
|
||||||
&[0x00],
|
&[0x00],
|
||||||
]
|
]
|
||||||
.concat()
|
.concat()
|
||||||
|
|
|
||||||
232
src/packet.rs
232
src/packet.rs
|
|
@ -37,6 +37,8 @@ impl<'a> Packet<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(PartialEq)]
|
||||||
pub enum Opcode {
|
pub enum Opcode {
|
||||||
Rrq = 0x0001,
|
Rrq = 0x0001,
|
||||||
Wrq = 0x0002,
|
Wrq = 0x0002,
|
||||||
|
|
@ -58,8 +60,13 @@ impl Opcode {
|
||||||
_ => Err("invalid opcode"),
|
_ => Err("invalid opcode"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_bytes(self) -> [u8; 2] {
|
||||||
|
return (self as u16).to_be_bytes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct TransferOption {
|
pub struct TransferOption {
|
||||||
pub option: OptionType,
|
pub option: OptionType,
|
||||||
pub value: usize,
|
pub value: usize,
|
||||||
|
|
@ -77,6 +84,7 @@ impl TransferOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum OptionType {
|
pub enum OptionType {
|
||||||
BlockSize,
|
BlockSize,
|
||||||
TransferSize,
|
TransferSize,
|
||||||
|
|
@ -92,6 +100,10 @@ impl OptionType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_bytes(&self) -> &[u8] {
|
||||||
|
self.as_str().as_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
fn from_str(value: &str) -> Result<Self, &'static str> {
|
fn from_str(value: &str) -> Result<Self, &'static str> {
|
||||||
match value {
|
match value {
|
||||||
"blksize" => Ok(OptionType::BlockSize),
|
"blksize" => Ok(OptionType::BlockSize),
|
||||||
|
|
@ -103,7 +115,7 @@ impl OptionType {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum ErrorCode {
|
pub enum ErrorCode {
|
||||||
NotDefined = 0,
|
NotDefined = 0,
|
||||||
FileNotFound = 1,
|
FileNotFound = 1,
|
||||||
|
|
@ -129,6 +141,10 @@ impl ErrorCode {
|
||||||
_ => Err("invalid error code"),
|
_ => Err("invalid error code"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_bytes(self) -> [u8; 2] {
|
||||||
|
return (self as u16).to_be_bytes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_rq(buf: &[u8], opcode: Opcode) -> Result<Packet, Box<dyn Error>> {
|
fn parse_rq(buf: &[u8], opcode: Opcode) -> Result<Packet, Box<dyn Error>> {
|
||||||
|
|
@ -172,7 +188,7 @@ fn parse_rq(buf: &[u8], opcode: Opcode) -> Result<Packet, Box<dyn Error>> {
|
||||||
fn parse_data(buf: &[u8]) -> Result<Packet, Box<dyn Error>> {
|
fn parse_data(buf: &[u8]) -> Result<Packet, Box<dyn Error>> {
|
||||||
Ok(Packet::Data {
|
Ok(Packet::Data {
|
||||||
block_num: Convert::to_u16(&buf[2..])?,
|
block_num: Convert::to_u16(&buf[2..])?,
|
||||||
data: &buf[2..],
|
data: &buf[4..],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,4 +205,216 @@ fn parse_error(buf: &[u8]) -> Result<Packet, Box<dyn Error>> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_read_request() {
|
||||||
|
let buf = [
|
||||||
|
&Opcode::Rrq.as_bytes()[..],
|
||||||
|
&"test.png".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&"octet".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
if let Ok(Packet::Rrq {
|
||||||
|
filename,
|
||||||
|
mode,
|
||||||
|
options,
|
||||||
|
}) = parse_rq(&buf, Opcode::Rrq)
|
||||||
|
{
|
||||||
|
assert_eq!(filename, "test.png");
|
||||||
|
assert_eq!(mode, "octet");
|
||||||
|
assert_eq!(options.len(), 0);
|
||||||
|
} else {
|
||||||
|
panic!("cannot parse read request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_read_request_with_options() {
|
||||||
|
let buf = [
|
||||||
|
&Opcode::Rrq.as_bytes()[..],
|
||||||
|
&"test.png".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&"octet".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&OptionType::TransferSize.as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&"0".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&OptionType::Timeout.as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&"5".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
if let Ok(Packet::Rrq {
|
||||||
|
filename,
|
||||||
|
mode,
|
||||||
|
options,
|
||||||
|
}) = parse_rq(&buf, Opcode::Rrq)
|
||||||
|
{
|
||||||
|
assert_eq!(filename, "test.png");
|
||||||
|
assert_eq!(mode, "octet");
|
||||||
|
assert_eq!(options.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
options[0],
|
||||||
|
TransferOption {
|
||||||
|
option: OptionType::TransferSize,
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
options[1],
|
||||||
|
TransferOption {
|
||||||
|
option: OptionType::Timeout,
|
||||||
|
value: 5
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panic!("cannot parse read request with options")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_write_request() {
|
||||||
|
let buf = [
|
||||||
|
&Opcode::Wrq.as_bytes()[..],
|
||||||
|
&"test.png".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&"octet".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
if let Ok(Packet::Wrq {
|
||||||
|
filename,
|
||||||
|
mode,
|
||||||
|
options,
|
||||||
|
}) = parse_rq(&buf, Opcode::Wrq)
|
||||||
|
{
|
||||||
|
assert_eq!(filename, "test.png");
|
||||||
|
assert_eq!(mode, "octet");
|
||||||
|
assert_eq!(options.len(), 0);
|
||||||
|
} else {
|
||||||
|
panic!("cannot parse write request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_write_request_with_options() {
|
||||||
|
let buf = [
|
||||||
|
&Opcode::Wrq.as_bytes()[..],
|
||||||
|
&"test.png".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&"octet".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&OptionType::TransferSize.as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&"12341234".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&OptionType::BlockSize.as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
&"1024".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
if let Ok(Packet::Wrq {
|
||||||
|
filename,
|
||||||
|
mode,
|
||||||
|
options,
|
||||||
|
}) = parse_rq(&buf, Opcode::Wrq)
|
||||||
|
{
|
||||||
|
assert_eq!(filename, "test.png");
|
||||||
|
assert_eq!(mode, "octet");
|
||||||
|
assert_eq!(options.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
options[0],
|
||||||
|
TransferOption {
|
||||||
|
option: OptionType::TransferSize,
|
||||||
|
value: 12341234
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
options[1],
|
||||||
|
TransferOption {
|
||||||
|
option: OptionType::BlockSize,
|
||||||
|
value: 1024
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panic!("cannot parse write request with options")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_data() {
|
||||||
|
let buf = [
|
||||||
|
&Opcode::Data.as_bytes()[..],
|
||||||
|
&5u16.to_be_bytes(),
|
||||||
|
&[
|
||||||
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
if let Ok(Packet::Data { block_num, data }) = parse_data(&buf) {
|
||||||
|
assert_eq!(block_num, 5);
|
||||||
|
assert_eq!(
|
||||||
|
data,
|
||||||
|
[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panic!("cannot parse data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_ack() {
|
||||||
|
let buf = [&Opcode::Ack.as_bytes()[..], &12u16.to_be_bytes()].concat();
|
||||||
|
|
||||||
|
if let Ok(Packet::Ack(block_num)) = parse_ack(&buf) {
|
||||||
|
assert_eq!(block_num, 12);
|
||||||
|
} else {
|
||||||
|
panic!("cannot parse ack")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_error() {
|
||||||
|
let buf = [
|
||||||
|
&Opcode::Error.as_bytes()[..],
|
||||||
|
&ErrorCode::FileExists.as_bytes(),
|
||||||
|
"file already exists".as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
if let Ok(Packet::Error { code, msg }) = parse_error(&buf) {
|
||||||
|
assert_eq!(code, ErrorCode::FileExists);
|
||||||
|
assert_eq!(msg, "file already exists");
|
||||||
|
} else {
|
||||||
|
panic!("cannot parse error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_error_without_message() {
|
||||||
|
let buf = [
|
||||||
|
&Opcode::Error.as_bytes()[..],
|
||||||
|
&ErrorCode::FileExists.as_bytes(),
|
||||||
|
&[0x00],
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
if let Ok(Packet::Error { code, msg }) = parse_error(&buf) {
|
||||||
|
assert_eq!(code, ErrorCode::FileExists);
|
||||||
|
assert_eq!(msg, "");
|
||||||
|
} else {
|
||||||
|
panic!("cannot parse error")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue