-
-
Notifications
You must be signed in to change notification settings - Fork 172
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
Add tests for async_channel #837
base: theseus_main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! The initial set of tests is decent, but there are a few more scenarios that ought to be tested. The major issue is that the sending and receiving tasks are the same -- for a real test, the sending ought to be done on a different task from the receiving.
The following important scenarios aren't tested (there may be more too):
- the
clone
method onSender
/Receiver
- dropping a
Sender
orReceiver
when multiple ones exist already, rather than just a single one. - Trying to send to or receive from a disconnected channel
- Whether the
Receiver::sender()
andSender::receiver()
methods properly change the Channel's state in the case of multiple endpoints or a single endpoint. - Actually recovering from a channel disconnection using the above methods and then successfully sending and/or receiving through the channel
- Anything with more than two tasks -- a common use case would be to have several sending tasks and several receiving tasks, so we want to test whether that works properly.
Testing is tough, but in general the goal is to be as rigorous and difficult as possible in order to stress the mechanisms we've implemented rather than test only the easiest scenarios.
applications/test_channel/src/lib.rs
Outdated
@@ -54,7 +54,7 @@ macro_rules! receive_count { | |||
() => (RECEIVE_COUNT.load(Ordering::SeqCst)) | |||
} | |||
|
|||
pub fn main(args: Vec<String>) -> isize { | |||
pub fn main<T:Send>(args: Vec<String>) -> isize { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the main
function cannot be parameterized with generic types, since there's no way to invoke it. Have you actually tried to run it to see what happens? T
would be nothing, i.e., random junk, meaning it would sporadically fail in the oddest ways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test app itself would need to choose a type for T
, or a few different types across different test iterations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can I use the Sender<T>
then, without introducing generic parameter T
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you choose a type when you create the channel, just like with Rust's std
channels. So, something like:
let (sender, receiver) = new_channel::<u64>(...);
let (sender, receiver) = new_channel::<String>(...);
let (sender, receiver) = new_channel::<YourCustomTypeThatImplsSend>(...);
kernel/async_channel/src/lib.rs
Outdated
@@ -103,11 +103,11 @@ impl From<Error> for core2::io::Error { | |||
/// | |||
/// This channel object is not Send/Sync or cloneable itself; | |||
/// it can be shared across tasks using an `Arc`. | |||
struct Channel<T: Send> { | |||
pub struct Channel<T: Send> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rather than making the Channel
type public, or any of its fields or methods, we should simply add methods to Sender
and Receiver
that invoke the private Channel
methods.
I think the only one we really need is channel_status()
, so you can add a Sender::channel_status()
and Receiver::channel_status()
that just call the existing function within Channel
.
The reason for this is to achieve proper encapsulation through limited visibility -- we don't necessarily want a caller to be able to access any and all fields/methods within Channel
, because then they could potentially violate its semantics by changing things in unforeseen ways.
applications/test_channel/src/lib.rs
Outdated
warn!("WARNING: failed to change ChannelStatus to SenderDisconnected for cloned receiver"); | ||
} | ||
|
||
// not sure how to test whether receivers were notified |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should print something or assert something in the receiver task, and vice versa for the other example.
applications/test_channel/src/lib.rs
Outdated
drop(sender); | ||
|
||
if receiver.channel.channel_status.as_ptr() != &mut async_channel::ChannelStatus::SenderDisconnected { | ||
warn!("WARNING: failed to change ChannelStatus to SenderDisconnected for original receiver"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warning is good, but asserting or throwing an error would be even better. It is a test, after all, so failures should be loud.
How can I tell whether the sending and receiving tasks are the same? |
Just like with regular Rust, unless you manually spawned a new task, all of your code in a function or within a series of functions that call each other will all run in the same task. Here I'm suggesting that you create a test scenario that is more representative of real-world channel usage, in that the sender(s) and receiver(s) are typically on different tasks, as it doesn't make much sense for a task to send itself a message via a channel. |
374e56d
to
ca426bd
Compare
addresses 3rd checkbox in #715
Let me know if I did it correctly and if there's anything missing