Skip to content

Commit b0d2fd4

Browse files
added e2e tests from cairo repo
1 parent 645b5c7 commit b0d2fd4

9 files changed

Lines changed: 204 additions & 3 deletions

File tree

.github/workflows/starknet-blocks.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ on:
88

99
jobs:
1010
run-blocks:
11-
if: false
1211
name: Run Blocks
1312
runs-on: ubuntu-latest
1413
env:
@@ -33,7 +32,7 @@ jobs:
3332
with:
3433
repository: starkware-libs/starknet-replay
3534
path: starknet-replay
36-
ref: 5e75e33d67de77fe68aba06e05571175c0976733
35+
ref: 4b896e523bd33da66b95c8b2afdb7423785e99d4
3736

3837
# We need native to use the linux deps ci action
3938
- name: Checkout Native

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ cairo-*.tar
3434

3535
*.ipynb
3636
cairo-native-stress-logs.jsonl
37-
37+
3838
.DS_Store
3939
bench_starknet_compilation_data
4040
cairo
4141

42+
test_data/e2e_sierra/
43+
test_data/e2e_libfuncs_raw/
4244
test_data_artifacts/programs
4345
test_data_artifacts/contracts
4446
test_data_artifacts/tests

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ install-scarb-macos:
191191
.PHONY: pull-external-projects
192192
pull-external-projects:
193193
python3 ./scripts/pull_external_projects.py v${CAIRO_2_VERSION}
194+
python3 ./scripts/extract_e2e_sierra.py
195+
196+
.PHONY: extract-e2e-sierra
197+
extract-e2e-sierra:
198+
python3 ./scripts/extract_e2e_sierra.py
199+
194200

195201
.PHONY: compile-test-data
196202
compile-test-data:

scripts/extract_e2e_sierra.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Extract sierra_code sections from cairo e2e test data files.
4+
5+
Input: test_data/e2e_libfuncs_raw/ (files in //! > format)
6+
Output: test_data/e2e_sierra/ (individual .sierra files)
7+
"""
8+
9+
import os
10+
import re
11+
import shutil
12+
import sys
13+
14+
SRC_DIR = "test_data/e2e_libfuncs_raw"
15+
DST_DIR = "test_data/e2e_sierra"
16+
17+
18+
def sanitize_name(name):
19+
"""Convert test name to a safe filename."""
20+
return re.sub(r"[^\w\-]", "_", name.strip()).strip("_")
21+
22+
23+
def parse_test_cases(text):
24+
"""Parse //! > format into list of test-case dicts mapping section_key -> content."""
25+
cases = re.split(r"^//! > =+\s*$", text, flags=re.MULTILINE)
26+
results = []
27+
for case in cases:
28+
sections = {}
29+
section_order = []
30+
current_section = None
31+
lines = []
32+
for line in case.splitlines():
33+
m = re.match(r"^//! > (.+)$", line)
34+
if m:
35+
if current_section is not None:
36+
sections[current_section] = "\n".join(lines).strip()
37+
current_section = m.group(1).strip()
38+
section_order.append(current_section)
39+
lines = []
40+
else:
41+
lines.append(line)
42+
if current_section is not None:
43+
sections[current_section] = "\n".join(lines).strip()
44+
if sections:
45+
sections["_order"] = section_order
46+
results.append(sections)
47+
return results
48+
49+
50+
def main():
51+
if not os.path.isdir(SRC_DIR):
52+
print(
53+
f"Source directory {SRC_DIR} not found. "
54+
"Run 'make pull-external-projects' first."
55+
)
56+
sys.exit(1)
57+
58+
shutil.rmtree(DST_DIR, ignore_errors=True)
59+
60+
count = 0
61+
for dirpath, _, filenames in sorted(os.walk(SRC_DIR)):
62+
for filename in sorted(filenames):
63+
filepath = os.path.join(dirpath, filename)
64+
65+
with open(filepath, "r") as f:
66+
content = f.read()
67+
68+
# Preserve subdirectory structure (e.g. starknet/syscalls)
69+
rel_dir = os.path.relpath(dirpath, SRC_DIR)
70+
test_cases = parse_test_cases(content)
71+
for case in test_cases:
72+
sierra = case.get("sierra_code", "").strip()
73+
if not sierra:
74+
continue
75+
76+
# The test name is the first section key (not a section called "test_name")
77+
order = case.get("_order", [])
78+
if not order:
79+
continue
80+
test_name = order[0]
81+
82+
safe_name = sanitize_name(test_name)
83+
if not safe_name:
84+
continue
85+
86+
out_dir = os.path.join(DST_DIR, rel_dir, filename)
87+
os.makedirs(out_dir, exist_ok=True)
88+
out_path = os.path.join(out_dir, f"{safe_name}.sierra")
89+
90+
with open(out_path, "w") as f:
91+
f.write(sierra + "\n")
92+
count += 1
93+
94+
print(f"Extracted {count} sierra files into {DST_DIR}/")
95+
96+
97+
if __name__ == "__main__":
98+
main()

scripts/pull_external_projects.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,8 @@
3939
os.path.join(root, "crates/cairo-lang-starknet/cairo_level_tests"),
4040
"test_data/tests_starknet/cairo_level_tests",
4141
)
42+
shutil.rmtree("test_data/e2e_libfuncs_raw", ignore_errors=True)
43+
shutil.copytree(
44+
os.path.join(root, "tests/e2e_test_data/libfuncs"),
45+
"test_data/e2e_libfuncs_raw",
46+
)

src/libfuncs/circuit.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ fn build_init_circuit_data<'ctx, 'this>(
102102
metadata: &mut MetadataStorage,
103103
info: &SignatureAndTypeConcreteLibfunc,
104104
) -> Result<()> {
105+
metadata.get_or_insert_with(|| ReallocBindingsMeta::new(context, helper));
106+
105107
let circuit_info = match registry.get_type(&info.ty)? {
106108
CoreTypeConcrete::Circuit(CircuitTypeConcrete::Circuit(info)) => &info.circuit_info,
107109
_ => return Err(SierraAssertError::BadTypeInfo.into()),
@@ -307,6 +309,8 @@ fn build_eval<'ctx, 'this>(
307309
metadata: &mut MetadataStorage,
308310
info: &SignatureAndTypeConcreteLibfunc,
309311
) -> Result<()> {
312+
metadata.get_or_insert_with(|| ReallocBindingsMeta::new(context, helper));
313+
310314
let circuit_info = match registry.get_type(&info.ty)? {
311315
CoreTypeConcrete::Circuit(CircuitTypeConcrete::Circuit(info)) => &info.circuit_info,
312316
_ => return Err(SierraAssertError::BadTypeInfo.into()),

src/types/circuit.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ pub fn build_circuit_accumulator<'ctx>(
111111
metadata: &mut MetadataStorage,
112112
info: WithSelf<InfoOnlyConcreteType>,
113113
) -> Result<Type<'ctx>> {
114+
if metadata.get::<ReallocBindingsMeta>().is_none() {
115+
metadata.insert(ReallocBindingsMeta::new(context, module));
116+
}
117+
114118
let Some(GenericArg::Type(circuit_type_id)) = info.info.long_id.generic_args.first() else {
115119
return Err(SierraAssertError::BadTypeInfo.into());
116120
};
@@ -211,6 +215,10 @@ pub fn build_circuit_data<'ctx>(
211215
metadata: &mut MetadataStorage,
212216
info: WithSelf<InfoOnlyConcreteType>,
213217
) -> Result<Type<'ctx>> {
218+
if metadata.get::<ReallocBindingsMeta>().is_none() {
219+
metadata.insert(ReallocBindingsMeta::new(context, module));
220+
}
221+
214222
let Some(GenericArg::Type(circuit_type_id)) = info.info.long_id.generic_args.first() else {
215223
return Err(SierraAssertError::BadTypeInfo.into());
216224
};
@@ -302,6 +310,10 @@ pub fn build_circuit_outputs<'ctx>(
302310
metadata: &mut MetadataStorage,
303311
info: WithSelf<InfoOnlyConcreteType>,
304312
) -> Result<Type<'ctx>> {
313+
if metadata.get::<ReallocBindingsMeta>().is_none() {
314+
metadata.insert(ReallocBindingsMeta::new(context, module));
315+
}
316+
305317
let Some(GenericArg::Type(circuit_type_id)) = info.info.long_id.generic_args.first() else {
306318
return Err(SierraAssertError::BadTypeInfo.into());
307319
};

tests/tests/e2e_libfuncs.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use cairo_lang_sierra::ProgramParser;
2+
use cairo_native::context::NativeContext;
3+
use std::fs;
4+
use std::path::Path;
5+
6+
/// Collects all `.sierra` files under the e2e_sierra directory and compiles each
7+
/// through cairo-native (Sierra → LLVM) to verify they compile without errors.
8+
#[test]
9+
fn compile_e2e_libfunc_sierra() {
10+
let sierra_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("test_data/e2e_sierra");
11+
if !sierra_dir.exists() {
12+
eprintln!(
13+
"Skipping e2e libfunc tests: {} not found. Run 'make pull-external-projects' first.",
14+
sierra_dir.display()
15+
);
16+
return;
17+
}
18+
19+
let context = NativeContext::new();
20+
let parser = ProgramParser::new();
21+
22+
let mut total = 0;
23+
let mut failures = Vec::new();
24+
25+
for entry in walkdir::WalkDir::new(&sierra_dir)
26+
.sort_by_file_name()
27+
.into_iter()
28+
{
29+
let entry = entry.unwrap();
30+
let path = entry.path();
31+
if path.extension().is_some_and(|e| e == "sierra") {
32+
total += 1;
33+
let source = fs::read_to_string(path).unwrap();
34+
let rel_path = path.strip_prefix(&sierra_dir).unwrap();
35+
36+
let program = match parser.parse(&source) {
37+
Ok(p) => p,
38+
Err(e) => {
39+
failures.push(format!("{}: parse error: {e}", rel_path.display()));
40+
continue;
41+
}
42+
};
43+
44+
eprintln!("compiling {}", rel_path.display());
45+
if let Err(e) = context.compile(&program, false, Some(Default::default()), None) {
46+
failures.push(format!("{}: compile error: {e}", rel_path.display()));
47+
}
48+
}
49+
}
50+
51+
assert!(
52+
total > 0,
53+
"No .sierra files found in {}",
54+
sierra_dir.display()
55+
);
56+
57+
if !failures.is_empty() {
58+
panic!(
59+
"{}/{} e2e libfunc sierra files failed to compile:\n{}",
60+
failures.len(),
61+
total,
62+
failures.join("\n")
63+
);
64+
}
65+
}
66+
67+
fn sorted_read_dir(dir: &Path) -> Vec<std::path::PathBuf> {
68+
let mut entries: Vec<_> = fs::read_dir(dir)
69+
.unwrap()
70+
.map(|e| e.unwrap().path())
71+
.collect();
72+
entries.sort();
73+
entries
74+
}

tests/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod cases;
55
pub mod circuit;
66
pub mod compile_library;
77
pub mod dict;
8+
pub mod e2e_libfuncs;
89
pub mod ec;
910
pub mod felt252;
1011
pub mod libfuncs;

0 commit comments

Comments
 (0)