Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(forge test): stream test results #798

Merged
merged 11 commits into from
Feb 26, 2022

Conversation

lattejed
Copy link
Contributor

@gakonst this adds streaming for test results. Makes the UI more responsive for large projects / older machines.

@onbjerg onbjerg mentioned this pull request Feb 23, 2022
8 tasks
cli/src/cmd/test.rs Show resolved Hide resolved
forge/src/multi_runner.rs Show resolved Hide resolved
forge/src/multi_runner.rs Outdated Show resolved Hide resolved
@onbjerg onbjerg added the T-feature Type: feature label Feb 23, 2022
cli/src/cmd/test.rs Outdated Show resolved Hide resolved
Copy link
Member

@onbjerg onbjerg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested with forge test, forge test --gas-report and forge test -vvvvv and it all worked as expected (tested on solmate). It didn't make much of a difference on my machine, but I'm sure it will on someone elses. Great work!

Copy link
Member

@onbjerg onbjerg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nvm, it actually isn't really using the channel as we discussed - the call to runner.test blocks since we're waiting for results, so the receiving/outputting loop is not run until that finishes and the behavior is the same as if we were not using a channel

@lattejed
Copy link
Contributor Author

@onbjerg yeah, I think we'll have to ditch the mpsc since it will block if we put runner.test after it.

@gakonst
Copy link
Member

gakonst commented Feb 24, 2022

Also something to consider: AFAIK this streams results on a per-contract basis, do you think we could do something like a streaming terminal that fills stdout across the entire board, by dynamically adding new contract tags as tests for that contract complete?

example:

$ forge test
< no output>

then when Contract::testContract1() completes it does:

Contract.sol:Contract
testContract1()

then when another contract test completes, it'd do

Contract.sol:Contract
testContract1()

Contract2.sol:Contract2
testContract2()

and then when contract 1's other function completes it'd show

Contract.sol:Contract
testContract1()
testContract1Other()

Contract2.sol:Contract2
testContract2()

@lattejed
Copy link
Contributor Author

lattejed commented Feb 24, 2022

@onbjerg can keep the mpsc by sticking the while loop in a separate thread.

@gakonst could store the lines in memory and redraw the whole set as it updates. I'm not sure what terminal lib would work but seems doable?

I'd worry about messing up scrolling or having updates appear offscreen though. If it does behave with scrolling though it would be pretty cool.

@lattejed
Copy link
Contributor Author

@onbjerg if you're happy with this please go ahead with it. I'll have to give what @gakonst is suggesting some thought and I'll open a separate PR if it seems doable.

@onbjerg
Copy link
Member

onbjerg commented Feb 24, 2022

I think this is good for terminals where we cannot move the cursor back (like CIs) and then for interactive terminals we could do what @gakonst suggested with https://github.com/fdehau/tui-rs (see the paragraph example). Will give this a go again

Copy link
Member

@onbjerg onbjerg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works as intended now 😄

@lattejed
Copy link
Contributor Author

This does appear to be running tests in sync again whereas an earlier version did not. Needs more investigation

@lattejed
Copy link
Contributor Author

lattejed commented Feb 25, 2022

Actually, no, this seems to be working as expected. These are tests with hashing in a loop, the last part of the test fn name is how many times the loop runs.

First test setup:

forge test
Compiling...
No files changed, compilation skipped

Running 1 test for CTest.json:CTest
[PASS] test_keccak256_16384() (gas: 6162268)

Running 1 test for ATest.json:ATest
[PASS] test_keccak256_16384() (gas: 6162268)

Running 1 test for BTest.json:BTest
[PASS] test_keccak256_16384() (gas: 6162268)
forge test
Compiling...
No files changed, compilation skipped

Running 1 test for ATest.json:ATest
[PASS] test_keccak256_16384() (gas: 6162268)

Running 1 test for BTest.json:BTest
[PASS] test_keccak256_16384() (gas: 6162268)

Running 1 test for CTest.json:CTest
[PASS] test_keccak256_16384() (gas: 6162268)
forge test
Compiling...
No files changed, compilation skipped

Running 1 test for ATest.json:ATest
[PASS] test_keccak256_16384() (gas: 6162268)

Running 1 test for CTest.json:CTest
[PASS] test_keccak256_16384() (gas: 6162268)

Running 1 test for BTest.json:BTest
[PASS] test_keccak256_16384() (gas: 6162268)

Tests print at approximately the same time but in indeterminate order. This is expected.

Second test setup:

forge test
Compiling...
Compiling 2 files with 0.8.12
Compilation finished successfully
Success

Running 1 test for CTest.json:CTest
[PASS] test_keccak256_1() (gas: 1492)

Running 1 test for BTest.json:BTest
[PASS] test_keccak256_256() (gas: 65232)

Running 1 test for ATest.json:ATest
[PASS] test_keccak256_16384() (gas: 6162268)
forge test
Compiling...
No files changed, compilation skipped

Running 1 test for CTest.json:CTest
[PASS] test_keccak256_1() (gas: 1492)

Running 1 test for BTest.json:BTest
[PASS] test_keccak256_256() (gas: 65232)

Running 1 test for ATest.json:ATest
[PASS] test_keccak256_16384() (gas: 6162268)

Tests stream in that order consistently. This is also expected.

@gakonst @onbjerg we agree that's expected behavior, right? Maybe I'm overlooking something.

Copy link
Member

@gakonst gakonst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merging cautiously. This PR made me think that it's worth extracting our integration tests from here to Criterion benchmarks

}
let mut gas_report = GasReport::new(gas_reports.1);
let (tx, rx) = channel::<(String, BTreeMap<String, TestResult>)>();
let known_contracts = runner.known_contracts.clone();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not expensive to clone each time?

@gakonst gakonst merged commit 135cb12 into foundry-rs:master Feb 26, 2022
@lattejed
Copy link
Contributor Author

My intuition would be no but I'd be happy to quantify it somehow -- or possibly just remove the clone() -- if you'd like

@gakonst
Copy link
Member

gakonst commented Feb 26, 2022

Let's leave as-is, no big deal and want to be respectful of your time invested in this already. Thank you again!

@lattejed
Copy link
Contributor Author

Thanks @gakonst 🤝

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-feature Type: feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants