| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- // SPDX-License-Identifier: Apache-2.0
- use rayon::prelude::*;
- use solang::{file_resolver::FileResolver, parse_and_resolve, sema::ast, Target};
- use std::{ffi::OsStr, fs, path::Path};
- use walkdir::WalkDir;
- fn test_solidity(src: &str) -> ast::Namespace {
- let mut cache = FileResolver::default();
- cache.set_file_contents("test.sol", src.to_string());
- let ns = parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::EVM);
- ns.print_diagnostics_in_plain(&cache, false);
- ns
- }
- #[test]
- fn address() {
- let ns = test_solidity(
- "
- contract address_tester {
- function encode_const() public returns (address) {
- return 0x52908400098527886E0F7030069857D2E4169EE7;
- }
- function test_arg(address foo) public {
- assert(foo == 0x27b1fdb04752bbc536007a920d24acb045561c26);
- // this literal is a number
- int x = 0x27b1fdb047_52bbc536007a920d24acb045561C26;
- assert(int(foo) == x);
- }
- function allones() public returns (address) {
- return address(1);
- }
- }",
- );
- assert!(!ns.diagnostics.any_errors());
- }
- #[test]
- fn try_catch() {
- let ns = test_solidity(
- r#"
- contract b {
- int32 x;
- constructor(int32 a) public {
- x = a;
- }
- function get_x(int32 t) public returns (int32) {
- if (t == 0) {
- revert("cannot be zero");
- }
- return x * t;
- }
- }
- contract c {
- b x;
- constructor() public {
- x = new b(102);
- }
- function test() public returns (int32) {
- int32 state = 0;
- try x.get_x(0) returns (int32 l) {
- state = 1;
- } catch Error(string err) {
- if (err == "cannot be zero") {
- state = 2;
- } else {
- state = 3;
- }
- } catch (bytes ) {
- state = 4;
- }
- return state;
- }
- }"#,
- );
- assert!(!ns.diagnostics.any_errors());
- }
- #[test]
- fn selfdestruct() {
- let ns = test_solidity(
- r##"
- contract other {
- function goaway(address payable recipient) public returns (bool) {
- selfdestruct(recipient);
- }
- }
- contract c {
- other o;
- function step1() public {
- o = new other{value: 511}();
- }
- function step2() public {
- bool foo = o.goaway(payable(address(this)));
- }
- }"##,
- );
- assert!(!ns.diagnostics.any_errors());
- }
- #[test]
- fn eth_builtins() {
- let ns = test_solidity(
- r#"
- contract testing {
- function test_address() public view returns (uint256 ret) {
- assembly {
- let a := address()
- ret := a
- }
- }
- function test_balance() public view returns (uint256 ret) {
- assembly {
- let a := address()
- ret := balance(a)
- }
- }
- function test_selfbalance() public view returns (uint256 ret) {
- assembly {
- let a := selfbalance()
- ret := a
- }
- }
- function test_caller() public view returns (uint256 ret) {
- assembly {
- let a := caller()
- ret := a
- }
- }
- function test_callvalue() public view returns (uint256 ret) {
- assembly {
- let a := callvalue()
- ret := a
- }
- }
- function test_extcodesize() public view returns (uint256 ret) {
- assembly {
- let a := address()
- ret := extcodesize(a)
- }
- }
- }
- "#,
- );
- assert!(!ns.diagnostics.any_errors());
- }
- #[test]
- fn ethereum_solidity_tests() {
- let error_matcher =
- regex::Regex::new(r"// ----\r?\n(// Warning \d+: .*\n)*// \w+Error( \d+)?: (.*)").unwrap();
- let entries = WalkDir::new(
- Path::new(env!("CARGO_MANIFEST_DIR"))
- .join("testdata/solidity/test/libsolidity/semanticTests"),
- )
- .into_iter()
- .chain(WalkDir::new(
- Path::new(env!("CARGO_MANIFEST_DIR"))
- .join("testdata/solidity/test/libsolidity/syntaxTests"),
- ));
- let errors: usize = entries
- .par_bridge()
- .filter_map(|e| {
- let entry = e.unwrap();
- let file_name = entry.file_name().to_string_lossy();
- // FIXME: max_depth_reached_4.sol causes a stack overflow in resolve_expression.rs
- // FIXME: others listed explicitly cause panics and need fixing
- if !file_name.ends_with("max_depth_reached_4.sol")
- && !file_name.ends_with("invalid_utf8_sequence.sol")
- && file_name.ends_with(".sol")
- {
- Some(entry)
- } else {
- None
- }
- })
- .map(|entry| {
- let path = entry.path().parent().unwrap();
- let source = fs::read_to_string(entry.path()).unwrap();
- let expect_error = error_matcher
- .captures(&source)
- .map(|captures| captures.get(3).unwrap().as_str());
- let (mut cache, names) = set_file_contents(&source, entry.path());
- cache.add_import_path(path).unwrap();
- let errors: usize = names
- .iter()
- .map(|name| {
- let ns = parse_and_resolve(OsStr::new(&name), &mut cache, Target::EVM);
- if ns.diagnostics.any_errors() {
- if expect_error.is_none() {
- println!("file: {} name:{}", entry.path().display(), name);
- ns.print_diagnostics_in_plain(&cache, false);
- 1
- } else {
- 0
- }
- } else if let Some(error) = expect_error {
- println!("file: {} name:{}", entry.path().display(), name);
- println!("expecting error {error}");
- 1
- } else {
- 0
- }
- })
- .sum();
- errors
- })
- .sum();
- assert_eq!(errors, 1036);
- }
- fn set_file_contents(source: &str, path: &Path) -> (FileResolver, Vec<String>) {
- let mut cache = FileResolver::default();
- let mut name = path.to_string_lossy().to_string();
- let mut names = Vec::new();
- let mut contents = String::new();
- let source_delimiter = regex::Regex::new(r"==== Source: (.*) ====").unwrap();
- let external_source_delimiter = regex::Regex::new(r"==== ExternalSource: (.*) ====").unwrap();
- let equals = regex::Regex::new("([a-zA-Z0-9_]+)=(.*)").unwrap();
- for line in source.lines() {
- if let Some(cap) = source_delimiter.captures(line) {
- if !contents.is_empty() {
- cache.set_file_contents(&name, contents);
- names.push(name);
- }
- name = cap.get(1).unwrap().as_str().to_owned();
- if name == "////" {
- name = "test.sol".to_owned();
- }
- contents = String::new();
- } else if let Some(cap) = external_source_delimiter.captures(line) {
- let mut name = cap.get(1).unwrap().as_str().to_owned();
- if let Some(cap) = equals.captures(&name) {
- let mut ext = path.parent().unwrap().to_path_buf();
- ext.push(cap.get(2).unwrap().as_str());
- name = cap.get(1).unwrap().as_str().to_owned();
- let source = fs::read_to_string(ext).unwrap();
- cache.set_file_contents(&name, source);
- }
- // else rely on file resolver to import stuff
- } else {
- contents.push_str(line);
- contents.push('\n');
- }
- }
- if !contents.is_empty() {
- cache.set_file_contents(&name, contents);
- names.push(name);
- }
- (cache, names)
- }
|