Skip to content

Commit c693788

Browse files
enitratpiotmag769
andauthored
feat: should_panic attribute (#399)
<!-- Reference any GitHub issues resolved by this PR --> Closes #355 ## Introduced changes <!-- A brief description of the changes --> - Adds support for a should_panic macro. The runner checks whether the panicked data is the one expected. - Added tests. ## Breaking changes <!-- List of all breaking changes, if applicable --> ## Checklist <!-- Make sure all of these are complete --> - [x] Linked relevant issue - [x] Updated relevant documentation - [x] Added relevant tests - [x] Performed self-review of the code - [x] Added changes to `CHANGELOG.md` --------- Co-authored-by: Piotr Magiera <[email protected]> Co-authored-by: Piotr Magiera <[email protected]>
1 parent 99206c7 commit c693788

File tree

11 files changed

+306
-45
lines changed

11 files changed

+306
-45
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
#### Added
1313

14+
- `#[should_panic]` attribute support
1415
- Documentation to public methods
1516

1617
#### Changed
@@ -91,4 +92,4 @@ cheatcodes = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "0.
9192

9293
#### Added
9394

94-
- Initial release
95+
- Initial release

crates/forge/src/lib.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ fn strip_path_from_test_names(test_cases: Vec<TestCase>) -> Result<Vec<TestCase>
345345
Ok(TestCase {
346346
name,
347347
available_gas: test_case.available_gas,
348+
expected_result: test_case.expected_result,
348349
})
349350
})
350351
.collect()
@@ -381,6 +382,7 @@ fn test_name_contains(test_name_filter: &str, test: &TestCase) -> Result<bool> {
381382
mod tests {
382383
use super::*;
383384
use assert_fs::fixture::PathCopy;
385+
use test_collector::ExpectedTestResult;
384386

385387
#[test]
386388
fn collecting_tests() {
@@ -412,14 +414,17 @@ mod tests {
412414
TestCase {
413415
name: "crate1::do_thing".to_string(),
414416
available_gas: None,
417+
expected_result: ExpectedTestResult::Success,
415418
},
416419
TestCase {
417420
name: "crate2::run_other_thing".to_string(),
418421
available_gas: None,
422+
expected_result: ExpectedTestResult::Success,
419423
},
420424
TestCase {
421425
name: "outer::crate2::execute_next_thing".to_string(),
422426
available_gas: None,
427+
expected_result: ExpectedTestResult::Success,
423428
},
424429
];
425430

@@ -429,6 +434,7 @@ mod tests {
429434
vec![TestCase {
430435
name: "crate1::do_thing".to_string(),
431436
available_gas: None,
437+
expected_result: ExpectedTestResult::Success,
432438
},]
433439
);
434440

@@ -438,6 +444,7 @@ mod tests {
438444
vec![TestCase {
439445
name: "crate2::run_other_thing".to_string(),
440446
available_gas: None,
447+
expected_result: ExpectedTestResult::Success,
441448
},]
442449
);
443450

@@ -448,14 +455,17 @@ mod tests {
448455
TestCase {
449456
name: "crate1::do_thing".to_string(),
450457
available_gas: None,
458+
expected_result: ExpectedTestResult::Success,
451459
},
452460
TestCase {
453461
name: "crate2::run_other_thing".to_string(),
454462
available_gas: None,
463+
expected_result: ExpectedTestResult::Success,
455464
},
456465
TestCase {
457466
name: "outer::crate2::execute_next_thing".to_string(),
458467
available_gas: None,
468+
expected_result: ExpectedTestResult::Success,
459469
},
460470
]
461471
);
@@ -470,14 +480,17 @@ mod tests {
470480
TestCase {
471481
name: "crate1::do_thing".to_string(),
472482
available_gas: None,
483+
expected_result: ExpectedTestResult::Success,
473484
},
474485
TestCase {
475486
name: "crate2::run_other_thing".to_string(),
476487
available_gas: None,
488+
expected_result: ExpectedTestResult::Success,
477489
},
478490
TestCase {
479491
name: "outer::crate2::execute_next_thing".to_string(),
480492
available_gas: None,
493+
expected_result: ExpectedTestResult::Success,
481494
},
482495
]
483496
);
@@ -489,14 +502,17 @@ mod tests {
489502
TestCase {
490503
name: "crate1::do_thing".to_string(),
491504
available_gas: None,
505+
expected_result: ExpectedTestResult::Success,
492506
},
493507
TestCase {
494508
name: "crate2::run_other_thing".to_string(),
495509
available_gas: None,
510+
expected_result: ExpectedTestResult::Success,
496511
},
497512
TestCase {
498513
name: "outer::crate2::run_other_thing".to_string(),
499514
available_gas: None,
515+
expected_result: ExpectedTestResult::Success,
500516
},
501517
];
502518

@@ -510,18 +526,22 @@ mod tests {
510526
TestCase {
511527
name: "crate1::do_thing".to_string(),
512528
available_gas: None,
529+
expected_result: ExpectedTestResult::Success,
513530
},
514531
TestCase {
515532
name: "crate2::run_other_thing".to_string(),
516533
available_gas: None,
534+
expected_result: ExpectedTestResult::Success,
517535
},
518536
TestCase {
519537
name: "outer::crate3::run_other_thing".to_string(),
520538
available_gas: None,
539+
expected_result: ExpectedTestResult::Success,
521540
},
522541
TestCase {
523542
name: "do_thing".to_string(),
524543
available_gas: None,
544+
expected_result: ExpectedTestResult::Success,
525545
},
526546
];
527547

@@ -537,6 +557,7 @@ mod tests {
537557
vec![TestCase {
538558
name: "do_thing".to_string(),
539559
available_gas: None,
560+
expected_result: ExpectedTestResult::Success,
540561
},]
541562
);
542563

@@ -547,6 +568,7 @@ mod tests {
547568
vec![TestCase {
548569
name: "crate1::do_thing".to_string(),
549570
available_gas: None,
571+
expected_result: ExpectedTestResult::Success,
550572
},]
551573
);
552574

@@ -561,6 +583,7 @@ mod tests {
561583
vec![TestCase {
562584
name: "outer::crate3::run_other_thing".to_string(),
563585
available_gas: None,
586+
expected_result: ExpectedTestResult::Success,
564587
},]
565588
);
566589
}
@@ -571,14 +594,17 @@ mod tests {
571594
TestCase {
572595
name: "crate1::do_thing".to_string(),
573596
available_gas: None,
597+
expected_result: ExpectedTestResult::Success,
574598
},
575599
TestCase {
576600
name: "crate2::run_other_thing".to_string(),
577601
available_gas: None,
602+
expected_result: ExpectedTestResult::Success,
578603
},
579604
TestCase {
580605
name: "thing".to_string(),
581606
available_gas: None,
607+
expected_result: ExpectedTestResult::Success,
582608
},
583609
];
584610

@@ -589,14 +615,17 @@ mod tests {
589615
TestCase {
590616
name: "crate1::do_thing".to_string(),
591617
available_gas: None,
618+
expected_result: ExpectedTestResult::Success,
592619
},
593620
TestCase {
594621
name: "crate2::run_other_thing".to_string(),
595622
available_gas: None,
623+
expected_result: ExpectedTestResult::Success,
596624
},
597625
TestCase {
598626
name: "thing".to_string(),
599627
available_gas: None,
628+
expected_result: ExpectedTestResult::Success,
600629
},
601630
]
602631
);
@@ -608,14 +637,17 @@ mod tests {
608637
TestCase {
609638
name: "/Users/user/forge/tests/data/simple_package/src::test::test_fib".to_string(),
610639
available_gas: None,
640+
expected_result: ExpectedTestResult::Success,
611641
},
612642
TestCase {
613643
name: "crate2::run_other_thing".to_string(),
614644
available_gas: None,
645+
expected_result: ExpectedTestResult::Success,
615646
},
616647
TestCase {
617648
name: "src/crate2::run_other_thing".to_string(),
618649
available_gas: None,
650+
expected_result: ExpectedTestResult::Success,
619651
},
620652
];
621653

@@ -626,14 +658,17 @@ mod tests {
626658
TestCase {
627659
name: "src::test::test_fib".to_string(),
628660
available_gas: None,
661+
expected_result: ExpectedTestResult::Success,
629662
},
630663
TestCase {
631664
name: "crate2::run_other_thing".to_string(),
632665
available_gas: None,
666+
expected_result: ExpectedTestResult::Success,
633667
},
634668
TestCase {
635669
name: "crate2::run_other_thing".to_string(),
636670
available_gas: None,
671+
expected_result: ExpectedTestResult::Success,
637672
},
638673
]
639674
);

crates/forge/src/test_case_summary.rs

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
use cairo_felt::Felt252;
12
use cairo_lang_runner::short_string::as_cairo_short_string;
23
use cairo_lang_runner::{RunResult, RunResultValue};
34
use std::option::Option;
4-
use test_collector::TestCase;
5+
use test_collector::{ExpectedPanicValue, ExpectedTestResult, TestCase};
56

67
/// Summary of running a single test case
78
#[derive(Debug, PartialEq, Clone)]
@@ -34,16 +35,41 @@ pub enum TestCaseSummary {
3435
impl TestCaseSummary {
3536
#[must_use]
3637
pub(crate) fn from_run_result(run_result: RunResult, test_case: &TestCase) -> Self {
37-
match run_result.value {
38-
RunResultValue::Success(_) => TestCaseSummary::Passed {
39-
name: test_case.name.to_string(),
40-
msg: extract_result_data(&run_result),
41-
run_result,
38+
let name = test_case.name.to_string();
39+
let msg = extract_result_data(&run_result, &test_case.expected_result);
40+
match run_result.clone().value {
41+
RunResultValue::Success(_) => match &test_case.expected_result {
42+
ExpectedTestResult::Success => TestCaseSummary::Passed {
43+
name,
44+
msg,
45+
run_result,
46+
},
47+
ExpectedTestResult::Panics(_) => TestCaseSummary::Failed {
48+
name,
49+
msg,
50+
run_result: Some(run_result),
51+
},
4252
},
43-
RunResultValue::Panic(_) => TestCaseSummary::Failed {
44-
name: test_case.name.to_string(),
45-
msg: extract_result_data(&run_result),
46-
run_result: Some(run_result),
53+
RunResultValue::Panic(value) => match &test_case.expected_result {
54+
ExpectedTestResult::Success => TestCaseSummary::Failed {
55+
name,
56+
msg,
57+
run_result: Some(run_result),
58+
},
59+
ExpectedTestResult::Panics(panic_expectation) => match panic_expectation {
60+
ExpectedPanicValue::Exact(expected) if &value != expected => {
61+
TestCaseSummary::Failed {
62+
name,
63+
msg,
64+
run_result: Some(run_result),
65+
}
66+
}
67+
_ => TestCaseSummary::Passed {
68+
name,
69+
msg,
70+
run_result,
71+
},
72+
},
4773
},
4874
}
4975
}
@@ -56,12 +82,8 @@ impl TestCaseSummary {
5682
}
5783
}
5884

59-
#[must_use]
60-
fn extract_result_data(run_result: &RunResult) -> Option<String> {
61-
let data = match &run_result.value {
62-
RunResultValue::Panic(data) | RunResultValue::Success(data) => data,
63-
};
64-
85+
/// Helper function to build `readable_text` from a run data.
86+
fn build_readable_text(data: &Vec<Felt252>) -> Option<String> {
6587
let mut readable_text = String::new();
6688

6789
for felt in data {
@@ -78,3 +100,49 @@ fn extract_result_data(run_result: &RunResult) -> Option<String> {
78100
Some(readable_text)
79101
}
80102
}
103+
104+
#[must_use]
105+
/// Returns a string with the data that was produced by the test case.
106+
/// If the test was expected to fail with specific data e.g. `#[should_panic(expected: ('data',))]`
107+
/// and failed to do so, it returns a string comparing the panic data and the expected data.
108+
pub(crate) fn extract_result_data(
109+
run_result: &RunResult,
110+
expectation: &ExpectedTestResult,
111+
) -> Option<String> {
112+
match &run_result.value {
113+
RunResultValue::Success(data) => build_readable_text(data),
114+
RunResultValue::Panic(panic_data) => {
115+
let expected_data = match expectation {
116+
ExpectedTestResult::Panics(panic_expectation) => match panic_expectation {
117+
ExpectedPanicValue::Exact(data) => Some(data),
118+
ExpectedPanicValue::Any => None,
119+
},
120+
ExpectedTestResult::Success => None,
121+
};
122+
123+
let panic_string: String = panic_data
124+
.iter()
125+
.map(|felt| as_cairo_short_string(felt).unwrap_or_default())
126+
.collect::<Vec<String>>()
127+
.join(", ");
128+
129+
match expected_data {
130+
Some(expected) if expected == panic_data => None,
131+
Some(expected) => {
132+
let expected_string = expected
133+
.iter()
134+
.map(|felt| as_cairo_short_string(felt).unwrap_or_default())
135+
.collect::<Vec<String>>()
136+
.join(", ");
137+
138+
Some(format!(
139+
"\n Incorrect panic data\n {}\n {}\n",
140+
format_args!("Actual: {panic_data:?} ({panic_string})"),
141+
format_args!("Expected: {expected:?} ({expected_string})")
142+
))
143+
}
144+
None => build_readable_text(panic_data),
145+
}
146+
}
147+
}
148+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[package]
2+
name = "should_panic_test"
3+
version = "0.1.0"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)