just/tests/string.rs
2025-02-26 09:26:43 -08:00

676 lines
9.2 KiB
Rust

use super::*;
#[test]
fn raw_string() {
Test::new()
.justfile(
r#"
export EXPORTED_VARIABLE := '\z'
recipe:
printf "$EXPORTED_VARIABLE"
"#,
)
.stdout("\\z")
.stderr("printf \"$EXPORTED_VARIABLE\"\n")
.run();
}
#[test]
fn multiline_raw_string() {
Test::new()
.arg("a")
.justfile(
"
string := 'hello
whatever'
a:
echo '{{string}}'
",
)
.stdout(
"hello
whatever
",
)
.stderr(
"echo 'hello
whatever'
",
)
.run();
}
#[test]
fn multiline_backtick() {
Test::new()
.arg("a")
.justfile(
"
string := `echo hello
echo goodbye
`
a:
echo '{{string}}'
",
)
.stdout("hello\ngoodbye\n")
.stderr(
"echo 'hello
goodbye'
",
)
.run();
}
#[test]
fn multiline_cooked_string() {
Test::new()
.arg("a")
.justfile(
r#"
string := "hello
whatever"
a:
echo '{{string}}'
"#,
)
.stdout(
"hello
whatever
",
)
.stderr(
"echo 'hello
whatever'
",
)
.run();
}
#[test]
fn cooked_string_suppress_newline() {
Test::new()
.justfile(
r#"
a := """
foo\
bar
"""
@default:
printf %s '{{a}}'
"#,
)
.stdout(
"
foobar
",
)
.run();
}
#[test]
fn invalid_escape_sequence() {
Test::new()
.arg("a")
.justfile(
r#"x := "\q"
a:"#,
)
.stderr(
"error: `\\q` is not a valid escape sequence
——▶ justfile:1:6
1 │ x := \"\\q\"
│ ^^^^
",
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn error_line_after_multiline_raw_string() {
Test::new()
.arg("a")
.justfile(
"
string := 'hello
whatever' + 'yo'
a:
echo '{{foo}}'
",
)
.stderr(
"error: Variable `foo` not defined
——▶ justfile:6:11
6 │ echo '{{foo}}'
│ ^^^
",
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn error_column_after_multiline_raw_string() {
Test::new()
.arg("a")
.justfile(
"
string := 'hello
whatever' + bar
a:
echo '{{string}}'
",
)
.stderr(
"error: Variable `bar` not defined
——▶ justfile:3:13
3 │ whatever' + bar
│ ^^^
",
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn multiline_raw_string_in_interpolation() {
Test::new()
.arg("a")
.justfile(
r#"
a:
echo '{{"a" + '
' + "b"}}'
"#,
)
.stdout(
"
a
b
",
)
.stderr(
"
echo 'a
b'
",
)
.run();
}
#[test]
fn error_line_after_multiline_raw_string_in_interpolation() {
Test::new()
.arg("a")
.justfile(
r#"
a:
echo '{{"a" + '
' + "b"}}'
echo {{b}}
"#,
)
.stderr(
"error: Variable `b` not defined
——▶ justfile:5:10
5 │ echo {{b}}
│ ^
",
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn unterminated_raw_string() {
Test::new()
.arg("a")
.justfile(
"
a b= ':
",
)
.stderr(
"
error: Unterminated string
——▶ justfile:1:6
1 │ a b= ':
│ ^
",
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn unterminated_string() {
Test::new()
.arg("a")
.justfile(
r#"
a b= ":
"#,
)
.stderr(
r#"
error: Unterminated string
——▶ justfile:1:6
1 │ a b= ":
│ ^
"#,
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn unterminated_backtick() {
Test::new()
.justfile(
"
foo a=\t`echo blaaaaaah:
echo {{a}}
",
)
.stderr(
r"
error: Unterminated backtick
——▶ justfile:1:8
1 │ foo a= `echo blaaaaaah:
│ ^
",
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn unterminated_indented_raw_string() {
Test::new()
.arg("a")
.justfile(
"
a b= ''':
",
)
.stderr(
"
error: Unterminated string
——▶ justfile:1:6
1 │ a b= ''':
│ ^^^
",
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn unterminated_indented_string() {
Test::new()
.arg("a")
.justfile(
r#"
a b= """:
"#,
)
.stderr(
r#"
error: Unterminated string
——▶ justfile:1:6
1 │ a b= """:
│ ^^^
"#,
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn unterminated_indented_backtick() {
Test::new()
.justfile(
"
foo a=\t```echo blaaaaaah:
echo {{a}}
",
)
.stderr(
r"
error: Unterminated backtick
——▶ justfile:1:8
1 │ foo a= ```echo blaaaaaah:
│ ^^^
",
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn indented_raw_string_contents_indentation_removed() {
Test::new()
.justfile(
"
a := '''
foo
bar
'''
@default:
printf '{{a}}'
",
)
.stdout(
"
foo
bar
",
)
.run();
}
#[test]
fn indented_cooked_string_contents_indentation_removed() {
Test::new()
.justfile(
r#"
a := """
foo
bar
"""
@default:
printf '{{a}}'
"#,
)
.stdout(
"
foo
bar
",
)
.run();
}
#[test]
fn indented_backtick_string_contents_indentation_removed() {
Test::new()
.justfile(
r"
a := ```
printf '
foo
bar
'
```
@default:
printf '{{a}}'
",
)
.stdout("\n\nfoo\nbar")
.run();
}
#[test]
fn indented_raw_string_escapes() {
Test::new()
.justfile(
r"
a := '''
foo\n
bar
'''
@default:
printf %s '{{a}}'
",
)
.stdout(
r"
foo\n
bar
",
)
.run();
}
#[test]
fn indented_cooked_string_escapes() {
Test::new()
.justfile(
r#"
a := """
foo\n
bar
"""
@default:
printf %s '{{a}}'
"#,
)
.stdout(
"
foo
bar
",
)
.run();
}
#[test]
fn indented_backtick_string_escapes() {
Test::new()
.justfile(
r"
a := ```
printf %s '
foo\n
bar
'
```
@default:
printf %s '{{a}}'
",
)
.stdout("\n\nfoo\\n\nbar")
.run();
}
#[test]
fn shebang_backtick() {
Test::new()
.justfile(
"
x := `#!/usr/bin/env sh`
",
)
.stderr(
"
error: Backticks may not start with `#!`
——▶ justfile:1:6
1 │ x := `#!/usr/bin/env sh`
│ ^^^^^^^^^^^^^^^^^^^
",
)
.status(EXIT_FAILURE)
.run();
}
#[test]
fn valid_unicode_escape() {
Test::new()
.justfile(r#"x := "\u{1f916}\u{1F916}""#)
.args(["--evaluate", "x"])
.stdout("🤖🤖")
.run();
}
#[test]
fn unicode_escapes_with_all_hex_digits() {
Test::new()
.justfile(r#"x := "\u{012345}\u{6789a}\u{bcdef}\u{ABCDE}\u{F}""#)
.args(["--evaluate", "x"])
.stdout("\u{012345}\u{6789a}\u{bcdef}\u{ABCDE}\u{F}")
.run();
}
#[test]
fn maximum_valid_unicode_escape() {
Test::new()
.justfile(r#"x := "\u{10FFFF}""#)
.args(["--evaluate", "x"])
.stdout("\u{10FFFF}")
.run();
}
#[test]
fn unicode_escape_no_braces() {
Test::new()
.justfile("x := \"\\u1234\"")
.args(["--evaluate", "x"])
.status(1)
.stderr(
r#"
error: expected unicode escape sequence delimiter `{` but found `1`
——▶ justfile:1:6
1 │ x := "\u1234"
│ ^^^^^^^^
"#,
)
.run();
}
#[test]
fn unicode_escape_empty() {
Test::new()
.justfile("x := \"\\u{}\"")
.args(["--evaluate", "x"])
.status(1)
.stderr(
r#"
error: unicode escape sequences must not be empty
——▶ justfile:1:6
1 │ x := "\u{}"
│ ^^^^^^
"#,
)
.run();
}
#[test]
fn unicode_escape_requires_immediate_opening_brace() {
Test::new()
.justfile("x := \"\\u {1f916}\"")
.args(["--evaluate", "x"])
.status(1)
.stderr(
r#"
error: expected unicode escape sequence delimiter `{` but found ` `
——▶ justfile:1:6
1 │ x := "\u {1f916}"
│ ^^^^^^^^^^^^
"#,
)
.run();
}
#[test]
fn unicode_escape_non_hex() {
Test::new()
.justfile("x := \"\\u{foo}\"")
.args(["--evaluate", "x"])
.status(1)
.stderr(
r#"
error: expected hex digit [0-9A-Fa-f] but found `o`
——▶ justfile:1:6
1 │ x := "\u{foo}"
│ ^^^^^^^^^
"#,
)
.run();
}
#[test]
fn unicode_escape_invalid_character() {
Test::new()
.justfile("x := \"\\u{BadBad}\"")
.args(["--evaluate", "x"])
.status(1)
.stderr(
r#"
error: unicode escape sequence value `BadBad` greater than maximum valid code point `10FFFF`
——▶ justfile:1:6
1 │ x := "\u{BadBad}"
│ ^^^^^^^^^^^^
"#,
)
.run();
}
#[test]
fn unicode_escape_too_long() {
Test::new()
.justfile("x := \"\\u{FFFFFFFFFF}\"")
.args(["--evaluate", "x"])
.status(1)
.stderr(
r#"
error: unicode escape sequence starting with `\u{FFFFFFF` longer than six hex digits
——▶ justfile:1:6
1 │ x := "\u{FFFFFFFFFF}"
│ ^^^^^^^^^^^^^^^^
"#,
)
.run();
}
#[test]
fn unicode_escape_unterminated() {
Test::new()
.justfile("x := \"\\u{1f917\"")
.args(["--evaluate", "x"])
.status(1)
.stderr(
r#"
error: unterminated unicode escape sequence
——▶ justfile:1:6
1 │ x := "\u{1f917"
│ ^^^^^^^^^^
"#,
)
.run();
}