Эх сурвалжийг харах

test(parser): semantic & syntax tests from ethereum/solidity (#787)

* test(parser): semantic tests from ethereum/solidity

Signed-off-by: Alexey Shekhirin <a.shekhirin@gmail.com>
Alexey Shekhirin 3 жил өмнө
parent
commit
f2bbcbd954

+ 7 - 0
.github/workflows/test.yml

@@ -39,6 +39,8 @@ jobs:
       # Make sure "git describe --tags" works for solang --version
       # checkout@v2 requires git 2.18 or higher, which is not in our image
       uses: actions/checkout@v1
+      with:
+        submodules: recursive
     - name: Rust stable
       run: rustup default 1.59.0
     - name: Build
@@ -61,6 +63,8 @@ jobs:
       # Make sure "git describe --tags" works for solang --version
       # checkout@v2 requires git 2.18 or higher, which is not in our image
       uses: actions/checkout@v1
+      with:
+        submodules: recursive
     - name: Rust stable
       run: rustup default 1.59.0
     - name: Build
@@ -81,6 +85,7 @@ jobs:
       with:
         # Make sure "git describe --tags" works for solang --version
         fetch-depth: 0
+        submodules: recursive
     - name: Download LLVM
       run: curl -sSL -o c:\llvm.zip https://github.com/hyperledger-labs/solang/releases/download/v0.1.11/llvm13.0-win.zip
     - name: Extract LLVM
@@ -115,6 +120,7 @@ jobs:
       with:
         # Make sure "git describe --tags" works for solang --version
         fetch-depth: 0
+        submodules: recursive
     - name: Download LLVM
       run: curl -L --output llvm13.0-mac-arm.tar.xz https://github.com/hyperledger-labs/solang/releases/download/v0.1.11/llvm13.0-mac-arm.tar.xz
     - name: Extract LLVM
@@ -139,6 +145,7 @@ jobs:
       with:
         # Make sure "git describe --tags" works for solang --version
         fetch-depth: 0
+        submodules: recursive
     - name: Download LLVM
       run: wget -q -O llvm13.0-mac-intel.tar.xz https://github.com/hyperledger-labs/solang/releases/download/v0.1.11/llvm13.0-mac-intel.tar.xz
     - name: Extract LLVM

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "solang-parser/testdata/solidity"]
+	path = solang-parser/testdata/solidity
+	url = https://github.com/ethereum/solidity

+ 4 - 0
solang-parser/Cargo.toml

@@ -18,3 +18,7 @@ lalrpop-util = "0.19"
 phf = { version = "0.10", features = ["macros"] }
 unicode-xid = "0.2.0"
 itertools = "0.10"
+
+[dev-dependencies]
+walkdir = "2.3.2"
+regex = "1.5.5"

+ 114 - 0
solang-parser/src/test.rs

@@ -1,6 +1,10 @@
 use crate::lexer::Lexer;
 use crate::pt::*;
 use crate::solidity;
+use std::sync::mpsc;
+use std::time::Duration;
+use std::{fs, path::Path, thread};
+use walkdir::WalkDir;
 
 #[test]
 fn parse_test() {
@@ -1003,3 +1007,113 @@ contract C {
     let (actual_parse_tree, _) = crate::parse(src, 0).unwrap();
     assert_eq!(actual_parse_tree.0.len(), 1);
 }
+
+#[test]
+fn test_libsolidity() {
+    fn timeout_after<T, F>(d: Duration, f: F) -> Result<T, String>
+    where
+        T: Send + 'static,
+        F: FnOnce() -> T,
+        F: Send + 'static,
+    {
+        let (done_tx, done_rx) = mpsc::channel();
+        let handle = thread::spawn(move || {
+            let val = f();
+            done_tx.send(()).expect("Unable to send completion signal");
+            val
+        });
+
+        match done_rx.recv_timeout(d) {
+            Ok(_) => Ok(handle.join().expect("Thread panicked")),
+            Err(_) => Err(format!("Thread timeout-ed after {d:?}")),
+        }
+    }
+
+    let source_delimiter = regex::Regex::new(r"====.*====").unwrap();
+    let error_matcher = regex::Regex::new(r"// ----\r?\n// \w+( \d+)?:").unwrap();
+
+    let semantic_tests = WalkDir::new(
+        Path::new(env!("CARGO_MANIFEST_DIR"))
+            .join("testdata/solidity/test/libsolidity/semanticTests"),
+    )
+    .into_iter()
+    .collect::<Result<Vec<_>, _>>()
+    .unwrap()
+    .into_iter()
+    .map(|entry| (false, entry));
+
+    let syntax_tests = WalkDir::new(
+        Path::new(env!("CARGO_MANIFEST_DIR"))
+            .join("testdata/solidity/test/libsolidity/syntaxTests"),
+    )
+    .into_iter()
+    .collect::<Result<Vec<_>, _>>()
+    .unwrap()
+    .into_iter()
+    .map(|entry| (true, entry));
+
+    let errors = semantic_tests
+        .into_iter()
+        .chain(syntax_tests)
+        .map::<Result<_, String>, _>(|(syntax_test, entry)| {
+            if entry.file_name().to_string_lossy().ends_with(".sol") {
+                let source = match fs::read_to_string(entry.path()) {
+                    Ok(source) => source,
+                    Err(err) if matches!(err.kind(), std::io::ErrorKind::InvalidData) => {
+                        return Ok(vec![])
+                    }
+                    Err(err) => return Err(err.to_string()),
+                };
+
+                let expect_error = syntax_test && error_matcher.is_match(&source);
+
+                Ok(source_delimiter
+                    .split(&source)
+                    .filter(|source_part| !source_part.is_empty())
+                    .map(|part| {
+                        (
+                            entry.path().to_string_lossy().to_string(),
+                            expect_error,
+                            part.to_string(),
+                        )
+                    })
+                    .collect::<Vec<_>>())
+            } else {
+                Ok(vec![])
+            }
+        })
+        .collect::<Result<Vec<_>, _>>()
+        .unwrap()
+        .into_iter()
+        .flatten()
+        .filter_map(|(path, expect_error, source_part)| {
+            let result = match timeout_after(Duration::from_secs(5), move || {
+                crate::parse(&source_part, 0)
+            }) {
+                Ok(result) => result,
+                Err(err) => return Some(format!("{:?}: \n\t{}", path, err)),
+            };
+
+            if let (Err(err), false) = (
+                result.map_err(|diags| {
+                    format!(
+                        "{:?}:\n\t{}",
+                        path,
+                        diags
+                            .iter()
+                            .map(|diag| format!("{diag:?}"))
+                            .collect::<Vec<_>>()
+                            .join("\n\t")
+                    )
+                }),
+                expect_error,
+            ) {
+                return Some(err);
+            }
+
+            None
+        })
+        .collect::<Vec<_>>();
+
+    assert!(errors.is_empty(), "{}", errors.join("\n"));
+}

+ 1 - 0
solang-parser/testdata/solidity

@@ -0,0 +1 @@
+Subproject commit d55b84ff63b1707f974f38c4d84088adb995b46a