use caddyfile::tokenizer::{Token, Tokenizer};
use pretty_assertions::assert_eq;
#[test]
fn very_simple() {
let input = r#"host:123"#;
let expect = vec![Token {
file: None,
line: 1,
text: "host:123".to_string(),
..Default::default()
}];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expect
);
}
#[test]
fn one_directive() {
let input = r#"host:123
directive"#;
let expect = vec![
Token {
file: None,
line: 1,
text: "host:123".to_string(),
..Default::default()
},
Token {
file: None,
line: 3,
text: "directive".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expect
);
}
#[test]
fn directive_in_block() {
let input = r#"host:123 {
directive
}"#;
let expect = vec![
Token {
file: None,
line: 1,
text: "host:123".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "{".to_string(),
..Default::default()
},
Token {
file: None,
line: 2,
text: "directive".to_string(),
..Default::default()
},
Token {
file: None,
line: 3,
text: "}".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expect
);
}
#[test]
fn directive_in_one_line_block() {
let input = r#"host:123 { directive }"#;
let expect = vec![
Token {
file: None,
line: 1,
text: "host:123".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "{".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "directive".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "}".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expect
);
}
#[test]
fn comments() {
let input = r#"host:123 {
#comment
directive
# comment
foobar # another comment
}"#;
let expect = vec![
Token {
file: None,
line: 1,
text: "host:123".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "{".to_string(),
..Default::default()
},
Token {
file: None,
line: 3,
text: "directive".to_string(),
..Default::default()
},
Token {
file: None,
line: 5,
text: "foobar".to_string(),
..Default::default()
},
Token {
file: None,
line: 6,
text: "}".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expect
);
}
#[test]
fn hash_within_string_is_not_a_comment() {
let input = r#"host:123 {
# hash inside string is not a comment
redir / /some/#/path
}"#;
let expect = vec![
Token {
file: None,
line: 1,
text: "host:123".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "{".to_string(),
..Default::default()
},
Token {
file: None,
line: 3,
text: "redir".to_string(),
..Default::default()
},
Token {
file: None,
line: 3,
text: "/".to_string(),
..Default::default()
},
Token {
file: None,
line: 3,
text: "/some/#/path".to_string(),
..Default::default()
},
Token {
file: None,
line: 4,
text: "}".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expect
);
}
#[test]
fn more_comments() {
let input = r#"# comment at beginning of file
# comment at beginning of line
host:123"#;
let expect = vec![Token {
file: None,
line: 3,
text: "host:123".to_string(),
..Default::default()
}];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expect
);
}
#[test]
fn quoted_value() {
let input = r#"a "quoted value" b
foobar"#;
let expected = vec![
Token {
file: None,
line: 1,
text: "a".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "quoted value".to_string(),
was_quoted: Some('"'),
..Default::default()
},
Token {
file: None,
line: 1,
text: "b".to_string(),
..Default::default()
},
Token {
file: None,
line: 2,
text: "foobar".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn quoted_twice() {
let input = r#"A "quoted \"value\" inside" B"#;
let expected = vec![
Token {
file: None,
line: 1,
text: "A".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: r#"quoted "value" inside"#.to_string(),
was_quoted: Some('"'),
..Default::default()
},
Token {
file: None,
line: 1,
text: "B".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn escaped_newline_inside_quote() {
let input = r#"An escaped "newline\
inside" quotes"#;
let expected = vec![
Token {
file: None,
line: 1,
text: "An".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "escaped".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "newline\\\ninside".to_string(),
was_quoted: Some('"'),
..Default::default()
},
Token {
file: None,
line: 2,
text: "quotes".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn escaped_newline_outside_quote() {
let input = r#"An escaped newline\
outside quotes"#;
let expected = vec![
Token {
file: None,
line: 1,
text: "An".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "escaped".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "newline".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "outside".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "quotes".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn many_escaped_newlines() {
let input = "line1\\\nescaped\nline2\nline3";
let expected = vec![
Token {
file: None,
line: 1,
text: "line1".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "escaped".to_string(),
..Default::default()
},
Token {
file: None,
line: 3,
text: "line2".to_string(),
..Default::default()
},
Token {
file: None,
line: 4,
text: "line3".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn even_more_escaped_newlines() {
let input = "line1\\\nescaped1\\\nescaped2\nline4\nline5";
let expected = vec![
Token {
file: None,
line: 1,
text: "line1".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "escaped1".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "escaped2".to_string(),
..Default::default()
},
Token {
file: None,
line: 4,
text: "line4".to_string(),
..Default::default()
},
Token {
file: None,
line: 5,
text: "line5".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn unescapable_in_quotes() {
let input = r#""unescapable\ in quotes""#;
let expected = vec![Token {
file: None,
line: 1,
text: r#"unescapable\ in quotes"#.to_string(),
was_quoted: Some('"'),
..Default::default()
}];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn more_escape_tests() {
let input_1 = r#""don't\escape""#;
let expected_1 = vec![Token {
file: None,
line: 1,
text: r#"don't\escape"#.to_string(),
was_quoted: Some('"'),
..Default::default()
}];
assert_eq!(
Tokenizer::new(&mut input_1.as_bytes(), None).collect::<Vec<_>>(),
expected_1
);
let input_2 = r#"don't\\escape"#;
let expected_2 = vec![Token {
file: None,
line: 1,
text: r#"don't\\escape"#.to_string(),
..Default::default()
}];
assert_eq!(
Tokenizer::new(&mut input_2.as_bytes(), None).collect::<Vec<_>>(),
expected_2
);
let input_3 = r#"un\escapable"#;
let expected_3 = vec![Token {
file: None,
line: 1,
text: r#"un\escapable"#.to_string(),
..Default::default()
}];
assert_eq!(
Tokenizer::new(&mut input_3.as_bytes(), None).collect::<Vec<_>>(),
expected_3
);
}
#[test]
fn quoted_newline() {
let input = r#"A "quoted value with line
break inside" {
foobar
}"#;
let expected = vec![
Token {
file: None,
line: 1,
text: "A".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "quoted value with line\n\t\t\t\t\tbreak inside".to_string(),
was_quoted: Some('"'),
..Default::default()
},
Token {
file: None,
line: 2,
text: "{".to_string(),
..Default::default()
},
Token {
file: None,
line: 3,
text: "foobar".to_string(),
..Default::default()
},
Token {
file: None,
line: 4,
text: "}".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn handle_win_paths() {
let input = r#"C:\php\php-cgi.exe"#;
let expected = vec![Token {
file: None,
line: 1,
text: r#"C:\php\php-cgi.exe"#.to_string(),
..Default::default()
}];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn empty_quotes() {
let input = r#"empty "" string"#;
let expected = vec![
Token {
file: None,
line: 1,
text: "empty".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "".to_string(),
was_quoted: Some('"'),
..Default::default()
},
Token {
file: None,
line: 1,
text: "string".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn skip_cr_characters() {
let input = "skip those\r\nCR characters";
let expected = vec![
Token {
file: None,
line: 1,
text: "skip".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "those".to_string(),
..Default::default()
},
Token {
file: None,
line: 2,
text: "CR".to_string(),
..Default::default()
},
Token {
file: None,
line: 2,
text: "characters".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn ignore_leading_byte_order_mark() {
let input = "\u{feff}:8080";
let expected = vec![Token {
file: None,
line: 1,
text: ":8080".to_string(),
..Default::default()
}];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn handle_backtick_quoted() {
let input = r#"simple `backtick quoted` string"#;
let expected = vec![
Token {
file: None,
line: 1,
text: "simple".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "backtick quoted".to_string(),
was_quoted: Some('`'),
..Default::default()
},
Token {
file: None,
line: 1,
text: "string".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn multiline_backtick_quoted() {
let input = r#"multiline `backtick
quoted
` string"#;
let expected = vec![
Token {
file: None,
line: 1,
text: "multiline".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: "backtick\nquoted\n".to_string(),
was_quoted: Some('`'),
..Default::default()
},
Token {
file: None,
line: 3,
text: "string".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn nested_quotes_inside_backticks() {
let input = r#"nested `"quotes inside" backticks` string"#;
let expected = vec![
Token {
file: None,
line: 1,
text: "nested".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: r#""quotes inside" backticks"#.to_string(),
was_quoted: Some('`'),
..Default::default()
},
Token {
file: None,
line: 1,
text: "string".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}
#[test]
fn reverse_nested_quotes_inside_backticks() {
let input = r#"reverse-nested "`backticks` inside" quotes"#;
let expected = vec![
Token {
file: None,
line: 1,
text: "reverse-nested".to_string(),
..Default::default()
},
Token {
file: None,
line: 1,
text: r#"`backticks` inside"#.to_string(),
was_quoted: Some('"'),
..Default::default()
},
Token {
file: None,
line: 1,
text: "quotes".to_string(),
..Default::default()
},
];
assert_eq!(
Tokenizer::new(&mut input.as_bytes(), None).collect::<Vec<_>>(),
expected
);
}