about summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/sgx/abi/tls.rs116
-rw-r--r--library/std/src/sys/sgx/abi/tls/sync_bitset.rs85
-rw-r--r--library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs25
-rw-r--r--library/std/src/sys/sgx/rwlock.rs50
-rw-r--r--library/std/src/sys/sgx/rwlock/tests.rs43
-rw-r--r--library/std/src/sys/sgx/waitqueue.rs398
-rw-r--r--library/std/src/sys/sgx/waitqueue/spin_mutex.rs76
-rw-r--r--library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs23
-rw-r--r--library/std/src/sys/sgx/waitqueue/tests.rs20
-rw-r--r--library/std/src/sys/sgx/waitqueue/unsafe_list.rs146
-rw-r--r--library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs103
-rw-r--r--library/std/src/sys/unix/ext/net.rs382
-rw-r--r--library/std/src/sys/unix/ext/net/tests.rs374
-rw-r--r--library/std/src/sys/unix/fd.rs16
-rw-r--r--library/std/src/sys/unix/fd/tests.rs9
-rw-r--r--library/std/src/sys/unix/os.rs30
-rw-r--r--library/std/src/sys/unix/os/tests.rs23
-rw-r--r--library/std/src/sys/unix/process/process_common.rs71
-rw-r--r--library/std/src/sys/unix/process/process_common/tests.rs64
-rw-r--r--library/std/src/sys/vxworks/net.rs30
-rw-r--r--library/std/src/sys/vxworks/net/tests.rs23
-rw-r--r--library/std/src/sys/windows/args.rs69
-rw-r--r--library/std/src/sys/windows/args/tests.rs61
-rw-r--r--library/std/src/sys/windows/os.rs20
-rw-r--r--library/std/src/sys/windows/os/tests.rs13
-rw-r--r--library/std/src/sys/windows/process.rs41
-rw-r--r--library/std/src/sys/windows/process/tests.rs31
27 files changed, 1160 insertions, 1182 deletions
diff --git a/library/std/src/sys/sgx/abi/tls.rs b/library/std/src/sys/sgx/abi/tls.rs
index 2b0485c4f03..0d8952b2f27 100644
--- a/library/std/src/sys/sgx/abi/tls.rs
+++ b/library/std/src/sys/sgx/abi/tls.rs
@@ -1,3 +1,5 @@
+mod sync_bitset;
+
 use self::sync_bitset::*;
 use crate::cell::Cell;
 use crate::mem;
@@ -125,117 +127,3 @@ impl Tls {
         TLS_KEY_IN_USE.clear(key.to_index());
     }
 }
-
-mod sync_bitset {
-    use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS};
-    use crate::iter::{Enumerate, Peekable};
-    use crate::slice::Iter;
-    use crate::sync::atomic::{AtomicUsize, Ordering};
-
-    /// A bitset that can be used synchronously.
-    pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]);
-
-    pub(super) const SYNC_BITSET_INIT: SyncBitset =
-        SyncBitset([AtomicUsize::new(0), AtomicUsize::new(0)]);
-
-    impl SyncBitset {
-        pub fn get(&self, index: usize) -> bool {
-            let (hi, lo) = Self::split(index);
-            (self.0[hi].load(Ordering::Relaxed) & lo) != 0
-        }
-
-        /// Not atomic.
-        pub fn iter(&self) -> SyncBitsetIter<'_> {
-            SyncBitsetIter { iter: self.0.iter().enumerate().peekable(), elem_idx: 0 }
-        }
-
-        pub fn clear(&self, index: usize) {
-            let (hi, lo) = Self::split(index);
-            self.0[hi].fetch_and(!lo, Ordering::Relaxed);
-        }
-
-        /// Sets any unset bit. Not atomic. Returns `None` if all bits were
-        /// observed to be set.
-        pub fn set(&self) -> Option<usize> {
-            'elems: for (idx, elem) in self.0.iter().enumerate() {
-                let mut current = elem.load(Ordering::Relaxed);
-                loop {
-                    if 0 == !current {
-                        continue 'elems;
-                    }
-                    let trailing_ones = (!current).trailing_zeros() as usize;
-                    match elem.compare_exchange(
-                        current,
-                        current | (1 << trailing_ones),
-                        Ordering::AcqRel,
-                        Ordering::Relaxed,
-                    ) {
-                        Ok(_) => return Some(idx * USIZE_BITS + trailing_ones),
-                        Err(previous) => current = previous,
-                    }
-                }
-            }
-            None
-        }
-
-        fn split(index: usize) -> (usize, usize) {
-            (index / USIZE_BITS, 1 << (index % USIZE_BITS))
-        }
-    }
-
-    pub(super) struct SyncBitsetIter<'a> {
-        iter: Peekable<Enumerate<Iter<'a, AtomicUsize>>>,
-        elem_idx: usize,
-    }
-
-    impl<'a> Iterator for SyncBitsetIter<'a> {
-        type Item = usize;
-
-        fn next(&mut self) -> Option<usize> {
-            self.iter.peek().cloned().and_then(|(idx, elem)| {
-                let elem = elem.load(Ordering::Relaxed);
-                let low_mask = (1 << self.elem_idx) - 1;
-                let next = elem & !low_mask;
-                let next_idx = next.trailing_zeros() as usize;
-                self.elem_idx = next_idx + 1;
-                if self.elem_idx >= 64 {
-                    self.elem_idx = 0;
-                    self.iter.next();
-                }
-                match next_idx {
-                    64 => self.next(),
-                    _ => Some(idx * USIZE_BITS + next_idx),
-                }
-            })
-        }
-    }
-
-    #[cfg(test)]
-    mod tests {
-        use super::*;
-
-        fn test_data(bitset: [usize; 2], bit_indices: &[usize]) {
-            let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]);
-            assert_eq!(set.iter().collect::<Vec<_>>(), bit_indices);
-            for &i in bit_indices {
-                assert!(set.get(i));
-            }
-        }
-
-        #[test]
-        fn iter() {
-            test_data([0b0110_1001, 0], &[0, 3, 5, 6]);
-            test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]);
-            test_data([0, 0], &[]);
-        }
-
-        #[test]
-        fn set_get_clear() {
-            let set = SYNC_BITSET_INIT;
-            let key = set.set().unwrap();
-            assert!(set.get(key));
-            set.clear(key);
-            assert!(!set.get(key));
-        }
-    }
-}
diff --git a/library/std/src/sys/sgx/abi/tls/sync_bitset.rs b/library/std/src/sys/sgx/abi/tls/sync_bitset.rs
new file mode 100644
index 00000000000..4eeff8f6ef7
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/tls/sync_bitset.rs
@@ -0,0 +1,85 @@
+#[cfg(test)]
+mod tests;
+
+use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS};
+use crate::iter::{Enumerate, Peekable};
+use crate::slice::Iter;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+
+/// A bitset that can be used synchronously.
+pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]);
+
+pub(super) const SYNC_BITSET_INIT: SyncBitset =
+    SyncBitset([AtomicUsize::new(0), AtomicUsize::new(0)]);
+
+impl SyncBitset {
+    pub fn get(&self, index: usize) -> bool {
+        let (hi, lo) = Self::split(index);
+        (self.0[hi].load(Ordering::Relaxed) & lo) != 0
+    }
+
+    /// Not atomic.
+    pub fn iter(&self) -> SyncBitsetIter<'_> {
+        SyncBitsetIter { iter: self.0.iter().enumerate().peekable(), elem_idx: 0 }
+    }
+
+    pub fn clear(&self, index: usize) {
+        let (hi, lo) = Self::split(index);
+        self.0[hi].fetch_and(!lo, Ordering::Relaxed);
+    }
+
+    /// Sets any unset bit. Not atomic. Returns `None` if all bits were
+    /// observed to be set.
+    pub fn set(&self) -> Option<usize> {
+        'elems: for (idx, elem) in self.0.iter().enumerate() {
+            let mut current = elem.load(Ordering::Relaxed);
+            loop {
+                if 0 == !current {
+                    continue 'elems;
+                }
+                let trailing_ones = (!current).trailing_zeros() as usize;
+                match elem.compare_exchange(
+                    current,
+                    current | (1 << trailing_ones),
+                    Ordering::AcqRel,
+                    Ordering::Relaxed,
+                ) {
+                    Ok(_) => return Some(idx * USIZE_BITS + trailing_ones),
+                    Err(previous) => current = previous,
+                }
+            }
+        }
+        None
+    }
+
+    fn split(index: usize) -> (usize, usize) {
+        (index / USIZE_BITS, 1 << (index % USIZE_BITS))
+    }
+}
+
+pub(super) struct SyncBitsetIter<'a> {
+    iter: Peekable<Enumerate<Iter<'a, AtomicUsize>>>,
+    elem_idx: usize,
+}
+
+impl<'a> Iterator for SyncBitsetIter<'a> {
+    type Item = usize;
+
+    fn next(&mut self) -> Option<usize> {
+        self.iter.peek().cloned().and_then(|(idx, elem)| {
+            let elem = elem.load(Ordering::Relaxed);
+            let low_mask = (1 << self.elem_idx) - 1;
+            let next = elem & !low_mask;
+            let next_idx = next.trailing_zeros() as usize;
+            self.elem_idx = next_idx + 1;
+            if self.elem_idx >= 64 {
+                self.elem_idx = 0;
+                self.iter.next();
+            }
+            match next_idx {
+                64 => self.next(),
+                _ => Some(idx * USIZE_BITS + next_idx),
+            }
+        })
+    }
+}
diff --git a/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs b/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs
new file mode 100644
index 00000000000..d7eb2e139d0
--- /dev/null
+++ b/library/std/src/sys/sgx/abi/tls/sync_bitset/tests.rs
@@ -0,0 +1,25 @@
+use super::*;
+
+fn test_data(bitset: [usize; 2], bit_indices: &[usize]) {
+    let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]);
+    assert_eq!(set.iter().collect::<Vec<_>>(), bit_indices);
+    for &i in bit_indices {
+        assert!(set.get(i));
+    }
+}
+
+#[test]
+fn iter() {
+    test_data([0b0110_1001, 0], &[0, 3, 5, 6]);
+    test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]);
+    test_data([0, 0], &[]);
+}
+
+#[test]
+fn set_get_clear() {
+    let set = SYNC_BITSET_INIT;
+    let key = set.set().unwrap();
+    assert!(set.get(key));
+    set.clear(key);
+    assert!(!set.get(key));
+}
diff --git a/library/std/src/sys/sgx/rwlock.rs b/library/std/src/sys/sgx/rwlock.rs
index 722b4f5e0ba..3bf2a7d8fb4 100644
--- a/library/std/src/sys/sgx/rwlock.rs
+++ b/library/std/src/sys/sgx/rwlock.rs
@@ -1,3 +1,6 @@
+#[cfg(test)]
+mod tests;
+
 use crate::num::NonZeroUsize;
 
 use super::waitqueue::{
@@ -198,50 +201,3 @@ pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 {
     (*p).unlock();
     return 0;
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::mem::{self, MaybeUninit};
-    use core::array::FixedSizeArray;
-
-    // Verify that the bytes of initialized RWLock are the same as in
-    // libunwind. If they change, `src/UnwindRustSgx.h` in libunwind needs to
-    // be changed too.
-    #[test]
-    fn test_c_rwlock_initializer() {
-        #[rustfmt::skip]
-        const RWLOCK_INIT: &[u8] = &[
-            /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-        ];
-
-        #[inline(never)]
-        fn zero_stack() {
-            test::black_box(MaybeUninit::<[RWLock; 16]>::zeroed());
-        }
-
-        #[inline(never)]
-        unsafe fn rwlock_new(init: &mut MaybeUninit<RWLock>) {
-            init.write(RWLock::new());
-        }
-
-        unsafe {
-            // try hard to make sure that the padding/unused bytes in RWLock
-            // get initialized as 0. If the assertion below fails, that might
-            // just be an issue with the test code and not with the value of
-            // RWLOCK_INIT.
-            zero_stack();
-            let mut init = MaybeUninit::<RWLock>::zeroed();
-            rwlock_new(&mut init);
-            assert_eq!(mem::transmute::<_, [u8; 144]>(init.assume_init()).as_slice(), RWLOCK_INIT)
-        };
-    }
-}
diff --git a/library/std/src/sys/sgx/rwlock/tests.rs b/library/std/src/sys/sgx/rwlock/tests.rs
new file mode 100644
index 00000000000..05b36c633f8
--- /dev/null
+++ b/library/std/src/sys/sgx/rwlock/tests.rs
@@ -0,0 +1,43 @@
+use super::*;
+use crate::mem::{self, MaybeUninit};
+use core::array::FixedSizeArray;
+
+// Verify that the bytes of initialized RWLock are the same as in
+// libunwind. If they change, `src/UnwindRustSgx.h` in libunwind needs to
+// be changed too.
+#[test]
+fn test_c_rwlock_initializer() {
+    #[rustfmt::skip]
+    const RWLOCK_INIT: &[u8] = &[
+        /* 0x00 */ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        /* 0x10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        /* 0x20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        /* 0x30 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        /* 0x40 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        /* 0x50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        /* 0x60 */ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        /* 0x70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        /* 0x80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    ];
+
+    #[inline(never)]
+    fn zero_stack() {
+        test::black_box(MaybeUninit::<[RWLock; 16]>::zeroed());
+    }
+
+    #[inline(never)]
+    unsafe fn rwlock_new(init: &mut MaybeUninit<RWLock>) {
+        init.write(RWLock::new());
+    }
+
+    unsafe {
+        // try hard to make sure that the padding/unused bytes in RWLock
+        // get initialized as 0. If the assertion below fails, that might
+        // just be an issue with the test code and not with the value of
+        // RWLOCK_INIT.
+        zero_stack();
+        let mut init = MaybeUninit::<RWLock>::zeroed();
+        rwlock_new(&mut init);
+        assert_eq!(mem::transmute::<_, [u8; 144]>(init.assume_init()).as_slice(), RWLOCK_INIT)
+    };
+}
diff --git a/library/std/src/sys/sgx/waitqueue.rs b/library/std/src/sys/sgx/waitqueue.rs
index 070afa55f30..e464dc3ee9d 100644
--- a/library/std/src/sys/sgx/waitqueue.rs
+++ b/library/std/src/sys/sgx/waitqueue.rs
@@ -9,6 +9,18 @@
 //! Since userspace may send spurious wake-ups, the wakeup event state is
 //! recorded in the enclave. The wakeup event state is protected by a spinlock.
 //! The queue and associated wait state are stored in a `WaitVariable`.
+
+#[cfg(test)]
+mod tests;
+
+/// A doubly-linked list where callers are in charge of memory allocation
+/// of the nodes in the list.
+mod unsafe_list;
+
+/// Trivial spinlock-based implementation of `sync::Mutex`.
+// FIXME: Perhaps use Intel TSX to avoid locking?
+mod spin_mutex;
+
 use crate::num::NonZeroUsize;
 use crate::ops::{Deref, DerefMut};
 use crate::time::Duration;
@@ -231,389 +243,3 @@ impl WaitQueue {
         }
     }
 }
-
-/// A doubly-linked list where callers are in charge of memory allocation
-/// of the nodes in the list.
-mod unsafe_list {
-    use crate::mem;
-    use crate::ptr::NonNull;
-
-    pub struct UnsafeListEntry<T> {
-        next: NonNull<UnsafeListEntry<T>>,
-        prev: NonNull<UnsafeListEntry<T>>,
-        value: Option<T>,
-    }
-
-    impl<T> UnsafeListEntry<T> {
-        fn dummy() -> Self {
-            UnsafeListEntry { next: NonNull::dangling(), prev: NonNull::dangling(), value: None }
-        }
-
-        pub fn new(value: T) -> Self {
-            UnsafeListEntry { value: Some(value), ..Self::dummy() }
-        }
-    }
-
-    pub struct UnsafeList<T> {
-        head_tail: NonNull<UnsafeListEntry<T>>,
-        head_tail_entry: Option<UnsafeListEntry<T>>,
-    }
-
-    impl<T> UnsafeList<T> {
-        pub const fn new() -> Self {
-            unsafe {
-                UnsafeList { head_tail: NonNull::new_unchecked(1 as _), head_tail_entry: None }
-            }
-        }
-
-        unsafe fn init(&mut self) {
-            if self.head_tail_entry.is_none() {
-                self.head_tail_entry = Some(UnsafeListEntry::dummy());
-                self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap());
-                self.head_tail.as_mut().next = self.head_tail;
-                self.head_tail.as_mut().prev = self.head_tail;
-            }
-        }
-
-        pub fn is_empty(&self) -> bool {
-            unsafe {
-                if self.head_tail_entry.is_some() {
-                    let first = self.head_tail.as_ref().next;
-                    if first == self.head_tail {
-                        // ,-------> /---------\ next ---,
-                        // |         |head_tail|         |
-                        // `--- prev \---------/ <-------`
-                        rtassert!(self.head_tail.as_ref().prev == first);
-                        true
-                    } else {
-                        false
-                    }
-                } else {
-                    true
-                }
-            }
-        }
-
-        /// Pushes an entry onto the back of the list.
-        ///
-        /// # Safety
-        ///
-        /// The entry must remain allocated until the entry is removed from the
-        /// list AND the caller who popped is done using the entry. Special
-        /// care must be taken in the caller of `push` to ensure unwinding does
-        /// not destroy the stack frame containing the entry.
-        pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry<T>) -> &'a T {
-            self.init();
-
-            // BEFORE:
-            //     /---------\ next ---> /---------\
-            // ... |prev_tail|           |head_tail| ...
-            //     \---------/ <--- prev \---------/
-            //
-            // AFTER:
-            //     /---------\ next ---> /-----\ next ---> /---------\
-            // ... |prev_tail|           |entry|           |head_tail| ...
-            //     \---------/ <--- prev \-----/ <--- prev \---------/
-            let mut entry = NonNull::new_unchecked(entry);
-            let mut prev_tail = mem::replace(&mut self.head_tail.as_mut().prev, entry);
-            entry.as_mut().prev = prev_tail;
-            entry.as_mut().next = self.head_tail;
-            prev_tail.as_mut().next = entry;
-            // unwrap ok: always `Some` on non-dummy entries
-            (*entry.as_ptr()).value.as_ref().unwrap()
-        }
-
-        /// Pops an entry from the front of the list.
-        ///
-        /// # Safety
-        ///
-        /// The caller must make sure to synchronize ending the borrow of the
-        /// return value and deallocation of the containing entry.
-        pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> {
-            self.init();
-
-            if self.is_empty() {
-                None
-            } else {
-                // BEFORE:
-                //     /---------\ next ---> /-----\ next ---> /------\
-                // ... |head_tail|           |first|           |second| ...
-                //     \---------/ <--- prev \-----/ <--- prev \------/
-                //
-                // AFTER:
-                //     /---------\ next ---> /------\
-                // ... |head_tail|           |second| ...
-                //     \---------/ <--- prev \------/
-                let mut first = self.head_tail.as_mut().next;
-                let mut second = first.as_mut().next;
-                self.head_tail.as_mut().next = second;
-                second.as_mut().prev = self.head_tail;
-                first.as_mut().next = NonNull::dangling();
-                first.as_mut().prev = NonNull::dangling();
-                // unwrap ok: always `Some` on non-dummy entries
-                Some((*first.as_ptr()).value.as_ref().unwrap())
-            }
-        }
-
-        /// Removes an entry from the list.
-        ///
-        /// # Safety
-        ///
-        /// The caller must ensure that `entry` has been pushed onto `self`
-        /// prior to this call and has not moved since then.
-        pub unsafe fn remove(&mut self, entry: &mut UnsafeListEntry<T>) {
-            rtassert!(!self.is_empty());
-            // BEFORE:
-            //     /----\ next ---> /-----\ next ---> /----\
-            // ... |prev|           |entry|           |next| ...
-            //     \----/ <--- prev \-----/ <--- prev \----/
-            //
-            // AFTER:
-            //     /----\ next ---> /----\
-            // ... |prev|           |next| ...
-            //     \----/ <--- prev \----/
-            let mut prev = entry.prev;
-            let mut next = entry.next;
-            prev.as_mut().next = next;
-            next.as_mut().prev = prev;
-            entry.next = NonNull::dangling();
-            entry.prev = NonNull::dangling();
-        }
-    }
-
-    #[cfg(test)]
-    mod tests {
-        use super::*;
-        use crate::cell::Cell;
-
-        unsafe fn assert_empty<T>(list: &mut UnsafeList<T>) {
-            assert!(list.pop().is_none(), "assertion failed: list is not empty");
-        }
-
-        #[test]
-        fn init_empty() {
-            unsafe {
-                assert_empty(&mut UnsafeList::<i32>::new());
-            }
-        }
-
-        #[test]
-        fn push_pop() {
-            unsafe {
-                let mut node = UnsafeListEntry::new(1234);
-                let mut list = UnsafeList::new();
-                assert_eq!(list.push(&mut node), &1234);
-                assert_eq!(list.pop().unwrap(), &1234);
-                assert_empty(&mut list);
-            }
-        }
-
-        #[test]
-        fn push_remove() {
-            unsafe {
-                let mut node = UnsafeListEntry::new(1234);
-                let mut list = UnsafeList::new();
-                assert_eq!(list.push(&mut node), &1234);
-                list.remove(&mut node);
-                assert_empty(&mut list);
-            }
-        }
-
-        #[test]
-        fn push_remove_pop() {
-            unsafe {
-                let mut node1 = UnsafeListEntry::new(11);
-                let mut node2 = UnsafeListEntry::new(12);
-                let mut node3 = UnsafeListEntry::new(13);
-                let mut node4 = UnsafeListEntry::new(14);
-                let mut node5 = UnsafeListEntry::new(15);
-                let mut list = UnsafeList::new();
-                assert_eq!(list.push(&mut node1), &11);
-                assert_eq!(list.push(&mut node2), &12);
-                assert_eq!(list.push(&mut node3), &13);
-                assert_eq!(list.push(&mut node4), &14);
-                assert_eq!(list.push(&mut node5), &15);
-
-                list.remove(&mut node1);
-                assert_eq!(list.pop().unwrap(), &12);
-                list.remove(&mut node3);
-                assert_eq!(list.pop().unwrap(), &14);
-                list.remove(&mut node5);
-                assert_empty(&mut list);
-
-                assert_eq!(list.push(&mut node1), &11);
-                assert_eq!(list.pop().unwrap(), &11);
-                assert_empty(&mut list);
-
-                assert_eq!(list.push(&mut node3), &13);
-                assert_eq!(list.push(&mut node4), &14);
-                list.remove(&mut node3);
-                list.remove(&mut node4);
-                assert_empty(&mut list);
-            }
-        }
-
-        #[test]
-        fn complex_pushes_pops() {
-            unsafe {
-                let mut node1 = UnsafeListEntry::new(1234);
-                let mut node2 = UnsafeListEntry::new(4567);
-                let mut node3 = UnsafeListEntry::new(9999);
-                let mut node4 = UnsafeListEntry::new(8642);
-                let mut list = UnsafeList::new();
-                list.push(&mut node1);
-                list.push(&mut node2);
-                assert_eq!(list.pop().unwrap(), &1234);
-                list.push(&mut node3);
-                assert_eq!(list.pop().unwrap(), &4567);
-                assert_eq!(list.pop().unwrap(), &9999);
-                assert_empty(&mut list);
-                list.push(&mut node4);
-                assert_eq!(list.pop().unwrap(), &8642);
-                assert_empty(&mut list);
-            }
-        }
-
-        #[test]
-        fn cell() {
-            unsafe {
-                let mut node = UnsafeListEntry::new(Cell::new(0));
-                let mut list = UnsafeList::new();
-                let noderef = list.push(&mut node);
-                assert_eq!(noderef.get(), 0);
-                list.pop().unwrap().set(1);
-                assert_empty(&mut list);
-                assert_eq!(noderef.get(), 1);
-            }
-        }
-    }
-}
-
-/// Trivial spinlock-based implementation of `sync::Mutex`.
-// FIXME: Perhaps use Intel TSX to avoid locking?
-mod spin_mutex {
-    use crate::cell::UnsafeCell;
-    use crate::ops::{Deref, DerefMut};
-    use crate::sync::atomic::{spin_loop_hint, AtomicBool, Ordering};
-
-    #[derive(Default)]
-    pub struct SpinMutex<T> {
-        value: UnsafeCell<T>,
-        lock: AtomicBool,
-    }
-
-    unsafe impl<T: Send> Send for SpinMutex<T> {}
-    unsafe impl<T: Send> Sync for SpinMutex<T> {}
-
-    pub struct SpinMutexGuard<'a, T: 'a> {
-        mutex: &'a SpinMutex<T>,
-    }
-
-    impl<'a, T> !Send for SpinMutexGuard<'a, T> {}
-    unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {}
-
-    impl<T> SpinMutex<T> {
-        pub const fn new(value: T) -> Self {
-            SpinMutex { value: UnsafeCell::new(value), lock: AtomicBool::new(false) }
-        }
-
-        #[inline(always)]
-        pub fn lock(&self) -> SpinMutexGuard<'_, T> {
-            loop {
-                match self.try_lock() {
-                    None => {
-                        while self.lock.load(Ordering::Relaxed) {
-                            spin_loop_hint()
-                        }
-                    }
-                    Some(guard) => return guard,
-                }
-            }
-        }
-
-        #[inline(always)]
-        pub fn try_lock(&self) -> Option<SpinMutexGuard<'_, T>> {
-            if !self.lock.compare_and_swap(false, true, Ordering::Acquire) {
-                Some(SpinMutexGuard { mutex: self })
-            } else {
-                None
-            }
-        }
-    }
-
-    /// Lock the Mutex or return false.
-    pub macro try_lock_or_false($e:expr) {
-        if let Some(v) = $e.try_lock() { v } else { return false }
-    }
-
-    impl<'a, T> Deref for SpinMutexGuard<'a, T> {
-        type Target = T;
-
-        fn deref(&self) -> &T {
-            unsafe { &*self.mutex.value.get() }
-        }
-    }
-
-    impl<'a, T> DerefMut for SpinMutexGuard<'a, T> {
-        fn deref_mut(&mut self) -> &mut T {
-            unsafe { &mut *self.mutex.value.get() }
-        }
-    }
-
-    impl<'a, T> Drop for SpinMutexGuard<'a, T> {
-        fn drop(&mut self) {
-            self.mutex.lock.store(false, Ordering::Release)
-        }
-    }
-
-    #[cfg(test)]
-    mod tests {
-        #![allow(deprecated)]
-
-        use super::*;
-        use crate::sync::Arc;
-        use crate::thread;
-        use crate::time::Duration;
-
-        #[test]
-        fn sleep() {
-            let mutex = Arc::new(SpinMutex::<i32>::default());
-            let mutex2 = mutex.clone();
-            let guard = mutex.lock();
-            let t1 = thread::spawn(move || {
-                *mutex2.lock() = 1;
-            });
-
-            thread::sleep(Duration::from_millis(50));
-
-            assert_eq!(*guard, 0);
-            drop(guard);
-            t1.join().unwrap();
-            assert_eq!(*mutex.lock(), 1);
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::sync::Arc;
-    use crate::thread;
-
-    #[test]
-    fn queue() {
-        let wq = Arc::new(SpinMutex::<WaitVariable<()>>::default());
-        let wq2 = wq.clone();
-
-        let locked = wq.lock();
-
-        let t1 = thread::spawn(move || {
-            // if we obtain the lock, the main thread should be waiting
-            assert!(WaitQueue::notify_one(wq2.lock()).is_ok());
-        });
-
-        WaitQueue::wait(locked, || {});
-
-        t1.join().unwrap();
-    }
-}
diff --git a/library/std/src/sys/sgx/waitqueue/spin_mutex.rs b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs
new file mode 100644
index 00000000000..d99ce895da5
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs
@@ -0,0 +1,76 @@
+#[cfg(test)]
+mod tests;
+
+use crate::cell::UnsafeCell;
+use crate::ops::{Deref, DerefMut};
+use crate::sync::atomic::{spin_loop_hint, AtomicBool, Ordering};
+
+#[derive(Default)]
+pub struct SpinMutex<T> {
+    value: UnsafeCell<T>,
+    lock: AtomicBool,
+}
+
+unsafe impl<T: Send> Send for SpinMutex<T> {}
+unsafe impl<T: Send> Sync for SpinMutex<T> {}
+
+pub struct SpinMutexGuard<'a, T: 'a> {
+    mutex: &'a SpinMutex<T>,
+}
+
+impl<'a, T> !Send for SpinMutexGuard<'a, T> {}
+unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {}
+
+impl<T> SpinMutex<T> {
+    pub const fn new(value: T) -> Self {
+        SpinMutex { value: UnsafeCell::new(value), lock: AtomicBool::new(false) }
+    }
+
+    #[inline(always)]
+    pub fn lock(&self) -> SpinMutexGuard<'_, T> {
+        loop {
+            match self.try_lock() {
+                None => {
+                    while self.lock.load(Ordering::Relaxed) {
+                        spin_loop_hint()
+                    }
+                }
+                Some(guard) => return guard,
+            }
+        }
+    }
+
+    #[inline(always)]
+    pub fn try_lock(&self) -> Option<SpinMutexGuard<'_, T>> {
+        if !self.lock.compare_and_swap(false, true, Ordering::Acquire) {
+            Some(SpinMutexGuard { mutex: self })
+        } else {
+            None
+        }
+    }
+}
+
+/// Lock the Mutex or return false.
+pub macro try_lock_or_false($e:expr) {
+    if let Some(v) = $e.try_lock() { v } else { return false }
+}
+
+impl<'a, T> Deref for SpinMutexGuard<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe { &*self.mutex.value.get() }
+    }
+}
+
+impl<'a, T> DerefMut for SpinMutexGuard<'a, T> {
+    fn deref_mut(&mut self) -> &mut T {
+        unsafe { &mut *self.mutex.value.get() }
+    }
+}
+
+impl<'a, T> Drop for SpinMutexGuard<'a, T> {
+    fn drop(&mut self) {
+        self.mutex.lock.store(false, Ordering::Release)
+    }
+}
diff --git a/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs b/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs
new file mode 100644
index 00000000000..4c5994bea61
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/spin_mutex/tests.rs
@@ -0,0 +1,23 @@
+#![allow(deprecated)]
+
+use super::*;
+use crate::sync::Arc;
+use crate::thread;
+use crate::time::Duration;
+
+#[test]
+fn sleep() {
+    let mutex = Arc::new(SpinMutex::<i32>::default());
+    let mutex2 = mutex.clone();
+    let guard = mutex.lock();
+    let t1 = thread::spawn(move || {
+        *mutex2.lock() = 1;
+    });
+
+    thread::sleep(Duration::from_millis(50));
+
+    assert_eq!(*guard, 0);
+    drop(guard);
+    t1.join().unwrap();
+    assert_eq!(*mutex.lock(), 1);
+}
diff --git a/library/std/src/sys/sgx/waitqueue/tests.rs b/library/std/src/sys/sgx/waitqueue/tests.rs
new file mode 100644
index 00000000000..bf91fdd08ed
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/tests.rs
@@ -0,0 +1,20 @@
+use super::*;
+use crate::sync::Arc;
+use crate::thread;
+
+#[test]
+fn queue() {
+    let wq = Arc::new(SpinMutex::<WaitVariable<()>>::default());
+    let wq2 = wq.clone();
+
+    let locked = wq.lock();
+
+    let t1 = thread::spawn(move || {
+        // if we obtain the lock, the main thread should be waiting
+        assert!(WaitQueue::notify_one(wq2.lock()).is_ok());
+    });
+
+    WaitQueue::wait(locked, || {});
+
+    t1.join().unwrap();
+}
diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs
new file mode 100644
index 00000000000..7a246542739
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs
@@ -0,0 +1,146 @@
+#[cfg(test)]
+mod tests;
+
+use crate::mem;
+use crate::ptr::NonNull;
+
+pub struct UnsafeListEntry<T> {
+    next: NonNull<UnsafeListEntry<T>>,
+    prev: NonNull<UnsafeListEntry<T>>,
+    value: Option<T>,
+}
+
+impl<T> UnsafeListEntry<T> {
+    fn dummy() -> Self {
+        UnsafeListEntry { next: NonNull::dangling(), prev: NonNull::dangling(), value: None }
+    }
+
+    pub fn new(value: T) -> Self {
+        UnsafeListEntry { value: Some(value), ..Self::dummy() }
+    }
+}
+
+pub struct UnsafeList<T> {
+    head_tail: NonNull<UnsafeListEntry<T>>,
+    head_tail_entry: Option<UnsafeListEntry<T>>,
+}
+
+impl<T> UnsafeList<T> {
+    pub const fn new() -> Self {
+        unsafe { UnsafeList { head_tail: NonNull::new_unchecked(1 as _), head_tail_entry: None } }
+    }
+
+    unsafe fn init(&mut self) {
+        if self.head_tail_entry.is_none() {
+            self.head_tail_entry = Some(UnsafeListEntry::dummy());
+            self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap());
+            self.head_tail.as_mut().next = self.head_tail;
+            self.head_tail.as_mut().prev = self.head_tail;
+        }
+    }
+
+    pub fn is_empty(&self) -> bool {
+        unsafe {
+            if self.head_tail_entry.is_some() {
+                let first = self.head_tail.as_ref().next;
+                if first == self.head_tail {
+                    // ,-------> /---------\ next ---,
+                    // |         |head_tail|         |
+                    // `--- prev \---------/ <-------`
+                    rtassert!(self.head_tail.as_ref().prev == first);
+                    true
+                } else {
+                    false
+                }
+            } else {
+                true
+            }
+        }
+    }
+
+    /// Pushes an entry onto the back of the list.
+    ///
+    /// # Safety
+    ///
+    /// The entry must remain allocated until the entry is removed from the
+    /// list AND the caller who popped is done using the entry. Special
+    /// care must be taken in the caller of `push` to ensure unwinding does
+    /// not destroy the stack frame containing the entry.
+    pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry<T>) -> &'a T {
+        self.init();
+
+        // BEFORE:
+        //     /---------\ next ---> /---------\
+        // ... |prev_tail|           |head_tail| ...
+        //     \---------/ <--- prev \---------/
+        //
+        // AFTER:
+        //     /---------\ next ---> /-----\ next ---> /---------\
+        // ... |prev_tail|           |entry|           |head_tail| ...
+        //     \---------/ <--- prev \-----/ <--- prev \---------/
+        let mut entry = NonNull::new_unchecked(entry);
+        let mut prev_tail = mem::replace(&mut self.head_tail.as_mut().prev, entry);
+        entry.as_mut().prev = prev_tail;
+        entry.as_mut().next = self.head_tail;
+        prev_tail.as_mut().next = entry;
+        // unwrap ok: always `Some` on non-dummy entries
+        (*entry.as_ptr()).value.as_ref().unwrap()
+    }
+
+    /// Pops an entry from the front of the list.
+    ///
+    /// # Safety
+    ///
+    /// The caller must make sure to synchronize ending the borrow of the
+    /// return value and deallocation of the containing entry.
+    pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> {
+        self.init();
+
+        if self.is_empty() {
+            None
+        } else {
+            // BEFORE:
+            //     /---------\ next ---> /-----\ next ---> /------\
+            // ... |head_tail|           |first|           |second| ...
+            //     \---------/ <--- prev \-----/ <--- prev \------/
+            //
+            // AFTER:
+            //     /---------\ next ---> /------\
+            // ... |head_tail|           |second| ...
+            //     \---------/ <--- prev \------/
+            let mut first = self.head_tail.as_mut().next;
+            let mut second = first.as_mut().next;
+            self.head_tail.as_mut().next = second;
+            second.as_mut().prev = self.head_tail;
+            first.as_mut().next = NonNull::dangling();
+            first.as_mut().prev = NonNull::dangling();
+            // unwrap ok: always `Some` on non-dummy entries
+            Some((*first.as_ptr()).value.as_ref().unwrap())
+        }
+    }
+
+    /// Removes an entry from the list.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `entry` has been pushed onto `self`
+    /// prior to this call and has not moved since then.
+    pub unsafe fn remove(&mut self, entry: &mut UnsafeListEntry<T>) {
+        rtassert!(!self.is_empty());
+        // BEFORE:
+        //     /----\ next ---> /-----\ next ---> /----\
+        // ... |prev|           |entry|           |next| ...
+        //     \----/ <--- prev \-----/ <--- prev \----/
+        //
+        // AFTER:
+        //     /----\ next ---> /----\
+        // ... |prev|           |next| ...
+        //     \----/ <--- prev \----/
+        let mut prev = entry.prev;
+        let mut next = entry.next;
+        prev.as_mut().next = next;
+        next.as_mut().prev = prev;
+        entry.next = NonNull::dangling();
+        entry.prev = NonNull::dangling();
+    }
+}
diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs
new file mode 100644
index 00000000000..1f031ed1959
--- /dev/null
+++ b/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs
@@ -0,0 +1,103 @@
+use super::*;
+use crate::cell::Cell;
+
+unsafe fn assert_empty<T>(list: &mut UnsafeList<T>) {
+    assert!(list.pop().is_none(), "assertion failed: list is not empty");
+}
+
+#[test]
+fn init_empty() {
+    unsafe {
+        assert_empty(&mut UnsafeList::<i32>::new());
+    }
+}
+
+#[test]
+fn push_pop() {
+    unsafe {
+        let mut node = UnsafeListEntry::new(1234);
+        let mut list = UnsafeList::new();
+        assert_eq!(list.push(&mut node), &1234);
+        assert_eq!(list.pop().unwrap(), &1234);
+        assert_empty(&mut list);
+    }
+}
+
+#[test]
+fn push_remove() {
+    unsafe {
+        let mut node = UnsafeListEntry::new(1234);
+        let mut list = UnsafeList::new();
+        assert_eq!(list.push(&mut node), &1234);
+        list.remove(&mut node);
+        assert_empty(&mut list);
+    }
+}
+
+#[test]
+fn push_remove_pop() {
+    unsafe {
+        let mut node1 = UnsafeListEntry::new(11);
+        let mut node2 = UnsafeListEntry::new(12);
+        let mut node3 = UnsafeListEntry::new(13);
+        let mut node4 = UnsafeListEntry::new(14);
+        let mut node5 = UnsafeListEntry::new(15);
+        let mut list = UnsafeList::new();
+        assert_eq!(list.push(&mut node1), &11);
+        assert_eq!(list.push(&mut node2), &12);
+        assert_eq!(list.push(&mut node3), &13);
+        assert_eq!(list.push(&mut node4), &14);
+        assert_eq!(list.push(&mut node5), &15);
+
+        list.remove(&mut node1);
+        assert_eq!(list.pop().unwrap(), &12);
+        list.remove(&mut node3);
+        assert_eq!(list.pop().unwrap(), &14);
+        list.remove(&mut node5);
+        assert_empty(&mut list);
+
+        assert_eq!(list.push(&mut node1), &11);
+        assert_eq!(list.pop().unwrap(), &11);
+        assert_empty(&mut list);
+
+        assert_eq!(list.push(&mut node3), &13);
+        assert_eq!(list.push(&mut node4), &14);
+        list.remove(&mut node3);
+        list.remove(&mut node4);
+        assert_empty(&mut list);
+    }
+}
+
+#[test]
+fn complex_pushes_pops() {
+    unsafe {
+        let mut node1 = UnsafeListEntry::new(1234);
+        let mut node2 = UnsafeListEntry::new(4567);
+        let mut node3 = UnsafeListEntry::new(9999);
+        let mut node4 = UnsafeListEntry::new(8642);
+        let mut list = UnsafeList::new();
+        list.push(&mut node1);
+        list.push(&mut node2);
+        assert_eq!(list.pop().unwrap(), &1234);
+        list.push(&mut node3);
+        assert_eq!(list.pop().unwrap(), &4567);
+        assert_eq!(list.pop().unwrap(), &9999);
+        assert_empty(&mut list);
+        list.push(&mut node4);
+        assert_eq!(list.pop().unwrap(), &8642);
+        assert_empty(&mut list);
+    }
+}
+
+#[test]
+fn cell() {
+    unsafe {
+        let mut node = UnsafeListEntry::new(Cell::new(0));
+        let mut list = UnsafeList::new();
+        let noderef = list.push(&mut node);
+        assert_eq!(noderef.get(), 0);
+        list.pop().unwrap().set(1);
+        assert_empty(&mut list);
+        assert_eq!(noderef.get(), 1);
+    }
+}
diff --git a/library/std/src/sys/unix/ext/net.rs b/library/std/src/sys/unix/ext/net.rs
index 55803ddfc43..0e07106f5ce 100644
--- a/library/std/src/sys/unix/ext/net.rs
+++ b/library/std/src/sys/unix/ext/net.rs
@@ -2,6 +2,9 @@
 
 //! Unix-specific networking functionality
 
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
 // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
 #[cfg(not(unix))]
 #[allow(non_camel_case_types)]
@@ -1620,382 +1623,3 @@ impl IntoRawFd for UnixDatagram {
         self.0.into_inner()
     }
 }
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod test {
-    use crate::io::prelude::*;
-    use crate::io::{self, ErrorKind};
-    use crate::sys_common::io::test::tmpdir;
-    use crate::thread;
-    use crate::time::Duration;
-
-    use super::*;
-
-    macro_rules! or_panic {
-        ($e:expr) => {
-            match $e {
-                Ok(e) => e,
-                Err(e) => panic!("{}", e),
-            }
-        };
-    }
-
-    #[test]
-    fn basic() {
-        let dir = tmpdir();
-        let socket_path = dir.path().join("sock");
-        let msg1 = b"hello";
-        let msg2 = b"world!";
-
-        let listener = or_panic!(UnixListener::bind(&socket_path));
-        let thread = thread::spawn(move || {
-            let mut stream = or_panic!(listener.accept()).0;
-            let mut buf = [0; 5];
-            or_panic!(stream.read(&mut buf));
-            assert_eq!(&msg1[..], &buf[..]);
-            or_panic!(stream.write_all(msg2));
-        });
-
-        let mut stream = or_panic!(UnixStream::connect(&socket_path));
-        assert_eq!(Some(&*socket_path), stream.peer_addr().unwrap().as_pathname());
-        or_panic!(stream.write_all(msg1));
-        let mut buf = vec![];
-        or_panic!(stream.read_to_end(&mut buf));
-        assert_eq!(&msg2[..], &buf[..]);
-        drop(stream);
-
-        thread.join().unwrap();
-    }
-
-    #[test]
-    fn vectored() {
-        let (mut s1, mut s2) = or_panic!(UnixStream::pair());
-
-        let len = or_panic!(s1.write_vectored(&[
-            IoSlice::new(b"hello"),
-            IoSlice::new(b" "),
-            IoSlice::new(b"world!")
-        ],));
-        assert_eq!(len, 12);
-
-        let mut buf1 = [0; 6];
-        let mut buf2 = [0; 7];
-        let len = or_panic!(
-            s2.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
-        );
-        assert_eq!(len, 12);
-        assert_eq!(&buf1, b"hello ");
-        assert_eq!(&buf2, b"world!\0");
-    }
-
-    #[test]
-    fn pair() {
-        let msg1 = b"hello";
-        let msg2 = b"world!";
-
-        let (mut s1, mut s2) = or_panic!(UnixStream::pair());
-        let thread = thread::spawn(move || {
-            // s1 must be moved in or the test will hang!
-            let mut buf = [0; 5];
-            or_panic!(s1.read(&mut buf));
-            assert_eq!(&msg1[..], &buf[..]);
-            or_panic!(s1.write_all(msg2));
-        });
-
-        or_panic!(s2.write_all(msg1));
-        let mut buf = vec![];
-        or_panic!(s2.read_to_end(&mut buf));
-        assert_eq!(&msg2[..], &buf[..]);
-        drop(s2);
-
-        thread.join().unwrap();
-    }
-
-    #[test]
-    fn try_clone() {
-        let dir = tmpdir();
-        let socket_path = dir.path().join("sock");
-        let msg1 = b"hello";
-        let msg2 = b"world";
-
-        let listener = or_panic!(UnixListener::bind(&socket_path));
-        let thread = thread::spawn(move || {
-            let mut stream = or_panic!(listener.accept()).0;
-            or_panic!(stream.write_all(msg1));
-            or_panic!(stream.write_all(msg2));
-        });
-
-        let mut stream = or_panic!(UnixStream::connect(&socket_path));
-        let mut stream2 = or_panic!(stream.try_clone());
-
-        let mut buf = [0; 5];
-        or_panic!(stream.read(&mut buf));
-        assert_eq!(&msg1[..], &buf[..]);
-        or_panic!(stream2.read(&mut buf));
-        assert_eq!(&msg2[..], &buf[..]);
-
-        thread.join().unwrap();
-    }
-
-    #[test]
-    fn iter() {
-        let dir = tmpdir();
-        let socket_path = dir.path().join("sock");
-
-        let listener = or_panic!(UnixListener::bind(&socket_path));
-        let thread = thread::spawn(move || {
-            for stream in listener.incoming().take(2) {
-                let mut stream = or_panic!(stream);
-                let mut buf = [0];
-                or_panic!(stream.read(&mut buf));
-            }
-        });
-
-        for _ in 0..2 {
-            let mut stream = or_panic!(UnixStream::connect(&socket_path));
-            or_panic!(stream.write_all(&[0]));
-        }
-
-        thread.join().unwrap();
-    }
-
-    #[test]
-    fn long_path() {
-        let dir = tmpdir();
-        let socket_path = dir.path().join(
-            "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa\
-                                    sasdfasdfasdasdfasdfasdfadfasdfasdfasdfasdfasdf",
-        );
-        match UnixStream::connect(&socket_path) {
-            Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
-            Err(e) => panic!("unexpected error {}", e),
-            Ok(_) => panic!("unexpected success"),
-        }
-
-        match UnixListener::bind(&socket_path) {
-            Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
-            Err(e) => panic!("unexpected error {}", e),
-            Ok(_) => panic!("unexpected success"),
-        }
-
-        match UnixDatagram::bind(&socket_path) {
-            Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
-            Err(e) => panic!("unexpected error {}", e),
-            Ok(_) => panic!("unexpected success"),
-        }
-    }
-
-    #[test]
-    fn timeouts() {
-        let dir = tmpdir();
-        let socket_path = dir.path().join("sock");
-
-        let _listener = or_panic!(UnixListener::bind(&socket_path));
-
-        let stream = or_panic!(UnixStream::connect(&socket_path));
-        let dur = Duration::new(15410, 0);
-
-        assert_eq!(None, or_panic!(stream.read_timeout()));
-
-        or_panic!(stream.set_read_timeout(Some(dur)));
-        assert_eq!(Some(dur), or_panic!(stream.read_timeout()));
-
-        assert_eq!(None, or_panic!(stream.write_timeout()));
-
-        or_panic!(stream.set_write_timeout(Some(dur)));
-        assert_eq!(Some(dur), or_panic!(stream.write_timeout()));
-
-        or_panic!(stream.set_read_timeout(None));
-        assert_eq!(None, or_panic!(stream.read_timeout()));
-
-        or_panic!(stream.set_write_timeout(None));
-        assert_eq!(None, or_panic!(stream.write_timeout()));
-    }
-
-    #[test]
-    fn test_read_timeout() {
-        let dir = tmpdir();
-        let socket_path = dir.path().join("sock");
-
-        let _listener = or_panic!(UnixListener::bind(&socket_path));
-
-        let mut stream = or_panic!(UnixStream::connect(&socket_path));
-        or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
-
-        let mut buf = [0; 10];
-        let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
-        assert!(
-            kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
-            "unexpected_error: {:?}",
-            kind
-        );
-    }
-
-    #[test]
-    fn test_read_with_timeout() {
-        let dir = tmpdir();
-        let socket_path = dir.path().join("sock");
-
-        let listener = or_panic!(UnixListener::bind(&socket_path));
-
-        let mut stream = or_panic!(UnixStream::connect(&socket_path));
-        or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
-
-        let mut other_end = or_panic!(listener.accept()).0;
-        or_panic!(other_end.write_all(b"hello world"));
-
-        let mut buf = [0; 11];
-        or_panic!(stream.read(&mut buf));
-        assert_eq!(b"hello world", &buf[..]);
-
-        let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
-        assert!(
-            kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
-            "unexpected_error: {:?}",
-            kind
-        );
-    }
-
-    // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
-    // when passed zero Durations
-    #[test]
-    fn test_unix_stream_timeout_zero_duration() {
-        let dir = tmpdir();
-        let socket_path = dir.path().join("sock");
-
-        let listener = or_panic!(UnixListener::bind(&socket_path));
-        let stream = or_panic!(UnixStream::connect(&socket_path));
-
-        let result = stream.set_write_timeout(Some(Duration::new(0, 0)));
-        let err = result.unwrap_err();
-        assert_eq!(err.kind(), ErrorKind::InvalidInput);
-
-        let result = stream.set_read_timeout(Some(Duration::new(0, 0)));
-        let err = result.unwrap_err();
-        assert_eq!(err.kind(), ErrorKind::InvalidInput);
-
-        drop(listener);
-    }
-
-    #[test]
-    fn test_unix_datagram() {
-        let dir = tmpdir();
-        let path1 = dir.path().join("sock1");
-        let path2 = dir.path().join("sock2");
-
-        let sock1 = or_panic!(UnixDatagram::bind(&path1));
-        let sock2 = or_panic!(UnixDatagram::bind(&path2));
-
-        let msg = b"hello world";
-        or_panic!(sock1.send_to(msg, &path2));
-        let mut buf = [0; 11];
-        or_panic!(sock2.recv_from(&mut buf));
-        assert_eq!(msg, &buf[..]);
-    }
-
-    #[test]
-    fn test_unnamed_unix_datagram() {
-        let dir = tmpdir();
-        let path1 = dir.path().join("sock1");
-
-        let sock1 = or_panic!(UnixDatagram::bind(&path1));
-        let sock2 = or_panic!(UnixDatagram::unbound());
-
-        let msg = b"hello world";
-        or_panic!(sock2.send_to(msg, &path1));
-        let mut buf = [0; 11];
-        let (usize, addr) = or_panic!(sock1.recv_from(&mut buf));
-        assert_eq!(usize, 11);
-        assert!(addr.is_unnamed());
-        assert_eq!(msg, &buf[..]);
-    }
-
-    #[test]
-    fn test_connect_unix_datagram() {
-        let dir = tmpdir();
-        let path1 = dir.path().join("sock1");
-        let path2 = dir.path().join("sock2");
-
-        let bsock1 = or_panic!(UnixDatagram::bind(&path1));
-        let bsock2 = or_panic!(UnixDatagram::bind(&path2));
-        let sock = or_panic!(UnixDatagram::unbound());
-        or_panic!(sock.connect(&path1));
-
-        // Check send()
-        let msg = b"hello there";
-        or_panic!(sock.send(msg));
-        let mut buf = [0; 11];
-        let (usize, addr) = or_panic!(bsock1.recv_from(&mut buf));
-        assert_eq!(usize, 11);
-        assert!(addr.is_unnamed());
-        assert_eq!(msg, &buf[..]);
-
-        // Changing default socket works too
-        or_panic!(sock.connect(&path2));
-        or_panic!(sock.send(msg));
-        or_panic!(bsock2.recv_from(&mut buf));
-    }
-
-    #[test]
-    fn test_unix_datagram_recv() {
-        let dir = tmpdir();
-        let path1 = dir.path().join("sock1");
-
-        let sock1 = or_panic!(UnixDatagram::bind(&path1));
-        let sock2 = or_panic!(UnixDatagram::unbound());
-        or_panic!(sock2.connect(&path1));
-
-        let msg = b"hello world";
-        or_panic!(sock2.send(msg));
-        let mut buf = [0; 11];
-        let size = or_panic!(sock1.recv(&mut buf));
-        assert_eq!(size, 11);
-        assert_eq!(msg, &buf[..]);
-    }
-
-    #[test]
-    fn datagram_pair() {
-        let msg1 = b"hello";
-        let msg2 = b"world!";
-
-        let (s1, s2) = or_panic!(UnixDatagram::pair());
-        let thread = thread::spawn(move || {
-            // s1 must be moved in or the test will hang!
-            let mut buf = [0; 5];
-            or_panic!(s1.recv(&mut buf));
-            assert_eq!(&msg1[..], &buf[..]);
-            or_panic!(s1.send(msg2));
-        });
-
-        or_panic!(s2.send(msg1));
-        let mut buf = [0; 6];
-        or_panic!(s2.recv(&mut buf));
-        assert_eq!(&msg2[..], &buf[..]);
-        drop(s2);
-
-        thread.join().unwrap();
-    }
-
-    // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
-    // when passed zero Durations
-    #[test]
-    fn test_unix_datagram_timeout_zero_duration() {
-        let dir = tmpdir();
-        let path = dir.path().join("sock");
-
-        let datagram = or_panic!(UnixDatagram::bind(&path));
-
-        let result = datagram.set_write_timeout(Some(Duration::new(0, 0)));
-        let err = result.unwrap_err();
-        assert_eq!(err.kind(), ErrorKind::InvalidInput);
-
-        let result = datagram.set_read_timeout(Some(Duration::new(0, 0)));
-        let err = result.unwrap_err();
-        assert_eq!(err.kind(), ErrorKind::InvalidInput);
-    }
-
-    #[test]
-    fn abstract_namespace_not_allowed() {
-        assert!(UnixStream::connect("\0asdf").is_err());
-    }
-}
diff --git a/library/std/src/sys/unix/ext/net/tests.rs b/library/std/src/sys/unix/ext/net/tests.rs
new file mode 100644
index 00000000000..be98766f0f3
--- /dev/null
+++ b/library/std/src/sys/unix/ext/net/tests.rs
@@ -0,0 +1,374 @@
+use crate::io::prelude::*;
+use crate::io::{self, ErrorKind};
+use crate::sys_common::io::test::tmpdir;
+use crate::thread;
+use crate::time::Duration;
+
+use super::*;
+
+macro_rules! or_panic {
+    ($e:expr) => {
+        match $e {
+            Ok(e) => e,
+            Err(e) => panic!("{}", e),
+        }
+    };
+}
+
+#[test]
+fn basic() {
+    let dir = tmpdir();
+    let socket_path = dir.path().join("sock");
+    let msg1 = b"hello";
+    let msg2 = b"world!";
+
+    let listener = or_panic!(UnixListener::bind(&socket_path));
+    let thread = thread::spawn(move || {
+        let mut stream = or_panic!(listener.accept()).0;
+        let mut buf = [0; 5];
+        or_panic!(stream.read(&mut buf));
+        assert_eq!(&msg1[..], &buf[..]);
+        or_panic!(stream.write_all(msg2));
+    });
+
+    let mut stream = or_panic!(UnixStream::connect(&socket_path));
+    assert_eq!(Some(&*socket_path), stream.peer_addr().unwrap().as_pathname());
+    or_panic!(stream.write_all(msg1));
+    let mut buf = vec![];
+    or_panic!(stream.read_to_end(&mut buf));
+    assert_eq!(&msg2[..], &buf[..]);
+    drop(stream);
+
+    thread.join().unwrap();
+}
+
+#[test]
+fn vectored() {
+    let (mut s1, mut s2) = or_panic!(UnixStream::pair());
+
+    let len = or_panic!(s1.write_vectored(&[
+        IoSlice::new(b"hello"),
+        IoSlice::new(b" "),
+        IoSlice::new(b"world!")
+    ],));
+    assert_eq!(len, 12);
+
+    let mut buf1 = [0; 6];
+    let mut buf2 = [0; 7];
+    let len =
+        or_panic!(s2.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],));
+    assert_eq!(len, 12);
+    assert_eq!(&buf1, b"hello ");
+    assert_eq!(&buf2, b"world!\0");
+}
+
+#[test]
+fn pair() {
+    let msg1 = b"hello";
+    let msg2 = b"world!";
+
+    let (mut s1, mut s2) = or_panic!(UnixStream::pair());
+    let thread = thread::spawn(move || {
+        // s1 must be moved in or the test will hang!
+        let mut buf = [0; 5];
+        or_panic!(s1.read(&mut buf));
+        assert_eq!(&msg1[..], &buf[..]);
+        or_panic!(s1.write_all(msg2));
+    });
+
+    or_panic!(s2.write_all(msg1));
+    let mut buf = vec![];
+    or_panic!(s2.read_to_end(&mut buf));
+    assert_eq!(&msg2[..], &buf[..]);
+    drop(s2);
+
+    thread.join().unwrap();
+}
+
+#[test]
+fn try_clone() {
+    let dir = tmpdir();
+    let socket_path = dir.path().join("sock");
+    let msg1 = b"hello";
+    let msg2 = b"world";
+
+    let listener = or_panic!(UnixListener::bind(&socket_path));
+    let thread = thread::spawn(move || {
+        let mut stream = or_panic!(listener.accept()).0;
+        or_panic!(stream.write_all(msg1));
+        or_panic!(stream.write_all(msg2));
+    });
+
+    let mut stream = or_panic!(UnixStream::connect(&socket_path));
+    let mut stream2 = or_panic!(stream.try_clone());
+
+    let mut buf = [0; 5];
+    or_panic!(stream.read(&mut buf));
+    assert_eq!(&msg1[..], &buf[..]);
+    or_panic!(stream2.read(&mut buf));
+    assert_eq!(&msg2[..], &buf[..]);
+
+    thread.join().unwrap();
+}
+
+#[test]
+fn iter() {
+    let dir = tmpdir();
+    let socket_path = dir.path().join("sock");
+
+    let listener = or_panic!(UnixListener::bind(&socket_path));
+    let thread = thread::spawn(move || {
+        for stream in listener.incoming().take(2) {
+            let mut stream = or_panic!(stream);
+            let mut buf = [0];
+            or_panic!(stream.read(&mut buf));
+        }
+    });
+
+    for _ in 0..2 {
+        let mut stream = or_panic!(UnixStream::connect(&socket_path));
+        or_panic!(stream.write_all(&[0]));
+    }
+
+    thread.join().unwrap();
+}
+
+#[test]
+fn long_path() {
+    let dir = tmpdir();
+    let socket_path = dir.path().join(
+        "asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa\
+                                sasdfasdfasdasdfasdfasdfadfasdfasdfasdfasdfasdf",
+    );
+    match UnixStream::connect(&socket_path) {
+        Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
+        Err(e) => panic!("unexpected error {}", e),
+        Ok(_) => panic!("unexpected success"),
+    }
+
+    match UnixListener::bind(&socket_path) {
+        Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
+        Err(e) => panic!("unexpected error {}", e),
+        Ok(_) => panic!("unexpected success"),
+    }
+
+    match UnixDatagram::bind(&socket_path) {
+        Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
+        Err(e) => panic!("unexpected error {}", e),
+        Ok(_) => panic!("unexpected success"),
+    }
+}
+
+#[test]
+fn timeouts() {
+    let dir = tmpdir();
+    let socket_path = dir.path().join("sock");
+
+    let _listener = or_panic!(UnixListener::bind(&socket_path));
+
+    let stream = or_panic!(UnixStream::connect(&socket_path));
+    let dur = Duration::new(15410, 0);
+
+    assert_eq!(None, or_panic!(stream.read_timeout()));
+
+    or_panic!(stream.set_read_timeout(Some(dur)));
+    assert_eq!(Some(dur), or_panic!(stream.read_timeout()));
+
+    assert_eq!(None, or_panic!(stream.write_timeout()));
+
+    or_panic!(stream.set_write_timeout(Some(dur)));
+    assert_eq!(Some(dur), or_panic!(stream.write_timeout()));
+
+    or_panic!(stream.set_read_timeout(None));
+    assert_eq!(None, or_panic!(stream.read_timeout()));
+
+    or_panic!(stream.set_write_timeout(None));
+    assert_eq!(None, or_panic!(stream.write_timeout()));
+}
+
+#[test]
+fn test_read_timeout() {
+    let dir = tmpdir();
+    let socket_path = dir.path().join("sock");
+
+    let _listener = or_panic!(UnixListener::bind(&socket_path));
+
+    let mut stream = or_panic!(UnixStream::connect(&socket_path));
+    or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
+
+    let mut buf = [0; 10];
+    let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
+    assert!(
+        kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
+        "unexpected_error: {:?}",
+        kind
+    );
+}
+
+#[test]
+fn test_read_with_timeout() {
+    let dir = tmpdir();
+    let socket_path = dir.path().join("sock");
+
+    let listener = or_panic!(UnixListener::bind(&socket_path));
+
+    let mut stream = or_panic!(UnixStream::connect(&socket_path));
+    or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
+
+    let mut other_end = or_panic!(listener.accept()).0;
+    or_panic!(other_end.write_all(b"hello world"));
+
+    let mut buf = [0; 11];
+    or_panic!(stream.read(&mut buf));
+    assert_eq!(b"hello world", &buf[..]);
+
+    let kind = stream.read_exact(&mut buf).err().expect("expected error").kind();
+    assert!(
+        kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut,
+        "unexpected_error: {:?}",
+        kind
+    );
+}
+
+// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+// when passed zero Durations
+#[test]
+fn test_unix_stream_timeout_zero_duration() {
+    let dir = tmpdir();
+    let socket_path = dir.path().join("sock");
+
+    let listener = or_panic!(UnixListener::bind(&socket_path));
+    let stream = or_panic!(UnixStream::connect(&socket_path));
+
+    let result = stream.set_write_timeout(Some(Duration::new(0, 0)));
+    let err = result.unwrap_err();
+    assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+    let result = stream.set_read_timeout(Some(Duration::new(0, 0)));
+    let err = result.unwrap_err();
+    assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+    drop(listener);
+}
+
+#[test]
+fn test_unix_datagram() {
+    let dir = tmpdir();
+    let path1 = dir.path().join("sock1");
+    let path2 = dir.path().join("sock2");
+
+    let sock1 = or_panic!(UnixDatagram::bind(&path1));
+    let sock2 = or_panic!(UnixDatagram::bind(&path2));
+
+    let msg = b"hello world";
+    or_panic!(sock1.send_to(msg, &path2));
+    let mut buf = [0; 11];
+    or_panic!(sock2.recv_from(&mut buf));
+    assert_eq!(msg, &buf[..]);
+}
+
+#[test]
+fn test_unnamed_unix_datagram() {
+    let dir = tmpdir();
+    let path1 = dir.path().join("sock1");
+
+    let sock1 = or_panic!(UnixDatagram::bind(&path1));
+    let sock2 = or_panic!(UnixDatagram::unbound());
+
+    let msg = b"hello world";
+    or_panic!(sock2.send_to(msg, &path1));
+    let mut buf = [0; 11];
+    let (usize, addr) = or_panic!(sock1.recv_from(&mut buf));
+    assert_eq!(usize, 11);
+    assert!(addr.is_unnamed());
+    assert_eq!(msg, &buf[..]);
+}
+
+#[test]
+fn test_connect_unix_datagram() {
+    let dir = tmpdir();
+    let path1 = dir.path().join("sock1");
+    let path2 = dir.path().join("sock2");
+
+    let bsock1 = or_panic!(UnixDatagram::bind(&path1));
+    let bsock2 = or_panic!(UnixDatagram::bind(&path2));
+    let sock = or_panic!(UnixDatagram::unbound());
+    or_panic!(sock.connect(&path1));
+
+    // Check send()
+    let msg = b"hello there";
+    or_panic!(sock.send(msg));
+    let mut buf = [0; 11];
+    let (usize, addr) = or_panic!(bsock1.recv_from(&mut buf));
+    assert_eq!(usize, 11);
+    assert!(addr.is_unnamed());
+    assert_eq!(msg, &buf[..]);
+
+    // Changing default socket works too
+    or_panic!(sock.connect(&path2));
+    or_panic!(sock.send(msg));
+    or_panic!(bsock2.recv_from(&mut buf));
+}
+
+#[test]
+fn test_unix_datagram_recv() {
+    let dir = tmpdir();
+    let path1 = dir.path().join("sock1");
+
+    let sock1 = or_panic!(UnixDatagram::bind(&path1));
+    let sock2 = or_panic!(UnixDatagram::unbound());
+    or_panic!(sock2.connect(&path1));
+
+    let msg = b"hello world";
+    or_panic!(sock2.send(msg));
+    let mut buf = [0; 11];
+    let size = or_panic!(sock1.recv(&mut buf));
+    assert_eq!(size, 11);
+    assert_eq!(msg, &buf[..]);
+}
+
+#[test]
+fn datagram_pair() {
+    let msg1 = b"hello";
+    let msg2 = b"world!";
+
+    let (s1, s2) = or_panic!(UnixDatagram::pair());
+    let thread = thread::spawn(move || {
+        // s1 must be moved in or the test will hang!
+        let mut buf = [0; 5];
+        or_panic!(s1.recv(&mut buf));
+        assert_eq!(&msg1[..], &buf[..]);
+        or_panic!(s1.send(msg2));
+    });
+
+    or_panic!(s2.send(msg1));
+    let mut buf = [0; 6];
+    or_panic!(s2.recv(&mut buf));
+    assert_eq!(&msg2[..], &buf[..]);
+    drop(s2);
+
+    thread.join().unwrap();
+}
+
+// Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+// when passed zero Durations
+#[test]
+fn test_unix_datagram_timeout_zero_duration() {
+    let dir = tmpdir();
+    let path = dir.path().join("sock");
+
+    let datagram = or_panic!(UnixDatagram::bind(&path));
+
+    let result = datagram.set_write_timeout(Some(Duration::new(0, 0)));
+    let err = result.unwrap_err();
+    assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+    let result = datagram.set_read_timeout(Some(Duration::new(0, 0)));
+    let err = result.unwrap_err();
+    assert_eq!(err.kind(), ErrorKind::InvalidInput);
+}
+
+#[test]
+fn abstract_namespace_not_allowed() {
+    assert!(UnixStream::connect("\0asdf").is_err());
+}
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index ba169b251b0..b2f15eda9d7 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -1,5 +1,8 @@
 #![unstable(reason = "not public", issue = "none", feature = "fd")]
 
+#[cfg(test)]
+mod tests;
+
 use crate::cmp;
 use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
 use crate::mem;
@@ -279,16 +282,3 @@ impl Drop for FileDesc {
         let _ = unsafe { libc::close(self.fd) };
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use super::{FileDesc, IoSlice};
-    use core::mem::ManuallyDrop;
-
-    #[test]
-    fn limit_vector_count() {
-        let stdout = ManuallyDrop::new(FileDesc { fd: 1 });
-        let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
-        assert!(stdout.write_vectored(&bufs).is_ok());
-    }
-}
diff --git a/library/std/src/sys/unix/fd/tests.rs b/library/std/src/sys/unix/fd/tests.rs
new file mode 100644
index 00000000000..a932043cbc6
--- /dev/null
+++ b/library/std/src/sys/unix/fd/tests.rs
@@ -0,0 +1,9 @@
+use super::{FileDesc, IoSlice};
+use core::mem::ManuallyDrop;
+
+#[test]
+fn limit_vector_count() {
+    let stdout = ManuallyDrop::new(FileDesc { fd: 1 });
+    let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
+    assert!(stdout.write_vectored(&bufs).is_ok());
+}
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 2fcb5b9c4e6..4aa61fc5bf6 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -2,6 +2,9 @@
 
 #![allow(unused_imports)] // lots of cfg code here
 
+#[cfg(all(test, target_env = "gnu"))]
+mod tests;
+
 use crate::os::unix::prelude::*;
 
 use crate::error::Error as StdError;
@@ -645,30 +648,3 @@ fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
         _ => None,
     }
 }
-
-#[cfg(all(test, target_env = "gnu"))]
-mod test {
-    use super::*;
-
-    #[test]
-    fn test_glibc_version() {
-        // This mostly just tests that the weak linkage doesn't panic wildly...
-        glibc_version();
-    }
-
-    #[test]
-    fn test_parse_glibc_version() {
-        let cases = [
-            ("0.0", Some((0, 0))),
-            ("01.+2", Some((1, 2))),
-            ("3.4.5.six", Some((3, 4))),
-            ("1", None),
-            ("1.-2", None),
-            ("1.foo", None),
-            ("foo.1", None),
-        ];
-        for &(version_str, parsed) in cases.iter() {
-            assert_eq!(parsed, parse_glibc_version(version_str));
-        }
-    }
-}
diff --git a/library/std/src/sys/unix/os/tests.rs b/library/std/src/sys/unix/os/tests.rs
new file mode 100644
index 00000000000..0e1dcb390a0
--- /dev/null
+++ b/library/std/src/sys/unix/os/tests.rs
@@ -0,0 +1,23 @@
+use super::*;
+
+#[test]
+fn test_glibc_version() {
+    // This mostly just tests that the weak linkage doesn't panic wildly...
+    glibc_version();
+}
+
+#[test]
+fn test_parse_glibc_version() {
+    let cases = [
+        ("0.0", Some((0, 0))),
+        ("01.+2", Some((1, 2))),
+        ("3.4.5.six", Some((3, 4))),
+        ("1", None),
+        ("1.-2", None),
+        ("1.foo", None),
+        ("foo.1", None),
+    ];
+    for &(version_str, parsed) in cases.iter() {
+        assert_eq!(parsed, parse_glibc_version(version_str));
+    }
+}
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index 6e33cdd3c48..f8666485eec 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -1,3 +1,6 @@
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests;
+
 use crate::os::unix::prelude::*;
 
 use crate::collections::BTreeMap;
@@ -399,71 +402,3 @@ impl ExitCode {
         self.0 as i32
     }
 }
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests {
-    use super::*;
-
-    use crate::ffi::OsStr;
-    use crate::mem;
-    use crate::ptr;
-    use crate::sys::cvt;
-
-    macro_rules! t {
-        ($e:expr) => {
-            match $e {
-                Ok(t) => t,
-                Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
-            }
-        };
-    }
-
-    // See #14232 for more information, but it appears that signal delivery to a
-    // newly spawned process may just be raced in the macOS, so to prevent this
-    // test from being flaky we ignore it on macOS.
-    #[test]
-    #[cfg_attr(target_os = "macos", ignore)]
-    // When run under our current QEMU emulation test suite this test fails,
-    // although the reason isn't very clear as to why. For now this test is
-    // ignored there.
-    #[cfg_attr(target_arch = "arm", ignore)]
-    #[cfg_attr(target_arch = "aarch64", ignore)]
-    #[cfg_attr(target_arch = "riscv64", ignore)]
-    fn test_process_mask() {
-        unsafe {
-            // Test to make sure that a signal mask does not get inherited.
-            let mut cmd = Command::new(OsStr::new("cat"));
-
-            let mut set = mem::MaybeUninit::<libc::sigset_t>::uninit();
-            let mut old_set = mem::MaybeUninit::<libc::sigset_t>::uninit();
-            t!(cvt(sigemptyset(set.as_mut_ptr())));
-            t!(cvt(sigaddset(set.as_mut_ptr(), libc::SIGINT)));
-            t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), old_set.as_mut_ptr())));
-
-            cmd.stdin(Stdio::MakePipe);
-            cmd.stdout(Stdio::MakePipe);
-
-            let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
-            let stdin_write = pipes.stdin.take().unwrap();
-            let stdout_read = pipes.stdout.take().unwrap();
-
-            t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, old_set.as_ptr(), ptr::null_mut())));
-
-            t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT)));
-            // We need to wait until SIGINT is definitely delivered. The
-            // easiest way is to write something to cat, and try to read it
-            // back: if SIGINT is unmasked, it'll get delivered when cat is
-            // next scheduled.
-            let _ = stdin_write.write(b"Hello");
-            drop(stdin_write);
-
-            // Either EOF or failure (EPIPE) is okay.
-            let mut buf = [0; 5];
-            if let Ok(ret) = stdout_read.read(&mut buf) {
-                assert_eq!(ret, 0);
-            }
-
-            t!(cat.wait());
-        }
-    }
-}
diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs
new file mode 100644
index 00000000000..e72fbf0beb4
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_common/tests.rs
@@ -0,0 +1,64 @@
+use super::*;
+
+use crate::ffi::OsStr;
+use crate::mem;
+use crate::ptr;
+use crate::sys::cvt;
+
+macro_rules! t {
+    ($e:expr) => {
+        match $e {
+            Ok(t) => t,
+            Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
+        }
+    };
+}
+
+// See #14232 for more information, but it appears that signal delivery to a
+// newly spawned process may just be raced in the macOS, so to prevent this
+// test from being flaky we ignore it on macOS.
+#[test]
+#[cfg_attr(target_os = "macos", ignore)]
+// When run under our current QEMU emulation test suite this test fails,
+// although the reason isn't very clear as to why. For now this test is
+// ignored there.
+#[cfg_attr(target_arch = "arm", ignore)]
+#[cfg_attr(target_arch = "aarch64", ignore)]
+#[cfg_attr(target_arch = "riscv64", ignore)]
+fn test_process_mask() {
+    unsafe {
+        // Test to make sure that a signal mask does not get inherited.
+        let mut cmd = Command::new(OsStr::new("cat"));
+
+        let mut set = mem::MaybeUninit::<libc::sigset_t>::uninit();
+        let mut old_set = mem::MaybeUninit::<libc::sigset_t>::uninit();
+        t!(cvt(sigemptyset(set.as_mut_ptr())));
+        t!(cvt(sigaddset(set.as_mut_ptr(), libc::SIGINT)));
+        t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), old_set.as_mut_ptr())));
+
+        cmd.stdin(Stdio::MakePipe);
+        cmd.stdout(Stdio::MakePipe);
+
+        let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
+        let stdin_write = pipes.stdin.take().unwrap();
+        let stdout_read = pipes.stdout.take().unwrap();
+
+        t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, old_set.as_ptr(), ptr::null_mut())));
+
+        t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT)));
+        // We need to wait until SIGINT is definitely delivered. The
+        // easiest way is to write something to cat, and try to read it
+        // back: if SIGINT is unmasked, it'll get delivered when cat is
+        // next scheduled.
+        let _ = stdin_write.write(b"Hello");
+        drop(stdin_write);
+
+        // Either EOF or failure (EPIPE) is okay.
+        let mut buf = [0; 5];
+        if let Ok(ret) = stdout_read.read(&mut buf) {
+            assert_eq!(ret, 0);
+        }
+
+        t!(cat.wait());
+    }
+}
diff --git a/library/std/src/sys/vxworks/net.rs b/library/std/src/sys/vxworks/net.rs
index 32c27ab6e9e..7613fbec46f 100644
--- a/library/std/src/sys/vxworks/net.rs
+++ b/library/std/src/sys/vxworks/net.rs
@@ -1,3 +1,6 @@
+#[cfg(all(test, taget_env = "gnu"))]
+mod tests;
+
 use crate::cmp;
 use crate::ffi::CStr;
 use crate::io;
@@ -330,30 +333,3 @@ fn on_resolver_failure() {
 
 #[cfg(not(target_env = "gnu"))]
 fn on_resolver_failure() {}
-
-#[cfg(all(test, taget_env = "gnu"))]
-mod test {
-    use super::*;
-
-    #[test]
-    fn test_res_init() {
-        // This mostly just tests that the weak linkage doesn't panic wildly...
-        res_init_if_glibc_before_2_26().unwrap();
-    }
-
-    #[test]
-    fn test_parse_glibc_version() {
-        let cases = [
-            ("0.0", Some((0, 0))),
-            ("01.+2", Some((1, 2))),
-            ("3.4.5.six", Some((3, 4))),
-            ("1", None),
-            ("1.-2", None),
-            ("1.foo", None),
-            ("foo.1", None),
-        ];
-        for &(version_str, parsed) in cases.iter() {
-            assert_eq!(parsed, parse_glibc_version(version_str));
-        }
-    }
-}
diff --git a/library/std/src/sys/vxworks/net/tests.rs b/library/std/src/sys/vxworks/net/tests.rs
new file mode 100644
index 00000000000..e7c6e348f8e
--- /dev/null
+++ b/library/std/src/sys/vxworks/net/tests.rs
@@ -0,0 +1,23 @@
+use super::*;
+
+#[test]
+fn test_res_init() {
+    // This mostly just tests that the weak linkage doesn't panic wildly...
+    res_init_if_glibc_before_2_26().unwrap();
+}
+
+#[test]
+fn test_parse_glibc_version() {
+    let cases = [
+        ("0.0", Some((0, 0))),
+        ("01.+2", Some((1, 2))),
+        ("3.4.5.six", Some((3, 4))),
+        ("1", None),
+        ("1.-2", None),
+        ("1.foo", None),
+        ("foo.1", None),
+    ];
+    for &(version_str, parsed) in cases.iter() {
+        assert_eq!(parsed, parse_glibc_version(version_str));
+    }
+}
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
index 5fbea2a2910..bcc2ea9ae00 100644
--- a/library/std/src/sys/windows/args.rs
+++ b/library/std/src/sys/windows/args.rs
@@ -1,5 +1,8 @@
 #![allow(dead_code)] // runtime init functions not used during testing
 
+#[cfg(test)]
+mod tests;
+
 use crate::ffi::OsString;
 use crate::fmt;
 use crate::os::windows::prelude::*;
@@ -198,69 +201,3 @@ impl ExactSizeIterator for Args {
         self.parsed_args_list.len()
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use crate::ffi::OsString;
-    use crate::sys::windows::args::*;
-
-    fn chk(string: &str, parts: &[&str]) {
-        let mut wide: Vec<u16> = OsString::from(string).encode_wide().collect();
-        wide.push(0);
-        let parsed = unsafe {
-            parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE"))
-        };
-        let expected: Vec<OsString> = parts.iter().map(|k| OsString::from(k)).collect();
-        assert_eq!(parsed.as_slice(), expected.as_slice());
-    }
-
-    #[test]
-    fn empty() {
-        chk("", &["TEST.EXE"]);
-        chk("\0", &["TEST.EXE"]);
-    }
-
-    #[test]
-    fn single_words() {
-        chk("EXE one_word", &["EXE", "one_word"]);
-        chk("EXE a", &["EXE", "a"]);
-        chk("EXE 😅", &["EXE", "😅"]);
-        chk("EXE 😅🤦", &["EXE", "😅🤦"]);
-    }
-
-    #[test]
-    fn official_examples() {
-        chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]);
-        chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]);
-        chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]);
-        chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]);
-    }
-
-    #[test]
-    fn whitespace_behavior() {
-        chk(r#" test"#, &["", "test"]);
-        chk(r#"  test"#, &["", "test"]);
-        chk(r#" test test2"#, &["", "test", "test2"]);
-        chk(r#" test  test2"#, &["", "test", "test2"]);
-        chk(r#"test test2 "#, &["test", "test2"]);
-        chk(r#"test  test2 "#, &["test", "test2"]);
-        chk(r#"test "#, &["test"]);
-    }
-
-    #[test]
-    fn genius_quotes() {
-        chk(r#"EXE "" """#, &["EXE", "", ""]);
-        chk(r#"EXE "" """"#, &["EXE", "", "\""]);
-        chk(
-            r#"EXE "this is """all""" in the same argument""#,
-            &["EXE", "this is \"all\" in the same argument"],
-        );
-        chk(r#"EXE "a"""#, &["EXE", "a\""]);
-        chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]);
-        // quotes cannot be escaped in command names
-        chk(r#""EXE" check"#, &["EXE", "check"]);
-        chk(r#""EXE check""#, &["EXE check"]);
-        chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]);
-        chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#, "check"]);
-    }
-}
diff --git a/library/std/src/sys/windows/args/tests.rs b/library/std/src/sys/windows/args/tests.rs
new file mode 100644
index 00000000000..756a4361ea3
--- /dev/null
+++ b/library/std/src/sys/windows/args/tests.rs
@@ -0,0 +1,61 @@
+use crate::ffi::OsString;
+use crate::sys::windows::args::*;
+
+fn chk(string: &str, parts: &[&str]) {
+    let mut wide: Vec<u16> = OsString::from(string).encode_wide().collect();
+    wide.push(0);
+    let parsed =
+        unsafe { parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE")) };
+    let expected: Vec<OsString> = parts.iter().map(|k| OsString::from(k)).collect();
+    assert_eq!(parsed.as_slice(), expected.as_slice());
+}
+
+#[test]
+fn empty() {
+    chk("", &["TEST.EXE"]);
+    chk("\0", &["TEST.EXE"]);
+}
+
+#[test]
+fn single_words() {
+    chk("EXE one_word", &["EXE", "one_word"]);
+    chk("EXE a", &["EXE", "a"]);
+    chk("EXE 😅", &["EXE", "😅"]);
+    chk("EXE 😅🤦", &["EXE", "😅🤦"]);
+}
+
+#[test]
+fn official_examples() {
+    chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]);
+    chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]);
+    chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]);
+    chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]);
+}
+
+#[test]
+fn whitespace_behavior() {
+    chk(r#" test"#, &["", "test"]);
+    chk(r#"  test"#, &["", "test"]);
+    chk(r#" test test2"#, &["", "test", "test2"]);
+    chk(r#" test  test2"#, &["", "test", "test2"]);
+    chk(r#"test test2 "#, &["test", "test2"]);
+    chk(r#"test  test2 "#, &["test", "test2"]);
+    chk(r#"test "#, &["test"]);
+}
+
+#[test]
+fn genius_quotes() {
+    chk(r#"EXE "" """#, &["EXE", "", ""]);
+    chk(r#"EXE "" """"#, &["EXE", "", "\""]);
+    chk(
+        r#"EXE "this is """all""" in the same argument""#,
+        &["EXE", "this is \"all\" in the same argument"],
+    );
+    chk(r#"EXE "a"""#, &["EXE", "a\""]);
+    chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]);
+    // quotes cannot be escaped in command names
+    chk(r#""EXE" check"#, &["EXE", "check"]);
+    chk(r#""EXE check""#, &["EXE check"]);
+    chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]);
+    chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#, "check"]);
+}
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
index a0da2498bb7..77c378a66af 100644
--- a/library/std/src/sys/windows/os.rs
+++ b/library/std/src/sys/windows/os.rs
@@ -2,6 +2,9 @@
 
 #![allow(nonstandard_style)]
 
+#[cfg(test)]
+mod tests;
+
 use crate::os::windows::prelude::*;
 
 use crate::error::Error as StdError;
@@ -328,20 +331,3 @@ pub fn exit(code: i32) -> ! {
 pub fn getpid() -> u32 {
     unsafe { c::GetCurrentProcessId() as u32 }
 }
-
-#[cfg(test)]
-mod tests {
-    use crate::io::Error;
-    use crate::sys::c;
-
-    // tests `error_string` above
-    #[test]
-    fn ntstatus_error() {
-        const STATUS_UNSUCCESSFUL: u32 = 0xc000_0001;
-        assert!(
-            !Error::from_raw_os_error((STATUS_UNSUCCESSFUL | c::FACILITY_NT_BIT) as _)
-                .to_string()
-                .contains("FormatMessageW() returned error")
-        );
-    }
-}
diff --git a/library/std/src/sys/windows/os/tests.rs b/library/std/src/sys/windows/os/tests.rs
new file mode 100644
index 00000000000..458d6e11c20
--- /dev/null
+++ b/library/std/src/sys/windows/os/tests.rs
@@ -0,0 +1,13 @@
+use crate::io::Error;
+use crate::sys::c;
+
+// tests `error_string` above
+#[test]
+fn ntstatus_error() {
+    const STATUS_UNSUCCESSFUL: u32 = 0xc000_0001;
+    assert!(
+        !Error::from_raw_os_error((STATUS_UNSUCCESSFUL | c::FACILITY_NT_BIT) as _)
+            .to_string()
+            .contains("FormatMessageW() returned error")
+    );
+}
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index 7d6d4775eec..e18521bb30d 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -1,5 +1,8 @@
 #![unstable(feature = "process_internals", issue = "none")]
 
+#[cfg(test)]
+mod tests;
+
 use crate::borrow::Borrow;
 use crate::collections::BTreeMap;
 use crate::env;
@@ -526,41 +529,3 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
         None => Ok((ptr::null(), Vec::new())),
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use super::make_command_line;
-    use crate::ffi::{OsStr, OsString};
-
-    #[test]
-    fn test_make_command_line() {
-        fn test_wrapper(prog: &str, args: &[&str]) -> String {
-            let command_line = &make_command_line(
-                OsStr::new(prog),
-                &args.iter().map(|a| OsString::from(a)).collect::<Vec<OsString>>(),
-            )
-            .unwrap();
-            String::from_utf16(command_line).unwrap()
-        }
-
-        assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"]), "\"prog\" aaa bbb ccc");
-
-        assert_eq!(
-            test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
-            "\"C:\\Program Files\\blah\\blah.exe\" aaa"
-        );
-        assert_eq!(
-            test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
-            "\"C:\\Program Files\\test\" aa\\\"bb"
-        );
-        assert_eq!(test_wrapper("echo", &["a b c"]), "\"echo\" \"a b c\"");
-        assert_eq!(
-            test_wrapper("echo", &["\" \\\" \\", "\\"]),
-            "\"echo\" \"\\\" \\\\\\\" \\\\\" \\"
-        );
-        assert_eq!(
-            test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
-            "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\""
-        );
-    }
-}
diff --git a/library/std/src/sys/windows/process/tests.rs b/library/std/src/sys/windows/process/tests.rs
new file mode 100644
index 00000000000..81627ad139b
--- /dev/null
+++ b/library/std/src/sys/windows/process/tests.rs
@@ -0,0 +1,31 @@
+use super::make_command_line;
+use crate::ffi::{OsStr, OsString};
+
+#[test]
+fn test_make_command_line() {
+    fn test_wrapper(prog: &str, args: &[&str]) -> String {
+        let command_line = &make_command_line(
+            OsStr::new(prog),
+            &args.iter().map(|a| OsString::from(a)).collect::<Vec<OsString>>(),
+        )
+        .unwrap();
+        String::from_utf16(command_line).unwrap()
+    }
+
+    assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"]), "\"prog\" aaa bbb ccc");
+
+    assert_eq!(
+        test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
+        "\"C:\\Program Files\\blah\\blah.exe\" aaa"
+    );
+    assert_eq!(
+        test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
+        "\"C:\\Program Files\\test\" aa\\\"bb"
+    );
+    assert_eq!(test_wrapper("echo", &["a b c"]), "\"echo\" \"a b c\"");
+    assert_eq!(test_wrapper("echo", &["\" \\\" \\", "\\"]), "\"echo\" \"\\\" \\\\\\\" \\\\\" \\");
+    assert_eq!(
+        test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
+        "\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\""
+    );
+}