use std::fs;
use assert_fs::prelude::*;
use assert_fs::TempDir;
use indoc::indoc;
use predicates::prelude::*;
use crate::support::command::Scarb;
use crate::support::fsx::ChildPathEx;
use crate::support::project_builder::ProjectBuilder;
#[test]
fn compile_simple() {
let cache_dir = TempDir::new().unwrap().child("c");
let t = TempDir::new().unwrap();
ProjectBuilder::start().name("hello").build(&t);
Scarb::quick_snapbox()
.env("SCARB_CACHE", cache_dir.path())
.arg("build")
.current_dir(&t)
.assert()
.success();
assert_eq!(t.child("target").files(), vec!["CACHEDIR.TAG", "dev"]);
assert_eq!(t.child("target/dev").files(), vec!["hello.sierra"]);
cache_dir
.child("registry/std")
.assert(predicates::path::exists());
cache_dir
.child("CACHEDIR.TAG")
.assert(predicates::path::exists());
}
#[test]
fn quiet_output() {
let t = TempDir::new().unwrap();
ProjectBuilder::start().build(&t);
Scarb::quick_snapbox()
.args(["build", "-q"])
.current_dir(&t)
.assert()
.success()
.stdout_eq("");
Scarb::quick_snapbox()
.args(["--json", "-q", "build"])
.current_dir(&t)
.assert()
.success()
.stdout_eq("");
}
#[test]
fn compile_with_syntax_error() {
let t = TempDir::new().unwrap();
ProjectBuilder::start()
.name("hello")
.version("0.1.0")
.lib_cairo("not_a_keyword")
.build(&t);
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.code(1)
.stdout_matches(indoc! {r#"
Compiling hello v0.1.0 ([..]Scarb.toml)
error: Skipped tokens. Expected: Const/Module/Use/FreeFunction/ExternFunction/ExternType/Trait/Impl/Struct/Enum/TypeAlias or an attribute.
--> lib.cairo:1:1
not_a_keyword
^***********^
error: could not compile `hello` due to previous error
"#});
}
#[test]
fn compile_with_syntax_error_json() {
let t = TempDir::new().unwrap();
ProjectBuilder::start()
.name("hello")
.version("0.1.0")
.lib_cairo("not_a_keyword")
.build(&t);
Scarb::quick_snapbox()
.arg("--json")
.arg("build")
.current_dir(&t)
.assert()
.code(1)
.stdout_matches(indoc! {r#"
{"status":"compiling","message":"hello v0.1.0 ([..]Scarb.toml)"}
{"type":"diagnostic","message":"error: Skipped tokens. Expected: Const/Module/Use/FreeFunction/ExternFunction/ExternType/Trait/Impl/Struct/Enum/TypeAlias or an attribute./n --> lib.cairo:1:1/nnot_a_keyword/n^***********^/n/n"}
{"type":"error","message":"could not compile `hello` due to previous error"}
"#});
}
#[test]
fn compile_without_manifest() {
let t = TempDir::new().unwrap();
let cause = fs::read(t.child("Scarb.toml")).unwrap_err();
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.code(1)
.stdout_matches(format!(
"\
error: failed to read manifest at `[..]/Scarb.toml`
Caused by:
{cause}
"
));
}
#[test]
#[cfg(target_os = "linux")]
fn compile_with_lowercase_scarb_toml() {
let t = TempDir::new().unwrap();
t.child("scarb.toml")
.write_str(
r#"
[package]
name = "hello"
version = "0.1.0"
"#,
)
.unwrap();
let cause = fs::read(t.child("Scarb.toml")).unwrap_err();
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.code(1)
.stdout_matches(format!(
"\
error: failed to read manifest at `[..]/Scarb.toml`
Caused by:
{cause}
"
));
}
#[test]
fn compile_with_manifest_not_a_file() {
let t = TempDir::new().unwrap();
t.child("Scarb.toml").create_dir_all().unwrap();
let cause = fs::read(t.child("Scarb.toml")).unwrap_err();
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.code(1)
.stdout_matches(format!(
"\
error: failed to read manifest at `[..]/Scarb.toml`
Caused by:
{cause}
"
));
}
#[test]
fn compile_with_invalid_empty_name() {
let t = TempDir::new().unwrap();
t.child("Scarb.toml")
.write_str(
r#"
[package]
name = ""
version = "0.1.0"
"#,
)
.unwrap();
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.code(1)
.stdout_matches(indoc! {r#"
error: failed to parse manifest at `[..]/Scarb.toml`
Caused by:
TOML parse error at line 3, column 20
|
3 | name = ""
| ^^
empty string cannot be used as package name
"#});
}
#[test]
fn compile_with_invalid_version() {
let t = TempDir::new().unwrap();
t.child("Scarb.toml")
.write_str(
r#"
[package]
name = "foo"
version = "y"
"#,
)
.unwrap();
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.code(1)
.stdout_matches(indoc! {r#"
error: failed to parse manifest at `[..]/Scarb.toml`
Caused by:
TOML parse error at line 4, column 23
|
4 | version = "y"
| ^^^
unexpected character 'y' while parsing major version number
"#});
}
#[test]
fn compile_with_invalid_cairo_version() {
let t = TempDir::new().unwrap();
t.child("Scarb.toml")
.write_str(
r#"
[package]
name = "foo"
version = "0.1.0"
cairo-version = "f"
"#,
)
.unwrap();
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.code(1)
.stdout_matches(indoc! {r#"
error: failed to parse manifest at `[..]/Scarb.toml`
Caused by:
TOML parse error at line 5, column 29
|
5 | cairo-version = "f"
| ^^^
unexpected character 'f' while parsing major version number
"#});
}
#[test]
fn compile_with_incompatible_cairo_version() {
let t = TempDir::new().unwrap();
t.child("Scarb.toml")
.write_str(
r#"
[package]
name = "hello"
version = "0.1.0"
cairo-version = "3.0.0"
"#,
)
.unwrap();
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.code(1)
.stdout_matches(indoc! {r#"
error: Package hello. Required Cairo version isn't compatible with current version. Should be: ^3.0.0 is: [..]
error: For each package, the required Cairo version must match the current Cairo version.
"#});
}
#[test]
fn compile_with_compatible_cairo_version() {
let version = env!("SCARB_CAIRO_VERSION").to_string();
let t = TempDir::new().unwrap();
ProjectBuilder::start()
.name("hello")
.version("0.1.0")
.cairo_version(&version)
.build(&t);
Scarb::quick_snapbox()
.args(["build"])
.current_dir(&t)
.assert()
.success();
}
#[test]
fn compile_with_invalid_non_numeric_dep_version() {
let t = TempDir::new().unwrap();
t.child("Scarb.toml")
.write_str(
r#"
[package]
name = "hello"
version = "0.1.0"
[dependencies]
moo = "y"
"#,
)
.unwrap();
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.code(1)
.stdout_matches(indoc! {r#"
error: failed to parse manifest at `[..]/Scarb.toml`
Caused by:
TOML parse error at line 7, column 19
|
7 | moo = "y"
| ^^^
data did not match any variant of untagged enum TomlDependency
"#});
}
#[test]
fn compile_multiple_packages() {
let t = TempDir::new().unwrap();
t.child("Scarb.toml")
.write_str(
r#"
[package]
name = "fib"
version = "1.0.0"
[dependencies]
decrement = { path = "decrement" }
"#,
)
.unwrap();
t.child("src/lib.cairo")
.write_str(
r#"
mod sum_two;
fn fib(a: felt252, b: felt252, n: felt252) -> felt252 {
match n {
0 => a,
_ => fib(b, sum_two::sum_two(a, b), decrement::decrement(n)),
}
}
"#,
)
.unwrap();
t.child("src/sum_two.cairo")
.write_str(r#"fn sum_two(a: felt252, b: felt252) -> felt252 { a + b }"#)
.unwrap();
t.child("decrement/Scarb.toml")
.write_str(
r#"
[package]
name = "decrement"
version = "1.0.0"
"#,
)
.unwrap();
t.child("decrement/src/lib.cairo")
.write_str(
r#"
fn decrement(x: felt252) -> felt252 { x - 1 }
"#,
)
.unwrap();
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.success()
.stdout_matches(indoc! {r#"
[..] Compiling fib v1.0.0 ([..]Scarb.toml)
[..] Finished release target(s) in [..]
"#});
t.child("target/dev/fib.sierra")
.assert(predicates::str::is_empty().not());
}
#[test]
fn compile_with_nested_deps() {
let t = TempDir::new().unwrap();
t.child("Scarb.toml")
.write_str(
r#"
[package]
name = "x"
version = "1.0.0"
[dependencies]
y = { path = "y" }
"#,
)
.unwrap();
t.child("src/lib.cairo")
.write_str(r"fn f() -> felt252 { y::f() }")
.unwrap();
t.child("y/Scarb.toml")
.write_str(
r#"
[package]
name = "y"
version = "1.0.0"
[dependencies]
q = { path = "../q" }
z = { path = "../z" }
"#,
)
.unwrap();
t.child("y/src/lib.cairo")
.write_str(r"fn f() -> felt252 { z::f() }")
.unwrap();
t.child("z/Scarb.toml")
.write_str(
r#"
[package]
name = "z"
version = "1.0.0"
[dependencies]
q = { path = "../q" }
"#,
)
.unwrap();
t.child("z/src/lib.cairo")
.write_str(r"fn f() -> felt252 { q::f() }")
.unwrap();
t.child("q/Scarb.toml")
.write_str(
r#"
[package]
name = "q"
version = "1.0.0"
"#,
)
.unwrap();
t.child("q/src/lib.cairo")
.write_str(r"fn f() -> felt252 { 42 }")
.unwrap();
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.success()
.stdout_matches(indoc! {r#"
[..] Compiling x v1.0.0 ([..]Scarb.toml)
[..] Finished release target(s) in [..]
"#});
t.child("target/dev/x.sierra")
.assert(predicates::str::is_empty().not());
}
#[test]
fn override_target_dir() {
let target_dir = TempDir::new().unwrap();
let t = TempDir::new().unwrap();
ProjectBuilder::start().name("hello").build(&t);
Scarb::quick_snapbox()
.arg("--target-dir")
.arg(target_dir.path())
.arg("build")
.current_dir(&t)
.assert()
.success();
t.child("target").assert(predicates::path::exists().not());
target_dir
.child("dev/hello.sierra")
.assert(predicates::path::exists());
}
#[test]
fn sierra_replace_ids() {
let t = TempDir::new().unwrap();
ProjectBuilder::start()
.name("hello")
.lib_cairo("fn example() -> felt252 { 42 }")
.manifest_extra(
r#"
[cairo]
sierra-replace-ids = true
"#,
)
.build(&t);
Scarb::quick_snapbox()
.arg("build")
.current_dir(&t)
.assert()
.success();
t.child("target/dev/hello.sierra")
.assert(predicates::str::contains(
"hello::example@0() -> (felt252);",
));
}