Skip to content

Commit c8086ad

Browse files
DaniPopesgrandizzy
andauthored
test: refactor testdata/ tests to be run in forge test (foundry-rs#12049)
* test: run forge test on testdata/ * chore: refactor to use common Test contract * chore: disable testGasMeteringExternal, via-ir * test: rm unused repros * fix: paths * upd * fmt * fix more tests * test: turn testNonExistingContractRevert into expectRevert * fix some more paths * legacy assertions * compile paris with paris * fix: set configs for fs tests * fix remaining paths in cheats * restrict fs permissions * fix: set runtime evm_version too * fix vyper * fix: a couple of repros * fix: we have storage layouts * fix: 3223, 3674: set sender * reorder * feat: move repros expected failures to snapshots * feat: migrate remaining repros tests * feat: rm migrated files * skip testRevertIfGetUnlinked * move expected core/ failures * upd * move logs/ * move all forgetest tests from it/ to cli/ * fix fork test * move trace/ * tmp: move fuzz/invariant out of fuzz/ * move fuzz/ * forge fmt * wips * fix: both vyper and paris; set src/ * canon * lib log * logs * Revert "fix: set runtime evm_version too" This reverts commit 7ca544b. Contract-level inline config will set evm version for libraries too, which means we fail on deploying libraries that are compiled with newer evm version. * fix: set evm version where needed, per test function * test: reduce gas wastage * chore: clippy * invariant mod.rs * test: fix linking tests with new utils * redact_with * Revert "wips" This reverts commit ee2c17a. * migrate invariant/target{,Abi} * migrate InvariantAfterInvariant.t.sol * migrate InvariantAssume.t.sol * migrate InvariantCalldataDictionary.t.sol, more test utils * migrate InvariantCustomError.t.sol * migrate InvariantExcludedSenders.t.sol * migrate InvariantFixtures.t.sol * migrate InvariantHandlerFailure.t.sol * interlude: forgot to use a new file * migrate InvariantInnerContract.t.sol * migrate InvariantPreserveState.t.sol * migrate InvariantReentrancy.t.sol * migrate InvariantRollFork.t.sol * migrate InvariantScrapeValues.t.sol * migrate InvariantSequenceNoReverts.t.sol * migrate InvariantShrinkBigSequence.t.sol * migrate InvariantShrinkFailOnRevert.t.sol * migrate InvariantShrinkWithAssert.t.sol * migrate InvariantTest1.t.sol * fix InvariantInnerContract.t.sol * update new Rlp test * com * better com * nuke tests/it * test: fix testdata paths in script tester * test: fix relative paths in test_cmd * test: redact more in issue_2851 * fix: copy testdata correctly * trace addrs * manual retry logic with --retry * fix nondeterministic output * debug: fs lock error context * test: fix project root for windows * test: skip project root test if unset * normalize both * typo * Revert "typo" This reverts commit 402bea1. * Revert "debug: fs lock error context" This reverts commit e5caedd. * fix * fix: locked_write_line for windows * chore: clippy * fmt * chore: speed up fuzzed_selected_targets * other way * fix nondeterministic output 2 * fix: disable persistence * test: revert old via-ir * ci: tweak cache key * do not run trace test when isolate --------- Co-authored-by: grandizzy <grandizzy.the.egg@gmail.com>
1 parent 25b9483 commit c8086ad

283 files changed

Lines changed: 5539 additions & 6779 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/nextest.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ jobs:
9898
~/.config/.foundry/cache
9999
testdata/cache
100100
testdata/out
101-
key: ${{ runner.os }}-foundry-${{ matrix.name }}
101+
# Use a unique key for each run to always update the cache.
102+
key: foundry-${{ matrix.name }}-${{ github.run_id }}
103+
restore-keys: |
104+
foundry-${{ matrix.name }}-
102105
- uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9
103106
- name: Setup Git config
104107
run: |

crates/cheatcodes/spec/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,7 @@ mod tests {
117117
#[cfg(feature = "schema")]
118118
const SCHEMA_PATH: &str =
119119
concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/cheatcodes.schema.json");
120-
const IFACE_PATH: &str =
121-
concat!(env!("CARGO_MANIFEST_DIR"), "/../../../testdata/cheats/Vm.sol");
120+
const IFACE_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../testdata/utils/Vm.sol");
122121

123122
/// Generates the `cheatcodes.json` file contents.
124123
fn json_cheatcodes() -> String {

crates/chisel/src/source.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use walkdir::WalkDir;
2626
pub const MIN_VM_VERSION: Version = Version::new(0, 6, 2);
2727

2828
/// Solidity source for the `Vm` interface in [forge-std](https://github.com/foundry-rs/forge-std)
29-
static VM_SOURCE: &str = include_str!("../../../testdata/cheats/Vm.sol");
29+
static VM_SOURCE: &str = include_str!("../../../testdata/utils/Vm.sol");
3030

3131
/// [`SessionSource`] build output.
3232
pub struct GeneratedOutput {

crates/common/src/fs.rs

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use flate2::{Compression, read::GzDecoder, write::GzEncoder};
55
use serde::{Serialize, de::DeserializeOwned};
66
use std::{
77
fs::{self, File},
8-
io::{BufReader, BufWriter, Read, Write},
8+
io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write},
99
path::{Component, Path, PathBuf},
1010
};
1111

@@ -62,25 +62,29 @@ pub fn read_json_gzip_file<T: DeserializeOwned>(path: &Path) -> Result<T> {
6262
/// Reads the entire contents of a locked shared file into a string.
6363
pub fn locked_read_to_string(path: impl AsRef<Path>) -> Result<String> {
6464
let path = path.as_ref();
65-
let file =
65+
let mut file =
6666
fs::OpenOptions::new().read(true).open(path).map_err(|err| FsPathError::open(err, path))?;
6767
file.lock_shared().map_err(|err| FsPathError::lock(err, path))?;
68-
let mut contents = String::new();
69-
(&file).read_to_string(&mut contents).map_err(|err| FsPathError::read(err, path))?;
68+
let contents = read_inner(path, &mut file)?;
7069
file.unlock().map_err(|err| FsPathError::unlock(err, path))?;
71-
Ok(contents)
70+
String::from_utf8(contents).map_err(|err| FsPathError::read(std::io::Error::other(err), path))
7271
}
7372

7473
/// Reads the entire contents of a locked shared file into a bytes vector.
7574
pub fn locked_read(path: impl AsRef<Path>) -> Result<Vec<u8>> {
7675
let path = path.as_ref();
77-
let file =
76+
let mut file =
7877
fs::OpenOptions::new().read(true).open(path).map_err(|err| FsPathError::open(err, path))?;
7978
file.lock_shared().map_err(|err| FsPathError::lock(err, path))?;
79+
let contents = read_inner(path, &mut file)?;
80+
file.unlock().map_err(|err| FsPathError::unlock(err, path))?;
81+
Ok(contents)
82+
}
83+
84+
fn read_inner(path: &Path, file: &mut File) -> Result<Vec<u8>> {
8085
let file_len = file.metadata().map_err(|err| FsPathError::open(err, path))?.len() as usize;
8186
let mut buffer = Vec::with_capacity(file_len);
82-
(&file).read_to_end(&mut buffer).map_err(|err| FsPathError::read(err, path))?;
83-
file.unlock().map_err(|err| FsPathError::unlock(err, path))?;
87+
file.read_to_end(&mut buffer).map_err(|err| FsPathError::read(err, path))?;
8488
Ok(buffer)
8589
}
8690

@@ -136,18 +140,39 @@ pub fn locked_write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Resul
136140
}
137141

138142
/// Writes a line in an exclusive locked file.
139-
pub fn locked_write_line(path: impl AsRef<Path>, line: &String) -> Result<()> {
143+
pub fn locked_write_line(path: impl AsRef<Path>, line: &str) -> Result<()> {
140144
let path = path.as_ref();
145+
if cfg!(windows) {
146+
return locked_write_line_windows(path, line);
147+
}
148+
141149
let mut file = std::fs::OpenOptions::new()
142150
.append(true)
143151
.create(true)
144152
.open(path)
145153
.map_err(|err| FsPathError::open(err, path))?;
154+
146155
file.lock().map_err(|err| FsPathError::lock(err, path))?;
147156
writeln!(file, "{line}").map_err(|err| FsPathError::write(err, path))?;
148157
file.unlock().map_err(|err| FsPathError::unlock(err, path))
149158
}
150159

160+
// Locking fails on Windows if the file is opened in append mode.
161+
fn locked_write_line_windows(path: &Path, line: &str) -> Result<()> {
162+
let mut file = std::fs::OpenOptions::new()
163+
.write(true)
164+
.truncate(false)
165+
.create(true)
166+
.open(path)
167+
.map_err(|err| FsPathError::open(err, path))?;
168+
file.lock().map_err(|err| FsPathError::lock(err, path))?;
169+
170+
file.seek(SeekFrom::End(0)).map_err(|err| FsPathError::write(err, path))?;
171+
writeln!(file, "{line}").map_err(|err| FsPathError::write(err, path))?;
172+
173+
file.unlock().map_err(|err| FsPathError::unlock(err, path))
174+
}
175+
151176
/// Wrapper for `std::fs::copy`
152177
pub fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<u64> {
153178
let from = from.as_ref();
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//! Core test functionality tests
2+
3+
use foundry_test_utils::str;
4+
5+
forgetest_init!(failing_test_after_failed_setup, |prj, cmd| {
6+
prj.wipe_contracts();
7+
prj.add_test(
8+
"FailingTestAfterFailedSetup.t.sol",
9+
r#"
10+
import "forge-std/Test.sol";
11+
12+
contract FailingTestAfterFailedSetupTest is Test {
13+
function setUp() public {
14+
assertTrue(false);
15+
}
16+
17+
function testAssertSuccess() public {
18+
assertTrue(true);
19+
}
20+
21+
function testAssertFailure() public {
22+
assertTrue(false);
23+
}
24+
}
25+
"#,
26+
);
27+
28+
cmd.arg("test").assert_failure().stdout_eq(str![[r#"
29+
...
30+
Ran 1 test for test/FailingTestAfterFailedSetup.t.sol:FailingTestAfterFailedSetupTest
31+
[FAIL: assertion failed] setUp() ([GAS])
32+
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED]
33+
34+
Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests)
35+
36+
Failing tests:
37+
Encountered 1 failing test in test/FailingTestAfterFailedSetup.t.sol:FailingTestAfterFailedSetupTest
38+
[FAIL: assertion failed] setUp() ([GAS])
39+
40+
Encountered a total of 1 failing tests, 0 tests succeeded
41+
42+
Tip: Run `forge test --rerun` to retry only the 1 failed test
43+
44+
"#]]);
45+
});
46+
47+
forgetest_init!(legacy_assertions, |prj, cmd| {
48+
prj.wipe_contracts();
49+
prj.add_test(
50+
"LegacyAssertions.t.sol",
51+
r#"
52+
import "forge-std/Test.sol";
53+
54+
contract NoAssertionsRevertTest is Test {
55+
function testMultipleAssertFailures() public {
56+
vm.assertEq(uint256(1), uint256(2));
57+
vm.assertLt(uint256(5), uint256(4));
58+
}
59+
}
60+
61+
/// forge-config: default.legacy_assertions = true
62+
contract LegacyAssertionsTest {
63+
bool public failed;
64+
65+
function testFlagNotSetSuccess() public {}
66+
67+
function testFlagSetFailure() public {
68+
failed = true;
69+
}
70+
}
71+
"#,
72+
);
73+
74+
cmd.args(["test", "-j1"]).assert_failure().stdout_eq(str![[r#"
75+
...
76+
Ran 2 tests for test/LegacyAssertions.t.sol:LegacyAssertionsTest
77+
[PASS] testFlagNotSetSuccess() ([GAS])
78+
[FAIL] testFlagSetFailure() ([GAS])
79+
Suite result: FAILED. 1 passed; 1 failed; 0 skipped; [ELAPSED]
80+
81+
Ran 1 test for test/LegacyAssertions.t.sol:NoAssertionsRevertTest
82+
[FAIL: assertion failed: 1 != 2] testMultipleAssertFailures() ([GAS])
83+
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED]
84+
85+
Ran 2 test suites [ELAPSED]: 1 tests passed, 2 failed, 0 skipped (3 total tests)
86+
87+
Failing tests:
88+
Encountered 1 failing test in test/LegacyAssertions.t.sol:LegacyAssertionsTest
89+
[FAIL] testFlagSetFailure() ([GAS])
90+
91+
Encountered 1 failing test in test/LegacyAssertions.t.sol:NoAssertionsRevertTest
92+
[FAIL: assertion failed: 1 != 2] testMultipleAssertFailures() ([GAS])
93+
94+
Encountered a total of 2 failing tests, 1 tests succeeded
95+
96+
Tip: Run `forge test --rerun` to retry only the 2 failed tests
97+
98+
"#]]);
99+
});
100+
101+
forgetest_init!(payment_failure, |prj, cmd| {
102+
prj.wipe_contracts();
103+
prj.add_test(
104+
"PaymentFailure.t.sol",
105+
r#"
106+
import "forge-std/Test.sol";
107+
108+
contract Payable {
109+
function pay() public payable {}
110+
}
111+
112+
contract PaymentFailureTest is Test {
113+
function testCantPay() public {
114+
Payable target = new Payable();
115+
vm.prank(address(1));
116+
target.pay{value: 1}();
117+
}
118+
}
119+
"#,
120+
);
121+
122+
cmd.arg("test").assert_failure().stdout_eq(str![[r#"
123+
[COMPILING_FILES] with [SOLC_VERSION]
124+
[SOLC_VERSION] [ELAPSED]
125+
Compiler run successful!
126+
127+
Ran 1 test for test/PaymentFailure.t.sol:PaymentFailureTest
128+
[FAIL: EvmError: Revert] testCantPay() ([GAS])
129+
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED]
130+
131+
Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests)
132+
133+
Failing tests:
134+
Encountered 1 failing test in test/PaymentFailure.t.sol:PaymentFailureTest
135+
[FAIL: EvmError: Revert] testCantPay() ([GAS])
136+
137+
Encountered a total of 1 failing tests, 0 tests succeeded
138+
139+
Tip: Run `forge test --rerun` to retry only the 1 failed test
140+
141+
"#]]);
142+
});

0 commit comments

Comments
 (0)