]> git.lizzy.rs Git - rust.git/commitdiff
Add test cases for select
authorBen Blum <bblum@andrew.cmu.edu>
Sat, 20 Jul 2013 00:45:54 +0000 (20:45 -0400)
committerBen Blum <bblum@andrew.cmu.edu>
Tue, 30 Jul 2013 17:19:25 +0000 (13:19 -0400)
src/libstd/rt/select.rs
src/libstd/unstable/sync.rs

index 130084fd1fc2a3225a102933c01d259e28e28fa1..c2e471a338625d294ee810bbcaec84a4af5425eb 100644 (file)
@@ -100,3 +100,231 @@ pub fn select2<TA, A: SelectPort<TA>, TB, B: SelectPort<TB>>(mut a: A, mut b: B)
 }
 
 */
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use option::*;
+    use rt::comm::*;
+    use rt::test::*;
+    use vec::*;
+    use comm::GenericChan;
+    use task;
+    use cell::Cell;
+
+    #[test] #[ignore(cfg(windows))] #[should_fail]
+    fn select_doesnt_get_trolled() {
+        select::<PortOne<()>>([]);
+    }
+
+    /* non-blocking select tests */
+
+    #[cfg(test)]
+    fn select_helper(num_ports: uint, send_on_chans: &[uint]) {
+        // Unfortunately this does not actually test the block_on early-break
+        // codepath in select -- racing between the sender and the receiver in
+        // separate tasks is necessary to get around the optimistic check.
+        let (ports, chans) = unzip(from_fn(num_ports, |_| oneshot::<()>()));
+        let mut dead_chans = ~[];
+        let mut ports = ports;
+        for chans.consume_iter().enumerate().advance |(i, chan)| {
+            if send_on_chans.contains(&i) {
+                chan.send(());
+            } else {
+                dead_chans.push(chan);
+            }
+        }
+        let ready_index = select(ports);
+        assert!(send_on_chans.contains(&ready_index));
+        assert!(ports.swap_remove(ready_index).recv_ready().is_some());
+        let _ = dead_chans;
+
+        // Same thing with streams instead.
+        // FIXME(#7971): This should be in a macro but borrowck isn't smart enough.
+        let (ports, chans) = unzip(from_fn(num_ports, |_| stream::<()>()));
+        let mut dead_chans = ~[];
+        let mut ports = ports;
+        for chans.consume_iter().enumerate().advance |(i, chan)| {
+            if send_on_chans.contains(&i) {
+                chan.send(());
+            } else {
+                dead_chans.push(chan);
+            }
+        }
+        let ready_index = select(ports);
+        assert!(send_on_chans.contains(&ready_index));
+        assert!(ports.swap_remove(ready_index).recv_ready().is_some());
+        let _ = dead_chans;
+    }
+
+    #[test]
+    fn select_one() {
+        do run_in_newsched_task { select_helper(1, [0]) }
+    }
+
+    #[test]
+    fn select_two() {
+        // NB. I would like to have a test that tests the first one that is
+        // ready is the one that's returned, but that can't be reliably tested
+        // with the randomized behaviour of optimistic_check.
+        do run_in_newsched_task { select_helper(2, [1]) }
+        do run_in_newsched_task { select_helper(2, [0]) }
+        do run_in_newsched_task { select_helper(2, [1,0]) }
+    }
+
+    #[test]
+    fn select_a_lot() {
+        do run_in_newsched_task { select_helper(12, [7,8,9]) }
+    }
+
+    #[test]
+    fn select_stream() {
+        use util;
+        use comm::GenericChan;
+
+        // Sends 10 buffered packets, and uses select to retrieve them all.
+        // Puts the port in a different spot in the vector each time.
+        do run_in_newsched_task {
+            let (ports, _) = unzip(from_fn(10, |_| stream()));
+            let (port, chan) = stream();
+            for 10.times { chan.send(31337); }
+            let mut ports = ports;
+            let mut port = Some(port);
+            let order = [5u,0,4,3,2,6,9,8,7,1];
+            for order.iter().advance |&index| {
+                // put the port in the vector at any index
+                util::swap(port.get_mut_ref(), &mut ports[index]);
+                assert!(select(ports) == index);
+                // get it back out
+                util::swap(port.get_mut_ref(), &mut ports[index]);
+                // NB. Not recv(), because optimistic_check randomly fails.
+                let (data, new_port) = port.take_unwrap().recv_ready().unwrap();
+                assert!(data == 31337);
+                port = Some(new_port);
+            }
+        }
+    }
+
+    #[test]
+    fn select_unkillable() {
+        do run_in_newsched_task {
+            unsafe { do task::unkillable { select_helper(2, [1]) } }
+        }
+    }
+
+    /* blocking select tests */
+
+    #[test]
+    fn select_blocking() {
+        select_blocking_helper(true);
+        select_blocking_helper(false);
+
+        fn select_blocking_helper(killable: bool) {
+            do run_in_newsched_task {
+                let (p1,_c) = oneshot();
+                let (p2,c2) = oneshot();
+                let mut ports = [p1,p2];
+
+                let (p3,c3) = oneshot();
+                let (p4,c4) = oneshot();
+
+                let x = Cell::new((c2, p3, c4));
+                do task::spawn {
+                    let (c2, p3, c4) = x.take();
+                    p3.recv();   // handshake parent
+                    c4.send(()); // normal receive
+                    task::yield();
+                    c2.send(()); // select receive
+                }
+
+                // Try to block before child sends on c2.
+                c3.send(());
+                p4.recv();
+                if killable {
+                    assert!(select(ports) == 1);
+                } else {
+                    unsafe { do task::unkillable { assert!(select(ports) == 1); } }
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn select_racing_senders() {
+        static NUM_CHANS: uint = 10;
+
+        select_racing_senders_helper(true,  ~[0,1,2,3,4,5,6,7,8,9]);
+        select_racing_senders_helper(false, ~[0,1,2,3,4,5,6,7,8,9]);
+        select_racing_senders_helper(true,  ~[0,1,2]);
+        select_racing_senders_helper(false, ~[0,1,2]);
+        select_racing_senders_helper(true,  ~[3,4,5,6]);
+        select_racing_senders_helper(false, ~[3,4,5,6]);
+        select_racing_senders_helper(true,  ~[7,8,9]);
+        select_racing_senders_helper(false, ~[7,8,9]);
+
+        fn select_racing_senders_helper(killable: bool, send_on_chans: ~[uint]) {
+            use uint;
+            use rt::test::spawntask_random;
+
+            do run_in_newsched_task {
+                // A bit of stress, since ordinarily this is just smoke and mirrors.
+                for 4.times {
+                    let send_on_chans = send_on_chans.clone();
+                    do task::spawn {
+                        let mut ports = ~[];
+                        for uint::range(0, NUM_CHANS) |i| {
+                            let (p,c) = oneshot();
+                            ports.push(p);
+                            if send_on_chans.contains(&i) {
+                                let c = Cell::new(c);
+                                do spawntask_random {
+                                    task::yield();
+                                    c.take().send(());
+                                }
+                            }
+                        }
+                        // nondeterministic result, but should succeed
+                        if killable {
+                            select(ports);
+                        } else {
+                            unsafe { do task::unkillable { select(ports); } }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    #[test] #[ignore(cfg(windows))]
+    fn select_killed() {
+        do run_in_newsched_task {
+            let (success_p, success_c) = oneshot::<bool>();
+            let success_c = Cell::new(success_c);
+            do task::try {
+                unsafe {
+                    let success_c = Cell::new(success_c.take());
+                    do task::unkillable {
+                        let (p,c) = oneshot();
+                        let c = Cell::new(c);
+                        do task::spawn {
+                            let (dead_ps, dead_cs) = unzip(from_fn(5, |_| oneshot::<()>()));
+                            let mut ports = dead_ps;
+                            select(ports); // should get killed; nothing should leak
+                            c.take().send(()); // must not happen
+                            // Make sure dead_cs doesn't get closed until after select.
+                            let _ = dead_cs;
+                        }
+                        do task::spawn {
+                            fail!(); // should kill sibling awake
+                        }
+
+                        // wait for killed selector to close (NOT send on) its c.
+                        // hope to send 'true'.
+                        success_c.take().send(p.try_recv().is_none());
+                    }
+                }
+            };
+            assert!(success_p.recv());
+        }
+    }
+}
index 41a179c35b324dd927ec05b2c063415faba1b6a1..16bbd33136cc532be64dc6e3b994e3270064cd91 100644 (file)
@@ -444,6 +444,32 @@ fn exclusive_new_poison() {
         }
     }
 
+    #[test]
+    fn arclike_newN() {
+        // Tests that the many-refcounts-at-once constructors don't leak.
+        let _ = UnsafeAtomicRcBox::new2(~~"hello");
+        let x = UnsafeAtomicRcBox::newN(~~"hello", 0);
+        assert_eq!(x.len(), 0)
+        let x = UnsafeAtomicRcBox::newN(~~"hello", 1);
+        assert_eq!(x.len(), 1)
+        let x = UnsafeAtomicRcBox::newN(~~"hello", 10);
+        assert_eq!(x.len(), 10)
+    }
+
+    #[test]
+    fn arclike_cloneN() {
+        // Tests that the many-refcounts-at-once special-clone doesn't leak.
+        let x = UnsafeAtomicRcBox::new(~~"hello");
+        let x = x.cloneN(0);
+        assert_eq!(x.len(), 0);
+        let x = UnsafeAtomicRcBox::new(~~"hello");
+        let x = x.cloneN(1);
+        assert_eq!(x.len(), 1);
+        let x = UnsafeAtomicRcBox::new(~~"hello");
+        let x = x.cloneN(10);
+        assert_eq!(x.len(), 10);
+    }
+
     #[test]
     fn arclike_unwrap_basic() {
         unsafe {